Fix rollover-unsafe millis() timeout in scannerGraphic.ino#1073
Merged
Conversation
Replace `unsigned long inputTimeout = millis() + 7000` / `millis() < inputTimeout` with a subtraction-based elapsed-time check: `unsigned long inputTimeout = millis()` / `millis() - inputTimeout < 7000`. With unsigned arithmetic the subtraction wraps correctly across the ~49.7-day millis() rollover boundary, so the 7-second wait is always correct regardless of when the sketch starts.
Copilot
AI
changed the title
[WIP] Fix rollover-unsafe millis timeout patterns in nRF24/RF24
Fix rollover-unsafe millis() timeout in scannerGraphic.ino
Jun 4, 2026
Contributor
Memory usage change @ eee89fd
Click for full report per board
|
| Sketch | Flash | % | RAM for global variables | % |
|---|---|---|---|---|
| examples/GettingStarted | 0 | 0 | 0 | 0 |
| examples/AcknowledgementPayloads | 0 | 0 | 0 | 0 |
| examples/ManualAcknowledgements | 0 | 0 | 0 | 0 |
| examples/StreamingData | 0 | 0 | 0 | 0 |
| examples/MulticeiverDemo | 0 | 0 | 0 | 0 |
| examples/InterruptConfigure | 0 | 0 | 0 | 0 |
| examples/scanner | 0 | 0 | 0 | 0 |
| examples/encodeRadioDetails | 0 | 0 | 0 | 0 |
arduino:samd:mkrzero
| Sketch | Flash | % | RAM for global variables | % |
|---|---|---|---|---|
| examples/GettingStarted | 0 | 0 | 0 | 0 |
| examples/AcknowledgementPayloads | 0 | 0 | 0 | 0 |
| examples/ManualAcknowledgements | 0 | 0 | 0 | 0 |
| examples/StreamingData | 0 | 0 | 0 | 0 |
| examples/MulticeiverDemo | 0 | 0 | 0 | 0 |
| examples/InterruptConfigure | 0 | 0 | 0 | 0 |
| examples/scanner | 0 | 0 | 0 | 0 |
| examples/encodeRadioDetails | 0 | 0 | 0 | 0 |
2bndy5
approved these changes
Jun 4, 2026
Member
|
The size deltas is 0 because the changed example is not part of the calculation. And this is only effective when debugging the example. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
millis() + Nproduces an absolute deadline that wraps incorrectly near the 32-bit rollover (~49.7 days), causing the comparisonmillis() < deadlineto fail immediately and skip the intended wait.Change
examples/scannerGraphic/scannerGraphic.ino— replace absolute-deadline pattern with unsigned elapsed-time subtraction:Unsigned subtraction wraps symmetrically, so
millis() - startalways yields the correct elapsed time regardless of rollover.Original prompt
Patch rollover-unsafe
millis()timeout patterns in thenRF24/RF24repository by replacing absolute-deadline logic likeuint32_t timeout = millis() + N; while (millis() < timeout) {}orif (millis() > timeout)with rollover-safe elapsed-time comparisons using unsigned subtraction, such asconst uint32_t start = millis(); while (millis() - start < N) {}.Confirmed unsafe site already identified from repository code/search results:
examples/scannerGraphic/scannerGraphic.ino: input timeout usingunsigned long inputTimeout = millis() + 7000;withwhile (!Serial.available() && millis() < inputTimeout).Requirements:
nRF24/RF24.millis()deadline comparisons are discovered during implementation in this repo and they are clearly the same class of bug, patch them too.The following is the prior conversation context from the user's chat exploration (may be truncated):
User: can you find all rollover unsafe millis() calls in RF24, RF24Network, RF24Mesh, RF24Ethernet & RF24Gateway?
Assistant: Yes — I found the clearly rollover-unsafe
millis()usage patterns in those repos.Unsafe pattern I looked for: storing an absolute deadline like
timeout = millis() + Nand later comparing withmillis() < timeoutor similar. That breaks acrossmillis()wraparound.Safe pattern:
if (millis() - start >= interval)using unsigned subtraction.Confirmed rollover-unsafe calls
RF24Mesh
RF24Mesh.cpp—requestAddress()This is rollover-unsafe because it compares current time to an absolute future timestamp. If wrap happens before/around the loop, the condition can fail early or behave incorrectly.
RF24Ethernet
RF24Client.h—ConnectStatein-class initializationThese initializations are not inherently unsafe by themselves, but they are suspect state seeds because they store absolute times. Whether they’re unsafe depends on how they’re compared later.
RF24Client.cpp— reopen-window timingThis one is safe. Included here only because it came up in search and uses the correct subtraction form.
RF24Client.cpp—waitAvailable()Also safe.
So for RF24Ethernet, from the results I could verify, I found no confirmed unsafe compare pattern, only timestamp storage in
ConnectStatethat should be audited further if you want exhaustive proof.RF24Gateway
RF24Gateway.cpp— ARP timeout waitThis is safe.
The gateway examples also use:
Those are safe too. Example:
examples/RF24GatewayNode.cpp.So for RF24Gateway, I found no confirmed rollover-unsafe
millis()comparisons in the surfaced results.Repos with no confirmed unsafe hits from what I could verify
millis()comparison surfaced in the returned results.millis()comparison surfaced in the returned results.This pull request was created from Copilot chat.