Skip to content

Commit 4a7c88e

Browse files
committed
Cleanup pico_flash task
Remove unnecessary `os:cmd` usage and replace with native Erlang functions wherever possible. Move the reset code out of `do_flash/3` to separate `do_reset/1` function. Signed-off-by: Winford <winford@object.stream>
1 parent 8ab3e77 commit 4a7c88e

File tree

1 file changed

+114
-79
lines changed

1 file changed

+114
-79
lines changed

src/atomvm_pico_flash_provider.erl

Lines changed: 114 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
%%
2-
%% Copyright (c) 2023 Uncle Grumpy <winford@object.stream>
2+
%% Copyright (c) 2023-2024 Uncle Grumpy <winford@object.stream>
33
%% All rights reserved.
44
%%
55
%% Based on esp32_flash_provider.erl
@@ -106,53 +106,56 @@ env_opts() ->
106106
),
107107
reset => os:getenv(
108108
"ATOMVM_REBAR3_PLUGIN_PICO_RESET_DEV",
109-
get_reset_dev()
109+
get_reset_base()
110110
)
111111
}.
112112

113113
%% @private
114114
get_stty_file_flag() ->
115-
System = os:cmd("uname -s"),
115+
{_Fam, System} = os:type(),
116116
case System of
117-
"Linux\n" ->
117+
linux ->
118118
"-F";
119119
_Other ->
120120
"-f"
121121
end.
122122

123123
%% @private
124-
get_reset_dev() ->
125-
System = os:cmd("uname -s"),
124+
get_reset_base() ->
125+
{_Fam, System} = os:type(),
126126
case System of
127-
"Linux\n" ->
128-
"/dev/ttyACM0";
129-
"Darwin\n" ->
127+
linux ->
128+
"/dev/ttyACM*";
129+
darwin ->
130130
"/dev/cu.usbmodem14*";
131131
_Other ->
132132
""
133133
end.
134134

135135
%% @private
136136
get_default_mount() ->
137-
System = os:cmd("uname -s"),
137+
{_Fam, System} = os:type(),
138138
case System of
139-
"Linux\n" ->
140-
"/run/media/${USER}/RPI-RP2";
141-
"Darwin\n" ->
139+
linux ->
140+
"/run/media/" ++ os:getenv("USER") ++ "/RPI-RP2";
141+
darwin ->
142142
"/Volumes/RPI-RP2";
143143
_Other ->
144144
""
145145
end.
146146

147147
%% @private
148148
wait_for_mount(Mount, Count) when Count < 30 ->
149-
Cmd = lists:join(" ", [
150-
"sh -c \"(test -d", Mount, "&& echo 'true') || echo 'false'\""
151-
]),
152-
case os:cmd(Cmd) of
153-
"true\n" ->
154-
ok;
155-
"false\n" ->
149+
try
150+
case file:read_link_info(Mount) of
151+
{ok, #file_info{type = directory} = _Info} ->
152+
ok;
153+
_ ->
154+
timer:sleep(1000),
155+
wait_for_mount(Mount, Count + 1)
156+
end
157+
catch
158+
_ ->
156159
timer:sleep(1000),
157160
wait_for_mount(Mount, Count + 1)
158161
end;
@@ -162,31 +165,80 @@ wait_for_mount(Mount, 30) ->
162165

163166
%% @private
164167
check_pico_mount(Mount) ->
165-
Cmd = lists:join(" ", [
166-
"sh -c \"(test -d", Mount, "&& echo 'true') || echo 'false'\""
167-
]),
168-
case os:cmd(Cmd) of
169-
"true\n" ->
170-
ok;
171-
"false\n" ->
168+
try
169+
case file:read_link_info(Mount) of
170+
{ok, #file_info{type = directory} = _Info} ->
171+
rebar_api:debug("Pico mounted at ~s.", [Mount]),
172+
ok;
173+
_ ->
174+
rebar_api:error("Pico not mounted at ~s.", [Mount]),
175+
erlang:throw(no_device)
176+
end
177+
catch
178+
_ ->
172179
rebar_api:error("Pico not mounted at ~s.", [Mount]),
173180
erlang:throw(no_device)
174181
end.
175182

176183
%% @private
177-
needs_reset(ResetPort) ->
178-
Test = lists:join(" ", [
179-
"sh -c \"(test -e",
180-
ResetPort,
181-
"&& echo 'true') || echo 'false'\""
182-
]),
183-
case os:cmd(Test) of
184-
"true\n" ->
185-
true;
186-
"false\n" ->
184+
needs_reset(ResetBase) ->
185+
case filelib:wildcard(ResetBase) of
186+
[] ->
187+
false;
188+
[ResetPort | _T] ->
189+
case file:read_link_info(ResetPort) of
190+
{ok, #file_info{type = device} = _Info} ->
191+
{true, ResetPort};
192+
_ ->
193+
false
194+
end;
195+
_ ->
187196
false
188197
end.
189198

199+
%% @private
200+
do_reset(ResetPort) ->
201+
Flag = get_stty_file_flag(),
202+
BootselMode = lists:join(" ", [
203+
"stty", Flag, ResetPort, "1200"
204+
]),
205+
rebar_api:debug("Resetting device at path ~s", [ResetPort]),
206+
ResetStatus = os:cmd(BootselMode),
207+
case ResetStatus of
208+
"" ->
209+
ok;
210+
Error ->
211+
case os:find_executable(picotool) of
212+
false ->
213+
rebar_api:error(
214+
"Warning: ~s~nUnable to locate 'picotool', close the serial monitor before flashing, or install picotool for automatic disconnect and BOOTSEL mode.",
215+
[Error]
216+
),
217+
erlang:throw(reset_error);
218+
Picotool ->
219+
rebar_api:warn(
220+
"Warning: ~s~nFor faster flashing remember to disconnect serial monitor first.",
221+
[Error]
222+
),
223+
DevReset = lists:join(" ", [
224+
Picotool, "reboot", "-f", "-u"
225+
]),
226+
rebar_api:warn("Disconnecting serial monitor with: `~s' in 5 seconds...", [
227+
DevReset
228+
]),
229+
timer:sleep(5000),
230+
RebootReturn = os:cmd(DevReset),
231+
RebootStatus = string:trim(RebootReturn),
232+
case RebootStatus of
233+
"The device was asked to reboot into BOOTSEL mode." ->
234+
ok;
235+
BootError ->
236+
rebar_api:error("Failed to prepare pico for flashing: ~s", [BootError]),
237+
erlang:throw(picoflash_reboot_error)
238+
end
239+
end
240+
end.
241+
190242
%% @private
191243
get_uf2_file(ProjectApps) ->
192244
[App | _] = [ProjectApp || ProjectApp <- ProjectApps],
@@ -196,53 +248,36 @@ get_uf2_file(ProjectApps) ->
196248
filename:join(DirName, Name ++ ".uf2").
197249

198250
%% @private
199-
do_flash(ProjectApps, PicoPath, ResetPort) ->
200-
case needs_reset(ResetPort) of
251+
get_uf2_appname(ProjectApps) ->
252+
[App | _] = [ProjectApp || ProjectApp <- ProjectApps],
253+
binary_to_list(rebar_app_info:name(App)).
254+
255+
%% @private
256+
do_flash(ProjectApps, PicoPath, ResetBase) ->
257+
case needs_reset(ResetBase) of
201258
false ->
259+
rebar_api:debug("No Pico found matching ~s.", [ResetBase]),
202260
ok;
203-
true ->
204-
Flag = get_stty_file_flag(),
205-
BootselMode = lists:join(" ", [
206-
"stty", Flag, ResetPort, "1200"
261+
{true, ResetPort} ->
262+
rebar_api:debug("Pico at ~s needs reset...", [ResetPort]),
263+
do_reset(ResetPort),
264+
rebar_api:info("Waiting for the device at path ~s to settle and mount...", [
265+
string:trim(PicoPath)
207266
]),
208-
rebar_api:info("Resetting device at path ~s", [ResetPort]),
209-
ResetStatus = os:cmd(BootselMode),
210-
case ResetStatus of
211-
"" ->
212-
ok;
213-
Error ->
214-
case os:find_executable(picotool) of
215-
false ->
216-
rebar_api:error("Warning: ~s~nUnable to locate 'picotool', close the serial monitor before flashing, or install picotool for automatic disconnect and BOOTSEL mode.", [Error]),
217-
erlang:throw(reset_error);
218-
_Path ->
219-
rebar_api:warn("Warning: ~s~nFor faster flashing remember to disconnect serial monitor first.", [Error]),
220-
DevReset = lists:join(" ", [
221-
"picotool", "reboot", "-f", "-u"
222-
]),
223-
rebar_api:warn("Disconnecting serial monitor with: `~s' in 5 seconds...", [DevReset]),
224-
timer:sleep(5000),
225-
RebootReturn = os:cmd(DevReset),
226-
RebootStatus = string:trim(RebootReturn),
227-
case RebootStatus of
228-
"The device was asked to reboot into BOOTSEL mode." ->
229-
ok;
230-
BootError ->
231-
rebar_api:error("Failed to prepare pico for flashing: ~s", [BootError]),
232-
erlang:throw(picoflash_reboot_error)
233-
end
234-
end
235-
end,
236-
PrettyPath = lists:join(" ", [
237-
"echo", PicoPath
238-
]),
239-
rebar_api:info("Waiting for the device at path ~s to settle and mount...", [string:trim(os:cmd(PrettyPath))]),
240267
wait_for_mount(PicoPath, 0)
241268
end,
242269
check_pico_mount(PicoPath),
243270
TargetUF2 = get_uf2_file(ProjectApps),
244-
Cmd = lists:join(" ", [
245-
"cp", "-v", TargetUF2, PicoPath
246-
]),
247-
rebar_api:info("Copying packbeam files...~n~s", [os:cmd(Cmd)]),
271+
App = get_uf2_appname(ProjectApps),
272+
File = App ++ ".uf2",
273+
Dest = filename:join(PicoPath, File),
274+
rebar_api:info("Copying ~s", [File]),
275+
case file:copy(TargetUF2, Dest) of
276+
{ok, _Size} ->
277+
ok;
278+
CopyError ->
279+
rebar_api:error("Failed to copy application file ~s to pico: ~s", [File, CopyError]),
280+
erlang:throw(picoflash_copy_error)
281+
end,
282+
rebar_api:info("Successfully loaded ~s application to device at path ~s.", [App, PicoPath]),
248283
ok.

0 commit comments

Comments
 (0)