mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-09 23:16:38 +02:00
* started to implement POST api
* improved web serial console * added multi inverter total values (published through MQTT) * fixed: after boot there were transferred wrong data because of incorrect assignment (mqtt, visualization) -> not tested with sun
This commit is contained in:
parent
9e1b6be70e
commit
6bd7e01f1a
13 changed files with 404 additions and 379 deletions
|
@ -92,17 +92,17 @@ To set the active power limit (controled value is the AC Power of the inverter)
|
|||
The implementation allows to set any of the available `<DevControlCmdType>` Commands:
|
||||
```C
|
||||
typedef enum {
|
||||
TurnOn = 0, // 0x00
|
||||
TurnOff = 1, // 0x01
|
||||
Restart = 2, // 0x02
|
||||
Lock = 3, // 0x03
|
||||
Unlock = 4, // 0x04
|
||||
ActivePowerContr = 11, // 0x0b
|
||||
ReactivePowerContr = 12, // 0x0c
|
||||
PFSet = 13, // 0x0d
|
||||
TurnOn = 0, // 0x00
|
||||
TurnOff = 1, // 0x01
|
||||
Restart = 2, // 0x02
|
||||
Lock = 3, // 0x03
|
||||
Unlock = 4, // 0x04
|
||||
ActivePowerContr = 11, // 0x0b
|
||||
ReactivePowerContr = 12, // 0x0c
|
||||
PFSet = 13, // 0x0d
|
||||
CleanState_LockAndAlarm = 20, // 0x14
|
||||
SelfInspection = 40, // 0x28, self-inspection of grid-connected protection files
|
||||
Init = 0xff
|
||||
SelfInspection = 40, // 0x28, self-inspection of grid-connected protection files
|
||||
Init = 0xff
|
||||
} DevControlCmdType;
|
||||
```
|
||||
The MQTT payload will be set on first to bytes and `<DATA2>`, which is taken from the topic path will be set on the second two bytes if the corresponding DevControlCmdType supports 4 byte data.
|
||||
|
@ -215,8 +215,8 @@ Internally this values will be set for the second two bytes for MainCmd: 0x51 Su
|
|||
typedef enum {
|
||||
AbsolutNonPersistent = 0x0000, // 0
|
||||
RelativNonPersistent = 0x0001, // 1
|
||||
AbsolutPersistent = 0x0100, // 256
|
||||
RelativPersistent = 0x0101 // 257
|
||||
AbsolutPersistent = 0x0100, // 256
|
||||
RelativPersistent = 0x0101 // 257
|
||||
} PowerLimitControlType;
|
||||
```
|
||||
|
||||
|
|
|
@ -381,23 +381,47 @@ void app::processPayload(bool retransmit) {
|
|||
// MQTT send out
|
||||
if(mMqttActive) {
|
||||
char topic[30], val[10];
|
||||
for (uint8_t id = 0; id < mSys->getNumInverters(); id++)
|
||||
{
|
||||
float total[4];
|
||||
memset(total, 0, sizeof(float) * 4);
|
||||
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
||||
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||
if (NULL != iv)
|
||||
{
|
||||
if (iv->isAvailable(mTimestamp))
|
||||
{
|
||||
for (uint8_t i = 0; i < iv->listLen; i++)
|
||||
{
|
||||
if (NULL != iv) {
|
||||
if (iv->isAvailable(mTimestamp)) {
|
||||
for (uint8_t i = 0; i < iv->listLen; i++) {
|
||||
snprintf(topic, 30, "%s/ch%d/%s", iv->name, iv->assign[i].ch, fields[iv->assign[i].fieldId]);
|
||||
snprintf(val, 10, "%.3f", iv->getValue(i));
|
||||
mMqtt.sendMsg(topic, val);
|
||||
if(iv->isLiveDataAssignment()) {
|
||||
if(CH0 == iv->assign[i].ch) {
|
||||
switch(iv->assign[i].fieldId) {
|
||||
case FLD_PAC: total[0] += iv->getValue(i); break;
|
||||
case FLD_YT: total[1] += iv->getValue(i); break;
|
||||
case FLD_YD: total[2] += iv->getValue(i); break;
|
||||
case FLD_PDC: total[3] += iv->getValue(i); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// total values (sum of all inverters)
|
||||
if(mSys->getNumInverters() > 1) {
|
||||
uint8_t fieldId = 0;
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
switch(i) {
|
||||
case 0: fieldId = FLD_PAC; break;
|
||||
case 1: fieldId = FLD_YT; break;
|
||||
case 2: fieldId = FLD_YD; break;
|
||||
case 3: fieldId = FLD_PDC; break;
|
||||
}
|
||||
snprintf(topic, 30, "total/%s", fields[fieldId]);
|
||||
snprintf(val, 10, "%.3f", total[i]);
|
||||
mMqtt.sendMsg(topic, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __MQTT_AFTER_RX__
|
||||
|
|
|
@ -23,55 +23,50 @@ typedef struct {
|
|||
} packet_t;
|
||||
|
||||
typedef enum {
|
||||
InverterDevInform_Simple = 0, // 0x00
|
||||
InverterDevInform_All = 1, // 0x01
|
||||
GridOnProFilePara = 2, // 0x02
|
||||
HardWareConfig = 3, // 0x03
|
||||
SimpleCalibrationPara = 4, // 0x04
|
||||
SystemConfigPara = 5, // 0x05
|
||||
RealTimeRunData_Debug = 11, // 0x0b
|
||||
RealTimeRunData_Reality = 12, // 0x0c
|
||||
RealTimeRunData_A_Phase = 13, // 0x0d
|
||||
RealTimeRunData_B_Phase = 14, // 0x0e
|
||||
RealTimeRunData_C_Phase = 15, // 0x0f
|
||||
AlarmData = 17, // 0x11, Alarm data - all unsent alarms
|
||||
AlarmUpdate = 18, // 0x12, Alarm data - all pending alarms
|
||||
RecordData = 19, // 0x13
|
||||
InternalData = 20, // 0x14
|
||||
GetLossRate = 21, // 0x15
|
||||
GetSelfCheckState = 30, // 0x1e
|
||||
InitDataState = 0xff
|
||||
InverterDevInform_Simple = 0, // 0x00
|
||||
InverterDevInform_All = 1, // 0x01
|
||||
GridOnProFilePara = 2, // 0x02
|
||||
HardWareConfig = 3, // 0x03
|
||||
SimpleCalibrationPara = 4, // 0x04
|
||||
SystemConfigPara = 5, // 0x05
|
||||
RealTimeRunData_Debug = 11, // 0x0b
|
||||
RealTimeRunData_Reality = 12, // 0x0c
|
||||
RealTimeRunData_A_Phase = 13, // 0x0d
|
||||
RealTimeRunData_B_Phase = 14, // 0x0e
|
||||
RealTimeRunData_C_Phase = 15, // 0x0f
|
||||
AlarmData = 17, // 0x11, Alarm data - all unsent alarms
|
||||
AlarmUpdate = 18, // 0x12, Alarm data - all pending alarms
|
||||
RecordData = 19, // 0x13
|
||||
InternalData = 20, // 0x14
|
||||
GetLossRate = 21, // 0x15
|
||||
GetSelfCheckState = 30, // 0x1e
|
||||
InitDataState = 0xff
|
||||
} InfoCmdType;
|
||||
|
||||
typedef enum {
|
||||
TurnOn = 0, // 0x00
|
||||
TurnOff = 1, // 0x01
|
||||
Restart = 2, // 0x02
|
||||
Lock = 3, // 0x03
|
||||
Unlock = 4, // 0x04
|
||||
ActivePowerContr = 11, // 0x0b
|
||||
ReactivePowerContr = 12, // 0x0c
|
||||
PFSet = 13, // 0x0d
|
||||
TurnOn = 0, // 0x00
|
||||
TurnOff = 1, // 0x01
|
||||
Restart = 2, // 0x02
|
||||
Lock = 3, // 0x03
|
||||
Unlock = 4, // 0x04
|
||||
ActivePowerContr = 11, // 0x0b
|
||||
ReactivePowerContr = 12, // 0x0c
|
||||
PFSet = 13, // 0x0d
|
||||
CleanState_LockAndAlarm = 20, // 0x14
|
||||
SelfInspection = 40, // 0x28, self-inspection of grid-connected protection files
|
||||
Init = 0xff
|
||||
SelfInspection = 40, // 0x28, self-inspection of grid-connected protection files
|
||||
Init = 0xff
|
||||
} DevControlCmdType;
|
||||
|
||||
typedef enum { // ToDo: to be verified by field tests
|
||||
NoPowerLimit = 0xffff, // ahoy internal value, no hoymiles value!
|
||||
AbsolutNonPersistent = 0UL, // 0x0000
|
||||
RelativNonPersistent = 1UL, // 0x0001
|
||||
AbsolutPersistent = 256UL, // 0x0100
|
||||
RelativPersistent = 257UL // 0x0101
|
||||
typedef enum {
|
||||
NoPowerLimit = 0xffff, // ahoy internal value, no hoymiles value!
|
||||
AbsolutNonPersistent = 0UL, // 0x0000
|
||||
RelativNonPersistent = 1UL, // 0x0001
|
||||
AbsolutPersistent = 256UL, // 0x0100
|
||||
RelativPersistent = 257UL // 0x0101
|
||||
} PowerLimitControlType;
|
||||
|
||||
// minimum serial interval
|
||||
#define MIN_SERIAL_INTERVAL 5
|
||||
|
||||
// minimum send interval
|
||||
#define MIN_SEND_INTERVAL 15
|
||||
|
||||
// minimum mqtt interval
|
||||
#define MIN_MQTT_INTERVAL 60
|
||||
|
||||
//-------------------------------------
|
||||
|
@ -90,27 +85,16 @@ typedef enum { // ToDo: to be verified by field tests
|
|||
#define INV_MAX_RTRY_LEN 1 // uint8_t
|
||||
#define INV_PWR_LIM_LEN MAX_NUM_INVERTERS * 2 // uint16_t
|
||||
|
||||
#define PINOUT_LEN 3 // 3 pins: CS, CE, IRQ
|
||||
|
||||
#define RF24_AMP_PWR_LEN 1
|
||||
|
||||
#define NTP_ADDR_LEN 32 // DNS Name
|
||||
#define NTP_PORT_LEN 2 // uint16_t
|
||||
|
||||
#define MQTT_ADDR_LEN 32 // DNS Name
|
||||
#define MQTT_USER_LEN 16
|
||||
#define MQTT_PWD_LEN 32
|
||||
#define MQTT_TOPIC_LEN 32
|
||||
#define MQTT_INTERVAL_LEN 2 // uint16_t
|
||||
#define MQTT_PORT_LEN 2 // uint16_t
|
||||
#define MQTT_DISCOVERY_PREFIX "homeassistant"
|
||||
#define MQTT_MAX_PACKET_SIZE 384
|
||||
#define MQTT_RECONNECT_DELAY 5000
|
||||
|
||||
#define SER_ENABLE_LEN 1 // uint8_t
|
||||
#define SER_DEBUG_LEN 1 // uint8_t
|
||||
#define SER_INTERVAL_LEN 2 // uint16_t
|
||||
|
||||
#pragma pack(push) // push current alignment to stack
|
||||
#pragma pack(1) // set alignment to 1 byte boundary
|
||||
typedef struct {
|
||||
|
@ -119,8 +103,10 @@ typedef struct {
|
|||
char user[MQTT_USER_LEN];
|
||||
char pwd[MQTT_PWD_LEN];
|
||||
char topic[MQTT_TOPIC_LEN];
|
||||
} /*__attribute__((__packed__))*/ mqttConfig_t;
|
||||
} mqttConfig_t;
|
||||
#pragma pack(pop) // restore original alignment from stack
|
||||
|
||||
|
||||
typedef struct {
|
||||
char deviceName[DEVNAME_LEN];
|
||||
|
||||
|
@ -151,7 +137,7 @@ typedef struct {
|
|||
uint16_t serialInterval;
|
||||
bool serialShowIv;
|
||||
bool serialDebug;
|
||||
} /*__attribute__((__packed__))*/ config_t;
|
||||
} config_t;
|
||||
#pragma pack(pop) // restore original alignment from stack
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -136,27 +136,26 @@ class Inverter {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void enqueCommand(uint8_t cmd)
|
||||
{
|
||||
void enqueCommand(uint8_t cmd) {
|
||||
_commandQueue.push(std::make_shared<T>(cmd));
|
||||
DPRINTLN(DBG_INFO, "enqueuedCmd: " + String(cmd));
|
||||
}
|
||||
|
||||
void setQueuedCmdFinished(){
|
||||
if (!_commandQueue.empty()){
|
||||
void setQueuedCmdFinished() {
|
||||
if (!_commandQueue.empty()) {
|
||||
// Will destroy CommandAbstract Class Object (?)
|
||||
_commandQueue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void clearCmdQueue(){
|
||||
while (!_commandQueue.empty()){
|
||||
void clearCmdQueue() {
|
||||
while (!_commandQueue.empty()) {
|
||||
// Will destroy CommandAbstract Class Object (?)
|
||||
_commandQueue.pop();
|
||||
}
|
||||
}
|
||||
uint8_t getQueuedCmd()
|
||||
{
|
||||
|
||||
uint8_t getQueuedCmd() {
|
||||
if (_commandQueue.empty()){
|
||||
// Fill with default commands
|
||||
enqueCommand<InfoCommand>(RealTimeRunData_Debug);
|
||||
|
@ -296,36 +295,30 @@ class Inverter {
|
|||
return false;
|
||||
}
|
||||
|
||||
uint32_t getLastTs(void)
|
||||
{
|
||||
uint32_t getLastTs(void) {
|
||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getLastTs"));
|
||||
return ts;
|
||||
}
|
||||
|
||||
void getAssignment()
|
||||
{
|
||||
void getAssignment() {
|
||||
DPRINTLN(DBG_DEBUG, F("hmInverter.h:getAssignment"));
|
||||
// Default assignment;
|
||||
if (INV_TYPE_1CH == type)
|
||||
{
|
||||
if (INV_TYPE_1CH == type) {
|
||||
listLen = (uint8_t)(HM1CH_LIST_LEN);
|
||||
assign = (byteAssign_t *)hm1chAssignment;
|
||||
channels = 1;
|
||||
}
|
||||
else if (INV_TYPE_2CH == type)
|
||||
{
|
||||
else if (INV_TYPE_2CH == type) {
|
||||
listLen = (uint8_t)(HM2CH_LIST_LEN);
|
||||
assign = (byteAssign_t *)hm2chAssignment;
|
||||
channels = 2;
|
||||
}
|
||||
else if (INV_TYPE_4CH == type)
|
||||
{
|
||||
else if (INV_TYPE_4CH == type) {
|
||||
listLen = (uint8_t)(HM4CH_LIST_LEN);
|
||||
assign = (byteAssign_t *)hm4chAssignment;
|
||||
channels = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
listLen = 0;
|
||||
channels = 0;
|
||||
assign = NULL;
|
||||
|
@ -352,217 +345,89 @@ class Inverter {
|
|||
break;
|
||||
}
|
||||
}
|
||||
String getAlarmStr(u_int16_t alarmCode)
|
||||
{
|
||||
switch (alarmCode)
|
||||
{
|
||||
case 1:
|
||||
return String(F("Inverter start"));
|
||||
break;
|
||||
case 2:
|
||||
return String(F("DTU command failed"));
|
||||
break;
|
||||
case 121:
|
||||
return String(F("Over temperature protection"));
|
||||
break;
|
||||
case 125:
|
||||
return String(F("Grid configuration parameter error"));
|
||||
break;
|
||||
case 126:
|
||||
return String(F("Software error code 126"));
|
||||
break;
|
||||
case 127:
|
||||
return String(F("Firmware error"));
|
||||
break;
|
||||
case 128:
|
||||
return String(F("Software error code 128"));
|
||||
break;
|
||||
case 129:
|
||||
return String(F("Software error code 129"));
|
||||
break;
|
||||
case 130:
|
||||
return String(F("Offline"));
|
||||
break;
|
||||
case 141:
|
||||
return String(F("Grid overvoltage"));
|
||||
break;
|
||||
case 142:
|
||||
return String(F("Average grid overvoltage"));
|
||||
break;
|
||||
case 143:
|
||||
return String(F("Grid undervoltage"));
|
||||
break;
|
||||
case 144:
|
||||
return String(F("Grid overfrequency"));
|
||||
break;
|
||||
case 145:
|
||||
return String(F("Grid underfrequency"));
|
||||
break;
|
||||
case 146:
|
||||
return String(F("Rapid grid frequency change"));
|
||||
break;
|
||||
case 147:
|
||||
return String(F("Power grid outage"));
|
||||
break;
|
||||
case 148:
|
||||
return String(F("Grid disconnection"));
|
||||
break;
|
||||
case 149:
|
||||
return String(F("Island detected"));
|
||||
break;
|
||||
case 205:
|
||||
return String(F("Input port 1 & 2 overvoltage"));
|
||||
break;
|
||||
case 206:
|
||||
return String(F("Input port 3 & 4 overvoltage"));
|
||||
break;
|
||||
case 207:
|
||||
return String(F("Input port 1 & 2 undervoltage"));
|
||||
break;
|
||||
case 208:
|
||||
return String(F("Input port 3 & 4 undervoltage"));
|
||||
break;
|
||||
case 209:
|
||||
return String(F("Port 1 no input"));
|
||||
break;
|
||||
case 210:
|
||||
return String(F("Port 2 no input"));
|
||||
break;
|
||||
case 211:
|
||||
return String(F("Port 3 no input"));
|
||||
break;
|
||||
case 212:
|
||||
return String(F("Port 4 no input"));
|
||||
break;
|
||||
case 213:
|
||||
return String(F("PV-1 & PV-2 abnormal wiring"));
|
||||
break;
|
||||
case 214:
|
||||
return String(F("PV-3 & PV-4 abnormal wiring"));
|
||||
break;
|
||||
case 215:
|
||||
return String(F("PV-1 Input overvoltage"));
|
||||
break;
|
||||
case 216:
|
||||
return String(F("PV-1 Input undervoltage"));
|
||||
break;
|
||||
case 217:
|
||||
return String(F("PV-2 Input overvoltage"));
|
||||
break;
|
||||
case 218:
|
||||
return String(F("PV-2 Input undervoltage"));
|
||||
break;
|
||||
case 219:
|
||||
return String(F("PV-3 Input overvoltage"));
|
||||
break;
|
||||
case 220:
|
||||
return String(F("PV-3 Input undervoltage"));
|
||||
break;
|
||||
case 221:
|
||||
return String(F("PV-4 Input overvoltage"));
|
||||
break;
|
||||
case 222:
|
||||
return String(F("PV-4 Input undervoltage"));
|
||||
break;
|
||||
case 301:
|
||||
return String(F("Hardware error code 301"));
|
||||
break;
|
||||
case 302:
|
||||
return String(F("Hardware error code 302"));
|
||||
break;
|
||||
case 303:
|
||||
return String(F("Hardware error code 303"));
|
||||
break;
|
||||
case 304:
|
||||
return String(F("Hardware error code 304"));
|
||||
break;
|
||||
case 305:
|
||||
return String(F("Hardware error code 305"));
|
||||
break;
|
||||
case 306:
|
||||
return String(F("Hardware error code 306"));
|
||||
break;
|
||||
case 307:
|
||||
return String(F("Hardware error code 307"));
|
||||
break;
|
||||
case 308:
|
||||
return String(F("Hardware error code 308"));
|
||||
break;
|
||||
case 309:
|
||||
return String(F("Hardware error code 309"));
|
||||
break;
|
||||
case 310:
|
||||
return String(F("Hardware error code 310"));
|
||||
break;
|
||||
case 311:
|
||||
return String(F("Hardware error code 311"));
|
||||
break;
|
||||
case 312:
|
||||
return String(F("Hardware error code 312"));
|
||||
break;
|
||||
case 313:
|
||||
return String(F("Hardware error code 313"));
|
||||
break;
|
||||
case 314:
|
||||
return String(F("Hardware error code 314"));
|
||||
break;
|
||||
case 5041:
|
||||
return String(F("Error code-04 Port 1"));
|
||||
break;
|
||||
case 5042:
|
||||
return String(F("Error code-04 Port 2"));
|
||||
break;
|
||||
case 5043:
|
||||
return String(F("Error code-04 Port 3"));
|
||||
break;
|
||||
case 5044:
|
||||
return String(F("Error code-04 Port 4"));
|
||||
break;
|
||||
case 5051:
|
||||
return String(F("PV Input 1 Overvoltage/Undervoltage"));
|
||||
break;
|
||||
case 5052:
|
||||
return String(F("PV Input 2 Overvoltage/Undervoltage"));
|
||||
break;
|
||||
case 5053:
|
||||
return String(F("PV Input 3 Overvoltage/Undervoltage"));
|
||||
break;
|
||||
case 5054:
|
||||
return String(F("PV Input 4 Overvoltage/Undervoltage"));
|
||||
break;
|
||||
case 5060:
|
||||
return String(F("Abnormal bias"));
|
||||
break;
|
||||
case 5070:
|
||||
return String(F("Over temperature protection"));
|
||||
break;
|
||||
case 5080:
|
||||
return String(F("Grid Overvoltage/Undervoltage"));
|
||||
break;
|
||||
case 5090:
|
||||
return String(F("Grid Overfrequency/Underfrequency"));
|
||||
break;
|
||||
case 5100:
|
||||
return String(F("Island detected"));
|
||||
break;
|
||||
case 5120:
|
||||
return String(F("EEPROM reading and writing error"));
|
||||
break;
|
||||
case 5150:
|
||||
return String(F("10 min value grid overvoltage"));
|
||||
break;
|
||||
case 5200:
|
||||
return String(F("Firmware error"));
|
||||
break;
|
||||
case 8310:
|
||||
return String(F("Shut down"));
|
||||
break;
|
||||
case 9000:
|
||||
return String(F("Microinverter is suspected of being stolen"));
|
||||
break;
|
||||
default:
|
||||
return String(F("Unknown"));
|
||||
break;
|
||||
|
||||
bool isLiveDataAssignment(void) {
|
||||
if(assign == (byteAssign_t *)hm1chAssignment)
|
||||
return true;
|
||||
else if(assign == (byteAssign_t *)hm2chAssignment)
|
||||
return true;
|
||||
else if(assign == (byteAssign_t *)hm4chAssignment)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
String getAlarmStr(u_int16_t alarmCode) {
|
||||
switch (alarmCode) { // breaks are intentionally missing!
|
||||
case 1: return String(F("Inverter start"));
|
||||
case 2: return String(F("DTU command failed"));
|
||||
case 121: return String(F("Over temperature protection"));
|
||||
case 125: return String(F("Grid configuration parameter error"));
|
||||
case 126: return String(F("Software error code 126"));
|
||||
case 127: return String(F("Firmware error"));
|
||||
case 128: return String(F("Software error code 128"));
|
||||
case 129: return String(F("Software error code 129"));
|
||||
case 130: return String(F("Offline"));
|
||||
case 141: return String(F("Grid overvoltage"));
|
||||
case 142: return String(F("Average grid overvoltage"));
|
||||
case 143: return String(F("Grid undervoltage"));
|
||||
case 144: return String(F("Grid overfrequency"));
|
||||
case 145: return String(F("Grid underfrequency"));
|
||||
case 146: return String(F("Rapid grid frequency change"));
|
||||
case 147: return String(F("Power grid outage"));
|
||||
case 148: return String(F("Grid disconnection"));
|
||||
case 149: return String(F("Island detected"));
|
||||
case 205: return String(F("Input port 1 & 2 overvoltage"));
|
||||
case 206: return String(F("Input port 3 & 4 overvoltage"));
|
||||
case 207: return String(F("Input port 1 & 2 undervoltage"));
|
||||
case 208: return String(F("Input port 3 & 4 undervoltage"));
|
||||
case 209: return String(F("Port 1 no input"));
|
||||
case 210: return String(F("Port 2 no input"));
|
||||
case 211: return String(F("Port 3 no input"));
|
||||
case 212: return String(F("Port 4 no input"));
|
||||
case 213: return String(F("PV-1 & PV-2 abnormal wiring"));
|
||||
case 214: return String(F("PV-3 & PV-4 abnormal wiring"));
|
||||
case 215: return String(F("PV-1 Input overvoltage"));
|
||||
case 216: return String(F("PV-1 Input undervoltage"));
|
||||
case 217: return String(F("PV-2 Input overvoltage"));
|
||||
case 218: return String(F("PV-2 Input undervoltage"));
|
||||
case 219: return String(F("PV-3 Input overvoltage"));
|
||||
case 220: return String(F("PV-3 Input undervoltage"));
|
||||
case 221: return String(F("PV-4 Input overvoltage"));
|
||||
case 222: return String(F("PV-4 Input undervoltage"));
|
||||
case 301: return String(F("Hardware error code 301"));
|
||||
case 302: return String(F("Hardware error code 302"));
|
||||
case 303: return String(F("Hardware error code 303"));
|
||||
case 304: return String(F("Hardware error code 304"));
|
||||
case 305: return String(F("Hardware error code 305"));
|
||||
case 306: return String(F("Hardware error code 306"));
|
||||
case 307: return String(F("Hardware error code 307"));
|
||||
case 308: return String(F("Hardware error code 308"));
|
||||
case 309: return String(F("Hardware error code 309"));
|
||||
case 310: return String(F("Hardware error code 310"));
|
||||
case 311: return String(F("Hardware error code 311"));
|
||||
case 312: return String(F("Hardware error code 312"));
|
||||
case 313: return String(F("Hardware error code 313"));
|
||||
case 314: return String(F("Hardware error code 314"));
|
||||
case 5041: return String(F("Error code-04 Port 1"));
|
||||
case 5042: return String(F("Error code-04 Port 2"));
|
||||
case 5043: return String(F("Error code-04 Port 3"));
|
||||
case 5044: return String(F("Error code-04 Port 4"));
|
||||
case 5051: return String(F("PV Input 1 Overvoltage/Undervoltage"));
|
||||
case 5052: return String(F("PV Input 2 Overvoltage/Undervoltage"));
|
||||
case 5053: return String(F("PV Input 3 Overvoltage/Undervoltage"));
|
||||
case 5054: return String(F("PV Input 4 Overvoltage/Undervoltage"));
|
||||
case 5060: return String(F("Abnormal bias"));
|
||||
case 5070: return String(F("Over temperature protection"));
|
||||
case 5080: return String(F("Grid Overvoltage/Undervoltage"));
|
||||
case 5090: return String(F("Grid Overfrequency/Underfrequency"));
|
||||
case 5100: return String(F("Island detected"));
|
||||
case 5120: return String(F("EEPROM reading and writing error"));
|
||||
case 5150: return String(F("10 min value grid overvoltage"));
|
||||
case 5200: return String(F("Firmware error"));
|
||||
case 8310: return String(F("Shut down"));
|
||||
case 9000: return String(F("Microinverter is suspected of being stolen"));
|
||||
default: return String(F("Unknown"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#define RF_CHANNELS 5
|
||||
#define RF_LOOP_CNT 300
|
||||
|
||||
#define TX_REQ_INFO 0X15
|
||||
#define TX_REQ_INFO 0x15
|
||||
#define TX_REQ_DEVCONTROL 0x51
|
||||
#define ALL_FRAMES 0x80
|
||||
#define SINGLE_FRAME 0x81
|
||||
|
|
|
@ -89,7 +89,9 @@ class HmSystem {
|
|||
|
||||
INVERTERTYPE *getInverterByPos(uint8_t pos, bool check = true) {
|
||||
DPRINTLN(DBG_VERBOSE, F("hmSystem.h:getInverterByPos"));
|
||||
if((mInverter[pos].initialized && mInverter[pos].serial.u64 != 0ULL) || false == check)
|
||||
if(pos >= MAX_INVERTER)
|
||||
return NULL;
|
||||
else if((mInverter[pos].initialized && mInverter[pos].serial.u64 != 0ULL) || false == check)
|
||||
return &mInverter[pos];
|
||||
else
|
||||
return NULL;
|
||||
|
|
|
@ -8,17 +8,19 @@ function toggle(name, hide) {
|
|||
elm.classList.remove('hide');
|
||||
}
|
||||
|
||||
function getAjax(url, ptr) {
|
||||
var http = new XMLHttpRequest();
|
||||
if(http != null) {
|
||||
http.open("GET", url, true);
|
||||
http.onreadystatechange = p;
|
||||
http.send(null);
|
||||
function getAjax(url, ptr, method="GET", json=null) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
if(xhr != null) {
|
||||
xhr.open(method, url, true);
|
||||
xhr.onreadystatechange = p;
|
||||
if("POST" == method)
|
||||
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
xhr.send(json);
|
||||
}
|
||||
function p() {
|
||||
if(http.readyState == 4) {
|
||||
if(null != http.responseText)
|
||||
ptr(JSON.parse(http.responseText));
|
||||
if(xhr.readyState == 4) {
|
||||
if(null != xhr.responseText)
|
||||
ptr(JSON.parse(xhr.responseText));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,19 @@
|
|||
<div class="serial">
|
||||
<textarea id="serial" cols="80" rows="20" readonly></textarea><br/>
|
||||
conntected: <span class="dot" id="connected"></span> Uptime: <span id="uptime"></span><input type="button" value="clear" class="btn" id="clear"/> <input type="button" value="autoscroll" class="btn" id="scroll"/>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
<hr>
|
||||
<h3>handle next buttons with care - test / debug only!!</h3>
|
||||
<br/>
|
||||
<input type="button" value="power limit 100%" class="btn" id="pwrlim2"/>
|
||||
<input type="button" value="power limit 10%" class="btn" id="pwrlim"/>
|
||||
<input type="button" value="Turn Off" class="btn" id="turnoff"/>
|
||||
<input type="button" value="Turn On" class="btn" id="turnon"/><br/>
|
||||
Ctrl result: <span id="result">n/a</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
|
@ -70,6 +83,45 @@
|
|||
}
|
||||
|
||||
getAjax("/api/system", parseSys);
|
||||
|
||||
// only for test
|
||||
function ctrlCb(obj) {
|
||||
var e = document.getElementById("result");
|
||||
if(obj["success"])
|
||||
e.innerHTML = "ok";
|
||||
else
|
||||
e.innerHTML = "Error: " + obj["error"];
|
||||
}
|
||||
|
||||
document.getElementById("turnon").addEventListener("click", function() {
|
||||
var obj = new Object();
|
||||
obj.cmd = 0;
|
||||
obj.tx_request = 81;
|
||||
getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj));
|
||||
});
|
||||
|
||||
document.getElementById("turnoff").addEventListener("click", function() {
|
||||
var obj = new Object();
|
||||
obj.cmd = 1;
|
||||
obj.tx_request = 81;
|
||||
getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj));
|
||||
});
|
||||
|
||||
document.getElementById("pwrlim").addEventListener("click", function() {
|
||||
var obj = new Object();
|
||||
obj.cmd = 11;
|
||||
obj.tx_request = 81;
|
||||
obj.payload = [10, 1];
|
||||
getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj));
|
||||
});
|
||||
|
||||
document.getElementById("pwrlim2").addEventListener("click", function() {
|
||||
var obj = new Object();
|
||||
obj.cmd = 11;
|
||||
obj.tx_request = 81;
|
||||
obj.payload = [2000, 1];
|
||||
getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj));
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -48,10 +48,10 @@
|
|||
ch0.appendChild(sub);
|
||||
|
||||
switch(j) {
|
||||
case 2: total[j] += val; break; // P_AC
|
||||
case 6: total[j] += val; break; // YieldTotal
|
||||
case 7: total[j] += val; break; // YieldDay
|
||||
case 8: total[j] += val; break; // P_DC
|
||||
case 2: total[j] += val; break; // P_AC
|
||||
case 6: total[j] += val; break; // YieldTotal
|
||||
case 7: total[j] += val; break; // YieldDay
|
||||
case 8: total[j] += val; break; // P_DC
|
||||
case 10: total[j] += val; break; // P_ACr
|
||||
}
|
||||
}
|
||||
|
|
|
@ -351,12 +351,10 @@ void web::showWebApi(AsyncWebServerRequest *request) {
|
|||
uint8_t iv_id = response["inverter"];
|
||||
uint8_t cmd = response["cmd"];
|
||||
Inverter<> *iv = mMain->mSys->getInverterByPos(iv_id);
|
||||
if (NULL != iv)
|
||||
{
|
||||
if (response["tx_request"] == (uint8_t)TX_REQ_INFO)
|
||||
{
|
||||
if (NULL != iv) {
|
||||
if (response["tx_request"] == (uint8_t)TX_REQ_INFO) {
|
||||
// if the AlarmData is requested set the Alarm Index to the requested one
|
||||
if (cmd == AlarmData || cmd == AlarmUpdate){
|
||||
if (cmd == AlarmData || cmd == AlarmUpdate) {
|
||||
// set the AlarmMesIndex for the request from user input
|
||||
iv->alarmMesIndex = response["payload"];
|
||||
}
|
||||
|
@ -366,43 +364,32 @@ void web::showWebApi(AsyncWebServerRequest *request) {
|
|||
}
|
||||
|
||||
|
||||
if (response["tx_request"] == (uint8_t)TX_REQ_DEVCONTROL)
|
||||
{
|
||||
if (response["cmd"] == (uint8_t)ActivePowerContr)
|
||||
{
|
||||
if (response["tx_request"] == (uint8_t)TX_REQ_DEVCONTROL) {
|
||||
if (response["cmd"] == (uint8_t)ActivePowerContr) {
|
||||
uint16_t webapiPayload = response["payload"];
|
||||
uint16_t webapiPayload2 = response["payload2"];
|
||||
if (webapiPayload > 0 && webapiPayload < 10000)
|
||||
{
|
||||
if (webapiPayload > 0 && webapiPayload < 10000) {
|
||||
iv->devControlCmd = ActivePowerContr;
|
||||
iv->powerLimit[0] = webapiPayload;
|
||||
if (webapiPayload2 > 0)
|
||||
{
|
||||
iv->powerLimit[1] = webapiPayload2; // dev option, no sanity check
|
||||
}
|
||||
else
|
||||
{ // if not set, set it to 0x0000 default
|
||||
iv->powerLimit[1] = AbsolutNonPersistent; // payload will be seted temporay in Watt absolut
|
||||
}
|
||||
else // if not set, set it to 0x0000 default
|
||||
iv->powerLimit[1] = AbsolutNonPersistent; // payload will be seted temporary in Watt absolut
|
||||
if (iv->powerLimit[1] & 0x0001)
|
||||
{
|
||||
DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("% via REST API"));
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("W via REST API"));
|
||||
}
|
||||
iv->devControlRequest = true; // queue it in the request loop
|
||||
}
|
||||
}
|
||||
if (response["cmd"] == (uint8_t)TurnOff){
|
||||
if (response["cmd"] == (uint8_t)TurnOff) {
|
||||
iv->devControlCmd = TurnOff;
|
||||
iv->devControlRequest = true; // queue it in the request loop
|
||||
}
|
||||
if (response["cmd"] == (uint8_t)TurnOn){
|
||||
}
|
||||
if (response["cmd"] == (uint8_t)TurnOn) {
|
||||
iv->devControlCmd = TurnOn;
|
||||
iv->devControlRequest = true; // queue it in the request loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
request->send(200, "text/json", "{success:true}");
|
||||
|
@ -480,8 +467,10 @@ void web::serialCb(String msg) {
|
|||
strncpy(&mSerialBuf[mSerialBufFill], mMain->getTimeStr().c_str(), 9);
|
||||
mSerialBufFill += 9;
|
||||
}
|
||||
else
|
||||
else {
|
||||
mSerialBufFill = 0;
|
||||
mEvts->send("webSerial, buffer overflow!", "serial", millis());
|
||||
}
|
||||
mSerialAddTime = false;
|
||||
}
|
||||
|
||||
|
@ -493,7 +482,9 @@ void web::serialCb(String msg) {
|
|||
strncpy(&mSerialBuf[mSerialBufFill], msg.c_str(), length);
|
||||
mSerialBufFill += length;
|
||||
}
|
||||
else
|
||||
else {
|
||||
mSerialBufFill = 0;
|
||||
mEvts->send("webSerial, buffer overflow!", "serial", millis());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "app.h"
|
||||
#include "webApi.h"
|
||||
|
||||
#define WEB_SERIAL_BUF_SIZE 1024
|
||||
#define WEB_SERIAL_BUF_SIZE 2048
|
||||
|
||||
class app;
|
||||
class webApi;
|
||||
|
|
|
@ -23,7 +23,9 @@ webApi::webApi(AsyncWebServer *srv, app *app, sysConfig_t *sysCfg, config_t *con
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
void webApi::setup(void) {
|
||||
mSrv->on("/api", HTTP_GET, std::bind(&webApi::onApi, this, std::placeholders::_1));
|
||||
mSrv->on("/api", HTTP_GET, std::bind(&webApi::onApi, this, std::placeholders::_1));
|
||||
mSrv->on("/api", HTTP_POST, std::bind(&webApi::onApiPost, this, std::placeholders::_1)).onBody(
|
||||
std::bind(&webApi::onApiPostBody, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
||||
}
|
||||
|
||||
|
||||
|
@ -59,6 +61,44 @@ void webApi::onApi(AsyncWebServerRequest *request) {
|
|||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void webApi::onApiPost(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, "onApiPost");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void webApi::onApiPostBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
|
||||
DPRINTLN(DBG_VERBOSE, "onApiPostBody");
|
||||
DynamicJsonDocument json(200);
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, 200);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
DeserializationError err = deserializeJson(json, (const char *)data);
|
||||
root[F("success")] = (err) ? false : true;
|
||||
if(!err) {
|
||||
String path = request->url().substring(5);
|
||||
if(path == "ctrl")
|
||||
root[F("success")] = setCtrl(json, root);
|
||||
else {
|
||||
root[F("success")] = false;
|
||||
root[F("error")] = "Path not found: " + path;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (err.code()) {
|
||||
case DeserializationError::Ok: break;
|
||||
case DeserializationError::InvalidInput: root[F("error")] = F("Invalid input"); break;
|
||||
case DeserializationError::NoMemory: root[F("error")] = F("Not enough memory"); break;
|
||||
default: root[F("error")] = F("Deserialization failed"); break;
|
||||
}
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void webApi::getNotFound(JsonObject obj, String url) {
|
||||
JsonObject ep = obj.createNestedObject("avail_endpoints");
|
||||
|
@ -227,43 +267,100 @@ void webApi::getLive(JsonObject obj) {
|
|||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
|
||||
iv = mApp->mSys->getInverterByPos(i);
|
||||
if(NULL != iv) {
|
||||
JsonObject obj2 = invArr.createNestedObject();
|
||||
obj2[F("name")] = String(iv->name);
|
||||
obj2[F("channels")] = iv->channels;
|
||||
obj2[F("power_limit_read")] = iv->actPowerLimit;
|
||||
obj2[F("power_limit_active")] = NoPowerLimit != iv->powerLimit[1];
|
||||
obj2[F("last_alarm")] = String(iv->lastAlarmMsg);
|
||||
obj2[F("ts_last_success")] = iv->ts;
|
||||
if(iv->isLiveDataAssignment()) {
|
||||
JsonObject obj2 = invArr.createNestedObject();
|
||||
obj2[F("name")] = String(iv->name);
|
||||
obj2[F("channels")] = iv->channels;
|
||||
obj2[F("power_limit_read")] = iv->actPowerLimit;
|
||||
obj2[F("power_limit_active")] = NoPowerLimit != iv->powerLimit[1];
|
||||
obj2[F("last_alarm")] = String(iv->lastAlarmMsg);
|
||||
obj2[F("ts_last_success")] = iv->ts;
|
||||
|
||||
JsonArray ch = obj2.createNestedArray("ch");
|
||||
JsonArray ch0 = ch.createNestedArray();
|
||||
obj2[F("ch_names")][0] = "AC";
|
||||
for (uint8_t fld = 0; fld < 11; fld++) {
|
||||
pos = (iv->getPosByChFld(CH0, list[fld]));
|
||||
ch0[fld] = (0xff != pos) ? iv->getValue(pos) : 0.0;
|
||||
obj[F("ch0_fld_units")][fld] = (0xff != pos) ? String(iv->getUnit(pos)) : F("n/a");
|
||||
obj[F("ch0_fld_names")][fld] = (0xff != pos) ? String(iv->getFieldName(pos)) : F("n/a");
|
||||
}
|
||||
JsonArray ch = obj2.createNestedArray("ch");
|
||||
JsonArray ch0 = ch.createNestedArray();
|
||||
obj2[F("ch_names")][0] = "AC";
|
||||
for (uint8_t fld = 0; fld < 11; fld++) {
|
||||
pos = (iv->getPosByChFld(CH0, list[fld]));
|
||||
ch0[fld] = (0xff != pos) ? iv->getValue(pos) : 0.0;
|
||||
obj[F("ch0_fld_units")][fld] = (0xff != pos) ? String(iv->getUnit(pos)) : F("n/a");
|
||||
obj[F("ch0_fld_names")][fld] = (0xff != pos) ? String(iv->getFieldName(pos)) : F("n/a");
|
||||
}
|
||||
|
||||
for(uint8_t j = 1; j <= iv->channels; j ++) {
|
||||
obj2[F("ch_names")][j] = String(iv->chName[j-1]);
|
||||
JsonArray cur = ch.createNestedArray();
|
||||
for (uint8_t k = 0; k < 6; k++) {
|
||||
switch(k) {
|
||||
default: pos = (iv->getPosByChFld(j, FLD_UDC)); break;
|
||||
case 1: pos = (iv->getPosByChFld(j, FLD_IDC)); break;
|
||||
case 2: pos = (iv->getPosByChFld(j, FLD_PDC)); break;
|
||||
case 3: pos = (iv->getPosByChFld(j, FLD_YD)); break;
|
||||
case 4: pos = (iv->getPosByChFld(j, FLD_YT)); break;
|
||||
case 5: pos = (iv->getPosByChFld(j, FLD_IRR)); break;
|
||||
}
|
||||
cur[k] = (0xff != pos) ? iv->getValue(pos) : 0.0;
|
||||
if(1 == j) {
|
||||
obj[F("fld_units")][k] = (0xff != pos) ? String(iv->getUnit(pos)) : F("n/a");
|
||||
obj[F("fld_names")][k] = (0xff != pos) ? String(iv->getFieldName(pos)) : F("n/a");
|
||||
for(uint8_t j = 1; j <= iv->channels; j ++) {
|
||||
obj2[F("ch_names")][j] = String(iv->chName[j-1]);
|
||||
JsonArray cur = ch.createNestedArray();
|
||||
for (uint8_t k = 0; k < 6; k++) {
|
||||
switch(k) {
|
||||
default: pos = (iv->getPosByChFld(j, FLD_UDC)); break;
|
||||
case 1: pos = (iv->getPosByChFld(j, FLD_IDC)); break;
|
||||
case 2: pos = (iv->getPosByChFld(j, FLD_PDC)); break;
|
||||
case 3: pos = (iv->getPosByChFld(j, FLD_YD)); break;
|
||||
case 4: pos = (iv->getPosByChFld(j, FLD_YT)); break;
|
||||
case 5: pos = (iv->getPosByChFld(j, FLD_IRR)); break;
|
||||
}
|
||||
cur[k] = (0xff != pos) ? iv->getValue(pos) : 0.0;
|
||||
if(1 == j) {
|
||||
obj[F("fld_units")][k] = (0xff != pos) ? String(iv->getUnit(pos)) : F("n/a");
|
||||
obj[F("fld_names")][k] = (0xff != pos) ? String(iv->getFieldName(pos)) : F("n/a");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool webApi::setCtrl(DynamicJsonDocument jsonIn, JsonObject jsonOut) {
|
||||
uint8_t cmd = jsonIn[F("cmd")];
|
||||
if(TX_REQ_DEVCONTROL == jsonIn[F("tx_request")]) {
|
||||
DPRINTLN(DBG_INFO, F("devcontrol, cmd: 0x") + String(cmd, HEX));
|
||||
if(ActivePowerContr == cmd) {
|
||||
Inverter<> *iv = getInverter(jsonIn, jsonOut);
|
||||
if(NULL != iv) {
|
||||
JsonArray payload = jsonIn[F("payload")].as<JsonArray>();
|
||||
iv->powerLimit[0] = payload[0];
|
||||
iv->powerLimit[1] = payload[1];
|
||||
}
|
||||
}
|
||||
else if(TurnOn == cmd) {
|
||||
Inverter<> *iv = getInverter(jsonIn, jsonOut);
|
||||
if(NULL != iv) {
|
||||
iv->devControlCmd = TurnOn;
|
||||
iv->devControlRequest = true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if(TurnOff == cmd) {
|
||||
Inverter<> *iv = getInverter(jsonIn, jsonOut);
|
||||
if(NULL != iv) {
|
||||
iv->devControlCmd = TurnOff;
|
||||
iv->devControlRequest = true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
jsonOut["error"] = "unknown 'cmd' = " + String(cmd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
jsonOut["error"] = "unknown 'tx_request'";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Inverter<> *webApi::getInverter(DynamicJsonDocument jsonIn, JsonObject jsonOut) {
|
||||
uint8_t id = jsonIn[F("inverter")];
|
||||
Inverter<> *iv = mApp->mSys->getInverterByPos(id);
|
||||
if(NULL == iv)
|
||||
jsonOut["error"] = F("inverter index to high: ") + String(id);
|
||||
return iv;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ class webApi {
|
|||
|
||||
private:
|
||||
void onApi(AsyncWebServerRequest *request);
|
||||
void onApiPost(AsyncWebServerRequest *request);
|
||||
void onApiPostBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total);
|
||||
void getNotFound(JsonObject obj, String url);
|
||||
|
||||
void getSystem(JsonObject obj);
|
||||
|
@ -34,6 +36,10 @@ class webApi {
|
|||
void getSetup(JsonObject obj);
|
||||
void getLive(JsonObject obj);
|
||||
|
||||
bool setCtrl(DynamicJsonDocument jsonIn, JsonObject jsonOut);
|
||||
|
||||
Inverter<> *getInverter(DynamicJsonDocument jsonIn, JsonObject jsonOut);
|
||||
|
||||
AsyncWebServer *mSrv;
|
||||
app *mApp;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue