Merge pull request #9955 from VeijoPesonen/esp8266_wifi_scan_timeout

ESP8266: treats Wi-Fi scan results as out-of-band data; new API to adjusting Wi-Fi scan settings
pull/9904/head
Cruz Monrreal 2019-03-27 00:25:43 -05:00 committed by GitHub
commit ecfe0c83ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 119 additions and 48 deletions

View File

@ -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("ALREADY CONNECTED", callback(this, &ESP8266::_oob_conn_already));
_parser.oob("ERROR", callback(this, &ESP8266::_oob_err)); _parser.oob("ERROR", callback(this, &ESP8266::_oob_err));
_parser.oob("ready", callback(this, &ESP8266::_oob_ready)); _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 // Don't expect to find anything about the watchdog reset in official documentation
//https://techtutorialsx.com/2017/01/21/esp8266-watchdog-functions/ //https://techtutorialsx.com/2017/01/21/esp8266-watchdog-functions/
_parser.oob("wdt reset", callback(this, &ESP8266::_oob_watchdog_reset)); _parser.oob("wdt reset", callback(this, &ESP8266::_oob_watchdog_reset));
@ -444,29 +445,28 @@ int8_t ESP8266::rssi()
return 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(); _smutex.lock();
set_timeout(ESP8266_CONNECT_TIMEOUT);
if (!_parser.send("AT+CWLAP")) { // Default timeout plus time spend scanning each channel
_smutex.unlock(); set_timeout(ESP8266_MISC_TIMEOUT + 13 * (t_max ? t_max : ESP8266_SCAN_TIME_MAX_DEFAULT));
return NSAPI_ERROR_DEVICE_ERROR;
}
while (_recv_ap(&ap)) { _scan_r.res = res;
if (cnt < limit) { _scan_r.limit = limit;
res[cnt] = WiFiAccessPoint(ap); _scan_r.cnt = 0;
}
cnt++; if (!(_parser.send("AT+CWLAP=,,,%u,%u,%u", (mode == SCANMODE_ACTIVE ? 0 : 1), t_min, t_max) && _parser.recv("OK\n"))) {
if (limit != 0 && cnt >= limit) { tr_warning("scan(): AP info parsing aborted");
break; // 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(); set_timeout();
_smutex.unlock(); _smutex.unlock();
@ -966,38 +966,42 @@ void ESP8266::attach(Callback<void()> status_cb)
bool ESP8266::_recv_ap(nsapi_wifi_ap_t *ap) bool ESP8266::_recv_ap(nsapi_wifi_ap_t *ap)
{ {
int sec; int sec = NSAPI_SECURITY_UNKNOWN;
int dummy; 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)) { 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", ret = _parser.scanf("(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu,%d,%d,%d,%d,%d,%d)\n",
&sec, &sec,
ap->ssid, ap->ssid,
&ap->rssi, &ap->rssi,
&ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5], &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5],
&ap->channel, &ap->channel,
&dummy, &dummy,
&dummy, &dummy,
&dummy, &dummy,
&dummy, &dummy,
&dummy, &dummy,
&dummy); &dummy);
} else { } else {
ret = _parser.recv("+CWLAP:(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu,%d,%d)\n", ret = _parser.scanf("(%d,\"%32[^\"]\",%hhd,\"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\",%hhu,%d,%d)\n",
&sec, &sec,
ap->ssid, ap->ssid,
&ap->rssi, &ap->rssi,
&ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5], &ap->bssid[0], &ap->bssid[1], &ap->bssid[2], &ap->bssid[3], &ap->bssid[4], &ap->bssid[5],
&ap->channel, &ap->channel,
&dummy, &dummy,
&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; ap->security = sec < 5 ? (nsapi_security_t)sec : NSAPI_SECURITY_UNKNOWN;
return ret; return ret < 0 ? false : true;
} }
void ESP8266::_oob_watchdog_reset() void ESP8266::_oob_watchdog_reset()
@ -1063,6 +1067,19 @@ void ESP8266::_oob_tcp_data_hdlr()
_sock_i[_sock_active_id].tcp_data_rcvd = len; _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() void ESP8266::_oob_connect_err()
{ {
_fail = false; _fail = false;

View File

@ -44,6 +44,11 @@
#define ESP8266_MISC_TIMEOUT 2000 #define ESP8266_MISC_TIMEOUT 2000
#endif #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 // Firmware version
#define ESP8266_SDK_VERSION 2000000 #define ESP8266_SDK_VERSION 2000000
#define ESP8266_SDK_VERSION_MAJOR ESP8266_SDK_VERSION/1000000 #define ESP8266_SDK_VERSION_MAJOR ESP8266_SDK_VERSION/1000000
@ -193,14 +198,23 @@ public:
*/ */
int8_t rssi(); int8_t rssi();
/** Scan mode
*/
enum scan_mode {
SCANMODE_ACTIVE = 0, /*!< active mode */
SCANMODE_PASSIVE = 1 /*!< passive mode */
};
/** Scan for available networks /** Scan for available networks
* *
* @param ap Pointer to allocated array to store discovered AP * @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 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 * @return Number of entries in @a res, or if @a count was 0 number of available networks, negative on error
* see @a nsapi_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 /**Perform a dns query
* *
@ -447,6 +461,7 @@ private:
void _oob_busy(); void _oob_busy();
void _oob_tcp_data_hdlr(); void _oob_tcp_data_hdlr();
void _oob_ready(); void _oob_ready();
void _oob_scan_results();
// OOB state variables // OOB state variables
int _connect_error; int _connect_error;
@ -475,6 +490,14 @@ private:
}; };
struct _sock_info _sock_i[SOCKET_COUNT]; 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 // Connection state reporting
nsapi_connection_status_t _conn_status; nsapi_connection_status_t _conn_status;
mbed::Callback<void()> _conn_stat_cb; // ESP8266Interface registered mbed::Callback<void()> _conn_stat_cb; // ESP8266Interface registered

View File

@ -371,14 +371,25 @@ int8_t ESP8266Interface::get_rssi()
int ESP8266Interface::scan(WiFiAccessPoint *res, unsigned count) 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) { if (status != NSAPI_ERROR_OK) {
return status; 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() bool ESP8266Interface::_get_firmware_ok()

View File

@ -157,18 +157,38 @@ public:
*/ */
virtual int8_t get_rssi(); virtual int8_t get_rssi();
/** Scan mode
*/
enum scan_mode {
SCANMODE_ACTIVE, /*!< active mode */
SCANMODE_PASSIVE /*!< passive mode */
};
/** Scan for available networks /** Scan for available networks
* *
* This function will block. * This function will block.
* *
* @param ap Pointer to allocated array to store discovered AP * @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 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
* @return Number of entries in @a, or if @a count was 0 number of available networks, negative on error * see @a nsapi_error
* see @a nsapi_error
*/ */
virtual int scan(WiFiAccessPoint *res, unsigned count); 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 /** Translates a hostname to an IP address with specific version
* *
* The hostname may be either a domain name or an IP address. If the * The hostname may be either a domain name or an IP address. If the