2
votes

What I'm doing:

Implementing RS-485 protocol, I have to:

  1. Set a write pin to high and go into write mode.
  2. Write my data (of 16 bytes length) to serial port.
  3. Set my pin to low again and go into read mode.
  4. Wait until 16 bytes have arrived.
  5. End transmission.

The details including why a write pin must be set to high and low, why 16 packets exactly, and ... are some hardware implementation details I can not change.


Current solution:

// .....

private final Serial serial;
private final GpioPinDigitalOutput pin;
private final long DELAY_M = 2;
private final int DELAY_N = 0

// .....

public boolean send(final byte[] packet) {

  result = false;

  try {
      this.pin.high();
      this.serial.write(packet);
      Thread.sleep(DELAY_M, DELAY_S); // -> THE TROUBLE MAKER
      this.pin.low();
      result = true;
  }

  // ... Now in read mode, recv 16 bytes in a pre-defined time window

  return result;


The problem

Before I go into read mode (setting the pin to low) I must wait till all data in the serial buffer is transmitted. I am using Pi4J library and it has no facility for checking the remaining bytes in buffer. The dirty solution is to wait a constant DELAY_M milliseconds BUT this constant time changes in different environments, different hardware and ...

Looking into Pi4J's code, in native implementations (JNI) it's calling WiringPi's API. WiringPi in turn treats the serial port as a regular linux file and writes into the file. Again, WiringPi provides no method to check the remaining bytes in buffer. Then it must be a Linux-Hardware-Kernely thing and not necessarily Pi4j's responsibility. So: How do you check remaining data in raspberry's serial port buffer? wich is /dev/ttyAMA0

P.S: the serial interface in Pi4j has a method flush() with this documentation:

Forces the transmission of any remaining data in the serial port transmit buffer. Please note that this does not force the transmission of data, it discards it!

Update:

Regarding what @sawdust pointed out in comments, I've found this tutorial. It enables what's called RTS and CTS (more about these flags here and here) BUT it is not working yet. My oscilloscope shows no signal on CTS and RTS pins.

Also note that the article dates back to 2013 and gpio_setfunc wont even compile. It needs some strange scripts not available anywhere. But do look in apt-get packages list with apt-cache search gpio and you'll find the required tools.

1
Does RPi support RS-485 out of the box ? I thought it needed a shield/pi hat/converter/etc.George Profenza
@GeorgeProfenza No, It's a custom board with a MAX-485 and I'm controlling the board. board inputs are a GPIO pin indicating read/write mode and the /dev/ttyAMA0 as the serial line.hkoosha
"P.S: the serial interface ... has a method flush()" -- The function you want to use is tcdrain(), which "waits until all output written to the object referred to by fd has been transmitted". BTW the preferred method of controlling the RTS line for half-duplex RS-485 is within the device driver, but only a handful of Linux serial port drivers have actually implemented it (e.g. atmel_serial.c).sawdust
@sawdust yes it seems so. I've been trying to add it as an extra method to wiringPi and Pi4j, but it's not working yet. pulling my hairs out right now. I'll do a PR on github when (if) finished. thanks!hkoosha
Ever found a good solution for this? I'm struggling with the exact same..Tinus81

1 Answers

1
votes

You can hold the receive enable low which means you receive your own transmission. That way you know when the transmission is complete and can then take Tx enable low. Then just filter your transmission from the response.

eg. for your transmit routine:

 synchronized(mutex) {
     transmitEnable.high();
     awaitingEcho = true;
     expectedEcho = "test\n";
     serial.writeln("test");
 }

and the receive:

synchronized(mutex) {
    data = event.getAsciiString();
    if (awaitingEcho && data.contains(expectedEcho)) {
        transmitEnable.low();
        data = data.replace(expectedEcho, EMPTY);
        expectedEcho = null;
    }
}