mirror of
https://github.com/lumapu/ahoy.git
synced 2025-04-30 02:36:20 +02:00
added command queue
+ command queue + feedback from inverter for actual power limit via InfoCmd -> SystemConfigPara (0x05) + REST API will enqueue a new info command + change in power limit will enqueue infocm to get actual power limit + actual power limit is available under mqtt topic <TOPIC>/<NAME>/ch0/PowerLimit ALWAYS in percent + Firmware information will be requested automatically up on start of dtu
This commit is contained in:
parent
76f2de853c
commit
51fbe7868c
8 changed files with 202 additions and 129 deletions
|
@ -111,37 +111,6 @@ void app::loop(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (mSys->InfoCmd){
|
|
||||||
case InverterDevInform_Simple:
|
|
||||||
{
|
|
||||||
DPRINT(DBG_INFO, "Response from inform simple\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case InverterDevInform_All:
|
|
||||||
{
|
|
||||||
DPRINT(DBG_INFO, "Response from inform all\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GetLossRate:
|
|
||||||
{
|
|
||||||
DPRINT(DBG_INFO, "Response from get loss rate\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case AlarmData:
|
|
||||||
{
|
|
||||||
DPRINT(DBG_INFO, "Response from AlarmData\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case AlarmUpdate:
|
|
||||||
{
|
|
||||||
DPRINT(DBG_INFO, "Response from AlarmUpdate\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RealTimeRunData_Debug:
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(NULL != iv && p->packet[0] == (TX_REQ_DEVCONTROL + 0x80)) { // response from dev control command
|
if(NULL != iv && p->packet[0] == (TX_REQ_DEVCONTROL + 0x80)) { // response from dev control command
|
||||||
DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received"));
|
DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received"));
|
||||||
|
@ -180,7 +149,7 @@ void app::loop(void) {
|
||||||
|
|
||||||
|
|
||||||
if(rxRdy) {
|
if(rxRdy) {
|
||||||
processPayload(true,mSys->InfoCmd);
|
processPayload(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +235,7 @@ void app::loop(void) {
|
||||||
|
|
||||||
if(NULL != iv) {
|
if(NULL != iv) {
|
||||||
if(!mPayload[iv->id].complete)
|
if(!mPayload[iv->id].complete)
|
||||||
processPayload(false,mSys->InfoCmd);
|
processPayload(false);
|
||||||
|
|
||||||
if(!mPayload[iv->id].complete) {
|
if(!mPayload[iv->id].complete) {
|
||||||
mRxFailed++;
|
mRxFailed++;
|
||||||
|
@ -286,8 +255,9 @@ void app::loop(void) {
|
||||||
if(mConfig.serialDebug)
|
if(mConfig.serialDebug)
|
||||||
DPRINTLN(DBG_INFO, F("Devcontrol request ") + String(iv->devControlCmd) + F(" power limit ") + String(iv->powerLimit[0]));
|
DPRINTLN(DBG_INFO, F("Devcontrol request ") + String(iv->devControlCmd) + F(" power limit ") + String(iv->powerLimit[0]));
|
||||||
mSys->Radio.sendControlPacket(iv->radioId.u64,iv->devControlCmd ,iv->powerLimit);
|
mSys->Radio.sendControlPacket(iv->radioId.u64,iv->devControlCmd ,iv->powerLimit);
|
||||||
|
iv->enqueCommand<InfoCommand>(SystemConfigPara);
|
||||||
} else {
|
} else {
|
||||||
mSys->Radio.sendTimePacket(iv->radioId.u64, mSys->InfoCmd, mPayload[iv->id].ts,iv->alarmMesIndex);
|
mSys->Radio.sendTimePacket(iv->radioId.u64,iv->getQueuedCmd(), mPayload[iv->id].ts,iv->alarmMesIndex);
|
||||||
mRxTicker = 0;
|
mRxTicker = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,9 +304,6 @@ bool app::buildPayload(uint8_t id) {
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void app::processPayload(bool retransmit) {
|
void app::processPayload(bool retransmit) {
|
||||||
processPayload(retransmit, RealTimeRunData_Debug);
|
|
||||||
}
|
|
||||||
void app::processPayload(bool retransmit, uint8_t cmd = RealTimeRunData_Debug) { // cmd value decides which parser is used to decode payload
|
|
||||||
|
|
||||||
#ifdef __MQTT_AFTER_RX__
|
#ifdef __MQTT_AFTER_RX__
|
||||||
boolean doMQTT = false;
|
boolean doMQTT = false;
|
||||||
|
@ -369,7 +336,7 @@ void app::processPayload(bool retransmit, uint8_t cmd = RealTimeRunData_Debug) {
|
||||||
if(0x00 != mLastPacketId)
|
if(0x00 != mLastPacketId)
|
||||||
mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, mLastPacketId, true);
|
mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, mLastPacketId, true);
|
||||||
else
|
else
|
||||||
mSys->Radio.sendTimePacket(iv->radioId.u64, mSys->InfoCmd, mPayload[iv->id].ts,iv->alarmMesIndex);
|
mSys->Radio.sendTimePacket(iv->radioId.u64, iv->getQueuedCmd(), mPayload[iv->id].ts,iv->alarmMesIndex);
|
||||||
}
|
}
|
||||||
mSys->Radio.switchRxCh(100);
|
mSys->Radio.switchRxCh(100);
|
||||||
}
|
}
|
||||||
|
@ -392,20 +359,18 @@ void app::processPayload(bool retransmit, uint8_t cmd = RealTimeRunData_Debug) {
|
||||||
mSys->Radio.dumpBuf(NULL, payload, offs);
|
mSys->Radio.dumpBuf(NULL, payload, offs);
|
||||||
}
|
}
|
||||||
mRxSuccess++;
|
mRxSuccess++;
|
||||||
mSys->InfoCmd = mSys->NextInfoCmd; // On success set next
|
|
||||||
mSys->NextInfoCmd = RealTimeRunData_Debug; // Set next to default. Can/will be overwritten by REST API
|
|
||||||
|
|
||||||
iv->getAssignment(cmd); // choose the parser
|
iv->getAssignment(); // choose the parser
|
||||||
for(uint8_t i = 0; i < iv->listLen; i++) {
|
for(uint8_t i = 0; i < iv->listLen; i++) {
|
||||||
iv->addValue(i, payload,cmd); // cmd value decides which parser is used to decode payload
|
iv->addValue(i, payload); // cmd value decides which parser is used to decode payload
|
||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
iv->doCalculations(cmd); // cmd value decides which parser is used to decode payload
|
iv->doCalculations(); // cmd value decides which parser is used to decode payload
|
||||||
|
|
||||||
#ifdef __MQTT_AFTER_RX__
|
#ifdef __MQTT_AFTER_RX__
|
||||||
doMQTT = true;
|
doMQTT = true;
|
||||||
#endif
|
#endif
|
||||||
|
iv->setQueuedCmdFinished();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
yield();
|
yield();
|
||||||
|
@ -560,32 +525,48 @@ String app::getStatistics(void) {
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
String app::getLiveData(void) {
|
String app::getLiveData(void)
|
||||||
|
{
|
||||||
String modHtml;
|
String modHtml;
|
||||||
for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
for (uint8_t id = 0; id < mSys->getNumInverters(); id++)
|
||||||
|
{
|
||||||
Inverter<> *iv = mSys->getInverterByPos(id);
|
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||||
if(NULL != iv) {
|
if (NULL != iv)
|
||||||
|
{
|
||||||
#ifdef LIVEDATA_VISUALIZED
|
#ifdef LIVEDATA_VISUALIZED
|
||||||
uint8_t modNum, pos;
|
uint8_t modNum, pos;
|
||||||
switch(iv->type) {
|
switch (iv->type)
|
||||||
|
{
|
||||||
default:
|
default:
|
||||||
case INV_TYPE_1CH: modNum = 1; break;
|
case INV_TYPE_1CH:
|
||||||
case INV_TYPE_2CH: modNum = 2; break;
|
modNum = 1;
|
||||||
case INV_TYPE_4CH: modNum = 4; break;
|
break;
|
||||||
|
case INV_TYPE_2CH:
|
||||||
|
modNum = 2;
|
||||||
|
break;
|
||||||
|
case INV_TYPE_4CH:
|
||||||
|
modNum = 4;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
modHtml += F("<div class=\"iv\">"
|
modHtml += F("<div class=\"iv\">"
|
||||||
"<div class=\"ch-iv\"><span class=\"head\">") + String(iv->name) + F(" Limit ") + String(iv->powerLimit[0]);
|
"<div class=\"ch-iv\"><span class=\"head\">") +
|
||||||
if (iv->powerLimit[1] & 0x0001){
|
String(iv->name) + F(" Limit ") + String(iv->actPowerLimit);
|
||||||
|
if (true)
|
||||||
|
{ // live Power Limit from inverter is always in %
|
||||||
modHtml += F(" %</span>");
|
modHtml += F(" %</span>");
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
modHtml += F(" W</span>");
|
modHtml += F(" W</span>");
|
||||||
}
|
}
|
||||||
uint8_t list[] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PCT, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF, FLD_PRA, FLD_ALARM_MES_ID};
|
uint8_t list[] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PCT, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF, FLD_PRA, FLD_ALARM_MES_ID};
|
||||||
|
|
||||||
for(uint8_t fld = 0; fld < 12; fld++) {
|
for (uint8_t fld = 0; fld < 12; fld++)
|
||||||
|
{
|
||||||
pos = (iv->getPosByChFld(CH0, list[fld]));
|
pos = (iv->getPosByChFld(CH0, list[fld]));
|
||||||
if(0xff != pos) {
|
if (0xff != pos)
|
||||||
|
{
|
||||||
modHtml += F("<div class=\"subgrp\">");
|
modHtml += F("<div class=\"subgrp\">");
|
||||||
modHtml += F("<span class=\"value\">") + String(iv->getValue(pos));
|
modHtml += F("<span class=\"value\">") + String(iv->getValue(pos));
|
||||||
modHtml += F("<span class=\"unit\">") + String(iv->getUnit(pos)) + F("</span></span>");
|
modHtml += F("<span class=\"unit\">") + String(iv->getUnit(pos)) + F("</span></span>");
|
||||||
|
@ -595,23 +576,39 @@ String app::getLiveData(void) {
|
||||||
}
|
}
|
||||||
modHtml += "</div>";
|
modHtml += "</div>";
|
||||||
|
|
||||||
for(uint8_t ch = 1; ch <= modNum; ch ++) {
|
for (uint8_t ch = 1; ch <= modNum; ch++)
|
||||||
|
{
|
||||||
modHtml += F("<div class=\"ch\"><span class=\"head\">");
|
modHtml += F("<div class=\"ch\"><span class=\"head\">");
|
||||||
if(iv->chName[ch-1][0] == 0)
|
if (iv->chName[ch - 1][0] == 0)
|
||||||
modHtml += F("CHANNEL ") + String(ch);
|
modHtml += F("CHANNEL ") + String(ch);
|
||||||
else
|
else
|
||||||
modHtml += String(iv->chName[ch-1]);
|
modHtml += String(iv->chName[ch - 1]);
|
||||||
modHtml += F("</span>");
|
modHtml += F("</span>");
|
||||||
for(uint8_t j = 0; j < 6; j++) {
|
for (uint8_t j = 0; j < 6; j++)
|
||||||
switch(j) {
|
{
|
||||||
default: pos = (iv->getPosByChFld(ch, FLD_UDC)); break;
|
switch (j)
|
||||||
case 1: pos = (iv->getPosByChFld(ch, FLD_IDC)); break;
|
{
|
||||||
case 2: pos = (iv->getPosByChFld(ch, FLD_PDC)); break;
|
default:
|
||||||
case 3: pos = (iv->getPosByChFld(ch, FLD_YD)); break;
|
pos = (iv->getPosByChFld(ch, FLD_UDC));
|
||||||
case 4: pos = (iv->getPosByChFld(ch, FLD_YT)); break;
|
break;
|
||||||
case 5: pos = (iv->getPosByChFld(ch, FLD_IRR)); break;
|
case 1:
|
||||||
|
pos = (iv->getPosByChFld(ch, FLD_IDC));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
pos = (iv->getPosByChFld(ch, FLD_PDC));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
pos = (iv->getPosByChFld(ch, FLD_YD));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
pos = (iv->getPosByChFld(ch, FLD_YT));
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
pos = (iv->getPosByChFld(ch, FLD_IRR));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if(0xff != pos) {
|
if (0xff != pos)
|
||||||
|
{
|
||||||
modHtml += F("<span class=\"value\">") + String(iv->getValue(pos));
|
modHtml += F("<span class=\"value\">") + String(iv->getValue(pos));
|
||||||
modHtml += F("<span class=\"unit\">") + String(iv->getUnit(pos)) + F("</span></span>");
|
modHtml += F("<span class=\"unit\">") + String(iv->getUnit(pos)) + F("</span></span>");
|
||||||
modHtml += F("<span class=\"info\">") + String(iv->getFieldName(pos)) + F("</span>");
|
modHtml += F("<span class=\"info\">") + String(iv->getFieldName(pos)) + F("</span>");
|
||||||
|
@ -626,7 +623,8 @@ String app::getLiveData(void) {
|
||||||
// dump all data to web frontend
|
// dump all data to web frontend
|
||||||
modHtml = F("<pre>");
|
modHtml = F("<pre>");
|
||||||
char topic[30], val[10];
|
char topic[30], val[10];
|
||||||
for(uint8_t i = 0; i < iv->listLen; i++) {
|
for (uint8_t i = 0; i < iv->listLen; i++)
|
||||||
|
{
|
||||||
snprintf(topic, 30, "%s/ch%d/%s", iv->name, iv->assign[i].ch, iv->getFieldName(i));
|
snprintf(topic, 30, "%s/ch%d/%s", iv->name, iv->assign[i].ch, iv->getFieldName(i));
|
||||||
snprintf(val, 10, "%.3f %s", iv->getValue(i), iv->getUnit(i));
|
snprintf(val, 10, "%.3f %s", iv->getValue(i), iv->getUnit(i));
|
||||||
modHtml += String(topic) + ": " + String(val) + "\n";
|
modHtml += String(topic) + ": " + String(val) + "\n";
|
||||||
|
@ -638,7 +636,6 @@ String app::getLiveData(void) {
|
||||||
return modHtml;
|
return modHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
String app::getJson(void) {
|
String app::getJson(void) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("app::showJson"));
|
DPRINTLN(DBG_VERBOSE, F("app::showJson"));
|
||||||
|
|
|
@ -25,22 +25,22 @@ typedef struct {
|
||||||
typedef enum {
|
typedef enum {
|
||||||
InverterDevInform_Simple = 0, // 0x00
|
InverterDevInform_Simple = 0, // 0x00
|
||||||
InverterDevInform_All = 1, // 0x01
|
InverterDevInform_All = 1, // 0x01
|
||||||
//GridOnProFilePara = 2, // 0x02
|
GridOnProFilePara = 2, // 0x02
|
||||||
//HardWareConfig = 3, // 0x03
|
HardWareConfig = 3, // 0x03
|
||||||
//SimpleCalibrationPara = 4, // 0x04
|
SimpleCalibrationPara = 4, // 0x04
|
||||||
//SystemConfigPara = 5, // 0x05
|
SystemConfigPara = 5, // 0x05
|
||||||
RealTimeRunData_Debug = 11, // 0x0b
|
RealTimeRunData_Debug = 11, // 0x0b
|
||||||
//RealTimeRunData_Reality = 12, // 0x0c
|
RealTimeRunData_Reality = 12, // 0x0c
|
||||||
//RealTimeRunData_A_Phase = 13, // 0x0d
|
RealTimeRunData_A_Phase = 13, // 0x0d
|
||||||
//RealTimeRunData_B_Phase = 14, // 0x0e
|
RealTimeRunData_B_Phase = 14, // 0x0e
|
||||||
//RealTimeRunData_C_Phase = 15, // 0x0f
|
RealTimeRunData_C_Phase = 15, // 0x0f
|
||||||
AlarmData = 17, // 0x11, Alarm data - all unsent alarms
|
AlarmData = 17, // 0x11, Alarm data - all unsent alarms
|
||||||
AlarmUpdate = 18, // 0x12, Alarm data - all pending alarms
|
AlarmUpdate = 18, // 0x12, Alarm data - all pending alarms
|
||||||
//RecordData = 19, // 0x13
|
RecordData = 19, // 0x13
|
||||||
//InternalData = 20, // 0x14
|
InternalData = 20, // 0x14
|
||||||
GetLossRate = 21, // 0x15
|
GetLossRate = 21, // 0x15
|
||||||
//GetSelfCheckState = 30, // 0x1e
|
GetSelfCheckState = 30, // 0x1e
|
||||||
//InitDataState = 0xff
|
InitDataState = 0xff
|
||||||
} InfoCmdType;
|
} InfoCmdType;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
|
@ -23,9 +23,9 @@ const char* const units[] = {"V", "A", "W", "Wh", "kWh", "Hz", "°C", "%","VAr",
|
||||||
|
|
||||||
// field types
|
// field types
|
||||||
enum {FLD_UDC = 0, FLD_IDC, FLD_PDC, FLD_YD, FLD_YW, FLD_YT,
|
enum {FLD_UDC = 0, FLD_IDC, FLD_PDC, FLD_YD, FLD_YW, FLD_YT,
|
||||||
FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_T, FLD_PCT, FLD_EFF, FLD_IRR, FLD_PRA,FLD_ALARM_MES_ID,FLD_FW_VERSION,FLD_FW_BUILD_YEAR,FLD_FW_BUILD_MONTH_DAY,FLD_HW_ID};
|
FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_T, FLD_PCT, FLD_EFF, FLD_IRR, FLD_PRA,FLD_ALARM_MES_ID,FLD_FW_VERSION,FLD_FW_BUILD_YEAR,FLD_FW_BUILD_MONTH_DAY,FLD_HW_ID,FLD_ACT_PWR_LIMIT};
|
||||||
const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
|
const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
|
||||||
"U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct", "Efficiency", "Irradiation","P_ACr","ALARM_MES_ID","FWVersion","FWBuildYear","FWBuildMonthDay","HWPartId"};
|
"U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct", "Efficiency", "Irradiation","P_ACr","ALARM_MES_ID","FWVersion","FWBuildYear","FWBuildMonthDay","HWPartId","PowerLimit"};
|
||||||
|
|
||||||
// mqtt discovery device classes
|
// mqtt discovery device classes
|
||||||
enum {DEVICE_CLS_NONE = 0, DEVICE_CLS_CURRENT, DEVICE_CLS_ENERGY, DEVICE_CLS_PWR, DEVICE_CLS_VOLTAGE, DEVICE_CLS_FREQ, DEVICE_CLS_TEMP};
|
enum {DEVICE_CLS_NONE = 0, DEVICE_CLS_CURRENT, DEVICE_CLS_ENERGY, DEVICE_CLS_PWR, DEVICE_CLS_VOLTAGE, DEVICE_CLS_FREQ, DEVICE_CLS_TEMP};
|
||||||
|
@ -92,6 +92,12 @@ const byteAssign_t InfoAssignment[] = {
|
||||||
};
|
};
|
||||||
#define HMINFO_LIST_LEN (sizeof(InfoAssignment) / sizeof(byteAssign_t))
|
#define HMINFO_LIST_LEN (sizeof(InfoAssignment) / sizeof(byteAssign_t))
|
||||||
|
|
||||||
|
const byteAssign_t SystemConfigParaAssignment[] = {
|
||||||
|
{ FLD_ACT_PWR_LIMIT, UNIT_PCT, CH0, 2, 2, 10 }
|
||||||
|
};
|
||||||
|
#define HMSYSTEM_LIST_LEN (sizeof(SystemConfigParaAssignment) / sizeof(byteAssign_t))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "hmDefines.h"
|
#include "hmDefines.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For values which are of interest and not transmitted by the inverter can be
|
* For values which are of interest and not transmitted by the inverter can be
|
||||||
|
@ -51,9 +53,35 @@ template<class T=float>
|
||||||
struct calcFunc_t {
|
struct calcFunc_t {
|
||||||
uint8_t funcId; // unique id
|
uint8_t funcId; // unique id
|
||||||
func_t<T>* func; // function pointer
|
func_t<T>* func; // function pointer
|
||||||
} ;
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CommandAbstract {
|
||||||
|
public:
|
||||||
|
CommandAbstract(uint8_t txType = 0, uint8_t cmd = 0){
|
||||||
|
_TxType = txType;
|
||||||
|
_Cmd = cmd;
|
||||||
|
};
|
||||||
|
virtual ~CommandAbstract() {};
|
||||||
|
|
||||||
|
const uint8_t getCmd()
|
||||||
|
{
|
||||||
|
return _Cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t _TxType;
|
||||||
|
uint8_t _Cmd;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InfoCommand : public CommandAbstract {
|
||||||
|
public:
|
||||||
|
InfoCommand(uint8_t cmd){
|
||||||
|
_TxType = 0x15;
|
||||||
|
_Cmd = cmd;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// list of all available functions, mapped in hmDefines.h
|
// list of all available functions, mapped in hmDefines.h
|
||||||
template<class T=float>
|
template<class T=float>
|
||||||
const calcFunc_t<T> calcFunctions[] = {
|
const calcFunc_t<T> calcFunctions[] = {
|
||||||
|
@ -77,6 +105,7 @@ class Inverter {
|
||||||
uint16_t alarmMesIndex; // Last recorded Alarm Message Index
|
uint16_t alarmMesIndex; // Last recorded Alarm Message Index
|
||||||
uint16_t fwVersion; // Firmware Version from Info Command Request
|
uint16_t fwVersion; // Firmware Version from Info Command Request
|
||||||
uint16_t powerLimit[2]; // limit power output
|
uint16_t powerLimit[2]; // limit power output
|
||||||
|
uint16_t actPowerLimit; //
|
||||||
uint8_t devControlCmd; // carries the requested cmd
|
uint8_t devControlCmd; // carries the requested cmd
|
||||||
bool devControlRequest; // true if change needed
|
bool devControlRequest; // true if change needed
|
||||||
serial_u serial; // serial number as on barcode
|
serial_u serial; // serial number as on barcode
|
||||||
|
@ -92,6 +121,7 @@ class Inverter {
|
||||||
ts = 0;
|
ts = 0;
|
||||||
powerLimit[0] = 0xffff; // 65535 W Limit -> unlimited
|
powerLimit[0] = 0xffff; // 65535 W Limit -> unlimited
|
||||||
powerLimit[1] = 0x0000; //
|
powerLimit[1] = 0x0000; //
|
||||||
|
actPowerLimit = 0xffff; // init feedback from inverter to -1
|
||||||
devControlRequest = false;
|
devControlRequest = false;
|
||||||
devControlCmd = 0xff;
|
devControlCmd = 0xff;
|
||||||
initialized = false;
|
initialized = false;
|
||||||
|
@ -102,6 +132,30 @@ class Inverter {
|
||||||
// TODO: cleanup
|
// TODO: cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void enqueCommand(uint8_t cmd)
|
||||||
|
{
|
||||||
|
_commandQueue.push(std::make_shared<T>(cmd));
|
||||||
|
DPRINTLN(DBG_INFO, "enqueuedCmd: " + String(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setQueuedCmdFinished(){
|
||||||
|
if (!_commandQueue.empty()){
|
||||||
|
_commandQueue.pop(); // Will destroy CommandAbstract Class Object (?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getQueuedCmd()
|
||||||
|
{
|
||||||
|
if (_commandQueue.empty()){
|
||||||
|
// Fill with default commands
|
||||||
|
enqueCommand<InfoCommand>(RealTimeRunData_Debug);
|
||||||
|
//enqueCommand<InfoCommand>(SystemConfigPara);
|
||||||
|
}
|
||||||
|
return _commandQueue.front().get()->getCmd();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void init(void) {
|
void init(void) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:init"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:init"));
|
||||||
getAssignment();
|
getAssignment();
|
||||||
|
@ -110,6 +164,8 @@ class Inverter {
|
||||||
memset(name, 0, MAX_NAME_LENGTH);
|
memset(name, 0, MAX_NAME_LENGTH);
|
||||||
memset(chName, 0, MAX_NAME_LENGTH * 4);
|
memset(chName, 0, MAX_NAME_LENGTH * 4);
|
||||||
memset(record, 0, sizeof(RECORDTYPE) * listLen);
|
memset(record, 0, sizeof(RECORDTYPE) * listLen);
|
||||||
|
enqueCommand<InfoCommand>(InverterDevInform_All);
|
||||||
|
enqueCommand<InfoCommand>(SystemConfigPara);
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,8 +194,9 @@ class Inverter {
|
||||||
return assign[pos].ch;
|
return assign[pos].ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addValue(uint8_t pos, uint8_t buf[],uint8_t cmd) {
|
void addValue(uint8_t pos, uint8_t buf[]) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:addValue"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:addValue"));
|
||||||
|
uint8_t cmd = getQueuedCmd();
|
||||||
uint8_t ptr = assign[pos].start;
|
uint8_t ptr = assign[pos].start;
|
||||||
uint8_t end = ptr + assign[pos].num;
|
uint8_t end = ptr + assign[pos].num;
|
||||||
uint16_t div = assign[pos].div;
|
uint16_t div = assign[pos].div;
|
||||||
|
@ -149,7 +206,6 @@ class Inverter {
|
||||||
val <<= 8;
|
val <<= 8;
|
||||||
val |= buf[ptr];
|
val |= buf[ptr];
|
||||||
} while(++ptr != end);
|
} while(++ptr != end);
|
||||||
|
|
||||||
record[pos] = (RECORDTYPE)(val) / (RECORDTYPE)(div);
|
record[pos] = (RECORDTYPE)(val) / (RECORDTYPE)(div);
|
||||||
}
|
}
|
||||||
if (cmd == RealTimeRunData_Debug) {
|
if (cmd == RealTimeRunData_Debug) {
|
||||||
|
@ -165,6 +221,13 @@ class Inverter {
|
||||||
DPRINT(DBG_DEBUG, F("Inverter FW-Version: ") + String(fwVersion));
|
DPRINT(DBG_DEBUG, F("Inverter FW-Version: ") + String(fwVersion));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (cmd == SystemConfigPara) {
|
||||||
|
// get at least the firmware version and save it to the inverter object
|
||||||
|
if (getPosByChFld(0, FLD_ACT_PWR_LIMIT) == pos){
|
||||||
|
actPowerLimit = record[pos];
|
||||||
|
DPRINT(DBG_DEBUG, F("Inverter actual power limit: ") + String(actPowerLimit));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RECORDTYPE getValue(uint8_t pos) {
|
RECORDTYPE getValue(uint8_t pos) {
|
||||||
|
@ -172,9 +235,10 @@ class Inverter {
|
||||||
return record[pos];
|
return record[pos];
|
||||||
}
|
}
|
||||||
|
|
||||||
void doCalculations(uint8_t cmd=RealTimeRunData_Debug) {
|
void doCalculations() {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:doCalculations"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:doCalculations"));
|
||||||
getAssignment(cmd);
|
uint8_t cmd = getQueuedCmd();
|
||||||
|
getAssignment();
|
||||||
if (cmd == RealTimeRunData_Debug){
|
if (cmd == RealTimeRunData_Debug){
|
||||||
for(uint8_t i = 0; i < listLen; i++) {
|
for(uint8_t i = 0; i < listLen; i++) {
|
||||||
if(CMD_CALC == assign[i].div) {
|
if(CMD_CALC == assign[i].div) {
|
||||||
|
@ -204,37 +268,52 @@ class Inverter {
|
||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void getAssignment(uint8_t cmd=RealTimeRunData_Debug) {
|
void getAssignment() {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getAssignment"));
|
DPRINTLN(DBG_DEBUG, F("hmInverter.h:getAssignment"));
|
||||||
if(cmd == RealTimeRunData_Debug){
|
uint8_t cmd = getQueuedCmd();
|
||||||
if(INV_TYPE_1CH == type) {
|
switch (cmd)
|
||||||
|
{
|
||||||
|
case RealTimeRunData_Debug:
|
||||||
|
if (INV_TYPE_1CH == type)
|
||||||
|
{
|
||||||
listLen = (uint8_t)(HM1CH_LIST_LEN);
|
listLen = (uint8_t)(HM1CH_LIST_LEN);
|
||||||
assign = (byteAssign_t*)hm1chAssignment;
|
assign = (byteAssign_t *)hm1chAssignment;
|
||||||
channels = 1;
|
channels = 1;
|
||||||
}
|
}
|
||||||
else if(INV_TYPE_2CH == type) {
|
else if (INV_TYPE_2CH == type)
|
||||||
|
{
|
||||||
listLen = (uint8_t)(HM2CH_LIST_LEN);
|
listLen = (uint8_t)(HM2CH_LIST_LEN);
|
||||||
assign = (byteAssign_t*)hm2chAssignment;
|
assign = (byteAssign_t *)hm2chAssignment;
|
||||||
channels = 2;
|
channels = 2;
|
||||||
}
|
}
|
||||||
else if(INV_TYPE_4CH == type) {
|
else if (INV_TYPE_4CH == type)
|
||||||
|
{
|
||||||
listLen = (uint8_t)(HM4CH_LIST_LEN);
|
listLen = (uint8_t)(HM4CH_LIST_LEN);
|
||||||
assign = (byteAssign_t*)hm4chAssignment;
|
assign = (byteAssign_t *)hm4chAssignment;
|
||||||
channels = 4;
|
channels = 4;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
listLen = 0;
|
listLen = 0;
|
||||||
channels = 0;
|
channels = 0;
|
||||||
assign = NULL;
|
assign = NULL;
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
if(cmd == InverterDevInform_All){
|
case InverterDevInform_All:
|
||||||
listLen = (uint8_t)(HMINFO_LIST_LEN);
|
listLen = (uint8_t)(HMINFO_LIST_LEN);
|
||||||
assign = (byteAssign_t*)InfoAssignment;
|
assign = (byteAssign_t *)InfoAssignment;
|
||||||
|
break;
|
||||||
|
case SystemConfigPara:
|
||||||
|
listLen = (uint8_t)(HMSYSTEM_LIST_LEN);
|
||||||
|
assign = (byteAssign_t *)SystemConfigParaAssignment;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DPRINTLN(DBG_INFO, "Parser not implemented");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::queue<std::shared_ptr<CommandAbstract>> _commandQueue;
|
||||||
void toRadioId(void) {
|
void toRadioId(void) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:toRadioId"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:toRadioId"));
|
||||||
radioId.u64 = 0ULL;
|
radioId.u64 = 0ULL;
|
||||||
|
|
|
@ -19,13 +19,10 @@ class HmSystem {
|
||||||
RadioType Radio;
|
RadioType Radio;
|
||||||
typedef BUFFER BufferType;
|
typedef BUFFER BufferType;
|
||||||
BufferType BufCtrl;
|
BufferType BufCtrl;
|
||||||
InfoCmdType NextInfoCmd; // For cmd-queue FIFO with one place
|
|
||||||
InfoCmdType InfoCmd; // For cmd-queue FIFO with one place
|
|
||||||
//DevControlCmdType DevControlCmd;
|
//DevControlCmdType DevControlCmd;
|
||||||
|
|
||||||
HmSystem() {
|
HmSystem() {
|
||||||
mNumInv = 0;
|
mNumInv = 0;
|
||||||
InfoCmd = RealTimeRunData_Debug; // default case
|
|
||||||
}
|
}
|
||||||
~HmSystem() {
|
~HmSystem() {
|
||||||
// TODO: cleanup
|
// TODO: cleanup
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#ifndef __INDEX_HTML_H__
|
#ifndef __INDEX_HTML_H__
|
||||||
#define __INDEX_HTML_H__
|
#define __INDEX_HTML_H__
|
||||||
const char index_html[] PROGMEM = "<!doctype html><html><head><title>Index - {DEVICE}</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><script type=\"text/javascript\">document.addEventListener('DOMContentLoaded', function(){document.getElementById(\"btn_req_fw\").addEventListener(\"click\", function(){getInverterInfo(`{\"inverter\":0,\"tx_request\": 21,\"cmd\": 1}`);});});getAjax('/uptime', 'uptime');getAjax('/cmdstat', 'cmds');window.setInterval(\"getAjax('/uptime', 'uptime')\", {JS_TS});window.setInterval(\"getAjax('/cmdstat', 'cmds')\", {JS_TS});function getAjax(url, resid) {var http = null;http = new XMLHttpRequest();if(http != null) {http.open(\"GET\", url, true);http.onreadystatechange = print;http.send(null);}function print() {if(http.readyState == 4) {document.getElementById(resid).innerHTML = http.responseText;}}}function getInverterInfo(data){var http = null;http = new XMLHttpRequest();if(http != null) {http.open(\"POST\", \"/api\");http.setRequestHeader(\"Accept\", \"application/json\");http.setRequestHeader(\"Content-Type\", \"application/json\");http.send(data);}}</script></head><body><h1>AHOY - {DEVICE}</h1><div id=\"content\" class=\"content\"><p><a href=\"/visualization\">Visualization</a><br/><br/><a href=\"/setup\">Setup</a><br/></p><p><span class=\"des\">Uptime: </span><span id=\"uptime\"></span></p><p><span class=\"des\">Statistics: </span><pre id=\"cmds\"></pre></p><p>Every {TS}seconds the values are updated</p><p><input type=\"button\" id=\"btn_req_fw\" value=\"Get FW-Info\"/></p><div id=\"note\">This project was started from <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">this discussion. (Mikrocontroller.net)</a><br/>New updates can be found on Github: <a href=\"https://github.com/grindylow/ahoy\" target=\"_blank\">https://github.com/grindylow/ahoy</a><br/><br/>Please report issues using the feature provided by <a href=\"https://github.com/grindylow/ahoy/issues\">Github</a><br/><br/>Discuss with us on <a href=\"https://discord.gg/WzhxEY62mB\">Discord</a><br/><p class=\"lic\"><a href=\"https://creativecommons.org/licenses/by-nc-sa/3.0/de\">Creative Commons - https://creativecommons.org/licenses/by-nc-sa/3.0/de/</a><br/>Check the licenses which are published on <a href=\"https://github.com/grindylow/ahoy\">https://github.com/grindylow/ahoy</a>as well</p></div></div><div id=\"footer\"><p class=\"left\">© 2022</p><p class=\"left\"><a href=\"/update\">Update Firmware</a></p><p class=\"right\">AHOY :: {VERSION}</p><p class=\"right\"><a href=\"/reboot\">Reboot</a></p><p class=\"right\">Git SHA: {BUILD}</p></div></body></html>";
|
const char index_html[] PROGMEM = "<!doctype html><html><head><title>Index - {DEVICE}</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><script type=\"text/javascript\">getAjax('/uptime', 'uptime');getAjax('/cmdstat', 'cmds');window.setInterval(\"getAjax('/uptime', 'uptime')\", {JS_TS});window.setInterval(\"getAjax('/cmdstat', 'cmds')\", {JS_TS});function getAjax(url, resid) {var http = null;http = new XMLHttpRequest();if(http != null) {http.open(\"GET\", url, true);http.onreadystatechange = print;http.send(null);}function print() {if(http.readyState == 4) {document.getElementById(resid).innerHTML = http.responseText;}}}function getInverterInfo(data){var http = null;http = new XMLHttpRequest();if(http != null) {http.open(\"POST\", \"/api\");http.setRequestHeader(\"Accept\", \"application/json\");http.setRequestHeader(\"Content-Type\", \"application/json\");http.send(data);}}</script></head><body><h1>AHOY - {DEVICE}</h1><div id=\"content\" class=\"content\"><p><a href=\"/visualization\">Visualization</a><br/><br/><a href=\"/setup\">Setup</a><br/></p><p><span class=\"des\">Uptime: </span><span id=\"uptime\"></span></p><p><span class=\"des\">Statistics: </span><pre id=\"cmds\"></pre></p><p>Every {TS}seconds the values are updated</p><div id=\"note\">This project was started from <a href=\"https://www.mikrocontroller.net/topic/525778\" target=\"_blank\">this discussion. (Mikrocontroller.net)</a><br/>New updates can be found on Github: <a href=\"https://github.com/grindylow/ahoy\" target=\"_blank\">https://github.com/grindylow/ahoy</a><br/><br/>Please report issues using the feature provided by <a href=\"https://github.com/grindylow/ahoy/issues\">Github</a><br/><br/>Discuss with us on <a href=\"https://discord.gg/WzhxEY62mB\">Discord</a><br/><p class=\"lic\"><a href=\"https://creativecommons.org/licenses/by-nc-sa/3.0/de\">Creative Commons - https://creativecommons.org/licenses/by-nc-sa/3.0/de/</a><br/>Check the licenses which are published on <a href=\"https://github.com/grindylow/ahoy\">https://github.com/grindylow/ahoy</a>as well</p></div></div><div id=\"footer\"><p class=\"left\">© 2022</p><p class=\"left\"><a href=\"/update\">Update Firmware</a></p><p class=\"right\">AHOY :: {VERSION}</p><p class=\"right\"><a href=\"/reboot\">Reboot</a></p><p class=\"right\">Git SHA: {BUILD}</p></div></body></html>";
|
||||||
#endif /*__INDEX_HTML_H__*/
|
#endif /*__INDEX_HTML_H__*/
|
||||||
|
|
|
@ -5,11 +5,6 @@
|
||||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
document.addEventListener('DOMContentLoaded', function(){
|
|
||||||
document.getElementById("btn_req_fw").addEventListener("click", function(){
|
|
||||||
getInverterInfo(`{"inverter":0,"tx_request": 21,"cmd": 1}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
getAjax('/uptime', 'uptime');
|
getAjax('/uptime', 'uptime');
|
||||||
getAjax('/cmdstat', 'cmds');
|
getAjax('/cmdstat', 'cmds');
|
||||||
window.setInterval("getAjax('/uptime', 'uptime')", {JS_TS});
|
window.setInterval("getAjax('/uptime', 'uptime')", {JS_TS});
|
||||||
|
@ -53,7 +48,6 @@
|
||||||
<p><span class="des">Uptime: </span><span id="uptime"></span></p>
|
<p><span class="des">Uptime: </span><span id="uptime"></span></p>
|
||||||
<p><span class="des">Statistics: </span><pre id="cmds"></pre></p>
|
<p><span class="des">Statistics: </span><pre id="cmds"></pre></p>
|
||||||
<p>Every {TS}seconds the values are updated</p>
|
<p>Every {TS}seconds the values are updated</p>
|
||||||
<p><input type="button" id="btn_req_fw" value="Get FW-Info"/></p>
|
|
||||||
|
|
||||||
<div id="note">
|
<div id="note">
|
||||||
This project was started from <a href="https://www.mikrocontroller.net/topic/525778" target="_blank">this discussion. (Mikrocontroller.net)</a><br/>
|
This project was started from <a href="https://www.mikrocontroller.net/topic/525778" target="_blank">this discussion. (Mikrocontroller.net)</a><br/>
|
||||||
|
|
|
@ -443,19 +443,19 @@ void web::showWebApi(void)
|
||||||
deserializeJson(response, mWeb->arg("plain"));
|
deserializeJson(response, mWeb->arg("plain"));
|
||||||
// ToDo: error handling for payload
|
// ToDo: error handling for payload
|
||||||
uint8_t iv_id = response["inverter"];
|
uint8_t iv_id = response["inverter"];
|
||||||
|
uint8_t cmd = response["cmd"];
|
||||||
Inverter<> *iv = mMain->mSys->getInverterByPos(iv_id);
|
Inverter<> *iv = mMain->mSys->getInverterByPos(iv_id);
|
||||||
if (NULL != iv)
|
if (NULL != iv)
|
||||||
{
|
{
|
||||||
if (response["tx_request"] == (uint8_t)TX_REQ_INFO)
|
if (response["tx_request"] == (uint8_t)TX_REQ_INFO)
|
||||||
{
|
{
|
||||||
//mMain->resetPayload(iv); // start request from new
|
// if the AlarmData is requested set the Alarm Index to the requested one
|
||||||
mMain->mSys->NextInfoCmd = response["cmd"];
|
if (cmd == AlarmData){
|
||||||
// process payload from web request corresponding to the cmd
|
|
||||||
if (mMain->mSys->NextInfoCmd == AlarmData)
|
|
||||||
iv->alarmMesIndex = response["payload"];
|
iv->alarmMesIndex = response["payload"];
|
||||||
DPRINTLN(DBG_INFO, F("Will make tx-request 0x15 with subcmd ") + String(mMain->mSys->InfoCmd) + F(" and payload ") + String((uint16_t) response["payload"]));
|
}
|
||||||
//DPRINTLN(DBG_INFO, F("Will make tx-request 0x15 with subcmd ") + String(mMain->mSys->InfoCmd) + F(" and payload "));
|
DPRINTLN(DBG_INFO, F("Will make tx-request 0x15 with subcmd ") + String(cmd) + F(" and payload ") + String(response["payload"]));
|
||||||
|
// process payload from web request corresponding to the cmd
|
||||||
|
iv->enqueCommand<InfoCommand>(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue