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.
197 lines
5.9 KiB
197 lines
5.9 KiB
/* |
|
Library for Software Serial over RS485 connection |
|
Created by Stepan Richter, November 2023 |
|
*/ |
|
|
|
#include "Arduino.h" |
|
#include "SoftRS485.h" |
|
|
|
//#define SEND |
|
#define MSG_LEN 64 |
|
|
|
// Pins |
|
int _RO, _RE, _DE, _DI; |
|
|
|
const long _pulse_len = 616; |
|
const long _break = _pulse_len * 20; |
|
boolean _line = LOW; // current state of the transmission line |
|
long _last_flip = 0; // timestamp since last flip of line state |
|
boolean _start = false; // indicates whether a start bit shall be ignored |
|
|
|
// Receive vars |
|
byte _recv_msg[MSG_LEN]; // message buffer |
|
boolean _avail = false; // indicates, wheter a message has been received |
|
int _pos = 0; // character position within the buffer |
|
int _idx = 0; // bit position within the current character |
|
|
|
// variables, that could be local, but are stored globally for speed optimization |
|
long _diff = 0; // time difference between last flips |
|
long _now = 0; // current time |
|
long _count = 0; // helper for bit iteration |
|
|
|
void interrupt485(){ |
|
_line = !digitalRead(_RO); // HIGH level = logical zero and vice versa |
|
_now = micros(); // get current time |
|
_diff = _now - _last_flip; // duration of the _previous_ bit |
|
_last_flip = _now; // store time of the current flip |
|
|
|
#ifdef RECV |
|
Serial.print(!_line); |
|
Serial.print("-("); |
|
Serial.print(_diff); |
|
Serial.print("µs)→"); |
|
Serial.print(_line); |
|
Serial.print(" "); |
|
#endif |
|
|
|
if (_diff > _break){ // long break means we are starting with a new transmission |
|
_start = true; // first bit will be ignored on the next edge |
|
_pos = 0; // start writing at the beginning of the message buffer |
|
_idx = 0; |
|
#ifdef RECV |
|
Serial.println("==RESET=="); |
|
#endif |
|
} else { |
|
_count = _pulse_len>>1; // bit lengths may vary in certain bounds. this makes sure we always are in the bounds |
|
while (_count < _diff){ // iterate though each bit |
|
_count += _pulse_len; |
|
if (_idx){ // not the first bit |
|
_recv_msg[_pos] |= !_line << _idx; // set the bit of the current character |
|
} else { // first bit |
|
_recv_msg[_pos] = !_line; // reset all bits, set the first bit of the current character |
|
} |
|
if (_start){ // current bit is start bit |
|
_start = false; // ignore by not increasing the bit index |
|
} else { // current bit is not a start bit |
|
_idx++; // advance to the next bit |
|
} |
|
#ifdef RECV |
|
Serial.print(_line); |
|
#endif |
|
if (_idx == 8){ // full byte received |
|
if (_recv_msg[_pos] == 0x00){ // received byte is string terminator |
|
_avail = true; // notify about complete transmission |
|
// TODO: prevent writing out of buffer boundaries! |
|
} |
|
_pos++; // advance to nect character of buffer |
|
_idx=0; // start with first bit of that character |
|
} |
|
} |
|
#ifdef RECV |
|
Serial.println(); |
|
#endif |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
void init485(int RO, int nRE, int DE, int DI){ |
|
_RO=RO; // save the pin numbers |
|
_RE=nRE; |
|
_DE=DE; |
|
_DI=DI; |
|
_avail = false; |
|
|
|
#if defined SEND || defined RECV |
|
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 |
|
|
|
#ifdef RECV |
|
Serial.println("RECV enabled"); |
|
#endif |
|
#ifdef SEND |
|
Serial.println("SEND enabled"); |
|
#endif |
|
|
|
pinMode(_RO,INPUT); // we are reading on line _RO |
|
|
|
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); |
|
_last_flip = micros(); |
|
} |
|
|
|
boolean available485(){ |
|
return _avail; |
|
} |
|
|
|
String get485message(){ |
|
_avail = false; |
|
return String((char*)_recv_msg); |
|
} |
|
|
|
boolean writeBit(boolean bit){ |
|
digitalWrite(_DI,!bit); |
|
delayMicroseconds(5); |
|
if (_line != bit){ // collision! |
|
digitalWrite(_DE,LOW); // abort transmission immediately |
|
#ifdef SEND |
|
Serial.println(F("Collision!")); |
|
#endif |
|
return false; // notify caller |
|
} |
|
delayMicroseconds(_pulse_len-5); |
|
return true; |
|
} |
|
|
|
boolean writeByte(byte b){ |
|
return writeBit(b & 0x01) |
|
&& writeBit(b & 0x02) |
|
&& writeBit(b & 0x04) |
|
&& writeBit(b & 0x08) |
|
&& writeBit(b & 0x10) |
|
&& writeBit(b & 0x20) |
|
&& writeBit(b & 0x40) |
|
&& writeBit(b & 0x80); |
|
} |
|
|
|
boolean send485(char message[]){ |
|
#ifdef SEND |
|
Serial.print(F("preparing to send ")); |
|
Serial.print(message); |
|
Serial.print(F(": line state = ")); |
|
Serial.print(_line); |
|
Serial.print(F(" / last_flip = ")); |
|
Serial.print(_last_flip); |
|
Serial.print(F(" / micros = ")); |
|
Serial.println(micros()); |
|
#endif |
|
|
|
// if the line is busy: wait for it to get free |
|
while (_line || (micros() - _last_flip) < _break) { |
|
delayMicroseconds(_break); |
|
} |
|
#ifdef SEND |
|
Serial.println(F("enabling driver…")); |
|
#endif |
|
digitalWrite(_DI,LOW); |
|
digitalWrite(_DE,HIGH); // enable driver |
|
int pos = -1; // increment pos prior to sending char! |
|
boolean sync = writeBit(0); |
|
do { |
|
pos++; // incrementation at beginning of loop in order to allow post-loop condition! |
|
sync = sync && writeByte(message[pos]); |
|
} while (sync && message[pos]); |
|
digitalWrite(_DI,LOW); |
|
digitalWrite(_DE,LOW); // disable driver |
|
#ifdef SEND |
|
Serial.println(F("disabled driver…")); |
|
#endif |
|
return sync; |
|
}
|
|
|