/* ESP8266WiFiSTA.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 "ESP8266WiFi.h" #include "ESP8266WiFiGeneric.h" #include "ESP8266WiFiAP.h" #include 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 // LWIP_VERSION_* } #include "debug.h" // ----------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------- Private functions ------------------------------------------------ // ----------------------------------------------------------------------------------------------------------------------- static bool softap_config_equal(const softap_config& lhs, const softap_config& rhs); /** * compare two AP configurations * @param lhs softap_config * @param rhs softap_config * @return equal */ static bool softap_config_equal(const softap_config& lhs, const softap_config& rhs) { if(lhs.ssid_len != rhs.ssid_len) { return false; } if(memcmp(lhs.ssid, rhs.ssid, lhs.ssid_len) != 0) { return false; } if(strncmp(reinterpret_cast(lhs.password), reinterpret_cast(rhs.password), sizeof(softap_config::password)) != 0) { return false; } if(lhs.channel != rhs.channel) { return false; } if(lhs.ssid_hidden != rhs.ssid_hidden) { return false; } if(lhs.max_connection != rhs.max_connection) { return false; } if(lhs.beacon_interval != rhs.beacon_interval) { return false; } if(lhs.authmode != rhs.authmode) { return false; } return true; } // ----------------------------------------------------------------------------------------------------------------------- // ----------------------------------------------------- AP function ----------------------------------------------------- // ----------------------------------------------------------------------------------------------------------------------- /** * Set up an access point * @param ssid Pointer to the SSID (max 32 char). * @param psk For WPA2 min 8 char max 64 char, for open use "" or NULL. * @param channel WiFi channel number, 1 - 13. * @param ssid_hidden Network cloaking (0 = broadcast SSID, 1 = hide SSID) * @param max_connection Max simultaneous connected clients, 0 - 8. https://bbs.espressif.com/viewtopic.php?f=46&t=481&p=1832&hilit=max_connection#p1832 * @param beacon_interval set arbitrary beacon interval (influences DTIM) */ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* psk, int channel, int ssid_hidden, int max_connection, int beacon_interval) { if(!WiFi.enableAP(true)) { // enable AP failed DEBUG_WIFI("[AP] enableAP failed!\n"); return false; } size_t ssid_len = ssid ? strlen(ssid) : 0; if(ssid_len == 0 || ssid_len > 32) { DEBUG_WIFI("[AP] SSID length %zu, too long or missing!\n", ssid_len); return false; } size_t psk_len = psk ? strlen(psk) : 0; if(psk_len > 0 && (psk_len > 64 || psk_len < 8)) { DEBUG_WIFI("[AP] fail psk length %zu, too long or short!\n", psk_len); return false; } bool ret = true; struct softap_config conf; memcpy(reinterpret_cast(conf.ssid), ssid, ssid_len); if (ssid_len < sizeof(conf.ssid)) { conf.ssid[ssid_len] = 0; } conf.ssid_len = ssid_len; if(psk_len) { conf.authmode = AUTH_WPA2_PSK; memcpy(reinterpret_cast(conf.password), psk, psk_len); if (psk_len < sizeof(conf.password)) { conf.password[psk_len] = 0; } } else { conf.authmode = AUTH_OPEN; conf.password[0] = 0; } conf.channel = channel; conf.ssid_hidden = ssid_hidden; conf.max_connection = max_connection; conf.beacon_interval = beacon_interval; struct softap_config conf_compare; if(WiFi._persistent){ wifi_softap_get_config_default(&conf_compare); } else { wifi_softap_get_config(&conf_compare); } if(!softap_config_equal(conf, conf_compare)) { ETS_UART_INTR_DISABLE(); if(WiFi._persistent) { ret = wifi_softap_set_config(&conf); } else { ret = wifi_softap_set_config_current(&conf); } ETS_UART_INTR_ENABLE(); if(!ret) { DEBUG_WIFI("[AP] set_config failed!\n"); return false; } } else { DEBUG_WIFI("[AP] softap config unchanged\n"); } wifi_softap_dhcps_stop(); // check IP config struct ip_info ip; if(wifi_get_ip_info(SOFTAP_IF, &ip)) { if(ip.ip.addr == 0x00000000) { DEBUG_WIFI("[AP] IP config Invalid resetting...\n"); ret = softAPConfig( IPAddress(192, 168, 4, 1), IPAddress(192, 168, 4, 1), IPAddress(255, 255, 255, 0)); } } else { DEBUG_WIFI("[AP] wifi_get_ip_info failed!\n"); ret = false; } wifi_softap_dhcps_start(); return ret; } bool ESP8266WiFiAPClass::softAP(const String& ssid, const String& psk, int channel, int ssid_hidden, int max_connection, int beacon_interval) { return softAP(ssid.c_str(), psk.c_str(), channel, ssid_hidden, max_connection, beacon_interval); } /** * Configure access point * @param local_ip access point IP * @param gateway gateway IP (0.0.0.0 to disable) * @param subnet subnet mask */ bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet) { DEBUG_WIFI("[APConfig] local_ip: %s gateway: %s subnet: %s\n", local_ip.toString().c_str(), gateway.toString().c_str(), subnet.toString().c_str()); if(!WiFi.enableAP(true)) { // enable AP failed DEBUG_WIFI("[APConfig] enableAP failed!\n"); return false; } bool ret = true; if ( !local_ip.isV4() || !subnet.isV4() #if LWIP_IPV6 // uninitialized gateway is valid || gateway.isV6() #endif ) { return false; } struct ip_info info; info.ip.addr = local_ip.v4(); info.gw.addr = gateway.v4(); info.netmask.addr = subnet.v4(); // use SDK function for dhcps, not just server.begin() // setting info with static IPs will fail otherwise // (TODO: dhcps_flag seems to store 'SDK' DHCPs status) wifi_softap_dhcps_stop(); if(!wifi_set_ip_info(SOFTAP_IF, &info)) { DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n"); ret = false; } struct dhcps_lease dhcp_lease; dhcp_lease.enable = true; IPAddress ip = local_ip; ip[3] += 99; dhcp_lease.start_ip.addr = ip.v4(); DEBUG_WIFI("[APConfig] DHCP IP start: %s\n", ip.toString().c_str()); ip[3] += 100; dhcp_lease.end_ip.addr = ip.v4(); DEBUG_WIFI("[APConfig] DHCP IP end: %s\n", ip.toString().c_str()); auto& server = softAPDhcpServer(); if(!server.set_dhcps_lease(&dhcp_lease)) { DEBUG_WIFI("[APConfig] server set_dhcps_lease failed!\n"); ret = false; } // send ROUTER option with netif's gateway IP server.setRouter(true); wifi_softap_dhcps_start(); // check config if(wifi_get_ip_info(SOFTAP_IF, &info)) { if(info.ip.addr == 0x00000000) { DEBUG_WIFI("[APConfig] IP config Invalid?!\n"); ret = false; } else if(local_ip.v4() != info.ip.addr) { DEBUG_WIFI("[APConfig] IP config not set correct?! new IP: %s\n", IPAddress(info.ip.addr).toString().c_str()); ret = false; } } else { DEBUG_WIFI("[APConfig] wifi_get_ip_info failed!\n"); ret = false; } return ret; } /** * Disconnect from the network (close AP) * @param wifioff disable mode? * @return operation success */ bool ESP8266WiFiAPClass::softAPdisconnect(bool wifioff) { bool ret; struct softap_config conf; *conf.ssid = 0; *conf.password = 0; conf.authmode = AUTH_OPEN; ETS_UART_INTR_DISABLE(); if(WiFi._persistent) { ret = wifi_softap_set_config(&conf); } else { ret = wifi_softap_set_config_current(&conf); } ETS_UART_INTR_ENABLE(); if(!ret) { DEBUG_WIFI("[APdisconnect] set_config failed!\n"); } if(ret && wifioff) { ret = WiFi.enableAP(false); } return ret; } /** * Get the count of the Station / client that are connected to the softAP interface * @return Stations count */ uint8_t ESP8266WiFiAPClass::softAPgetStationNum() { return wifi_softap_get_station_num(); } /** * Get the softAP interface IP address. * @return IPAddress softAP IP */ IPAddress ESP8266WiFiAPClass::softAPIP() { struct ip_info ip; wifi_get_ip_info(SOFTAP_IF, &ip); return IPAddress(ip.ip.addr); } /** * Get the softAP broadcast ip address. * @return IPAddress softAP broadcast IP */ IPAddress ESP8266WiFiAPClass::softAPbroadcastIP() { struct ip_info ip; wifi_get_ip_info(SOFTAP_IF, &ip); return IPAddress(ip.ip.addr | ~(ip.netmask.addr)); } /** * Get the softAP interface MAC address. * @param mac pointer to uint8_t array with length WL_MAC_ADDR_LENGTH * @return pointer to uint8_t* */ uint8_t* ESP8266WiFiAPClass::softAPmacAddress(uint8_t* mac) { wifi_get_macaddr(SOFTAP_IF, mac); return mac; } /** * Get the softAP interface MAC address. * @return String mac */ String ESP8266WiFiAPClass::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); } /** * Get the configured(Not-In-Flash) softAP SSID name. * @return String SSID. */ String ESP8266WiFiAPClass::softAPSSID() const { struct softap_config config; wifi_softap_get_config(&config); String ssid; ssid.concat(reinterpret_cast(config.ssid), config.ssid_len); return ssid; } /** * Get the configured(Not-In-Flash) softAP PSK. * @return String psk. */ String ESP8266WiFiAPClass::softAPPSK() const { struct softap_config config; wifi_softap_get_config(&config); char* ptr = reinterpret_cast(config.password); String psk; psk.concat(ptr, strnlen(ptr, sizeof(config.password))); return psk; } /** * Get the static DHCP server instance attached to the softAP interface * @return DhcpServer instance. */ DhcpServer& ESP8266WiFiAPClass::softAPDhcpServer() { return getNonOSDhcpServer(); }