New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RS485 and Raspberry pi #210

Closed
hyndruide opened this Issue Jun 10, 2018 · 19 comments

Comments

Projects
None yet
3 participants
@hyndruide

hyndruide commented Jun 10, 2018

hi and thx a lot for this beautifull library,

I wonder to know if it's possible to use RS485 ThroughSerial between an arduino and a raspbery pi.
I try to use GPIO 18 for "DE" and "RE".

bus.set_enable_RS485_pin(18)

but it seem to does work.

I can send information from the rapsberry to the arduino but not to the other way?

do you have a solution ?

@gioblu

This comment has been minimized.

Owner

gioblu commented Jun 11, 2018

Ciao @hyndruide thank you for reporting, yes acknowledgment feature on RPI needs still to be fixed in master to work out of the box. The necessary changes to obtain this result will be released with the next minor version.

Disabling the synchronous acknowledgment on transmitter side using:

  // Avoid sync ack
  bus.set_synchronous_acknowledge(false);

should make it work on both sides, although no acknowledgment feature is provided.

If acknowledgment is a requirement define:

#define TS_RESPONSE_TIME_OUT 35000

before PJON.h inclusion on both sides and activate the synchronous acknowledgment.

Feel free to ask for support if required.
Please, report us precious test results.

Happy tinkering!

@hyndruide

This comment has been minimized.

hyndruide commented Jun 11, 2018

Hi,
I think i found a first problem the GPIO is not 18.

if(wiringPiSetup() == -1) printf("WiringPi setup fail");

with this configuration the 18 (BCM GPIO) become pin 1 for WiringPi.
I found this with a LED connect between this PIN and GND.

the problem is not solved yet, but now DE and RE are connected. :-)

@hyndruide

This comment has been minimized.

hyndruide commented Jun 11, 2018

Ok, I find a way.
there is a problem with endtx() who shut off pin (DE,RE) too soon,
for now i try to add a delay but 200ms seem good for few packet not all of them.

@gioblu

This comment has been minimized.

Owner

gioblu commented Jun 11, 2018

Ciao @hyndruide thank you for reporting, it seems wiringPi flush function seems bugged/broken because is not flushing as expected (waiting for all bytes to be sent before returning) for that reason you can see the enable pin go low in the oscilloscope before all data has been transmitted. This is the motivation why this issue affects only RPIs if using acknowledgment.

Some iterations ago I have added the set_baudrate method that passing the baudrate can wait for all data to be transmitted, although that for now is a bad hack also because there is an offset in the expected and real transmission duration:

https://github.com/gioblu/PJON/blob/master/src/interfaces/RPI/PJON_RPI_Interface.h#L96

Probably tweaking that value will fix the issue, although there is for sure a better solution for this (for example to fix the wiringPi flush bug). If you have any idea feel free to share.

@hyndruide

This comment has been minimized.

hyndruide commented Jun 11, 2018

I have this problem to my arduino side for the raspberry pi it's seem work pretty well.
I sent you my code When i go to my pc.

@hyndruide

This comment has been minimized.

hyndruide commented Jun 11, 2018

The code for the raspberry pi 2 :

// For printf used below
#include <stdio.h>
// PJON library
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
// RPI serial interface
#include <wiringPi.h>
#include <wiringSerial.h>
#include <time.h>

#ifndef RPI
  #define RPI true
#endif

#define TS_RESPONSE_TIME_OUT 35000
/* Maximum accepted timeframe between transmission and synchronous
   acknowledgement. This timeframe is affected by latency and CRC computation.
   Could be necessary to higher this value if devices are separated by long
   physical distance and or if transmitting long packets. */

#define PJON_INCLUDE_TS true // Include only ThroughSerial
#include <PJON.h>

void receiver_function(uint8_t *payload, uint16_t length, const PJON_Packet_Info &packet_info) {
  /* Make use of the payload before sending something, the buffer where payload points to is
     overwritten when a new message is dispatched */
  printf("something here\n");
  printf("%c",payload[0] );
  if(payload[0] == 'B') {
    printf("BLINK\n");
    fflush(stdout);
  }
};

void my_delay(int i)    /*Pause l'application pour i seconds*/
{
    clock_t start,end;
    start=clock();
    while(((end=clock())-start)<=i*CLOCKS_PER_SEC);
}


int main() {
  if(wiringPiSetup() == -1) printf("WiringPi setup fail");
  printf("PJON instantiation... \n");
  PJON<ThroughSerial> bus(45);
  uint32_t baud_rate = 9600;
  printf("Opening serial... \n");
  int s = serialOpen("/dev/serial0", baud_rate);
  if(int(s) < 0) printf("\n\nSerial open fail!\n\n");
  printf("Setting serial... \n");
  bus.strategy.set_serial(s);
  bus.strategy.set_baud_rate(baud_rate);
  bus.set_receiver(receiver_function);
  bus.set_synchronous_acknowledge(false);
  bus.strategy.set_enable_RS485_pin(1);  // GPIO 18
  printf("Opening bus... \n");
  bus.begin();
  printf("Attempting to send a packet... \n");
  bus.send(44, "B", 1);
  printf("Attempting to roll bus... \n");
  bus.update();
  printf("Attempting to receive from bus... \n");
  bus.receive();
  printf("Success! \n");

  while(true) {
    bus.update();
    bus.receive();
  }

  return 0;
};

The code for Arduino Nano MEGA328P driver (ch340).


#include <PJON.h>

// <Strategy name> bus(selected device id)
PJON<ThroughSerial> bus(44);

void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW); // Initialize LED 13 to be off
  pinMode(6, OUTPUT);
  Serial.begin(9600);
  bus.strategy.set_serial(&Serial);
  bus.begin();
  bus.strategy.set_enable_RS485_pin(6);
  bus.set_receiver(receiver_function);
  bus.set_synchronous_acknowledge(false);
};

void receiver_function(uint8_t *payload, uint16_t length, const PJON_Packet_Info &packet_info) {
  /* Make use of the payload before sending something, the buffer where payload points to is
     overwritten when a new message is dispatched */
  if(payload[0] == 'B') {
    digitalWrite(13, HIGH);
    delay(30);
    digitalWrite(13, LOW);
    delay(60);  //delay to send back
     bus.send(45, "B", 1); 
    
  }
};

void loop() {
  bus.update();
  bus.receive();
};

in the ThroughSerial.h Arduino side line 237 :

    void end_tx() {
      delay(30); // add for end txe pin late
      if(_enable_RS485_txe_pin != TS_NOT_ASSIGNED) {
        PJON_IO_WRITE(_enable_RS485_txe_pin, LOW);
        if(_enable_RS485_rxe_pin != TS_NOT_ASSIGNED)
          PJON_IO_WRITE(_enable_RS485_rxe_pin, LOW);
      }
    };
@gioblu

This comment has been minimized.

Owner

gioblu commented Jun 11, 2018

Ciao @hyndruide thank you for reporting.
So with the attached codebase works? How it behaves? All packets are received?

I will run some tests, thank you again.

@hyndruide

This comment has been minimized.

hyndruide commented Jun 11, 2018

i change 2 thing more :

side arduino :

    void start_tx() {

      if(_enable_RS485_txe_pin != TS_NOT_ASSIGNED) {
        PJON_IO_WRITE(_enable_RS485_txe_pin, HIGH);
        if(_enable_RS485_rxe_pin != TS_NOT_ASSIGNED)
          PJON_IO_WRITE(_enable_RS485_rxe_pin, HIGH);
      }
    delay(30);
    };

    void end_tx() {
      delay(30);
      if(_enable_RS485_txe_pin != TS_NOT_ASSIGNED) {
        PJON_IO_WRITE(_enable_RS485_txe_pin, LOW);
        if(_enable_RS485_rxe_pin != TS_NOT_ASSIGNED)
          PJON_IO_WRITE(_enable_RS485_rxe_pin, LOW);
      }
    };

and i send Back "b" to the arduino once the raspberry pi receive to make a loop :-)

a video

@gioblu

This comment has been minimized.

Owner

gioblu commented Jun 11, 2018

Ciao @hyndruide thank you for your detailed report + the video, really nice and clean setup.
I am testing now the same setup will give you feedback soon.

@hyndruide

This comment has been minimized.

hyndruide commented Jun 11, 2018

You're welcome, Now i try to send string fromage Arduino to raspberry pi but it doesnt work i'dont know why yet...

@gioblu

This comment has been minimized.

Owner

gioblu commented Jun 11, 2018

Ciao @hyndruide I have replicated your setup using the codebase that you provided.
I cannot replicate the issue, the Arduino receives and answer correctly, I can see the BLINK in RPI screen, and what is shown in the oscilloscope is pretty consistent (binary data + enable pin) also if the 2 delays you have added are not present. See the video, excuse the crudity of this model :)

Could you try and see if it still works removing the 2 delays you have added?
I am not sure those are required.

@hyndruide

This comment has been minimized.

hyndruide commented Jun 12, 2018

nope i have nothing :-( I really don't understand.
I try with another arduino and i had same result without delay.

you ask me if i loose some packet so i create a knew code based on the first

rpi send a random char and the arduino send it back.

my new code :
for RPI :

// For printf used below
#include <stdio.h>
// PJON library
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
// RPI serial interface
#include <wiringPi.h>
#include <wiringSerial.h>
#include <time.h>

#ifndef RPI
  #define RPI true
#endif

#define TS_RESPONSE_TIME_OUT 35000
/* Maximum accepted timeframe between transmission and synchronous
   acknowledgement. This timeframe is affected by latency and CRC computation.
   Could be necessary to higher this value if devices are separated by long
   physical distance and or if transmitting long packets. */
#define PJON_INCLUDE_TS true // Include only ThroughSerial
#include <PJON.h>

bool sendv = false;
char randomletter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[random () % 26];
int i = 0,goodpacket = 0,badpacket = 0;

void receiver_function(uint8_t *payload, uint16_t length, const PJON_Packet_Info &packet_info) {
  /* Make use of the payload before sending something, the buffer where payload points to is
     overwritten when a new message is dispatched */
  i++;
if(payload[0] == randomletter ) goodpacket++;
else badpacket++;

  printf("recv : %c",payload[0] );
  printf(" number of packet : %d",i);
  printf(" good packet : %d",goodpacket);
  printf(" bad packet : %d\n",badpacket);
    fflush(stdout);
    sendv = true;
};

int main() {
  
  if(wiringPiSetup() == -1) printf("WiringPi setup fail");
  printf("PJON instantiation... \n");
  PJON<ThroughSerial> bus(45);
  uint32_t baud_rate = 9600;
  printf("Opening serial... \n");
  int s = serialOpen("/dev/serial0", baud_rate);
  if(int(s) < 0) printf("\n\nSerial open fail!\n\n");
  printf("Setting serial... \n");
  bus.strategy.set_serial(s);
  bus.strategy.set_baud_rate(baud_rate);
  bus.set_receiver(receiver_function);
  bus.set_synchronous_acknowledge(false);
  bus.strategy.set_enable_RS485_pin(1);
  printf("Opening bus... \n");
  bus.begin();
  printf("Attempting to send a packet... \n");
  printf("send : %c ",randomletter );
  bus.send(44, &randomletter, 1);
  bus.update();

  bus.receive();


  while(true) {
    if (sendv == true){
      randomletter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[random () % 26];
      sendv = false;
      printf("send : %c ",randomletter );
      bus.send(44, &randomletter, 1);

    }
    bus.update();
    bus.receive();
  }

  return 0;
};

For Arduino

#include <PJON.h>

// <Strategy name> bus(selected device id)
PJON<ThroughSerial> bus(44);

void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW); // Initialize LED 13 to be off
  pinMode(6, OUTPUT);
  Serial.begin(9600);
  bus.strategy.set_serial(&Serial);
  bus.begin();
  bus.strategy.set_enable_RS485_pin(6);
  bus.set_receiver(receiver_function);
  bus.set_synchronous_acknowledge(false);
};

void receiver_function(uint8_t *payload, uint16_t length, const PJON_Packet_Info &packet_info) {
  /* Make use of the payload before sending something, the buffer where payload points to is
     overwritten when a new message is dispatched */
    digitalWrite(13, HIGH);
    delay(30);
    digitalWrite(13, LOW);
    delay(100);
    bus.send(45, payload, 1); 
};

void loop() {
  bus.update();
  bus.receive();
};
@gioblu

This comment has been minimized.

Owner

gioblu commented Jun 12, 2018

Ciao @hyndruide thank you for the attached code I will test as soon as possible.
Looking back to your video I see your RPI seems a newer version, that may be the difference.
If data is not received by Arduino without that additional delay in RPI codebase it really may be required to higher the https://github.com/gioblu/PJON/blob/master/src/interfaces/RPI/PJON_RPI_Interface.h#L96 defining it before PJON.h inclusion.

@hyndruide

This comment has been minimized.

hyndruide commented Jun 13, 2018

Hi! the Arduino receive the data without a problem but it doesn't send back if I don't add a delay on txstart and txend. I don't add time to the raspberry pi.

@gioblu

This comment has been minimized.

Owner

gioblu commented Jun 15, 2018

Ciao @hyndruide thank you very much for your support. As you well described I have replicated the setup and the verified that the delay is required. Looking around in other libraries it seems a widely applied fix to this problem. I have added here the documentation for the use of the new setter set_RS485_delay. Thank you very much again.

I will go further testing the random character program you proposed.

@hyndruide

This comment has been minimized.

hyndruide commented Jun 25, 2018

hi! @gioblu, i have the same problem but a bit different i set set_RS485_delay to 5 and add a wait_RS485_pin_change(); to the end of startx();

but i can't send more than 1 character at time. do you have a code who works with a string arduino -> RPI i can try ?

Thx for your change :-)

@gioblu

This comment has been minimized.

Owner

gioblu commented Jun 26, 2018

Ciao @hyndruide I see you have also commented probably for mistake in an Arduino issue related to a ATmega serial bug I have reported, that should not be related to what you are reporting. I have taken some time to study the issue.

Strange, testing with my RPI I cannot reproduce the need of that delay, although it is clearly possibly needed there. It does not work without wait_RS485_pin_change at the end of starttx?

WiringPi flush method does not behave as expected and does not wait for all data to be transmitted.
@ekarlso contributed a hack to wait for data to be transmitted using a delay although it may be the cause of the issue.

I think It may be required to ditch the flush method of wiringPi and its related hack and use tcdrain lower level method although a small delay still may be required in some circumstances (if data is buffered in a following stage for example if using a usb hub). If you want to try it yourself edit here

https://github.com/gioblu/PJON/blob/master/src/interfaces/RPI/PJON_RPI_Interface.h#L94

the implementation of PJON_SERIAL_FLUSH using tcdrain(S) instead and commenting the whole preprocessor condition here

https://github.com/gioblu/PJON/blob/master/src/strategies/ThroughSerial/ThroughSerial.h#L199

I will make further testing too.

Was also previously determined that a longer TS_BYTE_TIME_OUT let longer strings go through on RPI see #203. For this reason its value was highered recently to 50 millis, is that the value you have locally? What happens using a higher value?

Also using a higher baudrate may help.
Thank you very much for your support, feel free to get in touch with me directly on gitter if required.

@fabpolli

This comment has been minimized.

Contributor

fabpolli commented Jul 25, 2018

I've tested TS with Raspberry Pi and an Arduino device, if on Rpi I run a dedicated program that read continuously the RS485 bus and sometimes put a message for other device on the bus work perfectly without and with syncronous ack.
But with a daemon that sleep for one second, check the bus and manage messages. In this case sync ack will fail and sender notify error 101, without sync ack bidirectional communication works fine

@gioblu

This comment has been minimized.

Owner

gioblu commented Jul 25, 2018

Thank you very much for your feedback @fabpolli !!! I will add to the known issues what you report.
Closing here for now, feel free to re-open if required.
Thanks again.

@gioblu gioblu closed this Jul 25, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment