diff --git a/examples/LR11x0/LR11x0_WiFi_Scan_Blocking/LR11x0_WiFi_Scan_Blocking.ino b/examples/LR11x0/LR11x0_WiFi_Scan_Blocking/LR11x0_WiFi_Scan_Blocking.ino new file mode 100644 index 00000000..b1643a33 --- /dev/null +++ b/examples/LR11x0/LR11x0_WiFi_Scan_Blocking/LR11x0_WiFi_Scan_Blocking.ino @@ -0,0 +1,112 @@ +/* + RadioLib LR11x0 WiFi scan Blocking Example + + This example performs a passive scan of WiFi networks. + The scan shows basic information about the networks, + such as the frequency, country code and SSID. + + Other modules from LR11x0 family can also be used. + + Using blocking scan is not recommended, as depending + on the scan settings, the program may be blocked + for several seconds! Instead, interrupt scan is recommended. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---wifi-scan + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[LR1110] Running WiFi scan ... ")); + + // scan all WiFi signals with default scan configuration + uint8_t count = 0; + int state = radio.wifiScan('*', &count); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + + // print the table header + Serial.print(F("[LR1110] Reading ")); + Serial.print(count); + Serial.println(F(" scan results:")); + Serial.println(F(" # | WiFi type\t| Frequency\t| MAC Address\t | Country\t| RSSI [dBm]\t| SSID")); + + // read all results one by one + // this result type contains the most information, including the SSID + LR11x0WifiResultExtended_t result; + for(int i = 0; i < count; i++) { + if(i < 10) { Serial.print(" "); } Serial.print(i); Serial.print(" | "); + state = radio.getWifiScanResult(&result, i); + if(state != RADIOLIB_ERR_NONE) { + Serial.print(F("Failed to read result, code ")); + Serial.println(state); + continue; + } + + // print the basic information + Serial.print(F("802.11")); Serial.print(result.type); Serial.print("\t| "); + Serial.print(result.channelFreq); Serial.print(" MHz\t| "); + + // print MAC address + for(int j = 0; j < 6; j++) { + if(result.mac[j] < 0x10) { Serial.print("0"); } + Serial.print(result.mac[j], HEX); + if(j < 5) { Serial.print(":"); } + } + Serial.print(" | "); + + // print the two-letter country code + String country = result.countryCode; + Serial.print(country); + Serial.print(" \t| "); + + // print the RSSI + Serial.print(result.rssi); + Serial.print("\t| "); + + // print the network SSID + Serial.println((char*)result.ssid); + + } + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + // wait for a second before scanning again + delay(1000); +} diff --git a/examples/LR11x0/LR11x0_WiFi_Scan_Interrupt/LR11x0_WiFi_Scan_Interrupt.ino b/examples/LR11x0/LR11x0_WiFi_Scan_Interrupt/LR11x0_WiFi_Scan_Interrupt.ino new file mode 100644 index 00000000..bf999ba1 --- /dev/null +++ b/examples/LR11x0/LR11x0_WiFi_Scan_Interrupt/LR11x0_WiFi_Scan_Interrupt.ino @@ -0,0 +1,150 @@ +/* + RadioLib LR11x0 WiFi scan Interrupt Example + + This example performs a passive scan of WiFi networks. + The scan shows basic information about the networks, + such as the frequency, country code and SSID. + + Other modules from LR11x0 family can also be used. + + Using blocking scan is not recommended, as depending + on the scan settings, the program may be blocked + for several seconds! Instead, interrupt scan is recommended. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---wifi-scan + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when WiFi scan is complete + radio.setIrqAction(setFlag); + + // scan all WiFi signals with default scan configuration + Serial.print(F("[LR1110] Starting passive WiFi scan ... ")); + state = radio.startWifiScan('*'); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } +} + +// flag to indicate that a scan was completed +volatile bool scanFlag = false; + +// this function is called when a scan is completed +// IMPORTANT: this function MUST be 'void' type +// and MUST NOT have any arguments! +#if defined(ESP8266) || defined(ESP32) + ICACHE_RAM_ATTR +#endif +void setFlag(void) { + // scan is complete, set the flag + scanFlag = true; +} + +void loop() { + // check if the flag is set + if(scanFlag) { + // reset flag + scanFlag = false; + + // get the number of scan results + uint8_t count = 0; + Serial.print(F("[LR1110] Reading WiFi scan results ... ")); + int state = radio.getWifiScanResultsCount(&count); + if(state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + + // print the table header + Serial.print(F("[LR1110] Reading ")); + Serial.print(count); + Serial.println(F(" scan results:")); + Serial.println(F(" # | WiFi type\t| Frequency\t| MAC Address\t | Country\t| RSSI [dBm]\t| SSID")); + + // read all results one by one + // this result type contains the most information, including the SSID + LR11x0WifiResultExtended_t result; + for(int i = 0; i < count; i++) { + if(i < 10) { Serial.print(" "); } Serial.print(i); Serial.print(" | "); + state = radio.getWifiScanResult(&result, i); + if(state != RADIOLIB_ERR_NONE) { + Serial.print(F("Failed to read result, code ")); + Serial.println(state); + continue; + } + + // print the basic information + Serial.print(F("802.11")); Serial.print(result.type); Serial.print("\t| "); + Serial.print(result.channelFreq); Serial.print(" MHz\t| "); + + // print MAC address + for(int j = 0; j < 6; j++) { + if(result.mac[j] < 0x10) { Serial.print("0"); } + Serial.print(result.mac[j], HEX); + if(j < 5) { Serial.print(":"); } + } + Serial.print(" | "); + + // print the two-letter country code + String country = result.countryCode; + Serial.print(country); + Serial.print(" \t| "); + + // print the RSSI + Serial.print(result.rssi); + Serial.print("\t| "); + + // print the network SSID + Serial.println((char*)result.ssid); + } + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + } + + // start scanning again + Serial.print(F("[LR1110] Starting passive WiFi scan ... ")); + state = radio.startWifiScan('*'); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } + } +} diff --git a/keywords.txt b/keywords.txt index f53f5d1c..fac7aa1c 100644 --- a/keywords.txt +++ b/keywords.txt @@ -91,6 +91,11 @@ AS923 KEYWORD1 KR920 KEYWORD1 IN865 KEYWORD1 +# LR11x0 scan results +LR11x0WifiResult_t KEYWORD1 +LR11x0WifiResultFull_t KEYWORD1 +LR11x0WifiResultExtended_t KEYWORD1 + ####################################### # Methods and Functions (KEYWORD2) ####################################### @@ -243,6 +248,12 @@ setAutoAck KEYWORD2 # LR11x0 beginLRFHSS KEYWORD2 setLrFhssConfig KEYWORD2 +startWifiScan KEYWORD2 +getWifiScanResultsCount KEYWORD2 +getWifiScanResult KEYWORD2 +wifiScan KEYWORD2 +setWiFiScanAction KEYWORD2 +clearWiFiScanAction KEYWORD2 # RTTY idle KEYWORD2 diff --git a/src/TypeDef.h b/src/TypeDef.h index 61109d52..245eb72a 100644 --- a/src/TypeDef.h +++ b/src/TypeDef.h @@ -563,6 +563,13 @@ */ #define RADIOLIB_LORAWAN_NO_DOWNLINK (-1116) +// LR11x0-specific status codes + +/*! + \brief The selected 802.11 WiFi type is invalid. +*/ +#define RADIOLIB_ERR_INVALID_WIFI_TYPE (-1200) + /*! \} */ diff --git a/src/modules/LR11x0/LR11x0.cpp b/src/modules/LR11x0/LR11x0.cpp index c9244bb8..3001563c 100644 --- a/src/modules/LR11x0/LR11x0.cpp +++ b/src/modules/LR11x0/LR11x0.cpp @@ -1335,6 +1335,168 @@ int16_t LR11x0::setLrFhssConfig(uint8_t bw, uint8_t cr, uint8_t hdrCount, uint16 return(RADIOLIB_ERR_NONE); } +int16_t LR11x0::startWifiScan(char wifiType, uint8_t mode, uint16_t chanMask, uint8_t numScans, uint16_t timeout) { + uint8_t type; + switch(wifiType) { + case('b'): + type = RADIOLIB_LR11X0_WIFI_SCAN_802_11_B; + break; + case('g'): + type = RADIOLIB_LR11X0_WIFI_SCAN_802_11_G; + break; + case('n'): + type = RADIOLIB_LR11X0_WIFI_SCAN_802_11_N; + break; + case('*'): + type = RADIOLIB_LR11X0_WIFI_SCAN_ALL; + break; + default: + return(RADIOLIB_ERR_INVALID_WIFI_TYPE); + } + + // go to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // reset cumulative timings + state = wifiResetCumulTimings(); + RADIOLIB_ASSERT(state); + + // set DIO mapping + state = setDioIrqParams(RADIOLIB_LR11X0_IRQ_WIFI_DONE, 0); + RADIOLIB_ASSERT(state); + + // start scan with the maximum number of results and abort on timeout + this->wifiScanMode = mode; + state = wifiScan(type, chanMask, this->wifiScanMode, RADIOLIB_LR11X0_WIFI_MAX_NUM_RESULTS, numScans, timeout, RADIOLIB_LR11X0_WIFI_ABORT_ON_TIMEOUT_ENABLED); + return(state); +} + +void LR11x0::setWiFiScanAction(void (*func)(void)) { + this->setIrqAction(func); +} + +void LR11x0::clearWiFiScanAction() { + this->clearIrqAction(); +} + +int16_t LR11x0::getWifiScanResultsCount(uint8_t* count) { + // clear IRQ first, as this is likely to be called right after scan has finished + int16_t state = clearIrq(RADIOLIB_LR11X0_IRQ_ALL); + RADIOLIB_ASSERT(state); + + uint8_t buff[1] = { 0 }; + state = this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_GET_NB_RESULTS, false, buff, sizeof(buff)); + + // pass the replies + if(count) { *count = buff[0]; } + + return(state); +} + +int16_t LR11x0::getWifiScanResult(LR11x0WifiResult_t* result, uint8_t index, bool brief) { + if(!result) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + + // read a single result + uint8_t format = brief ? RADIOLIB_LR11X0_WIFI_RESULT_TYPE_BASIC : RADIOLIB_LR11X0_WIFI_RESULT_TYPE_COMPLETE; + uint8_t raw[RADIOLIB_LR11X0_WIFI_RESULT_MAX_LEN] = { 0 }; + int16_t state = wifiReadResults(index, 1, format, raw); + RADIOLIB_ASSERT(state); + + // parse the information + switch(raw[0] & 0x03) { + case(RADIOLIB_LR11X0_WIFI_SCAN_802_11_B): + result->type = 'b'; + break; + case(RADIOLIB_LR11X0_WIFI_SCAN_802_11_G): + result->type = 'g'; + break; + case(RADIOLIB_LR11X0_WIFI_SCAN_802_11_N): + result->type = 'n'; + break; + } + result->dataRateId = (raw[0] & 0xFC) >> 2; + result->channelFreq = 2407 + (raw[1] & 0x0F)*5; + result->origin = (raw[1] & 0x30) >> 4; + result->ap = (raw[1] & 0x40) != 0; + result->rssi = (float)raw[2] / -2.0f;; + memcpy(result->mac, &raw[3], RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN); + + if(!brief) { + if(this->wifiScanMode == RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON) { + LR11x0WifiResultExtended_t* resultExtended = (LR11x0WifiResultExtended_t*)result; + resultExtended->rate = raw[3]; + resultExtended->service = (((uint16_t)raw[4] << 8) | ((uint16_t)raw[5])); + resultExtended->length = (((uint16_t)raw[6] << 8) | ((uint16_t)raw[7])); + resultExtended->frameType = raw[9] & 0x03; + resultExtended->frameSubType = (raw[9] & 0x3C) >> 2; + resultExtended->toDistributionSystem = (raw[9] & 0x40) != 0; + resultExtended->fromDistributionSystem = (raw[9] & 0x80) != 0; + memcpy(resultExtended->mac0, &raw[10], RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN); + memcpy(resultExtended->mac, &raw[16], RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN); + memcpy(resultExtended->mac2, &raw[22], RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN); + resultExtended->timestamp = (((uint64_t)raw[28] << 56) | ((uint64_t)raw[29] << 48)) | + (((uint64_t)raw[30] << 40) | ((uint64_t)raw[31] << 32)) | + (((uint64_t)raw[32] << 24) | ((uint64_t)raw[33] << 16)) | + (((uint64_t)raw[34] << 8) | (uint64_t)raw[35]); + resultExtended->periodBeacon = (((uint16_t)raw[36] << 8) | ((uint16_t)raw[37])) * 1024UL; + resultExtended->seqCtrl = (((uint16_t)raw[38] << 8) | ((uint16_t)raw[39])); + memcpy(resultExtended->ssid, &raw[40], RADIOLIB_LR11X0_WIFI_RESULT_SSID_LEN); + resultExtended->currentChannel = raw[72]; + memcpy(resultExtended->countryCode, &raw[73], 2); + resultExtended->countryCode[2] = '\0'; + resultExtended->ioReg = raw[75]; + resultExtended->fcsCheckOk = (raw[76] != 0); + resultExtended->phiOffset = (((uint16_t)raw[77] << 8) | ((uint16_t)raw[78])); + return(RADIOLIB_ERR_NONE); + } + + LR11x0WifiResultFull_t* resultFull = (LR11x0WifiResultFull_t*)result; + resultFull->frameType = raw[3] & 0x03; + resultFull->frameSubType = (raw[3] & 0x3C) >> 2; + resultFull->toDistributionSystem = (raw[3] & 0x40) != 0; + resultFull->fromDistributionSystem = (raw[3] & 0x80) != 0; + memcpy(resultFull->mac, &raw[4], RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN); + resultFull->phiOffset = (((uint16_t)raw[10] << 8) | ((uint16_t)raw[11])); + resultFull->timestamp = (((uint64_t)raw[12] << 56) | ((uint64_t)raw[13] << 48)) | + (((uint64_t)raw[14] << 40) | ((uint64_t)raw[15] << 32)) | + (((uint64_t)raw[16] << 24) | ((uint64_t)raw[17] << 16)) | + (((uint64_t)raw[18] << 8) | (uint64_t)raw[19]); + resultFull->periodBeacon = (((uint16_t)raw[20] << 8) | ((uint16_t)raw[21])) * 1024UL; + } + + return(RADIOLIB_ERR_NONE); +} + +int16_t LR11x0::wifiScan(uint8_t wifiType, uint8_t* count, uint8_t mode, uint16_t chanMask, uint8_t numScans, uint16_t timeout) { + if(!count) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + + // start scan + RADIOLIB_DEBUG_BASIC_PRINTLN("WiFi scan start"); + int16_t state = startWifiScan(wifiType, mode, chanMask, numScans, timeout); + RADIOLIB_ASSERT(state); + + // wait for scan finished or timeout + RadioLibTime_t softTimeout = 30UL * 1000UL; + RadioLibTime_t start = this->mod->hal->millis(); + while(!this->mod->hal->digitalRead(this->mod->getIrq())) { + this->mod->hal->yield(); + if(this->mod->hal->millis() - start > softTimeout) { + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout waiting for IRQ"); + this->standby(); + return(RADIOLIB_ERR_RX_TIMEOUT); + } + } + RADIOLIB_DEBUG_BASIC_PRINTLN("WiFi scan done in %d ms", this->mod->hal->millis() - start); + + // read number of results + return(getWifiScanResultsCount(count)); +} + int16_t LR11x0::modSetup(float tcxoVoltage, uint8_t modem) { this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); @@ -2282,7 +2444,9 @@ int16_t LR11x0::wifiScan(uint8_t type, uint16_t mask, uint8_t acqMode, uint8_t n (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF), abortOnTimeout }; - return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_SCAN, true, buff, sizeof(buff))); + + // call the SPI write stream directly to skip waiting for BUSY - it will be set to high once the scan starts + return(this->mod->SPIwriteStream(RADIOLIB_LR11X0_CMD_WIFI_SCAN, buff, sizeof(buff), false, false)); } int16_t LR11x0::wifiScanTimeLimit(uint8_t type, uint16_t mask, uint8_t acqMode, uint8_t nbMaxRes, uint16_t timePerChan, uint16_t timeout) { @@ -2315,19 +2479,9 @@ int16_t LR11x0::wifiCountryCodeTimeLimit(uint16_t mask, uint8_t nbMaxRes, uint16 return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_COUNTRY_CODE_TIME_LIMIT, true, buff, sizeof(buff))); } -int16_t LR11x0::wifiGetNbResults(uint8_t* nbResults) { - uint8_t buff[1] = { 0 }; - int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_GET_NB_RESULTS, false, buff, sizeof(buff)); - - // pass the replies - if(nbResults) { *nbResults = buff[0]; } - - return(state); -} - int16_t LR11x0::wifiReadResults(uint8_t index, uint8_t nbResults, uint8_t format, uint8_t* results) { - uint8_t reqBuff[3] = { index, nbResults, format }; - return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_READ_RESULTS, false, results, nbResults, reqBuff, sizeof(reqBuff))); + uint8_t buff[3] = { index, nbResults, format }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_READ_RESULTS, false, results, RADIOLIB_LR11X0_WIFI_RESULT_MAX_LEN, buff, sizeof(buff))); } int16_t LR11x0::wifiResetCumulTimings(void) { diff --git a/src/modules/LR11x0/LR11x0.h b/src/modules/LR11x0/LR11x0.h index 3a100e63..53edd721 100644 --- a/src/modules/LR11x0/LR11x0.h +++ b/src/modules/LR11x0/LR11x0.h @@ -472,10 +472,15 @@ #define RADIOLIB_LR11X0_WIFI_ACQ_MODE_SSID_BEACON (0x05UL << 0) // 7 0 SSID beacon #define RADIOLIB_LR11X0_WIFI_ABORT_ON_TIMEOUT_ENABLED (0x01UL << 0) // 7 0 abort scanning on preamble timeout: enabled #define RADIOLIB_LR11X0_WIFI_ABORT_ON_TIMEOUT_DISABLED (0x00UL << 0) // 7 0 disabled +#define RADIOLIB_LR11X0_WIFI_MAX_NUM_RESULTS (32) // 7 0 maximum possible number of Wi-Fi scan results +#define RADIOLIB_LR11X0_WIFI_ALL_CHANNELS (0x3FFFUL) // 16 0 scan all channels // RADIOLIB_LR11X0_CMD_WIFI_READ_RESULTS #define RADIOLIB_LR11X0_WIFI_RESULT_TYPE_COMPLETE (0x01UL << 0) // 7 0 Wi-Fi scan result type: complete #define RADIOLIB_LR11X0_WIFI_RESULT_TYPE_BASIC (0x04UL << 0) // 7 0 basic +#define RADIOLIB_LR11X0_WIFI_RESULT_MAX_LEN (79) // 7 0 maximum possible Wi-Fi scan size +#define RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN (6) // 7 0 MAC address length in bytes +#define RADIOLIB_LR11X0_WIFI_RESULT_SSID_LEN (32) // 7 0 SSID length in bytes // RADIOLIB_LR11X0_CMD_GNSS_SET_CONSTELLATION_TO_USE #define RADIOLIB_LR11X0_GNSS_CONSTELLATION_GPS (0x01UL << 0) // 7 0 GNSS constellation to use: GPS @@ -536,10 +541,108 @@ // RADIOLIB_LR11X0_REG_LORA_HIGH_POWER_FIX #define RADIOLIB_LR11X0_LORA_HIGH_POWER_FIX (0x00UL << 30) // 30 30 fix for errata +/*! + \struct LR11x0WifiResult_t + \brief Structure to save result of passive WiFi scan. + This result only saves the basic information. +*/ +struct LR11x0WifiResult_t { + /*! \brief WiFi (802.11) signal type, 'b', 'n' or 'g' */ + char type; + + /*! \brief Data rate ID holding information about modulation and coding rate. See LR11x0 user manual for details. */ + uint8_t dataRateId; + + /*! \brief Channel frequency in MHz */ + uint16_t channelFreq; + + /*! \brief MAC address origin: from gateway (1), phone (2) or undetermined (3) */ + uint8_t origin; + + /*! \brief Whether this signal was sent by an access point (true) or end device (false) */ + bool ap; + + /*! \brief RSSI in dBm */ + float rssi; + + /*! \brief MAC address */ + uint8_t mac[RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN]; +}; + +/*! + \struct LR11x0WifiResultFull_t + \brief Structure to save result of passive WiFi scan. + This result saves additional information alongside that in LR11x0WifiResult_t. +*/ +struct LR11x0WifiResultFull_t: public LR11x0WifiResult_t { + /*! \brief Frame type. See LR11x0 user manual for details. */ + uint8_t frameType; + + /*! \brief Frame sub type. See LR11x0 user manual for details. */ + uint8_t frameSubType; + + /*! \brief Frame sent from client station to distribution system. */ + bool toDistributionSystem; + + /*! \brief Frame sent from distribution system to client station. */ + bool fromDistributionSystem; + + /*! \brief See LR11x0 user manual for details. */ + uint16_t phiOffset; + + /*! \brief Number of microseconds the AP has been active. */ + uint64_t timestamp; + + /*! \brief Beacon period in microseconds. */ + uint32_t periodBeacon; +}; + +/*! + \struct LR11x0WifiResultExtended_t + \brief Structure to save result of passive WiFi scan. + This result saves additional information alongside that in LR11x0WifiResultFull_t. + Only scans performed with RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON acquisition mode + can yield this result! +*/ +struct LR11x0WifiResultExtended_t: public LR11x0WifiResultFull_t { + /*! \brief Data rate. See LR11x0 user manual for details. */ + uint8_t rate; + + /*! \brief Refer to IEEE Std 802.11, 2016, Part 11: Wireless LAN MAC and PHY Spec. */ + uint16_t service; + + /*! \brief Refer to IEEE Std 802.11, 2016, Part 11: Wireless LAN MAC and PHY Spec. */ + uint16_t length; + + /*! \brief MAC address 0 */ + uint8_t mac0[RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN]; + + /*! \brief MAC address 2 */ + uint8_t mac2[RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN]; + + /*! \brief Refer to IEEE Std 802.11, 2016, Part 11: Wireless LAN MAC and PHY Spec. */ + uint16_t seqCtrl; + + /*! \brief SSID */ + uint8_t ssid[RADIOLIB_LR11X0_WIFI_RESULT_SSID_LEN]; + + /*! \brief WiFi channel number */ + uint8_t currentChannel; + + /*! \brief Two-letter country code (null-terminated string). */ + char countryCode[3]; + + /*! \brief Refer to IEEE Std 802.11, 2016, Part 11: Wireless LAN MAC and PHY Spec. */ + uint8_t ioReg; + + /*! \brief True if frame check sequences is valid, false otherwise. */ + bool fcsCheckOk; +}; /*! \class LR11x0 - \brief + \brief Base class for %LR11x0 series. All derived classes for %LR11x0 (e.g. LR1110 or LR1120) inherit from this base class. + This class should not be instantiated directly from user code, only from its derived classes. */ class LR11x0: public PhysicalLayer { public: @@ -1053,6 +1156,69 @@ class LR11x0: public PhysicalLayer { \returns \ref status_codes */ int16_t setLrFhssConfig(uint8_t bw, uint8_t cr, uint8_t hdrCount = 3, uint16_t hopSeed = 0x13A); + + /*! + \brief Start passive WiFi scan. BUSY pin will be de-activated when the scan is finished. + \param wifiType Type of WiFi (802.11) signals to scan, 'b', 'n', 'g' or '*' for all signals. + \param mode Scan acquisition mode, one of RADIOLIB_LR11X0_WIFI_ACQ_MODE_*. + The type of results available after the scan depends on this mode. + Defaults to RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON, which provides the most information. + \param chanMask Bit mask of WiFi channels to scan, defaults to all channels. + More channels leads to longer overall scan duration. + \param numScans Number of scans to perform per each enabled channel. Defaults to 16 scans. + More scans leads to longer overall scan duration. + \param timeout Timeout of each scan in milliseconds. Defaults to 100 ms + Longer timeout leads to longer overall scan duration. + \returns \ref status_codes + */ + int16_t startWifiScan(char wifiType, uint8_t mode = RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON, uint16_t chanMask = RADIOLIB_LR11X0_WIFI_ALL_CHANNELS, uint8_t numScans = 16, uint16_t timeout = 100); + + /*! + \brief Sets interrupt service routine to call when a WiFi scan is completed. + \param func ISR to call. + */ + void setWiFiScanAction(void (*func)(void)); + + /*! + \brief Clears interrupt service routine to call when a WiFi scan is completed. + */ + void clearWiFiScanAction(); + + /*! + \brief Get number of WiFi scan results after the scan is finished. + \param count Pointer to a variable that will hold the number of scan results. + \returns \ref status_codes + */ + int16_t getWifiScanResultsCount(uint8_t* count); + + /*! + \brief Retrieve passive WiFi scan result. + \param result Pointer to structure to hold the result data. + \param index Result index, starting from 0. The number of scan results can be retrieved by calling getWifiScanResultsCount. + \param brief Whether to only retrieve the results in brief format. If set to false, only information in LR11x0WifiResult_t + will be retrieved. If set to true, information in LR11x0WifiResultFull_t will be retrieved. In addition, if WiFi scan mode + was set to RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON, all information in LR11x0WifiResultExtended_t will be retrieved. + \returns \ref status_codes + */ + int16_t getWifiScanResult(LR11x0WifiResult_t* result, uint8_t index, bool brief = false); + + /*! + \brief Blocking WiFi scan method. Performs a full passive WiFi scan. + This method may block for several seconds! + \param wifiType Type of WiFi (802.11) signals to scan, 'b', 'n', 'g' or '*' for all signals. + \param count Pointer to a variable that will hold the number of scan results. + \param mode Scan acquisition mode, one of RADIOLIB_LR11X0_WIFI_ACQ_MODE_*. + The type of results available after the scan depends on this mode. + Defaults to RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON, which provides the most information. + \param chanMask Bit mask of WiFi channels to scan, defaults to all channels. + More channels leads to longer overall scan duration. + \param numScans Number of scans to perform per each enabled channel. Defaults to 16 scans. + More scans leads to longer overall scan duration. + \param timeout Timeout of each scan in milliseconds. Defaults to 100 ms + Longer timeout leads to longer overall scan duration. + \returns \ref status_codes + */ + int16_t wifiScan(uint8_t wifiType, uint8_t* count, uint8_t mode = RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON, uint16_t chanMask = RADIOLIB_LR11X0_WIFI_ALL_CHANNELS, uint8_t numScans = 16, uint16_t timeout = 100); #if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL protected: @@ -1229,6 +1395,8 @@ class LR11x0: public PhysicalLayer { float dataRateMeasured = 0; + uint8_t wifiScanMode = 0; + int16_t modSetup(float tcxoVoltage, uint8_t modem); static int16_t SPIparseStatus(uint8_t in); static int16_t SPIcheckStatus(Module* mod);