• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

blues / note-arduino / 28679653251

03 Jul 2026 07:23PM UTC coverage: 75.549% (-17.5%) from 93.061%
28679653251

Pull #168

github

web-flow
Merge 5b716ba08 into 8e06624fe
Pull Request #168: feat: add Notecard ping

120 of 190 branches covered (63.16%)

Branch coverage included in aggregate %.

20 of 100 new or added lines in 4 files covered. (20.0%)

362 of 448 relevant lines covered (80.8%)

11.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

13.39
/src/NotecardPing.cpp
1
/*!
2
 * @file NotecardPing.cpp
3
 *
4
 * Implementation of `Notecard::ping()` — ping the Notecard to verify
5
 * reachability and, on a serial link, negotiate the AUX port's baud rate
6
 * to match the rate passed to `Notecard::begin()` if necessary.
7
 *
8
 * Written by Ray Ozzie and Blues Inc. team.
9
 *
10
 * Copyright (c) 2019 Blues Inc. MIT License. Use of this source code is
11
 * governed by licenses granted by the copyright holder including that found in
12
 * the
13
 * <a href="https://github.com/blues/note-arduino/blob/master/LICENSE">LICENSE</a>
14
 * file.
15
 */
16

17
#include "Notecard.h"
18

19
#include <stdio.h>
20
#include <string.h>
21

22
#ifndef NOTE_MOCK
23
#include <note-c/note.h>
24
#else
25
#include "mock/mock-arduino.hpp"
26
#include "mock/mock-parameters.hpp"
27
#endif
28

29
// Local formatting helpers that route through note-c's level-filtered log
30
// macros. The standard NOTE_C_LOG_* macros accept only literal/string-pointer
31
// arguments, so we format into a stack buffer first and then pass it through.
32
// This keeps ping() output consistent with logging elsewhere in note-c and
33
// still honors runtime log-level filtering via NoteSetLogLevel().
34
#define PING_LOG_BUF_LEN 96
35

36
#define PING_LOG_INFO(...) do {                                     \
37
    char _pingBuf[PING_LOG_BUF_LEN];                                \
38
    snprintf(_pingBuf, sizeof(_pingBuf), __VA_ARGS__);              \
39
    NOTE_C_LOG_INFO(_pingBuf);                                      \
40
} while (0)
41

42
#define PING_LOG_WARN(...) do {                                     \
43
    char _pingBuf[PING_LOG_BUF_LEN];                                \
44
    snprintf(_pingBuf, sizeof(_pingBuf), __VA_ARGS__);              \
45
    NOTE_C_LOG_WARN(_pingBuf);                                      \
46
} while (0)
47

48
#define PING_LOG_DEBUG(...) do {                                    \
49
    char _pingBuf[PING_LOG_BUF_LEN];                                \
50
    snprintf(_pingBuf, sizeof(_pingBuf), __VA_ARGS__);              \
51
    NOTE_C_LOG_DEBUG(_pingBuf);                                     \
52
} while (0)
53

54
namespace
55
{
56

57
// Baud rates to scan when the initial ping fails on a serial link. Ordered
58
// by prevalence on modern Notecard deployments: higher rates first (most
59
// common for aux-port configurations), then 9600 as the legacy default.
60
//
61
// The caller's desired rate is skipped during the scan because it was
62
// already tried by the initial ping at the top of ping().
63
const uint32_t kScanBaudRates[] = {
64
    115200,
65
    230400,
66
    460800,
67
    921600,
68
    9600,
69
};
70
const size_t kScanBaudRateCount = sizeof(kScanBaudRates) / sizeof(kScanBaudRates[0]);
71

72
// Small settling delay after receiving the response to `card.aux.serial`
73
// before switching the host UART to the new rate. The Notecard sends the
74
// response at the old rate and then commits the new rate; this pause lets
75
// the final bytes clear the wire before we switch.
76
const uint32_t kAuxSerialSettleMs = 20;
77

78
// After switching the host UART to the new rate, the Notecard may need a
79
// brief period to fully initialize its port at the new rate before it can
80
// service a fresh request. We retry the verification ping up to
81
// kPostReconfigPingAttempts times with kPostReconfigPingDelayMs between
82
// attempts to accommodate this.
83
const uint8_t  kPostReconfigPingAttempts = 5;
84
const uint32_t kPostReconfigPingDelayMs  = 250;
85

86
bool pingNotecard(void)
2✔
87
{
88
    J *req = NoteNewRequest("card.version");
2✔
89
    if (req == nullptr) {
2!
NEW
90
        return false;
×
91
    }
92

93
    J *rsp = NoteRequestResponse(req);
2✔
94
    if (rsp == nullptr) {
2✔
95
        return false;
1✔
96
    }
97

98
    const bool ok = !NoteResponseError(rsp);
1✔
99
    NoteDeleteResponse(rsp);
1✔
100
    return ok;
1✔
101
}
102

103
} // namespace
104

105
bool Notecard::ping(void)
2✔
106
{
107
    // I2C: the baud rate is meaningless on I2C, so just ping for
108
    // connectivity to catch wiring/address problems and return the result.
109
    if (NoteGetActiveInterface() == NOTE_C_INTERFACE_I2C) {
2!
110
        NOTE_C_LOG_DEBUG("ping: starting (i2c)");
111
        const bool ok = pingNotecard();
2✔
112
        if (ok) {
113
            NOTE_C_LOG_INFO("ping: Notecard reachable over i2c");
114
        } else {
115
            NOTE_C_LOG_WARN("ping: Notecard unreachable over i2c");
116
        }
117
        return ok;
2✔
118
    }
119

120
    // Serial: must have a NoteSerial singleton to proceed.
NEW
121
    NoteSerial *ns = getNoteSerial();
×
NEW
122
    if (ns == nullptr) {
×
123
        NOTE_C_LOG_WARN("ping: no NoteSerial singleton (did you call Notecard::begin?)");
NEW
124
        return false;
×
125
    }
126

NEW
127
    const uint32_t desiredRate = ns->getBaudRate();
×
NEW
128
    PING_LOG_DEBUG("ping: starting (serial, %lu baud)", (unsigned long)desiredRate);
×
129

130
    // Step 1: ping at the currently-configured host UART rate. If the
131
    // Notecard is already reachable, we are done and nothing else changes.
NEW
132
    if (pingNotecard()) {
×
NEW
133
        PING_LOG_INFO("ping: Notecard reachable at %lu baud", (unsigned long)desiredRate);
×
NEW
134
        return true;
×
135
    }
136

NEW
137
    PING_LOG_INFO("ping: initial probe at %lu baud failed; scanning baud rates",
×
138
                  (unsigned long)desiredRate);
139

140
    // Step 2: scan through the known aux-port rates looking for one at
141
    // which the Notecard will talk to us. The desired rate is skipped
142
    // because it was already tried by the initial ping above. On
143
    // success, the host UART is left at the discovered rate (i.e.,
144
    // whatever `ns->setBaudRate` was last called with) and we fall
145
    // through to step 3.
NEW
146
    bool found = false;
×
NEW
147
    uint32_t discoveredRate = 0;
×
NEW
148
    for (size_t i = 0; i < kScanBaudRateCount; ++i) {
×
NEW
149
        const uint32_t rate = kScanBaudRates[i];
×
NEW
150
        if (rate == desiredRate) {
×
NEW
151
            continue;
×
152
        }
NEW
153
        PING_LOG_DEBUG("ping: trying %lu baud", (unsigned long)rate);
×
NEW
154
        if (!ns->setBaudRate(rate)) {
×
155
            // Implementation can't change baud rate at runtime — nothing we
156
            // can do but give up.
157
            NOTE_C_LOG_WARN("ping: host Serial does not support runtime baud rate change");
NEW
158
            return false;
×
159
        }
NEW
160
        if (pingNotecard()) {
×
NEW
161
            found = true;
×
NEW
162
            discoveredRate = rate;
×
NEW
163
            break;
×
164
        }
165
    }
166

NEW
167
    if (!found) {
×
168
        // Couldn't reach the Notecard at any scanned rate. Restore the
169
        // host UART to the caller's originally-requested rate and fail.
NEW
170
        PING_LOG_WARN("ping: no response at any scanned rate; restoring host UART to %lu baud",
×
171
                      (unsigned long)desiredRate);
NEW
172
        ns->setBaudRate(desiredRate);
×
NEW
173
        return false;
×
174
    }
175

NEW
176
    PING_LOG_INFO("ping: found Notecard at %lu baud", (unsigned long)discoveredRate);
×
177

178
    // Step 3: comms is working at the discovered rate. Query `card.io` with
179
    // mode:"port" to determine whether we are on the aux port or on a
180
    // non-configurable port (e.g., usb, notecard).
NEW
181
    J *req = NoteNewRequest("card.io");
×
NEW
182
    if (req == nullptr) {
×
183
        NOTE_C_LOG_WARN("ping: failed to allocate card.io request");
NEW
184
        return false;
×
185
    }
NEW
186
    JAddStringToObject(req, "mode", "port");
×
NEW
187
    J *rsp = NoteRequestResponse(req);
×
NEW
188
    if (rsp == nullptr) {
×
189
        NOTE_C_LOG_WARN("ping: card.io request failed");
NEW
190
        return false;
×
191
    }
NEW
192
    const bool ioHasErr = NoteResponseError(rsp);
×
NEW
193
    const char *portStatus = JGetString(rsp, "status");
×
NEW
194
    const bool isAux = (strcmp(portStatus, "aux") == 0);
×
NEW
195
    PING_LOG_INFO("ping: card.io reports port '%s'", portStatus);
×
NEW
196
    NoteDeleteResponse(rsp);
×
197

NEW
198
    if (ioHasErr) {
×
199
        // Unexpected: NotePing proved connectivity at the discovered rate,
200
        // but card.io returned an error. We cannot determine which port
201
        // we are on, so we cannot safely reconfigure. Return false and
202
        // leave the host UART at the discovered rate.
203
        NOTE_C_LOG_WARN("ping: card.io returned an error; aborting");
NEW
204
        return false;
×
205
    }
206

NEW
207
    if (!isAux) {
×
208
        // Connected to a non-aux port (e.g., usb or notecard). Its rate
209
        // cannot be changed, so the host UART is correctly set to the
210
        // discovered rate and comms is working. Return true even though
211
        // the host UART may differ from what was passed to begin().
NEW
212
        PING_LOG_INFO("ping: non-aux port; host UART stays at %lu baud",
×
213
                      (unsigned long)discoveredRate);
NEW
214
        return true;
×
215
    }
216

217
    // Step 4: aux port confirmed. Reconfigure it to the desired rate.
NEW
218
    PING_LOG_INFO("ping: reconfiguring aux port from %lu to %lu baud",
×
219
                  (unsigned long)discoveredRate, (unsigned long)desiredRate);
NEW
220
    req = NoteNewRequest("card.aux.serial");
×
NEW
221
    if (req == nullptr) {
×
222
        NOTE_C_LOG_WARN("ping: failed to allocate card.aux.serial request");
NEW
223
        return false;
×
224
    }
NEW
225
    JAddStringToObject(req, "mode", "req");
×
NEW
226
    JAddIntToObject(req, "rate", (JINTEGER)desiredRate);
×
NEW
227
    rsp = NoteRequestResponse(req);
×
NEW
228
    if (rsp == nullptr) {
×
229
        // Transport failure. Host UART stays at the discovered rate so
230
        // the caller can still reach the Notecard.
NEW
231
        PING_LOG_WARN("ping: card.aux.serial request failed; host UART remains at %lu baud",
×
232
                      (unsigned long)discoveredRate);
NEW
233
        return false;
×
234
    }
NEW
235
    const bool auxErr = NoteResponseError(rsp);
×
NEW
236
    NoteDeleteResponse(rsp);
×
NEW
237
    if (auxErr) {
×
238
        // Notecard rejected the rate change. Host UART stays at the
239
        // discovered rate (per the design decision for option (a)) so
240
        // the caller can still reach the Notecard.
NEW
241
        PING_LOG_WARN("ping: aux rate change rejected; host UART remains at %lu baud",
×
242
                      (unsigned long)discoveredRate);
NEW
243
        return false;
×
244
    }
245

246
    // Response was received at the old (discovered) rate; the Notecard
247
    // has now committed to desiredRate. Let residual bytes clear the
248
    // wire, then switch the host UART to match.
NEW
249
    NoteDelayMs(kAuxSerialSettleMs);
×
NEW
250
    if (!ns->setBaudRate(desiredRate)) {
×
251
        NOTE_C_LOG_WARN("ping: failed to switch host UART after aux reconfig");
NEW
252
        return false;
×
253
    }
254

255
    // Verify with a retry loop. The Notecard may need a short initialization
256
    // period after committing to the new aux-port rate before it can service
257
    // a fresh request; a single probe is not reliable here.
NEW
258
    for (uint8_t attempt = 1; attempt <= kPostReconfigPingAttempts; ++attempt) {
×
NEW
259
        if (pingNotecard()) {
×
NEW
260
            PING_LOG_INFO("ping: aux port now at %lu baud; verified (attempt %u)",
×
261
                          (unsigned long)desiredRate, (unsigned)attempt);
NEW
262
            return true;
×
263
        }
NEW
264
        if (attempt < kPostReconfigPingAttempts) {
×
NEW
265
            PING_LOG_DEBUG("ping: post-reconfig probe %u/%u failed; retrying in %lu ms",
×
266
                           (unsigned)attempt,
267
                           (unsigned)kPostReconfigPingAttempts,
268
                           (unsigned long)kPostReconfigPingDelayMs);
NEW
269
            NoteDelayMs(kPostReconfigPingDelayMs);
×
270
        }
271
    }
272

NEW
273
    PING_LOG_WARN("ping: verification at %lu baud failed after %u attempts",
×
274
                  (unsigned long)desiredRate,
275
                  (unsigned)kPostReconfigPingAttempts);
NEW
276
    return false;
×
277
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc