0.2.3 RS485

This commit is contained in:
rob tillaart 2022-11-23 16:14:02 +01:00
parent b25fa79612
commit 13dd934f92
9 changed files with 252 additions and 137 deletions

View File

@ -0,0 +1,29 @@
platforms:
rpipico:
board: rp2040:rp2040:rpipico
package: rp2040:rp2040
gcc:
features:
defines:
- ARDUINO_ARCH_RP2040
warnings:
flags:
packages:
rp2040:rp2040:
url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
compile:
# Choosing to run compilation tests on 2 different Arduino platforms
# selected only those that work
platforms:
- uno
# - due
# - zero
# - leonardo
- m4
- esp32
- esp8266
# - mega2560
- rpipico

View File

@ -3,44 +3,43 @@
// FILE: ASCII_CONTROL.h
// AUTHOR: Rob Tillaart
// DATE: 2020-08-26
// VERSION: 0.1.0
// PURPOSE: ASCII control characters
// URL: https://github.com/RobTillaart/RS485
//
#define NUL 0x00 // NULL char
#define SOH 0x01 // Start Of Header
#define STX 0x02 // Start of Text
#define ETX 0x03 // End of Text
#define EOT 0x04 // End of Transmission
#define ENQ 0x05 // ENQuiry
#define ACK 0x06 // ACKnowledge
#define BEL 0x07 // Bell
#define BS 0x08 // Back Space
#define TAB 0x09 // TAB char
#define LF 0x0A // Line Feed
#define VT 0x0B // Vertical TAB
#define FF 0x0C // Form Feed
#define CR 0x0D // Carriage Return
#define SO 0x0E // Shift Out
#define SI 0x0F // Shift In
#define DLE 0x10 // Data Link Escape
#define DC1 0x11 // Device Control 1
#define DC2 0x12 // Device Control 2
#define DC3 0x13 // Device Control 3
#define DC4 0x14 // Device Control 4
#define NAK 0x15 // NOT ACKnowledge
#define SYN 0x16 // Synchronous idle
#define ETB 0x17 // End of transmission block
#define CAN 0x18 // CANcel
#define EM 0x19 // End of Medium
#define SUB 0x1A // Substitute
#define ESC 0x1B // Escape
#define FS 0x1C // File Separator
#define GS 0x1D // Group Separator
#define RS 0x1E // Record Separator
#define US 0x1F // Unit Separator
#define DEL 0x7F // DELete
#define NUL 0x00 // NULL char
#define SOH 0x01 // Start Of Header
#define STX 0x02 // Start of Text
#define ETX 0x03 // End of Text
#define EOT 0x04 // End of Transmission
#define ENQ 0x05 // ENQuiry
#define ACK 0x06 // ACKnowledge
#define BEL 0x07 // Bell
#define BS 0x08 // Back Space
#define TAB 0x09 // TAB char
#define LF 0x0A // Line Feed
#define VT 0x0B // Vertical TAB
#define FF 0x0C // Form Feed
#define CR 0x0D // Carriage Return
#define SO 0x0E // Shift Out
#define SI 0x0F // Shift In
#define DLE 0x10 // Data Link Escape
#define DC1 0x11 // Device Control 1
#define DC2 0x12 // Device Control 2
#define DC3 0x13 // Device Control 3
#define DC4 0x14 // Device Control 4
#define NAK 0x15 // NOT ACKnowledge
#define SYN 0x16 // Synchronous idle
#define ETB 0x17 // End of transmission block
#define CAN 0x18 // CANcel
#define EM 0x19 // End of Medium
#define SUB 0x1A // Substitute
#define ESC 0x1B // Escape
#define FS 0x1C // File Separator
#define GS 0x1D // Group Separator
#define RS 0x1E // Record Separator
#define US 0x1F // Unit Separator
#define DEL 0x7F // DELete
// -- END OF FILE --
// -- END OF FILE --

View File

@ -0,0 +1,30 @@
# Change Log RS485
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.2.3] - 2022-11-23
- add changelog.md
- add RP2040 to build-CI
- remove version from ASCII_CONTROL
- update keywords.txt
- minor edits
## [0.2.2] - 2022-05-25
- rewrite blocking write(uint8_t \* array, length).
- added write(char \* array, length). (convenience)
## [0.2.1] - 2022-05-24
- add setTXmode(), setRXmode(), getMode().
## [0.2.0] - 2022-05-24
- first published version
----
## [0.1.x] - 2017-10-30
- experimental versions.

View File

@ -108,10 +108,35 @@ on the baud rate. Use with care.
TODO: to be tested on ESP32 - RTOS .
#### Protocol design
An error I made in one of my first RS485 experiments was that a possible
response of one module would trigger another module to also send a response.
Of course these two responses interacted quite consistent but wrong.
It took some time to find the cause and to redesign the protocol used.
Lesson learned was to spend more time designing the protocol.
An example of a simple byte protocol could use commands all with
bit 7 set to 1, and all responses with bit 7 set to 0 (E.g ASCII).
#### Useful links
- https://www.ti.com/lit/an/snla049b/snla049b.pdf
- https://www.gammon.com.au/forum/?id=11428
- https://www.arduino.cc/reference/en/language/functions/communication/stream/
## Future
#### must
- improve documentation
- setUsPerByte() parameter does not feel 100%
#### should
- setUsPerByte() parameter does not feel 100% (investigate)
#### could
- add **send()** and **receive()** for longer messages.
- which handshake?
- dynamic buffer size?

View File

@ -2,24 +2,17 @@
// FILE: RS485.cpp
// AUTHOR: Rob Tillaart
// DATE: 30-okt-2017
// VERSION: 0.2.2
// VERSION: 0.2.3
// PURPOSE: Arduino library for RS485 modules (MAX485)
// URL: https://github.com/RobTillaart/RS485
//
// HISTORY:
// 0.1.x 2017-10-30 experimental versions.
// 0.2.0 2022-05-24 first published version
// 0.2.1 2022-05-24 add setTXmode(), setRXmode(), getMode().
// 0.2.2 2022-05-25 rewrite blocking write(uint8_t * array, length).
// added write(char * array, length). (convenience)
#include "RS485.h"
// - a stream - SWserial of HW serial (preferred)
// - a pin for TX- enable line
// - slave ID, or 0 as master (or don't care)
// - a stream - SWserial of HW serial (preferred)
// - a pin for TX- enable line
// - slave ID, or 0 as master (or don't care)
RS485::RS485(Stream * stream, uint8_t sendPin, uint8_t deviceID)
{
_stream = stream;
@ -36,21 +29,25 @@ int RS485::available()
return _stream->available();
}
int RS485::read()
{
return _stream->read();
}
int RS485::peek()
{
return _stream->peek();
}
void RS485::flush()
{
_stream->flush();
}
size_t RS485::write(uint8_t c)
{
setTXmode(); // transmit mode
@ -73,11 +70,11 @@ size_t RS485::write(char * array, uint8_t length)
// - https://github.com/RobTillaart/RS485/issues/2
//
// #define RS485_YIELD_ENABLE 1
// #define RS485_YIELD_ENABLE 1
#ifdef RS485_YIELD_ENABLE
// smallest and slightly fastest.
// smallest and slightly fastest.
size_t RS485::write(uint8_t * array, uint8_t length)
{
uint8_t n = 0;
@ -107,7 +104,7 @@ size_t RS485::write(uint8_t * array, uint8_t length)
void RS485::setMicrosPerByte(uint32_t baudRate)
{
// count 11 bits / byte
// count 11 bits / byte
_microsPerByte = (11 * 1000000UL) / baudRate;
}
@ -138,109 +135,136 @@ void RS485::setMicrosPerByte(uint32_t baudRate)
// deviceID to
// deviceID sender
// length length of message
// STX start of text
// message idem
// CHECKSUM idem, message only!
// ETX end of text
// STX start of text
// message idem
// CHECKSUM idem, message only!
// ETX end of text
// EOT end of transmission
//
/*
void RS485::send(uint8_t deviceID, uint8_t msg[], uint8_t len)
void RS485::send(uint8_t receiverID, uint8_t msg[], uint8_t len)
{
uint8_t CHKSUM = 0;
digitalWrite(_enablePin, HIGH);
setTXmode(); // transmit mode
_stream->write(SOH);
_stream->write(deviceID); // TO
_stream->write(_deviceID); // FROM
_stream->write(len); // LENGTH BODY
_stream->write(receiverID); // TO
_stream->write(_deviceID); // FROM
_stream->write(len); // LENGTH BODY
_stream->write(STX);
for (int i = 0; i < len; i++)
{
_stream->write(msg[i]);
CHKSUM ^= msg[i]; // TODO good for now => CRC8 ?
delayMicroseconds(_microsPerByte);
CHKSUM ^= msg[i]; // Simple XOR checksum.
}
_stream->write(CHKSUM);
delayMicroseconds(_microsPerByte);
_stream->write(ETX);
delayMicroseconds(_microsPerByte);
digitalWrite(_enablePin, LOW);
_stream->write(EOT);
delayMicroseconds((len + 8 + 2) * _microsPerByte); // + 2 to be sure...
setRXmode();
}
// returns true if complete message is captured.
// multiple calls needed as it should be non-blocking.
bool RS485::receive(uint8_t &deviceID, uint8_t msg[], uint8_t &len)
bool RS485::receive(uint8_t &senderID, uint8_t msg[], uint8_t &msglen)
{
static uint8_t state = 0;
static uint8_t sender = 255; // unknown / anonymous.
static uint8_t length = 0;
static bool forMe = false;
static bool forMe = false;
uint8_t CHKSUM = 0;
if (_stream->available() == 0) return false;
uint8_t v = _stream->read();
// if (debug) Serial.print(v, HEX);
if (v == SOH)
switch(state)
{
_bidx = 0; // start new packet
_buffer[_bidx++] = v;
forMe = true;
return false;
// waiting for new packet
case 0:
if (v == SOH)
{
_bidx = 0; // start new packet
state = 1;
}
break;
// extract receiver
case 1:
forMe = ((v == _deviceID) || (v == 255)); // PM or broadcast?
if (not forMe) state = 99;
else state = 2;
break;
// extract sender
case 2:
sender = v;
state = 3;
break;
// extract length
case 3:
msglen = v;
state = 3;
break;
// expect STX
case 4:
if (v == STX) state = 5;
else state = 99;
break;
// extract message
case 5:
if (length == 0)
{
state = 6;
break;
}
// error handling if v not ascii ?
_buffer[_bidx++] = v;
CHKSUM ^= v;
length--;
break;
// expect checksum
case 6:
if (CHKSUM == v) state = 7;
else state = 99;
break;
// expect STX
case 7:
if (v == STX) state = 8;
else state = 99;
break;
// expect EOT
case 8:
if (v == EOT)
{
senderID = sender;
msglen = _bidx -1;
for (int i = 0; i < msglen; i++)
{
msg[i] = _buffer[i];
}
return true;
}
state = 0;
break;
// SKIP until next packet
case 99: // wait for EOT in case of error
if (v == EOT) state = 0;
break;
}
if (_bidx == 1) // expect TO
{
forMe = (v == _deviceID);
_buffer[_bidx++] = v;
return false;
}
if (!forMe) return false;
if (_bidx == 2) // expect SENDER
{
_buffer[_bidx++] = v;
return false;
}
if (_bidx == 3) // expect LEN
{
length = v;
_buffer[_bidx++] = v;
return false;
}
if (_bidx == 4) // expect STX
{
_buffer[_bidx++] = v;
return false;
}
if (_bidx >= 48) // prevent buffer overflow
{
len = 0;
forMe = false;
_bidx = 0;
return false;
}
if (v == ETX)
{
for (int i = 0, j = 5; i < length; i++, j++)
{
msg[i] = _buffer[j]; // copy buffer
CHKSUM ^= _buffer[j];
}
if (CHKSUM != _buffer[_bidx - 1])
len = length; // copy length
deviceID = _buffer[2]; // copy sender
forMe = false;
_bidx = 0;
return true;
}
return false;
return false;
}
*/
//////////////////////////////////////////////////

View File

@ -3,7 +3,7 @@
// FILE: RS485.h
// AUTHOR: Rob Tillaart
// DATE: 30-okt-2017
// VERSION: 0.2.2
// VERSION: 0.2.3
// PURPOSE: Arduino library for RS485 modules
// URL: https://github.com/RobTillaart/RS485
@ -11,7 +11,7 @@
#include "Arduino.h"
#include "ASCII_CONTROL.h"
#define RS485_LIB_VERSION (F("0.2.2"))
#define RS485_LIB_VERSION (F("0.2.3"))
class RS485 : public Stream
@ -44,9 +44,15 @@ public:
uint8_t getMode() { return digitalRead(_sendPin) == HIGH; };
// TODO TEST 0.3.0
// void send(uint8_t deviceID, uint8_t msg[], uint8_t len);
// bool receive(uint8_t &deviceID, uint8_t msg[], uint8_t &len);
// 0.3.0 EXPERIMENTAL
// send ASCII encoded messages from one master to multiple clients.
// msg[] = 32..127
// len = 1..48 ?
//
// should be a p2p network option.
void send(uint8_t receiverID, uint8_t msg[], uint8_t len);
bool receive(uint8_t &senderID, uint8_t msg[], uint8_t &len);
private:
Stream * _stream;
@ -54,11 +60,9 @@ private:
uint8_t _deviceID = 0;
uint16_t _microsPerByte = 1000;
// TODO TEST
// uint8_t _bidx = 0;
// uint8_t _buffer[50]; // internal receive buffer
// 0.3.0
uint8_t _bidx = 0;
uint8_t _buffer[50]; // internal receive buffer
};

View File

@ -11,8 +11,12 @@ setMicrosPerByte KEYWORD2
getMicrosPerByte KEYWORD2
getDeviceID KEYWORD2
setTXmode KEYWORD2
setRXmode KEYWORD2
getMode KEYWORD2
# TODO
# EXPERIMENTAL
send KEYWORD2
receive KEYWORD2

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/RS485"
},
"version": "0.2.2",
"version": "0.2.3",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=RS485
version=0.2.2
version=0.2.3
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=RS485 library for Arduino.