Skip to content

Task: Firmware update via SFTP#55

Draft
jubeormk1 wants to merge 133 commits intobrainstorm:mainfrom
jubeormk1:sftp-ota
Draft

Task: Firmware update via SFTP#55
jubeormk1 wants to merge 133 commits intobrainstorm:mainfrom
jubeormk1:sftp-ota

Conversation

@jubeormk1
Copy link
Collaborator

@jubeormk1 jubeormk1 commented Jan 14, 2026

Finally I got to the point where I am close enough to a working feature for the issue Firmware update via SFTP, that I have decided to open a pull request for review.

So far I have:

  • SftpServer: Working SSH subsystem running on the target esp32c6
  • ota-packer": A binary to pack binary files together with metadata
  • ota-validation: FSM that deals with receiving metadata, calculates an sha256 hash of the binary received and validates its integrity on sftp file close
  • TLV tests: Tests for metadata serialising roundtrip
  • Rebase: The branch has been rebased to the (Almost) most updated branch fix_passwd_auth
  • Workspace restructuring: Added my code as a member package, creating clear boundaries with the OTA functionality.

Remains to be done:

  • Writing binary to ota partition as the binary chunks are received
  • Restart the target once the integrity validation is completed
  • Implement for all targets so github checks are happy
  • Wrong ota file check won't hang sftp server and fails fast

Difficulties

I am finding some issues including the required crates to interact with the MCUs flash storage in the default target esp32c6. I am going to study carefully the project main crate esp-hal dependencies to get all the elements finish the job. I am going to follow esp-hal ota update as a guide.

All reasonable comments and suggestions are welcome.

brainstorm and others added 30 commits June 1, 2025 16:12
…assumptions around mutexes and rename Fl/FlashCo fig struct names
…onfigure the UART's pins at runtime (possible?) and also pass the config object around (safely/mutex-ing) too
…t a CriticalSectionRawMutex for the SSHConfig. Next up: figure out non-unsafe() ways to steal() pins for the UART pins
…channel, needs more work/thought... also PeripheralRef is not present in 1.0.0-beta.1
Co-authored-by: Marko Malenic <mmalenic1@gmail.com>
… call is now required. The partition checking/handling functionalities might be relevant to @jubeormk1 too, btw
…with the UART operation, need to debug further
…X/TX/CTS/RTS... etc.. pin assignment and at-runtime validation et al

Co-authored-by: Marko Malenic <mmalenic1@gmail.com>
…erialization/deserialization "on the wire" of SSHConfig, unfortunately /cc @mmalenic
Introduced custom Errors
Introduced env vars and started refactoring accordingly.

Config and peripherals are borrow checker challenges.

Co-authored-by: Marko Malenic <mmalenic1@gmail.com>
….. PinConfig no longer needs a new() method but OTOH resolve_pin() requires a bit more rework (or remove it altogether?)
Co-authored-by: Marko Malenic <mmalenic1@gmail.com>
… PinConfig::new()... Also channel cannot send at the end of uart_task() because there's an infinite loop right before.

Co-authored-by: Marko Malenic <mmalenic1@gmail.com>
Use channels to send individual pins around... probably we should refactor to Signal instead since message size N=1.

Co-authored-by: Marko Malenic <mmalenic1@gmail.com>
… lifetimes and futures.

Co-authored-by: Marko Malenic <mmalenic1@gmail.com>
Partitions uploaded via sftp .ota file and espfash write-bin result in the same md5

The problem must arise when writing otadata partition in target.rs set_current()

My apologies for build.yml not passing but I have not targeted all MCUs. I will work on that after the ota succeeds
This saves me from the crash with otadata rejected by bootloader but ok for `esp_bootloader_esp_idf::ota::Ota::current_slot()` making it target running app partition.
@jubeormk1
Copy link
Collaborator Author

Thanks for the feedback and sorry about the issues building. I will address them when I fix the issue that corrupts otadata partition.

… sftp-ota

Making version verbosity optional and accepting your way more direct and better way to compare the computed and original hash
0.1->0.4 The example works with different methods

I am going to update now ota/src/target.rs to use the 0.4 api
This is going to be problematic since there are a lot of changes since we need to bump esp_storage and the crate and embassy already depends on it.
Wil require adjustments in the code. To begin with the package storage
Applied the changes to get the OTA running
Steps to reproduce (esp32c6 only):
```
# extract app bin from elf file
cargo build-esp32c6
espflash save-image --chip=esp32c6 target/riscv32imac-unknown-none-elf/release/ssh-stamp ssh-stamp.bin

# pack the application for ota:

cargo ota-packer -- ssh-stamp.bin

# Now run the application
espflash erase-flash
cargo run-esp32c6
```

At the end of the bootloader log look for the app offset (At this point Factory)
`boot: Loaded app from partition at offset 0x10000`

Connect to the wifi ssh-stamp

In a separate bash session run:

```
sftp 192.168.0.1 <<< $'put ssh-stamp.ota'
```

Wait for the put operation to finish (might take up to 4 minutes and have patchy progress and show stalled at some point)

After that the target will reboot and the line

`boot: Loaded app from partition at offset 0x10000`

will be replaced by the address of the ota slot 0 partiton:

`boot: Loaded app from partition at offset 0x210000`

Repeating the upload process with modify the offset of app loaded:

`boot: Loaded app from partition at offset 0x410000`

The first SFTP OTA working is here. What a ride!!

:-)
@jubeormk1
Copy link
Collaborator Author

As a note, the OTA functionality is finally here. Some more work will come to tidy up and fulfill reasonable expectations.

Also, I have made a mess using LOG level messages. I am happy to consolidate all to LOG or move my info messages to println and remove the debug ones.

jubeormk1 and others added 8 commits January 23, 2026 10:40
Current build uses 1296K so an app partition of 1920K is appropriate and we can have an AB ota system in 4kB
It cannot be larger than next ota partition
This test uploads an OTA file and check if it has been correctly written in the flash memory.

It does not test the current running application but can test if the application was written to ota_1.

Is there a way in espflash to obtain current running app partition?
Avoids any details of the data ota access implementation delegating it to storage.
Using sed now. I am removing trailing zeros and any 0x prefix and dec to hex transformation to keep it simple
I ran

```
clear; cargo +stable clippy --features esp32c6 --target riscv32imac-unknown-none-elf -- -D warnings
```

And clean all the errors coming from warnings. Additionally I removed ota.rs that is no longer in use.

I will let this one run to catch more errors
Extending the heap of esp32s2 to 72kB reclaiming memory from bootloader and other config updates related to esp_hal 1.0.0 detected on different devices builds
…t chip for all builds

That causes duplicates in chip definitions.

Also cleaning and commenting out nightly channel
Run locally with act for each of the devices in the list and passed all the steps.

This is necessary to comply with the next dependencies:
- darling
- esp-bootloader-esp-idf
- esp-hal
- esp-hal-procmacros
- esp-radio
- esp-rom-sys
- esp-sync
- heapless
- instability
- sunset
- xtensa-lx-rt
I will use this to reset the MCU using the ota_writer instance.
Not packing sftp-ota saves around 127kB (7%) in binary size.

Bumping the channel to stable to avoid [rust-lang issue](rust-lang/rust#133508) on ota-packer.rs:106
To detect breaking changes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants