[SolaxX1] Improve web presentation (#20535)

* [SolaxX1] Improve web presentation

* [SolaxX1] delay
This commit is contained in:
SteWers 2024-01-20 11:10:40 +01:00 committed by GitHub
parent 6c3f70589a
commit 47384e7057
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 61 additions and 69 deletions

View File

@ -108,7 +108,7 @@ struct SOLAXX1_LIVEDATA {
float dc2_power = 0; float dc2_power = 0;
int16_t runMode = 0; int16_t runMode = 0;
uint32_t errorCode = 0; uint32_t errorCode = 0;
uint8_t SerialNumber[16] = {0x6e, 0x2f, 0x61}; // "n/a" uint8_t SerialNumber[16] = {0x00};
} solaxX1; } solaxX1;
struct SOLAXX1_GLOBALDATA { struct SOLAXX1_GLOBALDATA {
@ -127,15 +127,14 @@ struct SOLAXX1_SENDDATA {
uint8_t ControlCode[1] = {0x00}; uint8_t ControlCode[1] = {0x00};
uint8_t FunctionCode[1] = {0x00}; uint8_t FunctionCode[1] = {0x00};
uint8_t DataLength[1] = {0x00}; uint8_t DataLength[1] = {0x00};
uint8_t Payload[16] = {0}; uint8_t Payload[16] = {0x00};
} solaxX1_SendData; } solaxX1_SendData;
TasmotaSerial *solaxX1Serial; TasmotaSerial *solaxX1Serial;
/*********************************************************************************************/ /*********************************************************************************************/
void solaxX1_RS485Send(void) void solaxX1_RS485Send(void) {
{
uint8_t message[30]; uint8_t message[30];
memcpy(message, solaxX1_SendData.Header, 2); memcpy(message, solaxX1_SendData.Header, 2);
memcpy(message + 2, solaxX1_SendData.Source, 2); memcpy(message + 2, solaxX1_SendData.Source, 2);
@ -162,8 +161,7 @@ void solaxX1_RS485Send(void)
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, 9 + solaxX1_SendData.DataLength[0]); AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, 9 + solaxX1_SendData.DataLength[0]);
} }
bool solaxX1_RS485Receive(uint8_t *ReadBuffer) bool solaxX1_RS485Receive(uint8_t *ReadBuffer) {
{
uint8_t len = 0; uint8_t len = 0;
while (solaxX1Serial->available() > 0) { while (solaxX1Serial->available() > 0) {
ReadBuffer[len++] = (uint8_t)solaxX1Serial->read(); ReadBuffer[len++] = (uint8_t)solaxX1Serial->read();
@ -173,8 +171,7 @@ bool solaxX1_RS485Receive(uint8_t *ReadBuffer)
return !(ReadBuffer[len - 1] == lowByte(crc) && ReadBuffer[len - 2] == highByte(crc)); return !(ReadBuffer[len - 1] == lowByte(crc) && ReadBuffer[len - 2] == highByte(crc));
} }
uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) {
{
uint8_t i; uint8_t i;
uint16_t wChkSum = 0; uint16_t wChkSum = 0;
for (i = 0; i < bLen; i++) { for (i = 0; i < bLen; i++) {
@ -183,8 +180,7 @@ uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen)
return wChkSum; return wChkSum;
} }
void solaxX1_ExtractText(uint8_t *DataIn, uint8_t *DataOut, uint8_t Begin, uint8_t End) void solaxX1_ExtractText(uint8_t *DataIn, uint8_t *DataOut, uint8_t Begin, uint8_t End) {
{
uint8_t i; uint8_t i;
for (i = Begin; i <= End; i++) { for (i = Begin; i <= End; i++) {
DataOut[i - Begin] = DataIn[i]; DataOut[i - Begin] = DataIn[i];
@ -192,8 +188,7 @@ void solaxX1_ExtractText(uint8_t *DataIn, uint8_t *DataOut, uint8_t Begin, uint8
DataOut[End - Begin + 1] = 0; DataOut[End - Begin + 1] = 0;
} }
void solaxX1_QueryOfflineInverters(void) void solaxX1_QueryOfflineInverters(void) {
{
solaxX1_SendData.Source[0] = 0x01; solaxX1_SendData.Source[0] = 0x01;
solaxX1_SendData.Destination[0] = 0x00; solaxX1_SendData.Destination[0] = 0x00;
solaxX1_SendData.Destination[1] = 0x00; solaxX1_SendData.Destination[1] = 0x00;
@ -203,8 +198,7 @@ void solaxX1_QueryOfflineInverters(void)
solaxX1_RS485Send(); solaxX1_RS485Send();
} }
void solaxX1_SendInverterAddress(void) void solaxX1_SendInverterAddress(void) {
{
solaxX1_SendData.Source[0] = 0x00; solaxX1_SendData.Source[0] = 0x00;
solaxX1_SendData.Destination[0] = 0x00; solaxX1_SendData.Destination[0] = 0x00;
solaxX1_SendData.Destination[1] = 0x00; solaxX1_SendData.Destination[1] = 0x00;
@ -215,8 +209,7 @@ void solaxX1_SendInverterAddress(void)
solaxX1_RS485Send(); solaxX1_RS485Send();
} }
void solaxX1_QueryLiveData(void) void solaxX1_QueryLiveData(void) {
{
solaxX1_SendData.Source[0] = 0x01; solaxX1_SendData.Source[0] = 0x01;
solaxX1_SendData.Destination[0] = 0x00; solaxX1_SendData.Destination[0] = 0x00;
solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; solaxX1_SendData.Destination[1] = INVERTER_ADDRESS;
@ -226,8 +219,7 @@ void solaxX1_QueryLiveData(void)
solaxX1_RS485Send(); solaxX1_RS485Send();
} }
void solaxX1_QueryIDData(void) void solaxX1_QueryIDData(void) {
{
solaxX1_SendData.Source[0] = 0x01; solaxX1_SendData.Source[0] = 0x01;
solaxX1_SendData.Destination[0] = 0x00; solaxX1_SendData.Destination[0] = 0x00;
solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; solaxX1_SendData.Destination[1] = INVERTER_ADDRESS;
@ -237,8 +229,7 @@ void solaxX1_QueryIDData(void)
solaxX1_RS485Send(); solaxX1_RS485Send();
} }
void solaxX1_QueryConfigData(void) void solaxX1_QueryConfigData(void) {
{
solaxX1_SendData.Source[0] = 0x01; solaxX1_SendData.Source[0] = 0x01;
solaxX1_SendData.Destination[0] = 0x00; solaxX1_SendData.Destination[0] = 0x00;
solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; solaxX1_SendData.Destination[1] = INVERTER_ADDRESS;
@ -248,7 +239,7 @@ void solaxX1_QueryConfigData(void)
solaxX1_RS485Send(); solaxX1_RS485Send();
} }
uint8_t solaxX1_ParseErrorCode(uint32_t code){ uint8_t solaxX1_ParseErrorCode(uint32_t code) {
solaxX1_ErrCode.ErrMessage = code; solaxX1_ErrCode.ErrMessage = code;
if (code == 0) return 0; if (code == 0) return 0;
if (solaxX1_ErrCode.MainsLostFault) return 1; if (solaxX1_ErrCode.MainsLostFault) return 1;
@ -264,8 +255,7 @@ uint8_t solaxX1_ParseErrorCode(uint32_t code){
/*********************************************************************************************/ /*********************************************************************************************/
void solaxX1_250MSecond(void) // Every 250 milliseconds void solaxX1_250MSecond(void) { // Every 250 milliseconds
{
uint8_t DataRead[80] = {0}; uint8_t DataRead[80] = {0};
uint8_t TempData[16] = {0}; uint8_t TempData[16] = {0};
char TempDataChar[32]; char TempDataChar[32];
@ -299,7 +289,8 @@ void solaxX1_250MSecond(void) // Every 250 milliseconds
Energy->apparent_power[0] = Energy->active_power[0]; // U*I from inverter is not valid for apparent power; U*I could be lower than active power Energy->apparent_power[0] = Energy->active_power[0]; // U*I from inverter is not valid for apparent power; U*I could be lower than active power
//temporal = (float)((DataRead[29] << 8) | DataRead[30]) * 0.1f; // Not Used //temporal = (float)((DataRead[29] << 8) | DataRead[30]) * 0.1f; // Not Used
Energy->import_active[0] = ((DataRead[31] << 24) | (DataRead[32] << 16) | (DataRead[33] << 8) | DataRead[34]) * 0.1f; // Energy Total Energy->import_active[0] = ((DataRead[31] << 24) | (DataRead[32] << 16) | (DataRead[33] << 8) | DataRead[34]) * 0.1f; // Energy Total
solaxX1.runtime_total = (DataRead[35] << 24) | (DataRead[36] << 16) | (DataRead[37] << 8) | DataRead[38]; // Work Time Total uint32_t runtime_total = (DataRead[35] << 24) | (DataRead[36] << 16) | (DataRead[37] << 8) | DataRead[38]; // Work Time Total
if (runtime_total) solaxX1.runtime_total = runtime_total; // Work Time valid
solaxX1.runMode = (DataRead[39] << 8) | DataRead[40]; // Work mode solaxX1.runMode = (DataRead[39] << 8) | DataRead[40]; // Work mode
//temporal = (float)((DataRead[41] << 8) | DataRead[42]); // Grid voltage fault value 0.1V //temporal = (float)((DataRead[41] << 8) | DataRead[42]); // Grid voltage fault value 0.1V
//temporal = (float)((DataRead[43] << 8) | DataRead[44]); // Gird frequency fault value 0.01Hz //temporal = (float)((DataRead[43] << 8) | DataRead[44]); // Gird frequency fault value 0.01Hz
@ -460,12 +451,10 @@ void solaxX1_250MSecond(void) // Every 250 milliseconds
} }
} }
solaxX1_global.SendRetry_count--; solaxX1_global.SendRetry_count--;
return; return;
} // end solaxX1_250MSecond } // end solaxX1_250MSecond
void solaxX1_SnsInit(void) 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)); 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));
solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1); solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1);
if (solaxX1Serial->begin(SOLAXX1_SPEED)) { if (solaxX1Serial->begin(SOLAXX1_SPEED)) {
@ -481,15 +470,13 @@ void solaxX1_SnsInit(void)
} }
} }
void solaxX1_DrvInit(void) void solaxX1_DrvInit(void) {
{
if (PinUsed(GPIO_SOLAXX1_RX) && PinUsed(GPIO_SOLAXX1_TX)) { if (PinUsed(GPIO_SOLAXX1_RX) && PinUsed(GPIO_SOLAXX1_TX)) {
TasmotaGlobal.energy_driver = XNRG_12; TasmotaGlobal.energy_driver = XNRG_12;
} }
} }
bool SolaxX1_cmd(void) bool SolaxX1_cmd(void) {
{
if (!solaxX1_global.AddressAssigned) { if (!solaxX1_global.AddressAssigned) {
AddLog(LOG_LEVEL_INFO, PSTR("SX1: No inverter registered")); AddLog(LOG_LEVEL_INFO, PSTR("SX1: No inverter registered"));
return false; return false;
@ -514,12 +501,11 @@ bool SolaxX1_cmd(void)
} }
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
const char HTTP_SNS_solaxX1_Num[] PROGMEM = "{s}" D_SOLAX_X1 " %s{m}</td><td style='text-align:%s'>%s</td><td>&nbsp;</td><td> %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{m}</td><td colspan='3' style='text-align:%s'>%s{e}"; const char HTTP_SNS_solaxX1_Str[] PROGMEM = "{s}" D_SOLAX_X1 " %s{m}%s{e}";
#endif // USE_WEBSERVER #endif // USE_WEBSERVER
void solaxX1_Show(bool json) void solaxX1_Show(uint32_t function) {
{
char solar_power[33]; char solar_power[33];
dtostrfd(solaxX1.dc1_power + solaxX1.dc2_power, Settings->flag2.wattage_resolution, solar_power); dtostrfd(solaxX1.dc1_power + solaxX1.dc2_power, Settings->flag2.wattage_resolution, solar_power);
char pv1_voltage[33]; char pv1_voltage[33];
@ -539,41 +525,49 @@ void solaxX1_Show(bool json)
char status[33]; char status[33];
GetTextIndexed(status, sizeof(status), solaxX1.runMode + 1, kSolaxMode); GetTextIndexed(status, sizeof(status), solaxX1.runMode + 1, kSolaxMode);
if (json) { switch (function) {
ResponseAppend_P(PSTR(",\"" D_JSON_SOLAR_POWER "\":%s,\"" D_JSON_PV1_VOLTAGE "\":%s,\"" D_JSON_PV1_CURRENT "\":%s,\"" D_JSON_PV1_POWER "\":%s"), case FUNC_JSON_APPEND:
solar_power, pv1_voltage, pv1_current, pv1_power); ResponseAppend_P(PSTR(",\"" D_JSON_SOLAR_POWER "\":%s,\"" D_JSON_PV1_VOLTAGE "\":%s,\"" D_JSON_PV1_CURRENT "\":%s,\"" D_JSON_PV1_POWER "\":%s"),
solar_power, pv1_voltage, pv1_current, pv1_power);
#ifdef SOLAXX1_PV2 #ifdef SOLAXX1_PV2
ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"), ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"),
pv2_voltage, pv2_current, pv2_power); pv2_voltage, pv2_current, pv2_power);
#endif #endif
ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%d,\"" D_JSON_RUNTIME "\":%d,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"), ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%d,\"" D_JSON_RUNTIME "\":%d,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"),
solaxX1.temperature, solaxX1.runtime_total, status, solaxX1.errorCode); solaxX1.temperature, solaxX1.runtime_total, status, solaxX1.errorCode);
#ifdef USE_DOMOTICZ #ifdef USE_DOMOTICZ
// Avoid bad temperature report at beginning of the day (spikes of 1200 celsius degrees) // Avoid bad temperature report at beginning of the day (spikes of 1200 celsius degrees)
if (0 == TasmotaGlobal.tele_period && solaxX1.temperature < 100) { DomoticzSensor(DZ_TEMP, solaxX1.temperature); } if (0 == TasmotaGlobal.tele_period && solaxX1.temperature < 100) { DomoticzSensor(DZ_TEMP, solaxX1.temperature); }
#endif // USE_DOMOTICZ #endif // USE_DOMOTICZ
break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
} else { case FUNC_WEB_COL_SENSOR: {
String table_align = Settings->flag5.gui_table_align?"right":"left"; String table_align = Settings->flag5.gui_table_align?"right":"left";
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_SOLAR_POWER, table_align.c_str(), solar_power, D_UNIT_WATT); static uint32_t LastOnlineTime;
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV1_VOLTAGE, table_align.c_str(), pv1_voltage, D_UNIT_VOLT); if (solaxX1.runMode != -1) LastOnlineTime = TasmotaGlobal.uptime;
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV1_CURRENT, table_align.c_str(), pv1_current, D_UNIT_AMPERE); if (TasmotaGlobal.uptime < LastOnlineTime + 300) { // Hide numeric live data, when inverter is offline for more than 5 min
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV1_POWER, table_align.c_str(), pv1_power, D_UNIT_WATT); WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_SOLAR_POWER, table_align.c_str(), solar_power, D_UNIT_WATT);
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);
#ifdef SOLAXX1_PV2 #ifdef SOLAXX1_PV2
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV2_VOLTAGE, table_align.c_str(), pv2_voltage, D_UNIT_VOLT); WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV2_VOLTAGE, table_align.c_str(), pv2_voltage, D_UNIT_VOLT);
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV2_CURRENT, table_align.c_str(), pv2_current, D_UNIT_AMPERE); WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV2_CURRENT, table_align.c_str(), pv2_current, D_UNIT_AMPERE);
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV2_POWER, table_align.c_str(), pv2_power, D_UNIT_WATT); WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV2_POWER, table_align.c_str(), pv2_power, D_UNIT_WATT);
#endif #endif
char SXTemperature[16]; char SXTemperature[16];
dtostrfd(solaxX1.temperature, Settings->flag2.temperature_resolution, SXTemperature); dtostrfd(solaxX1.temperature, Settings->flag2.temperature_resolution, SXTemperature);
WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_TEMPERATURE, table_align.c_str(), SXTemperature, D_UNIT_DEGREE D_UNIT_CELSIUS); WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_TEMPERATURE, table_align.c_str(), SXTemperature, D_UNIT_DEGREE D_UNIT_CELSIUS);
WSContentSend_P(HTTP_SNS_solaxX1_Num, D_UPTIME, table_align.c_str(), String(solaxX1.runtime_total).c_str(), D_UNIT_HOUR); }
WSContentSend_P(HTTP_SNS_solaxX1_Str, D_STATUS, table_align.c_str(), status); WSContentSend_P(HTTP_SNS_solaxX1_Num, D_UPTIME, table_align.c_str(), String(solaxX1.runtime_total).c_str(), D_UNIT_HOUR);
char errorCodeString[33]; break; }
WSContentSend_P(HTTP_SNS_solaxX1_Str, D_ERROR, table_align.c_str(), GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError)); case FUNC_WEB_SENSOR:
WSContentSend_P(HTTP_SNS_solaxX1_Str, "Inverter SN", table_align.c_str(), solaxX1.SerialNumber); 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));
if (solaxX1.SerialNumber[0]) WSContentSend_P(HTTP_SNS_solaxX1_Str, "Inverter SN", solaxX1.SerialNumber);
break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER
} }
} }
@ -582,22 +576,20 @@ void solaxX1_Show(bool json)
* Interface * Interface
\*********************************************************************************************/ \*********************************************************************************************/
bool Xnrg12(uint32_t function) bool Xnrg12(uint32_t function) {
{
bool result = false; bool result = false;
switch (function) { switch (function) {
case FUNC_EVERY_250_MSECOND: case FUNC_EVERY_250_MSECOND:
solaxX1_250MSecond(); solaxX1_250MSecond();
break; break;
case FUNC_JSON_APPEND:
solaxX1_Show(1);
break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_WEB_COL_SENSOR: case FUNC_WEB_COL_SENSOR:
solaxX1_Show(0); case FUNC_WEB_SENSOR:
break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER
case FUNC_JSON_APPEND:
solaxX1_Show(function);
break;
case FUNC_INIT: case FUNC_INIT:
solaxX1_SnsInit(); solaxX1_SnsInit();
break; break;