ModbusCRC16校验 示例代码

简介: 本文提供了ModbusCRC16校验的示例代码,包括计算CRC、添加CRC校验位、删除CRC校验位和比较CRC校验位等四种常用函数的实现,以及一个完整的示例代码。这些代码在Ubuntu20.04环境下经过测试并确认有效。示例代码采用C++编写,展示了如何使用这些函数来处理Modbus通信中的数据校验。

作者: Herman Ye @Galbot @Auromix
测试环境: Ubuntu20.04
更新日期: 2023/08/30
注1: @Auromix 是一个机器人爱好者开源组织。
注2: 本文在更新日期经过测试,确认有效。

笔者出于学习交流目的,
给出以下ModbusCRC16校验常用的四种函数以及完整示例代码:

1.计算CRC

注意: 此处在末尾进行了高低位交换,可根据需求删减代码交换高低位顺序。

/**
 * @brief Calculate the Modbus CRC-16 checksum for a given data vector.
 *
 * This function calculates the CRC-16 checksum using the Modbus protocol
 * polynomial for the provided data vector.
 *
 * @param data The vector of bytes for which to calculate the CRC-16 checksum.
 * @return The calculated CRC-16 checksum value as an unsigned short.
 */
unsigned short calculateModbusCRC16(const vector<uint8_t> &data)
{
   
    int length = data.size();
    unsigned short CRC = 0xFFFF; // Initial value
    for (int i = 0; i < length; i++)
    {
   
        CRC = CRC ^ data[i]; // XOR byte into the least significant byte of CRC
        for (int j = 0; j < 8; j++)
        {
   
            if (CRC & 1)
            {
   
                CRC >>= 1;
                CRC ^= 0xA001;
            }
            else
            {
   
                CRC >>= 1;
            }
        }
    }
    // Swap the bytes of the CRC to match Modbus convention
    unsigned short swappedCRC = ((CRC >> 8) & 0xFF) | ((CRC & 0xFF) << 8);
    return swappedCRC;
}

2.添加CRC校验位

注意: 此处进行了高低位交换,可根据需求删减代码交换高低位顺序。

/**
 * @brief Add Modbus CRC-16 to a data vector.
 * 
 * This function calculates the Modbus CRC-16 checksum for the provided data vector
 * and appends the high and low bytes of the CRC to the end of the data vector.
 * 
 * @param data The data vector to which the CRC will be added.
 */
void addModbusCRC16(vector<uint8_t> &data)
{
   
    // Calculate the CRC-16 checksum
    unsigned short crc = calculateModbusCRC16(data);

    // Append the high byte of CRC to the data vector
    data.push_back((crc >> 8) & 0xFF);

    // Append the low byte of CRC to the data vector
    data.push_back(crc & 0xFF);
}

3.删除CRC校验位

/**
 * @brief Remove Modbus CRC-16 from a vector of data.
 * 
 * This function takes a vector of data with Modbus CRC-16 at the end and removes
 * the CRC-16 bytes from the end of the vector, effectively stripping the CRC-16
 * checksum from the data.
 * 
 * @param dataWithCRC A reference to the vector containing the data with CRC-16.
 * 
 * @note This function does not perform any CRC-16 validation or calculation. It
 * assumes that the last two bytes of the vector represent the CRC-16 checksum
 * and removes them regardless of their validity.
 * 
 * @warning It is the responsibility of the caller to ensure that the input vector
 * has a length of at least 2, as this function does not perform length checking.
 * If the length is less than 2, an error message is printed to the standard error
 * stream, and no modifications are made to the input vector.
 */
void removeModbusCRC16(vector<uint8_t> &dataWithCRC) {
   
    int length = dataWithCRC.size();
    // Error check
    if (length < 2) {
   
        cerr << "Invalid data length" << endl;
        return;
    }
    // Delete CRC at the end
    dataWithCRC.resize(length - 2);
}

4.比较CRC校验位

/**
 * @brief Compare Modbus CRC-16
 * 
 * This function compares the CRC-16 checksum in the given data with the calculated CRC-16 checksum
 * of the data without the CRC bytes. If they match, it indicates that the data is intact and has not
 * been corrupted during transmission.
 * 
 * @param dataWithCRC A vector containing the data along with the CRC-16 checksum bytes.
 * @return True if the calculated CRC-16 matches the original CRC-16, indicating data integrity.
 *         False if the data length is invalid or if the CRCs do not match.
 */
bool compareModbusCRC16(const vector<uint8_t> &dataWithCRC) {
   
    int length = dataWithCRC.size();
    // Error check
    if (length < 2) {
   
        cerr << "Invalid data length" << endl;
        return false;
    }

    // Get data without CRC
    vector<uint8_t> dataWithoutCRC(dataWithCRC.begin(), dataWithCRC.end() - 2);

    // Calculate CRC-16 checksum
    unsigned short calculatedCRC = calculateModbusCRC16(dataWithoutCRC);

    // Get original CRC-16 checksum from the last two bytes of the data
    unsigned short originalCRC = (dataWithCRC[length - 2] << 8) | dataWithCRC[length - 1];

    return originalCRC == calculatedCRC;
}

5.完整示例代码

#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;


/**
 * @brief Calculate the Modbus CRC-16 checksum for a given data vector.
 *
 * This function calculates the CRC-16 checksum using the Modbus protocol
 * polynomial for the provided data vector.
 *
 * @param data The vector of bytes for which to calculate the CRC-16 checksum.
 * @return The calculated CRC-16 checksum value as an unsigned short.
 */
unsigned short calculateModbusCRC16(const vector<uint8_t> &data)
{
   
    int length = data.size();
    unsigned short CRC = 0xFFFF; // Initial value
    for (int i = 0; i < length; i++)
    {
   
        CRC = CRC ^ data[i]; // XOR byte into the least significant byte of CRC
        for (int j = 0; j < 8; j++)
        {
   
            if (CRC & 1)
            {
   
                CRC >>= 1;
                CRC ^= 0xA001;
            }
            else
            {
   
                CRC >>= 1;
            }
        }
    }
    // Swap the bytes of the CRC to match Modbus convention
    unsigned short swappedCRC = ((CRC >> 8) & 0xFF) | ((CRC & 0xFF) << 8);
    return swappedCRC;
}


/**
 * @brief Add Modbus CRC-16 to a data vector.
 * 
 * This function calculates the Modbus CRC-16 checksum for the provided data vector
 * and appends the high and low bytes of the CRC to the end of the data vector.
 * 
 * @param data The data vector to which the CRC will be added.
 */
void addModbusCRC16(vector<uint8_t> &data)
{
   
    // Calculate the CRC-16 checksum
    unsigned short crc = calculateModbusCRC16(data);

    // Append the high byte of CRC to the data vector
    data.push_back((crc >> 8) & 0xFF);

    // Append the low byte of CRC to the data vector
    data.push_back(crc & 0xFF);
}

/**
 * @brief Remove Modbus CRC-16 from a vector of data.
 * 
 * This function takes a vector of data with Modbus CRC-16 at the end and removes
 * the CRC-16 bytes from the end of the vector, effectively stripping the CRC-16
 * checksum from the data.
 * 
 * @param dataWithCRC A reference to the vector containing the data with CRC-16.
 * 
 * @note This function does not perform any CRC-16 validation or calculation. It
 * assumes that the last two bytes of the vector represent the CRC-16 checksum
 * and removes them regardless of their validity.
 * 
 * @warning It is the responsibility of the caller to ensure that the input vector
 * has a length of at least 2, as this function does not perform length checking.
 * If the length is less than 2, an error message is printed to the standard error
 * stream, and no modifications are made to the input vector.
 */
void removeModbusCRC16(vector<uint8_t> &dataWithCRC) {
   
    int length = dataWithCRC.size();
    // Error check
    if (length < 2) {
   
        cerr << "Invalid data length" << endl;
        return;
    }
    // Delete CRC at the end
    dataWithCRC.resize(length - 2);
}

/**
 * @brief Compare Modbus CRC-16
 * 
 * This function compares the CRC-16 checksum in the given data with the calculated CRC-16 checksum
 * of the data without the CRC bytes. If they match, it indicates that the data is intact and has not
 * been corrupted during transmission.
 * 
 * @param dataWithCRC A vector containing the data along with the CRC-16 checksum bytes.
 * @return True if the calculated CRC-16 matches the original CRC-16, indicating data integrity.
 *         False if the data length is invalid or if the CRCs do not match.
 */
bool compareModbusCRC16(const vector<uint8_t> &dataWithCRC) {
   
    int length = dataWithCRC.size();
    // Error check
    if (length < 2) {
   
        cerr << "Invalid data length" << endl;
        return false;
    }

    // Get data without CRC
    vector<uint8_t> dataWithoutCRC(dataWithCRC.begin(), dataWithCRC.end() - 2);

    // Calculate CRC-16 checksum
    unsigned short calculatedCRC = calculateModbusCRC16(dataWithoutCRC);

    // Get original CRC-16 checksum from the last two bytes of the data
    unsigned short originalCRC = (dataWithCRC[length - 2] << 8) | dataWithCRC[length - 1];

    return originalCRC == calculatedCRC;
}

int main() {
   
    // Example data 1
    vector<uint8_t> deviceData1 = {
   
        0x01, 0x10, 0x00, 0x02, 0x00, 0x06, 0x0C, 0x41, 0x20,
        0x00, 0x00, 0x42, 0xC8, 0x00, 0x00, 0x42, 0x48, 0x00, 0x00,0x84, 0xC1
    }; // Example CRC: 0x84, 0xC1

    // Print original data
    cout << "Original data 1: ";
    for (uint8_t byte : deviceData1) {
   
        cout << hex << uppercase << setw(2) << setfill('0') << (int)byte << " ";
    }
    cout << endl;
    bool comparedResult=compareModbusCRC16(deviceData1);
    if (comparedResult)
        cout<<"Compared result: "<<"TRUE"<<endl;
    else
        cout<<"Compared result: "<<"FALSE"<<endl;

    // Example data 2
    cout<<endl;
    vector<uint8_t> deviceData2 = {
   
        0x01, 0x06, 0x00, 0x00, 0x01, 0x02, 0x02
    };// Example CRC: 0xDA, 0xC7

    cout << "Original data 2: ";
    for (uint8_t byte : deviceData2) {
   
        cout << hex << uppercase << setw(2) << setfill('0') << (int)byte << " ";
    }
    cout << endl;

    // Add CRC and print modified data
    addModbusCRC16(deviceData2);
    cout << "Add CRC to original data 2: ";
    for (uint8_t byte : deviceData2) {
   
        cout << hex << uppercase << setw(2) << setfill('0') << (int)byte << " ";
    }
    cout << endl;

    // Remove CRC from data
    removeModbusCRC16(deviceData2);
    cout << "Remove CRC from modified data 2: ";
    for (uint8_t byte : deviceData2) {
   
        cout << hex << uppercase << setw(2) << setfill('0') << (int)byte << " ";
    }
    cout << endl;    
    return 0;
}
相关实践学习
基于Hologres轻量实时的高性能OLAP分析
本教程基于GitHub Archive公开数据集,通过DataWorks将GitHub中的项⽬、行为等20多种事件类型数据实时采集至Hologres进行分析,同时使用DataV内置模板,快速搭建实时可视化数据大屏,从开发者、项⽬、编程语⾔等多个维度了解GitHub实时数据变化情况。
阿里云实时数仓实战 - 用户行为数仓搭建
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求:熟练掌握 SQL 语法熟悉 Linux 命令,对 Hadoop 大数据体系有一定的了解 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
目录
相关文章
|
传感器 算法
【STM32】I2C练习,HAL库读取MPU6050角度陀螺仪
【STM32】I2C练习,HAL库读取MPU6050角度陀螺仪
1510 0
|
存储 网络协议 C语言
【C/C++ 串口编程 】深入探讨C/C++与Qt串口编程中的粘包现象及其解决策略
【C/C++ 串口编程 】深入探讨C/C++与Qt串口编程中的粘包现象及其解决策略
1537 0
|
传感器 Linux
在Linux中使用libmodbus库进行Modbus RTU主从机通信
Modbus RTU是一种常见的工业通信协议,用于在自动化系统中传输数据。libmodbus是一个流行的C库,用于在Linux系统上实现Modbus通信。本文将介绍如何使用libmodbus库在Linux上创建Modbus RTU主从机通信的示例代码。
6602 0
|
存储 传感器 移动开发
嵌入式系统中详解 Modbus 通信协议(清晰易懂)
嵌入式系统中详解 Modbus 通信协议(清晰易懂)
5055 1
|
测试技术 Linux Android开发
i2c总线及设备测试工具i2ctools:i2cdetect、i2cdump、i2cget、i2cset
本文介绍了i2ctools工具集的使用,包括i2cdetect、i2cdump、i2cget和i2cset,这些工具有助于I2C设备的开发和调试,通过检测设备、读写寄存器和数据块来提高开发效率。
4209 1
QT TextEdit控件 全面详解
本文详细的介绍了TextEdit控件的各种操作,例如:获取内容、输入控件字符、保持在最后一行添加(自动滚屏)、定时关闭、添加数据换行、向鼠标位置插入一行字符、设置字体颜色属性等操作。 本系列QT全面详解文章目前共有十五篇,本系列文章较为详细的讲述了QT控件的基础操作和使用,也谢谢大家的关注、点赞、收藏。
2164 2
QT TextEdit控件 全面详解
|
编译器 开发工具 C语言
vscode安装+配置+使用+调试【保姆级教程】
vscode安装+配置+使用+调试【保姆级教程】
60042 9

热门文章

最新文章