From bf826a33b0aa4773892bd61eeef71fd02e7ba2fe Mon Sep 17 00:00:00 2001 From: Artem Kovalenko Date: Tue, 24 Sep 2024 11:23:48 +0300 Subject: [PATCH 01/10] add modbus support --- platformio.ini | 3 +- src/PI_Serial/PI_Serial.cpp | 145 ++++++++----- src/PI_Serial/PI_Serial.h | 10 +- src/modbus/modbus.cpp | 379 ++++++++++++++++++++++++++++++++++ src/modbus/modbus.h | 105 ++++++++++ src/modbus/modbus_registers.h | 81 ++++++++ 6 files changed, 668 insertions(+), 55 deletions(-) create mode 100644 src/modbus/modbus.cpp create mode 100644 src/modbus/modbus.h create mode 100644 src/modbus/modbus_registers.h diff --git a/platformio.ini b/platformio.ini index 8c1afc9..143de3d 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.1.7 +custom_prog_version = 1.2.0 build_flags = -DVERSION=${this.custom_prog_version} -DPIO_SRC_NAM="Solar2MQTT" @@ -30,6 +30,7 @@ lib_deps = https://github.com/dok-net/ghostl robtillaart/CRC@^1.0.1 asjdf/WebSerialLite@^2.2.0 + 4-20ma/ModbusMaster@^2.0.1 [env:d1_mini] board = d1_mini diff --git a/src/PI_Serial/PI_Serial.cpp b/src/PI_Serial/PI_Serial.cpp index cdc40e1..93fc56f 100644 --- a/src/PI_Serial/PI_Serial.cpp +++ b/src/PI_Serial/PI_Serial.cpp @@ -1,7 +1,7 @@ // #define isDEBUG #include "ArduinoJson.h" #include "PI_Serial.h" -SoftwareSerial myPort; + #include "CRC16.h" #include "CRC.h" CRC16 crc; @@ -22,9 +22,7 @@ extern void writeLog(const char *format, ...); PI_Serial::PI_Serial(int rx, int tx) { - soft_rx = rx; - soft_tx = tx; - this->my_serialIntf = &myPort; + this->my_serialIntf = new SoftwareSerial(rx, tx, false); // init pins } bool PI_Serial::Init() @@ -36,9 +34,17 @@ bool PI_Serial::Init() return false; } autoDetect(); + if (isModbus()) + { + if (requestCallback) + { + modbus->callback(requestCallback); + } + return true; + } this->my_serialIntf->setTimeout(500); this->my_serialIntf->enableRxGPIOPullUp(true); - this->my_serialIntf->begin(serialIntfBaud, SWSERIAL_8N1, soft_rx, soft_tx, false); + this->my_serialIntf->begin(serialIntfBaud, SWSERIAL_8N1); return true; } @@ -55,50 +61,64 @@ bool PI_Serial::loop() previousTime = millis(); return true; } - switch (requestStaticData) + if (isModbus()) { - case true: - switch (requestCounter) - { - case 0: - requestCounter = PIXX_QPIRI() ? (requestCounter + 1) : 0; - break; - case 1: - requestCounter = PIXX_QMN() ? (requestCounter + 1) : 0; - break; - case 2: - requestCounter = PIXX_QPI() ? (requestCounter + 1) : 0; - requestCounter = 0; - requestStaticData = false; - break; - } - break; - case false: - switch (requestCounter) + modbus->loop(); + } + else + { + switch (requestStaticData) { - case 0: - requestCounter = PIXX_QPIGS() ? (requestCounter + 1) : 0; - break; - case 1: - requestCounter = PIXX_QPIGS2() ? (requestCounter + 1) : 0; + case true: + switch (requestCounter) + { + case 0: + requestCounter = PIXX_QPIRI() ? (requestCounter + 1) : 0; + break; + case 1: + requestCounter = PIXX_QMN() ? (requestCounter + 1) : 0; + break; + case 2: + requestCounter = PIXX_QPI() ? (requestCounter + 1) : 0; + requestCounter = 0; + requestStaticData = false; + break; + } break; - case 2: - requestCounter = PIXX_QMOD() ? (requestCounter + 1) : 0; - break; - case 3: - requestCounter = PIXX_Q1() ? (requestCounter + 1) : 0; - break; - case 4: - requestCounter = PIXX_QEX() ? (requestCounter + 1) : 0; - break; - case 5: - requestCallback(); - requestCounter = 0; + case false: + switch (requestCounter) + { + case 0: + requestCounter = PIXX_QPIGS() ? (requestCounter + 1) : 0; + break; + case 1: + requestCounter = PIXX_QPIGS2() ? (requestCounter + 1) : 0; + break; + case 2: + requestCounter = PIXX_QMOD() ? (requestCounter + 1) : 0; + break; + case 3: + requestCounter = PIXX_Q1() ? (requestCounter + 1) : 0; + break; + case 4: + requestCounter = PIXX_QEX() ? (requestCounter + 1) : 0; + break; + case 5: + requestCallback(); + requestCounter = 0; + break; + } break; } - break; } - connection = (connectionCounter < 10) ? true : false; + if (isModbus()) + { + connection = modbus->connection; + } + else + { + connection = (connectionCounter < 10) ? true : false; + } previousTime = millis(); } else @@ -138,9 +158,9 @@ void PI_Serial::autoDetect() // function for autodetect the inverter type startChar = "("; serialIntfBaud = 2400; - this->my_serialIntf->begin(serialIntfBaud, SWSERIAL_8N1, soft_rx, soft_tx, false); + this->my_serialIntf->begin(serialIntfBaud, SWSERIAL_8N1); String qpi = this->requestData("QPI"); - writeLog("QPI:\t\t%s (Length: %d)", qpi,qpi.length()); + writeLog("QPI:\t\t%s (Length: %d)", qpi, qpi.length()); if (qpi != "" && qpi.substring(0, 2) == "PI") { writeLog(" Match protocol: PI3X"); @@ -149,7 +169,7 @@ void PI_Serial::autoDetect() // function for autodetect the inverter type break; } startChar = "^Dxxx"; - this->my_serialIntf->begin(serialIntfBaud, SWSERIAL_8N1, soft_rx, soft_tx, false); + this->my_serialIntf->begin(serialIntfBaud, SWSERIAL_8N1); String P005PI = this->requestData("^P005PI"); writeLog("^P005PI:\t\t%s (Length: %d)", P005PI, P005PI.length()); if (P005PI != "" && P005PI == "18") @@ -161,20 +181,38 @@ void PI_Serial::autoDetect() // function for autodetect the inverter type } this->my_serialIntf->end(); } + this->my_serialIntf->end(); writeLog("----------------- End Autodetect -----------------"); + if (protocol == NoD) + { + modbus = new MODBUS(this->my_serialIntf); + modbus->Init(); + if (modbus->autoDetect()){ + protocol = MODBUS_MUST; + } + } } bool PI_Serial::sendCustomCommand() { if (customCommandBuffer == "") return false; - get.raw.commandAnswer = requestData(customCommandBuffer); + + if (isModbus()) + { + get.raw.commandAnswer = modbus->requestData(customCommandBuffer); + } + else + { + get.raw.commandAnswer = requestData(customCommandBuffer); + } customCommandBuffer = ""; return true; } String PI_Serial::requestData(String command) { + String commandBuffer = ""; uint16_t crcCalc = 0; uint16_t crcRecive = 0; @@ -199,7 +237,7 @@ String PI_Serial::requestData(String command) commandBuffer.remove(commandBuffer.length() - 2); // remove the crc commandBuffer.remove(0, strlen(startChar)); // remove the start char ( for Pi30 and ^Dxxx for Pi18 - //requestOK++; + // requestOK++; connectionCounter = 0; } else if (getCHK(commandBuffer.substring(0, commandBuffer.length() - 1)) + 1 == commandBuffer[commandBuffer.length() - 1] && @@ -212,7 +250,7 @@ String PI_Serial::requestData(String command) commandBuffer.remove(commandBuffer.length() - 1); // remove the crc commandBuffer.remove(0, strlen(startChar)); // remove the start char ( for Pi30 and ^Dxxx for Pi18 - //requestOK++; + // requestOK++; connectionCounter = 0; } else if (commandBuffer.indexOf("NAK", strlen(startChar)) > 0) // catch NAK without crc @@ -225,7 +263,7 @@ String PI_Serial::requestData(String command) } else { - writeLog("ERROR Send: >%s< Recive: >%s<", command, commandBuffer); + writeLog("ERROR Send: >%s< Recive: >%s<", command, commandBuffer); connectionCounter++; commandBuffer = "ERCRC"; } @@ -345,4 +383,9 @@ char *PI_Serial::getModeDesc(char mode) // get the char from QMOD and make reada break; } return modeString; -} \ No newline at end of file +} + +bool PI_Serial::isModbus() +{ + return protocol == MODBUS_MUST; +} diff --git a/src/PI_Serial/PI_Serial.h b/src/PI_Serial/PI_Serial.h index 5b39ba1..e11f24c 100644 --- a/src/PI_Serial/PI_Serial.h +++ b/src/PI_Serial/PI_Serial.h @@ -3,6 +3,7 @@ #define PI_SERIAL_H #include +#include extern JsonObject deviceJson; extern JsonObject staticData; extern JsonObject liveData; @@ -85,9 +86,7 @@ class PI_Serial void callback(std::function func); std::function requestCallback; -private: - unsigned int soft_tx; - unsigned int soft_rx; +private: unsigned int serialIntfBaud; unsigned long previousTime = 0; @@ -99,11 +98,14 @@ class PI_Serial byte qexCounter = 0; String customCommandBuffer; + + MODBUS *modbus; enum protocolType { NoD, PI18, PI30, + MODBUS_MUST }; /** @@ -148,6 +150,8 @@ class PI_Serial bool PIXX_QPIRI(); bool PIXX_QPI(); bool PIXX_QMN(); + + bool isModbus(); }; #endif \ No newline at end of file diff --git a/src/modbus/modbus.cpp b/src/modbus/modbus.cpp new file mode 100644 index 0000000..4362081 --- /dev/null +++ b/src/modbus/modbus.cpp @@ -0,0 +1,379 @@ +// #define isDEBUG +#include "ArduinoJson.h" +#include "modbus.h" + +extern void writeLog(const char *format, ...); +//---------------------------------------------------------------------- +// Public Functions +//---------------------------------------------------------------------- + +MODBUS::MODBUS(SoftwareSerial *port) +{ + this->my_serialIntf = port; +} + +void MODBUS::preTransmission() +{ + digitalWrite(RS485_DIR_PIN, 1); +} + +void MODBUS::postTransmission() +{ + digitalWrite(RS485_DIR_PIN, 0); +} + +bool MODBUS::Init() +{ + // Null check the serial interface + if (this->my_serialIntf == NULL) + { + writeLog("No serial specificed!"); + return false; + } + this->my_serialIntf->setTimeout(500); + this->my_serialIntf->begin(RS485_BAUDRATE, SWSERIAL_8N1); + + // Init in receive mode + pinMode(RS485_DIR_PIN, OUTPUT); + this->postTransmission(); + + // Callbacks allow us to configure the RS485 transceiver correctly + mb.preTransmission(preTransmission); + mb.postTransmission(postTransmission); + + mb.begin(INVERTER_MODBUS_ADDR, *this->my_serialIntf); + + live_info = { + .variant = &liveData, + .registers = registers_live, + .array_size = sizeof(registers_live) / sizeof(modbus_register_t), + .curr_register = 0}; + + return true; +} + +void MODBUS::loop() +{ + if (!device_found) + return; + if (parseModbusToJson(live_info)) + { + connectionCounter = 0; + requestCallback(); + } + else + { + connectionCounter++; + } + connection = (connectionCounter < 10) ? true : false; +} + +void MODBUS::callback(std::function func) +{ + requestCallback = func; +} + +String MODBUS::requestData(String command) +{ + + return ""; +} + +bool MODBUS::getModbusResultMsg(uint8_t result) +{ + String tmpstr2 = ""; + switch (result) + { + case mb.ku8MBSuccess: + return true; + break; + case mb.ku8MBIllegalFunction: + tmpstr2 = "Illegal Function"; + break; + case mb.ku8MBIllegalDataAddress: + tmpstr2 = "Illegal Data Address"; + break; + case mb.ku8MBIllegalDataValue: + tmpstr2 = "Illegal Data Value"; + break; + case mb.ku8MBSlaveDeviceFailure: + tmpstr2 = "Slave Device Failure"; + break; + case mb.ku8MBInvalidSlaveID: + tmpstr2 = "Invalid Slave ID"; + break; + case mb.ku8MBInvalidFunction: + tmpstr2 = "Invalid Function"; + break; + case mb.ku8MBResponseTimedOut: + tmpstr2 = "Response Timed Out"; + break; + case mb.ku8MBInvalidCRC: + tmpstr2 = "Invalid CRC"; + break; + default: + tmpstr2 = "Unknown error: " + String(result); + break; + } + writeLog("%s", tmpstr2.c_str()); + return false; +} + +bool MODBUS::getModbusValue(uint16_t register_id, modbus_entity_t modbus_entity, uint16_t *value_ptr) +{ + // writeLog("Requesting data"); + for (uint8_t i = 0; i < MODBUS_RETRIES; ++i) + { + if (MODBUS_RETRIES > 1) + { + writeLog("Trial %d/%d", i + 1, MODBUS_RETRIES); + } + if (modbus_entity == MODBUS_TYPE_HOLDING) + { + uint8_t result = mb.readHoldingRegisters(register_id, 1); + if (getModbusResultMsg(result)) + { + *value_ptr = mb.getResponseBuffer(0); + return true; + } + } + else + { + writeLog("Unsupported Modbus entity type"); + value_ptr = nullptr; + return false; + } + } + // Time-out + writeLog("Time-out"); + value_ptr = nullptr; + return false; +} + +String MODBUS::toBinary(uint16_t input) +{ + String output; + while (input != 0) + { + output = (input % 2 == 0 ? "0" : "1") + output; + input /= 2; + } + return output; +} + +bool MODBUS::decodeDiematicDecimal(uint16_t int_input, int8_t decimals, float *value_ptr) +{ + if (int_input == 65535) + { + value_ptr = nullptr; + return false; + } + else + { + uint16_t masked_input = int_input & 0x7FFF; + float output = static_cast(masked_input); + if (int_input >> 15 == 1) + { + output = -output; + } + *value_ptr = output / pow(10, decimals); + return true; + } +} + +bool MODBUS::readModbusRegisterToJson(const modbus_register_t *reg, JsonObject *variant) +{ + // register found + if (reg == nullptr || variant == nullptr) + { + return false; // Return false if invalid input + } + writeLog("Register id=%d type=0x%x name=%s", reg->id, reg->type, reg->name); + uint16_t raw_value = 0; + + float final_value; + if (getModbusValue(reg->id, reg->modbus_entity, &raw_value)) + { + // writeLog("Raw value: %s=%#06x", reg->name, raw_value); + + switch (reg->type) + { + case REGISTER_TYPE_U16: + // writeLog("Value: %u", raw_value); + (*variant)[reg->name] = raw_value; + break; + + case REGISTER_TYPE_DIEMATIC_ONE_DECIMAL: + if (decodeDiematicDecimal(raw_value, 1, &final_value)) + { + // writeLog("Value: %.1f", final_value); + (*variant)[reg->name] = final_value; + } + else + { + writeLog("Invalid Diematic value"); + } + break; + + case REGISTER_TYPE_DIEMATIC_TWO_DECIMAL: + if (decodeDiematicDecimal(raw_value, 2, &final_value)) + { + // writeLog("Value: %.1f", final_value); + (*variant)[reg->name] = final_value; + } + else + { + writeLog("Invalid Diematic value"); + } + break; + case REGISTER_TYPE_BITFIELD: + + for (uint8_t j = 0; j < 16; ++j) + { + const char *bit_varname = reg->optional_param.bitfield[j]; + if (bit_varname == nullptr) + { + writeLog("[bit%02d] end of bitfield reached", j); + break; + } + const uint8_t bit_value = raw_value >> j & 1; + writeLog(" [bit%02d] %s=%d", j, bit_varname, bit_value); + (*variant)[bit_varname] = bit_value; + } + break; + + case REGISTER_TYPE_CUSTOM_VAL_NAME: + { + bool isfound = false; + for (uint8_t j = 0; j < 16; ++j) + { + const char *bit_varname = reg->optional_param.bitfield[j]; + if (bit_varname == nullptr) + { + writeLog("bitfield[%d] is null", j); + break; + } + if (j == raw_value) + { + // writeLog("Match found, value: %s", bit_varname); + (*variant)[reg->name] = bit_varname; + isfound = true; + break; + } + } + if (!isfound) + { + writeLog("CUSTOM_VAL_NAME not found for raw_value=%d", raw_value); + } + break; + } + case REGISTER_TYPE_ASCII: + { + String out = ""; + char high_byte = (raw_value >> 8) & 0xFF; + char low_byte = raw_value & 0xFF; + + out += high_byte; + out += low_byte; + (*variant)[reg->name] = out; + break; + } + case REGISTER_TYPE_DEBUG: + + writeLog("Raw DEBUG value: %s=%#06x %s", reg->name, raw_value, toBinary(raw_value).c_str()); + break; + + default: + // Unsupported type + + writeLog("Unsupported register type"); + break; + } + } + else + { + writeLog("Request failed!"); + return false; + } + writeLog("Request OK!"); + return true; +} + +bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info) +{ + // writeLog("Parsing Modbus registers"); + if (register_info.curr_register >= register_info.array_size) + { + register_info.curr_register = 0; + } + writeLog("Registers size %d", register_info.array_size); + previousTime = millis(); + for (; register_info.curr_register <= register_info.array_size - 1; register_info.curr_register++) + { + writeLog("curr %d", register_info.curr_register); + bool ret_val = readModbusRegisterToJson(®ister_info.registers[register_info.curr_register], register_info.variant); + + if (!ret_val) + { + return false; + } + + if (millis() - previousTime > cmdDelayTime) //limit execution time + { + return false; + } + } + + return true; +} + +//---------------------------------------------------------------------- +// Private Functions +//---------------------------------------------------------------------- +bool MODBUS::autoDetect() // function for autodetect the inverter type +{ + String modelName = retrieveModel(); + if (modelName) + { + writeLog(" Found Modbus device: %s", modelName); + staticData["Device_Model"] = modelName; + device_found = true; + return true; + } + + return false; +} + +String MODBUS::retrieveModel() +{ + bool found = false; + String model = ""; + DynamicJsonDocument doc(256); + JsonObject jsonObj = doc.to(); // Create and get JsonObject + modbus_register_info_t model_info = { + .variant = &jsonObj, + .registers = registers_device_model, + .array_size = sizeof(registers_device_model) / sizeof(modbus_register_t), + .curr_register = 0}; + + for (size_t i = 0; i < 6; i++) + { + if (parseModbusToJson(model_info)) + { + found = true; + break; + } + delay(100); + } + + if (found) + { + const char *modelHigh = doc[DEVICE_MODEL_HIGH]; + int modelLow = doc[DEVICE_MODEL_LOW]; + String jsonString; + serializeJson(doc, jsonString); + writeLog("JSON MODEL: %s", jsonString); + return String(modelHigh) + String(modelLow); + } + return model; +} diff --git a/src/modbus/modbus.h b/src/modbus/modbus.h new file mode 100644 index 0000000..95df0a1 --- /dev/null +++ b/src/modbus/modbus.h @@ -0,0 +1,105 @@ +#include "SoftwareSerial.h" +#ifndef MODBUS_H +#define MODBUS_H + +#include +#include +#include "modbus_registers.h" +extern JsonObject deviceJson; +extern JsonObject staticData; +extern JsonObject liveData; + +#define RS485_DIR_PIN D5 +#define RS485_BAUDRATE 19200 + +#define INVERTER_MODBUS_ADDR 4 + +#define MODBUS_RETRIES 2 + +typedef struct +{ + JsonObject *variant; + const modbus_register_t *registers; + uint8_t array_size; + uint8_t curr_register; +} modbus_register_info_t; + +class MODBUS +{ +public: + bool requestStaticData = true; + bool connection = false; + modbus_register_info_t live_info; + MODBUS(SoftwareSerial *port); + + /** + * @brief Initializes this driver + * @details Configures the serial peripheral and pre-loads the transmit buffer with command-independent bytes + */ + bool Init(); + + /** + * @brief Updating the Data from the inverter + */ + void loop(); + + /** + * @brief Send custom command to the device + */ + bool sendCommand(String command); + + /** + * @brief callback function + * + */ + void callback(std::function func); + std::function requestCallback; + bool readModbusRegisterToJson(const modbus_register_t *reg, JsonObject *variant); + bool parseModbusToJson(modbus_register_info_t ®ister_info); + bool autoDetect(); + /** + * @brief Sends a complete packet with the specified command + * @details sends the command over the specified serial connection + */ + String requestData(String command); + +private: + bool device_found = false; + unsigned int soft_tx; + unsigned int soft_rx; + unsigned long previousTime = 0; + unsigned long cmdDelayTime = 3000; + byte requestCounter = 0; + + long long int connectionCounter = 0; + + byte qexCounter = 0; + + static void preTransmission(); + static void postTransmission(); + String toBinary(uint16_t input); + bool decodeDiematicDecimal(uint16_t int_input, int8_t decimals, float *value_ptr); + bool getModbusResultMsg(uint8_t result); + bool getModbusValue(uint16_t register_id, modbus_entity_t modbus_entity, uint16_t *value_ptr); + /** + * @brief get the crc from a string + */ + uint16_t getCRC(String data); + + /** + * @brief get the crc from a string + */ + byte getCHK(String data); + + String retrieveModel(); + + /** + * @brief Serial interface used for communication + * @details This is set in the constructor + */ + SoftwareSerial *my_serialIntf; + + ModbusMaster mb; +}; + +#endif diff --git a/src/modbus/modbus_registers.h b/src/modbus/modbus_registers.h new file mode 100644 index 0000000..abbdce2 --- /dev/null +++ b/src/modbus/modbus_registers.h @@ -0,0 +1,81 @@ +#ifndef SRC_MODBUS_REGISTERS_H_ +#define SRC_MODBUS_REGISTERS_H_ +#include "Arduino.h" + +typedef enum +{ + MODBUS_TYPE_HOLDING = 0x00, /*!< Modbus Holding register. */ + // MODBUS_TYPE_INPUT, /*!< Modbus Input register. */ + // MODBUS_TYPE_COIL, /*!< Modbus Coils. */ + // MODBUS_TYPE_DISCRETE, /*!< Modbus Discrete bits. */ + // MODBUS_TYPE_COUNT, + // MODBUS_TYPE_UNKNOWN = 0xFF +} modbus_entity_t; + +typedef enum +{ + // REGISTER_TYPE_U8 = 0x00, /*!< Unsigned 8 */ + REGISTER_TYPE_U16 = 0x01, /*!< Unsigned 16 */ + // REGISTER_TYPE_U32 = 0x02, /*!< Unsigned 32 */ + // REGISTER_TYPE_FLOAT = 0x03, /*!< Float type */ + REGISTER_TYPE_ASCII = 0x04, /*!< ASCII type */ + REGISTER_TYPE_DIEMATIC_ONE_DECIMAL = 0x05, + REGISTER_TYPE_DIEMATIC_TWO_DECIMAL = 0x06, + REGISTER_TYPE_BITFIELD = 0x07, + REGISTER_TYPE_DEBUG = 0x08, + REGISTER_TYPE_CUSTOM_VAL_NAME = 0x09, +} register_type_t; + +typedef union +{ + const char *bitfield[16]; +} optional_param_t; + +typedef struct +{ + uint16_t id; + modbus_entity_t modbus_entity; /*!< Type of modbus parameter */ + register_type_t type; /*!< Float, U8, U16, U32, ASCII, etc. */ + const char *name; + optional_param_t optional_param; +} modbus_register_t; + +const modbus_register_t registers_live[] = { + + {25201, MODBUS_TYPE_HOLDING, REGISTER_TYPE_CUSTOM_VAL_NAME, "Inverter_Operation_Mode", {.bitfield = { + "Power On", + "Self Test", + "OffGrid", + "GridTie", + "ByPass", + "Stop", + "GridCharging", + }}}, + + {25205, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "Battery_Voltage"}, + {25274, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Battery_Load"}, + {25207, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "AC_in_Voltage"}, + {25226, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_TWO_DECIMAL, "AC_in_Frequenz"}, + + {25206, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "AC_out_Voltage"}, + {25225, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_TWO_DECIMAL, "AC_out_Frequenz"}, + + {25208, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "Inverter_Bus_Voltage"}, + {25216, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Output_load_percent"}, + {15205, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "PV_Input_Voltage"}, + {15208, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "PV_Charging_Power"}, + {15207, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "PV_Input_Current"}, + {25233, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Inverter_Bus_Temperature"}, + {25234, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Transformer_temperature"}, + {15209, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "MPPT1_Charger_Temperature"}, +}; + +#define DEVICE_MODEL_HIGH "Device_Model_Hight" +#define DEVICE_MODEL_LOW "Device_Model_Low" + +const modbus_register_t registers_device_model[] = { + {20000, MODBUS_TYPE_HOLDING, REGISTER_TYPE_ASCII, "Device_Model_Hight"}, + {20001, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Device_Model_Low"} +}; + +#endif // SRC_MODBUS_REGISTERS_H_ \ No newline at end of file From 86a857b11707b6f04c7388f330873737df09701e Mon Sep 17 00:00:00 2001 From: Artem Kovalenko Date: Tue, 24 Sep 2024 14:57:50 +0300 Subject: [PATCH 02/10] convert floats to string, to strip decimals. reassign dir pin for esp01 --- src/PI_Serial/PI_Serial.h | 15 ++++++++------- src/main.cpp | 2 +- src/modbus/modbus.cpp | 34 +++++++++++++++++++++------------- src/modbus/modbus.h | 16 ++++++++++------ 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/PI_Serial/PI_Serial.h b/src/PI_Serial/PI_Serial.h index e11f24c..b1b9444 100644 --- a/src/PI_Serial/PI_Serial.h +++ b/src/PI_Serial/PI_Serial.h @@ -86,6 +86,14 @@ class PI_Serial void callback(std::function func); std::function requestCallback; + enum protocolType + { + NoD, + PI18, + PI30, + MODBUS_MUST + }; + private: unsigned int serialIntfBaud; @@ -100,13 +108,6 @@ class PI_Serial String customCommandBuffer; MODBUS *modbus; - enum protocolType - { - NoD, - PI18, - PI30, - MODBUS_MUST - }; /** * @brief get the crc from a string diff --git a/src/main.cpp b/src/main.cpp index 1b35cf8..4b20343 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -451,7 +451,7 @@ void loop() if (restartNow && millis() >= (RestartTimer + 500)) { ESP.restart(); - } + } notificationLED(); // notification LED routine } diff --git a/src/modbus/modbus.cpp b/src/modbus/modbus.cpp index 4362081..4ac616f 100644 --- a/src/modbus/modbus.cpp +++ b/src/modbus/modbus.cpp @@ -3,23 +3,32 @@ #include "modbus.h" extern void writeLog(const char *format, ...); + +unsigned int dir_pin; + //---------------------------------------------------------------------- // Public Functions //---------------------------------------------------------------------- MODBUS::MODBUS(SoftwareSerial *port) { - this->my_serialIntf = port; + my_serialIntf = port; + dir_pin = RS485_DIR_PIN; + + if (strcmp(HWBOARD, "esp01_1m") == 0) + { + dir_pin = RS485_ESP01_DIR_PIN; + } } void MODBUS::preTransmission() { - digitalWrite(RS485_DIR_PIN, 1); + digitalWrite(dir_pin, 1); } void MODBUS::postTransmission() { - digitalWrite(RS485_DIR_PIN, 0); + digitalWrite(dir_pin, 0); } bool MODBUS::Init() @@ -34,7 +43,7 @@ bool MODBUS::Init() this->my_serialIntf->begin(RS485_BAUDRATE, SWSERIAL_8N1); // Init in receive mode - pinMode(RS485_DIR_PIN, OUTPUT); + pinMode(dir_pin, OUTPUT); this->postTransmission(); // Callbacks allow us to configure the RS485 transceiver correctly @@ -55,7 +64,7 @@ bool MODBUS::Init() void MODBUS::loop() { if (!device_found) - return; + return; if (parseModbusToJson(live_info)) { connectionCounter = 0; @@ -122,11 +131,11 @@ bool MODBUS::getModbusResultMsg(uint8_t result) bool MODBUS::getModbusValue(uint16_t register_id, modbus_entity_t modbus_entity, uint16_t *value_ptr) { // writeLog("Requesting data"); - for (uint8_t i = 0; i < MODBUS_RETRIES; ++i) + for (uint8_t i = 0; i < MODBUS_RETRIES; i++) { if (MODBUS_RETRIES > 1) { - writeLog("Trial %d/%d", i + 1, MODBUS_RETRIES); + // writeLog("Trial %d/%d", i + 1, MODBUS_RETRIES); } if (modbus_entity == MODBUS_TYPE_HOLDING) { @@ -206,8 +215,8 @@ bool MODBUS::readModbusRegisterToJson(const modbus_register_t *reg, JsonObject * case REGISTER_TYPE_DIEMATIC_ONE_DECIMAL: if (decodeDiematicDecimal(raw_value, 1, &final_value)) { - // writeLog("Value: %.1f", final_value); - (*variant)[reg->name] = final_value; + // writeLog("Raw value: %#06x, floatValue: %f",raw_value, final_value); + (*variant)[reg->name] = String(final_value, 1); } else { @@ -219,7 +228,7 @@ bool MODBUS::readModbusRegisterToJson(const modbus_register_t *reg, JsonObject * if (decodeDiematicDecimal(raw_value, 2, &final_value)) { // writeLog("Value: %.1f", final_value); - (*variant)[reg->name] = final_value; + (*variant)[reg->name] = String(final_value, 2); } else { @@ -306,11 +315,10 @@ bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info) { register_info.curr_register = 0; } - writeLog("Registers size %d", register_info.array_size); + // writeLog("Registers size %d", register_info.array_size); previousTime = millis(); for (; register_info.curr_register <= register_info.array_size - 1; register_info.curr_register++) { - writeLog("curr %d", register_info.curr_register); bool ret_val = readModbusRegisterToJson(®ister_info.registers[register_info.curr_register], register_info.variant); if (!ret_val) @@ -318,7 +326,7 @@ bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info) return false; } - if (millis() - previousTime > cmdDelayTime) //limit execution time + if (millis() - previousTime > cmdDelayTime) // limit execution time { return false; } diff --git a/src/modbus/modbus.h b/src/modbus/modbus.h index 95df0a1..ae7ff9e 100644 --- a/src/modbus/modbus.h +++ b/src/modbus/modbus.h @@ -9,7 +9,11 @@ extern JsonObject deviceJson; extern JsonObject staticData; extern JsonObject liveData; -#define RS485_DIR_PIN D5 + +#define RS485_DIR_PIN 14 //D5 +#define RS485_ESP01_DIR_PIN 0 + + #define RS485_BAUDRATE 19200 #define INVERTER_MODBUS_ADDR 4 @@ -64,11 +68,11 @@ class MODBUS String requestData(String command); private: - bool device_found = false; - unsigned int soft_tx; - unsigned int soft_rx; - unsigned long previousTime = 0; - unsigned long cmdDelayTime = 3000; + bool device_found = false; + unsigned long previousTime = 0; + unsigned long cmdDelayTime = 3000; + + byte requestCounter = 0; long long int connectionCounter = 0; From 09a291b00f12d73692943fd1201b81bcc9693e9b Mon Sep 17 00:00:00 2001 From: Artem Kovalenko Date: Tue, 24 Sep 2024 16:26:45 +0300 Subject: [PATCH 03/10] add static data query --- src/modbus/modbus.cpp | 22 ++++++++++++++++++---- src/modbus/modbus.h | 1 + src/modbus/modbus_registers.h | 15 +++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/modbus/modbus.cpp b/src/modbus/modbus.cpp index 4ac616f..b5711ec 100644 --- a/src/modbus/modbus.cpp +++ b/src/modbus/modbus.cpp @@ -57,7 +57,11 @@ bool MODBUS::Init() .registers = registers_live, .array_size = sizeof(registers_live) / sizeof(modbus_register_t), .curr_register = 0}; - + static_info = { + .variant = &staticData, + .registers = registers_static, + .array_size = sizeof(registers_static) / sizeof(modbus_register_t), + .curr_register = 0}; return true; } @@ -65,15 +69,24 @@ void MODBUS::loop() { if (!device_found) return; - if (parseModbusToJson(live_info)) + + modbus_register_info_t *cur_info_registers = &live_info; + if (requestStaticData) + { + cur_info_registers = &static_info; + } + + if (parseModbusToJson(*cur_info_registers)) { connectionCounter = 0; + requestStaticData = false; requestCallback(); } else { connectionCounter++; } + connection = (connectionCounter < 10) ? true : false; } @@ -84,7 +97,7 @@ void MODBUS::callback(std::function func) String MODBUS::requestData(String command) { - + requestStaticData = true; return ""; } @@ -140,7 +153,8 @@ bool MODBUS::getModbusValue(uint16_t register_id, modbus_entity_t modbus_entity, if (modbus_entity == MODBUS_TYPE_HOLDING) { uint8_t result = mb.readHoldingRegisters(register_id, 1); - if (getModbusResultMsg(result)) + bool is_received = getModbusResultMsg(result); + if (is_received) { *value_ptr = mb.getResponseBuffer(0); return true; diff --git a/src/modbus/modbus.h b/src/modbus/modbus.h index ae7ff9e..424a6f9 100644 --- a/src/modbus/modbus.h +++ b/src/modbus/modbus.h @@ -34,6 +34,7 @@ class MODBUS bool requestStaticData = true; bool connection = false; modbus_register_info_t live_info; + modbus_register_info_t static_info; MODBUS(SoftwareSerial *port); /** diff --git a/src/modbus/modbus_registers.h b/src/modbus/modbus_registers.h index abbdce2..a3498b0 100644 --- a/src/modbus/modbus_registers.h +++ b/src/modbus/modbus_registers.h @@ -70,6 +70,21 @@ const modbus_register_t registers_live[] = { {15209, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "MPPT1_Charger_Temperature"}, }; +const modbus_register_t registers_static[] = { + + {10110, MODBUS_TYPE_HOLDING, REGISTER_TYPE_CUSTOM_VAL_NAME, "Battery_type", {.bitfield = { + "No choose", + "User defined", + "Lithium", + "Sealed Lead", + "AGM", + "GEL", + "Flooded", + }}}, + {10103, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "Battery_float_voltage"}, +}; + + #define DEVICE_MODEL_HIGH "Device_Model_Hight" #define DEVICE_MODEL_LOW "Device_Model_Low" From 3fa9ccb5afd654ab8b6c50eedfbbe894a057160e Mon Sep 17 00:00:00 2001 From: Artem Kovalenko Date: Wed, 25 Sep 2024 09:13:01 +0300 Subject: [PATCH 04/10] fix issue when curr_register not changing in case of timeout modbus reply --- src/modbus/modbus.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/modbus/modbus.cpp b/src/modbus/modbus.cpp index b5711ec..fd59e82 100644 --- a/src/modbus/modbus.cpp +++ b/src/modbus/modbus.cpp @@ -148,7 +148,7 @@ bool MODBUS::getModbusValue(uint16_t register_id, modbus_entity_t modbus_entity, { if (MODBUS_RETRIES > 1) { - // writeLog("Trial %d/%d", i + 1, MODBUS_RETRIES); + writeLog("Trial %d/%d", i + 1, MODBUS_RETRIES); } if (modbus_entity == MODBUS_TYPE_HOLDING) { @@ -331,10 +331,10 @@ bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info) } // writeLog("Registers size %d", register_info.array_size); previousTime = millis(); - for (; register_info.curr_register <= register_info.array_size - 1; register_info.curr_register++) + while (register_info.curr_register <= register_info.array_size) { - bool ret_val = readModbusRegisterToJson(®ister_info.registers[register_info.curr_register], register_info.variant); - + bool ret_val = readModbusRegisterToJson(®ister_info.registers[register_info.curr_register], register_info.variant); + register_info.curr_register++; if (!ret_val) { return false; @@ -355,14 +355,13 @@ bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info) bool MODBUS::autoDetect() // function for autodetect the inverter type { String modelName = retrieveModel(); - if (modelName) + if (!modelName.isEmpty()) { writeLog(" Found Modbus device: %s", modelName); staticData["Device_Model"] = modelName; device_found = true; return true; } - return false; } From 23a0dc3c3f01d64a1caa9c29d6442d2cecdd0fe0 Mon Sep 17 00:00:00 2001 From: Artem Kovalenko Date: Wed, 25 Sep 2024 09:31:28 +0300 Subject: [PATCH 05/10] adjust autodetect logging --- src/PI_Serial/PI_Serial.cpp | 4 ++-- src/modbus/modbus.cpp | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/PI_Serial/PI_Serial.cpp b/src/PI_Serial/PI_Serial.cpp index 93fc56f..55fb3ed 100644 --- a/src/PI_Serial/PI_Serial.cpp +++ b/src/PI_Serial/PI_Serial.cpp @@ -182,7 +182,6 @@ void PI_Serial::autoDetect() // function for autodetect the inverter type this->my_serialIntf->end(); } this->my_serialIntf->end(); - writeLog("----------------- End Autodetect -----------------"); if (protocol == NoD) { modbus = new MODBUS(this->my_serialIntf); @@ -190,7 +189,8 @@ void PI_Serial::autoDetect() // function for autodetect the inverter type if (modbus->autoDetect()){ protocol = MODBUS_MUST; } - } + } + writeLog("----------------- End Autodetect -----------------"); } bool PI_Serial::sendCustomCommand() diff --git a/src/modbus/modbus.cpp b/src/modbus/modbus.cpp index fd59e82..60708db 100644 --- a/src/modbus/modbus.cpp +++ b/src/modbus/modbus.cpp @@ -333,7 +333,7 @@ bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info) previousTime = millis(); while (register_info.curr_register <= register_info.array_size) { - bool ret_val = readModbusRegisterToJson(®ister_info.registers[register_info.curr_register], register_info.variant); + bool ret_val = readModbusRegisterToJson(®ister_info.registers[register_info.curr_register], register_info.variant); register_info.curr_register++; if (!ret_val) { @@ -354,15 +354,15 @@ bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info) //---------------------------------------------------------------------- bool MODBUS::autoDetect() // function for autodetect the inverter type { + writeLog("Try Autodetect Modbus device"); + device_found = false; String modelName = retrieveModel(); if (!modelName.isEmpty()) { writeLog(" Found Modbus device: %s", modelName); staticData["Device_Model"] = modelName; - device_found = true; - return true; } - return false; + return device_found; } String MODBUS::retrieveModel() @@ -377,7 +377,7 @@ String MODBUS::retrieveModel() .array_size = sizeof(registers_device_model) / sizeof(modbus_register_t), .curr_register = 0}; - for (size_t i = 0; i < 6; i++) + for (size_t i = 0; i < model_info.array_size * 2; i++) { if (parseModbusToJson(model_info)) { @@ -391,9 +391,6 @@ String MODBUS::retrieveModel() { const char *modelHigh = doc[DEVICE_MODEL_HIGH]; int modelLow = doc[DEVICE_MODEL_LOW]; - String jsonString; - serializeJson(doc, jsonString); - writeLog("JSON MODEL: %s", jsonString); return String(modelHigh) + String(modelLow); } return model; From c6a103508c8ea3f660d4de3f1daa9de283010caf Mon Sep 17 00:00:00 2001 From: Artem Kovalenko Date: Fri, 27 Sep 2024 09:21:39 +0300 Subject: [PATCH 06/10] fix autodetect in case first request was failed to receive --- src/modbus/modbus.cpp | 16 +++++++++++----- src/modbus/modbus.h | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/modbus/modbus.cpp b/src/modbus/modbus.cpp index 60708db..03675c3 100644 --- a/src/modbus/modbus.cpp +++ b/src/modbus/modbus.cpp @@ -322,7 +322,7 @@ bool MODBUS::readModbusRegisterToJson(const modbus_register_t *reg, JsonObject * return true; } -bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info) +bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info, bool skip_reg_on_error) { // writeLog("Parsing Modbus registers"); if (register_info.curr_register >= register_info.array_size) @@ -334,7 +334,10 @@ bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info) while (register_info.curr_register <= register_info.array_size) { bool ret_val = readModbusRegisterToJson(®ister_info.registers[register_info.curr_register], register_info.variant); - register_info.curr_register++; + if (skip_reg_on_error) + { + register_info.curr_register++; + } if (!ret_val) { return false; @@ -379,7 +382,7 @@ String MODBUS::retrieveModel() for (size_t i = 0; i < model_info.array_size * 2; i++) { - if (parseModbusToJson(model_info)) + if (parseModbusToJson(model_info, false)) { found = true; break; @@ -390,8 +393,11 @@ String MODBUS::retrieveModel() if (found) { const char *modelHigh = doc[DEVICE_MODEL_HIGH]; - int modelLow = doc[DEVICE_MODEL_LOW]; - return String(modelHigh) + String(modelLow); + if (modelHigh != nullptr) // check is not nullptr before converting it to a String. + { + int modelLow = doc[DEVICE_MODEL_LOW]; + return String(modelHigh) + String(modelLow); + } } return model; } diff --git a/src/modbus/modbus.h b/src/modbus/modbus.h index 424a6f9..efd15cf 100644 --- a/src/modbus/modbus.h +++ b/src/modbus/modbus.h @@ -60,7 +60,7 @@ class MODBUS void callback(std::function func); std::function requestCallback; bool readModbusRegisterToJson(const modbus_register_t *reg, JsonObject *variant); - bool parseModbusToJson(modbus_register_info_t ®ister_info); + bool parseModbusToJson(modbus_register_info_t ®ister_info, bool skip_reg_on_error = true); bool autoDetect(); /** * @brief Sends a complete packet with the specified command From 218607106afc9717fd890cb6a1349b86a95e02de Mon Sep 17 00:00:00 2001 From: Artem Kovalenko Date: Fri, 27 Sep 2024 21:42:16 +0300 Subject: [PATCH 07/10] simplify autodetect and fix while in parseModbusToJson --- src/modbus/modbus.cpp | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/modbus/modbus.cpp b/src/modbus/modbus.cpp index 03675c3..a27734e 100644 --- a/src/modbus/modbus.cpp +++ b/src/modbus/modbus.cpp @@ -328,13 +328,13 @@ bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info, bool skip_ if (register_info.curr_register >= register_info.array_size) { register_info.curr_register = 0; - } - // writeLog("Registers size %d", register_info.array_size); + } previousTime = millis(); - while (register_info.curr_register <= register_info.array_size) + while (register_info.curr_register < register_info.array_size) { + bool ret_val = readModbusRegisterToJson(®ister_info.registers[register_info.curr_register], register_info.variant); - if (skip_reg_on_error) + if (ret_val || skip_reg_on_error) { register_info.curr_register++; } @@ -364,13 +364,13 @@ bool MODBUS::autoDetect() // function for autodetect the inverter type { writeLog(" Found Modbus device: %s", modelName); staticData["Device_Model"] = modelName; + device_found = true; } return device_found; } String MODBUS::retrieveModel() { - bool found = false; String model = ""; DynamicJsonDocument doc(256); JsonObject jsonObj = doc.to(); // Create and get JsonObject @@ -384,20 +384,13 @@ String MODBUS::retrieveModel() { if (parseModbusToJson(model_info, false)) { - found = true; + const char *modelHigh = doc[DEVICE_MODEL_HIGH]; + int modelLow = doc[DEVICE_MODEL_LOW]; + model = String(modelHigh) + String(modelLow); break; } delay(100); } - if (found) - { - const char *modelHigh = doc[DEVICE_MODEL_HIGH]; - if (modelHigh != nullptr) // check is not nullptr before converting it to a String. - { - int modelLow = doc[DEVICE_MODEL_LOW]; - return String(modelHigh) + String(modelLow); - } - } return model; } From 69911392e898812b37f9062c5b6e4223f5771132 Mon Sep 17 00:00:00 2001 From: Artem Kovalenko Date: Fri, 27 Sep 2024 22:14:49 +0300 Subject: [PATCH 08/10] add delay between commands, for faster communication --- src/modbus/modbus.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modbus/modbus.cpp b/src/modbus/modbus.cpp index a27734e..ccc77d2 100644 --- a/src/modbus/modbus.cpp +++ b/src/modbus/modbus.cpp @@ -39,7 +39,7 @@ bool MODBUS::Init() writeLog("No serial specificed!"); return false; } - this->my_serialIntf->setTimeout(500); + this->my_serialIntf->setTimeout(2000); this->my_serialIntf->begin(RS485_BAUDRATE, SWSERIAL_8N1); // Init in receive mode @@ -154,6 +154,7 @@ bool MODBUS::getModbusValue(uint16_t register_id, modbus_entity_t modbus_entity, { uint8_t result = mb.readHoldingRegisters(register_id, 1); bool is_received = getModbusResultMsg(result); + delay(100); if (is_received) { *value_ptr = mb.getResponseBuffer(0); @@ -328,11 +329,11 @@ bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info, bool skip_ if (register_info.curr_register >= register_info.array_size) { register_info.curr_register = 0; - } + } previousTime = millis(); while (register_info.curr_register < register_info.array_size) { - + bool ret_val = readModbusRegisterToJson(®ister_info.registers[register_info.curr_register], register_info.variant); if (ret_val || skip_reg_on_error) { From db07befabfac80f4c456615367ddc15e1e9bcb46 Mon Sep 17 00:00:00 2001 From: Artem Kovalenko Date: Mon, 30 Sep 2024 13:01:25 +0300 Subject: [PATCH 09/10] parse a single register at time, to leave to the main threas ASAP. Add registers. Fix json memory usage overflow for decimals. --- src/modbus/modbus.cpp | 76 ++++++++++++++++++++++------------- src/modbus/modbus.h | 21 ++++++---- src/modbus/modbus_registers.h | 16 +++++--- 3 files changed, 70 insertions(+), 43 deletions(-) diff --git a/src/modbus/modbus.cpp b/src/modbus/modbus.cpp index ccc77d2..c8ad0db 100644 --- a/src/modbus/modbus.cpp +++ b/src/modbus/modbus.cpp @@ -62,32 +62,47 @@ bool MODBUS::Init() .registers = registers_static, .array_size = sizeof(registers_static) / sizeof(modbus_register_t), .curr_register = 0}; + previousTime = millis(); return true; } void MODBUS::loop() { if (!device_found) + { + return; + } + + if (millis() - previousTime < cmdDelayTime) + { return; + } modbus_register_info_t *cur_info_registers = &live_info; if (requestStaticData) { cur_info_registers = &static_info; } - - if (parseModbusToJson(*cur_info_registers)) + switch (parseModbusToJson(*cur_info_registers)) { + case READ_OK: connectionCounter = 0; - requestStaticData = false; - requestCallback(); + break; + case READ_FAIL: + connectionCounter++; + break; + default: + break; } - else + + connection = connectionCounter < MAX_CONNECTION_ATTEMPTS; + if (isAllRegistersRead(*cur_info_registers)) { - connectionCounter++; + requestStaticData = false; + requestCallback(); } - connection = (connectionCounter < 10) ? true : false; + previousTime = millis(); } void MODBUS::callback(std::function func) @@ -148,13 +163,12 @@ bool MODBUS::getModbusValue(uint16_t register_id, modbus_entity_t modbus_entity, { if (MODBUS_RETRIES > 1) { - writeLog("Trial %d/%d", i + 1, MODBUS_RETRIES); + // writeLog("Trial %d/%d", i + 1, MODBUS_RETRIES); } if (modbus_entity == MODBUS_TYPE_HOLDING) { uint8_t result = mb.readHoldingRegisters(register_id, 1); bool is_received = getModbusResultMsg(result); - delay(100); if (is_received) { *value_ptr = mb.getResponseBuffer(0); @@ -212,13 +226,13 @@ bool MODBUS::readModbusRegisterToJson(const modbus_register_t *reg, JsonObject * { return false; // Return false if invalid input } - writeLog("Register id=%d type=0x%x name=%s", reg->id, reg->type, reg->name); + // writeLog("Register id=%d type=0x%x name=%s", reg->id, reg->type, reg->name); uint16_t raw_value = 0; float final_value; if (getModbusValue(reg->id, reg->modbus_entity, &raw_value)) { - // writeLog("Raw value: %s=%#06x", reg->name, raw_value); + writeLog("Raw value: %s=%#06x\n", reg->name, raw_value); switch (reg->type) { @@ -226,12 +240,16 @@ bool MODBUS::readModbusRegisterToJson(const modbus_register_t *reg, JsonObject * // writeLog("Value: %u", raw_value); (*variant)[reg->name] = raw_value; break; + case REGISTER_TYPE_INT16: + // writeLog("Value: %u", raw_value); + (*variant)[reg->name] = static_cast(raw_value); + break; case REGISTER_TYPE_DIEMATIC_ONE_DECIMAL: if (decodeDiematicDecimal(raw_value, 1, &final_value)) { // writeLog("Raw value: %#06x, floatValue: %f",raw_value, final_value); - (*variant)[reg->name] = String(final_value, 1); + (*variant)[reg->name] = (int)(final_value * 100 + 0.5) / 100.0; } else { @@ -243,7 +261,7 @@ bool MODBUS::readModbusRegisterToJson(const modbus_register_t *reg, JsonObject * if (decodeDiematicDecimal(raw_value, 2, &final_value)) { // writeLog("Value: %.1f", final_value); - (*variant)[reg->name] = String(final_value, 2); + (*variant)[reg->name] = (int)(final_value * 1000 + 0.5) / 1000.0; } else { @@ -319,38 +337,37 @@ bool MODBUS::readModbusRegisterToJson(const modbus_register_t *reg, JsonObject * writeLog("Request failed!"); return false; } - writeLog("Request OK!"); + // writeLog("Request OK!"); return true; } -bool MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info, bool skip_reg_on_error) +response_type_t MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info, bool skip_reg_on_error) { - // writeLog("Parsing Modbus registers"); + if (register_info.curr_register >= register_info.array_size) { register_info.curr_register = 0; } - previousTime = millis(); while (register_info.curr_register < register_info.array_size) { - bool ret_val = readModbusRegisterToJson(®ister_info.registers[register_info.curr_register], register_info.variant); if (ret_val || skip_reg_on_error) { register_info.curr_register++; } - if (!ret_val) - { - return false; - } - if (millis() - previousTime > cmdDelayTime) // limit execution time - { - return false; - } + return ret_val ? READ_OK : READ_FAIL; } + return READ_FAIL; +} - return true; +bool MODBUS::isAllRegistersRead(modbus_register_info_t ®ister_info) +{ + if (register_info.curr_register >= register_info.array_size) + { + return true; + } + return false; } //---------------------------------------------------------------------- @@ -383,14 +400,15 @@ String MODBUS::retrieveModel() for (size_t i = 0; i < model_info.array_size * 2; i++) { - if (parseModbusToJson(model_info, false)) + parseModbusToJson(model_info, false); + if (isAllRegistersRead(model_info)) { const char *modelHigh = doc[DEVICE_MODEL_HIGH]; int modelLow = doc[DEVICE_MODEL_LOW]; model = String(modelHigh) + String(modelLow); break; } - delay(100); + delay(50); } return model; diff --git a/src/modbus/modbus.h b/src/modbus/modbus.h index efd15cf..d96fc3d 100644 --- a/src/modbus/modbus.h +++ b/src/modbus/modbus.h @@ -9,10 +9,8 @@ extern JsonObject deviceJson; extern JsonObject staticData; extern JsonObject liveData; - -#define RS485_DIR_PIN 14 //D5 -#define RS485_ESP01_DIR_PIN 0 - +#define RS485_DIR_PIN 14 // D5 +#define RS485_ESP01_DIR_PIN 0 #define RS485_BAUDRATE 19200 @@ -20,6 +18,12 @@ extern JsonObject liveData; #define MODBUS_RETRIES 2 +typedef enum +{ + READ_FAIL = 0, + READ_OK = 1, +} response_type_t; + typedef struct { JsonObject *variant; @@ -31,6 +35,7 @@ typedef struct class MODBUS { public: + const uint8_t MAX_CONNECTION_ATTEMPTS = 10; bool requestStaticData = true; bool connection = false; modbus_register_info_t live_info; @@ -60,7 +65,8 @@ class MODBUS void callback(std::function func); std::function requestCallback; bool readModbusRegisterToJson(const modbus_register_t *reg, JsonObject *variant); - bool parseModbusToJson(modbus_register_info_t ®ister_info, bool skip_reg_on_error = true); + response_type_t parseModbusToJson(modbus_register_info_t ®ister_info, bool skip_reg_on_error = true); + bool isAllRegistersRead(modbus_register_info_t ®ister_info); bool autoDetect(); /** * @brief Sends a complete packet with the specified command @@ -69,10 +75,9 @@ class MODBUS String requestData(String command); private: - bool device_found = false; + bool device_found = false; unsigned long previousTime = 0; - unsigned long cmdDelayTime = 3000; - + unsigned long cmdDelayTime = 100; byte requestCounter = 0; diff --git a/src/modbus/modbus_registers.h b/src/modbus/modbus_registers.h index a3498b0..3be4d62 100644 --- a/src/modbus/modbus_registers.h +++ b/src/modbus/modbus_registers.h @@ -16,6 +16,7 @@ typedef enum { // REGISTER_TYPE_U8 = 0x00, /*!< Unsigned 8 */ REGISTER_TYPE_U16 = 0x01, /*!< Unsigned 16 */ + REGISTER_TYPE_INT16 = 0x02, /*!< Signed 16 */ // REGISTER_TYPE_U32 = 0x02, /*!< Unsigned 32 */ // REGISTER_TYPE_FLOAT = 0x03, /*!< Float type */ REGISTER_TYPE_ASCII = 0x04, /*!< ASCII type */ @@ -52,15 +53,13 @@ const modbus_register_t registers_live[] = { "GridCharging", }}}, - {25205, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "Battery_Voltage"}, - {25274, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Battery_Load"}, - {25207, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "AC_in_Voltage"}, - {25226, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_TWO_DECIMAL, "AC_in_Frequenz"}, - + {25205, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "Battery_Voltage"}, {25206, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "AC_out_Voltage"}, + {25207, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "AC_in_Voltage"}, + {25208, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "Inverter_Bus_Voltage"}, {25225, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_TWO_DECIMAL, "AC_out_Frequenz"}, + {25226, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_TWO_DECIMAL, "AC_in_Frequenz"}, - {25208, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "Inverter_Bus_Voltage"}, {25216, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Output_load_percent"}, {15205, MODBUS_TYPE_HOLDING, REGISTER_TYPE_DIEMATIC_ONE_DECIMAL, "PV_Input_Voltage"}, {15208, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "PV_Charging_Power"}, @@ -68,6 +67,11 @@ const modbus_register_t registers_live[] = { {25233, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Inverter_Bus_Temperature"}, {25234, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "Transformer_temperature"}, {15209, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "MPPT1_Charger_Temperature"}, + + {25215, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "AC_out_Watt"}, //W + {25216, MODBUS_TYPE_HOLDING, REGISTER_TYPE_U16, "AC_out_percent"}, //% + + {25274, MODBUS_TYPE_HOLDING, REGISTER_TYPE_INT16, "Battery_Load"}, }; const modbus_register_t registers_static[] = { From 84909721a35f8a8cb8fe535a82e9c08d9020286f Mon Sep 17 00:00:00 2001 From: SoftWareCrash Date: Wed, 2 Oct 2024 14:34:55 +0200 Subject: [PATCH 10/10] =?UTF-8?q?=F0=9F=90=9B=20Fix=20IOS=2018=20Safari=20?= =?UTF-8?q?webUI=20Bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- platformio.ini | 8 ++++---- src/main.cpp | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/platformio.ini b/platformio.ini index 143de3d..c140edb 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 +custom_prog_version = 1.2.1 build_flags = -DVERSION=${this.custom_prog_version} -DPIO_SRC_NAM="Solar2MQTT" @@ -23,13 +23,13 @@ extra_scripts = pre:tools/mini_html.py post:tools/post_compile.py lib_deps = bblanchon/ArduinoJson @ ^6.21.2 - ottowinter/ESPAsyncTCP-esphome@^1.2.3 - ottowinter/ESPAsyncWebServer-esphome @ ^3.1.0 + esphome/ESPAsyncTCP-esphome @ 2.0.0 + mathieucarbou/ESPAsyncWebServer @ 3.3.7 + 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 - asjdf/WebSerialLite@^2.2.0 4-20ma/ModbusMaster@^2.0.1 [env:d1_mini] diff --git a/src/main.cpp b/src/main.cpp index 4b20343..60b2a15 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -327,14 +327,14 @@ void setup() server.on("/set", HTTP_GET, [](AsyncWebServerRequest *request) { if(strlen(settings.data.httpUser) > 0 && !request->authenticate(settings.data.httpUser, settings.data.httpPass)) return request->requestAuthentication(); - AsyncWebParameter *p = request->getParam(0); - if (p->name() == "CC") - { - commandFromUser = (p->value()); + String message; + if (request->hasParam("CC")) { + message = request->getParam("CC")->value(); + commandFromUser = (message); } - if (p->name() == "ha") - { - haDiscTrigger = true; + if (request->hasParam("ha")) { + message = request->getParam("ha")->value(); + haDiscTrigger = true; } request->send(200, "text/plain", "message received"); });