diff --git a/src/c3card.erl b/src/c3card.erl index eee4afd..6fe2631 100644 --- a/src/c3card.erl +++ b/src/c3card.erl @@ -26,54 +26,26 @@ start() -> {ok, _} = c3card_app:start(normal, []), ?LOG_NOTICE("entering loop..."), - loop(#{sleep_ms => 50, screen => c3card_screen:default_screen()}). + loop(#{sleep_ms => 50}). %% Internal functions %% @hidden loop(State) -> - #{sleep_ms := SleepMs, screen := CurrentScreen} = State, - _ = timer:sleep(SleepMs), - {ok, Readings} = c3card_sensor:read_sensors(), + #{sleep_ms := SleepMs} = State, {ok, Buttons} = c3card_buttons:button_status(), - CardInfo = #{readings => Readings, - platform => esp32c3, - screen => CurrentScreen, - system_info => c3card_system:info(), - control => c3card_comm:get_port(), - buttons => Buttons}, - NextScreen = btn_to_screen(Buttons, CurrentScreen), - spawn(fun() -> handle_buttons(Buttons, NextScreen) end), - case CurrentScreen == NextScreen of - false -> c3card_screen:clear(); - true -> ok - end, - ok = c3card_screen:render_screen(NextScreen, CardInfo), - spawn(fun() -> maybe_send_info(CardInfo) end), - loop(State#{screen => NextScreen}). - -%% @hidden -btn_to_screen(#{1 := low}, CurrentScreen) -> - c3card_screen:switch_screen(CurrentScreen); -btn_to_screen(_Buttons, CurrentScreen) -> - CurrentScreen. + handle_buttons(Buttons), + _ = timer:sleep(SleepMs), + loop(State). %% @hidden -handle_buttons(#{1 := low}, _Screen) -> +handle_buttons(#{1 := low}) -> c3card_neopixel:toggle_led(0, 350), - c3card_neopixel:clear_all(); -handle_buttons(_Buttons, _Screen) -> + c3card_neopixel:clear_all(), + c3card_screen:next_screen(); +handle_buttons(#{4 := low}) -> + c3card_gateway:reconnect(); +handle_buttons(#{2 := low}) -> + c3card_codebeam:request_candy(); +handle_buttons(_Buttons) -> ok. - -%% @hidden -maybe_send_info(CardInfo) -> - case c3card_data:send_data(CardInfo) of - ok -> - c3card_neopixel:toggle_led(2, 200); - {error, offline} -> - c3card_neopixel:toggle_led(2, 50); - Error -> - c3card_neopixel:toggle_led(2, 350), - ?LOG_ERROR("error sending data: ~p", [Error]) - end, - c3card_neopixel:clear_all(). diff --git a/src/c3card_config.erl b/src/c3card_config.erl index 9aa5afe..11a16f0 100644 --- a/src/c3card_config.erl +++ b/src/c3card_config.erl @@ -21,7 +21,7 @@ | {c3card_neopixel, c3card_neopixel:config()} | {c3card_screen, c3card_screen:config()} | {c3card_comm, c3card_comm:config()} - | {c3card_data, c3card_data:config()} + | {c3card_gateway, c3card_gateway:config()} | {c3card_sensor, c3card_sensor:config()} | {c3card_wifi, c3card_wifi:config()}. %% `c3card' configuration option @@ -66,12 +66,14 @@ default_config() -> {c3card_buttons, []}, {c3card_neopixel, []}, {c3card_screen, []}, + {c3card_codebeam, []}, + {c3card_status, []}, {c3card_comm, [{handler, ?DEFAULT_GW_HANDLER}, - {backend, ?DEFAULT_INET_BACKEND}, - {gateway, ?DEFAULT_GW_HOST}, - {port, ?DEFAULT_GW_COMM_PORT}]}, - {c3card_data, [{gateway, ?DEFAULT_GW_HOST}, - {port, ?DEFAULT_GW_DATA_PORT}]}, + {backend, ?DEFAULT_INET_BACKEND}, + {gateway, ?DEFAULT_GW_HOST}, + {port, ?DEFAULT_GW_COMM_PORT}]}, + {c3card_gateway, [{gateway, ?DEFAULT_GW_HOST}, + {port, ?DEFAULT_GW_DATA_PORT}]}, {c3card_sensor, [{sensors, ?DEFAULT_SENSORS}]}, {c3card_wifi, [{ssid, ?DEFAULT_STA_SSID}, {psk, ?DEFAULT_STA_PSK}, diff --git a/src/c3card_data.erl b/src/c3card_gateway.erl similarity index 55% rename from src/c3card_data.erl rename to src/c3card_gateway.erl index a8c8e24..5a68713 100644 --- a/src/c3card_data.erl +++ b/src/c3card_gateway.erl @@ -1,29 +1,30 @@ %%%------------------------------------------------------------------- -%% @doc Gateway TCP data socket public API. +%% @doc Gateway TCP communications public API. %% %% Provides an active TCP socket connected to the gateway for sending %% any Erlang term over the wireless interface. %% @end %%%------------------------------------------------------------------- --module(c3card_data). +-module(c3card_gateway). -include_lib("kernel/include/logger.hrl"). -behaviour(gen_server). --export([send_data/1, - start_link/1]). +-export([reconnect/0, + send_data/1, + start_link/1]). -export([init/1, - handle_call/3, - handle_cast/2, - handle_info/2]). + handle_call/3, + handle_cast/2, + handle_info/2]). -define(SERVER, ?MODULE). -type data_option() :: - {gateway, inet:ip4_address()} + {gateway, inet:ip4_address()} | {port, non_neg_integer()}. %% Gateway configuration option @@ -34,6 +35,11 @@ %% API +%% @doc Reconnect to the gateway +-spec reconnect() -> ok | offline. +reconnect() -> + gen_server:call(?SERVER, reconnect). + %% @doc Send any Erlang term to the gateway -spec send_data(Data :: term()) -> ok | {error, Reason :: term()}. send_data(Data) -> @@ -50,20 +56,21 @@ start_link(Config) -> init(Config) -> Host = proplists:get_value(gateway, Config), Port = proplists:get_value(port, Config), - case gen_tcp:connect(Host, Port, []) of - {ok, Socket} -> - {ok, Socket}; - Error -> - ?LOG_WARNING("unable to connect to gateway: ~p", [Error]), - {ok, offline} - end. + ?LOG_NOTICE("starting gateway socket"), + {ok, #{socket => try_connect(Host, Port), + host => Host, + port => Port}}. %% @private -handle_call({send_data, _Data}, _From, offline) -> - {reply, {error, offline}, offline}; -handle_call({send_data, Data}, _From, Socket) -> +handle_call(reconnect, _From, State) -> + #{host := Host, port := Port} = State, + Socket = try_connect(Host, Port), + {reply, Socket, State#{socket => Socket}}; +handle_call({send_data, _Data}, _From, #{socket := offline} = State) -> + {reply, {error, offline}, State}; +handle_call({send_data, Data}, _From, #{socket := Socket} = State) -> Payload = erlang:term_to_binary(Data), - {reply, gen_tcp:send(Socket, Payload), Socket}; + {reply, gen_tcp:send(Socket, Payload), State}; handle_call(_Message, _From, State) -> {reply, ok, State}. @@ -72,11 +79,20 @@ handle_cast(_Message, State) -> {noreply, State}. %% @private -handle_info({tcp_closed, Socket}, Socket) -> +handle_info({tcp_closed, _Socket}, _State) -> {stop, tcp_closed}; -handle_info({tcp_error, Socket, Reason}, Socket) -> +handle_info({tcp_error, _Socket, Reason}, _State) -> {stop, {tcp_error, Reason}}; -handle_info({tcp, Socket, _Packet}, Socket) -> - {noreply, Socket}; +handle_info({tcp, _Socket, _Packet}, State) -> + {noreply, State}; handle_info(_Message, State) -> {noreply, State}. + +try_connect(Host, Port) -> + case gen_tcp:connect(Host, Port, []) of + {ok, Socket} -> + Socket; + Error -> + ?LOG_WARNING("unable to connect to gateway: ~p", [Error]), + offline + end. diff --git a/src/c3card_sup.erl b/src/c3card_sup.erl index 1b34d4e..037b14e 100644 --- a/src/c3card_sup.erl +++ b/src/c3card_sup.erl @@ -37,14 +37,16 @@ init([]) -> {ok, I2CBus} = i2c_bus:start_link(I2CBusConfig), ChildSpecs = - [ - worker(c3card_buttons, Config, []), - worker(c3card_neopixel, Config, []), - worker(c3card_screen, Config, [{i2c_bus, I2CBus}]), - worker(c3card_data, Config, []), - worker(c3card_comm, Config, []), - worker(c3card_sensor, Config, [{i2c_bus, I2CBus}]) - ], + [ + worker(c3card_sensor, Config, [{i2c_bus, I2CBus}]), + worker(c3card_buttons, Config, []), + worker(c3card_neopixel, Config, []), + worker(c3card_screen, Config, [{i2c_bus, I2CBus}]), + worker(c3card_gateway, Config, []), + worker(c3card_comm, Config, []), + worker(c3card_codebeam, Config, []), + worker(c3card_status, Config, []) + ], SupFlags = {one_for_one, ?INTENSITY, ?PERIOD}, {ok, {SupFlags, ChildSpecs}}.