0.2.5 RS485

This commit is contained in:
Rob Tillaart 2023-05-11 11:14:17 +02:00
parent da7349212d
commit 3fa1723780
21 changed files with 947 additions and 60 deletions

View File

@ -6,13 +6,23 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.2.5] - 2023-04-28
- fix send / receive protocol bugs (as far as known)
- add **RS485_master_send_receive.ino** demo
- add **RS485_slave_send_receive.ino** demo
- add **RS485_sniffer.ino** debug tool
- add **EXPERIMENTAL** SoftwareSerial examples.
- update examples.
- move some code from .h to .cpp
- update readme.md
## [0.2.4] - 2023-02-05
- update readme.md
- added messages.md to give som ideas about handshakes/protocols.
- update GitHub actions
- update license 2023
## [0.2.3] - 2022-11-23
- add changelog.md
- add RP2040 to build-CI

View File

@ -1,13 +1,30 @@
# Messages
Examples of command/answer protocols.
Examples of command/answer protocols over RS485.
(to be elaborated)
This is part of the RS485 class - https://github.com/RobTillaart/RS485
#### Guidelines
Designing a protocol is most often not trivial and might need more
than one iteration.
- Keep protocols as simple as possible.
- be aware of implications of future expansion.
- Make sure that baud rates match between the nodes in the network.
- Software Serial can work when time between requests is long enough.
It depends on how much processing is needed.
- Be aware of implications of future expansion.
Design your protocol with room for additional commands.
Or even a version number in it.
- In designing a protocol be aware of ambiguities.
An request or answer may contain bytes (sequences)
that might trigger other nodes.
- If you request data from a node, it might happen
that it won't answer or that the answer fails to
arrive in correct state (missing bytes, flipped bits etc.)
#### ASCII codes
@ -24,6 +41,7 @@ Possible useful char codes.
| NAK | 0x15 | Not Acknowledge
| CAN | 0x18 | CANcel
See also **ASCII_CONTROL.h**
----
@ -42,10 +60,11 @@ requirement:
- All devices listen to a disjunct command set.
----
## example 2
using a device id to send the command to.
Using a device id to send the command to.
command :=
@ -60,9 +79,11 @@ answer :=
bytes requested bytes (send them back)
----
## example 3
command and answer have same layout.
Command and answer have same layout.
Uses device ID's to address specific device.
SOH start of header 0x01
@ -76,6 +97,8 @@ optional extend with:
EOT optional (end of transmission)
----
## example 4
More complex package with multiple fields and crc per message.
@ -99,8 +122,22 @@ More complex package with multiple fields and crc per message.
----
## example 5 (still alive)
Maybe most simple protocol, just send an ID,
and if the slave is alive it responds with its ID.
Think of it as the **PING** command
command = ID
answer = ID
Could be embedded in other protocols too,
----
## Future
- binary protocols
- example code

View File

@ -17,14 +17,39 @@ RS485 is an **experimental** library to make half duplex communication easier.
The library implements the Stream interface so the user can use
**print()** and **write()** calls just like one does with **Serial**.
Preferably the library is to be used with a hardwareSerial as these
Preferably the library is to be used with a **hardwareSerial** as these
can buffer incoming characters in the background.
A software Serial that uses pin interrupts would also work quite well.
A software Serial that uses pin interrupts would also work quite well,
needs to be tested.
The 0.2.0 version of the library has no (tested) protocol for multi-byte
messages so the user must implement such on top of this class.
#### Connection schema
```
Processor MAX485
+------------------+ +-------------+
| | | |
| RX |<-----------| RO |
| TX |----------->| DI |
| | | |
| | +--->| RE |
| sendPin |-------+--->| DE |
| | | |
| VCC |------------| VCC |
| GND |------------| GND |
| | | |
+------------------+ +-------------+
RX = Receive Serial RO = Receiver Output
TX = Transmit Serial DI = Driver Input
sendPin = IO.pin RE = Receiver Output Enable
DE = Driver Output Enable
```
## Interface
```cpp
@ -38,7 +63,8 @@ messages so the user must implement such on top of this class.
The default device ID is 0 (typically master uses this, or if deviceID is not used).
The stream is typically Serial, and the baud rate, timeout etc. should be set
via the Serial class.
The sendPin is the pin that connects to the transmit/receive enable pins.
The sendPin is the pin that connects to the transmit/receive enable (DE/RE) pins.
See connection schema above.
The library sets the pinMode and defaults it to LOW (receiving mode).
- **void setMicrosPerByte(uint32_t baudRate)** set the delay per character needed.
This gives the hardware enough time to flush the buffer.
@ -67,6 +93,27 @@ An important command from the stream interface is the **setTimeOut()** as
this allows reads on the RS485 bus that are limited.
#### Experimental
Work in progress. The library has an experimental protocol implemented to
send and receive larger messages between the nodes in the network.
This network consists of one master that can poll multiple slaves.
In 0.2.5 this protocol has been tested and some bugs in the receive parser
have been fixed. It still is experimental and it needs more testing.
The library functions are:
- **void send(uint8_t receiverID, uint8_t msg[], uint8_t len)**
- send a buffer of given length to a receiver.
- **bool receive(uint8_t &senderID, uint8_t msg[], uint8_t &len)**
- receive a packet, senderID identifies the sender.
Current implementation limits messages up to 48 bytes (hardcoded buffer size)
which is in many cases enough.
See two example sketches.
## Operation
A RS485 controlled device is typically waiting for a command or message
@ -76,6 +123,14 @@ chip in sending mode, wait for enough time to "flush the buffer" and
resumes with listening.
#### Sniffer hex dump
Since 0.2.5 an example is added that snifs the bytes on the RS485 bus
and prints them as a HEX dump.
Can be used for debugging.
#### Pull up resistors
Do not forget to use one pull up (A line) and one pull down (B line)
@ -104,7 +159,9 @@ so the function can call **yield()** every 4 milliseconds if enabled.
To enable **yield()** uncomment the following line in **RS485.cpp**
```cpp
// #define RS485_YIELD_ENABLE 1
```
or set this flag in the command line compile option.
@ -142,20 +199,30 @@ Would allow 127 different 1 byte commands.
- improve documentation
#### Should
- setUsPerByte() parameter does not feel 100% (investigate)
- improve **send()** and **receive()** for longer messages.
- should be a message class.
- extract from this lib (for now in the library).
- dynamic receive buffer size?
- investigate error handling?
- expand write to other data types?
- test other platforms
- ESP32.
#### Could
- add **send()** and **receive()** for longer messages.
- which handshake?
- dynamic buffer size?
- should this be a sort of message class / struct. fixed size?
- add examples
- setUsPerByte() parameter does not feel 100% (investigate)
- rename to begin().
- improve the examples.
- add binary protocol example
- multi-master?
- add unit tests
- investigate yield() on ESP32/RTOS behaviour
#### Wont

View File

@ -2,7 +2,7 @@
// FILE: RS485.cpp
// AUTHOR: Rob Tillaart
// DATE: 30-okt-2017
// VERSION: 0.2.4
// VERSION: 0.2.5
// PURPOSE: Arduino library for RS485 modules (MAX485)
// URL: https://github.com/RobTillaart/RS485
@ -24,6 +24,37 @@ RS485::RS485(Stream * stream, uint8_t sendPin, uint8_t deviceID)
}
// 0.3.0
// void RS485::begin(uint32_t baudRate)
// {
// _stream->begin(baudRate);
// setMicrosPerByte(baudRate);
// }
void RS485::setMicrosPerByte(uint32_t baudRate)
{
// count 11 bits time per byte
_microsPerByte = (11 * 1000000) / baudRate ;
}
uint32_t RS485::getMicrosPerByte()
{
return _microsPerByte;
}
uint8_t RS485::getDeviceID()
{
return _deviceID;
}
///////////////////////////////////////////////////////
//
// STREAM INTERFACE
//
int RS485::available()
{
return _stream->available();
@ -102,17 +133,10 @@ size_t RS485::write(uint8_t * array, uint8_t length)
#endif
void RS485::setMicrosPerByte(uint32_t baudRate)
{
// count 11 bits / byte
_microsPerByte = (11 * 1000000UL) / baudRate;
}
///////////////////////////////////////////////////////
//
// EXPERIMENTAL - to be tested - use at own risk.
// EXPERIMENTAL - use at own risk.
//
///////////////////////////////////////////////////////
//
@ -170,15 +194,17 @@ void RS485::send(uint8_t receiverID, 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;
uint8_t CHKSUM = 0;
static uint8_t CHKSUM = 0;
if (_stream->available() == 0) return false;
uint8_t v = _stream->read();
// if (debug) Serial.print(v, HEX);
// Serial.print(state, HEX);
// Serial.print('\t');
// Serial.println(v, HEX);
switch(state)
{
@ -187,6 +213,7 @@ bool RS485::receive(uint8_t &senderID, uint8_t msg[], uint8_t &msglen)
if (v == SOH)
{
_bidx = 0; // start new packet
CHKSUM = 0;
state = 1;
}
break;
@ -200,14 +227,15 @@ bool RS485::receive(uint8_t &senderID, uint8_t msg[], uint8_t &msglen)
// extract sender
case 2:
sender = v;
senderID = v;
state = 3;
break;
// extract length
case 3:
msglen = v;
state = 3;
length = v;
state = 4;
break;
// expect STX
@ -220,7 +248,16 @@ bool RS485::receive(uint8_t &senderID, uint8_t msg[], uint8_t &msglen)
case 5:
if (length == 0)
{
state = 6;
// expect checksum
if (CHKSUM == v) state = 6;
else
{
// debug failing checksum
// Serial.print(CHKSUM, HEX);
// Serial.print('\t');
// Serial.println(v, HEX);
state = 99; // for debug change to state = 6;
}
break;
}
// error handling if v not ASCII ?
@ -229,31 +266,25 @@ bool RS485::receive(uint8_t &senderID, uint8_t msg[], uint8_t &msglen)
length--;
break;
// expect checksum
// expect ETX
case 6:
if (CHKSUM == v) state = 7;
else state = 99;
break;
// expect STX
case 7:
if (v == STX) state = 8;
if (v == ETX) state = 7;
else state = 99;
break;
// expect EOT
case 8:
case 7:
if (v == EOT)
{
senderID = sender;
msglen = _bidx -1;
msglen = _bidx;
for (int i = 0; i < msglen; i++)
{
msg[i] = _buffer[i];
}
state = 0;
return true;
}
state = 0;
state = 99;
break;
// SKIP until next packet
@ -266,6 +297,7 @@ bool RS485::receive(uint8_t &senderID, uint8_t msg[], uint8_t &msglen)
}
//////////////////////////////////////////////////
//
// PRIVATE

View File

@ -3,7 +3,7 @@
// FILE: RS485.h
// AUTHOR: Rob Tillaart
// DATE: 30-okt-2017
// VERSION: 0.2.4
// VERSION: 0.2.5
// 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.4"))
#define RS485_LIB_VERSION (F("0.2.5"))
class RS485 : public Stream
@ -19,13 +19,18 @@ class RS485 : public Stream
public:
RS485(Stream * stream, uint8_t sendPin, uint8_t deviceID = 0);
// sets the baud rate of the stream.
// 0.3.0
// void begin(uint32_t baudRate);
// baudRate must match Serial baudRate
// micros are needed before RS485 stream may be reset
// to (default) receiving mode.
void setMicrosPerByte(uint32_t baudRate);
uint32_t getMicrosPerByte() { return _microsPerByte; };
uint32_t getMicrosPerByte();
uint8_t getDeviceID();
uint8_t getDeviceID() { return _deviceID; };
// Stream interface
int available();
@ -33,23 +38,23 @@ public:
int peek();
void flush();
// Write
size_t write(uint8_t c);
size_t write(char * array, uint8_t length); // wrapper
size_t write(uint8_t * array, uint8_t length);
// Mode functions
inline void setTXmode() { digitalWrite(_sendPin, HIGH); };
inline void setRXmode() { digitalWrite(_sendPin, LOW); };
uint8_t getMode() { return digitalRead(_sendPin) == HIGH; };
// 0.3.0 EXPERIMENTAL
// EXPERIMENTAL - use at own risk.- (for 0.3.0)
// send ASCII encoded messages from one master to multiple clients.
// msg[] = 32..127
// len = 1..48 ?
//
// should be a p2p network option.
// len = 1..48 ?
void send(uint8_t receiverID, uint8_t msg[], uint8_t len);
bool receive(uint8_t &senderID, uint8_t msg[], uint8_t &len);
@ -60,9 +65,9 @@ private:
uint8_t _deviceID = 0;
uint16_t _microsPerByte = 1000;
// 0.3.0
// EXPERIMENTAL (for 0.3.0)
uint8_t _bidx = 0;
uint8_t _buffer[50]; // internal receive buffer
uint8_t _buffer[50]; // internal receive buffer
};

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

@ -0,0 +1,83 @@
// FILE: RS485_MEGA_master_send_receive.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo master of send / receive
// URL: https://github.com/RobTillaart/RS485
// This is the code for a master (to be used with slave example)
// Best is it to use on a MEGA or another board with multiple hardware Serials.
// The master should be connected with two slaves
// slave 2 is requested for temperature
// slave 1 is requested for humidity
//
// Note that Serial is used for debugging messages, so better use
// a 2nd Serial for the RS485 bus.
// otherwise some binary messages will show up in the Serial monitor.
#include "Arduino.h"
#include "RS485.h"
const uint8_t sendPin = 4;
const uint8_t deviceID = 0;
// use a 2nd Serial port.
RS485 rs485(&Serial2, sendPin, deviceID);
// times of last requests.
uint32_t lastT = 0;
uint32_t lastH = 0;
// for receiving (must be global)
uint8_t ID;
uint8_t arr[32];
uint8_t len;
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println();
Serial.println(__FILE__);
Serial2.begin(38400);
rs485.setMicrosPerByte(38400);
}
void loop()
{
if (millis() - lastT >= 3000)
{
lastT = millis();
char msg[] = "Get Temperature";
rs485.send(1, (uint8_t *)msg, strlen(msg));
Serial.print("SEND: ");
Serial.println(msg);
}
if (millis() - lastH >= 7000)
{
lastH = millis();
char msg[] = "Get Humidity";
rs485.send(1, (uint8_t *)msg, strlen(msg));
Serial.print("SEND: ");
Serial.println(msg);
}
if (rs485.receive(ID, arr, len))
{
arr[len] = 0;
Serial.print("RECV:\t");
Serial.print(ID);
Serial.print("\t");
Serial.println((char*) arr);
}
}
// -- END OF FILE --

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

@ -0,0 +1,71 @@
// FILE: RS485_MEGA_slave_send_receive.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo slave of send / receive
// URL: https://github.com/RobTillaart/RS485
// This is the code for a slave (to be used with master example)
// Best is it to use on a MEGA or another board with multiple hardware Serials.
// The master should be connected with two slaves
// slave 2 is waiting for temperature requests.
// slave 1 is waiting for humidity requests.
//
// Note that Serial is used for debugging messages, so better use
// a 2nd Serial for the RS485 bus.
// otherwise some binary messages will show up in the Serial monitor.
#include "Arduino.h"
#include "RS485.h"
const uint8_t sendPin = 4;
const uint8_t deviceID = 1;
// use a 2nd Serial port.
RS485 rs485(&Serial2, sendPin, deviceID);
// for receiving (must be global)
uint8_t ID;
uint8_t buffer[32];
uint8_t len;
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println();
Serial.println(__FILE__);
Serial2.begin(38400);
rs485.setMicrosPerByte(38400);
}
void loop()
{
if (rs485.receive(ID, buffer, len))
{
buffer[len] = 0;
Serial.print("RECV: ");
Serial.println((char*) buffer);
if (strcmp((char*)buffer, "Get Humidity") == 0)
{
int humidity = 50 + random(10);
sprintf((char*)buffer, "HUM: %d", humidity);
rs485.send(ID, buffer, strlen((char*)buffer));
}
if (strcmp((char*)buffer, "Get Temperature") == 0)
{
int temperature = 15 + random(10);
sprintf((char*)buffer, "TEM: %d", temperature);
rs485.send(ID, buffer, strlen((char*)buffer));
}
}
}
// -- END OF FILE --

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

@ -0,0 +1,86 @@
// FILE: RS485_SWS_master_send_receive.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo master of send / receive
// URL: https://github.com/RobTillaart/RS485
// VERY EXPERIMENTAL
// This is the code for a master (to be used with slave example)
// Best is it to use on a MEGA or another board with multiple hardware Serials.
// The master should be connected with two slaves
// slave 2 is requested for temperature
// slave 1 is requested for humidity
//
// Note that Serial is used for debugging messages, so better use
// a 2nd Serial for the RS485 bus.
// otherwise some binary messages will show up in the Serial monitor.
#include "Arduino.h"
#include "RS485.h"
#include "SoftwareSerial.h"
const uint8_t sendPin = 4;
const uint8_t deviceID = 0;
SoftwareSerial SWS(6,7); // RX, TX
// use Software Serial
RS485 rs485(&SWS, sendPin, deviceID);
// times of last requests.
uint32_t lastT = 0;
uint32_t lastH = 2000;
// for receiving (must be global)
uint8_t ID;
uint8_t buffer[32];
uint8_t len;
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println();
Serial.println(__FILE__);
SWS.begin(38400);
rs485.setMicrosPerByte(38400);
}
void loop()
{
if (millis() - lastT >= 5000)
{
lastT = millis();
char msg[] = "Get Temperature";
rs485.send(2, (uint8_t *)msg, strlen(msg));
Serial.print("SEND: ");
Serial.println(msg);
}
if (millis() - lastH >= 5000)
{
lastH = millis();
char msg[] = "Get Humidity";
rs485.send(1, (uint8_t *)msg, strlen(msg));
Serial.print("SEND: ");
Serial.println(msg);
}
if (rs485.receive(ID, buffer, len))
{
buffer[len] = 0;
Serial.print("RECV: ");
Serial.println((char*) buffer);
}
}
// -- END OF FILE --

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

@ -0,0 +1,80 @@
// FILE: RS485_slave_send_receive.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo slave of send / receive
// URL: https://github.com/RobTillaart/RS485
// VERY EXPERIMENTAL
// This is the code for a slave (to be used with master example)
// Best is it to use on a MEGA or another board with multiple hardware Serials.
// The master should be connected with two slaves
// slave 2 is waiting for temperature requests.
// slave 1 is waiting for humidity requests.
//
// Note that Serial is used for debugging messages, so better use
// a 2nd Serial for the RS485 bus.
// otherwise some binary messages will show up in the Serial monitor.
#include "Arduino.h"
#include "RS485.h"
#include "SoftwareSerial.h"
const uint8_t sendPin = 4;
const uint8_t deviceID = 1;
SoftwareSerial SWS(6,7); // RX, TX
// use Software Serial
RS485 rs485(&SWS, sendPin, deviceID);
// for receiving (must be global)
uint8_t ID;
uint8_t buffer[32];
uint8_t len;
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println();
Serial.println(__FILE__);
SWS.begin(38400);
rs485.setMicrosPerByte(38400);
}
void loop()
{
if (rs485.receive(ID, buffer, len))
{
buffer[len] = 0;
Serial.print("RECV: ");
Serial.println((char*) buffer);
if (strcmp((char*)buffer, "Get Humidity") == 0)
{
int humidity = 50 + random(10);
sprintf((char*)buffer, "HUM: %d", humidity);
rs485.send(ID, buffer, strlen((char*)buffer));
// tidy up output
Serial.println();
}
if (strcmp((char*)buffer, "Get Temperature") == 0)
{
int temperature = 15 + random(10);
sprintf((char*)buffer, "TEM: %d", temperature);
rs485.send(ID, buffer, strlen((char*)buffer));
// tidy up output
Serial.println();
}
}
}
// -- END OF FILE --

View File

@ -0,0 +1,83 @@
// FILE: RS485_master_send_receive.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo master of send / receive
// URL: https://github.com/RobTillaart/RS485
// This is the code for a master (to be used with slave example)
// Best is it to use on a MEGA or another board with multiple hardware Serials.
// The master should be connected with two slaves
// slave 2 is requested for temperature
// slave 1 is requested for humidity
//
// Note that Serial is used for debugging messages, so better use
// a 2nd Serial for the RS485 bus.
// otherwise some binary messages will show up in the Serial monitor.
#include "Arduino.h"
#include "RS485.h"
const uint8_t sendPin = 4;
const uint8_t deviceID = 0;
// use a 2nd Serial port.
RS485 rs485(&Serial, sendPin, deviceID);
// times of last requests.
uint32_t lastT = 0;
uint32_t lastH = 0;
// for receiving (must be global)
uint8_t ID;
uint8_t arr[32];
uint8_t len;
void setup()
{
Serial.begin(38400);
while (!Serial);
Serial.println();
Serial.println(__FILE__);
rs485.setMicrosPerByte(38400);
}
void loop()
{
if (millis() - lastT >= 3000)
{
lastT = millis();
char msg[] = "Get Temperature";
rs485.send(1, (uint8_t *)msg, strlen(msg));
Serial.print("SEND: ");
Serial.println(msg);
}
if (millis() - lastH >= 7000)
{
lastH = millis();
char msg[] = "Get Humidity";
rs485.send(1, (uint8_t *)msg, strlen(msg));
Serial.print("SEND: ");
Serial.println(msg);
}
if (rs485.receive(ID, arr, len))
{
arr[len] = 0;
Serial.print("RECV:\t");
Serial.print(ID);
Serial.print("\t");
Serial.println((char*) arr);
}
}
// -- END OF FILE --

View File

@ -3,13 +3,18 @@
// PURPOSE: simple performance test for write(array, length)
// URL: https://github.com/RobTillaart/RS485
// minimal test to see how fast data can be send over the
// RS485 bus at a given baud rate of 4800. (adjust if needed).
#include "Arduino.h"
#include "RS485.h"
RS485 rs485(&Serial, 4); // uses default deviceID
RS485 rs485(&Serial, 4); // uses default deviceID 0
uint32_t start, stop = 0;
void setup()
{
Serial.begin(4800);
@ -17,8 +22,11 @@ void setup()
// Serial.println(__FILE__);
test(4800);
Serial.println("\ndone...");
}
void loop()
{
}
@ -26,13 +34,18 @@ void loop()
void test(uint32_t baudrate)
{
delay(10);
char buffer[64] = "123456789012345678901234567890123456789012345678901234567890";
rs485.setMicrosPerByte(baudrate);
start = micros();
rs485.write((uint8_t *)buffer, 60);
stop = micros();
Serial.print("\nTIME: ");
Serial.print("BAUD:\t");
Serial.print(baudrate);
Serial.print("\tTIME: ");
Serial.println(stop - start);
}
// -- END OF FILE --
// -- END OF FILE --

View File

@ -3,16 +3,31 @@
// PURPOSE: simple master
// URL: https://github.com/RobTillaart/RS485
// this is the code of a simple master (needs simple slave)
// it send one of 3 (single char) commands to the slave
// '0' == set LED LOW
// '1' == set LED HIGH
// '2' == request status.
//
// print debug messages SEND and RECV with data.
// Note that one needs a 2nd Serial port for nice debugging.
// (or an LCD screen whatever).
#include "Arduino.h"
#include "RS485.h"
const uint8_t sendPin = 4;
const uint8_t deviceID = 0;
RS485 rs485(&Serial, sendPin); // uses default deviceID
uint32_t lastCommand = 0;
void setup()
{
Serial.begin(115200);
@ -22,20 +37,25 @@ void setup()
rs485.setMicrosPerByte(115200);
}
void loop()
{
if (millis() - lastCommand >= 2000)
{
lastCommand = millis();
char cmd = random(3); // 0,1,2
char cmd = '0' + random(3); // sends 0, 1, 2 as character (not as value)
rs485.print(cmd);
Serial.print("SEND: ");
Serial.println(cmd);
}
if (rs485.available() > 0)
{
int status = rs485.read();
Serial.print("RECV: ");
Serial.println(status);
}
}
// -- END OF FILE --
// -- END OF FILE --

View File

@ -3,17 +3,32 @@
// PURPOSE: simple listening slave
// URL: https://github.com/RobTillaart/RS485
// this is the code of a simple slave (needs simple master)
// it receives one of 3 (single char) commands to the slave
// '0' == set LED LOW.
// '1' == set LED HIGH.
// '2' == return status to master.
//
// print debug messages SEND and RECV with data.
// Note that one needs a 2nd Serial port for nice debugging.
// (or an LCD screen whatever).
#include "Arduino.h"
#include "RS485.h"
const uint8_t sendPin = 4;
const uint8_t deviceID = 1;
RS485 rs485(&Serial, sendPin, deviceID);
const uint8_t LED = 13;
uint8_t status = LOW;
void setup()
{
Serial.begin(115200);
@ -26,17 +41,27 @@ void setup()
digitalWrite(LED, LOW);
}
void loop()
{
if (rs485.available() > 0)
{
int c = rs485.read();
Serial.print("RECV: ");
Serial.println(c);
if (c == '0') status = LOW;
if (c == '1') status = HIGH;
if (c == '2') rs485.print(status);
if (c == '2')
{
Serial.print("SEND: ");
Serial.println(status);
rs485.print(status);
}
digitalWrite(LED, status);
}
}
// -- END OF FILE --
// -- END OF FILE --

View File

@ -0,0 +1,69 @@
// FILE: RS485_slave_send_receive.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo slave of send / receive
// URL: https://github.com/RobTillaart/RS485
// This is the code for a slave (to be used with master example)
// Best is it to use on a MEGA or another board with multiple hardware Serials.
// The master should be connected with two slaves
// slave 2 is waiting for temperature requests.
// slave 1 is waiting for humidity requests.
//
// Note that Serial is used for debugging messages, so better use
// a 2nd Serial for the RS485 bus.
// otherwise some binary messages will show up in the Serial monitor.
#include "Arduino.h"
#include "RS485.h"
const uint8_t sendPin = 4;
const uint8_t deviceID = 1;
// use a 2nd Serial port.
RS485 rs485(&Serial, sendPin, deviceID);
// for receiving (must be global)
uint8_t ID;
uint8_t buffer[32];
uint8_t len;
void setup()
{
Serial.begin(38400);
while (!Serial);
Serial.println();
Serial.println(__FILE__);
rs485.setMicrosPerByte(38400);
}
void loop()
{
if (rs485.receive(ID, buffer, len))
{
buffer[len] = 0;
// Serial.print("RECV: ");
// Serial.println((char*) buffer);
if (strcmp((char*)buffer, "Get Humidity") == 0)
{
int humidity = 50 + random(10);
sprintf((char*)buffer, "HUM: %d", humidity);
rs485.send(ID, buffer, strlen((char*)buffer));
}
if (strcmp((char*)buffer, "Get Temperature") == 0)
{
int temperature = 15 + random(10);
sprintf((char*)buffer, "TEM: %d", temperature);
rs485.send(ID, buffer, strlen((char*)buffer));
}
}
}
// -- END OF FILE --

View File

@ -0,0 +1,90 @@
// FILE: RS485_sniffer.ino
// AUTHOR: Rob Tillaart
// PURPOSE: simple sniffer prints data as hex dump
// URL: https://github.com/RobTillaart/RS485
// use a 2nd Serial port.
// or only connect the RX of the MAX485.
//
// Note this sniffer if not 100% fool proof, known issues are
// - no detection of buffer overrun etc
// - ASCII output is only printed after 16 bytes are decoded.
// - still might be useful for debugging.
#include "Arduino.h"
#include "RS485.h"
const uint8_t sendPin = 4; // not used, listening only.
const uint8_t deviceID = 250; // not used, maybe in future.
// use a 2nd Serial port.
// or only connect the RX of the MAX485.
RS485 rs485(&Serial, sendPin, deviceID);
const uint8_t LED = 13;
uint8_t status = LOW;
uint32_t count = 0;
char buffer[17];
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println();
Serial.println(__FILE__);
rs485.setMicrosPerByte(115200);
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
}
void loop()
{
if (rs485.available() > 0)
{
// toggle on receive
digitalWrite(LED, !digitalRead(LED));
if (count % 4 == 0)
{
Serial.print(' ');
}
if (count % 16 == 0)
{
Serial.print(" ");
Serial.println(buffer);
if (count % 160 == 0)
{
Serial.println();
}
Serial.print(count, HEX);
Serial.print('\t');
}
int x = rs485.read();
// HEX DUMP PART
if (x < 0x10) Serial.print('0');
Serial.print(x, HEX);
Serial.print(" ");
// fill ASCII buffer
buffer[count % 16] = isprint(x) ? x : '.';
buffer[count % 16 + 1] = 0;
count++;
}
}
// -- END OF FILE --

View File

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

View File

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