Commit 4c8f5645 authored by Pontus Oldberg's avatar Pontus Oldberg

Initial commit

parents
# A GitHub Manifesto
### Notes on contributing to my repositories
Jack Christensen
Jan 2018
Surely Git and GitHub are wonderful tools. They make coding and collaboration so much easier. I'm equally impressed with the open source movement, and with the Arduino ecosystem in particular.
I'm just one guy, mostly a hobbyist. Posting my projects to GitHub is my way of giving back a little to the community. It's very gratifying that some of my code has received a modicum of popularity.
Like many things, this has been somewhat of a double-edged sword. Especially since I tend to be a pretty busy guy with many varied interests.
First, I am always interested in bug reports. Please raise an issue in the appropriate repository and please please please include a good, concise description of the issue and a Short, Self Contained, Correct (Compilable), Example (see [sscce.org](http://www.sscce.org/)). I will need to be able to reproduce the issue, with minimal hardware, and without installing a dozen other libraries. I work exclusively with the AVR architecture so most times I will not be able to reproduce issues on other platforms. (There have been one or two occasions where relatively simple changes have been made to accommodate another platform; I am not necessarily averse to these.)
Second, bug reports should always be for problems with *my* code. I will not use GitHub to help you with *your* code, even if you happen to be using one of my libraries. Please use the [Arduino forum](https://forum.arduino.cc/) or other such venue instead.
Finally, pull requests can be problematic, especially if they represent enhancements rather than fixes. I seldom intend my code to be all things to all people. This is mostly a hobby activity and I have very limited bandwidth. Reviewing and managing PRs requires time that I do not often have. Sometimes a PR will take a library in a direction that I'm not interested in. Sometimes a PR will be counter to my original design intent. No doubt the author of a PR thinks that their new feature is the best thing since canned beer, but if I don't happen to share that opinion, then I'll decline it. OTOH, I am certainly capable of making stupid mistakes and missing absolutely fundamental things, and I do appreciate it when these are pointed out.
All this to say, that if I do decline a request, please do not take it personally. Feel free to consider it my problem, not yours. At the end of the day, it's my code, and I reserve the right to decline issues or PRs for any reason, or for no reason at all. But here is the beauty of open source. You can always fork the repository and have your way with it.
This diff is collapsed.
cWiFi KEYWORD1
begin KEYWORD2
name=cWiFi
version=0.1
author=Pontus Oldberg <pontus.oldberg@invector.se>
maintainer=Pontus Oldberg <pontus.oldberg@invector.se>
sentence=Arduino WIFI library for the ESP8285 AT WiFi controller on the Challenger board
paragraph=
category=Network
url=
architectures=*
/*
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 <list>
#include <string.h>
#include "cWiFi.h"
#include "ESP8266WiFiGeneric.h"
//#include "WiFiClient.h"
//#include "WiFiUdp.h"
//#include "debug.h"
#include "include/WiFiState.h"
// -----------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------- Generic WiFi function -----------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
bool ESP8266WiFiGenericClass::_persistent = true;
WiFiMode_t ESP8266WiFiGenericClass::_forceSleepLastMode = WIFI_OFF;
ESP8266WiFiGenericClass::ESP8266WiFiGenericClass()
{
cWiFi._status = WL_DISCONNECTED;
cWiFi._ip_valid = false;
}
/**
* Return the current channel associated with the network
* @return channel (1-13)
*/
int32_t ESP8266WiFiGenericClass::channel(void) {
cWiFi.send_at("+CWJAP?");
return cWiFi._channel;
}
/**
* set Sleep mode
* @param type sleep_type_t
* @return bool
* Set Command:
*
* AT+SLEEPWKCFG=<wakeup source>,<param1>[,<param2>]
*
* Response:
*
* OK
*
* Parameters:
*
* <wakeup source>:
* 0: Wakeup by timer.
* 1: Wakeup by UART. (Only Support ESP32)
* 2: Wakeup by GPIO.
*
* <param1>:
* If the wakeup source is timer, this param is time before wakeup, the units is millisecond.
* If the wakeup source is UART. this param is the Uart number.
* If the wakeup source is GPIO, the param is the GPIO number.
*
* <param2>:
* If the wakeup source is GPIO, the param is the wakeup level, 0: Low level, 1: High level.
*
* Example:
*
* AT+SLEEPWKCFG=0,1000 // Timer wakeup
* AT+SLEEPWKCFG=1,1 // Uart1 wakeup, Only Support ESP32
* AT+SLEEPWKCFG=2,12,0 // GPIO12 wakeup, low level.
*/
bool ESP8266WiFiGenericClass::setSleepMode(WiFiSleepType_t type, uint8_t listenInterval) {
bool resp;
if (type == WIFI_LIGHT_SLEEP && listenInterval)
cWiFi.send_at("AT+SLEEPWKCFG=0,%d", listenInterval * 100);
resp = cWiFi.send_at("+SLEEP=%d", (int)type);
if (resp)
cWiFi._sleep_type = type;
return resp;
}
/**
* get Sleep mode
* @return sleep_type_t
*/
WiFiSleepType_t ESP8266WiFiGenericClass::getSleepMode() {
bool resp;
int ret = WIFI_NONE_SLEEP;
resp = cWiFi.send_at("+SLEEP?");
if (resp && !strncmp(cWiFi.at_ptrs[0], "+SLEEP", 10))
ret = strtol(strchr(cWiFi.at_ptrs[0], ':')+1, NULL, 10);
return (WiFiSleepType_t)ret;
}
/**
* set phy Mode
* AT+CWAPPROTO—Sets the 802.11 b/g/n protocol standard of SoftAP mode.
*
* Query Command:
*
* AT+CWAPPROTO?
*
* Response:
*
* +CWAPPROTO=<protocol>
* OK
*
* Set Command:
*
* AT+CWAPPROTO=<protocol>
*
* Response:
*
* OK
*
* Parameters:
*
* <protocol>:
* bit0: 802.11b protocol standard.
* bit1: 802.11g protocol standard.
* bit2: 802.11n protocol standard.
* @return bool
*/
bool ESP8266WiFiGenericClass::setPhyMode(WiFiPhyMode_t mode) {
return cWiFi.send_at("+CWAPPROTO=%d", (int)mode);
}
/**
* get phy Mode
* @return phy_mode_t
*/
WiFiPhyMode_t ESP8266WiFiGenericClass::getPhyMode() {
bool resp;
int mode = 0;
resp = cWiFi.send_at("+CWAPPROTO?");
if (resp && !strncmp(cWiFi.at_ptrs[0], "+CWAPPROTO", 10))
mode = strtol(strchr(cWiFi.at_ptrs[0], ':')+1, NULL, 10);
return (WiFiPhyMode_t)mode;
}
/**
* set the output power of WiFi
AT+RFPOWER-Set RF TX Power
Query Command:
AT+RFPOWER?
Function: to query the RF TX Power.
Response:
+RFPOWER:<wifi_power>,<ble_adv_power>,<ble_scan_power>,<ble_conn_power>
OK
Set Command:
AT+RFPOWER=<wifi_power>[,<ble_adv_power>,<ble_scan_power>,<ble_conn_power>]
Response:
OK
Parameters:
<wifi_power>: range [40, 82], the unit is 0.25dBm, for example, if the value is 78, then RF max power is 78*0.25 dBm=19.5dBm
* @param dBm max: +20.5dBm min: 0dBm
*/
void ESP8266WiFiGenericClass::setOutputPower(float dBm) {
if(dBm > 20.5) {
dBm = 20.5;
} else if(dBm < 0) {
dBm = 0;
}
int val = (dBm*4.0f);
cWiFi.send_at("+RFPOWER=%d", val);
}
float ESP8266WiFiGenericClass::getOutputPower() {
bool resp;
float dBm = 0;
resp = cWiFi.send_at("+RFPOWER?");
if (resp && !strncmp(cWiFi.at_ptrs[0], "+RFPOWER", 8))
dBm = strtof(strchr(cWiFi.at_ptrs[0], ':')+1, NULL);
return dBm*0.25;
}
/**
* store WiFi config in SDK flash area
* @param persistent
*/
void ESP8266WiFiGenericClass::persistent(bool persistent) {
cWiFi.send_at("+SYSSTORE=%d", (int)persistent);
}
/**
* gets the persistent state
* @return bool
*/
bool ESP8266WiFiGenericClass::getPersistent(){
bool resp;
int pers = 0;
resp = cWiFi.send_at("+SYSSTORE?");
if (resp && !strncmp(cWiFi.at_ptrs[0], "+SYSSTORE", 9))
pers = strtol(strchr(cWiFi.at_ptrs[0], ':')+1, NULL, 10);
return pers;
}
/**
* set new mode
* @param m WiFiMode_t
*/
bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) {
bool ret;
if (m == WIFI_SHUTDOWN) {
return false;
}
else if (m == WIFI_RESUME) {
return false;
}
else if (m & ~(WIFI_STA | WIFI_AP))
// any other bits than legacy disallowed
return false;
// Send the request to update the mode to the modem
ret = cWiFi.send_at("+CWMODE=%d", (int)m);
if (ret) {
cWiFi._mode = m;
}
return ret;
}
/**
* get WiFi mode
* @return WiFiMode
*/
WiFiMode_t ESP8266WiFiGenericClass::getMode() {
cWiFi.send_at("+CWMODE?");
return _mode;
}
/**
* control STA mode
* @param enable bool
* @return ok
*/
bool ESP8266WiFiGenericClass::enableSTA(bool enable) {
WiFiMode_t currentMode = getMode();
bool isEnabled = ((currentMode & WIFI_STA) != 0);
if (isEnabled == enable)
return true;
if (enable)
return mode((WiFiMode_t)(currentMode | WIFI_STA));
return mode((WiFiMode_t)(currentMode & (~WIFI_STA)));
}
/**
* control AP mode
* @param enable bool
* @return ok
*/
bool ESP8266WiFiGenericClass::enableAP(bool enable){
WiFiMode_t currentMode = getMode();
bool isEnabled = ((currentMode & WIFI_AP) != 0);
if(isEnabled != enable) {
if(enable) {
return mode((WiFiMode_t)(currentMode | WIFI_AP));
} else {
return mode((WiFiMode_t)(currentMode & (~WIFI_AP)));
}
} else {
return true;
}
}
/**
* Disable WiFi for x us when value is not 0
* @param sleep_time_in_us
* Set Command:
*
* AT+GSLP=<time>
*
* Response:
*
* <time>
*
* OK
*
* @return ok
*/
bool ESP8266WiFiGenericClass::forceSleepBegin(uint32 sleepUs) {
bool resp = false;
if (sleepUs) {
resp = cWiFi.send_at("+GSLP=%u", sleepUs / 1000);
}
return resp;
}
/**
* wake up WiFi Modem
* The only way we have to wake the modem from deepsleep is to reset and
* start over.
* @return ok
*/
bool ESP8266WiFiGenericClass::forceSleepWake() {
cWiFi.init();
return true;
}
/**
* Get listen interval of maximum sleep level for modem sleep and light sleep.
* The listen interval is returned by AT+CWJAP? among other things.
* https://github.com/espressif/esp-at/blob/master/docs/en/get-started/ESP_AT_Commands_Set.md#WiFi-AT
* @return interval
*/
uint8_t ESP8266WiFiGenericClass::getListenInterval () {
#if 0
#ifndef NONOSDK3V0
return 0;
#else
return wifi_get_listen_interval();
#endif
#endif
return 0;
}
/**
* Get sleep level of modem sleep and light sleep
* @return true if max level
*/
bool ESP8266WiFiGenericClass::isSleepLevelMax () {
#if 0
#ifndef NONOSDK3V0
return false;
#else
return wifi_get_sleep_level() == MAX_SLEEP_T;
#endif
#endif
return false;
}
// -----------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------ Generic Network function ---------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg);
static bool _dns_lookup_pending = 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 0
*/
int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult)
{
return hostByName(aHostname, aResult, 10000);
}
int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms)
{
ip_addr_t addr;
bool ret;
aResult = static_cast<uint32_t>(INADDR_NONE);
if(aResult.fromString(aHostname)) {
// Host name is a IP address use it!
DEBUG_WIFI_GENERIC("[hostByName] Host: %s is a IP!\n", aHostname);
return 1;
}
DEBUG_WIFI_GENERIC("[hostByName] request IP for: %s\n", aHostname);
ret = cWiFi.send_at("+CIPDOMAIN=\"%s\"", aHostname);
if (ret) {
aResult = cWiFi._hostIP;
}
return (int)ret;
}
uint32_t ESP8266WiFiGenericClass::shutdownCRC (const WiFiState* state)
{
#if 0
return state? crc32(&state->state, sizeof(state->state)): 0;
#endif
return 0;
}
bool ESP8266WiFiGenericClass::shutdownValidCRC (const WiFiState* state)
{
#if 0
return state && (crc32(&state->state, sizeof(state->state)) == state->crc);
#endif
return 0;
}
bool ESP8266WiFiGenericClass::shutdown (uint32 sleepUs, WiFiState* state)
{
#if 0
bool persistent = _persistent;
WiFiMode_t before_off_mode = getMode();
if ((before_off_mode & WIFI_STA) && state)
{
bool ret = wifi_get_ip_info(STATION_IF, &state->state.ip);
if (!ret)
{
DEBUG_WIFI("core: error with wifi_get_ip_info(STATION_IF)\n");
return false;
}
memset(state->state.fwconfig.bssid, 0xff, 6);
ret = wifi_station_get_config(&state->state.fwconfig);
if (!ret)
{
DEBUG_WIFI("core: error with wifi_station_get_config\n");
return false;
}
state->state.channel = wifi_get_channel();
}
// disable persistence in FW so in case of power failure
// it doesn't wake up in off mode.
// persistence state will be restored on WiFi resume.
cWiFi.persistent(false);
if (!cWiFi.forceSleepBegin(sleepUs))
{
// WIFI_OFF mode set by forceSleepBegin()
DEBUG_WIFI("core: error with forceSleepBegin()\n");
cWiFi.mode(before_off_mode);
cWiFi.persistent(persistent);
return false;
}
// WiFi is now in force-sleep mode
if (state)
{
// finish filling state and process crc
state->state.persistent = persistent;
state->state.mode = before_off_mode;
uint8_t i = 0;
for (auto& ntp: state->state.ntp)
{
#if LWIP_VERSION_MAJOR == 1
ntp = sntp_getserver(i++);
#else
ntp = *sntp_getserver(i++);
#endif
}
i = 0;
for (auto& dns: state->state.dns)
dns = cWiFi.dnsIP(i++);
state->crc = shutdownCRC(state);
DEBUG_WIFI("core: state is saved\n");
}
return true;
#endif
return false;
}
bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState* state)
{
#if 0
if (wifi_fpm_get_sleep_type() != NONE_SLEEP_T) {
wifi_fpm_do_wakeup();
wifi_fpm_close();
}
if (!state || shutdownCRC(state) != state->crc)
{
DEBUG_WIFI("core: resume: no state or bad crc\n");
return false;
}
persistent(state->state.persistent);
if (!mode(state->state.mode))
{
DEBUG_WIFI("core: resume: can't set wifi mode to %d\n", state->state.mode);
return false;
}
if (state->state.mode & WIFI_STA)
{
IPAddress local(state->state.ip.ip);
if (local)
{
DEBUG_WIFI("core: resume: static address '%s'\n", local.toString().c_str());
cWiFi.config(state->state.ip.ip, state->state.ip.gw, state->state.ip.netmask, state->state.dns[0], state->state.dns[1]);
uint8_t i = 0;
for (CONST auto& ntp: state->state.ntp)
{
IPAddress ip(ntp);
if (ip.isSet())
{
DEBUG_WIFI("core: resume: start SNTP, server='%s'\n", ip.toString().c_str());
sntp_setserver(i++, &ntp);
}
}
}
// state->state.fwconfig.bssid is not real bssid (it's what user may have provided when bssid_set==1)
if (cWiFi.begin((const char*)state->state.fwconfig.ssid,
(const char*)state->state.fwconfig.password,
state->state.channel,
nullptr/*(const uint8_t*)state->state.fwconfig.bssid*/, // <- try with gw's mac address?
true) == WL_CONNECT_FAILED)
{
DEBUG_WIFI("core: resume: cWiFi.begin failed\n");
return false;
}
}
if (state->state.mode & WIFI_AP)
{
DEBUG_WIFI("core: resume AP mode TODO\n");
return false;
}
// success, invalidate saved state
state->crc++;
return true;
#endif
return false;
}
//meant to be called from user-defined ::preinit()
void ESP8266WiFiGenericClass::preinitWiFiOff () {
// https://github.com/esp8266/Arduino/issues/2111#issuecomment-224251391
// WiFi.persistent(false);
// WiFi.mode(WIFI_OFF);
// WiFi.forceSleepBegin();
//WiFi.mode(WIFI_OFF) equivalent:
// datasheet:
// Set Wi-Fi working mode to Station mode, SoftAP
// or Station + SoftAP, and do not update flash
// (not persistent)
//wifi_set_opmode_current(WIFI_OFF);
//WiFi.forceSleepBegin(/*default*/0) equivalent:
// sleep forever until wifi_fpm_do_wakeup() is called