Trying to read registers from a solar charge controller over RS232 (solved!)

I have the Renogy Wanderer 30amp solar charge controller, which has an RS232 port.

I'm trying to read the battery voltage and other data via it's RS232 port from an ESP32 or an Arduino. I haven't found any projects that do that, but I did find one that uses a Pi and NodeJS:

It describes how to build the RS232 cable, which has a somewhat non-standard pinout:

That's the same pinout as this project, which also interfaces the Renology to a Pi but doesn't give much documentation:

image

I'm using this RS232 to TTL Serial converter. On the TTL side I have ground connected to an ESP32 ground, VCC to ESP32 3.3v, the adaptor's RXD to ESP32 pin TX2, the adaptor's TXD to ESP32 pin RX2.

I'm currently using this sketch:

#define RXD2 16
#define TXD2 17

String incoming;

void setup() {
  Serial.begin(115200);
  Serial.println("Started!");
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
}

void loop() { 
  while (Serial2.available() >= 1) {
    //Serial.println("checking...");
    Serial.print(char(Serial2.read()));
    //incoming = Serial2.readStringUntil('\n');
    //Serial.println(incoming);
  }
}

No data reaches the console other than boot data, but I think that's because I'm not checking the charge controller's registers. Here are the registers are described at that NodeJS project:

The below is a list of supported registers for device information:

|0x00A|Controller voltage rating|Volts|
|0x00A|Controller current rating|Amps|
|0x00B|Controller discharge current rating|Amps|
|0x00B|Controller type||
|0x00C - 0x013|Controller model name||
|0x014 - 0x015|Controller software version||
|0x016 - 0x017|Controller hardware version||
|0x018 - 0x019|Controller serial number||
|0x01A|Controller MODBUS address|

The below is a list of supported registers for state data:

|0x100|Battery Capacity|Percent|
|0x101|Battery Voltage|Volts|
|0x102|Battery Charge Current|Amps|
|0x103|Battery Temperature|Celcius|
|0x103|Controller Temperature|Celcius|
|0x104|Load Voltage|Volts|
|0x105|Load Current|Amps|
|0x106|Load Power|Watts|
|0x107|Solar Panel (PV) Voltage|Volts|
|0x108|Solar Panel (PV) Current|Amps|
|0x109|Solar Panel (PV) Power|Watts|
|0x10B|Min Battery Voltage Today|Volts|
|0x10C|Min Battery Voltage Today|Volts|
|0x10D|Max Charge Current Today|Amps|
|0x10E|Max Discharge Current Today|Amps|
|0x10F|Max Charge Power Today|Watts|
|0x110|Max Discharge Power Today|Watts|
|0x111|Charge Amp/Hrs Today|Amp Hours|
|0x112|Discharge Amp/Hrs Today|Amp Hours|
|0x113|Charge Watt/Hrs Today|Watt Hours|
|0x114|Discharge Watt/Hrs Today|Watt Hours|
|0x115|Controller Uptime|Days|
|0x116|Total Battery Over-charges|Count|
|0x117|Total Battery Full Charges|Count|

Any guidance on how I would check one of those registers? I think it uses modbus to do so.

Thanks for any help.

And aha, I found documentation for the Rover's modbus. It's a bear finding this stuff since their support forum is now defunct. But someone uploaded the file here:

I think it's mostly just enumerating the registers which are posted in my previous post though.

Edit: found it on github too:

That's the normal outcome from searching.

Don't expect helpers to plow through project presentations to find out what You aim at. Select the facts and present them here.

Good, You've found the necessary RS232 to TTL converter.

Be aware, know, there are 3.3 volt logic level families but also 5 volt families. Don't connect them "just like that".

Please provide schematics. Without that analysis are just guesses consuming time for no good.

How would that help forum helpers?

Don't expect helpers to plow through project presentations to find out what You aim at. Select the facts and present them here.

Apologies if I sounded like I was expecting people to do google research. I wasn't, I'm just presenting the facts as I know them as background to my question.

Be aware, know, there are 3.3 volt logic level families but also 5 volt families. Don't connect them "just like that".

Sorry I should have mentioned, the Amazon reviews for the TTL converter say it works with 3.3v.

And confirmed, the Renogy uses modbus. So really where I'm stumped is how to read a modbus register using a TTL converter like this. For example, how would I read register 0x00A.

Good idea, attached.
ROVER MODBUS.pdf (549.9 KB)

Sorry, just gave You the hard reality, what helpers are willing to, and not. There are highly experienced and skilled helpers once working for NASA, the space industry, the military.... They don't like questions lacking facts, like "mama help me".

You better present facts according to the expectations of forum. That's told in the advice in "How to get the best out of this forum".

Good! Signals can be sent from 3.3 volt logic to 5 volt logic but not the opposite.

What is Renogy? Some helpers might know and can reply. Other helpers don't reply......

Modbus has no registers. If it works handling modbus the question is accessing the item in the other end of the line, the device being accessed.
There is a mix of RS32 and modbus as I feel.
Reading the solar stuff is the aim. Right?
Late here. Tomorrow is another day.

ok, MODBUS, nothing special at first glance. They even have copied/pasted parts of the MODBUS specification in their document.

Install the library and start with one register. For example page 17 chapter 3.5 read the SoC as you have a good example. The Table on page 2 is missing the function code. So better start with that particular example in 3.5

If your controller is using modbus over RS232 then it might be worth using your PC along with a USB-serial adapter to try and talk to it using one of the free modbus tools out there.

It will give you a feel for the messaging without having to upload numerous sketches to your board.

For example page 17 chapter 3.5 read the SoC as you have a good example.

Thanks. That's:

3.5 To read battery capacity SOC, and the PDU address is known to be 0100H
To send: 01 03 0100 0002 C5F7
To receive: 01 03 02 0064 B9AF
Parsing: (the highest byte OOH is not used) the battery capacity SOC is 64H% (decimal 100%)

I'm not sure how I plug in those values. Here's a starting point I'm working on based on example code here (using "Modbus Master" Library from Doc Walker):

#include <ModbusMaster.h>

// instantiate ModbusMaster object
ModbusMaster node;

#define RXD2 16
#define TXD2 17

void setup()
{
  Serial.begin(115200);
  Serial.println("Started!");

  // create a second serial interface for modbus
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); // is SERIAL_8N1 correct?
  
  node.begin(0, Serial2); // I think the addy is 255. With anything else I get E0 for all reads (node.ku8MBInvalidSlaveID)

}


void loop()
{
  static uint32_t i;
  uint8_t j, result;
  uint16_t data[6];
  
  i++;
  
  // set word 0 of TX buffer to least-significant word of counter (bits 15..0)
  node.setTransmitBuffer(0, lowWord(i));
  
  // set word 1 of TX buffer to most-significant word of counter (bits 31..16)
  node.setTransmitBuffer(1, highWord(i));
  
  // slave: write TX buffer to (2) 16-bit registers starting at register 0
  //result = node.writeMultipleRegisters(0, 2);
  
  
  // slave: read (6) 16-bit registers starting at register 2 to RX buffer
  //result = node.readHoldingRegisters(2, 6);
  result = node.readHoldingRegisters(0x101, 6);

  Serial.println(result, HEX); // 0 is node.ku8MBSuccess. E2 is timeout, which is what I always get
  
  // do something with data if read is successful
  if (result == node.ku8MBSuccess)
  {
    Serial.println("Got some data!");
    for (j = 0; j < 6; j++)
    {
      data[j] = node.getResponseBuffer(j);
      Serial.println(data[j]);
    }
  }
}

Note that I'm trying to use modbus on a second serial port so I can still print output to the console.

In theory that should read 6 holding registers starting with 0x101 (battery voltage), but so far no joy. The above script just outputs "E2" every iteration (for "Serial.println(result)"), which is a timeout. So maybe I'm just not connecting to the controller correctly.

Here's a schematic of my wiring:

Phew, got it working. Thank you so much for all the help. The "Modbus Master" Library from Doc Walker put me on the right path.

For anyone else who comes this way: the modbus address of the Renogy Wander is 255! If that's in the docs I can't find it. Also make sure your RJ12 cable is making contact with all the pins in the RS232 port, it's possible mine wasn't making full contact.

And for making the cable, here's a picture of my RJ12 cable's jack showing the wire colors. You use the first 3 wires starting from the left (on my cable that's white, black, red).

And here's an updated version of my wiring diagram, showing the wire colors coming from the RJ12 cable:

Here's my current code, this gets all 30 data registers (not all are used on my cheapie controller) and 17 info registers. I haven't processed or formatted the data yet, but there's lots of processing and explanation in that nodejs project above.

// Pins used by ESP32 for the 2nd serial port. Not sure if this is possible on an Arduino.
#define RXD2 16
#define TXD2 17

// Number of registers to check. I think all Renogy controlls have 30
// data registers (not all of which are used) and 17 info registers.
int num_data_registers = 30;
int num_info_registers = 17;




// https://github.com/syvic/ModbusMaster
#include <ModbusMaster.h>
ModbusMaster node;


void setup()
{
  Serial.begin(115200);
  Serial.println("Started!");

  // create a second serial interface for modbus
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); // is SERIAL_8N1 correct?

  int modbus_address = 255; // my Renogy Wanderer has an (slave) address of 255! Not in docs???
  node.begin(modbus_address, Serial2); 
}


void loop()
{
  static uint32_t i;
  uint8_t j, result;
  uint16_t data_registers[num_data_registers];
  uint16_t info_registers[num_info_registers];
  
  i++;
  
  // set word 0 of TX buffer to least-significant word of counter (bits 15..0)
  node.setTransmitBuffer(0, lowWord(i));  
  // set word 1 of TX buffer to most-significant word of counter (bits 31..16)
  node.setTransmitBuffer(1, highWord(i));
    

  //////////////////////////////
  // Read the 30 data registers
  //////////////////////////////
  result = node.readHoldingRegisters(0x100, num_data_registers);
  if (result == node.ku8MBSuccess)
  {
    Serial.println("Successfully read the data registers!");
    for (j = 0; j < num_data_registers; j++)
    {
      data_registers[j] = node.getResponseBuffer(j);
      Serial.println(data_registers[j]);
    }
    Serial.println("---");
  }
  else {
    Serial.print("Failed to read the data registers... ");
    Serial.println(result, HEX); // E2 is timeout
  }


  //////////////////////////////
  // Read the 17 info registers
  //////////////////////////////
  result = node.readHoldingRegisters(0x00A, num_info_registers);
  if (result == node.ku8MBSuccess)
  {
    Serial.println("Successfully read the info registers!");
    for (j = 0; j < num_info_registers; j++)
    {
      info_registers[j] = node.getResponseBuffer(j);
      Serial.println(info_registers[j]);
    }
    Serial.println("---");
    Serial.println();
  }
  else {
    Serial.print("Failed to read the info registers... ");
    Serial.println(result, HEX); // E2 is timeout
  }

  delay(1000);

}

On my controller with no solar panel connected that outputs:

Successfully read the data registers!
120
0
4121
0
0
0
0
0
0
0
120
120
0
0
0
0
0
0
0
0
3
0
0
0
0
0
0
0
0
0

Successfully read the info registers!
3102
5120
8224
21070
18221
17236
21068
11607
20036
13104
1
4
1
3
4363
32
1

Sure is going to be nice having access to the data from these charge controllers without having to use a Raspberry Pi! Life is too short for long bootup times, corruptible SD cards and excessive power overhead, especially in a solar environment.

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