Skip to content

Commit 77d8b61

Browse files
committed
Initial commit - Modbus RTU w/ two PMCs
1 parent 42ba9e8 commit 77d8b61

File tree

1 file changed

+218
-0
lines changed
  • content/hardware/05.pro-solutions/solutions-and-kits/portenta-machine-control/tutorials/user-manual

1 file changed

+218
-0
lines changed

content/hardware/05.pro-solutions/solutions-and-kits/portenta-machine-control/tutorials/user-manual/content.md

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,6 +1434,224 @@ You should see the four onboard LEDs of the Opta™ device turn on and off, as s
14341434

14351435
![Onboard LEDs of an Opta™ device controlled by a Portenta Machine Control device via Modbus TCP](assets/rtu-blink.gif)
14361436

1437+
The next example shows how to establish Modbus RTU communication between two Portenta Machine Control devices. Since Portenta Machine Control supports half-duplex and full-duplex mode, each mode requires different wiring setup.
1438+
1439+
We will begin showing full-duplex mode example following the connection diagram below between two Portenta Machine Control devices:
1440+
1441+
TODO Update image with two PMC in full-duplex wiring
1442+
1443+
![Modbus RTU (Full-Duplex) between two Portenta Machine Control](assets/modbus-rtu.png)
1444+
1445+
The following script defines a Portenta Machine Control as a Client device, which sends 4 coils to the Server Portenta Machine Control.
1446+
1447+
```arduino
1448+
/*
1449+
Portenta's Machine Control Modbus RTU Client Example
1450+
Name: portenta_machine_control_modbus_rtu_client_led_control.ino
1451+
Purpose: Demonstrates controlling Modbus RTU coils using a
1452+
Portenta Machine Control device.
1453+
@author Arduino PRO Content Team
1454+
@version 1.0 01/10/23
1455+
*/
1456+
// Include the necessary libraries
1457+
#include <Arduino_PortentaMachineControl.h>
1458+
#include <ArduinoRS485.h>
1459+
#include <ArduinoModbus.h>
1460+
1461+
// Define the baud rate for Modbus communication
1462+
constexpr auto baudrate{ 38400 };
1463+
1464+
// Calculate preDelay and postDelay in microseconds as per Modbus RTU Specification
1465+
// Modbus over serial line specification and implementation guide V1.02
1466+
// Paragraph 2.5.1.1 Modbus Message RTU Framing
1467+
// https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
1468+
constexpr auto bitduration{ 1.f / baudrate };
1469+
constexpr auto preDelay{ bitduration * 9.6f * 3.5f * 1e6 };
1470+
constexpr auto postDelay{ bitduration * 9.6f * 3.5f * 1e6 };
1471+
1472+
// Counter variable to demonstrate coil control logic
1473+
int counter = 0;
1474+
1475+
void setup() {
1476+
// Begin serial communication at 9600 baud for debug messages
1477+
Serial.begin(9600);
1478+
1479+
// Wait for serial port to connect (necessary for boards with native USB)
1480+
//while (!Serial);
1481+
// Initialize RS-485 communication with specified baud rate and delays
1482+
MachineControl_RS485Comm.begin(baudrate, preDelay, postDelay);
1483+
1484+
MachineControl_RS485Comm.setFullDuplex(true);
1485+
1486+
// Short delay to ensure RS-485 communication is stable
1487+
delay(2500);
1488+
1489+
// Indicate start of Modbus RTU client operation
1490+
Serial.println("- Modbus RTU Coils control");
1491+
1492+
// Start the Modbus RTU client with the RS-485 communication settings
1493+
if (!ModbusRTUClient.begin(MachineControl_RS485Comm, baudrate, SERIAL_8N1)) {
1494+
Serial.println("- Failed to start Modbus RTU Client!");
1495+
// Halt execution if unable to start
1496+
while (1)
1497+
;
1498+
}
1499+
}
1500+
void loop() {
1501+
// Increment counter to change coil values on each iteration
1502+
counter++;
1503+
1504+
// Determine coil value based on the counter's parity
1505+
byte coilValue = ((counter % 2) == 0) ? 0x00 : 0x01;
1506+
1507+
// Attempt to write coil values to a Modbus RTU server
1508+
Serial.print("- Writing coil values ... ");
1509+
1510+
// Begin transmission to Modbus server (slave ID 1) to write coil values at address 0x00
1511+
ModbusRTUClient.beginTransmission(1, COILS, 0x00, 4);
1512+
1513+
for (int i = 0; i < 4; i++) {
1514+
// Write the same value to all 4 coils
1515+
ModbusRTUClient.write(coilValue);
1516+
}
1517+
1518+
// Check for successful transmission and report errors if any
1519+
// Print error code if transmission failed
1520+
// Or confirm successful coil value writing
1521+
if (!ModbusRTUClient.endTransmission()) {
1522+
Serial.print("- Failed! Error code: ");
1523+
Serial.println(ModbusRTUClient.lastError());
1524+
} else {
1525+
Serial.println("- Success!");
1526+
}
1527+
1528+
// Delay before next operation to simulate periodic control
1529+
delay(1000);
1530+
}
1531+
```
1532+
1533+
Because the Portenta Machine Control is operating in Full-Duplex mode, the following line is important to enable Full-Duplex mode:
1534+
1535+
```arduino
1536+
MachineControl_RS485Comm.setFullDuplex(true);
1537+
```
1538+
1539+
The Server Portenta Machine Control uses the script below, which translates received coils into corresponding Digital Outputs. It will blink four Digital Outputs accordingly in timely manner.
1540+
1541+
```arduino
1542+
// Include the necessary libraries
1543+
#include <ArduinoRS485.h>
1544+
#include <ArduinoModbus.h>
1545+
#include <Arduino_PortentaMachineControl.h>
1546+
1547+
// Define the number of coils to control LEDs
1548+
const int numCoils = 4;
1549+
1550+
// Define the baud rate for Modbus communication
1551+
constexpr auto baudrate{ 38400 };
1552+
1553+
// Calculate preDelay and postDelay in microseconds as per Modbus RTU Specification
1554+
// Modbus over serial line specification and implementation guide V1.02
1555+
// Paragraph 2.5.1.1 Modbus Message RTU Framing
1556+
// https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
1557+
constexpr auto bitduration{ 1.f / baudrate };
1558+
constexpr auto preDelay{ bitduration * 9.6f * 3.5f * 1e6 };
1559+
constexpr auto postDelay{ bitduration * 9.6f * 3.5f * 1e6 };
1560+
1561+
void setup() {
1562+
// Begin serial communication at a baud rate of 9600 for debug messages
1563+
Serial.begin(9600);
1564+
1565+
// Print a startup message
1566+
Serial.println("- Modbus RTU Server");
1567+
1568+
// Set RS485 transmission delays as per Modbus specification
1569+
MachineControl_RS485Comm.begin(baudrate, preDelay, postDelay);
1570+
1571+
// Enable full duplex mode and 120 Ohm termination resistors
1572+
MachineControl_RS485Comm.setFullDuplex(true);
1573+
1574+
// Set the RS-485 interface in receive mode initially
1575+
MachineControl_RS485Comm.receive();
1576+
1577+
// Start the Modbus RTU server with a specific slave ID and baud rate
1578+
// Halt execution if the server fails to start
1579+
if (!ModbusRTUServer.begin(MachineControl_RS485Comm, 1, baudrate, SERIAL_8N1)) {
1580+
Serial.println("- Failed to start Modbus RTU Server!");
1581+
while (1)
1582+
;
1583+
}
1584+
1585+
//Set over current behavior of all channels to latch mode (true)
1586+
MachineControl_DigitalOutputs.begin(true);
1587+
1588+
//At startup set all channels to OPEN
1589+
MachineControl_DigitalOutputs.writeAll(0);
1590+
1591+
MachineControl_DigitalOutputs.write(7, HIGH);
1592+
1593+
// Configure coils for controlling the onboard LEDs
1594+
ModbusRTUServer.configureCoils(0x00, numCoils);
1595+
}
1596+
1597+
void loop() {
1598+
// Poll for Modbus RTU requests and process them
1599+
int packetReceived = ModbusRTUServer.poll();
1600+
Serial.println(packetReceived);
1601+
if (packetReceived) {
1602+
// Process each coil's state and control LEDs accordingly
1603+
for (int i = 0; i < numCoils; i++) {
1604+
// Read coil value
1605+
// Update discrete input with the coil's state
1606+
int coilValue = ModbusRTUServer.coilRead(i);
1607+
ModbusRTUServer.discreteInputWrite(i, coilValue);
1608+
1609+
// Debug output to the IDE's serial monitor
1610+
Serial.print("LED ");
1611+
Serial.print(i);
1612+
Serial.print(" = ");
1613+
Serial.println(coilValue);
1614+
1615+
// Control the onboard LEDs based on the coil values
1616+
switch (i) {
1617+
case 0:
1618+
MachineControl_DigitalOutputs.write(0, coilValue ? HIGH : LOW);
1619+
break;
1620+
case 1:
1621+
MachineControl_DigitalOutputs.write(1, coilValue ? HIGH : LOW);
1622+
break;
1623+
case 2:
1624+
MachineControl_DigitalOutputs.write(2, coilValue ? HIGH : LOW);
1625+
break;
1626+
case 3:
1627+
MachineControl_DigitalOutputs.write(3, coilValue ? HIGH : LOW);
1628+
// New line for better readability
1629+
Serial.println();
1630+
break;
1631+
default:
1632+
// Error handling for unexpected coil addresses
1633+
Serial.println("- Output out of scope!");
1634+
break;
1635+
}
1636+
}
1637+
}
1638+
}
1639+
```
1640+
1641+
To establish communication between two Portenta Machine Control with Modbus RTU in Half-Duplex mode, following wiring setup is required:
1642+
1643+
TODO Update image with two PMC in half-duplex wiring
1644+
1645+
![Modbus RTU (Half-Duplex) between two Portenta Machine Control](assets/modbus-rtu.png)
1646+
1647+
Previous examples can be used in Half-Duplex mode and it requires only one minor change in the following line:
1648+
1649+
```arduino
1650+
MachineControl_RS485Comm.setFullDuplex(false);
1651+
```
1652+
1653+
The line can be updated to disable *Full-Duplex* mode or by commenting the line to ignore the corresponding process.
1654+
14371655
#### Modbus TCP
14381656

14391657
Modbus TCP, taking advantage of Ethernet connectivity, allows easy integration with existing computer networks and facilitates data communication over long distances using the existing network infrastructure. It operates in full-duplex mode, allowing simultaneous sending and receiving of data.

0 commit comments

Comments
 (0)