|
STAR
Cal STAR — firmware, DAQ server, and engine design
|
Classes | |
| class | OtaError |
| class | ConnectError |
Functions | |
| print_banner () | |
| str | find_pio_command () |
| str | compile_firmware (str message) |
| upload_firmware (str bin_path, str ip, int port) | |
| main () | |
| bytes | parse_mac (str mac) |
| str | recovery_address (str mac) |
| tuple | board_from_config (str name, Path config) |
| list | candidate_addresses (Optional[str] ip, Optional[str] mac) |
| str | firmware_sha256 (bytes data) |
| Optional[str] | board_reported_sha256 (bytes data) |
| str | upload (bytes data, str ip, int port=DEFAULT_PORT, Optional[ProgressFn] on_progress=None, float connect_timeout=CONNECT_TIMEOUT, float transfer_timeout=TRANSFER_TIMEOUT) |
| str | upload_to_first (bytes data, list hosts, int port=DEFAULT_PORT, **kwargs) |
| str | find_pio () |
| Path | build (Path project, Optional[str] env=None, Optional[str] message=None) |
| ProgressFn | _progress_printer () |
| int | main (argv=None) |
| None | _self_test () |
Variables | |
| str | DEFAULT_IP = "192.168.2.5" |
| int | DEFAULT_PORT = 3232 |
| int | CHUNK_SIZE = 4096 |
| int | CONNECT_TIMEOUT = 5 |
| int | TRANSFER_TIMEOUT = 30 |
| SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| PROJECT_DIR = os.path.join(SCRIPT_DIR, "OTA_Test_Firmware") | |
| PIO_BUILD_DIR = os.path.join(PROJECT_DIR, ".pio", "build", "adafruit_feather_esp32s3") | |
| FIRMWARE_BIN = os.path.join(PIO_BUILD_DIR, "firmware.bin") | |
| int | MAX_IMAGE_BYTES = 0x200000 |
| ProgressFn = Callable[[int, int], None] | |
| REPO_ROOT = Path(__file__).resolve().parents[2] | |
| str | DEFAULT_CONFIG = REPO_ROOT / "daq-server" / "config" / "config.toml" |
ESP32 Ethernet OTA Upload Script
=================================
Recompiles the OTA_Test_Firmware with a custom serial message,
then pushes the resulting binary to the ESP32 over TCP (W5500 Ethernet).
Usage:
python ota_upload.py # Uses timestamped default message
python ota_upload.py --message "Hello world" # Custom message
python ota_upload.py --ip 192.168.2.10 # Override target IP
Protocol:
1. Send 4-byte firmware size (big-endian)
2. Send raw firmware binary in 4 KB chunks
Push firmware to a STAR board over Ethernet.
Board-agnostic: anything running ``libraries/STAR_EthernetOTA`` speaks this
protocol, which is all of them (sense boards, actuator, stacklight, encoder,
environmental tracker).
# upload a binary you already built
python firmware/tools/ota_upload.py --ip 192.168.2.41 \
--bin firmware/Hotfire_Code/LC_Hotfire/.pio/build/adafruit_feather_esp32s3/firmware.bin
# build the project first, then upload it
python firmware/tools/ota_upload.py --ip 192.168.2.41 \
--project firmware/Hotfire_Code/LC_Hotfire
# by MAC or by config name: tries the board's DHCP address, then its
# recovery address 169.254.x.y (a board whose DHCP failed sits there)
python firmware/tools/ota_upload.py --mac 4a:27:e2:3a:ad:71 --bin rtd.bin
python firmware/tools/ota_upload.py --board rtd_board --bin rtd.bin
# bake a marker into the build so you can SEE the update land on serial:
# the board starts printing "[OTA-MSG] third try" instead of whatever it
# printed before
python firmware/tools/ota_upload.py --ip 192.168.2.41 \
--project firmware/Hotfire_Code/LC_Hotfire --message "third try"
Wire protocol (see libraries/STAR_EthernetOTA/src/STAR_EthernetOTA.h):
client -> board : [4-byte big-endian image size][raw firmware bytes]
board -> client: "OK\\r\\n", then reboot
Two different SHA-256 values are involved, and confusing them makes a good
update look like a failed one:
* ``sha256`` of the .bin file — what you get from ``shasum -a 256``.
* the **appended image hash**, the last 32 bytes of the .bin, which is what
``esp_partition_get_sha256()`` returns and therefore what the board prints
at boot and reports in BOARD_HEARTBEAT (see
``Hotfire_Code/common/FIRMWARE_HASH_VERIFICATION.md``).
They are never equal. This tool prints both and labels which one to compare
against the board.
Pure standard library. ``python -m ota_upload --self-test`` runs an offline
round trip against a fake board.
|
protected |
|
protected |
Round-trip against a fake board implementing the same wire protocol.
Definition at line 437 of file ota_upload.py.
References candidate_addresses(), firmware_sha256(), main(), min, recovery_address(), upload(), and upload_to_first().
Referenced by main().
| tuple ota_upload.board_from_config | ( | str | name, |
| Path | config ) |
(mac, ip) of [boards.<name>] in a daq-server config.toml.
Definition at line 107 of file ota_upload.py.
Referenced by main().
| Optional[str] ota_upload.board_reported_sha256 | ( | bytes | data | ) |
The digest the board prints for this image, or None if it has none. The board uses esp_partition_get_sha256(), which returns the 32-byte SHA-256 that esptool appends to the image -- computed over the image *excluding* those 32 bytes. Hashing the whole .bin gives a different number that will never match, which makes a perfectly good update look like a failed one. The appended hash is present when byte 23 of the ESP32 image header (magic 0xE9) has the hash_appended flag set.
Definition at line 146 of file ota_upload.py.
Referenced by main().
| Path ota_upload.build | ( | Path | project, |
| Optional[str] | env = None, | ||
| Optional[str] | message = None ) |
Build `project` and return the path to firmware.bin. `message` is injected as STAR_OTA_TEST_MESSAGE through PLATFORMIO_BUILD_FLAGS, so nothing in the repo has to be edited to make one build visibly different from the last.
Definition at line 271 of file ota_upload.py.
References find_pio().
Referenced by main().
| list ota_upload.candidate_addresses | ( | Optional[str] | ip, |
| Optional[str] | mac ) |
Addresses to try, in order: the DHCP one, then recovery.
Definition at line 129 of file ota_upload.py.
References recovery_address().
Referenced by _self_test(), and main().
| str ota_upload.compile_firmware | ( | str | message | ) |
Compile the firmware with the given OTA_MESSAGE baked in. Returns the path to the compiled .bin file.
Definition at line 81 of file ota_upload.py.
References find_pio_command(), and split().
Referenced by main().
| str ota_upload.find_pio | ( | ) |
Locate the PlatformIO CLI, including its default install locations.
Definition at line 253 of file ota_upload.py.
Referenced by build().
| str ota_upload.find_pio_command | ( | ) |
Try to find the 'pio' command in PATH or common installation locations.
Definition at line 49 of file ota_upload.py.
Referenced by compile_firmware().
| str ota_upload.firmware_sha256 | ( | bytes | data | ) |
SHA-256 of the .bin file itself. NOT what the board reports.
Definition at line 141 of file ota_upload.py.
Referenced by _self_test(), and main().
| ota_upload.main | ( | void | ) |
Definition at line 230 of file ota_upload.py.
References compile_firmware(), main(), print_banner(), and upload_firmware().
| int ota_upload.main | ( | argv = None | ) |
Definition at line 341 of file ota_upload.py.
References _progress_printer(), _self_test(), board_from_config(), board_reported_sha256(), build(), candidate_addresses(), firmware_sha256(), and upload_to_first().
| bytes ota_upload.parse_mac | ( | str | mac | ) |
| ota_upload.print_banner | ( | ) |
Definition at line 42 of file ota_upload.py.
Referenced by main().
| str ota_upload.recovery_address | ( | str | mac | ) |
Where a board sits when no DHCP server answered it: 169.254.<1 + mac[4] % 254>.<mac[5]>. Must match BoardNet::recoveryAddress (firmware/Hotfire_Code/common/board_net.h) and daq-server's config check.
Definition at line 99 of file ota_upload.py.
References parse_mac().
Referenced by _self_test(), and candidate_addresses().
| str ota_upload.upload | ( | bytes | data, |
| str | ip, | ||
| int | port = DEFAULT_PORT, | ||
| Optional[ProgressFn] | on_progress = None, | ||
| float | connect_timeout = CONNECT_TIMEOUT, | ||
| float | transfer_timeout = TRANSFER_TIMEOUT ) |
Send `data` to the board and wait for its acknowledgement. Returns the board's reply text. Raises OtaError with something actionable on any failure.
Definition at line 161 of file ota_upload.py.
References decode().
Referenced by _self_test(), and upload_to_first().
| ota_upload.upload_firmware | ( | str | bin_path, |
| str | ip, | ||
| int | port ) |
Send the firmware binary to the ESP32 over TCP.
Definition at line 142 of file ota_upload.py.
References decode(), and time.time().
Referenced by main().
| str ota_upload.upload_to_first | ( | bytes | data, |
| list | hosts, | ||
| int | port = DEFAULT_PORT, | ||
| ** | kwargs ) |
Upload to the first of `hosts` that accepts a connection. Moves on only when nothing answered; a board that answered and then failed is an error.
Definition at line 227 of file ota_upload.py.
References upload().
Referenced by _self_test(), and main().
| int ota_upload.CHUNK_SIZE = 4096 |
Definition at line 31 of file ota_upload.py.
| float ota_upload.CONNECT_TIMEOUT = 5 |
Definition at line 32 of file ota_upload.py.
| str ota_upload.DEFAULT_CONFIG = REPO_ROOT / "daq-server" / "config" / "config.toml" |
Definition at line 85 of file ota_upload.py.
| str ota_upload.DEFAULT_IP = "192.168.2.5" |
Definition at line 29 of file ota_upload.py.
| int ota_upload.DEFAULT_PORT = 3232 |
Definition at line 30 of file ota_upload.py.
| ota_upload.FIRMWARE_BIN = os.path.join(PIO_BUILD_DIR, "firmware.bin") |
Definition at line 39 of file ota_upload.py.
| int ota_upload.MAX_IMAGE_BYTES = 0x200000 |
Definition at line 68 of file ota_upload.py.
| ota_upload.PIO_BUILD_DIR = os.path.join(PROJECT_DIR, ".pio", "build", "adafruit_feather_esp32s3") |
Definition at line 38 of file ota_upload.py.
| ota_upload.ProgressFn = Callable[[int, int], None] |
Definition at line 70 of file ota_upload.py.
| ota_upload.PROJECT_DIR = os.path.join(SCRIPT_DIR, "OTA_Test_Firmware") |
Definition at line 37 of file ota_upload.py.
| ota_upload.REPO_ROOT = Path(__file__).resolve().parents[2] |
Definition at line 84 of file ota_upload.py.
| ota_upload.SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) |
Definition at line 36 of file ota_upload.py.
| float ota_upload.TRANSFER_TIMEOUT = 30 |
Definition at line 33 of file ota_upload.py.