diff --git a/components/wifi/esp8266-driver/ESP8266/ESP8266.cpp b/components/wifi/esp8266-driver/ESP8266/ESP8266.cpp index 5c2e911afb..018fda7410 100644 --- a/components/wifi/esp8266-driver/ESP8266/ESP8266.cpp +++ b/components/wifi/esp8266-driver/ESP8266/ESP8266.cpp @@ -75,6 +75,7 @@ ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts) _parser.oob("ALREADY CONNECTED", callback(this, &ESP8266::_oob_conn_already)); _parser.oob("ERROR", callback(this, &ESP8266::_oob_err)); _parser.oob("ready", callback(this, &ESP8266::_oob_ready)); + _parser.oob("+CWLAP:", callback(this, &ESP8266::_oob_scan_results)); // Don't expect to find anything about the watchdog reset in official documentation //https://techtutorialsx.com/2017/01/21/esp8266-watchdog-functions/ _parser.oob("wdt reset", callback(this, &ESP8266::_oob_watchdog_reset)); @@ -444,29 +445,28 @@ int8_t ESP8266::rssi() return rssi; } -int ESP8266::scan(WiFiAccessPoint *res, unsigned limit) +int ESP8266::scan(WiFiAccessPoint *res, unsigned limit, scan_mode mode, unsigned t_max, unsigned t_min) { - unsigned cnt = 0; - nsapi_wifi_ap_t ap; - _smutex.lock(); - set_timeout(ESP8266_CONNECT_TIMEOUT); - if (!_parser.send("AT+CWLAP")) { - _smutex.unlock(); - return NSAPI_ERROR_DEVICE_ERROR; - } + // Default timeout plus time spend scanning each channel + set_timeout(ESP8266_MISC_TIMEOUT + 13 * (t_max ? t_max : ESP8266_SCAN_TIME_MAX_DEFAULT)); - while (_recv_ap(&ap)) { - if (cnt < limit) { - res[cnt] = WiFiAccessPoint(ap); - } + _scan_r.res = res; + _scan_r.limit = limit; + _scan_r.cnt = 0; - cnt++; - if (limit != 0 && cnt >= limit) { - break; + if (!(_parser.send("AT+CWLAP=,,,%u,%u,%u", (mode == SCANMODE_ACTIVE ? 0 : 1), t_min, t_max) && _parser.recv("OK\n"))) { + tr_warning("scan(): AP info parsing aborted"); + // Lets be happy about partial success and not return NSAPI_ERROR_DEVICE_ERROR + if (!_scan_r.cnt) { + _scan_r.cnt = NSAPI_ERROR_DEVICE_ERROR; } } + + int cnt = _scan_r.cnt; + _scan_r.res = NULL; + set_timeout(); _smutex.unlock(); @@ -966,38 +966,42 @@ void ESP8266::attach(Callback status_cb) bool ESP8266::_recv_ap(nsapi_wifi_ap_t *ap) { - int sec; + int sec = NSAPI_SECURITY_UNKNOWN; int dummy; - bool ret; + int ret; if (FW_AT_LEAST_VERSION(_at_v.major, _at_v.minor, _at_v.patch, 0, ESP8266_AT_VERSION_WIFI_SCAN_CHANGE)) { - ret = _parser.recv("+CWLAP:(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu,%d,%d,%d,%d,%d,%d)\n", - &sec, - ap->ssid, - &ap->rssi, - &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5], - &ap->channel, - &dummy, - &dummy, - &dummy, - &dummy, - &dummy, - &dummy); + ret = _parser.scanf("(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu,%d,%d,%d,%d,%d,%d)\n", + &sec, + ap->ssid, + &ap->rssi, + &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5], + &ap->channel, + &dummy, + &dummy, + &dummy, + &dummy, + &dummy, + &dummy); } else { - ret = _parser.recv("+CWLAP:(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu,%d,%d)\n", - &sec, - ap->ssid, - &ap->rssi, - &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5], - &ap->channel, - &dummy, - &dummy); + ret = _parser.scanf("(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu,%d,%d)\n", + &sec, + ap->ssid, + &ap->rssi, + &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5], + &ap->channel, + &dummy, + &dummy); + } + if (ret < 0) { + _parser.abort(); + tr_warning("_recv_ap(): AP info missing"); } ap->security = sec < 5 ? (nsapi_security_t)sec : NSAPI_SECURITY_UNKNOWN; - return ret; + return ret < 0 ? false : true; } void ESP8266::_oob_watchdog_reset() @@ -1063,6 +1067,19 @@ void ESP8266::_oob_tcp_data_hdlr() _sock_i[_sock_active_id].tcp_data_rcvd = len; } +void ESP8266::_oob_scan_results() +{ + nsapi_wifi_ap_t ap; + + if (_recv_ap(&ap)) { + if (_scan_r.res && _scan_r.cnt < _scan_r.limit) { + _scan_r.res[_scan_r.cnt] = WiFiAccessPoint(ap); + } + + _scan_r.cnt++; + } +} + void ESP8266::_oob_connect_err() { _fail = false; diff --git a/components/wifi/esp8266-driver/ESP8266/ESP8266.h b/components/wifi/esp8266-driver/ESP8266/ESP8266.h index 542d7c100a..adb9167204 100644 --- a/components/wifi/esp8266-driver/ESP8266/ESP8266.h +++ b/components/wifi/esp8266-driver/ESP8266/ESP8266.h @@ -44,6 +44,11 @@ #define ESP8266_MISC_TIMEOUT 2000 #endif +#define ESP8266_SCAN_TIME_MIN 0 // [ms] +#define ESP8266_SCAN_TIME_MAX 1500 // [ms] +#define ESP8266_SCAN_TIME_MIN_DEFAULT 120 // [ms] +#define ESP8266_SCAN_TIME_MAX_DEFAULT 360 // [ms] + // Firmware version #define ESP8266_SDK_VERSION 2000000 #define ESP8266_SDK_VERSION_MAJOR ESP8266_SDK_VERSION/1000000 @@ -193,14 +198,23 @@ public: */ int8_t rssi(); + /** Scan mode + */ + enum scan_mode { + SCANMODE_ACTIVE = 0, /*!< active mode */ + SCANMODE_PASSIVE = 1 /*!< passive mode */ + }; + /** Scan for available networks * * @param ap Pointer to allocated array to store discovered AP * @param limit Size of allocated @a res array, or 0 to only count available AP + * @param t_max Maximum scan time per channel + * @param t_min Minimum scan time per channel in active mode, can be omitted in passive mode * @return Number of entries in @a res, or if @a count was 0 number of available networks, negative on error * see @a nsapi_error */ - int scan(WiFiAccessPoint *res, unsigned limit); + int scan(WiFiAccessPoint *res, unsigned limit, scan_mode mode, unsigned t_max, unsigned t_min); /**Perform a dns query * @@ -447,6 +461,7 @@ private: void _oob_busy(); void _oob_tcp_data_hdlr(); void _oob_ready(); + void _oob_scan_results(); // OOB state variables int _connect_error; @@ -475,6 +490,14 @@ private: }; struct _sock_info _sock_i[SOCKET_COUNT]; + // Scan results + struct _scan_results { + WiFiAccessPoint *res; + unsigned limit; + unsigned cnt; + }; + struct _scan_results _scan_r; + // Connection state reporting nsapi_connection_status_t _conn_status; mbed::Callback _conn_stat_cb; // ESP8266Interface registered diff --git a/components/wifi/esp8266-driver/ESP8266Interface.cpp b/components/wifi/esp8266-driver/ESP8266Interface.cpp index be6ae90af7..c93f433352 100644 --- a/components/wifi/esp8266-driver/ESP8266Interface.cpp +++ b/components/wifi/esp8266-driver/ESP8266Interface.cpp @@ -371,14 +371,25 @@ int8_t ESP8266Interface::get_rssi() int ESP8266Interface::scan(WiFiAccessPoint *res, unsigned count) { - nsapi_error_t status; + return scan(res, count, SCANMODE_ACTIVE, 0, 0); +} - status = _init(); +int ESP8266Interface::scan(WiFiAccessPoint *res, unsigned count, scan_mode mode, unsigned t_max, unsigned t_min) +{ + if (t_max > ESP8266_SCAN_TIME_MAX) { + return NSAPI_ERROR_PARAMETER; + } + if (mode == SCANMODE_ACTIVE && t_min > t_max) { + return NSAPI_ERROR_PARAMETER; + } + + nsapi_error_t status = _init(); if (status != NSAPI_ERROR_OK) { return status; } - return _esp.scan(res, count); + return _esp.scan(res, count, (mode == SCANMODE_ACTIVE ? ESP8266::SCANMODE_ACTIVE : ESP8266::SCANMODE_PASSIVE), + t_min, t_max); } bool ESP8266Interface::_get_firmware_ok() diff --git a/components/wifi/esp8266-driver/ESP8266Interface.h b/components/wifi/esp8266-driver/ESP8266Interface.h index bb2d888752..7466b9d87d 100644 --- a/components/wifi/esp8266-driver/ESP8266Interface.h +++ b/components/wifi/esp8266-driver/ESP8266Interface.h @@ -157,18 +157,38 @@ public: */ virtual int8_t get_rssi(); + /** Scan mode + */ + enum scan_mode { + SCANMODE_ACTIVE, /*!< active mode */ + SCANMODE_PASSIVE /*!< passive mode */ + }; + /** Scan for available networks * * This function will block. * - * @param ap Pointer to allocated array to store discovered AP - * @param count Size of allocated @a res array, or 0 to only count available AP - * @param timeout Timeout in milliseconds; 0 for no timeout (Default: 0) - * @return Number of entries in @a, or if @a count was 0 number of available networks, negative on error - * see @a nsapi_error + * @param ap Pointer to allocated array to store discovered AP + * @param count Size of allocated @a res array, or 0 to only count available AP + * @return Number of entries in @a, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error */ virtual int scan(WiFiAccessPoint *res, unsigned count); + /** Scan for available networks + * + * This function will block. + * + * @param ap Pointer to allocated array to store discovered AP + * @param count Size of allocated @a res array, or 0 to only count available AP + * @param t_max Scan time for each channel - 0-1500ms. If 0 - uses default value + * @param t_min Minimum for each channel in active mode - 0-1500ms. If 0 - uses default value. Omit in passive mode + * @return Number of entries in @a, or if @a count was 0 number of available networks, negative on error + * see @a nsapi_error + */ + virtual int scan(WiFiAccessPoint *res, unsigned count, scan_mode mode = SCANMODE_PASSIVE, + unsigned t_max = 0, unsigned t_min = 0); + /** Translates a hostname to an IP address with specific version * * The hostname may be either a domain name or an IP address. If the