Browse Source

ethernet and wifi are working for ESP32

pull/1549/head
lumapu 10 months ago
parent
commit
89f7b4f1c6
  1. 32
      src/app.cpp
  2. 39
      src/app.h
  3. 4
      src/appInterface.h
  4. 3
      src/config/settings.h
  5. 179
      src/eth/ahoyeth.cpp
  6. 65
      src/eth/ahoyeth.h
  7. 77
      src/network/AhoyEthernet.h
  8. 43
      src/network/AhoyNetwork.h
  9. 20
      src/network/AhoyNetworkHelper.cpp
  10. 12
      src/network/AhoyNetworkHelper.h
  11. 11
      src/network/AhoyWifiAp.h
  12. 112
      src/network/AhoyWifiEsp32.h
  13. 14
      src/publisher/pubMqtt.h
  14. 4
      src/web/RestApi.h
  15. 490
      src/wifi/ahoywifi.cpp
  16. 100
      src/wifi/ahoywifi.h

32
src/app.cpp

@ -53,17 +53,16 @@ void app::setup() {
mCmtRadio.setup(&mConfig->serial.debug, &mConfig->serial.privacyLog, &mConfig->serial.printWholeTrace, mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb, mConfig->sys.region);
}
#endif
#ifdef ETHERNET
delay(1000);
mEth.setup(mConfig, &mTimestamp, [this](bool gotIp) { this->onNetwork(gotIp); }, [this](bool gotTime) { this->onNtpUpdate(gotTime); });
mNetwork = (AhoyNetwork*) new AhoyEthernet();
#else
mNetwork = (AhoyNetwork*) new AhoyWifi();
#endif // ETHERNET
#if !defined(ETHERNET)
mWifi.setup(mConfig, &mTimestamp, [this](bool gotIp) { this->onNetwork(gotIp); }, [this](bool gotTime) { this->onNtpUpdate(gotTime); });
#if !defined(AP_ONLY)
everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi), "wifiL");
#endif
#endif /* defined(ETHERNET) */
mNetwork->setup(mConfig, &mTimestamp, [this](bool gotIp) { this->onNetwork(gotIp); }, [this](bool gotTime) { this->onNtpUpdate(gotTime); });
mNetwork->begin();
everySec(std::bind(&AhoyNetwork::tickNetworkLoop, mNetwork), "net");
esp_task_wdt_reset();
@ -89,7 +88,7 @@ void app::setup() {
#if defined(ENABLE_MQTT)
mMqttEnabled = (mConfig->mqtt.broker[0] > 0);
if (mMqttEnabled) {
mMqtt.setup(&mConfig->mqtt, mConfig->sys.deviceName, mVersion, &mSys, &mTimestamp, &mUptime);
mMqtt.setup(this, &mConfig->mqtt, mConfig->sys.deviceName, mVersion, &mSys, &mTimestamp, &mUptime);
mMqtt.setSubscriptionCb(std::bind(&app::mqttSubRxCb, this, std::placeholders::_1));
mCommunication.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); });
}
@ -171,12 +170,6 @@ void app::onNetwork(bool gotIp) {
mMqttReconnect = true;
mSunrise = 0; // needs to be set to 0, to reinstall sunrise and ivComm tickers!
once(std::bind(&app::tickNtpUpdate, this), 2, "ntp2");
#if !defined(ETHERNET)
if (WIFI_AP == WiFi.getMode()) {
mMqttEnabled = false;
}
everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi), "wifiL");
#endif /* !defined(ETHERNET) */
}
//-----------------------------------------------------------------------------
@ -248,17 +241,10 @@ void app::updateNtp(void) {
void app::tickNtpUpdate(void) {
uint32_t nxtTrig = 5; // default: check again in 5 sec
#if defined(ETHERNET)
if (!mNtpReceived)
mEth.updateNtpTime();
mNetwork->updateNtpTime();
else
mNtpReceived = false;
#else
if (!mNtpReceived)
mWifi.updateNtpTime();
else
mNtpReceived = false;
#endif
updateNtp();
nxtTrig = mConfig->ntp.interval * 60; // check again in 12h

39
src/app.h

@ -37,9 +37,9 @@
#include "web/web.h"
#include "hm/Communication.h"
#if defined(ETHERNET)
#include "eth/ahoyeth.h"
#include "network/AhoyEthernet.h"
#else /* defined(ETHERNET) */
#include "wifi/ahoywifi.h"
#include "network/AhoyWifiEsp32.h"
#include "utils/improv.h"
#endif /* defined(ETHERNET) */
@ -164,31 +164,30 @@ class app : public IApp, public ah::Scheduler {
#if !defined(ETHERNET)
void scanAvailNetworks() override {
mWifi.scanAvailNetworks();
mNetwork->scanAvailNetworks();
}
bool getAvailNetworks(JsonObject obj) override {
return mWifi.getAvailNetworks(obj);
return mNetwork->getAvailNetworks(obj);
}
void setupStation(void) override {
mWifi.setupStation();
mNetwork->begin();
}
void setStopApAllowedMode(bool allowed) override {
/*void setStopApAllowedMode(bool allowed) override {
mWifi.setStopApAllowedMode(allowed);
}
String getStationIp(void) override {
return mWifi.getStationIp();
}
}*/
bool getWasInCh12to14(void) const override {
return mWifi.getWasInCh12to14();
return false; // @todo mWifi.getWasInCh12to14();
}
#endif /* !defined(ETHERNET) */
String getIp(void) override {
return mNetwork->getIp();
}
void setRebootFlag() override {
once(std::bind(&app::tickReboot, this), 3, "rboot");
}
@ -295,13 +294,7 @@ class app : public IApp, public ah::Scheduler {
DPRINT(DBG_DEBUG, F("setTimestamp: "));
DBGPRINTLN(String(newTime));
if(0 == newTime)
{
#if defined(ETHERNET)
mEth.updateNtpTime();
#else /* defined(ETHERNET) */
mWifi.updateNtpTime();
#endif /* defined(ETHERNET) */
}
mNetwork->updateNtpTime();
else
Scheduler::setTimestamp(newTime);
}
@ -414,11 +407,7 @@ class app : public IApp, public ah::Scheduler {
bool mShowRebootRequest = false;
#if defined(ETHERNET)
ahoyeth mEth;
#else /* defined(ETHERNET) */
ahoywifi mWifi;
#endif /* defined(ETHERNET) */
AhoyNetwork *mNetwork;
WebType mWeb;
RestApiType mApi;
Protection *mProtection = nullptr;

4
src/appInterface.h

@ -29,10 +29,10 @@ class IApp {
virtual void scanAvailNetworks() = 0;
virtual bool getAvailNetworks(JsonObject obj) = 0;
virtual void setupStation(void) = 0;
virtual void setStopApAllowedMode(bool allowed) = 0;
virtual String getStationIp(void) = 0;
//virtual void setStopApAllowedMode(bool allowed) = 0;
virtual bool getWasInCh12to14(void) const = 0;
#endif /* defined(ETHERNET) */
virtual String getIp(void) = 0;
virtual uint32_t getUptime() = 0;
virtual uint32_t getTimestamp() = 0;

3
src/config/settings.h

@ -91,10 +91,11 @@ typedef struct {
char stationSsid[SSID_LEN];
char stationPwd[PWD_LEN];
bool isHidden;
#else
cfgEth_t eth;
#endif /* !defined(ETHERNET) */
cfgIp_t ip;
cfgEth_t eth;
} cfgSys_t;
typedef struct {

179
src/eth/ahoyeth.cpp

@ -1,179 +0,0 @@
//-----------------------------------------------------------------------------
// 2023 Ahoy, https://www.mikrocontroller.net/topic/525778
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//-----------------------------------------------------------------------------
#if defined(ETHERNET)
#if defined(ESP32) && defined(F)
#undef F
#define F(sl) (sl)
#endif
#include "ahoyeth.h"
#include <ESPmDNS.h>
//-----------------------------------------------------------------------------
ahoyeth::ahoyeth()
{
// WiFi.onEvent(ESP32_W5500_event);
}
//-----------------------------------------------------------------------------
void ahoyeth::setup(settings_t *config, uint32_t *utcTimestamp, OnNetworkCB onNetworkCB, OnTimeCB onTimeCB) {
mConfig = config;
mUtcTimestamp = utcTimestamp;
mOnNetworkCB = onNetworkCB;
mOnTimeCB = onTimeCB;
mEthConnected = false;
Serial.flush();
WiFi.onEvent([this](WiFiEvent_t event, arduino_event_info_t info) -> void { this->onEthernetEvent(event, info); });
Serial.flush();
mEthSpi.begin(config->sys.eth.pinMiso, config->sys.eth.pinMosi, config->sys.eth.pinSclk, config->sys.eth.pinCs, config->sys.eth.pinIrq, config->sys.eth.pinRst);
if(mConfig->sys.ip.ip[0] != 0) {
IPAddress ip(mConfig->sys.ip.ip);
IPAddress mask(mConfig->sys.ip.mask);
IPAddress dns1(mConfig->sys.ip.dns1);
IPAddress dns2(mConfig->sys.ip.dns2);
IPAddress gateway(mConfig->sys.ip.gateway);
if(!ETH.config(ip, gateway, mask, dns1, dns2))
DPRINTLN(DBG_ERROR, F("failed to set static IP!"));
}
}
//-----------------------------------------------------------------------------
bool ahoyeth::updateNtpTime(void) {
if (!ETH.localIP())
return false;
DPRINTLN(DBG_DEBUG, F("updateNtpTime: checking udp \"connection\"...")); Serial.flush();
if (!mUdp.connected()) {
DPRINTLN(DBG_DEBUG, F("updateNtpTime: About to (re)connect...")); Serial.flush();
IPAddress timeServer;
if (!WiFi.hostByName(mConfig->ntp.addr, timeServer))
return false;
if (!mUdp.connect(timeServer, mConfig->ntp.port))
return false;
DPRINTLN(DBG_DEBUG, F("updateNtpTime: Connected...")); Serial.flush();
mUdp.onPacket([this](AsyncUDPPacket packet) {
DPRINTLN(DBG_DEBUG, F("updateNtpTime: about to handle ntp packet...")); Serial.flush();
this->handleNTPPacket(packet);
});
}
DPRINTLN(DBG_DEBUG, F("updateNtpTime: prepare packet...")); Serial.flush();
// set all bytes in the buffer to 0
memset(mUdpPacketBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
mUdpPacketBuffer[0] = 0b11100011; // LI, Version, Mode
mUdpPacketBuffer[1] = 0; // Stratum, or type of clock
mUdpPacketBuffer[2] = 6; // Polling Interval
mUdpPacketBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
mUdpPacketBuffer[12] = 49;
mUdpPacketBuffer[13] = 0x4E;
mUdpPacketBuffer[14] = 49;
mUdpPacketBuffer[15] = 52;
//Send unicast
DPRINTLN(DBG_DEBUG, F("updateNtpTime: send packet...")); Serial.flush();
mUdp.write(mUdpPacketBuffer, sizeof(mUdpPacketBuffer));
return true;
}
//-----------------------------------------------------------------------------
void ahoyeth::handleNTPPacket(AsyncUDPPacket packet) {
char buf[80];
memcpy(buf, packet.data(), sizeof(buf));
unsigned long highWord = word(buf[40], buf[41]);
unsigned long lowWord = word(buf[42], buf[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
*mUtcTimestamp = secsSince1900 - 2208988800UL; // UTC time
DPRINTLN(DBG_INFO, "[NTP]: " + ah::getDateTimeStr(*mUtcTimestamp) + " UTC");
mOnTimeCB(true);
}
//-----------------------------------------------------------------------------
void ahoyeth::welcome(String ip, String mode) {
DBGPRINTLN(F("\n\n--------------------------------"));
DBGPRINTLN(F("Welcome to AHOY!"));
DBGPRINT(F("\npoint your browser to http://"));
DBGPRINT(ip);
DBGPRINTLN(mode);
DBGPRINTLN(F("to configure your device"));
DBGPRINTLN(F("--------------------------------\n"));
}
void ahoyeth::onEthernetEvent(WiFiEvent_t event, arduino_event_info_t info) {
DPRINTLN(DBG_VERBOSE, F("[ETH]: Got event..."));
switch (event) {
case ARDUINO_EVENT_ETH_START:
DPRINTLN(DBG_VERBOSE, F("ETH Started"));
if(String(mConfig->sys.deviceName) != "")
ETH.setHostname(mConfig->sys.deviceName);
else
ETH.setHostname(F("ESP32_W5500"));
break;
case ARDUINO_EVENT_ETH_CONNECTED:
DPRINTLN(DBG_VERBOSE, F("ETH Connected"));
break;
case ARDUINO_EVENT_ETH_GOT_IP:
if (!mEthConnected) {
/*DPRINT(DBG_INFO, F("ETH MAC: "));
DBGPRINT(mEthSpi.macAddress());*/
welcome(ETH.localIP().toString(), F(" (Station)"));
mEthConnected = true;
mOnNetworkCB(true);
}
if (!MDNS.begin(mConfig->sys.deviceName)) {
DPRINTLN(DBG_ERROR, F("Error setting up MDNS responder!"));
} else {
DBGPRINT(F("mDNS established: "));
DBGPRINT(mConfig->sys.deviceName);
DBGPRINTLN(F(".local"));
}
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
DPRINTLN(DBG_INFO, F("ETH Disconnected"));
mEthConnected = false;
mUdp.close();
mOnNetworkCB(false);
break;
case ARDUINO_EVENT_ETH_STOP:
DPRINTLN(DBG_INFO, F("ETH Stopped"));
mEthConnected = false;
mUdp.close();
mOnNetworkCB(false);
break;
default:
break;
}
}
#endif /* defined(ETHERNET) */

65
src/eth/ahoyeth.h

@ -1,65 +0,0 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://github.com/lumpapu/ahoy
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#if defined(ETHERNET)
#ifndef __AHOYETH_H__
#define __AHOYETH_H__
#include <functional>
#include "../utils/dbg.h"
#include <Arduino.h>
#include <AsyncUDP.h>
#include <DNSServer.h>
#include "ethSpi.h"
#include <ETH.h>
#include "../utils/dbg.h"
#include "../config/config.h"
#include "../config/settings.h"
class app;
#define NTP_PACKET_SIZE 48
class ahoyeth {
public: /* types */
typedef std::function<void(bool)> OnNetworkCB;
typedef std::function<void(bool)> OnTimeCB;
public:
ahoyeth();
void setup(settings_t *config, uint32_t *utcTimestamp, OnNetworkCB onNetworkCB, OnTimeCB onTimeCB);
bool updateNtpTime(void);
private:
void setupEthernet();
void handleNTPPacket(AsyncUDPPacket packet);
void welcome(String ip, String mode);
void onEthernetEvent(WiFiEvent_t event, arduino_event_info_t info);
private:
//#if defined(CONFIG_IDF_TARGET_ESP32S3)
EthSpi mEthSpi;
//#endif
settings_t *mConfig = nullptr;
uint32_t *mUtcTimestamp;
AsyncUDP mUdp; // for time server
byte mUdpPacketBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
OnNetworkCB mOnNetworkCB;
OnTimeCB mOnTimeCB;
bool mEthConnected;
};
#endif /*__AHOYETH_H__*/
#endif /* defined(ETHERNET) */

77
src/network/AhoyEthernet.h

@ -6,77 +6,64 @@
#ifndef __AHOY_ETHERNET_H__
#define __AHOY_ETHERNET_H__
#if defined(ETHERNET)
#include <functional>
#include <AsyncUDP.h>
#include <ETH.h>
#include "AhoyEthernetSpi.h"
#include "AhoyEthernet.h"
#include "AhoyNetwork.h"
class AhoyEthernet : public AhoyNetwork {
public:
void begin() override {
mAp.enable();
// static IP
setupIp([this](IPAddress ip, IPAddress gateway, IPAddress mask, IPAddress dns1, IPAddress dns2) -> bool {
return ETH.config(ip, gateway, mask, dns1, dns2);
});
ETH.setHostname(mConfig->sys.deviceName);
}
void tickNetworkLoop() override {
switch(mState) {
if(mAp.isEnabled())
mAp.tickLoop();
switch(mStatus) {
case NetworkState::DISCONNECTED:
break;
}
if(mConnected) {
mConnected = false;
mOnNetworkCB(false);
mAp.enable();
}
private:
/*switch (event) {
case ARDUINO_EVENT_ETH_START:
DPRINTLN(DBG_VERBOSE, F("ETH Started"));
if(String(mConfig->sys.deviceName) != "")
ETH.setHostname(mConfig->sys.deviceName);
else
ETH.setHostname(F("ESP32_W5500"));
break;
case ARDUINO_EVENT_ETH_CONNECTED:
DPRINTLN(DBG_VERBOSE, F("ETH Connected"));
case NetworkState::CONNECTED:
break;
case ARDUINO_EVENT_ETH_GOT_IP:
if (!mEthConnected) {
welcome(ETH.localIP().toString(), F(" (Station)"));
case NetworkState::GOT_IP:
mAp.disable();
mEthConnected = true;
if(!mConnected) {
mConnected = true;
ah::welcome(ETH.localIP().toString(), F("Station"));
MDNS.begin(mConfig->sys.deviceName);
mOnNetworkCB(true);
}
if (!MDNS.begin(mConfig->sys.deviceName)) {
DPRINTLN(DBG_ERROR, F("Error setting up MDNS responder!"));
} else {
DBGPRINT(F("mDNS established: "));
DBGPRINT(mConfig->sys.deviceName);
DBGPRINTLN(F(".local"));
}
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
DPRINTLN(DBG_INFO, F("ETH Disconnected"));
mEthConnected = false;
mUdp.close();
mOnNetworkCB(false);
break;
}
}
case ARDUINO_EVENT_ETH_STOP:
DPRINTLN(DBG_INFO, F("ETH Stopped"));
mEthConnected = false;
mUdp.close();
mOnNetworkCB(false);
break;
String getIp(void) override {
return ETH.localIP().toString();
}
default:
break;
}*/
void scanAvailNetworks(void) override {};
bool getAvailNetworks(JsonObject obj) override {
return false;
}
};
#endif /*ETHERNET*/
#endif /*__AHOY_ETHERNET_H__*/

43
src/network/AhoyNetwork.h

@ -7,9 +7,11 @@
#define __AHOY_NETWORK_H__
#include "AhoyNetworkHelper.h"
#include <WiFiUdp.h>
#include <AsyncUDP.h>
#include "../config/settings.h"
#include "../utils/helper.h"
#include "AhoyWifiAp.h"
#include "AsyncJson.h"
#if defined(ESP32)
#include <ESPmDNS.h>
@ -31,9 +33,15 @@ class AhoyNetwork {
mOnNetworkCB = onNetworkCB;
mOnTimeCB = onTimeCB;
if('\0' == mConfig->sys.deviceName[0])
snprintf(mConfig->sys.deviceName, DEVNAME_LEN, "%s", DEF_DEVICE_NAME);
WiFi.hostname(mConfig->sys.deviceName);
mAp.setup(&mConfig->sys);
#if defined(ESP32)
WiFi.onEvent([this](WiFiEvent_t event) -> void {
this->OnEvent(event);
WiFi.onEvent([this](WiFiEvent_t event, arduino_event_info_t info) -> void {
OnEvent(event);
});
#else
wifiConnectHandler = WiFi.onStationModeConnected(
@ -52,15 +60,15 @@ class AhoyNetwork {
}
bool isConnected() const {
return (mStatus == NetworkState.CONNECTED);
return (mStatus == NetworkState::CONNECTED);
}
bool updateNtpTime(void) {
if(CONNECTED != mStatus)
return;
if(NetworkState::CONNECTED != mStatus)
return false;
if (!mUdp.connected()) {
IPAddress timeServer;
if (!mUdp.connected()) {
if (!WiFi.hostByName(mConfig->ntp.addr, timeServer))
return false;
if (!mUdp.connect(timeServer, mConfig->ntp.port))
@ -78,17 +86,19 @@ class AhoyNetwork {
public:
virtual void begin() = 0;
virtual void tickNetworkLoop() = 0;
virtual void connectionEvent(WiFiStatus_t status) = 0;
virtual String getIp(void) = 0;
virtual void scanAvailNetworks(void) = 0;
virtual bool getAvailNetworks(JsonObject obj) = 0;
protected:
void setupIp(void) {
void setupIp(std::function<bool(IPAddress ip, IPAddress gateway, IPAddress mask, IPAddress dns1, IPAddress dns2)> cb) {
if(mConfig->sys.ip.ip[0] != 0) {
IPAddress ip(mConfig->sys.ip.ip);
IPAddress mask(mConfig->sys.ip.mask);
IPAddress dns1(mConfig->sys.ip.dns1);
IPAddress dns2(mConfig->sys.ip.dns2);
IPAddress gateway(mConfig->sys.ip.gateway);
if(!ETH.config(ip, gateway, mask, dns1, dns2))
if(cb(ip, gateway, mask, dns1, dns2))
DPRINTLN(DBG_ERROR, F("failed to set static IP!"));
}
}
@ -169,25 +179,22 @@ class AhoyNetwork {
protected:
enum class NetworkState : uint8_t {
DISCONNECTED,
CONNECTING,
CONNECTED,
IN_AP_MODE,
GOT_IP,
IN_STA_MODE,
RESET,
SCAN_READY
GOT_IP
};
protected:
settings_t *mConfig = nullptr;
uint32_t *mUtcTimestamp = nullptr;
bool mConnected = false;
OnNetworkCB mOnNetworkCB;
OnTimeCB mOnTimeCB;
NetworkState mStatus = NetworkState.DISCONNECTED;
NetworkState mStatus = NetworkState::DISCONNECTED;
WiFiUDP mUdp; // for time server
AhoyWifiAp mAp;
AsyncUDP mUdp; // for time server
DNSServer mDns;
};

20
src/network/AhoyNetworkHelper.cpp

@ -0,0 +1,20 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://ahoydtu.de
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#include "AhoyNetworkHelper.h"
namespace ah {
void welcome(String ip, String info) {
DBGPRINTLN(F("\n\n-------------------"));
DBGPRINTLN(F("Welcome to AHOY!"));
DBGPRINT(F("\npoint your browser to http://"));
DBGPRINT(ip);
DBGPRINT(" (");
DBGPRINT(info);
DBGPRINTLN(")");
DBGPRINTLN(F("to configure your device"));
DBGPRINTLN(F("-------------------\n"));
}
}

12
src/network/AhoyNetworkHelper.h

@ -13,17 +13,7 @@
#include <DNSServer.h>
namespace ah {
void welcome(String ip, String info) {
DBGPRINTLN(F("\n\n-------------------"));
DBGPRINTLN(F("Welcome to AHOY!"));
DBGPRINT(F("\npoint your browser to http://"));
DBGPRINT(ip);
DBGPRINT(" (");
DBGPRINT(info);
DBGPRINTLN(")");
DBGPRINTLN(F("to configure your device"));
DBGPRINTLN(F("-------------------\n"));
}
void welcome(String ip, String info);
}
#endif /*__AHOY_NETWORK_HELPER_H__*/

11
src/network/AhoyWifiAp.h

@ -25,10 +25,10 @@ class AhoyWifiAp {
}
void enable() {
if(mEnabled)
return;
ah::welcome(mIp.toString(), String(F("Password: ") + String(mCfg->apPwd)));
if('\0' == mCfg->deviceName[0])
snprintf(mCfg->deviceName, DEVNAME_LEN, "%s", DEF_DEVICE_NAME);
WiFi.hostname(mCfg->deviceName);
#if defined(ETHERNET)
WiFi.mode(WIFI_AP);
@ -45,6 +45,9 @@ class AhoyWifiAp {
}
void disable() {
if(!mEnabled)
return;
mDns.stop();
WiFi.softAPdisconnect();
#if defined(ETHERNET)
@ -56,7 +59,7 @@ class AhoyWifiAp {
mEnabled = false;
}
bool getEnable() const {
bool isEnabled() const {
return mEnabled;
}

112
src/network/AhoyWifiEsp32.h

@ -0,0 +1,112 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://ahoydtu.de
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#ifndef __AHOY_Wifi_ESP32_H__
#define __AHOY_Wifi_ESP32_H__
#if defined(ESP32) && !defined(ETHERNET)
#include <functional>
#include <AsyncUDP.h>
#include <Wifi.h>
#include "AhoyNetwork.h"
class AhoyWifi : public AhoyNetwork {
public:
void begin() override {
mAp.enable();
// static IP
setupIp([this](IPAddress ip, IPAddress gateway, IPAddress mask, IPAddress dns1, IPAddress dns2) -> bool {
return WiFi.config(ip, gateway, mask, dns1, dns2);
});
WiFi.setHostname(mConfig->sys.deviceName);
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL);
WiFi.begin(mConfig->sys.stationSsid, mConfig->sys.stationPwd, WIFI_ALL_CHANNEL_SCAN);
DBGPRINT(F("connect to network '")); Serial.flush();
DBGPRINT(mConfig->sys.stationSsid);
}
void tickNetworkLoop() override {
if(mAp.isEnabled())
mAp.tickLoop();
switch(mStatus) {
case NetworkState::DISCONNECTED:
if(mConnected) {
mConnected = false;
mOnNetworkCB(false);
mAp.enable();
}
if (WiFi.softAPgetStationNum() > 0) {
DBGPRINTLN(F("AP client connected"));
}
break;
case NetworkState::CONNECTED:
break;
case NetworkState::GOT_IP:
if(!mConnected) {
mAp.disable();
mConnected = true;
ah::welcome(WiFi.localIP().toString(), F("Station"));
MDNS.begin(mConfig->sys.deviceName);
mOnNetworkCB(true);
}
break;
}
}
String getIp(void) override {
return WiFi.localIP().toString();
}
void scanAvailNetworks(void) override {
if(!mScanActive) {
mScanActive = true;
WiFi.scanNetworks(true);
}
}
bool getAvailNetworks(JsonObject obj) override {
JsonArray nets = obj.createNestedArray(F("networks"));
int n = WiFi.scanComplete();
if (n < 0)
return false;
if(n > 0) {
int sort[n];
sortRSSI(&sort[0], n);
for (int i = 0; i < n; ++i) {
nets[i][F("ssid")] = WiFi.SSID(sort[i]);
nets[i][F("rssi")] = WiFi.RSSI(sort[i]);
}
}
mScanActive = false;
WiFi.scanDelete();
return true;
}
private:
void sortRSSI(int *sort, int n) {
for (int i = 0; i < n; i++)
sort[i] = i;
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
if (WiFi.RSSI(sort[j]) > WiFi.RSSI(sort[i]))
std::swap(sort[i], sort[j]);
}
private:
bool mScanActive = false;
};
#endif /*ESP32 & !ETHERNET*/
#endif /*__AHOY_Wifi_ESP32_H__*/

14
src/publisher/pubMqtt.h

@ -16,10 +16,6 @@
#endif
#include <array>
#if defined(ETHERNET)
#include "../eth/ahoyeth.h"
#endif
#include "../utils/dbg.h"
#include "../config/config.h"
#include <espMqttClient.h>
@ -56,7 +52,8 @@ class PubMqtt {
~PubMqtt() { }
void setup(cfgMqtt_t *cfg_mqtt, const char *devName, const char *version, HMSYSTEM *sys, uint32_t *utcTs, uint32_t *uptime) {
void setup(IApp *app, cfgMqtt_t *cfg_mqtt, const char *devName, const char *version, HMSYSTEM *sys, uint32_t *utcTs, uint32_t *uptime) {
mApp = app;
mCfgMqtt = cfg_mqtt;
mDevName = devName;
mVersion = version;
@ -256,11 +253,7 @@ class PubMqtt {
publish(subtopics[MQTT_VERSION], mVersion, true);
publish(subtopics[MQTT_DEVICE], mDevName, true);
#if defined(ETHERNET)
publish(subtopics[MQTT_IP_ADDR], ETH.localIP().toString().c_str(), true);
#else
publish(subtopics[MQTT_IP_ADDR], WiFi.localIP().toString().c_str(), true);
#endif
publish(subtopics[MQTT_IP_ADDR], mApp->getIp().c_str(), true);
tickerMinute();
publish(mLwtTopic.data(), mqttStr[MQTT_STR_LWT_CONN], true, false);
@ -611,6 +604,7 @@ class PubMqtt {
espMqttClient mClient;
cfgMqtt_t *mCfgMqtt = nullptr;
IApp *mApp;
#if defined(ESP8266)
WiFiEventHandler mHWifiCon, mHWifiDiscon;
#endif

4
src/web/RestApi.h

@ -893,7 +893,7 @@ class RestApi {
mApp->getAvailNetworks(obj);
}
void getWifiIp(JsonObject obj) {
obj[F("ip")] = mApp->getStationIp();
obj[F("ip")] = mApp->getIp();
}
#endif /* !defined(ETHERNET) */
@ -1048,7 +1048,7 @@ class RestApi {
snprintf(mConfig->sys.stationSsid, SSID_LEN, "%s", jsonIn[F("ssid")].as<const char*>());
snprintf(mConfig->sys.stationPwd, PWD_LEN, "%s", jsonIn[F("pwd")].as<const char*>());
mApp->saveSettings(false); // without reboot
mApp->setStopApAllowedMode(false);
//mApp->setStopApAllowedMode(false);
mApp->setupStation();
}
#endif /* !defined(ETHERNET */

490
src/wifi/ahoywifi.cpp

@ -1,490 +0,0 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://ahoydtu.de
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#if !defined(ETHERNET)
#if defined(ESP32) && defined(F)
#undef F
#define F(sl) (sl)
#endif
#include "ahoywifi.h"
#if defined(ESP32)
#include <ESPmDNS.h>
#else
#include <ESP8266mDNS.h>
#endif
// NTP CONFIG
#define NTP_PACKET_SIZE 48
//-----------------------------------------------------------------------------
ahoywifi::ahoywifi() : mApIp(192, 168, 4, 1) {}
/**
* TODO: ESP32 has native strongest AP support!
* WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL);
*/
//-----------------------------------------------------------------------------
void ahoywifi::setup(settings_t *config, uint32_t *utcTimestamp, appWifiCb cb, OnTimeCB onTimeCb) {
mConfig = config;
mUtcTimestamp = utcTimestamp;
mAppWifiCb = cb;
mOnTimeCb = onTimeCb;
mGotDisconnect = false;
mStaConn = DISCONNECTED;
mCnt = 0;
mScanActive = false;
mScanCnt = 0;
mStopApAllowed = true;
#if defined(ESP8266)
wifiConnectHandler = WiFi.onStationModeConnected(std::bind(&ahoywifi::onConnect, this, std::placeholders::_1));
wifiGotIPHandler = WiFi.onStationModeGotIP(std::bind(&ahoywifi::onGotIP, this, std::placeholders::_1));
wifiDisconnectHandler = WiFi.onStationModeDisconnected(std::bind(&ahoywifi::onDisconnect, this, std::placeholders::_1));
#else
WiFi.onEvent(std::bind(&ahoywifi::onWiFiEvent, this, std::placeholders::_1));
#endif
setupWifi(true);
}
//-----------------------------------------------------------------------------
void ahoywifi::setupWifi(bool startAP = false) {
#if !defined(FB_WIFI_OVERRIDDEN)
if(startAP) {
setupAp();
delay(1000);
}
#endif
#if !defined(AP_ONLY)
#if defined(FB_WIFI_OVERRIDDEN)
snprintf(mConfig->sys.stationSsid, SSID_LEN, "%s", FB_WIFI_SSID);
snprintf(mConfig->sys.stationPwd, PWD_LEN, "%s", FB_WIFI_PWD);
setupStation();
#else
if(mConfig->valid) {
if(strncmp(mConfig->sys.stationSsid, FB_WIFI_SSID, 14) != 0)
setupStation();
}
#endif
#endif
}
void ahoywifi::tickWifiLoop() {
static const uint8_t TIMEOUT = 20;
static const uint8_t SCAN_TIMEOUT = 10;
#if !defined(AP_ONLY)
mCnt++;
switch (mStaConn) {
case IN_STA_MODE:
// Nothing to do
if (mGotDisconnect) {
mStaConn = RESET;
}
#if !defined(ESP32)
MDNS.update();
if(WiFi.channel() > 11)
mWasInCh12to14 = true;
#endif
return;
case IN_AP_MODE:
if ((WiFi.softAPgetStationNum() == 0) || (!mStopApAllowed)) {
mCnt = 0;
mDns.stop();
WiFi.mode(WIFI_AP_STA);
mStaConn = DISCONNECTED;
} else {
mDns.processNextRequest();
return;
}
break;
case DISCONNECTED:
if ((WiFi.softAPgetStationNum() > 0) && (mStopApAllowed)) {
mStaConn = IN_AP_MODE;
// first time switch to AP Mode
if (mScanActive) {
WiFi.scanDelete();
mScanActive = false;
}
DBGPRINTLN(F("AP client connected"));
welcome(mApIp.toString(), "");
WiFi.mode(WIFI_AP);
mDns.start(53, "*", mApIp);
mAppWifiCb(true);
mDns.processNextRequest();
return;
} else if (!mScanActive) {
DBGPRINT(F("scanning APs with SSID "));
DBGPRINTLN(String(mConfig->sys.stationSsid));
mScanCnt = 0;
mCnt = 0;
mScanActive = true;
#if defined(ESP8266)
WiFi.scanNetworks(true, true, 0U, ([this]() {
if (mConfig->sys.isHidden)
return (uint8_t*)NULL;
return (uint8_t*)(mConfig->sys.stationSsid);
})());
#else
WiFi.scanNetworks(true, true, false, 300U, 0U, ([this]() {
if (mConfig->sys.isHidden)
return (char*)NULL;
return (mConfig->sys.stationSsid);
})());
#endif
return;
} else if(getBSSIDs()) {
// Scan ready
mStaConn = SCAN_READY;
} else {
// In case of a timeout, what do we do?
// For now we start scanning again as the original code did.
// Would be better to into PA mode
if (isTimeout(SCAN_TIMEOUT)) {
WiFi.scanDelete();
mScanActive = false;
}
}
break;
case SCAN_READY:
mStaConn = CONNECTING;
mCnt = 0;
DBGPRINT(F("try to connect to AP with BSSID:"));
uint8_t bssid[6];
for (int j = 0; j < 6; j++) {
bssid[j] = mBSSIDList.front();
mBSSIDList.pop_front();
DBGPRINT(" " + String(bssid[j], HEX));
}
DBGPRINTLN("");
mGotDisconnect = false;
WiFi.begin(mConfig->sys.stationSsid, mConfig->sys.stationPwd, 0, &bssid[0]);
break;
case CONNECTING:
if (isTimeout(TIMEOUT)) {
WiFi.disconnect();
mStaConn = mBSSIDList.empty() ? DISCONNECTED : SCAN_READY;
}
break;
case CONNECTED:
// Connection but no IP yet
if (isTimeout(TIMEOUT) || mGotDisconnect) {
mStaConn = RESET;
}
break;
case GOT_IP:
welcome(WiFi.localIP().toString(), F(" (Station)"));
if(mStopApAllowed) {
WiFi.softAPdisconnect();
WiFi.mode(WIFI_STA);
DBGPRINTLN(F("[WiFi] AP disabled"));
delay(100);
}
mAppWifiCb(true);
mGotDisconnect = false;
mStaConn = IN_STA_MODE;
if (!MDNS.begin(mConfig->sys.deviceName)) {
DPRINTLN(DBG_ERROR, F("Error setting up MDNS responder!"));
} else {
DBGPRINT(F("mDNS established: "));
DBGPRINT(mConfig->sys.deviceName);
DBGPRINTLN(F(".local"));
}
break;
case RESET:
mGotDisconnect = false;
mStaConn = DISCONNECTED;
mCnt = 5; // try to reconnect in 5 sec
setupWifi(); // reconnect with AP / Station setup
mAppWifiCb(false);
DPRINTLN(DBG_INFO, "[WiFi] Connection Lost");
break;
default:
DBGPRINTLN(F("Unhandled status"));
break;
}
#endif
}
//-----------------------------------------------------------------------------
void ahoywifi::setupAp(void) {
DPRINTLN(DBG_VERBOSE, F("wifi::setupAp"));
DBGPRINTLN(F("\n---------\nAhoyDTU Info:"));
DBGPRINT(F("Version: "));
DBGPRINT(String(VERSION_MAJOR));
DBGPRINT(F("."));
DBGPRINT(String(VERSION_MINOR));
DBGPRINT(F("."));
DBGPRINTLN(String(VERSION_PATCH));
DBGPRINT(F("Github Hash: "));
DBGPRINTLN(String(AUTO_GIT_HASH));
DBGPRINT(F("\n---------\nAP MODE\nSSID: "));
DBGPRINTLN(WIFI_AP_SSID);
DBGPRINT(F("PWD: "));
DBGPRINTLN(mConfig->sys.apPwd);
DBGPRINT(F("IP Address: http://"));
DBGPRINTLN(mApIp.toString());
DBGPRINTLN(F("---------\n"));
if(String(mConfig->sys.deviceName) != "")
WiFi.hostname(mConfig->sys.deviceName);
WiFi.mode(WIFI_AP_STA);
WiFi.softAPConfig(mApIp, mApIp, IPAddress(255, 255, 255, 0));
WiFi.softAP(WIFI_AP_SSID, mConfig->sys.apPwd);
}
//-----------------------------------------------------------------------------
void ahoywifi::setupStation(void) {
DPRINTLN(DBG_VERBOSE, F("wifi::setupStation"));
if(mConfig->sys.ip.ip[0] != 0) {
IPAddress ip(mConfig->sys.ip.ip);
IPAddress mask(mConfig->sys.ip.mask);
IPAddress dns1(mConfig->sys.ip.dns1);
IPAddress dns2(mConfig->sys.ip.dns2);
IPAddress gateway(mConfig->sys.ip.gateway);
if(!WiFi.config(ip, gateway, mask, dns1, dns2))
DPRINTLN(DBG_ERROR, F("failed to set static IP!"));
}
mBSSIDList.clear();
if(String(mConfig->sys.deviceName) != "")
WiFi.hostname(mConfig->sys.deviceName);
WiFi.mode(WIFI_AP_STA);
DBGPRINT(F("connect to network '"));
DBGPRINT(mConfig->sys.stationSsid);
DBGPRINTLN(F("' ..."));
}
//-----------------------------------------------------------------------------
bool ahoywifi::updateNtpTime(void) {
if(IN_STA_MODE != mStaConn)
return false;
IPAddress timeServer;
uint8_t buf[NTP_PACKET_SIZE];
uint8_t retry = 0;
if (WiFi.hostByName(mConfig->ntp.addr, timeServer) != 1)
return false;
mUdp.begin(mConfig->ntp.port);
sendNTPpacket(timeServer);
while(retry++ < 5) {
int wait = 150;
while(--wait) {
if(NTP_PACKET_SIZE <= mUdp.parsePacket()) {
uint64_t secsSince1900;
mUdp.read(buf, NTP_PACKET_SIZE);
secsSince1900 = ((uint64_t)buf[40] << 24);
secsSince1900 |= (buf[41] << 16);
secsSince1900 |= (buf[42] << 8);
secsSince1900 |= (buf[43] );
*mUtcTimestamp = secsSince1900 - 2208988800UL; // UTC time
DPRINTLN(DBG_INFO, "[NTP]: " + ah::getDateTimeStr(*mUtcTimestamp) + " UTC");
mOnTimeCb(true);
return true;
} else
delay(10);
}
}
DPRINTLN(DBG_INFO, F("[NTP]: getNtpTime failed"));
return false;
}
//-----------------------------------------------------------------------------
void ahoywifi::sendNTPpacket(IPAddress& address) {
//DPRINTLN(DBG_VERBOSE, F("wifi::sendNTPpacket"));
uint8_t buf[NTP_PACKET_SIZE] = {0};
buf[0] = B11100011; // LI, Version, Mode
buf[1] = 0; // Stratum
buf[2] = 6; // Max Interval between messages in seconds
buf[3] = 0xEC; // Clock Precision
// bytes 4 - 11 are for Root Delay and Dispersion and were set to 0 by memset
buf[12] = 49; // four-byte reference ID identifying
buf[13] = 0x4E;
buf[14] = 49;
buf[15] = 52;
mUdp.beginPacket(address, 123); // NTP request, port 123
mUdp.write(buf, NTP_PACKET_SIZE);
mUdp.endPacket();
}
//-----------------------------------------------------------------------------
void ahoywifi::sortRSSI(int *sort, int n) {
for (int i = 0; i < n; i++)
sort[i] = i;
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
if (WiFi.RSSI(sort[j]) > WiFi.RSSI(sort[i]))
std::swap(sort[i], sort[j]);
}
//-----------------------------------------------------------------------------
void ahoywifi::scanAvailNetworks(void) {
if(!mScanActive) {
mScanActive = true;
if(WIFI_AP == WiFi.getMode())
WiFi.mode(WIFI_AP_STA);
WiFi.scanNetworks(true);
}
}
//-----------------------------------------------------------------------------
bool ahoywifi::getAvailNetworks(JsonObject obj) {
JsonArray nets = obj.createNestedArray("networks");
int n = WiFi.scanComplete();
if (n < 0)
return false;
if(n > 0) {
int sort[n];
sortRSSI(&sort[0], n);
for (int i = 0; i < n; ++i) {
nets[i]["ssid"] = WiFi.SSID(sort[i]);
nets[i]["rssi"] = WiFi.RSSI(sort[i]);
}
}
mScanActive = false;
WiFi.scanDelete();
if(mStaConn == IN_AP_MODE)
WiFi.mode(WIFI_AP);
return true;
}
//-----------------------------------------------------------------------------
bool ahoywifi::getBSSIDs() {
bool result = false;
int n = WiFi.scanComplete();
if (n < 0) {
if (++mScanCnt < 20)
return false;
}
if(n > 0) {
mBSSIDList.clear();
int sort[n];
sortRSSI(&sort[0], n);
for (int i = 0; i < n; i++) {
DBGPRINT("BSSID " + String(i) + ":");
uint8_t *bssid = WiFi.BSSID(sort[i]);
for (int j = 0; j < 6; j++){
DBGPRINT(" " + String(bssid[j], HEX));
mBSSIDList.push_back(bssid[j]);
}
DBGPRINTLN("");
}
result = true;
}
mScanActive = false;
WiFi.scanDelete();
return result;
}
//-----------------------------------------------------------------------------
void ahoywifi::connectionEvent(WiFiStatus_t status) {
DPRINTLN(DBG_INFO, "connectionEvent");
switch(status) {
case CONNECTED:
if(mStaConn != CONNECTED) {
mStaConn = CONNECTED;
mGotDisconnect = false;
DBGPRINTLN(F("\n[WiFi] Connected"));
}
break;
case GOT_IP:
mStaConn = GOT_IP;
break;
case DISCONNECTED:
mGotDisconnect = true;
break;
default:
break;
}
}
//-----------------------------------------------------------------------------
#if defined(ESP8266)
//-------------------------------------------------------------------------
void ahoywifi::onConnect(const WiFiEventStationModeConnected& event) {
connectionEvent(CONNECTED);
}
//-------------------------------------------------------------------------
void ahoywifi::onGotIP(const WiFiEventStationModeGotIP& event) {
connectionEvent(GOT_IP);
}
//-------------------------------------------------------------------------
void ahoywifi::onDisconnect(const WiFiEventStationModeDisconnected& event) {
connectionEvent(DISCONNECTED);
}
#else
//-------------------------------------------------------------------------
void ahoywifi::onWiFiEvent(WiFiEvent_t event) {
DBGPRINT(F("Wifi event: "));
DBGPRINTLN(String(event));
switch(event) {
case SYSTEM_EVENT_STA_CONNECTED:
connectionEvent(CONNECTED);
break;
case SYSTEM_EVENT_STA_GOT_IP:
connectionEvent(GOT_IP);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
connectionEvent(DISCONNECTED);
break;
default:
break;
}
}
#endif
//-----------------------------------------------------------------------------
void ahoywifi::welcome(String ip, String mode) {
DBGPRINTLN(F("\n\n--------------------------------"));
DBGPRINTLN(F("Welcome to AHOY!"));
DBGPRINT(F("\npoint your browser to http://"));
DBGPRINT(ip);
DBGPRINTLN(mode);
DBGPRINTLN(F("to configure your device"));
DBGPRINTLN(F("--------------------------------\n"));
}
#endif /* !defined(ETHERNET) */

100
src/wifi/ahoywifi.h

@ -1,100 +0,0 @@
//------------------------------------//-----------------------------------------------------------------------------
// 2024 Ahoy, https://github.com/lumpapu/ahoy
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#if !defined(ETHERNET)
#ifndef __AHOYWIFI_H__
#define __AHOYWIFI_H__
#include "../utils/dbg.h"
#include <Arduino.h>
#include <list>
#include <WiFiUdp.h>
#include <DNSServer.h>
#include "ESPAsyncWebServer.h"
#include "../config/settings.h"
class app;
class ahoywifi {
public:
typedef std::function<void(bool)> appWifiCb;
typedef std::function<void(bool)> OnTimeCB;
ahoywifi();
void setup(settings_t *config, uint32_t *utcTimestamp, appWifiCb cb, OnTimeCB onTimeCB);
void tickWifiLoop(void);
bool updateNtpTime(void);
void scanAvailNetworks(void);
bool getAvailNetworks(JsonObject obj);
void setStopApAllowedMode(bool allowed) {
mStopApAllowed = allowed;
}
String getStationIp(void) {
return WiFi.localIP().toString();
}
void setupStation(void);
bool getWasInCh12to14() const {
return mWasInCh12to14;
}
private:
typedef enum WiFiStatus {
DISCONNECTED = 0,
SCAN_READY,
CONNECTING,
CONNECTED,
IN_AP_MODE,
GOT_IP,
IN_STA_MODE,
RESET
} WiFiStatus_t;
void setupWifi(bool startAP);
void setupAp(void);
void sendNTPpacket(IPAddress& address);
void sortRSSI(int *sort, int n);
bool getBSSIDs(void);
void connectionEvent(WiFiStatus_t status);
bool isTimeout(uint8_t timeout) { return (mCnt % timeout) == 0; }
#if defined(ESP8266)
void onConnect(const WiFiEventStationModeConnected& event);
void onGotIP(const WiFiEventStationModeGotIP& event);
void onDisconnect(const WiFiEventStationModeDisconnected& event);
#else
void onWiFiEvent(WiFiEvent_t event);
#endif
void welcome(String ip, String mode);
settings_t *mConfig = nullptr;
appWifiCb mAppWifiCb;
OnTimeCB mOnTimeCb;
DNSServer mDns;
IPAddress mApIp;
WiFiUDP mUdp; // for time server
#if defined(ESP8266)
WiFiEventHandler wifiConnectHandler, wifiDisconnectHandler, wifiGotIPHandler;
#endif
WiFiStatus_t mStaConn = DISCONNECTED;
uint8_t mCnt = 0;
uint32_t *mUtcTimestamp = nullptr;
uint8_t mScanCnt = 0;
bool mScanActive = false;
bool mGotDisconnect = false;
std::list<uint8_t> mBSSIDList;
bool mStopApAllowed = false;
bool mWasInCh12to14 = false;
};
#endif /*__AHOYWIFI_H__*/
#endif /* !defined(ETHERNET) */
Loading…
Cancel
Save