PICAXE and Arduino Interface with a MLX90614 IR Sensor

copyright, Peter H Anderson, Baltimore, MD, Nov 11, '11



MLX90614 Product Page

SMBus Specs.

Interface with the PICAXE-20X2

Measuring Temperature - MLX90614_1.Bas.

This program continually measures the temperature of the TO5 can package and the temperature of the object emmitting the infrared energy. This is a matter of reading RAM locations $06 and $07. Note that RAM locations are 16 bits wide.

The temperature in degrees Kelvin is then calculated as 0.02 * Val. Or TC_100 = 2 * Val - 27315.

Note that in subroutine ReadRAM, an 8-bit CRC consisting of all bytes sent and all byte received is calculated. The result of the final step involving the received packet error correction (PEC) byte should result in zero. If this is true, variable Status is set to a 1 to indicate the read was successful.

Timing is important with the SMBus and thus all CRC calculations are performed outside of the communication sequences.

Note that the ReadRAM involves an I2C read which is of the form;

  Start, SlaveAdr*2, RAMLocation, Start, SlaveAdr*2 + 1, Read, Read, Read, Stop
 

(Note that Slave*2, RAMLocation, SlaveAdr * 2 + 1, Lo, Hi and PEC are used in calculating the CRC).

The PICAXE abbreviates the above sequence as;

   Hi2cin [SlaveAdr_2], RamLocation, (Lo, Hi, PEC)

Note that a general slave address may be used in addressing a device if it is the only device on the I2C bus.

' MLX90614_1.Bas
'
' Continually measures and displays T_ambient and T_object.
'
' PICAXE-20X2               MLX90614ESF-AAA
'
' Term 11 SCL --------------- SCL
' Term 13 SDA --------------- SDA
'
' 4.7K resistors to +5 VDC on SDA and SCL
'
' Note that this is a direct interface with the PICAXE.  The Parallax and
' Sparkfun boards are not required.
' 
' copyright, Peter H Anderson, Baltimore, MD, Mar 12, 11

#picaxe 20x2
#Terminal 9600
#No_Table
#No_Data
#freq m4

Symbol Lo = B0
Symbol Hi = B1
Symbol PEC = B2
Symbol Status = B3
Symbol Whole = B4
Symbol Fract = B5
Symbol Val = W3
Symbol TC_100 = W4
Symbol SlaveAdr_2 = B10
Symbol RamLocation = B11
Symbol X = B12
Symbol N = B13
Symbol CRC8 = B14

Top:

    Hi2cSetup I2CMaster, 45, I2CSlow, I2CByte 

    SlaveAdr_2 = $00 * 2 ' use general slave adr

Again:

    RamLocation = $06 ' ambient temperature

    GoSub ReadRAM
    If Status = 1 Then
       TC_100 = Val * 2 - 27315
       SerTxD ("T_ambient = ") 
       GoSub DisplayTc
       
    Else
       SerTxD ("T_ambient - Error")
    End If

    SerTxD (",  ")

    RamLocation = $07 ' ambient temperature

    GoSub ReadRAM
    If Status = 1 Then
       TC_100 = Val * 2 - 27315
       SerTxD ("T_object = ") 
       GoSub DisplayTc
    Else
       SerTxD ("T_object - Error")  
    End If
      
    SerTxD (CR, LF)

    Pause 1000
    GoTo Again

ReadRAM:
      
    Hi2cin [SlaveAdr_2], RamLocation, (Lo, Hi, PEC)

    Val = Hi
    Val = Val * 256 + Lo
   
    'SerTxD ("Hello ", #TC_100, "  ", #PEC, CR, LF)
                        
    CRC8 = $00
    X = SlaveAdr_2
    GoSub CalcCRC8
    X = RamLocation
    GoSub CalcCRC8
    X = SlaveAdr_2 + 1
    GoSub CalcCRC8
    X = Lo
    GoSub CalcCRC8
    X = Hi
    GoSub CalcCRC8
    X = PEC
    GoSub CalcCRC8

    'SerTxD ("CRC = ", #CRC8, CR, LF)
    If CRC8 = 0 Then
       Status = 1 ' success
    Else
       Status = 0
    Endif
    
    Return

DisplayTc:

    Whole = TC_100 / 100
    Fract = TC_100 // 100

    SerTxD (#Whole, ".")
    If Fract < 10 Then
        SerTxD ("0")
    Endif
    SerTxD (#Fract)

    Return
   
CalcCRC8:

   X = X ^ CRC8
   For N = 0 to 7
      If X > 127 Then
         X = X * 2
         X = X ^ $07
      Else
         X = X * 2
      Endif
   Next
   CRC8 = X
   Return
     


Reading EEPROM - MLX90614_2.Bas

This routine illustrates how to read from the 32 EEPROM locations. Of particular interest is EEPROM location $04 (emissity) and $0e (assigned I2C slave address). Note that all EEPROM locations are 16 bits and the emissivity does use all 16 (values 0 - 65535). However, the slave address, in the range of 0 - 127 uses only the lower byte.

Note that RAM occupies addresses 0 - 31 (or $00 - $1F) and EEPROM occupies addresses $20 - $3F

    Hi2cin [SlaveAdr_2], EEPROMLocation, (Lo, Hi, PEC)

' MLX90614_2.Bas
'
' Reads and display 32 words of EEPROM.  Note that EEPROM is 16 bits wide.  
' 
' Note that location $04 is the emissivity and $0e (14) is the slave address. 
'
' PICAXE-20X2               MLX90614ESF-AAA
'
' Term 11 SCL --------------- SCL
' Term 13 SDA --------------- SDA
'
' 4.7K resistors to +5 VDC on SDA and SCL
'
' Note that this is a direct interface with the PICAXE.  The Parallax and
' Sparkfun boards are not required.
' 
'
' copyright, Peter H Anderson, Baltimore, MD, Mar 13, 11

#picaxe 20x2
#Terminal 9600
#No_Table
#No_Data
#freq m4

Symbol Lo = B0
Symbol Hi = B1
Symbol PEC = B2
Symbol Status = B3
Symbol Digit = B4
Symbol Ch = B5
Symbol Val = W3
Symbol I = B8
Symbol J = B9
Symbol K = B10
Symbol SlaveAdr_2 = B11
Symbol EEPROMLocation = B12

Symbol CRC8 = B13
Symbol X = B14

Top:

    Hi2cSetup I2CMaster, 45, I2CSlow, I2CByte 

    SlaveAdr_2 = $00 * 2  ' use the general slave address

Again:

    For I = 0 to 31
       EEPROMLocation = $20 + I
       GoSub ReadEEPROM

       If Status = 1 Then
          GoSub DisplayEEPROM
       Else
          SerTxD (#I, "  ", "Invalid", CR, LF)
       EndIf

       Pause 1000
    Next
    
    Pause 10000
    GoTo Again

ReadEEPROM:
      
    Hi2cin [SlaveAdr_2], EEPROMLocation, (Lo, Hi, PEC)

    Val = Hi
    Val = Val * 256 + Lo
                              
    CRC8 = $00
    X = SlaveAdr_2
    GoSub CalcCRC8
    X = EEPROMLocation
    GoSub CalcCRC8
    X = SlaveAdr_2 + 1
    GoSub CalcCRC8
    X = Lo
    GoSub CalcCRC8
    X = Hi
    GoSub CalcCRC8
    X = PEC
    GoSub CalcCRC8

    'SerTxD ("CRC = ", #CRC8, CR, LF)
    If CRC8 = 0 Then
       Status = 1 ' success
    Else
       Status = 0
    Endif
    
    Return

DisplayEEPROM:

    SerTxD (#I, "  ")

    For J = 0 to 3

       Digit = Val / 4096

       If Digit >= 10 Then
          Ch = "A" + Digit - 10
       Else
          Ch = "0" + Digit 
       EndIf
       SerTxD (Ch)
       Val = Val * 16
    Next

    SerTxD (CR, LF)
    Return

CalcCRC8:

   X = X ^ CRC8
   For K = 0 to 7
      If X > 127 Then
         X = X * 2
         X = X ^ $07
      Else
         X = X * 2
      Endif
   Next
   CRC8 = X
   Return


' MLX90614_3.Bas
'
' Prompts user for SlaveAddress and writes this to EEPROM address $0e.  Note that
' $00 is used as the SlaveAddress up to this point.  But the new slave address is
' used to read and display the content of location $0e. 
'
' The intent is a framework for a program to modify the slave address to something 
' other than $5a to allow for more than one device on the same bus.
'
' Note that after a lot of frustration, I discovered that after programming a new
' slave address, I had to recycle power to the MLX90614 for the new slave address to
' be recognized.
'
'
' PICAXE-20X2               MLX90614ESF-AAA
'
' Term 11 SCL --------------- SCL
' Term 13 SDA --------------- SDA
'
' 4.7K resistors to +5 VDC on SDA and SCL
'
' Note that this is a direct interface with the PICAXE.  The Parallax and
' Sparkfun boards are not required.
' 
'
' copyright, Peter H Anderson, Baltimore, MD, Mar 15, 11

#picaxe 20x2
#Terminal 9600
#No_Table
#No_Data
#freq m4

Symbol Lo = B0
Symbol Hi = B1
Symbol PEC = B2
Symbol Status = B3

Symbol Ch = B5
Symbol Val = W3
Symbol I = B8
Symbol J = B9
Symbol K = B10
Symbol SlaveAdr_2 = B11
Symbol EEPROMLocation = B12

Symbol CRC8 = B13
Symbol X = B14
Symbol NewSlaveAdr = B15

Top:
    Pause 1000
    Hi2cSetup I2CMaster, 45, I2CSlow, I2CByte 

TryAgn:
    SerTxD ("Enter New Slave Address: ")
    SerRxD [15000, TryAgn], #NewSlaveAdr
    SerTxD (#NewSlaveAdr, CR, LF);

    Pause 5000 ' pause to admire
        
    SlaveAdr_2 = $00 * 2   ' Use the general address for now


Again:

    EEPROMLocation = $20 + $0e
    GoSub ReadEEPROM

    If Status = 1 Then
          Val = Val & $00ff ' take the low byte
          SerTxD ("Current Slave Adr: ", #Val, CR, LF)
    Else
          SerTxD ("Current Slave Adr", "Invalid", CR, LF)
    EndIf

    Pause 5000
   
    SlaveAdr_2 = $00 * 2  

    Val = $00
    GoSub WriteEEPROM 		' erase location $0e

    Val = NewSlaveAdr 		' now program in the new slave address
    GoSub WriteEEPROM

    ' recycle power to the MLX90614 before trying to use the new slave address

    SerTxD ("Recycle power to the MLX90614.  Key in a number when done", CR, LF)
    SerRxD [45000], Ch

    SlaveAdr_2 = NewSlaveAdr  * 2
    GoSub ReadEEPROM

    If Status = 1 Then
          Val = Val & $00ff ' take the low byte
          SerTxD ("Current Slave Adr: ", #Val, CR, LF)
    Else
          SerTxD ("Current Slave Adr", "Invalid", #Val, CR, LF)
    EndIf
      

    Pause 10000

    GoTo Again

ReadEEPROM:

    Hi2cin [SlaveAdr_2], EEPROMLocation, (Lo, Hi, PEC)

    Val = Hi
    Val = Val * 256 + Lo
                              
    CRC8 = $00
    X = SlaveAdr_2
    GoSub CalcCRC8
    X = EEPROMLocation
    GoSub CalcCRC8
    X = SlaveAdr_2 + 1
    GoSub CalcCRC8
    X = Lo
    GoSub CalcCRC8
    X = Hi
    GoSub CalcCRC8
    X = PEC
    GoSub CalcCRC8

    ' SerTxD ("CRC = ", #CRC8, CR, LF)
    If CRC8 = 0 Then
       Status = 1 ' success
    Else
       Status = 0
    Endif

    SerTxD (#SlaveAdr_2, "  ", #EEPROMLocation, CR, LF)    
    Return

WriteEEPROM:

     Lo = Val     
     Hi = $00

     CRC8 = $00

     X = SlaveAdr_2
     GoSub CalcCRC8
     X = EEPROMLocation
     GoSub CalcCRC8
      
     X = Lo
     GoSub CalcCRC8
     X = Hi
     GoSub CalcCRC8
     PEC = CRC8

     Hi2cOut [SlaveAdr_2], EEPROMLocation, (Lo, Hi, PEC)
     Pause 20
  
     Return
 
CalcCRC8:

   X = X ^ CRC8
   For K = 0 to 7
      If X > 127 Then
         X = X * 2
         X = X ^ $07
      Else
         X = X * 2
      Endif
   Next
   CRC8 = X
   Return



' MLX90614_4.Bas
'
' Continually measures and displays T_ambient and T_object for three devices
' on a single bus.  
'
' Note that MLX90614_3 was used to program the slave addresses for the three
' sensors 90, 91 and 92.
'
' PICAXE-20X2               MLX90614ESF-AAA
'
' Term 11 SCL --------------- SCL -------------------- To two additional
' Term 13 SDA --------------- SDA -------------------- devices
'
' 4.7K resistors to +5 VDC on SDA and SCL
'
' Note that this is a direct interface with the PICAXE.  The Parallax and
' Sparkfun boards are not required.
' 
'
' copyright, Peter H Anderson, Baltimore, MD, Mar 12, 11


#picaxe 20x2
#Terminal 9600
#No_Table
#No_Data
#freq m4


Symbol Lo = B0
Symbol Hi = B1
Symbol PEC = B2
Symbol Status = B3
Symbol Whole = B4
Symbol Fract = B5
Symbol Val = W3
Symbol TC_100 = W4
Symbol SlaveAdr_2 = B10
Symbol RamLocation = B11
Symbol X = B12
Symbol N = B13
Symbol CRC8 = B14
Symbol J = B15

Top:

    Hi2cSetup I2CMaster, 45, I2CSlow, I2CByte 

Again:

    For J = 0 to 2
       Lookup J, (180, 182, 184), SlaveAdr_2 
       RamLocation = $06 ' ambient temperature

       GoSub ReadRAM
       SerTxD (#J, "  ")
       If Status = 1 Then
          TC_100 = Val * 2 - 27315
          SerTxD ("T_ambient = ") 
       GoSub DisplayTc
       
       Else
          SerTxD ("T_ambient - Error")
       End If

       SerTxD (",  ")

       RamLocation = $07 ' ambient temperature

       GoSub ReadRAM
          If Status = 1 Then
          TC_100 = Val * 2 - 27315
          SerTxD ("T_object = ") 
          GoSub DisplayTc
       Else
          SerTxD ("T_object - Error")  
       End If
      
       SerTxD (CR, LF)

    Next
     
    Pause 2000
    GoTo Again

ReadRAM:
      
    Hi2cin [SlaveAdr_2], RamLocation, (Lo, Hi, PEC)

    Val = Hi
    Val = Val * 256 + Lo
   
    'SerTxD ("Hello ", #TC_100, "  ", #PEC, CR, LF)
                        
    CRC8 = $00
    X = SlaveAdr_2
    GoSub CalcCRC8
    X = RamLocation
    GoSub CalcCRC8
    X = SlaveAdr_2 + 1
    GoSub CalcCRC8
    X = Lo
    GoSub CalcCRC8
    X = Hi
    GoSub CalcCRC8
    X = PEC
    GoSub CalcCRC8

    'SerTxD ("CRC = ", #CRC8, CR, LF)
    If CRC8 = 0 Then
       Status = 1 ' success
    Else
       Status = 0
    Endif
    
    Return

DisplayTc:

    Whole = TC_100 / 100
    Fract = TC_100 // 100

    SerTxD (#Whole, ".")
    If Fract < 10 Then
        SerTxD ("0")
    Endif
    SerTxD (#Fract)

    Return
   

CalcCRC8:

   X = X ^ CRC8
   For N = 0 to 7
      If X > 127 Then
         X = X * 2
         X = X ^ $07
      Else
         X = X * 2
      Endif
   Next
   CRC8 = X
   Return


' MLX90614_5.Bas
'
' Sets the emissivity to 1.0 (65535) and performs ten temperature 
' measurements. Then prompts the user for an emmisivity in the range 
' of 0 to 65535 (corresponding to 0.0 to 1.0).  Then performs 10
' measurements using this emmisivity.
'
' Terminates in a loop.
' 
' PICAXE-20X2               MLX90614ESF-AAA
'
' Term 11 SCL --------------- SCL 
' Term 13 SDA --------------- SDA 
'
' 4.7K resistors to +5 VDC on SDA and SCL
'
' Note that this is a direct interface with the PICAXE.  The Parallax and
' Sparkfun boards are not required.
' 
'
' copyright, Peter H Anderson, Baltimore, MD, Mar 20, 11

#picaxe 20x2
#Terminal 9600
#No_Table
#No_Data
#freq m4

Symbol Lo = B0
Symbol Ch = B0
Symbol Hi = B1
Symbol PEC = B2
Symbol Status = B3
Symbol Whole = B4
Symbol Fract = B5
Symbol Val = W3
Symbol TC_100 = W4
Symbol SlaveAdr_2 = B10
Symbol RamLocation = B11
Symbol EEPROMLocation = B12
Symbol X = B13
Symbol N = B14
Symbol CRC8 = B15
Symbol J = B16

Top:

    Hi2cSetup I2CMaster, 45, I2CSlow, I2CByte 


    SlaveAdr_2 = $00 * 2
    EEPROMLocation = $20 + $04

    Val = $0000		' erase the eeprom
    GoSub WriteEEPROM

    Val = $FFFF ' emissivity set to 1.0
    GoSub WriteEEPROM

#REM ' this was used in debugging to verify the EEPROM was being written
    GoSub ReadEEPROM
    SerTxD ("Emis = ", #Val, CR, LF)

    ' recycle power to the MLX90614 before trying to use the new emmisivity

    ' SerTxD ("Recycle power to the MLX90614.  Key in a number when done", CR, LF)
    ' SerRxD [45000], Ch
#ENDREM

    For J = 0 to 9 ' ten measurements

       RamLocation = $06 ' ambient temperature

       GoSub ReadRAM

       SerTxD (#J, "  ")

       If Status = 1 Then
          TC_100 = Val * 2 - 27315
          SerTxD ("T_ambient = ") 
          GoSub DisplayTc
       
       Else
          SerTxD ("T_ambient - Error")
       End If

       SerTxD (",  ")

       RamLocation = $07 ' ambient temperature

       GoSub ReadRAM
       If Status = 1 Then
          TC_100 = Val * 2 - 27315
          SerTxD ("T_object = ") 
          GoSub DisplayTc
       Else
          SerTxD ("T_object - Error")  
       End If
      
       SerTxD (CR, LF)
    Next
   
    Pause 10000

  
    ' now cahnge to emmisivity
    EEPROMLocation = $20 + $04
    Val = $0000			' erase the location
    GoSub WriteEEPROM

TryAgn:

    SerTxD ("Enter emmisivity: ")
    SerRxD [15000, TryAgn], #Val
    SerTxD (#Val, CR, LF);

    GoSub WriteEEPROM

#REM ' for debugging as noted above
    GoSub ReadEEPROM
    SerTxD ("Emis = ", #Val, CR, LF)

    ' recycle power to the MLX90614 before trying to use the new slave address

    ' SerTxD ("Recycle power to the MLX90614.  Key in a number when done", CR, LF)
    ' SerRxD [45000], Ch
#ENDREM

    For J = 0 to 9 ' ten measurements with the new emmisivity

       RamLocation = $06 ' ambient temperature

       GoSub ReadRAM

       SerTxD (#J, "  ")

       If Status = 1 Then
          TC_100 = Val * 2 - 27315
          SerTxD ("T_ambient = ") 
          GoSub DisplayTc
       
       Else
          SerTxD ("T_ambient - Error")
       End If

       SerTxD (",  ")

       RamLocation = $07 ' ambient temperature

       GoSub ReadRAM
       If Status = 1 Then
          TC_100 = Val * 2 - 27315
          SerTxD ("T_object = ") 
          GoSub DisplayTc
       Else
          SerTxD ("T_object - Error")  
       End If
      
       SerTxD (CR, LF)
    Next

Agn: ' loop forever

    SerTxD (".");
    Pause 10000
    Goto Agn
    
ReadRAM:
      
    Hi2cin [SlaveAdr_2], RamLocation, (Lo, Hi, PEC)

    Val = Hi
    Val = Val * 256 + Lo
   
    'SerTxD ("Hello ", #TC_100, "  ", #PEC, CR, LF)
                        
    CRC8 = $00
    X = SlaveAdr_2
    GoSub CalcCRC8
    X = RamLocation
    GoSub CalcCRC8
    X = SlaveAdr_2 + 1
    GoSub CalcCRC8
    X = Lo
    GoSub CalcCRC8
    X = Hi
    GoSub CalcCRC8
    X = PEC
    GoSub CalcCRC8

    'SerTxD ("CRC = ", #CRC8, CR, LF)
    If CRC8 = 0 Then
       Status = 1 ' success
    Else
       Status = 0
    Endif
    
    Return

ReadEEPROM:

    Hi2cin [SlaveAdr_2], EEPROMLocation, (Lo, Hi, PEC)

    Val = Hi
    Val = Val * 256 + Lo
                              
    CRC8 = $00
    X = SlaveAdr_2
    GoSub CalcCRC8
    X = EEPROMLocation
    GoSub CalcCRC8
    X = SlaveAdr_2 + 1
    GoSub CalcCRC8
    X = Lo
    GoSub CalcCRC8
    X = Hi
    GoSub CalcCRC8
    X = PEC
    GoSub CalcCRC8

    ' SerTxD ("CRC = ", #CRC8, CR, LF)
    If CRC8 = 0 Then
       Status = 1 ' success
    Else
       Status = 0
    Endif

    SerTxD (#SlaveAdr_2, "  ", #EEPROMLocation, CR, LF)    
    Return

WriteEEPROM:

     Lo = Val     
     Hi = Val / 256

     CRC8 = $00

     X = SlaveAdr_2
     GoSub CalcCRC8
     X = EEPROMLocation
     GoSub CalcCRC8
      
     X = Lo
     GoSub CalcCRC8
     X = Hi
     GoSub CalcCRC8
     PEC = CRC8

     Hi2cOut [SlaveAdr_2], EEPROMLocation, (Lo, Hi, PEC)
     Pause 20
  
     Return
 
DisplayTc:

    Whole = TC_100 / 100
    Fract = TC_100 // 100

    SerTxD (#Whole, ".")
    If Fract < 10 Then
        SerTxD ("0")
    Endif
    SerTxD (#Fract)

    Return
   
CalcCRC8:

   X = X ^ CRC8
   For N = 0 to 7
      If X > 127 Then
         X = X * 2
         X = X ^ $07
      Else
         X = X * 2
      Endif
   Next
   CRC8 = X
   Return


Arduino

// MLX_Temperature (Arduino - ATMega328)
//
// Arduino                      Melexis MLX90614
//
// 9 (PORTB.1) ----------------- SDA (2)
// 8 (PORTB.0) ----------------- SCL (1)
//                       +5 ---- VDD (3)
//                       GRD --- VSS (4)
//
// 1 K pullups to + 5 VDC on SDA and SCL
//
//
// Continually measures ambient Ta and object To and displays values
// in degrees F to terminal.
//
// copyright, Peter H Anderson, Baltimore, MD, Feb 23, '11
//

#include <avr\io.h>

#define cbi(sfr, bit) (sfr &= (~(0x01 << bit)))
#define sbi(sfr, bit) (sfr |= (0x01 << bit))

#define TRUE 1
#define FALSE 0

#define SUCCESS !0
#define FAILURE 0

#define SCL 0
#define SDA 1  

byte meas_temperature(float *pT, byte slave_address, byte ram_location);
float celcius_to_fahr(byte Tc);

byte CalcCRC8(byte CRC8, byte v);

void smI2CStart(void);
void smI2CStop(void);
void smI2COutByte(byte X);
byte smI2CInByte(byte Ack);

void smSDA_HIGH(void);
void smSCL_HIGH(void);
void smSDA_LOW(void);
void smSCL_LOW(void);

void print_float(float f, int num_digits);

void setup(void)
{
  
   Serial.begin(9600);
   delay(5000);
   Serial.println(">>>>>>>>>>>>>>>>>>>>>>>>");  // just to be sure things are working
}

void loop(void)
{

       float Ta_f, To_f;
     
       meas_temperature(&Ta_f, 0x00, 0x06);
       meas_temperature(&To_f, 0x00, 0x07);
       
       print_float(Ta_f, 2);
       Serial.print("     ");
       
       print_float(To_f, 2);
       Serial.print("\n");
                   
       delay(1000);             
}
    
byte meas_temperature(float *pT, byte slave_address, byte ram_location)  // 0x06 for Ta, 0x07 for Tobj
{
       byte low, high, PEC, CRC8;
       unsigned int v;
       float Tc, Tf;
     
       smI2CStart();
       smI2COutByte(slave_address * 2); 
       smI2COutByte(ram_location); 
       
       smI2CStart();
       smI2COutByte(slave_address * 2 | 0x01);
       low = smI2CInByte(1);
       high = smI2CInByte(1);
       PEC = smI2CInByte(1);
       smI2CStop();
     
       CRC8 = 0x00;
       CRC8 = CalcCRC8(CRC8, slave_address*2);
       CRC8 = CalcCRC8(CRC8, ram_location);
       CRC8 = CalcCRC8(CRC8, slave_address*2+1);
       CRC8 = CalcCRC8(CRC8, low);
       CRC8 = CalcCRC8(CRC8, high);
       CRC8 = CalcCRC8(CRC8, PEC);
       
       if (CRC8 != 0)
       {
          *pT = 99.9; // used as failure indication
          return(FAILURE);
       }
       
       else
       {
           v = (unsigned int) high * 256 + low;
       
           Tc = (float)(v) * 0.02 - 273.15;
           Tf = 1.8 * Tc + 32.0;
           *pT = Tf;
           return(SUCCESS);
       }
}   

byte CalcCRC8(byte CRC8, byte v)
{
   byte n;
   v = v ^ CRC8;
   for (n=0; n<8; n++)
   {
      if (v & 0x80)
      {
          v = (v << 1) ^ 0x07;
      }
      else
      {
          v = v << 1;
      }
    
   }
   return(v);
}   
        
void smI2CStart(void)
{
    smSDA_HIGH();    
    smSCL_HIGH();   
    smSDA_LOW();   
    smSCL_LOW();   
}

void smI2CStop(void)
{
    smSCL_LOW();   
    smSDA_LOW();  ;
    smSCL_HIGH();   
    smSDA_HIGH(); 
}

void smI2COutByte(byte X)
{
    byte n;
    for (n=0; n<8; n++)
    {
        if (X & 0x80)
        {
            smSDA_HIGH();
        }
        else
        {
            smSDA_LOW();
        }
        
        smSCL_HIGH();
        smSCL_LOW();
        X = X << 1;
    }

    smSDA_HIGH();
    smSCL_HIGH();
    smSCL_LOW();
    smSDA_LOW();
}    

byte smI2CInByte(byte Ack)
{
    byte n, X;
    
    smSDA_HIGH();
    for (n=0; n<8; n++)
    {    
        smSCL_HIGH();
        if (digitalRead(9))
        {
            X = (X << 1) | 0x01;
        }
        else
        {
            X = X << 1;
        }
        smSCL_LOW();
    }
    
    if (Ack)
    {
        smSDA_LOW();
    }
    else
    {   
        smSDA_HIGH();
    }
    smSCL_HIGH();
    smSCL_LOW();
    
    smSDA_LOW();
    
    return(X);
}
       

void smSDA_HIGH(void)
{
   cbi(DDRB, SDA);
   delayMicroseconds(10);   
}

void smSCL_HIGH(void)
{
    cbi(DDRB, SCL);
    delayMicroseconds(10);
  
}
       
void smSDA_LOW(void)
{
    cbi(PORTB, SDA);
    sbi(DDRB, SDA);
    delayMicroseconds(10);
}

void smSCL_LOW(void)
{
    cbi(PORTB, SCL);
    sbi(DDRB, SCL); 
    delayMicroseconds(10);
}

void print_float(float f, int num_digits)
{
    int f_int;
    int pows_of_ten[4] = {1, 10, 100, 1000};
    int multiplier, whole, fract, d, n;

    multiplier = pows_of_ten[num_digits];
    if (f < 0.0)
    {
        f = -f;
        Serial.print("-");
    }
    whole = (int) f;
    fract = (int) (multiplier * (f - (float)whole));

    Serial.print(whole);
    Serial.print(".");

    for (n=num_digits-1; n>=0; n--) // print each digit with no leading zero suppression
    {
         d = fract / pows_of_ten[n];
         Serial.print(d);
         fract = fract % pows_of_ten[n];
    }
}


// Melexis_EEPROM
//
// Arduino                      Melexis MLX90614
//
// 9 (PORTB.1) ----------------- SDA (2)
// 8 (PORTB.0) ----------------- SCL (1)
//                       +5 ---- VDD (3)
//                       GRD --- VSS (4)
//
// 1 K pullups to + 5 VDC on SDA and SCL
//
// Reads 32 EEPROM locations and displays.
//
// EEPROM location 0x0e is changed to 0x5A.  The device is then addressed as 0x5a.
//
// copyright, Peter H Anderson, Baltimore, MD, Feb 5, '11
//

#include <avr\io.h>

#define cbi(sfr, bit) (sfr &= (~(0x01<<bit)))
#define sbi(sfr, bit) (sfr |= (0x01 << bit))

#define TRUE 1
#define FALSE 0

#define SUCCESS !0
#define FAILURE 0

#define SCL 0
#define SDA 1  

byte MLXReadEEPROM(unsigned int *pdat, byte slave_adr, byte adr);
byte MLXWriteEEPROM(byte slave_adr, byte eeprom_adr, unsigned int v);
void print_eeprom(byte adr, int v);

byte CalcCRC8(byte CRC8, byte v);

void smI2CStart(void);
void smI2CStop(void);
void smI2COutByte(byte X);
byte smI2CInByte(byte Ack);

void smSDA_HIGH(void);
void smSCL_HIGH(void);
void smSDA_LOW(void);
void sbSCL_LOW(void);

void setup(void)
{
   Serial.begin(9600);
   delay(5000);
   Serial.print(">>>>>>>>>>>>>>>>>>>>>>>>\n");
}

void loop(void)
{
    byte n;
    unsigned int v;
    for(n=0; n<32; n++)
    {
       MLXReadEEPROM(&v, 0x00, n);
       print_eeprom(n, v);
    }
    n = 10;
    while(n--)
    {
       Serial.print(".");
       delay(1000);
    }
    
    Serial.println();
    
    // erase location 004      
    MLXWriteEEPROM(0x00, 0x0e, 0x0000);  // write zero to erase
    delay(10);
    
    MLXReadEEPROM(&v, 0x00, 0x0e);  // read to verify

    Serial.println(v, HEX);
 
    MLXWriteEEPROM(0x00, 0x0e, 0x005a);  // write slave adr
    delay(10);
    
    MLXReadEEPROM(&v, 0x5a / 2, 0x0e);   // use the new slave adr to read 
    Serial.println(v, HEX);  
          
    while(1)
    {
       Serial.print("!");
       delay(1000);
    }             
}

byte MLXWriteEEPROM(byte slave_address, byte eeprom_adr, unsigned int v)
{
   byte high, low, CRC8, PEC;
   
   low = v & 0xff;  high = (v>>8) & 0xff;
   
   // compute packet error
   
   CRC8 = 0x00;
   CRC8 = CalcCRC8(CRC8, 2*slave_address);
   CRC8 = CalcCRC8(CRC8, 0x20 + eeprom_adr);     
   CRC8 = CalcCRC8(CRC8, low);
   CRC8 = CalcCRC8(CRC8, high);
   PEC = CRC8;
   
   smI2CStart();
   smI2COutByte(slave_address*2); 
   smI2COutByte(0x20 + eeprom_adr);  //  EEPROM location  
   smI2COutByte(low);
   smI2COutByte(high);
   smI2COutByte(PEC);
   smI2CStop();
   
   return(SUCCESS);
} 

byte MLXReadEEPROM(unsigned int *pdat, byte slave_adr, byte adr)
{
    byte low, high, PEC, CRC8;
    
    smI2CStart();
    smI2COutByte(slave_adr*2); // assume device adr of 00
    smI2COutByte(0x20 + adr);  // read EEPROM location
   
    smI2CStart();
    smI2COutByte(slave_adr * 2 + 0x01);
    low = smI2CInByte(1);
    high = smI2CInByte(1);
    PEC = smI2CInByte(1);
    smI2CStop();   
    
    CRC8 = 0;
    CRC8 = CalcCRC8(CRC8, 2 * slave_adr);
    CRC8 = CalcCRC8(CRC8, 0x20 + adr);
    CRC8 = CalcCRC8(CRC8, slave_adr * 2 + 1);
    CRC8 = CalcCRC8(CRC8, low);
    CRC8 = CalcCRC8(CRC8, high); 
     
    if (CRC8 != PEC)
    {
        *pdat = 0;
        return(FAILURE);
    }
    else
    {
       *pdat =  high * 256 + low;
       return(SUCCESS);
    }
}

void print_eeprom(byte adr, unsigned int v)
{
    v = v & 0x0000ffff;
    Serial.print(adr, HEX);
    Serial.print(" ");
    Serial.println(v, HEX);
}

byte CalcCRC8(byte CRC8, byte v)
{
   byte n;
   v = v ^ CRC8;
   for (n=0; n<8; n++)
   {
      if (v&0x80)
      {
          v = (v << 1) ^ 0x07;
      }
      else
      {
          v = v << 1;
      }
    
   }
   return(v);
}   
        
void smI2CStart(void)
{
    smSDA_HIGH();    
    smSCL_HIGH();   
    smSDA_LOW();   
    smSCL_LOW();   
}

void smI2CStop(void)
{
    smSCL_LOW();   
    smSDA_LOW();  ;
    smSCL_HIGH();   
    smSDA_HIGH(); 
}

void smI2COutByte(byte X)
{
    byte n;
    for (n=0; n<8; n++)
    {
        if (X & 0x80)
        {
            smSDA_HIGH();
        }
        else
        {
            smSDA_LOW();
        }
        
        smSCL_HIGH();
        smSCL_LOW();
        X = X << 1;
    }

    smSDA_HIGH();
    smSCL_HIGH();
    smSCL_LOW();
    smSDA_LOW();
}    

byte smI2CInByte(byte Ack)
{
    byte n, X;
    
    smSDA_HIGH();
    for (n=0; n<8; n++)
    {    
        smSCL_HIGH();
        if (digitalRead(9))
        {
            X = (X << 1) | 0x01;
        }
        else
        {
            X = X << 1;
        }
        smSCL_LOW();
    }
    
    if (Ack)
    {
        smSDA_LOW();
    }
    else
    {   
        smSDA_HIGH();
    }
    smSCL_HIGH();
    smSCL_LOW();
    
    smSDA_LOW();
    
    return(X);
}
       

void smSDA_HIGH(void)
{
   cbi(DDRB, SDA);
   delayMicroseconds(10);   
}

void smSCL_HIGH(void)
{
    cbi(DDRB, SCL);
    delayMicroseconds(10);
  
}
       
void smSDA_LOW(void)
{
    cbi(PORTB, SDA);
    sbi(DDRB, SDA);
    delayMicroseconds(10);
}

void smSCL_LOW(void)
{
    cbi(PORTB, SCL);
    sbi(DDRB, SCL); 
    delayMicroseconds(10);
}


// MLX_emis (Arduino - ATMega328)
//
// Arduino                      Melexis MLX90614
//
// 9 (PORTB.1) ----------------- SDA (2)
// 8 (PORTB.0) ----------------- SCL (1)
//                       +5 ---- VDD (3)
//                       GRD --- VSS (4)
//
// 1.0 K pullup resistors to +5 VDC on SDA and SCL
//
// Changes emisivity at EEPROM location 0x04 to 0.8 and performs 10 temperature measurements.
// Change emissivity to 1.0 and performs 10 measurments.
//
// copyright, Peter H Anderson, Baltimore, MD, Feb 23, '10
//

#include <avr\io.h>

#define cbi(sfr, bit) (sfr &= (~(0x01<<bit)))
#define sbi(sfr, bit) (sfr |= (0x01 << bit))

#define TRUE 1
#define FALSE 0

#define SUCCESS !0
#define FAILURE 0

#define SCL 0
#define SDA 1  

byte meas_temperature(float *pT, byte slave_address, byte ram_location);
float celcius_to_fahr(byte Tc);

byte MLXReadEEPROM(unsigned int *pdat, byte slave_adr, byte adr);
byte MLXWriteEEPROM(byte slave_adr, byte eeprom_adr, unsigned int v);

byte CalcCRC8(byte CRC8, byte v);

void smI2CStart(void);
void smI2CStop(void);
void smI2COutByte(byte X);
byte smI2CInByte(byte Ack);

void smSDA_HIGH(void);
void smSCL_HIGH(void);
void smSDA_LOW(void);
void smSCL_LOW(void);

void print_float(float f, int num_digits);

void setup(void)
{
  
   Serial.begin(9600);
   delay(5000);
   Serial.println(">>>>>>>>>>>>>>>>>>>>>>>>");  // just to be sure things are working
}

void loop()
{
   unsigned int v, w;
   byte slave_address, n;
   float Ta_f, To_f, emissivity;
   
   // read emissitvity  
   MLXReadEEPROM(&v, 0x00, 0x04);
   emissivity = ((float) (v-1)) / 65535.0 * 100.0;
   Serial.print("0.");
   Serial.println((unsigned int)emissivity, DEC);
   
   // change emisivity to 0.8
   emissivity = 0.8;
   w = (unsigned int) (emissivity * 65535.0);
   
   MLXWriteEEPROM(0x00, 0x04, 0x0000);  // write zero to erase
   delay(25);
    
   MLXReadEEPROM(&v, 0x00, 0x04); // verify
   Serial.println(v, HEX);   // should be 0
   
   MLXWriteEEPROM(0x00, 0x04, w);  
   delay(25);
   
   MLXReadEEPROM(&v, 0x00, 0x04);   
   emissivity = ((float) (v-1)) / 65535.0 * 100.0;
   Serial.print("0.");
   Serial.println((unsigned int)emissivity, DEC);
   
   // now measure temperatures
   for (n=0; n<10; n++)
   {   
      meas_temperature(&Ta_f, 0x00, 0x06);
      meas_temperature(&To_f, 0x00, 0x07);
       
      print_float(Ta_f, 2);
      Serial.print("     ");
       
      print_float(To_f, 2);
      Serial.print("\n");
                   
      delay(1000);  
   }

   // now write emissivity of 1.0   
   emissivity = 1.0;
   w = (unsigned int) (emissivity * 65535.0);
   
   MLXWriteEEPROM(0x00, 0x04, 0x0000);  // write zero to erase
   delay(25);
    
   MLXReadEEPROM(&v, 0x00, 0x04);
   Serial.println(v, HEX);   // should be 0
   
   MLXWriteEEPROM(0x00, 0x04, w);  
   delay(25);
   
   MLXReadEEPROM(&v, 0x00, 0x04);   
   emissivity = ((float) (v-1)) / 65535.0 * 100.0;
   Serial.print("0.");
   Serial.println((unsigned int)emissivity, DEC);
   
   // now measure temperatures
   for (n=0; n<10; n++)
   {   
      meas_temperature(&Ta_f, 0x00, 0x06);
      meas_temperature(&To_f, 0x00, 0x07);
       
      print_float(Ta_f, 2);
      Serial.print("     ");
       
      print_float(To_f, 2);
      Serial.print("\n");
                   
      delay(1000);  
   }
    
   while(1)
   {
      //Serial.print(".");
      delay(1000);
   }
}  

byte meas_temperature(float *pT, byte slave_address, byte ram_location)  // 0x06 for Ta, 0x07 for Tobj
{
       byte low, high, PEC, CRC8;
       unsigned int v;
       float Tc, Tf;
     
       smI2CStart();
       smI2COutByte(slave_address * 2); 
       smI2COutByte(ram_location); 
       
       smI2CStart();
       smI2COutByte(slave_address * 2 | 0x01);
       low = smI2CInByte(1);
       high = smI2CInByte(1);
       PEC = smI2CInByte(1);
       smI2CStop();
     
       CRC8 = 0x00;
       CRC8 = CalcCRC8(CRC8, slave_address*2);
       CRC8 = CalcCRC8(CRC8, ram_location);
       CRC8 = CalcCRC8(CRC8, slave_address*2+1);
       CRC8 = CalcCRC8(CRC8, low);
       CRC8 = CalcCRC8(CRC8, high);
       CRC8 = CalcCRC8(CRC8, PEC);
       
       if (CRC8 != 0)
       {
          *pT = 99.9; // used as failure indication
          return(FAILURE);
       }
       
       else
       {
           v = (unsigned int) high * 256 + low;
       
           Tc = (float)(v) * 0.02 - 273.15;
           Tf = 1.8 * Tc + 32.0;
           *pT = Tf;
           return(SUCCESS);
       }
}   

byte MLXWriteEEPROM(byte slave_address, byte eeprom_adr, unsigned int v)
{
   byte high, low, CRC8, PEC;
   
   low = v & 0xff;  high = (v>>8) & 0xff;
   
   // compute packet error
   
   CRC8 = 0x00;
   CRC8 = CalcCRC8(CRC8, 2*slave_address);
   CRC8 = CalcCRC8(CRC8, 0x20 + eeprom_adr);     
   CRC8 = CalcCRC8(CRC8, low);
   CRC8 = CalcCRC8(CRC8, high);
   PEC = CRC8;
   
   
   smI2CStart();
   smI2COutByte(slave_address*2); 
   smI2COutByte(0x20 + eeprom_adr);  //  EEPROM location  
   smI2COutByte(low);
   smI2COutByte(high);
   smI2COutByte(PEC);
   smI2CStop();
   
   return(SUCCESS);
} 

byte MLXReadEEPROM(unsigned int *pdat, byte slave_adr, byte adr)
{
    byte low, high, PEC, CRC8;
    
    smI2CStart();
    smI2COutByte(slave_adr*2); 
    smI2COutByte(0x20 + adr);  // read EEPROM location

    smI2CStart();
    smI2COutByte(slave_adr * 2 + 0x01);
    low = smI2CInByte(1);
    high = smI2CInByte(1);
    PEC = smI2CInByte(1);
    smI2CStop();   
    
    CRC8 = 0;
    CRC8 = CalcCRC8(CRC8, 2 * slave_adr);
    CRC8 = CalcCRC8(CRC8, 0x20 + adr);
    CRC8 = CalcCRC8(CRC8, slave_adr * 2 + 1);
    CRC8 = CalcCRC8(CRC8, low);
    CRC8 = CalcCRC8(CRC8, high); 
    
    //Serial.print(CRC8, HEX);
    //Serial.print("... ");
    //Serial.println(PEC, HEX);
   
    if (CRC8 != PEC)
    {   
        Serial.print(CRC8, HEX);
        Serial.print("..");
        Serial.println(PEC, HEX);
        *pdat = high * 256 + low;
        return(FAILURE);
    }
    else
    {
       *pdat =  high * 256 + low;
       return(SUCCESS);
    }
}

void print_eeprom(byte adr, unsigned int v)
{
    v = v & 0x0000ffff;
    Serial.print(adr, HEX);
    Serial.print(" ");
    Serial.println(v, HEX);
}

byte CalcCRC8(byte CRC8, byte v)
{
   byte n;
   v = v ^ CRC8;
   for (n=0; n<8; n++)
   {
      if (v&0x80)
      {
          v = (v << 1) ^ 0x07;
      }
      else
      {
          v = v << 1;
      }
    
   }
   return(v);
}   
        
void smI2CStart(void)
{
    smSDA_HIGH();    
    smSCL_HIGH();   
    smSDA_LOW();   
    smSCL_LOW();   
}

void smI2CStop(void)
{
    smSCL_LOW();   
    smSDA_LOW();  ;
    smSCL_HIGH();   
    smSDA_HIGH(); 
}

void smI2COutByte(byte X)
{
    byte n;
    for (n=0; n<8; n++)
    {
        if (X & 0x80)
        {
            smSDA_HIGH();
        }
        else
        {
            smSDA_LOW();
        }
        
        smSCL_HIGH();
        smSCL_LOW();
        X = X << 1;
    }

    smSDA_HIGH();
    smSCL_HIGH();
    smSCL_LOW();
    smSDA_LOW();
}    

byte smI2CInByte(byte Ack)
{
    byte n, X;
    
    smSDA_HIGH();
    for (n=0; n<8; n++)
    {    
        smSCL_HIGH();
        if (digitalRead(9))
        {
            X = (X << 1) | 0x01;
        }
        else
        {
            X = X << 1;
        }
        smSCL_LOW();
    }
    
    if (Ack)
    {
        smSDA_LOW();
    }
    else
    {   
        smSDA_HIGH();
    }
    smSCL_HIGH();
    smSCL_LOW();
    
    smSDA_LOW();
    
    return(X);
}
       

void smSDA_HIGH(void)
{
   cbi(DDRB, SDA);
   delayMicroseconds(10);   
}

void smSCL_HIGH(void)
{
    cbi(DDRB, SCL);
    delayMicroseconds(10);
  
}
       
void smSDA_LOW(void)
{
    cbi(PORTB, SDA);
    sbi(DDRB, SDA);
    delayMicroseconds(10);
}

void smSCL_LOW(void)
{
    cbi(PORTB, SCL);
    sbi(DDRB, SCL); 
    delayMicroseconds(10);
}

void print_float(float f, int num_digits)
{
    int f_int;
    int pows_of_ten[4] = {1, 10, 100, 1000};
    int multiplier, whole, fract, d, n;

    multiplier = pows_of_ten[num_digits];
    if (f < 0.0)
    {
        f = -f;
        Serial.print("-");
    }
    whole = (int) f;
    fract = (int) (multiplier * (f - (float)whole));

    Serial.print(whole);
    Serial.print(".");

    for (n=num_digits-1; n>=0; n--) // print each digit with no leading zero suppression
    {
         d = fract / pows_of_ten[n];
         Serial.print(d);
         fract = fract % pows_of_ten[n];
    }
}


Ordering Info