Browse Source

0.8.1030001-zero

Merge branch 'development03' into zero-export
pull/1557/head
lumapu 6 months ago
parent
commit
a86a55e9de
  1. 6
      .github/ISSUE_TEMPLATE/report.yaml
  2. 1
      .gitignore
  3. 20
      scripts/convertHtml.py
  4. 2
      src/.gitignore
  5. 28
      src/CHANGES.md
  6. 98
      src/app.cpp
  7. 57
      src/app.h
  8. 7
      src/appInterface.h
  9. 2
      src/config/config.h
  10. 59
      src/config/settings.h
  11. 3
      src/defines.h
  12. 179
      src/eth/ahoyeth.cpp
  13. 65
      src/eth/ahoyeth.h
  14. 2
      src/hm/nrfHal.h
  15. 2
      src/hms/hmsRadio.h
  16. 70
      src/network/AhoyEthernet.h
  17. 13
      src/network/AhoyEthernetSpi.h
  18. 246
      src/network/AhoyNetwork.h
  19. 20
      src/network/AhoyNetworkHelper.cpp
  20. 39
      src/network/AhoyNetworkHelper.h
  21. 83
      src/network/AhoyWifiAp.h
  22. 74
      src/network/AhoyWifiEsp32.h
  23. 161
      src/network/AhoyWifiEsp8266.h
  24. 280
      src/platformio.ini
  25. 18
      src/publisher/pubMqtt.h
  26. 2
      src/publisher/pubMqttIvData.h
  27. 8
      src/utils/improv.h
  28. 49
      src/web/RestApi.h
  29. 6
      src/web/html/history.html
  30. 103
      src/web/html/setup.html
  31. 224
      src/web/html/wizard.html
  32. 70
      src/web/lang.json
  33. 61
      src/web/web.h
  34. 490
      src/wifi/ahoywifi.cpp
  35. 100
      src/wifi/ahoywifi.h

6
.github/ISSUE_TEMPLATE/report.yaml

@ -12,7 +12,7 @@ body:
Wir lesen auch gerne Deutsch, bitte fülle die u.a. Fragen aus damit wir Dir bestmöglich helfen können Danke!
Bitte unser FAQ als Hilfestellung prüfen: https://ahoydtu.de/faq
Please read, copy & fill in the template from our Posting Guide lines into your Support Forum post.
Please read, then copy & fill in the template from our Posting Guide lines into your Support Forum post.
We do enjoy the english language, but we need a couple of things to best support you in your goal, please fill in all / most of the details given below. Thanks!
Check our FAQ: https://ahoydtu.de/faq
- type: markdown
@ -35,7 +35,7 @@ body:
label: Assembly
description:
options:
- I did the assebly by myself
- I did the assembly by myself
- the DTU was already assembled
validations:
required: true
@ -84,7 +84,7 @@ body:
label: Connection picture
description:
options:
- label: I will attach/upload an Image of my wiring
- label: I will attach/upload an image of my wiring
validations:
required: true
- type: markdown

1
.gitignore

@ -15,3 +15,4 @@ src/web/html/tmp/*
src/output.map
/.venv
/scripts/__pycache__/htmlPreprocessorDefines.cpython-311.pyc

20
scripts/convertHtml.py

@ -9,19 +9,29 @@ from pathlib import Path
import subprocess
import configparser
Import("env")
build_flags = []
import htmlPreprocessorDefines as prepro
def getFlagsOfEnv(env):
config = configparser.ConfigParser()
config.read('platformio.ini')
global build_flags
flags = config[env]['build_flags'].split('\n')
for i in range(len(flags)):
if flags[i][:2] == "-D" or flags[i][:2] == "${":
flags[i] = flags[i][2:]
if flags[i][-13:-1] == ".build_flags":
getFlagsOfEnv(flags[i].split(".build_flags")[0])
elif len(flags[i]) > 0:
build_flags = build_flags + [flags[i]]
def get_build_flags():
getFlagsOfEnv("env:" + env['PIOENV'])
config = configparser.ConfigParser()
config.read('platformio.ini')
global build_flags
build_flags = config["env:" + env['PIOENV']]['build_flags'].split('\n')
for i in range(len(build_flags)):
build_flags[i] = build_flags[i][2:]
# translate board
board = config["env:" + env['PIOENV']]['board']

2
src/.gitignore

@ -3,3 +3,5 @@
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
scripts/__pycache__/*
*.pyc

28
src/CHANGES.md

@ -1,5 +1,33 @@
# Development Changes
## 0.8.103 - 2024-04-02
* merge PR: fix: get refresh property from object #1552
* merge PR: fix typos and spelling in Github Issue template #1550
* merge PR: shorten last cmt waiting time #1549
* fix cppcheck warnings
* changed MqTT retained flags of some topics
## 0.8.102 - 2024-04-01
* fix NTP for `opendtufusion` #1542
* fix scan WiFi in AP mode
* fix MDNS #1538
* improved Wizard
* improved MqTT on devcontrol e.g. set power limit
## 0.8.101 - 2024-03-28
* updated converter scripts to include all enabled features again (redundant scan of build flags) #1534
## 0.8.100 - 2024-03-27
* fix captions in `/history #1532
* fix get NTP time #1529 #1530
* fix translation #1516
## 0.8.99 - 2024-03-27
* fix compilation of all environments
## 0.8.98 - 2024-03-24
* new network routines
## 0.8.97 - 2024-03-22
* add support for newest generation of inverters with A-F in their serial number
* fix NTP and sunrise / sunset

98
src/app.cpp

@ -57,17 +57,15 @@ 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 = static_cast<AhoyNetwork*>(new AhoyEthernet());
#else
mNetwork = static_cast<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();
esp_task_wdt_reset();
@ -116,7 +114,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); });
}
@ -205,7 +203,6 @@ void app::loop(void) {
//-----------------------------------------------------------------------------
void app::onNetwork(bool gotIp) {
DPRINTLN(DBG_INFO, F("onNetwork"));
mNetworkConnected = gotIp;
ah::Scheduler::resetTicker();
regularTickers(); //reinstall regular tickers
@ -213,12 +210,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) */
}
//-----------------------------------------------------------------------------
@ -226,6 +217,7 @@ void app::regularTickers(void) {
DPRINTLN(DBG_DEBUG, F("regularTickers"));
everySec(std::bind(&WebType::tickSecond, &mWeb), "webSc");
everySec([this]() { mProtection->tickSecond(); }, "prot");
everySec([this]() {mNetwork->tickNetworkLoop(); }, "net");
// Plugins
#if defined(PLUGIN_DISPLAY)
@ -300,26 +292,14 @@ 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();
else
mNetwork->updateNtpTime();
else {
nxtTrig = mConfig->ntp.interval * 60; // check again in configured interval
mNtpReceived = false;
#else
if (!mNtpReceived)
mWifi.updateNtpTime();
else
mNtpReceived = false;
#endif
}
updateNtp();
nxtTrig = mConfig->ntp.interval * 60; // check again in 12h
// immediately start communicating
if (mSendFirst) {
mSendFirst = false;
once(std::bind(&app::tickSend, this), 1, "senOn");
}
mMqttReconnect = false;
@ -476,19 +456,44 @@ void app::tickSend(void) {
for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
Inverter<> *iv = mSys.getInverterByPos(i);
if(!sendIv(iv))
notAvail = false;
// Plugin ZeroExport
// #if defined(PLUGIN_ZEROEXPORT)
// TODO: aufr�umen
// if(mConfig->nrf.enabled || mConfig->cmt.enabled) {
// mZeroExport.loop();
// zeroexport();
// }
// #endif
// Plugin ZeroExport - Ende
}
if(mAllIvNotAvail != notAvail)
once(std::bind(&app::notAvailChanged, this), 1, "avail");
mAllIvNotAvail = notAvail;
updateLed();
}
//-----------------------------------------------------------------------------
bool app::sendIv(Inverter<> *iv) {
if(NULL == iv)
continue;
return true;
if(!iv->config->enabled)
return true;
if(iv->config->enabled) {
if(!iv->commEnabled) {
DPRINT_IVID(DBG_INFO, iv->id);
DBGPRINTLN(F("no communication to the inverter (night time)"));
continue;
return true;
}
if(!iv->radio->isChipConnected())
continue;
return true;
bool notAvail = true;
if(InverterStatus::OFF != iv->status)
notAvail = false;
@ -499,23 +504,7 @@ void app::tickSend(void) {
mCommunication.add(iv, cmd);
});
// Plugin ZeroExport
// #if defined(PLUGIN_ZEROEXPORT)
// TODO: aufräumen
// if(mConfig->nrf.enabled || mConfig->cmt.enabled) {
// mZeroExport.loop();
// zeroexport();
// }
// #endif
// Plugin ZeroExport - Ende
}
}
if(mAllIvNotAvail != notAvail)
once(std::bind(&app::notAvailChanged, this), 1, "avail");
mAllIvNotAvail = notAvail;
updateLed();
return notAvail;
}
//-----------------------------------------------------------------------------
@ -614,7 +603,6 @@ void app::resetSystem(void) {
mTimestamp = 1;
#endif
mSendFirst = true;
mAllIvNotAvail = true;
mSunrise = 0;
@ -690,7 +678,7 @@ void app::updateLed(void) {
#if defined(PLUGIN_ZEROEXPORT)
void app::zeroexport() {
return;
// TODO: aufräumen
// TODO: aufrumen
// TODO: umziehen nach loop

57
src/app.h

@ -37,9 +37,13 @@
#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"
#if defined(ESP32)
#include "network/AhoyWifiEsp32.h"
#else
#include "network/AhoyWifiEsp8266.h"
#endif
#include "utils/improv.h"
#endif /* defined(ETHERNET) */
@ -170,32 +174,31 @@ class app : public IApp, public ah::Scheduler {
}
#if !defined(ETHERNET)
void scanAvailNetworks() override {
mWifi.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 {
mWifi.setStopApAllowedMode(allowed);
bool getWasInCh12to14(void) const override {
#if defined(ESP8266)
return mNetwork->getWasInCh12to14();
#else
return false;
#endif
}
#endif /* !defined(ETHERNET) */
String getStationIp(void) override {
return mWifi.getStationIp();
String getIp(void) override {
return mNetwork->getIp();
}
bool getWasInCh12to14(void) const override {
return mWifi.getWasInCh12to14();
bool isApActive(void) override {
return mNetwork->isApActive();
}
#endif /* !defined(ETHERNET) */
void setRebootFlag() override {
once(std::bind(&app::tickReboot, this), 3, "rboot");
}
@ -302,13 +305,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);
}
@ -406,8 +403,10 @@ class app : public IApp, public ah::Scheduler {
bool mNtpReceived = false;
void updateNtp(void);
void triggerTickSend() override {
once(std::bind(&app::tickSend, this), 0, "tSend");
void triggerTickSend(uint8_t id) override {
once([this, id]() {
sendIv(mSys.getInverterByPos(id));
}, 0, "devct");
}
void tickCalcSunrise(void);
@ -416,6 +415,7 @@ class app : public IApp, public ah::Scheduler {
void tickSunrise(void);
void tickComm(void);
void tickSend(void);
bool sendIv(Inverter<> *iv);
void tickMinute(void);
void tickZeroValues(void);
void tickMidnight(void);
@ -427,11 +427,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 = nullptr;
WebType mWeb;
RestApiType mApi;
Protection *mProtection = nullptr;
@ -460,7 +456,6 @@ class app : public IApp, public ah::Scheduler {
bool mSaveReboot = false;
uint8_t mSendLastIvId = 0;
bool mSendFirst = false;
bool mAllIvNotAvail = false;
bool mNetworkConnected = false;

7
src/appInterface.h

@ -26,13 +26,12 @@ class IApp {
virtual const char *getVersionModules() = 0;
#if !defined(ETHERNET)
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 bool getWasInCh12to14(void) const = 0;
#endif /* defined(ETHERNET) */
virtual String getIp(void) = 0;
virtual bool isApActive(void) = 0;
virtual uint32_t getUptime() = 0;
virtual uint32_t getTimestamp() = 0;
@ -44,7 +43,7 @@ class IApp {
virtual void getSchedulerInfo(uint8_t *max) = 0;
virtual void getSchedulerNames() = 0;
virtual void triggerTickSend() = 0;
virtual void triggerTickSend(uint8_t id) = 0;
virtual bool getRebootRequestState() = 0;
virtual bool getSettingsValid() = 0;

2
src/config/config.h

@ -78,7 +78,7 @@
#define DEF_ETH_CS_PIN 15
#endif
#ifndef DEF_ETH_RST_PIN
#define DEF_ETH_RST_PIN 2
#define DEF_ETH_RST_PIN DEF_PIN_OFF
#endif
#else /* defined(ETHERNET) */
// time in seconds how long the station info (ssid + pwd) will be tried

59
src/config/settings.h

@ -63,6 +63,19 @@ typedef struct {
uint8_t gateway[4]; // standard gateway
} cfgIp_t;
#if defined(ETHERNET)
typedef struct {
bool enabled;
uint8_t pinCs;
uint8_t pinSclk;
uint8_t pinMiso;
uint8_t pinMosi;
uint8_t pinIrq;
uint8_t pinRst;
} cfgEth_t;
#endif
typedef struct {
char deviceName[DEVNAME_LEN];
char adminPwd[PWD_LEN];
@ -72,12 +85,14 @@ typedef struct {
uint8_t region;
int8_t timezone;
char apPwd[PWD_LEN];
#if !defined(ETHERNET)
// wifi
char stationSsid[SSID_LEN];
char stationPwd[PWD_LEN];
char apPwd[PWD_LEN];
bool isHidden;
#else
cfgEth_t eth;
#endif /* !defined(ETHERNET) */
cfgIp_t ip;
@ -554,10 +569,24 @@ class settings {
else {
snprintf(mCfg.sys.stationSsid, SSID_LEN, FB_WIFI_SSID);
snprintf(mCfg.sys.stationPwd, PWD_LEN, FB_WIFI_PWD);
snprintf(mCfg.sys.apPwd, PWD_LEN, WIFI_AP_PWD);
mCfg.sys.isHidden = false;
}
#endif /* !defined(ETHERNET) */
#endif
snprintf(mCfg.sys.apPwd, PWD_LEN, WIFI_AP_PWD);
#if defined(ETHERNET)
#if defined(DEF_ETH_ENABLED)
mCfg.sys.eth.enabled = true;
#else
mCfg.sys.eth.enabled = false;
#endif
mCfg.sys.eth.pinCs = DEF_ETH_CS_PIN;
mCfg.sys.eth.pinSclk = DEF_ETH_SCK_PIN;
mCfg.sys.eth.pinMiso = DEF_ETH_MISO_PIN;
mCfg.sys.eth.pinMosi = DEF_ETH_MOSI_PIN;
mCfg.sys.eth.pinIrq = DEF_ETH_IRQ_PIN;
mCfg.sys.eth.pinRst = DEF_ETH_RST_PIN;
#endif
snprintf(mCfg.sys.deviceName, DEVNAME_LEN, DEF_DEVICE_NAME);
mCfg.sys.region = 0; // Europe
@ -570,7 +599,11 @@ class settings {
mCfg.nrf.pinMosi = DEF_NRF_MOSI_PIN;
mCfg.nrf.pinSclk = DEF_NRF_SCLK_PIN;
#if defined(ETHERNET)
mCfg.nrf.enabled = false;
#else
mCfg.nrf.enabled = true;
#endif
#if defined(ESP32)
mCfg.cmt.pinSclk = DEF_CMT_SCLK;
@ -800,6 +833,16 @@ class settings {
ah::ip2Char(mCfg.sys.ip.dns1, buf); obj[F("dns1")] = String(buf);
ah::ip2Char(mCfg.sys.ip.dns2, buf); obj[F("dns2")] = String(buf);
ah::ip2Char(mCfg.sys.ip.gateway, buf); obj[F("gtwy")] = String(buf);
#if defined(ETHERNET)
obj[F("en")] = mCfg.sys.eth.enabled;
obj[F("cs")] = mCfg.sys.eth.pinCs;
obj[F("sclk")] = mCfg.sys.eth.pinSclk;
obj[F("miso")] = mCfg.sys.eth.pinMiso;
obj[F("mosi")] = mCfg.sys.eth.pinMosi;
obj[F("irq")] = mCfg.sys.eth.pinIrq;
obj[F("rst")] = mCfg.sys.eth.pinRst;
#endif
} else {
#if !defined(ETHERNET)
getChar(obj, F("ssid"), mCfg.sys.stationSsid, SSID_LEN);
@ -823,6 +866,16 @@ class settings {
if(mCfg.sys.protectionMask == 0)
mCfg.sys.protectionMask = DEF_PROT_INDEX | DEF_PROT_LIVE | DEF_PROT_SERIAL | DEF_PROT_SETUP
| DEF_PROT_UPDATE | DEF_PROT_SYSTEM | DEF_PROT_API | DEF_PROT_MQTT | DEF_PROT_HISTORY;
#if defined(ETHERNET)
getVal<bool>(obj, F("en"), &mCfg.sys.eth.enabled);
getVal<uint8_t>(obj, F("cs"), &mCfg.sys.eth.pinCs);
getVal<uint8_t>(obj, F("sclk"), &mCfg.sys.eth.pinSclk);
getVal<uint8_t>(obj, F("miso"), &mCfg.sys.eth.pinMiso);
getVal<uint8_t>(obj, F("mosi"), &mCfg.sys.eth.pinMosi);
getVal<uint8_t>(obj, F("irq"), &mCfg.sys.eth.pinIrq);
getVal<uint8_t>(obj, F("rst"), &mCfg.sys.eth.pinRst);
#endif
}
}

3
src/defines.h

@ -13,8 +13,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 8
#define VERSION_PATCH 970010
#define VERSION_PATCH 1030001
//-------------------------------------
typedef struct {
uint8_t ch;

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(DEF_ETH_MISO_PIN, DEF_ETH_MOSI_PIN, DEF_ETH_SCK_PIN, DEF_ETH_CS_PIN, DEF_ETH_IRQ_PIN, DEF_ETH_RST_PIN);
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) */

2
src/hm/nrfHal.h

@ -11,7 +11,7 @@
#include "../utils/spiPatcher.h"
#include <esp_rom_gpio.h>
#include <RF24_hal.h>
#include <RF24.h>
#define NRF_MAX_TRANSFER_SZ 64
#define NRF_DEFAULT_SPI_SPEED 10000000 // 10 MHz

2
src/hms/hmsRadio.h

@ -183,7 +183,7 @@ class CmtRadio : public Radio {
if(p.packet[9] > ALL_FRAMES) { // indicates last frame
setExpectedFrames(p.packet[9] - ALL_FRAMES);
mRadioWaitTime.startTimeMonitor(DURATION_PAUSE_LASTFR); // let the inverter first get back to rx mode?
mRadioWaitTime.startTimeMonitor(2); // let the inverter first get back to rx mode?
}
}

70
src/network/AhoyEthernet.h

@ -0,0 +1,70 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://ahoydtu.de
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#ifndef __AHOY_ETHERNET_H__
#define __AHOY_ETHERNET_H__
#if defined(ETHERNET)
#include <functional>
#include <AsyncUDP.h>
#include <ETH.h>
#include "AhoyEthernetSpi.h"
#include "AhoyNetwork.h"
class AhoyEthernet : public AhoyNetwork {
public:
void begin() override {
mAp.enable();
if(!mConfig->sys.eth.enabled)
return;
mEthSpi.begin(mConfig->sys.eth.pinMiso, mConfig->sys.eth.pinMosi, mConfig->sys.eth.pinSclk, mConfig->sys.eth.pinCs, mConfig->sys.eth.pinIrq, mConfig->sys.eth.pinRst);
ETH.setHostname(mConfig->sys.deviceName);
// static IP
setupIp([this](IPAddress ip, IPAddress gateway, IPAddress mask, IPAddress dns1, IPAddress dns2) -> bool {
return ETH.config(ip, gateway, mask, dns1, dns2);
});
}
void tickNetworkLoop() override {
if(mAp.isEnabled())
mAp.tickLoop();
switch(mStatus) {
case NetworkState::DISCONNECTED:
if(mConnected) {
mConnected = false;
mOnNetworkCB(false);
mAp.enable();
}
break;
case NetworkState::CONNECTED:
break;
case NetworkState::GOT_IP:
if(!mConnected) {
mAp.disable();
mConnected = true;
ah::welcome(ETH.localIP().toString(), F("Station"));
MDNS.begin(mConfig->sys.deviceName);
mOnNetworkCB(true);
}
break;
}
}
String getIp(void) override {
return ETH.localIP().toString();
}
private:
AhoyEthernetSpi mEthSpi;
};
#endif /*ETHERNET*/
#endif /*__AHOY_ETHERNET_H__*/

13
src/eth/ethSpi.h → src/network/AhoyEthernetSpi.h

@ -1,6 +1,6 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://www.mikrocontroller.net/topic/525778
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
// 2024 Ahoy, https://ahoydtu.de
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#if defined(ETHERNET)
@ -18,17 +18,19 @@
void tcpipInit();
void add_esp_interface_netif(esp_interface_t interface, esp_netif_t* esp_netif);
class EthSpi {
class AhoyEthernetSpi {
public:
EthSpi() :
AhoyEthernetSpi() :
eth_handle(nullptr),
eth_netif(nullptr) {}
void begin(int8_t pin_miso, int8_t pin_mosi, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst) {
if(-1 != pin_rst) {
gpio_reset_pin(static_cast<gpio_num_t>(pin_rst));
gpio_set_direction(static_cast<gpio_num_t>(pin_rst), GPIO_MODE_OUTPUT);
gpio_set_level(static_cast<gpio_num_t>(pin_rst), 0);
}
gpio_reset_pin(static_cast<gpio_num_t>(pin_sclk));
gpio_reset_pin(static_cast<gpio_num_t>(pin_mosi));
@ -42,6 +44,7 @@ class EthSpi {
gpio_reset_pin(static_cast<gpio_num_t>(pin_int));
gpio_set_pull_mode(static_cast<gpio_num_t>(pin_int), GPIO_PULLUP_ONLY);
spi_bus_config_t buscfg = {
.mosi_io_num = pin_mosi,
.miso_io_num = pin_miso,
@ -80,9 +83,11 @@ class EthSpi {
ESP_ERROR_CHECK(spi_bus_add_device(SPI3_HOST, &devcfg, &spi));
// Reset sequence
if(-1 != pin_rst) {
delayMicroseconds(500);
gpio_set_level(static_cast<gpio_num_t>(pin_rst), 1);
delayMicroseconds(1000);
}
// Arduino function to start networking stack if not already started
tcpipInit();

246
src/network/AhoyNetwork.h

@ -0,0 +1,246 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://ahoydtu.de
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#ifndef __AHOY_NETWORK_H__
#define __AHOY_NETWORK_H__
#include "AhoyNetworkHelper.h"
#include "../config/settings.h"
#include "../utils/helper.h"
#include "AhoyWifiAp.h"
#include "AsyncJson.h"
#define NTP_PACKET_SIZE 48
class AhoyNetwork {
public:
typedef std::function<void(bool)> OnNetworkCB;
typedef std::function<void(bool)> OnTimeCB;
public:
void setup(settings_t *config, uint32_t *utcTimestamp, OnNetworkCB onNetworkCB, OnTimeCB onTimeCB) {
mConfig = config;
mUtcTimestamp = utcTimestamp;
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, arduino_event_info_t info) -> void {
OnEvent(event);
});
#else
wifiConnectHandler = WiFi.onStationModeConnected(
[this](const WiFiEventStationModeConnected& event) -> void {
OnEvent((WiFiEvent_t)SYSTEM_EVENT_STA_CONNECTED);
});
wifiGotIPHandler = WiFi.onStationModeGotIP(
[this](const WiFiEventStationModeGotIP& event) -> void {
OnEvent((WiFiEvent_t)SYSTEM_EVENT_STA_GOT_IP);
});
wifiDisconnectHandler = WiFi.onStationModeDisconnected(
[this](const WiFiEventStationModeDisconnected& event) -> void {
OnEvent((WiFiEvent_t)SYSTEM_EVENT_STA_DISCONNECTED);
});
#endif
}
bool isConnected() const {
return (mStatus == NetworkState::CONNECTED);
}
bool updateNtpTime(void) {
if(NetworkState::GOT_IP != mStatus)
return false;
if (!mUdp.connected()) {
IPAddress timeServer;
if (!WiFi.hostByName(mConfig->ntp.addr, timeServer))
return false;
if (!mUdp.connect(timeServer, mConfig->ntp.port))
return false;
}
mUdp.onPacket([this](AsyncUDPPacket packet) {
this->handleNTPPacket(packet);
});
sendNTPpacket();
return true;
}
public:
virtual void begin() = 0;
virtual void tickNetworkLoop() = 0;
virtual String getIp(void) = 0;
virtual bool getWasInCh12to14() {
return false;
}
bool isApActive() {
return mAp.isEnabled();
}
#if !defined(ETHERNET)
bool getAvailNetworks(JsonObject obj) {
JsonArray nets = obj.createNestedArray(F("networks"));
if(!mScanActive) {
mScanActive = true;
if(NetworkState::GOT_IP != mStatus)
WiFi.disconnect();
WiFi.scanNetworks(true, true);
return false;
}
int n = WiFi.scanComplete();
if (WIFI_SCAN_RUNNING == n)
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;
}
#endif
protected:
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(cb(ip, gateway, mask, dns1, dns2))
DPRINTLN(DBG_ERROR, F("failed to set static IP!"));
}
}
void OnEvent(WiFiEvent_t event) {
switch(event) {
case SYSTEM_EVENT_STA_CONNECTED:
[[fallthrough]];
case ARDUINO_EVENT_ETH_CONNECTED:
if(NetworkState::CONNECTED != mStatus) {
mStatus = NetworkState::CONNECTED;
DPRINTLN(DBG_INFO, F("Network connected"));
}
break;
case SYSTEM_EVENT_STA_GOT_IP:
[[fallthrough]];
case ARDUINO_EVENT_ETH_GOT_IP:
mStatus = NetworkState::GOT_IP;
break;
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
[[fallthrough]];
case ARDUINO_EVENT_WIFI_STA_STOP:
[[fallthrough]];
case SYSTEM_EVENT_STA_DISCONNECTED:
[[fallthrough]];
case ARDUINO_EVENT_ETH_STOP:
[[fallthrough]];
case ARDUINO_EVENT_ETH_DISCONNECTED:
mStatus = NetworkState::DISCONNECTED;
break;
default:
break;
}
}
#if !defined(ETHERNET)
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]);
}
#endif
private:
void sendNTPpacket(void) {
uint8_t buf[NTP_PACKET_SIZE];
memset(buf, 0, NTP_PACKET_SIZE);
buf[0] = 0b11100011; // 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.write(buf, NTP_PACKET_SIZE);
}
void 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);
mUdp.close();
}
protected:
enum class NetworkState : uint8_t {
DISCONNECTED,
CONNECTED,
GOT_IP,
SCAN_READY, // ESP8266
CONNECTING // ESP8266
};
protected:
settings_t *mConfig = nullptr;
uint32_t *mUtcTimestamp = nullptr;
bool mConnected = false;
bool mScanActive = false;
OnNetworkCB mOnNetworkCB;
OnTimeCB mOnTimeCB;
NetworkState mStatus = NetworkState::DISCONNECTED;
AhoyWifiAp mAp;
DNSServer mDns;
AsyncUDP mUdp; // for time server
#if defined(ESP8266)
WiFiEventHandler wifiConnectHandler, wifiDisconnectHandler, wifiGotIPHandler;
#endif
};
#endif /*__AHOY_NETWORK_H__*/

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"));
}
}

39
src/network/AhoyNetworkHelper.h

@ -0,0 +1,39 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://ahoydtu.de
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#ifndef __AHOY_NETWORK_HELPER_H__
#define __AHOY_NETWORK_HELPER_H__
#include "../utils/dbg.h"
#include <Arduino.h>
#if defined(ESP32)
#include "ESPAsyncWebServer.h"
#include <WiFiType.h>
#include <ESPmDNS.h>
#else
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
//#include <WiFiUdp.h>
#include "ESPAsyncUDP.h"
enum {
SYSTEM_EVENT_STA_CONNECTED = 1,
ARDUINO_EVENT_ETH_CONNECTED,
SYSTEM_EVENT_STA_GOT_IP,
ARDUINO_EVENT_ETH_GOT_IP,
ARDUINO_EVENT_WIFI_STA_LOST_IP,
ARDUINO_EVENT_WIFI_STA_STOP,
SYSTEM_EVENT_STA_DISCONNECTED,
ARDUINO_EVENT_ETH_STOP,
ARDUINO_EVENT_ETH_DISCONNECTED
};
#endif
#include <DNSServer.h>
namespace ah {
void welcome(String ip, String info);
}
#endif /*__AHOY_NETWORK_HELPER_H__*/

83
src/network/AhoyWifiAp.h

@ -0,0 +1,83 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://ahoydtu.de
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#ifndef __AHOY_WIFI_AP_H__
#define __AHOY_WIFI_AP_H__
#include "../utils/dbg.h"
#include <Arduino.h>
#include "../config/settings.h"
#include "AhoyNetworkHelper.h"
class AhoyWifiAp {
public:
AhoyWifiAp() : mIp(192, 168, 4, 1) {}
void setup(cfgSys_t *cfg) {
mCfg = cfg;
}
void tickLoop() {
if(mEnabled)
mDns.processNextRequest();
if (WiFi.softAPgetStationNum() != mLast) {
mLast = WiFi.softAPgetStationNum();
if(mLast > 0)
DBGPRINTLN(F("AP client connected"));
}
}
void enable() {
if(mEnabled)
return;
ah::welcome(mIp.toString(), String(F("Password: ") + String(mCfg->apPwd)));
#if defined(ETHERNET)
WiFi.mode(WIFI_AP);
#else
WiFi.mode(WIFI_AP_STA);
#endif
WiFi.softAPConfig(mIp, mIp, IPAddress(255, 255, 255, 0));
WiFi.softAP(WIFI_AP_SSID, mCfg->apPwd);
mDns.start(53, "*", mIp);
mEnabled = true;
tickLoop();
}
void disable() {
if(!mEnabled)
return;
if(WiFi.softAPgetStationNum() > 0)
return;
mDns.stop();
WiFi.softAPdisconnect();
#if defined(ETHERNET)
WiFi.mode(WIFI_OFF);
#else
WiFi.mode(WIFI_STA);
#endif
mEnabled = false;
}
bool isEnabled() const {
return mEnabled;
}
private:
cfgSys_t *mCfg = nullptr;
DNSServer mDns;
IPAddress mIp;
bool mEnabled = false;
uint8_t mLast = 0;
};
#endif /*__AHOY_WIFI_AP_H__*/

74
src/network/AhoyWifiEsp32.h

@ -0,0 +1,74 @@
//-----------------------------------------------------------------------------
// 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 "AhoyNetwork.h"
#include "ESPAsyncWebServer.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);
#if !defined(AP_ONLY)
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 '"));
DBGPRINT(mConfig->sys.stationSsid);
#endif
}
void tickNetworkLoop() override {
if(mAp.isEnabled())
mAp.tickLoop();
switch(mStatus) {
case NetworkState::DISCONNECTED:
if(mConnected) {
mConnected = false;
mOnNetworkCB(false);
mAp.enable();
MDNS.end();
}
break;
case NetworkState::CONNECTED:
break;
case NetworkState::GOT_IP:
if(mAp.isEnabled())
mAp.disable();
if(!mConnected) {
mConnected = true;
ah::welcome(WiFi.localIP().toString(), F("Station"));
MDNS.begin(mConfig->sys.deviceName);
MDNS.addServiceTxt("http", "tcp", "path", "/");
mOnNetworkCB(true);
}
break;
}
}
String getIp(void) override {
return WiFi.localIP().toString();
}
};
#endif /*ESP32 & !ETHERNET*/
#endif /*__AHOY_WIFI_ESP32_H__*/

161
src/network/AhoyWifiEsp8266.h

@ -0,0 +1,161 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://ahoydtu.de
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#ifndef __AHOY_WIFI_ESP8266_H__
#define __AHOY_WIFI_ESP8266_H__
#if defined(ESP8266)
#include <functional>
#include <list>
#include <WiFiUdp.h>
#include "AhoyNetwork.h"
#include "ESPAsyncWebServer.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);
mBSSIDList.clear();
}
void tickNetworkLoop() override {
if(mAp.isEnabled())
mAp.tickLoop();
mCnt++;
switch(mStatus) {
case NetworkState::DISCONNECTED:
if(mConnected) {
mConnected = false;
mOnNetworkCB(false);
mAp.enable();
MDNS.end();
}
if (WiFi.softAPgetStationNum() > 0) {
DBGPRINTLN(F("AP client connected"));
}
#if !defined(AP_ONLY)
else if (!mScanActive) {
DBGPRINT(F("scanning APs with SSID "));
DBGPRINTLN(String(mConfig->sys.stationSsid));
mScanCnt = 0;
mCnt = 0;
mScanActive = true;
WiFi.scanNetworks(true, true, 0U, ([this]() {
if (mConfig->sys.isHidden)
return (uint8_t*)NULL;
return (uint8_t*)(mConfig->sys.stationSsid);
})());
} else if(getBSSIDs()) {
mStatus = NetworkState::SCAN_READY;
DBGPRINT(F("connect to network '")); Serial.flush();
DBGPRINTLN(mConfig->sys.stationSsid);
}
#endif
break;
case NetworkState::SCAN_READY:
mStatus = NetworkState::CONNECTING;
DBGPRINT(F("try to connect to 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("");
WiFi.begin(mConfig->sys.stationSsid, mConfig->sys.stationPwd, 0, &bssid[0]);
break;
case NetworkState::CONNECTING:
if (isTimeout(TIMEOUT)) {
WiFi.disconnect();
mStatus = mBSSIDList.empty() ? NetworkState::DISCONNECTED : NetworkState::SCAN_READY;
}
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);
MDNSResponder::hMDNSService hRes = MDNS.addService(NULL, "http", "tcp", 80);
MDNS.addServiceTxt(hRes, "path", "/");
MDNS.announce();
mOnNetworkCB(true);
}
MDNS.update();
if(WiFi.channel() > 11)
mWasInCh12to14 = true;
break;
}
}
String getIp(void) override {
return WiFi.localIP().toString();
}
bool getWasInCh12to14() override {
return mWasInCh12to14;
}
private:
bool 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;
}
bool isTimeout(uint8_t timeout) {
return ((mCnt % timeout) == 0);
}
private:
uint8_t mCnt = 0;
uint8_t mScanCnt = 0;
std::list<uint8_t> mBSSIDList;
bool mWasInCh12to14 = false;
static constexpr uint8_t TIMEOUT = 20;
static constexpr uint8_t SCAN_TIMEOUT = 10;
};
#endif /*ESP8266*/
#endif /*__AHOY_WIFI_ESP8266_H__*/

280
src/platformio.ini

@ -33,6 +33,7 @@ lib_deps =
https://github.com/JChristensen/Timezone @ ^1.2.4
olikraus/U8g2 @ ^2.35.9
https://github.com/zinggjm/GxEPD2#1.5.3
build_flags =
-std=c++17
-std=gnu++17
@ -40,26 +41,37 @@ build_unflags =
-std=gnu++11
[env:esp8266]
[env:esp8266-minimal]
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
lib_deps =
${env.lib_deps}
https://github.com/me-no-dev/ESPAsyncUDP
build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096
-DENABLE_MQTT
;-Wl,-Map,output.map
monitor_filters =
esp8266_exception_decoder
[env:esp8266]
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
lib_deps = ${env:esp8266-minimal.lib_deps}
https://github.com/me-no-dev/ESPAsyncUDP
build_flags = ${env:esp8266-minimal.build_flags}
-DENABLE_MQTT
monitor_filters =
esp8266_exception_decoder
[env:esp8266-de]
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096
lib_deps = ${env:esp8266.lib_deps}
build_flags = ${env:esp8266.build_flags}
-DLANG_DE
-DENABLE_MQTT
;-Wl,-Map,output.map
monitor_filters =
esp8266_exception_decoder
@ -67,12 +79,10 @@ monitor_filters =
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096
-DENABLE_MQTT
lib_deps = ${env:esp8266.lib_deps}
build_flags = ${env:esp8266.build_flags}
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
;-Wl,-Map,output.map
monitor_filters =
esp8266_exception_decoder
@ -80,13 +90,9 @@ monitor_filters =
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096
lib_deps = ${env:esp8266.lib_deps}
build_flags = ${env:esp8266-all.build_flags}
-DLANG_DE
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
;-Wl,-Map,output.map
monitor_filters =
esp8266_exception_decoder
@ -94,12 +100,9 @@ monitor_filters =
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096
lib_deps = ${env:esp8266.lib_deps}
build_flags = ${env:esp8266-all.build_flags}
-DENABLE_PROMETHEUS_EP
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
monitor_filters =
esp8266_exception_decoder
@ -107,23 +110,9 @@ monitor_filters =
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096
-DENABLE_PROMETHEUS_EP
lib_deps = ${env:esp8266.lib_deps}
build_flags = ${env:esp8266-prometheus.build_flags}
-DLANG_DE
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
monitor_filters =
esp8266_exception_decoder
[env:esp8266-minimal]
platform = espressif8266
board = esp12e
board_build.f_cpu = 80000000L
build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096
;-Wl,-Map,output.map
monitor_filters =
esp8266_exception_decoder
@ -134,6 +123,7 @@ platform = espressif8266
board = esp8285
board_build.ldscript = eagle.flash.1m64.ld
board_build.f_cpu = 80000000L
lib_deps = ${env:esp8266.lib_deps}
build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096
-DENABLE_MQTT
@ -147,6 +137,7 @@ platform = espressif8266
board = esp8285
board_build.ldscript = eagle.flash.1m64.ld
board_build.f_cpu = 80000000L
lib_deps = ${env:esp8266.lib_deps}
build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096
-DLANG_DE
@ -156,33 +147,29 @@ build_flags = ${env.build_flags}
monitor_filters =
esp8266_exception_decoder
[env:esp32-wroom32]
[env:esp32-wroom32-minimal]
platform = espressif32@6.5.0
board = lolin_d32
build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
monitor_filters =
esp32_exception_decoder
[env:esp32-wroom32-minimal]
[env:esp32-wroom32]
platform = espressif32@6.5.0
board = lolin_d32
build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD
build_flags = ${env:esp32-wroom32-minimal.build_flags}
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DPLUGIN_ZEROEXPORT
monitor_filters =
esp32_exception_decoder
[env:esp32-wroom32-de]
platform = espressif32@6.5.0
board = lolin_d32
build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
build_flags = ${env:esp32-wroom32.build_flags}
-DLANG_DE
monitor_filters =
esp32_exception_decoder
@ -190,38 +177,24 @@ monitor_filters =
[env:esp32-wroom32-prometheus]
platform = espressif32@6.5.0
board = lolin_d32
build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD
build_flags = ${env:esp32-wroom32.build_flags}
-DENABLE_PROMETHEUS_EP
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
monitor_filters =
esp32_exception_decoder
[env:esp32-wroom32-prometheus-de]
platform = espressif32@6.5.0
board = lolin_d32
build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD
build_flags = ${env:esp32-wroom32-prometheus.build_flags}
-DLANG_DE
-DENABLE_PROMETHEUS_EP
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
monitor_filters =
esp32_exception_decoder
[env:esp32-wroom32-ethernet]
platform = espressif32
board = lolin_d32
build_flags = ${env.build_flags}
build_flags = ${env:esp32-wroom32.build_flags}
-DETHERNET
-DRELEASE
-DUSE_HSPI_FOR_EPD
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DDEF_ETH_CS_PIN=15
-DDEF_ETH_SCK_PIN=14
-DDEF_ETH_MISO_PIN=12
@ -240,26 +213,8 @@ monitor_filters =
[env:esp32-wroom32-ethernet-de]
platform = espressif32
board = lolin_d32
build_flags = ${env.build_flags}
-DETHERNET
-DRELEASE
-DUSE_HSPI_FOR_EPD
build_flags = ${env:esp32-wroom32-ethernet.build_flags}
-DLANG_DE
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DDEF_ETH_CS_PIN=15
-DDEF_ETH_SCK_PIN=14
-DDEF_ETH_MISO_PIN=12
-DDEF_ETH_MOSI_PIN=13
-DDEF_ETH_IRQ_PIN=4
-DDEF_ETH_RST_PIN=2
-DDEF_NRF_CS_PIN=5
-DDEF_NRF_CE_PIN=17
-DDEF_NRF_IRQ_PIN=16
-DDEF_NRF_MISO_PIN=19
-DDEF_NRF_MOSI_PIN=23
-DDEF_NRF_SCLK_PIN=18
monitor_filters =
esp32_exception_decoder
@ -289,23 +244,7 @@ monitor_filters =
[env:esp32-s2-mini-de]
platform = espressif32@6.5.0
board = lolin_s2_mini
build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DPLUGIN_ZEROEXPORT
-DDEF_NRF_CS_PIN=12
-DDEF_NRF_CE_PIN=3
-DDEF_NRF_IRQ_PIN=5
-DDEF_NRF_MISO_PIN=9
-DDEF_NRF_MOSI_PIN=11
-DDEF_NRF_SCLK_PIN=7
-DDEF_CMT_CSB=16
-DDEF_CMT_FCSB=18
-DDEF_CMT_IRQ=33
-DDEF_CMT_SDIO=35
-DDEF_CMT_SCLK=37
build_flags = ${env:esp32-s2-mini.build_flags}
-DLANG_DE
monitor_filters =
esp32_exception_decoder
@ -318,6 +257,7 @@ build_flags = ${env.build_flags}
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DPLUGIN_ZEROEXPORT
-DDEF_NRF_CS_PIN=5
-DDEF_NRF_CE_PIN=0
-DDEF_NRF_IRQ_PIN=1
@ -335,36 +275,16 @@ monitor_filters =
[env:esp32-c3-mini-de]
platform = espressif32@6.5.0
board = lolin_c3_mini
build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DPLUGIN_ZEROEXPORT
-DDEF_NRF_CS_PIN=5
-DDEF_NRF_CE_PIN=0
-DDEF_NRF_IRQ_PIN=1
-DDEF_NRF_MISO_PIN=3
-DDEF_NRF_MOSI_PIN=4
-DDEF_NRF_SCLK_PIN=2
-DDEF_CMT_CSB=255
-DDEF_CMT_FCSB=255
-DDEF_CMT_IRQ=255
-DDEF_CMT_SDIO=255
-DDEF_CMT_SCLK=255
build_flags = ${env:esp32-c3-mini.build_flags}
-DLANG_DE
monitor_filters =
esp32_exception_decoder
[env:opendtufusion]
[env:opendtufusion-minimal]
platform = espressif32@6.5.0
board = esp32-s3-devkitc-1
upload_protocol = esp-builtin
build_flags = ${env.build_flags}
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DPLUGIN_ZEROEXPORT
-DSPI_HAL
-DDEF_NRF_CS_PIN=37
-DDEF_NRF_CE_PIN=38
@ -385,76 +305,33 @@ build_flags = ${env.build_flags}
monitor_filters =
esp32_exception_decoder, colorize
[env:opendtufusion-de]
[env:opendtufusion]
platform = espressif32@6.5.0
board = esp32-s3-devkitc-1
upload_protocol = esp-builtin
build_flags = ${env.build_flags}
-DLANG_DE
build_flags = ${env:opendtufusion-minimal.build_flags}
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DPLUGIN_ZEROEXPORT
-DSPI_HAL
-DDEF_NRF_CS_PIN=37
-DDEF_NRF_CE_PIN=38
-DDEF_NRF_IRQ_PIN=47
-DDEF_NRF_MISO_PIN=48
-DDEF_NRF_MOSI_PIN=35
-DDEF_NRF_SCLK_PIN=36
-DDEF_CMT_CSB=4
-DDEF_CMT_FCSB=21
-DDEF_CMT_IRQ=8
-DDEF_CMT_SDIO=5
-DDEF_CMT_SCLK=6
-DDEF_LED0=18
-DDEF_LED1=17
-DLED_ACTIVE_HIGH
-DARDUINO_USB_MODE=1
monitor_filters =
esp32_exception_decoder, colorize
[env:opendtufusion-minimal]
[env:opendtufusion-de]
platform = espressif32@6.5.0
board = esp32-s3-devkitc-1
upload_protocol = esp-builtin
build_flags = ${env.build_flags}
-DSPI_HAL
-DDEF_NRF_CS_PIN=37
-DDEF_NRF_CE_PIN=38
-DDEF_NRF_IRQ_PIN=47
-DDEF_NRF_MISO_PIN=48
-DDEF_NRF_MOSI_PIN=35
-DDEF_NRF_SCLK_PIN=36
-DDEF_CMT_CSB=4
-DDEF_CMT_FCSB=21
-DDEF_CMT_IRQ=8
-DDEF_CMT_SDIO=5
-DDEF_CMT_SCLK=6
-DDEF_LED0=18
-DDEF_LED1=17
-DLED_ACTIVE_HIGH
-DARDUINO_USB_MODE=1
build_flags = ${env:opendtufusion.build_flags}
-DLANG_DE
monitor_filters =
esp32_exception_decoder, colorize
[env:opendtufusion-ethernet]
platform = espressif32@6.5.0
board = esp32-s3-devkitc-1
#lib_deps =
# khoih-prog/AsyncWebServer_ESP32_W5500
# khoih-prog/AsyncUDP_ESP32_W5500
# https://github.com/nrf24/RF24 @ ^1.4.8
# paulstoffregen/Time @ ^1.6.1
# https://github.com/bertmelis/espMqttClient#v1.6.0
# bblanchon/ArduinoJson @ ^6.21.3
# https://github.com/JChristensen/Timezone @ ^1.2.4
# olikraus/U8g2 @ ^2.35.9
# https://github.com/zinggjm/GxEPD2#1.5.3
upload_protocol = esp-builtin
build_flags = ${env.build_flags}
build_flags = ${env:opendtufusion-minimal.build_flags}
-DETHERNET
-DSPI_HAL
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
@ -465,68 +342,15 @@ build_flags = ${env.build_flags}
-DDEF_ETH_MOSI_PIN=40
-DDEF_ETH_IRQ_PIN=44
-DDEF_ETH_RST_PIN=43
-DDEF_NRF_CS_PIN=37
-DDEF_NRF_CE_PIN=38
-DDEF_NRF_IRQ_PIN=47
-DDEF_NRF_MISO_PIN=48
-DDEF_NRF_MOSI_PIN=35
-DDEF_NRF_SCLK_PIN=36
-DDEF_CMT_CSB=4
-DDEF_CMT_FCSB=21
-DDEF_CMT_IRQ=8
-DDEF_CMT_SDIO=5
-DDEF_CMT_SCLK=6
-DDEF_LED0=18
-DDEF_LED1=17
-DLED_ACTIVE_HIGH
-DARDUINO_USB_MODE=1
#-DARDUINO_USB_CDC_ON_BOOT=1
-DDEF_ETH_ENABLED
monitor_filters =
esp32_exception_decoder, colorize
[env:opendtufusion-ethernet-de]
platform = espressif32@6.5.0
board = esp32-s3-devkitc-1
#lib_deps =
# khoih-prog/AsyncWebServer_ESP32_W5500
# khoih-prog/AsyncUDP_ESP32_W5500
# https://github.com/nrf24/RF24 @ ^1.4.8
# paulstoffregen/Time @ ^1.6.1
# https://github.com/bertmelis/espMqttClient#v1.6.0
# bblanchon/ArduinoJson @ ^6.21.3
# https://github.com/JChristensen/Timezone @ ^1.2.4
# olikraus/U8g2 @ ^2.35.9
# https://github.com/zinggjm/GxEPD2#1.5.3
upload_protocol = esp-builtin
build_flags = ${env.build_flags}
-DETHERNET
-DSPI_HAL
build_flags = ${env:opendtufusion-ethernet.build_flags}
-DLANG_DE
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DPLUGIN_ZEROEXPORT
-DDEF_ETH_CS_PIN=42
-DDEF_ETH_SCK_PIN=39
-DDEF_ETH_MISO_PIN=41
-DDEF_ETH_MOSI_PIN=40
-DDEF_ETH_IRQ_PIN=44
-DDEF_ETH_RST_PIN=43
-DDEF_NRF_CS_PIN=37
-DDEF_NRF_CE_PIN=38
-DDEF_NRF_IRQ_PIN=47
-DDEF_NRF_MISO_PIN=48
-DDEF_NRF_MOSI_PIN=35
-DDEF_NRF_SCLK_PIN=36
-DDEF_CMT_CSB=4
-DDEF_CMT_FCSB=21
-DDEF_CMT_IRQ=8
-DDEF_CMT_SDIO=5
-DDEF_CMT_SCLK=6
-DDEF_LED0=18
-DDEF_LED1=17
-DLED_ACTIVE_HIGH
-DARDUINO_USB_MODE=1
#-DARDUINO_USB_CDC_ON_BOOT=1
monitor_filters =
esp32_exception_decoder, colorize

18
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;
@ -254,13 +251,9 @@ class PubMqtt {
void onConnect(bool sessionPreset) {
DPRINTLN(DBG_INFO, F("MQTT connected"));
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_VERSION], mVersion, false);
publish(subtopics[MQTT_DEVICE], mDevName, false);
publish(subtopics[MQTT_IP_ADDR], mApp->getIp().c_str(), true);
tickerMinute();
publish(mLwtTopic.data(), mqttStr[MQTT_STR_LWT_CONN], true, false);
@ -612,6 +605,7 @@ class PubMqtt {
espMqttClient mClient;
cfgMqtt_t *mCfgMqtt = nullptr;
IApp *mApp;
#if defined(ESP8266)
WiFiEventHandler mHWifiCon, mHWifiDiscon;
#endif

2
src/publisher/pubMqttIvData.h

@ -187,7 +187,6 @@ class PubMqttIvData {
static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_FW_BUILD_MONTH_DAY, rec)),
static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_FW_BUILD_HOUR_MINUTE, rec)),
static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_BOOTLOADER_VER, rec)));
retained = true;
} else if(InverterDevInform_Simple == mCmd) {
snprintf(mSubTopic.data(), mSubTopic.size(), "%s/hardware", mIv->config->name);
snprintf(mVal.data(), mVal.size(), "{\"part\":%d,\"version\":\"%d\",\"grid_profile_code\":%d,\"grid_profile_version\":%d}",
@ -195,7 +194,6 @@ class PubMqttIvData {
static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_HW_VERSION, rec)),
static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_GRID_PROFILE_CODE, rec)),
static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_GRID_PROFILE_VERSION, rec)));
retained = true;
} else {
snprintf(mSubTopic.data(), mSubTopic.size(), "%s/ch%d/%s", mIv->config->name, rec->assign[mPos].ch, fields[rec->assign[mPos].fieldId]);
snprintf(mVal.data(), mVal.size(), "%g", ah::round3(mIv->getValue(mPos, rec)));

8
src/utils/improv.h

@ -147,10 +147,12 @@ class Improv {
}
void getNetworks(void) {
if(!mScanRunning)
mApp->scanAvailNetworks();
JsonObject obj;
if(!mScanRunning) {
mApp->getAvailNetworks(obj);
return;
}
if(!mApp->getAvailNetworks(obj))
return;

49
src/web/RestApi.h

@ -100,7 +100,7 @@ class RestApi {
else if(path == "setup") getSetup(request, root);
#if !defined(ETHERNET)
else if(path == "setup/networks") getNetworks(root);
else if(path == "setup/getip") getWifiIp(root);
else if(path == "setup/getip") getIp(root);
#endif /* !defined(ETHERNET) */
else if(path == "live") getLive(request,root);
else if (path == "powerHistory") getPowerHistory(request, root);
@ -166,7 +166,7 @@ class RestApi {
#else
DynamicJsonDocument json(12000); // does this work? I have no ESP32 :-(
#endif
DeserializationError err = deserializeJson(json, (const char *)mTmpBuf, mTmpSize);
DeserializationError err = deserializeJson(json, static_cast<const char *>(mTmpBuf, mTmpSize));
json.shrinkToFit();
JsonObject obj = json.as<JsonObject>();
@ -375,9 +375,9 @@ class RestApi {
}
void getSysInfo(AsyncWebServerRequest *request, JsonObject obj) {
obj[F("ap_pwd")] = mConfig->sys.apPwd;
#if !defined(ETHERNET)
obj[F("ssid")] = mConfig->sys.stationSsid;
obj[F("ap_pwd")] = mConfig->sys.apPwd;
obj[F("hidd")] = mConfig->sys.isHidden;
obj[F("mac")] = WiFi.macAddress();
obj[F("wifi_channel")] = WiFi.channel();
@ -762,6 +762,18 @@ class RestApi {
}
#endif
#if defined(ETHERNET)
void getEthernet(JsonObject obj) {
obj[F("en")] = mConfig->sys.eth.enabled;
obj[F("cs")] = mConfig->sys.eth.pinCs;
obj[F("sclk")] = mConfig->sys.eth.pinSclk;
obj[F("miso")] = mConfig->sys.eth.pinMiso;
obj[F("mosi")] = mConfig->sys.eth.pinMosi;
obj[F("irq")] = mConfig->sys.eth.pinIrq;
obj[F("reset")] = mConfig->sys.eth.pinRst;
}
#endif
void getRadioNrf(JsonObject obj) {
obj[F("en")] = (bool) mConfig->nrf.enabled;
if(mConfig->nrf.enabled) {
@ -920,6 +932,9 @@ class RestApi {
#if defined(ESP32)
getRadioCmt(obj.createNestedObject(F("radioCmt")));
#endif
#if defined(ETHERNET)
getEthernet(obj.createNestedObject(F("eth")));
#endif
getRadioNrf(obj.createNestedObject(F("radioNrf")));
getSerial(obj.createNestedObject(F("serial")));
getStaticIp(obj.createNestedObject(F("static_ip")));
@ -935,13 +950,14 @@ class RestApi {
#if !defined(ETHERNET)
void getNetworks(JsonObject obj) {
mApp->getAvailNetworks(obj);
}
void getWifiIp(JsonObject obj) {
obj[F("ip")] = mApp->getStationIp();
obj[F("success")] = mApp->getAvailNetworks(obj);
}
#endif /* !defined(ETHERNET) */
void getIp(JsonObject obj) {
obj[F("ip")] = mApp->getIp();
}
void getLive(AsyncWebServerRequest *request, JsonObject obj) {
getGeneric(request, obj.createNestedObject(F("generic")));
obj[F("refresh")] = mConfig->inst.sendInterval;
@ -1054,7 +1070,7 @@ class RestApi {
accepted = iv->setDevControlRequest(ActivePowerContr);
if(accepted)
mApp->triggerTickSend();
mApp->triggerTickSend(iv->id);
} else if(F("dev") == jsonIn[F("cmd")]) {
DPRINTLN(DBG_INFO, F("dev cmd"));
iv->setDevCommand(jsonIn[F("val")].as<int>());
@ -1075,11 +1091,6 @@ class RestApi {
if(isProtected(jsonIn, jsonOut, clientIP))
return false;
#if !defined(ETHERNET)
if(F("scan_wifi") == jsonIn[F("cmd")])
mApp->scanAvailNetworks();
else
#endif /* !defined(ETHERNET) */
if(F("set_time") == jsonIn[F("cmd")])
mApp->setTimestamp(jsonIn[F("val")]);
else if(F("sync_ntp") == jsonIn[F("cmd")])
@ -1093,9 +1104,19 @@ 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->setupStation();
}
#else
else if(F("save_eth") == jsonIn[F("cmd")]) {
mConfig->sys.eth.enabled = jsonIn[F("en")].as<bool>();
mConfig->sys.eth.pinCs = jsonIn[F("cs")].as<uint8_t>();
mConfig->sys.eth.pinSclk = jsonIn[F("sclk")].as<uint8_t>();
mConfig->sys.eth.pinMiso = jsonIn[F("miso")].as<uint8_t>();
mConfig->sys.eth.pinMosi = jsonIn[F("mosi")].as<uint8_t>();
mConfig->sys.eth.pinIrq = jsonIn[F("irq")].as<uint8_t>();
mConfig->sys.eth.pinRst = jsonIn[F("reset")].as<uint8_t>();
mApp->saveSettings(true);
}
#endif /* !defined(ETHERNET */
else if(F("save_iv") == jsonIn[F("cmd")]) {
Inverter<> *iv = mSys->getInverterByPos(jsonIn[F("id")], false);

6
src/web/html/history.html

@ -136,7 +136,7 @@
return [
mlNs("polyline", {stroke: "url(#gLine)", fill: "none", points: pts}),
mlNs("polyline", {stroke: "none", fill: "url(#gFill)", points: pts2}),
mlNs("text", {x: i*.8, y: 10}, "{#MAX_DAY}: " + String(obj.max) + "W"),
mlNs("text", {x: i*.8, y: 10}, "{#MAXIMUM}: " + String(obj.max) + "W"),
mlNs("text", {x: i*.8, y: 25}, "{#LAST_VALUE}: " + String(lastVal) + "W")
]
}
@ -150,11 +150,11 @@
parseRssi(obj.generic)
window.setInterval("getAjax('/api/powerHistory', parsePowerHistory)", obj.refresh * 1000)
setTimeout(() => {
window.setInterval("getAjax('/api/powerHistoryDay', parsePowerHistoryDay)", refresh * 1000)
window.setInterval("getAjax('/api/powerHistoryDay', parsePowerHistoryDay)", obj.refresh * 1000)
}, 200)
/*IF_ENABLE_HISTORY_YIELD_PER_DAY*/
setTimeout(() => {
window.setInterval("getAjax('/api/yieldDayHistory', parseYieldDayHistory)", refresh * 1000)
window.setInterval("getAjax('/api/yieldDayHistory', parseYieldDayHistory)", obj.refresh * 1000)
}, 400)
/*ENDIF_ENABLE_HISTORY_YIELD_PER_DAY*/
}

103
src/web/html/setup.html

@ -57,22 +57,9 @@
<div class="col-12 col-sm-3 my-2">{#AP_PWD}</div>
<div class="col-12 col-sm-9"><input type="text" name="ap_pwd" minlength="8" /></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">{#SEARCH_NETWORKS}</div>
<div class="col-12 col-sm-9"><input type="button" name="scanbtn" id="scanbtn" class="btn" value="{#BTN_SCAN}" onclick="scan()"/></div>
</div>
<div class="row mb-2 mb-sm-3">
<div class="col-12 col-sm-3 my-2">{#AVAIL_NETWORKS}</div>
<div class="col-12 col-sm-9">
<select name="networks" id="networks" onChange="selNet()">
<option value="-1" selected disabled hidden>{#NETWORK_NOT_SCANNED}</option>
</select>
</div>
</div>
<div class="row mb-2 mb-sm-3">
<div class="col-12 col-sm-3 my-2">SSID</div>
<div class="col-12 col-sm-9"><input type="text" name="ssid"/></div>
<div class="col-12 col-sm-9"><input type="text" name="ssid"/><br/><a href="/wizard">{#SCAN_WIFI}</a></div>
</div>
<div class="row mb-2 mb-sm-3">
<div class="col-12 col-sm-3">{#SSID_HIDDEN}</div>
@ -247,7 +234,7 @@
<p class="des">{#MQTT_NOTE}</p>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">{#INTERVAL}</div>
<div class="col-12 col-sm-9"><input type="number" name="mqttInterval" title="Invalid input" /></div>
<div class="col-12 col-sm-9"><input tyCMT2300Ape="number" name="mqttInterval" title="Invalid input" /></div>
</div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Discovery Config (homeassistant)</div>
@ -272,6 +259,10 @@
<p class="des">{#RADIO} (CMT2300A)</p>
<div id="cmt"></div>
<!--ENDIF_ESP32-->
<!--IF_ETHERNET-->
<p class="des">Ethernet</p>
<div id="eth"></div>
<!--ENDIF_ETHERNET-->
</fieldset>
</div>
<!--IF_PLUGIN_DISPLAY-->
@ -625,12 +616,6 @@
setTimeout(function() {getAjax('/api/index', apiCbNtp2)}, 2000)
}
function scan() {
var obj = {cmd: "scan_wifi", token: "*"}
getAjax("/api/setup", apiCbWifi, "POST", JSON.stringify(obj));
setTimeout(function() {getAjax('/api/setup/networks', listNetworks)}, 5000);
}
function syncTime() {
var obj = {cmd: "sync_ntp", token: "*"}
getAjax("/api/setup", apiCbNtp, "POST", JSON.stringify(obj))
@ -968,7 +953,7 @@
}
}
function parsePinout(obj, type, system) {
function parsePinout(obj) {
var e = document.getElementById("pinout");
/*IF_ESP32*/
var pinList = esp32pins;
@ -1009,7 +994,7 @@
)
}
function parseNrfRadio(obj, objPin, type, system) {
function parseNrfRadio(obj, objPin) {
var e = document.getElementById("rf24");
var en = inp("nrfEnable", null, null, ["cb"], "nrfEnable", "checkbox");
en.checked = obj["en"];
@ -1036,11 +1021,11 @@
])
);
if ("ESP8266" == type) {
pins = [['cs', 'pinCs'], ['ce', 'pinCe'], ['irq', 'pinIrq']];
} else {
pins = [['cs', 'pinCs'], ['ce', 'pinCe'], ['irq', 'pinIrq'], ['sclk', 'pinSclk'], ['mosi', 'pinMosi'], ['miso', 'pinMiso']];
}
/*IF_ESP32*/
var pins = [['cs', 'pinCs'], ['ce', 'pinCe'], ['irq', 'pinIrq'], ['sclk', 'pinSclk'], ['mosi', 'pinMosi'], ['miso', 'pinMiso']];
/*ELSE*/
var pins = [['cs', 'pinCs'], ['ce', 'pinCe'], ['irq', 'pinIrq']];
/*ENDIF_ESP32*/
for(p of pins) {
e.append(
ml("div", {class: "row mb-3"}, [
@ -1054,7 +1039,7 @@
}
/*IF_ESP32*/
function parseCmtRadio(obj, type, system) {
function parseCmtRadio(obj) {
var e = document.getElementById("cmt");
var en = inp("cmtEnable", null, null, ["cb"], "cmtEnable", "checkbox");
var pinList = esp32pins;
@ -1069,7 +1054,6 @@
/*ENDIF_ESP32-C3*/
en.checked = obj["en"];
e.replaceChildren (
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-8 col-sm-3 my-2"}, "{#CMT_ENABLE}"),
@ -1096,6 +1080,42 @@
}
/*ENDIF_ESP32*/
/*IF_ETHERNET*/
function parseEth(obj) {
var e = document.getElementById("eth");
var en = inp("ethEn", null, null, ["cb"], "ethEn", "checkbox");
var pinList = esp32pins;
/*IF_ESP32-S2*/
pinList = esp32sXpins;
/*ENDIF_ESP32-S2*/
/*IF_ESP32-S3*/
pinList = esp32sXpins;
/*ENDIF_ESP32-S3*/
/*IF_ESP32-C3*/
pinList = esp32c3pins;
/*ENDIF_ESP32-C3*/
en.checked = obj["en"];
e.replaceChildren (
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-8 col-sm-3 my-2"}, "{#ETH_ENABLE}"),
ml("div", {class: "col-4 col-sm-9"}, en)
])
);
pins = [['cs', 'ethCs'], ['sclk', 'ethSclk'], ['miso', 'ethMiso'], ['mosi', 'ethMosi'], ['irq', 'ethIrq'], ['reset', 'ethRst']];
for(p of pins) {
e.append(
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, p[0].toUpperCase()),
ml("div", {class: "col-12 col-sm-9"},
sel(p[1], pinList, obj[p[0]])
)
])
);
}
}
/*ENDIF_ETHERNET*/
function parseSerial(obj) {
var e = document.getElementById("serialCb")
var l = [["serEn", "show_live_data", "{#LOG_PRINT_INVERTER_DATA}"], ["serDbg", "debug", "{#LOG_SERIAL_DEBUG}"], ["priv", "priv", "{#LOG_PRIVACY_MODE}"], ["wholeTrace", "wholeTrace", "{#LOG_PRINT_TRACES}"], ["log2mqtt", "log2mqtt", "{#LOG_TO_MQTT}"]]
@ -1442,10 +1462,10 @@
modal("{#ZE_GROUP_EDIT_MODAL}: " + obj.id, html);
// ser.dispatchEvent(new Event('change'));
// Inhalt fr pm_type aus config laden und in eine Funktion ausgliedern
// Inhalt f?r pm_type aus config laden und in eine Funktion ausgliedern
var e = document.getElementById("pm_type");
selDelAllOpt(e);
// TODO: bersetzen?
// TODO: ?bersetzen?
e.appendChild(opt("0", "---"));
e.appendChild(opt("1", "Shelly"));
e.appendChild(opt("2", "Tasmota"));
@ -1482,7 +1502,7 @@
for (var inv = 0; inv < maxInv; inv++) {
var e = document.getElementById("invTarget"+inv);
selDelAllOpt(e);
// TODO: bersetzen?
// TODO: ?bersetzen?
e.appendChild(opt("-1", "---"));
e.appendChild(opt("0", "Sum"));
e.appendChild(opt("1", "L1"));
@ -1719,13 +1739,15 @@
parseMqtt(root["mqtt"]);
parseNtp(root["ntp"]);
parseSun(root["sun"]);
parsePinout(root["pinout"], root["system"]["esp_type"], root["system"]);
parseNrfRadio(root["radioNrf"], root["pinout"], root["system"]["esp_type"], root["system"]);
parsePinout(root.pinout);
parseNrfRadio(root["radioNrf"], root["pinout"]);
/*IF_ESP32*/
parseCmtRadio(root["radioCmt"], root["system"]["esp_type"], root["system"]);
parseCmtRadio(root.radioCmt);
/*ENDIF_ESP32*/
/*IF_ETHERNET*/
parseEth(root.eth)
/*ENDIF_ETHERNET*/
parseSerial(root["serial"]);
/*IF_PLUGIN_DISPLAY*/
parseDisplay(root["display"], root["system"]["esp_type"], root["system"]);
@ -1748,13 +1770,6 @@
s.appendChild(opt("-1", "{#NO_NETWORK_FOUND}"));
}
function selNet() {
var s = document.getElementById("networks");
var e = document.getElementsByName("ssid")[0];
if(-1 != s.value)
e.value = s.value;
}
getAjax("/api/setup", parse);
</script>
</body>

224
src/web/html/wizard.html

@ -15,6 +15,165 @@
var found = false;
var c = document.getElementById("con");
/*IF_ESP32*/
var pinList = [
[255, "{#PIN_OFF}"],
[0, "GPIO0"],
[1, "TX (GPIO1)"],
[2, "GPIO2 (LED)"],
[3, "RX (GPIO3)"],
[4, "GPIO4"],
[5, "GPIO5"],
[12, "GPIO12 (HSPI MISO)"],
[13, "GPIO13 (HSPI MOSI)"],
[14, "GPIO14 (HSPI SCLK)"],
[15, "GPIO15"],
[16, "GPIO16"],
[17, "GPIO17"],
[18, "GPIO18 (VSPI SCLK)"],
[19, "GPIO19 (VSPI MISO)"],
[21, "GPIO21 (SDA)"],
[22, "GPIO22 (SCL)"],
[23, "GPIO23 (VSPI MOSI)"],
[25, "GPIO25"],
[26, "GPIO26"],
[27, "GPIO27"],
[32, "GPIO32"],
[33, "GPIO33"],
[34, "GPIO34 ({#PIN_INPUT_ONLY})"],
[35, "GPIO35 ({#PIN_INPUT_ONLY})"],
[36, "VP (GPIO36, {#PIN_INPUT_ONLY})"],
[39, "VN (GPIO39, {#PIN_INPUT_ONLY})"]
];
/*IF_ESP32-S2*/
pinList = [
[255, "off / default"],
[0, "GPIO0 ({#PIN_DONT_USE} - BOOT)"],
[1, "GPIO1"],
[2, "GPIO2"],
[3, "GPIO3"],
[4, "GPIO4"],
[5, "GPIO5"],
[6, "GPIO6"],
[7, "GPIO7"],
[8, "GPIO8"],
[9, "GPIO9"],
[10, "GPIO10"],
[11, "GPIO11"],
[12, "GPIO12"],
[13, "GPIO13"],
[14, "GPIO14"],
[15, "GPIO15"],
[16, "GPIO16"],
[17, "GPIO17"],
[18, "GPIO18"],
[19, "GPIO19 ({#PIN_DONT_USE} - USB-)"],
[20, "GPIO20 ({#PIN_DONT_USE} - USB+)"],
[21, "GPIO21"],
[26, "GPIO26 (PSRAM - {#PIN_NOT_AVAIL})"],
[27, "GPIO27 (FLASH - {#PIN_NOT_AVAIL})"],
[28, "GPIO28 (FLASH - {#PIN_NOT_AVAIL})"],
[29, "GPIO29 (FLASH - {#PIN_NOT_AVAIL})"],
[30, "GPIO30 (FLASH - {#PIN_NOT_AVAIL})"],
[31, "GPIO31 (FLASH - {#PIN_NOT_AVAIL})"],
[32, "GPIO32 (FLASH - {#PIN_NOT_AVAIL})"],
[33, "GPIO33 (not exposed on S3-WROOM modules)"],
[34, "GPIO34 (not exposed on S3-WROOM modules)"],
[35, "GPIO35"],
[36, "GPIO36"],
[37, "GPIO37"],
[38, "GPIO38"],
[39, "GPIO39"],
[40, "GPIO40"],
[41, "GPIO41"],
[42, "GPIO42"],
[43, "GPIO43"],
[44, "GPIO44"],
[45, "GPIO45 ({#PIN_DONT_USE} - STRAPPING PIN)"],
[46, "GPIO46 ({#PIN_DONT_USE} - STRAPPING PIN)"],
[47, "GPIO47"],
[48, "GPIO48"],
];
/*ENDIF_ESP32-S2*/
/*IF_ESP32-S3*/
pinList = [
[255, "off / default"],
[0, "GPIO0 ({#PIN_DONT_USE} - BOOT)"],
[1, "GPIO1"],
[2, "GPIO2"],
[3, "GPIO3"],
[4, "GPIO4"],
[5, "GPIO5"],
[6, "GPIO6"],
[7, "GPIO7"],
[8, "GPIO8"],
[9, "GPIO9"],
[10, "GPIO10"],
[11, "GPIO11"],
[12, "GPIO12"],
[13, "GPIO13"],
[14, "GPIO14"],
[15, "GPIO15"],
[16, "GPIO16"],
[17, "GPIO17"],
[18, "GPIO18"],
[19, "GPIO19 ({#PIN_DONT_USE} - USB-)"],
[20, "GPIO20 ({#PIN_DONT_USE} - USB+)"],
[21, "GPIO21"],
[26, "GPIO26 (PSRAM - {#PIN_NOT_AVAIL})"],
[27, "GPIO27 (FLASH - {#PIN_NOT_AVAIL})"],
[28, "GPIO28 (FLASH - {#PIN_NOT_AVAIL})"],
[29, "GPIO29 (FLASH - {#PIN_NOT_AVAIL})"],
[30, "GPIO30 (FLASH - {#PIN_NOT_AVAIL})"],
[31, "GPIO31 (FLASH - {#PIN_NOT_AVAIL})"],
[32, "GPIO32 (FLASH - {#PIN_NOT_AVAIL})"],
[33, "GPIO33 (not exposed on S3-WROOM modules)"],
[34, "GPIO34 (not exposed on S3-WROOM modules)"],
[35, "GPIO35"],
[36, "GPIO36"],
[37, "GPIO37"],
[38, "GPIO38"],
[39, "GPIO39"],
[40, "GPIO40"],
[41, "GPIO41"],
[42, "GPIO42"],
[43, "GPIO43"],
[44, "GPIO44"],
[45, "GPIO45 ({#PIN_DONT_USE} - STRAPPING PIN)"],
[46, "GPIO46 ({#PIN_DONT_USE} - STRAPPING PIN)"],
[47, "GPIO47"],
[48, "GPIO48"],
];
/*ENDIF_ESP32-S3*/
/*IF_ESP32-C3*/
pinList = [
[255, "off / default"],
[0, "GPIO0"],
[1, "GPIO1"],
[2, "GPIO2"],
[3, "GPIO3"],
[4, "GPIO4"],
[5, "GPIO5"],
[6, "GPIO6"],
[7, "GPIO7"],
[8, "GPIO8"],
[9, "GPIO9"],
[10, "GPIO10"],
[11, "GPIO11"],
[12, "GPIO12 (PSRAM/FLASH)"],
[13, "GPIO13 (PSRAM/FLASH)"],
[14, "GPIO14 (PSRAM/FLASH)"],
[15, "GPIO15 (PSRAM/FLASH)"],
[16, "GPIO16 (PSRAM/FLASH)"],
[17, "GPIO17 (PSRAM/FLASH)"],
[18, "GPIO18 ({#PIN_DONT_USE} - USB-)"],
[19, "GPIO19 ({#PIN_DONT_USE} - USB+)"],
[20, "GPIO20 (RX)"],
[21, "GPIO21 (TX)"],
];
/*ENDIF_ESP32-C3*/
/*ENDIF_ESP32*/
function sect(e1, e2) {
return ml("div", {class: "row"}, [
ml("div", {class: "col-12"}, ml("p", {}, e1)),
@ -22,7 +181,36 @@
])
}
function wifi() {
/*IF_ETHERNET*/
var pins = ['cs', 'sclk', 'miso', 'mosi', 'irq', 'reset']
function step1(obj) {
console.log(obj)
lst = []
for(p of pins) {
lst.push(
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-12 col-sm-3 my-2"}, p.toUpperCase()),
ml("div", {class: "col-12 col-sm-9"},
sel(p, pinList, obj[p])
)
])
)
}
let en = inp("en", null, null, ["cb"], "en", "checkbox");
en.checked = obj["en"];
return sect("{#NETWORK_SETUP}", [
ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-8"}, "{#ETH_ENABLE}"),
ml("div", {class: "col-4"}, en)
]),
...lst,
ml("div", {class: "row my-4"}, ml("div", {class: "col a-r"}, ml("input", {type: "button", class:"btn", value: "{#BTN_REBOOT}", onclick: () => {saveEth()}}, null))),
ml("div", {class: "row mt-5"}, ml("div", {class: "col a-c"}, ml("a", {href: "http://192.168.4.1/"}, "{#STOP_WIZARD}")))
])
}
/*ELSE*/
function step1() {
return ml("div", {}, [
ml("div", {class: "row my-5"}, ml("div", {class: "col"}, ml("span", {class: "fs-1"}, "{#WELCOME}"))),
ml("div", {class: "row"}, ml("div", {class: "col"}, ml("span", {class: "fs-5"}, "{#NETWORK_SETUP}"))),
@ -30,9 +218,10 @@
sect("{#WIFI_MANUAL}", ml("input", {id: "man", type: "text"})),
sect("{#WIFI_PASSWORD}", ml("input", {id: "pwd", type: "password"})),
ml("div", {class: "row my-4"}, ml("div", {class: "col a-r"}, ml("input", {type: "button", class:"btn", value: "{#BTN_NEXT}", onclick: () => {saveWifi()}}, null))),
ml("div", {class: "row mt-5"}, ml("div", {class: "col a-c"}, ml("a", {href: "http://192.168.4.1/"}, "{#STOP_WIZARD}")))
ml("div", {class: "row mt-5"}, ml("div", {class: "col a-c"}, ml("a", {href: "http://192.168.4.1/index"}, "{#STOP_WIZARD}")))
])
}
/*ENDIF_ETHERNET*/
function checkWifi() {
c.replaceChildren(
@ -40,9 +229,9 @@
ml("div", {class: "row"}, ml("div", {class: "col"}, ml("span", {class: "fs-5"}, "{#TEST_CONNECTION}"))),
sect("{#TRY_TO_CONNECT}", ml("span", {id: "state"}, "{#CONNECTING}")),
ml("div", {class: "row my-4"}, ml("div", {class: "col a-r"}, ml("input", {type: "button", class:"btn hide", id: "btn", value: "{#BTN_FINISH}", onclick: () => {redirect()}}, null))),
ml("div", {class: "row mt-5"}, ml("div", {class: "col a-c"}, ml("a", {href: "http://192.168.4.1/"}, "{#STOP_WIZARD}")))
ml("div", {class: "row mt-5"}, ml("div", {class: "col a-c"}, ml("a", {href: "http://192.168.4.1/index"}, "{#STOP_WIZARD}")))
)
v = setInterval(() => {getAjax('/api/setup/getip', printIp)}, 2500);
v = setInterval(() => {getAjax('/api/setup/getip', printIp)}, 300);
}
function redirect() {
@ -57,14 +246,33 @@
}
}
/*IF_ETHERNET*/
function saveEth() {
let o = {
cmd: "save_eth",
en: document.getElementsByName("en")[0].checked
}
for(p of pins) {
o[p] = document.getElementsByName(p)[0].value
}
getAjax("/api/setup", ((o) => {}), "POST", JSON.stringify(o));
}
/*ELSE*/
function saveWifi() {
var ssid = document.getElementById("net").value;
if(-1 == ssid)
ssid = document.getElementById("man").value;
getAjax("/api/setup", ((o) => {if(!o.error) checkWifi()}), "POST", JSON.stringify({cmd: "save_wifi", ssid: ssid, pwd: document.getElementById("pwd").value}));
}
/*ENDIF_ETHERNET*/
/*IF_ETHERNET*/
getAjax("/api/setup", ((o) => c.append(step1(o.eth))));
/*ELSE*/
function nets(obj) {
if(!obj.success)
return;
var e = document.getElementById("net");
if(obj.networks.length > 0) {
var a = []
@ -75,13 +283,13 @@
}
e.replaceChildren(...a)
}
getAjax("/api/setup", ((o) => {}), "POST", JSON.stringify({cmd: "scan_wifi"}));
}
getAjax("/api/setup", ((o) => {}), "POST", JSON.stringify({cmd: "scan_wifi"}));
c.append(wifi())
c.append(step1())
getAjax('/api/setup/networks', nets)
v = setInterval(() => {getAjax('/api/setup/networks', nets)}, 1000)
/*ENDIF_ETHERNET*/
v = setInterval(() => {getAjax('/api/setup/networks', nets)}, 2500);
</script>
</body>
</html>

70
src/web/lang.json

@ -81,12 +81,17 @@
{
"token": "BTN_NEXT",
"en": "next >>",
"de": "prüfen >>"
"de": "pr&uuml;fen >>"
},
{
"token": "BTN_REBOOT",
"en": "reboot >>",
"de": "Ahoy neustarten >>"
},
{
"token": "TEST_CONNECTION",
"en": "Test Connection",
"de": "Verbindung wird überprüft"
"de": "Verbindung wird &uuml;berpr&uuml;ft"
},
{
"token": "TRY_TO_CONNECT",
@ -112,6 +117,36 @@
"token": "NUM_NETWORKS_FOUND",
"en": "Network(s) found",
"de": "Netzwerk(e) gefunden"
},
{
"token": "PIN_OFF",
"en": "off / default",
"de": "aus / Standard"
},
{
"token": "PIN_NO_IRQ",
"en": "no IRQ!",
"de": "kein Interrupt!"
},
{
"token": "PIN_INPUT_ONLY",
"en": "in only",
"de": "nur Eingang"
},
{
"token": "PIN_DONT_USE",
"en": "DONT USE",
"de": "nicht benutzen"
},
{
"token": "PIN_NOT_AVAIL",
"en": "not available",
"de": "nicht verf&uuml;gbar"
},
{
"token": "ETH_ENABLE",
"en": "Ethernet enable",
"de": "Ethernet aktivieren"
}
]
},
@ -224,19 +259,9 @@
"de": "Netzwerke suchen"
},
{
"token": "BTN_SCAN",
"en": "scan",
"de": "Suche starten"
},
{
"token": "AVAIL_NETWORKS",
"en": "Avail Networks",
"de": "Verf&uuml;gbare Netzwerke"
},
{
"token": "NETWORK_NOT_SCANNED",
"en": "not scanned",
"de": "nicht gesucht"
"token": "SCAN_WIFI",
"en": "scan for WiFi networks",
"de": "nach WiFi Netzwerken suchen"
},
{
"token": "SSID_HIDDEN",
@ -596,7 +621,7 @@
{
"token": "BTN_INV_ADD",
"en": "add Inverter",
"de": "Wechselrichter hinzufuegen"
"de": "Wechselrichter hinzuf\u00FCgen"
},
{
"token": "INV_INPUT",
@ -621,7 +646,7 @@
{
"token": "TAB_INPUTS",
"en": "Inputs",
"de": "Eingaenge"
"de": "Eing&auml;nge"
},
{
"token": "TAB_RADIO",
@ -723,6 +748,11 @@
"en": "CMT2300A radio enable",
"de": "CMT2300A Funkmodul aktivieren"
},
{
"token": "ETH_ENABLE",
"en": "Ethernet enable",
"de": "Ethernet aktivieren"
},
{
"token": "DISP_NONE",
"en": "None",
@ -1769,9 +1799,9 @@
"de": "Gesamtertrag pro Tag"
},
{
"token": "MAX_DAY",
"en": "Maximum day",
"de": "Tagesmaximum"
"token": "MAXIMUM",
"en": "Maximum",
"de": "Maximum"
},
{
"token": "LAST_VALUE",

61
src/web/web.h

@ -40,14 +40,19 @@
//#define WEB_SERIAL_BUF_SIZE 2048
#define WEB_SERIAL_BUF_SIZE 3072
const char* const pinArgNames[] = {"pinCs", "pinCe", "pinIrq", "pinSclk", "pinMosi", "pinMiso", "pinLed0", "pinLed1", "pinLed2", "pinLedHighActive", "pinLedLum", "pinCmtSclk", "pinSdio", "pinCsb", "pinFcsb", "pinGpio3"};
const char* const pinArgNames[] = {
"pinCs", "pinCe", "pinIrq", "pinSclk", "pinMosi", "pinMiso", "pinLed0",
"pinLed1", "pinLed2", "pinLedHighActive", "pinLedLum", "pinCmtSclk",
"pinSdio", "pinCsb", "pinFcsb", "pinGpio3"
#if defined (ETHERNET)
, "ethCs", "ethSclk", "ethMiso", "ethMosi", "ethIrq", "ethRst"
#endif
};
template <class HMSYSTEM>
class Web {
public:
Web(void) : mWeb(80), mEvts("/events") {
memset(mSerialBuf, 0, WEB_SERIAL_BUF_SIZE);
}
Web(void) : mWeb(80), mEvts("/events") {}
void setup(IApp *app, HMSYSTEM *sys, settings_t *config) {
mApp = app;
@ -55,7 +60,8 @@ class Web {
mConfig = config;
DPRINTLN(DBG_VERBOSE, F("app::setup-on"));
mWeb.on("/", HTTP_GET, std::bind(&Web::onIndex, this, std::placeholders::_1));
mWeb.on("/", HTTP_GET, std::bind(&Web::onIndex, this, std::placeholders::_1, true));
mWeb.on("/index", HTTP_GET, std::bind(&Web::onIndex, this, std::placeholders::_1, false));
mWeb.on("/login", HTTP_ANY, std::bind(&Web::onLogin, this, std::placeholders::_1));
mWeb.on("/logout", HTTP_GET, std::bind(&Web::onLogout, this, std::placeholders::_1));
mWeb.on("/colors.css", HTTP_GET, std::bind(&Web::onColor, this, std::placeholders::_1));
@ -105,11 +111,17 @@ class Web {
void tickSecond() {
if (mSerialClientConnnected) {
if(nullptr == mSerialBuf)
return;
if (mSerialBufFill > 0) {
mEvts.send(mSerialBuf, "serial", millis());
memset(mSerialBuf, 0, WEB_SERIAL_BUF_SIZE);
mSerialBufFill = 0;
}
} else if(nullptr != mSerialBuf) {
delete[] mSerialBuf;
mSerialBuf = nullptr;
}
}
@ -181,6 +193,9 @@ class Web {
if (!mSerialClientConnnected)
return;
if(nullptr == mSerialBuf)
return;
msg.replace("\r\n", "<rn>");
if (mSerialAddTime) {
if ((13 + mSerialBufFill) < WEB_SERIAL_BUF_SIZE) {
@ -297,6 +312,10 @@ class Web {
void onConnect(AsyncEventSourceClient *client) {
DPRINTLN(DBG_VERBOSE, "onConnect");
if(nullptr == mSerialBuf) {
mSerialBuf = new char[WEB_SERIAL_BUF_SIZE];
memset(mSerialBuf, 0, WEB_SERIAL_BUF_SIZE);
}
mSerialClientConnnected = true;
if (client->lastId())
@ -305,7 +324,11 @@ class Web {
client->send("hello!", NULL, millis(), 1000);
}
void onIndex(AsyncWebServerRequest *request) {
void onIndex(AsyncWebServerRequest *request, bool checkAp = true) {
if(mApp->isApActive() && checkAp) {
onWizard(request);
return;
}
getPage(request, PROT_MASK_INDEX, index_html, index_html_len);
}
@ -388,6 +411,7 @@ class Web {
void showNotFound(AsyncWebServerRequest *request) {
checkProtection(request);
//DBGPRINTLN(request->url());
request->redirect("/wizard");
}
@ -433,10 +457,10 @@ class Web {
request->arg("ssid").toCharArray(mConfig->sys.stationSsid, SSID_LEN);
if (request->arg("pwd") != "{PWD}")
request->arg("pwd").toCharArray(mConfig->sys.stationPwd, PWD_LEN);
if (request->arg("ap_pwd") != "")
request->arg("ap_pwd").toCharArray(mConfig->sys.apPwd, PWD_LEN);
mConfig->sys.isHidden = (request->arg("hidd") == "on");
#endif /* !defined(ETHERNET) */
if (request->arg("ap_pwd") != "")
request->arg("ap_pwd").toCharArray(mConfig->sys.apPwd, PWD_LEN);
if (request->arg("device") != "")
request->arg("device").toCharArray(mConfig->sys.deviceName, DEVNAME_LEN);
mConfig->sys.darkMode = (request->arg("darkMode") == "on");
@ -486,7 +510,12 @@ class Web {
// pinout
for (uint8_t i = 0; i < 16; i++) {
#if defined(ETHERNET)
for (uint8_t i = 0; i < 22; i++)
#else
for (uint8_t i = 0; i < 16; i++)
#endif
{
uint8_t pin = request->arg(String(pinArgNames[i])).toInt();
switch(i) {
case 0: mConfig->nrf.pinCs = ((pin != 0xff) ? pin : DEF_NRF_CS_PIN); break;
@ -505,11 +534,23 @@ class Web {
case 13: mConfig->cmt.pinCsb = pin; break;
case 14: mConfig->cmt.pinFcsb = pin; break;
case 15: mConfig->cmt.pinIrq = pin; break;
#if defined(ETHERNET)
case 16: mConfig->sys.eth.pinCs = pin; break;
case 17: mConfig->sys.eth.pinSclk = pin; break;
case 18: mConfig->sys.eth.pinMiso = pin; break;
case 19: mConfig->sys.eth.pinMosi = pin; break;
case 20: mConfig->sys.eth.pinIrq = pin; break;
case 21: mConfig->sys.eth.pinRst = pin; break;
#endif
}
}
mConfig->nrf.enabled = (request->arg("nrfEnable") == "on");
mConfig->cmt.enabled = (request->arg("cmtEnable") == "on");
#if defined(ETHERNET)
mConfig->sys.eth.enabled = (request->arg("ethEn") == "on");
#endif
// ntp
if (request->arg("ntpAddr") != "") {
@ -953,7 +994,7 @@ class Web {
settings_t *mConfig = nullptr;
bool mSerialAddTime = true;
char mSerialBuf[WEB_SERIAL_BUF_SIZE];
char *mSerialBuf = nullptr;
uint16_t mSerialBufFill = 0;
bool mSerialClientConnnected = false;

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