Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: CI

on:
push:
pull_request:

jobs:
format:
name: clang-format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Check formatting
uses: jidicula/clang-format-action@v4.13.0
with:
clang-format-version: "17"
check-path: "."

lint:
name: arduino-lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Lint library
uses: arduino/arduino-lint-action@v3
with:
# Use "submit" until the library is accepted into the Library Manager
# index; switch to "update" once it is listed.
library-manager: submit
compliance: strict

compile:
name: compile examples
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
board:
# Each board carries the platform(s) the FQBN needs. AVR ships with
# the Arduino CLI; the ESP cores are pulled from their package indexes.
- fqbn: arduino:avr:uno
platforms: |
- name: arduino:avr
- fqbn: arduino:avr:mega
platforms: |
- name: arduino:avr
- fqbn: esp8266:esp8266:nodemcuv2
platforms: |
- name: esp8266:esp8266
source-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json
- fqbn: esp32:esp32:esp32
platforms: |
- name: esp32:esp32
source-url: https://espressif.github.io/arduino-esp32/package_esp32_index.json
- fqbn: rp2040:rp2040:rpipico
platforms: |
- name: rp2040:rp2040
source-url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
steps:
- uses: actions/checkout@v7
- name: Compile example sketches
uses: arduino/compile-sketches@v1
with:
fqbn: ${{ matrix.board.fqbn }}
platforms: ${{ matrix.board.platforms }}
libraries: |
- source-path: ./
- source-url: https://github.com/madleech/Auto485.git
sketch-paths: |
- examples/echo
- examples/periodic_transmit_1
- examples/periodic_transmit_2

test:
name: native unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.x"
- name: Install PlatformIO
run: pip install --upgrade platformio
- name: Run tests
run: pio test -e native
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Mac crap
.DS_Store


#############
## PlatformIO
#############

.pio/
.pioenvs/
.piolibdeps/
.vscode/
*.o
*.a
27 changes: 9 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,32 +58,23 @@ In our main loop we check if any input is available, and if it is, we set the bu

Documentation
-------------
**Auto485(int DE_pin)**
Creates a new Auto485 object. The Driver and Receiver Enable pins on the MAX485 are connected together.
`Auto485(int DE_pin)` – Creates a new Auto485 object. The Driver and Receiver Enable pins on the MAX485 are connected together.

**Auto485(int DE_pin, int RE_pin)**
Creates a new Auto485 object. The Driver and Receiver Enable pins on the MAX485 are connected to separate pins on the Arduino.
`Auto485(int DE_pin, int RE_pin)` – Creates a new Auto485 object. The Driver and Receiver Enable pins on the MAX485 are connected to separate pins on the Arduino.

**Auto485(int DE_pin, int RE_pin, HardwareSerial serial_port)**
Creates a new Auto485 object using the specified serial port. Usually the defaults are fine (Serial on most boards, Serial1 on the Leonardo and other USBCOM boards). If you are using a Mega with multiple serial ports this lets you choose which serial port to use.
`Auto485(int DE_pin, int RE_pin, HardwareSerial serial_port)` – Creates a new Auto485 object using the specified serial port. Usually the defaults are fine (Serial on most boards, Serial1 on the Leonardo and other USBCOM boards). If you are using a Mega with multiple serial ports this lets you choose which serial port to use.

**Auto485::TX**, **Auto485::RX**
These are constants to let you toggle the mode of the bus.
`Auto485::TX`, `Auto485::RX` – These are constants to let you toggle the mode of the bus.

**begin(baud), begin(baud, config)**
Initiate a serial connection at the given speed, and optionally with the given config settings (e.g. `SERIAL_8N2` to use two stop bits, etc).
`begin(baud), begin(baud, config)` – Initiate a serial connection at the given speed, and optionally with the given config settings (e.g. `SERIAL_8N2` to use two stop bits, etc).

**set_mode(Auto485::TX)**, **set_mode(Auto485::RX)**
Manually change to transmit or receive mode. When returning to receive mode, the function will pause until all pending serial data has been sent.
`set_mode(Auto485::TX)`, `set_mode(Auto485::RX)` – Manually change to transmit or receive mode. When returning to receive mode, the function will pause until all pending serial data has been sent.

**write(...)**, **print(...)**
When in receive mode, the first call to any output functions will change to transmit mode, then send out the data as expected. It handles all the formatting options of the regular Arduino `print` and `write` functions.
`write(...)`, `print(...)` – When in receive mode, the first call to any output functions will change to transmit mode, then send out the data as expected. It handles all the formatting options of the regular Arduino `print` and `write` functions.

**flush()**
Finish writing data, then switch to receive mode. Usually the serial writing functions happen asynchronously, with no delay while the data is sent out the serial port. When we're operating in half duplex mode though, we need to wait for the data to finish being sent before we change the mode of the bus. By calling `.flush()` we ensure there is no unsent data in the buffer. Once all pending data has been sent, we automatically switch back to receive mode!
`flush()` – Finish writing data, then switch to receive mode. Usually the serial writing functions happen asynchronously, with no delay while the data is sent out the serial port. When we're operating in half duplex mode though, we need to wait for the data to finish being sent before we change the mode of the bus. By calling `.flush()` we ensure there is no unsent data in the buffer. Once all pending data has been sent, we automatically switch back to receive mode!

**println(...)**
Like the `write(...)` and `print(...)` functions, calling `println(...)` will automatically switch to transmit mode. Unlike the lower-level functions though, println will return to receive mode at the end of the line. This means you can easily print simple messages to the bus and everything operates as expected, but if you're sending data byte-by-byte, that functionality if there too and Auto485 won't toggle between RX and TX for every single byte you send.
`println(...)` – Like the `write(...)` and `print(...)` functions, calling `println(...)` will automatically switch to transmit mode. Unlike the lower-level functions though, println will return to receive mode at the end of the line. This means you can easily print simple messages to the bus and everything operates as expected, but if you're sending data byte-by-byte, that functionality if there too and Auto485 won't toggle between RX and TX for every single byte you send.

License
-------
Expand Down
25 changes: 14 additions & 11 deletions examples/echo/echo.ino
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/*
* Listens for data from PC and echos it back.
*
*
* Uses both the "auto" and "manual" modes of Auto485.
*/

Expand All @@ -11,16 +11,19 @@

Auto485 bus(DE_PIN, RE_PIN); // new Auto485 wrapper using DE_PIN & RE_PIN to toggle read/write mode on the MAX485

void setup() {
bus.begin(9600); // open bus at 9600bps
bus.println("Hello world, now listening"); // at the end of println, we return to listening
void setup()
{
bus.begin(9600); // open bus at 9600bps
bus.println("Hello world, now listening"); // at the end of println, we return to listening
}

void loop() {
if (bus.available() > 0) {
while (bus.available() > 0)
bus.write(bus.read()); // mode -> transmit
bus.println(); // mode -> receive
}
delay(1000);
void loop()
{
if (bus.available() > 0)
{
while (bus.available() > 0)
bus.write(bus.read()); // mode -> transmit
bus.println(); // mode -> receive
}
delay(1000);
}
21 changes: 12 additions & 9 deletions examples/periodic_transmit_1/periodic_transmit_1.ino
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/*
* Periodically writes some characters to the hardware serial port,
* which should be hooked up to the MAX485.
*
*
* Uses the "transparent" mode of Auto485, which automatically toggles between TX and RX mode.
*/

Expand All @@ -12,19 +12,22 @@

Auto485 bus(DE_PIN, RE_PIN); // new Auto485 wrapper using DE_PIN & RE_PIN to toggle read/write mode on the MAX485

void setup() {
bus.begin(9600); // start talking at 9600bps
void setup()
{
bus.begin(9600); // start talking at 9600bps
}

int i = 0;
char x[4] = {'A', 'B', 'C', 'D'};

void loop() {
tx(x[i++ % 4]);
delay(500);
void loop()
{
tx(x[i++ % 4]);
delay(500);
}

void tx(char c) {
bus.write(c);
bus.flush(); // since hardware serial is asynchronous. This ends the transmit cycle and returns to receive mode
void tx(char c)
{
bus.write(c);
bus.flush(); // since hardware serial is asynchronous. This ends the transmit cycle and returns to receive mode
}
23 changes: 13 additions & 10 deletions examples/periodic_transmit_2/periodic_transmit_2.ino
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/*
* Periodically writes some characters to the hardware serial port,
* which should be hooked up to the MAX485.
*
*
* Uses the "manual" mode of Auto485, which lets you control TX and RX mode.
*/

Expand All @@ -12,20 +12,23 @@

Auto485 bus(DE_PIN, RE_PIN); // new Auto485 wrapper using DE_PIN & RE_PIN to toggle read/write mode on the MAX485

void setup() {
bus.begin(9600); // start talking at 9600bps
void setup()
{
bus.begin(9600); // start talking at 9600bps
}

int i = 0;
char x[4] = {'A', 'B', 'C', 'D'};

void loop() {
tx(x[i++ % 4]);
delay(500);
void loop()
{
tx(x[i++ % 4]);
delay(500);
}

void tx(char c) {
bus.set_mode(Auto485::TX); // mode = transmit
bus.write(c);
bus.set_mode(Auto485::RX); // mode = receive, will pause until all pending serial data has been transmitted
void tx(char c)
{
bus.set_mode(Auto485::TX); // mode = transmit
bus.write(c);
bus.set_mode(Auto485::RX); // mode = receive, will pause until all pending serial data has been transmitted
}
24 changes: 24 additions & 0 deletions extras/hardware_test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Test on Real Hardware

This is a tiny sketch to test the current library code on a real device. It
drives a half-duplex RS485 transceiver over the hardware Serial port: once a
second it transmits a heartbeat line, and it echoes any received bytes back onto
the bus. Both directions rely on Auto485 flipping the DE/RE pins automatically.

Wiring: Arduino pin 2 -> MAX485 DE and RE (tied together); Serial TX/RX -> the
MAX485 DI/RO.

Build + upload + monitor:
```
$ pio run -d extras/hardware_test -e uno -t upload
$ pio device monitor -b 9600
```

Meaningfully exercising the bus needs a second RS485 node (or a USB RS485
adapter) to talk to. Without one, this still serves as a compile/upload/boot
smoke test against the local library source.
```
$ pio run -d extras/hardware_test -e uno
...
SUCCESS
```
27 changes: 27 additions & 0 deletions extras/hardware_test/platformio.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
; Hardware smoke-test for the Auto485 library.
;
; Builds the demo sketch in src/main.cpp against the LOCAL library source (this
; working tree - i.e. your unreleased changes), not a published release, and
; uploads it to a real board.
;
; lib_extra_dirs points the Library Dependency Finder at the directory that
; CONTAINS this library (the parent folder), so PlatformIO reads the source in
; place - no copying and no symlinks. Editing ../../src/Auto485.* and re-running
; upload immediately tests the new code.
;
; pio run -d extras/hardware_test -e uno -t upload
; pio device monitor -b 9600
;
; (Run from the repo root. Or `cd extras/hardware_test` and drop the `-d`.)
;
; Note: meaningfully exercising the bus needs a second RS485 node (or a USB
; RS485 adapter) to talk to; without one this is a compile/upload/boot smoke
; test.

[env:uno]
platform = atmelavr
board = uno
framework = arduino
lib_extra_dirs = ${PROJECT_DIR}/../../..
lib_deps = Auto485
monitor_speed = 9600
53 changes: 53 additions & 0 deletions extras/hardware_test/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Hardware smoke-test for Auto485.

Drives a half-duplex RS485 transceiver (e.g. a MAX485) over the hardware
Serial port, exercising both directions of the automatic DE/RE switching:

- every second it transmits a heartbeat line (write + println), which
flips the driver to TX and back to RX automatically
- any bytes received are echoed straight back, and the trailing println()
returns the bus to RX

Wiring: Arduino pin 2 -> MAX485 DE and RE (tied together), Serial TX/RX ->
the MAX485 DI/RO. Connect A/B to a second RS485 node or a USB RS485 adapter
to see the traffic; without a peer this simply confirms the sketch builds,
uploads and boots against the local source.

Upload + monitor:
pio run -d extras/hardware_test -e uno -t upload
pio device monitor -b 9600
*/

#include <Arduino.h>
#include <Auto485.h>

const int DE_PIN = 2;

Auto485 bus(DE_PIN); // pin 2 -> MAX485 DE and RE

void setup()
{
bus.begin(9600); // must match the speed of the other node
}

unsigned long last_beat = 0;

void loop()
{
// Echo anything received back onto the bus.
if (bus.available() > 0)
{
while (bus.available() > 0)
bus.write(bus.read()); // switches to TX
bus.println(); // completes the line and returns to RX
}

// Transmit a heartbeat once a second.
if (millis() - last_beat >= 1000)
{
last_beat = millis();
bus.print("beat ");
bus.println(last_beat / 1000); // returns to RX when done
}
}
8 changes: 2 additions & 6 deletions library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Auto485",
"version": "1.0.0",
"version": "1.1.0",
"description": "Auto485 - a small helper library that takes some of the tedium out of RS485 communication. It automatically handles the DE/RE pin toggling for half-duplex RS485 transceivers.",
"keywords": "RS485, communication, serial, auto485, arduino",
"repository": {
Expand All @@ -17,9 +17,5 @@
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",
"headers": "Auto485.h",
"build": {
"srcDir": ".",
"includeDir": "."
}
"headers": "Auto485.h"
}
Loading