First commit

This commit is contained in:
marcel
2023-12-29 13:49:44 +01:00
commit 7d38f2cb37
261 changed files with 38553 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
Copyright (c) 2015, Andr<64> Sarmento Barbosa
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,140 @@
# Modbus Serial Library for Arduino
Over Serial Line Implementation RTU (OSI 2/OSI 1)
[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/epsilonrt/modbus-serial?include_prereleases)](https://github.com/epsilonrt/modbus-serial/releases)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/epsilonrt/library/modbus-serial.svg)](https://registry.platformio.org/libraries/epsilonrt/modbus-serial)
[![Arduino Registry](https://www.ardu-badge.com/badge/Modbus-Serial.svg)](https://www.arduinolibraries.info/libraries/modbus-serial)
[![Framework](https://img.shields.io/badge/Framework-Arduino-blue)](https://www.arduino.cc/)
[![Uno](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_uno.yml/badge.svg)](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_uno.yml)
[![Due](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_due.yml/badge.svg)](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_due.yml)
[![Mega](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_mega.yml/badge.svg)](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_mega.yml)
[![Nano33IoT](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_nano33iot.yml/badge.svg)](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_nano33iot.yml)
[![Teensy](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_teensy.yml/badge.svg)](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_teensy.yml)
[![Esp8266](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_esp8266.yml/badge.svg)](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_esp8266.yml)
[![Esp32](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_esp32.yml/badge.svg)](https://github.com/epsilonrt/modbus-serial/actions/workflows/build_esp32.yml)
---
This library allows your Arduino to communicate via Modbus protocol. The Modbus is a master-slave protocol
used in industrial automation and can be used in other areas, such as home automation.
In the current version the library allows the Arduino operate **as a slave**, supporting Modbus over Serial Line RTU.
## How to
There are four classes corresponding to five headers that may be used:
* [Modbus-Arduino](http://github.com/epsilonrt/modbus-arduino ) - Base Library
* Modbus-Serial - Modbus Serial RTU Library (this repository)
* [Modbus-Ethernet](https://github.com/epsilonrt/modbus-ethernet) - Modbus TCP Library (standard Ethernet Shield)
* [Modbus-EtherCard](https://github.com/epsilonrt/modbus-ethercard) - Modbus TCP Library (for ENC28J60 chip)
* [Modbus-Esp8266AT](https://github.com/epsilonrt/modbus-esp8266at) - Modbus IP Library (for ESP8266 chip with AT firmware)
By opting for Modbus Serial or Modbus TCP you must include in your sketch the corresponding header, eg:
#include <ModbusSerial.h>
## Modbus-Serial
There are five examples that can be accessed from the Arduino IDE or Visual
Studio Code with Platformio, once you have installed the library.
Let's look at the example Lamp.ino (only the parts concerning Modbus will be commented):
#include <ModbusSerial.h>
Inclusion of the necessary header.
const int Lamp1Coil = 0;
Sets the Modbus register to represent a lamp or LED. This value is the offset (0-based) to be placed in its supervisory or testing software.
Note that if your software uses offsets 1-based the set value there should be 1, for this example.
ModbusSerial mb (Serial, SlaveId);
Create the mb instance (ModbusSerial) to be used with the serial port `Serial`.
Note that the serial port is passed as reference, which permits the use of other
serial ports in other Arduino models, also ets the slave Id.
If you are using RS-485 driver, the configuration of another pin to control
transmission/reception is required, this pin would be connected to the DE and
/RE pins of the driver. This is done as follows:
const int TxenPin = 4;
// ....
ModbusSerial mb (Serial, SlaveId, TxenPin);
In this case, the pin 4 will be used to control TX/RX (DE must be connected with /RE).
Serial.begin (Baudrate, MB_PARITY_EVEN);
while (! Serial)
;
Configure the serial port and wait for it to be ready.
To simplify the modification of the port, you can define a macro in the following way :
#define MySerial Serial // define serial port used, Serial most of the time, or Serial1, Serial2 ... if available
Then the block above can be written as follows :
ModbusSerial mb (MySerial, SlaveId, TxenPin);
void setup() {
MySerial.begin (Baudrate, MB_PARITY_EVEN); // prefer this line in accordance with the modbus standard.
while (! MySerial)
;
mb.config (Baudrate);
The last line configures the ModbusSerial object.
mb.addCoil (Lamp1Coil);
Adds the register type Coil (digital output) that will be responsible for
activating the LED or lamp and verify their status.
The library allows you to set an initial value for the register:
mb.addCoil (Lamp1Coil, true);
In this case the register is added and set to true. If you use the first form
the default value is false.
mb.task ();
This method makes all magic, answering requests and changing the registers if
necessary, it should be called only once, early in the loop.
digitalWrite (LedPin, mb.coil (Lamp1Coil));
Finally the value of Lamp1Coil register is used to drive the lamp or LED.
In much the same way, the other examples show the use of other methods available in the library:
void addCoil (offset word, bool value)
void addHreg (offset word, word value)
void addIsts (offset word, bool value)
void addIreg (offset word, word value)
Adds registers and configures initial value if specified.
bool setCoil (offset word, bool value)
bool setHreg (offset word, word value)
bool setIsts (offset word, bool value)
bool setIReg (offset word, word value)
Sets a value to the register.
bool coil (offset word)
word hreg (word offset)
bool ists (offset word)
word ireg (word offset)
Returns the value of a register.
License
=======
The code in this repo is licensed under the BSD New License. See LICENSE for more info.

View File

@@ -0,0 +1,47 @@
/**
@file Lamp.ino
Modbus-Arduino Example - Lamp (Modbus Serial)
Copyright (C) 2023 Pascal JEAN aka epsilonrt
Copyright (C) 2014 André Sarmento Barbosa
https://github.com/epsilonrt/modbus-serial
*/
#include <ModbusSerial.h>
// Used Pins
const int LedPin = 13; // Change that to match your led
const int TxenPin = -1; // -1 disables the feature, change that if you are using an RS485 driver, this pin would be connected to the DE and /RE pins of the driver.
const byte SlaveId = 10;
// Modbus Registers Offsets (0-9999)
const int Lamp1Coil = 0;
#define MySerial Serial // define serial port used, Serial most of the time, or Serial1, Serial2 ... if available
const unsigned long Baudrate = 38400;
// ModbusSerial object
ModbusSerial mb (MySerial, SlaveId, TxenPin);
void setup() {
MySerial.begin (Baudrate); // works on all boards but the configuration is 8N1 which is incompatible with the MODBUS standard
// prefer the line below instead if possible
// MySerial.begin (Baudrate, MB_PARITY_EVEN);
mb.config (Baudrate);
mb.setAdditionalServerData ("LAMP"); // for Report Server ID function (0x11)
// Set LedPin mode
pinMode (LedPin, OUTPUT);
// Add Lamp1Coil register - Use addCoil() for digital outputs
mb.addCoil (Lamp1Coil);
}
void loop() {
// Call once inside loop() - all magic here
mb.task();
// Attach LedPin to Lamp1Coil register
digitalWrite (LedPin, mb.Coil (Lamp1Coil));
}

View File

@@ -0,0 +1,83 @@
/**
@file LampDimmer.ino
Modbus-Arduino Example - Lamp dimmer (Modbus Serial)
Copyright (C) 2023 Pascal JEAN aka epsilonrt
https://github.com/epsilonrt/modbus-serial
*/
#include <ModbusSerial.h>
// Used Pins
const int LedPin = 3; // Must be PWM output !
const int TxenPin = -1; // -1 disables the feature, change that if you are using an RS485 driver, this pin would be connected to the DE and /RE pins of the driver.
const byte SlaveId = 11; // our Modbus slave address (1-247)
// Modbus Registers Offsets (0-9999), PDU adressing ! from the data model we have to add 1
const int Lamp1Coil = 0;
const int Lamp1Hreg = 0;
#define MySerial Serial // define serial port used, Serial most of the time, or Serial1, Serial2 ... if available
const unsigned long Baudrate = 38400;
// ModbusSerial object
ModbusSerial mb (MySerial, SlaveId, TxenPin);
// Lamp status variables
bool lampOn = true;
word lampDimming = 128;
void setup() {
MySerial.begin (Baudrate); // works on all boards but the configuration is 8N1 which is incompatible with the MODBUS standard
// prefer the line below instead if possible
// MySerial.begin (Baudrate, MB_PARITY_EVEN);
mb.config (Baudrate);
mb.setAdditionalServerData ("LDIMMER"); // for Report Server ID function (0x11)
// Set LedPin
pinMode (LedPin, OUTPUT);
mb.addCoil (Lamp1Coil, lampOn);
mb.addHreg (Lamp1Hreg, lampDimming);
// The led flashes 20 times to signal the end of setup,
// then it is set to its default state.
for (int i = 0; i < 20; i++) {
digitalWrite (LedPin, HIGH);
delay (50);
digitalWrite (LedPin, LOW);
delay (100);
}
analogWrite (LedPin, (lampOn ? lampDimming : 0));
}
void loop() {
// Call once inside loop() - all magic here
mb.task();
if (mb.Coil (Lamp1Coil) != lampOn) {
// If coil was modified
lampOn = mb.Coil (Lamp1Coil);
if (lampOn) {
analogWrite (LedPin, lampDimming);
}
else {
analogWrite (LedPin, 0);
}
}
if ( (mb.Hreg (Lamp1Hreg) & 0x00FF) != lampDimming) {
// If holding register was modified (only LSB will used)
lampDimming = (mb.Hreg (Lamp1Hreg) & 0x00FF);
if (lampOn) {
analogWrite (LedPin, lampDimming);
}
}
}

View File

@@ -0,0 +1,54 @@
/**
@file Servo.ino
Copyright (C) 2023 Pascal JEAN aka epsilonrt
Copyright (C) 2014 André Sarmento Barbosa
https://github.com/epsilonrt/modbus-serial
Use Servo lib
This library only supports boards with an AVR, SAM, SAMD, NRF52 or STM32F4 processor.
https://registry.platformio.org/libraries/arduino-libraries/Servo
*/
#include <ModbusSerial.h>
#include <Servo.h>
// Used Pins
const int servoPin = 9;
const int TxenPin = -1; // -1 disables the feature, change that if you are using an RS485 driver, this pin would be connected to the DE and /RE pins of the driver.
const byte SlaveId = 12;
// Modbus Registers Offsets (0-9999)
const int ServoHreg = 0;
#define MySerial Serial // define serial port used, Serial most of the time, or Serial1, Serial2 ... if available
const unsigned long Baudrate = 38400;
// ModbusSerial object
ModbusSerial mb (MySerial, SlaveId, TxenPin);
// Servo object
Servo servo;
void setup() {
MySerial.begin (Baudrate); // works on all boards but the configuration is 8N1 which is incompatible with the MODBUS standard
// prefer the line below instead if possible
// MySerial.begin (Baudrate, MB_PARITY_EVEN);
mb.config (Baudrate);
mb.setAdditionalServerData ("SWITCH"); // for Report Server ID function (0x11)
// Attaches the servo pin to the servo object
servo.attach (servoPin);
// Add ServoHreg register - Use addHreg() for analog outpus or to store values in device
mb.addHreg (ServoHreg, 127);
}
void loop() {
//Call once inside loop() - all magic here
mb.task();
//Attach switchPin to SWITCH_ISTS register
servo.write (mb.Hreg (ServoHreg));
delay (15);
}

View File

@@ -0,0 +1,46 @@
/**
@file Switch.ino
Modbus-Arduino Example - Switch (Modbus Serial)
Copyright (C) 2023 Pascal JEAN aka epsilonrt
Copyright (C) 2014 André Sarmento Barbosa
https://github.com/epsilonrt/modbus-serial
*/
#include <ModbusSerial.h>
// Used Pins
const int SwitchPin = 3;
const int TxenPin = -1; // -1 disables the feature, change that if you are using an RS485 driver, this pin would be connected to the DE and /RE pins of the driver.
const byte SlaveId = 13;
// Modbus Registers Offsets (0-9999)
const int SwitchIsts = 0;
#define MySerial Serial // define serial port used, Serial most of the time, or Serial1, Serial2 ... if available
const unsigned long Baudrate = 38400;
// ModbusSerial object
ModbusSerial mb (MySerial, SlaveId, TxenPin);
void setup() {
MySerial.begin (Baudrate); // works on all boards but the configuration is 8N1 which is incompatible with the MODBUS standard
// prefer the line below instead if possible
// MySerial.begin (Baudrate, MB_PARITY_EVEN);
mb.config (Baudrate);
mb.setAdditionalServerData ("SWITCH"); // for Report Server ID function (0x11)
// Set SwitchPin mode
pinMode (SwitchPin, INPUT);
// Add SwitchIsts register - Use addIsts() for digital inputs
mb.addIsts (SwitchIsts);
}
void loop() {
//Call once inside loop() - all magic here
mb.task();
//Attach SwitchPin to SwitchIsts register
mb.Ists (SwitchIsts, digitalRead (SwitchPin));
}

View File

@@ -0,0 +1,55 @@
/**
@file TempSensor.ino
Modbus-Arduino Example - TempSensor (Modbus Serial)
Copyright (C) 2023 Pascal JEAN aka epsilonrt
Copyright (C) 2014 André Sarmento Barbosa
https://github.com/epsilonrt/modbus-serial
*/
#include <ModbusSerial.h>
// Used Pins
const int SensorPin = A0;
const int TxenPin = -1; // -1 disables the feature, change that if you are using an RS485 driver, this pin would be connected to the DE and /RE pins of the driver.
const byte SlaveId = 14;
// Modbus Registers Offsets (0-9999)
const int SensorIreg = 0;
#define MySerial Serial // define serial port used, Serial most of the time, or Serial1, Serial2 ... if available
const unsigned long Baudrate = 38400;
// ModbusSerial object
ModbusSerial mb (MySerial, SlaveId, TxenPin);
long ts;
void setup() {
MySerial.begin (Baudrate); // works on all boards but the configuration is 8N1 which is incompatible with the MODBUS standard
// prefer the line below instead if possible
// MySerial.begin (Baudrate, MB_PARITY_EVEN);
mb.config (Baudrate);
mb.setAdditionalServerData ("TEMP_SENSOR"); // for Report Server ID function (0x11)
// Add SensorIreg register - Use addIreg() for analog Inputs
mb.addIreg (SensorIreg);
ts = millis();
}
void loop() {
// Call once inside loop() - all magic here
mb.task();
// Read each two seconds
if (millis() > ts + 2000) {
ts = millis();
// Setting raw value (0-1024)
mb.Ireg (SensorIreg, analogRead (SensorPin));
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,15 @@
# Syntax Coloring Map For ModbusSerial
# Datatypes (KEYWORD1)
ModbusSerial KEYWORD1
# Methods and Functions (KEYWORD2)
setSlaveId KEYWORD2
getSlaveId KEYWORD2
config KEYWORD2
task KEYWORD2
# Constants (LITERAL1)
MB_PARITY_NONE LITERAL1
MB_PARITY_EVEN LITERAL1
MB_PARITY_ODD LITERAL1

View File

@@ -0,0 +1,33 @@
{
"name": "Modbus-Serial",
"version": "2.0.6",
"keywords": "modbus, server, slave, rtu, serial",
"description": "A library that allows your Arduino to communicate via Modbus protocol, acting as a slave. Over serial line implementation (OSI 2/OSI 1)",
"homepage": "https://epsilonrt.github.io/modbus-serial",
"authors":
[
{
"name": "Pascal JEAN aka epsilonrt",
"url": "https://github.com/epsilonrt",
"maintainer": true
},
{
"name": "André Sarmento Barbosa",
"url": "https://github.com/andresarmento"
}
],
"repository":
{
"type": "git",
"url": "https://github.com/epsilonrt/modbus-serial.git"
},
"license": "BSD-3-Clause",
"examples":[
"examples/*/*.ino"
],
"dependencies": {
"epsilonrt/Modbus-Arduino": "^1.2.0"
},
"frameworks": "arduino",
"platforms": "*"
}

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<CodeLite_Project Name="library" Version="10.0.0" InternalType="">
<VirtualDirectory Name=".github">
<VirtualDirectory Name="workflows">
<File Name=".github/workflows/build_teensy.yml"/>
<File Name=".github/workflows/build_due.yml"/>
<File Name=".github/workflows/build_mega.yml"/>
<File Name=".github/workflows/build_nano33iot.yml"/>
<File Name=".github/workflows/build_uno.yml"/>
<File Name=".github/workflows/build_esp32.yml"/>
<File Name=".github/workflows/build_esp8266.yml"/>
<File Name=".github/workflows/doxygen-gh-pages.yml"/>
</VirtualDirectory>
<File Name=".github/stale.yml"/>
</VirtualDirectory>
<VirtualDirectory Name="examples">
<VirtualDirectory Name="Lamp">
<File Name="examples/Lamp/Lamp.ino"/>
</VirtualDirectory>
<VirtualDirectory Name="LampDimmer">
<File Name="examples/LampDimmer/LampDimmer.ino"/>
</VirtualDirectory>
<VirtualDirectory Name="Servo">
<File Name="examples/Servo/Servo.ino"/>
</VirtualDirectory>
<VirtualDirectory Name="Switch">
<File Name="examples/Switch/Switch.ino"/>
</VirtualDirectory>
<VirtualDirectory Name="TempSensor">
<File Name="examples/TempSensor/TempSensor.ino"/>
</VirtualDirectory>
</VirtualDirectory>
<Description/>
<Dependencies/>
<Settings Type="Executable">
<GlobalSettings>
<Compiler Options="" C_Options="" Assembler="">
<IncludePath Value="."/>
</Compiler>
<Linker Options="">
<LibraryPath Value="."/>
</Linker>
<ResourceCompiler Options=""/>
</GlobalSettings>
<Configuration Name="Debug" CompilerType="gnu gcc" DebuggerType="GNU gdb debugger" Type="Executable" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append">
<Compiler Options="-g -Wall" C_Options="" Assembler="" Required="yes" PreCompiledHeader="" PCHInCommandLine="no" PCHFlags="" PCHFlagsPolicy="0">
<IncludePath Value="."/>
</Compiler>
<Linker Options="-O0" Required="yes">
<LibraryPath Value="."/>
<LibraryPath Value="Debug"/>
</Linker>
<ResourceCompiler Options="" Required="no"/>
<General OutputFile="$(IntermediateDirectory)/$(ProjectName)" IntermediateDirectory="./Debug" Command="./$(ProjectName)" CommandArguments="" UseSeparateDebugArgs="no" DebugArguments="" WorkingDirectory="./Debug" PauseExecWhenProcTerminates="no" IsGUIProgram="no" IsEnabled="yes"/>
<BuildSystem Name="Default"/>
<Environment EnvVarSetName="&lt;Use Workspace Settings&gt;" DbgSetName="&lt;Use Global Settings&gt;">
<![CDATA[]]>
</Environment>
<Debugger IsRemote="no" RemoteHostName="" RemoteHostPort="" DebuggerPath="" IsExtended="no">
<DebuggerSearchPaths/>
<PostConnectCommands/>
<StartupCommands/>
</Debugger>
<PreBuild/>
<PostBuild/>
<CustomBuild Enabled="no">
<RebuildCommand/>
<CleanCommand/>
<BuildCommand/>
<PreprocessFileCommand/>
<SingleFileCommand/>
<MakefileGenerationCommand/>
<ThirdPartyToolName/>
<WorkingDirectory/>
</CustomBuild>
<AdditionalRules>
<CustomPostBuild/>
<CustomPreBuild/>
</AdditionalRules>
<Completion EnableCpp11="no" EnableCpp14="no">
<ClangCmpFlagsC/>
<ClangCmpFlags/>
<ClangPP/>
<SearchPaths/>
</Completion>
</Configuration>
</Settings>
<VirtualDirectory Name="src">
<File Name="src/ModbusSerial.cpp"/>
<File Name="src/ModbusSerial.h"/>
</VirtualDirectory>
<VirtualDirectory Name="modbus-serial">
<File Name=".gitignore"/>
<File Name="library.json"/>
<File Name="LICENSE"/>
<File Name="README.md"/>
<File Name="keywords.txt"/>
<File Name="library.properties"/>
<File Name="Doxyfile"/>
</VirtualDirectory>
</CodeLite_Project>

View File

@@ -0,0 +1,10 @@
name=Modbus-Serial
version=2.0.6
author=Pascal Jean aka epsilonrt,André Sarmento Barbosa
maintainer=epsilonrt
sentence=A library that allows your Arduino to communicate via Modbus protocol, acting as a slave.
paragraph=Over serial line implementation (OSI 2/OSI 1)
category=Communication
url=https://epsilonrt.github.io/modbus-serial
architectures=*
license=BSD-3-Clause

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<CodeLite_Workspace Name="modbus-serial" Database="" Version="10.0.0">
<Project Name="library" Path="library.project" Active="Yes"/>
<BuildMatrix>
<WorkspaceConfiguration Name="Debug" Selected="yes">
<Environment/>
<Project Name="library" ConfigName="Debug"/>
</WorkspaceConfiguration>
<WorkspaceConfiguration Name="Release" Selected="yes">
<Environment/>
<Project Name="library" ConfigName="Debug"/>
</WorkspaceConfiguration>
</BuildMatrix>
</CodeLite_Workspace>

View File

@@ -0,0 +1,239 @@
/*
ModbusSerial.cpp - Source for Modbus Serial Library
Copyright (C) 2023 Pascal JEAN aka epsilonrt
Copyright (C) 2014 André Sarmento Barbosa
*/
#include "ModbusSerial.h"
ModbusSerial::ModbusSerial(Stream & port, byte slaveId, int txenPin) :
m_stream(&port), m_txenPin(txenPin), m_slaveId(slaveId)
{}
void ModbusSerial::config (unsigned long baud) {
if (m_txenPin >= 0) {
pinMode (m_txenPin, OUTPUT);
digitalWrite (m_txenPin, LOW);
}
if (baud > 19200) {
m_t15 = 750;
m_t35 = 1750;
}
else {
m_t15 = 16500000UL / baud; // 1T * 1.5 = T1.5, 1T = 11 bits
m_t35 = 38500000UL / baud; // 1T * 3.5 = T3.5, 1T = 11 bits
}
}
void ModbusSerial::task() {
_len = 0;
while ( m_stream->available() > _len) {
_len = m_stream->available();
delayMicroseconds (m_t15);
}
if (_len == 0) {
return;
}
byte i;
_frame = (byte*) malloc (_len);
for (i = 0 ; i < _len ; i++) {
_frame[i] = m_stream->read();
}
if (receive (_frame)) {
if (_reply == MB_REPLY_NORMAL) {
sendPDU (_frame);
}
else if (_reply == MB_REPLY_ECHO) {
send (_frame);
}
}
free (_frame);
_len = 0;
}
void ModbusSerial::setSlaveId (byte slaveId) {
m_slaveId = slaveId;
}
byte ModbusSerial::getSlaveId() {
return m_slaveId;
}
// protected
bool ModbusSerial::receive (byte* frame) {
//first byte of frame = address
byte address = frame[0];
//Last two bytes = crc
word crc = ( (frame[_len - 2] << 8) | frame[_len - 1]);
//Slave Check
if (address != BroadcastAddress && address != getSlaveId()) {
return false;
}
//CRC Check
if (crc != calcCrc (_frame[0], _frame + 1, _len - 3)) {
return false;
}
//PDU starts after first byte
//framesize PDU = framesize - address(1) - crc(2)
receivePDU (frame + 1);
//No reply to Broadcasts
if (address == BroadcastAddress) {
_reply = MB_REPLY_OFF;
}
return true;
}
// protected
bool ModbusSerial::send (byte* frame) {
byte i;
if (m_txenPin >= 0) {
digitalWrite (m_txenPin, HIGH);
delay (1);
}
for (i = 0 ; i < _len ; i++) {
m_stream->write (frame[i]);
}
m_stream->flush();
delayMicroseconds (m_t35);
if (m_txenPin >= 0) {
digitalWrite (m_txenPin, LOW);
}
return true;
}
// protected
bool ModbusSerial::sendPDU (byte* pduframe) {
if (m_txenPin >= 0) {
digitalWrite (m_txenPin, HIGH);
delay (1);
}
//Send slaveId
m_stream->write (m_slaveId);
//Send PDU
byte i;
for (i = 0 ; i < _len ; i++) {
m_stream->write (pduframe[i]);
}
//Send CRC
word crc = calcCrc (m_slaveId, _frame, _len);
m_stream->write (crc >> 8);
m_stream->write (crc & 0xFF);
m_stream->flush();
delayMicroseconds (m_t35);
if (m_txenPin >= 0) {
digitalWrite (m_txenPin, LOW);
}
return true;
}
// protected
void ModbusSerial::reportServerId() {
Modbus::reportServerId();
_frame[2] = getSlaveId(); // Server ID
}
// private
/* Table of CRC values for high–order byte */
const byte _auchCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40
};
/* Table of CRC values for low–order byte */
const byte _auchCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40
};
word ModbusSerial::calcCrc (byte address, byte* pduFrame, byte pduLen) {
byte CRCHi = 0xFF, CRCLo = 0x0FF, Index;
Index = CRCHi ^ address;
CRCHi = CRCLo ^ _auchCRCHi[Index];
CRCLo = _auchCRCLo[Index];
while (pduLen--) {
Index = CRCHi ^ *pduFrame++;
CRCHi = CRCLo ^ _auchCRCHi[Index];
CRCLo = _auchCRCLo[Index];
}
return (CRCHi << 8) | CRCLo;
}

View File

@@ -0,0 +1,111 @@
/*
ModbusSerial.h - Header for ModbusSerial Library
Copyright (C) 2023 Pascal JEAN aka epsilonrt
Copyright (C) 2014 André Sarmento Barbosa
*/
#include <Arduino.h>
#include <Modbus.h>
#pragma once
/**
* @enum MB_PARITY
* @brief Parity
*
* See paragraph [2.5.1 RTU Transmission Mode] from
* MODBUS over serial line specification and implementation guide V1.02
* http://www.modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
*
* The format (11 bits) for each byte in RTU mode is :
* Coding System:
*
* * 8bit binary
*
* Bits per Byte:
*
* * 1 start bit
* * 8 data bits, least significant bit sent first
* * 1 bit for parity completion
* * 1 stop bit
* .
*
* Even parity is required, other modes ( odd parity, no parity )
* may also be used. In order to ensure a maximum compatibility with
* other products, it is recommended to support also No parity mode.
* The default parity mode must be even parity.
* Remark : the use of no parity requires 2 stop bits.
*/
enum MB_PARITY {
MB_PARITY_NONE = SERIAL_8N2, ///< No parity
MB_PARITY_EVEN = SERIAL_8E1, ///< Even parity
MB_PARITY_ODD = SERIAL_8O1 ///< Odd parity
};
/**
* @class ModbusSerial
* @brief Modbus over serial line Class
*/
class ModbusSerial : public Modbus {
public:
/**
* @brief Constructor
* @param port serial port to use
* @param txenPin if an RS485 circuit is used, this corresponds to the
* pin number connected to the transmit enable (DE) and receive disable (/RE) pin.
* -1 if not used.
*/
ModbusSerial (Stream & port, byte slaveId, int txenPin = -1);
/**
* @brief Configure ModbusSerial object
*
* configures txenPin on output if necessary and calculates the times of the RTU frame.
*
* @warning the \c begin() function of the serial port passed to the constructor
* must be called **BEFORE** calling this function.
*
* @param baud baudrate in accordance with that used by the serial port
*/
void config (unsigned long baud);
/**
* @brief Task that performs all operations on MODBUS
*
* Call once inside loop(), all magic here !
*/
void task();
/**
* @brief Change the value of slave identifier
* @param slaveId identifier 1-247
*/
void setSlaveId (byte slaveId);
/**
* @brief Return slave identifier
*/
byte getSlaveId();
/**
* @brief Broadcast address
*/
static const byte BroadcastAddress = 0;
protected:
bool receive (byte * frame);
bool sendPDU (byte * pduframe);
bool send (byte * frame);
void reportServerId();
private:
Stream * m_stream;
int m_txenPin;
byte m_slaveId;
unsigned int m_t15; // inter character time out (us)
unsigned int m_t35; // frame delay (us)
private:
word calcCrc (byte address, byte * pduframe, byte pdulen);
};