pdf-icon

Arduino 上手教程

StamPLC Modbus Slave 从机

StamPLC驱动库整合了Modbus Slave功能。在整机完成简易的初始化配置后,便能作为从机设备,经由PWR-485接口便捷接入Modbus总线。其他设备也能够非常方便的控制StamPLC的继电器和读取输入信号状态。

案例程序

StamPLC Modbus Slave

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
/*
*SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
*SPDX-License-Identifier: MIT
*/
#include <Arduino.h>
#include <M5StamPLC.h>
void setup()
{
/* Enable Modbus */
auto config = M5StamPLC.config();
config.enableModbusSlave = true;
config.modbusBaudRate = 115200;
config.modbusSlaveId = 1;
config.enableCan = true;
M5StamPLC.config(config);
/* Init M5StamPLC */
M5StamPLC.begin();
}
void loop()
{
/* Now you can controll StamPLC via Modbus RS485 */
/*
* Modbus Slave Configuration for M5StamPLC
* =======================================
*
* Register Map:
* -------------
* 1. Coils (Read/Write)
* - Address 0: Relay 1 output (true/false)
* - Address 1: Relay 2 output (true/false)
* - Address 2: Relay 3 output (true/false)
* - Address 3: Relay 4 output (true/false)
*
* 2. Input Registers (Read-only)
* - Address 0-7: Inputs (true/false) - 8 registers
* - Address 8-9: Temperature (FLOAT32) - 2 registers
* - Address 10-11: Bus Voltage (FLOAT32) - 2 registers
* - Address 12-13: Shunt Current (FLOAT32) - 2 registers
*
* Note: FLOAT32 values use 2 consecutive registers (32 bits total)
*/
delay(1000);
}

Modbus Master

使用任意 ESP32 主控设备,搭配 Unit RS485, 实现Modbus Master。然后通过PWR-485接口对 StamPLC 进行控制。
cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
#include <ArduinoModbus.h>
#include <ArduinoRS485.h>
RS485Class RS485(Serial2, 39, 0, 46, -1);
uint8_t slave_id = 1;
// 将两个 16 位寄存器解析为 FLOAT32
float parseFloat(uint16_t high, uint16_t low)
{
union {
uint32_t i;
float f;
} value;
value.i = ((uint32_t)high << 16) | low;
return value.f;
}
void setup()
{
Serial.begin(115200);
delay(2000);
if (!ModbusRTUClient.begin(115200, SERIAL_8N1)) {
Serial.println("Failed to start Modbus RTU Client");
while (1);
}
}
void loop()
{
/* Now you can controll StamPLC via Modbus RS485 */
/*
* Modbus Slave Configuration for M5StamPLC
* =======================================
*
* Register Map:
* -------------
* 1. Coils (Read/Write)
* - Address 0: Relay 1 output (true/false)
* - Address 1: Relay 2 output (true/false)
* - Address 2: Relay 3 output (true/false)
* - Address 3: Relay 4 output (true/false)
*
* 2. Input Registers (Read-only)
* - Address 0-7: Inputs (true/false) - 8 registers
* - Address 8-9: Temperature (FLOAT32) - 2 registers
* - Address 10-11: Bus Voltage (FLOAT32) - 2 registers
* - Address 12-13: Shunt Current (FLOAT32) - 2 registers
*
* Note: FLOAT32 values use 2 consecutive registers (32 bits total)
*/
Serial.println("\n--- Reading Modbus Registers ---");
// 1. 控制 Coils(0x01)
Serial.println("Turning Relays ON:");
for (uint8_t addr = 0; addr < 4; addr++) {
if (ModbusRTUClient.coilWrite(slave_id, addr, 0xFF)) {
Serial.printf("Relay %d ON\n", addr);
} else {
Serial.printf("Failed to turn on Relay %d\n", addr);
}
delay(500);
}
Serial.println("Reading Coils Status:");
for (uint8_t addr = 0; addr < 4; addr++) {
if (ModbusRTUClient.requestFrom(slave_id, COILS, addr, 1)) {
Serial.printf("Relay %d: ", addr);
while (ModbusRTUClient.available()) {
Serial.print(ModbusRTUClient.read(), HEX);
Serial.print(' ');
}
Serial.println();
} else {
Serial.printf("Failed to read Relay %d\n", addr);
}
}
Serial.println("Turning Relays OFF:");
for (uint8_t addr = 0; addr < 4; addr++) {
if (ModbusRTUClient.coilWrite(slave_id, addr, 0x00)) {
Serial.printf("Relay %d OFF\n", addr);
} else {
Serial.printf("Failed to turn off Relay %d\n", addr);
}
delay(500);
}
Serial.println("Reading Coils Status Again:");
for (uint8_t addr = 0; addr < 4; addr++) {
if (ModbusRTUClient.requestFrom(slave_id, COILS, addr, 1)) {
Serial.printf("Relay %d: ", addr);
while (ModbusRTUClient.available()) {
Serial.print(ModbusRTUClient.read(), HEX);
Serial.print(' ');
}
Serial.println();
} else {
Serial.printf("Failed to read Relay %d\n", addr);
}
}
delay(500);
// 分开读取各个 Input Register 区间
Serial.println("Reading Input Registers:");
uint16_t tempRegisters[2], voltageRegisters[2], currentRegisters[2];
float temperature, busVoltage, shuntCurrent;
if (ModbusRTUClient.requestFrom(slave_id, INPUT_REGISTERS, 8, 2)) {
for (uint8_t i = 0; i < 2; i++) {
tempRegisters[i] = ModbusRTUClient.read();
}
temperature = parseFloat(tempRegisters[0], tempRegisters[1]);
Serial.printf("Temperature: %.2f°C\n", temperature);
} else {
Serial.println("Failed to read Temperature Registers");
}
if (ModbusRTUClient.requestFrom(slave_id, INPUT_REGISTERS, 10, 2)) {
for (uint8_t i = 0; i < 2; i++) {
voltageRegisters[i] = ModbusRTUClient.read();
}
busVoltage = parseFloat(voltageRegisters[0], voltageRegisters[1]);
Serial.printf("Bus Voltage: %.2fV\n", busVoltage);
} else {
Serial.println("Failed to read Bus Voltage Registers");
}
if (ModbusRTUClient.requestFrom(slave_id, INPUT_REGISTERS, 12, 2)) {
for (uint8_t i = 0; i < 2; i++) {
currentRegisters[i] = ModbusRTUClient.read();
}
shuntCurrent = parseFloat(currentRegisters[0], currentRegisters[1]);
Serial.printf("Shunt Current: %.2fA\n", shuntCurrent);
} else {
Serial.println("Failed to read Shunt Current Registers");
}
Serial.printf(">>>>>> Temperature: %.2f°C, Bus Voltage: %.2fV, Shunt Current: %.2fA\n", temperature, busVoltage,
shuntCurrent);
delay(5000);
}
On This Page