From 4fead4a4be55dfd8317e7a1726d9bfd144aa98b8 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Tue, 4 Oct 2016 17:40:06 +1100 Subject: [PATCH] More WiFi module work. Station connect/disconnect/scan, plus event handling via wifi.on(). --- components/modules/wifi.c | 228 ++++++++++++++++++++++++++++++- components/modules/wifi_common.c | 64 +++++++++ components/modules/wifi_common.h | 15 ++ components/modules/wifi_sta.c | 159 ++++++++++++++++++++- 4 files changed, 463 insertions(+), 3 deletions(-) create mode 100644 components/modules/wifi_common.c diff --git a/components/modules/wifi.c b/components/modules/wifi.c index 355c0660..4cf013af 100644 --- a/components/modules/wifi.c +++ b/components/modules/wifi.c @@ -35,10 +35,220 @@ #include "lextra.h" #include "esp_wifi.h" #include "wifi_common.h" +#include "nodemcu_esp_event.h" #include +typedef void (*fill_cb_arg_fn) (lua_State *L, const system_event_t *evt); +typedef struct +{ + const char *name; + system_event_id_t event_id; + fill_cb_arg_fn fill_cb_arg; +} event_desc_t; -static int wifi_setmode (lua_State *L) + +// Forward declarations +static void on_event (const system_event_t *evt); + +static void sta_conn (lua_State *L, const system_event_t *evt); +static void sta_disconn (lua_State *L, const system_event_t *evt); +static void sta_authmode (lua_State *L, const system_event_t *evt); +static void sta_got_ip (lua_State *L, const system_event_t *evt); +static void ap_staconn (lua_State *L, const system_event_t *evt); +static void ap_stadisconn (lua_State *L, const system_event_t *evt); +static void ap_probe_req (lua_State *L, const system_event_t *evt); + +static void empty_arg (lua_State *L, const system_event_t *evt) {} + +static const event_desc_t events[] = +{ + { "sta_start", SYSTEM_EVENT_STA_START, empty_arg }, + { "sta_stop", SYSTEM_EVENT_STA_STOP, empty_arg }, + { "sta_connected", SYSTEM_EVENT_STA_CONNECTED, sta_conn }, + { "sta_disconnected", SYSTEM_EVENT_STA_DISCONNECTED, sta_disconn }, + { "sta_authmode_changed", SYSTEM_EVENT_STA_AUTHMODE_CHANGE, sta_authmode }, + { "sta_got_ip", SYSTEM_EVENT_STA_GOT_IP, sta_got_ip }, + + { "ap_start", SYSTEM_EVENT_AP_START, empty_arg }, + { "ap_stop", SYSTEM_EVENT_AP_STOP, empty_arg }, + { "ap_sta_connected", SYSTEM_EVENT_AP_STACONNECTED, ap_staconn }, + { "ap_sta_disconnected", SYSTEM_EVENT_AP_STADISCONNECTED, ap_stadisconn }, + { "ap_probe_req", SYSTEM_EVENT_AP_PROBEREQRECVED, ap_probe_req } +}; + +#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) +static int event_cb[ARRAY_LEN(events)]; + +NODEMCU_ESP_EVENT(SYSTEM_EVENT_STA_START, on_event); +NODEMCU_ESP_EVENT(SYSTEM_EVENT_STA_STOP, on_event); +NODEMCU_ESP_EVENT(SYSTEM_EVENT_STA_CONNECTED, on_event); +NODEMCU_ESP_EVENT(SYSTEM_EVENT_STA_DISCONNECTED, on_event); +NODEMCU_ESP_EVENT(SYSTEM_EVENT_STA_AUTHMODE_CHANGE, on_event); +NODEMCU_ESP_EVENT(SYSTEM_EVENT_STA_GOT_IP, on_event); + +NODEMCU_ESP_EVENT(SYSTEM_EVENT_AP_START, on_event); +NODEMCU_ESP_EVENT(SYSTEM_EVENT_AP_STOP, on_event); +NODEMCU_ESP_EVENT(SYSTEM_EVENT_AP_STACONNECTED, on_event); +NODEMCU_ESP_EVENT(SYSTEM_EVENT_AP_STADISCONNECTED, on_event); +NODEMCU_ESP_EVENT(SYSTEM_EVENT_AP_PROBEREQRECVED, on_event); + + +static int wifi_getmode (lua_State *L) +{ + wifi_mode_t mode; + esp_err_t err = esp_wifi_get_mode (&mode); + if (err != ESP_OK) + return luaL_error (L, "failed to get mode, code %d", err); + lua_pushinteger (L, mode); + return 1; +} + + +static int event_idx_by_name (const char *name) +{ + for (unsigned i = 0; i < ARRAY_LEN(events); ++i) + if (strcmp (events[i].name, name) == 0) + return i; + return -1; +} + + +static int event_idx_by_id (system_event_id_t id) +{ + for (unsigned i = 0; i < ARRAY_LEN(events); ++i) + if (events[i].event_id == id) + return i; + return -1; +} + + +static void sta_conn (lua_State *L, const system_event_t *evt) +{ + lua_pushlstring (L, + (const char *)evt->event_info.connected.ssid, + evt->event_info.connected.ssid_len); + lua_setfield (L, -2, "ssid"); + + char bssid_str[MAC_STR_SZ]; + macstr (bssid_str, evt->event_info.connected.bssid); + lua_pushstring (L, bssid_str); + lua_setfield (L, -2, "bssid"); + + lua_pushinteger (L, evt->event_info.connected.channel); + lua_setfield (L, -2, "channel"); + + lua_pushinteger (L, evt->event_info.connected.authmode); + lua_setfield (L, -2, "auth"); +} + +static void sta_disconn (lua_State *L, const system_event_t *evt) +{ + lua_pushlstring (L, + (const char *)evt->event_info.disconnected.ssid, + evt->event_info.disconnected.ssid_len); + lua_setfield (L, -2, "ssid"); + + char bssid_str[MAC_STR_SZ]; + macstr (bssid_str, evt->event_info.disconnected.bssid); + lua_pushstring (L, bssid_str); + lua_setfield (L, -2, "bssid"); + + lua_pushinteger (L, evt->event_info.disconnected.reason); + lua_setfield (L, -2, "reason"); +} + +static void sta_authmode (lua_State *L, const system_event_t *evt) +{ + lua_pushinteger (L, evt->event_info.auth_change.old_mode); + lua_setfield (L, -2, "old_mode"); + lua_pushinteger (L, evt->event_info.auth_change.new_mode); + lua_setfield (L, -2, "new_mode"); +} + +static void sta_got_ip (lua_State *L, const system_event_t *evt) +{ + char ipstr[IP_STR_SZ] = { 0 }; + ip4str (ipstr, &evt->event_info.got_ip.ip_info.ip); + lua_pushstring (L, ipstr); + lua_setfield (L, -2, "ip"); + + ip4str (ipstr, &evt->event_info.got_ip.ip_info.netmask); + lua_pushstring (L, ipstr); + lua_setfield (L, -2, "netmask"); + + ip4str (ipstr, &evt->event_info.got_ip.ip_info.gw); + lua_pushstring (L, ipstr); + lua_setfield (L, -2, "gw"); +} + +static void ap_staconn (lua_State *L, const system_event_t *evt) +{ + char mac[MAC_STR_SZ]; + macstr (mac, evt->event_info.sta_connected.mac); + lua_pushstring (L, mac); + lua_setfield (L, -2, "mac"); + + lua_pushinteger (L, evt->event_info.sta_connected.aid); + lua_setfield (L, -2, "id"); +} + +static void ap_stadisconn (lua_State *L, const system_event_t *evt) +{ + char mac[MAC_STR_SZ]; + macstr (mac, evt->event_info.sta_disconnected.mac); + lua_pushstring (L, mac); + lua_setfield (L, -2, "mac"); + + lua_pushinteger (L, evt->event_info.sta_disconnected.aid); + lua_setfield (L, -2, "id"); +} + +static void ap_probe_req (lua_State *L, const system_event_t *evt) +{ + char str[MAC_STR_SZ]; + macstr (str, evt->event_info.ap_probereqrecved.mac); + lua_pushstring (L, str); + lua_setfield (L, -2, "from"); + + lua_pushinteger (L, evt->event_info.ap_probereqrecved.rssi); + lua_setfield (L, -2, "rssi"); +} + +static void on_event (const system_event_t *evt) +{ + int idx = event_idx_by_id (evt->event_id); + if (idx < 0 || event_cb[idx] == LUA_NOREF) + return; + + lua_State *L = lua_getstate (); + lua_rawgeti (L, LUA_REGISTRYINDEX, event_cb[idx]); + lua_pushstring (L, events[idx].name); + lua_createtable (L, 0, 5); + events[idx].fill_cb_arg (L, evt); + lua_call (L, 2, 0); +} + + +static int wifi_eventmon (lua_State *L) +{ + const char *event_name = luaL_checkstring (L, 1); + if (!lua_isnoneornil (L, 2)) + luaL_checkanyfunction (L, 2); + lua_settop (L, 2); + + int idx = event_idx_by_name (event_name); + if (idx < 0) + return luaL_error (L, "unknown event: %s", event_name); + + luaL_unref (L, LUA_REGISTRYINDEX, event_cb[idx]); + event_cb[idx] = lua_isnoneornil (L, 2) ? + LUA_NOREF : luaL_ref (L, LUA_REGISTRYINDEX); + + return 0; +} + + +static int wifi_mode (lua_State *L) { int mode = luaL_checkinteger (L, 1); bool save = luaL_optbool (L, 2, DEFAULT_SAVE); @@ -66,8 +276,19 @@ static int wifi_start (lua_State *L) } +static int wifi_stop (lua_State *L) +{ + esp_err_t err = esp_wifi_stop (); + return (err == ESP_OK) ? + 0 : luaL_error (L, "failed to stop wifi, code %d", err); +} + + static int wifi_init (lua_State *L) { + for (unsigned i = 0; i < ARRAY_LEN(event_cb); ++i) + event_cb[i] = LUA_NOREF; + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); esp_err_t err = esp_wifi_init (&cfg); return (err == ESP_OK) ? @@ -80,8 +301,11 @@ extern const LUA_REG_TYPE wifi_ap_map[]; static const LUA_REG_TYPE wifi_map[] = { - { LSTRKEY( "setmode" ), LFUNCVAL( wifi_setmode ) }, + { LSTRKEY( "getmode" ), LFUNCVAL( wifi_getmode ) }, + { LSTRKEY( "on" ), LFUNCVAL( wifi_eventmon ) }, + { LSTRKEY( "mode" ), LFUNCVAL( wifi_mode ) }, { LSTRKEY( "start" ), LFUNCVAL( wifi_start ) }, + { LSTRKEY( "stop" ), LFUNCVAL( wifi_stop ) }, { LSTRKEY( "sta" ), LROVAL( wifi_sta_map ) }, { LSTRKEY( "ap" ), LROVAL( wifi_ap_map ) }, diff --git a/components/modules/wifi_common.c b/components/modules/wifi_common.c new file mode 100644 index 00000000..21c134e8 --- /dev/null +++ b/components/modules/wifi_common.c @@ -0,0 +1,64 @@ +/* + * Copyright 2016 Dius Computing Pty Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the copyright holders nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Johny Mattsson + */ +#include "wifi_common.h" +#include +#include "lwip/sockets.h" +#include "lwip/ip_addr.h" +#include "lwip/ip4_addr.h" + +void macstr (char *str, const uint8_t *mac) +{ + sprintf (str, "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + + +void ipstr (char *out, const ip_addr_t *ip) +{ + if (ip->type == IPADDR_TYPE_V4) + ip4str (out, &ip->u_addr.ip4); + else if (ip->type == IPADDR_TYPE_V6) + ip6str (out, &ip->u_addr.ip6); +} + + +void ip4str (char *out, const ip4_addr_t *ip) +{ + ip4addr_ntoa_r (ip, out, IP_STR_SZ); +} + + +void ip6str (char *out, const ip6_addr_t *ip) +{ + ip6addr_ntoa_r (ip, out, IP_STR_SZ); +} diff --git a/components/modules/wifi_common.h b/components/modules/wifi_common.h index 6aac7cb3..de87d600 100644 --- a/components/modules/wifi_common.h +++ b/components/modules/wifi_common.h @@ -33,6 +33,9 @@ #ifndef _NODEMCU_WIFI_H_ #define _NODEMCU_WIFI_H_ +#include +#include "tcpip_adapter.h" + // Shared sta/ap macros #define DEFAULT_SAVE false @@ -45,4 +48,16 @@ } while(0) +/* The mac/ip formatters should probably go into its own utility file... */ + +// String buffer for a formatted MAC/BSSID +#define MAC_STR_SZ (6*2+5+1) +void macstr (char *out, const uint8_t *mac); + +// String buffer for a formatted IPv4 or IPv6 address +#define IP_STR_SZ (8*4+7+1) +void ipstr (char *out, const ip_addr_t *ip); +void ip4str (char *out, const ip4_addr_t *ip); +void ip6str (char *out, const ip6_addr_t *ip); + #endif diff --git a/components/modules/wifi_sta.c b/components/modules/wifi_sta.c index b278b336..eb7801fb 100644 --- a/components/modules/wifi_sta.c +++ b/components/modules/wifi_sta.c @@ -33,11 +33,14 @@ #include "module.h" #include "lauxlib.h" #include "lextra.h" +#include "lmem.h" #include "nodemcu_esp_event.h" #include "wifi_common.h" #include "esp_wifi.h" #include +static int scan_cb_ref = LUA_NOREF; + static void do_connect (const system_event_t *evt) { @@ -94,15 +97,168 @@ static int wifi_sta_config (lua_State *L) return luaL_error (L, "invalid BSSID: %s", bssid); } + lua_getfield (L, 1, "auto"); + bool auto_conn = luaL_optbool (L, -1, true); + SET_SAVE_MODE(save); esp_err_t err = esp_wifi_set_config (WIFI_IF_STA, &cfg); + if (err != ESP_OK) + return luaL_error (L, "failed to set wifi config, code %d", err); + + if (auto_conn) + err = esp_wifi_connect (); + if (err != ESP_OK) + return luaL_error (L, "failed to begin connect, code %d", err); + + err = esp_wifi_set_auto_connect (auto_conn); return (err == ESP_OK) ? - 0 : luaL_error (L, "failed to set wifi config, code %d", err); + 0 : luaL_error (L, "failed to set wifi auto-connect, code %d", err); +} + + +static int wifi_sta_connect (lua_State *L) +{ + esp_err_t err = esp_wifi_connect (); + return (err == ESP_OK) ? 0 : luaL_error (L, "connect failed, code %d", err); +} + + +static int wifi_sta_disconnect (lua_State *L) +{ + esp_err_t err = esp_wifi_disconnect (); + return (err == ESP_OK) ? 0 : luaL_error(L, "disconnect failed, code %d", err); +} + + +static int wifi_sta_getconfig (lua_State *L) +{ + wifi_config_t cfg; + esp_err_t err = esp_wifi_get_config (WIFI_IF_STA, &cfg); + if (err != ESP_OK) + return luaL_error (L, "failed to get config, code %d", err); + + lua_createtable (L, 0, 3); + size_t ssid_len = strnlen (cfg.sta.ssid, sizeof (cfg.sta.ssid)); + lua_pushlstring (L, cfg.sta.ssid, ssid_len); + lua_setfield (L, -2, "ssid"); + + size_t pwd_len = strnlen (cfg.sta.password, sizeof (cfg.sta.password)); + lua_pushlstring (L, cfg.sta.password, pwd_len); + lua_setfield (L, -2, "pwd"); + + if (cfg.sta.bssid_set) + { + char bssid_str[MAC_STR_SZ]; + macstr (bssid_str, cfg.sta.bssid); + lua_pushstring (L, bssid_str); + lua_setfield (L, -2, "bssid"); + } + + bool auto_conn; + err = esp_wifi_get_auto_connect (&auto_conn); + if (err != ESP_OK) + return luaL_error (L, "failed to get auto-connect, code %d", err); + + lua_pushboolean (L, auto_conn); + lua_setfield (L, -2, "auto"); + + return 1; +} + + +static void on_scan_done (const system_event_t *evt) +{ + (void)evt; + + lua_State *L = lua_getstate (); + lua_rawgeti (L, LUA_REGISTRYINDEX, scan_cb_ref); + luaL_unref (L, LUA_REGISTRYINDEX, scan_cb_ref); + scan_cb_ref = LUA_NOREF; + int nargs = 1; + if (!lua_isnoneornil (L, -1)) + { + uint16_t num_ap = 0; + esp_err_t err = esp_wifi_get_ap_num (&num_ap); + wifi_ap_list_t *aps = luaM_malloc (L, num_ap * sizeof (wifi_ap_list_t)); + if ((err == ESP_OK) && (aps) && + (err = esp_wifi_get_ap_list (&num_ap, aps)) == ESP_OK) + { + lua_pushnil (L); // no error + + lua_createtable (L, num_ap, 0); // prepare array + ++nargs; + for (unsigned i = 0; i < num_ap; ++i) + { + lua_createtable (L, 0, 6); // prepare table for AP entry + + char bssid_str[MAC_STR_SZ]; + macstr (bssid_str, aps[i].bssid); + lua_pushstring (L, bssid_str); + lua_setfield (L, -2, "bssid"); + + size_t ssid_len = + strnlen ((const char *)aps[i].ssid, sizeof (aps[i].ssid)); + lua_pushlstring (L, (const char *)aps[i].ssid, ssid_len); + lua_setfield (L, -2, "ssid"); + + lua_pushinteger (L, aps[i].primary); + lua_setfield (L, -2, "channel"); + + lua_pushinteger (L, aps[i].rssi); + lua_setfield (L, -2, "rssi"); + + lua_pushinteger (L, aps[i].authmode); + lua_setfield (L, -2, "auth"); + + lua_pushstring (L, (aps[i].second == WIFI_SECOND_CHAN_NONE) ? + "ht20" : "ht40"); + lua_setfield (L, -2, "bandwidth"); + + lua_rawseti (L, -2, i + 1); // add table to array + } + } + else + lua_pushfstring (L, "failure on scan done"); + luaM_free (L, aps); + lua_call (L, nargs, 0); + } +} + + +static int wifi_sta_scan (lua_State *L) +{ + if (scan_cb_ref != LUA_NOREF) + return luaL_error (L, "scan already in progress"); + + luaL_checkanytable (L, 1); + + wifi_scan_config_t scan_cfg; + memset (&scan_cfg, 0, sizeof (scan_cfg)); + + // TODO: support ssid/bssid/channel/hidden features of wifi_scan_config_t + + luaL_checkanyfunction (L, 2); + lua_settop (L, 2); + scan_cb_ref = luaL_ref (L, LUA_REGISTRYINDEX); + + esp_err_t err = esp_wifi_scan_start (&scan_cfg, false); + if (err != ESP_OK) + { + luaL_unref (L, LUA_REGISTRYINDEX, scan_cb_ref); + scan_cb_ref = LUA_NOREF; + return luaL_error (L, "failed to start scan, code %d", err); + } + else + return 0; } const LUA_REG_TYPE wifi_sta_map[] = { { LSTRKEY( "config" ), LFUNCVAL( wifi_sta_config ) }, + { LSTRKEY( "connect" ), LFUNCVAL( wifi_sta_connect ) }, + { LSTRKEY( "disconnect" ), LFUNCVAL( wifi_sta_disconnect ) }, + { LSTRKEY( "getconfig" ), LFUNCVAL( wifi_sta_getconfig ) }, + { LSTRKEY( "scan" ), LFUNCVAL( wifi_sta_scan ) }, { LNILKEY, LNILVAL } }; @@ -111,3 +267,4 @@ const LUA_REG_TYPE wifi_sta_map[] = { // Currently no auto-connect, so do that in response to events NODEMCU_ESP_EVENT(SYSTEM_EVENT_STA_START, do_connect); NODEMCU_ESP_EVENT(SYSTEM_EVENT_STA_DISCONNECTED, do_connect); +NODEMCU_ESP_EVENT(SYSTEM_EVENT_SCAN_DONE, on_scan_done);