ModbusCRC16校验 示例代码

本文涉及的产品
数据可视化DataV,5个大屏 1个月
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
简介: 本文提供了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;
}
相关实践学习
DataV Board用户界面概览
本实验带领用户熟悉DataV Board这款可视化产品的用户界面
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
目录
相关文章
|
4月前
|
Java Spring 容器
详解java参数校验之:顺序校验、自定义校验、分组校验(@Validated @GroupSequence)
详解java参数校验之:顺序校验、自定义校验、分组校验(@Validated @GroupSequence)
|
5月前
|
JSON JavaScript 前端开发
vue的 blob文件下载文件时,后端自定义异常,并返回json错误提示信息,前端捕获信息并展示给用户
vue的 blob文件下载文件时,后端自定义异常,并返回json错误提示信息,前端捕获信息并展示给用户
|
5月前
|
数据格式 Python
添加 自定义校验方法,让用户自定义校验规则
添加 自定义校验方法,让用户自定义校验规则
72 0
|
5月前
|
数据格式 Python
添加 常用校验方法,校验常见数据格式
添加 常用校验方法,校验常见数据格式
57 0
|
前端开发 JavaScript Java
SpringBoot 统一功能处理:用户登录权限校验-拦截器、异常处理、数据格式返回
本篇将要学习 Spring Boot 统一功能处理模块,这也是 AOP 的实战环节 用户登录权限的校验实现接口 HandlerInterceptor + WebMvcConfigurer 异常处理使用注解 @RestControllerAdvice + @ExceptionHandler 数据格式返回使用注解 @ControllerAdvice 并且实现接口 @ResponseBodyAdvice
626 0
java上传图片获取文件信息 校验文件尺寸
一开始我是用的是下面这种方法,测试了几个图片都没有问题,然后随便去网上下载了个图片测试,竟然报了空指针,获取不到高度和宽度,于是我右键查看图片属性,详细信息竟然都是空的
|
消息中间件 JavaScript 小程序
SpringBoot 统一功能处理:用户登录权限校验-拦截器、异常处理、数据格式返回 下
SpringBoot 统一功能处理:用户登录权限校验-拦截器、异常处理、数据格式返回 下
SpringBoot 统一功能处理:用户登录权限校验-拦截器、异常处理、数据格式返回 下
|
JavaScript 前端开发 小程序
SpringBoot 统一功能处理:用户登录权限校验-拦截器、异常处理、数据格式返回 上
SpringBoot 统一功能处理:用户登录权限校验-拦截器、异常处理、数据格式返回 上
|
索引
v-for中动态校验el-form表单项代码示例
v-for中动态校验el-form表单项代码示例
226 0