github.com/simpleiot/simpleiot@v0.18.3/modbus/crc.go (about)

     1  package modbus
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  )
     7  
     8  // RtuCrc calculates CRC for a Modbus RTU packet
     9  func RtuCrc(buf []byte) uint16 {
    10  	crc := uint16(0xFFFF)
    11  
    12  	for _, b := range buf {
    13  		crc ^= uint16(b) // XOR byte into least sig. byte of crc
    14  
    15  		for i := 8; i != 0; i-- { // Loop over each bit
    16  			if (crc & 0x0001) != 0 { // If the LSB is set
    17  				crc >>= 1 // Shift right and XOR 0xA001
    18  				crc ^= 0xA001
    19  			} else { // Else LSB is not set
    20  				crc >>= 1 // Just shift right
    21  			}
    22  		}
    23  	}
    24  	// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
    25  	return (crc >> 8) | (crc << 8)
    26  }
    27  
    28  // ErrCRC is returned if a crc check fails
    29  var ErrCRC = errors.New("CRC error")
    30  
    31  // ErrNotEnoughData is returned if not enough data
    32  var ErrNotEnoughData = errors.New("Not enough data to calculate CRC")
    33  
    34  // CheckRtuCrc returns error if CRC fails
    35  func CheckRtuCrc(packet []byte) error {
    36  	if len(packet) < 4 {
    37  		return ErrNotEnoughData
    38  	}
    39  
    40  	crcCalc := RtuCrc(packet[:len(packet)-2])
    41  
    42  	crcPacket := binary.BigEndian.Uint16(packet[len(packet)-2:])
    43  	if crcCalc != crcPacket {
    44  		return ErrCRC
    45  	}
    46  
    47  	return nil
    48  }