Library to interact with RS485 serial.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

204 lines
5.4 KiB

/*
Library for Software Serial over RS485 connection
Created by Stepan Richter, November 2023
*/
#include "Arduino.h"
#include "SoftRS485.h"
//#define DEBUG
#define MSG_LEN 64
#define IDLE 0
#define START 1
#define RECEIVING 2
#define SENDING 3
#define COMPLETE 4
// Pins
int _RO, _RE, _DE, _DI;
int _idle_cycles = 0;
/* INCOMING */
char _recv_message[MSG_LEN];
byte _recv_byte = 0x00;
byte _recv_state = IDLE;
boolean _recv_bit = false;
byte _recv_idx = 0;
int _recv_pos = 0;
/* OUTGOING */
char _send_message[MSG_LEN];
boolean _send_bit = false;
byte _send_state = IDLE;
byte _send_wait = 0;
byte _send_idx = 0;
int _send_pos = 0;
ISR(TIMER1_COMPA_vect){
OCR1A += 3228; // Advance The COMPA Register
if (_send_state == SENDING){
char c = _send_message[_send_pos];
_send_bit = c & 1<<_send_idx;
digitalWrite(_DI,!_send_bit);
#ifdef DEBUG
Serial.print("Sending bit "); Serial.print(_send_idx); Serial.print(" of "); Serial.print(c); Serial.print(": "); Serial.print(_send_bit); Serial.print(" / ");
#endif
_send_idx++;
if (_send_idx==8){ // byte completed
_send_pos++; // advance to next byte
_send_idx = 0; // start with first bit
if (c == 0x00 || _send_idx == MSG_LEN){ // we just send a 0-byte
_send_state = COMPLETE;
}
}
}
if (_send_state == START && _idle_cycles > 9){
_send_bit = HIGH;
digitalWrite(_DE,HIGH);
digitalWrite(_DI,!_send_bit);
_send_state = SENDING;
_send_idx = 0;
_send_pos = 0;
#ifdef DEBUG
Serial.print("Sent start bit: "); Serial.print(_send_bit); Serial.print(" / ");
#endif
}
_recv_bit = !digitalRead(_RO); // LOW = 1
if (_recv_bit) {
_idle_cycles = 0;
} else if (_idle_cycles<1000) {
_idle_cycles++;
}
#ifdef DEBUG
Serial.print("idle cycles: "); Serial.print(_idle_cycles); Serial.print(" / ");
Serial.print("received: "); Serial.print(_recv_bit); Serial.print(" / ");
#endif
if (_send_bit != _recv_bit && _send_state == SENDING){ // collision while sending
digitalWrite(_DE,LOW); // disable driver
_send_state = START; // start over
_idle_cycles = 0;
#ifdef DEBUG
Serial.println("\nCOLLISION");
#endif
}
if (_send_state == COMPLETE){
digitalWrite(_DE,LOW); // disable driver
_send_message[0]=0x00; // clear message
_send_state = IDLE;
_idle_cycles =0;
#ifdef DEBUG
Serial.print("transmission complete / ");
#endif
}
if (_recv_state == START){
_recv_byte = 0x00;
_recv_pos = 0;
_recv_idx = 0;
_recv_state = RECEIVING;
#ifdef DEBUG
Serial.print("Start reception / ");
#endif
}
if (_recv_bit && _recv_state == IDLE){
_recv_state = START;
}
if (_recv_state == RECEIVING){
_recv_byte |= _recv_bit << _recv_idx; // set bit
_recv_idx++; // increase bit index
if (_recv_idx == 8){ // byte full
#ifdef DEBUG
Serial.print("pushing "); Serial.print((char)_recv_byte); Serial.print(" / ");
#endif
_recv_message[_recv_pos] = _recv_byte; // push character to string
if (_recv_byte == 0x00) { // end of message
_recv_state = IDLE;
#ifdef DEBUG
Serial.print("reception complete: "); Serial.print(_recv_message);
#endif
}
_recv_idx=0; // start new byte
_recv_byte = 0;
_recv_pos++; // goto next string pos
if (_recv_pos == MSG_LEN) _recv_pos--; // avoid overflow
}
}
#ifdef DEBUG
Serial.println();
#endif
}
void init485(int RO, int nRE, int DE, int DI){
_RO=RO;
_RE=nRE;
_DE=DE;
_DI=DI;
_recv_message[0] = 0x00; // empty
_send_message[0] = 0x00; // empty
pinMode(_RO,INPUT);
pinMode(_RE,OUTPUT);
digitalWrite(_RE,LOW); // enable reading
pinMode(_DE,OUTPUT);
digitalWrite(_DE,LOW); // disable writing
pinMode(_DI,OUTPUT);
digitalWrite(_DI,LOW); // output line = LOW
TCCR1A = 0; // Init Timer1A
TCCR1B = 0; // Init Timer1B
TCCR1B |= B00000001; // Prescaler = 1
OCR1A = 3328; // Timer Compare1A Register
TIMSK1 |= B00000010; // Enable Timer COMPA Interrupt
#ifdef DEBUG
Serial.begin(115200);
Serial.println("Started timer!");
Serial.print("RO = ");
Serial.println(_RO);
Serial.print("^RE = ");
Serial.println(_RE);
Serial.print("DE = ");
Serial.println(_DE);
Serial.print("DI = ");
Serial.println(_DI);
#endif
}
boolean available485(){
return _recv_state == IDLE && _recv_message[0] != 0;
}
String get485Message(){
String res = String(_recv_message);
_recv_message[0] = 0;
return res;
}
boolean send485(char message[]){
if (_send_message[0] != 0x00) return false; // we already have a queued message, abort!
for (int idx = 0; idx<MSG_LEN; idx++){
_send_message[idx] = message[idx]; // copy message
if (message[idx] == 0x00) break;
}
if (_send_message[0] == 0x00) return false; // message is empty! abort
_send_state = START;
#ifdef DEBUG
Serial.print("sending "); Serial.println(_send_message);
#endif
return true;
}