/* ESP8266WiFi.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 */ #include "ESP8266WiFi.h" extern "C" { #include "c_types.h" #include "ets_sys.h" #include "os_type.h" #include "osapi.h" #include "mem.h" #include "user_interface.h" #include "smartconfig.h" #include "lwip/opt.h" #include "lwip/err.h" #include "lwip/dns.h" } extern "C" void esp_schedule(); extern "C" void esp_yield(); ESP8266WiFiClass::ESP8266WiFiClass() : _useApMode(false) , _useClientMode(false) , _useStaticIp(false) { } void ESP8266WiFiClass::mode(WiFiMode m) { if(wifi_get_opmode() == (uint8)m) { return; } ETS_UART_INTR_DISABLE(); wifi_set_opmode(m); ETS_UART_INTR_ENABLE(); } int ESP8266WiFiClass::begin(char* ssid, char *passphrase, int32_t channel, uint8_t bssid[6]){ return begin((const char*) ssid, (const char*) passphrase, channel, bssid); } int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase, int32_t channel, uint8_t bssid[6]){ _useClientMode = true; if(_useApMode) { // turn on AP+STA mode mode(WIFI_AP_STA); } else { // turn on STA mode mode(WIFI_STA); } if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) { // fail SSID to long or missing! return WL_CONNECT_FAILED; } if(passphrase && strlen(passphrase) > 63) { // fail passphrase to long! return WL_CONNECT_FAILED; } struct station_config conf; strcpy(reinterpret_cast(conf.ssid), ssid); if (passphrase) { strcpy(reinterpret_cast(conf.password), passphrase); } else { *conf.password = 0; } if (bssid) { conf.bssid_set = 1; memcpy((void *) &conf.bssid[0], (void *) bssid, 6); } else { conf.bssid_set = 0; } ETS_UART_INTR_DISABLE(); wifi_station_set_config(&conf); wifi_station_connect(); ETS_UART_INTR_ENABLE(); if(channel > 0 && channel <= 13) { wifi_set_channel(channel); } if(!_useStaticIp) wifi_station_dhcpc_start(); return status(); } uint8_t ESP8266WiFiClass::waitForConnectResult(){ if ((wifi_get_opmode() & 1) == 0)//1 and 3 have STA enabled return WL_DISCONNECTED; while (status() == WL_DISCONNECTED) delay(100); return status(); } // You will have to set the DNS-Server manually later since this will not enable DHCP void ESP8266WiFiClass::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) { struct ip_info info; info.ip.addr = static_cast(local_ip); info.gw.addr = static_cast(gateway); info.netmask.addr = static_cast(subnet); wifi_station_dhcpc_stop(); wifi_set_ip_info(STATION_IF, &info); _useStaticIp = true; } void ESP8266WiFiClass::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns) { struct ip_info info; info.ip.addr = static_cast(local_ip); info.gw.addr = static_cast(gateway); info.netmask.addr = static_cast(subnet); wifi_station_dhcpc_stop(); wifi_set_ip_info(STATION_IF, &info); // Set DNS-Server ip_addr_t d; d.addr = static_cast(dns); dns_setserver(0,&d); _useStaticIp = true; } int ESP8266WiFiClass::disconnect() { struct station_config conf; *conf.ssid = 0; *conf.password = 0; ETS_UART_INTR_DISABLE(); wifi_station_set_config(&conf); wifi_station_disconnect(); ETS_UART_INTR_ENABLE(); return 0; } void ESP8266WiFiClass::softAP(const char* ssid) { softAP(ssid, 0); } void ESP8266WiFiClass::softAP(const char* ssid, const char* passphrase, int channel) { if(_useClientMode) { // turn on AP+STA mode mode(WIFI_AP_STA); } else { // turn on STA mode mode(WIFI_AP); } if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) { // fail SSID to long or missing! return; } if(passphrase && strlen(passphrase) > 63) { // fail passphrase to long! return; } struct softap_config conf; wifi_softap_get_config(&conf); strcpy(reinterpret_cast(conf.ssid), ssid); conf.channel = channel; conf.ssid_len = strlen(ssid); conf.ssid_hidden = 0; conf.max_connection = 4; conf.beacon_interval = 100; if (!passphrase || strlen(passphrase) == 0) { conf.authmode = AUTH_OPEN; *conf.password = 0; } else { conf.authmode = AUTH_WPA2_PSK; strcpy(reinterpret_cast(conf.password), passphrase); } ETS_UART_INTR_DISABLE(); wifi_softap_set_config(&conf); ETS_UART_INTR_ENABLE(); } void ESP8266WiFiClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet) { struct ip_info info; info.ip.addr = static_cast(local_ip); info.gw.addr = static_cast(gateway); info.netmask.addr = static_cast(subnet); wifi_softap_dhcps_stop(); wifi_set_ip_info(SOFTAP_IF, &info); wifi_softap_dhcps_start(); } uint8_t* ESP8266WiFiClass::macAddress(uint8_t* mac) { wifi_get_macaddr(STATION_IF, mac); return mac; } String ESP8266WiFiClass::macAddress(void) { uint8_t mac[6]; char macStr[18] = {0}; wifi_get_macaddr(STATION_IF, mac); sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return String(macStr); } uint8_t* ESP8266WiFiClass::softAPmacAddress(uint8_t* mac) { wifi_get_macaddr(SOFTAP_IF, mac); return mac; } String ESP8266WiFiClass::softAPmacAddress(void) { uint8_t mac[6]; char macStr[18] = {0}; wifi_get_macaddr(SOFTAP_IF, mac); sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return String(macStr); } IPAddress ESP8266WiFiClass::localIP() { struct ip_info ip; wifi_get_ip_info(STATION_IF, &ip); return IPAddress(ip.ip.addr); } IPAddress ESP8266WiFiClass::softAPIP() { struct ip_info ip; wifi_get_ip_info(SOFTAP_IF, &ip); return IPAddress(ip.ip.addr); } IPAddress ESP8266WiFiClass::subnetMask() { struct ip_info ip; wifi_get_ip_info(STATION_IF, &ip); return IPAddress(ip.netmask.addr); } IPAddress ESP8266WiFiClass::gatewayIP() { struct ip_info ip; wifi_get_ip_info(STATION_IF, &ip); return IPAddress(ip.gw.addr); } char* ESP8266WiFiClass::SSID() { static struct station_config conf; wifi_station_get_config(&conf); return reinterpret_cast(conf.ssid); } uint8_t* ESP8266WiFiClass::BSSID(void) { static struct station_config conf; wifi_station_get_config(&conf); return reinterpret_cast(conf.bssid); } String ESP8266WiFiClass::BSSIDstr(void) { static struct station_config conf; char mac[18] = {0}; wifi_station_get_config(&conf); sprintf(mac,"%02X:%02X:%02X:%02X:%02X:%02X", conf.bssid[0], conf.bssid[1], conf.bssid[2], conf.bssid[3], conf.bssid[4], conf.bssid[5]); return String(mac); } int32_t ESP8266WiFiClass::channel(void) { return wifi_get_channel(); } int32_t ESP8266WiFiClass::RSSI(void) { return wifi_station_get_rssi(); } extern "C" { typedef STAILQ_HEAD(, bss_info) bss_info_head_t; } void ESP8266WiFiClass::_scanDone(void* result, int status) { if (status != OK) { ESP8266WiFiClass::_scanCount = 0; ESP8266WiFiClass::_scanResult = 0; } else { int i = 0; bss_info_head_t* head = reinterpret_cast(result); for (bss_info* it = STAILQ_FIRST(head); it; it = STAILQ_NEXT(it, next), ++i); ESP8266WiFiClass::_scanCount = i; if (i == 0) { ESP8266WiFiClass::_scanResult = 0; } else { bss_info* copied_info = new bss_info[i]; i = 0; for (bss_info* it = STAILQ_FIRST(head); it; it = STAILQ_NEXT(it, next), ++i) { memcpy(copied_info + i, it, sizeof(bss_info)); } ESP8266WiFiClass::_scanResult = copied_info; } } esp_schedule(); } int8_t ESP8266WiFiClass::scanNetworks() { if(_useApMode) { // turn on AP+STA mode mode(WIFI_AP_STA); } else { // turn on STA mode mode(WIFI_STA); } int status = wifi_station_get_connect_status(); if (status != STATION_GOT_IP && status != STATION_IDLE) { disconnect(); } if (ESP8266WiFiClass::_scanResult) { delete[] reinterpret_cast(ESP8266WiFiClass::_scanResult); ESP8266WiFiClass::_scanResult = 0; ESP8266WiFiClass::_scanCount = 0; } struct scan_config config; config.ssid = 0; config.bssid = 0; config.channel = 0; config.show_hidden = 0; wifi_station_scan(&config, reinterpret_cast(&ESP8266WiFiClass::_scanDone)); esp_yield(); return ESP8266WiFiClass::_scanCount; } void * ESP8266WiFiClass::_getScanInfoByIndex(int i) { if (!ESP8266WiFiClass::_scanResult || (size_t)i > ESP8266WiFiClass::_scanCount) { return 0; } return reinterpret_cast(ESP8266WiFiClass::_scanResult) + i; } const char* ESP8266WiFiClass::SSID(uint8_t i) { struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); if (!it) return 0; return reinterpret_cast(it->ssid); } uint8_t * ESP8266WiFiClass::BSSID(uint8_t i) { struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); if (!it) return 0; return it->bssid; } String ESP8266WiFiClass::BSSIDstr(uint8_t i) { char mac[18] = {0}; struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); if (!it) return String(""); sprintf(mac,"%02X:%02X:%02X:%02X:%02X:%02X", it->bssid[0], it->bssid[1], it->bssid[2], it->bssid[3], it->bssid[4], it->bssid[5]); return String(mac); } int32_t ESP8266WiFiClass::channel(uint8_t i) { struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); if (!it) return 0; return it->channel; } bool ESP8266WiFiClass::isHidden(uint8_t i) { struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); if (!it) return false; return (it->is_hidden != 0); } bool ESP8266WiFiClass::getNetworkInfo(uint8_t i, String &ssid, uint8_t &encType, int32_t &rssi, uint8_t* &bssid, int32_t &channel, bool &isHidden) { struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); if (!it) return false; ssid = (const char*)it->ssid; encType = encryptionType(i); rssi = it->rssi; bssid = it->bssid; // move ptr channel = it->channel; isHidden = (it->is_hidden != 0); return true; } int32_t ESP8266WiFiClass::RSSI(uint8_t i) { struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); if (!it) return 0; return it->rssi; } uint8_t ESP8266WiFiClass::encryptionType(uint8_t i) { struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); if (!it) return -1; int authmode = it->authmode; if (authmode == AUTH_OPEN) return ENC_TYPE_NONE; if (authmode == AUTH_WEP) return ENC_TYPE_WEP; if (authmode == AUTH_WPA_PSK) return ENC_TYPE_TKIP; if (authmode == AUTH_WPA2_PSK) return ENC_TYPE_CCMP; if (authmode == AUTH_WPA_WPA2_PSK) return ENC_TYPE_AUTO; return -1; } wl_status_t ESP8266WiFiClass::status() { int status = wifi_station_get_connect_status(); if (status == STATION_GOT_IP) return WL_CONNECTED; else if (status == STATION_NO_AP_FOUND) return WL_NO_SSID_AVAIL; else if (status == STATION_CONNECT_FAIL || status == STATION_WRONG_PASSWORD) return WL_CONNECT_FAILED; else if (status == STATION_IDLE) return WL_IDLE_STATUS; else return WL_DISCONNECTED; } void wifi_dns_found_callback(const char *name, ip_addr_t *ipaddr, void *callback_arg) { if (ipaddr) (*reinterpret_cast(callback_arg)) = ipaddr->addr; esp_schedule(); // resume the hostByName function } int ESP8266WiFiClass::hostByName(const char* aHostname, IPAddress& aResult) { ip_addr_t addr; aResult = static_cast(0); err_t err = dns_gethostbyname(aHostname, &addr, &wifi_dns_found_callback, &aResult); if (err == ERR_OK) { aResult = addr.addr; } else if (err == ERR_INPROGRESS) { esp_yield(); // will return here when dns_found_callback fires } return (aResult != 0) ? 1 : 0; } void ESP8266WiFiClass::beginSmartConfig() { if (_smartConfigStarted) return; if(_useApMode) { // turn on AP+STA mode mode(WIFI_AP_STA); } else { // turn on STA mode mode(WIFI_STA); } _smartConfigStarted = true; _smartConfigDone = false; //SC_TYPE_ESPTOUCH use ESPTOUCH for smartconfig, or use SC_TYPE_AIRKISS for AIRKISS smartconfig_start(SC_TYPE_ESPTOUCH, reinterpret_cast(&ESP8266WiFiClass::_smartConfigCallback), 1); } void ESP8266WiFiClass::stopSmartConfig() { if (!_smartConfigStarted) return; smartconfig_stop(); _smartConfigStarted = false; } bool ESP8266WiFiClass::smartConfigDone() { if (!_smartConfigStarted) return false; return _smartConfigDone; } void ESP8266WiFiClass::_smartConfigCallback(uint32_t st, void* result) { sc_status status = (sc_status) st; if (status == SC_STATUS_LINK) { station_config* sta_conf = reinterpret_cast(result); wifi_station_set_config(sta_conf); wifi_station_disconnect(); wifi_station_connect(); WiFi._smartConfigDone = true; } else if (status == SC_STATUS_LINK_OVER) { WiFi.stopSmartConfig(); } } void ESP8266WiFiClass::printDiag(Print& p) { const char* modes[] = {"NULL", "STA", "AP", "STA+AP"}; p.print("Mode: "); p.println(modes[wifi_get_opmode()]); const char* phymodes[] = {"", "B", "G", "N"}; p.print("PHY mode: "); p.println(phymodes[(int) wifi_get_phy_mode()]); p.print("Channel: "); p.println(wifi_get_channel()); p.print("AP id: "); p.println(wifi_station_get_current_ap_id()); p.print("Status: "); p.println(wifi_station_get_connect_status()); p.print("Auto connect: "); p.println(wifi_station_get_auto_connect()); static struct station_config conf; wifi_station_get_config(&conf); const char* ssid = reinterpret_cast(conf.ssid); p.print("SSID ("); p.print(strlen(ssid)); p.print("): "); p.println(ssid); const char* passphrase = reinterpret_cast(conf.password); p.print("Passphrase ("); p.print(strlen(passphrase)); p.print("): "); p.println(passphrase); p.print("BSSID set: "); p.println(conf.bssid_set); } size_t ESP8266WiFiClass::_scanCount = 0; void* ESP8266WiFiClass::_scanResult = 0; ESP8266WiFiClass WiFi;