Skip to content

Commit 6fe8c61

Browse files
committed
ESP32: move "dev mode" code to esp32devmode
Keep esp32init as small as possible. Signed-off-by: Davide Bettio <[email protected]>
1 parent 3a39b11 commit 6fe8c61

File tree

5 files changed

+345
-300
lines changed

5 files changed

+345
-300
lines changed

libs/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ add_subdirectory(eavmlib/src)
2626
add_subdirectory(alisp/src)
2727
add_subdirectory(etest/src)
2828
add_subdirectory(esp32boot)
29+
add_subdirectory(esp32devmode/src)
2930

3031
if (Elixir_FOUND)
3132
add_subdirectory(exavmlib/lib)

libs/esp32boot/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ project(esp32boot)
2323
include(BuildErlang)
2424

2525
if (Elixir_FOUND)
26-
pack_runnable(esp32boot esp32init eavmlib estdlib alisp exavmlib)
26+
pack_runnable(esp32boot esp32init esp32devmode eavmlib estdlib alisp exavmlib)
2727
else()
28-
pack_runnable(esp32boot esp32init eavmlib estdlib alisp)
28+
pack_runnable(esp32boot esp32init esp32devmode eavmlib estdlib alisp)
2929
endif()

libs/esp32boot/esp32init.erl

Lines changed: 3 additions & 298 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,7 @@
2020

2121
-module(esp32init).
2222

23-
-export([
24-
start/0,
25-
start_repl/1,
26-
start_network/0,
27-
handle_req/3,
28-
erase_net_config/0,
29-
save_net_config/2
30-
]).
31-
32-
-record(nc_state, {socket, pending_pid, pending_ref}).
33-
34-
-define(DEFAULT_AP_SSID, <<"AtomVM-ESP32">>).
35-
-define(DEFAULT_AP_PSK, <<"esp32default">>).
36-
-define(DEFAULT_CONSOLE_PORT, 2323).
37-
-define(DEFAULT_WEB_SERVER_PORT, 8080).
23+
-export([start/0]).
3824

3925
start() ->
4026
console:print(<<"AtomVM init.\n">>),
@@ -63,24 +49,15 @@ loop() ->
6349
maybe_start_dev_mode(SystemStatus) ->
6450
case {SystemStatus, esp:nvs_get_binary(atomvm, dev_mode)} of
6551
{_, <<"always">>} ->
66-
start_dev_mode();
52+
ep32devmode:start_dev_mode();
6753
{_, <<"never">>} ->
6854
not_started;
6955
{ok, undefined} ->
7056
not_started;
7157
{failed_app_start, undefined} ->
72-
start_dev_mode()
58+
esp32devmode:start_dev_mode()
7359
end.
7460

75-
start_dev_mode() ->
76-
avm_pubsub:start(default_pubsub),
77-
spawn(fun maybe_start_network/0),
78-
started.
79-
80-
%%
81-
%% Boot handling
82-
%%
83-
8461
% TODO: add support for multiple apps
8562
% /dev/partition/by-name/app1.avm
8663
% /dev/partition/by-name/app2.avm
@@ -119,275 +96,3 @@ get_start_module() ->
11996
Module ->
12097
erlang:binary_to_atom(Module, latin1)
12198
end.
122-
123-
%%
124-
%% Network management
125-
%%
126-
127-
erase_net_config() ->
128-
io:format("Erasing net config.~n"),
129-
esp:nvs_erase_key(atomvm, sta_ssid),
130-
esp:nvs_erase_key(atomvm, sta_psk).
131-
132-
save_net_config(SSID, Pass) ->
133-
io:format("Saving config: SSID: ~p Pass: ~p.~n", [SSID, Pass]),
134-
esp:nvs_set_binary(atomvm, sta_ssid, erlang:list_to_binary(SSID)),
135-
esp:nvs_set_binary(atomvm, sta_psk, erlang:list_to_binary(Pass)).
136-
137-
get_net_config() ->
138-
case esp:nvs_get_binary(atomvm, sta_ssid) of
139-
undefined ->
140-
get_default_net_config();
141-
SSID ->
142-
case esp:nvs_get_binary(atomvm, sta_psk) of
143-
undefined ->
144-
get_default_net_config();
145-
Psk ->
146-
get_net_config(SSID, Psk)
147-
end
148-
end.
149-
150-
get_default_net_config() ->
151-
Creds = [
152-
{ssid, ?DEFAULT_AP_SSID},
153-
{psk, ?DEFAULT_AP_PSK}
154-
],
155-
{wait_for_ap, Creds}.
156-
157-
get_net_config(SSID, Psk) ->
158-
Creds = [
159-
{ssid, SSID},
160-
{psk, Psk}
161-
],
162-
{wait_for_sta, Creds}.
163-
164-
maybe_start_network() ->
165-
case esp:nvs_get_binary(atomvm, wlan_enabled) of
166-
undefined ->
167-
start_network();
168-
<<"always">> ->
169-
start_network();
170-
<<"never">> ->
171-
not_started
172-
end.
173-
174-
start_network() ->
175-
io:format("Starting network...~n"),
176-
{WaitFunc, Creds} = get_net_config(),
177-
case network:WaitFunc(Creds) of
178-
ok ->
179-
io:format("WLAN AP ready. Waiting connections.~n"),
180-
Event = #{
181-
event => wlan_ap_started
182-
},
183-
avm_pubsub:pub(default_pubsub, [system, network, wlan, connected], Event),
184-
maybe_start_web_server(),
185-
maybe_start_console(),
186-
started;
187-
{ok, {Address, Netmask, Gateway}} ->
188-
io:format(
189-
"Acquired IP address: ~s Netmask: ~s Gateway: ~s~n",
190-
[to_string(Address), to_string(Netmask), to_string(Gateway)]
191-
),
192-
Event = #{
193-
event => wlan_connected,
194-
address => Address,
195-
netmask => Netmask,
196-
gateway => Gateway
197-
},
198-
avm_pubsub:pub(default_pubsub, [system, network, wlan, connected], Event),
199-
maybe_start_web_server(),
200-
maybe_start_console(),
201-
started;
202-
Error ->
203-
io:format("An error occurred starting network: ~p~n", [Error]),
204-
not_started
205-
end.
206-
207-
to_string({{A, B, C, D}, Port}) ->
208-
io_lib:format("~p.~p.~p.~p:~p", [A, B, C, D, Port]);
209-
to_string({A, B, C, D}) ->
210-
io_lib:format("~p.~p.~p.~p", [A, B, C, D]).
211-
212-
%%
213-
%% LISP
214-
%%
215-
216-
maybe_start_console() ->
217-
case get_console_config() of
218-
{always, Port} ->
219-
listen(Port);
220-
_ ->
221-
io:format("ALISP console not enabled: skipping.~n"),
222-
not_started
223-
end.
224-
225-
listen(Port) ->
226-
case gen_tcp:listen(Port, []) of
227-
{ok, ListenSocket} ->
228-
io:format("ALISP console listening on port ~p~n", [Port]),
229-
spawn(fun() -> accept(ListenSocket) end),
230-
started;
231-
Error ->
232-
io:format("An error occurred listening: ~p~n", [Error]),
233-
{error, Error}
234-
end.
235-
236-
get_console_config() ->
237-
Enable =
238-
case esp:nvs_get_binary(atomvm, console_enable) of
239-
undefined ->
240-
always;
241-
<<"always">> ->
242-
always;
243-
<<"never">> ->
244-
never
245-
end,
246-
Port =
247-
case esp:nvs_get_binary(atomvm, console_port) of
248-
undefined ->
249-
?DEFAULT_CONSOLE_PORT;
250-
PortBinary ->
251-
try erlang:binary_to_integer(PortBinary) of
252-
PortInt -> PortInt
253-
catch
254-
Error ->
255-
io:format("Unable to read ALISP console port: ~p.~n", [Error]),
256-
?DEFAULT_CONSOLE_PORT
257-
end
258-
end,
259-
{Enable, Port}.
260-
261-
accept(ListenSocket) ->
262-
io:format("Waiting to accept shell connection...~n"),
263-
case gen_tcp:accept(ListenSocket) of
264-
{ok, Socket} ->
265-
spawn_opt(?MODULE, start_repl, [self()], [link]),
266-
io:format("Accepted shell connection. local: ~s peer: ~s~n", [
267-
local_address(Socket), peer_address(Socket)
268-
]),
269-
spawn(fun() -> accept(ListenSocket) end),
270-
loop(#nc_state{socket = Socket});
271-
Error ->
272-
io:format("An error occurred accepting connection: ~p~n", [Error])
273-
end.
274-
275-
loop(State) ->
276-
receive
277-
{tcp_closed, _Socket} ->
278-
io:format("Connection closed.~n"),
279-
erlang:exit(connection_closed);
280-
{tcp, _Socket, <<255, 244, 255, 253, 6>>} ->
281-
io:format("Break.~n"),
282-
gen_tcp:close(State#nc_state.socket),
283-
erlang:exit(break);
284-
{tcp, _Socket, Packet} ->
285-
Reply = {io_reply, State#nc_state.pending_ref, Packet},
286-
State#nc_state.pending_pid ! Reply,
287-
loop(State#nc_state{pending_pid = undefined, pending_ref = undefined});
288-
{io_request, FPid, FRef, Request} ->
289-
{ok, NewState} = io_request(Request, FPid, FRef, State),
290-
loop(NewState)
291-
end.
292-
293-
local_address(Socket) ->
294-
{ok, SockName} = inet:sockname(Socket),
295-
to_string(SockName).
296-
297-
peer_address(Socket) ->
298-
{ok, Peername} = inet:peername(Socket),
299-
to_string(Peername).
300-
301-
start_repl(SocketIOLeader) ->
302-
erlang:group_leader(SocketIOLeader, self()),
303-
arepl:start().
304-
305-
io_request({get_line, unicode, Data}, FPid, FRef, State) ->
306-
gen_tcp:send(State#nc_state.socket, Data),
307-
{ok, State#nc_state{pending_pid = FPid, pending_ref = FRef}};
308-
io_request({put_chars, unicode, Data}, FPid, FRef, State) ->
309-
gen_tcp:send(State#nc_state.socket, Data),
310-
FPid ! {io_reply, FRef, ok},
311-
{ok, State}.
312-
313-
%%
314-
%% Web Server
315-
%%
316-
317-
maybe_start_web_server() ->
318-
case get_web_server_config() of
319-
{always, Port} ->
320-
Router = [
321-
{"*", ?MODULE, []}
322-
],
323-
http_server:start_server(Port, Router),
324-
io:format("Web server listening on port ~p~n", [Port]),
325-
started;
326-
_ ->
327-
io:format("Web server not enabled: skipping.~n"),
328-
not_started
329-
end.
330-
331-
get_web_server_config() ->
332-
Enable =
333-
case esp:nvs_get_binary(atomvm, web_server_enable) of
334-
undefined ->
335-
always;
336-
<<"always">> ->
337-
always;
338-
<<"never">> ->
339-
never
340-
end,
341-
Port =
342-
case esp:nvs_get_binary(atomvm, web_server_port) of
343-
undefined ->
344-
?DEFAULT_WEB_SERVER_PORT;
345-
PortBinary ->
346-
try erlang:binary_to_integer(PortBinary) of
347-
PortInt -> PortInt
348-
catch
349-
Error ->
350-
io:format("Unable to read web server port: ~p.~n", [Error]),
351-
?DEFAULT_WEB_SERVER_PORT
352-
end
353-
end,
354-
{Enable, Port}.
355-
356-
handle_req("GET", [], Conn) ->
357-
Body =
358-
<<
359-
"<html>\n"
360-
" <body>\n"
361-
" <h1>Configuration</h1>\n"
362-
" <form method=\"post\">\n"
363-
" <p>SSID: <input type=\"text\" name=\"ssid\"></p>\n"
364-
" <p>Pass: <input type=\"text\" name=\"pass\"></p>\n"
365-
" <input type=\"submit\" value=\"Submit\">\n"
366-
" </form>\n"
367-
" </body>\n"
368-
"</html>"
369-
>>,
370-
http_server:reply(200, Body, Conn);
371-
handle_req("POST", [], Conn) ->
372-
ParamsBody = proplists:get_value(body_chunk, Conn),
373-
Params = http_server:parse_query_string(ParamsBody),
374-
375-
SSID = proplists:get_value("ssid", Params),
376-
Pass = proplists:get_value("pass", Params),
377-
save_net_config(SSID, Pass),
378-
379-
Body =
380-
<<
381-
"<html>\n"
382-
" <body>\n"
383-
" <h1>Configuration</h1>\n"
384-
" <p>Configured.</p>\n"
385-
" </body>\n"
386-
"</html>"
387-
>>,
388-
http_server:reply(200, Body, Conn);
389-
handle_req(Method, Path, Conn) ->
390-
erlang:display(Conn),
391-
erlang:display({Method, Path}),
392-
Body = <<"<html><body><h1>Not Found</h1></body></html>">>,
393-
http_server:reply(404, Body, Conn).
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#
2+
# This file is part of AtomVM.
3+
#
4+
# Copyright 2023 Davide Bettio <[email protected]>
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
#
20+
21+
project(esp32devmode)
22+
23+
include(BuildErlang)
24+
25+
pack_archive(esp32devmode esp32devmode)

0 commit comments

Comments
 (0)