Battery BMS RS485 not responding to Arduino

OK this one has stumped me.

Basically I am wanting to read data from a JBD battery BMS which I've done successfully many times although not with this exact model.
If I use the specific application on a PC (Overkill solar) this communicates and displays data on the screen proving the RS485 shield on the UART port of the BMS and the RS485 dongle and wiring are all correct. I then connected a Mega with a RS485/TTL shield to act as a 'sniffer' I can see all traffic going to and from the BMS. Se below schematic (drawn from memory);

The received traffic is;

image

The short message is the data request from the PC, 0xDD being the start bit and 0x77 the stop bit. The long string being the reply, again 0xDD as the start and 0x77 as the stop bit. The second byte is 0xA5 for requesting data and 0x5A for the returned data packet. 3 different sets of registers are being called for.
The RS485 shield on the battery BMS is the XY-017 type that uses timing rather than a hardware transmit pin as the UART port does not have that facility.

Now then, if I try to send the exact same HEX string from the Arduino the BMS does not respond, I know the hardware is OK as the Mega can see the traffic, the TX enable has to be working as it will block the request from the PC if set incorrectly and I can see the the RX led flicker on the battery RS485 shield every time the Arduino sends the request. The Baud rate is 9600 which has to be correct as traffic can be seen, the BMS protocol calls for no parity, 8 bits and 1 stop bit which I believe is standard for the Arduino serial. I remove the dongle from the PC when trying.

Here is the code,

/* This code uses an Arduino Mega and RS485 shield to communicate with a JBD battery BMS
 *  transmit and receive using the Serial1 port, transmit enable connected to pin 17
 *  With the 'request' call commented out this works as a data sniffer 
 */


int tx_en = 17; // transmit enable pin

unsigned long previousMillis = 0;
const long interval = 1000; //  transmit request repaet time

void setup() {

  pinMode(tx_en, OUTPUT);  // set transmit enable pin as output
  Serial.begin(115200);    // serial output to console
  Serial1.begin(9600);     // RS485 serial
  digitalWrite(tx_en, LOW);// Set transmit enable low (debugging)
  delay(1000);             // Add delay for debugging

  Serial.print("Starting");// this just prints something on the console to show things are alive and the baud is correct

  delay(100);              // another debuggung delay



}

void loop() {

  //-------------- untimed code -------------------------------------------------

  //  this part taken from the standard multi serial sketch

  // read from port 1, send to port 0:
  if (Serial1.available()) {
    int inByte = Serial1.read();
    //Serial.write(inByte);
    Serial.print(inByte, HEX);

    if (inByte == 0x77) Serial.println(" ");  //  new line at stop bit for reading clarity
  }

//--------------- end of untimed code ------------------------------------------------------

  unsigned long currentMillis = millis(); //  sets transmit repeat time

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;


    //------------------timed code---------------------------------------------------


    request();  //  call the data request routine

  }
}

//------------------- end of loop ----------------------------------------------------

//----------------- request routine ----------------------------------------------

void request() {



  digitalWrite(tx_en, HIGH);  //  Set RS485 shield to transmit mode
  Serial1.write(0xDD);        //  start bit
  Serial1.write(0xA5);        //  status bit    A5=read   5A=write
  Serial1.write(0x30);        //  error status  03=OK     80=error
  Serial1.write(0xFF);        //  Data sent
  Serial1.write(0xFD);        //  possible check sum
  Serial1.write(0x77);        //  stop bit
  delay(3);                   //  short delay so serial can finish sending
  digitalWrite(tx_en, LOW);   //  set RS485 shield to receive mode

}

So, what an earth am I overlooking??

I'd want to look at the serial lines with something independent to verify that what the arduino is doing is identical to what the PC is doing. Have you tried leaving the dongle in place and running a terminal program on the PC to sniff the arduino output?

Awesome idea, I'll try downloading PuTTY, there is definitely something different.

Thanks ZX80, sadly that's a slightly different model, I've actually designed a board using an Atmega328 and a RS485 chip to successfully communicate with that very unit, with the BMS in question the first thing I did was to try that board on this BMS fully expecting it to work....wrong.
The PC application however works seamlessly with both.

RustyKipper/BoxOfSunshine: Solar battery to inverter interface (github.com)

Wow, this looks like it might be the issue, this is what the terminal program thinks the Arduino is sending, strange as it receives perfectly so can't be baud rate or timing.......

It could be a faulty RS485 shield, I should have another one somewhere.
I will double check the settings in putty.

image

Edit. OK, scrub that, its looks like PuTTY doesn't work too well with the RS485 dongle.
I might have to go old school and look at the data on the 'scope.

Edit2. When I say old school I mean old school, my trusty 30 year old Hameg.

OK it looks like the TX enable is dropping out half way through sending the data....that'll do it!

Ah. The serial.print is sending data into a transmit buffer, so when you switch the enable off, the buffered data is still being transmitted.

Rather than do that, call Serial1.flush() which will block until the buffer is empty. At that point, it is safe to toggle the TX enable line

Well. it just gets stranger, a 7mS delay seems to allow all data, I've always got away with 3mS before. The Serial.flush() works a treat, see below;


Now this bit is kind of confusing, reading the 'A' line of the RS485, top trace is what the Arduino is sending, the bottom trace is from the PC, they are nothing like;

You can see the start of the reply from the BMS on the lower trace.
The start and stop bits look the same, this was using the 7mS delay hence the top signal staying high briefly at the end. I'm not convinced the timing looks the same.
I'm going to hook a second Arduino up and use that to read what the first Arduino is sending.

Edit, Arduino 2 reading Arduino 1 over the RS485 network;
image
This is Arduino 2 reading what the PC is sending;
image

I was concerned about the DC ofset so tried anothe RS485 shield, the signal looks better but still not working, top trace Arduino, lower trace PC, the PC seems to be sending more data????
image

This is very technically interesting but its starting to make my head hurt!

Success!!

I found the protocol for the BMS and it seems there was an 0x00 missing from the data request packet, when the Arduino reads the data on the RS485 line it didn't seem to think a zero was important so didn't print it in the serial console hence the oscilloscope showing the PC sending an extra bit (byte) of data.

This gets the required response if anyone else has the same issue;

  Serial1.write(0xDD);
  Serial1.write(0xA5);
  Serial1.write(0x03);
  Serial1.write((byte)0x00);
  Serial1.write(0xFF);
  Serial1.write(0xFD);
  Serial1.write(0x77);

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