mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 01:39:30 +02:00
Merge pull request #1378 from jonny5532/feature/modbus-inverter-ram-usage
Make Modbus inverters allocate their own register memory as required
This commit is contained in:
commit
73834cceeb
21 changed files with 307 additions and 237 deletions
|
@ -2,22 +2,9 @@
|
||||||
#include "../datalayer/datalayer.h"
|
#include "../datalayer/datalayer.h"
|
||||||
#include "../devboard/hal/hal.h"
|
#include "../devboard/hal/hal.h"
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "../lib/eModbus-eModbus/RTUutils.h"
|
|
||||||
#include "../lib/eModbus-eModbus/scripts/mbServerFCs.h"
|
|
||||||
|
|
||||||
// For modbus register definitions, see https://gitlab.com/pelle8/inverter_resources/-/blob/main/byd_registers_modbus_rtu.md
|
// For modbus register definitions, see https://gitlab.com/pelle8/inverter_resources/-/blob/main/byd_registers_modbus_rtu.md
|
||||||
|
|
||||||
#define HISTORY_LENGTH 3 // Amount of samples(minutes) that needs to match for register to be considered stale
|
|
||||||
static unsigned long previousMillis60s = 0; // will store last time a 60s event occured
|
|
||||||
static uint32_t user_configured_max_discharge_W = 0;
|
|
||||||
static uint32_t user_configured_max_charge_W = 0;
|
|
||||||
static uint32_t max_discharge_W = 0;
|
|
||||||
static uint32_t max_charge_W = 0;
|
|
||||||
static uint16_t register_401_history[HISTORY_LENGTH] = {0};
|
|
||||||
static uint8_t history_index = 0;
|
|
||||||
static uint8_t bms_char_dis_status = STANDBY;
|
|
||||||
static bool all_401_values_equal = false;
|
|
||||||
|
|
||||||
void BydModbusInverter::update_values() {
|
void BydModbusInverter::update_values() {
|
||||||
verify_temperature();
|
verify_temperature();
|
||||||
verify_inverter_modbus();
|
verify_inverter_modbus();
|
||||||
|
@ -41,14 +28,20 @@ void BydModbusInverter::handle_static_data() {
|
||||||
static uint16_t i = 100;
|
static uint16_t i = 100;
|
||||||
for (uint8_t arr_idx = 0; arr_idx < sizeof(data_array_pointers) / sizeof(uint16_t*); arr_idx++) {
|
for (uint8_t arr_idx = 0; arr_idx < sizeof(data_array_pointers) / sizeof(uint16_t*); arr_idx++) {
|
||||||
uint16_t data_size = data_sizes[arr_idx];
|
uint16_t data_size = data_sizes[arr_idx];
|
||||||
memcpy(&mbPV[i], data_array_pointers[arr_idx], data_size);
|
for (int j = 0; j < data_size / sizeof(uint16_t); j++) {
|
||||||
|
mbPV[i + j] = data_array_pointers[arr_idx][j];
|
||||||
|
}
|
||||||
i += data_size / sizeof(uint16_t);
|
i += data_size / sizeof(uint16_t);
|
||||||
}
|
}
|
||||||
static uint16_t init_p201[13] = {0, 0, 0, MAX_POWER, MAX_POWER, 0, 0, 53248, 10, 53248, 10, 0, 0};
|
static uint16_t init_p201[13] = {0, 0, 0, MAX_POWER, MAX_POWER, 0, 0, 53248, 10, 53248, 10, 0, 0};
|
||||||
memcpy(&mbPV[200], init_p201, sizeof(init_p201));
|
for (int i = 0; i < sizeof(init_p201) / sizeof(uint16_t); i++) {
|
||||||
|
mbPV[200 + i] = init_p201[i];
|
||||||
|
}
|
||||||
static uint16_t init_p301[24] = {0, 0, 128, 0, 0, 0, 0, 0, 0, 2000, 0, 2000,
|
static uint16_t init_p301[24] = {0, 0, 128, 0, 0, 0, 0, 0, 0, 2000, 0, 2000,
|
||||||
75, 95, 0, 0, 16, 22741, 0, 0, 13, 52064, 230, 9900};
|
75, 95, 0, 0, 16, 22741, 0, 0, 13, 52064, 230, 9900};
|
||||||
memcpy(&mbPV[300], init_p301, sizeof(init_p301));
|
for (int i = 0; i < sizeof(init_p301) / sizeof(uint16_t); i++) {
|
||||||
|
mbPV[300 + i] = init_p301[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BydModbusInverter::handle_update_data_modbusp201_byd() {
|
void BydModbusInverter::handle_update_data_modbusp201_byd() {
|
||||||
|
@ -150,10 +143,8 @@ bool BydModbusInverter::setup(void) { // Performs one time setup at startup ove
|
||||||
// Init Static data to the RTU Modbus
|
// Init Static data to the RTU Modbus
|
||||||
handle_static_data();
|
handle_static_data();
|
||||||
|
|
||||||
#if HAS_FREERTOS
|
|
||||||
// Init Serial2 connected to the RTU Modbus
|
// Init Serial2 connected to the RTU Modbus
|
||||||
RTUutils::prepareHardwareSerial(Serial2);
|
RTUutils::prepareHardwareSerial(Serial2);
|
||||||
#endif
|
|
||||||
|
|
||||||
auto rx_pin = esp32hal->RS485_RX_PIN();
|
auto rx_pin = esp32hal->RS485_RX_PIN();
|
||||||
auto tx_pin = esp32hal->RS485_TX_PIN();
|
auto tx_pin = esp32hal->RS485_TX_PIN();
|
||||||
|
@ -163,16 +154,9 @@ bool BydModbusInverter::setup(void) { // Performs one time setup at startup ove
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial2.begin(9600, SERIAL_8N1, rx_pin, tx_pin);
|
Serial2.begin(9600, SERIAL_8N1, rx_pin, tx_pin);
|
||||||
#if HAS_FREERTOS
|
|
||||||
// Register served function code worker for server
|
|
||||||
MBserver->registerWorker(MBTCP_ID, READ_HOLD_REGISTER, &FC03);
|
|
||||||
MBserver->registerWorker(MBTCP_ID, WRITE_HOLD_REGISTER, &FC06);
|
|
||||||
MBserver->registerWorker(MBTCP_ID, WRITE_MULT_REGISTERS, &FC16);
|
|
||||||
MBserver->registerWorker(MBTCP_ID, R_W_MULT_REGISTERS, &FC23);
|
|
||||||
|
|
||||||
// Start ModbusRTU background task
|
// Start ModbusRTU background task
|
||||||
((ModbusServerRTU*)MBserver)->begin(Serial2, esp32hal->MODBUS_CORE());
|
MBserver.begin(Serial2, esp32hal->MODBUS_CORE());
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,12 @@
|
||||||
#define SELECTED_INVERTER_CLASS BydModbusInverter
|
#define SELECTED_INVERTER_CLASS BydModbusInverter
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "../devboard/utils/types.h"
|
||||||
#include "ModbusInverterProtocol.h"
|
#include "ModbusInverterProtocol.h"
|
||||||
|
|
||||||
class BydModbusInverter : public ModbusInverterProtocol {
|
class BydModbusInverter : public ModbusInverterProtocol {
|
||||||
public:
|
public:
|
||||||
|
BydModbusInverter() : ModbusInverterProtocol(21) {}
|
||||||
const char* name() override { return Name; }
|
const char* name() override { return Name; }
|
||||||
bool setup() override;
|
bool setup() override;
|
||||||
void update_values();
|
void update_values();
|
||||||
|
@ -23,6 +25,18 @@ class BydModbusInverter : public ModbusInverterProtocol {
|
||||||
|
|
||||||
//BYD Modbus specific value
|
//BYD Modbus specific value
|
||||||
const uint16_t MAX_POWER = 40960;
|
const uint16_t MAX_POWER = 40960;
|
||||||
|
|
||||||
|
static const int HISTORY_LENGTH =
|
||||||
|
3; // Amount of samples(minutes) that needs to match for register to be considered stale
|
||||||
|
unsigned long previousMillis60s = 0; // will store last time a 60s event occured
|
||||||
|
uint32_t user_configured_max_discharge_W = 0;
|
||||||
|
uint32_t user_configured_max_charge_W = 0;
|
||||||
|
uint32_t max_discharge_W = 0;
|
||||||
|
uint32_t max_charge_W = 0;
|
||||||
|
uint16_t register_401_history[HISTORY_LENGTH] = {0};
|
||||||
|
uint8_t history_index = 0;
|
||||||
|
uint8_t bms_char_dis_status = STANDBY;
|
||||||
|
bool all_401_values_equal = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,10 +1,181 @@
|
||||||
#include "ModbusInverterProtocol.h"
|
#include "ModbusInverterProtocol.h"
|
||||||
|
#include "../devboard/utils/logging.h"
|
||||||
#include "../lib/eModbus-eModbus/ModbusServerRTU.h"
|
#include "../lib/eModbus-eModbus/ModbusServerRTU.h"
|
||||||
|
|
||||||
static const int MB_RTU_NUM_VALUES = 13100;
|
// Creates a ModbusRTU server instance with 2000ms timeout
|
||||||
uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory
|
ModbusInverterProtocol::ModbusInverterProtocol(int serverId) : MBserver(2000) {
|
||||||
|
_serverId = serverId;
|
||||||
|
|
||||||
ModbusInverterProtocol::ModbusInverterProtocol() {
|
MBserver.registerWorker(_serverId, READ_HOLD_REGISTER,
|
||||||
mbPV = ::mbPV;
|
[this](ModbusMessage request) -> ModbusMessage { return FC03(request); });
|
||||||
MBserver = new ModbusServerRTU(2000);
|
MBserver.registerWorker(_serverId, WRITE_HOLD_REGISTER,
|
||||||
|
[this](ModbusMessage request) -> ModbusMessage { return FC06(request); });
|
||||||
|
MBserver.registerWorker(_serverId, WRITE_MULT_REGISTERS,
|
||||||
|
[this](ModbusMessage request) -> ModbusMessage { return FC16(request); });
|
||||||
|
MBserver.registerWorker(_serverId, R_W_MULT_REGISTERS,
|
||||||
|
[this](ModbusMessage request) -> ModbusMessage { return FC23(request); });
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusInverterProtocol::~ModbusInverterProtocol() {
|
||||||
|
MBserver.unregisterWorker(_serverId, READ_HOLD_REGISTER);
|
||||||
|
MBserver.unregisterWorker(_serverId, WRITE_HOLD_REGISTER);
|
||||||
|
MBserver.unregisterWorker(_serverId, WRITE_MULT_REGISTERS);
|
||||||
|
MBserver.unregisterWorker(_serverId, R_W_MULT_REGISTERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server function to handle FC 0x03
|
||||||
|
ModbusMessage ModbusInverterProtocol::FC03(ModbusMessage request) {
|
||||||
|
ModbusMessage response; // The Modbus message we are going to give back
|
||||||
|
uint16_t addr = 0; // Start address
|
||||||
|
uint16_t words = 0; // # of words requested
|
||||||
|
request.get(2, addr); // read address from request
|
||||||
|
request.get(4, words); // read # of words from request
|
||||||
|
|
||||||
|
// Address overflow?
|
||||||
|
if ((addr + words) > MBPV_MAX) {
|
||||||
|
// Yes - send respective error response
|
||||||
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||||
|
#ifdef DEBUG_LOG
|
||||||
|
logging.printf("Modbus FC03 error: illegal request addr=%d words=%d\n", addr, words);
|
||||||
|
#endif
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up response
|
||||||
|
response.add(request.getServerID(), request.getFunctionCode(), (uint8_t)(words * 2));
|
||||||
|
for (uint8_t i = 0; i < words; ++i) {
|
||||||
|
// send increasing data values
|
||||||
|
response.add((uint16_t)(mbPV[addr + i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// #ifdef DEBUG_LOG
|
||||||
|
// logging.printf("Modbus FC03 response: %d %d\n", addr, mbPV[addr]);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server function to handle FC 0x06
|
||||||
|
ModbusMessage ModbusInverterProtocol::FC06(ModbusMessage request) {
|
||||||
|
ModbusMessage response; // The Modbus message we are going to give back
|
||||||
|
uint16_t addr = 0; // Start address
|
||||||
|
uint16_t val = 0; // value to write
|
||||||
|
request.get(2, addr); // read address from request
|
||||||
|
request.get(4, val); // read # of words from request
|
||||||
|
|
||||||
|
// Address overflow?
|
||||||
|
if ((addr) > MBPV_MAX) {
|
||||||
|
// Yes - send respective error response
|
||||||
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||||
|
#ifdef DEBUG_LOG
|
||||||
|
logging.printf("Modbus FC06 error: illegal request addr=%d val=%d\n", addr, val);
|
||||||
|
#endif
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the write
|
||||||
|
mbPV[addr] = val;
|
||||||
|
|
||||||
|
// Set up response
|
||||||
|
response.add(request.getServerID(), request.getFunctionCode(), mbPV[addr]);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server function to handle FC 0x10 (FC16)
|
||||||
|
ModbusMessage ModbusInverterProtocol::FC16(ModbusMessage request) {
|
||||||
|
ModbusMessage response; // The Modbus message we are going to give back
|
||||||
|
uint16_t addr = 0; // Start address
|
||||||
|
uint16_t words = 0; // total words to write
|
||||||
|
uint8_t bytes = 0; // # of data bytes in request
|
||||||
|
uint16_t val = 0; // value to be written
|
||||||
|
request.get(2, addr); // read address from request
|
||||||
|
request.get(4, words); // read # of words from request
|
||||||
|
request.get(6, bytes); // read # of data bytes from request (seems redundant with # of words)
|
||||||
|
|
||||||
|
// # of registers proper?
|
||||||
|
if ((bytes != (words * 2)) // byte count in request must match # of words in request
|
||||||
|
|| (words > 123)) // can't support more than this in request packet
|
||||||
|
{ // Yes - send respective error response
|
||||||
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_VALUE);
|
||||||
|
#ifdef DEBUG_LOG
|
||||||
|
logging.printf("Modbus FC16 error: bad registers addr=%d words=%d bytes=%d\n", addr, words, bytes);
|
||||||
|
#endif
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
// Address overflow?
|
||||||
|
if ((addr + words) > MBPV_MAX) {
|
||||||
|
// Yes - send respective error response
|
||||||
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||||
|
#ifdef DEBUG_LOG
|
||||||
|
logging.printf("Modbus FC16 error: overflow addr=%d words=%d\n", addr, words);
|
||||||
|
#endif
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the writes
|
||||||
|
for (uint8_t i = 0; i < words; ++i) {
|
||||||
|
request.get(7 + (i * 2), val); //data starts at byte 6 in request packet
|
||||||
|
mbPV[addr + i] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up response
|
||||||
|
response.add(request.getServerID(), request.getFunctionCode(), addr, words);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server function to handle FC 0x17 (FC23)
|
||||||
|
ModbusMessage ModbusInverterProtocol::FC23(ModbusMessage request) {
|
||||||
|
ModbusMessage response; // The Modbus message we are going to give back
|
||||||
|
uint16_t read_addr = 0; // Start address for read
|
||||||
|
uint16_t read_words = 0; // # of words requested for read
|
||||||
|
uint16_t write_addr = 0; // Start address for write
|
||||||
|
uint16_t write_words = 0; // total words to write
|
||||||
|
uint8_t write_bytes = 0; // # of data bytes in write request
|
||||||
|
uint16_t write_val = 0; // value to be written
|
||||||
|
request.get(2, read_addr); // read address from request
|
||||||
|
request.get(4, read_words); // read # of words from request
|
||||||
|
request.get(6, write_addr); // read address from request
|
||||||
|
request.get(8, write_words); // read # of words from request
|
||||||
|
request.get(10, write_bytes); // read # of data bytes from request (seems redundant with # of words)
|
||||||
|
|
||||||
|
// ERROR CHECKS
|
||||||
|
// # of registers proper?
|
||||||
|
if ((write_bytes != (write_words * 2)) // byte count in request must match # of words in request
|
||||||
|
|| (write_words > 121) // can't fit more than this in the packet for FC23
|
||||||
|
|| (read_words > 125)) // can't fit more than this in the response packet
|
||||||
|
{ // Yes - send respective error response
|
||||||
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_VALUE);
|
||||||
|
#ifdef DEBUG_LOG
|
||||||
|
logging.printf("Modbus FC23 error: bad registers write_addr=%d write_words=%d write_bytes=%d read_words=%d\n",
|
||||||
|
write_addr, write_words, write_bytes, read_words);
|
||||||
|
#endif
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
// Address overflow?
|
||||||
|
if (((write_addr + write_words) > MBPV_MAX) ||
|
||||||
|
((read_addr + read_words) > MBPV_MAX)) { // Yes - send respective error response
|
||||||
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||||
|
#ifdef DEBUG_LOG
|
||||||
|
logging.printf("Modbus FC23 error: overflow write_addr=%d write_words=%d read_addr=%d read_words=%d\n", write_addr,
|
||||||
|
write_words, read_addr, read_words);
|
||||||
|
#endif
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
//WRITE SECTION - write is done before read for FC23
|
||||||
|
// Do the writes
|
||||||
|
for (uint8_t i = 0; i < write_words; ++i) {
|
||||||
|
request.get(11 + (i * 2), write_val); //data starts at byte 6 in request packet
|
||||||
|
mbPV[write_addr + i] = write_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// READ SECTION
|
||||||
|
// Set up response
|
||||||
|
response.add(request.getServerID(), request.getFunctionCode(), (uint8_t)(read_words * 2));
|
||||||
|
for (uint8_t i = 0; i < read_words; ++i) {
|
||||||
|
// send increasing data values
|
||||||
|
response.add((uint16_t)(mbPV[read_addr + i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
#ifndef MODBUS_INVERTER_PROTOCOL_H
|
#ifndef MODBUS_INVERTER_PROTOCOL_H
|
||||||
#define MODBUS_INVERTER_PROTOCOL_H
|
#define MODBUS_INVERTER_PROTOCOL_H
|
||||||
|
|
||||||
#include <HardwareSerial.h>
|
#include "../lib/eModbus-eModbus/ModbusMessage.h"
|
||||||
#include <stdint.h>
|
#include "../lib/eModbus-eModbus/ModbusServerRTU.h"
|
||||||
#include "../lib/eModbus-eModbus/ModbusServer.h"
|
|
||||||
#include "InverterProtocol.h"
|
#include "InverterProtocol.h"
|
||||||
|
|
||||||
extern uint16_t mbPV[];
|
#include <HardwareSerial.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
// The abstract base class for all Modbus inverter protocols
|
// The abstract base class for all Modbus inverter protocols
|
||||||
class ModbusInverterProtocol : public InverterProtocol {
|
class ModbusInverterProtocol : public InverterProtocol {
|
||||||
|
@ -14,15 +16,22 @@ class ModbusInverterProtocol : public InverterProtocol {
|
||||||
InverterInterfaceType interface_type() { return InverterInterfaceType::Modbus; }
|
InverterInterfaceType interface_type() { return InverterInterfaceType::Modbus; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Create a ModbusRTU server instance with 2000ms timeout
|
ModbusInverterProtocol(int serverId);
|
||||||
ModbusInverterProtocol();
|
~ModbusInverterProtocol();
|
||||||
|
|
||||||
static const int MB_RTU_NUM_VALUES = 13100;
|
ModbusMessage FC03(ModbusMessage request);
|
||||||
|
ModbusMessage FC06(ModbusMessage request);
|
||||||
|
ModbusMessage FC16(ModbusMessage request);
|
||||||
|
ModbusMessage FC23(ModbusMessage request);
|
||||||
|
|
||||||
// Modbus register file
|
// The highest Modbus register we allow reads/writes from
|
||||||
uint16_t* mbPV;
|
static const int MBPV_MAX = 30000;
|
||||||
|
// The Modbus server ID we respond to
|
||||||
|
int _serverId;
|
||||||
|
// The Modbus registers themselves
|
||||||
|
std::map<uint16_t, uint16_t> mbPV;
|
||||||
|
|
||||||
ModbusServer* MBserver;
|
ModbusServerRTU MBserver;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -440,7 +440,7 @@ ModbusMessage RTUutils::receive(uint8_t caller, Stream& serial, uint32_t timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lower 7 bit ASCII characters - all invalid are set to 0xFF
|
// Lower 7 bit ASCII characters - all invalid are set to 0xFF
|
||||||
const char RTUutils::ASCIIread[] = {
|
const uint8_t RTUutils::ASCIIread[] = {
|
||||||
/* 00-07 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
/* 00-07 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
/* 08-0F */ 0xFF, 0xFF, 0xF2, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, // LF + CR
|
/* 08-0F */ 0xFF, 0xFF, 0xF2, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, // LF + CR
|
||||||
/* 10-17 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
/* 10-17 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
|
|
@ -60,7 +60,7 @@ static void prepareHardwareSerial(HardwareSerial& s, uint16_t bufferSize = 260)
|
||||||
protected:
|
protected:
|
||||||
// Printable characters for ASCII protocol: 012345678ABCDEF
|
// Printable characters for ASCII protocol: 012345678ABCDEF
|
||||||
static const char ASCIIwrite[];
|
static const char ASCIIwrite[];
|
||||||
static const char ASCIIread[];
|
static const uint8_t ASCIIread[];
|
||||||
|
|
||||||
RTUutils() = delete;
|
RTUutils() = delete;
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
The scripts in this folder originate from [eModbus discussion 147](https://github.com/eModbus/eModbus/discussions/147).
|
|
|
@ -1,173 +0,0 @@
|
||||||
#include "mbServerFCs.h"
|
|
||||||
#include "../Logging.h"
|
|
||||||
|
|
||||||
//modbus register memory - declared in main.cpp
|
|
||||||
|
|
||||||
extern uint16_t mbPV[MBPV_MAX];
|
|
||||||
|
|
||||||
// Server function to handle FC 0x03
|
|
||||||
ModbusMessage FC03(ModbusMessage request) {
|
|
||||||
//Serial.println(request);
|
|
||||||
ModbusMessage response; // The Modbus message we are going to give back
|
|
||||||
uint16_t addr = 0; // Start address
|
|
||||||
uint16_t words = 0; // # of words requested
|
|
||||||
request.get(2, addr); // read address from request
|
|
||||||
request.get(4, words); // read # of words from request
|
|
||||||
char debugString[1000];
|
|
||||||
|
|
||||||
LOG_D("FC03 received: read %d words @ %d\r\n", words, addr);
|
|
||||||
|
|
||||||
// Address overflow?
|
|
||||||
if ((addr + words) > MBPV_MAX) {
|
|
||||||
// Yes - send respective error response
|
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
|
||||||
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up response
|
|
||||||
response.add(request.getServerID(), request.getFunctionCode(), (uint8_t)(words * 2));
|
|
||||||
sprintf(debugString, "Read : ");
|
|
||||||
for (uint8_t i = 0; i < words; ++i) {
|
|
||||||
// send increasing data values
|
|
||||||
response.add((uint16_t)(mbPV[addr + i]));
|
|
||||||
sprintf(debugString + strlen(debugString), "%i ", mbPV[addr + i]);
|
|
||||||
}
|
|
||||||
LOG_V("%s\r\n", debugString);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server function to handle FC 0x06
|
|
||||||
ModbusMessage FC06(ModbusMessage request) {
|
|
||||||
//Serial.println(request);
|
|
||||||
ModbusMessage response; // The Modbus message we are going to give back
|
|
||||||
uint16_t addr = 0; // Start address
|
|
||||||
uint16_t val = 0; // value to write
|
|
||||||
request.get(2, addr); // read address from request
|
|
||||||
request.get(4, val); // read # of words from request
|
|
||||||
|
|
||||||
LOG_D("FC06 received: write 1 word @ %d\r\n", addr);
|
|
||||||
|
|
||||||
// Address overflow?
|
|
||||||
if ((addr) > MBPV_MAX) {
|
|
||||||
// Yes - send respective error response
|
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
|
||||||
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the write
|
|
||||||
mbPV[addr] = val;
|
|
||||||
LOG_V("Write : %i", val);
|
|
||||||
|
|
||||||
// Set up response
|
|
||||||
response.add(request.getServerID(), request.getFunctionCode(), mbPV[addr]);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server function to handle FC 0x10 (FC16)
|
|
||||||
ModbusMessage FC16(ModbusMessage request) {
|
|
||||||
//Serial.println(request);
|
|
||||||
ModbusMessage response; // The Modbus message we are going to give back
|
|
||||||
uint16_t addr = 0; // Start address
|
|
||||||
uint16_t words = 0; // total words to write
|
|
||||||
uint8_t bytes = 0; // # of data bytes in request
|
|
||||||
uint16_t val = 0; // value to be written
|
|
||||||
request.get(2, addr); // read address from request
|
|
||||||
request.get(4, words); // read # of words from request
|
|
||||||
request.get(6, bytes); // read # of data bytes from request (seems redundant with # of words)
|
|
||||||
char debugString[1000];
|
|
||||||
|
|
||||||
LOG_D("FC16 received: write %d words @ %d\r\n", words, addr);
|
|
||||||
|
|
||||||
// # of registers proper?
|
|
||||||
if ((bytes != (words * 2)) // byte count in request must match # of words in request
|
|
||||||
|| (words > 123)) // can't support more than this in request packet
|
|
||||||
{ // Yes - send respective error response
|
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_VALUE);
|
|
||||||
LOG_D("ERROR - ILLEGAL DATA VALUE\r\n");
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
// Address overflow?
|
|
||||||
if ((addr + words) > MBPV_MAX) {
|
|
||||||
// Yes - send respective error response
|
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
|
||||||
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the writes
|
|
||||||
sprintf(debugString, "Write : ");
|
|
||||||
for (uint8_t i = 0; i < words; ++i) {
|
|
||||||
request.get(7 + (i * 2), val); //data starts at byte 6 in request packet
|
|
||||||
mbPV[addr + i] = val;
|
|
||||||
sprintf(debugString + strlen(debugString), "%i ", mbPV[addr + i]);
|
|
||||||
}
|
|
||||||
LOG_V("%s\r\n", debugString);
|
|
||||||
|
|
||||||
// Set up response
|
|
||||||
response.add(request.getServerID(), request.getFunctionCode(), addr, words);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server function to handle FC 0x17 (FC23)
|
|
||||||
ModbusMessage FC23(ModbusMessage request) {
|
|
||||||
//Serial.println(request);
|
|
||||||
ModbusMessage response; // The Modbus message we are going to give back
|
|
||||||
uint16_t read_addr = 0; // Start address for read
|
|
||||||
uint16_t read_words = 0; // # of words requested for read
|
|
||||||
uint16_t write_addr = 0; // Start address for write
|
|
||||||
uint16_t write_words = 0; // total words to write
|
|
||||||
uint8_t write_bytes = 0; // # of data bytes in write request
|
|
||||||
uint16_t write_val = 0; // value to be written
|
|
||||||
request.get(2, read_addr); // read address from request
|
|
||||||
request.get(4, read_words); // read # of words from request
|
|
||||||
request.get(6, write_addr); // read address from request
|
|
||||||
request.get(8, write_words); // read # of words from request
|
|
||||||
request.get(10, write_bytes); // read # of data bytes from request (seems redundant with # of words)
|
|
||||||
char debugString[1000];
|
|
||||||
|
|
||||||
LOG_D("FC23 received: write %d @ %d, read %d @ %d\r\n", write_words, write_addr, read_words, read_addr);
|
|
||||||
|
|
||||||
// ERROR CHECKS
|
|
||||||
// # of registers proper?
|
|
||||||
if ((write_bytes != (write_words * 2)) // byte count in request must match # of words in request
|
|
||||||
|| (write_words > 121) // can't fit more than this in the packet for FC23
|
|
||||||
|| (read_words > 125)) // can't fit more than this in the response packet
|
|
||||||
{ // Yes - send respective error response
|
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_VALUE);
|
|
||||||
LOG_D("ERROR - ILLEGAL DATA VALUE\r\n");
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
// Address overflow?
|
|
||||||
if (((write_addr + write_words) > MBPV_MAX) ||
|
|
||||||
((read_addr + read_words) > MBPV_MAX)) { // Yes - send respective error response
|
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
|
||||||
LOG_D("ERROR - ILLEGAL DATA ADDRESS\r\n");
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
//WRITE SECTION - write is done before read for FC23
|
|
||||||
// Do the writes
|
|
||||||
sprintf(debugString, "Write: ");
|
|
||||||
for (uint8_t i = 0; i < write_words; ++i) {
|
|
||||||
request.get(11 + (i * 2), write_val); //data starts at byte 6 in request packet
|
|
||||||
mbPV[write_addr + i] = write_val;
|
|
||||||
sprintf(debugString + strlen(debugString), "%i ", mbPV[write_addr + i]);
|
|
||||||
}
|
|
||||||
LOG_V("%s\r\n", debugString);
|
|
||||||
|
|
||||||
// READ SECTION
|
|
||||||
// Set up response
|
|
||||||
sprintf(debugString, "Read: ");
|
|
||||||
response.add(request.getServerID(), request.getFunctionCode(), (uint8_t)(read_words * 2));
|
|
||||||
for (uint8_t i = 0; i < read_words; ++i) {
|
|
||||||
// send increasing data values
|
|
||||||
response.add((uint16_t)(mbPV[read_addr + i]));
|
|
||||||
sprintf(debugString + strlen(debugString), "%i ", mbPV[read_addr + i]);
|
|
||||||
}
|
|
||||||
LOG_V("%s\r\n", debugString);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
#include "../ModbusServerRTU.h"
|
|
||||||
|
|
||||||
#define MBTCP_ID 21 // modbus TCP server ID
|
|
||||||
#define MBPV_MAX 30000
|
|
||||||
|
|
||||||
ModbusMessage FC03(ModbusMessage request);
|
|
||||||
ModbusMessage FC06(ModbusMessage request);
|
|
||||||
ModbusMessage FC16(ModbusMessage request);
|
|
||||||
ModbusMessage FC23(ModbusMessage request);
|
|
|
@ -62,7 +62,7 @@ include_directories("${source_dir}/googletest/include"
|
||||||
include_directories(emul)
|
include_directories(emul)
|
||||||
|
|
||||||
# For eModBus
|
# For eModBus
|
||||||
add_compile_definitions(__linux__ HW_LILYGO NISSAN_LEAF_BATTERY)
|
add_compile_definitions(ESP32 HW_LILYGO NISSAN_LEAF_BATTERY)
|
||||||
|
|
||||||
# add the executable
|
# add the executable
|
||||||
add_executable(tests
|
add_executable(tests
|
||||||
|
@ -74,9 +74,12 @@ add_executable(tests
|
||||||
../Software/src/devboard/utils/events.cpp
|
../Software/src/devboard/utils/events.cpp
|
||||||
../Software/src/datalayer/datalayer.cpp
|
../Software/src/datalayer/datalayer.cpp
|
||||||
../Software/src/datalayer/datalayer_extended.cpp
|
../Software/src/datalayer/datalayer_extended.cpp
|
||||||
../Software/src/lib/eModbus-eModbus/ModbusMessage.cpp
|
|
||||||
../Software/src/lib/eModbus-eModbus/ModbusTypeDefs.cpp
|
|
||||||
../Software/src/lib/eModbus-eModbus/Logging.cpp
|
../Software/src/lib/eModbus-eModbus/Logging.cpp
|
||||||
|
../Software/src/lib/eModbus-eModbus/ModbusMessage.cpp
|
||||||
|
../Software/src/lib/eModbus-eModbus/ModbusServer.cpp
|
||||||
|
../Software/src/lib/eModbus-eModbus/ModbusServerRTU.cpp
|
||||||
|
../Software/src/lib/eModbus-eModbus/ModbusTypeDefs.cpp
|
||||||
|
../Software/src/lib/eModbus-eModbus/RTUutils.cpp
|
||||||
# ../Software/src/lib/eModbus-eModbus/scripts/mbServerFCs.cpp
|
# ../Software/src/lib/eModbus-eModbus/scripts/mbServerFCs.cpp
|
||||||
../Software/USER_SETTINGS.cpp
|
../Software/USER_SETTINGS.cpp
|
||||||
../Software/src/battery/BATTERIES.cpp
|
../Software/src/battery/BATTERIES.cpp
|
||||||
|
@ -90,6 +93,7 @@ add_executable(tests
|
||||||
emul/time.cpp
|
emul/time.cpp
|
||||||
emul/serial.cpp
|
emul/serial.cpp
|
||||||
emul/Arduino.cpp
|
emul/Arduino.cpp
|
||||||
|
emul/freertos/FreeRTOS.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(tests
|
target_link_libraries(tests
|
||||||
|
|
|
@ -1,5 +1,16 @@
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
void delay(unsigned long ms) {}
|
||||||
|
void delayMicroseconds(unsigned long us) {}
|
||||||
|
int digitalRead(uint8_t pin) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void digitalWrite(uint8_t pin, uint8_t val) {}
|
||||||
|
unsigned long micros() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void pinMode(uint8_t pin, uint8_t mode) {}
|
||||||
|
|
||||||
int max(int a, int b) {
|
int max(int a, int b) {
|
||||||
return (a > b) ? a : b;
|
return (a > b) ? a : b;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
#ifndef ARDUINO_H
|
#ifndef ARDUINO_H
|
||||||
#define ARDUINO_H
|
#define ARDUINO_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "HardwareSerial.h"
|
||||||
|
#include "Print.h"
|
||||||
|
|
||||||
#include "esp-hal-gpio.h"
|
#include "esp-hal-gpio.h"
|
||||||
|
|
||||||
|
@ -9,10 +15,13 @@ void pinMode(uint8_t pin, uint8_t mode);
|
||||||
void digitalWrite(uint8_t pin, uint8_t val);
|
void digitalWrite(uint8_t pin, uint8_t val);
|
||||||
int digitalRead(uint8_t pin);
|
int digitalRead(uint8_t pin);
|
||||||
|
|
||||||
|
unsigned long micros();
|
||||||
// Can be previously declared as a macro in stupid eModbus
|
// Can be previously declared as a macro in stupid eModbus
|
||||||
#undef millis
|
#undef millis
|
||||||
unsigned long millis();
|
unsigned long millis();
|
||||||
|
|
||||||
|
void delay(unsigned long ms);
|
||||||
|
void delayMicroseconds(unsigned long us);
|
||||||
int max(int a, int b);
|
int max(int a, int b);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#define HARDWARESERIAL_H
|
#define HARDWARESERIAL_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include "Print.h"
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
|
|
||||||
enum SerialConfig {
|
enum SerialConfig {
|
||||||
|
@ -33,11 +35,18 @@ enum SerialConfig {
|
||||||
|
|
||||||
class HardwareSerial : public Stream {
|
class HardwareSerial : public Stream {
|
||||||
public:
|
public:
|
||||||
|
int available() { return 0; }
|
||||||
|
uint32_t baudRate() { return 9600; }
|
||||||
void begin(unsigned long baud, uint32_t config = SERIAL_8N1, int8_t rxPin = -1, int8_t txPin = -1,
|
void begin(unsigned long baud, uint32_t config = SERIAL_8N1, int8_t rxPin = -1, int8_t txPin = -1,
|
||||||
bool invert = false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 120) {}
|
bool invert = false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 120) {}
|
||||||
|
int read() { return 0; }
|
||||||
|
void setTxBufferSize(uint16_t size) {}
|
||||||
|
void setRxBufferSize(uint16_t size) {}
|
||||||
|
bool setRxFIFOFull(uint8_t fifoBytes) { return false; }
|
||||||
|
size_t write(uint8_t) { return 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
extern HardwareSerial Serial0;
|
extern HardwareSerial Serial;
|
||||||
extern HardwareSerial Serial1;
|
extern HardwareSerial Serial1;
|
||||||
extern HardwareSerial Serial2;
|
extern HardwareSerial Serial2;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
#ifndef PRINT_H
|
#ifndef PRINT_H
|
||||||
#define PRINT_H
|
#define PRINT_H
|
||||||
|
|
||||||
class Print {};
|
class Print {
|
||||||
|
public:
|
||||||
|
virtual void flush() {}
|
||||||
|
void printf(const char* format, ...) {}
|
||||||
|
virtual size_t write(uint8_t) = 0;
|
||||||
|
virtual size_t write(const char* s) { return 0; }
|
||||||
|
virtual size_t write(const uint8_t* buffer, size_t size) { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
#ifndef STREAM_H
|
#ifndef STREAM_H
|
||||||
#define STREAM_H
|
#define STREAM_H
|
||||||
|
|
||||||
class Stream {};
|
#include "Print.h"
|
||||||
|
|
||||||
|
class Stream : public Print {
|
||||||
|
public:
|
||||||
|
virtual int available() = 0;
|
||||||
|
virtual int read() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
10
test/emul/freertos/FreeRTOS.cpp
Normal file
10
test/emul/freertos/FreeRTOS.cpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pxTaskCode, const char* const pcName, const uint32_t ulStackDepth,
|
||||||
|
void* const pvParameters, UBaseType_t uxPriority, TaskHandle_t* const pxCreatedTask,
|
||||||
|
const BaseType_t xCoreID) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void vTaskDelete(TaskHandle_t xTaskToDelete) {}
|
||||||
|
}
|
21
test/emul/freertos/FreeRTOS.h
Normal file
21
test/emul/freertos/FreeRTOS.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef _FREERTOS_H_
|
||||||
|
#define _FREERTOS_H_
|
||||||
|
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef int BaseType_t;
|
||||||
|
typedef unsigned int UBaseType_t;
|
||||||
|
|
||||||
|
const BaseType_t tskNO_AFFINITY = -1;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pxTaskCode, const char* const pcName, const uint32_t ulStackDepth,
|
||||||
|
void* const pvParameters, UBaseType_t uxPriority, TaskHandle_t* const pxCreatedTask,
|
||||||
|
const BaseType_t xCoreID);
|
||||||
|
|
||||||
|
void vTaskDelete(TaskHandle_t xTaskToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
2
test/emul/freertos/task.h
Normal file
2
test/emul/freertos/task.h
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
typedef void* TaskHandle_t;
|
||||||
|
typedef void (*TaskFunction_t)(void*);
|
|
@ -1,5 +1,5 @@
|
||||||
#include "HardwareSerial.h"
|
#include "HardwareSerial.h"
|
||||||
|
|
||||||
HardwareSerial Serial0;
|
HardwareSerial Serial;
|
||||||
HardwareSerial Serial1;
|
HardwareSerial Serial1;
|
||||||
HardwareSerial Serial2;
|
HardwareSerial Serial2;
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#include "../Software/src/datalayer/datalayer.h"
|
#include "../Software/src/datalayer/datalayer.h"
|
||||||
#include "../Software/src/devboard/safety/safety.h"
|
#include "../Software/src/devboard/safety/safety.h"
|
||||||
#include "../Software/src/devboard/utils/events.h"
|
#include "../Software/src/devboard/utils/events.h"
|
||||||
#include "../Software/src/inverter/ModbusInverterProtocol.h"
|
|
||||||
|
|
||||||
TEST(SafetyTests, ShouldSetEventWhenTemperatureTooHigh) {
|
TEST(SafetyTests, ShouldSetEventWhenTemperatureTooHigh) {
|
||||||
init_events();
|
init_events();
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include "../Software/src/datalayer/datalayer.h"
|
#include "../Software/src/datalayer/datalayer.h"
|
||||||
#include "../Software/src/devboard/safety/safety.h"
|
#include "../Software/src/devboard/safety/safety.h"
|
||||||
#include "../Software/src/devboard/utils/events.h"
|
#include "../Software/src/devboard/utils/events.h"
|
||||||
#include "../Software/src/inverter/ModbusInverterProtocol.h"
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
testing::InitGoogleTest(&argc, argv);
|
testing::InitGoogleTest(&argc, argv);
|
||||||
|
@ -12,5 +11,3 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void store_settings_equipment_stop(void) {}
|
void store_settings_equipment_stop(void) {}
|
||||||
|
|
||||||
ModbusInverterProtocol::ModbusInverterProtocol() {}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue