diff --git a/platformio.ini b/platformio.ini index 618ab98..3e31204 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.8 +custom_prog_version = 1.2.2 build_flags = -DVERSION=${this.custom_prog_version} -DPIO_SRC_NAM="Solar2MQTT" @@ -30,6 +30,7 @@ lib_deps = plerup/EspSoftwareSerial @ ^8.2.0 https://github.com/dok-net/ghostl robtillaart/CRC@^1.0.1 + 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 d3fdf39..3720e89 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; @@ -23,9 +23,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() @@ -37,9 +35,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; } @@ -56,53 +62,68 @@ 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; - break; - case 2: - requestCounter = PIXX_QMOD() ? (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 3: - requestCounter = PIXX_Q1() ? (requestCounter + 1) : 0; - break; - case 4: - requestCounter = PIXX_QEX() ? (requestCounter + 1) : 0; - break; - case 5: - requestCounter = PIXX_QPIWS() ? (requestCounter + 1) : 0; - break; - case 6: - 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: + requestCounter = PIXX_QPIWS() ? (requestCounter + 1) : 0; + break; + case 6: + 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 @@ -142,9 +163,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"); @@ -153,7 +174,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") @@ -165,6 +186,15 @@ void PI_Serial::autoDetect() // function for autodetect the inverter type } this->my_serialIntf->end(); } + this->my_serialIntf->end(); + if (protocol == NoD) + { + modbus = new MODBUS(this->my_serialIntf); + modbus->Init(); + if (modbus->autoDetect()){ + protocol = MODBUS_MUST; + } + } writeLog("----------------- End Autodetect -----------------"); } @@ -172,13 +202,22 @@ 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; @@ -203,7 +242,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] && @@ -216,7 +255,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 @@ -229,7 +268,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"; } @@ -349,4 +388,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 f0fe1e8..df9aaf1 100644 --- a/src/PI_Serial/PI_Serial.h +++ b/src/PI_Serial/PI_Serial.h @@ -3,6 +3,7 @@ #define PI_SERIAL_H #include "vector" #include +#include extern JsonObject deviceJson; extern JsonObject staticData; extern JsonObject liveData; @@ -86,9 +87,15 @@ class PI_Serial void callback(std::function func); std::function requestCallback; -private: - unsigned int soft_tx; - unsigned int soft_rx; + enum protocolType + { + NoD, + PI18, + PI30, + MODBUS_MUST + }; + +private: unsigned int serialIntfBaud; unsigned long previousTime = 0; @@ -100,12 +107,8 @@ class PI_Serial byte qexCounter = 0; String customCommandBuffer; - enum protocolType - { - NoD, - PI18, - PI30, - }; + + MODBUS *modbus; /** * @brief get the crc from a string @@ -150,6 +153,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/main.cpp b/src/main.cpp index 15d9e19..fa3c495 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 new file mode 100644 index 0000000..c8ad0db --- /dev/null +++ b/src/modbus/modbus.cpp @@ -0,0 +1,415 @@ +// #define isDEBUG +#include "ArduinoJson.h" +#include "modbus.h" + +extern void writeLog(const char *format, ...); + +unsigned int dir_pin; + +//---------------------------------------------------------------------- +// Public Functions +//---------------------------------------------------------------------- + +MODBUS::MODBUS(SoftwareSerial *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(dir_pin, 1); +} + +void MODBUS::postTransmission() +{ + digitalWrite(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(2000); + this->my_serialIntf->begin(RS485_BAUDRATE, SWSERIAL_8N1); + + // Init in receive mode + pinMode(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}; + static_info = { + .variant = &staticData, + .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; + } + switch (parseModbusToJson(*cur_info_registers)) + { + case READ_OK: + connectionCounter = 0; + break; + case READ_FAIL: + connectionCounter++; + break; + default: + break; + } + + connection = connectionCounter < MAX_CONNECTION_ATTEMPTS; + if (isAllRegistersRead(*cur_info_registers)) + { + requestStaticData = false; + requestCallback(); + } + + previousTime = millis(); +} + +void MODBUS::callback(std::function func) +{ + requestCallback = func; +} + +String MODBUS::requestData(String command) +{ + requestStaticData = true; + 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); + bool is_received = getModbusResultMsg(result); + if (is_received) + { + *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\n", 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_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] = (int)(final_value * 100 + 0.5) / 100.0; + } + 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] = (int)(final_value * 1000 + 0.5) / 1000.0; + } + 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; +} + +response_type_t MODBUS::parseModbusToJson(modbus_register_info_t ®ister_info, bool skip_reg_on_error) +{ + + if (register_info.curr_register >= register_info.array_size) + { + register_info.curr_register = 0; + } + 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++; + } + + return ret_val ? READ_OK : READ_FAIL; + } + return READ_FAIL; +} + +bool MODBUS::isAllRegistersRead(modbus_register_info_t ®ister_info) +{ + if (register_info.curr_register >= register_info.array_size) + { + return true; + } + return false; +} + +//---------------------------------------------------------------------- +// Private Functions +//---------------------------------------------------------------------- +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 device_found; +} + +String MODBUS::retrieveModel() +{ + 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 < model_info.array_size * 2; i++) + { + 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(50); + } + + return model; +} diff --git a/src/modbus/modbus.h b/src/modbus/modbus.h new file mode 100644 index 0000000..d96fc3d --- /dev/null +++ b/src/modbus/modbus.h @@ -0,0 +1,115 @@ +#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 14 // D5 +#define RS485_ESP01_DIR_PIN 0 + +#define RS485_BAUDRATE 19200 + +#define INVERTER_MODBUS_ADDR 4 + +#define MODBUS_RETRIES 2 + +typedef enum +{ + READ_FAIL = 0, + READ_OK = 1, +} response_type_t; + +typedef struct +{ + JsonObject *variant; + const modbus_register_t *registers; + uint8_t array_size; + uint8_t curr_register; +} modbus_register_info_t; + +class MODBUS +{ +public: + const uint8_t MAX_CONNECTION_ATTEMPTS = 10; + bool requestStaticData = true; + bool connection = false; + modbus_register_info_t live_info; + modbus_register_info_t static_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); + 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 + * @details sends the command over the specified serial connection + */ + String requestData(String command); + +private: + bool device_found = false; + unsigned long previousTime = 0; + unsigned long cmdDelayTime = 100; + + 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..3be4d62 --- /dev/null +++ b/src/modbus/modbus_registers.h @@ -0,0 +1,100 @@ +#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_INT16 = 0x02, /*!< Signed 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"}, + {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"}, + + {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"}, + + {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[] = { + + {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" + +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