![]() | ![]() | ![]() |
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; }