Applying a firmware update.
NervesHub sends an update message naming a URL, a size and a SHA-256. This
downloads that archive into the slot the device is not running, checks it,
and points the boot path at it. Nothing reboots here — see nh_slots for the
slot model and commit/0 and revert/0 for what happens on the way back up.
A packbeam is far larger than the heap an ESP32 has to spare, so the download
is streamed: ahttp_client hands back {data, Ref, Bin} as the socket
delivers it, each chunk is buffered only to a flash block, written, hashed
and dropped. Peak memory is one block, whatever the archive weighs.
The digest is computed while writing rather than by reading the partition back, so a download that was corrupted in flight is caught. The archive is then read back from flash and walked, which catches a write that did not land. Only after both does the boot path move.
Order matters: until the boot path is written the device still boots what it was running, so a failure at any earlier point costs nothing but the download.update_result() = {ok, binary()} | {error, term()}
| apply_update/1 | Equivalent to apply_update(Payload, #{}).
|
| apply_update/2 | Download and install an update, returning the slot it was written to. |
| available/0 | Whether this platform can write flash at all. |
| available/1 | As available/0, against a given esp module. |
| commit/0 | Accept the running firmware, so it is no longer on trial. |
| commit/1 | As commit/0, against a given esp module. |
| digest_matches/2 | Compare a computed digest against the one NervesHub sent. |
| parse_url/1 | Split a firmware URL into what ahttp_client:connect/4 takes. |
| pending/0 | The slot a reboot is on trial for, if any. |
| pending/1 | As pending/0, against a given esp module. |
| revert/0 | Point the boot path back at the firmware that was running before. |
| revert/1 | As revert/0, against a given esp module. |
| start_update/2 | Run an update in its own process, reporting back to the caller. |
| start_update/3 | As start_update/2, with options for apply_update/2. |
apply_update(Payload::map()) -> update_result()
Equivalent to apply_update(Payload, #{}).
apply_update(Payload::map(), Opts::map()) -> update_result()
Download and install an update, returning the slot it was written to.
Payload is what NervesHub sends: firmware_url, size and checksum.
Opts may carry a progress function of one argument, called with a
percentage as the download proceeds, and a slot to override the target.
available() -> boolean()
Whether this platform can write flash at all.
available(Opts::map()) -> boolean()
As available/0, against a given esp module.
commit() -> ok | {error, term()}
Accept the running firmware, so it is no longer on trial.
Called once the device has done something that proves the update worked — joining NervesHub is the evidence this library uses, because an update that cannot reach the server is one nothing could recover from remotely.commit(Opts::map()) -> ok | {error, term()}
As commit/0, against a given esp module.
digest_matches(Digest::binary(), Checksum::binary() | undefined) -> boolean()
Compare a computed digest against the one NervesHub sent.
Case insensitive: NervesHub stores a firmware checksum upper case and this library works in lower case, and a comparison that failed on that alone would reject every good download.parse_url(Url::binary() | string() | undefined) -> {ok, map()} | {error, term()}
Split a firmware URL into what ahttp_client:connect/4 takes.
pending() -> {ok, binary()} | none
The slot a reboot is on trial for, if any.
Set by an update and cleared bycommit/0. A device that finds one here is
running firmware that has not yet proved itself.
pending(Opts::map()) -> {ok, binary()} | none
As pending/0, against a given esp module.
revert() -> {ok, binary()} | {error, term()}
Point the boot path back at the firmware that was running before.
Does not reboot. The caller decides when, because reverting mid-flight and rebooting immediately would cut off whatever it was trying to report.revert(Opts::map()) -> {ok, binary()} | {error, term()}
As revert/0, against a given esp module.
start_update(Payload::map(), Owner::pid()) -> pid()
Run an update in its own process, reporting back to the caller.
The download takes as long as it takes, and the agent has heartbeats to send while it runs — so it does not run on the agent's process. The caller receives{nh_ota, self(), {progress, Percent}} as it goes and
{nh_ota, self(), Result} at the end.
start_update(Payload::map(), Owner::pid(), Opts::map()) -> pid()
As start_update/2, with options for apply_update/2.
Generated by EDoc