RS485

The Pi-Tron has an RS485 interface connected to uart0. In order to use uart0 the following adjustments have to be made.

Baud rate: 115200

1. Update Raspberry Pi OS

pi@raspberry:~ $ sudo apt-get udpate
pi@raspberry:~ $ sudo apt-get dist-upgrade

2. Edit the config.txt

pi@raspberry:~ $ sudo nano /boot/config.txt

Enter the following at the end of the file:

### Serial interface
dtoverlay=uart0,txd0_pin=32,rxd0_pin=33,pin_func=7       ## RS485 on UART0

3. UART and Serial device assignments

The following table shows the relation between the UART, tty and serial devices on the Pi-Tron.

Pi-Tron Used as Accessible via Connector
uart0 Console; TTL; RS485 /dev/ttyAMA0, /dev/serial0 X800
uart1 Console; TTL; RS485; RS232 /dev/ttyS0, /dev/serial1 X900

Send and Receive mode: The send and receive direction of the RS485 chip can be controlled through GPIO 30. The GPIO has to be configured as output and setting it to 0 or Low switches the chip to receive mode, whereas setting it to 1 or High switches the chip to send mode.


After the activation of a UART on the Pi-Tron, the relation between serial device and tty device can be looked up in the /dev folder of the OS. The following command shows which serial device points to which tty device.

pi@pitron:~ $ ls -la /dev/ser*
lrwxrwxrwx 1 root root 7 Feb 14  2019 /dev/serial0 -> ttyAMA0
lrwxrwxrwx 1 root root 5 Feb 14  2019 /dev/serial1 -> ttyS0

In the above listing both Pi-Tron serial devices have been activated. If only one UART device is on then this command will only produce one line as a result or if no UARTs are active, an error will be shown.

Note: After activating a serial device on the Pi-Tron, there could still be an operating system console active on the serial device. To make sure the serial device can be used exclusively by the user, the cmdline.txt file in the /boot folder has to be checked.

pi@pitron:~ $ sudo nano /boot/cmdline.txt

Look for the part which reads console=serial0,115200 or console=serial1,115200
and delete it from the cmdline.txt file if it is present. Make sure to not insert any line breaks, all the information in the cmdline.txt has to be on one single line.

Example:
Before, the system console is active on serial0:

dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=43c1e3bb-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles

After, the console is now deactivated on all serial devices:

dwc_otg.lpm_enable=0 console=tty1 root=PARTUUID=43c1e3bb-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles

4. Further reading

For more information about the Raspberry Pi serial devices, visit the Raspberry Pi website here: https://www.raspberrypi.org/documentation/computers/configuration.html#configuring-uarts


5. Examples

The following example shows how to write and read data from the RS485 interface using ttyAMA0 (other names: serial0 or uart0). We use the GPIO 30 to switch between send and receive mode of the RS485 chip. The example was made for Python 3 using pySerial 3.4 (sudo pip3 install pyserial==3.4).

#!/usr/bin/python3

import serial, time
import RPi.GPIO as GPIO

# Set GPIO configuration for GPIO 30, we need output
# GPIO 30 switches between send and receive mode of the
# RS485 chip. HIGH = send, LOW = recieve
GPIO.setmode(GPIO.BCM)
GPIO.setup(30, GPIO.OUT)
GPIO.output(30, GPIO.HIGH)

# Create serial object and set configuration
ser = serial.Serial()
ser.port = "/dev/ttyAMA0"
ser.baudrate = 115200
ser.bytesize = serial.EIGHTBITS
ser.parity = serial.PARITY_NONE
ser.stopbits = serial.STOPBITS_ONE
#Set timeout to None, blocking read
ser.timeout = None          
ser.writeTimeout = 2

# Open serial device....
try:
    ser.open()
except Exception as e:
    print ("Error while opening the serial port: " + str(e))
    exit()

# When the serial port is open, we can communicate
if ser.isOpen():
    try:
        # Empty input buffer on start
        ser.flushInput()
        # Empty and delete out going data
        ser.flushOutput()
        # Wait
        time.sleep(0.1)
        # Variable to hold how many lines of data we wrote and received
        numOfLines = 0
        while True:
            # Write a "Hello World!" to the serial device
            ser.write("Hellow World!\n".encode())
            # Flush out the data in the buffer
            ser.flush()
            # Print something to the consle...
            print("Write data! " + str(numOfLines))
            # Count lines written .. and received...
            numOfLines = numOfLines + 1
            # Set GPIO 30 to low, meaning receive data on RS485
            GPIO.output(30, GPIO.LOW)
            # Sleep
            time.sleep(0.01) 
            # Try to read from the serial port until LF ir \n is received
            try:
                # Wait for data, blocking call, as timeout is set to None
                myvar = ser.read_until() # Wait for LF
            except:
                # If wrong data is received or status of serial port not ready...
                myvar = b'Error receiving!'

            # Print recieved data to console...
            print("Read data! " + myvar.decode('ascii'))
            # If we have written 50 time the line "Hello World!" to the 
            # serial port and also received something, leave the program
            if (numOfLines >= 50):
                break
            # Change the GPIO 30 back to high, write data 
            GPIO.output(30, GPIO.HIGH)
            # Wait some time...
            time.sleep(0.05)
        # When we are done, close the serial port and leave the program...
        ser.close()
    except Exception as e1:
        # If there is a problem while communicating...
        print ("Error while communicating....:" + str(e1))

else:
    #Something went wrong while opening the serial port.
    print ("Cannot open serial port!")

The following two examples are written in C and use the WiringPi library to facilitate the communication over a serial device, which is in this case /dev/ttyAMA0. The first program just writes one string to the RS485 and then quits. The second program just receives characters and prints them to the console.

First we have to install the WiringPi library:

sudo apt-get update
sudo apt-get install git-core
cd ~
git clone https://github.com/WiringPi/WiringPi.git
cd WiringPi
./build
gpio -v

Executing "gpio -v" prints the current WiringPi version to the console as well as the Raspberry Pi model. The WiringPi library should report version 2.52 or later.

The programs below can be compiled like this:

gcc -o rs485_receive rs485_receive.c -lwiringPi

In this case the source code file is named "rs485_receive.c", but you can simply substitute that with your filename. The parameter "-o" for output allows you to name the compiled program which can be different from the name of the source code file if you choose so.


Send data via RS485:

#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/types.h>
#include <inttypes.h>
#include <wiringPi.h>
#include <softPwm.h>
#include <wiringPiSPI.h>
#include <wiringSerial.h>

int fd;
int pin_rs485_dir = 30;
char strDevice[256] = "/dev/ttyAMA0";
char strToSend[256] = "Hello World!";
// We are using a baud rate of 19200
int  baudRate = 19200;

int main (void)
{
    int chr;
    int nRet;    

    //Setup
    wiringPiSetup();
    //Set GPIO 30 as output
    pinMode(pin_rs485_dir, OUTPUT);
    //Switch RS485 to send
    digitalWrite(pin_rs485_dir,1);
    // Opening the serial device, we just supply the baud rate,
    // WiringPi does the rest for us and sets 8 databits and no parity
    fd = serialOpen(strDevice,baudRate);
    if(fd==-1)
    {
        printf("Error: Couldn't open serial device\n");
    }
    else
    {
        printf("Opened serial device [fd=%d]\n",fd);
        // Write our string to the serial device and thus to the RS485 chip
        serialPuts(fd, strToSend);
        printf("put string %s\n",strToSend);
        serialClose(fd);
        printf("Closed serial device\n");
    }
}


Receive data via RS485:

#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/types.h>
#include <inttypes.h>
#include <wiringPi.h>
#include <softPwm.h>
#include <wiringPiSPI.h>
#include <wiringSerial.h>

int fd;
int pin_rs485_dir = 30;
char strDevice[256] = "/dev/ttyAMA0";
// We are using a baud rate of 19200
int  baudRate = 19200;

int main (void)
{
    int chr;
    int nRet;    

    // Setup
    wiringPiSetup();
    // Set GPIO 30 as output
    pinMode(pin_rs485_dir, OUTPUT);
    // Switch RS485 to receive
    digitalWrite(pin_rs485_dir,0);
    // Opening the serial device, we just supply the baud rate,
    // WiringPi does the rest for us and sets 8 databits and no parity
    fd = serialOpen(strDevice,baudRate);
    if(fd==-1)
    {
        printf("Error: Couldn't open serial device\n");
    }
    else
    {
        printf("Opened serial device [fd=%d]\n",fd);
        printf("Recv: ");
        fflush(stdout);
        while(1)
        {
            // Check to see if we received data
            nRet = serialDataAvail(fd);
            if (nRet>=0)
            {
                // Get data from the buffer
                chr = serialGetchar(fd);
                if(chr!=-1)
                {
                    // Print it to the console
                    printf("%c",chr);
                    fflush(stdout);
                }
            }
            else
            {
                printf("Error: serialDataAvail=-1\n");
                serialFlush(fd);
                printf("Flushed serial device\n");
                serialClose(fd);
                printf("Closed serial device\n");
                return 0;
            }

        }
    }
}