Skip to content

Commit 586a1ef

Browse files
committed
Enhancements for the esp32_flash task
The `offset` for the beam application partition is now read from the partition table on the attached esp32 device. When a custom partition table is used that does not use `main.avm` for the beam app partition name the `app_partition` parameter should be used to specify the application partition to be flashed. Valid application partition sub-types (the type is `data`) are `phy` or `0xAA`. If the `offset` parameter is specified it will be used to verify that the offset address of the application partition matched the expected value. This may be used to prevent flashing to a standard build of AtomMV for application that require a custom partition table. The `port` that the ESP32 is attached to is now auto discovered by default. When more than one ESP32 device is plugged into USB the port should be specified to control which device is flashed. Error reporting has been improved with descriptive error messages. Dialyzer warnings for the esp32_flash task have been fixed when analyzing atomvm_rebar3_plugin. Signed-off-by: Winford <winford@object.stream>
1 parent 15dfaa7 commit 586a1ef

File tree

13 files changed

+326
-35
lines changed

13 files changed

+326
-35
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121
- Added dialyzer task to simplify running dialyzer on AtomVM applications.
2222
- Added support for rp2350 devices to allow for default detection of the device mount path.
2323
- Added configuration paramenter for setting the path to picotool for the pico_flash task.
24+
- Added `app_partition` parameter to `esp32_flash` task. This is only needed to be provided for
25+
custom partition tables that do not use `main.avm` for the beam application partition name, or to
26+
flash to a custom alternate partition.
2427

2528
### Changed
2629
- The `uf2create` task now creates `universal` format uf2 files by default, suitable for both
2730
rp2040 or rp2350 devices.
2831
- The `pico_flash` task now checks that a device is an RP2 platform before resetting to `BOOTSEL`
2932
mode, preventing interference with other MCUs that may be attached to the host system.
3033
- The `pico_flash` task now aborts on all errors rather than trying to continue after a failure.
34+
- The `offset` used by the `esp32_flash` task is now read from the partition table of the device.
35+
When this parameter is provided it will be used to verify the offset of the application partition on
36+
flash matches the expected value.
37+
- The `esp32_flash` task now uses auto discovery for the `port` by default.
38+
- Stacktraces are not shown by default if the `esp32_flash` fails, instead a descriptive error
39+
message is displayed. To view the stacktrace use diagnostic mode.
40+
41+
### Fixed
42+
- The `esp32_flash` task aborts when an error occurs, rather than attempt to continue after a step
43+
has failed.
3144

3245
## [0.7.5] (2025.05.27)
3346

README.md

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -260,24 +260,30 @@ Running this AVM file will boot the `myapp` application automatically, without h
260260

261261
You may use the `esp32_flash` task to flash the generated AtomVM packbeam application to the flash storage on an ESP32 device connected over a serial connection.
262262

263-
shell$ rebar3 help atomvm esp32_flash
264-
265-
Use this plugin to flash an AtomVM packbeam file to an ESP32 device.
266-
267-
Usage: rebar3 atomvm esp32_flash [-e <esptool>] [-c <chip>] [-p <port>]
268-
[-b <baud>] [-o <offset>]
269-
270-
-e, --esptool Path to esptool.py
271-
-c, --chip ESP chip (default auto)
272-
-p, --port Device port (default /dev/ttyUSB0)
273-
-b, --baud Baud rate (default 115200)
274-
-o, --offset Offset (default 0x210000)
263+
```shell
264+
shell$ rebar3 help atomvm esp32_flash
265+
266+
Use this plugin to flash an AtomVM packbeam file to an ESP32 device.
267+
268+
Usage: rebar3 atomvm esp32_flash [-e <esptool>] [-c <chip>] [-p <port>]
269+
[-b <baud>] [-o <offset>]
270+
[-a <app_partition>]
271+
272+
-e, --esptool Path to esptool.py
273+
-c, --chip ESP chip (default auto)
274+
-p, --port Device port (default auto discovery)
275+
-b, --baud Baud rate (default 115200)
276+
-o, --offset Offset (default read from device) *old behavior
277+
deprecated, use app_partition. When given, verifies
278+
expected offset to actual
279+
-a, --app_partition Application partition name (default main.avm)
280+
```
275281

276282
The `esp32_flash` task will use the `esptool.py` command to flash the ESP32 device. This tool is available via the <a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/">IDF SDK</a>, or directly via <a href="https://github.com/espressif/esptool">github</a>. The `esptool.py` command is also available via many package managers (e.g., MacOS Homebrew).
277283

278-
By default, the `esp32_flash` task will assume the `esptool.py` command is available on the user's executable path. Alternatively, you may specify the full path to the `esptool.py` command via the `-e` (or `--esptool`) option
284+
By default, the `esp32_flash` task will assume the `esptool.py` command is available on the user's executable path. Alternatively, you may specify the full path to the `esptool.py` command via the `-e` (or `--esptool`) option.
279285

280-
By default, the `esp32_flash` task will write to port `/dev/ttyUSB0` at a baud rate of `115200`. You may control the port and baud settings for connecting to your ESP device via the `-port` and `-baud` options to the `esp32_flash` task, e.g.,
286+
By default, the `esp32_flash` task uses port auto discovery at a baud rate of `115200`. You may control the port and baud settings for connecting to your ESP device via the `-port` and `-baud` options to the `esp32_flash` task, e.g.,
281287

282288
shell$ rebar3 atomvm esp32_flash --port /dev/tty.SLAB_USBtoUART --baud 921600
283289
...
@@ -306,7 +312,8 @@ The following table enumerates the properties that may be defined in your projec
306312
| `chip` | `string()` | ESP32 chip type |
307313
| `port` | `string()` | Device port on which the ESP32 can be located |
308314
| `baud` | `integer()` | Device BAUD rate |
309-
| `offset` | `string()` | Offset into which to write AtomVM application |
315+
| `offset` | `string()` | Optionally verify offset on flash matches expected value. Original behavior deprecated: use `app_partition` for custom images or to target a different partition |
316+
| `app_partition` | `string()` | Name of application partition to write AtomVM application for custom partition tables |
310317

311318
Example:
312319

@@ -319,9 +326,21 @@ Alternatively, the following environment variables may be used to control the ab
319326
* `ATOMVM_REBAR3_PLUGIN_ESP32_FLASH_PORT`
320327
* `ATOMVM_REBAR3_PLUGIN_ESP32_FLASH_BAUD`
321328
* `ATOMVM_REBAR3_PLUGIN_ESP32_FLASH_OFFSET`
329+
* `ATOMVM_REBAR3_PLUGIN_ESP32_APP_PARTITION`
322330

323331
Any setting specified on the command line take precedence over settings in `rebar.config`, which in turn take precedence over environment variable settings, which in turn take precedence over the default values specified above.
324332

333+
```note
334+
The behavior of the `offset` configuration option has changed, the correct offset for standard
335+
AtomVM builds are determined by the partition table flashed to the device. Elixir supported builds
336+
are recognized and the correct offset will be used. When using a custom partition table it is
337+
necessary to supply the `app_partition` name. If an offset is given it will be compared to the
338+
address of the discovered `app_partition` and an error will be raised if they do not match. The
339+
partition name of the application partition for standard AtomVM partition tables is `main.avm`, but
340+
it is not necessary to supply the name when using a release image or one of the standard partition
341+
tables.
342+
```
343+
325344
The `esp32_flash` task depends on the `packbeam` task, so the packbeam file will get automatically built if any changes have been made to its dependencies.
326345

327346
### The `stm32_flash` task

UPDATING.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@
66

77
# `atomvm_rebar3_plugin` Update Instructions
88

9+
## (unreleased)
10+
11+
- The `esp32_flash` task now reads the application `offset` from the partition table on the device.
12+
If you are using a custom partition table that does not use `main.avm` for the application partition
13+
name you should supply the name used with the `app_partition` parameter. An `offset` may optionally
14+
be supplied to assure the offset of the application partition matches the expected offset, this may
15+
be helpful to assure that specific applications are only flashed to devices with a custom build of
16+
AtomVM.
17+
- Pico 2 (RP2350) devices are recognized and now work with default parameters. Specifying device
18+
path and uf2 flavor for these chipsets is no longer necessary.
19+
920
## 0.6.* -> 0.7.*
1021

1122
- The `atomvm_rebar3_plugin` tasks have been moved into the `atomvm` namespace (from the [`rebar3`](https://rebar3.org) `default` namespace). The "legacy" tasks in the `default` namespace are deprecated, and users will be issued a warning when used. Be sure to use the `atomvm` namespace in any future usage of this plugin, as the deprecated tasks may be removed without warning. E.g., `rebar3 atomvm packbeam ...`

src/atomvm_esp32_flash_provider.erl

Lines changed: 98 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,21 @@
3030
-define(OPTS, [
3131
{esptool, $e, "esptool", string, "Path to esptool.py"},
3232
{chip, $c, "chip", string, "ESP chip (default auto)"},
33-
{port, $p, "port", string, "Device port (default /dev/ttyUSB0)"},
33+
{port, $p, "port", string, "Device port (default auto discovery)"},
3434
{baud, $b, "baud", integer, "Baud rate (default 115200)"},
35-
{offset, $o, "offset", string, "Offset (default 0x210000)"}
35+
{offset, $o, "offset", string,
36+
"Offset (default read from device) *old behavior deprecated, use app_partition."
37+
" When given, verifies expected offset to actual"},
38+
{app_partition, $a, "app_partition", string, "Application partition name (default main.avm)"}
3639
]).
3740

3841
-define(DEFAULT_OPTS, #{
3942
esptool => "esptool.py",
4043
chip => "auto",
41-
port => "/dev/ttyUSB0",
42-
baud => 115200,
43-
offset => "0x210000"
44+
port => "auto",
45+
baud => "115200",
46+
offset => auto,
47+
app_partition => "main.avm"
4448
}).
4549

4650
%%
@@ -81,13 +85,22 @@ do(State) ->
8185
maps:get(chip, Opts),
8286
maps:get(port, Opts),
8387
maps:get(baud, Opts),
84-
maps:get(offset, Opts)
88+
maybe_convert_string(maps:get(offset, Opts)),
89+
list_to_binary(maps:get(app_partition, Opts)),
90+
State
8591
),
8692
{ok, State}
8793
catch
94+
C:rebar_abort:S ->
95+
rebar_api:error(
96+
"A fatal error occurred in the ~p task.",
97+
[?PROVIDER]
98+
),
99+
rebar_api:debug("Class=~p, Error=~p~nSTACKTRACE:~n~p~n", [C, rebar_abort, S]),
100+
{error, rebar_abort};
88101
C:E:S ->
89102
rebar_api:error(
90-
"An error occurred in the ~p task. Error=~p",
103+
"An unhandled error occurred in the ~p task. Error=~p",
91104
[?PROVIDER, E]
92105
),
93106
rebar_api:debug("Class=~p, Error=~p~nSTACKTRACE:~n~p~n", [C, E, S]),
@@ -105,6 +118,7 @@ format_error(Reason) ->
105118
%% @private
106119
get_opts(State) ->
107120
{ParsedArgs, _} = rebar_state:command_parsed_args(State),
121+
rebar_api:debug("ParsedArgs = ~p", [ParsedArgs]),
108122
RebarOpts = atomvm_rebar3_plugin:get_atomvm_rebar_provider_config(State, ?PROVIDER),
109123
ParsedOpts = atomvm_rebar3_plugin:proplist_to_map(ParsedArgs),
110124
maps:merge(
@@ -133,20 +147,47 @@ env_opts() ->
133147
maps:get(baud, ?DEFAULT_OPTS)
134148
)
135149
),
136-
offset => os:getenv(
137-
"ATOMVM_REBAR3_PLUGIN_ESP32_FLASH_OFFSET",
138-
maps:get(offset, ?DEFAULT_OPTS)
150+
offset => maybe_convert_string(
151+
os:getenv(
152+
"ATOMVM_REBAR3_PLUGIN_ESP32_FLASH_OFFSET",
153+
maps:get(offset, ?DEFAULT_OPTS)
154+
)
155+
),
156+
app_partition => os:getenv(
157+
"ATOMVM_REBAR3_PLUGIN_ESP32_APP_PARTITION",
158+
maps:get(app_partition, ?DEFAULT_OPTS)
139159
)
140160
}.
141161

142162
%% @private
143163
maybe_convert_string(S) when is_list(S) ->
144-
list_to_integer(S);
164+
case lists:prefix("0x", S) of
165+
true ->
166+
list_to_integer(lists:subtract(S, "0x"), 16);
167+
false ->
168+
list_to_integer(S)
169+
end;
145170
maybe_convert_string(I) ->
146171
I.
147172

148173
%% @private
149-
do_flash(ProjectApps, EspTool, Chip, Port, Baud, Offset) ->
174+
do_flash(ProjectApps, EspTool, Chip, Port, Baud, Address, Partition, State) ->
175+
Offset =
176+
case Address of
177+
auto ->
178+
read_flash_offset(EspTool, Port, Partition, State);
179+
Val ->
180+
Offset0 = read_flash_offset(EspTool, Port, Partition, State),
181+
case Val =:= Offset0 of
182+
true ->
183+
Offset0;
184+
false ->
185+
rebar_api:abort(
186+
"The configured offset 0x~.16B does not match the partition table on the device (0x~.16B).",
187+
[Val, Offset0]
188+
)
189+
end
190+
end,
150191
[ProjectAppAVM | _] = [get_avm_file(ProjectApp) || ProjectApp <- ProjectApps],
151192
Portparam =
152193
case Port of
@@ -172,9 +213,12 @@ do_flash(ProjectApps, EspTool, Chip, Port, Baud, Offset) ->
172213
"keep",
173214
"--flash_size",
174215
"detect",
175-
Offset,
216+
integer_to_list(Offset),
176217
ProjectAppAVM
177218
]),
219+
AVMApp = filename:basename(ProjectAppAVM),
220+
rebar_api:info("Flashing ~s to device.", [AVMApp]),
221+
%% The following log output is parsed by the tests and should not be changed or removed.
178222
rebar_api:info("~s~n", [Cmd]),
179223
rebar_api:console("~s", [os:cmd(Cmd)]),
180224
ok.
@@ -185,3 +229,44 @@ get_avm_file(App) ->
185229
Name = binary_to_list(rebar_app_info:name(App)),
186230
DirName = filename:dirname(OutDir),
187231
filename:join(DirName, Name ++ ".avm").
232+
233+
%% @private
234+
read_flash_offset(Esptool, Port, PartName, State) ->
235+
TempFile = get_part_tempfile(State),
236+
rebar_api:info("Reading application partition offset from device...", []),
237+
try esp_part_dump:read_app_offset(Esptool, Port, PartName, TempFile) of
238+
Offset ->
239+
Offset
240+
catch
241+
_:invalid_partition_table:_ ->
242+
rebar_api:abort("Invalid partition data!", []);
243+
_:no_device:_ ->
244+
rebar_api:abort("No ESP32 device attached!", []);
245+
_:{partition_not_found, Partition}:_ ->
246+
rebar_api:error("The partition ~s was not fount on device partition table!", [Partition]),
247+
rebar_api:abort(
248+
"When using a custom partition table always specify the 'app_partition' NAME.", []
249+
);
250+
_:{invalid_subtype, Type}:_ ->
251+
rebar_api:abort("The partition ~s was found, but used invalid subtype 0x~s.", [
252+
PartName, Type
253+
]);
254+
_:invalid_partition_data:_ ->
255+
rebar_api:abort("The partition ~s was found, but partition data is invalid.", [PartName]);
256+
_:Error:_ ->
257+
rebar_api:abort("Unexpected error reading partition table from device, ~p.", [Error])
258+
end.
259+
260+
%% @private
261+
get_part_tempfile(State) ->
262+
OutDir = filename:absname(rebar_dir:base_dir(State)),
263+
TempFile = filename:absname_join(OutDir, "part.tmp"),
264+
case filelib:is_file(TempFile) of
265+
true ->
266+
rebar_api:debug("Removing possibly stale partition dump data ~s", [TempFile]),
267+
Cmd = lists:join(" ", ["rm", TempFile]),
268+
os:cmd(Cmd);
269+
false ->
270+
ok
271+
end,
272+
TempFile.

test/driver/scripts/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<!--
2+
Copyright 2025 Winford (Uncle Grumpy) <winford@object.stream>
3+
4+
SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
5+
-->
6+
# esptool.sh and partition binary files
7+
8+
The partition binary files in this directory are used by esptool.sh to simulate the real esptool.py
9+
dummping the partiton table from an AtomVM installed device. They were built using esp-idf and the
10+
following partiton.csv contents were used to generate the partition tables:
11+
12+
## partition.bin
13+
This partition table starts with the standard Erlang only partiton table, but adds several extra
14+
partitons used for tests to flash to an alternate partition and test failures for invalid partiton
15+
tyes.
16+
17+
```csv
18+
# Name, Type, SubType, Offset, Size, Flags
19+
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
20+
nvs, data, nvs, 0x9000, 0x6000, \
21+
phy_init, data, phy, 0xf000, 0x1000, \
22+
factory, app, factory, 0x10000, 0x1C0000, > these are standard partitons
23+
boot.avm, data, phy, 0x1D0000, 0x40000, /
24+
main.avm, data, phy, 0x210000, 0x100000, /
25+
app1.avm, data, 0xAA, 0x310000, 0x070000, \
26+
bad1, data, undefined, 0x380000, 0x010000, > extra test partitons
27+
bad2, app, test, 0x390000, 0x010000 /
28+
```
29+
30+
## partition_elixir.bin
31+
This partition talbe is just the standard Elixir supported build partiton table.
32+
33+
```csv
34+
# Name, Type, SubType, Offset, Size, Flags
35+
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
36+
nvs, data, nvs, 0x9000, 0x6000,
37+
phy_init, data, phy, 0xf000, 0x1000,
38+
factory, app, factory, 0x10000, 0x1C0000,
39+
boot.avm, data, 0xAB, 0x1D0000, 0x80000,
40+
main.avm, data, 0xAA, 0x250000, 0x100000
41+
```

test/driver/scripts/esptool.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/sh
2+
##
3+
## Copyright (c) Winford (UncleGrumpy) <winford@object.stream>
4+
## All rights reserved.
5+
##
6+
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
7+
8+
while [ "${1}" != "" ]; do
9+
case ${1} in
10+
read_flash ) shift
11+
if ( [ ${1} = "0x8000" ] && [ ${2} = "0xc00" ] ); then
12+
target=${3}
13+
cp ${ATOMVM_REBAR3_PLUGIN_PARTITION_DATA:=${ATOMVM_REBAR3_PLUGIN_ESP32_PARTITION_DUMP}} ${target}
14+
unset ATOMVM_REBAR3_PLUGIN_PARTITION_DATA
15+
break;
16+
fi
17+
esac
18+
shift
19+
done
20+
21+
echo "${@}"

test/driver/scripts/partition.bin

3 KB
Binary file not shown.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
SPDX-License-Identifier: Apache-2.0
2+
SPDX-FileCopyrightText: Winford (Uncle Grumpy) <winford@object.stream>
3 KB
Binary file not shown.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
SPDX-License-Identifier: Apache-2.0
2+
SPDX-FileCopyrightText: Winford (Uncle Grumpy) <winford@object.stream>

0 commit comments

Comments
 (0)