mirror of
https://github.com/lumapu/ahoy.git
synced 2025-04-29 18:26:21 +02:00
merged SH1106 1.3" Display, thx @dAjaY85
added SH1106 to automatic build added IP address to MQTT (version, device and IP are retained and only transmitted once after boot) #556 added `set_power_limit` acknowledge MQTT publish #553 changed: version, device name are only published via MQTT once after boot added `Login` to menu if admin password is set #554 added `development` to second changelog link in `index.html` #543 added interval for MQTT (as option). With this settings MQTT live data is published in a fixed timing (only if inverter is available) #542, #523 added MQTT `comm_disabled` #529
This commit is contained in:
parent
2de7f25981
commit
712b5af9b9
15 changed files with 107 additions and 31 deletions
2
.github/workflows/compile_development.yml
vendored
2
.github/workflows/compile_development.yml
vendored
|
@ -47,7 +47,7 @@ jobs:
|
|||
run: python convert.py
|
||||
|
||||
- name: Run PlatformIO
|
||||
run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp32-wroom32-release --environment esp32-wroom32-nokia5110 --environment esp32-wroom32-ssd1306
|
||||
run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp8266-sh1106 --environment esp32-wroom32-release --environment esp32-wroom32-nokia5110 --environment esp32-wroom32-ssd1306 --environment esp32-wroom32-sh1106
|
||||
|
||||
- name: Rename Binary files
|
||||
id: rename-binary-files
|
||||
|
|
2
.github/workflows/compile_release.yml
vendored
2
.github/workflows/compile_release.yml
vendored
|
@ -51,7 +51,7 @@ jobs:
|
|||
run: python convert.py
|
||||
|
||||
- name: Run PlatformIO
|
||||
run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp32-wroom32-release --environment esp32-wroom32-nokia5110 --environment esp32-wroom32-ssd1306
|
||||
run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp8266-sh1106 --environment esp32-wroom32-release --environment esp32-wroom32-nokia5110 --environment esp32-wroom32-ssd1306 --environment esp32-wroom32-sh1106
|
||||
|
||||
- name: Rename Binary files
|
||||
id: rename-binary-files
|
||||
|
|
|
@ -29,6 +29,7 @@ The AhoyDTU will publish on the following topics
|
|||
| `uptime` | 73630 | uptime in seconds | false |
|
||||
| `version` | 0.5.61 | current installed verison of AhoyDTU | true |
|
||||
| `wifi_rssi` | -75 | WiFi signal strength | false |
|
||||
| `ip_addr` | 192.168.178.25 | WiFi Station IP Address | true |
|
||||
|
||||
| status code | Remarks |
|
||||
|---|---|
|
||||
|
@ -43,6 +44,7 @@ The AhoyDTU will publish on the following topics
|
|||
|---|---|---|---|
|
||||
| `available` | 2 | see table below | true |
|
||||
| `last_success` | 1672155690 | UTC Timestamp | true |
|
||||
| `ack_pwr_limit` | true | fast information if inverter has accepted power limit | false |
|
||||
|
||||
| status code | Remarks |
|
||||
|---|---|
|
||||
|
|
|
@ -2,6 +2,17 @@
|
|||
|
||||
(starting from release version `0.5.66`)
|
||||
|
||||
## 0.5.69
|
||||
* merged SH1106 1.3" Display, thx @dAjaY85
|
||||
* added SH1106 to automatic build
|
||||
* added IP address to MQTT (version, device and IP are retained and only transmitted once after boot) #556
|
||||
* added `set_power_limit` acknowledge MQTT publish #553
|
||||
* changed: version, device name are only published via MQTT once after boot
|
||||
* added `Login` to menu if admin password is set #554
|
||||
* added `development` to second changelog link in `index.html` #543
|
||||
* added interval for MQTT (as option). With this settings MQTT live data is published in a fixed timing (only if inverter is available) #542, #523
|
||||
* added MQTT `comm_disabled` #529
|
||||
|
||||
## 0.5.68
|
||||
* repaired receive payload
|
||||
* Powerlimit is transfered immediately to inverter
|
||||
|
|
|
@ -51,7 +51,7 @@ void app::setup() {
|
|||
#endif
|
||||
|
||||
mSys->addInverters(&mConfig->inst);
|
||||
mPayload.setup(mSys, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
|
||||
mPayload.setup(this, mSys, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
|
||||
mPayload.enableSerialDebug(mConfig->serial.debug);
|
||||
|
||||
if(!mSys->Radio.isChipConnected())
|
||||
|
@ -162,14 +162,17 @@ void app::tickIVCommunication(void) {
|
|||
nxtTrig = mSunrise - mConfig->sun.offsetSec;
|
||||
} else {
|
||||
if (mTimestamp > (mSunset + mConfig->sun.offsetSec)) { // current time is past communication stop, nothing to do. Next update will be done at midnight by tickCalcSunrise
|
||||
return;
|
||||
nxtTrig = 0;
|
||||
} else { // current time lies within communication start/stop time, set next trigger to communication stop
|
||||
mIVCommunicationOn = true;
|
||||
nxtTrig = mSunset + mConfig->sun.offsetSec;
|
||||
}
|
||||
}
|
||||
onceAt(std::bind(&app::tickIVCommunication, this), nxtTrig);
|
||||
if (nxtTrig != 0)
|
||||
onceAt(std::bind(&app::tickIVCommunication, this), nxtTrig);
|
||||
}
|
||||
if (mConfig->mqtt.broker[0] > 0)
|
||||
mMqtt.tickerComm(mIVCommunicationOn);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -122,6 +122,10 @@ class app : public IApp, public ah::Scheduler {
|
|||
once(std::bind(&PubMqttType::sendDiscoveryConfig, &mMqtt), 1);
|
||||
}
|
||||
|
||||
void setMqttPowerLimitAck(Inverter<> *iv) {
|
||||
mMqtt.setPowerLimitAck(iv);
|
||||
}
|
||||
|
||||
void ivSendHighPrio(Inverter<> *iv) {
|
||||
mPayload.ivSendHighPrio(iv);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ class IApp {
|
|||
virtual bool getRebootRequestState() = 0;
|
||||
virtual bool getSettingsValid() = 0;
|
||||
virtual void setMqttDiscoveryFlag() = 0;
|
||||
virtual void setMqttPowerLimitAck(Inverter<> *iv) = 0;
|
||||
|
||||
virtual void ivSendHighPrio(Inverter<> *iv) = 0;
|
||||
|
||||
|
|
|
@ -96,6 +96,7 @@ typedef struct {
|
|||
char user[MQTT_USER_LEN];
|
||||
char pwd[MQTT_PWD_LEN];
|
||||
char topic[MQTT_TOPIC_LEN];
|
||||
uint16_t interval;
|
||||
} cfgMqtt_t;
|
||||
|
||||
typedef struct {
|
||||
|
@ -297,6 +298,7 @@ class settings {
|
|||
snprintf(mCfg.mqtt.user, MQTT_USER_LEN, "%s", DEF_MQTT_USER);
|
||||
snprintf(mCfg.mqtt.pwd, MQTT_PWD_LEN, "%s", DEF_MQTT_PWD);
|
||||
snprintf(mCfg.mqtt.topic, MQTT_TOPIC_LEN, "%s", DEF_MQTT_TOPIC);
|
||||
mCfg.mqtt.interval = 0; // off
|
||||
|
||||
mCfg.led.led0 = DEF_LED0_PIN;
|
||||
mCfg.led.led1 = DEF_LED1_PIN;
|
||||
|
@ -396,8 +398,10 @@ class settings {
|
|||
obj[F("user")] = mCfg.mqtt.user;
|
||||
obj[F("pwd")] = mCfg.mqtt.pwd;
|
||||
obj[F("topic")] = mCfg.mqtt.topic;
|
||||
obj[F("intvl")] = mCfg.mqtt.interval;
|
||||
} else {
|
||||
mCfg.mqtt.port = obj[F("port")];
|
||||
mCfg.mqtt.port = obj[F("port")];
|
||||
mCfg.mqtt.interval = obj[F("intvl")];
|
||||
snprintf(mCfg.mqtt.broker, MQTT_ADDR_LEN, "%s", obj[F("broker")].as<const char*>());
|
||||
snprintf(mCfg.mqtt.user, MQTT_USER_LEN, "%s", obj[F("user")].as<const char*>());
|
||||
snprintf(mCfg.mqtt.pwd, MQTT_PWD_LEN, "%s", obj[F("pwd")].as<const char*>());
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//-------------------------------------
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 5
|
||||
#define VERSION_PATCH 68
|
||||
#define VERSION_PATCH 69
|
||||
|
||||
//-------------------------------------
|
||||
typedef struct {
|
||||
|
|
|
@ -35,7 +35,8 @@ class Payload : public Handler<payloadListenerType> {
|
|||
public:
|
||||
Payload() : Handler() {}
|
||||
|
||||
void setup(HMSYSTEM *sys, statistics_t *stat, uint8_t maxRetransmits, uint32_t *timestamp) {
|
||||
void setup(IApp *app, HMSYSTEM *sys, statistics_t *stat, uint8_t maxRetransmits, uint32_t *timestamp) {
|
||||
mApp = app;
|
||||
mSys = sys;
|
||||
mStat = stat;
|
||||
mMaxRetrans = maxRetransmits;
|
||||
|
@ -141,7 +142,11 @@ class Payload : public Handler<payloadListenerType> {
|
|||
iv->devControlRequest = false;
|
||||
|
||||
if ((p->packet[12] == ActivePowerContr) && (p->packet[13] == 0x00)) {
|
||||
String msg = (p->packet[10] == 0x00 && p->packet[11] == 0x00) ? "" : "NOT ";
|
||||
String msg = "";
|
||||
if((p->packet[10] == 0x00) && (p->packet[11] == 0x00)) {
|
||||
msg = "NOT ";
|
||||
mApp->setMqttPowerLimitAck(iv);
|
||||
}
|
||||
DPRINTLN(DBG_INFO, F("Inverter ") + String(iv->id) + F(" has ") + msg + F("accepted power limit set point ") + String(iv->powerLimit[0]) + F(" with PowerLimitControl ") + String(iv->powerLimit[1]));
|
||||
}
|
||||
iv->devControlCmd = Init;
|
||||
|
@ -272,6 +277,7 @@ class Payload : public Handler<payloadListenerType> {
|
|||
}
|
||||
|
||||
private:
|
||||
IApp *mApp;
|
||||
HMSYSTEM *mSys;
|
||||
statistics_t *mStat;
|
||||
uint8_t mMaxRetrans;
|
||||
|
|
|
@ -41,11 +41,13 @@ class PubMqtt {
|
|||
~PubMqtt() { }
|
||||
|
||||
void setup(cfgMqtt_t *cfg_mqtt, const char *devName, const char *version, HMSYSTEM *sys, uint32_t *utcTs) {
|
||||
mCfgMqtt = cfg_mqtt;
|
||||
mDevName = devName;
|
||||
mVersion = version;
|
||||
mSys = sys;
|
||||
mUtcTimestamp = utcTs;
|
||||
mCfgMqtt = cfg_mqtt;
|
||||
mDevName = devName;
|
||||
mVersion = version;
|
||||
mSys = sys;
|
||||
mUtcTimestamp = utcTs;
|
||||
mExeOnce = true;
|
||||
mIntervalTimeout = 1;
|
||||
|
||||
snprintf(mLwtTopic, MQTT_TOPIC_LEN + 5, "%s/mqtt", mCfgMqtt->topic);
|
||||
|
||||
|
@ -73,7 +75,16 @@ class PubMqtt {
|
|||
}
|
||||
|
||||
void tickerSecond() {
|
||||
sendIvData();
|
||||
if(0 == mCfgMqtt->interval) // no fixed interval, publish once new data were received (from inverter)
|
||||
sendIvData();
|
||||
else { // send mqtt data in a fixed interval
|
||||
if(--mIntervalTimeout == 0) {
|
||||
mIntervalTimeout = mCfgMqtt->interval;
|
||||
mSendList.push(RealTimeRunData_Debug);
|
||||
sendIvData();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void tickerMinute() {
|
||||
|
@ -98,9 +109,16 @@ class PubMqtt {
|
|||
publish("dis_night_comm", ((disNightCom) ? "true" : "false"), true);
|
||||
}
|
||||
|
||||
void tickerComm(bool disabled) {
|
||||
publish("comm_disabled", ((disabled) ? "true" : "false"), true);
|
||||
publish("comm_dis_ts", String(*mUtcTimestamp).c_str(), true);
|
||||
}
|
||||
|
||||
void payloadEventListener(uint8_t cmd) {
|
||||
if(mClient.connected()) // prevent overflow if MQTT broker is not reachable but set
|
||||
mSendList.push(cmd);
|
||||
if(mClient.connected()) { // prevent overflow if MQTT broker is not reachable but set
|
||||
if((0 == mCfgMqtt->interval) || (RealTimeRunData_Debug != cmd)) // no interval or no live data
|
||||
mSendList.push(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void publish(const char *subTopic, const char *payload, bool retained = false, bool addTopic = true) {
|
||||
|
@ -188,6 +206,15 @@ class PubMqtt {
|
|||
}
|
||||
}
|
||||
|
||||
void setPowerLimitAck(Inverter<> *iv) {
|
||||
if (NULL != iv) {
|
||||
char topic[7 + MQTT_TOPIC_LEN];
|
||||
|
||||
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ack_pwr_limit", iv->config->name);
|
||||
publish(topic, "true", true);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
#if defined(ESP8266)
|
||||
void onWifiConnect(const WiFiEventStationModeGotIP& event) {
|
||||
|
@ -223,8 +250,12 @@ class PubMqtt {
|
|||
DPRINTLN(DBG_INFO, F("MQTT connected"));
|
||||
mEnReconnect = true;
|
||||
|
||||
publish("version", mVersion, true);
|
||||
publish("device", mDevName, true);
|
||||
if(mExeOnce) {
|
||||
publish("version", mVersion, true);
|
||||
publish("device", mDevName, true);
|
||||
publish("ip_addr", WiFi.localIP().toString().c_str(), true);
|
||||
mExeOnce = false;
|
||||
}
|
||||
tickerMinute();
|
||||
publish(mLwtTopic, mLwtOnline, true, false);
|
||||
|
||||
|
@ -494,6 +525,8 @@ class PubMqtt {
|
|||
subscriptionCb mSubscriptionCb;
|
||||
bool mIvAvail; // shows if at least one inverter is available
|
||||
uint8_t mLastIvState[MAX_NUM_INVERTERS];
|
||||
bool mExeOnce;
|
||||
uint16_t mIntervalTimeout;
|
||||
|
||||
// last will topic and payload must be available trough lifetime of 'espMqttClient'
|
||||
char mLwtTopic[MQTT_TOPIC_LEN+5];
|
||||
|
|
|
@ -276,11 +276,12 @@ class RestApi {
|
|||
}
|
||||
|
||||
void getMqtt(JsonObject obj) {
|
||||
obj[F("broker")] = String(mConfig->mqtt.broker);
|
||||
obj[F("port")] = String(mConfig->mqtt.port);
|
||||
obj[F("user")] = String(mConfig->mqtt.user);
|
||||
obj[F("pwd")] = (strlen(mConfig->mqtt.pwd) > 0) ? F("{PWD}") : String("");
|
||||
obj[F("topic")] = String(mConfig->mqtt.topic);
|
||||
obj[F("broker")] = String(mConfig->mqtt.broker);
|
||||
obj[F("port")] = String(mConfig->mqtt.port);
|
||||
obj[F("user")] = String(mConfig->mqtt.user);
|
||||
obj[F("pwd")] = (strlen(mConfig->mqtt.pwd) > 0) ? F("{PWD}") : String("");
|
||||
obj[F("topic")] = String(mConfig->mqtt.topic);
|
||||
obj[F("interval")] = String(mConfig->mqtt.interval);
|
||||
}
|
||||
|
||||
void getNtp(JsonObject obj) {
|
||||
|
@ -357,10 +358,15 @@ class RestApi {
|
|||
obj[F("name")][i] = "Documentation";
|
||||
obj[F("link")][i] = "https://ahoydtu.de";
|
||||
obj[F("trgt")][i++] = "_blank";
|
||||
if((strlen(mConfig->sys.adminPwd) > 0) && !mApp->getProtection()) {
|
||||
if(strlen(mConfig->sys.adminPwd) > 0) {
|
||||
obj[F("name")][i++] = "-";
|
||||
obj[F("name")][i] = "Logout";
|
||||
obj[F("link")][i++] = "/logout";
|
||||
if(mApp->getProtection()) {
|
||||
obj[F("name")][i] = "Login";
|
||||
obj[F("link")][i++] = "/login";
|
||||
} else {
|
||||
obj[F("name")][i] = "Logout";
|
||||
obj[F("link")][i++] = "/logout";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,6 +417,8 @@ class RestApi {
|
|||
JsonArray info = obj.createNestedArray(F("infos"));
|
||||
if(mApp->getMqttIsConnected())
|
||||
info.add(F("MQTT is connected, ") + String(mApp->getMqttTxCnt()) + F(" packets sent, ") + String(mApp->getMqttRxCnt()) + F(" packets received"));
|
||||
if(mConfig->mqtt.interval > 0)
|
||||
info.add(F("MQTT publishes in a fixed interval of ") + String(mConfig->mqtt.interval) + F(" seconds"));
|
||||
}
|
||||
|
||||
void getSetup(JsonObject obj) {
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
<li>Discuss with us on <a href="https://discord.gg/WzhxEY62mB">Discord</a></li>
|
||||
<li>Report <a href="https://github.com/lumapu/ahoy/issues" target="_blank">issues</a></li>
|
||||
<li>Contribute to <a href="https://github.com/lumapu/ahoy/blob/main/User_Manual.md" target="_blank">documentation</a></li>
|
||||
<li><a href="https://nightly.link/lumapu/ahoy/workflows/compile_development/development03/ahoydtu_dev.zip" target="_blank">Download</a> & Test development firmware, <a href="https://github.com/lumapu/ahoy/blob/development03/src/CHANGES.md" target="_blank">Changelog</a></li>
|
||||
<li><a href="https://nightly.link/lumapu/ahoy/workflows/compile_development/development03/ahoydtu_dev.zip" target="_blank">Download</a> & Test development firmware, <a href="https://github.com/lumapu/ahoy/blob/development03/src/CHANGES.md" target="_blank">Development Changelog</a></li>
|
||||
<li>make a <a href="https://paypal.me/lupusch" target="_blank">donation</a></li>
|
||||
</ul>
|
||||
<p class="lic">
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
<input type="button" id="btnAdd" class="btn" value="Add Inverter"/>
|
||||
<p class="subdes">General</p>
|
||||
<label for="invInterval">Interval [s]</label>
|
||||
<input type="text" class="text" name="invInterval"/>
|
||||
<input type="text" class="text" name="invInterval" pattern="[0-9]+" title="Invalid input"/>
|
||||
<label for="invRetry">Max retries per Payload</label>
|
||||
<input type="text" class="text" name="invRetry"/>
|
||||
</fieldset>
|
||||
|
@ -148,6 +148,9 @@
|
|||
<input type="password" class="text" name="mqttPwd"/>
|
||||
<label for="mqttTopic">Topic</label>
|
||||
<input type="text" class="text" name="mqttTopic" pattern="[A-Za-z0-9.\-_\+\/]+" title="Invalid input" />
|
||||
<p class="des">Send Inverter data in a fixed interval, even if there is no change. A value of '0' disables the fixed interval. The data is published once it was successfully received from inverter. (default: 0)</p>
|
||||
<label for="mqttIntvl">Interval [s]</label>
|
||||
<input type="text" class="text" name="mqttInterval" pattern="[0-9]+" title="Invalid input" />
|
||||
<label for="mqttBtn">Discovery Config (homeassistant)</label>
|
||||
<input type="button" name="mqttDiscovery" id="mqttDiscovery" class="btn" value="send" onclick="sendDiscoveryConfig()"/>
|
||||
<span id="apiResultMqtt"></span>
|
||||
|
@ -170,7 +173,7 @@
|
|||
<label for="serDbg">Serial Debug</label>
|
||||
<input type="checkbox" class="cb" name="serDbg"/><br/>
|
||||
<label for="serIntvl">Interval [s]</label>
|
||||
<input type="text" class="text" name="serIntvl"/>
|
||||
<input type="text" class="text" name="serIntvl" pattern="[0-9]+" title="Invalid input"/>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
|
@ -389,7 +392,7 @@
|
|||
}
|
||||
|
||||
function parseMqtt(obj) {
|
||||
for(var i of [["Addr", "broker"], ["Port", "port"], ["User", "user"], ["Pwd", "pwd"], ["Topic", "topic"]])
|
||||
for(var i of [["Addr", "broker"], ["Port", "port"], ["User", "user"], ["Pwd", "pwd"], ["Topic", "topic"], ["Interval", "interval"]])
|
||||
document.getElementsByName("mqtt"+i[0])[0].value = obj[i[1]];
|
||||
}
|
||||
|
||||
|
|
|
@ -494,6 +494,7 @@ class Web {
|
|||
request->arg("mqttPwd").toCharArray(mConfig->mqtt.pwd, MQTT_PWD_LEN);
|
||||
request->arg("mqttTopic").toCharArray(mConfig->mqtt.topic, MQTT_TOPIC_LEN);
|
||||
mConfig->mqtt.port = request->arg("mqttPort").toInt();
|
||||
mConfig->mqtt.interval = request->arg("mqttInterval").toInt();
|
||||
|
||||
// serial console
|
||||
if(request->arg("serIntvl") != "") {
|
||||
|
|
Loading…
Add table
Reference in a new issue