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.
 
 

164 lines
4.0 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
// Pins
int _RO, _RE, _DE, _DI;
long _pulse_len = 100;
// Receive vars
boolean _recv_bit = false;
long _last_flip; // this is the time when the bus switched to logical zero
byte _recv_msg[MSG_LEN];
int _recv_pos = 0;
int _recv_idx = 0;
boolean _avail = false;
void interrupt485(){
_recv_bit = !digitalRead(_RO); // HIGH level = logical zero and vice versa
long now = micros();
long diff = now - _last_flip;
_last_flip = now;
if (diff > 20*_pulse_len){ // we received a start bit
#ifdef DEBUG
Serial.print(F("State changed to ")); Serial.println(_recv_bit);
Serial.println(F("received start bit"));
#endif
_recv_pos = 0;
_recv_idx = 0;
_last_flip+=_pulse_len;
} else {
long count = diff/_pulse_len;
for (int i=0; i<count; i++){
if (_recv_idx == 0) {
_recv_msg[_recv_pos] = (!_recv_bit);
} else {
_recv_msg[_recv_pos] |= (!_recv_bit)<<_recv_idx;
}
_recv_idx++;
if (_recv_idx == 8){
_recv_idx = 0;
if (_recv_msg[_recv_pos] == 0x00){
_avail = _recv_pos != 0;
return;
}
_recv_pos++;
}
}
}
}
void init485(int RO, int nRE, int DE, int DI){
_RO=RO;
_RE=nRE;
_DE=DE;
_DI=DI;
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
attachInterrupt(digitalPinToInterrupt(_RO),interrupt485,CHANGE);
#ifdef DEBUG
Serial.begin(115200);
Serial.print(F("RO = "));
Serial.println(_RO);
Serial.print(F("^RE = "));
Serial.println(_RE);
Serial.print(F("DE = "));
Serial.println(_DE);
Serial.print(F("DI = "));
Serial.println(_DI);
#endif
_last_flip = micros();
}
void speed485(long baudrate){
#ifndef DEBUG
_pulse_len = 1e6 / baudrate;
#else
_pulse_len = 1000;
Serial.print(F("Set pulse_len to ")); Serial.print(_pulse_len); Serial.println(F("µs"));
#endif
}
boolean available485(){
return _avail;
}
String get485message(){
_avail = false;
return String((char*)_recv_msg);
}
boolean writeBit(boolean bit){
digitalWrite(_DI,!bit);
delayMicroseconds(5);
if (_recv_bit != bit){ // collision!
digitalWrite(_DE,LOW);
#ifdef DEBUG
Serial.println(F("Collision!"));
#endif
return false;
}
delayMicroseconds(_pulse_len-5);
return true;
}
boolean send485(char message[]){
#ifdef DEBUG
Serial.print(F("preparing to send "));
Serial.print(message);
Serial.print(F(": line state = "));
Serial.print(_recv_bit);
Serial.print(F(" / last_flip = "));
Serial.print(_last_flip);
Serial.print(F(" / micros = "));
Serial.println(micros());
#endif
while (_recv_bit || (micros() - _last_flip) < (20 * _pulse_len)) {
delayMicroseconds(20*_pulse_len);
}
#ifdef DEBUG
Serial.println(F("enabling driver…"));
#endif
digitalWrite(_DE,HIGH); // enable driver
if (!writeBit(HIGH)) return false;
int pos = -1;
boolean good = true;
do {
pos++;
#ifdef DEBUG
Serial.print(F("sending character ")); Serial.println(message[pos]);
#endif
good = writeBit(message[pos] & 0x01)
&& writeBit(message[pos] & 0x02)
&& writeBit(message[pos] & 0x04)
&& writeBit(message[pos] & 0x08)
&& writeBit(message[pos] & 0x10)
&& writeBit(message[pos] & 0x20)
&& writeBit(message[pos] & 0x40)
&& writeBit(message[pos] & 0x80);
} while (good && message[pos]);
if (good) writeBit(1);
digitalWrite(_DE,LOW); // disable driver
#ifdef DEBUG
Serial.println(F("disabled driver…"));
#endif
return good;
}