Reading Solar Charger COM via MODBUS (MAX485) Problem

Dear all,

I am trying to read the COM output from my EPEver Tracer 1210AN using a Mega+WiFi board via a Max485 module.

My primary orientation and references are the following...

  1. Similar project based on NodeMCU, Max485 and Blync:
    Cheap MPPT Controller Live Stats on Mobile - Hackster.io

  2. Pretty much the project of me except for the arduino board being different and a different charger model is being used, I copied the code from the screen, the code has strong similarities with previous orientational project reference code:
    TUTORIAL: How To Use RS-485 TTL MODBUS - Arduino Controller Module (Part 2/2 - Wire Up) Solar - YouTube

  3. This is the best reference on the pins of the RJ45 and the EPEver device, the official manual does not have anything on the COM except to use their own pricy products:
    python - Can't connect to EPsolar Tracer 3210an charge controller from Windows 10 via Serial / Modbus - Stack Overflow

Wiring:
COM RJ45 Pin 3 (B) to Max485 B
COM RJ45 Pin 5 (A) to Max485 A
COM RJ45 Pin 7 (GND) to Mega+WiFi GND

Max485 GND to Mega+WiFi GND
Max485 VCC to Mega+WiFi 5V

Max485 DI to Mega+WiFi TX D1
Max485 DE to Mega+WiFi D3
Max485 RE to Mega+WiFi D2
Max485 RO to Mega+WiFi RX D0

Used code:

#include <ModbusMaster.h>

#define MAX485_DE     3
#define MAX485_RE_NEG 2

ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1);
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  // put your setup code here, to run once:
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 115200 baud
  Serial.begin(9600);

  //Modbus slave ID 1
  node.begin(1, Serial);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  // put your main code here, to run repeatedly:
  uint8_t resultMain;

  resultMain = node.readInputRegisters(0x3100, 6);
  if (resultMain == node.ku8MBSuccess)
  {
    Serial.println(" - - - - - - - - ");
    Serial.println("PV Voltage: ");
    Serial.println(node.getResponseBuffer(0x00) / 100.0f);
    Serial.println("PV Current: ");
    Serial.println(node.getResponseBuffer(0x01) / 100.0f);
    Serial.println("Battery Voltage: ");
    Serial.println(node.getResponseBuffer(0x04) / 100.0f);
    Serial.println("Battery Charge Current: ");
    Serial.println(node.getResponseBuffer(0x05) / 100.0f);
  }

  delay(1000);

}

I tried different BAUDs including 115200, all I get is garbage output on the serial. It seems that I am close to have it working but for some reason the reading is not good.

I have no previous experience with modbus, and a good portion of above code is just copied with good hope of working.

Appreciate any feedback, hints and suggestions that might get me some steps further or even to the finish line.

Many thanks!

I think you are using the main serial port for your modbus communication, but this port is already in use for serial to USB comms. You may have to use a software serial port but they have baud rate limitations and usually max out at 19200 or 38400 baud.

The Mega+WiFi has multiple RX/TX (4 in total) pins and I assume those are not software serials, so I connected DI/RO to TX1/RX1 instead of the previously used TX/RX. Also I adjusted the code for the serial below.

#include <ModbusMaster.h>

#define MAX485_DE     3
#define MAX485_RE_NEG 2

ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1);
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  // put your setup code here, to run once:
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 115200 baud
  // Serial.begin(9600);
  Serial1.begin(115200);
  // Serial.begin(9600);

  //Modbus slave ID 1
  node.begin(1, Serial1);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  // put your main code here, to run repeatedly:
  uint8_t resultMain;

  resultMain = node.readInputRegisters(0x3100, 6);
  if (resultMain == node.ku8MBSuccess)
  {
    Serial1.println(" - - - - - - - - ");
    Serial1.println("PV Voltage: ");
    Serial1.println(node.getResponseBuffer(0x00) / 100.0f);
    Serial1.println("PV Current: ");
    Serial1.println(node.getResponseBuffer(0x01) / 100.0f);
    Serial1.println("Battery Voltage: ");
    Serial1.println(node.getResponseBuffer(0x04) / 100.0f);
    Serial1.println("Battery Charge Current: ");
    Serial1.println(node.getResponseBuffer(0x05) / 100.0f);
  }

  delay(2000);

}

This does not do anything, I dont even get garbage output, also I dont see any LED blinking anymore on the Mega+WiFi as it was blinking when connected to TX/RX and producing garbage output. Not sure what differences the code makes.

While it was producing garbage I also disconnected the charge controller from the MAX485 and it didnt have any impact, garbage code was still produced on the serial output with TX/RX connected. I assume that the garbage code could mean anything, from reading/printing values to stating some error of reading.

Besides of TX1/RX1 I also tried the other ones like TX3/RX3.

While seeing people using the MAX485 for the same purpose, I also see people using UART TTL to RS485 adapter 485 serial instead, Youtuber Colin Hickey even mentions that the MAX485 does not work for such purpose, I am thinking of giving it a try with that other piece.

Please let me know what you think.

Ah, my fault. For some reason I assumed that you had a Mega328P microcontroller with 1 hardware serial port rather than one of the MEGA boards with the 4 serial ports!

I had a look at the Youtube video you linked to and got to around the 4 minute point (of the 30 minute video). That module he discards seems to be the right one for the job. I have several of them myself, although not being used for Modbus at the moment.

One thing that I've seen on occasion with Modbus is that the address is assumed to be hexadecimal, when it's actually decimal. You might try a readInputRegisters( 3100, 6 ) just in case as I couldn't find any documentation in my brief search that detailed the actual registers.

You might try a readInputRegisters( 3100, 6 )

Did that, not much of a change, the garbage output remains, just looks a bit different.

That module he discards seems to be the right one for the job.

I think so too.

Thanks Mark, for continuing looking into this case :slight_smile:

I continued experimenting, what caught my attention is that if I completely disconnect the MAX from the microcontroller, the serial print continuous with the garbage code, so, eventually the garbage has nothing to do with the MAX at that point in time. This makes me focus a bit on below code part of the loop section:

void loop()
{
  // put your main code here, to run repeatedly:
  uint8_t resultMain;

  resultMain = node.readInputRegisters(0x3100, 6);
  // resultMain = node.readInputRegisters(3100, 6);
  if (resultMain == node.ku8MBSuccess)
  {

What exactly does this code do? I hope below understanding is correct.
The first part is a declarition of the variable resultMain as uint8_t which seems to be the same as bit.
Then resultMain gets the registers from the MAX, I guess.
Then there is some validation if resultMain is the same as ku8MBSuccess, this refers to the ModbusMaster library and I have no clue what this exactly means.

Now, independent if the MAX is connected or not, this code seems to create garbage code, the same garbage code independent of connection. It appears that the MAX signal is not being read.

To check the serial I extended the if statement with an else statement to see what I get returned:

void loop()
{
  // put your main code here, to run repeatedly:
  uint8_t resultMain;

  resultMain = node.readInputRegisters(0x3100, 6);
  // resultMain = node.readInputRegisters(3100, 6);
  if (resultMain == node.ku8MBSuccess)
  {
    Serial.println(" - - - - - - - - ");
    Serial.println("PV Voltage: ");
    Serial.println(node.getResponseBuffer(0x00) / 100.0f);
    Serial.println("PV Current: ");
    Serial.println(node.getResponseBuffer(0x01) / 100.0f);
    Serial.println("Battery Voltage: ");
    Serial.println(node.getResponseBuffer(0x04) / 100.0f);
    Serial.println("Battery Charge Current: ");
    Serial.println(node.getResponseBuffer(0x05) / 100.0f);
  }
  else {
    Serial.println(" Error ");
  }
  delay(2000);
}

The serial output is garbage plus "Error", something like this:
[][]1[]~? Error
[][]1[]~? Error
[][]1[]~? Error

This makes me wonder where the garbage code is now coming from as the if statement should just create the "Error" text.

In regards to the Modbus Register, this is probably the best available orientation:

It is not the same charger device but from the same company EPSolar/EPEver. Also the reference to the MT50 device is the company's own device to read the COM from the charger, I assume that the MT50 does not make a difference based on the COM signals on which device it reads from and the register structure therefore might be the very same for all their devices, but this is just an assumption.

For completeness I also attach the wiring to eventually increase understanding/focus with visuals.

One more thing to add, I get only garbage code when I use RX/TX... using any of the other serials does not create anything, of course not just changing pins but also adjusting the code, in the latest code post I just left it all on RX/TX

Ok, there's one thing that I can see. In order to read from a Modbus device and then print out the received data, you need to have 2 active serial ports. Something like this:

#include <ModbusMaster.h>

#define MAX485_DE     3
#define MAX485_RE_NEG 2

ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1);
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  // put your setup code here, to run once:
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // debug / monitor serial port
  Serial.begin(9600);

  // Modbus communication runs at 115200 baud
  Serial1.begin(115200);

  //Modbus slave ID 1
  node.begin(1, Serial1);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  // put your main code here, to run repeatedly:
  uint8_t resultMain;

  resultMain = node.readInputRegisters(0x3100, 6);
  if (resultMain == node.ku8MBSuccess)
  {
    Serial.println(" - - - - - - - - ");
    Serial.println("PV Voltage: ");
    Serial.println(node.getResponseBuffer(0x00) / 100.0f);
    Serial.println("PV Current: ");
    Serial.println(node.getResponseBuffer(0x01) / 100.0f);
    Serial.println("Battery Voltage: ");
    Serial.println(node.getResponseBuffer(0x04) / 100.0f);
    Serial.println("Battery Charge Current: ");
    Serial.println(node.getResponseBuffer(0x05) / 100.0f);
  }

  delay(2000);

}

So the main serial port is connected to your PC via the USB port as normal. Serial1 is used for the Modbus messages and as far as I know, once configured you do not access it directly. Only the Modbus library does that.

If you are using Serial1, then looking at the pins on the image, you need to connect MAX485 RO to MEGA pin 19, and MAX485 DI to MEGA pin 18.

Regarding that bit of code:

void loop()
{
  // put your main code here, to run repeatedly:
  uint8_t resultMain;

  resultMain = node.readInputRegisters(0x3100, 6);
  // resultMain = node.readInputRegisters(3100, 6);
  if (resultMain == node.ku8MBSuccess)
  {

it defines resultMain as a byte and uses it to hold the result of the call to node.readInputRegisters(). The result can hold several values depending on what happens during the call to node.readInputRegisters(). Anything but "node.ku8MBSuccess" is probably an indication of an error somewhere. Note that resultMain does not hold the register values have been requested.

You can see the innards of the ModbusMaster library on github here but it's not an easy read!

Looked at your code and took it completely over. Connected DI and RO on pins 18 and 19 (TX1/RX1). This leads to an empty serial on 9600 BAUD and also just to complete it also nothing on 115200 BAUD (not expecting anything to see on 115200 BAUD anyway).

Did the same with changing 0x3100 to 3100 which also didnt create anything on the serial.

Switched the TX1/RX1 pins to make sure there was no mistake on connecting, nothing to see here as well.

In case you get more ideas and have further suggestions, I am very keen on trying them out :grin:

Ok, in setup() just below the serial.begin(9600) I would suggest adding a serial.println() with something like:

serial.println("hello");

You should at least see the word "hello" on your serial monitor. That then shows that you have 1 working serial interface and we can use it to print out additional status information.

If you do see "hello", then add in:

resultMain = node.readInputRegisters(0x3100, 6);
serial.println( resultMain );     // <--- add in this line

This should print out the status result returned by the call to readInputRegisters().

I chose instead of "hello" some check statement and it works, adding the additional line leads to the following serial output:

Serial 9600 Check
224
224
224

224 is coming up every couple of seconds based on the delay of the loop.

Current code:

#include <ModbusMaster.h>

#define MAX485_DE     3
#define MAX485_RE_NEG 2

ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1);
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  // put your setup code here, to run once:
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // debug / monitor serial port
  Serial.begin(9600);
  
  Serial.println("Serial 9600 Check");

  // Modbus communication runs at 115200 baud
  Serial1.begin(115200);

  //Modbus slave ID 1
  node.begin(1, Serial1);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  // put your main code here, to run repeatedly:
  uint8_t resultMain;

  resultMain = node.readInputRegisters(0x3100, 6);
  Serial.println( resultMain );
  if (resultMain == node.ku8MBSuccess)
  {
    Serial.println(" - - - - - - - - ");
    Serial.println("PV Voltage: ");
    Serial.println(node.getResponseBuffer(0x00) / 100.0f);
    Serial.println("PV Current: ");
    Serial.println(node.getResponseBuffer(0x01) / 100.0f);
    Serial.println("Battery Voltage: ");
    Serial.println(node.getResponseBuffer(0x04) / 100.0f);
    Serial.println("Battery Charge Current: ");
    Serial.println(node.getResponseBuffer(0x05) / 100.0f);
  }

  delay(5000);

}

When I disconnect from the charger, the MAX stays powered as it is powered through the microcontroller, I get 226 every couple of seconds on the serial monitor.

226 (or 0xE2) is an error code, specifically a timeout which I think means that the remote device failed to respond.

224 is then hopefully a positive code :slight_smile:

In the innards of the library that you pointed me at, I see the following in case 224 is 0xE0:

/**
ModbusMaster invalid response slave ID exception.
The slave ID in the response does not match that of the request.
@ingroup constant
*/
static const uint8_t ku8MBInvalidSlaveID = 0xE0;

The current code takes "1" as the slave ID, do you think it makes sense to go through some additional IDs 2, 3, etc. to see if the serial provides a different response?

  //Modbus slave ID 1[color=#222222][/color]
  node.begin(1, Serial1);

I couldn't see any documentation for the solar charger that provided a useful guide to the RS485 interface.
I did find a discussion that hinted that the default address is 0 but also evidence the the address can be changed.
I also found this link that may be a starting point for the modbus interface.
I also found some useful links by the Google search term "epever tracer 1210an modbus".

Thanks Mark.

The charger producer certainly did something to not have it straight forward, I just wonder what it could be as they must have added something on top of the modbus approach to avoid people coming up with their own self-made solutions.

Also, their remote MT-50 monitor has changed, the older versions do not work with the newer charge controllers (Tracer A series is the older discontinued, Tracer AN is the new and current series), I have a newer charge controller.

There are two directions that I would like to further explore.

The first is related to connecting the charge controller via USB. Youtuber Adam Welch reviewed the AN version and highlighted for instance the issues with the old MT-50. He also mentions that the self-made arduion/ESP based monitoring solutions do not work anymore with the AN device, but he mentions that using the self-made USB cable works. He is using some converter like this.

....while writing this and re-watching Adam's video I was wondering why the usb cable works and the ESP/Arduino isnt, what is the difference? It is so obvious - the USB solution does not ground the charger!

I disconnected pin 7 from the microcontroller ground and get now this on the serial:


PV Voltage:
0.00
PV Current:
0.00
Battery Voltage:
12.47
Battery Charge Current:
0.00
0


PV Voltage:
0.00
PV Current:
0.00
Battery Voltage:
12.47
Battery Charge Current:
0.00
0

It works!
The problem is that many instructions relate to having the ground connected. The AN series is a common negative series, maybe that has something to do with the ground issue but I dont really know.

The fact that the error(s) go away and that you get a value back for a battery voltage that seems plausible for a 12V battery (assuming that it is a 12V battery!) suggest that you are on the right track. I'm guessing that the solar panel(s) are not connected given the first 2 PV readings. That would also increase confidence that you are on the right track and not just coincidence that a random number happened to be decoded to produce a believable voltage for the battery.

If you were still having issues, then I was going to suggest a PC type USB-RS485 adapter (like the one you linked to) and one of the PC based free Modbus tools to try an establish communications that way.

Indeed, I am using a 12V battery and the device screen also showed 12.5V battery voltage. PV was not connected for this test :slight_smile:

Originally I was planning to have an ESP-01S setup for this charger data aspect but as all my other project parts are being handled via a Mega+WiFi board that I used for testing, I also dont see any conflicts on staying with it, sensor data and charger data can be sent with just one php post setup. Very happy about the situation!

Luckily I dont currently see a need to further trouble shoot :slight_smile: but indeed, the USB validation would have been the next step I was thinking of too.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.