|
|
|
|
RS485 Gateway Sketch
| ||
| Back To Gateway |
I originally built the gateway on an Rpi-3b+ but had problems with RS-232 Port stability (caused by reboots after power fail), so after a number of times manually restoring RS232 from Rpi to Env server, I decided to try something else.
BTW, I really liked being able to ssh to the Rpi in the shop.
RS-232 requires that each device have a dedicated UART for each host it communicats with.
I thought about a RS-232 multiplexed scheme but decided to try RS-485, a daisy chain arrangement with one master controller.
When using RS-485 each host only needs one interface for communicating with all other devices.
The RS-485 interface (MAX485) is driven by a standard UART.
Ethernet:
I'm going to need an ethernet interface to an Arduino.
I really liked the Nano ENC28J60, becasue it simplified mounting, but it's library took up too much dynamic ram and I didn't have enough for my variables!
I tested several others with W5100 and W5500 chips and they worked great and left dynamic ram for me.
The only down side of these is they have to have their own mount.
Arduino Ethernet devices run on 3.3V but they are fed from 5v and each has it's own 3 terminal regulator.
/*H******************************************************
* ORIGINAL GATEWAY SKETCH
* EtherCard LIB FOR NANO ETHERNET SHIELD FROM RBBB_Server
********************************************************/
#include <SPI.h>
#include <Ethernet.h> // NANO ETH
#include <AltSoftSerial.h>
// ********************* DEFINES *************************
#define COMRX 8 // RS 485 RX PIN
#define COMTX 9 // RS 485 TX
#define ENATX 7 // COM WT ENABLE PIN
#define ERR -1
#define OK 1
#define NOTFND 0
#define ETHPIN 10 // 10:CS, 11:SI, 12:SO, 13:SCK
#define STRMAX 100
#define SLVWAIT 250
#define NXTPOLLTM 25 // POLL EVERY xx MS
#define NL 10
#define ENQ 05 // QUERY
#define ACK 06 // + ACKNOWLEDGE
#define NAK 21 // - ACKNOWLEDGE
#define SYN 22 // SYNC
#define MAXVCTRS 4
#define COMBAUD 57600 // 9600, 14400, 19200, 28800
#define SERBAUD 9600 // 31250, 38400, 57600, 115200
#define BUFSIZ 512
typedef unsigned long ulong;
// ********************* PROTOTYPES *************************
char *zotChr( char *str, char chr );
int fndSlv( char *str ); // RTRNS SLV#
int rdNet( EthernetClient Clnt );
int wtNet( EthernetClient Clnt, char *cp );
int getNetLen( char *cp );
int wtSlv( int slvNbr, char *str ); // STR POINTS TO MSG FOR SLAVE
int rdSlv( int slvNbr );
int getSLvLen( char *cp );
void poll();
// ********************* VARIABLES *************************
AltSoftSerial Com( COMRX, COMTX );
const char *Vctrs[] = { NULL,"EN","DA","AC","DC" };
char IoBuf[BUFSIZ];
byte Mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x78, 0xEE };
IPAddress Ip( 192, 168, 0, 40 ); // NANO.0
IPAddress Dns(192, 168, 0, 2 ); // LIN1
IPAddress GateWay( 192, 168, 0, 2 ); // GATEWAY
IPAddress NetMask( 255, 255, 255, 0 ); // NETMASK
EthernetServer Srv( 4001 ); // CREATE SERVER
EthernetClient Clnt;
int SlvNbr =0;
ulong NxtPoll;
/*I*******************************************************
* REQUEST FORM Net: "00040000ENV\n" or "00110000AC>Barfity\n"
* MSG FWD TO SLAVE: "#01ENV>args\n" "01" = slv\n#, '>' ONLY IF ARGS FOLLOW
* MSG Rtnd FROM SLAVE: "#01TI=72.50,HI=31.00,TE=72.86,HE=31.50\n"
* RESPONSE TO Net: "00360000TI=72.50,HI=31.00,TE=72.86,HE=31.50\n"
* POLLSLV: "#ssENQ\n" ASCII ENQ IS POLL MSG
* POLLANS: "#ssNAK\n" or "#ssACK>DestHostMsg\n" (DEST HOST FMT: TBA)
* ANNOUNCE SLAVE ON 485 NET: "#ssSYN\n"
* RTN: IN-BOUND MSG LEN
*********************************************************/
/*F****************************************************
*
*******************************************************/
void
setup()
{
Serial.begin( SERBAUD );
Com.begin( COMBAUD ); // AltSoftSerial Init
pinMode( ETHPIN, OUTPUT );
pinMode( ENATX, OUTPUT );
digitalWrite( ENATX, false ); // ENABLE RS485 TRANSMISSION
Serial.println("Init Ethernet");
Ethernet.init( ETHPIN ); // USE PIN 10 AS ETHERNET CSEL
Ethernet.begin( Mac, Ip, GateWay, NetMask); // INITIALIZE ETHERNET
if( Ethernet.hardwareStatus() == EthernetNoHardware)
{ // ETHERNET HARDWARE NOT PRESENT
Serial.println( "Ethernet Iface not found" );
while( 1 );
}
if( Ethernet.linkStatus() == LinkOFF)
Serial.println("Ethernet cable is not connected");
Srv.begin();
NxtPoll = millis() + NXTPOLLTM;
delay( 250 );
}
/*F****************************************************
*
*******************************************************/
void
loop()
{
int slv, len;
if( EthernetClient Clnt = Srv.available() )
{
rdNet( Clnt );
SlvNbr = fndSlv( IoBuf );
wtSlv( SlvNbr, IoBuf );
if( (len = rdSlv( SlvNbr )) > 0 )
wtNet( Clnt, IoBuf );
}
// if( millis() >= NXTPOLLTM )
// poll();
// while( 1 ); // DEBUG: WHOA RIGHT HERE
}
/*F****************************************************
* RECEIVE A TCP MESSAGE FROM NETWORK
*******************************************************/
int
rdNet( EthernetClient Clnt )
{
int ioNdx, hdrNdx, len, cnt;
ulong end;
char chr, buf[16];
if( Clnt )
{
end = millis() + SLVWAIT;
while( Clnt.connected() && (millis() < end) )
{ // HAVE AN ETHCONN
for( cnt =ioNdx =hdrNdx =0; Clnt.available() && (millis() < end)
; cnt++ )
{
chr = Clnt.read(); // READ REQ FROM Net, CHR BY CHR
switch( cnt )
{
case 0 ... 3: // CAPTURE MSG LEN FROM ENET HDR
buf[hdrNdx++] = chr;
break;
case 4: // READ LEN COMPLETE
buf[4] = 0;
len = atoi( buf );
break;
case 5 ... 7: // SKIP REMAINING HDR WORD
break;
case 8:
buf[7] =0;
default:
if( chr == NL )
{ // END OF REQUEST
IoBuf[ioNdx] = 0; // TERMINATE RCV STR
return( len );
} // END OF FOUND NL
IoBuf[ioNdx++] = chr; // PLACE NEW CHAR INTO IOBUF
}
} // END OF 'FOR AVAILABLE
} // END OF 'WHILE CONNECTED'
return( NOTFND );
} // END OF 'IF CLNT'
return( NOTFND );
}
/*F******************************************************************
* WRITE SLV RESPONSE TO ETHERNET REQUESTOR
**********************************************************************/
int
wtNet( EthernetClient Clnt, char *str ) // STR POINTS TO MSG FOR SLAVE
{
int len;
char chr, *cp, hdr[16];
cp = IoBuf; // SKIP SLV 485 HDR FOR RESPONSE
len = strlen( cp );
sprintf( hdr, "%04d0000", len ); // FMT SLAVE HDR
Clnt.write( hdr, 8 );
Clnt.write( IoBuf, len );
return( OK );
}
/*F******************************************************************
* RCV MSG FROM SLAVE
**********************************************************************/
int
rdSlv( int slvNbr ) // *STR POINTS TO RCV BUFF
{
int ioNdx, adrNdx, slv, cnt, len;
char chr, addr[8], buf[128];
ulong end;
Com.listen();
end = millis() + 250; // SET TIMEOUT
for( cnt =adrNdx =ioNdx =0; (millis() < end) ; )
{ // READ CHARS FROM SLAVE
if( Com.available() )
{
chr = Com.read();
switch( cnt )
{
case 0: // READ SLAVE MSG MARK
if( chr != '#' )
return( NOTFND ); // NOT A VALID SLV MSG TO ME
cnt++;
break;
case 1 ... 2:
addr[adrNdx++] = chr; // READ SLAVE MSG LEN
cnt++;
break;
default:
IoBuf[ioNdx++] = chr; // PUT NEW CHAR IN IOBUF, BUMP NDX
cnt++;
if( chr == NL )
{ // FOUND NL
IoBuf[ioNdx] =0; // TERMINATE MSG FROM SLV, RMV NL
addr[2] =0; // TERMINATE SLV NBR
slv = atoi( addr );
if( (slv != slvNbr) || !cnt )
return( NOTFND ); // NOT MSG FOR ME, DISCARD IT
return( strlen( IoBuf ) ); // RETURN DATA LEN OK
}
} // END OF SWITCH
} // END OF IF AVAIL
} // END OF FOR
return( NOTFND ); // TIMEOUT
}
/*F******************************************************************
* FWD REQUEST (IN IOBUF) TO SLAVE
**********************************************************************/
int
wtSlv( int slvNbr, char *str ) // STR POINTS TO MSG FOR SLAVE
{
char chr, *cp, hdr[8];
Com.listen();
digitalWrite( ENATX, true ); // TURN ON 485 WRITE ENABLE
sprintf( hdr, "#%02d", slvNbr ); // FMT SLAVE HDR
Com.print( hdr );
Com.print( IoBuf );
Com.print( "\n" ); // I DON'T LIKE PRINTLN() \r\n
digitalWrite( ENATX, false ); // TURN OFF 485 WRITE ENABLE
return( OK );
}
/*F******************************************************************
* ZOT A CHAR IN A CHAR ARRAY, RTRN: &NEXT CHAR
**********************************************************************/
char*
zotChr( char *str, char chr )
{
char *cp;
if( (cp = strchr( str, chr )))
*cp++ = 0; // ZOT CHAR (REPL WITH ZERO)
return( cp );
}
/*F******************************************************************
* FIND SLAVE NBR BY MATCHING MSG DEST ADDR
**********************************************************************/
int
fndSlv( char *str )
{
int ndx;
char buf[8];
strncpy( buf, str, 2 );
buf[2] = 0; // TERMINATE STRING
for( ndx =1; ndx <= MAXVCTRS; ndx++ )
{
if( !strncmp( buf, Vctrs[ndx], 2 ))
return( ndx );
}
return( NOTFND );
}
/*F******************************************************************
* POLL SLAVES
**********************************************************************/
int
poll( char *str )
{
int slv;
char hdr[16];
ulong timOt;
for( slv =0; slv < MAXVCTRS ; slv++ )
{
sprintf( hdr, "#%02d\n", slv );
wtSlv( slv, hdr );
}
NxtPoll = millis() + NXTPOLLTM;
}