← Modbus RTU Chiller Control with an M-Duino PLC
Bioreactor controlM-DuinoModbus RTUControl
Modbus RTU Chiller Control with an M-Duino PLC — full example
Control thermoelectric chillers over Modbus RTU with an M-Duino PLC. Write start/stop, cold/heat enable and a x10 temperature setpoint on RS485. Full code.
Complete, runnable program for the M-Duino (chiller-control-rtu.ino): wiring header, requirements and integration notes included.
Download the full project pack — freeThis example + the related ones + bill of materials
Read-only preview.
/*
* COMPLETE EXAMPLE — Thermoelectric chiller control over Modbus RTU
*
* Device: M-Duino (Industrial Shields, Arduino PLC with RS485)
* Based on: bioreactor control project, ModbusTCPSlaveRTUMaster.ino
*
* Bus: Modbus RTU over RS485, 9600 8N1. Two chillers at addr 1 and 2.
*
* Chiller RTU registers (write via function 0x06):
* 0x06 start/stop (1 = run, 0 = stop)
* 0x08 enable cooling (1 = active)
* 0x09 enable heating (1 = active)
* 0x0A temperature setpoint x10 (e.g. 18.5 C -> 185)
*
* Datasheet note: after each command write, leave a margin of about
* 500 ms before the next command so the chiller consolidates it.
*
* Integration: in the plant these commands arrive from the "Gateway
* Modbus TCP <-> RTU" module; here we demonstrate direct control of one chiller.
*/
#include
#include
ModbusRTUMaster master(RS485);
const uint8_t CHILLER[2] = {1, 2}; // RTU addresses of the two chillers
// Command registers
const uint16_t REG_RUN = 0x06;
const uint16_t REG_COLD = 0x08;
const uint16_t REG_HEAT = 0x09;
const uint16_t REG_SETP = 0x0A;
void setup() {
Serial.begin(115200);
master.begin(9600);
master.setTimeout(300);
// Example startup: chiller 1 in cooling mode at 18.5 C
startChiller(CHILLER[0], 1);
setMode(CHILLER[0], true, false); // cooling ON, heating OFF
setSetpoint(CHILLER[0], 185); // 18.5 C x10
}
void loop() {
// Periodic setpoint maintenance (defensive refresh every 10 s)
static uint32_t t = 0;
if (millis() - t > 10000) {
t = millis();
setSetpoint(CHILLER[0], 185);
Serial.println("Chiller 1 setpoint refreshed to 18.5 C");
}
}
// Writes the run/stop register and respects the datasheet margin
uint8_t startChiller(uint8_t addr, uint8_t status) {
if (!master.writeSingleRegister(addr, REG_RUN, status)) return 1;
if (master.isWaitingResponse()) {
ModbusResponse r = master.available();
if (r && !r.hasError()) {
delay(500); // datasheet requirement
return 0;
}
}
delay(500);
return 1;
}
// Enables cooling/heating in a mutually exclusive way
uint8_t setMode(uint8_t addr, bool cold, bool heat) {
uint8_t err = 0;
err += writeReg(addr, REG_COLD, cold ? 1 : 0);
err += writeReg(addr, REG_HEAT, heat ? 1 : 0);
return err;
}
// Temperature setpoint in tenths of a degree (18.5 C -> 185)
uint8_t setSetpoint(uint8_t addr, uint16_t tempx10) {
return writeReg(addr, REG_SETP, tempx10);
}
// Generic write with response confirmation
uint8_t writeReg(uint8_t addr, uint16_t reg, uint16_t val) {
if (!master.writeSingleRegister(addr, reg, val)) return 1;
if (master.isWaitingResponse()) {
ModbusResponse r = master.available();
if (r && !r.hasError()) {
delay(500); // margin between consecutive commands
return 0;
}
}
delay(500);
return 1;
}
Download the full project pack — freeThis example + the related ones + bill of materials