Auteur : Herman Ye @Galbot @Auromix
Environnement de test : Ubuntu20.04
Date de mise à jour : 2023/08/30
Note 1 : @Auromix est une organisation open source pour les passionnés de robotique.
Note 2 : Cet article a été testé et confirmé comme étant valide à la date de mise à jour.
À des fins d'apprentissage et de communication, l'auteur
donne les quatre fonctions suivantes couramment utilisées pour la vérification ModbusCRC16 et des exemples de codes complets :
1. Calculer le CRC
Remarque : Les bits hauts et bas sont échangés ici à la fin. Le code peut être supprimé et l'ordre des bits hauts et bas échangés selon les besoins.
/**
* @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.Ajouter le chiffre de contrôle CRC
Remarque : les bits hauts et bas sont échangés ici, et le code peut être supprimé pour échanger l'ordre des bits haut et bas en fonction des besoins.
/**
* @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. Supprimez le chiffre de contrôle 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. Comparez les chiffres de contrôle 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. Exemple de code complet
#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;
}