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

Add an example #5

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[build]
# Uncomment the relevant target for your chip here (ESP32, ESP32-S2, ESP32-S3 or ESP32-C3)
# you may need to change the toolchain in `rust-toolc
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is rust-toolc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meant to be rust toolchain, xtensa chips need a custom toolchain afaik

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like an unfinished sentence then?

#target = "xtensa-esp32-espidf"
#target = "xtensa-esp32s2-espidf"
#target = "xtensa-esp32s3-espidf"
Expand Down
66 changes: 66 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
ARG VARIANT=bookworm-slim
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove these files

FROM debian:${VARIANT}
ENV DEBIAN_FRONTEND=noninteractive
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8

# Arguments
ARG CONTAINER_USER=esp
ARG CONTAINER_GROUP=esp
ARG ESP_BOARD=all
ARG GITHUB_TOKEN

# Install dependencies
RUN apt-get update \
&& apt-get install -y pkg-config curl gcc clang libudev-dev unzip xz-utils \
git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0 \
&& apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts

# Set users
RUN adduser --disabled-password --gecos "" ${CONTAINER_USER}
USER ${CONTAINER_USER}
WORKDIR /home/${CONTAINER_USER}

# Install rustup
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- \
--default-toolchain none -y --profile minimal

# Update envs
ENV PATH=${PATH}:/home/${CONTAINER_USER}/.cargo/bin

# Install extra crates
RUN ARCH=$($HOME/.cargo/bin/rustup show | grep "Default host" | sed -e 's/.* //') && \
curl -L "https://github.com/esp-rs/espup/releases/latest/download/espup-${ARCH}" -o "${HOME}/.cargo/bin/espup" && \
chmod u+x "${HOME}/.cargo/bin/espup" && \
curl -L "https://github.com/esp-rs/espflash/releases/latest/download/cargo-espflash-${ARCH}.zip" -o "${HOME}/.cargo/bin/cargo-espflash.zip" && \
unzip "${HOME}/.cargo/bin/cargo-espflash.zip" -d "${HOME}/.cargo/bin/" && \
rm "${HOME}/.cargo/bin/cargo-espflash.zip" && \
chmod u+x "${HOME}/.cargo/bin/cargo-espflash" && \
curl -L "https://github.com/esp-rs/espflash/releases/latest/download/espflash-${ARCH}.zip" -o "${HOME}/.cargo/bin/espflash.zip" && \
unzip "${HOME}/.cargo/bin/espflash.zip" -d "${HOME}/.cargo/bin/" && \
rm "${HOME}/.cargo/bin/espflash.zip" && \
chmod u+x "${HOME}/.cargo/bin/espflash" && \
curl -L "https://github.com/esp-rs/embuild/releases/latest/download/ldproxy-${ARCH}.zip" -o "${HOME}/.cargo/bin/ldproxy.zip" && \
unzip "${HOME}/.cargo/bin/ldproxy.zip" -d "${HOME}/.cargo/bin/" && \
rm "${HOME}/.cargo/bin/ldproxy.zip" && \
chmod u+x "${HOME}/.cargo/bin/ldproxy" && \
curl -L "https://github.com/esp-rs/esp-web-flash-server/releases/latest/download/web-flash-${ARCH}.zip" -o "${HOME}/.cargo/bin/web-flash.zip" && \
unzip "${HOME}/.cargo/bin/web-flash.zip" -d "${HOME}/.cargo/bin/" && \
rm "${HOME}/.cargo/bin/web-flash.zip" && \
chmod u+x "${HOME}/.cargo/bin/web-flash"

# Install Xtensa Rust
RUN if [ -n "${GITHUB_TOKEN}" ]; then export GITHUB_TOKEN=${GITHUB_TOKEN}; fi \
&& ${HOME}/.cargo/bin/espup install\
--targets "${ESP_BOARD}" \
--log-level debug \
--export-file /home/${CONTAINER_USER}/export-esp.sh

# Set default toolchain
RUN rustup default esp


# Activate ESP environment
RUN echo "source /home/${CONTAINER_USER}/export-esp.sh" >> ~/.bashrc

CMD [ "/bin/bash" ]
45 changes: 45 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "esp-ota",
// Select between image and build propieties to pull or build the image.
"image": "docker.io/espressif/idf-rust:esp32_latest",
// "build": {
// "dockerfile": "Dockerfile",
// "args": {
// "CONTAINER_USER": "esp",
// "CONTAINER_GROUP": "esp",
// "ESP_BOARD": "esp32"
// }
// },
"settings": {
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file",
"editor.formatOnType": true,
"lldb.executable": "/usr/bin/lldb",
"files.watcherExclude": {
"**/target/**": true
},
"rust-analyzer.checkOnSave.command": "clippy",
"rust-analyzer.checkOnSave.allTargets": false,
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer"
}
},
"extensions": [
"rust-lang.rust-analyzer",
"tamasfe.even-better-toml",
"serayuzgur.crates",
"mutantdino.resourcemonitor",
"yzhang.markdown-all-in-one",
"ms-vscode.cpptools",
"actboy168.tasks",
"Wokwi.wokwi-vscode"
],
"forwardPorts": [
3333,
8000
],
"workspaceMount": "source=${localWorkspaceFolder},target=/home/esp/esp-ota,type=bind,consistency=cached",
"workspaceFolder": "/home/esp/esp-ota"
}

5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/target
/Cargo.lock
/.embuild
/.embuild
/.devcontainer
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You ignore the devcontainer folder here. But this PR also commits devcontainer definitions. I think you might want to remove the devcontainer files from this PR and keep it ignored?

.vscode/
*.bin
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ log = { version = "0.4", optional = true }

[build-dependencies]
embuild = "0.31.2"

[dev-dependencies]
esp-idf-svc = "0.48.1"
Binary file added beta.bin
Binary file not shown.
36 changes: 36 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Examples

The directory contains simple examples for OTA updates

## beta

This is the example app that will replace the running OTA app. Typically, any new image should contain OTA update logic.
This app should be built before any of the ota apps can use it.
It is better to build in release mode to minify the application.

Build:
```
RUSTFLAGS="-C strip=symbols -C debuginfo=0" cargo build --example beta --release
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the stripping etc really need to be done here? It significantly complicates the example instructions. Is it required to fit on some specific type of chip? Otherwise I think simple instructions is more valuable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't need to happen, but it is an important aspect of OTA updates in my experience. We should mention it in the docs if not as part of the example, but it does greatly speed up the flashing.

espflash save-image --chip <mcu_target> ./target/<mcu_target>/release/examples/beta ./beta.bin
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR commits the beta.bin file. I'm guessing that's a mistake, since these instructions build it? I don't think we should carry around built binaries in the repository.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, will amend

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

beta.bin is still part of this PR

```

## ota_from_flash

The app contains the update in it's flash memory at compile time. The `beta.bin` image in the ESP32 app image format must be available at compile time.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/it's/its/


Build:
```
cargo build --example ota_from_flash --release
```
Flash and monitor:
```
espflash flash ./target/<mcu_target>/release/examples/ota_from_flash --partition_table ./examples/partitions_4MB.csv --monitor
```

## partitions_4MB

A partition table that splits the two OTA partitions evenly.

## partitions_factory.csv

The default partition table shipped with the ESP32. _Not suitable for OTA._
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this partition table included, since it's not used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I included it as a reference. Dodgy things might happen if a user doesn't re-write their original partition table.
For example, if you flash OTA alpha and OTA beta into partition slot ota1 and ota2 respectively. In this case, OTA alpha loads, writes OTA beta into the ota2 slot and selects that as the boot partition.

When a user wants to flash a non-ota app into what they expect to be the factory partition, the ota2 slot could still be selected as the boot partition, and the factory partition never opens :-(

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's the responsibility of this repository to track the default partition table. If there are potential caveats (which are currently not even mentioned) I think it's far better to describe how to get out of the bad situation, and link to an online resource with the default partition table. Then we don't have to maintain it here

14 changes: 14 additions & 0 deletions examples/beta.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
extern crate esp_ota;
use log::*;

const FIRMWARE_ID: &str = "beta";

fn main() {
esp_idf_svc::sys::link_patches();
esp_idf_svc::log::EspLogger::initialize_default();
info!("FIRMWARE ID : {}", FIRMWARE_ID);

if FIRMWARE_ID == "beta" {
esp_ota::mark_app_valid();
}
}
35 changes: 35 additions & 0 deletions examples/ota_from_flash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
extern crate esp_ota;
use log::*;

const FIRMWARE_ID: &str = "alpha";

const NEW_APP: &[u8] = include_bytes!("../beta.bin");

fn main() {
esp_idf_svc::sys::link_patches();
esp_idf_svc::log::EspLogger::initialize_default();

info!("FIRMWARE ID : {}", FIRMWARE_ID);
if FIRMWARE_ID == "alpha" {
faern marked this conversation as resolved.
Show resolved Hide resolved
let mut ota = esp_ota::OtaUpdate::begin().unwrap();
// write app to flash

for app_chunk in NEW_APP.chunks(4096) {
if let Err(err) = ota.write(app_chunk) {
error!("Failed to write chunk");
break;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will just exit the for loop and go directly to ota.finalize(). Here we know the update failed already, and I don't think it's the best course of action to try and finalize the update. I think it's better in general (and teach the readers) to exit with an error here. Only run the finalize code if all write calls succeeded.

}
}
//validate the written app
match ota.finalize() {
Err(x) => {
error!("Failed to validate image.");
()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning unit is automatically implied when you don't return anything else. I believe this is superfluous here.

}
Ok(mut x) => {
x.set_as_boot_partition().unwrap();
x.restart();
}
};
}
}
6 changes: 6 additions & 0 deletions examples/partitions_4MB.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
ota_0, app, ota_0, 0x10000, 0x180000,
ota_1, app, ota_1, 0x190000, 0x180000,
5 changes: 5 additions & 0 deletions examples/partitions_factory.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,