mirror of
https://github.com/lumapu/ahoy.git
synced 2025-07-27 05:07:18 +02:00
refactored mqtt and sun
This commit is contained in:
parent
0a0d90dc92
commit
6cb0b99de3
4 changed files with 180 additions and 174 deletions
152
src/app.cpp
152
src/app.cpp
|
@ -9,8 +9,8 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
#include "utils/sun.h"
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
app::app() {
|
app::app() {
|
||||||
|
@ -37,11 +37,10 @@ void app::setup(uint32_t timeout) {
|
||||||
|
|
||||||
mWifi->setup(timeout, mWifiSettingsValid);
|
mWifi->setup(timeout, mWifiSettingsValid);
|
||||||
|
|
||||||
|
mSys->setup(mConfig.amplifierPower, mConfig.pinIrq, mConfig.pinCe, mConfig.pinCs);
|
||||||
#ifndef AP_ONLY
|
#ifndef AP_ONLY
|
||||||
setupMqtt();
|
setupMqtt();
|
||||||
#endif
|
#endif
|
||||||
mSys->setup(mConfig.amplifierPower, mConfig.pinIrq, mConfig.pinCe, mConfig.pinCs);
|
|
||||||
|
|
||||||
setupLed();
|
setupLed();
|
||||||
|
|
||||||
mWebInst = new web(this, &mSysConfig, &mConfig, &mStat, mVersion);
|
mWebInst = new web(this, &mSysConfig, &mConfig, &mStat, mVersion);
|
||||||
|
@ -85,7 +84,7 @@ void app::loop(void) {
|
||||||
|
|
||||||
if (mFlagSendDiscoveryConfig) {
|
if (mFlagSendDiscoveryConfig) {
|
||||||
mFlagSendDiscoveryConfig = false;
|
mFlagSendDiscoveryConfig = false;
|
||||||
mMqtt.sendMqttDiscoveryConfig(mSys, mConfig.mqtt.topic, mMqttInterval);
|
mMqtt.sendMqttDiscoveryConfig(mConfig.mqtt.topic, mMqttInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
mSys->Radio.loop();
|
mSys->Radio.loop();
|
||||||
|
@ -164,13 +163,13 @@ void app::loop(void) {
|
||||||
if (!mLatestSunTimestamp) { // first call: calculate time zone from longitude to refresh at local midnight
|
if (!mLatestSunTimestamp) { // first call: calculate time zone from longitude to refresh at local midnight
|
||||||
mCalculatedTimezoneOffset = (int8_t)((mConfig.sunLon >= 0 ? mConfig.sunLon + 7.5 : mConfig.sunLon - 7.5) / 15) * 3600;
|
mCalculatedTimezoneOffset = (int8_t)((mConfig.sunLon >= 0 ? mConfig.sunLon + 7.5 : mConfig.sunLon - 7.5) / 15) * 3600;
|
||||||
}
|
}
|
||||||
calculateSunriseSunset();
|
ah::calculateSunriseSunset(mUtcTimestamp, mCalculatedTimezoneOffset, mConfig.sunLat, mConfig.sunLon, &mSunrise, &mSunset);
|
||||||
mLatestSunTimestamp = mUtcTimestamp;
|
mLatestSunTimestamp = mUtcTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((++mMqttTicker >= mMqttInterval) && (mMqttInterval != 0xffff) && mMqttActive) {
|
if ((++mMqttTicker >= mMqttInterval) && (mMqttInterval != 0xffff) && mMqttActive) {
|
||||||
mMqttTicker = 0;
|
mMqttTicker = 0;
|
||||||
mMqtt.sendIvData(mSys, mUtcTimestamp, mMqttSendList);
|
mMqtt.sendIvData(mUtcTimestamp, mMqttSendList);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mConfig.serialShowIv) {
|
if (mConfig.serialShowIv) {
|
||||||
|
@ -405,110 +404,6 @@ void app::processPayload(bool retransmit) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void app::cbMqtt(char *topic, byte *payload, unsigned int length) {
|
|
||||||
// callback handling on subscribed devcontrol topic
|
|
||||||
DPRINTLN(DBG_INFO, F("app::cbMqtt"));
|
|
||||||
// subcribed topics are mTopic + "/devcontrol/#" where # is <inverter_id>/<subcmd in dec>
|
|
||||||
// eg. mypvsolar/devcontrol/1/11 with payload "400" --> inverter 1 active power limit 400 Watt
|
|
||||||
const char *token = strtok(topic, "/");
|
|
||||||
while (token != NULL) {
|
|
||||||
if (strcmp(token, "devcontrol") == 0) {
|
|
||||||
token = strtok(NULL, "/");
|
|
||||||
uint8_t iv_id = std::stoi(token);
|
|
||||||
|
|
||||||
if (iv_id >= 0 && iv_id <= MAX_NUM_INVERTERS) {
|
|
||||||
Inverter<> *iv = this->mSys->getInverterByPos(iv_id);
|
|
||||||
if (NULL != iv) {
|
|
||||||
if (!iv->devControlRequest) { // still pending
|
|
||||||
token = strtok(NULL, "/");
|
|
||||||
|
|
||||||
switch (std::stoi(token)) {
|
|
||||||
// Active Power Control
|
|
||||||
case ActivePowerContr:
|
|
||||||
token = strtok(NULL, "/"); // get ControlMode aka "PowerPF.Desc" in DTU-Pro Code from topic string
|
|
||||||
if (token == NULL) // default via mqtt ommit the LimitControlMode
|
|
||||||
iv->powerLimit[1] = AbsolutNonPersistent;
|
|
||||||
else
|
|
||||||
iv->powerLimit[1] = std::stoi(token);
|
|
||||||
if (length <= 5) { // if (std::stoi((char*)payload) > 0) more error handling powerlimit needed?
|
|
||||||
if (iv->powerLimit[1] >= AbsolutNonPersistent && iv->powerLimit[1] <= RelativPersistent) {
|
|
||||||
iv->devControlCmd = ActivePowerContr;
|
|
||||||
iv->powerLimit[0] = std::stoi(std::string((char *)payload, (unsigned int)length)); // THX to @silversurfer
|
|
||||||
/*if (iv->powerLimit[1] & 0x0001)
|
|
||||||
DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("%"));
|
|
||||||
else
|
|
||||||
DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("W"));*/
|
|
||||||
|
|
||||||
DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + String(iv->powerLimit[1] & 0x0001) ? F("%") : F("W"));
|
|
||||||
}
|
|
||||||
iv->devControlRequest = true;
|
|
||||||
} else {
|
|
||||||
DPRINTLN(DBG_INFO, F("Invalid mqtt payload recevied: ") + String((char *)payload));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Turn On
|
|
||||||
case TurnOn:
|
|
||||||
iv->devControlCmd = TurnOn;
|
|
||||||
DPRINTLN(DBG_INFO, F("Turn on inverter ") + String(iv->id));
|
|
||||||
iv->devControlRequest = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Turn Off
|
|
||||||
case TurnOff:
|
|
||||||
iv->devControlCmd = TurnOff;
|
|
||||||
DPRINTLN(DBG_INFO, F("Turn off inverter ") + String(iv->id));
|
|
||||||
iv->devControlRequest = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Restart
|
|
||||||
case Restart:
|
|
||||||
iv->devControlCmd = Restart;
|
|
||||||
DPRINTLN(DBG_INFO, F("Restart inverter ") + String(iv->id));
|
|
||||||
iv->devControlRequest = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Reactive Power Control
|
|
||||||
case ReactivePowerContr:
|
|
||||||
iv->devControlCmd = ReactivePowerContr;
|
|
||||||
if (true) { // if (std::stoi((char*)payload) > 0) error handling powerlimit needed?
|
|
||||||
iv->devControlCmd = ReactivePowerContr;
|
|
||||||
iv->powerLimit[0] = std::stoi(std::string((char *)payload, (unsigned int)length));
|
|
||||||
iv->powerLimit[1] = 0x0000; // if reactivepower limit is set via external interface --> set it temporay
|
|
||||||
DPRINTLN(DBG_DEBUG, F("Reactivepower limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("W"));
|
|
||||||
iv->devControlRequest = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Set Power Factor
|
|
||||||
case PFSet:
|
|
||||||
// iv->devControlCmd = PFSet;
|
|
||||||
// uint16_t power_factor = std::stoi(strtok(NULL, "/"));
|
|
||||||
DPRINTLN(DBG_INFO, F("Set Power Factor not implemented for inverter ") + String(iv->id));
|
|
||||||
break;
|
|
||||||
|
|
||||||
// CleanState lock & alarm
|
|
||||||
case CleanState_LockAndAlarm:
|
|
||||||
iv->devControlCmd = CleanState_LockAndAlarm;
|
|
||||||
DPRINTLN(DBG_INFO, F("CleanState lock & alarm for inverter ") + String(iv->id));
|
|
||||||
iv->devControlRequest = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
DPRINTLN(DBG_INFO, "Not implemented");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
token = strtok(NULL, "/");
|
|
||||||
}
|
|
||||||
DPRINTLN(DBG_INFO, F("app::cbMqtt finished"));
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
bool app::getWifiApActive(void) {
|
bool app::getWifiApActive(void) {
|
||||||
return mWifi->getApActive();
|
return mWifi->getApActive();
|
||||||
|
@ -685,15 +580,12 @@ void app::setupMqtt(void) {
|
||||||
if (mConfig.mqtt.broker[0] > 0) {
|
if (mConfig.mqtt.broker[0] > 0) {
|
||||||
mMqttActive = true;
|
mMqttActive = true;
|
||||||
if (mMqttInterval < MIN_MQTT_INTERVAL) mMqttInterval = MIN_MQTT_INTERVAL;
|
if (mMqttInterval < MIN_MQTT_INTERVAL) mMqttInterval = MIN_MQTT_INTERVAL;
|
||||||
} else {
|
} else
|
||||||
mMqttInterval = 0xffff;
|
mMqttInterval = 0xffff;
|
||||||
}
|
|
||||||
|
|
||||||
mMqttTicker = 0;
|
mMqttTicker = 0;
|
||||||
if(mMqttActive) {
|
if(mMqttActive)
|
||||||
mMqtt.setup(&mConfig.mqtt, mSysConfig.deviceName);
|
mMqtt.setup(&mConfig.mqtt, mSysConfig.deviceName, mSys);
|
||||||
mMqtt.setCallback(std::bind(&app::cbMqtt, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mMqttActive) {
|
if (mMqttActive) {
|
||||||
mMqtt.sendMsg("version", mVersion);
|
mMqtt.sendMsg("version", mVersion);
|
||||||
|
@ -747,31 +639,3 @@ void app::resetPayload(Inverter<> *iv) {
|
||||||
mPayload[iv->id].requested = false;
|
mPayload[iv->id].requested = false;
|
||||||
mPayload[iv->id].ts = mUtcTimestamp;
|
mPayload[iv->id].ts = mUtcTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void app::calculateSunriseSunset() {
|
|
||||||
// Source: https://en.wikipedia.org/wiki/Sunrise_equation#Complete_calculation_on_Earth
|
|
||||||
|
|
||||||
// Julian day since 1.1.2000 12:00 + correction 69.12s
|
|
||||||
double n_JulianDay = (mUtcTimestamp + mCalculatedTimezoneOffset) / 86400 - 10957.0 + 0.0008;
|
|
||||||
// Mean solar time
|
|
||||||
double J = n_JulianDay - mConfig.sunLon / 360;
|
|
||||||
// Solar mean anomaly
|
|
||||||
double M = fmod((357.5291 + 0.98560028 * J), 360);
|
|
||||||
// Equation of the center
|
|
||||||
double C = 1.9148 * SIN(M) + 0.02 * SIN(2 * M) + 0.0003 * SIN(3 * M);
|
|
||||||
// Ecliptic longitude
|
|
||||||
double lambda = fmod((M + C + 180 + 102.9372), 360);
|
|
||||||
// Solar transit
|
|
||||||
double Jtransit = 2451545.0 + J + 0.0053 * SIN(M) - 0.0069 * SIN(2 * lambda);
|
|
||||||
// Declination of the sun
|
|
||||||
double delta = ASIN(SIN(lambda) * SIN(23.44));
|
|
||||||
// Hour angle
|
|
||||||
double omega = ACOS(SIN(-0.83) - SIN(mConfig.sunLat) * SIN(delta) / COS(mConfig.sunLat) * COS(delta));
|
|
||||||
// Calculate sunrise and sunset
|
|
||||||
double Jrise = Jtransit - omega / 360;
|
|
||||||
double Jset = Jtransit + omega / 360;
|
|
||||||
// Julian sunrise/sunset to UTC unix timestamp (days incl. fraction to seconds + unix offset 1.1.2000 12:00)
|
|
||||||
mSunrise = (Jrise - 2451545.0) * 86400 + 946728000; // OPTIONAL: Add an offset of +-seconds to the end of the line
|
|
||||||
mSunset = (Jset - 2451545.0) * 86400 + 946728000; // OPTIONAL: Add an offset of +-seconds to the end of the line
|
|
||||||
}
|
|
||||||
|
|
|
@ -247,8 +247,6 @@ class app {
|
||||||
DPRINTLN(DBG_VERBOSE, F(" - frag: ") + String(frag));
|
DPRINTLN(DBG_VERBOSE, F(" - frag: ") + String(frag));
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateSunriseSunset(void);
|
|
||||||
|
|
||||||
uint32_t mUptimeSecs;
|
uint32_t mUptimeSecs;
|
||||||
uint32_t mPrevMillis;
|
uint32_t mPrevMillis;
|
||||||
uint8_t mHeapStatCnt;
|
uint8_t mHeapStatCnt;
|
||||||
|
@ -294,8 +292,7 @@ class app {
|
||||||
|
|
||||||
// sun
|
// sun
|
||||||
int32_t mCalculatedTimezoneOffset;
|
int32_t mCalculatedTimezoneOffset;
|
||||||
uint32_t mSunrise;
|
uint32_t mSunrise, mSunset;
|
||||||
uint32_t mSunset;
|
|
||||||
uint32_t mLatestSunTimestamp;
|
uint32_t mLatestSunTimestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
38
src/utils/sun.h
Normal file
38
src/utils/sun.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778
|
||||||
|
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef __SUN_H__
|
||||||
|
#define __SUN_H__
|
||||||
|
|
||||||
|
namespace ah {
|
||||||
|
void calculateSunriseSunset(uint32_t utcTs, uint32_t offset, float lat, float lon, uint32_t *sunrise, uint32_t *sunset) {
|
||||||
|
// Source: https://en.wikipedia.org/wiki/Sunrise_equation#Complete_calculation_on_Earth
|
||||||
|
|
||||||
|
// Julian day since 1.1.2000 12:00 + correction 69.12s
|
||||||
|
double n_JulianDay = (utcTs + offset) / 86400 - 10957.0 + 0.0008;
|
||||||
|
// Mean solar time
|
||||||
|
double J = n_JulianDay - lon / 360;
|
||||||
|
// Solar mean anomaly
|
||||||
|
double M = fmod((357.5291 + 0.98560028 * J), 360);
|
||||||
|
// Equation of the center
|
||||||
|
double C = 1.9148 * SIN(M) + 0.02 * SIN(2 * M) + 0.0003 * SIN(3 * M);
|
||||||
|
// Ecliptic longitude
|
||||||
|
double lambda = fmod((M + C + 180 + 102.9372), 360);
|
||||||
|
// Solar transit
|
||||||
|
double Jtransit = 2451545.0 + J + 0.0053 * SIN(M) - 0.0069 * SIN(2 * lambda);
|
||||||
|
// Declination of the sun
|
||||||
|
double delta = ASIN(SIN(lambda) * SIN(23.44));
|
||||||
|
// Hour angle
|
||||||
|
double omega = ACOS(SIN(-0.83) - SIN(lat) * SIN(delta) / COS(lat) * COS(delta));
|
||||||
|
// Calculate sunrise and sunset
|
||||||
|
double Jrise = Jtransit - omega / 360;
|
||||||
|
double Jset = Jtransit + omega / 360;
|
||||||
|
// Julian sunrise/sunset to UTC unix timestamp (days incl. fraction to seconds + unix offset 1.1.2000 12:00)
|
||||||
|
*sunrise = (Jrise - 2451545.0) * 86400 + 946728000; // OPTIONAL: Add an offset of +-seconds to the end of the line
|
||||||
|
*sunset = (Jset - 2451545.0) * 86400 + 946728000; // OPTIONAL: Add an offset of +-seconds to the end of the line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /*__SUN_H__*/
|
159
src/web/mqtt.h
159
src/web/mqtt.h
|
@ -38,15 +38,18 @@ class mqtt {
|
||||||
|
|
||||||
~mqtt() { }
|
~mqtt() { }
|
||||||
|
|
||||||
void setup(mqttConfig_t *cfg, const char *devname) {
|
void setup(mqttConfig_t *cfg, const char *devname, HMSYSTEM *sys) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("mqtt.h:setup"));
|
DPRINTLN(DBG_VERBOSE, F("mqtt.h:setup"));
|
||||||
mAddressSet = true;
|
mAddressSet = true;
|
||||||
|
|
||||||
mCfg = cfg;
|
mCfg = cfg;
|
||||||
snprintf(mDevName, DEVNAME_LEN, "%s", devname);
|
snprintf(mDevName, DEVNAME_LEN, "%s", devname);
|
||||||
|
mSys = sys;
|
||||||
|
|
||||||
mClient->setServer(mCfg->broker, mCfg->port);
|
mClient->setServer(mCfg->broker, mCfg->port);
|
||||||
mClient->setBufferSize(MQTT_MAX_PACKET_SIZE);
|
mClient->setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||||
|
|
||||||
|
setCallback(std::bind(&mqtt<HMSYSTEM>::cbMqtt, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCallback(MQTT_CALLBACK_SIGNATURE) {
|
void setCallback(MQTT_CALLBACK_SIGNATURE) {
|
||||||
|
@ -94,12 +97,12 @@ class mqtt {
|
||||||
return mTxCnt;
|
return mTxCnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendMqttDiscoveryConfig(HMSYSTEM *sys, const char *topic, uint32_t invertval) {
|
void sendMqttDiscoveryConfig(const char *topic, uint32_t invertval) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("app::sendMqttDiscoveryConfig"));
|
DPRINTLN(DBG_VERBOSE, F("sendMqttDiscoveryConfig"));
|
||||||
|
|
||||||
char stateTopic[64], discoveryTopic[64], buffer[512], name[32], uniq_id[32];
|
char stateTopic[64], discoveryTopic[64], buffer[512], name[32], uniq_id[32];
|
||||||
for (uint8_t id = 0; id < sys->getNumInverters(); id++) {
|
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
||||||
Inverter<> *iv = sys->getInverterByPos(id);
|
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||||
if (NULL != iv) {
|
if (NULL != iv) {
|
||||||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
DynamicJsonDocument deviceDoc(128);
|
DynamicJsonDocument deviceDoc(128);
|
||||||
|
@ -145,7 +148,7 @@ class mqtt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendIvData(HMSYSTEM *sys, uint32_t mUtcTs, std::queue<uint8_t> list) {
|
void sendIvData(uint32_t mUtcTs, std::queue<uint8_t> list) {
|
||||||
isConnected(true); // really needed? See comment from HorstG-57 #176
|
isConnected(true); // really needed? See comment from HorstG-57 #176
|
||||||
char topic[32 + MAX_NAME_LENGTH], val[32];
|
char topic[32 + MAX_NAME_LENGTH], val[32];
|
||||||
float total[4];
|
float total[4];
|
||||||
|
@ -160,8 +163,8 @@ class mqtt {
|
||||||
|
|
||||||
while(!list.empty()) {
|
while(!list.empty()) {
|
||||||
memset(total, 0, sizeof(float) * 4);
|
memset(total, 0, sizeof(float) * 4);
|
||||||
for (uint8_t id = 0; id < sys->getNumInverters(); id++) {
|
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
||||||
Inverter<> *iv = sys->getInverterByPos(id);
|
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||||
if (NULL == iv)
|
if (NULL == iv)
|
||||||
continue; // skip to next inverter
|
continue; // skip to next inverter
|
||||||
|
|
||||||
|
@ -256,24 +259,6 @@ class mqtt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *getFieldDeviceClass(uint8_t fieldId) {
|
|
||||||
uint8_t pos = 0;
|
|
||||||
for (; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) {
|
|
||||||
if (deviceFieldAssignment[pos].fieldId == fieldId)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : deviceClasses[deviceFieldAssignment[pos].deviceClsId];
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *getFieldStateClass(uint8_t fieldId) {
|
|
||||||
uint8_t pos = 0;
|
|
||||||
for (; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) {
|
|
||||||
if (deviceFieldAssignment[pos].fieldId == fieldId)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : stateClasses[deviceFieldAssignment[pos].stateClsId];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void reconnect(void) {
|
void reconnect(void) {
|
||||||
DPRINTLN(DBG_DEBUG, F("mqtt.h:reconnect"));
|
DPRINTLN(DBG_DEBUG, F("mqtt.h:reconnect"));
|
||||||
|
@ -311,8 +296,130 @@ class mqtt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *getFieldDeviceClass(uint8_t fieldId) {
|
||||||
|
uint8_t pos = 0;
|
||||||
|
for (; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) {
|
||||||
|
if (deviceFieldAssignment[pos].fieldId == fieldId)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : deviceClasses[deviceFieldAssignment[pos].deviceClsId];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getFieldStateClass(uint8_t fieldId) {
|
||||||
|
uint8_t pos = 0;
|
||||||
|
for (; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) {
|
||||||
|
if (deviceFieldAssignment[pos].fieldId == fieldId)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : stateClasses[deviceFieldAssignment[pos].stateClsId];
|
||||||
|
}
|
||||||
|
|
||||||
|
void cbMqtt(char *topic, byte *payload, unsigned int length) {
|
||||||
|
// callback handling on subscribed devcontrol topic
|
||||||
|
DPRINTLN(DBG_INFO, F("cbMqtt"));
|
||||||
|
// subcribed topics are mTopic + "/devcontrol/#" where # is <inverter_id>/<subcmd in dec>
|
||||||
|
// eg. mypvsolar/devcontrol/1/11 with payload "400" --> inverter 1 active power limit 400 Watt
|
||||||
|
const char *token = strtok(topic, "/");
|
||||||
|
while (token != NULL) {
|
||||||
|
if (strcmp(token, "devcontrol") == 0) {
|
||||||
|
token = strtok(NULL, "/");
|
||||||
|
uint8_t iv_id = std::stoi(token);
|
||||||
|
|
||||||
|
if (iv_id >= 0 && iv_id <= MAX_NUM_INVERTERS) {
|
||||||
|
Inverter<> *iv = mSys->getInverterByPos(iv_id);
|
||||||
|
if (NULL != iv) {
|
||||||
|
if (!iv->devControlRequest) { // still pending
|
||||||
|
token = strtok(NULL, "/");
|
||||||
|
|
||||||
|
switch (std::stoi(token)) {
|
||||||
|
// Active Power Control
|
||||||
|
case ActivePowerContr:
|
||||||
|
token = strtok(NULL, "/"); // get ControlMode aka "PowerPF.Desc" in DTU-Pro Code from topic string
|
||||||
|
if (token == NULL) // default via mqtt ommit the LimitControlMode
|
||||||
|
iv->powerLimit[1] = AbsolutNonPersistent;
|
||||||
|
else
|
||||||
|
iv->powerLimit[1] = std::stoi(token);
|
||||||
|
if (length <= 5) { // if (std::stoi((char*)payload) > 0) more error handling powerlimit needed?
|
||||||
|
if (iv->powerLimit[1] >= AbsolutNonPersistent && iv->powerLimit[1] <= RelativPersistent) {
|
||||||
|
iv->devControlCmd = ActivePowerContr;
|
||||||
|
iv->powerLimit[0] = std::stoi(std::string((char *)payload, (unsigned int)length)); // THX to @silversurfer
|
||||||
|
/*if (iv->powerLimit[1] & 0x0001)
|
||||||
|
DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("%"));
|
||||||
|
else
|
||||||
|
DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("W"));*/
|
||||||
|
|
||||||
|
DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + String(iv->powerLimit[1] & 0x0001) ? F("%") : F("W"));
|
||||||
|
}
|
||||||
|
iv->devControlRequest = true;
|
||||||
|
} else {
|
||||||
|
DPRINTLN(DBG_INFO, F("Invalid mqtt payload recevied: ") + String((char *)payload));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Turn On
|
||||||
|
case TurnOn:
|
||||||
|
iv->devControlCmd = TurnOn;
|
||||||
|
DPRINTLN(DBG_INFO, F("Turn on inverter ") + String(iv->id));
|
||||||
|
iv->devControlRequest = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Turn Off
|
||||||
|
case TurnOff:
|
||||||
|
iv->devControlCmd = TurnOff;
|
||||||
|
DPRINTLN(DBG_INFO, F("Turn off inverter ") + String(iv->id));
|
||||||
|
iv->devControlRequest = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Restart
|
||||||
|
case Restart:
|
||||||
|
iv->devControlCmd = Restart;
|
||||||
|
DPRINTLN(DBG_INFO, F("Restart inverter ") + String(iv->id));
|
||||||
|
iv->devControlRequest = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Reactive Power Control
|
||||||
|
case ReactivePowerContr:
|
||||||
|
iv->devControlCmd = ReactivePowerContr;
|
||||||
|
if (true) { // if (std::stoi((char*)payload) > 0) error handling powerlimit needed?
|
||||||
|
iv->devControlCmd = ReactivePowerContr;
|
||||||
|
iv->powerLimit[0] = std::stoi(std::string((char *)payload, (unsigned int)length));
|
||||||
|
iv->powerLimit[1] = 0x0000; // if reactivepower limit is set via external interface --> set it temporay
|
||||||
|
DPRINTLN(DBG_DEBUG, F("Reactivepower limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("W"));
|
||||||
|
iv->devControlRequest = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Set Power Factor
|
||||||
|
case PFSet:
|
||||||
|
// iv->devControlCmd = PFSet;
|
||||||
|
// uint16_t power_factor = std::stoi(strtok(NULL, "/"));
|
||||||
|
DPRINTLN(DBG_INFO, F("Set Power Factor not implemented for inverter ") + String(iv->id));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// CleanState lock & alarm
|
||||||
|
case CleanState_LockAndAlarm:
|
||||||
|
iv->devControlCmd = CleanState_LockAndAlarm;
|
||||||
|
DPRINTLN(DBG_INFO, F("CleanState lock & alarm for inverter ") + String(iv->id));
|
||||||
|
iv->devControlRequest = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DPRINTLN(DBG_INFO, "Not implemented");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
token = strtok(NULL, "/");
|
||||||
|
}
|
||||||
|
DPRINTLN(DBG_INFO, F("app::cbMqtt finished"));
|
||||||
|
}
|
||||||
|
|
||||||
WiFiClient mEspClient;
|
WiFiClient mEspClient;
|
||||||
PubSubClient *mClient;
|
PubSubClient *mClient;
|
||||||
|
HMSYSTEM *mSys;
|
||||||
|
|
||||||
bool mAddressSet;
|
bool mAddressSet;
|
||||||
mqttConfig_t *mCfg;
|
mqttConfig_t *mCfg;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue