RS485 Relay Raspberry pi

 
 


Download Build manual Build Manual.pdf


After building up the Iso RS485/422 adapter, I figured I needed to do a project that used RS485 loopback tests can only keep you amused for so long... So here is an RS485 relay controller using the PIC16F628A that can be controlled using serial commands from cron to switch devices on and off at predetermined time

Hardware


The hardware design is shown in the schematic below:-























Power is supplied via the the connector J1 and into a 7805 linear regulator to supply 5V to the circuit and the PIC micro controller. The PIC16F628A is run off an 1.8432 Mhz crystal in order to give direct integer divider for baud rates.


The RS485 node address is set via a 5 way DIP switch DSW1 that is pulled up internally by port B this gives 32 possible addresses . A standard RS485 converter is connected to the UART pins and direction is controlled by the DE/RE signal.


At the last node on the network then J3 should be fitted to terminate the RS485 network. The Relay is driven by the  RLY1 signal via Q1.


The layout is on a two layer PCB that fits into an open

enclosure that can be fitted on a DIN rail module

from pheonix contact shown here


http://www.farnell.com/datasheets/99364.pdf


This allows multiple PCB’s to be put in one enclosure.



The bill of materials for this project is


QTY      PART-REFS           VALUE              

              

Resistors

---------

2            R1,R2                     1k (May need to reduce to 560 for the Relay drive)                 

1            R3                          10k                

1            R4                          120                

 

Capacitors

----------

2            C1,C2                     220p               

2            C3,C4                     47uF               

 

Integrated Circuits

-------------------

1            U1                          PIC16F628A         

1            U2                          78L05              

1            U3                          MAX487             

 

Transistors

-----------

1            Q1                          BC549BP            

 

Diodes

------

2            D1,D3                   1N4001              

1            D2                         LED                

 

Miscellaneous

-------------

1            DSW1                    DIPSW_5            

1            J1                          TBLOCK-I5          

1            J2                          TBLOCK-I3          

1            J3                          CONN-H2             

1            RL1                       G5CLE-1-DC5 


The Protocol


For this initial network we’ll use a simple ASCII proprietary protocol as follows each command is 5 characters structured as below:



                                        $AACMx


            $ is the start of packet character

            AA is the address in ASCII for example $01 would access node 1

            CM is the command initially 2 commands are supported


                         ON= Turns relay on

                         OF= Turns relay off

   

            x is the number of the relay on the board


So to turn on the relay at address 22 the command would be $22ON1 and to turn it off would be $22OF1 note no return character is sent after the command.


The code is set up to run at 19K2 baud 8N1 as default.



The PIC code


The code is written in Hitech C and uses the onboard USART with an RX interrupt routine. The code now includes timeout and corrects a previous error where I had config set up to run from the internal oscillator rather than external oscillator.


The Address is read from the external DIP switch


/**********************************************************

*RS485 Node Software

*Copyright (c) Nigel Brooke 2013

*Processor=16F628A

*Crystal Frequency = 1.8432 MHz 

***********************************************************/


#include <stdio.h>

#include <htc.h>

#include "string.h"

#include <pic16f628a.h>


#define DE RB0

#define RELAY RA0

#define _XTAL_FREQ 1843200

#define SER_BUFFER_SIZE         8


#define RX_PIN TRISB1

#define TX_PIN TRISB2

#if NINE == 1

#define NINE_BITS 0x40

#else

#define NINE_BITS 0

#endif

#define SPEED 0x4


               

#define SER_FIFO_MASK            (SER_BUFFER_SIZE-1)


__CONFIG (0x3D21); //Note make sure to check this is set up for XT Oscillator


const char* Relayon[]=

{

"$00ON1","$01ON1","$02ON1","$03ON1","$04ON1","$05ON1","$06ON1","$07ON1","$08ON1","$09ON1","$10ON1","$11ON1","$12ON1","$13ON1","$14ON1","$15ON1","$16ON1","$17ON1","$18ON1","$19ON1","$20ON1","$21ON1","$22ON1","$23ON1","$24ON1","$25ON1","$26ON1","$27ON1","$28ON1","$29ON1","$30ON1","$31ON1","$32ON1",

};



const char* Relayoff[]=

{

"$00OF1","$01OF1","$02OF1","$03OF1","$04OF1","$05OF1","$06OF1","$07OF1","$08OF1","$09OF1","$10OF1","$11OF1","$12OF1","$13OF1","$14OF1","$15OF1","$16OF1","$17OF1","$18OF1","$19OF1","$20OF1","$21OF1","$22OF1","$23OF1","$24OF1","$25OF1","$26OF1","$27OF1","$28OF1","$29OF1","$30OF1","$31OF1","$32OF1",

};



unsigned char rxfifo[SER_BUFFER_SIZE];

volatile unsigned char rxiptr, rxoptr, Addptr,Timeout;

unsigned char input;


void interrupt ISR()

{


        if (T0IF)

                {

                        Timeout--;

                                if(Timeout == 0)

                                        {

                                        Timeout = 8;

                                        rxiptr = 0;

                                        }

                T0IF = 0;

                TMR0=0;

                }


        while(RCIF)

        {

                TMR0 = 0;

                Timeout = 8; //Set for around 1 second timeout

                input = getch();        // read a response from the user*/

                if (input == '$')       //Look for the beginning of packet

                        {

                                rxiptr = 0;     //If the beginning then reset the pointer

                        }

                rxfifo[rxiptr] = input;

                rxiptr++;

        }


        if(OERR)

                {

                        CREN = 0;

                        CREN = 1;

                }


}


init_comms()

        {

        RX_PIN = 1;    

        TX_PIN = 1;              

        SPBRG = 5;      

        RCSTA = (NINE_BITS|0x90);      

        TXSTA = (SPEED|NINE_BITS|0x20);

        }



void main(void){


/*Set up Ports*/


OPTION_REG = 0b01010111;

CMCON=0B00000111;


TRISA=0B11111110;

TRISB=0B11111110;




        RCIE = 1;

        T0IE = 1;   /* Enable peripheral interrupts */

        PSA = 0;

    PEIE = 1;

        GIE = 1;    /* Enable global interrupts */

        TRISA=0B11110000; //Setup Port A Registers

        TRISB=0B11111110; //Setup Port B Registers

        RELAY = 0;      //Set Relay to Off

        init_comms();   // set up the USART - settings defined in usart.h

        DE = 0;

        rxiptr = 0;

        Timeout = 8;

        while(1){

        Addptr = PORTB >>3 &0b00011111;

        if (rxiptr == 6) //wait until 6 characters have been received into buffer

                {

                        if(strncmp(rxfifo,Relayon[Addptr],6) == 0)

                        {

                                RELAY = 1;

                        }


                        else if (strncmp(rxfifo,Relayoff[Addptr],6) == 0)

                        {

                                RELAY = 0;

                        }



                 rxiptr = 0;

                }

        }

}


Running from the Raspberry pi


With a simple piece of python code below the relay can be switched on at the adress set by the DIP switch. To switch the relay off just change the command to OF.


If these are written  as separate functions then these can be called from CRON to make a simple scheduler.


#!/user/bin/env python

import serial

import datetime

from time import sleep

ser = serial.Serial("/dev/ttyAMA0",baudrate=19200,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE,bytesize=serial.EIGHTBITS )

ser.open

ser.write('$')

sleep(0.1)

ser.write('2')

sleep(0.1)

ser.write('4')

sleep(0.1)

ser.write('O')

sleep(0.1)

ser.write('N')

sleep(0.1)

ser.write('1')

sleep(0.1)



print "Relay On"

        

ser.close()


Running from Crontab


Now we have a program to switch the relay on and off we can set up a crontab schedule to automate the relays to switch on and off at various times during the day.


To edit the crontab first make sure it is installed apt-get install crontab


Then to edit the crontab type crontab -e

at the terminal window.


For example the command


01 22 * * * python /home/pi/Relayon1.py


will switch on relay 1 at 22:01 every day and the command


40 23 * * * python /home/pi/Relayoff1.py


will turn the same relay off at 23:40 each day.


Below is an example of my current crontab


# m h  dom mon dow   command

15 15 * * * python /home/pi/Relayon1.py

20 15 * * * python /home/pi/Relayoff1.py

40 23 * * * python /home/pi/Relayoff1.py

01 22 * * * python /home/pi/Relayon1.py

30 6 * * * python /home/pi/Relayon1.py

30 7 * * * python /home/pi/Relayoff1.py

36 15 * * * python /home/pi/Relayon2.py

56 15 * * * python /home/pi/Relayoff2.py    



 

Connecting up a RS485 networked relay to the raspberry pi