#include <Arduino.h>
// ---------- прототипы ----------
void MB_F03(uint8_t * data, uint16_t len);
void MB_F06(uint8_t * data, uint16_t len);
uint16_t GetCrc16(uint8_t * data, uint16_t len);
// ---------- глобальные переменные ----------
const uint16_t Slave_ID = 12; // вариант 3: адрес ведомого — 5
uint16_t crc16;
uint16_t Hreg = 0; // значение регистра AO
uint16_t Hreg_Addr = 5; // вариант 3: адрес регистра AO — 12
#define debug false // true — для отладки через RealTerm (эхо принятых байт)
uint8_t rcvData[256];
uint8_t idxBuf = 0;
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
}
void loop() {
// 1) приём байт из UART в буфер
while (Serial.available()) {
rcvData[idxBuf] = Serial.read();
if (debug) Serial.write(rcvData[idxBuf]);
idxBuf++;
delay(5);
}
// 2) разбор принятого запроса
if (idxBuf > 0) {
uint8_t req[idxBuf];
for (int i = 0; i < idxBuf; i++) req[i] = rcvData[i];
// проверка контрольной суммы CRC16
crc16 = GetCrc16(req, idxBuf - 2);
uint8_t crc16Low = crc16;
uint8_t crc16High = crc16 >> 8;
if ((crc16Low == req[sizeof(req) - 2]) && (crc16High == req[sizeof(req) - 1])) {
// проверка адреса Slave ID
if (req[0] == Slave_ID) {
switch (req[1]) { // проверка функционального кода Modbus
case 0x06:
MB_F06(&req[0], sizeof(req));
break;
case 0x03:
MB_F03(&req[0], sizeof(req));
break;
default:
break;
}
}
}
idxBuf = 0;
}
// 3) индикация текущего значения регистра светодиодом (вариант 3: условие <100)
if (Hreg < 73) {
digitalWrite(LED_BUILTIN, HIGH);
} else {
digitalWrite(LED_BUILTIN, LOW);
}
}
// ---------- чтение регистра AO (Modbus функция 0x03) ----------
void MB_F03(uint8_t * data, uint16_t len) {
if (data[3] == Hreg_Addr && data[5] == 1) {
uint8_t resSize = 3 + 2 + 2; // адрес + код + кол-во байт + 2 байта данных + CRC
uint8_t res[resSize];
res[0] = data[0]; // Slave ID
res[1] = data[1]; // функциональный код (эхо запроса)
res[2] = 0x02; // количество байт данных далее
res[3] = Hreg >> 8; // старший байт значения регистра
res[4] = Hreg; // младший байт значения регистра
crc16 = GetCrc16(res, sizeof(res) - 2);
res[5] = crc16;
res[6] = crc16 >> 8;
Serial.write(res, sizeof(res));
}
}
// ---------- запись регистра AO (Modbus функция 0x06) ----------
void MB_F06(uint8_t * data, uint16_t len) {
if (data[3] == Hreg_Addr) {
Hreg = data[5];
uint8_t resSize = 1 + 1 + 2 + 2 + 2; // адрес+код+адрес рег.(2)+значение(2)+CRC(2)
uint8_t res[resSize];
for (uint8_t i = 0; i < resSize - 2; i++) res[i] = data[i];
crc16 = GetCrc16(res, sizeof(res) - 2);
res[6] = crc16;
res[7] = crc16 >> 8;
Serial.write(res, sizeof(res));
}
}
// ---------- стандартный расчёт CRC16 (Modbus RTU) ----------
uint16_t GetCrc16(uint8_t * data, uint16_t len) {
uint16_t crc = 0xFFFF;
for (uint16_t pos = 0; pos < len; pos++) {
crc ^= (uint16_t)data[pos];
for (int i = 8; i != 0; i--) {
if ((crc & 0x0001) != 0) {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}