Files
earlephilhower_arduino-pico/libraries/WiFi/src/WiFiClass.cpp
T
Earle F. Philhower, III afbba68549 Set AP IP address on ::beginAP
The Arduino WiFi normalization ended up calling the underlying LWIP
::config after the AP was begin, resulting in a failure to set the
IP configuration of the AP.  Move the _wifi.begin() call to after
the IP configuration is set.

Fixes #1989
2024-01-02 18:20:25 -08:00

643 lines
16 KiB
C++

/*
WiFiClass.cpp - WiFi class "compat" w/WiFiNINA for Raspberry Pi Pico W
Copyright (c) 2022 Earle F. Philhower, III. All rights reserved.
Implements the API defined by the Arduino WiFiNINA library,
copyright (c) 2018 Arduino SA. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <inttypes.h>
#include <pico/cyw43_arch.h>
#include <cyw43.h>
#include "lwip/raw.h"
#include "lwip/icmp.h"
#include "lwip/inet_chksum.h"
#include <map>
#include "WiFi.h"
// This is the real WiFi network object, we just tickle it to do our magic
#include <LwipEthernet.h>
static CYW43lwIP _wifi(1);
WiFiClass::WiFiClass() {
}
/*
Get firmware version
*/
const char* WiFiClass::firmwareVersion() {
// TODO - does not look like driver reports this now
return PICO_SDK_VERSION_STRING;
}
void WiFiClass::mode(WiFiMode_t m) {
_calledESP = true;
switch (m) {
case WiFiMode_t::WIFI_OFF:
end();
break;
case WiFiMode_t::WIFI_AP:
_modeESP = WiFiMode_t::WIFI_AP;
break;
case WiFiMode_t::WIFI_STA:
_modeESP = WiFiMode_t::WIFI_STA;
break;
case WiFiMode_t::WIFI_AP_STA:
_modeESP = WiFiMode_t::WIFI_STA;
break;
}
}
/* Start WiFi connection for OPEN networks
param ssid: Pointer to the SSID string.
*/
int WiFiClass::begin(const char* ssid) {
return begin(ssid, nullptr);
}
int WiFiClass::beginBSSID(const char* ssid, const uint8_t *bssid) {
return begin(ssid, nullptr, bssid);
}
#ifdef ARDUINO_RASPBERRY_PI_PICO_W
/* Start WiFi connection with passphrase
the most secure supported mode will be automatically selected
param ssid: Pointer to the SSID string.
param passphrase: Passphrase. Valid characters in a passphrase
must be between ASCII 32-126 (decimal).
*/
int WiFiClass::begin(const char* ssid, const char *passphrase, const uint8_t *bssid) {
// Simple ESP8266 compatibility hack
if (_modeESP == WIFI_AP) {
return beginAP(ssid, passphrase);
}
end();
_ssid = ssid;
_password = passphrase;
if (bssid) {
memcpy(_bssid, bssid, sizeof(_bssid));
} else {
bzero(_bssid, sizeof(_bssid));
}
_wifi.setSSID(_ssid.c_str());
_wifi.setBSSID(_bssid);
_wifi.setPassword(passphrase);
_wifi.setTimeout(_timeout);
_wifi.setSTA();
_apMode = false;
_wifiHWInitted = true;
uint32_t start = millis(); // The timeout starts from network init, not network link up
if (!_wifi.begin()) {
return WL_IDLE_STATUS;
}
noLowPowerMode();
// Enable CYW43 event debugging (make sure Debug Port is set)
//cyw43_state.trace_flags = 0xffff;
while (!_calledESP && ((millis() - start < (uint32_t)2 * _timeout)) && !connected()) {
delay(10);
}
return status();
}
uint8_t WiFiClass::beginAP(const char *ssid) {
return beginAP(ssid, nullptr);
}
uint8_t WiFiClass::beginAP(const char *ssid, uint8_t channel) {
(void) channel;
return beginAP(ssid, nullptr);
}
uint8_t WiFiClass::beginAP(const char *ssid, const char* passphrase, uint8_t channel) {
(void) channel;
return beginAP(ssid, passphrase);
}
uint8_t WiFiClass::beginAP(const char *ssid, const char* passphrase) {
end();
_ssid = ssid;
_password = passphrase;
_wifi.setSSID(_ssid.c_str());
_wifi.setPassword(passphrase);
_wifi.setTimeout(_timeout);
_wifi.setAP();
_apMode = true;
IPAddress gw = _wifi.gatewayIP();
if (!gw.isSet()) {
gw = IPAddress(192, 168, 42, 1);
}
IPAddress mask = _wifi.subnetMask();
if (!mask.isSet()) {
mask = IPAddress(255, 255, 255, 0);
}
config(gw);
if (!_wifi.begin()) {
return WL_IDLE_STATUS;
}
noLowPowerMode();
_dhcpServer = (dhcp_server_t *)malloc(sizeof(dhcp_server_t));
if (!_dhcpServer) {
// OOM
return WL_IDLE_STATUS;
}
dhcp_server_init(_dhcpServer, gw, mask);
_wifiHWInitted = true;
return WL_CONNECTED;
}
#endif
uint8_t WiFiClass::softAPgetStationNum() {
if (!_apMode || !_wifiHWInitted) {
return 0;
}
int m;
cyw43_wifi_ap_get_max_stas(&cyw43_state, &m);
uint8_t *macs = (uint8_t*)malloc(m * 6);
if (!macs) {
return 0;
}
cyw43_wifi_ap_get_stas(&cyw43_state, &m, macs);
free(macs);
return m;
}
bool WiFiClass::connected() {
return (_apMode && _wifiHWInitted) || (_wifi.connected() && localIP().isSet() && (cyw43_wifi_link_status(&cyw43_state, _apMode ? 1 : 0) == CYW43_LINK_JOIN));
}
/* Change Ip configuration settings disabling the dhcp client
param local_ip: Static ip configuration
*/
void WiFiClass::config(IPAddress local_ip) {
_wifi.config(local_ip);
}
/* Change Ip configuration settings disabling the dhcp client
param local_ip: Static ip configuration
param dns_server: IP configuration for DNS server 1
*/
void WiFiClass::config(IPAddress local_ip, IPAddress dns_server) {
_wifi.config(local_ip, dns_server);
}
/* Change Ip configuration settings disabling the dhcp client
param local_ip: Static ip configuration
param dns_server: IP configuration for DNS server 1
param gateway : Static gateway configuration
*/
void WiFiClass::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway) {
_wifi.config(local_ip, dns_server, gateway);
}
/* Change Ip configuration settings disabling the dhcp client
param local_ip: Static ip configuration
param dns_server: IP configuration for DNS server 1
param gateway: Static gateway configuration
param subnet: Static Subnet mask
*/
void WiFiClass::config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) {
_wifi.config(local_ip, gateway, subnet, dns_server);
}
/* Change DNS Ip configuration
param dns_server1: ip configuration for DNS server 1
*/
void WiFiClass::setDNS(IPAddress dns_server1) {
dns_setserver(0, dns_server1);
}
/* Change DNS Ip configuration
param dns_server1: ip configuration for DNS server 1
param dns_server2: ip configuration for DNS server 2
*/
void WiFiClass::setDNS(IPAddress dns_server1, IPAddress dns_server2) {
dns_setserver(0, dns_server1);
dns_setserver(1, dns_server2);
}
/* Set the hostname used for DHCP requests
param name: hostname to set
*/
void WiFiClass::setHostname(const char* name) {
_wifi.setHostname(name);
}
const char *WiFiClass::getHostname() {
return _wifi.getHostname();
}
/*
Disconnect from the network
return: one value of wl_status_t enum
*/
int WiFiClass::disconnect(bool wifi_off __unused) {
if (_dhcpServer) {
dhcp_server_deinit(_dhcpServer);
free(_dhcpServer);
_dhcpServer = nullptr;
}
if (_wifiHWInitted) {
_wifiHWInitted = false;
cyw43_wifi_leave(&cyw43_state, _apMode ? 1 : 0);
_wifi.end();
}
return WL_DISCONNECTED;
}
void WiFiClass::end(void) {
if (_wifiHWInitted) {
disconnect();
}
}
/*
Get the interface MAC address.
return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
*/
uint8_t* WiFiClass::macAddress(uint8_t* mac) {
if (!_wifiHWInitted) {
_apMode = false;
cyw43_wifi_set_up(&cyw43_state, _apMode ? 1 : 0, true, CYW43_COUNTRY_WORLDWIDE);
}
cyw43_wifi_get_mac(&cyw43_state, _apMode ? 1 : 0, mac);
return mac;
}
/*
Get the interface IP address.
return: Ip address value
*/
IPAddress WiFiClass::localIP() {
return _wifi.localIP();
}
/*
Get the interface subnet mask address.
return: subnet mask address value
*/
IPAddress WiFiClass::subnetMask() {
return _wifi.subnetMask();
}
/*
Get the gateway ip address.
return: gateway ip address value
*/
IPAddress WiFiClass::gatewayIP() {
return _wifi.gatewayIP();
}
/*
Get the DNS ip address.
return: IPAddress DNS Server IP
*/
IPAddress WiFiClass::dnsIP(uint8_t dns_no) {
return _wifi.dnsIP(dns_no);
}
/*
Return the current SSID associated with the network
return: ssid string
*/
const String &WiFiClass::SSID() {
return _ssid;
}
/*
Return the current BSSID associated with the network.
It is the MAC address of the Access Point
return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
*/
uint8_t* WiFiClass::BSSID(uint8_t* bssid) {
#ifndef CYW43_IOCTL_GET_BSSID
#define CYW43_IOCTL_GET_BSSID ( (uint32_t)23 * 2 )
#endif
if (_wifi.connected()) {
cyw43_ioctl(&cyw43_state, CYW43_IOCTL_GET_BSSID, WL_MAC_ADDR_LENGTH, bssid, CYW43_ITF_STA);
} else {
memset(bssid, 0, WL_MAC_ADDR_LENGTH);
}
return bssid;
}
int WiFiClass::channel() {
#ifndef CYW43_IOCTL_GET_CHANNEL
#define CYW43_IOCTL_GET_CHANNEL 0x3a
#endif
int32_t channel;
if (_wifi.connected()) {
cyw43_ioctl(&cyw43_state, CYW43_IOCTL_GET_CHANNEL, sizeof channel, (uint8_t *)&channel, CYW43_ITF_STA);
} else {
channel = -1;
}
return channel;
}
/*
Return the current RSSI /Received Signal Strength in dBm)
associated with the network
return: signed value
*/
int32_t WiFiClass::RSSI() {
#ifndef CYW43_IOCTL_GET_RSSI
#define CYW43_IOCTL_GET_RSSI 0xFE
#endif
int32_t rssi;
if (_wifi.connected()) {
cyw43_ioctl(&cyw43_state, CYW43_IOCTL_GET_RSSI, sizeof rssi, (uint8_t *)&rssi, CYW43_ITF_STA);
} else {
rssi = -255;
}
return rssi;
}
/*
Return the Encryption Type associated with the network
return: one value of wl_enc_type enum
*/
uint8_t WiFiClass::encryptionType() {
// TODO - Driver does not return this?!
if (_password == nullptr) {
return ENC_TYPE_NONE;
}
return ENC_TYPE_AUTO;
}
//TODO - this can be in the class
static uint64_t _to64(uint8_t b[8]) {
uint64_t x = 0;
for (int i = 0; i < 6; i++) {
x <<= 8LL;
x |= b[i] & 255;
}
return x;
}
int WiFiClass::_scanCB(void *env, const cyw43_ev_scan_result_t *result) {
WiFiClass *w = (WiFiClass *)env;
if (result) {
cyw43_ev_scan_result_t s;
memcpy(&s, result, sizeof(s));
w->_scan.insert_or_assign(_to64(s.bssid), s);
}
return 0;
}
/*
Start scan WiFi networks available
return: Number of discovered networks
*/
int8_t WiFiClass::scanNetworks(bool async) {
cyw43_wifi_scan_options_t scan_options;
memset(&scan_options, 0, sizeof(scan_options));
_scan.clear();
if (!_wifiHWInitted) {
_apMode = false;
cyw43_arch_enable_sta_mode();
_wifiHWInitted = true;
}
int err = cyw43_wifi_scan(&cyw43_state, &scan_options, this, _scanCB);
if (err) {
return 0;
}
if (!async) {
uint32_t now = millis();
while (cyw43_wifi_scan_active(&cyw43_state) && (millis() - now < 10000)) {
delay(10);
}
return _scan.size();
} else {
return -1;
}
}
int8_t WiFiClass::scanComplete() {
if (cyw43_wifi_scan_active(&cyw43_state)) {
return -1;
} else {
return _scan.size();
}
}
void WiFiClass::scanDelete() {
_scan.clear();
}
/*
Return the SSID discovered during the network scan.
param networkItem: specify from which network item want to get the information
return: ssid string of the specified item on the networks scanned list
*/
const char*WiFiClass::SSID(uint8_t networkItem) {
if (networkItem >= _scan.size()) {
return nullptr;
}
auto it = _scan.begin();
for (int i = 0; i < networkItem; i++) {
++it;
}
return (const char *)it->second.ssid;
}
/*
Return the encryption type of the networks discovered during the scanNetworks
param networkItem: specify from which network item want to get the information
return: encryption type (enum wl_enc_type) of the specified item on the networks scanned list
*/
uint8_t WiFiClass::encryptionType(uint8_t networkItem) {
if (networkItem >= _scan.size()) {
return ENC_TYPE_UNKNOWN;
}
auto it = _scan.begin();
for (int i = 0; i < networkItem; i++) {
++it;
}
// TODO - the driver returns a small integer but does not actually provide a way of mapping that to the proper enc type. My best guesses here...
switch (it->second.auth_mode) {
case 0: return ENC_TYPE_NONE;
case 3: return ENC_TYPE_TKIP;
case 5: return ENC_TYPE_CCMP;
case 7: return ENC_TYPE_AUTO;
}
return ENC_TYPE_UNKNOWN;
}
uint8_t* WiFiClass::BSSID(uint8_t networkItem, uint8_t* bssid) {
if (networkItem >= _scan.size()) {
return nullptr;
}
auto it = _scan.begin();
for (int i = 0; i < networkItem; i++) {
++it;
}
memcpy(bssid, it->second.bssid, 6);
return bssid;
}
uint8_t WiFiClass::channel(uint8_t networkItem) {
if (networkItem >= _scan.size()) {
return 255;
}
auto it = _scan.begin();
for (int i = 0; i < networkItem; i++) {
++it;
}
return it->second.channel;
}
/*
Return the RSSI of the networks discovered during the scanNetworks
param networkItem: specify from which network item want to get the information
return: signed value of RSSI of the specified item on the networks scanned list
*/
int32_t WiFiClass::RSSI(uint8_t networkItem) {
if (networkItem >= _scan.size()) {
return -9999;
}
auto it = _scan.begin();
for (int i = 0; i < networkItem; i++) {
++it;
}
return it->second.rssi;
}
/*
Return Connection status.
return: one of the value defined in wl_status_t
*/
uint8_t WiFiClass::status() {
if (_apMode && _wifiHWInitted) {
return WL_CONNECTED;
}
switch (cyw43_wifi_link_status(&cyw43_state, _apMode ? 1 : 0)) {
case CYW43_LINK_DOWN: return WL_IDLE_STATUS;
case CYW43_LINK_JOIN: return localIP().isSet() ? WL_CONNECTED : WL_DISCONNECTED;
case CYW43_LINK_FAIL: return WL_CONNECT_FAILED;
case CYW43_LINK_NONET: return WL_CONNECT_FAILED;
case CYW43_LINK_BADAUTH: return WL_CONNECT_FAILED;
}
return WL_NO_MODULE;
}
/*
Return The deauthentication reason code.
return: the deauthentication reason code
*/
uint8_t WiFiClass::reasonCode() {
// TODO - driver does not report this?!
return WL_NO_SHIELD;
}
/**
Resolve the given hostname to an IP address.
@param aHostname Name to be resolved
@param aResult IPAddress structure to store the returned IP address
@return 1 if aIPAddrString was successfully converted to an IP address,
else 0
*/
// Note that there is now a global FCN for name lookup to use all Ethernet ports, no need to call WiFi.hostByName, just ::hostByName
int WiFiClass::hostByName(const char* aHostname, IPAddress& aResult, int timeout_ms) {
return ::hostByName(aHostname, aResult, timeout_ms);
}
// TODO
unsigned long WiFiClass::getTime() {
return millis();
}
void WiFiClass::aggressiveLowPowerMode() {
cyw43_wifi_pm(&cyw43_state, CYW43_AGGRESSIVE_PM);
}
void WiFiClass::defaultLowPowerMode() {
cyw43_wifi_pm(&cyw43_state, CYW43_DEFAULT_PM);
}
// The difference between the default CYW43_DEFAULT_PM (0xA11142) and not low power (0xA11140) is that it changed from "Powersave mode on specified interface with High throughput" to "No Powersave mode". All other parameters stayed the same.
void WiFiClass::noLowPowerMode() {
cyw43_wifi_pm(&cyw43_state, 0xA11140);
}
int WiFiClass::ping(const char* hostname, uint8_t ttl) {
IPAddress ip;
if (!hostByName(hostname, ip)) {
return WL_PING_UNKNOWN_HOST;
}
return ping(ip, ttl);
}
int WiFiClass::ping(const String &hostname, uint8_t ttl) {
return ping(hostname.c_str(), ttl);
}
int WiFiClass::ping(IPAddress host, uint8_t ttl) {
return _wifi.ping(host, ttl, _timeout);
}
void WiFiClass::setTimeout(unsigned long timeout) {
_timeout = timeout;
}
void WiFiClass::setFeedWatchdogFunc(FeedHostProcessorWatchdogFuncPointer func) {
(void) func;
}
void WiFiClass::feedWatchdog() {
}
WiFiClass WiFi;