Beruflich Dokumente
Kultur Dokumente
i
Communications between PLC and microcontroller using Modbus Protocol ii
ii
iii
Autor:
Eduardo Naranjo Piñar
Tutor:
Luis Fernando Castaño Castaño
Doctor Ingeniero Industrial
iii
Communications between PLC and microcontroller using Modbus Protocol iv
iv
v
Proyecto Fin de Carrera: Communications between PLC and microcontroller using Modbus Protocol
El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros:
Presidente:
Vocales:
Secretario:
Sevilla, 2016
v
Communications between PLC and microcontroller using Modbus Protocol vi
vi
vii
Agradecimientos
A toda aquella persona, animal o cosa que me ha tenido que aguantar durante mis años de estudios
vii
Communications between PLC and microcontroller using Modbus Protocol viii
viii
Abstract
The main goal of this document is developing multiple Modbus devices to communicate with
different computers and sensors. To explain this, the document will be divided in 3 chapters.
First of all, a small state of the art of the Modbus protocol will be done, explaining how it works
superficially, which are the most used functions, and how it handles the exceptions. Later there
will be an explanation on the development of the Modbus slave (serial and TCP/IP) with an
Arduino Mega board with the goal of sending a sensor output using a PC, and the development
of the Modbus master (serial) for reading data from a MASS-X solar sensor. Finally a Modbus
master which reads data from a solar panel (slave) and will send this data via Web Service in an
XML document has been documented.
ix
Communications between PLC and microcontroller using Modbus Protocol x
x
xi Introduction
Introduction
Whenever there are sensors in an industrial environment to have a better understatement about
what is happening, a way to send this data to a PLC or computer is necessary, which will read it
to make decisions about how the system has to respond. Because of this, the industry has
developed multiple communication protocols among all these devices, being Modbus the most
used nowadays.
Modbus is a serial protocol developed by Modicon (today Schneider Electric) for use it with
their PLCs. It is very simple and reliable thanks to its CRC, and it has become the standard de
facto protocol. It was written with industrial applications in mind, it is easy to maintain and to
use and royalty-free.
Even though PLCs are extremely reliable and robust computers, these are very expensive
compared to some new controllers such as Arduino from the Arduino Foundation and Raspberry
Pi from Element 14. Because of this, people are starting to use these computers to substitute
PLCs in some applications (like research or low-budget projects).
This is the main reason made this project was made, to make these devices compatible with the
protocol by using different libraries made by the user's community and to develop some user
interfaces to make the data visible for operators.
The first main goal is to program an Arduino Mega to read a sensor output and then send it to a
PLC or PC via Modbus. Thanks to this, the department will not need to buy expensive cards, and
will be able to connect the sensors far away from the PLC in case they are using Modbus
TCP/IP.
The second goal is to develop a Modbus master to read data from a system, which implements a
slave, and later will send this data through a Web Service in an XML format.
xi
Communications between PLC and microcontroller using Modbus Protocol xii
Resumen en español
1-Bit 16-Bits
xii
xiii Resumen en español
Cada dispositivo esclavo tiene asignado un único número de identificación. En Modbus serie sólo al maestro
se le permite iniciar una petición. Sobre Ethernet cualquier dispositivo puede hacerlo, sin embargo
normalmente es el maestro el que lo hace. La trama Modbus en su modalidad serie contiene el número de
esclavo con el que queremos comunicarnos y sólo el dispositivo con ese número responderá a la trama, incluso
aunque otros dispositivos también la reciban. Todas las tramas contienen información de redundancia para
detectar errores de transmisión.
La PDU (Protocol Data Unit) contiene toda la información necesaria para el esclavo y consta de los siguientes
elementos:
Código de función
Dirección del primer elemento
Número de elementos a leer o bytes de información
Los dispositivos se pueden dividir en dos tipos según la organización de los datos:
4 bloques diferentes (uno para cada tipo de objeto)
1 único bloque
xiii
Communications between PLC and microcontroller using Modbus Protocol xiv
Cuando un maestro envía un pedido a un dispositivo esclavo, éste espera una respuesta normal. Uno de los
siguientes cuatro posibles eventos pueden ocurrir cuando el esclavo recibe la trama:
Si el esclavo recibe la trama sin errores de comunicación, y puede manejar la trama de manera
normal, devuelve una respuesta normal.
Si el esclavo no recibe la trama debido a un problema de comunicación, no se devuelve una respuesta.
El programa maestro eventualmente procesa una condición de timeout.
Si el esclavo recibe la trama, pero detecta un error de comunicación, no se devuelve una respuesta. El
programa maestro eventualmente procesa una condición de timeout.
Si el esclavo recibe un pedido sin error de comunicación, pero no puede manejar el pedido, éste
devuelve una respuesta de excepción informando al maestro de la naturaleza del error. El primer Byte
es el código de función sumando a 0x80, y el segundo es el código de excepción.
xiv
xv Resumen en español
El mega 2560 se puede programar con el software de Arduino, y viene preprogramado con un boot-loader que
te permite subir un nuevo programa sin necesidad de hardware de programación. Usa librerías de C, y la
sintaxis de programación es muy similar.
La placa se puede alimentar vía USB o mediante una fuente externa (AC/DC (Alternating Current / Direct
Current) o batería). La fuente de potencia es seleccionada de manera automática. Las fuentes externas se
conectan al puerto jack o al pin Vin. La placa necesita entre 6 a 20V para funcionar, pero no se recomienda
menos de 7 o más de 12. Puede además ofrecer fuentes de alimentación de 5V y 3.3V a otros dispositivos.
Cuenta con 256KB de memoria Flash para guardar los programas, 8KB de memoria SRAM (Static Random
Access Memory) y 4KB de EEPROM (Electrically Erasable Programmable Read-Only Memory) para guardar
datos que no se desea que sean volátiles.
El Arduino Mega dispones además de 4 puertos UART (Universal Asynchronous Receiver-Transmitter) para
TTL (Transistor-Transistor Logic). En caso de que fuera necesario, se conecta mediante RS232 o RS485 hay
que usar adapatadores especiales como el de la siguiente imagen (para RS485).
´
Figura 2.2 Conversor TTL/RS485
xv
Communications between PLC and microcontroller using Modbus Protocol xvi
Es compatible con el shield Ethernet para Arduino, el cual dispone además de lector de tarjetas SD (Secure
Digital).
xvi
xvii Resumen en español
3.1 Librerías
Las librerías que se han usado son las siguientes:
3.1.1 EEPROM.h
El microcontrolador tiene memoria EEPROM, cuyos valores se mantienen cuando la placa se apaga (como un
disco duro muy pequeño). Esta librería permite leer y escribir esos bytes y sus funciones más importantes son:
EEPROM.update() Escribe un byte pero sólo si el valor difiere del ya guardado en la misma dirección. Una
escritura en la EEPROM lleva 3.3ms y la esperanza de vida es de 100.000 ciclos de
escritura/borrado, así que usar esta función en vez de write puede ayudar a mantener la
memoria por más tiempo.
3.1.2 Average.h
Ésta es una colección de rutina para llevar a cabo análisis matemático de listas de números. Las funciones
usadas durante el proyecto son las siguientes:
Average.stddev Calcula la desviación estándar de la lista. Esta función es la única que no devuelve el
mismo tipo de datos que la lista, devolviendo siempre un float.
3.1.3 Ultrasonic.h
Esta librería funciona como intermediador entre nuestro programa y el sensor de distancia por ultrasonidos
HC-RS04. Después de crear un objeto de la clase ultrasonic, tan sólo hay que llamar a su método Ranging(), el
cual devuelve la distancia entre el sensor y el objeto físico. En caso de error, se devuelve un 0.
xviii
xix Resumen en español
3.1.4 ModbusRtu.h
Esta librería permite una implementación de Modbus serie en nuestro Arduino. Las funciones usadas en el
proyecto son:
Modbus.poll() Este método comprueba la traba recibida. Después, genera una respuesta y se la envía
al maestro.
Modbus.query() Incluye todo lo necesario para que el maestro genere una trama Modbus.
3.1.5 Ethernet.h
Junto con el shield Ethernet de Arduino, esta librería permite a la placa conectarse a Internet. Puede servir
como servidor o cliente. La librería soporta hasta cuatro conexiones. El Arduino se comunica con el shield a
través del bus SPI (Serial Peripheral Interface). La única función necesaria en este proyecto es
Ethernet.begin(), la cual recibe como argumentos las direcciones MAC, IP y de la subred.
3.1.6 Mudbus.h
Como las tramas en RTU son diferentes a las de TCP, la librería MobusRtu no nos sirve si nos queremos
comunicar a través de Ethernet. Esta librería permite la conexión con un maestro en la misma red local o
Internet (no recomendado debido a la falta de seguridad en el protocolo).
La única función necesaria es Mudbus.Run(), la cual realiza todo el manejo de tramas y genera una respuesta.
xix
Communications between PLC and microcontroller using Modbus Protocol xx
3.1.7 LiquidCrystal_I2C.h
Esta librería se usa para mostrar datos por una pantalla LCD de caracteres a través del protocolo I2C (Inter-
Integrated Circuit). Usa los pines SDA y SCL del Arduino y necesita una fuente de 5V. La pantalla es además
retroiluminable. Las funciones principales son:
xx
xxi Resumen en español
Para el conexionado hay que usar un adaptador RS485, ya que los puertos del Arduino funcionan a 5V (TTL)
y el sensor con RS-485.
Los pines DE y RE están conectados al pin 3(se usa este pin porque lo hemos elegido en el programa). El pin
DI va al RX del Arduino y el RO al TX. Los conectores A y B son los RS485+ y RS485- del sensor. El
conversor necesita además una alimentación desde el Arduino. Afortunadamente todavía se dispone de
suficientes tomas de 5V.
xxi
Communications between PLC and microcontroller using Modbus Protocol xxii
xxii
xxiii Resumen en español
Index
Agradecimientos vii
Abstract ix
Introduction xi
Resumen en español xii
Acronyms List xxiv
1 Modbus Communication Protocol 1
2 Mega 2560 Microcontroller 13
3 Modbus devices with Arduino 19
4 M340 Modbus MASTER 46
Annex 62
References 78
xxiii
Communications between PLC and microcontroller using Modbus Protocol xxiv
ACRONYMS LIST
OSI: Open System Interconnection
TCP/IP: Transmission Control Protocol / Iternet Protocol
PDU: Protocol Data Unit
RTU: Remote Terminal Unit
CRC: Cyclic Redundancy Check
ID: Identification
ASCII: American Standard Code for Information Interchange
LRC: Longitudinal Redundancy Check
ADU: Application Data Unit
CR/LF: Carriage Return / Line Feed
FIFO: First In First Out
UART: Universal Asynchronous Receiver-Transmitter
USB: Universal Serial Bus
TTL: Transistor-Transistor Logic
PWM: Pulse-Width Modulation
AC/DC: Alternating Current / Direct Current
GND: Ground
DHCP: Dynamic Host Configuration Protocol
SD: Secure Digital
SPI: Serial Peripheral Interface
TWI: Two Wire Interface
MAC: Media Access Control
IDE: Integrated Development Environment
EEPROM: Electrically Erasable Programmable Read-Only Memory
SRAM: Static Random Access Memory
LCD: Liquid Crystal Display
I2C: Inter-Integrated Circuit
FOV: Field Of View
PLC: Programmable Logic Controller
LED: Light-Emitting Diode
FB: Function Block
xxiv
xxv Acronyms List
xxv
1 MODBUS COMMUNICATION PROTOCOL
1
Communications between PLC and microcontroller using Modbus Protocol 2
Depending on how long is the type or if it is writeable, it is possible to classify then into four
different types:
1-Bit 16-Bits
Each slave has a unique address. In serial, only the Master is able to initiate a command. On Ethernet, any
device can send out a Modbus command, although usually only one master device does so. A Modbus
command contains the Modbus address of the slave device(1 to 247). Only the intended device will act on
the command, even though other devices might receive it. All Modbus commands contain checksum
information, to allow the recipient to detect transmission errors.
Many modems and gateways support Modbus, as it is a very simple protocol and often copied. Some of
them were specifically designed for this protocol. Different implementations use wireline or wireless
communication. Typical problems that designers have to overcome include high latency and timing
issues.
2
3 Modbus Communication Protocol
Modbus frames are called Application Data Unit (ADU) [3]. In Modbus RTU, these frames are composed
by three elements:
However in other versions where the slave address and the error check are implicit inside another
protocol (like TCP/IP), the frame is composed only by the PDU, which is composed by three blocks:
Function code
Depending on the function, the address and data field may be non-existent.
3
Communications between PLC and microcontroller using Modbus Protocol 4
Only one data block: the data can be reached by several Modbus functions
4
5 Modbus Communication Protocol
The various reading, writing and other public operations are categorised as follows. The most primitive reads
and writes are shown in bold.
5
Communications between PLC and microcontroller using Modbus Protocol 6
Response:
Response:
6
7 Modbus Communication Protocol
Response:
*N = Number of registers
Error:
7
Communications between PLC and microcontroller using Modbus Protocol 8
Response:
*N = Number of registers
Error:
Response:
*N = Number of registers
Error:
8
9 Modbus Communication Protocol
Response:
*N = Number of registers
Error:
Value 1 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1
Address 27 26 25 24 23 22 21 20 - - - - - - 29 28
Table 1.3 Function 15 PDU example
9
Communications between PLC and microcontroller using Modbus Protocol 10
The first byte transmitted addresses outputs 27-20, with the least significant bit addressing the lowest
output (20) in this set [1].
The next byte transmitted (01 hex) addresses outputs 29-28, with the least significant bit addressing the
lowest output (28) in this set. Unused bits in the last data byte should be zero– filled [1].
Request:
Error:
10
11 Modbus Communication Protocol
*N = Quantity of Registers
Response:
Error:
11
Communications between PLC and microcontroller using Modbus Protocol 12
If the slave device receives the request without a communication error, and can handle the query
normally, it returns a normal response.
If the slave does not receive the request due to a communication error, no response is returned.
The master program will eventually process a timeout condition for the request.
If the slave receives the request, but detects a communication error, no response is returned. The
master program will eventually process a timeout condition for the request.
If the slave receives the request without a communication error, but cannot handle it, the slave
will return an exception response informing the master of the nature of the error. The first Byte
from the response is 0x80 + the function number, and the second one is an exception code. This
table shows the most important codes:
12
13 Mega 2560 Microcontroller
T he Arduino Mega 2560 is a microcontroller board based on the ATmega2560. It has 54 digital
input/output pins (of which 15 can be used as Pulse-Width Modulation (PWM) outputs), 16 analog inputs, 4
Universal Asynchronous Receiver-Transmitter (UART), a 16 MHz crystal oscillator, a Universal Serial Bus
(USB) connection, a power jack, an ICSP header, and a reset button. It contains everything needed to support
the microcontroller simply connect it to a computer with a USB cable or power it with an Alternating Current /
Direct Current (AC/DC) adapter or battery to get started. The Mega 2560 board is compatible with most
shields designed for the Arduino Uno [5].
13
Communications between PLC and microcontroller using Modbus Protocol 14
Operating Voltage 5V
SRAM 8 KB
EEPROM 4 KB
Length 101.52 mm
Width 53.3 mm
2.2 Programming
The Mega 2560 board can be programmed with the Arduino Software Integrated Development Environment
(IDE).
The ATmega2560 on the Mega 2560 comes pre-programmed with a boot loader that allows uploading new
code to it without the use of an external hardware programmer. It communicates using C headers.
The ATmega16U2 (or 8U2 in the rev1 and rev2 boards) firmware source code is available in the Arduino
repository. [5]
14
15 Mega 2560 Microcontroller
The board can operate on an external supply of 6 to 20 volts. If supplied with less than 7V, however, the
5V pin may supply less than five volts and the board may become unstable. If using more than 12V, the
voltage regulator may overheat and damage the board. The recommended range is 7 to 12 volts.
The power pins are as follows:
Vin. The input voltage to the board when it is using an external power source (as opposed to 5
volts from the USB connection or other regulated power source). Voltage can be supply through
this pin, or, if supplying voltage via the power jack, access it through this pin.
5V. This pin outputs a regulated 5V from the regulator on the board. The board can be supplied
with power either from the DC power jack (7 - 12V), the USB connector (5V), or the VIN pin of
the board (7-12V). Supplying voltage via the 5V or 3.3V pins bypasses the regulator, and can
damage the board.
3V3. A 3.3 volt supply generated by the on-board regulator. Maximum current draw is 50 mA.
GND. Ground pins.
IOREF. This pin on the board provides the voltage reference with which the microcontroller
operates. A properly configured shield can read the IOREF pin voltage and select the appropriate
power source or enable voltage translators on the outputs for working with the 5V or 3.3V.
2.4 Memory
The ATmega2560 has 256 KB of flash memory for storing code (of which 8 KB is used for the boot loader), 8
KB of Static Random Access Memory (SRAM) and 4 KB of Electrically Erasable Programmable Read-Only
Memory (EEPROM) (which can be read and written with the EEPROM library). [5]
Serial: 0 (RX) and 1 (TX); Serial 1: 19 (RX) and 18 (TX); Serial 2: 17 (RX) and 16 (TX); Serial 3: 15
(RX) and 14 (TX). Used to receive (RX) and transmit (TX) Transistor-Transistor Logic (TTL) serial
data. Pins 0 and 1 are also connected to the corresponding pins of the ATmega16U2 USB-to-TTL
Serial chip.
External Interrupts: 2 (interrupt 0), 3 (interrupt 1), 18 (interrupt 5), 19 (interrupt 4), 20 (interrupt 3),
and 21 (interrupt 2). These pins can be configured to trigger an interrupt on a low level, a rising or
falling edge, or a change in level. See the attachinterrupt() function for details.
PWM: 2 to 13 and 44 to 46. Provide 8-bit PWM output with the analogWrite() function.
Serial Peripheral Interface (SPI): 50 (MISO), 51 (MOSI), 52 (SCK), 53 (SS). These pins support SPI
communication using the SPI library. The SPI pins are also broken out on the ICSP header, which is
physically compatible with the Arduino /Genuino Uno and the old Duemilanove and Diecimila
Arduino boards.
LED: 13. There is a built-in Light-Emitting Diode (LED) connected to digital pin 13. When the pin is
HIGH value the LED is on when the pin is LOW, it is off.
Two Wire Interface (TWI): 20 (SDA) and 21 (SCL). Support TWI communication using the Wire
library. Note that these pins are not in the same location as the TWI pins on the old Duemilanove or
Diecimila Arduino boards.
The Mega 2560 has 16 analog inputs, each of which provide 10 bits of resolution (i.e. 1024 different values).
15
Communications between PLC and microcontroller using Modbus Protocol 16
By default they measure from ground to 5 volts, though is it possible to change the upper end of their range
using the AREF pin and analogReference() function.
There are a couple of other pins on the board:
AREF. Reference voltage for the analog inputs. Used with analogReference().
Reset. Bring this line LOW to reset the microcontroller. Typically used to add a reset button to
shields, which block the one on the board.
16
17 Mega 2560 Microcontroller
2.6 Communication
The Mega 2560 board has a number of facilities for communicating with a computer, another board, or other
microcontrollers. The ATmega2560 provides four hardware UARTs for TTL (5V) serial communication. An
ATmega16U2 (ATmega 8U2 on the revision 1 and revision 2 boards) on the board channels one of these over
USB and provides a virtual com port to software on the computer (Windows machines will need a .inf file, but
OSX and Linux machines will recognize the board as a COM port automatically. The Arduino Software (IDE)
includes a serial monitor, which allows simple textual data to be sent to and from the board. The RX and TX
LEDs on the board will flash when data is being transmitted via the ATmega8U2/ATmega16U2 chip and USB
connection to the computer (but not for serial communication on pins 0 and 1).
A SoftwareSerial library allows for serial communication on any of the Mega 2560's digital pins.
The Mega 2560 also supports TWI and SPI communication. The Arduino Software (IDE) includes a Wire
library to simplify use of the TWI bus; see the documentation for details. For SPI communication, use the SPI
library. [5]
The Mega 2560 is designed to be compatible with most shields designed for the Uno and the older
Diecimila or Duemilanove Arduino boards. Digital pins 0 to 13 (and the adjacent AREF and GND pins),
analog inputs 0 to 5, the power header, and ICSP header are all in equivalent locations. Furthermore, the
main UART (serial port) is located on the same pins (0 and 1), as are external interrupts 0 and 1 (pins 2
and 3 respectively). SPI is available through the ICSP header on both the Mega 2560 and Duemilanove /
Diecimila boards. Please note that Inter-Integrated Circuit (I2C) is not located on the same pins on the
Mega 2560 board (20 and 21) as the Duemilanove / Diecimila boards (analog inputs 4 and 5). [5]
17
Communications between PLC and microcontroller using Modbus Protocol 18
To use the shield, mount it on top of an Arduino board (e.g. the Uno). To upload sketches to the board,
connect it to a computer with a USB cable as normally would. Once the sketch has been uploaded, it is
able to disconnect the board from the computer and power it with an external power supply.
Connect the shield to the computer or a network hub or router using a standard Ethernet cable (CAT5 or
CAT6 with RJ45 connectors). Connecting to a computer may require the use of a crossover cable
(although many computers, including all recent Macs can do the crossover internally).
The shield has a Media Access Control (MAC) address and a fixed IP address using the Eternet.begin()
function. A MAC address is a globally unique identifier for a particular device. Current Ethernet shields come
with a sticker indicating the MAC address. For older shields without a dedicated MAC address, inventing a
random one should work, but do not use the same one for multiple boards. Valid IP addresses depend on the
configuration of the network. It is possible to use Dynamic Host Configuration Protocol (DHCP) to
dynamically assign an IP to the shield. Optionally, it is possible to also specify a network gateway and subnet.
The latest revision of the Ethernet Shield includes a micro-SD card slot, which can be interfaced
with using the SD library. The text of the Arduino getting started guide is licensed under a
Creative Commons Attribution-ShareAlike 3.0 License. Code samples in the guide are released
into the public domain.
18
19 3 Modbus devices with Arduino
T his chapter explains how some full functional Modbus devices using an Arduino MEGA board were
developed and which tools were used. The main goal of this device would be to read data from sensors,
storage the data, calculate some statistics and send the data to a master when this master sends a valid request
frame.
19
Communications between PLC and microcontroller using Modbus Protocol 20
3.2 Libraries
The Arduino environment can be extended with libraries, just like most programming platforms. Libraries
provide extra functionality for use in sketches, e.g. working with hardware or manipulating data.
A number of libraries come installed with the IDE, but it is also possible to install then or even make new
libraries. [7] Like in C, all the libraries name must end by the extension .h
3.2.1 EEPROM.h
The microcontroller on the Arduino and Genuino AVR based board has EEPROM: memory whose values are
kept when the board is turned off (like a tiny hard drive). This library enables to read and write those bytes. [8]
The most important functions from this library are:
EEPROM.read() Reads a byte from the EEPROM. Locations that have never been written to have the
value of 255.
EEPROM.update() Write a byte to the EEPROM. The value is written only if differs from the one already
saved at the same address. An EEPROM write takes 3.3 ms to complete. The EEPROM
memory has a specified life of 100,000 write/erase cycles, so using this function instead
of write() can save cycles if the written data does not change often.
20
21 3 Modbus devices with Arduino
3.2.2 Average.h
This is a collection of routines for performing mathematical analysis of arrays of numbers. [9]The functions
used during this project are:
Average.rolling() Adds value to the array history_array shifting all the values down one place.
Average.mean() Calculates the mean average of the values in array. slice_count is the number of entries
in the array.
Average.stddev This functions returns the standard deviation. This is the only function, which does not
return the same data type as the array. The standard deviation is always returned as a
float.
3.2.3 Ultrasonic.h
This library functions as an intermediate between the program and the ultrasonic distance sensor HC-RS04.
After creating an object from the class ultrasonic, it is only needed to call its method ultrasonic.Rangin(),
which will return the distance between an object and the sensor. In case of error, a 0 will be returned. [10]
21
Communications between PLC and microcontroller using Modbus Protocol 22
3.2.4 ModbusRtu.h
This library provides a Serial Modbus implementation for Arduino. [11] This works in case of the slave as one
block data device of 16-bits register and is compatible with all the main Modbus function codes. The functions
used in this project are:
Modbus.poll() This method checks if there is any incoming query. Afterwards, it would shoot a
validation routine plus a register query. It is recommended not to use any kind of
delay function. After a successful frame between the master and the slave, the time-
out timer is reset.
Modbus.query() This includes all the necessary fields to make the master generate a Modbus query. A
master may keep several of these structures and send them cyclically or use them
according to program needs.
3.2.5 Ethernet.h
With the Arduino Ethernet Shield, this library allows an Arduino board to connect to the internet. It can
serve as either a server accepting incoming connections or a client making outgoing ones. The library
supports up to four concurrent connection (incoming or outgoing or a combination).
Arduino communicates with the shield using the SPI bus. This is on digital pins 11, 12, and 13 on the
Uno and pins 50, 51, and 52 on the Mega. On both boards, pin 10 is used as SS. On the Mega, the
hardware SS pin, 53, is not used to select the W5100, but it must be kept as an output or the SPI interface
will not work. [13]
The only function used in this project is Ethernet.begin() which receives as arguments the MAC, IP and subnet
direction.
3.2.5 Mudbus.h
Since frames in RTU are different from frames in TCP, the library ModbusRtu.h is no longer useful if it is
needed to communicate with the device via Ethernet. This library allows us to connect to a distant master in
the same local network or the Internet (this last one is not recommended since lack of security in the protocol),
and it is actually easier to use thanks to the protocol TCP/ which is implemented in Ethernet.h.
The only function needed for an slave is Modbus.Run, which does all the frame handling and generates a
response.
3.2.6 LiquidCrystal_I2C.h
This library is used to show data in a Liquid Crystal Display (LCD) screen compatible with the I2C protocol. It
uses the pins SDA and SCL from them Mega an needs a 5V power supply. It also has a background light and
uses characters as data type to print. The main functions are:
23
Communications between PLC and microcontroller using Modbus Protocol 24
24
25 3 Modbus devices with Arduino
Then define some constants that are widely repeated in this code. If it is necessary to change one it is possible
to do it in these lines, so it is no longer necessary to look at them on the whole sketch. Define two constants for
the loop_timer EEPROM address because it is a 16-bit data, and the EEPROM works with 8 bits per register
(ATMEGA2560 is an 8-bit processor). The history array size is 300, and cannot be much larger. Because 9
registers are needed, that will be the size of the Modbus register, and if needed, it is only necessary to change
this number to reserve more space in memory.
//EEPROM addresses
#define SLAVE_ADDR 0
#define BAUDRATE_ADDR 1
#define SERMODE_ADDR 2
#define LOOP_TIMER_HIGH_ADDR 3
#define LOOP_TIMER_LOW_ADDR 4
The next line defines a long object that will later need to set a timer.
unsigned long Timer;
Declaration of a Modbus object with the slave number 1 in the serial port with the code 0 of the Arduino. The
second argument is for choosing the serial port, and the last one is for choosing an enable pin in case an RS-
485 is being used.
Modbus slave (1, 0 , 0); //ID,SerialPort USB or RS-232
Declaration of the state variable which will later be used to store the state of the function Modbus.poll()
byte state = 0;
Declaration of some configuration variables for the serial port, the timer loop timer in milliseconds (100 by
default) and the variable that will storage the data read from the range sensor. uint16_t is an unsigned 16-bit
integer
byte baudRateId = 0;
byte serModeId = 0;
uint16_t loop_timer = 100;
uint16_t measure;
Declaration of the Modbus registers array and the history array for the calculation of statistics.
uint16_t au16data[Modbus_REGISTER_SIZE];
Average<uint16_t> history(HISTORY_SIZE);
25
Communications between PLC and microcontroller using Modbus Protocol 26
Declaration of the Ultrasonic object. The trigger pin will be the 22 and the echo the 24.
Ultrasonic ultrasonic(22,24); // (Trig PIN,Echo PIN)
Configuration of the slave ID: This will get the ID from the EEPROM. If the number is 0, 1 will be set as slave
number and storage in the EERPOM. Finally, the data will be saved in the Modbus register.
//Setup Slave number
slave.setID(EEPROM.read(SLAVE_ADDR));
if (slave.getID() == 0) {
EEPROM.write(SLAVE_ADDR, 1);
slave.setID(1);
}
au16data[0] = uint16_t(slave.getID());
Configuration and start of serial communications. The beginSerial will be defined later on.
//configure and start communications
baudRateId = EEPROM.read(BAUDRATE_ADDR);
serModeId = EEPROM.read(SERMODE_ADDR);
au16data[1] = uint16_t(baudRateId);
au16data[2] = uint16_t(serModeId);
beginSerial(baudRateId, serModeId);
Configuration of the loop timer from the EEPROM. If a 0 is read from the memory, the timer will be set to
100 milliseconds. The two Bytes from the EERPOM must be parsed to a unsigned 16-bit integer, and parsed
again into two Bytes.
//configure loop timer
loop_timer = (uint16_t)EEPROM.read(LOOP_TIMER_HIGH_ADDR);
loop_timer = loop_timer << 8;
loop_timer |= (uint16_t)EEPROM.read(LOOP_TIMER_LOW_ADDR);
if (loop_timer == 0) {
loop_timer = 100;
EEPROM.write(LOOP_TIMER_LOW_ADDR, loop_timer);
EEPROM.write(LOOP_TIMER_HIGH_ADDR, loop_timer >> 8);
26
27 3 Modbus devices with Arduino
}
loop_timer << 8;
au16data[3] = loop_timer;
The function millis returns the milliseconds since the Arduino turned ON. Then storage this data into the timer
variable.
Timer = millis();
The function Modbus.poll() gets the frame from the master, and prepares an sends a response PDU to the
master with the data from the Modbus register.
state = slave.poll(au16data, Modbus_REGISTER_SIZE);
Ask if the timer has finished. If it is, then call the function reg_poll to read a new input from the sensor. Notice
that millis() will come back to 0 after a certain time (50 days approx.). After that, the timer is reset.
if ((millis() - Timer > loop_timer) | (millis() - Timer < 0)) {
//poll messages
reg_poll();
Timer = millis();
}
27
Communications between PLC and microcontroller using Modbus Protocol 28
Get an input from the sensor, roll it into the history array and calculate some statistics.
measure = ultrasonic.Ranging(CM);
history.rolling(measure);
au16data[4] = uint16_t(measure);
au16data[5] = history.mean();
au16data[6] = history.stddev();
au16data[7] = history.maximum();
au16data[8] = history.minimum();
Look up to updates from the Modbus master in the configuration variables (slave ID, baud rate, serial mode
and loop timer)
if (uint16_t(slave.getID()) != au16data[0]) {
slave.setID(uint8_t(au16data[0]));
EEPROM.update(SLAVE_ADDR, byte(au16data[0]));
}
if (uint16_t(baudRateId ) != au16data[1]) {
baudRateId = au16data[1];
Serial.end();
beginSerial(baudRateId, serModeId);
EEPROM.update(BAUDRATE_ADDR, byte(au16data[1]));
}
if (uint16_t(serModeId) != au16data[2]) {
serModeId = au16data[2];
Serial.end();
beginSerial(baudRateId, serModeId);
EEPROM.update(SERMODE_ADDR, byte(au16data[2]));
}
if (loop_timer != au16data[3]) {
28
29 3 Modbus devices with Arduino
loop_timer = au16data[3];
EEPROM.write(LOOP_TIMER_LOW_ADDR, loop_timer);
EEPROM.write(LOOP_TIMER_HIGH_ADDR, loop_timer >> 8);
loop_timer << 8;
}
Call a function, which returns the actual value of the baud rate from the ID
long baudRate = getBaudRate(baudRateId);
29
Communications between PLC and microcontroller using Modbus Protocol 30
0 Slave ID Slave ID
4 Last read value from the sensor Loop timer low Byte
5 Mean average
6 Standard variation
7 Maximum
8 Minimum
30
31 3 Modbus devices with Arduino
Serial Mode
ID Baud Rate
Bit number Parity Stop Bits
1 300 8 None 2
2 600 8 Even 1
3 1200 8 Even 2
4 2400 8 Odd 1
5 4800 8 Odd 2
6 14400 7 None 1
7 19200 7 None 2
8 28800 7 Even 1
9 38400 7 Even 2
10 57600 7 Odd 1
11 115200 7 Odd 2
31
Communications between PLC and microcontroller using Modbus Protocol 32
3.5 Modbus TCP slave with Arduino and the Ethernet shield
This device is intended to read data from a HC-RS04 range sensor, calculate some statistics and send this data
to a Modbus master trough Ethernet TCP/IP. The RMMS was used to test the device. It also uses the
EEPROM memory to save the last byte of the IP.
32
33 3 Modbus devices with Arduino
Next, declare the Timer variable, the Modbus object, the IP variable and the measure variable as done on the
RTU.
unsigned long Timer;
Mudbus Mb;
uint8_t ipB = 1;
uint16_t loop_timer = 100;
uint16_t measure;
Again, declare the history array and the Modbus register array.
uint16_t au16data[Modbus_REGISTER_SIZE];
Average<uint16_t> history(HISTORY_SIZE);
Declaration of the Ultrasonic object. The trigger pin will be the 22 and the echo the 24.
Ultrasonic ultrasonic(22,24); // (Trig PIN,Echo PIN)
Finally declare the Ethernet settings (IP by default will be 192.168.1.10, but it is possible to change the last
Byte)
// Ethernet settings (depending on MAC and Local network)
byte mac[] = {0x90, 0xA2, 0xDA, 0x0E, 0x94, 0xB5 };
byte ip[] = {192, 168, 1, 10};
byte subnet[] = {255, 255, 255, 0};
33
Communications between PLC and microcontroller using Modbus Protocol 34
Then take the settings for the loop timer as done before.
//configure loop timer
loop_timer = (uint16_t)EEPROM.read(LOOP_TIMER_HIGH_ADDR);
loop_timer = loop_timer << 8;
loop_timer |= (uint16_t)EEPROM.read(LOOP_TIMER_LOW_ADDR);
if (loop_timer == 0) {
loop_timer = 100;
EEPROM.write(LOOP_TIMER_LOW_ADDR, loop_timer);
EEPROM.write(LOOP_TIMER_HIGH_ADDR, loop_timer >> 8);
}
loop_timer << 8;
Mb.R[1] = loop_timer;
Finally call the function millis() and save the returned data into the Timer variable.
Timer = millis();
reg_poll();
Timer = millis();
}
}
34
35 3 Modbus devices with Arduino
In addition, storage the measure taken and statistics into the Modbus registers.
Mb.R[2] = uint16_t(measure);
Mb.R[3] = history.mean();
Mb.R[4] = history.stddev();
Mb.R[5] = history.maximum();
Mb.R[6] = history.minimum();
Finally, check is there is an update from the master in the IP or loop timer settings and save it into the
EEPROM.
if (ip[3] != Mb.R[0]) {
ip[3] = Mb.R[0];
EEPROM.write(IP_ADDR, ip[3]);
Ethernet.begin(mac, ip, subnet);
}
if (loop_timer != Mb.R[1]) {
loop_timer = Mb.R[1];
EEPROM.write(LOOP_TIMER_LOW_ADDR, loop_timer);
EEPROM.write(LOOP_TIMER_HIGH_ADDR, loop_timer >> 8);
loop_timer << 8;
}
2 Last read value from the sensor Loop timer low Byte
3 Mean average
4 Standard variation
5 Maximum
6 Minimum
35
Communications between PLC and microcontroller using Modbus Protocol 36
3.6.1 Wiring
The next schema shows how to connect the Arduino and the LCD screen to power it and establish
communication via I2C protocol (pines SDA and SCL).
Figure 3.8 Arduino Mega and I2C LCD screen wiring [17
36
37 3 Modbus devices with Arduino
As said on page 14, the serial ports for the Arduino Mega works with TTL connections, however the solar
sensor uses a RS-485 connector, so an interface between both connection types is needed. For this purpose, a
converter circuit is needed.
The pines DE (Data Enable) and RE (Receive Enable) are jumpered together to pin 3 (beacause we’ve chosen
it in this case). The pin DI (Data In) is jumpered to pin RX and the pin RO (receive out) to pin TX.
37
Communications between PLC and microcontroller using Modbus Protocol 38
The wires A and B are the wires ‘RS-485 +’ and ‘RS-485 –‘ from the sensor. Notice that the sensor will need
its own power supply from the arduino. Luckily there is enough 5V power supply pines in the Arduino. The
following table shows the color code from the sensor.
To show data on the scree the lyquid crystal object must be created with 2 rows and 16 colums
LiquidCrystal_I2C lcd(0x27,16,2);
This Modbus object receives as its last argument the control pin for the RS-485 interface.
Modbus master(0,0,SSerialTxControl); // this is master for RS-485
38
39 3 Modbus devices with Arduino
39
Communications between PLC and microcontroller using Modbus Protocol 40
case 2:
master.poll(); // check incoming messages
if (master.getState() == COM_IDLE) {
u8state++;
u32wait = millis() + 1000; }
break;
Finally send the data to the LCD screen after the state 2.
case 3:
u8state = 0;
lcd.setCursor(0, 0);
lcd.print("FOV: ");
lcd.print(au16data[1]);
lcd.setCursor(0, 1);
lcd.print("A Info: ");
lcd.print(au16data[7]);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("DNI R: ");
lcd.print(au16data[8]);
lcd.setCursor(0, 1);
lcd.print("T: ");
lcd.print(au16data[9]);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("FAX: ");
lcd.print(au16data[10]);
lcd.setCursor(0, 1);
lcd.print("FAY: ");
lcd.print(au16data[11]);
delay(3000);
40
41 3 Modbus devices with Arduino
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("AX: ");
lcd.print(au16data[12]);
lcd.setCursor(0, 1);
lcd.print("AY: ");
lcd.print(au16data[13]);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Azi: ");
lcd.print(au16data[14]);
lcd.setCursor(0, 1);
lcd.print("Ele: ");
lcd.print(au16data[15]);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("MVX: ");
lcd.print(au16data[16]);
lcd.setCursor(0, 1);
lcd.print("MVY: ");
lcd.print(au16data[17]);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("MVZ: ");
lcd.print(au16data[18]);
delay(2000);
lcd.clear();
lcd.setCursor(0, 0);
41
Communications between PLC and microcontroller using Modbus Protocol 42
lcd.print("AVX: ");
lcd.print(au16data[19]);
lcd.setCursor(0, 1);
lcd.print("AVY: ");
lcd.print(au16data[20]);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("AVZ: ");
lcd.print(au16data[21]);
delay(2000);
lcd.clear();
break;
}
42
43 3 Modbus devices with Arduino
The nex two table shows the correlation beetwen the symbols printed on the screen and the variables and
additional information code:
FAX Sun Sensor Filtered Angle X º Signed decimal, scale according to FOV:
60: scale of 0.01º
FAY Sun Sensor Filtered Angle Y º 5,15,25: scale of 0.001º
With third-order Butterworth filter
43
Communications between PLC and microcontroller using Modbus Protocol 44
44
45 3 Modbus devices with Arduino
45
Communications between PLC and microcontroller using Modbus Protocol 46
A Programmable Logic Controller (PLC) is a digital computer used for automation of typically
industrial electromechanical processes, such as control of machinery on factory assembly lines, amusement
rides, or light fixtures. PLCs are used in many machines, in many industries. PLCs are designed for multiple
arrangements of digital and analog inputs and outputs, extended temperature ranges, immunity to electrical
noise, and resistance to vibration and impact. Programs to control machine operation are typically stored in
battery-backed-up or non-volatile memory. A PLC is an example of a "hard" real-time system since output
results must be produced in response to input conditions within a limited time, otherwise unintended operation
will result. [22]
46
47 4 M340 Modbus MASTER
The M340 is a modulable PLC, it uses BMXP34 dedicated processors, has the Modicon X80 I/O platform, an
in a single-rack or multi-rack configuration Additional modules for various applications (application-specific,
Ethernet communication, etc .)
This chapter shows how to configurate this PLC to communicate with the Arduino via Modbus RTU (with
RS485) and Modbus TCP .
47
Communications between PLC and microcontroller using Modbus Protocol 48
Now on the configuration screen change all the options needed depending on the needs.
Double click on the new communication an then write an IP, the subnet Mask and the IP from the router.
48
49 4 M340 Modbus MASTER
But first if Windows 10 is being used, connect the computer to the router press the Windows button an then
write ‘cmd’ and press ENTER. The command window will open. Execute the command ‘ipconfig’ and look
for the Ethernet connection (or Wifi) with the router. Then copy the IPv4 (except for the last number, use one
different from the computer and the Arduino) into the IP in the PLC configuration and then copy the entires
mask and gateway numbers.
Now close the configuration to save the changes and go to the bus screen and choose the Ethernet port in the
main CPU.
Finally on the function choose the option ‘ETH TCP IP’ and the connection ‘Ethernet_1’
Now it is possible to build, transfer and execute to connect the PLC with any device in the same local network.
49
Communications between PLC and microcontroller using Modbus Protocol 50
This section consist of three Function Blocks (FB). ADDM receives a string of 4 bytes. Since ther will be a
connection with the RS-232 or RS485 on the main CPU module the 3 first bytes must be 0 and the last one is
the Modbus slave number of the device to communticate with. It returns an addres variable wich used on the
next FB.
The second FB receives the address, the type of object (%M for coil, %WM for holding register, %I for input
coil or %IW for input register)[23] , the address of the first object to read (NUM), the number of objects to read
(NB) and a gestion table. It returns the same gestion table and the table with all the variables readed. It only
works when the boolean variable ‘write’ is false.
50
51 4 M340 Modbus MASTER
Finally the third FB will write the Modbus data configuration when requested. WRITE_VAR receives the
same data as READ_VAR and also an array of registers to send to the Arduino, and does not have any
response array as an output. It only works when ‘write’ is True, and two are needed for the MASS-X sensor
case:
The next tables shows what to write in the different inputs and ouptus from the function blocks:
IN Input ‘0.0.0.1’ being 1 the slave number for the ‘Ethernet_1{192.168.1.10}’ being
Arduino 192.168.1.10 the Arduino IP
51
Communications between PLC and microcontroller using Modbus Protocol 52
Variable Type Value Range sensor RTU Value Range RTU Value MASS-X TCP
NUM Input 0 0 0
NB Input 9 7 24
Variable Type Value Range RTU Value Range TCP Value MASS-X TCP
NUM Input 0 0 0
NB Input 4 2 2
52
53 4 M340 Modbus MASTER
Next, it is really recomended to make a table to read the values in real time:
The next table shows all the variables needed for the program:
recp ARRAY [0..8]OF INT This array storage all the data readed from the Arduino
gest ARRAY[0..3]OF INT Array used for the READ_VAR function block. The [2]
variable is the timeout
enmis ARRAY [0..3] OF INT Data to send to the Arduino. The first data is the Slave ID,
then the baudrate, serial mode and time loop
53
Communications between PLC and microcontroller using Modbus Protocol 54
recp ARRAY [0..6] OF INT This array storage all the data readed from the Arduino
gest ARRAY[0..3]OF INT Array used for the READ_VAR function block. The [2]
variables is the timeout
enmis ARRAY [0..1] OF INT Data to send to the Arduino. The first data is the Slave ID,
then the baudrate, serial mode and time loop
recp ARRAY [0..6] OF INT This array storage all the data readed from the Arduino
gest ARRAY[0..3]OF INT Array used for the READ_VAR function block. The [2]
variables is the timeout
enmis ARRAY [0..1] OF INT Data to send to the Arduino. The first data is the Slave ID,
then the baudrate, serial mode and time loop
54
55 4 M340 Modbus MASTER
Last but not least there is an operator screen for the three cases:
55
Communications between PLC and microcontroller using Modbus Protocol 56
We have to link each display to each variable from the recp array (see tables3.5, 3.7 and 3.11). There are also
4 forms to write changes in the Arduino configuration (or 2 for TCP) and a button to summit all the changes.
56
57 4 M340 Modbus MASTER
One important thing before looking at the last case code is to be sure to enable the RS485 when declaring the
Modbus object in the first case:
Modbus slave (1, 0, 3);//Slave number 1, port 0 and pin 3 for
RS485 enable pin
Then declare the EEPROM addresses for the Modbus TCP configuration:
//EEPROM addresses
#define IP_ADDR 0
#define LOOP_TIMER_HIGH_ADDR 1
#define LOOP_TIMER_LOW_ADDR 2
uint8_t result;
57
Communications between PLC and microcontroller using Modbus Protocol 58
Now declare the Modbus TCP slave object an all the variables needed to configure it.
Mudbus Mb;
uint8_t ipB = 1;
uint16_t loop_timer = 100;
Finally configure the IP and the timer from the EEPROM and start the Ethernet interface.
//-----Setup Modbus Conf-----//
//configure and start communications
//ip[3] = EEPROM.read(IP_ADDR);
if (ip[3] == 0) {
ip[3] = 10;
EEPROM.write(IP_ADDR, ip[3]);
}
Mb.R[0] = ip[3];
58
59 4 M340 Modbus MASTER
if (loop_timer == 0) {
loop_timer = 100;
EEPROM.write(LOOP_TIMER_LOW_ADDR, loop_timer);
EEPROM.write(LOOP_TIMER_HIGH_ADDR, loop_timer >> 8);
}
loop_timer << 8;
Mb.R[1] = loop_timer;
Timer = millis();
Read data from the MASS-X sensor using the function readHoldingRegisters from the ModbusMaster library.
The 1 is the address for the first register to read in the sensor and 22 is the number of registers to read. If the
reading was a success then call the reg_poll function:
if ((millis() - Timer > loop_timer) | (millis() - Timer < 0)) {
//poll messages
result = node.readHoldingRegisters(1, 22);
if (result == node.ku8MBSuccess) {
reg_poll();
}
Timer = millis();
}
59
Communications between PLC and microcontroller using Modbus Protocol 60
if (loop_timer != Mb.R[1]) {
loop_timer = Mb.R[1];
EEPROM.write(LOOP_TIMER_LOW_ADDR, loop_timer);
EEPROM.write(LOOP_TIMER_HIGH_ADDR, loop_timer >> 8);
loop_timer << 8;
}
// transmit request
digitalWrite(3, HIGH); //RS485 pin
for (i = 0; i < u8ModbusADUSize; i++)
{
#if defined(ARDUINO) && ARDUINO >= 100
MBSerial->write(u8ModbusADU[i]);
#else
MBSerial->print(u8ModbusADU[i], BYTE);
#endif
}
u8ModbusADUSize = 0;
MBSerial->flush(); // flush transmit buffer
60
61 4 M340 Modbus MASTER
For the first case connect the sensor with the Arduino as said in chapter 3.4 and also connect the Arduino the
M340 with the RS485 converter. The pines A and B for the M340 are the 4 and 5 on the RJ45.
For the second case a router is necessary to connect both the Arduino and the M340:
Finally for the last case the connection between the Arduino and the MASS-X must be done by using the
RS485 converter as explained on chapter 3.6.
61
Communications between PLC and microcontroller using Modbus Protocol 62
ANNEX
Modbus_slave.ino
#include <ModbusRtu.h>
#include <EEPROM.h>
#include <Average.h>
#include <Ultrasonic.h>
//EEPROM addresses
#define SLAVE_ADDR 0
#define BAUDRATE_ADDR 1
#define SERMODE_ADDR 2
#define LOOP_TIMER_HIGH_ADDR 3
#define LOOP_TIMER_LOW_ADDR 4
byte state = 0;
byte baudRateId = 0;
62
63 Annex
byte serModeId = 0;
uint16_t loop_timer = 100;
uint16_t measure;
uint16_t au16data[MODBUS_REGISTER_SIZE];
Average<uint16_t> history(HISTORY_SIZE);
void setup() {
au16data[1] = uint16_t(baudRateId);
au16data[2] = uint16_t(serModeId);
beginSerial(baudRateId, serModeId);
63
Communications between PLC and microcontroller using Modbus Protocol 64
Timer = millis();
}
void loop() {
state = slave.poll(au16data, MODBUS_REGISTER_SIZE);
if ((millis() - Timer > loop_timer) | (millis() - Timer < 0)) {
//poll messages
reg_poll();
Timer = millis();
}
}
void reg_poll() {
//measure = ultrasonic.Ranging(CM);
measure = random(0,300);
history.rolling(measure);
au16data[4] = uint16_t(measure);
au16data[5] = history.mean();
au16data[6] = history.stddev();
au16data[7] = history.maximum();
au16data[8] = history.minimum();
if (uint16_t(slave.getID()) != au16data[0]) {
slave.setID(uint8_t(au16data[0]));
EEPROM.update(SLAVE_ADDR, byte(au16data[0]));
}
64
65 Annex
if (uint16_t(baudRateId ) != au16data[1]) {
baudRateId = au16data[1];
Serial.end();
beginSerial(baudRateId, serModeId);
EEPROM.update(BAUDRATE_ADDR, byte(au16data[1]));
}
if (uint16_t(serModeId) != au16data[2]) {
serModeId = au16data[2];
Serial.end();
beginSerial(baudRateId, serModeId);
EEPROM.update(SERMODE_ADDR, byte(au16data[2]));
}
if (loop_timer != au16data[3]) {
loop_timer = au16data[3];
EEPROM.write(LOOP_TIMER_LOW_ADDR, loop_timer);
EEPROM.write(LOOP_TIMER_HIGH_ADDR, loop_timer >> 8);
loop_timer << 8;
}
}
65
Communications between PLC and microcontroller using Modbus Protocol 66
66
67 Annex
Modbus_slave_tcp.ino
#include <Ethernet.h>
#include <SPI.h>
#include <Mudbus.h>
#include <EEPROM.h>
#include <Average.h>
#include <Ultrasonic.h>
//EEPROM addresses
#define IP_ADDR 0
#define LOOP_TIMER_HIGH_ADDR 1
#define LOOP_TIMER_LOW_ADDR 2
uint8_t ipB = 1;
uint16_t loop_timer = 100;
uint16_t measure;
uint16_t au16data[MODBUS_REGISTER_SIZE];
Average<uint16_t> history(HISTORY_SIZE);
67
Communications between PLC and microcontroller using Modbus Protocol 68
void setup() {
void loop() {
Mb.Run();
if ((millis() - Timer > loop_timer) | (millis() - Timer < 0)) {
//poll messages
reg_poll();
Timer = millis();
68
69 Annex
}
}
void reg_poll() {
//measure = ultrasonic.Ranging(CM);
measure = random(0,300);
history.rolling(measure);
Mb.R[2] = uint16_t(measure);
Mb.R[3] = history.mean();
Mb.R[4] = history.stddev();
Mb.R[5] = history.maximum();
Mb.R[6] = history.minimum();
if (ip[3] != Mb.R[0]) {
ip[3] = Mb.R[0];
EEPROM.write(IP_ADDR, ip[3]);
Ethernet.begin(mac, ip, subnet);
}
if (loop_timer != Mb.R[1]) {
loop_timer = Mb.R[1];
EEPROM.write(LOOP_TIMER_LOW_ADDR, loop_timer);
EEPROM.write(LOOP_TIMER_HIGH_ADDR, loop_timer >> 8);
loop_timer << 8;
}
}
69
Communications between PLC and microcontroller using Modbus Protocol 70
Modbus_master.ino
#include <ModbusRtu.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
/**
This is an structe which contains a query to an slave device
*/
modbus_t telegram;
70
71 Annex
void setup() {
// telegram 0: read registers
telegram.u8id = 1; // slave address
telegram.u8fct = 3; // function code (this one is registers
read)
telegram.u16RegAdd = 0; // start address in slave
telegram.u16CoilsNo = 22; // number of elements (coils or
registers) to read
telegram.au16reg = au16data; // pointer to a memory array in the
Arduino
void loop() {
switch ( u8state ) {
case 0:
if (millis() > u32wait) u8state++; // wait state
break;
case 1:
master.query( telegram ); // send query (only once)
u8state++;
break;
case 2:
master.poll(); // check incoming messages
if (master.getState() == COM_IDLE) {
u8state ++;
u32wait = millis() + 1000;}
break;
71
Communications between PLC and microcontroller using Modbus Protocol 72
case 3:
u8state = 0;
lcd.setCursor(0, 0);
lcd.print("FOV: ");
lcd.print(au16data[1]);
lcd.setCursor(0, 1);
lcd.print("A Info: ");
lcd.print(au16data[7]);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("DNI R: ");
lcd.print(au16data[8]);
lcd.setCursor(0, 1);
lcd.print("T: ");
lcd.print(au16data[9]);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("FAX: ");
lcd.print(au16data[10]);
lcd.setCursor(0, 1);
lcd.print("FAY: ");
lcd.print(au16data[11]);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("AX: ");
lcd.print(au16data[12]);
lcd.setCursor(0, 1);
lcd.print("AY: ");
lcd.print(au16data[13]);
72
73 Annex
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Azi: ");
lcd.print(au16data[14]);
lcd.setCursor(0, 1);
lcd.print("Ele: ");
lcd.print(au16data[15]);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("MVX: ");
lcd.print(au16data[16]);
lcd.setCursor(0, 1);
lcd.print("MVY: ");
lcd.print(au16data[17]);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("MVZ: ");
lcd.print(au16data[18]);
delay(2000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("AVX: ");
lcd.print(au16data[19]);
lcd.setCursor(0, 1);
lcd.print("AVY: ");
lcd.print(au16data[20]);
delay(3000);
lcd.clear();
73
Communications between PLC and microcontroller using Modbus Protocol 74
lcd.setCursor(0, 0);
lcd.print("AVZ: ");
lcd.print(au16data[21]);
delay(2000);
lcd.clear();
break;
}
}
Restart_EEPROM.ino
This program can be used to set the first byte of the EEPROM to 1 and the rest to 0.
#include <EEPROM.h>
void setup() {
// put your setup code here, to run once:
EEPROM.write(0,1);
for (int i = 1 ; i < EEPROM.length() ; i++) {
EEPROM.write(i, 0);
}
}
void loop() {
// put your main code here, to run repeatedly:
74
75 Annex
Modbus_tcp_mass-x.ino
#include <Ethernet.h>
#include <SPI.h>
#include <Mudbus.h>
#include <EEPROM.h>
#include <ModbusMaster.h>
//EEPROM addresses
#define IP_ADDR 0
#define LOOP_TIMER_HIGH_ADDR 1
#define LOOP_TIMER_LOW_ADDR 2
uint8_t result;
Mudbus Mb;
uint8_t ipB = 1;
uint16_t loop_timer = 100;
75
Communications between PLC and microcontroller using Modbus Protocol 76
void setup() {
pinMode(3, OUTPUT);
node.begin(19200);
Timer = millis();
}
76
77 Annex
void loop() {
Mb.Run();
if ((millis() - Timer > loop_timer) | (millis() - Timer < 0)) {
//poll messages
result = node.readHoldingRegisters(1, 22);
if (result == node.ku8MBSuccess) {
reg_poll();
}
Timer = millis();
}
}
void reg_poll() {
int i;
for (i = 0; i < 22; i++) {
Mb.R[i + 2] = node.getResponseBuffer(i);
}
if (ip[3] != Mb.R[0]) {
ip[3] = Mb.R[0];
EEPROM.write(IP_ADDR, ip[3]);
Ethernet.begin(mac, ip, subnet);
}
if (loop_timer != Mb.R[1]) {
loop_timer = Mb.R[1];
EEPROM.write(LOOP_TIMER_LOW_ADDR, loop_timer);
EEPROM.write(LOOP_TIMER_HIGH_ADDR, loop_timer >> 8);
loop_timer << 8;
}
77
Communications between PLC and microcontroller using Modbus Protocol 78
REFERENCES
78