diff --git a/tasmota/tasmota_xnrg_energy/xnrg_12_solaxX1.ino b/tasmota/tasmota_xnrg_energy/xnrg_12_solaxX1.ino index f74b59b5a..356c8d65d 100644 --- a/tasmota/tasmota_xnrg_energy/xnrg_12_solaxX1.ino +++ b/tasmota/tasmota_xnrg_energy/xnrg_12_solaxX1.ino @@ -108,7 +108,7 @@ struct SOLAXX1_LIVEDATA { float dc2_power = 0; int16_t runMode = 0; uint32_t errorCode = 0; - uint8_t SerialNumber[16] = {0x6e, 0x2f, 0x61}; // "n/a" + uint8_t SerialNumber[16] = {0x00}; } solaxX1; struct SOLAXX1_GLOBALDATA { @@ -127,15 +127,14 @@ struct SOLAXX1_SENDDATA { uint8_t ControlCode[1] = {0x00}; uint8_t FunctionCode[1] = {0x00}; uint8_t DataLength[1] = {0x00}; - uint8_t Payload[16] = {0}; + uint8_t Payload[16] = {0x00}; } solaxX1_SendData; TasmotaSerial *solaxX1Serial; /*********************************************************************************************/ -void solaxX1_RS485Send(void) -{ +void solaxX1_RS485Send(void) { uint8_t message[30]; memcpy(message, solaxX1_SendData.Header, 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]); } -bool solaxX1_RS485Receive(uint8_t *ReadBuffer) -{ +bool solaxX1_RS485Receive(uint8_t *ReadBuffer) { uint8_t len = 0; while (solaxX1Serial->available() > 0) { 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)); } -uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) -{ +uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) { uint8_t i; uint16_t wChkSum = 0; for (i = 0; i < bLen; i++) { @@ -183,8 +180,7 @@ uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) 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; for (i = Begin; i <= End; 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; } -void solaxX1_QueryOfflineInverters(void) -{ +void solaxX1_QueryOfflineInverters(void) { solaxX1_SendData.Source[0] = 0x01; solaxX1_SendData.Destination[0] = 0x00; solaxX1_SendData.Destination[1] = 0x00; @@ -203,8 +198,7 @@ void solaxX1_QueryOfflineInverters(void) solaxX1_RS485Send(); } -void solaxX1_SendInverterAddress(void) -{ +void solaxX1_SendInverterAddress(void) { solaxX1_SendData.Source[0] = 0x00; solaxX1_SendData.Destination[0] = 0x00; solaxX1_SendData.Destination[1] = 0x00; @@ -215,8 +209,7 @@ void solaxX1_SendInverterAddress(void) solaxX1_RS485Send(); } -void solaxX1_QueryLiveData(void) -{ +void solaxX1_QueryLiveData(void) { solaxX1_SendData.Source[0] = 0x01; solaxX1_SendData.Destination[0] = 0x00; solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; @@ -226,8 +219,7 @@ void solaxX1_QueryLiveData(void) solaxX1_RS485Send(); } -void solaxX1_QueryIDData(void) -{ +void solaxX1_QueryIDData(void) { solaxX1_SendData.Source[0] = 0x01; solaxX1_SendData.Destination[0] = 0x00; solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; @@ -237,8 +229,7 @@ void solaxX1_QueryIDData(void) solaxX1_RS485Send(); } -void solaxX1_QueryConfigData(void) -{ +void solaxX1_QueryConfigData(void) { solaxX1_SendData.Source[0] = 0x01; solaxX1_SendData.Destination[0] = 0x00; solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; @@ -248,7 +239,7 @@ void solaxX1_QueryConfigData(void) solaxX1_RS485Send(); } -uint8_t solaxX1_ParseErrorCode(uint32_t code){ +uint8_t solaxX1_ParseErrorCode(uint32_t code) { solaxX1_ErrCode.ErrMessage = code; if (code == 0) return 0; 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 TempData[16] = {0}; 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 //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 - 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 //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 @@ -460,12 +451,10 @@ void solaxX1_250MSecond(void) // Every 250 milliseconds } } solaxX1_global.SendRetry_count--; - return; } // 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)); solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1); 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)) { TasmotaGlobal.energy_driver = XNRG_12; } } -bool SolaxX1_cmd(void) -{ +bool SolaxX1_cmd(void) { if (!solaxX1_global.AddressAssigned) { AddLog(LOG_LEVEL_INFO, PSTR("SX1: No inverter registered")); return false; @@ -514,12 +501,11 @@ bool SolaxX1_cmd(void) } #ifdef USE_WEBSERVER -const char HTTP_SNS_solaxX1_Num[] PROGMEM = "{s}" D_SOLAX_X1 " %s{m}%s  %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}%s{m}{m} %s{e}"; +const char HTTP_SNS_solaxX1_Str[] PROGMEM = "{s}" D_SOLAX_X1 " %s{m}%s{e}"; #endif // USE_WEBSERVER -void solaxX1_Show(bool json) -{ +void solaxX1_Show(uint32_t function) { char solar_power[33]; dtostrfd(solaxX1.dc1_power + solaxX1.dc2_power, Settings->flag2.wattage_resolution, solar_power); char pv1_voltage[33]; @@ -539,41 +525,49 @@ void solaxX1_Show(bool json) char status[33]; GetTextIndexed(status, sizeof(status), solaxX1.runMode + 1, kSolaxMode); - if (json) { - 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); + switch (function) { + case FUNC_JSON_APPEND: + 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 - ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"), - pv2_voltage, pv2_current, pv2_power); + ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"), + pv2_voltage, pv2_current, pv2_power); #endif - 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); + 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); #ifdef USE_DOMOTICZ - // 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); } + // 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); } #endif // USE_DOMOTICZ - + break; #ifdef USE_WEBSERVER - } else { - 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); - 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); + case FUNC_WEB_COL_SENSOR: { + String table_align = Settings->flag5.gui_table_align?"right":"left"; + 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 + 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 - 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_POWER, table_align.c_str(), pv2_power, D_UNIT_WATT); + 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_POWER, table_align.c_str(), pv2_power, D_UNIT_WATT); #endif - char SXTemperature[16]; - 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_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); - char errorCodeString[33]; - WSContentSend_P(HTTP_SNS_solaxX1_Str, D_ERROR, table_align.c_str(), GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError)); - WSContentSend_P(HTTP_SNS_solaxX1_Str, "Inverter SN", table_align.c_str(), solaxX1.SerialNumber); + char SXTemperature[16]; + 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_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: + 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 } } @@ -582,22 +576,20 @@ void solaxX1_Show(bool json) * Interface \*********************************************************************************************/ -bool Xnrg12(uint32_t function) -{ +bool Xnrg12(uint32_t function) { bool result = false; switch (function) { case FUNC_EVERY_250_MSECOND: solaxX1_250MSecond(); break; - case FUNC_JSON_APPEND: - solaxX1_Show(1); - break; #ifdef USE_WEBSERVER case FUNC_WEB_COL_SENSOR: - solaxX1_Show(0); - break; + case FUNC_WEB_SENSOR: #endif // USE_WEBSERVER + case FUNC_JSON_APPEND: + solaxX1_Show(function); + break; case FUNC_INIT: solaxX1_SnsInit(); break;