RS485

https://en.wikipedia.org/wiki/RS-485

RS485 is a very inexpensive way to wire devices together in a network. A single twisted pair (such as two wires from regular UTP network cable) at low speeds can operate over a kilometre.

Devices must be daisy-chained together, and long runs should be properly terminated typically with 120 ohm resistors at either end.

Connecting devices in star topology causes signal reflections and generally won’t work except for very short runs.

One solution is to use multiple MAX485 (or similar) transceivers to create multiple physical network segments. Only one UART is required unless you need concurrent requests.

../../../../../../_images/multiplex.png

This example allows for one transceiver to be in receive mode, with others in transmit. That means slave addresses must be unique across both segments. Resistors R2 and R8 are current limit resistors to guard against both transceivers being switched into receive mode at the same time.

The logic for controlling this would be in a callback registered via IO::RS485::Controller::onSetDirection().

For completeness, here’s example connections for a 3.3v transceiver:

../../../../../../_images/max485.png

R22 is optional, typically installed only on longer runs. D3 is additional transient protection - always a good idea as a first line of defence even if the transceiver itself has some built in.

namespace RS485

RS485/Controller.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

RS485/Device.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

RS485/Request.h

Copyright 2022 mikee47 mike@sillyhouse.net

This file is part of the IOControl Library

This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 or later.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this library. If not, see https://www.gnu.org/licenses/.

Variables

constexpr unsigned DEFAULT_BAUDRATE = 9600
constexpr unsigned DEFAULT_TIMEOUT = 800
class Controller : public IO::Controller
#include <Controller.h>

Public Types

using SetDirectionCallback = void (*)(uint8_t segment, Direction direction)

Callback to handle hardware transmit/receive selection Typically called from interrupt context so implementation MUST be marked IRAM_ATTR.

Param segment:

Identifies physical connection for shared/multiplexed port

Param direction:

Public Functions

inline virtual const FlashString &classname() const override

Get the class name for this Controller.

virtual void start() override

Start the controller.

Controllers may now initiate communications with registered devices. Typically they’ll query device status, etc.

virtual void stop() override

Stop all controllers.

This method is called by the Device Manager, applications shouldn’t need it.

Note

MUST call canStop() first to ensure it’s safe to stop!

inline void onSetDirection(SetDirectionCallback callback)

Set the transmit callback handler.

Typically with RS485 a GPIO is used to toggle between transmit/receive modes. Using a callback allows flexibility for your particular hardware implementation. You don’t need to set this up if your hardware handles the switch automatically.

Parameters:

callback

inline void setDirection(IO::Direction direction)

Whilst a port is acquired, call this method to being or end transmission.

Note

Port should normally be left in receive mode on request completion.

Parameters:

direction

class Device : public IO::Device
#include <Device.h>

Base device class for communicating with an RS485 slave.

Subclassed by IO::DMX512::Device, IO::Modbus::Device

Public Functions

inline virtual uint16_t address() const override

Devices with a numeric address should implement this method.

virtual void handleEvent(IO::Request *request, Event event) override

Implementations may override this method to customise event handling.

struct Config
#include <Device.h>

RS485 configuration.

struct Slave
#include <Device.h>

Public Members

uint16_t address

Network device address or ‘slave ID’

uint8_t segment

Application-defined value for multiplexed serial ports. For example, several MAX485 transceivers can be connected to one serial port with appropriate multiplexing logic.

See IO::RS485::Controller::SetDirectionCallback

unsigned baudrate

Serial link speed

unsigned timeout

Max time between command/response in milliseconds.

template<class DeviceClass>
class FactoryTemplate : public IO::Device::Factory
#include <Device.h>

Subclassed by IO::DMX512::Device::Factory, IO::Modbus::Device::Factory, IO::Modbus::NT18B07::Device::Factory, IO::Modbus::R421A::Device::Factory, IO::Modbus::RID35::Device::Factory, IO::Modbus::STM8Relay::Device::Factory, IO::Modbus::STS::Fan::Device::Factory

Public Functions

inline virtual IO::Device *createDevice(IO::Controller &controller, const char *id) const override

Create a new device instance.

Called by DeviceManager::createDevice()

Parameters:
  • controller – The owning controller

  • id – Unique identifier for the device

Return values:

Device* – The constructed instance

inline virtual const FlashString &controllerClass() const override

Return the expected controller type for this device class, e.g. ‘rs485’.

The Device Manager uses this value to verify that devices are constructed using the correct controller.

class Request : public IO::Request
#include <Request.h>

Subclassed by IO::Modbus::Request