在Modbus RTU中同时进行读取和写入操作

简介: 在Modbus RTU中同时进行读取和写入操作
#include "ModbusRTU.h"
#include <iostream>
#include <cstring> // For memcpy

ModbusRTU::ModbusRTU(boost::asio::io_service& io_service, const std::string& port_name)
    : serial(io_service, port_name) {
    serial.set_option(boost::asio::serial_port_base::baud_rate(9600));
    serial.set_option(boost::asio::serial_port_base::character_size(8));
    serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
    serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
}

template<typename T>
void ModbusRTU::writeData(uint8_t slave_id, uint8_t function_code, uint16_t start_address, T value) {
    uint8_t frame[8 + sizeof(T)];
    frame[0] = slave_id;
    frame[1] = function_code;
    frame[2] = (start_address >> 8) & 0xFF;
    frame[3] = start_address & 0xFF;

    std::memcpy(&frame[4], &value, sizeof(T));

    uint16_t crc = calculateCRC(frame, 4 + sizeof(T));
    frame[4 + sizeof(T)] = crc & 0xFF;
    frame[5 + sizeof(T)] = (crc >> 8) & 0xFF;

    boost::asio::write(serial, boost::asio::buffer(frame, 6 + sizeof(T)));
}

template<typename T>
T ModbusRTU::readData(uint8_t slave_id, uint8_t function_code, uint16_t start_address, uint16_t num_registers) {
    uint8_t frame[8];
    frame[0] = slave_id;
    frame[1] = function_code;
    frame[2] = (start_address >> 8) & 0xFF;
    frame[3] = start_address & 0xFF;
    frame[4] = (num_registers >> 8) & 0xFF;
    frame[5] = num_registers & 0xFF;
    uint16_t crc = calculateCRC(frame, 6);
    frame[6] = crc & 0xFF;
    frame[7] = (crc >> 8) & 0xFF;

    boost::asio::write(serial, boost::asio::buffer(frame, 8));

    uint8_t response[256];
    size_t length = serial.read_some(boost::asio::buffer(response, 256));
    if (length > 0 && response[1] == function_code) {
        T result = 0;
        for (size_t i = 0; i < sizeof(T); ++i) {
            reinterpret_cast<uint8_t*>(&result)[i] = response[3 + i];
        }
        return result;
    }
    else {
        std::cerr << "错误:无效的响应或没有收到响应。" << std::endl;
    }
    return T(); // 返回类型 T 的默认值
}

uint16_t ModbusRTU::calculateCRC(const uint8_t* data, size_t length) {
    uint16_t crc = 0xFFFF;
    for (size_t i = 0; i < length; ++i) {
        crc ^= data[i];
        for (int j = 0; j < 8; ++j) {
            if (crc & 1) crc = (crc >> 1) ^ 0xA001;
            else crc >>= 1;
        }
    }
    return crc;
}



void main() {
    try {
        boost::asio::io_service io_service;
        ModbusRTU modbus(io_service, "/dev/ttyS0"); // 根据你的实际串口设备调整

        uint8_t slave_id = 1;  // 从站地址
        uint16_t start_address = 0x0001; // 起始寄存器地址
        uint16_t value_to_write = 12345;  // 要写入的值

        // 写入数据
        modbus.writeData<uint16_t>(slave_id, 0x06, start_address, value_to_write); // 0x06 是写单寄存器的功能码

        std::cout << "数据已写入。" << std::endl;

        // 读取数据
        uint16_t read_value = modbus.readData<uint16_t>(slave_id, 0x03, start_address, 1); // 0x03 是读保持寄存器的功能码

        std::cout << "读取的数据: " << read_value << std::endl;

    }
    catch (const std::exception& e) {
        std::cerr << "异常: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}
hylreg
+关注
目录
打赏
0
0
0
0
1
分享
相关文章
Winform控件优化之无边框窗体及其拖动、调整大小和实现最大最小化关闭功能的自定义标题栏效果
Winform中实现无边框窗体只需要设置FormBorderStyle = FormBorderStyle.None,但是无边框下我们需要保留移动窗体、拖拽调整大小、自定义美观好看的标题栏等...
4429 0
Winform控件优化之无边框窗体及其拖动、调整大小和实现最大最小化关闭功能的自定义标题栏效果
在Linux中使用libmodbus库进行Modbus RTU主从机通信
Modbus RTU是一种常见的工业通信协议,用于在自动化系统中传输数据。libmodbus是一个流行的C库,用于在Linux系统上实现Modbus通信。本文将介绍如何使用libmodbus库在Linux上创建Modbus RTU主从机通信的示例代码。
5388 0
【MODBUS】libmodbus库从Modbus从站读取值
【MODBUS】libmodbus库从Modbus从站读取值
359 0
消息队列 MQ产品使用合集之C++如何使用Paho MQTT库进行连接、发布和订阅消息
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
什么是数据块?西门子S7-200SMART数据块如何使用?
今天我们来学习在西门子S7-200 SMART中如何使用数据块。在讲解数据块的使用之前我们先来看一下什么是数据块:数据块用来对V存储区也叫变量存储区赋初始值;可以对字节、字或双字来分配数据值。
什么是数据块?西门子S7-200SMART数据块如何使用?
小米7B参数推理大模型首次开源!Xiaomi MiMo:数学代码双杀,超越32B巨头
小米开源的MiMo推理大模型通过联动预训练与强化学习算法,在7B参数规模下实现数学推理与代码生成能力的突破性提升,技术报告显示其性能超越部分32B级模型。
537 74
小米7B参数推理大模型首次开源!Xiaomi MiMo:数学代码双杀,超越32B巨头
Qt之QFtp
简述 QFtp 类提供了一个 FTP 协议的客户端实现。 该类提供了一个到 FTP 的直接接口,允许对请求有更多的控制。但是,对于新的应用程序,建议使用 QNetworkAccessManager 和 QNetworkReply,因为这些类拥有一个更简单、还更强大的 API。 简述 QFtp 工作流程 基本使用 连接并登录 FTP 服务器 切换工作目录 列出目
7352 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问