Skip to content

Commit 3a09303

Browse files
authored
rename writeStringToBuffer and some more documentation rework. (#53)
* fix handler explanation * Update readme * Update readme * change String to Array and pass uint16_t
1 parent 55681b5 commit 3a09303

File tree

6 files changed

+134
-111
lines changed

6 files changed

+134
-111
lines changed

README.md

Lines changed: 110 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,58 @@
22

33
##### ModbusSlave library for Arduino
44

5-
This modbus slave library uses callbacks to handle modbus requests for one or multiple slave ids.
6-
Handler functions are called on modbus request, and the users can implement them in their sketch.
5+
This Modbus RTU slave library uses callbacks to handle modbus requests for one or multiple slave ids.
6+
Handler functions are called on modbus a request, and the users can implement them within their sketch.
77

88
### ModbusSlave is fun and easy to use
9+
910
Register a handler function:
11+
1012
```c
1113
slave.cbVector[CB_READ_INPUT_REGISTERS] = ReadAnalogIn;
1214
```
15+
1316
Implement it:
17+
1418
```c
1519
void ReadAnalogIn(uint8_t fc, uint16_t address, uint16_t length) {
1620
for (int i = 0; i < length; i++)
1721
slave.writeRegisterToBuffer(i, analogRead(address + i));
1822
}
1923
```
24+
2025
And thats it, your sketch is modbus enabled. (see the full examples for more detail)
2126
22-
----
27+
---
2328
2429
- [Install](#install)
25-
- [Competabilty](#competabilty)
26-
- [Callback vector](#callback-vector)
27-
- [Multiple Slaves](#multiple-slaves)
28-
- [Slots](#slots)
29-
- [Handler function](#handler-function)
30-
- [Function codes](#function-codes)
31-
- [Reading and writing to the request buffer](#reading-and-writing-to-the-request-buffer)
32-
- [Examples](#examples)
33-
- [handle "Force Single Coil" as arduino digitalWrite](#handle-force-single-coil-as-arduino-digitalwrite)
34-
- [handle "Read Input Registers" as arduino analogRead](#handle-read-input-registers-as-arduino-analogread)
35-
36-
----
30+
- [Compatibility](#compatibility)
31+
- [Callback vector](#callback-vector) - [Multiple Slaves](#multiple-slaves) - [Slots](#slots) - [Handler function](#handler-function) - [Function codes](#function-codes) - [Reading and writing to the request buffer](#reading-and-writing-to-the-request-buffer)
32+
- [Examples](#examples) - [handle "Force Single Coil" as arduino digitalWrite](#handle-force-single-coil-as-arduino-digitalwrite) - [handle "Read Input Registers" as arduino analogRead](#handle-read-input-registers-as-arduino-analogread)
33+
34+
---
3735
3836
### Install
3937
4038
Download the zip package, and install it into your Arduino IDE. See the Arduino tutorial about installing 3rd party libraries: https://www.arduino.cc/en/Guide/Libraries#toc4
4139
42-
### Competabilty
40+
### Compatibility
4341
4442
###### This class implements:
4543
46-
* FC1 "Read Coil Status"
47-
* FC2 "Read Input Status"
48-
* FC3 "Read Holding Registers"
49-
* FC4 "Read Input Registers"
50-
* FC5 "Force Single Coil"
51-
* FC6 "Preset Single Register"
52-
* FC15 "Force Multiple Coils"
53-
* FC16 "Preset Multiple Registers"
44+
- FC1 "Read Coil Status"
45+
- FC2 "Read Input Status"
46+
- FC3 "Read Holding Registers"
47+
- FC4 "Read Input Registers"
48+
- FC5 "Force Single Coil"
49+
- FC6 "Preset Single Register"
50+
- FC15 "Force Multiple Coils"
51+
- FC16 "Preset Multiple Registers"
5452
5553
### Serial port
5654
57-
* The default serial port is Serial, but any class that inhirets from Stream can be used.
58-
To set a different Serial class, explicitly set the Stream in the Modbus class constuctor.
55+
- The default serial port is Serial, but any class that inherits from the Stream class can be used.
56+
To set a different Serial class, explicitly pass the Stream in the Modbus class constuctor.
5957
6058
### Callback vector
6159
@@ -67,127 +65,141 @@ This can be done independently for one or multiple slaves with different IDs.
6765
6866
###### Slots
6967
70-
The callback vector has 4 slots for request handlers:
68+
The callback vector has 7 slots for request handlers:
7169
72-
* slave.cbVector[CB_READ_COILS] - called on FC1
73-
* slave.cbVector[CB_READ_DISCRETE_INPUTS] - called on FC2
74-
* slave.cbVector[CB_READ_HOLDING_REGISTERS] - called on FC3
75-
* slave.cbVector[CB_READ_INPUT_REGISTERS] - called on FC4
76-
* slave.cbVector[CB_WRITE_COILS] - called on FC5 and FC15
77-
* slave.cbVector[CB_WRITE_HOLDING_REGISTERS] - called on FC6 and FC16
70+
- slave.cbVector[CB_READ_COILS] - called on FC1
71+
- slave.cbVector[CB_READ_DISCRETE_INPUTS] - called on FC2
72+
- slave.cbVector[CB_READ_HOLDING_REGISTERS] - called on FC3
73+
- slave.cbVector[CB_READ_INPUT_REGISTERS] - called on FC4
74+
- slave.cbVector[CB_WRITE_COILS] - called on FC5 and FC15
75+
- slave.cbVector[CB_WRITE_HOLDING_REGISTERS] - called on FC6 and FC16
76+
- slave.cbVector[CB_READ_EXCEPTION_STATUS] - called on FC7
7877
7978
###### Handler function
8079
81-
Handler functions must return unit8_t and take:
82-
* uint8_t fc - request function code
83-
* uint16_t address - first register / first coil address
84-
* uint16_t length - length of data
85-
86-
Return codes:
87-
88-
* STATUS_OK = 0,
89-
* STATUS_ILLEGAL_FUNCTION,
90-
* STATUS_ILLEGAL_DATA_ADDRESS,
91-
* STATUS_ILLEGAL_DATA_VALUE,
92-
* STATUS_SLAVE_DEVICE_FAILURE,
93-
* STATUS_ACKNOWLEDGE,
94-
* STATUS_SLAVE_DEVICE_BUSY,
95-
* STATUS_NEGATIVE_ACKNOWLEDGE,
96-
* STATUS_MEMORY_PARITY_ERROR,
97-
* STATUS_GATEWAY_PATH_UNAVAILABLE,
98-
* STATUS_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND
80+
A handler functions must return an uint8_t code and take the following as parameters:
81+
82+
- uint8_t fc - request function code
83+
- uint16_t address - first register / first coil address
84+
- uint16_t length - length of data
85+
86+
Usable return codes:
87+
88+
- STATUS_OK = 0,
89+
- STATUS_ILLEGAL_FUNCTION,
90+
- STATUS_ILLEGAL_DATA_ADDRESS,
91+
- STATUS_ILLEGAL_DATA_VALUE,
92+
- STATUS_SLAVE_DEVICE_FAILURE,
93+
- STATUS_ACKNOWLEDGE,
94+
- STATUS_SLAVE_DEVICE_BUSY,
95+
- STATUS_NEGATIVE_ACKNOWLEDGE,
96+
- STATUS_MEMORY_PARITY_ERROR,
97+
- STATUS_GATEWAY_PATH_UNAVAILABLE,
98+
- STATUS_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND
9999
100100
###### Function codes
101101
102-
* FC_READ_COILS = 1
103-
* FC_READ_DISCRETE_INPUT = 2
104-
* FC_READ_REGISTERS = 3
105-
* FC_READ_INPUT_REGISTERS = 4
106-
* FC_WRITE_COIL = 5
107-
* FC_WRITE_REGISTER = 6
108-
* FC_WRITE_MULTIPLE_COILS = 15
109-
* FC_WRITE_MULTIPLE_REGISTERS = 16
102+
- FC_READ_COILS = 1
103+
- FC_READ_DISCRETE_INPUT = 2
104+
- FC_READ_REGISTERS = 3
105+
- FC_READ_INPUT_REGISTERS = 4
106+
- FC_WRITE_COIL = 5
107+
- FC_WRITE_REGISTER = 6
108+
- FC_READ_EXCEPTION_STATUS = 7
109+
- FC_WRITE_MULTIPLE_COILS = 15
110+
- FC_WRITE_MULTIPLE_REGISTERS = 16
110111
111-
----
112+
---
112113
113114
###### Reading and writing to the request / response buffer
114115
115-
* int readCoilFromBuffer(int offset) : read one coil value from the request buffer.
116-
* uint16_t readRegisterFromBuffer(int offset) : read one register value from the request buffer.
117-
* void writeCoilToBuffer(int offset, int state) : write one coil state into the answer buffer.
118-
* void writeRegisterToBuffer(int offset, uint16_t value) : write one register value into the answer buffer.
116+
- bool readCoilFromBuffer(int offset) : read one coil value from the request buffer.
117+
- uint16_t readRegisterFromBuffer(int offset) : read one register value from the request buffer.
118+
- uint8_t writeExceptionStatusToBuffer(int offset, bool status) : write an exception status into the response buffer.
119+
- uint8_t writeCoilToBuffer(int offset, int state) : write one coil state into the response buffer.
120+
- uint8_t writeDiscreteInputToBuffer(int offset, bool state) : write one discrete input value into the response buffer.
121+
- uint8_t writeRegisterToBuffer(int offset, uint16_t value) : write one register value into the response buffer.
122+
- uint8_t writeArrayToBuffer(int offset, uint16_t \*str, uint8_t length); : writes an array of data into the response register.
119123
120-
----
124+
---
121125
122126
### Examples
123127
124-
----
125-
###### handle "Force Single Coil" as arduino digitalWrite
126-
```c
128+
---
129+
130+
###### Handle "Force Single Coil" and write the received value to digitalWrite()
131+
```cpp
127132
#include <ModbusSlave.h>
128133
129-
// implicitly set stream to use the Serial serialport
134+
// Implicitly set stream to use the Serial serialport.
130135
Modbus slave(1, 8); // [stream = Serial,] slave id = 1, rs485 control-pin = 8
131136
132137
void setup() {
133-
// register one handler functions
134-
// if a callback handler is not assigned to a modbus command
135-
// the default handler is called.
136-
// default handlers return a valid but empty replay.
137-
slave.cbVector[CB_WRITE_COILS] = writeDigitlOut;
138-
139-
// start slave at baud 9600 on Serial
140-
Serial.begin( 9600 ); // baud = 9600
141-
slave.begin( 9600 );
138+
// Register functions to call when a certain function code is received.
139+
// If there is no handler assigned to the function code a valid but empty message will be replied.
140+
slave.cbVector[CB_WRITE_COILS] = writeDigitalOut;
141+
142+
// Start the slave at a baudrate of 9600bps on the Serial port.
143+
Serial.begin(9600);
144+
slave.begin(9600);
142145
}
143146
144147
void loop() {
145-
// listen for modbus commands con serial port
148+
// Listen for modbus requests on the serial port.
149+
// When a request is received it's going to get validated.
150+
// And if there is a function registered to the received function code, this function will be executed.
146151
slave.poll();
147152
}
148153
149-
// Handel Force Single Coil (FC=05)
150-
uint8_t writeDigitlOut(uint8_t fc, uint16_t address, uint16_t length) {
151-
if (slave.readCoilFromBuffer(0) == HIGH) {
154+
// Handel Force Single Coil (FC=05).
155+
uint8_t writeDigitalOut(uint8_t fc, uint16_t address, uint16_t length) {
156+
if (slave.readCoilFromBuffer(0) == HIGH)
157+
{
152158
digitalWrite(address, HIGH);
153-
} else {
159+
}
160+
else
161+
{
154162
digitalWrite(address, LOW);
155163
}
156164
return STATUS_OK;
157165
}
158166
159167
```
160168

161-
----
162-
###### handle "Read Input Registers" as arduino analogRead
163-
```c
169+
---
170+
171+
###### Handle "Read Input Registers" and return analogRead()
172+
173+
```cpp
164174
#include <ModbusSlave.h>
165175

166-
// explicitly set stream to use the Serial serialport
176+
// Explicitly set a stream to use the Serial port.
167177
Modbus slave(Serial, 1, 8); // stream = Serial, slave id = 1, rs485 control-pin = 8
168178

169179
void setup() {
170-
// register handler functions
171-
slave.cbVector[CB_READ_INPUT_REGISTERS] = ReadAnalogIn;
172-
173-
// start slave at baud 9600 on Serial
174-
Serial.begin( 9600 ); // baud = 9600
175-
slave.begin( 9600 );
180+
// Register functions to call when a certain function code is received.
181+
// If there is no handler assigned to the function code a valid but empty message will be replied.
182+
slave.cbVector[CB_WRITE_COILS] = readAnalogIn;
183+
184+
// Start the slave at a baudrate of 9600bps on the Serial port.
185+
Serial.begin(9600);
186+
slave.begin(9600);
176187
}
177188

178189
void loop() {
179-
// listen for modbus commands con serial port
190+
// Listen for modbus requests on the serial port.
191+
// When a request is received it's going to get validated.
192+
// And if there is a function registered to the received function code, this function will be executed.
180193
slave.poll();
181194
}
182195

183-
// Handel Read Input Registers (FC=04)
184-
uint8_t ReadAnalogIn(uint8_t fc, uint16_t address, uint16_t length) {
185-
// write registers into the answer buffer
196+
// Handle Read Input Registers (FC=04).
197+
uint8_t readAnalogIn(uint8_t fc, uint16_t address, uint16_t length) {
198+
// Write the result of analogRead() into the response buffer.
186199
for (int i = 0; i < length; i++) {
187200
slave.writeRegisterToBuffer(i, analogRead(address + i));
188201
}
189202
return STATUS_OK;
190203
}
191204

192205
```
193-

examples/full/full.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ void loop()
8686
}
8787

8888
// Modbus handler functions
89-
// The handler functions must return void and take the following parameters:
89+
// The handler functions must return an uint8_t and take the following parameters:
9090
// uint8_t fc - function code
9191
// uint16_t address - first register/coil address
9292
// uint16_t length/status - length of data / coil status

examples/multi/multi.ino

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ void loop()
6262
modbus.poll();
6363
}
6464

65+
// Modbus handler functions
66+
// The handler functions must return an uint8_t and take the following parameters:
67+
// uint8_t fc - function code
68+
// uint16_t address - first register/coil address
69+
// uint16_t length/status - length of data / coil status
70+
6571
// Handle the function code Read Holding Registers (FC=03).
6672
uint8_t readMemorySlave1(uint8_t fc, uint16_t address, uint16_t length)
6773
{

examples/simple/simple.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ void loop()
9292
}
9393

9494
// Modbus handler functions
95-
// The handler functions must return void and take the following parameters:
95+
// The handler functions must return an uint8_t and take the following parameters:
9696
// uint8_t fc - function code
9797
// uint16_t address - first register/coil address
9898
// uint16_t length/status - length of data / coil status

src/ModbusSlave.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -462,31 +462,36 @@ uint8_t Modbus::writeRegisterToBuffer(int offset, uint16_t value)
462462
}
463463

464464
_responseBuffer[index] = value >> 8;
465-
_responseBuffer[index + 1] = value & 0xff;
465+
_responseBuffer[index + 1] = value & 0xFF;
466466

467467
return STATUS_OK;
468468
}
469469

470470
/**
471471
* Writes an uint8_t array to the output buffer.
472472
*
473-
* @param offset The offset from the first register in the buffer.
474-
* @param str The array to write into the buffer.
475-
* @param length The array length.
473+
* @param offset The offset from the first data register in the response buffer.
474+
* @param str The array to write into the response buffer.
475+
* @param length The length of the array.
476476
* @return STATUS_OK if succeeded, STATUS_ILLEGAL_DATA_ADDRESS if the data doesn't fit in the buffer.
477477
*/
478-
uint8_t Modbus::writeStringToBuffer(int offset, uint8_t *str, uint8_t length)
478+
uint8_t Modbus::writeArrayToBuffer(int offset, uint16_t *str, uint8_t length)
479479
{
480-
// (1 x valueBytes, n x values).
480+
// Index to start writing from (1 x valueBytes, n x values (offset)).
481481
uint8_t index = MODBUS_DATA_INDEX + 1 + (offset * 2);
482482

483-
// Check the length of the array.
484-
if ((index + length) > _responseBufferLength - MODBUS_CRC_LENGTH)
483+
// Check if the array fits in the remaining space of the response.
484+
if ((index + (length * 2)) > _responseBufferLength - MODBUS_CRC_LENGTH)
485485
{
486+
// If not return an exception.
486487
return STATUS_ILLEGAL_DATA_ADDRESS;
487488
}
488489

489-
memcpy(_responseBuffer + index, str, length);
490+
for (int i = 0; i < length; i++)
491+
{
492+
_responseBuffer[index + (i * 2)] = str[i] >> 8;
493+
_responseBuffer[index + (i * 2) + 1] = str[i] & 0xFF;
494+
}
490495

491496
return STATUS_OK;
492497
}
@@ -851,7 +856,7 @@ uint16_t Modbus::writeResponse()
851856

852857
// Calculate and add the CRC.
853858
uint16_t crc = Modbus::calculateCRC(_responseBuffer, _responseBufferLength - MODBUS_CRC_LENGTH);
854-
_responseBuffer[_responseBufferLength - MODBUS_CRC_LENGTH] = crc & 0xff;
859+
_responseBuffer[_responseBufferLength - MODBUS_CRC_LENGTH] = crc & 0xFF;
855860
_responseBuffer[(_responseBufferLength - MODBUS_CRC_LENGTH) + 1] = crc >> 8;
856861

857862
// Start transmission mode for RS485.

src/ModbusSlave.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class Modbus
112112
uint8_t writeCoilToBuffer(int offset, bool state);
113113
uint8_t writeDiscreteInputToBuffer(int offset, bool state);
114114
uint8_t writeRegisterToBuffer(int offset, uint16_t value);
115-
uint8_t writeStringToBuffer(int offset, uint8_t *str, uint8_t length);
115+
uint8_t writeArrayToBuffer(int offset, uint16_t *str, uint8_t length);
116116

117117
uint8_t readFunctionCode();
118118
uint8_t readUnitAddress();

0 commit comments

Comments
 (0)