pazucj
Posts: 14
Joined: Wed Dec 06, 2017 8:31 am

RS-485 communication Python

Fri Feb 02, 2018 8:35 am

Hello, I have trouble with serial programming with RPi 3 and FTDI, I wanted to check transmission and I used PySerial from Python. I have no idea what I do wrong. When I run program 1st time, and program doesn't show anything, stayed on loop. But when I run again it show me duplicate print x, never print y and after a few loops it fails and returns me error


Code: Select all

success=0
rsPin=22 #physical PIN 15 
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(rsPin,GPIO.OUT)
d1 = open_port("/dev/ttyUSB0")
d2 = open_port("/dev/ttyS0")
data = "data"
while True:
    GPIO.output(rsPin, GPIO.HIGH)
    x=d2.read(d2.inWaiting())
    time.sleep(1)
    d1.write(data)
    if x:
       print x
       success+=1
       GPIO.output(rxPin, GPIO.LOW)
       y = d1.read(d1.inWaiting())
       time.sleep(1)
       d2.write(x)
       if y:
          print y
          success+=1
          if y==data and success==2:
              print "success"
              exit(1)
          else:
              print "error"
              exit(1)
ERROR from terminal :

Code: Select all

d2.write(data)
File "/usr/lib/python2.7/dist-packages/serial/serialposix.py", line 554, in write
raise SerialException('write failed: {}'.format(v))
serial.serialutil.SerialException: write failed: [Errno 5] Input/output error
How I should set RTS to properly set direction of transmission? Is good practice to use setRTS from PySerial or using GPIO.output ? I asked on another forum and someone tell me that I should do not use a static adressing to " /dev/ttyS0 "

danjperron
Posts: 3620
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RS-485 communication Python

Fri Feb 02, 2018 5:57 pm

I found that the communnication using RS-485 with the Raspberry Pi is too slow to switch from transmit to receive mode using the build in serial.

I decide to use a small cpu to do the handling.

check my post about it!
viewtopic.php?f=44&t=75189#p538112

You should read all the posts!

shadetree01010100
Posts: 29
Joined: Thu Oct 12, 2017 9:19 pm
Location: CO, USA
Contact: Website

Re: RS-485 communication Python

Fri Feb 02, 2018 6:22 pm

I can hazard a guess, but without being able to play with your IO myself I'm really shooting in the dark: remember that x=d2.read(d2.inWaiting()) is a blocking call, so your program will wait here until some data is received. Furthermore, I believe that inWaiting is called before read, and will return 0 until some bytes are actually received, so in effect your method call is d2.read(0) the first time you run your program. From the documentation , I actually don't see anything to indicate the behavior of this call, but it's probably not what you want. Then, when you start the program a second time, inWaiting will actually return the number of bytes in buffer (that were not read on the previous run), and your read call will actually begin returning bytes.

http://pyserial.readthedocs.io/en/lates ... l_api.html

If d2 is going to be receiving discrete packets of known and consistent size, I would use that known size as your arg to read rather than InWaiting(). If packetsize is not known exactly, you can use an arbitrary number greater than maximum packet size and rely on a timeout to packetize your data. (if your data includes delimiters, you can read one byte at a time and construct packets in another function). You can specify an interval after which read will return whatever it's got (an empty byte field if nothing has been received), or set timeout to 0 to read in non-blocking mode, such as: s = serial.Serial('/dev/ttyUSB0', timeout=0') or whatever is going on in your open_port function.

I am much more confident that you should be using PySerial to control your RTS line, not GPIO. I'm guessing the IOError is being raised because you are not actually controlling the RTS line.

ps: use python3

pazucj
Posts: 14
Joined: Wed Dec 06, 2017 8:31 am

Re: RS-485 communication Python

Mon Feb 05, 2018 7:42 am

@shadetree01010100

U mean that I should use RS485 PySerial class? -> https://github.com/pyserial/pyserial/bl ... l/rs485.py
If it's true, have u some examples how to implement read from that class (I mean that I should change tx to rx and its all?). The next question is where and how I should implement serial options (ser = serial.Serial(port,baudrate....) ) ? Thank You for explain my problem ;)

shadetree01010100
Posts: 29
Joined: Thu Oct 12, 2017 9:19 pm
Location: CO, USA
Contact: Website

Re: RS-485 communication Python

Mon Feb 05, 2018 6:31 pm

I forgot about the rs485 class, yes I think it will make your life much easier, give it a try. And yes, that is how you set serial options, looks like you've got it!

danjperron
Posts: 3620
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RS-485 communication Python

Mon Feb 05, 2018 6:42 pm

Since you are using GPIO for RTS and now pyserial please set your rts0 using the proper GPIO.

https://ethertubes.com/raspberry-pi-rts ... w-control/


But like I said I had problem with the build in RTS and decide to use an hardware timer with a cpu to flip the signal direction.

pazucj
Posts: 14
Joined: Wed Dec 06, 2017 8:31 am

Re: RS-485 communication Python

Wed Feb 07, 2018 1:07 pm

@danjperron @shadetree01010100

I aborted RS485 class because I don't found any information where setRTS function is declaring and sets RTS PIN. But I make transmission with using serial.Serial class from PySerial.

Code: Select all

  serial1 = open_port("/dev/ttyUSB0") #USB device
    serial2 = open_port("/dev/ttyS0") #RPi 
    wrdata = "crazydata"
    while True:
        GPIO.output(rtsPIN, GPIO.LOW)
        time.sleep(1)
        received_data=serial2.read(10)
        time.sleep(1)
        serial1.write(wrdata)
        if received_data and received_data==wrdata:
            print received_data
            GPIO.output(rtsPin, GPIO.HIGH)
            time.sleep(1)
            sc_received_data=serial1.read(10)
            time.sleep(1)
            serial2.write(received_data)
            if sc_received_data:
                print "second transmission"
                print sc_received_data
                if sc_received_data==wrdata:
                    print "success"
            else:
                print "SOMETHING IS BAD"
                exit(1)
When I run program it returns me :

Code: Select all

    crazydata
    second transmission
    ����������
    SOMETHING IS BAD
Why received data at second time is trash?? How to fix it?

danjperron
Posts: 3620
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RS-485 communication Python

Wed Feb 07, 2018 3:18 pm

Wrong baud rate , data bit size, parity maybe ?

Normally on the rs-485 bus there is a pullup and a pulldown resistors to force the bus to be high ttl level on the cpu side when it is in tri-state mode.

Did you have your terminator resistor and the bias resistor on your bus?
You know that usb to rs-485 module are so cheap and handle very well the tx/rx.

http://www.dx.com/p/usb-to-rs485-conver ... pcs-429666


Maybe a schematic of your design will help. Is it a modbus device?

pazucj
Posts: 14
Joined: Wed Dec 06, 2017 8:31 am

Re: RS-485 communication Python

Thu Feb 08, 2018 6:57 am

Code: Select all

ser = serial.Serial(
                port=path,
                baudrate = 9600,
                stopbits=serial.STOPBITS_ONE,
                parity = serial.PARITY_NONE,
                bytesize=serial.EIGHTBITS,
                timeout=0,
                rtscts=False,
                dsrdtr=False
           )
Im using a FT232H from FTDI. On EEPROM im set CBUS0 to TXDEN (RX/TX transmission)

danjperron
Posts: 3620
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RS-485 communication Python

Thu Feb 08, 2018 12:30 pm

Ok it looks ok for the communication setup!

Now since you want to be stuck with a FTDI you will need to check the signal using a scope to see how it goes.

First what about the bias signal with one resistor on pull up and one on pull down to force the bus to be high level on tri-state? What about your terminator resistor ( 120 Ω typical) on both ends. some devices have it build in.


With a oscilloscope check the signal on transmit, in between and on receive! Check the receive clock to be at 9600 baud.


I thing Joan made a nice oscilloscope with the Pi long time ago. If you don't have a scope it could be an alternative.
http://abyz.me.uk/rpi/pigpio/piscope.html

Did you read my post about the RS-485 I builded and explained why I'm using a small cpu to do the handling?

The best choice will be to use a build in USB to RS-485. Right now you will have it working. and they are cheaper than your FTDI.

I.M.O. YOu could have two problem between the time you switch the signal to receive mode after transmitting the data.
- Problem #1 The transmitted data is not fully send because it goes to a memory buffer and the write command ust return very fast.

-Problem #2 By the time you switch the device to receive mode the external device already send the data and you lost the start of the communication. This is why you really need a scope to check the signal.

B.T.W. My little cpu (PIC12F1840) cost $2 US and you could program it using the raspberry PI.
https://github.com/danjperron/RS485switch
https://github.com/danjperron/burnLVP
Here I explain how to program the cpu using the PI. Is not really related but it is my multi-IO RS-485 device.
https://docs.google.com/document/d/1lXs ... sp=sharing

pazucj
Posts: 14
Joined: Wed Dec 06, 2017 8:31 am

Re: RS-485 communication Python

Thu Feb 08, 2018 12:37 pm

I can't use Yours CPU because I wanted to check transmission between RPi 3 and FTDI. It's only one option. I will check transmission in 9600 baud. I thought that it's working on one direction so should working in second direction ( received correct data from first read/write)

pazucj
Posts: 14
Joined: Wed Dec 06, 2017 8:31 am

Re: RS-485 communication Python

Fri Feb 09, 2018 11:53 am

@danjperron

I checked transmission via oscilator. RE and TE was on 1 signal. First data come properly but on second data wasn't send. I fixed program faults on 1st time run but still data doesn't receive.
I put on terminal for set PIN to alt3 RTS :

Code: Select all

gpio -g mode 17 alt3
Here is code :

Code: Select all

ser = serial.Serial(
                port=path,
                baudrate = 9600,
                stopbits=serial.STOPBITS_ONE,
                parity = serial.PARITY_NONE,
                bytesize=serial.EIGHTBITS,
                timeout=0,
                rtscts=True,
                xonxoff=False)
    ser.flushInput()
    ser.flushOutput()

serial2 = open_port("/dev/ttyUSB0")
serial1 = open_port("/dev/serial0")
command = "hello"
GPIO.output(rtsPin,GPIO.HIGH)
while True:
    time.sleep(0.1)
    first_data=serial2.read(8)
    n=serial1.write(command)
    if x and x==command:
        print(first_data)
        GPIO.output(rtsPin,GPIO.LOW)
        time.sleep(0.1)
        while True:
            second_data=serial1.read(n)
            serial2.write(first_data)
            time.sleep(0.1)
            print(second_data)
        if second_data:
            print("Second transmission")
            print(y)
            if y==command:
                print("Success")

danjperron
Posts: 3620
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RS-485 communication Python

Fri Feb 09, 2018 12:15 pm

OK serial1 /dev/serial0, serial2 /dev/ttyUSB0.

Could you post your full schematic!
I checked transmission via oscilator.
You mean an oscilloscope! then could you send picture on transmission, reception with data in, data out and the rts pin

pazucj
Posts: 14
Joined: Wed Dec 06, 2017 8:31 am

Re: RS-485 communication Python

Fri Feb 09, 2018 12:18 pm

danjperron wrote:
Fri Feb 09, 2018 12:15 pm
OK serial1 /dev/serial0, serial2 /dev/ttyUSB0.

Could you post your full schematic!
I checked transmission via oscilator.
You mean an oscilloscope! then could you send picture on transmission, reception with data in, data out and the rts pin
Now I don't have access to oscilloscope but it was that I said : Transmit is working only in one direction, after changed transmission device doesn't get any signal. Before probadly I send data via RX not TS, so there were some garbage data

/dev/serial0 is RPi and /dev/ttyUSB0 is FT232H. FT232H is looped to RPi via USB and RS485 ( I have connected RS485 converter which cables are :
RX to TX , TX to RX , RTS to pin 17, GND and D+ to 3v3)

danjperron
Posts: 3620
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RS-485 communication Python

Fri Feb 09, 2018 2:07 pm

Ok what about /dev/serial0 ? How did you connect it.

Maybe a small schema block with the parts number and how you are connected?

I'm at work right now. I will check it tonight (eastern time).

danjperron
Posts: 3620
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RS-485 communication Python

Sat Feb 10, 2018 5:06 am

Ok I added a TTL 3.3V RS-485 module into /dev/serial0 and use an USB Rs-485 dongle and connect them together.

http://www.dx.com/p/diy-3-3v-rs485-comm ... lue-152024
http://www.dx.com/p/usb-to-rs485-adapte ... een-296620

Since the RS-485 interface from pyserial doesn't work because of IOCTL error even if you toggle the GPIO for RTS and CTS it is quite simple to overwrite the class to use GPIO instead.


So I made two python applications which send a 'baton" like a relay race! The USB version first send the baton and the serial0 add one to it and send it back.

I did need to add a delay on my USB side because it was responding too fast!

P.S. start the /dev/serial0 first and then the USB one after.

This is the /dev/serial0 with the class overwrite to use GPIO for data direction instead of RTS

Code: Select all

#!/usr/bin/env python3
import RPi.GPIO as GPIO
import serial
import time

RTSGPIO = 23


class RS485(serial.Serial):

   def __init__(self,
             port=None,
             baudrate=9600,
             bytesize=serial.EIGHTBITS,
             parity=serial.PARITY_NONE,
             stopbits=serial.STOPBITS_ONE,
             timeout=None,
             xonxoff=False,
             rtscts=False,
             write_timeout=None,
             dsrdtr=False,
             inter_byte_timeout=None,
             PinGpioRTS=None,
             rts_level_for_tx=True,
             rts_level_for_rx=False,
             loopback=False,
             delay_before_tx=None,
             delay_before_rx=None,
             **kwargs):
        self.PinGpioRTS = PinGpioRTS
        self.rts_level_for_tx = rts_level_for_tx
        self.rts_level_for_rx = rts_level_for_rx
        self.loopback = loopback
        self.delay_before_tx = delay_before_tx
        self.delay_before_rx = delay_before_rx
        if PinGpioRTS is not None:
             GPIO.setmode(GPIO.BCM)
             GPIO.setwarnings(False)
             GPIO.setup(PinGpioRTS,GPIO.OUT)
             self.setGpioRTS(self.rts_level_for_rx)
        super().__init__(port=port,baudrate=baudrate,bytesize=bytesize,
                         parity=parity,stopbits=stopbits,timeout=timeout,
                         xonxoff=xonxoff,rtscts=rtscts,write_timeout=write_timeout,
                         dsrdtr=dsrdtr,inter_byte_timeout=inter_byte_timeout)

   def setGpioRTS(self, value):
       if self.PinGpioRTS is not None:
          GPIO.output(self.PinGpioRTS,value)

   def  write(self,b):
       if self.PinGpioRTS is not None:
            # apply level for TX and optional delay
            self.setGpioRTS(self.rts_level_for_tx)
            if self.delay_before_tx is not None:
                time.sleep(self.delay_before_tx)
            # write and wait for data to be written
            super().write(b)
            super().flush()
            # optional delay and apply level for RX
            if self.delay_before_rx is not None:
                time.sleep(self.delay_before_rx)
            self.setGpioRTS(self.rts_level_for_rx)
       else:
            super().write(b)




ser = RS485(port='/dev/serial0',baudrate=9600,timeout=1,PinGpioRTS=RTSGPIO)


ser.flushInput()




while True:

#wait for a transmission

     while  ser.inWaiting()==0:
       time.sleep(0.01)

     #read the data

     rcv_data=ser.readline()
     rcv_data_string = rcv_data.decode("utf-8")

     Count = int(rcv_data_string)
     Count = Count + 1 

     print(Count)
     # send the value + 1

     ser.write((str(Count)+ '\n').encode("utf-8"))


     if Count >= 1000 :
         break
And the Version for my usb to RS-485 which uses automatic switching build inside the dongle.

Code: Select all

#!/usr/bin/env python3


import serial
import time

#this is the master  it will send the first command

ser = serial.Serial(port='/dev/ttyUSB0', baudrate=9600,timeout=1)

baton  = 0
cycle  = 1

while True:

    print("writing baton={}".format(baton))
    ser.write((str(baton) + '\n').encode("utf-8"))
    # now wait for an answer

    while ser.inWaiting() ==0:
       time.sleep(0.001)

    rcv_string = ser.readline().decode("utf-8")

    print("Cycle={} baton={}".format(cycle,rcv_string))

    baton = int(rcv_string)

    cycle= cycle + 1

    #add delay to allow the data direction to change

    time.sleep(0.05)

    if baton >= 1000 :
       break
Last edited by danjperron on Sun Feb 11, 2018 2:54 am, edited 1 time in total.

danjperron
Posts: 3620
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RS-485 communication Python

Sat Feb 10, 2018 1:22 pm

About that delay I had to put on the ttyUSB0 to give enough time for the GPIO to return on received mode.

This was my frustrating part when I started with the raspberry pi on RS-485.

Why??

The protocol modbus specify 3.5 character length before swithing to received mode.

On my example from the last post I had to insert a 30 ms second delay which is way too long for modbus. I was loosing the response!!

(3.5 character at 9600, 8bits, 1 start, 1 stop) => (3.5 * 10)/9600 = 3.7 ms ! So 30 ms is way to slow!

The only methods to fix this problem was to use USB dongle with auto switch direction or create a small program on a cpu to handle the timing!

pazucj
Posts: 14
Joined: Wed Dec 06, 2017 8:31 am

Re: RS-485 communication Python

Wed Feb 14, 2018 12:39 pm

@danjperron

I have new information. After when I got loop in second transmission(data wasn't received) I closed the program and I got a short signal signal. WTF?

danjperron
Posts: 3620
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RS-485 communication Python

Wed Feb 14, 2018 1:13 pm

@pazucj

The only way to figure it out is to spy your bus with another receiver. If you have another Raspberry Pi you could just connect a MAX4385 or MAX485 i.c. , set it up in receive mode only, pin2 & 3 to ground , and connect pin 1 to the raspberry PI RX. After that use minicom or any serial monitor application to see what is going on.


P.S. With a max-485 you need to have a resistor divider since the MAX-485 run at 5V. 4k7/10K resistor divider is simple to add.



Code: Select all

                       4k7
  MAX-485 pin 1     -/\/\/\----+-----------    raspberry PI RX pin 10 
                               |      10K
                               L__ -/\/\/\----   GND
 
N.B. I still didn't get your schematic, This way I don't know if you have terminators on both ends. Does your system has pull up and pull down to bias the bus when everything is tri-state.
Did you ran my example? Did you install Joan pyscope to look at the signal? I don't have a crystal ball!

Return to “Python”