arduino-esp32/libraries/WiFi/src/WiFiGeneric.cpp

498 lines
15 KiB
C++

/*
ESP8266WiFiGeneric.cpp - WiFi library for esp8266
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
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
Reworked on 28 Dec 2015 by Markus Sattler
*/
#include "WiFi.h"
#include "WiFiGeneric.h"
extern "C" {
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <esp_err.h>
#include <esp_wifi.h>
#include <esp_event_loop.h>
#include "lwip/ip_addr.h"
#include "lwip/opt.h"
#include "lwip/err.h"
#include "lwip/dns.h"
#include "esp_ipc.h"
} //extern "C"
#include "esp32-hal-log.h"
#undef min
#undef max
#include <vector>
#include "sdkconfig.h"
#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif
static xQueueHandle _network_event_queue;
static TaskHandle_t _network_event_task_handle = NULL;
static void _network_event_task(void * arg){
system_event_t *event = NULL;
for (;;) {
if(xQueueReceive(_network_event_queue, &event, portMAX_DELAY) == pdTRUE){
WiFiGenericClass::_eventCallback(arg, event);
}
}
vTaskDelete(NULL);
_network_event_task_handle = NULL;
}
static esp_err_t _network_event_cb(void *arg, system_event_t *event){
if (xQueueSend(_network_event_queue, &event, portMAX_DELAY) != pdPASS) {
log_w("Network Event Queue Send Failed!");
return ESP_FAIL;
}
return ESP_OK;
}
static void _start_network_event_task(){
if(!_network_event_queue){
_network_event_queue = xQueueCreate(32, sizeof(system_event_t *));
if(!_network_event_queue){
log_e("Network Event Queue Create Failed!");
return;
}
}
if(!_network_event_task_handle){
xTaskCreatePinnedToCore(_network_event_task, "network_event", 4096, NULL, 2, &_network_event_task_handle, ARDUINO_RUNNING_CORE);
if(!_network_event_task_handle){
log_e("Network Event Task Start Failed!");
return;
}
}
esp_event_loop_init(&_network_event_cb, NULL);
}
void tcpipInit(){
static bool initialized = false;
if(!initialized){
initialized = true;
_start_network_event_task();
tcpip_adapter_init();
}
}
static bool wifiLowLevelInit(){
static bool lowLevelInitDone = false;
if(!lowLevelInitDone){
tcpipInit();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_err_t err = esp_wifi_init(&cfg);
if(err){
log_e("esp_wifi_init %d", err);
return false;
}
esp_wifi_set_storage(WIFI_STORAGE_FLASH);
esp_wifi_set_mode(WIFI_MODE_NULL);
lowLevelInitDone = true;
}
return true;
}
static bool wifiLowLevelDeinit(){
//deinit not working yet!
//esp_wifi_deinit();
return true;
}
static bool _esp_wifi_started = false;
static bool espWiFiStart(){
if(_esp_wifi_started){
return true;
}
if(!wifiLowLevelInit()){
return false;
}
esp_err_t err = esp_wifi_start();
if (err != ESP_OK) {
log_e("esp_wifi_start %d", err);
wifiLowLevelDeinit();
return false;
}
_esp_wifi_started = true;
return true;
}
static bool espWiFiStop(){
esp_err_t err;
if(!_esp_wifi_started){
return true;
}
err = esp_wifi_stop();
if(err){
log_e("Could not stop WiFi! %u", err);
return false;
}
_esp_wifi_started = false;
return wifiLowLevelDeinit();
}
// -----------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------- Generic WiFi function -----------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
typedef struct {
WiFiEventCb cb;
WiFiEventFullCb fcb;
WiFiEventSysCb scb;
system_event_id_t event;
} WiFiEventCbList_t;
// arduino dont like std::vectors move static here
static std::vector<WiFiEventCbList_t> cbEventList;
bool WiFiGenericClass::_persistent = true;
wifi_mode_t WiFiGenericClass::_forceSleepLastMode = WIFI_MODE_NULL;
WiFiGenericClass::WiFiGenericClass()
{
}
/**
* set callback function
* @param cbEvent WiFiEventCb
* @param event optional filter (WIFI_EVENT_MAX is all events)
*/
void WiFiGenericClass::onEvent(WiFiEventCb cbEvent, system_event_id_t event)
{
if(!cbEvent) {
return;
}
WiFiEventCbList_t newEventHandler;
newEventHandler.cb = cbEvent;
newEventHandler.fcb = NULL;
newEventHandler.scb = NULL;
newEventHandler.event = event;
cbEventList.push_back(newEventHandler);
}
void WiFiGenericClass::onEvent(WiFiEventFullCb cbEvent, system_event_id_t event)
{
if(!cbEvent) {
return;
}
WiFiEventCbList_t newEventHandler;
newEventHandler.cb = NULL;
newEventHandler.fcb = cbEvent;
newEventHandler.scb = NULL;
newEventHandler.event = event;
cbEventList.push_back(newEventHandler);
}
void WiFiGenericClass::onEvent(WiFiEventSysCb cbEvent, system_event_id_t event)
{
if(!cbEvent) {
return;
}
WiFiEventCbList_t newEventHandler;
newEventHandler.cb = NULL;
newEventHandler.fcb = NULL;
newEventHandler.scb = cbEvent;
newEventHandler.event = event;
cbEventList.push_back(newEventHandler);
}
/**
* removes a callback form event handler
* @param cbEvent WiFiEventCb
* @param event optional filter (WIFI_EVENT_MAX is all events)
*/
void WiFiGenericClass::removeEvent(WiFiEventCb cbEvent, system_event_id_t event)
{
if(!cbEvent) {
return;
}
for(uint32_t i = 0; i < cbEventList.size(); i++) {
WiFiEventCbList_t entry = cbEventList[i];
if(entry.cb == cbEvent && entry.event == event) {
cbEventList.erase(cbEventList.begin() + i);
}
}
}
void WiFiGenericClass::removeEvent(WiFiEventFullCb cbEvent, system_event_id_t event)
{
if(!cbEvent) {
return;
}
for(uint32_t i = 0; i < cbEventList.size(); i++) {
WiFiEventCbList_t entry = cbEventList[i];
if(entry.fcb == cbEvent && entry.event == event) {
cbEventList.erase(cbEventList.begin() + i);
}
}
}
void WiFiGenericClass::removeEvent(WiFiEventSysCb cbEvent, system_event_id_t event)
{
if(!cbEvent) {
return;
}
for(uint32_t i = 0; i < cbEventList.size(); i++) {
WiFiEventCbList_t entry = cbEventList[i];
if(entry.scb == cbEvent && entry.event == event) {
cbEventList.erase(cbEventList.begin() + i);
}
}
}
/**
* callback for WiFi events
* @param arg
*/
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
const char * system_event_names[] = { "WIFI_READY", "SCAN_DONE", "STA_START", "STA_STOP", "STA_CONNECTED", "STA_DISCONNECTED", "STA_AUTHMODE_CHANGE", "STA_GOT_IP", "STA_LOST_IP", "STA_WPS_ER_SUCCESS", "STA_WPS_ER_FAILED", "STA_WPS_ER_TIMEOUT", "STA_WPS_ER_PIN", "AP_START", "AP_STOP", "AP_STACONNECTED", "AP_STADISCONNECTED", "AP_PROBEREQRECVED", "GOT_IP6", "ETH_START", "ETH_STOP", "ETH_CONNECTED", "ETH_DISCONNECTED", "ETH_GOT_IP", "MAX"};
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN
const char * system_event_reasons[] = { "UNSPECIFIED", "AUTH_EXPIRE", "AUTH_LEAVE", "ASSOC_EXPIRE", "ASSOC_TOOMANY", "NOT_AUTHED", "NOT_ASSOCED", "ASSOC_LEAVE", "ASSOC_NOT_AUTHED", "DISASSOC_PWRCAP_BAD", "DISASSOC_SUPCHAN_BAD", "IE_INVALID", "MIC_FAILURE", "4WAY_HANDSHAKE_TIMEOUT", "GROUP_KEY_UPDATE_TIMEOUT", "IE_IN_4WAY_DIFFERS", "GROUP_CIPHER_INVALID", "PAIRWISE_CIPHER_INVALID", "AKMP_INVALID", "UNSUPP_RSN_IE_VERSION", "INVALID_RSN_IE_CAP", "802_1X_AUTH_FAILED", "CIPHER_SUITE_REJECTED", "BEACON_TIMEOUT", "NO_AP_FOUND", "AUTH_FAIL", "ASSOC_FAIL", "HANDSHAKE_TIMEOUT" };
#define reason2str(r) ((r>176)?system_event_reasons[r-176]:system_event_reasons[r-1])
#endif
esp_err_t WiFiGenericClass::_eventCallback(void *arg, system_event_t *event)
{
log_d("Event: %d - %s", event->event_id, system_event_names[event->event_id]);
if(event->event_id == SYSTEM_EVENT_SCAN_DONE) {
WiFiScanClass::_scanDone();
} else if(event->event_id == SYSTEM_EVENT_STA_DISCONNECTED) {
uint8_t reason = event->event_info.disconnected.reason;
log_w("Reason: %u - %s", reason, reason2str(reason));
if(reason == WIFI_REASON_NO_AP_FOUND) {
WiFiSTAClass::_setStatus(WL_NO_SSID_AVAIL);
} else if(reason == WIFI_REASON_AUTH_FAIL || reason == WIFI_REASON_ASSOC_FAIL) {
WiFiSTAClass::_setStatus(WL_CONNECT_FAILED);
} else if(reason == WIFI_REASON_BEACON_TIMEOUT || reason == WIFI_REASON_HANDSHAKE_TIMEOUT) {
WiFiSTAClass::_setStatus(WL_CONNECTION_LOST);
} else if(reason == WIFI_REASON_AUTH_EXPIRE) {
if(WiFi.getAutoReconnect()){
WiFi.begin();
}
} else {
WiFiSTAClass::_setStatus(WL_DISCONNECTED);
}
} else if(event->event_id == SYSTEM_EVENT_STA_START) {
WiFiSTAClass::_setStatus(WL_DISCONNECTED);
} else if(event->event_id == SYSTEM_EVENT_STA_STOP) {
WiFiSTAClass::_setStatus(WL_NO_SHIELD);
} else if(event->event_id == SYSTEM_EVENT_STA_CONNECTED) {
WiFiSTAClass::_setStatus(WL_IDLE_STATUS);
} else if(event->event_id == SYSTEM_EVENT_STA_GOT_IP) {
//#1081 https://github.com/espressif/arduino-esp32/issues/1081
// if(WiFiSTAClass::status() == WL_IDLE_STATUS)
{
WiFiSTAClass::_setStatus(WL_CONNECTED);
}
}
for(uint32_t i = 0; i < cbEventList.size(); i++) {
WiFiEventCbList_t entry = cbEventList[i];
if(entry.cb || entry.fcb || entry.scb) {
if(entry.event == (system_event_id_t) event->event_id || entry.event == SYSTEM_EVENT_MAX) {
if(entry.cb){
entry.cb((system_event_id_t) event->event_id);
} else if(entry.fcb){
entry.fcb((system_event_id_t) event->event_id, (system_event_info_t) event->event_info);
} else {
entry.scb(event);
}
}
}
}
return ESP_OK;
}
/**
* Return the current channel associated with the network
* @return channel (1-13)
*/
int32_t WiFiGenericClass::channel(void)
{
uint8_t primaryChan;
wifi_second_chan_t secondChan;
esp_wifi_get_channel(&primaryChan, &secondChan);
return primaryChan;
}
/**
* store WiFi config in SDK flash area
* @param persistent
*/
void WiFiGenericClass::persistent(bool persistent)
{
_persistent = persistent;
}
/**
* set new mode
* @param m WiFiMode_t
*/
bool WiFiGenericClass::mode(wifi_mode_t m)
{
wifi_mode_t cm = getMode();
if(cm == WIFI_MODE_MAX){
return false;
}
if(cm == m) {
return true;
}
esp_err_t err;
err = esp_wifi_set_mode(m);
if(err){
log_e("Could not set mode! %u", err);
return false;
}
if(m){
return espWiFiStart();
}
return espWiFiStop();
}
/**
* get WiFi mode
* @return WiFiMode
*/
wifi_mode_t WiFiGenericClass::getMode()
{
if(!wifiLowLevelInit()){
return WIFI_MODE_MAX;
}
uint8_t mode;
esp_wifi_get_mode((wifi_mode_t*)&mode);
return (wifi_mode_t)mode;
}
/**
* control STA mode
* @param enable bool
* @return ok
*/
bool WiFiGenericClass::enableSTA(bool enable)
{
wifi_mode_t currentMode = getMode();
bool isEnabled = ((currentMode & WIFI_MODE_STA) != 0);
if(isEnabled != enable) {
if(enable) {
return mode((wifi_mode_t)(currentMode | WIFI_MODE_STA));
} else {
return mode((wifi_mode_t)(currentMode & (~WIFI_MODE_STA)));
}
} else {
return true;
}
}
/**
* control AP mode
* @param enable bool
* @return ok
*/
bool WiFiGenericClass::enableAP(bool enable)
{
wifi_mode_t currentMode = getMode();
bool isEnabled = ((currentMode & WIFI_MODE_AP) != 0);
if(isEnabled != enable) {
if(enable) {
return mode((wifi_mode_t)(currentMode | WIFI_MODE_AP));
} else {
return mode((wifi_mode_t)(currentMode & (~WIFI_MODE_AP)));
}
} else {
return true;
}
}
// -----------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------ Generic Network function ---------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
static bool _dns_busy = false;
/**
* DNS callback
* @param name
* @param ipaddr
* @param callback_arg
*/
static void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
{
if(ipaddr) {
(*reinterpret_cast<IPAddress*>(callback_arg)) = ipaddr->u_addr.ip4.addr;
}
_dns_busy = false;
}
/**
* 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 error code
*/
int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult)
{
ip_addr_t addr;
aResult = static_cast<uint32_t>(0);
_dns_busy = true;
err_t err = dns_gethostbyname(aHostname, &addr, &wifi_dns_found_callback, &aResult);
if(err == ERR_OK && addr.u_addr.ip4.addr) {
aResult = addr.u_addr.ip4.addr;
_dns_busy = false;
} else if(err == ERR_INPROGRESS) {
while(_dns_busy){
delay(1);
}
} else {
_dns_busy = false;
return 0;
}
return 1;
}