Загрузка данных


#include <Arduino.h>

// ============================================================
// Протокол Modbus RTU — ведомое устройство (Slave)
// Вариант 10
//
// Slave ID       = 12
// AO-регистр     = 5
// Условие LED    = Hreg < 73
//
// Поддерживаемые функции:
// 0x03 — Read Holding Registers
// 0x06 — Write Single Register
// ============================================================


// ---------- Прототипы функций ----------
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);


// ---------- Настройки варианта 10 ----------
const uint16_t Slave_ID = 12;
const uint16_t Hreg_Addr = 5;


// ---------- Глобальные переменные ----------
uint16_t Hreg = 0;
uint16_t crc16;

uint8_t rcvData[256];
uint8_t idxBuf = 0;

#define debug false


// ============================================================
// SETUP
// ============================================================

void setup()
{
    // Скорость последовательного порта
    Serial.begin(115200);

    // Встроенный светодиод Arduino
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, LOW);
}


// ============================================================
// LOOP
// ============================================================

void loop()
{
    // --------------------------------------------------------
    // Принимаем Modbus RTU запрос.
    //
    // Стандартные запросы функций 03 и 06 имеют длину 8 байт:
    //
    // [Slave ID]
    // [Function]
    // [Address Hi]
    // [Address Lo]
    // [Data Hi]
    // [Data Lo]
    // [CRC Lo]
    // [CRC Hi]
    // --------------------------------------------------------

    while (Serial.available())
    {
        if (idxBuf < sizeof(rcvData))
        {
            rcvData[idxBuf] = Serial.read();

            if (debug)
            {
                Serial.write(rcvData[idxBuf]);
            }

            idxBuf++;
        }
        else
        {
            // Защита от переполнения буфера
            idxBuf = 0;
        }
    }


    // --------------------------------------------------------
    // Для функций 03 и 06 запрос содержит 8 байт.
    // Обрабатываем только полный кадр.
    // --------------------------------------------------------

    if (idxBuf >= 8)
    {
        // Проверяем Slave ID
        if (rcvData[0] == Slave_ID)
        {
            // Проверяем CRC
            crc16 = GetCrc16(rcvData, 6);

            uint8_t crcLow = crc16 & 0xFF;
            uint8_t crcHigh = (crc16 >> 8) & 0xFF;

            if ((crcLow == rcvData[6]) &&
                (crcHigh == rcvData[7]))
            {
                // Определяем функцию Modbus
                switch (rcvData[1])
                {
                    // Read Holding Registers
                    case 0x03:
                        MB_F03(rcvData, 8);
                        break;

                    // Write Single Register
                    case 0x06:
                        MB_F06(rcvData, 8);
                        break;

                    default:
                        // Неизвестная функция
                        break;
                }
            }
        }

        // После обработки начинаем принимать следующий кадр
        idxBuf = 0;
    }


    // ========================================================
    // Управление встроенным светодиодом
    //
    // Вариант 10:
    // LED включён, если значение AO-регистра < 73
    // ========================================================

    if (Hreg < 73)
    {
        digitalWrite(LED_BUILTIN, HIGH);
    }
    else
    {
        digitalWrite(LED_BUILTIN, LOW);
    }
}


// ============================================================
// ФУНКЦИЯ 03
// Read Holding Registers
// ============================================================

void MB_F03(uint8_t* data, uint16_t len)
{
    // Адрес регистра состоит из двух байт:
    // data[2] — старший байт
    // data[3] — младший байт

    uint16_t address =
        ((uint16_t)data[2] << 8) |
        data[3];

    // Количество регистров:
    // data[4] — старший байт
    // data[5] — младший байт

    uint16_t quantity =
        ((uint16_t)data[4] << 8) |
        data[5];


    // Проверяем, что запрошен именно наш регистр
    // и количество регистров равно 1.

    if (address != Hreg_Addr || quantity != 1)
    {
        return;
    }


    // --------------------------------------------------------
    // Формируем ответ:
    //
    // Slave ID
    // Function
    // Number of data bytes
    // Register High
    // Register Low
    // CRC Low
    // CRC High
    // --------------------------------------------------------

    uint8_t response[7];

    response[0] = Slave_ID;
    response[1] = 0x03;
    response[2] = 0x02;

    response[3] = Hreg >> 8;
    response[4] = Hreg & 0xFF;


    // Рассчитываем CRC
    crc16 = GetCrc16(response, 5);

    response[5] = crc16 & 0xFF;
    response[6] = (crc16 >> 8) & 0xFF;


    // Отправляем ответ
    Serial.write(response, sizeof(response));
}


// ============================================================
// ФУНКЦИЯ 06
// Write Single Register
// ============================================================

void MB_F06(uint8_t* data, uint16_t len)
{
    // Адрес регистра
    uint16_t address =
        ((uint16_t)data[2] << 8) |
        data[3];


    // Проверяем, что запись идёт именно в AO-регистр №5
    if (address != Hreg_Addr)
    {
        return;
    }


    // Получаем новое значение регистра
    Hreg =
        ((uint16_t)data[4] << 8) |
        data[5];


    // --------------------------------------------------------
    // Согласно Modbus RTU при функции 06 Slave
    // возвращает запрос обратно.
    // --------------------------------------------------------

    uint8_t response[8];

    for (uint8_t i = 0; i < 6; i++)
    {
        response[i] = data[i];
    }


    // Рассчитываем CRC
    crc16 = GetCrc16(response, 6);

    response[6] = crc16 & 0xFF;
    response[7] = (crc16 >> 8) & 0xFF;


    // Отправляем ответ
    Serial.write(response, sizeof(response));
}


// ============================================================
// CRC16 MODBUS
// ============================================================

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 (uint8_t i = 0; i < 8; i++)
        {
            if (crc & 0x0001)
            {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else
            {
                crc >>= 1;
            }
        }
    }

    return crc;
}