Browse Source

0.8.1030004

pull/1615/head
Patrick Amrhein 9 months ago
parent
commit
4c5e805370
  1. 72
      src/app.cpp
  2. 7
      src/app.h
  3. 95
      src/config/settings.h
  4. 2
      src/defines.h
  5. 110
      src/plugins/zeroExport/powermeter.h
  6. 2024
      src/plugins/zeroExport/zeroExport.h
  7. 10
      src/publisher/pubMqtt.h
  8. 1
      src/web/RestApi.h

72
src/app.cpp

@ -115,6 +115,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.setConnectionCb(std::bind(&app::mqttConnectCb, this));
mMqtt.setSubscriptionCb(std::bind(&app::mqttSubRxCb, this, std::placeholders::_1));
mCommunication.addAlarmListener([this](Inverter<> *iv) { mMqtt.alarmEvent(iv); });
}
@ -145,7 +146,7 @@ void app::setup() {
// Plugin ZeroExport
#if defined(PLUGIN_ZEROEXPORT)
mZeroExport.setup(&mConfig->plugin.zeroExport, &mSys, mConfig, &mApi, &mMqtt);
mZeroExport.setup(this, &mTimestamp, &mConfig->plugin.zeroExport, &mSys, mConfig, &mApi, &mMqtt);
#endif /*PLUGIN_ZEROEXPORT*/
// Plugin ZeroExport - Ende
@ -403,10 +404,6 @@ void app::tickMinute(void) {
//-----------------------------------------------------------------------------
void app::tickMidnight(void) {
#if defined(PLUGIN_ZEROEXPORT)
mZeroExport.tickMidnight();
#endif /*defined(PLUGIN_ZEROEXPORT)*/
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");
@ -439,6 +436,10 @@ void app::tickMidnight(void) {
mMqtt.tickerMidnight();
#endif
}
#if defined(PLUGIN_ZEROEXPORT)
mZeroExport.tickMidnight();
#endif /*defined(PLUGIN_ZEROEXPORT)*/
}
//-----------------------------------------------------------------------------
@ -619,6 +620,13 @@ void app::resetSystem(void) {
mNtpReceived = false;
}
//-----------------------------------------------------------------------------
void app::mqttConnectCb(void) {
#if defined(PLUGIN_ZEROEXPORT)
mZeroExport.onMqttConnect();
#endif
}
//-----------------------------------------------------------------------------
void app::mqttSubRxCb(JsonObject obj) {
mApi.ctrlRequest(obj);
@ -673,57 +681,3 @@ void app::updateLed(void) {
analogWrite(mConfig->led.led[2], led_off);
}
}
//-----------------------------------------------------------------------------
// Plugin ZeroExport
#if defined(PLUGIN_ZEROEXPORT)
void app::zeroexport() {
return;
// TODO: aufr�umen
// TODO: umziehen nach loop
/*
if (!mConfig->plugin.zeroExport.enabled ||
!mSys.getInverterByPos(mConfig->plugin.zeroExport.Iv)->isProducing()) { // check if plugin is enabled && indicate to send new value
mConfig->plugin.zeroExport.lastTime = millis(); // set last timestamp
return;
}
if (millis() - mConfig->plugin.zeroExport.lastTime > mConfig->plugin.zeroExport.count_avg * 1000UL)
{
Inverter<> *iv = mSys.getInverterByPos(mConfig->plugin.zeroExport.Iv);
DynamicJsonDocument doc(512);
JsonObject object = doc.to<JsonObject>();
double nValue = round(mZeroExport.getPowertoSetnewValue());
double twoPerVal = nValue <= (iv->getMaxPower() / 100 * 2 );
if(mConfig->plugin.zeroExport.two_percent && (nValue <= twoPerVal))
nValue = twoPerVal;
if(mConfig->plugin.zeroExport.max_power <= nValue)
nValue = mConfig->plugin.zeroExport.max_power;
if(iv->actPowerLimit == nValue) {
mConfig->plugin.zeroExport.lastTime = millis(); // set last timestamp
return; // if PowerLimit same as befor, then skip
}
object["val"] = nValue;
object["id"] = mConfig->plugin.zeroExport.Iv;
object["path"] = "ctrl";
object["cmd"] = "limit_nonpersistent_absolute";
String data;
serializeJsonPretty(object, data);
DPRINTLN(DBG_INFO, data);
mApi.ctrlRequest(object);
mConfig->plugin.zeroExport.lastTime = millis(); // set last timestamp
}
*/
}
#endif
// Plugin ZeroExport - Ende

7
src/app.h

@ -370,17 +370,12 @@ class app : public IApp, public ah::Scheduler {
updateLed();
}
void mqttConnectCb(void);
void mqttSubRxCb(JsonObject obj);
void setupLed();
void updateLed();
// Plugin ZeroExport
#if defined(PLUGIN_ZEROEXPORT)
void zeroexport();
#endif
// Plugin ZeroExport - Ende
void tickReboot(void) {
DPRINTLN(DBG_INFO, F("Rebooting..."));
ah::Scheduler::resetTicker();

95
src/config/settings.h

@ -203,7 +203,7 @@ typedef struct {
// Plugin ZeroExport
#if defined(PLUGIN_ZEROEXPORT)
#define ZEROEXPORT_DEV_POWERMETER
#define ZEROEXPORT_MAX_QUEUE_ENTRIES 64
#define ZEROEXPORT_MAX_GROUPS 6
#define ZEROEXPORT_GROUP_MAX_LEN_NAME 25
#define ZEROEXPORT_GROUP_MAX_LEN_PM_URL 100
@ -213,7 +213,7 @@ typedef struct {
#define ZEROEXPORT_GROUP_MAX_INVERTERS 3
#define ZEROEXPORT_POWERMETER_MAX_ERRORS 5
#define ZEROEXPORT_DEF_INV_WAITINGTIME_MS 10000
#define ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF 5
#define ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF 2
#define ZEROEXPORT_POWERMETER_SHELLY
//#define ZEROEXPORT_POWERMETER_TASMOTA
#define ZEROEXPORT_POWERMETER_MQTT
@ -221,23 +221,6 @@ typedef struct {
#define ZEROEXPORT_POWERMETER_TIBBER
#define ZEROEXPORT_POWERMETER_SHRDZM
enum class zeroExportState : uint8_t {
INIT,
WAITREFRESH,
GETINVERTERDATA,
BATTERYPROTECTION,
GETPOWERMETER,
CONTROLLER,
PROGNOSE,
AUFTEILEN,
SETREBOOT,
SETPOWER,
SETLIMIT,
EMERGENCY,
FINISH,
ERROR
};
typedef enum {
None = 0,
Shelly = 1,
@ -258,6 +241,20 @@ typedef enum {
L3Sum = 6,
} zeroExportInverterTarget_t;
typedef enum {
doNone = 0,
doRestart,
doTurnOn,
doTurnOff,
doActivePowerContr,
} zeroExportAction_t;
typedef struct {
uint8_t group;
uint8_t inv;
uint8_t id;
} zeroExportQueue_t;
typedef struct {
bool enabled;
int8_t id;
@ -266,18 +263,13 @@ typedef struct {
uint16_t powerMax;
//
int32_t power;
zeroExportAction_t action;
uint16_t power;
uint16_t MaxPower;
int32_t limit;
int32_t limitNew;
uint8_t waitAckSetLimit;
uint8_t waitAckSetPower;
uint8_t waitAckSetReboot;
unsigned long limitTsp;
bool state;
uint8_t waitAck;
//
int8_t doReboot;
int8_t doPower;
int8_t doLimit;
float dcVoltage;
} zeroExportGroupInverter_t;
@ -298,18 +290,14 @@ typedef struct {
bool battEnabled;
float battVoltageOn;
float battVoltageOff;
bool isChangedBattery;
// Advanced
int16_t setPoint;
uint8_t refresh;
int32_t power;
float power;
uint8_t powerTolerance;
uint16_t powerMax;
bool isChangedAdvanced;
//
zeroExportState state;
// zeroExportState stateNext;
unsigned long lastRun;
unsigned long lastRefresh;
uint16_t wait;
@ -318,26 +306,16 @@ typedef struct {
int32_t pm_P1;
int32_t pm_P2;
int32_t pm_P3;
bool publishPower = false;
bool battSwitch;
// PID controller
int32_t eSum;
int32_t eSum1;
int32_t eSum2;
int32_t eSum3;
int32_t eOld;
int32_t eOld1;
int32_t eOld2;
int32_t eOld3;
float eSum;
float eOld;
float Kp;
float Ki;
float Kd;
int32_t y;
int32_t y1;
int32_t y2;
int32_t y3;
float y;
} zeroExportGroup_t;
typedef struct {
@ -347,18 +325,6 @@ typedef struct {
bool log_over_mqtt;
bool debug;
zeroExportGroup_t groups[ZEROEXPORT_MAX_GROUPS];
// uint8_t query_device; // 0 - Tibber, 1 - Shelly, 2 - other (rs232?)
// char monitor_url[ZEXPORT_ADDR_LEN];
// char json_path[ZEXPORT_ADDR_LEN];
// char tibber_pw[10]; // needed for tibber QWGH-ED12
// uint8_t Iv; // saves the inverter that is used for regulation
// float power_avg;
// uint8_t count_avg;
// double total_power;
// unsigned long lastTime; // tic toc
// double max_power;
// bool two_percent; // ask if not go lower then 2%
} zeroExport_t;
#endif
@ -712,19 +678,16 @@ class settings {
mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMin = 10;
mCfg.plugin.zeroExport.groups[group].inverters[inv].powerMax = 600;
//
mCfg.plugin.zeroExport.groups[group].inverters[inv].waitAckSetLimit = false;
mCfg.plugin.zeroExport.groups[group].inverters[inv].waitAckSetPower = false;
mCfg.plugin.zeroExport.groups[group].inverters[inv].waitAckSetReboot = false;
mCfg.plugin.zeroExport.groups[group].inverters[inv].doReboot = -1;
mCfg.plugin.zeroExport.groups[group].inverters[inv].doPower = -1;
mCfg.plugin.zeroExport.groups[group].inverters[inv].doLimit = -1;
mCfg.plugin.zeroExport.groups[group].inverters[inv].waitAck = 0;
mCfg.plugin.zeroExport.groups[group].inverters[inv].action = zeroExportAction_t::doNone;
mCfg.plugin.zeroExport.groups[group].inverters[inv].dcVoltage = 0;
mCfg.plugin.zeroExport.groups[group].inverters[inv].limit = 0;
mCfg.plugin.zeroExport.groups[group].inverters[inv].limitNew = 0;
}
// Battery
mCfg.plugin.zeroExport.groups[group].battEnabled = false;
mCfg.plugin.zeroExport.groups[group].battVoltageOn = 0;
mCfg.plugin.zeroExport.groups[group].battVoltageOff = 0;
mCfg.plugin.zeroExport.groups[group].isChangedBattery = true;
// Advanced
mCfg.plugin.zeroExport.groups[group].setPoint = 0;
mCfg.plugin.zeroExport.groups[group].refresh = 10;
@ -733,9 +696,7 @@ class settings {
mCfg.plugin.zeroExport.groups[group].Kp = -1;
mCfg.plugin.zeroExport.groups[group].Ki = 0;
mCfg.plugin.zeroExport.groups[group].Kd = 0;
mCfg.plugin.zeroExport.groups[group].isChangedAdvanced = true;
//
mCfg.plugin.zeroExport.groups[group].state = zeroExportState::INIT;
mCfg.plugin.zeroExport.groups[group].lastRun = 0;
mCfg.plugin.zeroExport.groups[group].lastRefresh = 0;
mCfg.plugin.zeroExport.groups[group].wait = 60000;

2
src/defines.h

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

110
src/plugins/zeroExport/powermeter.h

@ -8,12 +8,15 @@
#include <AsyncJson.h>
#include <HTTPClient.h>
#include "config/settings.h"
#if defined(ZEROEXPORT_POWERMETER_TIBBER)
#include <base64.h>
#include <string.h>
#include <list>
#include "plugins/zeroExport/lib/sml.h"
typedef struct {
@ -32,28 +35,46 @@ typedef struct {
class powermeter {
public:
powermeter() {
}
~powermeter() {
}
/** powermeter
* constructor
*/
powermeter() {}
bool setup(zeroExport_t *cfg, JsonObject *log /*Hier muss noch geklärt werden was gebraucht wird*/) {
/** ~powermeter
* destructor
*/
~powermeter() {}
/** setup
* Initialisierung
* @param *cfg
* @param *mqtt
* @param *log
* @returns void
*/
bool setup(zeroExport_t *cfg, PubMqttType *mqtt, JsonObject *log) {
mCfg = cfg;
mMqtt = mqtt;
mLog = log;
return true;
}
/** loop
* abfrage der gruppen um die aktuellen Werte (Zähler) zu ermitteln.
* Arbeitsschleife
* @param void
* @returns void
* @todo emergency
*/
void loop(unsigned long *tsp, bool *doLog) {
if (*tsp - mPreviousTsp <= 1000) return; // skip when it is to fast
mPreviousTsp = *tsp;
void loop(void) {
if (millis() - mPreviousTsp <= 1000) return; // skip when it is to fast
mPreviousTsp = millis();
PowermeterBuffer_t power;
for (u_short group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) {
if ((!mCfg->groups[group].enabled) || (mCfg->groups[group].sleep)) continue;
switch (mCfg->groups[group].pm_type) {
#if defined(ZEROEXPORT_POWERMETER_SHELLY)
case zeroExportPowermeterType_t::Shelly:
@ -89,7 +110,19 @@ class powermeter {
}
bufferWrite(power, group);
if (mCfg->debug) *doLog = true;
// MQTT - Powermeter
if (mMqtt->isConnected()) {
// P
mqttObj["Sum"] = ah::round1(power.P);
mqttObj["L1"] = ah::round1(power.P1);
mqttObj["L2"] = ah::round1(power.P2);
mqttObj["L3"] = ah::round1(power.P3);
mMqtt->publish(String("zero/state/groups/" + String(group) + "/powermeter/P").c_str(), mqttDoc.as<std::string>().c_str(), false);
mqttDoc.clear();
// W (TODO)
}
}
}
@ -116,10 +149,35 @@ class powermeter {
return avg;
}
/**
*
*/
void onMqttConnect(void) {
}
/**
*
*/
void onMqttMessage(JsonObject obj) {
String topic = String(obj["topic"]);
#if defined(ZEROEXPORT_POWERMETER_MQTT)
// topic for powermeter?
for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) {
if (mCfg->groups[group].pm_type == zeroExportPowermeterType_t::Mqtt) {
// mLog["mqttDevice"] = "topicInverter";
if (!topic.equals(mCfg->groups[group].pm_jsonPath)) return;
mCfg->groups[group].pm_P = (int32_t)obj["val"];
}
}
#endif /*defined(ZEROEXPORT_POWERMETER_MQTT)*/
}
private:
HTTPClient http;
zeroExport_t *mCfg;
PubMqttType *mMqtt = nullptr;
JsonObject *mLog;
unsigned long mPreviousTsp = 0;
@ -127,9 +185,13 @@ class powermeter {
PowermeterBuffer_t mPowermeterBuffer[ZEROEXPORT_MAX_GROUPS][5] = {0};
short mPowermeterBufferPos[ZEROEXPORT_MAX_GROUPS] = {0};
StaticJsonDocument<512> mqttDoc; // DynamicJsonDocument mqttDoc(512);
JsonObject mqttObj = mqttDoc.to<JsonObject>();
// set HTTPClient header
void setHeader(HTTPClient* h) {
/**
*
*/
void setHeader(HTTPClient *h) {
h->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
h->setUserAgent("Ahoy-Agent");
// TODO: Ahoy-0.8.850024-zero
@ -405,12 +467,10 @@ class powermeter {
07 01 00 02 08 02 ff #objName: OBIS-Kennzahl für Wirkenergie Einspeisung Tarif2
*/
const std::list<OBISHandler> smlHandlerList{
{{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeterTotal}, // total - OBIS-Kennzahl für momentane Gesamtwirkleistung
{{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeterTotal}, // total - OBIS-Kennzahl für momentane Gesamtwirkleistung
{{0x01, 0x00, 0x24, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter1Power}, // OBIS-Kennzahl für momentane Wirkleistung in Phase L1
{{0x01, 0x00, 0x38, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter2Power}, // OBIS-Kennzahl für momentane Wirkleistung in Phase L2
{{0x01, 0x00, 0x4c, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter3Power}, // OBIS-Kennzahl für momentane Wirkleistung in Phase L3
{{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterImport},
{{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterExport}};
@ -423,14 +483,12 @@ class powermeter {
logObj["mod"] = "getPowermeterWattsTibber";
String auth;
if(strlen(mCfg->groups[group].pm_user) > 0 && strlen(mCfg->groups[group].pm_pass) > 0) {
if (strlen(mCfg->groups[group].pm_user) > 0 && strlen(mCfg->groups[group].pm_pass) > 0) {
auth = base64::encode(String(mCfg->groups[group].pm_user) + String(":") + String(mCfg->groups[group].pm_pass));
snprintf(mCfg->groups[group].pm_user, ZEROEXPORT_GROUP_MAX_LEN_PM_USER, "%s", DEF_ZEXPORT);
snprintf(mCfg->groups[group].pm_pass, ZEROEXPORT_GROUP_MAX_LEN_PM_PASS, "%s", auth.c_str());
snprintf(mCfg->groups[group].pm_user, ZEROEXPORT_GROUP_MAX_LEN_PM_USER, "%s", DEF_ZEXPORT);
snprintf(mCfg->groups[group].pm_pass, ZEROEXPORT_GROUP_MAX_LEN_PM_PASS, "%s", auth.c_str());
//@TODO:mApp->saveSettings(false);
}
else
{
} else {
auth = mCfg->groups[group].pm_pass;
}
@ -497,9 +555,7 @@ class powermeter {
String url =
String("http://") + String(mCfg->groups[group].pm_url) +
String("/") + String(mCfg->groups[group].pm_jsonPath +
String("?user=") + String(mCfg->groups[group].pm_user) +
String("&password=") + String(mCfg->groups[group].pm_pass));
String("/") + String(mCfg->groups[group].pm_jsonPath + String("?user=") + String(mCfg->groups[group].pm_user) + String("&password=") + String(mCfg->groups[group].pm_pass));
http.begin(url);
@ -518,7 +574,6 @@ class powermeter {
if (!(result.P1 && result.P2 && result.P3)) {
result.P1 = result.P2 = result.P3 = result.P / 3;
}
}
}
http.end();
@ -527,6 +582,9 @@ class powermeter {
}
#endif
/**
*
*/
void bufferWrite(PowermeterBuffer_t raw, short group) {
mPowermeterBuffer[group][mPowermeterBufferPos[group]] = raw;
mPowermeterBufferPos[group]++;

2024
src/plugins/zeroExport/zeroExport.h

File diff suppressed because it is too large

10
src/publisher/pubMqtt.h

@ -27,6 +27,7 @@
#include "pubMqttIvData.h"
typedef std::function<void(JsonObject)> subscriptionCb;
typedef std::function<void(void)> connectionCb;
typedef struct {
bool running;
@ -216,6 +217,10 @@ class PubMqtt {
mClient.subscribe(topic, qos);
}
void setConnectionCb(connectionCb cb) {
mConnectionCb = cb;
}
void setSubscriptionCb(subscriptionCb cb) {
mSubscriptionCb = cb;
}
@ -268,6 +273,10 @@ class PubMqtt {
snprintf(mVal.data(), mVal.size(), "ctrl/#");
subscribe(mVal.data(), QOS_2);
subscribe(subscr[MQTT_SUBS_SET_TIME]);
if(NULL == mConnectionCb)
return;
(mConnectionCb)();
}
void onDisconnect(espMqttClientTypes::DisconnectReason reason) {
@ -619,6 +628,7 @@ class PubMqtt {
uint32_t mRxCnt = 0, mTxCnt = 0;
std::queue<sendListCmdIv> mSendList;
std::array<bool, MAX_NUM_INVERTERS> mSendAlarm;
connectionCb mConnectionCb = nullptr;
subscriptionCb mSubscriptionCb = nullptr;
bool mLastAnyAvail = false;
std::array<InverterStatus, MAX_NUM_INVERTERS> mLastIvState;

1
src/web/RestApi.h

@ -1180,7 +1180,6 @@ class RestApi {
mConfig->plugin.zeroExport.groups[group].Ki = jsonIn[F("Ki")];
mConfig->plugin.zeroExport.groups[group].Kd = jsonIn[F("Kd")];
// Global
mConfig->plugin.zeroExport.groups[group].state = zeroExportState::INIT;
mApp->saveSettings(false); // without reboot
}
#endif

Loading…
Cancel
Save