[RS-485] bus configuration

Dear All,

I would like to add RS-485 communication capability to a board (duemilanove, chipkit uno32 or due) . During the development and testing phase a RS-485 shield will be wired to pins 3, 10 and 11 (TXControl, Rx and Tx) and I will use SoftwareSerial. When in production, the RS-485 communication will occur through the pins 0 and 1 (there will be no computer connected to the board).

Up until now, I have only developed software on the computer side (MAC) using adapters such as the ICUSB422 [1] to drive industrial devices. In such situation I only had to worry about configuring the bus. All data were transmitted in binary, using the HART protocol, so there wasn't any termination character, of course.

I have two question about this.

  1. How do I configure the software RS485 Serial bus (pins 3, 10, 11) and the built-in serial bus (pins 0, 1) in order to reflect those parameters:
Transmission speed: 19200 bauds
Parity: Odd
Stop bit:	1
Termination character: none
Flow contro : none
echo: NO
Timeout:	 50 ms
  1. Would you advise me against using binary data transmission (I am free to choose)? If so, what configuration should I better adopt?

Thank you for reading me.

[1] RS422 RS485 USB Serial Cable Adapter - Serial Cards & Adapters | StarTech.com

Namaan:
2. Would you advise me against using binary data transmission (I am free to choose)? If so, what configuration should I better adopt?

Text is easier to debug (just connect a serial monitor to see the data flow). Binary is more efficient (typically by twice). Which is more important to you?

Thank you for your reply.
Using binary is (by far) more important to me, but I don't know if that's possible using the Serial or Software serial library:

  • How do I configure the bus to match the configuration described in my first post?
  • Binary, of course, does not have an end transmission character. How do I detect the end of the transmission on the board (board is slave)?

Transmission speed: 19200 bauds
Parity: Odd
Stop bit: 1
Termination character: none
Flow contro : none
echo: NO
Timeout: 50 ms

All ok except parity and timeout. You have no control over parity with the standard hardware serial library (might have with SS) and as for timeout, exactly what that is is anyone's guess in this application but it's not supported either.

So you are in for some DIY serial-handler code.

How do I detect the end of the transmission on the board

Fixed message length, message length embedded in message, timeout, break condition...maybe something I haven't thought of.


Rob

Namaan:

  • How do I configure the bus to match the configuration described in my first post?
void setup( void )
{
  Serial.begin( 19200, SERIAL_8O1 );
}

As @Graynomad mentioned, flow control (time-out) is your responsibility.

  • Binary, of course, does not have an end transmission character. How do I detect the end of the transmission on the board (board is slave)?

I usually use frames with this basic structure...

  {synchronization character} {length} {data} {CRC}

The {synchronization character} simplifies the resynchronization code. I like greater-than (>) because it's easy to spot and matches the character used in the Optomux protocol. {length} is the number of bytes in {data} and {CRC} and is used to frame a candidate packet. {CRC} is a CRC-16 calculated over {length} and {data} (including {synchronization character} is neither beneficial nor costly; do it if it simplifies the code).

Nice. Thank you. I missed the "SERIAL_8O1" parameter, which does exactly what I want.

Currently, I use frames such as the one below:
{Preambles 3-10 bytes 0xFF}{Start char}{device address}{Command code}{status bytes}{data length}{data}{message XOR}

And, yes, since I know the length of the message as soon as I get the command code, so I can safely stop reading data as soon as I get the whole message. I just have to add some code to deal with the edge case when I get a transmission error that results in a valid command code different to the one intended and that is associated with a message of different length.

I did check the softwareSerial and unfortunately, the begin() function does not take any additional parameter. That would not be an issue if I could detect the line configuration. Any way to get this info?

I missed the "SERIAL_8O1" parameter

Me to, haven't looked at that for a long time.


Rob

Serial doesn't provide any mechanism for auto-detection. Some devices manage to auto-detect speed and such by testing for known (or presumably likely) bit patterns. For example, I had a NAS with a serial console port that asked you to press SPACE three times. It would then sample the input at different rates until it matched three spaces. Not exactly an exact science, and prone to failure if you're sending random bits of binary data. (Nothing looks inherently out of the ordinary if any combination of bits is possible.)

As to the protocol you use, that depends on the application. If you're designing two devices that will talk to each other, the sky's the limit. Use any method you'd like. Nick Gammon wrote an RS-485 library that will do this for you. You just provide a message, it will handle the encoding.

If you're interfacing with existing devices (and based on your having a bus spec, you are -- right?) then your protocol is already defined for you. You will have to comply with that, rather than devising your own. This includes whether you transmit binary or 7-bit ASCII. (Which, BTW, the serial libraries don't care about. Send whatever sequence of bits you want. Most supported serial protocols speak 8-bit natively, so there's no practical difference between binary and text except what you do with it.)

Well, I have a MAC equipped with the USB/RS-485 adapter described in my first post, and I am indeed using it to communicate with known hardware.
I can either put everything on the same bus, in which case, I have to program the arduino so that it behaves well on the bus (same bus settings, same message format), OR I can set up a separate bus for the arduinos.

Currently, I can't use option one, because I am using SoftwareSerial and apparently there are no methods to configure the bus. Option two would work, provided that I can find which bis configuration (parity, stop bit, etc) Software Serial is using. I could indeed try various settings on the computer. There are probably around 24 different combination or so. But since I have never actually programmed an arduino, nor even used one, I could be searching for a long time in case of program or hardware error.

Namaan:
...provided that I can find which bis configuration (parity, stop bit, etc) Software Serial is using.

That one is easy (I misunderstand what you wanted). Checking the source code...
https://github.com/arduino/Arduino/blob/master/libraries/SoftwareSerial/SoftwareSerial.cpp
...we find the method that writes one byte...
https://github.com/arduino/Arduino/blob/master/libraries/SoftwareSerial/SoftwareSerial.cpp#L445
...with these comments...

// Write the start bit
// Write each of the 8 bits
// restore pin to natural state

So, 8 data bits, no parity, one stop bit.

Nice ! Thank you so much for your help.
I read the source code. Apparently, they have several table constant to deal with preforming a delay in respect with the CPU frequency.

Why don't they use delayMicroseconds() ?

Their tunedDelay() function uses assembly. Will it it compile if I change architecture? My program is already quite big (20 KB). I still need to add a lot of features, so I may end up on an ARM CORTEX A3 (duo) or PIC32 architecture. Also, I lack a few extra pins (I know there are I2C capable chips that can help me there, but I can't help the binary size).

Below is the code for the tunedDelay() function.

inline void SoftwareSerial::tunedDelay(uint16_t delay) {
uint8_t tmp=0;

asm volatile("sbiw %0, 0x01 \n\t"
"ldi %1, 0xFF \n\t"
"cpi %A0, 0xFF \n\t"
"cpc %B0, %1 \n\t"
"brne .-10 \n\t"
: "+r" (delay), "+a" (tmp)
: "0" (delay)
);
}

Will it it compile if I change architecture?

No.


Rob