mirror of
https://github.com/lumapu/ahoy.git
synced 2025-04-28 09:46:26 +02:00
Merge branch 'development03'
This commit is contained in:
commit
f36d2ed380
38 changed files with 880 additions and 432 deletions
|
@ -1,26 +0,0 @@
|
|||
diff --git a/src/AsyncWebSocket.cpp b/src/AsyncWebSocket.cpp
|
||||
index 6e88da9..09359c3 100644
|
||||
--- a/src/AsyncWebSocket.cpp
|
||||
+++ b/src/AsyncWebSocket.cpp
|
||||
@@ -827,7 +827,7 @@ void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer)
|
||||
|
||||
IPAddress AsyncWebSocketClient::remoteIP() {
|
||||
if(!_client) {
|
||||
- return IPAddress((uint32_t)0);
|
||||
+ return IPAddress();
|
||||
}
|
||||
return _client->remoteIP();
|
||||
}
|
||||
diff --git a/src/WebResponses.cpp b/src/WebResponses.cpp
|
||||
index a22e991..babef18 100644
|
||||
--- a/src/WebResponses.cpp
|
||||
+++ b/src/WebResponses.cpp
|
||||
@@ -317,7 +317,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
- outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen;
|
||||
+ outLen = sprintf((char*)buf+headLen, "%04x", readLen) + headLen;
|
||||
while(outLen < headLen + 4) buf[outLen++] = ' ';
|
||||
buf[outLen++] = '\r';
|
||||
buf[outLen++] = '\n';
|
123
patches/espMqttClientSemaphore.patch
Normal file
123
patches/espMqttClientSemaphore.patch
Normal file
|
@ -0,0 +1,123 @@
|
|||
diff --git a/src/Helpers.h b/src/Helpers.h
|
||||
index 05ab136..50b4c2f 100644
|
||||
--- a/src/Helpers.h
|
||||
+++ b/src/Helpers.h
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright (c) 2022 Bert Melis. All rights reserved.
|
||||
|
||||
-This work is licensed under the terms of the MIT license.
|
||||
+This work is licensed under the terms of the MIT license.
|
||||
For a copy, see <https://opensource.org/licenses/MIT> or
|
||||
the LICENSE file.
|
||||
*/
|
||||
@@ -13,6 +13,7 @@ the LICENSE file.
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_task_wdt.h"
|
||||
+ #define EMC_SEMAPHORE_TAKE_CHECK() if(pdTRUE == xSemaphoreTake(_xSemaphore, portMAX_DELAY))
|
||||
#define EMC_SEMAPHORE_TAKE() xSemaphoreTake(_xSemaphore, portMAX_DELAY)
|
||||
#define EMC_SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore)
|
||||
#define EMC_GET_FREE_MEMORY() std::max(ESP.getMaxAllocHeap(), ESP.getMaxAllocPsram())
|
||||
@@ -25,9 +26,11 @@ the LICENSE file.
|
||||
// _xSemaphore defined as std::atomic<bool>
|
||||
#define EMC_SEMAPHORE_TAKE() while (_xSemaphore) { /*ESP.wdtFeed();*/ } _xSemaphore = true
|
||||
#define EMC_SEMAPHORE_GIVE() _xSemaphore = false
|
||||
+ #define EMC_SEMAPHORE_TAKE_CHECK() EMC_SEMAPHORE_TAKE
|
||||
#else
|
||||
#define EMC_SEMAPHORE_TAKE()
|
||||
#define EMC_SEMAPHORE_GIVE()
|
||||
+ #define EMC_SEMAPHORE_TAKE_CHECK()
|
||||
#endif
|
||||
#define EMC_GET_FREE_MEMORY() ESP.getMaxFreeBlockSize()
|
||||
// no need to yield for ESP8266, the Arduino framework does this internally
|
||||
diff --git a/src/MqttClient.cpp b/src/MqttClient.cpp
|
||||
index dc21f74..d524e50 100644
|
||||
--- a/src/MqttClient.cpp
|
||||
+++ b/src/MqttClient.cpp
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Copyright (c) 2022 Bert Melis. All rights reserved.
|
||||
|
||||
-This work is licensed under the terms of the MIT license.
|
||||
+This work is licensed under the terms of the MIT license.
|
||||
For a copy, see <https://opensource.org/licenses/MIT> or
|
||||
the LICENSE file.
|
||||
*/
|
||||
@@ -148,16 +148,20 @@ uint16_t MqttClient::publish(const char* topic, uint8_t qos, bool retain, const
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
- EMC_SEMAPHORE_TAKE();
|
||||
- uint16_t packetId = (qos > 0) ? _getNextPacketId() : 1;
|
||||
- if (!_addPacket(packetId, topic, payload, length, qos, retain)) {
|
||||
- emc_log_e("Could not create PUBLISH packet");
|
||||
+ uint16_t packetId = 0;
|
||||
+ EMC_SEMAPHORE_TAKE_CHECK() {
|
||||
+ packetId = (qos > 0) ? _getNextPacketId() : 1;
|
||||
+ if (!_addPacket(packetId, topic, payload, length, qos, retain)) {
|
||||
+ emc_log_e("Could not create PUBLISH packet");
|
||||
+ EMC_SEMAPHORE_GIVE();
|
||||
+ _onError(packetId, Error::OUT_OF_MEMORY);
|
||||
+ EMC_SEMAPHORE_TAKE_CHECK() {
|
||||
+ packetId = 0;
|
||||
+ }
|
||||
+ }
|
||||
EMC_SEMAPHORE_GIVE();
|
||||
- _onError(packetId, Error::OUT_OF_MEMORY);
|
||||
- EMC_SEMAPHORE_TAKE();
|
||||
- packetId = 0;
|
||||
+ yield();
|
||||
}
|
||||
- EMC_SEMAPHORE_GIVE();
|
||||
return packetId;
|
||||
}
|
||||
|
||||
@@ -174,16 +178,20 @@ uint16_t MqttClient::publish(const char* topic, uint8_t qos, bool retain, espMqt
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
- EMC_SEMAPHORE_TAKE();
|
||||
- uint16_t packetId = (qos > 0) ? _getNextPacketId() : 1;
|
||||
- if (!_addPacket(packetId, topic, callback, length, qos, retain)) {
|
||||
- emc_log_e("Could not create PUBLISH packet");
|
||||
+ uint16_t packetId = 0;
|
||||
+ EMC_SEMAPHORE_TAKE_CHECK() {
|
||||
+ packetId = (qos > 0) ? _getNextPacketId() : 1;
|
||||
+ if (!_addPacket(packetId, topic, callback, length, qos, retain)) {
|
||||
+ emc_log_e("Could not create PUBLISH packet");
|
||||
+ EMC_SEMAPHORE_GIVE();
|
||||
+ _onError(packetId, Error::OUT_OF_MEMORY);
|
||||
+ EMC_SEMAPHORE_TAKE_CHECK() {
|
||||
+ packetId = 0;
|
||||
+ }
|
||||
+ }
|
||||
EMC_SEMAPHORE_GIVE();
|
||||
- _onError(packetId, Error::OUT_OF_MEMORY);
|
||||
- EMC_SEMAPHORE_TAKE();
|
||||
- packetId = 0;
|
||||
+ yield();
|
||||
}
|
||||
- EMC_SEMAPHORE_GIVE();
|
||||
return packetId;
|
||||
}
|
||||
|
||||
@@ -237,11 +245,13 @@ void MqttClient::loop() {
|
||||
case State::connectingMqtt:
|
||||
#if EMC_WAIT_FOR_CONNACK
|
||||
if (_transport->connected()) {
|
||||
- EMC_SEMAPHORE_TAKE();
|
||||
- _sendPacket();
|
||||
- _checkIncoming();
|
||||
- _checkPing();
|
||||
- EMC_SEMAPHORE_GIVE();
|
||||
+ EMC_SEMAPHORE_TAKE_CHECK() {
|
||||
+ _sendPacket();
|
||||
+ _checkIncoming();
|
||||
+ _checkPing();
|
||||
+ EMC_SEMAPHORE_GIVE();
|
||||
+ yield();
|
||||
+ }
|
||||
} else {
|
||||
_setState(State::disconnectingTcp1);
|
||||
_disconnectReason = DisconnectReason::TCP_DISCONNECTED;
|
|
@ -25,9 +25,9 @@ def applyPatch(libName, patchFile):
|
|||
os.chdir(start)
|
||||
|
||||
|
||||
# list of patches to apply (relative to /src)
|
||||
applyPatch("ESPAsyncWebServer-esphome", "../patches/AsyncWeb_Prometheus.patch")
|
||||
applyPatch("espMqttClient", "../patches/espMqttClientSemaphore.patch")
|
||||
|
||||
# list of patches to apply (relative to /src)
|
||||
if (env['PIOENV'][:5] == "esp32") or (env['PIOENV'][:13] == "opendtufusion"):
|
||||
applyPatch("GxEPD2", "../patches/GxEPD2_HAL.patch")
|
||||
|
||||
|
|
|
@ -2,16 +2,14 @@
|
|||
#
|
||||
# Copyright (C) 2022 Thomas Basler and others
|
||||
#
|
||||
import pkg_resources
|
||||
|
||||
Import("env")
|
||||
|
||||
required_pkgs = {'dulwich'}
|
||||
installed_pkgs = {pkg.key for pkg in pkg_resources.working_set}
|
||||
missing_pkgs = required_pkgs - installed_pkgs
|
||||
|
||||
if missing_pkgs:
|
||||
try:
|
||||
from dulwich import porcelain
|
||||
except ModuleNotFoundError:
|
||||
env.Execute('"$PYTHONEXE" -m pip install dulwich')
|
||||
from dulwich import porcelain
|
||||
|
||||
from dulwich import porcelain
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Changelog v0.8.154
|
||||
Changelog v0.8.155
|
||||
|
||||
* fix display IP in ePaper display (ETH or WiFi, static or DHCP)
|
||||
* fix German translation
|
||||
|
@ -16,4 +16,4 @@ Changelog v0.8.154
|
|||
* increased maximum number of alarms to 50 for ESP32
|
||||
* updated libraries
|
||||
|
||||
full version log: [Development Log](https://github.com/lumapu/ahoy/blob/development03/src/CHANGES.md)
|
||||
full version log: [Development Log](https://github.com/lumapu/ahoy/blob/development03/src/CHANGES.md)
|
||||
|
|
151
src/app.cpp
151
src/app.cpp
|
@ -13,7 +13,11 @@
|
|||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
app::app() : ah::Scheduler {} {
|
||||
app::app()
|
||||
: ah::Scheduler {}
|
||||
, mSunrise {0}
|
||||
, mSunset {0}
|
||||
{
|
||||
memset(mVersion, 0, sizeof(char) * 12);
|
||||
memset(mVersionModules, 0, sizeof(char) * 12);
|
||||
}
|
||||
|
@ -51,7 +55,7 @@ void app::setup() {
|
|||
#else
|
||||
mNetwork = static_cast<AhoyNetwork*>(new AhoyWifi());
|
||||
#endif
|
||||
mNetwork->setup(mConfig, &mTimestamp, [this](bool gotIp) { this->onNetwork(gotIp); }, [this](bool gotTime) { this->onNtpUpdate(gotTime); });
|
||||
mNetwork->setup(mConfig, [this](bool gotIp) { this->onNetwork(gotIp); }, [this](uint32_t gotTime) { this->onNtpUpdate(gotTime); });
|
||||
mNetwork->begin();
|
||||
|
||||
esp_task_wdt_reset();
|
||||
|
@ -78,7 +82,7 @@ void app::setup() {
|
|||
mMqttEnabled = (mConfig->mqtt.broker[0] > 0);
|
||||
if (mMqttEnabled) {
|
||||
mMqtt.setup(this, &mConfig->mqtt, mConfig->sys.deviceName, mVersion, &mSys, &mTimestamp, &mUptime);
|
||||
mMqtt.setSubscriptionCb(std::bind(&app::mqttSubRxCb, this, std::placeholders::_1));
|
||||
mMqtt.setSubscriptionCb([this](JsonObject obj) { mqttSubRxCb(obj); });
|
||||
mCommunication.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); });
|
||||
}
|
||||
#endif
|
||||
|
@ -148,101 +152,90 @@ void app::loop(void) {
|
|||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::onNetwork(bool gotIp) {
|
||||
mNetworkConnected = gotIp;
|
||||
if(gotIp) {
|
||||
ah::Scheduler::resetTicker();
|
||||
regularTickers(); //reinstall regular tickers
|
||||
every(std::bind(&app::tickSend, this), mConfig->inst.sendInterval, "tSend");
|
||||
mTickerInstallOnce = true;
|
||||
mSunrise = 0; // needs to be set to 0, to reinstall sunrise and ivComm tickers!
|
||||
once(std::bind(&app::tickNtpUpdate, this), 2, "ntp2");
|
||||
void app::onNetwork(bool connected) {
|
||||
mNetworkConnected = connected;
|
||||
#if defined(ENABLE_MQTT)
|
||||
if (mMqttEnabled) {
|
||||
resetTickerByName("mqttS");
|
||||
resetTickerByName("mqttM");
|
||||
}
|
||||
#endif
|
||||
|
||||
if(connected) {
|
||||
mNetwork->updateNtpTime();
|
||||
|
||||
resetTickerByName("tSend");
|
||||
every([this]() { tickSend(); }, mConfig->inst.sendInterval, "tSend");
|
||||
|
||||
#if defined(ENABLE_MQTT)
|
||||
if (mMqttEnabled) {
|
||||
everySec([this]() { mMqtt.tickerSecond(); }, "mqttS");
|
||||
everyMin([this]() { mMqtt.tickerMinute(); }, "mqttM");
|
||||
}
|
||||
#endif /*ENABLE_MQTT*/
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::regularTickers(void) {
|
||||
DPRINTLN(DBG_DEBUG, F("regularTickers"));
|
||||
everySec(std::bind(&WebType::tickSecond, &mWeb), "webSc");
|
||||
everySec([this]() { mWeb.tickSecond(); }, "webSc");
|
||||
everySec([this]() { mProtection->tickSecond(); }, "prot");
|
||||
everySec([this]() {mNetwork->tickNetworkLoop(); }, "net");
|
||||
everySec([this]() { mNetwork->tickNetworkLoop(); }, "net");
|
||||
|
||||
if(mConfig->inst.startWithoutTime && !mNetworkConnected)
|
||||
every(std::bind(&app::tickSend, this), mConfig->inst.sendInterval, "tSend");
|
||||
if(mConfig->inst.startWithoutTime)
|
||||
every([this]() { tickSend(); }, mConfig->inst.sendInterval, "tSend");
|
||||
|
||||
|
||||
every([this]() { mNetwork->updateNtpTime(); }, mConfig->ntp.interval * 60, "ntp");
|
||||
|
||||
if (mConfig->inst.rstValsNotAvail)
|
||||
everyMin([this]() { tickMinute(); }, "tMin");
|
||||
|
||||
// Plugins
|
||||
#if defined(PLUGIN_DISPLAY)
|
||||
if (DISP_TYPE_T0_NONE != mConfig->plugin.display.type)
|
||||
everySec(std::bind(&DisplayType::tickerSecond, &mDisplay), "disp");
|
||||
everySec([this]() { mDisplay.tickerSecond(); }, "disp");
|
||||
#endif
|
||||
every(std::bind(&PubSerialType::tick, &mPubSerial), 5, "uart");
|
||||
every([this]() { mPubSerial.tick(); }, 5, "uart");
|
||||
//everySec([this]() { mImprov.tickSerial(); }, "impro");
|
||||
|
||||
#if defined(ENABLE_HISTORY)
|
||||
everySec(std::bind(&HistoryType::tickerSecond, &mHistory), "hist");
|
||||
everySec([this]() { mHistory.tickerSecond(); }, "hist");
|
||||
#endif /*ENABLE_HISTORY*/
|
||||
|
||||
#if defined(ENABLE_SIMULATOR)
|
||||
every(std::bind(&SimulatorType::tick, &mSimulator), 5, "sim");
|
||||
every([this]() {mSimulator->tick(); }, 5, "sim");
|
||||
#endif /*ENABLE_SIMULATOR*/
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::onNtpUpdate(bool gotTime) {
|
||||
mNtpReceived = true;
|
||||
if ((0 == mSunrise) && (0.0 != mConfig->sun.lat) && (0.0 != mConfig->sun.lon)) {
|
||||
mCalculatedTimezoneOffset = (int8_t)((mConfig->sun.lon >= 0 ? mConfig->sun.lon + 7.5 : mConfig->sun.lon - 7.5) / 15) * 3600;
|
||||
tickCalcSunrise();
|
||||
}
|
||||
void app::onNtpUpdate(uint32_t utcTimestamp) {
|
||||
if(0 == utcTimestamp) {
|
||||
// try again in 5s
|
||||
once([this]() { mNetwork->updateNtpTime(); }, 5, "ntp");
|
||||
} else {
|
||||
mTimestamp = utcTimestamp;
|
||||
DPRINTLN(DBG_INFO, "[NTP]: " + ah::getDateTimeStr(mTimestamp) + " UTC");
|
||||
|
||||
if (mTickerInstallOnce) {
|
||||
mTickerInstallOnce = false;
|
||||
#if defined(ENABLE_MQTT)
|
||||
if (mMqttEnabled) {
|
||||
mMqtt.tickerSecond();
|
||||
everySec(std::bind(&PubMqttType::tickerSecond, &mMqtt), "mqttS");
|
||||
everyMin(std::bind(&PubMqttType::tickerMinute, &mMqtt), "mqttM");
|
||||
uint32_t localTime = gTimezone.toLocal(mTimestamp);
|
||||
uint32_t midTrig = gTimezone.toUTC(localTime - (localTime % 86400) + 86400); // next midnight local time
|
||||
resetTickerByName("midNi");
|
||||
onceAt([this]() { tickMidnight(); }, midTrig, "midNi");
|
||||
|
||||
if (mConfig->sys.schedReboot) {
|
||||
uint32_t rebootTrig = gTimezone.toUTC(localTime - (localTime % 86400) + 86410); // reboot 10 secs after midnght
|
||||
resetTickerByName("midRe");
|
||||
onceAt([this]() { tickReboot(); }, rebootTrig, "midRe");
|
||||
}
|
||||
#endif /*ENABLE_MQTT*/
|
||||
|
||||
if (mConfig->inst.rstValsNotAvail)
|
||||
everyMin(std::bind(&app::tickMinute, this), "tMin");
|
||||
|
||||
if(mNtpReceived) {
|
||||
uint32_t localTime = gTimezone.toLocal(mTimestamp);
|
||||
uint32_t midTrig = gTimezone.toUTC(localTime - (localTime % 86400) + 86400); // next midnight local time
|
||||
onceAt(std::bind(&app::tickMidnight, this), midTrig, "midNi");
|
||||
|
||||
if (mConfig->sys.schedReboot) {
|
||||
uint32_t rebootTrig = gTimezone.toUTC(localTime - (localTime % 86400) + 86410); // reboot 10 secs after midnght
|
||||
onceAt(std::bind(&app::tickReboot, this), rebootTrig, "midRe");
|
||||
}
|
||||
if ((0 == mSunrise) && (0.0 != mConfig->sun.lat) && (0.0 != mConfig->sun.lon)) {
|
||||
mCalculatedTimezoneOffset = (int8_t)((mConfig->sun.lon >= 0 ? mConfig->sun.lon + 7.5 : mConfig->sun.lon - 7.5) / 15) * 3600;
|
||||
tickCalcSunrise();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::updateNtp(void) {
|
||||
if(mNtpReceived)
|
||||
onNtpUpdate(true);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::tickNtpUpdate(void) {
|
||||
uint32_t nxtTrig = 5; // default: check again in 5 sec
|
||||
|
||||
if (!mNtpReceived)
|
||||
mNetwork->updateNtpTime();
|
||||
else {
|
||||
nxtTrig = mConfig->ntp.interval * 60; // check again in configured interval
|
||||
mNtpReceived = false;
|
||||
}
|
||||
|
||||
updateNtp();
|
||||
|
||||
once(std::bind(&app::tickNtpUpdate, this), nxtTrig, "ntp");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::tickCalcSunrise(void) {
|
||||
if (mSunrise == 0) // on boot/reboot calc sun values for current time
|
||||
|
@ -254,11 +247,11 @@ void app::tickCalcSunrise(void) {
|
|||
tickIVCommunication();
|
||||
|
||||
uint32_t nxtTrig = mSunset + mConfig->sun.offsetSecEvening + 60; // set next trigger to communication stop, +60 for safety that it is certain past communication stop
|
||||
onceAt(std::bind(&app::tickCalcSunrise, this), nxtTrig, "Sunri");
|
||||
onceAt([this]() { tickCalcSunrise(); }, nxtTrig, "Sunri");
|
||||
if (mMqttEnabled) {
|
||||
tickSun();
|
||||
nxtTrig = mSunrise + mConfig->sun.offsetSecMorning + 1; // one second safety to trigger correctly
|
||||
onceAt(std::bind(&app::tickSunrise, this), nxtTrig, "mqSr"); // trigger on sunrise to update 'dis_night_comm'
|
||||
onceAt([this]() { tickSunrise(); }, nxtTrig, "mqSr"); // trigger on sunrise to update 'dis_night_comm'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,10 +289,10 @@ void app::tickIVCommunication(void) {
|
|||
}
|
||||
|
||||
if(restartTick) // at least one inverter
|
||||
onceAt(std::bind(&app::tickIVCommunication, this), nxtTrig, "ivCom");
|
||||
onceAt([this]() { tickIVCommunication(); }, nxtTrig, "ivCom");
|
||||
|
||||
if (zeroValues) // at least one inverter
|
||||
once(std::bind(&app::tickZeroValues, this), mConfig->inst.sendInterval, "tZero");
|
||||
once([this]() { tickZeroValues(); }, mConfig->inst.sendInterval, "tZero");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -307,7 +300,7 @@ void app::tickSun(void) {
|
|||
// only used and enabled by MQTT (see setup())
|
||||
#if defined(ENABLE_MQTT)
|
||||
if (!mMqtt.tickerSun(mSunrise, mSunset, mConfig->sun.offsetSecMorning, mConfig->sun.offsetSecEvening))
|
||||
once(std::bind(&app::tickSun, this), 1, "mqSun"); // MQTT not connected, retry
|
||||
once([this]() { tickSun(); }, 1, "mqSun"); // MQTT not connected, retry
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -316,7 +309,7 @@ void app::tickSunrise(void) {
|
|||
// only used and enabled by MQTT (see setup())
|
||||
#if defined(ENABLE_MQTT)
|
||||
if (!mMqtt.tickerSun(mSunrise, mSunset, mConfig->sun.offsetSecMorning, mConfig->sun.offsetSecEvening, true))
|
||||
once(std::bind(&app::tickSun, this), 1, "mqSun"); // MQTT not connected, retry
|
||||
once([this]() { tickSun(); }, 1, "mqSun"); // MQTT not connected, retry
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -344,7 +337,8 @@ void app::tickMinute(void) {
|
|||
void app::tickMidnight(void) {
|
||||
uint32_t localTime = gTimezone.toLocal(mTimestamp);
|
||||
uint32_t nxtTrig = gTimezone.toUTC(localTime - (localTime % 86400) + 86400); // next midnight local time
|
||||
onceAt(std::bind(&app::tickMidnight, this), nxtTrig, "mid2");
|
||||
resetTickerByName("midNi");
|
||||
onceAt([this]() { tickMidnight(); }, nxtTrig, "midNi");
|
||||
|
||||
Inverter<> *iv;
|
||||
for (uint8_t id = 0; id < mSys.getNumInverters(); id++) {
|
||||
|
@ -387,7 +381,7 @@ void app::tickSend(void) {
|
|||
}
|
||||
|
||||
if(mAllIvNotAvail != notAvail)
|
||||
once(std::bind(&app::notAvailChanged, this), 1, "avail");
|
||||
once([this]() { notAvailChanged(); }, 1, "avail");
|
||||
mAllIvNotAvail = notAvail;
|
||||
|
||||
updateLed();
|
||||
|
@ -524,9 +518,6 @@ void app::resetSystem(void) {
|
|||
|
||||
mAllIvNotAvail = true;
|
||||
|
||||
mSunrise = 0;
|
||||
mSunset = 0;
|
||||
|
||||
mMqttEnabled = false;
|
||||
|
||||
mSendLastIvId = 0;
|
||||
|
@ -535,8 +526,6 @@ void app::resetSystem(void) {
|
|||
mSaveReboot = false;
|
||||
|
||||
mNetworkConnected = false;
|
||||
mNtpReceived = false;
|
||||
mTickerInstallOnce = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
35
src/app.h
35
src/app.h
|
@ -89,7 +89,7 @@ class app : public IApp, public ah::Scheduler {
|
|||
|
||||
void setup(void);
|
||||
void loop(void) override;
|
||||
void onNetwork(bool gotIp);
|
||||
void onNetwork(bool connected);
|
||||
void regularTickers(void);
|
||||
|
||||
void handleIntr(void) {
|
||||
|
@ -195,6 +195,10 @@ class app : public IApp, public ah::Scheduler {
|
|||
return mNetwork->isApActive();
|
||||
}
|
||||
|
||||
bool isNetworkConnected() override {
|
||||
return mNetwork->isConnected();
|
||||
}
|
||||
|
||||
void setRebootFlag() override {
|
||||
once(std::bind(&app::tickReboot, this), 3, "rboot");
|
||||
}
|
||||
|
@ -287,6 +291,29 @@ class app : public IApp, public ah::Scheduler {
|
|||
return mConfig->cmt.enabled;
|
||||
}
|
||||
|
||||
bool cmtSearch(uint8_t id, uint8_t toCh) override {
|
||||
#if defined(ESP32)
|
||||
Inverter<> *iv;
|
||||
|
||||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
|
||||
iv = mSys.getInverterByPos(i, true);
|
||||
if(nullptr != iv) {
|
||||
if(i == id)
|
||||
break;
|
||||
else
|
||||
iv = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(nullptr != iv) {
|
||||
mCmtRadio.catchInverter(iv, toCh);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t getNrfIrqPin(void) {
|
||||
return mConfig->nrf.pinIrq;
|
||||
}
|
||||
|
@ -403,10 +430,7 @@ class app : public IApp, public ah::Scheduler {
|
|||
setRebootFlag();
|
||||
}
|
||||
|
||||
void tickNtpUpdate(void);
|
||||
void onNtpUpdate(bool gotTime);
|
||||
bool mNtpReceived = false;
|
||||
void updateNtp(void);
|
||||
void onNtpUpdate(uint32_t utcTimestamp);
|
||||
|
||||
void triggerTickSend(uint8_t id) override {
|
||||
once([this, id]() {
|
||||
|
@ -461,7 +485,6 @@ class app : public IApp, public ah::Scheduler {
|
|||
#if defined(ENABLE_MQTT)
|
||||
PubMqttType mMqtt;
|
||||
#endif
|
||||
bool mTickerInstallOnce = false;
|
||||
bool mMqttEnabled = false;
|
||||
|
||||
// sun
|
||||
|
|
|
@ -34,6 +34,7 @@ class IApp {
|
|||
virtual String getIp(void) = 0;
|
||||
virtual String getMac(void) = 0;
|
||||
virtual bool isApActive(void) = 0;
|
||||
virtual bool isNetworkConnected() = 0;
|
||||
|
||||
virtual uint32_t getUptime() = 0;
|
||||
virtual uint32_t getTimestamp() = 0;
|
||||
|
@ -55,6 +56,8 @@ class IApp {
|
|||
virtual bool getNrfEnabled() = 0;
|
||||
virtual bool getCmtEnabled() = 0;
|
||||
|
||||
virtual bool cmtSearch(uint8_t id, uint8_t toCh) = 0;
|
||||
|
||||
virtual uint32_t getMqttRxCnt() = 0;
|
||||
virtual uint32_t getMqttTxCnt() = 0;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//-------------------------------------
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 8
|
||||
#define VERSION_PATCH 154
|
||||
#define VERSION_PATCH 155
|
||||
//-------------------------------------
|
||||
typedef struct {
|
||||
uint8_t ch;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 2023 Ahoy, https://github.com/lumpapu/ahoy
|
||||
// 2024 Ahoy, https://github.com/lumpapu/ahoy
|
||||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
@ -11,36 +11,133 @@
|
|||
#include "hmInverter.h"
|
||||
#include "../utils/dbg.h"
|
||||
|
||||
#define DEFAULT_ATTEMPS 5
|
||||
#define MORE_ATTEMPS_ALARMDATA 3 // 8
|
||||
#define MORE_ATTEMPS_GRIDONPROFILEPARA 0 // 5
|
||||
#if !defined(ESP32)
|
||||
#if !defined(vSemaphoreDelete)
|
||||
#define vSemaphoreDelete(a)
|
||||
#define xSemaphoreTake(a, b) { while(a) { yield(); } a = true; }
|
||||
#define xSemaphoreGive(a) { a = false; }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
template <uint8_t N=200>
|
||||
#else
|
||||
template <uint8_t N=100>
|
||||
#endif
|
||||
class CommQueue {
|
||||
public:
|
||||
void addImportant(Inverter<> *iv, uint8_t cmd) {
|
||||
queue_s q(iv, cmd, true);
|
||||
if(!isIncluded(&q)) {
|
||||
dec(&mRdPtr);
|
||||
mQueue[mRdPtr] = q;
|
||||
protected: /* types */
|
||||
static constexpr uint8_t DefaultAttempts = 5;
|
||||
static constexpr uint8_t MoreAttemptsAlarmData = 3;
|
||||
static constexpr uint8_t MoreAttemptsGridProfile = 0;
|
||||
|
||||
protected:
|
||||
struct QueueElement {
|
||||
Inverter<> *iv;
|
||||
uint8_t cmd;
|
||||
uint8_t attempts;
|
||||
uint8_t attemptsMax;
|
||||
uint32_t ts;
|
||||
bool isDevControl;
|
||||
|
||||
QueueElement()
|
||||
: iv {nullptr}
|
||||
, cmd {0}
|
||||
, attempts {0}
|
||||
, attemptsMax {0}
|
||||
, ts {0}
|
||||
, isDevControl {false}
|
||||
{}
|
||||
|
||||
QueueElement(Inverter<> *iv, uint8_t cmd, bool devCtrl)
|
||||
: iv {iv}
|
||||
, cmd {cmd}
|
||||
, attempts {DefaultAttempts}
|
||||
, attemptsMax {DefaultAttempts}
|
||||
, ts {0}
|
||||
, isDevControl {devCtrl}
|
||||
{}
|
||||
|
||||
QueueElement(const QueueElement&) = delete;
|
||||
|
||||
QueueElement(QueueElement&& other) : QueueElement{} {
|
||||
this->swap(other);
|
||||
}
|
||||
|
||||
void changeCmd(uint8_t cmd) {
|
||||
this->cmd = cmd;
|
||||
this->isDevControl = false;
|
||||
}
|
||||
|
||||
void setTs(const uint32_t ts) {
|
||||
this->ts = ts;
|
||||
}
|
||||
|
||||
void setAttempt() {
|
||||
if(this->attempts)
|
||||
this->attempts--;
|
||||
}
|
||||
|
||||
void incrAttempt(uint8_t attempts = 1) {
|
||||
this->attempts += attempts;
|
||||
if (this->attempts > this->attemptsMax)
|
||||
this->attemptsMax = this->attempts;
|
||||
}
|
||||
|
||||
QueueElement& operator=(const QueueElement&) = delete;
|
||||
|
||||
QueueElement& operator = (QueueElement&& other) {
|
||||
this->swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(QueueElement& other) {
|
||||
std::swap(this->iv, other.iv);
|
||||
std::swap(this->cmd, other.cmd);
|
||||
std::swap(this->attempts, other.attempts);
|
||||
std::swap(this->attemptsMax, other.attemptsMax);
|
||||
std::swap(this->ts, other.ts);
|
||||
std::swap(this->isDevControl, other.isDevControl);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
CommQueue()
|
||||
: wrPtr {0}
|
||||
, rdPtr {0}
|
||||
{
|
||||
#if defined(ESP32)
|
||||
this->mutex = xSemaphoreCreateBinaryStatic(&this->mutex_buffer);
|
||||
xSemaphoreGive(this->mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
~CommQueue() {
|
||||
vSemaphoreDelete(this->mutex);
|
||||
}
|
||||
|
||||
void addImportant(Inverter<> *iv, uint8_t cmd) {
|
||||
QueueElement q(iv, cmd, true);
|
||||
xSemaphoreTake(this->mutex, portMAX_DELAY);
|
||||
if(!isIncluded(&q)) {
|
||||
dec(&this->rdPtr);
|
||||
mQueue[this->rdPtr] = std::move(q);
|
||||
}
|
||||
xSemaphoreGive(this->mutex);
|
||||
}
|
||||
|
||||
void add(Inverter<> *iv, uint8_t cmd) {
|
||||
queue_s q(iv, cmd, false);
|
||||
QueueElement q(iv, cmd, false);
|
||||
xSemaphoreTake(this->mutex, portMAX_DELAY);
|
||||
if(!isIncluded(&q)) {
|
||||
mQueue[mWrPtr] = q;
|
||||
inc(&mWrPtr);
|
||||
mQueue[this->wrPtr] = std::move(q);
|
||||
inc(&this->wrPtr);
|
||||
}
|
||||
}
|
||||
|
||||
void chgCmd(Inverter<> *iv, uint8_t cmd) {
|
||||
mQueue[mWrPtr] = queue_s(iv, cmd, false);
|
||||
xSemaphoreGive(this->mutex);
|
||||
}
|
||||
|
||||
uint8_t getFillState(void) const {
|
||||
//DPRINTLN(DBG_INFO, "wr: " + String(mWrPtr) + ", rd: " + String(mRdPtr));
|
||||
return abs(mRdPtr - mWrPtr);
|
||||
//DPRINTLN(DBG_INFO, "wr: " + String(this->wrPtr) + ", rd: " + String(this->rdPtr));
|
||||
return abs(this->rdPtr - this->wrPtr);
|
||||
}
|
||||
|
||||
uint8_t getMaxFill(void) const {
|
||||
|
@ -48,70 +145,44 @@ class CommQueue {
|
|||
}
|
||||
|
||||
protected:
|
||||
struct queue_s {
|
||||
Inverter<> *iv;
|
||||
uint8_t cmd;
|
||||
uint8_t attempts;
|
||||
uint8_t attemptsMax;
|
||||
uint32_t ts;
|
||||
bool isDevControl;
|
||||
queue_s() {}
|
||||
queue_s(Inverter<> *i, uint8_t c, bool dev) :
|
||||
iv(i), cmd(c), attempts(DEFAULT_ATTEMPS), attemptsMax(DEFAULT_ATTEMPS), ts(0), isDevControl(dev) {}
|
||||
};
|
||||
|
||||
protected:
|
||||
void add(queue_s q) {
|
||||
mQueue[mWrPtr] = q;
|
||||
inc(&mWrPtr);
|
||||
void add(QueueElement q) {
|
||||
xSemaphoreTake(this->mutex, portMAX_DELAY);
|
||||
mQueue[this->wrPtr] = q;
|
||||
inc(&this->wrPtr);
|
||||
xSemaphoreGive(this->mutex);
|
||||
}
|
||||
|
||||
void add(const queue_s *q, bool rstAttempts = false) {
|
||||
mQueue[mWrPtr] = *q;
|
||||
void add(QueueElement *q, bool rstAttempts = false) {
|
||||
xSemaphoreTake(this->mutex, portMAX_DELAY);
|
||||
if(rstAttempts) {
|
||||
mQueue[mWrPtr].attempts = DEFAULT_ATTEMPS;
|
||||
mQueue[mWrPtr].attemptsMax = DEFAULT_ATTEMPS;
|
||||
q->attempts = DefaultAttempts;
|
||||
q->attemptsMax = DefaultAttempts;
|
||||
}
|
||||
inc(&mWrPtr);
|
||||
mQueue[this->wrPtr] = std::move(*q);
|
||||
inc(&this->wrPtr);
|
||||
xSemaphoreGive(this->mutex);
|
||||
}
|
||||
|
||||
void chgCmd(uint8_t cmd) {
|
||||
mQueue[mRdPtr].cmd = cmd;
|
||||
mQueue[mRdPtr].isDevControl = false;
|
||||
}
|
||||
|
||||
void get(std::function<void(bool valid, const queue_s *q)> cb) {
|
||||
if(mRdPtr == mWrPtr) {
|
||||
cb(false, &mQueue[mRdPtr]); // empty
|
||||
return;
|
||||
void get(std::function<void(bool valid, QueueElement *q)> cb) {
|
||||
xSemaphoreTake(this->mutex, portMAX_DELAY);
|
||||
if(this->rdPtr == this->wrPtr) {
|
||||
xSemaphoreGive(this->mutex);
|
||||
cb(false, nullptr); // empty
|
||||
} else {
|
||||
QueueElement el = std::move(mQueue[this->rdPtr]);
|
||||
inc(&this->rdPtr);
|
||||
xSemaphoreGive(this->mutex);
|
||||
cb(true, &el);
|
||||
}
|
||||
cb(true, &mQueue[mRdPtr]);
|
||||
}
|
||||
|
||||
void cmdDone(bool keep = false) {
|
||||
if(keep) {
|
||||
mQueue[mRdPtr].attempts = DEFAULT_ATTEMPS;
|
||||
mQueue[mRdPtr].attemptsMax = DEFAULT_ATTEMPS;
|
||||
add(mQueue[mRdPtr]); // add to the end again
|
||||
}
|
||||
inc(&mRdPtr);
|
||||
}
|
||||
|
||||
void setTs(const uint32_t *ts) {
|
||||
mQueue[mRdPtr].ts = *ts;
|
||||
}
|
||||
|
||||
void setAttempt(void) {
|
||||
if(mQueue[mRdPtr].attempts)
|
||||
mQueue[mRdPtr].attempts--;
|
||||
}
|
||||
|
||||
void incrAttempt(uint8_t attempts = 1) {
|
||||
mQueue[mRdPtr].attempts += attempts;
|
||||
if (mQueue[mRdPtr].attempts > mQueue[mRdPtr].attemptsMax)
|
||||
mQueue[mRdPtr].attemptsMax = mQueue[mRdPtr].attempts;
|
||||
void cmdReset(QueueElement *q) {
|
||||
q->attempts = DefaultAttempts;
|
||||
q->attemptsMax = DefaultAttempts;
|
||||
add(q); // add to the end again
|
||||
}
|
||||
|
||||
private:
|
||||
void inc(uint8_t *ptr) {
|
||||
if(++(*ptr) >= N)
|
||||
*ptr = 0;
|
||||
|
@ -123,13 +194,14 @@ class CommQueue {
|
|||
--(*ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
bool isIncluded(const queue_s *q) {
|
||||
uint8_t ptr = mRdPtr;
|
||||
while (ptr != mWrPtr) {
|
||||
bool isIncluded(const QueueElement *q) {
|
||||
uint8_t ptr = this->rdPtr;
|
||||
while (ptr != this->wrPtr) {
|
||||
if(mQueue[ptr].cmd == q->cmd) {
|
||||
if(mQueue[ptr].iv->id == q->iv->id)
|
||||
return true;
|
||||
if(mQueue[ptr].iv->id == q->iv->id) {
|
||||
if(mQueue[ptr].isDevControl == q->isDevControl)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
inc(&ptr);
|
||||
}
|
||||
|
@ -137,9 +209,17 @@ class CommQueue {
|
|||
}
|
||||
|
||||
protected:
|
||||
std::array<queue_s, N> mQueue;
|
||||
uint8_t mWrPtr = 0;
|
||||
uint8_t mRdPtr = 0;
|
||||
std::array<QueueElement, N> mQueue;
|
||||
|
||||
private:
|
||||
uint8_t wrPtr;
|
||||
uint8_t rdPtr;
|
||||
#if defined(ESP32)
|
||||
SemaphoreHandle_t mutex;
|
||||
StaticSemaphore_t mutex_buffer;
|
||||
#else
|
||||
bool mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -21,6 +21,12 @@ typedef std::function<void(Inverter<> *)> alarmListenerType;
|
|||
|
||||
class Communication : public CommQueue<> {
|
||||
public:
|
||||
Communication()
|
||||
: CommQueue()
|
||||
{}
|
||||
|
||||
~Communication() {}
|
||||
|
||||
void setup(uint32_t *timestamp, bool *serialDebug, bool *privacyMode, bool *printWholeTrace) {
|
||||
mTimestamp = timestamp;
|
||||
mPrivacyMode = privacyMode;
|
||||
|
@ -28,11 +34,6 @@ class Communication : public CommQueue<> {
|
|||
mPrintWholeTrace = printWholeTrace;
|
||||
}
|
||||
|
||||
void addImportant(Inverter<> *iv, uint8_t cmd) {
|
||||
mState = States::RESET; // cancel current operation
|
||||
CommQueue::addImportant(iv, cmd);
|
||||
}
|
||||
|
||||
void addPayloadListener(payloadListenerType cb) {
|
||||
mCbPayload = cb;
|
||||
}
|
||||
|
@ -46,29 +47,39 @@ class Communication : public CommQueue<> {
|
|||
}
|
||||
|
||||
void loop() {
|
||||
get([this](bool valid, const queue_s *q) {
|
||||
if(!valid) {
|
||||
if(mPrintSequenceDuration) {
|
||||
mPrintSequenceDuration = false;
|
||||
DPRINT(DBG_INFO, F("com loop duration: "));
|
||||
DBGPRINT(String(millis() - mLastEmptyQueueMillis));
|
||||
DBGPRINTLN(F("ms"));
|
||||
DBGPRINTLN(F("-----"));
|
||||
if(States::IDLE == mState) {
|
||||
get([this](bool valid, QueueElement *q) {
|
||||
if(!valid) {
|
||||
if(mPrintSequenceDuration) {
|
||||
mPrintSequenceDuration = false;
|
||||
DPRINT(DBG_INFO, F("com loop duration: "));
|
||||
DBGPRINT(String(millis() - mLastEmptyQueueMillis));
|
||||
DBGPRINTLN(F("ms"));
|
||||
DBGPRINTLN(F("-----"));
|
||||
}
|
||||
return; // empty
|
||||
}
|
||||
return; // empty
|
||||
}
|
||||
if(!mPrintSequenceDuration) // entry was added to the queue
|
||||
mLastEmptyQueueMillis = millis();
|
||||
mPrintSequenceDuration = true;
|
||||
|
||||
innerLoop(q);
|
||||
});
|
||||
el = std::move(*q);
|
||||
mState = States::INIT;
|
||||
if(!mPrintSequenceDuration) // entry was added to the queue
|
||||
mLastEmptyQueueMillis = millis();
|
||||
mPrintSequenceDuration = true;
|
||||
});
|
||||
}
|
||||
|
||||
if(nullptr != el.iv)
|
||||
innerLoop(&el);
|
||||
}
|
||||
|
||||
private:
|
||||
inline void innerLoop(const queue_s *q) {
|
||||
inline void innerLoop(QueueElement *q) {
|
||||
switch(mState) {
|
||||
case States::RESET:
|
||||
default:
|
||||
case States::IDLE:
|
||||
break;
|
||||
|
||||
case States::INIT:
|
||||
if (!mWaitTime.isTimeout())
|
||||
return;
|
||||
|
||||
|
@ -85,21 +96,20 @@ class Communication : public CommQueue<> {
|
|||
q->iv->curFrmCnt = 0;
|
||||
q->iv->radioStatistics.txCnt++;
|
||||
mIsRetransmit = false;
|
||||
if(NULL == q->iv->radio)
|
||||
cmdDone(false); // can't communicate while radio is not defined!
|
||||
mFirstTry = (INV_RADIO_TYPE_NRF == q->iv->ivRadioType) && (q->iv->isAvailable());
|
||||
q->iv->mCmd = q->cmd;
|
||||
q->iv->mIsSingleframeReq = false;
|
||||
mFramesExpected = getFramesExpected(q); // function to get expected frame count.
|
||||
mTimeout = DURATION_TXFRAME + mFramesExpected*DURATION_ONEFRAME + duration_reserve[q->iv->ivRadioType];
|
||||
if((q->iv->ivGen == IV_MI) && ((q->cmd == MI_REQ_CH1) || (q->cmd == MI_REQ_4CH)))
|
||||
incrAttempt(q->iv->channels); // 2 more attempts for 2ch, 4 more for 4ch
|
||||
q->incrAttempt(q->iv->channels); // 2 more attempts for 2ch, 4 more for 4ch
|
||||
|
||||
mState = States::START;
|
||||
if(NULL != q->iv->radio)
|
||||
mState = States::START;
|
||||
break;
|
||||
|
||||
case States::START:
|
||||
setTs(mTimestamp);
|
||||
q->setTs(*mTimestamp);
|
||||
if(INV_RADIO_TYPE_CMT == q->iv->ivRadioType) {
|
||||
// frequency was changed during runtime
|
||||
if(q->iv->curCmtFreq != q->iv->config->frequency) {
|
||||
|
@ -118,10 +128,10 @@ class Communication : public CommQueue<> {
|
|||
//q->iv->radioStatistics.txCnt++;
|
||||
q->iv->radio->mRadioWaitTime.startTimeMonitor(mTimeout);
|
||||
if((!mIsRetransmit && (q->cmd == AlarmData)) || (q->cmd == GridOnProFilePara))
|
||||
incrAttempt((q->cmd == AlarmData)? MORE_ATTEMPS_ALARMDATA : MORE_ATTEMPS_GRIDONPROFILEPARA);
|
||||
q->incrAttempt((q->cmd == AlarmData)? CommQueue::MoreAttemptsAlarmData : CommQueue::MoreAttemptsGridProfile);
|
||||
|
||||
mIsRetransmit = false;
|
||||
setAttempt();
|
||||
q->setAttempt();
|
||||
mState = States::WAIT;
|
||||
break;
|
||||
|
||||
|
@ -182,17 +192,17 @@ class Communication : public CommQueue<> {
|
|||
q->iv->mDtuRxCnt++;
|
||||
|
||||
if (p->packet[0] == (TX_REQ_INFO + ALL_FRAMES)) { // response from get information command
|
||||
if(parseFrame(p)) {
|
||||
if(parseFrame(q, p)) {
|
||||
q->iv->curFrmCnt++;
|
||||
if(!mIsRetransmit && ((p->packet[9] == 0x02) || (p->packet[9] == 0x82)) && (p->millis < LIMIT_FAST_IV))
|
||||
mHeu.setIvRetriesGood(q->iv,p->millis < LIMIT_VERYFAST_IV);
|
||||
}
|
||||
} else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) { // response from dev control command
|
||||
q->iv->radio->mBufCtrl.pop();
|
||||
if(parseDevCtrl(p, q))
|
||||
closeRequest(q, true);
|
||||
else
|
||||
closeRequest(q, false);
|
||||
q->iv->radio->mBufCtrl.pop();
|
||||
return; // don't wait for empty buffer
|
||||
} else if(IV_MI == q->iv->ivGen) {
|
||||
parseMiFrame(p, q);
|
||||
|
@ -278,12 +288,12 @@ class Communication : public CommQueue<> {
|
|||
q->iv->radioStatistics.txCnt--;
|
||||
q->iv->radioStatistics.retransmits++;
|
||||
mCompleteRetry = true;
|
||||
mState = States::RESET;
|
||||
mState = States::IDLE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setAttempt();
|
||||
q->setAttempt();
|
||||
|
||||
if(*mSerialDebug) {
|
||||
DPRINT_IVID(DBG_WARN, q->iv->id);
|
||||
|
@ -312,7 +322,7 @@ class Communication : public CommQueue<> {
|
|||
}
|
||||
}
|
||||
|
||||
inline void printRxInfo(const queue_s *q, packet_t *p) {
|
||||
inline void printRxInfo(QueueElement *q, packet_t *p) {
|
||||
DPRINT_IVID(DBG_INFO, q->iv->id);
|
||||
DBGPRINT(F("RX "));
|
||||
if(p->millis < 100)
|
||||
|
@ -346,7 +356,7 @@ class Communication : public CommQueue<> {
|
|||
}
|
||||
|
||||
|
||||
inline uint8_t getFramesExpected(const queue_s *q) {
|
||||
inline uint8_t getFramesExpected(QueueElement *q) {
|
||||
if(q->isDevControl)
|
||||
return 1;
|
||||
|
||||
|
@ -410,7 +420,7 @@ class Communication : public CommQueue<> {
|
|||
return (ah::crc8(buf, len - 1) == buf[len-1]);
|
||||
}
|
||||
|
||||
inline bool parseFrame(packet_t *p) {
|
||||
inline bool parseFrame(QueueElement *q, packet_t *p) {
|
||||
uint8_t *frameId = &p->packet[9];
|
||||
if(0x00 == *frameId) {
|
||||
DPRINTLN(DBG_WARN, F("invalid frameId 0x00"));
|
||||
|
@ -429,7 +439,7 @@ class Communication : public CommQueue<> {
|
|||
if((*frameId & ALL_FRAMES) == ALL_FRAMES) {
|
||||
mMaxFrameId = (*frameId & 0x7f);
|
||||
if(mMaxFrameId > 8) // large payloads, e.g. AlarmData
|
||||
incrAttempt(mMaxFrameId - 6);
|
||||
q->incrAttempt(mMaxFrameId - 6);
|
||||
}
|
||||
|
||||
frame_t *f = &mLocalBuf[(*frameId & 0x7f) - 1];
|
||||
|
@ -440,7 +450,7 @@ class Communication : public CommQueue<> {
|
|||
return true;
|
||||
}
|
||||
|
||||
inline void parseMiFrame(packet_t *p, const queue_s *q) {
|
||||
inline void parseMiFrame(packet_t *p, QueueElement *q) {
|
||||
if((!mIsRetransmit && p->packet[9] == 0x00) && (p->millis < LIMIT_FAST_IV_MI)) //first frame is fast?
|
||||
mHeu.setIvRetriesGood(q->iv,p->millis < LIMIT_VERYFAST_IV_MI);
|
||||
if ((p->packet[0] == MI_REQ_CH1 + ALL_FRAMES)
|
||||
|
@ -464,7 +474,7 @@ class Communication : public CommQueue<> {
|
|||
}
|
||||
}
|
||||
|
||||
inline bool parseDevCtrl(const packet_t *p, const queue_s *q) {
|
||||
inline bool parseDevCtrl(const packet_t *p, QueueElement *q) {
|
||||
switch(p->packet[12]) {
|
||||
case ActivePowerContr:
|
||||
if(p->packet[13] != 0x00)
|
||||
|
@ -502,7 +512,7 @@ class Communication : public CommQueue<> {
|
|||
return accepted;
|
||||
}
|
||||
|
||||
inline bool compilePayload(const queue_s *q) {
|
||||
inline bool compilePayload(QueueElement *q) {
|
||||
uint16_t crc = 0xffff, crcRcv = 0x0000;
|
||||
for(uint8_t i = 0; i < mMaxFrameId; i++) {
|
||||
if(i == (mMaxFrameId - 1)) {
|
||||
|
@ -522,7 +532,7 @@ class Communication : public CommQueue<> {
|
|||
} else
|
||||
DBGPRINTLN(F("-> complete retransmit"));
|
||||
mCompleteRetry = true;
|
||||
mState = States::RESET;
|
||||
mState = States::IDLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -602,7 +612,7 @@ class Communication : public CommQueue<> {
|
|||
return true;
|
||||
}
|
||||
|
||||
void sendRetransmit(const queue_s *q, uint8_t i) {
|
||||
void sendRetransmit(QueueElement *q, uint8_t i) {
|
||||
mFramesExpected = 1;
|
||||
q->iv->radio->setExpectedFrames(mFramesExpected);
|
||||
q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (SINGLE_FRAME + i), true);
|
||||
|
@ -613,7 +623,7 @@ class Communication : public CommQueue<> {
|
|||
}
|
||||
|
||||
private:
|
||||
void closeRequest(const queue_s *q, bool crcPass) {
|
||||
void closeRequest(QueueElement *q, bool crcPass) {
|
||||
mHeu.evalTxChQuality(q->iv, crcPass, (q->attemptsMax - 1 - q->attempts), q->iv->curFrmCnt);
|
||||
if(crcPass)
|
||||
q->iv->radioStatistics.rxSuccess++;
|
||||
|
@ -627,17 +637,20 @@ class Communication : public CommQueue<> {
|
|||
if(q->isDevControl)
|
||||
keep = !crcPass;
|
||||
|
||||
cmdDone(keep);
|
||||
q->iv->mGotFragment = false;
|
||||
q->iv->mGotLastMsg = false;
|
||||
q->iv->miMultiParts = 0;
|
||||
|
||||
if(keep)
|
||||
cmdReset(q); // q will be zero'ed after that command
|
||||
|
||||
mIsRetransmit = false;
|
||||
mCompleteRetry = false;
|
||||
mState = States::RESET;
|
||||
mState = States::IDLE;
|
||||
DBGPRINTLN(F("-----"));
|
||||
}
|
||||
|
||||
inline void miHwDecode(packet_t *p, const queue_s *q) {
|
||||
inline void miHwDecode(packet_t *p, QueueElement *q) {
|
||||
record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_All); // choose the record structure
|
||||
rec->ts = q->ts;
|
||||
/*
|
||||
|
@ -751,7 +764,7 @@ class Communication : public CommQueue<> {
|
|||
}
|
||||
}
|
||||
|
||||
inline void miGPFDecode(packet_t *p, const queue_s *q) {
|
||||
inline void miGPFDecode(packet_t *p, QueueElement *q) {
|
||||
record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_Simple); // choose the record structure
|
||||
rec->ts = q->ts;
|
||||
rec->mqttSentStatus = MqttSentStatus::NEW_DATA;
|
||||
|
@ -777,10 +790,10 @@ class Communication : public CommQueue<> {
|
|||
q->iv->miMultiParts = 7; // indicate we are ready
|
||||
}
|
||||
|
||||
inline void miDataDecode(packet_t *p, const queue_s *q) {
|
||||
inline void miDataDecode(packet_t *p, QueueElement *q) {
|
||||
record_t<> *rec = q->iv->getRecordStruct(RealTimeRunData_Debug); // choose the parser
|
||||
rec->ts = q->ts;
|
||||
//mState = States::RESET;
|
||||
//mState = States::IDLE;
|
||||
if(q->iv->miMultiParts < 6)
|
||||
q->iv->miMultiParts += 6;
|
||||
|
||||
|
@ -830,7 +843,7 @@ class Communication : public CommQueue<> {
|
|||
q->iv->miMultiParts += 6; // indicate we are ready
|
||||
}
|
||||
|
||||
void miNextRequest(uint8_t cmd, const queue_s *q) {
|
||||
void miNextRequest(uint8_t cmd, QueueElement *q) {
|
||||
mHeu.evalTxChQuality(q->iv, true, (q->attemptsMax - 1 - q->attempts), q->iv->curFrmCnt);
|
||||
mHeu.getTxCh(q->iv);
|
||||
q->iv->radioStatistics.ivSent++;
|
||||
|
@ -850,12 +863,12 @@ class Communication : public CommQueue<> {
|
|||
DBGHEXLN(cmd);
|
||||
}
|
||||
mIsRetransmit = true;
|
||||
chgCmd(cmd);
|
||||
q->changeCmd(cmd);
|
||||
//mState = States::WAIT;
|
||||
}
|
||||
|
||||
void miRepeatRequest(const queue_s *q) {
|
||||
setAttempt(); // if function is called, we got something, and we necessarily need more transmissions for MI types...
|
||||
void miRepeatRequest(QueueElement *q) {
|
||||
q->setAttempt(); // if function is called, we got something, and we necessarily need more transmissions for MI types...
|
||||
q->iv->radio->sendCmdPacket(q->iv, q->cmd, 0x00, true);
|
||||
q->iv->radioStatistics.retransmits++;
|
||||
q->iv->radio->mRadioWaitTime.startTimeMonitor(DURATION_TXFRAME + DURATION_ONEFRAME + duration_reserve[q->iv->ivRadioType]);
|
||||
|
@ -869,7 +882,7 @@ class Communication : public CommQueue<> {
|
|||
//mIsRetransmit = false;
|
||||
}
|
||||
|
||||
void miStsConsolidate(const queue_s *q, uint8_t stschan, record_t<> *rec, uint8_t uState, uint8_t uEnum, uint8_t lState = 0, uint8_t lEnum = 0) {
|
||||
void miStsConsolidate(QueueElement *q, uint8_t stschan, record_t<> *rec, uint8_t uState, uint8_t uEnum, uint8_t lState = 0, uint8_t lEnum = 0) {
|
||||
//uint8_t status = (p->packet[11] << 8) + p->packet[12];
|
||||
uint16_t statusMi = 3; // regular status for MI, change to 1 later?
|
||||
if ( uState == 2 ) {
|
||||
|
@ -1014,7 +1027,7 @@ class Communication : public CommQueue<> {
|
|||
|
||||
private:
|
||||
enum class States : uint8_t {
|
||||
RESET, START, WAIT, CHECK_FRAMES, CHECK_PACKAGE
|
||||
IDLE, INIT, START, WAIT, CHECK_FRAMES, CHECK_PACKAGE
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
@ -1024,8 +1037,9 @@ class Communication : public CommQueue<> {
|
|||
} frame_t;
|
||||
|
||||
private:
|
||||
States mState = States::RESET;
|
||||
States mState = States::IDLE;
|
||||
uint32_t *mTimestamp = nullptr;
|
||||
QueueElement el;
|
||||
bool *mPrivacyMode = nullptr, *mSerialDebug = nullptr, *mPrintWholeTrace = nullptr;
|
||||
TimeMonitor mWaitTime = TimeMonitor(0, true); // start as expired (due to code in RESET state)
|
||||
std::array<frame_t, MAX_PAYLOAD_ENTRIES> mLocalBuf;
|
||||
|
|
|
@ -29,6 +29,7 @@ class Radio {
|
|||
virtual void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit) = 0;
|
||||
virtual bool switchFrequency(Inverter<> *iv, uint32_t fromkHz, uint32_t tokHz) { return true; }
|
||||
virtual bool switchFrequencyCh(Inverter<> *iv, uint8_t fromCh, uint8_t toCh) { return true; }
|
||||
virtual void catchInverter(Inverter<> *iv, uint8_t toCh) {}
|
||||
virtual bool isChipConnected(void) const { return false; }
|
||||
virtual uint16_t getBaseFreqMhz() { return 0; }
|
||||
virtual uint16_t getBootFreqMhz() { return 0; }
|
||||
|
|
|
@ -116,6 +116,13 @@ const calcFunc_t<T> calcFunctions[] = {
|
|||
|
||||
template <class REC_TYP>
|
||||
class Inverter {
|
||||
public: /*types*/
|
||||
#if defined(ESP32)
|
||||
constexpr static uint8_t MaxAlarmNum = 50;
|
||||
#else
|
||||
constexpr static uint8_t MaxAlarmNum = 10;
|
||||
#endif
|
||||
|
||||
public:
|
||||
uint8_t ivGen = IV_UNKNOWN; // generation of inverter (HM / MI)
|
||||
uint8_t ivRadioType = INV_RADIO_TYPE_UNKNOWN; // refers to used radio (nRF24 / CMT)
|
||||
|
@ -135,7 +142,7 @@ class Inverter {
|
|||
record_t<REC_TYP> recordConfig; // structure for system config values
|
||||
record_t<REC_TYP> recordAlarm; // structure for alarm values
|
||||
InverterStatus status = InverterStatus::OFF; // indicates the current inverter status
|
||||
std::array<alarm_t, 10> lastAlarm; // holds last 10 alarms
|
||||
std::array<alarm_t, MaxAlarmNum> lastAlarm; // holds last x alarms
|
||||
int8_t rssi = 0; // RSSI
|
||||
uint16_t alarmCnt = 0; // counts the total number of occurred alarms
|
||||
uint16_t alarmLastId = 0; // lastId which was received
|
||||
|
@ -822,9 +829,9 @@ class Inverter {
|
|||
if(start > end)
|
||||
end = 0;
|
||||
|
||||
for(; i < 10; i++) {
|
||||
for(; i < MaxAlarmNum; i++) {
|
||||
++mAlarmNxtWrPos;
|
||||
mAlarmNxtWrPos = mAlarmNxtWrPos % 10;
|
||||
mAlarmNxtWrPos = mAlarmNxtWrPos % MaxAlarmNum;
|
||||
|
||||
if(lastAlarm[mAlarmNxtWrPos].code == code && lastAlarm[mAlarmNxtWrPos].start == start) {
|
||||
// replace with same or update end time
|
||||
|
@ -834,11 +841,11 @@ class Inverter {
|
|||
}
|
||||
}
|
||||
|
||||
if(alarmCnt < 10 && alarmCnt <= mAlarmNxtWrPos)
|
||||
if(alarmCnt < MaxAlarmNum && alarmCnt <= mAlarmNxtWrPos)
|
||||
alarmCnt = mAlarmNxtWrPos + 1;
|
||||
|
||||
lastAlarm[mAlarmNxtWrPos] = alarm_t(code, start, end);
|
||||
if(++mAlarmNxtWrPos >= 10) // rolling buffer
|
||||
if(++mAlarmNxtWrPos >= MaxAlarmNum) // rolling buffer
|
||||
mAlarmNxtWrPos = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,11 @@ class CmtRadio : public Radio {
|
|||
return;
|
||||
|
||||
mCmt.loop();
|
||||
if(nullptr != mCatchIv) {
|
||||
if(mCmt.isTxReady())
|
||||
catchInverterLoop();
|
||||
}
|
||||
|
||||
if((!mIrqRcvd) && (!mRqstGetRx))
|
||||
return;
|
||||
getRx();
|
||||
|
@ -53,7 +58,8 @@ class CmtRadio : public Radio {
|
|||
if(!mCfg->enabled)
|
||||
return;
|
||||
|
||||
DPRINT(DBG_INFO, F("sendControlPacket cmd: "));
|
||||
DPRINT_IVID(DBG_INFO, iv->id);
|
||||
DBGPRINT(F("sendControlPacket cmd: "));
|
||||
DBGHEXLN(cmd);
|
||||
initPacket(iv->radioId.u64, TX_REQ_DEVCONTROL, SINGLE_FRAME);
|
||||
uint8_t cnt = 10;
|
||||
|
@ -92,6 +98,28 @@ class CmtRadio : public Radio {
|
|||
return true;
|
||||
}
|
||||
|
||||
void catchInverter(Inverter<> *iv, uint8_t toCh) override {
|
||||
if(!isChipConnected())
|
||||
return;
|
||||
|
||||
mCatchIv = iv;
|
||||
mCatchIvCh = 1;
|
||||
mCatchIvToCh = toCh;
|
||||
|
||||
mCmt.switchChannel(0);
|
||||
sendSwitchChCmd(iv, toCh);
|
||||
}
|
||||
|
||||
void catchInverterLoop() {
|
||||
mCmt.switchChannel(mCatchIvCh);
|
||||
sendSwitchChCmd(mCatchIv, mCatchIvToCh);
|
||||
|
||||
if(++mCatchIvCh == 0x29) {
|
||||
mCmt.switchChannel(mCatchIvToCh);
|
||||
mCatchIv = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t getBaseFreqMhz(void) override {
|
||||
return mCmt.getBaseFreqMhz();
|
||||
}
|
||||
|
@ -167,10 +195,6 @@ class CmtRadio : public Radio {
|
|||
}
|
||||
|
||||
inline void sendSwitchChCmd(Inverter<> *iv, uint8_t ch) {
|
||||
//if(CMT_SWITCH_CHANNEL_CYCLE > ++mSwitchCycle)
|
||||
// return;
|
||||
//mSwitchCycle = 0;
|
||||
|
||||
/** ch:
|
||||
* 0x00: 860.00 MHz
|
||||
* 0x01: 860.25 MHz
|
||||
|
@ -193,7 +217,6 @@ class CmtRadio : public Radio {
|
|||
packet_t p;
|
||||
p.millis = millis() - mMillis;
|
||||
if(CmtStatus::SUCCESS == mCmt.getRx(p.packet, &p.len, 28, &p.rssi)) {
|
||||
//mSwitchCycle = 0;
|
||||
p.ch = 0; // not used for CMT inverters
|
||||
mBufCtrl.push(p);
|
||||
}
|
||||
|
@ -209,7 +232,10 @@ class CmtRadio : public Radio {
|
|||
bool mCmtAvail = false;
|
||||
bool mRqstGetRx = false;
|
||||
uint32_t mMillis = 0;
|
||||
//uint8_t mSwitchCycle = 0;
|
||||
|
||||
Inverter<> *mCatchIv = nullptr;
|
||||
uint8_t mCatchIvCh = 0;
|
||||
uint8_t mCatchIvToCh = 0;
|
||||
};
|
||||
|
||||
#endif /*__HMS_RADIO_H__*/
|
||||
|
|
|
@ -168,8 +168,13 @@ enum class CmtStatus : uint8_t {
|
|||
#define CMT2300A_MASK_PKT_OK_FLG 0x01
|
||||
|
||||
class Cmt2300a {
|
||||
private: /*types*/
|
||||
static constexpr uint8_t CmtTimeoutMs = 40;
|
||||
|
||||
public:
|
||||
Cmt2300a() {}
|
||||
Cmt2300a()
|
||||
: lastMillis {0}
|
||||
{}
|
||||
|
||||
void setup(uint8_t pinSclk, uint8_t pinSdio, uint8_t pinCsb, uint8_t pinFcsb) {
|
||||
mSpi.init(pinSdio, pinSclk, pinCsb, pinFcsb);
|
||||
|
@ -182,12 +187,17 @@ class Cmt2300a {
|
|||
if(CMT2300A_MASK_TX_DONE_FLG == mSpi.readReg(CMT2300A_CUS_INT_CLR1)) {
|
||||
if(cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY)) {
|
||||
mTxPending = false;
|
||||
lastMillis = 0;
|
||||
goRx();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isTxReady() {
|
||||
return !mTxPending;
|
||||
}
|
||||
|
||||
CmtStatus goRx(void) {
|
||||
if(mTxPending)
|
||||
return CmtStatus::ERR_TX_PENDING;
|
||||
|
@ -222,9 +232,6 @@ class Cmt2300a {
|
|||
}
|
||||
|
||||
CmtStatus getRx(uint8_t buf[], uint8_t *rxLen, uint8_t maxlen, int8_t *rssi) {
|
||||
if(mTxPending)
|
||||
return CmtStatus::ERR_TX_PENDING;
|
||||
|
||||
if(0x1b != (mSpi.readReg(CMT2300A_CUS_INT_FLAG) & 0x1b))
|
||||
return CmtStatus::FIFO_EMPTY;
|
||||
|
||||
|
@ -248,8 +255,19 @@ class Cmt2300a {
|
|||
}
|
||||
|
||||
CmtStatus tx(uint8_t buf[], uint8_t len) {
|
||||
if(mTxPending)
|
||||
return CmtStatus::ERR_TX_PENDING;
|
||||
if(mTxPending) {
|
||||
if(CmtTimeoutMs < (millis() - lastMillis)) {
|
||||
DPRINT(DBG_ERROR, "CMT, last TX timeout: ");
|
||||
DBGPRINT(String(millis() - lastMillis));
|
||||
DBGPRINTLN("ms");
|
||||
}
|
||||
|
||||
while(mTxPending && (CmtTimeoutMs > (millis() - lastMillis))) {
|
||||
vTaskDelay(10);
|
||||
}
|
||||
mTxPending = false;
|
||||
goRx();
|
||||
}
|
||||
|
||||
if(mInRxMode) {
|
||||
mInRxMode = false;
|
||||
|
@ -280,6 +298,7 @@ class Cmt2300a {
|
|||
|
||||
if(!cmtSwitchStatus(CMT2300A_GO_TX, CMT2300A_STA_TX))
|
||||
return CmtStatus::ERR_SWITCH_STATE;
|
||||
lastMillis = millis();
|
||||
|
||||
// wait for tx done
|
||||
mTxPending = true;
|
||||
|
@ -556,6 +575,8 @@ class Cmt2300a {
|
|||
uint8_t mCusIntFlag = 0;
|
||||
uint8_t mRqstCh = 0, mCurCh = 0;
|
||||
RegionCfg mRegionCfg = RegionCfg::EUROPE;
|
||||
|
||||
uint32_t lastMillis;
|
||||
};
|
||||
|
||||
#endif /*__CMT2300A_H__*/
|
||||
|
|
|
@ -11,21 +11,22 @@
|
|||
#include "../utils/helper.h"
|
||||
#include "AhoyWifiAp.h"
|
||||
#include "AsyncJson.h"
|
||||
#include <lwip/dns.h>
|
||||
|
||||
#define NTP_PACKET_SIZE 48
|
||||
|
||||
class AhoyNetwork {
|
||||
public:
|
||||
typedef std::function<void(bool)> OnNetworkCB;
|
||||
typedef std::function<void(bool)> OnTimeCB;
|
||||
typedef std::function<void(uint32_t utcTimestamp)> OnTimeCB;
|
||||
|
||||
public:
|
||||
void setup(settings_t *config, uint32_t *utcTimestamp, OnNetworkCB onNetworkCB, OnTimeCB onTimeCB) {
|
||||
void setup(settings_t *config, OnNetworkCB onNetworkCB, OnTimeCB onTimeCB) {
|
||||
mConfig = config;
|
||||
mUtcTimestamp = utcTimestamp;
|
||||
mOnNetworkCB = onNetworkCB;
|
||||
mOnTimeCB = onTimeCB;
|
||||
|
||||
mNtpIp = IPADDR_NONE;
|
||||
|
||||
if('\0' == mConfig->sys.deviceName[0])
|
||||
snprintf(mConfig->sys.deviceName, DEVNAME_LEN, "%s", DEF_DEVICE_NAME);
|
||||
|
||||
|
@ -51,20 +52,67 @@ class AhoyNetwork {
|
|||
#endif
|
||||
}
|
||||
|
||||
bool isConnected() const {
|
||||
return (mStatus == NetworkState::CONNECTED);
|
||||
virtual void tickNetworkLoop() {
|
||||
if(mDnsCallbackReady) {
|
||||
mDnsCallbackReady = false;
|
||||
startNtpUpdate();
|
||||
}
|
||||
|
||||
if(mNtpTimeoutSec) {
|
||||
mNtpTimeoutSec--;
|
||||
if(!mNtpTimeoutSec)
|
||||
mOnTimeCB(0); // timeout
|
||||
}
|
||||
}
|
||||
|
||||
bool updateNtpTime(void) {
|
||||
if(NetworkState::GOT_IP != mStatus)
|
||||
return false;
|
||||
bool isConnected() const {
|
||||
return ((mStatus == NetworkState::CONNECTED) || (mStatus == NetworkState::GOT_IP));
|
||||
}
|
||||
|
||||
static void dnsCallback(const char *name, const ip_addr_t *ipaddr, void *pClass) {
|
||||
AhoyNetwork *obj = static_cast<AhoyNetwork*>(pClass);
|
||||
if (ipaddr) {
|
||||
#if defined(ESP32)
|
||||
obj->mNtpIp = ipaddr->u_addr.ip4.addr;
|
||||
#else
|
||||
obj->mNtpIp = ipaddr->addr;
|
||||
#endif
|
||||
}
|
||||
obj->mDnsCallbackReady = true;
|
||||
}
|
||||
|
||||
void updateNtpTime() {
|
||||
if(mNtpIp != IPADDR_NONE) {
|
||||
startNtpUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
mNtpTimeoutSec = 30;
|
||||
|
||||
ip_addr_t ipaddr;
|
||||
mNtpIp = WiFi.gatewayIP();
|
||||
// dns_gethostbyname runs asynchronous and sets the member mNtpIp which is then checked on
|
||||
// next call of updateNtpTime
|
||||
err_t err = dns_gethostbyname(mConfig->ntp.addr, &ipaddr, dnsCallback, this);
|
||||
|
||||
if (err == ERR_OK) {
|
||||
#if defined(ESP32)
|
||||
mNtpIp = ipaddr.u_addr.ip4.addr;
|
||||
#else
|
||||
mNtpIp = ipaddr.addr;
|
||||
#endif
|
||||
startNtpUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void startNtpUpdate() {
|
||||
DPRINTLN(DBG_INFO, F("get time from: ") + mNtpIp.toString());
|
||||
if (!mUdp.connected()) {
|
||||
IPAddress timeServer;
|
||||
if (!WiFi.hostByName(mConfig->ntp.addr, timeServer))
|
||||
return false;
|
||||
if (!mUdp.connect(timeServer, mConfig->ntp.port))
|
||||
return false;
|
||||
if (!mUdp.connect(mNtpIp, mConfig->ntp.port)) {
|
||||
mOnTimeCB(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mUdp.onPacket([this](AsyncUDPPacket packet) {
|
||||
|
@ -72,12 +120,12 @@ class AhoyNetwork {
|
|||
});
|
||||
sendNTPpacket();
|
||||
|
||||
return true;
|
||||
// reset to start with DNS lookup next time again
|
||||
mNtpIp = IPADDR_NONE;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void begin() = 0;
|
||||
virtual void tickNetworkLoop() = 0;
|
||||
virtual String getIp(void) = 0;
|
||||
virtual String getMac(void) = 0;
|
||||
|
||||
|
@ -185,7 +233,7 @@ class AhoyNetwork {
|
|||
std::swap(sort[i], sort[j]);
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
void sendNTPpacket(void) {
|
||||
uint8_t buf[NTP_PACKET_SIZE];
|
||||
memset(buf, 0, NTP_PACKET_SIZE);
|
||||
|
@ -194,11 +242,6 @@ class AhoyNetwork {
|
|||
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);
|
||||
}
|
||||
|
@ -215,10 +258,9 @@ class AhoyNetwork {
|
|||
// 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();
|
||||
mNtpTimeoutSec = 0; // clear timeout
|
||||
mOnTimeCB(secsSince1900 - 2208988800UL);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -230,12 +272,15 @@ class AhoyNetwork {
|
|||
CONNECTING // ESP8266
|
||||
};
|
||||
|
||||
public:
|
||||
bool mDnsCallbackReady = false;
|
||||
|
||||
protected:
|
||||
settings_t *mConfig = nullptr;
|
||||
uint32_t *mUtcTimestamp = nullptr;
|
||||
bool mConnected = false;
|
||||
bool mScanActive = false;
|
||||
bool mWifiConnecting = false;
|
||||
uint8_t mNtpTimeoutSec = 0;
|
||||
|
||||
OnNetworkCB mOnNetworkCB;
|
||||
OnTimeCB mOnTimeCB;
|
||||
|
@ -245,6 +290,8 @@ class AhoyNetwork {
|
|||
AhoyWifiAp mAp;
|
||||
DNSServer mDns;
|
||||
|
||||
IPAddress mNtpIp;
|
||||
|
||||
AsyncUDP mUdp; // for time server
|
||||
#if defined(ESP8266)
|
||||
WiFiEventHandler wifiConnectHandler, wifiDisconnectHandler, wifiGotIPHandler;
|
||||
|
|
|
@ -37,6 +37,7 @@ class AhoyWifi : public AhoyNetwork {
|
|||
}
|
||||
|
||||
void tickNetworkLoop() override {
|
||||
AhoyNetwork::tickNetworkLoop();
|
||||
if(mAp.isEnabled())
|
||||
mAp.tickLoop();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ class AhoyWifi : public AhoyNetwork {
|
|||
}
|
||||
|
||||
void tickNetworkLoop() override {
|
||||
AhoyNetwork::tickNetworkLoop();
|
||||
if(mAp.isEnabled())
|
||||
mAp.tickLoop();
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ extra_scripts =
|
|||
post:../scripts/add_littlefs_binary.py
|
||||
|
||||
lib_deps =
|
||||
https://github.com/esphome/ESPAsyncWebServer @ ^3.2.2
|
||||
https://github.com/nRF24/RF24.git#v1.4.8
|
||||
paulstoffregen/Time @ ^1.6.1
|
||||
https://github.com/bertmelis/espMqttClient#v1.7.0
|
||||
|
@ -38,6 +37,7 @@ lib_deps =
|
|||
build_flags =
|
||||
-std=c++17
|
||||
-std=gnu++17
|
||||
-DEMC_ALLOW_NOT_CONNECTED_PUBLISH
|
||||
build_unflags =
|
||||
-std=gnu++11
|
||||
|
||||
|
@ -48,9 +48,12 @@ board = esp12e
|
|||
board_build.f_cpu = 80000000L
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
https://github.com/esphome/ESPAsyncWebServer @ ^3.2.2
|
||||
https://github.com/me-no-dev/ESPAsyncUDP
|
||||
build_flags = ${env.build_flags}
|
||||
-DEMC_MIN_FREE_MEMORY=4096
|
||||
|
||||
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096
|
||||
;-Wl,-Map,output.map
|
||||
monitor_filters =
|
||||
esp8266_exception_decoder
|
||||
|
@ -149,16 +152,20 @@ monitor_filters =
|
|||
esp8266_exception_decoder
|
||||
|
||||
[env:esp32-wroom32-minimal]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = lolin_d32
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
https://github.com/mathieucarbou/ESPAsyncWebServer#v3.3.12
|
||||
build_flags = ${env.build_flags}
|
||||
-DSPI_HAL
|
||||
monitor_filters =
|
||||
esp32_exception_decoder
|
||||
|
||||
[env:esp32-wroom32]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = lolin_d32
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env:esp32-wroom32-minimal.build_flags}
|
||||
-DUSE_HSPI_FOR_EPD
|
||||
-DENABLE_MQTT
|
||||
|
@ -186,32 +193,36 @@ monitor_filters =
|
|||
esp32_exception_decoder
|
||||
|
||||
[env:esp32-wroom32-de]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = lolin_d32
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env:esp32-wroom32.build_flags}
|
||||
-DLANG_DE
|
||||
monitor_filters =
|
||||
esp32_exception_decoder
|
||||
|
||||
[env:esp32-wroom32-prometheus]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = lolin_d32
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env:esp32-wroom32.build_flags}
|
||||
-DENABLE_PROMETHEUS_EP
|
||||
monitor_filters =
|
||||
esp32_exception_decoder
|
||||
|
||||
[env:esp32-wroom32-prometheus-de]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = lolin_d32
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env:esp32-wroom32-prometheus.build_flags}
|
||||
-DLANG_DE
|
||||
monitor_filters =
|
||||
esp32_exception_decoder
|
||||
|
||||
[env:esp32-s2-mini]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = lolin_s2_mini
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env.build_flags}
|
||||
-DUSE_HSPI_FOR_EPD
|
||||
-DSPI_HAL
|
||||
|
@ -233,16 +244,18 @@ monitor_filters =
|
|||
esp32_exception_decoder
|
||||
|
||||
[env:esp32-s2-mini-de]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = lolin_s2_mini
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env:esp32-s2-mini.build_flags}
|
||||
-DLANG_DE
|
||||
monitor_filters =
|
||||
esp32_exception_decoder
|
||||
|
||||
[env:esp32-c3-mini]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = lolin_c3_mini
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env.build_flags}
|
||||
-DUSE_HSPI_FOR_EPD
|
||||
-DSPI_HAL
|
||||
|
@ -264,17 +277,19 @@ monitor_filters =
|
|||
esp32_exception_decoder
|
||||
|
||||
[env:esp32-c3-mini-de]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = lolin_c3_mini
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env:esp32-c3-mini.build_flags}
|
||||
-DLANG_DE
|
||||
monitor_filters =
|
||||
esp32_exception_decoder
|
||||
|
||||
[env:opendtufusion-minimal]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = esp32-s3-devkitc-1
|
||||
upload_protocol = esp-builtin
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env.build_flags}
|
||||
-DSPI_HAL
|
||||
-DDEF_NRF_CS_PIN=37
|
||||
|
@ -297,9 +312,10 @@ monitor_filters =
|
|||
esp32_exception_decoder, colorize
|
||||
|
||||
[env:opendtufusion]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = esp32-s3-devkitc-1
|
||||
upload_protocol = esp-builtin
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env:opendtufusion-minimal.build_flags}
|
||||
-DETHERNET
|
||||
-DENABLE_MQTT
|
||||
|
@ -315,28 +331,31 @@ monitor_filters =
|
|||
esp32_exception_decoder, colorize
|
||||
|
||||
[env:opendtufusion-de]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = esp32-s3-devkitc-1
|
||||
upload_protocol = esp-builtin
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env:opendtufusion.build_flags}
|
||||
-DLANG_DE
|
||||
monitor_filters =
|
||||
esp32_exception_decoder, colorize
|
||||
|
||||
[env:opendtufusion-16MB]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = esp32-s3-devkitc-1
|
||||
board_upload.flash_size = 16MB
|
||||
board_build.partitions = default_16MB.csv
|
||||
upload_protocol = esp-builtin
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env:opendtufusion.build_flags}
|
||||
monitor_filters =
|
||||
esp32_exception_decoder, colorize
|
||||
|
||||
[env:opendtufusion-16MB-de]
|
||||
platform = espressif32@6.7.0
|
||||
platform = espressif32@6.9.0
|
||||
board = esp32-s3-devkitc-1
|
||||
upload_protocol = esp-builtin
|
||||
lib_deps = ${env:esp32-wroom32-minimal.lib_deps}
|
||||
build_flags = ${env:opendtufusion-16MB.build_flags}
|
||||
-DLANG_DE
|
||||
monitor_filters =
|
||||
|
|
|
@ -195,7 +195,7 @@ class Display {
|
|||
}
|
||||
#if defined(ESP32)
|
||||
else if (DISP_TYPE_T10_EPAPER == mCfg->type) {
|
||||
mEpaper.loop((totalPower), totalYieldDay, totalYieldTotal, nrprod);
|
||||
mEpaper.loop((totalPower), totalYieldDay, totalYieldTotal, nrprod, mApp->getIp(), mApp->isNetworkConnected());
|
||||
mRefreshCycle++;
|
||||
|
||||
if (mRefreshCycle > 2880) { // 15 * 2280 = 44300s = 12h
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "Display_ePaper.h"
|
||||
|
||||
#if defined(ESP32)
|
||||
#include <WiFi.h>
|
||||
#include "../../utils/helper.h"
|
||||
#include "imagedata.h"
|
||||
#include "defines.h"
|
||||
|
@ -13,7 +12,9 @@ static const uint32_t spiClk = 4000000; // 4 MHz
|
|||
SPIClass hspi(HSPI);
|
||||
#endif
|
||||
|
||||
DisplayEPaper::DisplayEPaper() {
|
||||
DisplayEPaper::DisplayEPaper()
|
||||
: mNetworkConnected {false}
|
||||
{
|
||||
mDisplayRotation = 2;
|
||||
mHeadFootPadding = 16;
|
||||
memset(_fmtText, 0, EPAPER_MAX_TEXT_LEN);
|
||||
|
@ -122,8 +123,8 @@ void DisplayEPaper::headlineIP() {
|
|||
_display->fillScreen(GxEPD_BLACK);
|
||||
|
||||
do {
|
||||
if ((WiFi.isConnected() == true) && (WiFi.localIP() > 0)) {
|
||||
snprintf(_fmtText, EPAPER_MAX_TEXT_LEN, "%s", WiFi.localIP().toString().c_str());
|
||||
if (mNetworkConnected == true) {
|
||||
snprintf(_fmtText, EPAPER_MAX_TEXT_LEN, "%s", _settedIP.c_str());
|
||||
} else {
|
||||
snprintf(_fmtText, EPAPER_MAX_TEXT_LEN, STR_NO_WIFI);
|
||||
}
|
||||
|
@ -289,14 +290,15 @@ void DisplayEPaper::actualPowerPaged(float totalPower, float totalYieldDay, floa
|
|||
} while (_display->nextPage());
|
||||
}
|
||||
//***************************************************************************
|
||||
void DisplayEPaper::loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) {
|
||||
void DisplayEPaper::loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod, String ip, bool networkConnected) {
|
||||
mNetworkConnected = networkConnected;
|
||||
if(RefreshStatus::DONE != mRefreshState)
|
||||
return;
|
||||
|
||||
// check if the IP has changed
|
||||
if (_settedIP != WiFi.localIP().toString()) {
|
||||
if (_settedIP != ip) {
|
||||
// save the new IP and call the Headline Function to adapt the Headline
|
||||
_settedIP = WiFi.localIP().toString();
|
||||
_settedIP = ip;
|
||||
headlineIP();
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class DisplayEPaper {
|
|||
void fullRefresh();
|
||||
void init(uint8_t type, uint8_t _CS, uint8_t _DC, uint8_t _RST, uint8_t _BUSY, uint8_t _SCK, uint8_t _MOSI, uint32_t* utcTs, const char* version);
|
||||
void config(uint8_t rotation, bool enPowerSave);
|
||||
void loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod);
|
||||
void loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod, String ip, bool networkConnected);
|
||||
void refreshLoop();
|
||||
void tickerSecond();
|
||||
|
||||
|
@ -66,6 +66,7 @@ class DisplayEPaper {
|
|||
|
||||
uint8_t mSecondCnt;
|
||||
bool mLogoDisplayed;
|
||||
bool mNetworkConnected;
|
||||
#if defined(SPI_HAL)
|
||||
epdHal hal;
|
||||
#endif
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#define STR_OFFLINE "aus"
|
||||
#define STR_ONLINE "aktiv"
|
||||
#define STR_NO_INVERTER "kein inverter"
|
||||
#define STR_NO_WIFI "WLAN nicht verbunden"
|
||||
#define STR_NO_WIFI "Netzwerk nicht verbunden"
|
||||
#define STR_VERSION "Version"
|
||||
#define STR_ACTIVE_INVERTERS "aktive WR"
|
||||
#define STR_TODAY "heute"
|
||||
|
@ -23,7 +23,7 @@
|
|||
#define STR_OFFLINE "eteint"
|
||||
#define STR_ONLINE "online"
|
||||
#define STR_NO_INVERTER "pas d'onduleur"
|
||||
#define STR_NO_WIFI "WiFi not connected"
|
||||
#define STR_NO_WIFI "Network not connected"
|
||||
#define STR_VERSION "Version"
|
||||
#define STR_ACTIVE_INVERTERS "active Inv"
|
||||
#define STR_TODAY "today"
|
||||
|
@ -34,7 +34,7 @@
|
|||
#define STR_OFFLINE "offline"
|
||||
#define STR_ONLINE "online"
|
||||
#define STR_NO_INVERTER "no inverter"
|
||||
#define STR_NO_WIFI "WiFi not connected"
|
||||
#define STR_NO_WIFI "Network not connected"
|
||||
#define STR_VERSION "Version"
|
||||
#define STR_ACTIVE_INVERTERS "active Inv"
|
||||
#define STR_TODAY "today"
|
||||
|
|
|
@ -11,8 +11,11 @@
|
|||
#if defined(ENABLE_MQTT)
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#define xSemaphoreTake(a, b) { while(a) { yield(); } a = true; }
|
||||
#define xSemaphoreGive(a) { a = false; }
|
||||
#if !defined(vSemaphoreDelete)
|
||||
#define vSemaphoreDelete(a)
|
||||
#define xSemaphoreTake(a, b) { while(a) { yield(); } a = true; }
|
||||
#define xSemaphoreGive(a) { a = false; }
|
||||
#endif
|
||||
#elif defined(ESP32)
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
@ -158,7 +161,10 @@ class PubMqtt {
|
|||
publish(subtopics[MQTT_UPTIME], mVal.data());
|
||||
publish(subtopics[MQTT_RSSI], String(WiFi.RSSI()).c_str());
|
||||
publish(subtopics[MQTT_FREE_HEAP], String(ESP.getFreeHeap()).c_str());
|
||||
#ifndef ESP32
|
||||
#if defined(ESP32)
|
||||
snprintf(mVal.data(), mVal.size(), "%.2f", ah::readTemperature());
|
||||
publish(subtopics[MQTT_TEMP_SENS_C], mVal.data());
|
||||
#else
|
||||
publish(subtopics[MQTT_HEAP_FRAG], String(ESP.getHeapFragmentation()).c_str());
|
||||
#endif
|
||||
}
|
||||
|
@ -406,26 +412,25 @@ class PubMqtt {
|
|||
bool total = (mDiscovery.lastIvId == MAX_NUM_INVERTERS);
|
||||
|
||||
Inverter<> *iv = mSys->getInverterByPos(mDiscovery.lastIvId);
|
||||
record_t<> *rec = NULL;
|
||||
if (NULL != iv) {
|
||||
record_t<> *rec = nullptr;
|
||||
if (nullptr != iv) {
|
||||
rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||
if(0 == mDiscovery.sub)
|
||||
mDiscovery.foundIvCnt++;
|
||||
mDiscovery.foundIvCnt++;
|
||||
}
|
||||
|
||||
if ((NULL != iv) || total) {
|
||||
if ((nullptr != iv) || total) {
|
||||
if (!total) {
|
||||
doc[F("name")] = iv->config->name;
|
||||
doc[F("ids")] = String(iv->config->serial.u64, HEX);
|
||||
doc[F("mdl")] = iv->config->name;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
doc[F("name")] = node_id;
|
||||
doc[F("ids")] = node_id;
|
||||
doc[F("mdl")] = node_id;
|
||||
}
|
||||
|
||||
doc[F("cu")] = F("http://") + String(WiFi.localIP().toString());
|
||||
doc[F("cu")] = F("http://") + mApp->getIp();
|
||||
doc[F("mf")] = F("Hoymiles");
|
||||
JsonObject deviceObj = doc.as<JsonObject>(); // deviceObj is only pointer!?
|
||||
|
||||
|
@ -438,18 +443,21 @@ class PubMqtt {
|
|||
uniq_id.fill(0);
|
||||
buf.fill(0);
|
||||
const char *devCls, *stateCls;
|
||||
|
||||
if (!total) {
|
||||
if (rec->assign[mDiscovery.sub].ch == CH0)
|
||||
snprintf(name.data(), name.size(), "%s", iv->getFieldName(mDiscovery.sub, rec));
|
||||
else
|
||||
snprintf(name.data(), name.size(), "CH%d_%s", rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec));
|
||||
snprintf(topic.data(), name.size(), "/ch%d/%s", rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec));
|
||||
if (!mCfgMqtt->json)
|
||||
snprintf(topic.data(), name.size(), "/ch%d/%s", rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec));
|
||||
else
|
||||
snprintf(topic.data(), name.size(), "/ch%d", rec->assign[mDiscovery.sub].ch);
|
||||
snprintf(uniq_id.data(), uniq_id.size(), "ch%d_%s", rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec));
|
||||
|
||||
devCls = getFieldDeviceClass(rec->assign[mDiscovery.sub].fieldId);
|
||||
stateCls = getFieldStateClass(rec->assign[mDiscovery.sub].fieldId);
|
||||
}
|
||||
|
||||
else { // total values
|
||||
snprintf(name.data(), name.size(), "Total %s", fields[fldTotal[mDiscovery.sub]]);
|
||||
snprintf(topic.data(), topic.size(), "/%s", fields[fldTotal[mDiscovery.sub]]);
|
||||
|
@ -461,24 +469,43 @@ class PubMqtt {
|
|||
DynamicJsonDocument doc2(512);
|
||||
constexpr static const char* unitTotal[] = {"W", "kWh", "Wh", "W"};
|
||||
doc2[F("name")] = String(name.data());
|
||||
doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/" + ((!total) ? String(iv->config->name) : "total" ) + String(topic.data());
|
||||
|
||||
if (mCfgMqtt->json) {
|
||||
if (total) {
|
||||
doc2[F("val_tpl")] = String("{{ value_json.") + fields[fldTotal[mDiscovery.sub]] + String(" }}");
|
||||
doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/" + "total";
|
||||
}
|
||||
else {
|
||||
doc2[F("val_tpl")] = String("{{ value_json.") + iv->getFieldName(mDiscovery.sub, rec) + String(" }}");
|
||||
doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/" + String(iv->config->name) + String(topic.data());
|
||||
}
|
||||
}
|
||||
else {
|
||||
doc2[F("stat_t")] = String(mCfgMqtt->topic) + "/" + ((!total) ? String(iv->config->name) : "total" ) + String(topic.data());
|
||||
}
|
||||
doc2[F("unit_of_meas")] = ((!total) ? (iv->getUnit(mDiscovery.sub, rec)) : (unitTotal[mDiscovery.sub]));
|
||||
doc2[F("uniq_id")] = ((!total) ? (String(iv->config->serial.u64, HEX)) : (node_id)) + "_" + uniq_id.data();
|
||||
doc2[F("dev")] = deviceObj;
|
||||
|
||||
if (!(String(stateCls) == String("total_increasing")))
|
||||
doc2[F("exp_aft")] = MQTT_INTERVAL + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!?
|
||||
if (devCls != NULL)
|
||||
if (devCls != nullptr)
|
||||
doc2[F("dev_cla")] = String(devCls);
|
||||
if (stateCls != NULL)
|
||||
if (stateCls != nullptr)
|
||||
doc2[F("stat_cla")] = String(stateCls);
|
||||
|
||||
if (!total)
|
||||
snprintf(topic.data(), topic.size(), "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->config->name, rec->assign[mDiscovery.sub].ch, iv->getFieldName(mDiscovery.sub, rec));
|
||||
else // total values
|
||||
snprintf(topic.data(), topic.size(), "%s/sensor/%s/total_%s/config", MQTT_DISCOVERY_PREFIX, node_id.c_str(), fields[fldTotal[mDiscovery.sub]]);
|
||||
|
||||
size_t size = measureJson(doc2) + 1;
|
||||
serializeJson(doc2, buf.data(), size);
|
||||
if(FLD_EVT != rec->assign[mDiscovery.sub].fieldId)
|
||||
|
||||
if(nullptr != rec) {
|
||||
if(FLD_EVT != rec->assign[mDiscovery.sub].fieldId)
|
||||
publish(topic.data(), buf.data(), true, false);
|
||||
} else if(total)
|
||||
publish(topic.data(), buf.data(), true, false);
|
||||
|
||||
if(++mDiscovery.sub == ((!total) ? (rec->length) : 4)) {
|
||||
|
@ -656,7 +683,7 @@ class PubMqtt {
|
|||
size_t index;
|
||||
size_t total;
|
||||
|
||||
message_s()
|
||||
message_s()
|
||||
: topic { nullptr }
|
||||
, payload { nullptr }
|
||||
, len { 0 }
|
||||
|
|
|
@ -56,7 +56,8 @@ enum {
|
|||
MQTT_STATUS,
|
||||
MQTT_LWT_ONLINE,
|
||||
MQTT_LWT_OFFLINE,
|
||||
MQTT_ACK_PWR_LMT
|
||||
MQTT_ACK_PWR_LMT,
|
||||
MQTT_TEMP_SENS_C
|
||||
};
|
||||
|
||||
const char* const subtopics[] PROGMEM = {
|
||||
|
@ -76,7 +77,8 @@ const char* const subtopics[] PROGMEM = {
|
|||
"status",
|
||||
"connected",
|
||||
"not_connected",
|
||||
"ack_pwr_limit"
|
||||
"ack_pwr_limit",
|
||||
"cpu_temp"
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// 2023 Ahoy, https://github.com/lumpapu/ahoy
|
||||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "helper.h"
|
||||
#include "dbg.h"
|
||||
#include "../plugins/plugin_lang.h"
|
||||
|
@ -142,4 +142,24 @@ namespace ah {
|
|||
}
|
||||
DBGPRINTLN("");
|
||||
}
|
||||
|
||||
float readTemperature() {
|
||||
/*// ADC1 channel 0 is GPIO36
|
||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_0);
|
||||
int adc_reading = adc1_get_raw(ADC1_CHANNEL_0);
|
||||
// Convert the raw ADC reading to a voltage in mV
|
||||
esp_adc_cal_characteristics_t characteristics;
|
||||
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_0, ADC_WIDTH_BIT_12, 1100, &characteristics);
|
||||
uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, &characteristics);
|
||||
// Convert the voltage to a temperature in Celsius
|
||||
// This formula is an approximation and might need to be calibrated for your specific use case.
|
||||
float temperature = (voltage - 500) / 10.0;*/
|
||||
|
||||
#if defined(ESP32)
|
||||
return temperatureRead();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ namespace ah {
|
|||
String getTimeStrMs(uint64_t t);
|
||||
uint64_t Serial2u64(const char *val);
|
||||
void dumpBuf(uint8_t buf[], uint8_t len, uint8_t firstRepl = 0, uint8_t lastRepl = 0);
|
||||
|
||||
float readTemperature();
|
||||
}
|
||||
|
||||
#endif /*__HELPER_H__*/
|
||||
|
|
|
@ -73,25 +73,32 @@ namespace ah {
|
|||
|
||||
}
|
||||
|
||||
void once(scdCb c, uint32_t timeout, const char *name) { addTicker(c, timeout, 0, false, name); }
|
||||
void onceAt(scdCb c, uint32_t timestamp, const char *name) { addTicker(c, timestamp, 0, true, name); }
|
||||
uint8_t every(scdCb c, uint32_t interval, const char *name){ return addTicker(c, interval, interval, false, name); }
|
||||
uint8_t once(scdCb c, uint32_t timeout, const char *name) { return addTicker(c, timeout, 0, false, name); }
|
||||
uint8_t onceAt(scdCb c, uint32_t timestamp, const char *name) { return addTicker(c, timestamp, 0, true, name); }
|
||||
uint8_t every(scdCb c, uint32_t interval, const char *name) { return addTicker(c, interval, interval, false, name); }
|
||||
|
||||
void everySec(scdCb c, const char *name) { every(c, SCD_SEC, name); }
|
||||
void everyMin(scdCb c, const char *name) { every(c, SCD_MIN, name); }
|
||||
void everyHour(scdCb c, const char *name) { every(c, SCD_HOUR, name); }
|
||||
void every12h(scdCb c, const char *name) { every(c, SCD_12H, name); }
|
||||
void everyDay(scdCb c, const char *name) { every(c, SCD_DAY, name); }
|
||||
uint8_t everySec(scdCb c, const char *name) { return every(c, SCD_SEC, name); }
|
||||
uint8_t everyMin(scdCb c, const char *name) { return every(c, SCD_MIN, name); }
|
||||
uint8_t everyHour(scdCb c, const char *name) { return every(c, SCD_HOUR, name); }
|
||||
uint8_t every12h(scdCb c, const char *name) { return every(c, SCD_12H, name); }
|
||||
uint8_t everyDay(scdCb c, const char *name) { return every(c, SCD_DAY, name); }
|
||||
|
||||
virtual void setTimestamp(uint32_t ts) {
|
||||
mTimestamp = ts;
|
||||
}
|
||||
|
||||
bool resetEveryById(uint8_t id) {
|
||||
if (mTickerInUse[id] == false)
|
||||
return false;
|
||||
mTicker[id].timeout = mTicker[id].reload;
|
||||
return true;
|
||||
bool resetTickerByName(const char* name) {
|
||||
for (uint8_t id = 0; id < MAX_NUM_TICKER; id++) {
|
||||
if (mTickerInUse[id]) {
|
||||
if(strncmp(name, mTicker[id].name, strlen(name)) == 0) {
|
||||
mTicker[id].timeout = mTicker[id].reload;
|
||||
mTickerInUse[id] = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t getUptime(void) {
|
||||
|
|
|
@ -7,11 +7,6 @@
|
|||
#define __WEB_API_H__
|
||||
|
||||
#include "../utils/dbg.h"
|
||||
#ifdef ESP32
|
||||
#include "AsyncTCP.h"
|
||||
#else
|
||||
#include "ESPAsyncTCP.h"
|
||||
#endif
|
||||
#include "../appInterface.h"
|
||||
#include "../hm/hmSystem.h"
|
||||
#include "../utils/helper.h"
|
||||
|
@ -674,15 +669,15 @@ class RestApi {
|
|||
// find oldest alarm
|
||||
uint8_t offset = 0;
|
||||
uint32_t oldestStart = 0xffffffff;
|
||||
for(uint8_t i = 0; i < 10; i++) {
|
||||
for(uint8_t i = 0; i < Inverter<>::MaxAlarmNum; i++) {
|
||||
if((iv->lastAlarm[i].start != 0) && (iv->lastAlarm[i].start < oldestStart)) {
|
||||
offset = i;
|
||||
oldestStart = iv->lastAlarm[i].start;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < 10; i++) {
|
||||
uint8_t pos = (i + offset) % 10;
|
||||
for(uint8_t i = 0; i < Inverter<>::MaxAlarmNum; i++) {
|
||||
uint8_t pos = (i + offset) % Inverter<>::MaxAlarmNum;
|
||||
alarm[pos][F("code")] = iv->lastAlarm[pos].code;
|
||||
alarm[pos][F("str")] = iv->getAlarmStr(iv->lastAlarm[pos].code);
|
||||
alarm[pos][F("start")] = iv->lastAlarm[pos].start;
|
||||
|
@ -822,7 +817,9 @@ class RestApi {
|
|||
void getChipInfo(JsonObject obj) {
|
||||
obj[F("cpu_freq")] = ESP.getCpuFreqMHz();
|
||||
obj[F("sdk")] = ESP.getSdkVersion();
|
||||
|
||||
#if defined(ESP32)
|
||||
obj[F("temp_sensor_c")] = ah::readTemperature();
|
||||
obj[F("revision")] = ESP.getChipRevision();
|
||||
obj[F("model")] = ESP.getChipModel();
|
||||
obj[F("cores")] = ESP.getChipCores();
|
||||
|
@ -1134,6 +1131,11 @@ class RestApi {
|
|||
iv->setDevCommand(jsonIn[F("val")].as<int>());
|
||||
} else if(F("restart_ahoy") == jsonIn[F("cmd")]) {
|
||||
mApp->setRebootFlag();
|
||||
} else if(F("cmt_search") == jsonIn[F("cmd")]) {
|
||||
if(!mApp->cmtSearch(jsonIn[F("id")], jsonIn[F("to_ch")])) {
|
||||
jsonOut[F("error")] = F("ERR_INVERTER_NOT_FOUND");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
jsonOut[F("error")] = F("ERR_UNKNOWN_CMD");
|
||||
return false;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<div class="p-2">Used Libraries</div>
|
||||
</div>
|
||||
<div class="row"><a href="https://github.com/bertmelis/espMqttClient" target="_blank">bertmelis/espMqttClient</a></div>
|
||||
<div class="row"><a href="https://github.com/esphome/ESPAsyncWebServer" target="_blank">esphome/ESPAsyncWebServer</a></div>
|
||||
<div class="row"><a href="https://github.com/mathieucarbou/ESPAsyncWebServer" target="_blank">mathieucarbou/ESPAsyncWebServer</a></div>
|
||||
<div class="row"><a href="https://github.com/bblanchon/ArduinoJson" target="_blank">bblanchon/ArduinoJson</a></div>
|
||||
<div class="row"><a href="https://github.com/nrf24/RF24" target="_blank">nrf24/RF24</a></div>
|
||||
<div class="row"><a href="https://github.com/paulstoffregen/Time" target="_blank">paulstoffregen/Time</a></div>
|
||||
|
|
|
@ -152,7 +152,7 @@
|
|||
text = "{#INVERTER} #";
|
||||
p.append(
|
||||
svg(icon, 30, 30, "icon " + cl),
|
||||
span(text + i["id"] + ": " + i["name"] + " {#IS} " + avail),
|
||||
span(text + i["id"] + ": " + i["name"] + " " + avail),
|
||||
br()
|
||||
);
|
||||
|
||||
|
|
|
@ -736,7 +736,7 @@
|
|||
lines.push(ml("tr", {}, [
|
||||
ml("th", {style: "width: 10%; text-align: center;"}, ""),
|
||||
ml("th", {}, "Name"),
|
||||
ml("th", {}, "Serial"),
|
||||
ml("th", {class: "d-none d-sm-cell"}, "Serial"),
|
||||
ml("th", {style: "width: 10%; text-align: center;"}, "{#INV_EDIT}"),
|
||||
ml("th", {style: "width: 10%; text-align: center;"}, "{#INV_DELETE}")
|
||||
]));
|
||||
|
@ -745,7 +745,7 @@
|
|||
lines.push(ml("tr", {}, [
|
||||
ml("td", {}, badge(obj.inverter[i].enabled, (obj.inverter[i].enabled) ? "{#ENABLED}" : "{#DISABLED}")),
|
||||
ml("td", {}, obj.inverter[i].name),
|
||||
ml("td", {}, String(obj.inverter[i].serial)),
|
||||
ml("td", {class: "d-none d-sm-cell"}, String(obj.inverter[i].serial)),
|
||||
ml("td", {style: "text-align: center;", onclick: function() {ivModal(obj.inverter[i]);}}, svg(iconGear, 25, 25, "icon icon-fg pointer")),
|
||||
ml("td", {style: "text-align: center; ", onclick: function() {ivDel(obj.inverter[i]);}}, svg(iconDel, 25, 25, "icon icon-fg pointer"))
|
||||
]));
|
||||
|
@ -817,7 +817,8 @@
|
|||
ml("input", {type: "hidden", name: "isnrf"}, null),
|
||||
ml("div", {id: "setcmt"}, [
|
||||
divRow("{#INV_FREQUENCY}", sel("freq", esp32cmtFreq, obj.freq)),
|
||||
divRow("{#INV_POWER_LEVEL}", sel("cmtpa", esp32cmtPa, obj.pa))
|
||||
divRow("{#INV_POWER_LEVEL}", sel("cmtpa", esp32cmtPa, obj.pa)),
|
||||
divRow("{#INV_SEARCH}", ml("input", {type: "button", value: "{#BTN_SEARCH}", class: "btn", onclick: function() { cmtSearch(); }}, null))
|
||||
]),
|
||||
ml("div", {id: "setnrf"},
|
||||
divRow("{#INV_POWER_LEVEL}", sel("nrfpa", nrfPa, obj.pa))
|
||||
|
@ -902,6 +903,16 @@
|
|||
getAjax("/api/setup", cb, "POST", JSON.stringify(o));
|
||||
}
|
||||
|
||||
function cmtSearch() {
|
||||
var o = {}
|
||||
o.cmd = "cmt_search"
|
||||
o.token = "*"
|
||||
o.id = obj.id
|
||||
o.to_ch = document.getElementsByName("freq")[0].value;
|
||||
|
||||
getAjax("/api/ctrl", cb, "POST", JSON.stringify(o));
|
||||
}
|
||||
|
||||
function convHerf(sn) {
|
||||
let sn_int = 0n;
|
||||
const CHARS = "0123456789ABCDEFGHJKLMNPRSTUVWXY";
|
||||
|
|
|
@ -339,6 +339,7 @@ p {
|
|||
.fs-sm-8 { font-size: 1rem; }
|
||||
|
||||
.d-sm-block { display: block !important;}
|
||||
.d-sm-cell { display: table-cell !important;}
|
||||
.d-sm-none { display: none !important; }
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,9 @@
|
|||
tr("Version", obj.generic.version + " - " + obj.generic.build),
|
||||
tr("Chip", "CPU: " + obj.chip.cpu_freq + "MHz, " + obj.chip.cores + " Core(s)"),
|
||||
tr("Chip Model", obj.chip.model)
|
||||
/*IF_ESP32*/
|
||||
,tr("Chip temp.", Math.round(obj.chip.temp_sensor_c * 10) / 10 + "°C")
|
||||
/*ENDIF_ESP32*/
|
||||
]
|
||||
|
||||
document.getElementById("info").append(
|
||||
|
@ -62,8 +65,8 @@
|
|||
function irqBadge(state) {
|
||||
switch(state) {
|
||||
case 0: return badge(false, "unknown", "warning"); break;
|
||||
case 1: return badge(true, "true"); break;
|
||||
default: return badge(false, "false"); break;
|
||||
case 1: return badge(true, "{#TRUE}"); break;
|
||||
default: return badge(false, "{#FALSE}"); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,13 +128,13 @@
|
|||
function parseMqtt(obj) {
|
||||
if(obj.enabled) {
|
||||
lines = [
|
||||
tr("{#CONNECTED}", badge(obj.connected, ((obj.connected) ? "true" : "false"))),
|
||||
tr("{#CONNECTED}", badge(obj.connected, ((obj.connected) ? "{#TRUE}" : "{#FALSE}"))),
|
||||
tr("#TX", obj.tx_cnt),
|
||||
tr("#RX", obj.rx_cnt)
|
||||
]
|
||||
|
||||
} else
|
||||
lines = tr("enabled", badge(false, "false"));
|
||||
lines = tr("{#ENABLED}", badge(false, "{#FALSE}"));
|
||||
|
||||
document.getElementById("info").append(
|
||||
headline("MqTT"),
|
||||
|
@ -161,7 +164,7 @@
|
|||
function parseIndex(obj) {
|
||||
if(obj.ts_sunrise > 0) {
|
||||
document.getElementById("info").append(
|
||||
headline("Sun"),
|
||||
headline("{#SUN}"),
|
||||
ml("table", {class: "table"},
|
||||
ml("tbody", {}, [
|
||||
tr("{#SUNRISE}", new Date(obj.ts_sunrise * 1000).toLocaleString('de-DE')),
|
||||
|
|
|
@ -50,7 +50,9 @@
|
|||
}
|
||||
|
||||
function hide() {
|
||||
var bin = document.getElementsByName("update")[0].value.slice(-env.length-4, -4)
|
||||
let fw = document.getElementsByName("update")[0].value
|
||||
var bin = fw.slice(-env.length-4, -4)
|
||||
let ver = fw.split("_")[2].split(".")
|
||||
if (bin !== env) {
|
||||
var html = ml("div", {class: "row"}, [
|
||||
ml("div", {class: "row my-3"}, "{#WARN_DIFF_ENV}"),
|
||||
|
@ -60,8 +62,20 @@
|
|||
])
|
||||
])
|
||||
modal("{#UPDATE_MODAL}", html)
|
||||
} else
|
||||
start()
|
||||
} else {
|
||||
if(ver[1] != "9")
|
||||
start()
|
||||
else {
|
||||
var html = ml("div", {class: "row"}, [
|
||||
ml("div", {class: "row my-3"}, "{#ERROR_UPGRADE_NOT_POSSIBLE}"),
|
||||
ml("div", {class: "row"}, [
|
||||
ml("div", {class: "col-6"}, ml("input", {type: "button", class: "btn", value: "{#CANCEL}", onclick: function() { modalClose(); }}, null))
|
||||
])
|
||||
])
|
||||
modal("{#UPDATE_MODAL}", html)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function start() {
|
||||
|
|
|
@ -208,8 +208,11 @@
|
|||
if(obj.rssi > -127) {
|
||||
if(obj.generation < 2)
|
||||
ageInfo += " (RSSI: " + ((obj.rssi == -64) ? ">=" : "<") + " -64 dBm)";
|
||||
else
|
||||
else {
|
||||
if(obj.rssi == 0)
|
||||
obj.rssi = "--";
|
||||
ageInfo += " (RSSI: " + obj.rssi + " dBm)";
|
||||
}
|
||||
}
|
||||
|
||||
return ml("div", {class: "mb-5"}, [
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
{
|
||||
"token": "NAV_WIZARD",
|
||||
"en": "Setup Wizard",
|
||||
"de": "Einrichtungsassitent"
|
||||
"de": "Einrichtungsassistent"
|
||||
},
|
||||
{
|
||||
"token": "NAV_LIVE",
|
||||
|
@ -236,7 +236,7 @@
|
|||
{
|
||||
"token": "LOG_PRINT_TRACES",
|
||||
"en": "Print whole traces in Log",
|
||||
"de": "alle Informationen in Log schreiben"
|
||||
"de": "alle Informationen ins Log schreiben"
|
||||
},
|
||||
{
|
||||
"token": "LOG_TO_MQTT",
|
||||
|
@ -698,6 +698,16 @@
|
|||
"en": "Pause communication during night (lat. and lon. need to be set)",
|
||||
"de": "Kommunikation während der Nacht pausieren (Breiten- und Längengrad müssen gesetzt sein"
|
||||
},
|
||||
{
|
||||
"token": "INV_SEARCH",
|
||||
"en": "Catch Inverter",
|
||||
"de": "Wechselrichter suchen"
|
||||
},
|
||||
{
|
||||
"token": "BTN_SEARCH",
|
||||
"en": "start",
|
||||
"de": "starten"
|
||||
},
|
||||
{
|
||||
"token": "BTN_SAVE",
|
||||
"en": "save",
|
||||
|
@ -1052,6 +1062,11 @@
|
|||
"token": "COMMUNICATING",
|
||||
"en": "communicating",
|
||||
"de": "kommunizierend"
|
||||
},
|
||||
{
|
||||
"token": "SUN",
|
||||
"en": "Sun",
|
||||
"de": "Sonne"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1240,7 +1255,7 @@
|
|||
},
|
||||
{
|
||||
"token": "PRODUCING",
|
||||
"en": "producing",
|
||||
"en": "is producing",
|
||||
"de": "produziert"
|
||||
},
|
||||
{
|
||||
|
@ -1248,11 +1263,6 @@
|
|||
"en": "Inverter",
|
||||
"de": "Wechselrichter"
|
||||
},
|
||||
{
|
||||
"token": "IS",
|
||||
"en": "is",
|
||||
"de": "ist"
|
||||
},
|
||||
{
|
||||
"token": "LAST_SUCCESS",
|
||||
"en": "last successful transmission",
|
||||
|
@ -1323,6 +1333,11 @@
|
|||
"en": "your environment may not match the update file!",
|
||||
"de": "Die ausgewählte Firmware passt u.U. nicht zum Chipsatz!"
|
||||
},
|
||||
{
|
||||
"token": "ERROR_UPGRADE_NOT_POSSIBLE",
|
||||
"en": "OTA updade to version 0.9.x not possible, partition layout changed",
|
||||
"de": "Aktualisierung auf Version 0.9.x nicht per Update möglich (Partition Layout geändert), bitte per Websinstaller neu installieren"
|
||||
},
|
||||
{
|
||||
"token": "CONTIUE",
|
||||
"en": "continue",
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
|
||||
#include "../utils/dbg.h"
|
||||
#ifdef ESP32
|
||||
#include "AsyncTCP.h"
|
||||
#include "Update.h"
|
||||
#else
|
||||
#include "ESPAsyncTCP.h"
|
||||
#endif
|
||||
#include "../appInterface.h"
|
||||
#include "../hm/hmSystem.h"
|
||||
|
@ -219,33 +216,38 @@ class Web {
|
|||
}
|
||||
|
||||
private:
|
||||
inline void checkRedirect(AsyncWebServerRequest *request) {
|
||||
if ((mConfig->sys.protectionMask & PROT_MASK_INDEX) != PROT_MASK_INDEX)
|
||||
request->redirect(F("/index"));
|
||||
else if ((mConfig->sys.protectionMask & PROT_MASK_LIVE) != PROT_MASK_LIVE)
|
||||
request->redirect(F("/live"));
|
||||
else if ((mConfig->sys.protectionMask & PROT_MASK_HISTORY) != PROT_MASK_HISTORY)
|
||||
request->redirect(F("/history"));
|
||||
else if ((mConfig->sys.protectionMask & PROT_MASK_SERIAL) != PROT_MASK_SERIAL)
|
||||
request->redirect(F("/serial"));
|
||||
else if ((mConfig->sys.protectionMask & PROT_MASK_SYSTEM) != PROT_MASK_SYSTEM)
|
||||
request->redirect(F("/system"));
|
||||
else
|
||||
request->redirect(F("/login"));
|
||||
}
|
||||
|
||||
void checkProtection(AsyncWebServerRequest *request) {
|
||||
bool checkProtection(AsyncWebServerRequest *request) {
|
||||
if(mApp->isProtected(request->client()->remoteIP().toString().c_str(), "", true)) {
|
||||
checkRedirect(request);
|
||||
return;
|
||||
if ((mConfig->sys.protectionMask & PROT_MASK_INDEX) != PROT_MASK_INDEX) {
|
||||
request->redirect(F("/index"));
|
||||
return true;
|
||||
} else if ((mConfig->sys.protectionMask & PROT_MASK_LIVE) != PROT_MASK_LIVE) {
|
||||
request->redirect(F("/live"));
|
||||
return true;
|
||||
} else if ((mConfig->sys.protectionMask & PROT_MASK_HISTORY) != PROT_MASK_HISTORY) {
|
||||
request->redirect(F("/history"));
|
||||
return true;
|
||||
} else if ((mConfig->sys.protectionMask & PROT_MASK_SERIAL) != PROT_MASK_SERIAL) {
|
||||
request->redirect(F("/serial"));
|
||||
return true;
|
||||
} else if ((mConfig->sys.protectionMask & PROT_MASK_SYSTEM) != PROT_MASK_SYSTEM) {
|
||||
request->redirect(F("/system"));
|
||||
return true;
|
||||
} else {
|
||||
request->redirect(F("/login"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void getPage(AsyncWebServerRequest *request, uint16_t mask, const uint8_t *zippedHtml, uint32_t len) {
|
||||
if (CHECK_MASK(mConfig->sys.protectionMask, mask))
|
||||
checkProtection(request);
|
||||
if(checkProtection(request))
|
||||
return;
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), zippedHtml, len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, F("text/html; charset=UTF-8"), zippedHtml, len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
response->addHeader(F("content-type"), "text/html; charset=UTF-8");
|
||||
if(request->hasParam("v"))
|
||||
|
@ -339,11 +341,12 @@ class Web {
|
|||
if (request->args() > 0) {
|
||||
if (String(request->arg("pwd")) == String(mConfig->sys.adminPwd)) {
|
||||
mApp->unlock(request->client()->remoteIP().toString().c_str(), true);
|
||||
request->redirect("/");
|
||||
request->redirect("/index");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), login_html, login_html_len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, F("text/html; charset=UTF-8"), login_html, login_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
request->send(response);
|
||||
}
|
||||
|
@ -354,7 +357,7 @@ class Web {
|
|||
checkProtection(request);
|
||||
mApp->lock(true);
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), system_html, system_html_len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, F("text/html; charset=UTF-8"), system_html, system_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
request->send(response);
|
||||
}
|
||||
|
@ -363,9 +366,9 @@ class Web {
|
|||
DPRINTLN(DBG_VERBOSE, F("onColor"));
|
||||
AsyncWebServerResponse *response;
|
||||
if (mConfig->sys.darkMode)
|
||||
response = request->beginResponse_P(200, F("text/css"), colorDark_css, colorDark_css_len);
|
||||
response = beginResponse(request, 200, F("text/css"), colorDark_css, colorDark_css_len);
|
||||
else
|
||||
response = request->beginResponse_P(200, F("text/css"), colorBright_css, colorBright_css_len);
|
||||
response = beginResponse(request, 200, F("text/css"), colorBright_css, colorBright_css_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
if(request->hasParam("v")) {
|
||||
response->addHeader(F("Cache-Control"), F("max-age=604800"));
|
||||
|
@ -375,7 +378,7 @@ class Web {
|
|||
|
||||
void onCss(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onCss"));
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/css"), style_css, style_css_len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, F("text/css"), style_css, style_css_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
if(request->hasParam("v")) {
|
||||
response->addHeader(F("Cache-Control"), F("max-age=604800"));
|
||||
|
@ -386,7 +389,7 @@ class Web {
|
|||
void onApiJs(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onApiJs"));
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/javascript"), api_js, api_js_len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, F("text/javascript"), api_js, api_js_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
if(request->hasParam("v"))
|
||||
response->addHeader(F("Cache-Control"), F("max-age=604800"));
|
||||
|
@ -396,7 +399,7 @@ class Web {
|
|||
void onGridInfoJson(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onGridInfoJson"));
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("application/json; charset=utf-8"), grid_info_json, grid_info_json_len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, F("application/json; charset=utf-8"), grid_info_json, grid_info_json_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
if(request->hasParam("v"))
|
||||
response->addHeader(F("Cache-Control"), F("max-age=604800"));
|
||||
|
@ -405,7 +408,7 @@ class Web {
|
|||
|
||||
void onFavicon(AsyncWebServerRequest *request) {
|
||||
static const char favicon_type[] PROGMEM = "image/x-icon";
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, favicon_type, favicon_ico, favicon_ico_len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, favicon_type, favicon_ico, favicon_ico_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
request->send(response);
|
||||
}
|
||||
|
@ -418,7 +421,7 @@ class Web {
|
|||
|
||||
void onReboot(AsyncWebServerRequest *request) {
|
||||
mApp->setRebootFlag();
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), system_html, system_html_len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, F("text/html; charset=UTF-8"), system_html, system_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
request->send(response);
|
||||
}
|
||||
|
@ -426,7 +429,7 @@ class Web {
|
|||
void showHtml(AsyncWebServerRequest *request) {
|
||||
checkProtection(request);
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), system_html, system_html_len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, F("text/html; charset=UTF-8"), system_html, system_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
request->send(response);
|
||||
}
|
||||
|
@ -443,7 +446,7 @@ class Web {
|
|||
}
|
||||
#endif
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), wizard_html, wizard_html_len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, F("text/html; charset=UTF-8"), wizard_html, wizard_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
response->addHeader(F("content-type"), "text/html; charset=UTF-8");
|
||||
request->send(response);
|
||||
|
@ -635,7 +638,7 @@ class Web {
|
|||
|
||||
mApp->saveSettings((request->arg("reboot") == "on"));
|
||||
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), save_html, save_html_len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, F("text/html; charset=UTF-8"), save_html, save_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
request->send(response);
|
||||
}
|
||||
|
@ -649,7 +652,7 @@ class Web {
|
|||
}
|
||||
|
||||
void onAbout(AsyncWebServerRequest *request) {
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), about_html, about_html_len);
|
||||
AsyncWebServerResponse *response = beginResponse(request, 200, F("text/html; charset=UTF-8"), about_html, about_html_len);
|
||||
response->addHeader(F("Content-Encoding"), "gzip");
|
||||
response->addHeader(F("content-type"), "text/html; charset=UTF-8");
|
||||
if(request->hasParam("v")) {
|
||||
|
@ -673,6 +676,14 @@ class Web {
|
|||
getPage(request, PROT_MASK_SYSTEM, system_html, system_html_len);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse* beginResponse(AsyncWebServerRequest *request, int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr) {
|
||||
#if defined(ESP32)
|
||||
return request->beginResponse(code, contentType, content, len);
|
||||
#else
|
||||
return request->beginResponse_P(code, contentType, content, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_PROMETHEUS_EP
|
||||
// Note
|
||||
|
|
Loading…
Add table
Reference in a new issue