diff --git "a/Protocol/Deye Modbus\345\202\250\350\203\275-\347\273\204\344\270\262-\345\276\256\351\200\206\345\256\201\346\263\242\345\276\267\344\270\232V118.pdf" "b/Protocol/Deye Modbus\345\202\250\350\203\275-\347\273\204\344\270\262-\345\276\256\351\200\206\345\256\201\346\263\242\345\276\267\344\270\232V118.pdf" new file mode 100644 index 0000000..7c0a939 Binary files /dev/null and "b/Protocol/Deye Modbus\345\202\250\350\203\275-\347\273\204\344\270\262-\345\276\256\351\200\206\345\256\201\346\263\242\345\276\267\344\270\232V118.pdf" differ diff --git a/Protocol/PH1800 PV1800 EP1800 PV3500 EP3500 RS485 Modbud RTU communication Protocol 1.4.15 (1).xlsx b/Protocol/PH1800 PV1800 EP1800 PV3500 EP3500 RS485 Modbud RTU communication Protocol 1.4.15 (1).xlsx new file mode 100644 index 0000000..96482ac Binary files /dev/null and b/Protocol/PH1800 PV1800 EP1800 PV3500 EP3500 RS485 Modbud RTU communication Protocol 1.4.15 (1).xlsx differ diff --git a/platformio.ini b/platformio.ini index 81f0e96..39656c1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,7 +12,7 @@ platform = espressif8266@4.2.1 framework = arduino monitor_speed = 115200 -custom_prog_version = 1.2.0-Pre4 +custom_prog_version = 1.2.0-Pre4A6 build_flags = -DVERSION=${this.custom_prog_version} -DPIO_SRC_NAM="Solar2MQTT" @@ -22,14 +22,15 @@ extra_scripts = pre:tools/mini_html.py pre:tools/pre_compile.py post:tools/post_compile.py lib_deps = - bblanchon/ArduinoJson @ ^6.21.2 + ;bblanchon/ArduinoJson @ ^6.21.2 + bblanchon/ArduinoJson @ ^7.2.0 esphome/ESPAsyncTCP-esphome @ 2.0.0 - mathieucarbou/ESPAsyncWebServer @ 3.3.7 + mathieucarbou/ESPAsyncWebServer @ ^3.3.16 mathieucarbou/WebSerialLite@^6.2.0 alanswx/ESPAsyncWiFiManager @ ^0.31.0 plerup/EspSoftwareSerial @ ^8.2.0 https://github.com/dok-net/ghostl - robtillaart/CRC@^1.0.1 + robtillaart/CRC@^1.0.3 4-20ma/ModbusMaster@^2.0.1 paulstoffregen/OneWire@^2.3.7 milesburton/DallasTemperature@^3.11.0 diff --git a/src/PI_Serial/PI_Serial.cpp b/src/PI_Serial/PI_Serial.cpp index 2da3fb9..dd72344 100644 --- a/src/PI_Serial/PI_Serial.cpp +++ b/src/PI_Serial/PI_Serial.cpp @@ -405,7 +405,7 @@ bool PI_Serial::isModbus() bool PI_Serial::checkQFLAG(const String& flags, char symbol) { bool enabled = false; - for (int i = 0; i < flags.length(); i++) { + for (unsigned int i = 0; i < flags.length(); i++) { char c = flags.charAt(i); if (c == 'E') enabled = true; else if (c == 'D') enabled = false; diff --git a/src/PI_Serial/Q1.h b/src/PI_Serial/Q1.h index 776d020..d7c04a4 100644 --- a/src/PI_Serial/Q1.h +++ b/src/PI_Serial/Q1.h @@ -1,5 +1,121 @@ -static const char *const q1List[] = { +/* +All known devices with PIxx protocol are react to the Q1 comand + +examples: +27 fields, length 109: 00001 16971 01 00 00 026 033 022 029 02 00 000 0036 0000 0000 00.00 10 0 060 030 100 030 58.40 000 120 0 0000 - from https://github.com/jblance/mpp-solar/discussions/284 +27 fields, length 105: 0 00027 00 00 00 000 033 027 035 02 00 000 0030 0000 0000 49.95 10 0 000 000 000 000 00.00 000 000 0 0000 - from https://github.com/softwarecrash/Solar2MQTT/issues/158 +17 fields, length 70: 07792 00001 00 00 00 000 034 030 000 01 00 000 0030 0000 0000 00.00 13 from - -from https://github.com/softwarecrash/Solar2MQTT/discussions/144 +13 fields, length 47: 00 00 00 000 026 021 026 00 00 000 0030 0000 13 -from https://github.com/softwarecrash/Solar2MQTT/discussions/144 +13 fields, length 47: 00 00 00 000 039 030 031 00 00 000 0030 0000 11 - from manuA +22 fields, length 87 00000 00000 01 01 00 030 027 028 029 00 00 000 0031 0364 0000 00.00 11 07 00 3741 530 3 - from derLoosi +length helper: https://elmar-eigner.de/text-zeichen-laenge.html +*/ + +static const char *const DESCR_Time_until_absorb_charge = "Time_until_absorb_charge"; // time since absorb start +static const char *const DESCR_Time_until_float_charge = "Time_until_float_charge"; // tiem since float charge start +static const char *const DESCR_SCC_Flag = "SCC_Flag"; // SCC Flag +static const char *const DESCR_AllowSccOnFlag = "AllowSccOnFlag"; // AllowSccOnFlag +static const char *const DESCR_Charge_Average_Current = "Charge_Average_Current"; // ChargeAverageCurrent +static const char *const DESCR_Tracker_temperature = "Tracker_temperature"; // Temp sensor 1 SCC PWM temperature +static const char *const DESCR_Inverter_temperature = "Inverter_temperature"; // temp sensor 2 +static const char *const DESCR_Battery_temperature = "Battery_temperature"; // temp sensor 3 +static const char *const DESCR_Transformer_temperature = "Transformer_temperature"; // +static const char *const DESCR_Fan_lock_status = "Fan_lock_status"; // error flag for blocked fan +static const char *const DESCR_Fan_speed = "Fan_speed"; // Fan PWM speed +static const char *const DESCR_SCC_charge_power = "SCC_charge_power"; // SCC charge power +static const char *const DESCR_Parallel_Warning = "Parallel_Warning"; // Parallel Warning +static const char *const DESCR_Sync_frequency = "Sync_frequency"; // Sync frequency +static const char *const DESCR_Inverter_charge_state = "Inverter_charge_state"; // charge state +static const char *const DESCR_unknown = ""; // unknown state + +const char *const Q1_47[] = { // [PI34 / MPPT-3000], [PI30 HS MS MSX], [PI30 Revo], [PI30 PIP], [PI41 / LV5048] + DESCR_Time_until_absorb_charge, + DESCR_Time_until_float_charge, + DESCR_unknown, + DESCR_Tracker_temperature, + DESCR_Inverter_temperature, + DESCR_Battery_temperature, + DESCR_Transformer_temperature, + DESCR_unknown, + DESCR_unknown, + DESCR_unknown, + DESCR_Fan_speed, + DESCR_unknown, + DESCR_Inverter_charge_state, +}; +// Raw length 70 +static const char *const Q1_70[] = { + // [PI30] + DESCR_Time_until_absorb_charge, // Time until the end of absorb charging + DESCR_Time_until_float_charge, // Time until the end of float charging + DESCR_unknown, // SCC Flag + DESCR_unknown, // AllowSccOnFlag + DESCR_unknown, // ChargeAverageCurrent + DESCR_Tracker_temperature, // SCC PWM temperature + DESCR_Inverter_temperature, + DESCR_Battery_temperature, + DESCR_Transformer_temperature, + DESCR_unknown, // GPIO13 + DESCR_Fan_lock_status, + DESCR_unknown, + DESCR_Fan_speed, // Fan PWM speed + DESCR_unknown, // SCC charge power + DESCR_unknown, // Parallel Warning + DESCR_unknown, // Sync frequency + DESCR_Inverter_charge_state, +}; +static const char *const Q1_87[] = { + DESCR_Time_until_absorb_charge, // Time until the end of absorb charging + DESCR_Time_until_float_charge, // Time until the end of float charging + DESCR_unknown, // SCC Flag + DESCR_unknown, // AllowSccOnFlag + DESCR_unknown, // ChargeAverageCurrent + DESCR_Tracker_temperature, // SCC PWM temperature + DESCR_Inverter_temperature, + DESCR_Battery_temperature, + DESCR_Transformer_temperature, + DESCR_unknown, // GPIO13 + DESCR_Fan_lock_status, + DESCR_unknown, + DESCR_Fan_speed, // Fan PWM speed + DESCR_unknown, // SCC charge power + DESCR_unknown, // Parallel Warning + DESCR_unknown, // Sync frequency + DESCR_Inverter_charge_state, +}; +const char *const Q1_108[] = { + // [PI18] + DESCR_Time_until_absorb_charge, // Time until the end of absorb charging + DESCR_Time_until_float_charge, // Time until the end of float charging + DESCR_SCC_Flag, // SCC Flag + DESCR_AllowSccOnFlag, // AllowSccOnFlag + DESCR_Charge_Average_Current, // ChargeAverageCurrent + DESCR_Tracker_temperature, // SCC PWM temperature + DESCR_Inverter_temperature, + DESCR_Battery_temperature, + DESCR_Transformer_temperature, + DESCR_unknown, // GPIO13 + DESCR_Fan_lock_status, + DESCR_unknown, + DESCR_Fan_speed, // Fan PWM speed + DESCR_SCC_charge_power, // SCC charge power + DESCR_Parallel_Warning, // Parallel Warning + DESCR_Sync_frequency, // Sync frequency + DESCR_Inverter_charge_state, + DESCR_unknown, + DESCR_unknown, + DESCR_unknown, + DESCR_unknown, + DESCR_unknown, + DESCR_unknown, + DESCR_unknown, + DESCR_unknown, + DESCR_unknown, + DESCR_unknown, +}; +/* +static const char *const Q1_47[] = { "Time_until_absorb_charge", "Time_until_float_charge", "", @@ -14,14 +130,13 @@ static const char *const q1List[] = { "", "Inverter_charge_state", }; -static const char *const q1List2[] = { - // [PI30] +static const char *const Q1_70[] = { "Time_until_absorb_charge", // Time until the end of absorb charging - "Time_until_float_charge", // Time until the end of float charging - "", // SCC Flag - "", // AllowSccOnFlag - "", // ChargeAverageCurrent - "Tracker_temperature", // SCC PWM temperature + "Time_until_float_charge", // Time until the end of float charging + "", // SCC Flag + "", // AllowSccOnFlag + "", // ChargeAverageCurrent + "Tracker_temperature", // SCC PWM temperature "Inverter_temperature", "Battery_temperature", "Transformer_temperature", @@ -29,20 +144,18 @@ static const char *const q1List2[] = { "Fan_lock_status", "", "Fan_speed", // Fan PWM speed - "", // SCC charge power - "", // Parallel Warning - "", // Sync frequency + "", // SCC charge power + "", // Parallel Warning + "", // Sync frequency "Inverter_charge_state", }; -static const char *const q1ListP18[] = { - // [PI18] - //0 00026 00 00 00 000 042 030 040 02 00 000 0030 0000 0000 49.95 10 0 000 000 000 000 00.00 000 000 0 0000 +static const char *const Q1_87[] = { "Time_until_absorb_charge", // Time until the end of absorb charging - "Time_until_float_charge", // Time until the end of float charging - "SCC_Flag", // SCC Flag - "AllowSccOnFlag", // AllowSccOnFlag - "Charge_Average_Current", // ChargeAverageCurrent - "Tracker_temperature", // SCC PWM temperature + "Time_until_float_charge", // Time until the end of float charging + "", // SCC Flag + "", // AllowSccOnFlag + "", // ChargeAverageCurrent + "Tracker_temperature", // SCC PWM temperature "Inverter_temperature", "Battery_temperature", "Transformer_temperature", @@ -50,9 +163,29 @@ static const char *const q1ListP18[] = { "Fan_lock_status", "", "Fan_speed", // Fan PWM speed + "", // SCC charge power + "", // Parallel Warning + "", // Sync frequency + "Inverter_charge_state", +}; +static const char *const Q1_108[] = { + // [PI18] + "Time_until_absorb_charge", // Time until the end of absorb charging + "Time_until_float_charge", // Time until the end of float charging + "SCC_Flag", // SCC Flag + "AllowSccOnFlag", // AllowSccOnFlag + "Charge_Average_Current", // ChargeAverageCurrent + "Tracker_temperature", // SCC PWM temperature + "Inverter_temperature", + "Battery_temperature", + "Transformer_temperature", + "", // GPIO13 + "Fan_lock_status", + "", + "Fan_speed", // Fan PWM speed "SCC_charge_power", // SCC charge power "Parallel_Warning", // Parallel Warning - "Sync_frequency", // Sync frequency + "Sync_frequency", // Sync frequency "Inverter_charge_state", "", "", @@ -65,13 +198,16 @@ static const char *const q1ListP18[] = { "", "", }; +*/ bool PI_Serial::PIXX_Q1() { - if (protocol == PI30) + if (protocol == PI30 || protocol == PI18) // pi30 and pi18 devices react both on Q1 { + String strs[30]; + int StringCount = 0; String commandAnswer = this->requestData("Q1"); get.raw.q1 = commandAnswer; - byte commandAnswerLength = commandAnswer.length(); + if (commandAnswer == "NAK") { return true; @@ -80,9 +216,131 @@ bool PI_Serial::PIXX_Q1() { return false; } - if (commandAnswerLength == 47 || commandAnswerLength == 105) + switch (commandAnswer.length()) { - String strs[16]; + case 47: + // Split the string into substrings + while (commandAnswer.length() > 0) + { + // int index = commandAnswer.indexOf(delimiter); + int index = commandAnswer.indexOf(' '); + if (index == -1) // No space found + { + strs[StringCount++] = commandAnswer; + break; + } + else + { + strs[StringCount++] = commandAnswer.substring(0, index); + commandAnswer = commandAnswer.substring(index + 1); + } + } + for (unsigned int i = 0; i < sizeof Q1_47 / sizeof Q1_47[0]; i++) + { + if (!strs[i].isEmpty() && strcmp(Q1_47[i], "") != 0) + liveData[Q1_47[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; + } + break; + case 70: + // Split the string into substrings + while (commandAnswer.length() > 0) + { + // int index = commandAnswer.indexOf(delimiter); + int index = commandAnswer.indexOf(' '); + if (index == -1) // No space found + { + strs[StringCount++] = commandAnswer; + break; + } + else + { + strs[StringCount++] = commandAnswer.substring(0, index); + commandAnswer = commandAnswer.substring(index + 1); + } + } + for (unsigned int i = 0; i < sizeof Q1_70 / sizeof Q1_70[0]; i++) + { + if (!strs[i].isEmpty() && strcmp(Q1_70[i], "") != 0) + liveData[Q1_70[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; + } + break; + case 87: + // Split the string into substrings + while (commandAnswer.length() > 0) + { + // int index = commandAnswer.indexOf(delimiter); + int index = commandAnswer.indexOf(' '); + if (index == -1) // No space found + { + strs[StringCount++] = commandAnswer; + break; + } + else + { + strs[StringCount++] = commandAnswer.substring(0, index); + commandAnswer = commandAnswer.substring(index + 1); + } + } + for (unsigned int i = 0; i < sizeof Q1_87 / sizeof Q1_87[0]; i++) + { + if (!strs[i].isEmpty() && strcmp(Q1_87[i], "") != 0) + liveData[Q1_87[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; + } + break; + case 105: + case 108: + // Split the string into substrings + while (commandAnswer.length() > 0) + { + // int index = commandAnswer.indexOf(delimiter); + int index = commandAnswer.indexOf(' '); + if (index == -1) // No space found + { + strs[StringCount++] = commandAnswer; + break; + } + else + { + strs[StringCount++] = commandAnswer.substring(0, index); + commandAnswer = commandAnswer.substring(index + 1); + } + } + for (unsigned int i = 0; i < sizeof Q1_108 / sizeof Q1_108[0]; i++) + { + if (!strs[i].isEmpty() && strcmp(Q1_108[i], "") != 0) + liveData[Q1_108[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; + } + break; + + default: + get.raw.q1 = "Wrong Length(" + (String)get.raw.q1.length() + "), Contact Dev:" + get.raw.q1; + break; + } + if (liveData["Inverter_charge_state"].is()) + { + switch ((int)liveData["Inverter_charge_state"].as()) + { + default: + // liveData["Inverter_charge_state"] = "no data"; + break; + case 10: + liveData["Inverter_charge_state"] = "No charging"; + break; + case 11: + liveData["Inverter_charge_state"] = "Bulk stage"; + break; + case 12: + liveData["Inverter_charge_state"] = "Absorb"; + break; + case 13: + liveData["Inverter_charge_state"] = "Float"; + break; + } + } + return true; + /* if (commandAnswerLength == 47 || commandAnswerLength == 105) + { + String strs[30]; // Split the string into substrings int StringCount = 0; while (commandAnswer.length() > 0) @@ -101,15 +359,15 @@ bool PI_Serial::PIXX_Q1() } } - for (unsigned int i = 0; i < sizeof q1List / sizeof q1List[0]; i++) + for (unsigned int i = 0; i < sizeof Q1_47 / sizeof Q1_47[0]; i++) { - if (!strs[i].isEmpty() && strcmp(q1List[i], "") != 0) - liveData[q1List[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; + if (!strs[i].isEmpty() && strcmp(Q1_47[i], "") != 0) + liveData[Q1_47[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; } } if (commandAnswerLength == 70) { - String strs[16]; + String strs[30]; // Split the string into substrings int StringCount = 0; while (commandAnswer.length() > 0) @@ -128,32 +386,32 @@ bool PI_Serial::PIXX_Q1() } } - for (unsigned int i = 0; i < sizeof q1List2 / sizeof q1List2[0]; i++) + for (unsigned int i = 0; i < sizeof Q1_70 / sizeof Q1_70[0]; i++) { - if (!strs[i].isEmpty() && strcmp(q1List2[i], "") != 0) - liveData[q1List2[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; + if (!strs[i].isEmpty() && strcmp(Q1_70[i], "") != 0) + liveData[Q1_70[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; } } - if (liveData.containsKey("Inverter_charge_state")) + if (liveData["Inverter_charge_state"].is()) { switch ((int)liveData["Inverter_charge_state"].as()) { - default: - // liveData["Inverter_charge_state"] = "no data"; - break; - case 10: - liveData["Inverter_charge_state"] = "No charging"; - break; - case 11: - liveData["Inverter_charge_state"] = "Bulk stage"; - break; - case 12: - liveData["Inverter_charge_state"] = "Absorb"; - break; - case 13: - liveData["Inverter_charge_state"] = "Float"; - break; + default: + // liveData["Inverter_charge_state"] = "no data"; + break; + case 10: + liveData["Inverter_charge_state"] = "No charging"; + break; + case 11: + liveData["Inverter_charge_state"] = "Bulk stage"; + break; + case 12: + liveData["Inverter_charge_state"] = "Absorb"; + break; + case 13: + liveData["Inverter_charge_state"] = "Float"; + break; } } @@ -172,9 +430,10 @@ bool PI_Serial::PIXX_Q1() { return false; } - if (commandAnswerLength == 47 || commandAnswerLength == 105) + if (commandAnswerLength >= 47 && commandAnswerLength <= 105) + // if (commandAnswerLength == 47 || commandAnswerLength == 105) { - String strs[16]; + String strs[30]; // Split the string into substrings int StringCount = 0; while (commandAnswer.length() > 0) @@ -193,13 +452,13 @@ bool PI_Serial::PIXX_Q1() } } - for (unsigned int i = 0; i < sizeof q1ListP18 / sizeof q1ListP18[0]; i++) + for (unsigned int i = 0; i < sizeof Q1_108 / sizeof Q1_108[0]; i++) { - if (!strs[i].isEmpty() && strcmp(q1ListP18[i], "") != 0) - liveData[q1ListP18[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; + if (!strs[i].isEmpty() && strcmp(Q1_108[i], "") != 0) + liveData[Q1_108[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; } - if (liveData.containsKey("Inverter_charge_state")) + if (liveData["Inverter_charge_state"].is()) { switch ((int)liveData["Inverter_charge_state"].as()) { @@ -222,6 +481,7 @@ bool PI_Serial::PIXX_Q1() } } return true; + }*/ } else if (protocol == NoD) { diff --git a/src/PI_Serial/Q1_old.h b/src/PI_Serial/Q1_old.h new file mode 100644 index 0000000..2791805 --- /dev/null +++ b/src/PI_Serial/Q1_old.h @@ -0,0 +1,372 @@ +/* +All known devices with PIxx protocol are react to the Q1 comand + +examples: +27 fields, length 109: 00001 16971 01 00 00 026 033 022 029 02 00 000 0036 0000 0000 00.00 10 0 060 030 100 030 58.40 000 120 0 0000 - from https://github.com/jblance/mpp-solar/discussions/284 +27 fields, length 105: 0 00027 00 00 00 000 033 027 035 02 00 000 0030 0000 0000 49.95 10 0 000 000 000 000 00.00 000 000 0 0000 - from https://github.com/softwarecrash/Solar2MQTT/issues/158 +17 fields, length 70: 07792 00001 00 00 00 000 034 030 000 01 00 000 0030 0000 0000 00.00 13 from - -from https://github.com/softwarecrash/Solar2MQTT/discussions/144 +13 fields, length 47: 00 00 00 000 026 021 026 00 00 000 0030 0000 13 -from https://github.com/softwarecrash/Solar2MQTT/discussions/144 +13 fields, length 47: 00 00 00 000 039 030 031 00 00 000 0030 0000 11 - from manuA +22 fields, length 87 00000 00000 01 01 00 030 027 028 029 00 00 000 0031 0364 0000 00.00 11 07 00 3741 530 3 - from derLoosi +length helper: https://elmar-eigner.de/text-zeichen-laenge.html +*/ +// Raw length 47 + +/* const char Time_until_absorb_charge[] PROGMEM = "Time_until_absorb_charge"; +const char Time_until_float_charge[] PROGMEM = "Time_until_float_charge"; +const char SCC_Flag[] PROGMEM= "SCC_Flag"; // SCC Flag +const char AllowSccOnFlag[] PROGMEM= "AllowSccOnFlag"; // AllowSccOnFlag +const char Charge_Average_Current[] PROGMEM= "Charge_Average_Current"; // ChargeAverageCurrent +const char Tracker_temperature[] PROGMEM= "Tracker_temperature"; // SCC PWM temperature +const char Inverter_temperature[] PROGMEM= "Inverter_temperature"; +const char Battery_temperature[] PROGMEM= "Battery_temperature"; +const char Transformer_temperature[] PROGMEM= "Transformer_temperature"; +const char Fan_lock_status[] PROGMEM= "Fan_lock_status"; +const char Fan_speed[] PROGMEM= "Fan_speed"; // Fan PWM speed +const char SCC_charge_power[] PROGMEM= "SCC_charge_power"; // SCC charge power +const char Parallel_Warning[] PROGMEM= "Parallel_Warning"; // Parallel Warning +const char Sync_frequency[] PROGMEM= "Sync_frequency"; // Sync frequency +const char Inverter_charge_state[] = "Inverter_charge_state"; +const char unknown[] PROGMEM= ""; + + +const char *const Q1_47[] = { + // [PI34 / MPPT-3000], [PI30 HS MS MSX], [PI30 Revo], [PI30 PIP], [PI41 / LV5048] + Time_until_absorb_charge, + Time_until_float_charge, + unknown, + Tracker_temperature, + Inverter_temperature, + Battery_temperature, + Transformer_temperature, + unknown, + unknown, + unknown, + Fan_speed, + unknown, + Inverter_charge_state, +}; + +static const char *const q1List[] = { + // [PI34 / MPPT-3000], [PI30 HS MS MSX], [PI30 Revo], [PI30 PIP], [PI41 / LV5048] + Time_until_absorb_charge, + Time_until_float_charge, + unknown, + Tracker_temperature, + Inverter_temperature, + Battery_temperature, + Transformer_temperature, + unknown, + unknown, + unknown, + Fan_speed, + unknown, + Inverter_charge_state, +}; +// Raw length 70 +static const char *const q1List2[] = { + // [PI30] + Time_until_absorb_charge, // Time until the end of absorb charging + Time_until_float_charge, // Time until the end of float charging + unknown, // SCC Flag + unknown, // AllowSccOnFlag + unknown, // ChargeAverageCurrent + Tracker_temperature, // SCC PWM temperature + Inverter_temperature, + Battery_temperature, + Transformer_temperature, + unknown, // GPIO13 + Fan_lock_status, + unknown, + Fan_speed, // Fan PWM speed + unknown, // SCC charge power + unknown, // Parallel Warning + unknown, // Sync frequency + Inverter_charge_state, +}; +// raw length 102 or 105?????? +static const char *const q1ListP18[] = { + // [PI18] + Time_until_absorb_charge, // Time until the end of absorb charging + Time_until_float_charge, // Time until the end of float charging + SCC_Flag, // SCC Flag + AllowSccOnFlag, // AllowSccOnFlag + Charge_Average_Current, // ChargeAverageCurrent + Tracker_temperature, // SCC PWM temperature + Inverter_temperature, + Battery_temperature, + Transformer_temperature, + unknown, // GPIO13 + Fan_lock_status, + unknown, + Fan_speed, // Fan PWM speed + SCC_charge_power, // SCC charge power + Parallel_Warning, // Parallel Warning + Sync_frequency, // Sync frequency + Inverter_charge_state, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, +}; */ + + +static const char *const Q1_47[] = { + // [PI34 / MPPT-3000], [PI30 HS MS MSX], [PI30 Revo], [PI30 PIP], [PI41 / LV5048] + "Time_until_absorb_charge", + "Time_until_float_charge", + "", + "Tracker_temperature", + "Inverter_temperature", + "Battery_temperature", + "Transformer_temperature", + "", + "", + "", + "Fan_speed", + "", + "Inverter_charge_state", +}; + +static const char *const q1List[] = { + // [PI34 / MPPT-3000], [PI30 HS MS MSX], [PI30 Revo], [PI30 PIP], [PI41 / LV5048] + "Time_until_absorb_charge", + "Time_until_float_charge", + "", + "Tracker_temperature", + "Inverter_temperature", + "Battery_temperature", + "Transformer_temperature", + "", + "", + "", + "Fan_speed", + "", + "Inverter_charge_state", +}; +// Raw length 70 +static const char *const q1List2[] = { + // [PI30] + "Time_until_absorb_charge", // Time until the end of absorb charging + "Time_until_float_charge", // Time until the end of float charging + "", // SCC Flag + "", // AllowSccOnFlag + "", // ChargeAverageCurrent + "Tracker_temperature", // SCC PWM temperature + "Inverter_temperature", + "Battery_temperature", + "Transformer_temperature", + "", // GPIO13 + "Fan_lock_status", + "", + "Fan_speed", // Fan PWM speed + "", // SCC charge power + "", // Parallel Warning + "", // Sync frequency + "Inverter_charge_state", +}; +// raw length 102 or 105?????? +static const char *const q1ListP18[] = { + // [PI18] + "Time_until_absorb_charge", // Time until the end of absorb charging + "Time_until_float_charge", // Time until the end of float charging + "SCC_Flag", // SCC Flag + "AllowSccOnFlag", // AllowSccOnFlag + "Charge_Average_Current", // ChargeAverageCurrent + "Tracker_temperature", // SCC PWM temperature + "Inverter_temperature", + "Battery_temperature", + "Transformer_temperature", + "", // GPIO13 + "Fan_lock_status", + "", + "Fan_speed", // Fan PWM speed + "SCC_charge_power", // SCC charge power + "Parallel_Warning", // Parallel Warning + "Sync_frequency", // Sync frequency + "Inverter_charge_state", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", +}; +bool PI_Serial::PIXX_Q1() +{ + if (protocol == PI30) // pi30 and pi18 devices react both on Q1 + { + String commandAnswer = this->requestData("Q1"); + get.raw.q1 = commandAnswer; + byte commandAnswerLength = commandAnswer.length(); + if (commandAnswer == "NAK") + { + return true; + } + if (commandAnswer == "ERCRC") + { + return false; + } + if (commandAnswerLength == 47 || commandAnswerLength == 105) + { + String strs[30]; + // Split the string into substrings + int StringCount = 0; + while (commandAnswer.length() > 0) + { + // int index = commandAnswer.indexOf(delimiter); + int index = commandAnswer.indexOf(' '); + if (index == -1) // No space found + { + strs[StringCount++] = commandAnswer; + break; + } + else + { + strs[StringCount++] = commandAnswer.substring(0, index); + commandAnswer = commandAnswer.substring(index + 1); + } + } + + for (unsigned int i = 0; i < sizeof q1List / sizeof q1List[0]; i++) + { + if (!strs[i].isEmpty() && strcmp(q1List[i], "") != 0) + liveData[q1List[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; + } + } + if (commandAnswerLength == 70) + { + String strs[30]; + // Split the string into substrings + int StringCount = 0; + while (commandAnswer.length() > 0) + { + // int index = commandAnswer.indexOf(delimiter); + int index = commandAnswer.indexOf(' '); + if (index == -1) // No space found + { + strs[StringCount++] = commandAnswer; + break; + } + else + { + strs[StringCount++] = commandAnswer.substring(0, index); + commandAnswer = commandAnswer.substring(index + 1); + } + } + + for (unsigned int i = 0; i < sizeof q1List2 / sizeof q1List2[0]; i++) + { + if (!strs[i].isEmpty() && strcmp(q1List2[i], "") != 0) + liveData[q1List2[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; + } + } + + if (liveData["Inverter_charge_state"].is()) + { + switch ((int)liveData["Inverter_charge_state"].as()) + { + default: + // liveData["Inverter_charge_state"] = "no data"; + break; + case 10: + liveData["Inverter_charge_state"] = "No charging"; + break; + case 11: + liveData["Inverter_charge_state"] = "Bulk stage"; + break; + case 12: + liveData["Inverter_charge_state"] = "Absorb"; + break; + case 13: + liveData["Inverter_charge_state"] = "Float"; + break; + } + } + + return true; + } + else if (protocol == PI18) + { + String commandAnswer = this->requestData("Q1"); + get.raw.q1 = commandAnswer; + byte commandAnswerLength = commandAnswer.length(); + if (commandAnswer == "NAK") + { + return true; + } + if (commandAnswer == "ERCRC") + { + return false; + } + if (commandAnswerLength >= 47 && commandAnswerLength <= 105) + // if (commandAnswerLength == 47 || commandAnswerLength == 105) + { + String strs[30]; + // Split the string into substrings + int StringCount = 0; + while (commandAnswer.length() > 0) + { + // int index = commandAnswer.indexOf(delimiter); + int index = commandAnswer.indexOf(' '); + if (index == -1) // No space found + { + strs[StringCount++] = commandAnswer; + break; + } + else + { + strs[StringCount++] = commandAnswer.substring(0, index); + commandAnswer = commandAnswer.substring(index + 1); + } + } + + for (unsigned int i = 0; i < sizeof q1ListP18 / sizeof q1ListP18[0]; i++) + { + if (!strs[i].isEmpty() && strcmp(q1ListP18[i], "") != 0) + liveData[q1ListP18[i]] = (int)(strs[i].toFloat() * 100 + 0.5) / 100.0; + } + + if (liveData["Inverter_charge_state"].is()) + { + switch ((int)liveData["Inverter_charge_state"].as()) + { + default: + // liveData["Inverter_charge_state"] = "no data"; + break; + case 10: + liveData["Inverter_charge_state"] = "No charging"; + break; + case 11: + liveData["Inverter_charge_state"] = "Bulk stage"; + break; + case 12: + liveData["Inverter_charge_state"] = "Absorb"; + break; + case 13: + liveData["Inverter_charge_state"] = "Float"; + break; + } + } + } + return true; + } + else if (protocol == NoD) + { + return false; + } + else + { + return false; + } +} \ No newline at end of file diff --git a/src/PI_Serial/QFLAG.h b/src/PI_Serial/QFLAG.h index 55f7ba1..13a997d 100644 --- a/src/PI_Serial/QFLAG.h +++ b/src/PI_Serial/QFLAG.h @@ -15,6 +15,8 @@ bool PI_Serial::PIXX_QFLAG() } if (commandAnswerLength == 11) { + staticData["Buzzer_Enabled"] = checkQFLAG(commandAnswer, 'a'); + staticData["Buzzer_Enabled"] = checkQFLAG(commandAnswer, 'a'); staticData["Overload_bypass_Enabled"] = checkQFLAG(commandAnswer, 'b'); staticData["Power_saving_Enabled"] = checkQFLAG(commandAnswer, 'j'); @@ -27,6 +29,37 @@ bool PI_Serial::PIXX_QFLAG() } return true; } + else if (protocol == PI18) + { + String commandAnswer = this->requestData("^P007FLAG"); + get.raw.qflag = commandAnswer; + byte commandAnswerLength = commandAnswer.length(); + if (commandAnswer == "NAK") + { + return true; + } + if (commandAnswer == "ERCRC") + { + return false; + } + //[C: ^P007FLAG][CR: 190F][CC: 190F][L: 17] + //QFLAG 1,1,0,0,0,1,1,1,0 + if (commandAnswerLength == 17) + { + staticData["Buzzer_Enabled"] = ((String)commandAnswer.charAt(0) == "1") ? true : false; + staticData["Overload_bypass_Enabled"] = ((String)(commandAnswer.charAt(2)) == "1") ? true : false; + staticData["LCD_reset_to_default_Enabled"] = ((String)(commandAnswer.charAt(4)) == "1") ? true : false; + staticData["Overload_restart_Enabled"] = ((String)(commandAnswer.charAt(6)) == "1") ? true : false; + staticData["Over_temperature_restart_Enabled"] = ((String)(commandAnswer.charAt(8)) == "1") ? true : false; + staticData["LCD_backlight_Enabled"] = ((String)(commandAnswer.charAt(10)) == "1") ? true : false; + staticData["Primary_source_interrupt_alarm_Enabled"] = ((String)(commandAnswer.charAt(12)) == "1") ? true : false; + staticData["Record_fault_code_Enabled"] = ((String)(commandAnswer.charAt(14)) == "1") ? true : false; + + } else { + get.raw.qflag = "Wrong Length(" + (String)get.raw.qflag.length() + "), Contact Dev:" +get.raw.qflag; + } + return true; + } else if (protocol == NoD) { return false; diff --git a/src/PI_Serial/QPIWS.h b/src/PI_Serial/QPIWS.h index 7710731..7df9bf0 100644 --- a/src/PI_Serial/QPIWS.h +++ b/src/PI_Serial/QPIWS.h @@ -5,7 +5,6 @@ bool PI_Serial::PIXX_QPIWS() String commandAnswer = this->requestData("QPIWS"); //String commandAnswer = "10000000001010000000000000000000"; get.raw.qpiws = commandAnswer; - byte commandAnswerLength = commandAnswer.length(); if (commandAnswer == "NAK") { return true; @@ -14,7 +13,7 @@ bool PI_Serial::PIXX_QPIWS() { return false; } - if (commandAnswerLength == 32) + if (commandAnswer.length() == 32) { std::vector qpiwsStrings; if ((char)commandAnswer.charAt(1) == '1') qpiwsStrings.emplace_back("Inverter fault"); // 2 @@ -62,6 +61,87 @@ bool PI_Serial::PIXX_QPIWS() } return true; } + else if(protocol == PI18){ + String commandAnswer = this->requestData("^P005FWS"); + get.raw.qpiws = commandAnswer; + if (commandAnswer == "NAK") + { + return true; + } + if (commandAnswer == "ERCRC") + { + return false; + } + //[C: ^P005FWS][CR: B69E][CC: B69E][L: 36] from valqk + //QPIWS 00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + if (commandAnswer.length() == 36) + { + std::vector qpiwsStrings; + if (commandAnswer.substring(0,2) == "01") qpiwsStrings.emplace_back("Fan is locked"); // 2 + if (commandAnswer.substring(0,2) == "02") qpiwsStrings.emplace_back("Over temperature"); // 2 + if (commandAnswer.substring(0,2) == "03") qpiwsStrings.emplace_back("Battery voltage is too high"); // 2 + if (commandAnswer.substring(0,2) == "04") qpiwsStrings.emplace_back("Battery voltage is too low"); // 2 + if (commandAnswer.substring(0,2) == "05") qpiwsStrings.emplace_back("Output short circuited or Over temperature"); // 2 + if (commandAnswer.substring(0,2) == "06") qpiwsStrings.emplace_back("Output voltage is too high"); // 2 + if (commandAnswer.substring(0,2) == "07") qpiwsStrings.emplace_back("Over load time out"); // 2 + if (commandAnswer.substring(0,2) == "08") qpiwsStrings.emplace_back("Bus voltage is too high"); // 2 + if (commandAnswer.substring(0,2) == "09") qpiwsStrings.emplace_back("Bus soft start failed"); // 2 + if (commandAnswer.substring(0,2) == "11") qpiwsStrings.emplace_back("Main relay failed"); // 2 + if (commandAnswer.substring(0,2) == "51") qpiwsStrings.emplace_back("Over current inverter"); // 2 + if (commandAnswer.substring(0,2) == "52") qpiwsStrings.emplace_back("Bus soft start failed"); // 2 + if (commandAnswer.substring(0,2) == "53") qpiwsStrings.emplace_back("Inverter soft start failed"); // 2 + if (commandAnswer.substring(0,2) == "54") qpiwsStrings.emplace_back("Self-test failed"); // 2 + if (commandAnswer.substring(0,2) == "55") qpiwsStrings.emplace_back("Over DC voltage on output of inverter"); // 2 + if (commandAnswer.substring(0,2) == "56") qpiwsStrings.emplace_back("Battery connection is open"); // 2 + if (commandAnswer.substring(0,2) == "57") qpiwsStrings.emplace_back("Current sensor failed"); // 2 + if (commandAnswer.substring(0,2) == "58") qpiwsStrings.emplace_back("Output voltage is too low"); // 2 + if (commandAnswer.substring(0,2) == "60") qpiwsStrings.emplace_back("Inverter negative power"); // 2 + if (commandAnswer.substring(0,2) == "71") qpiwsStrings.emplace_back("Parallel version different"); // 2 + if (commandAnswer.substring(0,2) == "72") qpiwsStrings.emplace_back("Output circuit failed"); // 2 + if (commandAnswer.substring(0,2) == "80") qpiwsStrings.emplace_back("CAN communication failed"); // 2 + if (commandAnswer.substring(0,2) == "81") qpiwsStrings.emplace_back("Parallel host line lost"); // 2 + if (commandAnswer.substring(0,2) == "82") qpiwsStrings.emplace_back("Parallel synchronized signal lost"); // 2 + if (commandAnswer.substring(0,2) == "83") qpiwsStrings.emplace_back("Parallel battery voltage detect different"); // 2 + if (commandAnswer.substring(0,2) == "84") qpiwsStrings.emplace_back("Parallel Line voltage or frequency detect different"); // 2 + if (commandAnswer.substring(0,2) == "85") qpiwsStrings.emplace_back("Parallel Line input current unbalanced"); // 2 + if (commandAnswer.substring(0,2) == "86") qpiwsStrings.emplace_back("Parallel output setting different"); // 2 + + if ((char)commandAnswer.charAt(3) == '1') qpiwsStrings.emplace_back("Line fail"); // 2 + if ((char)commandAnswer.charAt(5) == '1') qpiwsStrings.emplace_back("Over temperature"); // 20 + if ((char)commandAnswer.charAt(7) == '1') qpiwsStrings.emplace_back("Output circuit short"); // 3 + if ((char)commandAnswer.charAt(9) == '1') qpiwsStrings.emplace_back("Inverter over temperature"); // 4 + if ((char)commandAnswer.charAt(11) == '1') qpiwsStrings.emplace_back("Fan lock"); // 5 + if ((char)commandAnswer.charAt(13) == '1') qpiwsStrings.emplace_back("Battery voltage high"); // 6 + if ((char)commandAnswer.charAt(15) == '1') qpiwsStrings.emplace_back("Battery low"); // 7 + if ((char)commandAnswer.charAt(17) == '1') qpiwsStrings.emplace_back("Battery under"); // 8 + if ((char)commandAnswer.charAt(19) == '1') qpiwsStrings.emplace_back("Over load"); // 9 + if ((char)commandAnswer.charAt(21) == '1') qpiwsStrings.emplace_back("Eeprom fail"); // 10 + if ((char)commandAnswer.charAt(23) == '1') qpiwsStrings.emplace_back("Power limit"); // 11 + if ((char)commandAnswer.charAt(25) == '1') qpiwsStrings.emplace_back("PV1 voltage high"); // 12 + if ((char)commandAnswer.charAt(27) == '1') qpiwsStrings.emplace_back("PV2 voltage high"); // 13 + if ((char)commandAnswer.charAt(29) == '1') qpiwsStrings.emplace_back("MPPT1 overload warning"); // 15 + if ((char)commandAnswer.charAt(31) == '1') qpiwsStrings.emplace_back("MPPT2 overload warning"); // 17 + if ((char)commandAnswer.charAt(33) == '1') qpiwsStrings.emplace_back("Battery too low to charge for SCC1"); // 18 + if ((char)commandAnswer.charAt(35) == '1') qpiwsStrings.emplace_back("Battery too low to charge for SCC2"); // 19 + + if (!qpiwsStrings.empty()) + { + String qpiwsStr = ""; + for (size_t i = 0; i < qpiwsStrings.size(); i++) { + qpiwsStr += qpiwsStrings[i]; + if (i < qpiwsStrings.size() - 1) { + qpiwsStr += "; "; + } + } + liveData["Fault_code"] = qpiwsStr; + } + else + { + liveData["Fault_code"] = "Ok"; + } + } + return true; + } else if (protocol == NoD) { return false; diff --git a/src/main.cpp b/src/main.cpp index 49589cb..7c2ead9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,10 +60,10 @@ String commandFromUser; String customResponse; bool firstPublish; -DynamicJsonDocument Json(JSON_BUFFER); // main Json -JsonObject deviceJson = Json.createNestedObject("EspData"); // basic device data -JsonObject staticData = Json.createNestedObject("DeviceData"); // battery package data -JsonObject liveData = Json.createNestedObject("LiveData"); // battery package data +JsonDocument Json; // main Json +JsonObject deviceJson = Json["EspData"].to(); // basic device data +JsonObject staticData = Json["DeviceData"].to(); // battery package data +JsonObject liveData = Json["LiveData"].to(); // battery package data //---------------------------------------------------------------------- void saveConfigCallback() @@ -518,8 +518,8 @@ void getJsonData() deviceJson[F("sw_version")] = SOFTWARE_VERSION; deviceJson[F("Free_Heap")] = ESP.getFreeHeap(); deviceJson[F("HEAP_Fragmentation")] = ESP.getHeapFragmentation(); - deviceJson[F("json_memory_usage")] = Json.memoryUsage(); - deviceJson[F("json_capacity")] = Json.capacity(); + //deviceJson[F("json_memory_usage")] = Json.memoryUsage(); + //deviceJson[F("json_capacity")] = Json.capacity(); deviceJson[F("runtime")] = millis() / 1000; deviceJson[F("ws_clients")] = ws.count(); deviceJson[F("detect_protocol")] = mppClient.protocol; @@ -699,7 +699,7 @@ bool sendHaDiscovery() char topBuff[128]; for (size_t i = 0; i < sizeof haStaticDescriptor / sizeof haStaticDescriptor[0]; i++) { - if (staticData.containsKey(haStaticDescriptor[i][0])) + if (staticData[haStaticDescriptor[i][0]].is()) { String haPayLoad = String("{") + "\"name\":\"" + haStaticDescriptor[i][0] + "\"," + @@ -727,7 +727,7 @@ bool sendHaDiscovery() for (size_t i = 0; i < sizeof haLiveDescriptor / sizeof haLiveDescriptor[0]; i++) { - if (liveData.containsKey(haLiveDescriptor[i][0])) + if (liveData[haLiveDescriptor[i][0]].is()) { String haPayLoad = String("{") + "\"name\":\"" + haLiveDescriptor[i][0] + "\"," + diff --git a/src/modbus/modbus.cpp b/src/modbus/modbus.cpp index c8ad0db..52a274c 100644 --- a/src/modbus/modbus.cpp +++ b/src/modbus/modbus.cpp @@ -390,7 +390,7 @@ bool MODBUS::autoDetect() // function for autodetect the inverter type String MODBUS::retrieveModel() { String model = ""; - DynamicJsonDocument doc(256); + JsonDocument doc; JsonObject jsonObj = doc.to(); // Create and get JsonObject modbus_register_info_t model_info = { .variant = &jsonObj,