Browse Source

0.8.1030001-zero

Merge branch 'development03' into zero-export
pull/1557/head
lumapu 1 year 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! 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 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! 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 Check our FAQ: https://ahoydtu.de/faq
- type: markdown - type: markdown
@ -35,7 +35,7 @@ body:
label: Assembly label: Assembly
description: description:
options: options:
- I did the assebly by myself - I did the assembly by myself
- the DTU was already assembled - the DTU was already assembled
validations: validations:
required: true required: true
@ -84,7 +84,7 @@ body:
label: Connection picture label: Connection picture
description: description:
options: options:
- label: I will attach/upload an Image of my wiring - label: I will attach/upload an image of my wiring
validations: validations:
required: true required: true
- type: markdown - type: markdown

1
.gitignore

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

20
scripts/convertHtml.py

@ -9,19 +9,29 @@ from pathlib import Path
import subprocess import subprocess
import configparser import configparser
Import("env") Import("env")
build_flags = []
import htmlPreprocessorDefines as prepro 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(): def get_build_flags():
getFlagsOfEnv("env:" + env['PIOENV'])
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read('platformio.ini') 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 # translate board
board = config["env:" + env['PIOENV']]['board'] board = config["env:" + env['PIOENV']]['board']

2
src/.gitignore

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

28
src/CHANGES.md

@ -1,5 +1,33 @@
# Development Changes # 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 ## 0.8.97 - 2024-03-22
* add support for newest generation of inverters with A-F in their serial number * add support for newest generation of inverters with A-F in their serial number
* fix NTP and sunrise / sunset * 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); 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 #endif
#ifdef ETHERNET #ifdef ETHERNET
delay(1000); 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 #endif // ETHERNET
mNetwork->setup(mConfig, &mTimestamp, [this](bool gotIp) { this->onNetwork(gotIp); }, [this](bool gotTime) { this->onNtpUpdate(gotTime); });
#if !defined(ETHERNET) mNetwork->begin();
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) */
esp_task_wdt_reset(); esp_task_wdt_reset();
@ -116,7 +114,7 @@ void app::setup() {
#if defined(ENABLE_MQTT) #if defined(ENABLE_MQTT)
mMqttEnabled = (mConfig->mqtt.broker[0] > 0); mMqttEnabled = (mConfig->mqtt.broker[0] > 0);
if (mMqttEnabled) { 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)); mMqtt.setSubscriptionCb(std::bind(&app::mqttSubRxCb, this, std::placeholders::_1));
mCommunication.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); }); mCommunication.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); });
} }
@ -205,7 +203,6 @@ void app::loop(void) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::onNetwork(bool gotIp) { void app::onNetwork(bool gotIp) {
DPRINTLN(DBG_INFO, F("onNetwork"));
mNetworkConnected = gotIp; mNetworkConnected = gotIp;
ah::Scheduler::resetTicker(); ah::Scheduler::resetTicker();
regularTickers(); //reinstall regular tickers regularTickers(); //reinstall regular tickers
@ -213,12 +210,6 @@ void app::onNetwork(bool gotIp) {
mMqttReconnect = true; mMqttReconnect = true;
mSunrise = 0; // needs to be set to 0, to reinstall sunrise and ivComm tickers! mSunrise = 0; // needs to be set to 0, to reinstall sunrise and ivComm tickers!
once(std::bind(&app::tickNtpUpdate, this), 2, "ntp2"); 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")); DPRINTLN(DBG_DEBUG, F("regularTickers"));
everySec(std::bind(&WebType::tickSecond, &mWeb), "webSc"); everySec(std::bind(&WebType::tickSecond, &mWeb), "webSc");
everySec([this]() { mProtection->tickSecond(); }, "prot"); everySec([this]() { mProtection->tickSecond(); }, "prot");
everySec([this]() {mNetwork->tickNetworkLoop(); }, "net");
// Plugins // Plugins
#if defined(PLUGIN_DISPLAY) #if defined(PLUGIN_DISPLAY)
@ -300,26 +292,14 @@ void app::updateNtp(void) {
void app::tickNtpUpdate(void) { void app::tickNtpUpdate(void) {
uint32_t nxtTrig = 5; // default: check again in 5 sec uint32_t nxtTrig = 5; // default: check again in 5 sec
#if defined(ETHERNET)
if (!mNtpReceived) if (!mNtpReceived)
mEth.updateNtpTime(); mNetwork->updateNtpTime();
else else {
nxtTrig = mConfig->ntp.interval * 60; // check again in configured interval
mNtpReceived = false; mNtpReceived = false;
#else }
if (!mNtpReceived)
mWifi.updateNtpTime();
else
mNtpReceived = false;
#endif
updateNtp(); 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; mMqttReconnect = false;
@ -476,19 +456,44 @@ void app::tickSend(void) {
for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
Inverter<> *iv = mSys.getInverterByPos(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) if(NULL == iv)
continue; return true;
if(!iv->config->enabled)
return true;
if(iv->config->enabled) {
if(!iv->commEnabled) { if(!iv->commEnabled) {
DPRINT_IVID(DBG_INFO, iv->id); DPRINT_IVID(DBG_INFO, iv->id);
DBGPRINTLN(F("no communication to the inverter (night time)")); DBGPRINTLN(F("no communication to the inverter (night time)"));
continue; return true;
} }
if(!iv->radio->isChipConnected()) if(!iv->radio->isChipConnected())
continue; return true;
bool notAvail = true;
if(InverterStatus::OFF != iv->status) if(InverterStatus::OFF != iv->status)
notAvail = false; notAvail = false;
@ -499,23 +504,7 @@ void app::tickSend(void) {
mCommunication.add(iv, cmd); mCommunication.add(iv, cmd);
}); });
// Plugin ZeroExport return notAvail;
// #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();
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -614,7 +603,6 @@ void app::resetSystem(void) {
mTimestamp = 1; mTimestamp = 1;
#endif #endif
mSendFirst = true;
mAllIvNotAvail = true; mAllIvNotAvail = true;
mSunrise = 0; mSunrise = 0;
@ -690,7 +678,7 @@ void app::updateLed(void) {
#if defined(PLUGIN_ZEROEXPORT) #if defined(PLUGIN_ZEROEXPORT)
void app::zeroexport() { void app::zeroexport() {
return; return;
// TODO: aufräumen // TODO: aufrumen
// TODO: umziehen nach loop // TODO: umziehen nach loop

57
src/app.h

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

7
src/appInterface.h

@ -26,13 +26,12 @@ class IApp {
virtual const char *getVersionModules() = 0; virtual const char *getVersionModules() = 0;
#if !defined(ETHERNET) #if !defined(ETHERNET)
virtual void scanAvailNetworks() = 0;
virtual bool getAvailNetworks(JsonObject obj) = 0; virtual bool getAvailNetworks(JsonObject obj) = 0;
virtual void setupStation(void) = 0; virtual void setupStation(void) = 0;
virtual void setStopApAllowedMode(bool allowed) = 0;
virtual String getStationIp(void) = 0;
virtual bool getWasInCh12to14(void) const = 0; virtual bool getWasInCh12to14(void) const = 0;
#endif /* defined(ETHERNET) */ #endif /* defined(ETHERNET) */
virtual String getIp(void) = 0;
virtual bool isApActive(void) = 0;
virtual uint32_t getUptime() = 0; virtual uint32_t getUptime() = 0;
virtual uint32_t getTimestamp() = 0; virtual uint32_t getTimestamp() = 0;
@ -44,7 +43,7 @@ class IApp {
virtual void getSchedulerInfo(uint8_t *max) = 0; virtual void getSchedulerInfo(uint8_t *max) = 0;
virtual void getSchedulerNames() = 0; virtual void getSchedulerNames() = 0;
virtual void triggerTickSend() = 0; virtual void triggerTickSend(uint8_t id) = 0;
virtual bool getRebootRequestState() = 0; virtual bool getRebootRequestState() = 0;
virtual bool getSettingsValid() = 0; virtual bool getSettingsValid() = 0;

2
src/config/config.h

@ -78,7 +78,7 @@
#define DEF_ETH_CS_PIN 15 #define DEF_ETH_CS_PIN 15
#endif #endif
#ifndef DEF_ETH_RST_PIN #ifndef DEF_ETH_RST_PIN
#define DEF_ETH_RST_PIN 2 #define DEF_ETH_RST_PIN DEF_PIN_OFF
#endif #endif
#else /* defined(ETHERNET) */ #else /* defined(ETHERNET) */
// time in seconds how long the station info (ssid + pwd) will be tried // 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 uint8_t gateway[4]; // standard gateway
} cfgIp_t; } 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 { typedef struct {
char deviceName[DEVNAME_LEN]; char deviceName[DEVNAME_LEN];
char adminPwd[PWD_LEN]; char adminPwd[PWD_LEN];
@ -72,12 +85,14 @@ typedef struct {
uint8_t region; uint8_t region;
int8_t timezone; int8_t timezone;
char apPwd[PWD_LEN];
#if !defined(ETHERNET) #if !defined(ETHERNET)
// wifi // wifi
char stationSsid[SSID_LEN]; char stationSsid[SSID_LEN];
char stationPwd[PWD_LEN]; char stationPwd[PWD_LEN];
char apPwd[PWD_LEN];
bool isHidden; bool isHidden;
#else
cfgEth_t eth;
#endif /* !defined(ETHERNET) */ #endif /* !defined(ETHERNET) */
cfgIp_t ip; cfgIp_t ip;
@ -554,10 +569,24 @@ class settings {
else { else {
snprintf(mCfg.sys.stationSsid, SSID_LEN, FB_WIFI_SSID); snprintf(mCfg.sys.stationSsid, SSID_LEN, FB_WIFI_SSID);
snprintf(mCfg.sys.stationPwd, PWD_LEN, FB_WIFI_PWD); snprintf(mCfg.sys.stationPwd, PWD_LEN, FB_WIFI_PWD);
snprintf(mCfg.sys.apPwd, PWD_LEN, WIFI_AP_PWD);
mCfg.sys.isHidden = false; 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); snprintf(mCfg.sys.deviceName, DEVNAME_LEN, DEF_DEVICE_NAME);
mCfg.sys.region = 0; // Europe mCfg.sys.region = 0; // Europe
@ -570,7 +599,11 @@ class settings {
mCfg.nrf.pinMosi = DEF_NRF_MOSI_PIN; mCfg.nrf.pinMosi = DEF_NRF_MOSI_PIN;
mCfg.nrf.pinSclk = DEF_NRF_SCLK_PIN; mCfg.nrf.pinSclk = DEF_NRF_SCLK_PIN;
#if defined(ETHERNET)
mCfg.nrf.enabled = false;
#else
mCfg.nrf.enabled = true; mCfg.nrf.enabled = true;
#endif
#if defined(ESP32) #if defined(ESP32)
mCfg.cmt.pinSclk = DEF_CMT_SCLK; 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.dns1, buf); obj[F("dns1")] = String(buf);
ah::ip2Char(mCfg.sys.ip.dns2, buf); obj[F("dns2")] = 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); 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 { } else {
#if !defined(ETHERNET) #if !defined(ETHERNET)
getChar(obj, F("ssid"), mCfg.sys.stationSsid, SSID_LEN); getChar(obj, F("ssid"), mCfg.sys.stationSsid, SSID_LEN);
@ -823,6 +866,16 @@ class settings {
if(mCfg.sys.protectionMask == 0) if(mCfg.sys.protectionMask == 0)
mCfg.sys.protectionMask = DEF_PROT_INDEX | DEF_PROT_LIVE | DEF_PROT_SERIAL | DEF_PROT_SETUP 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; | 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_MAJOR 0
#define VERSION_MINOR 8 #define VERSION_MINOR 8
#define VERSION_PATCH 970010 #define VERSION_PATCH 1030001
//------------------------------------- //-------------------------------------
typedef struct { typedef struct {
uint8_t ch; 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 "../utils/spiPatcher.h"
#include <esp_rom_gpio.h> #include <esp_rom_gpio.h>
#include <RF24_hal.h> #include <RF24.h>
#define NRF_MAX_TRANSFER_SZ 64 #define NRF_MAX_TRANSFER_SZ 64
#define NRF_DEFAULT_SPI_SPEED 10000000 // 10 MHz #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 if(p.packet[9] > ALL_FRAMES) { // indicates last frame
setExpectedFrames(p.packet[9] - ALL_FRAMES); 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 // 2024 Ahoy, https://ahoydtu.de
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ // Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#if defined(ETHERNET) #if defined(ETHERNET)
@ -18,17 +18,19 @@
void tcpipInit(); void tcpipInit();
void add_esp_interface_netif(esp_interface_t interface, esp_netif_t* esp_netif); void add_esp_interface_netif(esp_interface_t interface, esp_netif_t* esp_netif);
class EthSpi { class AhoyEthernetSpi {
public: public:
EthSpi() : AhoyEthernetSpi() :
eth_handle(nullptr), eth_handle(nullptr),
eth_netif(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) { 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_reset_pin(static_cast<gpio_num_t>(pin_rst));
gpio_set_direction(static_cast<gpio_num_t>(pin_rst), GPIO_MODE_OUTPUT); 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_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_sclk));
gpio_reset_pin(static_cast<gpio_num_t>(pin_mosi)); 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_reset_pin(static_cast<gpio_num_t>(pin_int));
gpio_set_pull_mode(static_cast<gpio_num_t>(pin_int), GPIO_PULLUP_ONLY); gpio_set_pull_mode(static_cast<gpio_num_t>(pin_int), GPIO_PULLUP_ONLY);
spi_bus_config_t buscfg = { spi_bus_config_t buscfg = {
.mosi_io_num = pin_mosi, .mosi_io_num = pin_mosi,
.miso_io_num = pin_miso, .miso_io_num = pin_miso,
@ -80,9 +83,11 @@ class EthSpi {
ESP_ERROR_CHECK(spi_bus_add_device(SPI3_HOST, &devcfg, &spi)); ESP_ERROR_CHECK(spi_bus_add_device(SPI3_HOST, &devcfg, &spi));
// Reset sequence // Reset sequence
if(-1 != pin_rst) {
delayMicroseconds(500); delayMicroseconds(500);
gpio_set_level(static_cast<gpio_num_t>(pin_rst), 1); gpio_set_level(static_cast<gpio_num_t>(pin_rst), 1);
delayMicroseconds(1000); delayMicroseconds(1000);
}
// Arduino function to start networking stack if not already started // Arduino function to start networking stack if not already started
tcpipInit(); 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 https://github.com/JChristensen/Timezone @ ^1.2.4
olikraus/U8g2 @ ^2.35.9 olikraus/U8g2 @ ^2.35.9
https://github.com/zinggjm/GxEPD2#1.5.3 https://github.com/zinggjm/GxEPD2#1.5.3
build_flags = build_flags =
-std=c++17 -std=c++17
-std=gnu++17 -std=gnu++17
@ -40,26 +41,37 @@ build_unflags =
-std=gnu++11 -std=gnu++11
[env:esp8266] [env:esp8266-minimal]
platform = espressif8266 platform = espressif8266
board = esp12e board = esp12e
board_build.f_cpu = 80000000L board_build.f_cpu = 80000000L
lib_deps =
${env.lib_deps}
https://github.com/me-no-dev/ESPAsyncUDP
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096 -DEMC_MIN_FREE_MEMORY=4096
-DENABLE_MQTT
;-Wl,-Map,output.map ;-Wl,-Map,output.map
monitor_filters = monitor_filters =
esp8266_exception_decoder 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] [env:esp8266-de]
platform = espressif8266 platform = espressif8266
board = esp12e board = esp12e
board_build.f_cpu = 80000000L board_build.f_cpu = 80000000L
build_flags = ${env.build_flags} lib_deps = ${env:esp8266.lib_deps}
-DEMC_MIN_FREE_MEMORY=4096 build_flags = ${env:esp8266.build_flags}
-DLANG_DE -DLANG_DE
-DENABLE_MQTT
;-Wl,-Map,output.map
monitor_filters = monitor_filters =
esp8266_exception_decoder esp8266_exception_decoder
@ -67,12 +79,10 @@ monitor_filters =
platform = espressif8266 platform = espressif8266
board = esp12e board = esp12e
board_build.f_cpu = 80000000L board_build.f_cpu = 80000000L
build_flags = ${env.build_flags} lib_deps = ${env:esp8266.lib_deps}
-DEMC_MIN_FREE_MEMORY=4096 build_flags = ${env:esp8266.build_flags}
-DENABLE_MQTT
-DPLUGIN_DISPLAY -DPLUGIN_DISPLAY
-DENABLE_HISTORY -DENABLE_HISTORY
;-Wl,-Map,output.map
monitor_filters = monitor_filters =
esp8266_exception_decoder esp8266_exception_decoder
@ -80,13 +90,9 @@ monitor_filters =
platform = espressif8266 platform = espressif8266
board = esp12e board = esp12e
board_build.f_cpu = 80000000L board_build.f_cpu = 80000000L
build_flags = ${env.build_flags} lib_deps = ${env:esp8266.lib_deps}
-DEMC_MIN_FREE_MEMORY=4096 build_flags = ${env:esp8266-all.build_flags}
-DLANG_DE -DLANG_DE
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
;-Wl,-Map,output.map
monitor_filters = monitor_filters =
esp8266_exception_decoder esp8266_exception_decoder
@ -94,12 +100,9 @@ monitor_filters =
platform = espressif8266 platform = espressif8266
board = esp12e board = esp12e
board_build.f_cpu = 80000000L board_build.f_cpu = 80000000L
build_flags = ${env.build_flags} lib_deps = ${env:esp8266.lib_deps}
-DEMC_MIN_FREE_MEMORY=4096 build_flags = ${env:esp8266-all.build_flags}
-DENABLE_PROMETHEUS_EP -DENABLE_PROMETHEUS_EP
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
monitor_filters = monitor_filters =
esp8266_exception_decoder esp8266_exception_decoder
@ -107,23 +110,9 @@ monitor_filters =
platform = espressif8266 platform = espressif8266
board = esp12e board = esp12e
board_build.f_cpu = 80000000L board_build.f_cpu = 80000000L
build_flags = ${env.build_flags} lib_deps = ${env:esp8266.lib_deps}
-DEMC_MIN_FREE_MEMORY=4096 build_flags = ${env:esp8266-prometheus.build_flags}
-DENABLE_PROMETHEUS_EP
-DLANG_DE -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 = monitor_filters =
esp8266_exception_decoder esp8266_exception_decoder
@ -134,6 +123,7 @@ platform = espressif8266
board = esp8285 board = esp8285
board_build.ldscript = eagle.flash.1m64.ld board_build.ldscript = eagle.flash.1m64.ld
board_build.f_cpu = 80000000L board_build.f_cpu = 80000000L
lib_deps = ${env:esp8266.lib_deps}
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096 -DEMC_MIN_FREE_MEMORY=4096
-DENABLE_MQTT -DENABLE_MQTT
@ -147,6 +137,7 @@ platform = espressif8266
board = esp8285 board = esp8285
board_build.ldscript = eagle.flash.1m64.ld board_build.ldscript = eagle.flash.1m64.ld
board_build.f_cpu = 80000000L board_build.f_cpu = 80000000L
lib_deps = ${env:esp8266.lib_deps}
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
-DEMC_MIN_FREE_MEMORY=4096 -DEMC_MIN_FREE_MEMORY=4096
-DLANG_DE -DLANG_DE
@ -156,33 +147,29 @@ build_flags = ${env.build_flags}
monitor_filters = monitor_filters =
esp8266_exception_decoder esp8266_exception_decoder
[env:esp32-wroom32] [env:esp32-wroom32-minimal]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = lolin_d32 board = lolin_d32
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD -DUSE_HSPI_FOR_EPD
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
monitor_filters = monitor_filters =
esp32_exception_decoder esp32_exception_decoder
[env:esp32-wroom32-minimal] [env:esp32-wroom32]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = lolin_d32 board = lolin_d32
build_flags = ${env.build_flags} build_flags = ${env:esp32-wroom32-minimal.build_flags}
-DUSE_HSPI_FOR_EPD -DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DPLUGIN_ZEROEXPORT
monitor_filters = monitor_filters =
esp32_exception_decoder esp32_exception_decoder
[env:esp32-wroom32-de] [env:esp32-wroom32-de]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = lolin_d32 board = lolin_d32
build_flags = ${env.build_flags} build_flags = ${env:esp32-wroom32.build_flags}
-DUSE_HSPI_FOR_EPD
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DLANG_DE -DLANG_DE
monitor_filters = monitor_filters =
esp32_exception_decoder esp32_exception_decoder
@ -190,38 +177,24 @@ monitor_filters =
[env:esp32-wroom32-prometheus] [env:esp32-wroom32-prometheus]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = lolin_d32 board = lolin_d32
build_flags = ${env.build_flags} build_flags = ${env:esp32-wroom32.build_flags}
-DUSE_HSPI_FOR_EPD
-DENABLE_PROMETHEUS_EP -DENABLE_PROMETHEUS_EP
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
monitor_filters = monitor_filters =
esp32_exception_decoder esp32_exception_decoder
[env:esp32-wroom32-prometheus-de] [env:esp32-wroom32-prometheus-de]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = lolin_d32 board = lolin_d32
build_flags = ${env.build_flags} build_flags = ${env:esp32-wroom32-prometheus.build_flags}
-DUSE_HSPI_FOR_EPD
-DLANG_DE -DLANG_DE
-DENABLE_PROMETHEUS_EP
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
monitor_filters = monitor_filters =
esp32_exception_decoder esp32_exception_decoder
[env:esp32-wroom32-ethernet] [env:esp32-wroom32-ethernet]
platform = espressif32 platform = espressif32
board = lolin_d32 board = lolin_d32
build_flags = ${env.build_flags} build_flags = ${env:esp32-wroom32.build_flags}
-DETHERNET -DETHERNET
-DRELEASE
-DUSE_HSPI_FOR_EPD
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DDEF_ETH_CS_PIN=15 -DDEF_ETH_CS_PIN=15
-DDEF_ETH_SCK_PIN=14 -DDEF_ETH_SCK_PIN=14
-DDEF_ETH_MISO_PIN=12 -DDEF_ETH_MISO_PIN=12
@ -240,26 +213,8 @@ monitor_filters =
[env:esp32-wroom32-ethernet-de] [env:esp32-wroom32-ethernet-de]
platform = espressif32 platform = espressif32
board = lolin_d32 board = lolin_d32
build_flags = ${env.build_flags} build_flags = ${env:esp32-wroom32-ethernet.build_flags}
-DETHERNET
-DRELEASE
-DUSE_HSPI_FOR_EPD
-DLANG_DE -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 = monitor_filters =
esp32_exception_decoder esp32_exception_decoder
@ -289,23 +244,7 @@ monitor_filters =
[env:esp32-s2-mini-de] [env:esp32-s2-mini-de]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = lolin_s2_mini board = lolin_s2_mini
build_flags = ${env.build_flags} build_flags = ${env:esp32-s2-mini.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
-DLANG_DE -DLANG_DE
monitor_filters = monitor_filters =
esp32_exception_decoder esp32_exception_decoder
@ -318,6 +257,7 @@ build_flags = ${env.build_flags}
-DENABLE_MQTT -DENABLE_MQTT
-DPLUGIN_DISPLAY -DPLUGIN_DISPLAY
-DENABLE_HISTORY -DENABLE_HISTORY
-DPLUGIN_ZEROEXPORT
-DDEF_NRF_CS_PIN=5 -DDEF_NRF_CS_PIN=5
-DDEF_NRF_CE_PIN=0 -DDEF_NRF_CE_PIN=0
-DDEF_NRF_IRQ_PIN=1 -DDEF_NRF_IRQ_PIN=1
@ -335,36 +275,16 @@ monitor_filters =
[env:esp32-c3-mini-de] [env:esp32-c3-mini-de]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = lolin_c3_mini board = lolin_c3_mini
build_flags = ${env.build_flags} build_flags = ${env:esp32-c3-mini.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
-DLANG_DE -DLANG_DE
monitor_filters = monitor_filters =
esp32_exception_decoder esp32_exception_decoder
[env:opendtufusion] [env:opendtufusion-minimal]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = esp32-s3-devkitc-1 board = esp32-s3-devkitc-1
upload_protocol = esp-builtin upload_protocol = esp-builtin
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
-DENABLE_MQTT
-DPLUGIN_DISPLAY
-DENABLE_HISTORY
-DPLUGIN_ZEROEXPORT
-DSPI_HAL -DSPI_HAL
-DDEF_NRF_CS_PIN=37 -DDEF_NRF_CS_PIN=37
-DDEF_NRF_CE_PIN=38 -DDEF_NRF_CE_PIN=38
@ -385,76 +305,33 @@ build_flags = ${env.build_flags}
monitor_filters = monitor_filters =
esp32_exception_decoder, colorize esp32_exception_decoder, colorize
[env:opendtufusion-de] [env:opendtufusion]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = esp32-s3-devkitc-1 board = esp32-s3-devkitc-1
upload_protocol = esp-builtin upload_protocol = esp-builtin
build_flags = ${env.build_flags} build_flags = ${env:opendtufusion-minimal.build_flags}
-DLANG_DE
-DENABLE_MQTT -DENABLE_MQTT
-DPLUGIN_DISPLAY -DPLUGIN_DISPLAY
-DENABLE_HISTORY -DENABLE_HISTORY
-DPLUGIN_ZEROEXPORT -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 = monitor_filters =
esp32_exception_decoder, colorize esp32_exception_decoder, colorize
[env:opendtufusion-minimal] [env:opendtufusion-de]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = esp32-s3-devkitc-1 board = esp32-s3-devkitc-1
upload_protocol = esp-builtin upload_protocol = esp-builtin
build_flags = ${env.build_flags} build_flags = ${env:opendtufusion.build_flags}
-DSPI_HAL -DLANG_DE
-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 = monitor_filters =
esp32_exception_decoder, colorize esp32_exception_decoder, colorize
[env:opendtufusion-ethernet] [env:opendtufusion-ethernet]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = esp32-s3-devkitc-1 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 upload_protocol = esp-builtin
build_flags = ${env.build_flags} build_flags = ${env:opendtufusion-minimal.build_flags}
-DETHERNET -DETHERNET
-DSPI_HAL
-DENABLE_MQTT -DENABLE_MQTT
-DPLUGIN_DISPLAY -DPLUGIN_DISPLAY
-DENABLE_HISTORY -DENABLE_HISTORY
@ -465,68 +342,15 @@ build_flags = ${env.build_flags}
-DDEF_ETH_MOSI_PIN=40 -DDEF_ETH_MOSI_PIN=40
-DDEF_ETH_IRQ_PIN=44 -DDEF_ETH_IRQ_PIN=44
-DDEF_ETH_RST_PIN=43 -DDEF_ETH_RST_PIN=43
-DDEF_NRF_CS_PIN=37 -DDEF_ETH_ENABLED
-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 = monitor_filters =
esp32_exception_decoder, colorize esp32_exception_decoder, colorize
[env:opendtufusion-ethernet-de] [env:opendtufusion-ethernet-de]
platform = espressif32@6.5.0 platform = espressif32@6.5.0
board = esp32-s3-devkitc-1 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 upload_protocol = esp-builtin
build_flags = ${env.build_flags} build_flags = ${env:opendtufusion-ethernet.build_flags}
-DETHERNET
-DSPI_HAL
-DLANG_DE -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 = monitor_filters =
esp32_exception_decoder, colorize esp32_exception_decoder, colorize

18
src/publisher/pubMqtt.h

@ -16,10 +16,6 @@
#endif #endif
#include <array> #include <array>
#if defined(ETHERNET)
#include "../eth/ahoyeth.h"
#endif
#include "../utils/dbg.h" #include "../utils/dbg.h"
#include "../config/config.h" #include "../config/config.h"
#include <espMqttClient.h> #include <espMqttClient.h>
@ -56,7 +52,8 @@ class PubMqtt {
~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; mCfgMqtt = cfg_mqtt;
mDevName = devName; mDevName = devName;
mVersion = version; mVersion = version;
@ -254,13 +251,9 @@ class PubMqtt {
void onConnect(bool sessionPreset) { void onConnect(bool sessionPreset) {
DPRINTLN(DBG_INFO, F("MQTT connected")); DPRINTLN(DBG_INFO, F("MQTT connected"));
publish(subtopics[MQTT_VERSION], mVersion, true); publish(subtopics[MQTT_VERSION], mVersion, false);
publish(subtopics[MQTT_DEVICE], mDevName, true); publish(subtopics[MQTT_DEVICE], mDevName, false);
#if defined(ETHERNET) publish(subtopics[MQTT_IP_ADDR], mApp->getIp().c_str(), true);
publish(subtopics[MQTT_IP_ADDR], ETH.localIP().toString().c_str(), true);
#else
publish(subtopics[MQTT_IP_ADDR], WiFi.localIP().toString().c_str(), true);
#endif
tickerMinute(); tickerMinute();
publish(mLwtTopic.data(), mqttStr[MQTT_STR_LWT_CONN], true, false); publish(mLwtTopic.data(), mqttStr[MQTT_STR_LWT_CONN], true, false);
@ -612,6 +605,7 @@ class PubMqtt {
espMqttClient mClient; espMqttClient mClient;
cfgMqtt_t *mCfgMqtt = nullptr; cfgMqtt_t *mCfgMqtt = nullptr;
IApp *mApp;
#if defined(ESP8266) #if defined(ESP8266)
WiFiEventHandler mHWifiCon, mHWifiDiscon; WiFiEventHandler mHWifiCon, mHWifiDiscon;
#endif #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_MONTH_DAY, rec)),
static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_FW_BUILD_HOUR_MINUTE, rec)), static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_FW_BUILD_HOUR_MINUTE, rec)),
static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_BOOTLOADER_VER, rec))); static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_BOOTLOADER_VER, rec)));
retained = true;
} else if(InverterDevInform_Simple == mCmd) { } else if(InverterDevInform_Simple == mCmd) {
snprintf(mSubTopic.data(), mSubTopic.size(), "%s/hardware", mIv->config->name); 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}", 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_HW_VERSION, rec)),
static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_GRID_PROFILE_CODE, rec)), static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_GRID_PROFILE_CODE, rec)),
static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_GRID_PROFILE_VERSION, rec))); static_cast<int>(mIv->getChannelFieldValue(CH0, FLD_GRID_PROFILE_VERSION, rec)));
retained = true;
} else { } else {
snprintf(mSubTopic.data(), mSubTopic.size(), "%s/ch%d/%s", mIv->config->name, rec->assign[mPos].ch, fields[rec->assign[mPos].fieldId]); 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))); 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) { void getNetworks(void) {
if(!mScanRunning)
mApp->scanAvailNetworks();
JsonObject obj; JsonObject obj;
if(!mScanRunning) {
mApp->getAvailNetworks(obj);
return;
}
if(!mApp->getAvailNetworks(obj)) if(!mApp->getAvailNetworks(obj))
return; return;

49
src/web/RestApi.h

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

6
src/web/html/history.html

@ -136,7 +136,7 @@
return [ return [
mlNs("polyline", {stroke: "url(#gLine)", fill: "none", points: pts}), mlNs("polyline", {stroke: "url(#gLine)", fill: "none", points: pts}),
mlNs("polyline", {stroke: "none", fill: "url(#gFill)", points: pts2}), 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") mlNs("text", {x: i*.8, y: 25}, "{#LAST_VALUE}: " + String(lastVal) + "W")
] ]
} }
@ -150,11 +150,11 @@
parseRssi(obj.generic) parseRssi(obj.generic)
window.setInterval("getAjax('/api/powerHistory', parsePowerHistory)", obj.refresh * 1000) window.setInterval("getAjax('/api/powerHistory', parsePowerHistory)", obj.refresh * 1000)
setTimeout(() => { setTimeout(() => {
window.setInterval("getAjax('/api/powerHistoryDay', parsePowerHistoryDay)", refresh * 1000) window.setInterval("getAjax('/api/powerHistoryDay', parsePowerHistoryDay)", obj.refresh * 1000)
}, 200) }, 200)
/*IF_ENABLE_HISTORY_YIELD_PER_DAY*/ /*IF_ENABLE_HISTORY_YIELD_PER_DAY*/
setTimeout(() => { setTimeout(() => {
window.setInterval("getAjax('/api/yieldDayHistory', parseYieldDayHistory)", refresh * 1000) window.setInterval("getAjax('/api/yieldDayHistory', parseYieldDayHistory)", obj.refresh * 1000)
}, 400) }, 400)
/*ENDIF_ENABLE_HISTORY_YIELD_PER_DAY*/ /*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-3 my-2">{#AP_PWD}</div>
<div class="col-12 col-sm-9"><input type="text" name="ap_pwd" minlength="8" /></div> <div class="col-12 col-sm-9"><input type="text" name="ap_pwd" minlength="8" /></div>
</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="row mb-2 mb-sm-3">
<div class="col-12 col-sm-3 my-2">SSID</div> <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>
<div class="row mb-2 mb-sm-3"> <div class="row mb-2 mb-sm-3">
<div class="col-12 col-sm-3">{#SSID_HIDDEN}</div> <div class="col-12 col-sm-3">{#SSID_HIDDEN}</div>
@ -247,7 +234,7 @@
<p class="des">{#MQTT_NOTE}</p> <p class="des">{#MQTT_NOTE}</p>
<div class="row mb-3"> <div class="row mb-3">
<div class="col-12 col-sm-3 my-2">{#INTERVAL}</div> <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>
<div class="row mb-3"> <div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Discovery Config (homeassistant)</div> <div class="col-12 col-sm-3 my-2">Discovery Config (homeassistant)</div>
@ -272,6 +259,10 @@
<p class="des">{#RADIO} (CMT2300A)</p> <p class="des">{#RADIO} (CMT2300A)</p>
<div id="cmt"></div> <div id="cmt"></div>
<!--ENDIF_ESP32--> <!--ENDIF_ESP32-->
<!--IF_ETHERNET-->
<p class="des">Ethernet</p>
<div id="eth"></div>
<!--ENDIF_ETHERNET-->
</fieldset> </fieldset>
</div> </div>
<!--IF_PLUGIN_DISPLAY--> <!--IF_PLUGIN_DISPLAY-->
@ -625,12 +616,6 @@
setTimeout(function() {getAjax('/api/index', apiCbNtp2)}, 2000) 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() { function syncTime() {
var obj = {cmd: "sync_ntp", token: "*"} var obj = {cmd: "sync_ntp", token: "*"}
getAjax("/api/setup", apiCbNtp, "POST", JSON.stringify(obj)) 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"); var e = document.getElementById("pinout");
/*IF_ESP32*/ /*IF_ESP32*/
var pinList = esp32pins; var pinList = esp32pins;
@ -1009,7 +994,7 @@
) )
} }
function parseNrfRadio(obj, objPin, type, system) { function parseNrfRadio(obj, objPin) {
var e = document.getElementById("rf24"); var e = document.getElementById("rf24");
var en = inp("nrfEnable", null, null, ["cb"], "nrfEnable", "checkbox"); var en = inp("nrfEnable", null, null, ["cb"], "nrfEnable", "checkbox");
en.checked = obj["en"]; en.checked = obj["en"];
@ -1036,11 +1021,11 @@
]) ])
); );
if ("ESP8266" == type) { /*IF_ESP32*/
pins = [['cs', 'pinCs'], ['ce', 'pinCe'], ['irq', 'pinIrq']]; var pins = [['cs', 'pinCs'], ['ce', 'pinCe'], ['irq', 'pinIrq'], ['sclk', 'pinSclk'], ['mosi', 'pinMosi'], ['miso', 'pinMiso']];
} else { /*ELSE*/
pins = [['cs', 'pinCs'], ['ce', 'pinCe'], ['irq', 'pinIrq'], ['sclk', 'pinSclk'], ['mosi', 'pinMosi'], ['miso', 'pinMiso']]; var pins = [['cs', 'pinCs'], ['ce', 'pinCe'], ['irq', 'pinIrq']];
} /*ENDIF_ESP32*/
for(p of pins) { for(p of pins) {
e.append( e.append(
ml("div", {class: "row mb-3"}, [ ml("div", {class: "row mb-3"}, [
@ -1054,7 +1039,7 @@
} }
/*IF_ESP32*/ /*IF_ESP32*/
function parseCmtRadio(obj, type, system) { function parseCmtRadio(obj) {
var e = document.getElementById("cmt"); var e = document.getElementById("cmt");
var en = inp("cmtEnable", null, null, ["cb"], "cmtEnable", "checkbox"); var en = inp("cmtEnable", null, null, ["cb"], "cmtEnable", "checkbox");
var pinList = esp32pins; var pinList = esp32pins;
@ -1069,7 +1054,6 @@
/*ENDIF_ESP32-C3*/ /*ENDIF_ESP32-C3*/
en.checked = obj["en"]; en.checked = obj["en"];
e.replaceChildren ( e.replaceChildren (
ml("div", {class: "row mb-3"}, [ ml("div", {class: "row mb-3"}, [
ml("div", {class: "col-8 col-sm-3 my-2"}, "{#CMT_ENABLE}"), ml("div", {class: "col-8 col-sm-3 my-2"}, "{#CMT_ENABLE}"),
@ -1096,6 +1080,42 @@
} }
/*ENDIF_ESP32*/ /*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) { function parseSerial(obj) {
var e = document.getElementById("serialCb") 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}"]] 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); modal("{#ZE_GROUP_EDIT_MODAL}: " + obj.id, html);
// ser.dispatchEvent(new Event('change')); // 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"); var e = document.getElementById("pm_type");
selDelAllOpt(e); selDelAllOpt(e);
// TODO: bersetzen? // TODO: ?bersetzen?
e.appendChild(opt("0", "---")); e.appendChild(opt("0", "---"));
e.appendChild(opt("1", "Shelly")); e.appendChild(opt("1", "Shelly"));
e.appendChild(opt("2", "Tasmota")); e.appendChild(opt("2", "Tasmota"));
@ -1482,7 +1502,7 @@
for (var inv = 0; inv < maxInv; inv++) { for (var inv = 0; inv < maxInv; inv++) {
var e = document.getElementById("invTarget"+inv); var e = document.getElementById("invTarget"+inv);
selDelAllOpt(e); selDelAllOpt(e);
// TODO: bersetzen? // TODO: ?bersetzen?
e.appendChild(opt("-1", "---")); e.appendChild(opt("-1", "---"));
e.appendChild(opt("0", "Sum")); e.appendChild(opt("0", "Sum"));
e.appendChild(opt("1", "L1")); e.appendChild(opt("1", "L1"));
@ -1719,13 +1739,15 @@
parseMqtt(root["mqtt"]); parseMqtt(root["mqtt"]);
parseNtp(root["ntp"]); parseNtp(root["ntp"]);
parseSun(root["sun"]); parseSun(root["sun"]);
parsePinout(root["pinout"], root["system"]["esp_type"], root["system"]); parsePinout(root.pinout);
parseNrfRadio(root["radioNrf"], root["pinout"], root["system"]["esp_type"], root["system"]); parseNrfRadio(root["radioNrf"], root["pinout"]);
/*IF_ESP32*/ /*IF_ESP32*/
parseCmtRadio(root["radioCmt"], root["system"]["esp_type"], root["system"]); parseCmtRadio(root.radioCmt);
/*ENDIF_ESP32*/ /*ENDIF_ESP32*/
/*IF_ETHERNET*/
parseEth(root.eth)
/*ENDIF_ETHERNET*/
parseSerial(root["serial"]); parseSerial(root["serial"]);
/*IF_PLUGIN_DISPLAY*/ /*IF_PLUGIN_DISPLAY*/
parseDisplay(root["display"], root["system"]["esp_type"], root["system"]); parseDisplay(root["display"], root["system"]["esp_type"], root["system"]);
@ -1748,13 +1770,6 @@
s.appendChild(opt("-1", "{#NO_NETWORK_FOUND}")); 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); getAjax("/api/setup", parse);
</script> </script>
</body> </body>

224
src/web/html/wizard.html

@ -15,6 +15,165 @@
var found = false; var found = false;
var c = document.getElementById("con"); 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) { function sect(e1, e2) {
return ml("div", {class: "row"}, [ return ml("div", {class: "row"}, [
ml("div", {class: "col-12"}, ml("p", {}, e1)), 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", {}, [ return ml("div", {}, [
ml("div", {class: "row my-5"}, ml("div", {class: "col"}, ml("span", {class: "fs-1"}, "{#WELCOME}"))), 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}"))), 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_MANUAL}", ml("input", {id: "man", type: "text"})),
sect("{#WIFI_PASSWORD}", ml("input", {id: "pwd", type: "password"})), 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 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() { function checkWifi() {
c.replaceChildren( c.replaceChildren(
@ -40,9 +229,9 @@
ml("div", {class: "row"}, ml("div", {class: "col"}, ml("span", {class: "fs-5"}, "{#TEST_CONNECTION}"))), ml("div", {class: "row"}, ml("div", {class: "col"}, ml("span", {class: "fs-5"}, "{#TEST_CONNECTION}"))),
sect("{#TRY_TO_CONNECT}", ml("span", {id: "state"}, "{#CONNECTING}")), 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 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() { 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() { function saveWifi() {
var ssid = document.getElementById("net").value; var ssid = document.getElementById("net").value;
if(-1 == ssid) if(-1 == ssid)
ssid = document.getElementById("man").value; 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})); 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) { function nets(obj) {
if(!obj.success)
return;
var e = document.getElementById("net"); var e = document.getElementById("net");
if(obj.networks.length > 0) { if(obj.networks.length > 0) {
var a = [] var a = []
@ -75,13 +283,13 @@
} }
e.replaceChildren(...a) 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(step1())
c.append(wifi()) getAjax('/api/setup/networks', nets)
v = setInterval(() => {getAjax('/api/setup/networks', nets)}, 1000)
/*ENDIF_ETHERNET*/
v = setInterval(() => {getAjax('/api/setup/networks', nets)}, 2500);
</script> </script>
</body> </body>
</html> </html>

70
src/web/lang.json

@ -81,12 +81,17 @@
{ {
"token": "BTN_NEXT", "token": "BTN_NEXT",
"en": "next >>", "en": "next >>",
"de": "prüfen >>" "de": "pr&uuml;fen >>"
},
{
"token": "BTN_REBOOT",
"en": "reboot >>",
"de": "Ahoy neustarten >>"
}, },
{ {
"token": "TEST_CONNECTION", "token": "TEST_CONNECTION",
"en": "Test Connection", "en": "Test Connection",
"de": "Verbindung wird überprüft" "de": "Verbindung wird &uuml;berpr&uuml;ft"
}, },
{ {
"token": "TRY_TO_CONNECT", "token": "TRY_TO_CONNECT",
@ -112,6 +117,36 @@
"token": "NUM_NETWORKS_FOUND", "token": "NUM_NETWORKS_FOUND",
"en": "Network(s) found", "en": "Network(s) found",
"de": "Netzwerk(e) gefunden" "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" "de": "Netzwerke suchen"
}, },
{ {
"token": "BTN_SCAN", "token": "SCAN_WIFI",
"en": "scan", "en": "scan for WiFi networks",
"de": "Suche starten" "de": "nach WiFi Netzwerken suchen"
},
{
"token": "AVAIL_NETWORKS",
"en": "Avail Networks",
"de": "Verf&uuml;gbare Netzwerke"
},
{
"token": "NETWORK_NOT_SCANNED",
"en": "not scanned",
"de": "nicht gesucht"
}, },
{ {
"token": "SSID_HIDDEN", "token": "SSID_HIDDEN",
@ -596,7 +621,7 @@
{ {
"token": "BTN_INV_ADD", "token": "BTN_INV_ADD",
"en": "add Inverter", "en": "add Inverter",
"de": "Wechselrichter hinzufuegen" "de": "Wechselrichter hinzuf\u00FCgen"
}, },
{ {
"token": "INV_INPUT", "token": "INV_INPUT",
@ -621,7 +646,7 @@
{ {
"token": "TAB_INPUTS", "token": "TAB_INPUTS",
"en": "Inputs", "en": "Inputs",
"de": "Eingaenge" "de": "Eing&auml;nge"
}, },
{ {
"token": "TAB_RADIO", "token": "TAB_RADIO",
@ -723,6 +748,11 @@
"en": "CMT2300A radio enable", "en": "CMT2300A radio enable",
"de": "CMT2300A Funkmodul aktivieren" "de": "CMT2300A Funkmodul aktivieren"
}, },
{
"token": "ETH_ENABLE",
"en": "Ethernet enable",
"de": "Ethernet aktivieren"
},
{ {
"token": "DISP_NONE", "token": "DISP_NONE",
"en": "None", "en": "None",
@ -1769,9 +1799,9 @@
"de": "Gesamtertrag pro Tag" "de": "Gesamtertrag pro Tag"
}, },
{ {
"token": "MAX_DAY", "token": "MAXIMUM",
"en": "Maximum day", "en": "Maximum",
"de": "Tagesmaximum" "de": "Maximum"
}, },
{ {
"token": "LAST_VALUE", "token": "LAST_VALUE",

61
src/web/web.h

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