Implementing CRC8 on Arduino to write to MLX90614 - arduino
UPDATE: I can't even get this calculator to reproduce the SMBus PECs illustrated in figures 8 and 9 of this datasheet!
So I'm interfacing an arduino with a Melexis temperature sensor, and it's going okay--aside from the fact that I can't seem to get the CRC check to work.
I've gotten read operations to complete successfully (although my software ignores the packet error code) but I have tried a lot of implementations of CRC8 to check the PEC byte to no avail. The code block I am using now came from OneWire:
uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len)
{
uint8_t crc = 0;
while (len--) {
uint8_t inbyte = *addr++;
for (uint8_t i = 8; i; i--) {
uint8_t mix = (crc ^ inbyte) & 0x01;
crc >>= 1;
if (mix) crc ^= 0x8C;
inbyte >>= 1;
}
}
return crc;
}
I rewrote it to consider just the one byte:
int smbCRC(int message) {
uint8_t crc = 0;
uint8_t inbyte = message & 0xFF;
for (uint8_t i = 8; i; i--) {
uint8_t mix = (crc ^ inbyte) & 0x01;
crc >>= 1;
if (mix) crc ^= 0x8C;
inbyte >>= 1;
}
return crc;
}
But its CRC does not match that of the MLX datasheet (Figure 8 from here for example). When I print an int with its CRC8 like so:
int message = 0x3aD2;
lcd.print(String(message,HEX) + " " + String(smbCRC(message),HEX));
I get back "3ad2 eb", though the datasheet says the correct PEC is 0x30. Where am I going wrong? It seems like this could be caused by a bad implementation of CRC or bad assumptions on my part about the CRC input, and I'm not sure where to start troubleshooting.
I haven't checked your CRC implementation but there is a mistake in the MLX datasheet or at least it's badly written. You have to include all the I2C frame's data for the PEC's calculation not just the replied data.
For a read word command you have to include [SA_W, Command, SA_R, LSB, MSB] and for a write word command [SA_W, Command, LSB, MSB].
So, for their first example the calculation must be made on [ 0xB4, 0x07, 0xB5, 0xD2, 0x3A ] and not just on [ 0xD2, 0x3A ] and this way you get the expected 0x30.
Here is a simple C implementation of the CRC with a lookup table (non Arduino but it must be quite simple to adapt):
static const uint8_t crc_table[] = {
0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31,
0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9,
0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1,
0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe,
0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16,
0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80,
0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8,
0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10,
0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f,
0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7,
0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83,
0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef,
0xfa, 0xfd, 0xf4, 0xf3
};
uint8_t
crc8(uint8_t *p, uint8_t len)
{
uint16_t i;
uint16_t crc = 0x0;
while (len--) {
i = (crc ^ *p++) & 0xFF;
crc = (crc_table[i] ^ (crc << 8)) & 0xFF;
}
return crc & 0xFF;
}
It is possible to get the compiler to figure out the lookup table for you, as follows:
#include <stdio.h>
#include <stdint.h>
/* * *
* Just change this define to whatever polynomial is in use
*/
#define CRC1B(b) ( (uint8_t)((b)<<1) ^ ((b)&0x80? 0x07 : 0) ) // MS first
/* * *
* 8+1 entry enum lookup table define
*/
#define CRC(b) CRC_##b // or CRC8B(b)
enum {
CRC(0x01) = CRC1B(0x80),
CRC(0x02) = CRC1B(CRC(0x01)),
CRC(0x04) = CRC1B(CRC(0x02)),
CRC(0x08) = CRC1B(CRC(0x04)),
CRC(0x10) = CRC1B(CRC(0x08)),
CRC(0x20) = CRC1B(CRC(0x10)),
CRC(0x40) = CRC1B(CRC(0x20)),
CRC(0x80) = CRC1B(CRC(0x40)),
// Add 0x03 to optimise in CRCTAB1
CRC(0x03) = CRC(0x02)^CRC(0x01)
};
/* * *
* Build a 256 byte CRC constant lookup table, built from from a reduced constant
* lookup table, namely CRC of each bit, 0x00 to 0x80. These will be defined as
* enumerations to take it easy on the compiler. This depends on the relation:
* CRC(a^b) = CRC(a)^CRC(b)
* In other words, we can build up each byte CRC as the xor of the CRC of each bit.
* So CRC(0x05) = CRC(0x04)^CRC(0x01). We include the CRC of 0x03 for a little more
* optimisation, since CRCTAB1 can use it instead of CRC(0x01)^CRC(0x02), again a
* little easier on the compiler.
*/
#define CRCTAB1(ex) CRC(0x01)ex, CRC(0x02)ex, CRC(0x03)ex,
#define CRCTAB2(ex) CRCTAB1(ex) CRC(0x04)ex, CRCTAB1(^CRC(0x04)ex)
#define CRCTAB3(ex) CRCTAB2(ex) CRC(0x08)ex, CRCTAB2(^CRC(0x08)ex)
#define CRCTAB4(ex) CRCTAB3(ex) CRC(0x10)ex, CRCTAB3(^CRC(0x10)ex)
#define CRCTAB5(ex) CRCTAB4(ex) CRC(0x20)ex, CRCTAB4(^CRC(0x20)ex)
#define CRCTAB6(ex) CRCTAB5(ex) CRC(0x40)ex, CRCTAB5(^CRC(0x40)ex)
/* * *
* This is the final lookup table. It is rough on the compiler, but generates the
* required lookup table automagically at compile time.
*/
static const uint8_t crc_table[256] = { 0, CRCTAB6() CRC(0x80), CRCTAB6(^CRC(0x80)) };
uint8_t crc8(uint8_t *p, uint8_t len)
{
uint8_t crc = 0x0;
while (len--) {
crc = crc_table[crc ^ *p++];
}
return crc;
}
void main( void )
{
int i, j;
printf("static const uint8_t crc_table[] = {");
for (i = 0; i < 0x10; i++)
{
printf("\n ");
for (j = 0; j < 0x10; j++)
{
printf( " 0x%02x,", crc_table[i*0x10+j] );
}
}
printf("\n};\n\n");
}
See reference on following links:
http://www.melexis.com/Asset/SMBus-communication-with-MLX90614-DownloadLink-5207.aspx
http://www.sbs-forum.org/marcom/dc2/20_crc-8_firmware_implementations.pdf
Function code for Arduino:
byte c8( byte x ){
for( byte i = 8; i--; ) {
x = ( x << 1 ) ^ ( x & 128 ? 7 : 0 );
}
return x;
}
void setup() {
Serial.begin( 9600 );
int msg = 0x3AD2;
Serial.print( '0x' );
Serial.print( c8( msg ), HEX );
// '0x30' is displayed on Serial Monitor
}
void loop() {}
Since argument x is strong-typed as byte (uint8_t), word-size data (such 0x3AD2) will be truncated to byte-size (which is 0xD2 in case of 0x3AD2).
Stripped code from linux kernel code, i am using it for smbus pec calculation..
#include<stdio.h>
#define POLY (0x1070U << 3)
#define DST_SLAVE_ADDRESS 0x1d //7-bit Slave Address
static unsigned char crc8(short int data)
{
int i;
for (i = 0; i < 8; i++) {
if (data & 0x8000)
data = data ^ POLY;
data = data << 1;
}
return (unsigned char)(data >> 8);
}
unsigned char i2c_smbus_pec(unsigned char crc, unsigned char *p, int count)
{
int i;
for (i = 0; i < count; i++)
crc = crc8((crc ^ p[i]) << 8);
return crc;
}
/* Assume a 7-bit address, which is reasonable for SMBus */
static unsigned char i2c_smbus_msg_pec(unsigned char slave_addr, char *msg, int len)
{
/* The address will be sent first */
unsigned char addr = slave_addr << 1;
int pec;
pec = i2c_smbus_pec(0, &addr, 1);
/* The data buffer follows */
return i2c_smbus_pec(pec, msg, len);
}
main()
{
//SMBUS PEC include everthing include dst slave address
//dst i2c address here is 0x1d (7bit) , 0x3a (8bit)
char msg[] = {0x0F,0x19,0x21,0x01,0x00,0x00,0xEB,0x84,0x08,0x00,0x00,0x01,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xAA,0xEF,0x81,0xB4};
//dst i2c address here is 0x10 (7bit) , 0x20 (8bit)
//char msg[] = { 0x0F,0x19,0x3B,0x01,0x00,0x00,0xD3,0x84,0x88,0x00,0x00,0x00,
// 0x00,0x00,0x00,0x30,0xFF,0xEF,0x00,0x00,0x00,0x00,0x00,0x21,0xE5,0xD5,0xA8};
int len = sizeof(msg);
int calc_pec = i2c_smbus_msg_pec(DST_SLAVE_ADDRESS, msg , len);
printf("pec = 0x%x\n", calc_pec);
}
Related
Unable to auth RFID using KeyA&B
I'm using PN532 and Arduino Uno I'm uploading this code to change the sector trailer for sector 4 in block 19 memcpy(data, (const uint8_t[]){ 0x45, 0x48, 0x61, 0x21, 0x26, 0x39, 0x0f, 0x00, 0xff, 0, 0x50, 0x67, 0x39, 0x78, 0x5e, 0x33 }, sizeof data); success = nfc.mifareclassic_WriteDataBlock (19, data); then im want to read blocks 16 and 18 with KeyB this is the auth code uint8_t keyb[6] = { 0x50, 0x67, 0x39, 0x78, 0x5E, 0x33 }; success = nfc.mifareclassic_AuthenticateBlock(uid, uidLength, 19, 0, keyb); are my access bits wrong or to use keyb to auth is use a different code
ESP8266: Send beacon frame with deep sleep mode does not work properly
I have a problem with my esp8266 project. My purpose is to use esp8266 to transmit beacon frames every one second so that my android device or my laptop can receive it and display in list of APs which i can connect to. Here is my code I wrote: #include <ESP8266WiFi.h> extern "C" { #include "user_interface.h" } void setup() { delay(500); sendBeacon("ESP8266"); ESP.deepSleep(10e5); } void loop() { } void sendBeacon(char* ssid) { // Randomize channel // byte channel = 1; wifi_set_channel(channel); uint8_t packet[128] = { 0x80, 0x00, //Frame Control 0x00, 0x00, //Duration /*4*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //Destination address /*10*/ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, //Source address - overwritten later /*16*/ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, //BSSID - overwritten to the same as the source address /*22*/ 0xc0, 0x6c, //Seq-ctl //Frame body starts here /*24*/ 0x83, 0x51, 0xf7, 0x8f, 0x0f, 0x00, 0x00, 0x00, //timestamp - the number of microseconds the AP has been active /*32*/ 0xFF, 0x00, //Beacon interval /*34*/ 0x01, 0x04, //Capability info /* SSID */ /*36*/ 0x00 }; int ssidLen = strlen(ssid); packet[37] = ssidLen; for(int i = 0; i < ssidLen; i++) { packet[38+i] = ssid[i]; } uint8_t postSSID[13] = {0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, //supported rate 0x03, 0x01, 0x04 /*DSSS (Current Channel)*/ }; for(int i = 0; i < 12; i++) { packet[38 + ssidLen + i] = postSSID[i]; } packet[50 + ssidLen] = channel; // get SRC MAC unsigned char mac[6]; WiFi.macAddress(mac); packet[10] = packet[16] = mac[0]; packet[11] = packet[17] = mac[1]; packet[12] = packet[18] = mac[2]; packet[13] = packet[19] = mac[3]; packet[14] = packet[20] = mac[4]; packet[15] = packet[21] = mac[5]; int packetSize = 51 + ssidLen; wifi_send_pkt_freedom(packet, packetSize, 0); delay(1); } I used tcpdump to capture those frame and yes, they are there. But I still couldn't see it in list of AP on my laptop and my android device. I can see it if I stop using deep sleep mode. For example: #include <ESP8266WiFi.h> extern "C" { #include "user_interface.h" } void setup() { delay(500); // sendBeacon("ESP8266"); // ESP.deepSleep(10e5); } void loop() { sendBeacon("ESP8266"); } void sendBeacon(char* ssid) { // Randomize channel // byte channel = 1; wifi_set_channel(channel); uint8_t packet[128] = { 0x80, 0x00, //Frame Control 0x00, 0x00, //Duration /*4*/ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //Destination address /*10*/ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, //Source address - overwritten later /*16*/ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, //BSSID - overwritten to the same as the source address /*22*/ 0xc0, 0x6c, //Seq-ctl //Frame body starts here /*24*/ 0x83, 0x51, 0xf7, 0x8f, 0x0f, 0x00, 0x00, 0x00, //timestamp - the number of microseconds the AP has been active /*32*/ 0xFF, 0x00, //Beacon interval /*34*/ 0x01, 0x04, //Capability info /* SSID */ /*36*/ 0x00 }; int ssidLen = strlen(ssid); packet[37] = ssidLen; for(int i = 0; i < ssidLen; i++) { packet[38+i] = ssid[i]; } uint8_t postSSID[13] = {0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, //supported rate 0x03, 0x01, 0x04 /*DSSS (Current Channel)*/ }; for(int i = 0; i < 12; i++) { packet[38 + ssidLen + i] = postSSID[i]; } packet[50 + ssidLen] = channel; // get SRC MAC unsigned char mac[6]; WiFi.macAddress(mac); packet[10] = packet[16] = mac[0]; packet[11] = packet[17] = mac[1]; packet[12] = packet[18] = mac[2]; packet[13] = packet[19] = mac[3]; packet[14] = packet[20] = mac[4]; packet[15] = packet[21] = mac[5]; int packetSize = 51 + ssidLen; wifi_send_pkt_freedom(packet, packetSize, 0); delay(1); } Does anyone know why? Please help me to get this, thanks!
First of all, ESP.deepSleep(10e5) makes the processor sleep for 100ms, not a second. The correct would be ESP.deepSleep(10e6). Secondly, you are trying to send just one packet every second or so, it's quite hard to make sure that the wifi reading from the device is gonna get that packet for sure and display it on the list of SSIDs. From my experiments, I had to send three packets every 200ms to make sure it was displayed on my android mobile and pc. You can try to play with the number of packets you send and the interval to see what fits you best... Also, make sure to configure the ESP correctly at the setup function. When I tried my similar code I had to use two instructions: wifi_set_opmode(STATION_MODE) and wifi_promiscuous_enable(1)
Raw tcp send and recv
I'm making an application, using raw sockets and need your advice: The application is a tester of some kind of tcp/ip stack. What I need I have an application that connects with a remote server and transmits some data to id. lets say - I cannot open sockets from my application - all I have is tcp/Ip buffer with all headers and so on. For testing I wanna make 2 raw sockets - 1 for sending and 1 for receiving ip buffers. for receiving i have this code: int saddr_size , data_size; struct sockaddr saddr; unsigned char *buffer = (unsigned char *)malloc(65536); //Its Big! printf("Starting...\n"); //Create a raw socket that shall sniff sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_TCP); if(sock_raw < 0) { printf("Socket Error\n"); return 1; } while(1) { saddr_size = sizeof saddr; //Receive a packet data_size = recvfrom(sock_raw , buffer , 65536 , 0 , &saddr , &saddr_size); if(data_size <0 ) { printf("Recvfrom error , failed to get packets\n"); return 1; } printf("Data size = %d", data_size); } close(sock_raw); printf("Finished"); return 0; And as I can see it works - it gets all TCP/IP packets. For sender I tryed this static const unsigned char pkt6[60] = { 0x32, 0x04, 0x34, 0xed, 0xf3, 0xab, 0x01, 0x02, /* 2.4..... */ 0x03, 0x04, 0x05, 0x06, 0x08, 0x00, 0x45, 0x00, /* ......E. */ 0x00, 0x2e, 0x00, 0x02, 0x00, 0x00, 0xff, 0x06, /* ........ */ 0x44, 0xc5, 0xc0, 0xa8, 0x01, 0x02, 0xac, 0x11, /* D....... */ 0x09, 0x47, 0x00, 0x08, 0x1a, 0x0b, 0x00, 0x00, /* .G...... */ 0x19, 0x6e, 0x23, 0x17, 0xc8, 0x36, 0x50, 0x18, /* .n#..6P. */ 0x08, 0x60, 0x2b, 0xb9, 0x00, 0x00, 0x6c, 0x6f, /* .`+...lo */ 0x6f, 0x6c, 0x0a, 0x00 /* ol.. */ }; if((s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) { perror("error:"); exit(EXIT_FAILURE); } while(1) { if(send(s, pkt6, sizeof(pkt6), 0)< 0) perror("error::"); } } And it always says error:: Destination address required So what do I need to change if i want to send READy IP packets and get raw ip packets?
I've not use raw sockets this way myself, but AIUI you'll probably need to use sendto() instead of send(), and pass the saddr structure that you obtained in the recvfrom() call.
How to turn Bytes into number (IEEE754 to number) Actionscript
How to write such C# code in Actionscript? Console.WriteLine(BitConverter.ToDouble(new byte[8] { 0x77, 0xBE, 0x9F, 0x1A, 0x2F, 0x0D, 0x4F, 0x40 }, 0));
You can rely on the ByteArray to do the conversion for you, but beware of the order of the bytes you write. var bytes:Array = [0x77, 0xBE, 0x9F, 0x1A, 0x2F, 0x0D, 0x4F, 0x40]; var ba:ByteArray = new ByteArray(); for (var i:int = 7;i>=0;--i) ba.writeByte(bytes[i]); ba.position = 0; trace(ba.readDouble());
Calculate maximum size for encrypted data
Is there any way to calculate the largest outcome from an Rijndael encryption with a fixed array length? Encryption method: RijndaelManaged Padding: PKCS7 CipherMode: CBC BlockSize 128 KeySize: 128 I need this as im converting a database where all string are going to be encrypted so i need to change the size of all string fields.
Everything you need to try this out: public partial class Form1 : Form { private SymmetricAlgorithm mEncryptionType; public Form1() { mEncryptionType = new RijndaelManaged(); mEncryptionType.Padding = PaddingMode.PKCS7; //PaddingMode.None; mEncryptionType.Mode = CipherMode.CBC; mEncryptionType.BlockSize = 128; // 192; // 256; // Update byte array to IV when changed mEncryptionType.KeySize = 128; // 192; // 256; // Update byte array to Key when changed mEncryptionType.IV = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; mEncryptionType.Key = new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }; int encrypted_size = CalculateEncryptedSize(new byte[] { 0x22, 0x23, 0x44 }); // Shows Theran's point about exact block size encrypted_size = CalculateEncryptedSize(new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }); } /// <summary> /// Calculate the encrypted size of input buffer /// </summary> /// <param name="inputBuffer">The input buffer</param> /// <returns>Size of the encrypted buffer</returns> public int CalculateEncryptedSize(byte[] inputBuffer) { int extra_padding = 0; if (mEncryptionType.Padding != PaddingMode.None) { int padding_size = (mEncryptionType.BlockSize / 8); extra_padding = (padding_size - (inputBuffer.Length % padding_size)); } return inputBuffer.Length + extra_padding; } }
Yes. Round up your input size to the nearest multiple of your block size (e.g. 128 / 8 = 16 bytes). extraBytesNeeded = (16 - (inputSize % 16)) % 16; maxSize = inputSize + extraBytesNeeded.
Jeff's answer is almost correct, except that PKCS7 will always add padding to the message, even if the message exactly fits inside an integral number of blocks. Also, don't forget that if using a random IV that the IV has to be stored too. The corrected formula for the length of a PKCS7 padded message is: extraBytesNeeded = (16 - (inputSize % 16)); // whole block of padding if input fits exactly maxSize = inputSize + extraBytesNeeded + IVbytes;