Hello
I have some PylonTech US2000 Plus batteries connected to a Sofar ME3000SP inverter which are communicating with each other using an RS485 ModBus. I wish to "tap" in to the line to gather certain data. I've got a MAX485 module setup and working by tapping in to the A&B lines on the cable using the following code :-
#include <SPI.h>
#include <MsTimer2.h>
#define MESSAGE_GAP_TIMEOUT_IN_MS 5
#define LED_TOGGLE_MSGS 50
int MESSAGE_COUNT = 0;
char RecvBuffer[256];
unsigned int recvIndex = 0;
unsigned long msgIndex = 0;
unsigned long receiveTime = 0;
bool LED_STATUS = true;
char receiveTimeString[32];
char msgIndexString[32];
void setup()
{
Serial.begin(9600); // PC USB output
Serial1.begin(115200); // RS485 module
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LED_STATUS);
// set an end-of-message detection timeout of 5ms
MsTimer2::set(MESSAGE_GAP_TIMEOUT_IN_MS, onTimer);
Serial.println("Setup complete");
}
bool dumpData = false;
bool timerStarted = false;
// If this timer expires, this means no additional character was received for a while: notify main loop
void onTimer() {
dumpData = true;
}
void loop()
{
char received;
if (Serial1.available() > 0) {
received = Serial1.read();
RecvBuffer[recvIndex++] = received;
}
// first time we get an empty buffer after receiving stuff:
// this could be the end of the message, (re)start the end-of-message detection timer.
if (recvIndex>0) {
if (!timerStarted){
MsTimer2::start();
timerStarted = true;
}
}
// If the timer expired and positioned this var, we should now dump the received message
// into a UDP packet to the remote host/logger.
if (dumpData) {
LED_STATUS = !LED_STATUS;
digitalWrite(LED_BUILTIN, LED_STATUS);
//Serial.print("Message received ");
//Serial.println(String(MESSAGE_COUNT));
Serial.print(RecvBuffer[0]);
Serial.println((char *)RecvBuffer);
MESSAGE_COUNT++;
receiveTime = micros() - MESSAGE_GAP_TIMEOUT_IN_MS*1000;
// reinitialize vars for next detection/dump
dumpData = false;
MsTimer2::stop();
timerStarted = false;
msgIndex++ ;
if (msgIndex > 999) msgIndex = 0;
// build and send UDP message
//Udp.beginPacket(remote_ip, 8888);
//sprintf(receiveTimeString, "%015lu:", receiveTime);
//Udp.write(receiveTimeString, strlen(receiveTimeString));
//sprintf(msgIndexString, "%04lu:", msgIndex);
//Udp.write(msgIndexString, strlen(msgIndexString));
//Udp.write(RecvBuffer, recvIndex);
//Udp.endPacket();
// reset index for next message
recvIndex = 0;
}
}
This is being done on a Arduino MEGA.
I've attached 2 "serial" files showing the output I'm getting from tapping in to the wire.
I suspect that the data I'm seeing is just coming from the battery to the invertor, but the data I require is only available from the inverter.
From the code below you can see what data I'm trying to get out of the inverter :-
#include <SoftwareSerial.h>
#include <ModbusMaster.h>
// rs485 enable
#define MAX485_ENABLE 2
// instantiate ModbusMaster object
ModbusMaster node;
void preTransmission()
{
digitalWrite(MAX485_ENABLE, 1);
}
void postTransmission()
{
digitalWrite(MAX485_ENABLE, 0);
}
void setup() {
// pin mode for enable
pinMode(MAX485_ENABLE, OUTPUT);
// Init in receive mode
postTransmission();
// start USB serial
Serial.begin(115200);
// Modbus communication runs at 115200 baud
// set the data rate for the SoftwareSerial port
Serial1.begin(115200);
// Modbus slave ID 1
node.begin(128, Serial1);
// Callbacks allow us to configure the RS485 transceiver correctly
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
}
bool state = true;
void loop() {
Serial.print("Loop ... ");
// put your main code here, to run repeatedly:
uint8_t result;
uint16_t data[16];
// Toggle the coil at address 0x0002 (Manual Load Control)
//result = node.writeSingleCoil(0x02, state);
//state = !state;
// Read registers
result = node.readInputRegisters(0x0200, 16);
Serial.println(String(result));
Serial.println(String(node.ku8MBSuccess));
if (true) // result == node.ku8MBSuccess
{
Serial.print("Charge/discharge power: ");
Serial.println(node.getResponseBuffer(0x0D));
Serial.print("Battery capacity: ");
Serial.println(node.getResponseBuffer(0x10));
Serial.print("Feed in/out power: ");
Serial.println(node.getResponseBuffer(0x12));
Serial.print("Load power: ");
Serial.println(node.getResponseBuffer(0x13));
Serial.print("Input/output power: ");
Serial.println(node.getResponseBuffer(0x14));
Serial.print("Generation power: ");
Serial.println(node.getResponseBuffer(0x15));
Serial.print("Generation today: ");
Serial.println(node.getResponseBuffer(0x18));
Serial.print("To grid today: ");
Serial.println(node.getResponseBuffer(0x19));
Serial.print("From grid today: ");
Serial.println(node.getResponseBuffer(0x1A));
Serial.print("Consumption today: ");
Serial.println(node.getResponseBuffer(0x1B));
Serial.print("Battery cycles: ");
Serial.println(node.getResponseBuffer(0x2C));
}
delay(5000);
}
I've commented out "if result == node.ku8MBSuccess" because I'm never getting a success reading. I've got DE & RE of the 485 module connected to pin 2 of the MEGA.
The invertor documentation is attached but I've not managed to get any battery documentation from the invertor.
I failing to understand the principle of slave/master on ModBus as I suspect the battery is the master and inverter is the slave, so do I need another master on the network or can a slave talk to another slave?
Any help would be appreciated.
SOFARSOLAR ModBus-RTU Communication Protocol.pdf (270 KB)
serial_output_text.txt (15.8 KB)