Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
Abhilash95
Published © Apache-2.0

Building a Flexible Modbus SDK for Arduino Projects

Create a flexible Modbus SDK for Arduino, allowing easy integration of any sensors for industrial-grade data communication and control.

IntermediateFull instructions provided1 hour142
Building a Flexible Modbus SDK for Arduino Projects

Things used in this project

Hardware components

Espressif ESP32 Development Board - Developer Edition
Espressif ESP32 Development Board - Developer Edition
×1
SCD40 Module
×1
Plantower PMS5003
×1
RS485 to TTL Module
×1
USB to RS485 Converter
×1

Software apps and online services

PlatformIO IDE
PlatformIO IDE
ModbusPoll

Story

Read more

Schematics

Block Diagram

Block diagram for connection

Modbus Poll Output

Serial Monitor Output

Code

ModbusSDK.h

C Header File
#ifndef MODBUSSDK_H
#define MODBUSSDK_H

#include <Arduino.h>
#include <vector>

class ModbusSlave {
public:
    ModbusSlave(HardwareSerial& serial);
    void handleRequest();
    void setSensorData(const std::vector<uint16_t>& data);

private:
    HardwareSerial& modbusSerial;
    std::vector<uint16_t> sensorData;

    bool isValidRequest(byte* request);
    void sendResponse();
    unsigned int calculateCRC(byte *buffer, byte length);
};

#endif

ModbusSDK.cpp

C/C++
#include "ModbusSDK.h"

ModbusSlave::ModbusSlave(HardwareSerial& serial) : modbusSerial(serial) {}

void ModbusSlave::handleRequest() {
    if (modbusSerial.available() > 0) {
        byte request[8];
        modbusSerial.readBytes(request, 8);

        Serial.print("Received request: ");
        for (int i = 0; i < 8; i++) {
            Serial.print(request[i], HEX);
            Serial.print(" ");
        }
        Serial.println();

        if (isValidRequest(request)) {
            sendResponse();
        } else {
            Serial.println("Invalid Modbus request received.");
        }
    }
}

void ModbusSlave::setSensorData(const std::vector<uint16_t>& data) {
    sensorData = data;
}

bool ModbusSlave::isValidRequest(byte* request) {
    return request[0] == 0x01 && request[1] == 0x03 && request[2] == 0x00 &&
           request[3] == 0x00 && request[4] == 0x00 && request[5] == (sensorData.size() * 2);
}

void ModbusSlave::sendResponse() {
    int byteCount = sensorData.size() * 2;
    int responseSize = 3 + byteCount + 2; // 3 bytes for header, byteCount bytes for data, 2 bytes for CRC
    byte response[responseSize];

    response[0] = 0x01; // Slave address
    response[1] = 0x03; // Function code
    response[2] = byteCount; // Byte count

    for (size_t i = 0; i < sensorData.size(); ++i) {
        response[3 + i * 2] = (sensorData[i] >> 8) & 0xFF; // High byte
        response[4 + i * 2] = sensorData[i] & 0xFF; // Low byte
    }

    unsigned int crc = calculateCRC(response, 3 + byteCount);
    response[3 + byteCount] = crc & 0xFF; // CRC low byte
    response[4 + byteCount] = (crc >> 8) & 0xFF; // CRC high byte

    // Print response for debugging
    Serial.print("Sending response: ");
    for (int i = 0; i < responseSize; i++) {
        Serial.print(response[i], HEX);
        Serial.print(" ");
    }
    Serial.println();

    modbusSerial.write(response, responseSize);
}

unsigned int ModbusSlave::calculateCRC(byte *buffer, byte length) {
    unsigned int crc = 0xFFFF;
    for (byte i = 0; i < length; i++) {
        crc ^= buffer[i];
        for (byte j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc >>= 1;
                crc ^= 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

Main.cpp

C/C++
#include <Wire.h>
#include "SensirionI2CScd4x.h"
#include "ModbusSDK.h"

SensirionI2CScd4x scd4x;
ModbusSlave modbusSlave(Serial2);

const int pms5003RxPin = 15; // RX for PMS5003
const int pms5003TxPin = 13; // TX for PMS5003

uint16_t pm1_0, pm2_5, pm10, co2;
float temperature, humidity;

void setup() {
    Serial.begin(115200);
    Serial1.begin(9600, SERIAL_8N1, pms5003RxPin, pms5003TxPin); // PMS5003
    Serial2.begin(9600, SERIAL_8N1, 16, 17); // RS485

    Wire.begin(21, 22); // Initialize I2C
    scd4x.begin(Wire);
    
    if (uint16_t error = scd4x.startPeriodicMeasurement()) {
        Serial.print("Error starting periodic measurement: ");
        Serial.println(error);
    } else {
        Serial.println("Periodic measurement started successfully.");
    }
}

void loop() {
    readPMS5003();
    readSCD4x();
    modbusSlave.handleRequest();
}

void readPMS5003() {
    if (Serial1.available() > 32) { // 32 bytes frame
        if (readPMS5003Data()) {
            std::vector<uint16_t> sensorData = {pm1_0, pm2_5, pm10};
            modbusSlave.setSensorData(sensorData);
            Serial.print("PM1.0: ");
            Serial.print(pm1_0);
            Serial.print(" PM2.5: ");
            Serial.print(pm2_5);
            Serial.print(" PM10: ");
            Serial.println(pm10);
        } else {
            Serial.println("Error reading PMS5003 data.");
        }
    }
}

void readSCD4x() {
    uint16_t error = scd4x.readMeasurement(co2, temperature, humidity);
    if (error) {
        Serial.print("Error reading SCD40 measurement: ");
        Serial.println(error);
    } else if (co2 == 0) {
        Serial.println("Invalid SCD40 sample detected, skipping.");
    } else {
        std::vector<uint16_t> sensorData = {co2, static_cast<uint16_t>(temperature * 100), static_cast<uint16_t>(humidity * 100)};
        modbusSlave.setSensorData(sensorData);
        Serial.print("CO2: ");
        Serial.print(co2);
        Serial.print(" ppm, Temperature: ");
        Serial.print(temperature);
        Serial.print(" degC, Humidity: ");
        Serial.print(humidity);
        Serial.println(" %RH");
    }
}

bool readPMS5003Data() {
    byte buffer[32];
    if (Serial1.readBytes(buffer, 32) == 32) {
        if (buffer[0] == 0x42 && buffer[1] == 0x4D) { // Frame header
            pm1_0 = (buffer[10] << 8) | buffer[11];
            pm2_5 = (buffer[12] << 8) | buffer[13];
            pm10 = (buffer[14] << 8) | buffer[15];
            return true;
        }
    }
    return false;
}

Credits

Abhilash95
1 project • 0 followers
Contact

Comments

Please log in or sign up to comment.