Repository navigation
fix(ota): working rollback, encrypted writes, real refusal reasons - #132
Open
PaulDWhite wants to merge 1 commit into
Open
PaulDWhite wants to merge 1 commit into
PaulDWhite wants to merge 1 commit into
Conversation
- Rollback: Arduino's initArduino() marks a new OTA image valid before setup() unless verifyRollbackLater() is overridden, and nothing overrode it, so the main.cpp check never ran and a bad image could not roll back. Override it so validation happens at the end of setup(). - Require an encrypted link (WRITE_ENC) for the OTA command and data characteristics, matching every config write, so only a paired phone can flash firmware. - CMD_START refusals now report why: 2 = armed, 3 = image too large, 4 = flash/partition error. 1 stays the generic/CRC failure. 8.0 sent 1 for everything. - Claim the OTA session before checking the arm state, so arming can't slip in during the multi-second esp_ota_begin() erase. Request the OTA connection parameters before the erase rather than after it. - After END, reboot from checkOtaTimeout() about 1 s later instead of inside the write callback, so the END write response and ACK actually reach the app. A disconnect in that window no longer aborts; arming stays blocked until the reboot. - CI: fetch full history for the firmware build, so VERSION_BUILD (the commit count) isn't 1. extra_script.py fails a shallow CI build and warns on a shallow local one.
PaulDWhite
added a commit
that referenced
this pull request
Oct 5, 2026
This branch has not been deployed
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.
Makes Bluetooth firmware updates safer and lets the app show the controller's real state. Everything is in
ota_service.cpp, plus the CI build-number fix.Changes
Rollback works now. Arduino's
initArduino()marks a freshly updated image valid beforesetup()runs unless the app overrides the weakverifyRollbackLater()hook. We never did, so thePENDING_VERIFYcheck at the end ofsetup()never fired, and a bad image couldn't roll back (8.0 too). This addsextern "C" bool verifyRollbackLater() { return true; }. I checkedfirmware.elf: the strong symbol replaces the weak default and returns 1.OTA writes require an encrypted link. The command and data characteristics get
WRITE_ENC, matching every config write, so only a paired phone can flash firmware. Notifications and indications are unchanged.START refusal reasons:
0x00020x00030x00040x00018.0 sent
0x0001for all of these. Existing apps still treat any non-zero code as a failure.No arming during START. The session is claimed (
otaInProgress) before the arm check, sotoggleArm()can't arm during the multi-secondesp_ota_begin()erase. The OTA connection parameters are now requested before the erase instead of after it.Reboot after END is deferred. Rebooting about 1 s later from
checkOtaTimeout()(bleNotifyTask, 50 Hz) lets the END write response and ACK go out. Before,ESP.restart()ran inside the write callback, so the write never completed. A disconnect in that window no longer aborts the session, and arming stays blocked until the reboot.CI build number.
pio-buildchecks out full history (fetch-depth: 0).VERSION_BUILDisgit rev-list --count HEAD, which a shallow clone reports as 1.extra_script.pynow fails a shallow CI build and warns on a shallow local one.Testing
pio run -e OpenPPG-CESP32S3-CAN-SP140passes: flash 36.3%, RAM 36.8%. cpplint is clean.firmware.binreads 8.1, build 1225.0x0002, and arming fails while an update runs.setup()and confirm it boots back into the previous image. Requires a rollback-capable bootloader, which comes from an 8.x full-flash install.