mirror of https://github.com/arendst/Tasmota.git
[SolaxX1] Add meter mode (#22330)
This commit is contained in:
parent
e4f431dc7b
commit
bbba5b9196
|
@ -1514,9 +1514,10 @@ bool Xdrv03(uint32_t function)
|
|||
case FUNC_SLEEP_LOOP:
|
||||
XnrgCall(FUNC_LOOP);
|
||||
break;
|
||||
case FUNC_EVERY_100_MSECOND:
|
||||
case FUNC_EVERY_250_MSECOND:
|
||||
if (TasmotaGlobal.uptime > 4) {
|
||||
XnrgCall(FUNC_EVERY_250_MSECOND);
|
||||
XnrgCall(function);
|
||||
}
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
|
|
|
@ -1916,9 +1916,10 @@ bool Xdrv03(uint32_t function)
|
|||
case FUNC_SLEEP_LOOP:
|
||||
XnrgCall(FUNC_LOOP);
|
||||
break;
|
||||
case FUNC_EVERY_100_MSECOND:
|
||||
case FUNC_EVERY_250_MSECOND:
|
||||
if (TasmotaGlobal.uptime > 4) {
|
||||
XnrgCall(FUNC_EVERY_250_MSECOND);
|
||||
XnrgCall(function);
|
||||
}
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
|
|
|
@ -37,9 +37,10 @@
|
|||
#define D_SOLAX_X1 "SolaxX1"
|
||||
|
||||
#include <TasmotaSerial.h>
|
||||
TasmotaSerial *solaxX1Serial;
|
||||
|
||||
const char kSolaxMode[] PROGMEM =
|
||||
D_OFF "|" D_SOLAX_MODE_0 "|" D_SOLAX_MODE_1 "|" D_SOLAX_MODE_2 "|" D_SOLAX_MODE_3 "|" D_SOLAX_MODE_4 "|"
|
||||
D_GATEWAY "|" D_OFF "|" D_SOLAX_MODE_0 "|" D_SOLAX_MODE_1 "|" D_SOLAX_MODE_2 "|" D_SOLAX_MODE_3 "|" D_SOLAX_MODE_4 "|"
|
||||
D_SOLAX_MODE_5 "|" D_SOLAX_MODE_6;
|
||||
|
||||
const char kSolaxError[] PROGMEM =
|
||||
|
@ -118,6 +119,10 @@ struct SOLAXX1_GLOBALDATA {
|
|||
uint8_t QueryID_count = 240;
|
||||
bool Command_QueryID = false;;
|
||||
bool Command_QueryConfig = false;
|
||||
bool MeterMode = false;
|
||||
float MeterPower = 5000;
|
||||
float MeterImport;
|
||||
float MeterExport;
|
||||
} solaxX1_global;
|
||||
|
||||
struct SOLAXX1_SENDDATA {
|
||||
|
@ -130,8 +135,6 @@ struct SOLAXX1_SENDDATA {
|
|||
uint8_t Payload[16] = {0x00};
|
||||
} solaxX1_SendData;
|
||||
|
||||
TasmotaSerial *solaxX1Serial;
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void solaxX1_RS485Send(void) {
|
||||
|
@ -143,27 +146,84 @@ void solaxX1_RS485Send(void) {
|
|||
memcpy(message + 7, solaxX1_SendData.FunctionCode, 1);
|
||||
memcpy(message + 8, solaxX1_SendData.DataLength, 1);
|
||||
memcpy(message + 9, solaxX1_SendData.Payload, sizeof(solaxX1_SendData.Payload));
|
||||
uint16_t crc = solaxX1_calculateCRC(message, 9 + solaxX1_SendData.DataLength[0]); // calculate out crc bytes
|
||||
while (solaxX1Serial->available() > 0) { // read serial if any old data is available
|
||||
solaxX1_RS485SendRaw(message, 9 + solaxX1_SendData.DataLength[0], 0);
|
||||
}
|
||||
|
||||
void solaxX1_RS485SendMeterFloat(float Value) {
|
||||
uint8_t MeterResponse[7] = {0x01, 0x04, 0x04, 0x00};
|
||||
for (uint8_t i = 0; i <= 3; i++) { // Store bytes in reverse order
|
||||
MeterResponse[i + 3] = *((char*)(&Value) + 3 - i);
|
||||
}
|
||||
solaxX1_RS485SendRaw(MeterResponse, 7, 1);
|
||||
}
|
||||
|
||||
void solaxX1_RS485SendMeterInt16(int16_t Value) {
|
||||
uint8_t MeterResponse[5] = {0x01, 0x03, 0x02, 0x00};
|
||||
MeterResponse[3] = highByte(Value);
|
||||
MeterResponse[4] = lowByte(Value);
|
||||
solaxX1_RS485SendRaw(MeterResponse, 5, 1);
|
||||
}
|
||||
|
||||
void solaxX1_RS485SendMeterTotalInt(uint32_t Export, uint32_t Import) {
|
||||
uint8_t MeterResponse[11] = {0x01, 0x03, 0x08, 0x00};
|
||||
for (uint8_t i = 0; i <= 3; i++) { // Store bytes in reverse order
|
||||
MeterResponse[i + 3] = *((char*)(&Export) + 3 - i);
|
||||
}
|
||||
for (uint8_t i = 0; i <= 3; i++) { // Store bytes in reverse order
|
||||
MeterResponse[i + 7] = *((char*)(&Import) + 3 - i);
|
||||
}
|
||||
solaxX1_RS485SendRaw(MeterResponse, 11, 1);
|
||||
}
|
||||
|
||||
void solaxX1_RS485SendRaw(uint8_t *SendBuffer, uint8_t DataLen, uint8_t CRCflag) {
|
||||
uint16_t crc;
|
||||
while (solaxX1Serial->available()) { // read serial if any old data is available
|
||||
solaxX1Serial->read();
|
||||
}
|
||||
if (PinUsed(GPIO_SOLAXX1_RTS)) {
|
||||
digitalWrite(Pin(GPIO_SOLAXX1_RTS), HIGH);
|
||||
if (PinUsed(GPIO_SOLAXX1_RTS)) digitalWrite(Pin(GPIO_SOLAXX1_RTS), HIGH);
|
||||
solaxX1Serial->flush();
|
||||
solaxX1Serial->write(SendBuffer, DataLen);
|
||||
if (CRCflag) {
|
||||
crc = solaxX1_calculateCRC_MBUS(SendBuffer, DataLen); // Use CRC MBUS algorithm
|
||||
solaxX1Serial->write(lowByte(crc));
|
||||
solaxX1Serial->write(highByte(crc));
|
||||
} else {
|
||||
crc = solaxX1_calculateCRC(SendBuffer, DataLen); // Use CRC Solax algorithm
|
||||
solaxX1Serial->write(highByte(crc));
|
||||
solaxX1Serial->write(lowByte(crc));
|
||||
}
|
||||
solaxX1Serial->flush();
|
||||
solaxX1Serial->write(message, 9 + solaxX1_SendData.DataLength[0]);
|
||||
solaxX1Serial->write(highByte(crc));
|
||||
solaxX1Serial->write(lowByte(crc));
|
||||
solaxX1Serial->flush();
|
||||
if (PinUsed(GPIO_SOLAXX1_RTS)) {
|
||||
digitalWrite(Pin(GPIO_SOLAXX1_RTS), LOW);
|
||||
}
|
||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, 9 + solaxX1_SendData.DataLength[0]);
|
||||
if (PinUsed(GPIO_SOLAXX1_RTS)) digitalWrite(Pin(GPIO_SOLAXX1_RTS), LOW);
|
||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, SendBuffer, DataLen);
|
||||
}
|
||||
|
||||
bool solaxX1_RS485Receive(uint8_t *ReadBuffer) {
|
||||
uint32_t SerWatchdogTime;
|
||||
|
||||
// Read header
|
||||
uint8_t len = 0;
|
||||
while (solaxX1Serial->available() > 0) {
|
||||
SerWatchdogTime = millis();
|
||||
while (len < 2) { // read exact length because of unaccurate timing of the inverter
|
||||
if (solaxX1Serial->available()) ReadBuffer[len++] = (uint8_t)solaxX1Serial->read();
|
||||
if (millis() > (SerWatchdogTime + 1000)) return true; // No data received -> bail out
|
||||
}
|
||||
|
||||
// Check and set meter mode
|
||||
solaxX1_SwitchMeterMode((ReadBuffer[0] == 0x01 || ReadBuffer[0] == 0x02) && (ReadBuffer[1] == 0x03 || ReadBuffer[1] == 0x04));
|
||||
|
||||
// Read data in meter mode
|
||||
if (solaxX1_global.MeterMode) { // Metermode
|
||||
SerWatchdogTime = millis();
|
||||
while (len < 8) { // read exact length because of unaccurate timing of the inverter
|
||||
if (solaxX1Serial->available()) ReadBuffer[len++] = (uint8_t)solaxX1Serial->read();
|
||||
if (millis() > (SerWatchdogTime + 1000)) return true; // No data received -> bail out
|
||||
}
|
||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, ReadBuffer, len);
|
||||
return false; // Ignore checksum
|
||||
} // end Metermode
|
||||
|
||||
// Process normal receive
|
||||
while (solaxX1Serial->available()) {
|
||||
ReadBuffer[len++] = (uint8_t)solaxX1Serial->read();
|
||||
}
|
||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, ReadBuffer, len);
|
||||
|
@ -175,11 +235,27 @@ uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) {
|
|||
uint8_t i;
|
||||
uint16_t wChkSum = 0;
|
||||
for (i = 0; i < bLen; i++) {
|
||||
wChkSum = wChkSum + bExternTxPackage[i];
|
||||
wChkSum += bExternTxPackage[i];
|
||||
}
|
||||
return wChkSum;
|
||||
}
|
||||
|
||||
uint16_t solaxX1_calculateCRC_MBUS(uint8_t *frame, uint8_t Len) {
|
||||
uint16_t crc = 0xFFFF;
|
||||
for (uint32_t i = 0; i < Len; i++) {
|
||||
crc ^= frame[i];
|
||||
for (uint32_t j = 8; j; j--) {
|
||||
if ((crc & 0x0001) != 0) { // If the LSB is set
|
||||
crc >>= 1; // Shift right and XOR 0xA001
|
||||
crc ^= 0xA001;
|
||||
} else { // Else LSB is not set
|
||||
crc >>= 1; // Just shift right
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void solaxX1_ExtractText(uint8_t *DataIn, uint8_t *DataOut, uint8_t Begin, uint8_t End) {
|
||||
uint8_t i;
|
||||
for (i = Begin; i <= End; i++) {
|
||||
|
@ -253,27 +329,78 @@ uint8_t solaxX1_ParseErrorCode(uint32_t code) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void solaxX1_SwitchMeterMode(bool MeterMode) {
|
||||
if (solaxX1_global.MeterMode == MeterMode) return;
|
||||
solaxX1_global.MeterMode = MeterMode;
|
||||
if (MeterMode) {
|
||||
Energy->data_valid[0] = ENERGY_WATCHDOG;
|
||||
solaxX1.runMode = -2;
|
||||
solaxX1.temperature = solaxX1.dc1_voltage = solaxX1.dc1_current = solaxX1.dc1_power = solaxX1.dc2_voltage = solaxX1.dc2_current = solaxX1.dc2_power = 0;
|
||||
} else {
|
||||
solaxX1.runMode = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
void solaxX1_250MSecond(void) { // Every 250 milliseconds
|
||||
void solaxX1_CyclicTask(void) { // Every 100/250 milliseconds
|
||||
uint8_t DataRead[80] = {0};
|
||||
uint8_t TempData[16] = {0};
|
||||
char TempDataChar[32];
|
||||
float TempFloat;
|
||||
static uint32_t LastMeterTime;
|
||||
static uint16_t MtrReg, MtrPwr32, MtrPwr16, MtrImp32, MtrExp32, MrtTot64, MtrRest;
|
||||
|
||||
if (solaxX1Serial->available()) {
|
||||
if (solaxX1_RS485Receive(DataRead)) { // CRC-error -> no further action
|
||||
DEBUG_SENSOR_LOG(PSTR("SX1: Data response CRC error"));
|
||||
if (solaxX1_RS485Receive(DataRead)) { // CRC or other error -> no further action
|
||||
AddLog(LOG_LEVEL_ERROR, PSTR("SX1: (CRC) error in received data"));
|
||||
return;
|
||||
}
|
||||
|
||||
solaxX1_global.SendRetry_count = 20; // Inverter is responding
|
||||
|
||||
if (DataRead[0] != 0xAA || DataRead[1] != 0x55) { // Check for header
|
||||
DEBUG_SENSOR_LOG(PSTR("SX1: Check for header failed"));
|
||||
if (solaxX1_global.MeterMode) { // Metermode
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("SX1: Metermode %02X %02X xx %02X"), DataRead[0], DataRead[1], DataRead[3]);
|
||||
LastMeterTime = TasmotaGlobal.uptime;
|
||||
if (DataRead[0] != 0x01) return; // Respond only to requests for meter #1
|
||||
switch (DataRead[3]) {
|
||||
case 0x0B: // received "Register meter request"
|
||||
//solaxX1_RS485SendMeterInt16(0); // Tell inverter to request int16 values
|
||||
solaxX1_RS485SendMeterInt16(0xa8); // Tell inverter to request float32 values
|
||||
MtrReg++;
|
||||
break;
|
||||
case 0x0C: // received "Power request (32 bit float)"
|
||||
solaxX1_RS485SendMeterFloat(solaxX1_global.MeterPower);
|
||||
MtrPwr32++;
|
||||
break;
|
||||
case 0x0E: // received "Power request (16 bit int)"
|
||||
solaxX1_RS485SendMeterInt16((int16_t)solaxX1_global.MeterPower);
|
||||
MtrPwr16++;
|
||||
break;
|
||||
case 0x48: // received "Import request (32 bit float)"
|
||||
solaxX1_RS485SendMeterFloat(solaxX1_global.MeterImport);
|
||||
MtrImp32++;
|
||||
break;
|
||||
case 0x4A: // received "Export request (32 bit float)"
|
||||
solaxX1_RS485SendMeterFloat(solaxX1_global.MeterExport);
|
||||
MtrExp32++;
|
||||
break;
|
||||
case 0x08: // received "Energy total request (2*32 bit uint)"
|
||||
solaxX1_RS485SendMeterTotalInt((uint32_t)(solaxX1_global.MeterExport * 100.0), (uint32_t)(solaxX1_global.MeterImport * 100.0));
|
||||
MrtTot64++;
|
||||
break;
|
||||
default:
|
||||
MtrRest++;
|
||||
}
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("SX1: MtrReg %d, MtrPwr32 %d, MtrPwr16 %d, MtrImp32 %d, MtrExp32 %d, MrtTot64 %d, MtrRest %d"), MtrReg, MtrPwr32, MtrPwr16, MtrImp32, MtrExp32, MrtTot64, MtrRest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DataRead[0] != 0xAA || DataRead[1] != 0x55) { // Check for header
|
||||
AddLog(LOG_LEVEL_ERROR, PSTR("SX1: Header check failed: %02X %02X"), DataRead[0], DataRead[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
solaxX1_global.SendRetry_count = 20; // Inverter is responding
|
||||
|
||||
if (DataRead[6] == 0x11 && DataRead[7] == 0x82) { // received "Response for query (live data)"
|
||||
Energy->data_valid[0] = 0;
|
||||
solaxX1.temperature = (DataRead[9] << 8) | DataRead[10]; // Temperature
|
||||
|
@ -416,6 +543,11 @@ void solaxX1_250MSecond(void) { // Every 250 milliseconds
|
|||
|
||||
} // end solaxX1Serial->available()
|
||||
|
||||
if(solaxX1_global.MeterMode) {
|
||||
if (TasmotaGlobal.uptime > LastMeterTime + 20) solaxX1_SwitchMeterMode(false); // Switch back to normal mode, when no Meter request is received for 20 sec.
|
||||
return;
|
||||
}
|
||||
|
||||
// DEBUG_SENSOR_LOG(PSTR("SX1: solaxX1_global.AddressAssigned: %d, solaxX1_global.QueryData_count: %d, solaxX1_global.SendRetry_count: %d, solaxX1_global.QueryID_count: %d"), solaxX1_global.AddressAssigned, solaxX1_global.QueryData_count, solaxX1_global.SendRetry_count, solaxX1_global.QueryID_count);
|
||||
if (solaxX1_global.AddressAssigned) {
|
||||
if (!solaxX1_global.QueryData_count) { // normal periodically query
|
||||
|
@ -437,13 +569,13 @@ void solaxX1_250MSecond(void) { // Every 250 milliseconds
|
|||
solaxX1_global.SendRetry_count = 20;
|
||||
DEBUG_SENSOR_LOG(PSTR("SX1: Inverter went \"off\""));
|
||||
Energy->data_valid[0] = ENERGY_WATCHDOG;
|
||||
solaxX1.temperature = solaxX1.dc1_voltage = solaxX1.dc2_voltage = solaxX1.dc1_current = solaxX1.dc2_current = solaxX1.dc1_power = 0;
|
||||
solaxX1.dc2_power = Energy->current[0] = Energy->voltage[0] = Energy->frequency[0] = Energy->active_power[0] = 0;
|
||||
solaxX1.temperature = solaxX1.dc1_voltage = solaxX1.dc1_current = solaxX1.dc1_power = solaxX1.dc2_voltage = solaxX1.dc2_current = solaxX1.dc2_power = 0;
|
||||
solaxX1.runMode = -1; // off(line)
|
||||
solaxX1_global.AddressAssigned = false;
|
||||
} // end Inverter went "off"
|
||||
} else { // sent query for inverters in offline status
|
||||
if (!solaxX1_global.SendRetry_count) {
|
||||
solaxX1_global.Command_QueryConfig = solaxX1_global.Command_QueryID = false; // Clear commands to be sure
|
||||
solaxX1_global.SendRetry_count = 20;
|
||||
DEBUG_SENSOR_LOG(PSTR("SX1: Sent query for inverters in offline state"));
|
||||
solaxX1_QueryOfflineInverters();
|
||||
|
@ -451,7 +583,7 @@ void solaxX1_250MSecond(void) { // Every 250 milliseconds
|
|||
}
|
||||
solaxX1_global.SendRetry_count--;
|
||||
return;
|
||||
} // end solaxX1_250MSecond
|
||||
} // end solaxX1_CyclicTask
|
||||
|
||||
void solaxX1_SnsInit(void) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("SX1: Init - RX-pin: %d, TX-pin: %d, RTS-pin: %d"), Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), Pin(GPIO_SOLAXX1_RTS));
|
||||
|
@ -461,22 +593,40 @@ void solaxX1_SnsInit(void) {
|
|||
#ifdef ESP32
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("SX1: Serial UART%d"), solaxX1Serial->getUart());
|
||||
#endif
|
||||
if (PinUsed(GPIO_SOLAXX1_RTS)) pinMode(Pin(GPIO_SOLAXX1_RTS), OUTPUT);
|
||||
} else {
|
||||
TasmotaGlobal.energy_driver = ENERGY_NONE;
|
||||
}
|
||||
if (PinUsed(GPIO_SOLAXX1_RTS)) {
|
||||
pinMode(Pin(GPIO_SOLAXX1_RTS), OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
void solaxX1_DrvInit(void) {
|
||||
if (PinUsed(GPIO_SOLAXX1_RX) && PinUsed(GPIO_SOLAXX1_TX)) {
|
||||
TasmotaGlobal.energy_driver = XNRG_12;
|
||||
Energy->type_dc = true; // Handle like DC, because U*I from inverter is not valid for apparent power; U*I could be lower than active power
|
||||
Energy->frequency[0] = 0; // Set value, to make frequency present in output
|
||||
}
|
||||
}
|
||||
|
||||
bool SolaxX1_cmd(void) {
|
||||
if (Energy->command_code != CMND_ENERGYCONFIG) return false; // Process unchanged data
|
||||
|
||||
if (!strncasecmp(XdrvMailbox.data, "MeterPower", 10)) {
|
||||
solaxX1_global.MeterPower = CharToFloat(&XdrvMailbox.data[11]);
|
||||
ResponseCmndFloat(solaxX1_global.MeterPower, 1);
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("SX1: MeterPower: %3_f"), &solaxX1_global.MeterPower);
|
||||
return false;
|
||||
} else if (!strncasecmp(XdrvMailbox.data, "MeterImport", 11)) {
|
||||
solaxX1_global.MeterImport = CharToFloat(&XdrvMailbox.data[12]);
|
||||
ResponseCmndFloat(solaxX1_global.MeterImport, 8);
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("SX1: MeterImport: %3_f"), &solaxX1_global.MeterImport);
|
||||
return false;
|
||||
} else if (!strncasecmp(XdrvMailbox.data, "MeterExport", 11)) {
|
||||
solaxX1_global.MeterExport = CharToFloat(&XdrvMailbox.data[12]);
|
||||
ResponseCmndFloat(solaxX1_global.MeterExport, 8);
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("SX1: MeterExport: %3_f"), &solaxX1_global.MeterExport);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!solaxX1_global.AddressAssigned) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("SX1: No inverter registered"));
|
||||
return false;
|
||||
|
@ -484,12 +634,10 @@ bool SolaxX1_cmd(void) {
|
|||
|
||||
if (!strcasecmp(XdrvMailbox.data, "ReadIDinfo")) {
|
||||
solaxX1_global.Command_QueryID = true;
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("SX1: ReadIDinfo sent..."));
|
||||
return true;
|
||||
} else if (!strcasecmp(XdrvMailbox.data, "ReadConfig")) {
|
||||
#ifdef SOLAXX1_READCONFIG
|
||||
solaxX1_global.Command_QueryConfig = true;
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("SX1: ReadConfig sent..."));
|
||||
return true;
|
||||
#else
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("SX1: Command not available. Please set compiler directive '#define SOLAXX1_READCONFIG'."));
|
||||
|
@ -501,8 +649,9 @@ bool SolaxX1_cmd(void) {
|
|||
}
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
const char HTTP_SNS_solaxX1_Num[] PROGMEM = "{s}" D_SOLAX_X1 " %s{m}</td><td style='text-align:%s'>%s{m}{m} %s{e}";
|
||||
const char HTTP_SNS_solaxX1_Str[] PROGMEM = "{s}" D_SOLAX_X1 " %s{m}%s{e}";
|
||||
const char HTTP_SNS_solaxX1_Num[] PROGMEM = "{s}" D_SOLAX_X1 " %s{m}</td><td style='text-align:%s'>%s{m}{m}%s{e}";
|
||||
const char HTTP_SNS_solaxX1_Str[] PROGMEM = "{s}" D_SOLAX_X1 " %s</td><td style='text-align:right'>%s{e}";
|
||||
const char HTTP_SNS_solaxX1_Mtr[] PROGMEM = "{s}" D_GATEWAY " %s{m}</td><td style='text-align:%s'>%s{m}{m}%s{e}";
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
void solaxX1_Show(uint32_t function) {
|
||||
|
@ -523,7 +672,7 @@ void solaxX1_Show(uint32_t function) {
|
|||
dtostrfd(solaxX1.dc2_power, Settings->flag2.wattage_resolution, pv2_power);
|
||||
#endif
|
||||
char status[33];
|
||||
GetTextIndexed(status, sizeof(status), solaxX1.runMode + 1, kSolaxMode);
|
||||
GetTextIndexed(status, sizeof(status), solaxX1.runMode + 2, kSolaxMode);
|
||||
|
||||
switch (function) {
|
||||
case FUNC_JSON_APPEND:
|
||||
|
@ -544,10 +693,23 @@ void solaxX1_Show(uint32_t function) {
|
|||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_COL_SENSOR: {
|
||||
String table_align = Settings->flag5.gui_table_align?"right":"left";
|
||||
if (solaxX1_global.MeterMode) {
|
||||
char TempDataChar[33];
|
||||
WSContentSend_P(PSTR("<tr><td colspan=5 style='font-size:2px'><hr size=1/>{e}"));
|
||||
dtostrfd(solaxX1_global.MeterPower, Settings->flag2.wattage_resolution, TempDataChar);
|
||||
WSContentSend_PD(HTTP_SNS_solaxX1_Mtr, D_POWERUSAGE, table_align.c_str(), TempDataChar, D_UNIT_WATT);
|
||||
dtostrfd(solaxX1_global.MeterImport, Settings->flag2.energy_resolution, TempDataChar);
|
||||
WSContentSend_PD(HTTP_SNS_solaxX1_Mtr, "Import", table_align.c_str(), TempDataChar, D_UNIT_KILOWATTHOUR);
|
||||
dtostrfd(solaxX1_global.MeterExport, Settings->flag2.energy_resolution, TempDataChar);
|
||||
WSContentSend_PD(HTTP_SNS_solaxX1_Mtr, "Export", table_align.c_str(), TempDataChar, D_UNIT_KILOWATTHOUR);
|
||||
return;
|
||||
}
|
||||
static uint32_t LastOnlineTime;
|
||||
if (solaxX1.runMode != -1) LastOnlineTime = TasmotaGlobal.uptime;
|
||||
if (TasmotaGlobal.uptime < LastOnlineTime + 300) { // Hide numeric live data, when inverter is offline for more than 5 min
|
||||
#ifdef SOLAXX1_PV2
|
||||
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_SOLAR_POWER, table_align.c_str(), solar_power, D_UNIT_WATT);
|
||||
#endif
|
||||
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV1_VOLTAGE, table_align.c_str(), pv1_voltage, D_UNIT_VOLT);
|
||||
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV1_CURRENT, table_align.c_str(), pv1_current, D_UNIT_AMPERE);
|
||||
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV1_POWER, table_align.c_str(), pv1_power, D_UNIT_WATT);
|
||||
|
@ -563,6 +725,7 @@ void solaxX1_Show(uint32_t function) {
|
|||
WSContentSend_P(HTTP_SNS_solaxX1_Num, D_UPTIME, table_align.c_str(), String(solaxX1.runtime_total).c_str(), D_UNIT_HOUR);
|
||||
break; }
|
||||
case FUNC_WEB_SENSOR:
|
||||
if (solaxX1_global.MeterMode) return;
|
||||
char errorCodeString[33];
|
||||
WSContentSend_P(HTTP_SNS_solaxX1_Str, D_STATUS, status);
|
||||
WSContentSend_P(HTTP_SNS_solaxX1_Str, D_ERROR, GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError));
|
||||
|
@ -580,8 +743,11 @@ bool Xnrg12(uint32_t function) {
|
|||
bool result = false;
|
||||
|
||||
switch (function) {
|
||||
case FUNC_EVERY_100_MSECOND:
|
||||
if (solaxX1_global.MeterMode) solaxX1_CyclicTask();
|
||||
break;
|
||||
case FUNC_EVERY_250_MSECOND:
|
||||
solaxX1_250MSecond();
|
||||
if (!solaxX1_global.MeterMode) solaxX1_CyclicTask();
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_COL_SENSOR:
|
||||
|
|
Loading…
Reference in New Issue