Skip to content

Commit 9af2e0a

Browse files
ArduinoBotTaddyHCmcmchrisjcarolinares
authored
[PC-1660] Portenta Machine Control: User Manual Modbus RTU w/ two PMCs (#2029)
* Initial commit - Modbus RTU w/ two PMCs * Modbus RTU PMCs content update * User manual RTU content update w/ Graphics * User manual content update * User manual content minor update * User manual content minor linter patch * Animation added to rtu ex * Animation added to rtu ex 2 * Update content/hardware/05.pro-solutions/solutions-and-kits/portenta-machine-control/tutorials/user-manual/content.md Co-authored-by: Julián Caro Linares <[email protected]> * Update content/hardware/05.pro-solutions/solutions-and-kits/portenta-machine-control/tutorials/user-manual/content.md Co-authored-by: Julián Caro Linares <[email protected]> * User manual struct update post review * Content minor tweak (Post review) * Content minor tweak (Post review) * Github PR Reconditioner empty commit --------- Co-authored-by: TaddyHC <[email protected]> Co-authored-by: Christopher Méndez <[email protected]> Co-authored-by: TaddyHC <[email protected]> Co-authored-by: Julián Caro Linares <[email protected]>
1 parent 6a52bf0 commit 9af2e0a

File tree

5 files changed

+218
-1
lines changed

5 files changed

+218
-1
lines changed

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

+218-1
Original file line numberDiff line numberDiff line change
@@ -1232,10 +1232,227 @@ To use the Modbus protocol with your Portenta Machine Control, you will need the
12321232

12331233
#### Modbus RTU
12341234

1235-
Modbus RTU, generally operating in half-duplex mode, with its capability to handle noisy and long-distance transmission lines, makes it an excellent choice for industrial environments. Modbus RTU communication is supported using Portenta's Machine Control RS-485 physical interface.
1235+
Modbus RTU, generally operating in half-duplex mode, has the capability to handle noisy and long-distance transmission lines, which makes it an excellent choice for industrial environments. Modbus RTU communication is supported using Portenta Machine Control's RS-485 physical interface.
12361236

12371237
***The Portenta Machine Control has onboard termination resistors; its RS-485 interface can be configured as a half or full duplex.***
12381238

1239+
#### Using Two Portenta Machine Controls
1240+
1241+
The following example shows how to establish Modbus RTU communication between **two Portenta Machine Control devices**. We will use four LEDs from the Digital Output port of the Portenta Machine Control as the visual indicator to confirm the communication is working as intended. Since Portenta Machine Control supports half-duplex and full-duplex modes, each mode requires a different wiring setup.
1242+
1243+
We will begin showing the **Full-Duplex mode** example following the connection diagram below between two Portenta Machine Control devices:
1244+
1245+
![Modbus RTU (Full-Duplex) between two Portenta Machine Control](assets/modbus-full-rtu-pmcs.png)
1246+
1247+
The following script assigns a Portenta Machine Control as a Client device, which sends four coil values to the Portenta Machine Control assigned as a Server.
1248+
1249+
```arduino
1250+
// Include the necessary libraries
1251+
#include <Arduino_PortentaMachineControl.h>
1252+
#include <ArduinoRS485.h>
1253+
#include <ArduinoModbus.h>
1254+
1255+
// Define the baud rate for Modbus communication
1256+
constexpr auto baudrate{ 38400 };
1257+
1258+
// Calculate preDelay and postDelay in microseconds as per Modbus RTU Specification
1259+
// Modbus over serial line specification and implementation guide V1.02
1260+
// Paragraph 2.5.1.1 Modbus Message RTU Framing
1261+
// https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
1262+
constexpr auto bitduration{ 1.f / baudrate };
1263+
constexpr auto preDelay{ bitduration * 9.6f * 3.5f * 1e6 };
1264+
constexpr auto postDelay{ bitduration * 9.6f * 3.5f * 1e6 };
1265+
1266+
// Counter variable to demonstrate coil control logic
1267+
int counter = 0;
1268+
1269+
void setup() {
1270+
// Begin serial communication at 9600 baud for debug messages
1271+
Serial.begin(9600);
1272+
1273+
// Wait for serial port to connect (necessary for boards with native USB)
1274+
//while (!Serial);
1275+
// Initialize RS-485 communication with specified baud rate and delays
1276+
MachineControl_RS485Comm.begin(baudrate, preDelay, postDelay);
1277+
1278+
MachineControl_RS485Comm.setFullDuplex(true);
1279+
1280+
// Short delay to ensure RS-485 communication is stable
1281+
delay(2500);
1282+
1283+
// Indicate start of Modbus RTU client operation
1284+
Serial.println("- Modbus RTU Coils control");
1285+
1286+
// Start the Modbus RTU client with the RS-485 communication settings
1287+
if (!ModbusRTUClient.begin(MachineControl_RS485Comm, baudrate, SERIAL_8N1)) {
1288+
Serial.println("- Failed to start Modbus RTU Client!");
1289+
// Halt execution if unable to start
1290+
while (1)
1291+
;
1292+
}
1293+
}
1294+
void loop() {
1295+
// Increment counter to change coil values on each iteration
1296+
counter++;
1297+
1298+
// Determine coil value based on the counter's parity
1299+
byte coilValue = ((counter % 2) == 0) ? 0x00 : 0x01;
1300+
1301+
// Attempt to write coil values to a Modbus RTU server
1302+
Serial.print("- Writing coil values ... ");
1303+
1304+
// Begin transmission to Modbus server (slave ID 1) to write coil values at address 0x00
1305+
ModbusRTUClient.beginTransmission(1, COILS, 0x00, 4);
1306+
1307+
for (int i = 0; i < 4; i++) {
1308+
// Write the same value to all 4 coils
1309+
ModbusRTUClient.write(coilValue);
1310+
}
1311+
1312+
// Check for successful transmission and report errors if any
1313+
// Print error code if transmission failed
1314+
// Or confirm successful coil value writing
1315+
if (!ModbusRTUClient.endTransmission()) {
1316+
Serial.print("- Failed! Error code: ");
1317+
Serial.println(ModbusRTUClient.lastError());
1318+
} else {
1319+
Serial.println("- Success!");
1320+
}
1321+
1322+
// Delay before next operation to simulate periodic control
1323+
delay(1000);
1324+
}
1325+
```
1326+
1327+
Because the Portenta Machine Control is operating in **Full-Duplex mode**, the following line of code is essential and must be included within the code to enable Full-Duplex mode:
1328+
1329+
```arduino
1330+
MachineControl_RS485Comm.setFullDuplex(true);
1331+
```
1332+
1333+
The Server Portenta Machine Control uses the script below, which translates received coil values into corresponding Digital Outputs. It will blink four Digital Outputs accordingly in a timely manner.
1334+
1335+
```arduino
1336+
// Include the necessary libraries
1337+
#include <ArduinoRS485.h>
1338+
#include <ArduinoModbus.h>
1339+
#include <Arduino_PortentaMachineControl.h>
1340+
1341+
// Define the number of coils to control LEDs
1342+
const int numCoils = 4;
1343+
1344+
// Define the baud rate for Modbus communication
1345+
constexpr auto baudrate{ 38400 };
1346+
1347+
// Calculate preDelay and postDelay in microseconds as per Modbus RTU Specification
1348+
// Modbus over serial line specification and implementation guide V1.02
1349+
// Paragraph 2.5.1.1 Modbus Message RTU Framing
1350+
// https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
1351+
constexpr auto bitduration{ 1.f / baudrate };
1352+
constexpr auto preDelay{ bitduration * 9.6f * 3.5f * 1e6 };
1353+
constexpr auto postDelay{ bitduration * 9.6f * 3.5f * 1e6 };
1354+
1355+
void setup() {
1356+
// Begin serial communication at a baud rate of 9600 for debug messages
1357+
Serial.begin(9600);
1358+
1359+
// Print a startup message
1360+
Serial.println("- Modbus RTU Server");
1361+
1362+
// Set RS485 transmission delays as per Modbus specification
1363+
MachineControl_RS485Comm.begin(baudrate, preDelay, postDelay);
1364+
1365+
// Enable full duplex mode and 120 Ohm termination resistors
1366+
MachineControl_RS485Comm.setFullDuplex(true);
1367+
1368+
// Set the RS-485 interface in receive mode initially
1369+
MachineControl_RS485Comm.receive();
1370+
1371+
// Start the Modbus RTU server with a specific slave ID and baud rate
1372+
// Halt execution if the server fails to start
1373+
if (!ModbusRTUServer.begin(MachineControl_RS485Comm, 1, baudrate, SERIAL_8N1)) {
1374+
Serial.println("- Failed to start Modbus RTU Server!");
1375+
while (1)
1376+
;
1377+
}
1378+
1379+
//Set over current behavior of all channels to latch mode (true)
1380+
MachineControl_DigitalOutputs.begin(true);
1381+
1382+
//At startup set all channels to OPEN
1383+
MachineControl_DigitalOutputs.writeAll(0);
1384+
1385+
// Set 7th Digital Output channel ON to show the port has been correctly configured
1386+
MachineControl_DigitalOutputs.write(7, HIGH);
1387+
1388+
// Configure coils for controlling the onboard LEDs
1389+
ModbusRTUServer.configureCoils(0x00, numCoils);
1390+
}
1391+
1392+
void loop() {
1393+
// Poll for Modbus RTU requests and process them
1394+
int packetReceived = ModbusRTUServer.poll();
1395+
Serial.println(packetReceived);
1396+
if (packetReceived) {
1397+
// Process each coil's state and control LEDs accordingly
1398+
for (int i = 0; i < numCoils; i++) {
1399+
// Read coil value
1400+
// Update discrete input with the coil's state
1401+
int coilValue = ModbusRTUServer.coilRead(i);
1402+
ModbusRTUServer.discreteInputWrite(i, coilValue);
1403+
1404+
// Debug output to the IDE's serial monitor
1405+
Serial.print("LED ");
1406+
Serial.print(i);
1407+
Serial.print(" = ");
1408+
Serial.println(coilValue);
1409+
1410+
// Control the onboard LEDs based on the coil values
1411+
switch (i) {
1412+
case 0:
1413+
MachineControl_DigitalOutputs.write(0, coilValue ? HIGH : LOW);
1414+
break;
1415+
case 1:
1416+
MachineControl_DigitalOutputs.write(1, coilValue ? HIGH : LOW);
1417+
break;
1418+
case 2:
1419+
MachineControl_DigitalOutputs.write(2, coilValue ? HIGH : LOW);
1420+
break;
1421+
case 3:
1422+
MachineControl_DigitalOutputs.write(3, coilValue ? HIGH : LOW);
1423+
// New line for better readability
1424+
Serial.println();
1425+
break;
1426+
default:
1427+
// Error handling for unexpected coil addresses
1428+
Serial.println("- Output out of scope!");
1429+
break;
1430+
}
1431+
}
1432+
}
1433+
}
1434+
```
1435+
1436+
With this, we have two Portenta Machine Control devices, each assigned as a Client and Server correspondingly, communicating with Modbus RTU in full-duplex mode.
1437+
1438+
![Modbus RTU (Full-Duplex) Demo](assets/rtu-full-ani.gif)
1439+
1440+
Alternatively, to establish communication between two Portenta Machine Control with Modbus RTU in **Half-Duplex mode**, the following wiring setup is required:
1441+
1442+
![Modbus RTU (Half-Duplex) between two Portenta Machine Control](assets/modbus-half-rtu-pmcs.png)
1443+
1444+
The previous example can be used in half-duplex mode, but it will require one minor change in the following line of code:
1445+
1446+
```arduino
1447+
MachineControl_RS485Comm.setFullDuplex(false);
1448+
```
1449+
1450+
By modifying this line, *Full-Duplex mode* is deactivated. This minor code tweak, plus the appropriate wiring, enable the two units to communicate using Modbus RTU in half-duplex mode.
1451+
1452+
![Modbus RTU (Half-Duplex) Demo](assets/rtu-half-ani.gif)
1453+
1454+
#### Using a Portenta Machine Control & Opta™
1455+
12391456
The example below shows how to enable Modbus RTU communication between a Portenta Machine Control device and an Opta™ device. For wiring both devices, follow the diagram below:
12401457

12411458
![Portenta Machine Control and Opta™ Modbus RTU wiring](assets/modbus-rtu.png)

0 commit comments

Comments
 (0)