ModbusCRC16校验 示例代码

本文涉及的产品
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
数据可视化DataV,5个大屏 1个月
简介: 本文提供了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;
}
相关实践学习
Github实时数据分析与可视化
基于Github Archive公开数据集,将项目、行为等20+种事件类型数据实时采集至Hologres进行分析,并搭建可视化大屏。
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
目录
相关文章
|
2月前
|
移动开发 JavaScript
thinkPHP5.0开发微信H5页面分享接口signature验证失败,signature与微信 JS 接口签名校验工具返回结果不一致
thinkPHP5.0开发微信H5页面分享接口signature验证失败,signature与微信 JS 接口签名校验工具返回结果不一致
70 0
|
3月前
|
Java Spring 容器
详解java参数校验之:顺序校验、自定义校验、分组校验(@Validated @GroupSequence)
详解java参数校验之:顺序校验、自定义校验、分组校验(@Validated @GroupSequence)
|
3月前
|
前端开发 JavaScript
阿里云验证码2.0 验证时报错 前端页面获取的验证参数有问题,动态JS加载失败,请问怎么解决啊?急,急,急。
用户反馈校验时遇到错误,日志显示验证码参数获取异常。采用无痕验证,失败后,返回`{captchaResult:false,bizResult:false}`,未触发滑块二次验证。
|
11月前
|
数据安全/隐私保护
fastadmin中写接口是时Validate规则验证自定义如何用
fastadmin中写接口是时Validate规则验证自定义如何用
159 0
|
4月前
|
JavaScript 前端开发 数据安全/隐私保护
jQuery最方便的前端验证方式2种(非空验证与比较验证)
jQuery最方便的前端验证方式2种(非空验证与比较验证)
83 0
|
4月前
|
数据格式 Python
添加 常用校验方法,校验常见数据格式
添加 常用校验方法,校验常见数据格式
48 0
|
4月前
|
数据格式 Python
添加 自定义校验方法,让用户自定义校验规则
添加 自定义校验方法,让用户自定义校验规则
62 0
|
前端开发
怎么使用async-validator快速校验表单
怎么使用async-validator快速校验表单
390 0
|
前端开发 Java Spring
更简洁的参数校验,使用 SpringBoot Validation 对参数进行校验
在开发接口时,如果要对参数进行校验,你会怎么写?编写 if-else 吗?虽然也能达到效果,但是不够优雅。 今天,推荐一种更简洁的写法,使用 SpringBoot Validation 对方法参数进行校验,特别是在编写 Controller 层的方法时,直接使用一个注解即可完成参数校验。
315 0
更简洁的参数校验,使用 SpringBoot Validation 对参数进行校验