Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OTA Upgrade Improvements #5

Closed
wants to merge 23 commits into from
Closed

OTA Upgrade Improvements #5

wants to merge 23 commits into from

Conversation

mattbnz
Copy link
Contributor

@mattbnz mattbnz commented Jun 3, 2022

3 OTA improvements in this pull request:

  1. Upgrades to a newer version of esp32FOTA that supports certificates on Filesystem (avoding the need to hardcode a cert into the repo)
  2. Adds support for configuring the OTA URL via the configuration (both on disk and via MQTT) - agian the intent being to avoid needing to hardcode config paths into the repo
  3. Implements "force OTA" support, so you can send an MQTT command to tell a device to upgrade to a specific firmware image - useful for remote development/testing.

(sorry, this pull request is a bit messy because it's from a branch off the LittleFS upgrade branch in my repo, but I don't know git/giithub well enough to figure out how to get that reflected here, so you see all the changes, not just the ones on top of the LittleFS branch...)

mattbnz added 21 commits June 3, 2022 12:12
The current 3.5 platform version doesn't seem to correctly pick-up the
board_build.filesystem = littlefs option when using the uploadfs target, and
builds a SPIFFS image instead of the desired LittleFS image...

Upgrading the platform to 4.4, which has now got LittleFS built in, rather than
as an external library fixes the uploadfs target.
Something in the newer platform code doesn't like the arguments we're passing
here, and fails to initialize the bus at all, whereas it works perfectly
without the arguments. The only argument that is potentially effectual here is
the clock, but that's already reset in the SCD30 specific code anyway, which
appears to be the main motivation for running at half the default speed, so
should be fine?
In the upgraded platform, failing to set the ledPin to OUTPUT prior to calling
ledcSetup results in nothing happening at all!
To avoid needing to hardcode certificates into the source, upgrade to >0.1.6 of
esp32FOTA which supports reading both cert (and an optional signing key from
the filesystem). But the default package only supports SPIFFS, and our FS is
LittleFS, so use the thorrak branch that's lingering in
chrisjoyce911/esp32FOTA#79 to enable that.
This is the currently active LetsEncrypt root CA[0], which seems like a
reasonable default that many folks may be using on their webservers and
therefore may "just work".

In the case that your server is secured with another CA, simply replace this file.

Note: this CA cert will change over time and will need updating before it
expires to prevent OTAs breaking (TODO!), but this is no worse than the
previous hard-coding in ota_cert.h really.

[0] sourced from https://letsencrypt.org/certificates/, specifically:
https://letsencrypt.org/certs/isrgrootx1.pem
Support supplying the ota URL via the configuration structure to avoid having
to hardcode private hostnames, etc into the source tree, or alternatively
having to maintain local diffs.

Also include support for viewing/modifying the otaUrl via the get/setConfig
MQTT messages.
Add a new MQTT message and associated handlers for triggering an OTA with a
specified firmware file, rather than relying on the "is there a new version"
logic from parsing firmware.json.

This is useful for debug/testing over OTA when you don't want to be frequently
updating firmware.json.
To support easily flashing different board configs (aka models) without having
to edit the source to change defines every time, set up platformio environments
for the different board configs (leds and neopixel) so you can simply use -e
MODEL to do the right thing with no editing required.

To support understanding what has been flashed, include a version number, build
timestamp and git revision hash in the built image, and display that in both
serial debug and MQTT messages. This works via a platformio extra_scripts pre
hook and associated python script. This hook also sets the generated firmware
filenames to include the model and version for convenience/clarity.

The versioning strategy is semver (for compatibility with esp32FOTA logic) with
the release version set in platformio.ini and used when the :release
environments are built. The default environments for each model, increment the
patch level of the release version and add a -BUILD_TIMESTAMP pre-release
suffix to the version they are built with, such that they can be forceOTA'd to
a device and a regular ota check will not cause them to be downgraded, but a
later release will cause them to be upgraded.
This makes the screen flash every few seconds as the message briefly appears
which can be distracting.
These helper scripts ease the common actions involved in developing via OTA and
maintaining an OTA repository for the different models of board/firmware that
need to be flashed.

push-ota will build the specified environment and if it is a release, copy it
to the webserver and call the gen-firmwarejson.py script on that remote host to
update firmware.json accordingly. Optionally, if you also supply a device ID it
will then use trigger-ota to tell the device to check for a new version.

When operating on a non-release environment, the new firmware will be copied to
the webserver, but firmware.json is not updated. If you suply a device ID,
trigger-ota will then send a forced OTA request for that specific newly built
firmware to the device.
It makes everything messy...
To avoid needing to hardcode certificates into the source, upgrade to >0.1.6 of
esp32FOTA which supports reading both cert (and an optional signing key from
the filesystem). But the default package only supports SPIFFS, and our FS is
LittleFS, so use the thorrak branch that's lingering in
chrisjoyce911/esp32FOTA#79 to enable that.
This is the currently active LetsEncrypt root CA[0], which seems like a
reasonable default that many folks may be using on their webservers and
therefore may "just work".

In the case that your server is secured with another CA, simply replace this file.

Note: this CA cert will change over time and will need updating before it
expires to prevent OTAs breaking (TODO!), but this is no worse than the
previous hard-coding in ota_cert.h really.

[0] sourced from https://letsencrypt.org/certificates/, specifically:
https://letsencrypt.org/certs/isrgrootx1.pem
Support supplying the ota URL via the configuration structure to avoid having
to hardcode private hostnames, etc into the source tree, or alternatively
having to maintain local diffs.

Also include support for viewing/modifying the otaUrl via the get/setConfig
MQTT messages.
Add a new MQTT message and associated handlers for triggering an OTA with a
specified firmware file, rather than relying on the "is there a new version"
logic from parsing firmware.json.

This is useful for debug/testing over OTA when you don't want to be frequently
updating firmware.json.
@oseiler2
Copy link
Owner

oseiler2 commented Jun 9, 2022

Question: Is there much value in adding the OTA URL to the configuration? I'd have thought that it's rather static and sufficient to be specified at build time, especially with the additional option to forceOTA with providing a URL which should cover the need to overwrite any given URL.
I'm trying to keep the configuration small to minimise memory footprint and also to keep the config portal UI simple (-ish - I know it's quite big already).

@oseiler2
Copy link
Owner

oseiler2 commented Jun 9, 2022

It would be much nicer if we could avoid linking to this branch https://github.com/thorrak/esp32FOTA/tree/multi_filesystem and instead try to get this PR merged chrisjoyce911/esp32FOTA#79

@mattbnz
Copy link
Contributor Author

mattbnz commented Jun 9, 2022

I don't like build parameters that can't be committed to the repo was why I made it a config option initially, but you're right that now that there's also the forceota method, it's hard to see a justification for this.

Thinking about my use-cases going forward, I can't see myself wanting devices to poll for OTAs, I'd much rather co-ordinate and trigger that centrally.

So yeah, I'd be happy to remove that code if you like.

@oseiler2
Copy link
Owner

oseiler2 commented Jun 9, 2022

Cool. No need to do anything, I've almost got it working in a local branch, that saves you resolving the merge conflicts.

@oseiler2
Copy link
Owner

oseiler2 commented Jun 9, 2022

See 2072097

mattbnz and others added 2 commits June 10, 2022 11:14
- Removes config support for otaURL (only hardcoded)
- But can always be specified via the forceOTA MQTT message
@mattbnz
Copy link
Contributor Author

mattbnz commented Jun 12, 2022

I had to merge this anyway to pick up your changes so no drama.

There's just two little diffs left:

  • your commits didn't have the prototype for the new forceUpdate in the header file, not strictly required, but given it's publicly exposed, I think it should probably be there.
  • the root CA, I guess you didn't like the idea of hardcoding lets encrypt? (no problem, I'll just carry that diff locally going forward)

Thanks.

@mattbnz
Copy link
Contributor Author

mattbnz commented Jun 12, 2022

actually, I see you added root_ca.pem to .gitignore already, which confirms my (2) question.

I'll delete this, and you can take card of the header prototype direclty if you think important.

I'm going to blow away my forked repo and restart now that I've got a better idea how to work with github's workflow so it's not so messy in future...

@mattbnz mattbnz closed this Jun 12, 2022
@mattbnz mattbnz deleted the fota-upgrade branch June 12, 2022 23:10
@oseiler2
Copy link
Owner

I'm currently not using Let's Encrypt for my OTA server, but I take your point it would be nice to have a default cert bundle that just works.
Maybe something along the lines of this? https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/esp_crt_bundle.html

@mattbnz
Copy link
Contributor Author

mattbnz commented Jun 13, 2022

Ah, that's a neat set of certs, that would be fine for me, but I don't think it's a big deal either way, I can easily push a cert in my provisioning script, but if you have other users wanting a more plug and play experience, having some default certs loaded would be good.

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.

2 participants