|
4 | 4 | Control and Read Arduino I/Os using Modbus serial connection.
|
5 | 5 |
|
6 | 6 | This sketch show how to use the callback vector for reading and
|
7 |
| - controleing Arduino I/Os. |
| 7 | + controlling Arduino I/Os. |
8 | 8 |
|
9 |
| - * Control digital pins mode using holding registers 0 .. 13. |
10 |
| - * Controls digital output pins as modbus coils. |
11 |
| - * Reads digital inputs state as discreet inputs. |
12 |
| - * Reads analog inputs as input registers. |
| 9 | + * Control digital pins mode using holding registers 0 .. 50. |
| 10 | + * Control digital output pins as modbus coils. |
| 11 | + * Read digital inputs as discreet inputs. |
| 12 | + * Read analog inputs as input registers. |
13 | 13 | * Write and Read EEPROM as holding registers.
|
14 | 14 |
|
15 |
| - The circuit: ( see: ./extras/ModbusSetch.pdf ) |
16 |
| - * An Arduino. |
17 |
| - * 2 x LEDs, with 220 ohm resistors in series. |
18 |
| - * A switch connected to a digital input pin. |
19 |
| - * A potentiometer connected to an analog input pin. |
20 |
| - * A RS485 module (Optional) connected to RX/TX and a digital control pin. |
21 |
| -
|
22 |
| - Created 8 12 2015 |
| 15 | + Created 08-12-2015 |
23 | 16 | By Yaacov Zamir
|
24 | 17 |
|
| 18 | + Updated 31-03-2020 |
| 19 | + By Yorick Smilda |
| 20 | +
|
25 | 21 | https://github.com/yaacov/ArduinoModbusSlave
|
26 | 22 |
|
27 | 23 | */
|
28 | 24 |
|
29 | 25 | #include <EEPROM.h>
|
30 | 26 | #include <ModbusSlave.h>
|
31 | 27 |
|
32 |
| -/* slave id = 1, control-pin = 8, baud = 9600 |
33 |
| - */ |
34 |
| -#define SLAVE_ID 1 |
35 |
| -#define CTRL_PIN 8 |
36 |
| -#define BAUDRATE 9600 |
| 28 | +#define SLAVE_ID 1 // The Modbus slave ID, change to the ID you want to use. |
| 29 | +#define RS485_CTRL_PIN 8 // Change to the pin the RE/DE pin of the RS485 controller is connected to. |
| 30 | +#define SERIAL_BAUDRATE 9600 // Change to the baudrate you want to use for Modbus communication. |
| 31 | +#define SERIAL_PORT Serial // Serial port to use for RS485 communication, change to the port you're using. |
37 | 32 |
|
38 |
| -#define PIN_MODE_INPUT 0 |
39 |
| -#define PIN_MODE_OUTPUT 1 |
| 33 | +// The position in the array determines the address. Position 0 will correspond to Coil, Discrete input or Input register 0. |
| 34 | +uint8_t digital_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; // Add the pins you want to read as a Discrete input. |
| 35 | +uint8_t analog_pins[] = {A0, A1, A2, A3, A4, A5}; // Add the pins you want to read as a Input register. |
40 | 36 |
|
41 |
| -/** |
42 |
| - * Modbus object declaration. |
43 |
| - */ |
44 |
| -Modbus slave(SLAVE_ID, CTRL_PIN); |
| 37 | +// The EEPROM layout is as follows |
| 38 | +// The first 50 bytes are reserved for storing digital pin pinMode_setting |
| 39 | +// Byte 51 and up are free to write any uint16_t to. |
45 | 40 |
|
46 |
| -void setup() { |
47 |
| - uint16_t pinIndex; |
48 |
| - uint16_t eepromValue; |
49 |
| - |
50 |
| - /* set pins for mode. |
51 |
| - */ |
52 |
| - for (pinIndex = 3; pinIndex < 14; pinIndex++) { |
53 |
| - // get one 16bit register from eeprom |
54 |
| - EEPROM.get(pinIndex * 2, eepromValue); |
55 |
| - |
56 |
| - // use the register value to set pin mode. |
57 |
| - switch (eepromValue) { |
58 |
| - case PIN_MODE_INPUT: |
59 |
| - pinMode(pinIndex, INPUT); |
60 |
| - break; |
61 |
| - case PIN_MODE_OUTPUT: |
62 |
| - pinMode(pinIndex, OUTPUT); |
63 |
| - break; |
64 |
| - } |
| 41 | +// You shouldn't have to change anything below this to get this example to work |
| 42 | + |
| 43 | +uint8_t digital_pins_size = sizeof(digital_pins) / sizeof(digital_pins[0]); // Get the size of the digital_pins array |
| 44 | +uint8_t analog_pins_size = sizeof(analog_pins) / sizeof(analog_pins[0]); // Get the size of the analog_pins array |
| 45 | + |
| 46 | +// Modbus object declaration |
| 47 | +Modbus slave(SERIAL_PORT, SLAVE_ID, RS485_CTRL_PIN); |
| 48 | + |
| 49 | +void setup() |
| 50 | +{ |
| 51 | + // Set the defined digital pins to the value stored in EEPROM. |
| 52 | + for (uint16_t i = 0; i < digital_pins_size; i++) |
| 53 | + { |
| 54 | + uint8_t pinMode_setting; |
| 55 | + // Get the pinMode_setting of this digital pin from the EEPROM. |
| 56 | + EEPROM.get(i, pinMode_setting); |
| 57 | + |
| 58 | + pinMode(digital_pins[i], pinMode_setting); |
65 | 59 | }
|
66 |
| - |
67 |
| - // RS485 control pin must be output |
68 |
| - pinMode(CTRL_PIN, OUTPUT); |
69 |
| - |
70 |
| - /* register handler functions. |
71 |
| - * into the modbus slave callback vector. |
72 |
| - */ |
| 60 | + |
| 61 | + // Set the defined analog pins to input mode. |
| 62 | + for (int i = 0; i < analog_pins_size; i++) |
| 63 | + { |
| 64 | + pinMode(analog_pins[i], INPUT); |
| 65 | + } |
| 66 | + |
| 67 | + // Register functions to call when a certain function code is received. |
73 | 68 | slave.cbVector[CB_READ_COILS] = readDigital;
|
74 | 69 | slave.cbVector[CB_READ_DISCRETE_INPUTS] = readDigital;
|
75 | 70 | slave.cbVector[CB_WRITE_COILS] = writeDigitalOut;
|
76 | 71 | slave.cbVector[CB_READ_INPUT_REGISTERS] = readAnalogIn;
|
77 | 72 | slave.cbVector[CB_READ_HOLDING_REGISTERS] = readMemory;
|
78 | 73 | slave.cbVector[CB_WRITE_HOLDING_REGISTERS] = writeMemory;
|
79 |
| - |
80 |
| - // set Serial and slave at baud 9600. |
81 |
| - Serial.begin( BAUDRATE ); |
82 |
| - slave.begin( BAUDRATE ); |
| 74 | + |
| 75 | + // Set the serial port and slave to the given baudrate. |
| 76 | + SERIAL_PORT.begin(SERIAL_BAUDRATE); |
| 77 | + slave.begin(SERIAL_BAUDRATE); |
83 | 78 | }
|
84 | 79 |
|
85 |
| -void loop() { |
86 |
| - /* listen for modbus commands con serial port. |
87 |
| - * |
88 |
| - * on a request, handle the request. |
89 |
| - * if the request has a user handler function registered in cbVector. |
90 |
| - * call the user handler function. |
91 |
| - */ |
| 80 | +void loop() |
| 81 | +{ |
| 82 | + // Listen for modbus requests on the serial port. |
| 83 | + // When a request is received it's going to get validated. |
| 84 | + // And if there is a function registered to the received function code, this function will be executed. |
92 | 85 | slave.poll();
|
93 | 86 | }
|
94 | 87 |
|
95 |
| -/** |
96 |
| - * Handel Read Input Status (FC=01/02) |
97 |
| - * write back the values from digital pins (input status). |
98 |
| - * |
99 |
| - * handler functions must return void and take: |
100 |
| - * uint8_t fc - function code. |
101 |
| - * uint16_t address - first register/coil address. |
102 |
| - * uint16_t length/status - length of data / coil status. |
103 |
| - */ |
104 |
| -uint8_t readDigital(uint8_t fc, uint16_t address, uint16_t length) { |
105 |
| - // read digital input |
106 |
| - for (int i = 0; i < length; i++) { |
107 |
| - // write one boolean (1 bit) to the response buffer. |
108 |
| - slave.writeCoilToBuffer(i, digitalRead(address + i)); |
| 88 | +// Modbus handler functions |
| 89 | +// The handler functions must return void and take the following parameters: |
| 90 | +// uint8_t fc - function code |
| 91 | +// uint16_t address - first register/coil address |
| 92 | +// uint16_t length/status - length of data / coil status |
| 93 | + |
| 94 | +// Handle the function codes Read Input Status (FC=01/02) and write back the values from the digital pins (input status). |
| 95 | +uint8_t readDigital(uint8_t fc, uint16_t address, uint16_t length) |
| 96 | +{ |
| 97 | + // Check if the requested addresses exist in the array |
| 98 | + if (address > digital_pins_size || (address + length) > digital_pins_size) |
| 99 | + { |
| 100 | + return STATUS_ILLEGAL_DATA_ADDRESS; |
| 101 | + } |
| 102 | + |
| 103 | + // Read the digital inputs. |
| 104 | + for (int i = 0; i < length; i++) |
| 105 | + { |
| 106 | + // Write the state of the digital pin to the response buffer. |
| 107 | + slave.writeCoilToBuffer(i, digitalRead(digital_pins[address + i])); |
109 | 108 | }
|
110 | 109 |
|
111 | 110 | return STATUS_OK;
|
112 | 111 | }
|
113 | 112 |
|
114 |
| -/** |
115 |
| - * Handel Read Holding Registers (FC=03) |
116 |
| - * write back the values from eeprom (holding registers). |
117 |
| - */ |
118 |
| -uint8_t readMemory(uint8_t fc, uint16_t address, uint16_t length) { |
119 |
| - uint16_t value; |
120 |
| - |
121 |
| - // read program memory. |
122 |
| - for (int i = 0; i < length; i++) { |
123 |
| - EEPROM.get((address + i) * 2, value); |
124 |
| - |
125 |
| - // write uint16_t value to the response buffer. |
126 |
| - slave.writeRegisterToBuffer(i, value); |
| 113 | +// Handle the function code Read Holding Registers (FC=03) and write back the values from the EEPROM (holding registers). |
| 114 | +uint8_t readMemory(uint8_t fc, uint16_t address, uint16_t length) |
| 115 | +{ |
| 116 | + // Read the requested EEPROM registers. |
| 117 | + for (int i = 0; i < length; i++) |
| 118 | + { |
| 119 | + // Below 50 is reserved for pinModes, above 50 is free to use. |
| 120 | + if (address + i <= 50) |
| 121 | + { |
| 122 | + uint8_t value; |
| 123 | + |
| 124 | + // Read a value from the EEPROM. |
| 125 | + EEPROM.get((address + i), value); |
| 126 | + |
| 127 | + // Write the pinMode from EEPROM to the response buffer. |
| 128 | + slave.writeRegisterToBuffer(i, value); |
| 129 | + } |
| 130 | + else |
| 131 | + { |
| 132 | + uint16_t value; |
| 133 | + |
| 134 | + // Read a value from the EEPROM. |
| 135 | + EEPROM.get((address + i) * 2, value); |
| 136 | + |
| 137 | + // Write the value from EEPROM to the response buffer. |
| 138 | + slave.writeRegisterToBuffer(i, value); |
| 139 | + } |
127 | 140 | }
|
128 | 141 |
|
129 | 142 | return STATUS_OK;
|
130 | 143 | }
|
131 | 144 |
|
132 |
| -/** |
133 |
| - * Handel Read Input Registers (FC=04) |
134 |
| - * write back the values from analog in pins (input registers). |
135 |
| - */ |
136 |
| -uint8_t readAnalogIn(uint8_t fc, uint16_t address, uint16_t length) { |
137 |
| - // read analog input |
138 |
| - for (int i = 0; i < length; i++) { |
139 |
| - // write uint16_t value to the response buffer. |
140 |
| - slave.writeRegisterToBuffer(i, analogRead(address + i)); |
| 145 | +// Handle the function code Read Input Registers (FC=04) and write back the values from the analog input pins (input registers). |
| 146 | +uint8_t readAnalogIn(uint8_t fc, uint16_t address, uint16_t length) |
| 147 | +{ |
| 148 | + // Check if the requested addresses exist in the array |
| 149 | + if (address > analog_pins_size || (address + length) > analog_pins_size) |
| 150 | + { |
| 151 | + return STATUS_ILLEGAL_DATA_ADDRESS; |
| 152 | + } |
| 153 | + |
| 154 | + // Read the analog inputs |
| 155 | + for (int i = 0; i < length; i++) |
| 156 | + { |
| 157 | + // Write the state of the analog pin to the response buffer. |
| 158 | + slave.writeRegisterToBuffer(i, analogRead(analog_pins[address + i])); |
141 | 159 | }
|
| 160 | + |
| 161 | + return STATUS_OK; |
142 | 162 | }
|
143 | 163 |
|
144 |
| -/** |
145 |
| - * Handle Force Single Coil (FC=05) and Force Multiple Coils (FC=15) |
146 |
| - * set digital output pins (coils). |
147 |
| - */ |
148 |
| -uint8_t writeDigitalOut(uint8_t fc, uint16_t address, uint16_t length) { |
149 |
| - // set digital pin state(s). |
150 |
| - for (int i = 0; i < length; i++) { |
151 |
| - digitalWrite(address + i, slave.readCoilFromBuffer(i)); |
| 164 | +// Handle the function codes Force Single Coil (FC=05) and Force Multiple Coils (FC=15) and set the digital output pins (coils). |
| 165 | +uint8_t writeDigitalOut(uint8_t fc, uint16_t address, uint16_t length) |
| 166 | +{ |
| 167 | + // Check if the requested addresses exist in the array |
| 168 | + if (address > digital_pins_size || (address + length) > digital_pins_size) |
| 169 | + { |
| 170 | + return STATUS_ILLEGAL_DATA_ADDRESS; |
| 171 | + } |
| 172 | + |
| 173 | + // Set the output pins to the given state. |
| 174 | + for (int i = 0; i < length; i++) |
| 175 | + { |
| 176 | + // Write the value in the input buffer to the digital pin. |
| 177 | + digitalWrite(digital_pins[address + i], slave.readCoilFromBuffer(i)); |
152 | 178 | }
|
153 | 179 |
|
154 | 180 | return STATUS_OK;
|
155 | 181 | }
|
156 | 182 |
|
157 |
| -/** |
158 |
| - * Handle Write Holding Register(s) (FC=06, FC=16) |
159 |
| - * write data into eeprom. |
160 |
| - */ |
161 |
| -uint8_t writeMemory(uint8_t fc, uint16_t address, uint16_t length) { |
162 |
| - uint16_t value; |
163 |
| - uint16_t registerIndex; |
164 |
| - |
165 |
| - // write to eeprom. |
166 |
| - for (int i = 0; i < length; i++) { |
167 |
| - registerIndex = address + i; |
168 |
| - |
169 |
| - // get uint16_t value from the request buffer. |
170 |
| - value = slave.readRegisterFromBuffer(i); |
171 |
| - |
172 |
| - EEPROM.put(registerIndex * 2, value); |
173 |
| - |
174 |
| - /* if this register sets digital pins mode, |
175 |
| - * set the digital pins mode. |
176 |
| - */ |
177 |
| - if (registerIndex > 2 && registerIndex < 14 && registerIndex != CTRL_PIN) { |
178 |
| - // use the register value to set pin mode. |
179 |
| - switch (value) { |
180 |
| - case PIN_MODE_INPUT: |
181 |
| - pinMode(registerIndex, INPUT); |
182 |
| - break; |
183 |
| - case PIN_MODE_OUTPUT: |
184 |
| - pinMode(registerIndex, OUTPUT); |
185 |
| - break; |
| 183 | +// Handle the function codes Write Holding Register(s) (FC=06, FC=16) and write data to the eeprom. |
| 184 | +uint8_t writeMemory(uint8_t fc, uint16_t address, uint16_t length) |
| 185 | +{ |
| 186 | + // Write the received data to EEPROM. |
| 187 | + for (int i = 0; i < length; i++) |
| 188 | + { |
| 189 | + if (address + i <= 50) |
| 190 | + { |
| 191 | + // Check if the requested addresses exist in the array. |
| 192 | + if (address + i > digital_pins_size) |
| 193 | + { |
| 194 | + return STATUS_ILLEGAL_DATA_ADDRESS; |
| 195 | + } |
| 196 | + |
| 197 | + // Read the value from the input buffer. |
| 198 | + uint8_t value = slave.readRegisterFromBuffer(i); |
| 199 | + |
| 200 | + // Check if the value is 0 (INPUT) or 1 (OUTPUT). |
| 201 | + if (value != INPUT || value != OUTPUT) |
| 202 | + { |
| 203 | + return STATUS_ILLEGAL_DATA_VALUE; |
186 | 204 | }
|
| 205 | + |
| 206 | + // Store the received value in the EEPROM. |
| 207 | + EEPROM.put(address + i, value); |
| 208 | + |
| 209 | + // Set the pinmode to the received value. |
| 210 | + pinMode(digital_pins[address + i], value); |
| 211 | + } |
| 212 | + else |
| 213 | + { |
| 214 | + // Read the value from the input buffer. |
| 215 | + uint16_t value = slave.readRegisterFromBuffer(i); |
| 216 | + |
| 217 | + // Store the received value in the EEPROM. |
| 218 | + EEPROM.put(address + i * 2, value); |
187 | 219 | }
|
188 | 220 | }
|
189 | 221 |
|
|
0 commit comments