CRC32 for Streams ( Ethernet, Serial, Wire, SoftwareSerial,...)

Hi all, I have a new piece of code to post.
This library can calculate a running CRC32 on a stream object.

Here are some other objects I have recently worked on, these are all part of a new library I'm planning to release.
Dual Writer
Base64Writer
URI Encoder

Below is the test code I wrote for it. The code will take anything and on a new line or carriage return the CRC of what has streamed in & out will be printed.

Like the tools posted above, they take a reference to a Print or Stream object, in this example, I use Serial.

The main functions ( apart from the print functionality ) are:

  • getCRC
  • resetCRC

Each one takes a value specifying the CRC to use. The options are CRC_INPUT and CRC_OUTPUT

This library is built off the CRC32 code I found here, linked from here.

Test code for StreamCRC:

#include <StreamCRC.h>

StreamCRC s_CRC( Serial );

void setup(){ 
  Serial.begin( 9600 ); 
  Serial.println( F("Type anything to get started, on a new line or carriage return the CRC of what has streamed in & out will be printed.") );
}

void loop(){

  if( s_CRC.available() > 0x00 ){

    switch( s_CRC.peek() ){
     
      case '\r':
      case '\n':
      
        //Only print CRC if something has been recieved other than a new line char.
        if( s_CRC.getCRC( CRC_INPUT ) != 0 ){
          
          Serial.print( "\r\nOUT CRC: " );
          Serial.println( s_CRC.getCRC( CRC_OUTPUT ), HEX );
          s_CRC.print( "IN CRC: " );
          s_CRC.println( s_CRC.getCRC( CRC_INPUT ), HEX );
        }
        s_CRC.read();
        s_CRC.resetCRC( CRC_OUTPUT );
        s_CRC.resetCRC( CRC_INPUT );
        break;
     
      default:
        s_CRC.write( s_CRC.read() );
    }
  }
}

A test typing HELLO printed a CRC verifyable with PHP.

Type anything to get started, on a new line or carriage return the CRC of what has streamed in & out will be printed.
HELLO
OUT CRC: C1446436
IN CRC: C1446436

I will add the code here for people not logged on, also the files are attached.

#include "Arduino.h"

#ifndef HEADER_CRCSTREAM
  #define HEADER_CRCSTREAM
  
  enum CRC_STREAM_ID{ CRC_INPUT, CRC_OUTPUT };
  
  class StreamCRC : public Stream{
    public:
      StreamCRC( Stream &s_Stream ) : stream( s_Stream ), CRCIN( ~0L ), CRCOUT( CRCIN ) { return; }
      
      int available( void ) { return stream.available(); }
      
      void flush( void ) { stream.flush(); }
      
      unsigned long getCRC( CRC_STREAM_ID c_ID ) { return ~( c_ID ? CRCOUT : CRCIN ); }
        
      int peek( void ) { return stream.peek(); }
        
      int read( void );
      
      void resetCRC( CRC_STREAM_ID c_ID ) { ( c_ID ? CRCOUT : CRCIN ) = ~0L; }
      
      size_t write( uint8_t u_Data );
    protected:
      Stream &stream;
      unsigned long CRCIN;
      unsigned long CRCOUT;
  };
#endif
  #include "StreamCRC.h"
  
  uint32_t crc_table[ 0x10 ] PROGMEM = {
    0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
    0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
  };

  void crc32Byte( unsigned long &u_CRC, uint8_t u_Data )
    {
      uint8_t u_Idx;
      u_Idx = u_CRC ^ u_Data;
      u_CRC = pgm_read_dword( crc_table + ( u_Idx & 0x0F ) ) ^ ( u_CRC >> 4 );
      u_Idx = u_CRC ^ ( u_Data >> 4 );
      u_CRC = pgm_read_dword( crc_table + ( u_Idx & 0x0F ) ) ^ ( u_CRC >> 4 );
    }  
  
  int StreamCRC::read( void )
    { 
      const int i_Read = stream.read();
      if( i_Read != -1 ) crc32Byte( CRCIN, ( uint8_t ) i_Read );
      return i_Read; 
    }  
    
  size_t StreamCRC::write( uint8_t u_Data )
    {
      crc32Byte( CRCOUT, u_Data );
      return stream.write( u_Data );      
    }

Hope someone can get some use out of this, and as always any comments are appreciated.

StreamCRC.h (854 Bytes)

StreamCRC.cpp (883 Bytes)

Thanks for sharing, no direct application at the moment.
Bookmarked this page as it is really valuable to have these kind of building blocks (like your base64).

Q: although CRC32 is better, did you have a look at CRC16 => I assume it would have a smaller footprint ?

Yeah, I certainly can do a CRC16, I did this one for quick compatibility with my PHP server. I'm using this in a form of secure communication and thought CRC32 would be better.

I'm also looking at others such as SHA-1 and MD5.

Huh. I don't think I've ever seen a 4-bit-at-a-time CRC algorithm. I guess it makes sense that that would work...