Hey Everyone,
I'm fairly new to the whole arduino world, and this is my first venture into datalogging territory, and of course, I'm running up against a wall. My goal is to read data of an IR array (MLX90620) and then save the temperatures to an SD card, exactly how it's printing to the Serial Monitor.
I've run through several SD library examples, and they seem to work - I can log the sketch data on the SD card, and then verify by popping it into my computer. All good. I can get temperatures from the MLX90620. All good. But I can't seem to get them to work together! This is the sketch that reads the MLX90620 - I have to break it up to fit here, so here's the first half:
/*
2-16-2013
Spark Fun Electronics
Nathan Seidle
This code is heavily based on maxbot's and IlBaboomba's code: http://arduino.cc/forum/index.php?topic=126244
They didn't have a license on it so I'm hoping it's public domain.
This example shows how to read and calculate the 64 temperatures for the 64 pixels of the MLX90620 thermopile sensor.
alpha_ij array is specific to every sensor and needs to be calculated separately. Please see the
'MLX90620_alphaCalculator' sketch to get these values. If you choose not to calculate these values
this sketch will still work but the temperatures shown will be very inaccurate.
Don't get confused by the bottom view of the device! The GND pin is connected to the housing.
To get this code to work, attached a MLX90620 to an Arduino Uno using the following pins:
A5 to 330 ohm to SCL
A4 to 330 ohm to SDA
3.3V to VDD
GND to VSS
I used the internal pull-ups on the SDA/SCL lines. Normally you should use ~4.7k pull-ups for I2C.
*/
#include <i2cmaster.h>
//i2cmaster comes from here: http://www.cheap-thermocam.bplaced.net/software/I2Cmaster.rar
#include "MLX90620_registers.h"
int refreshRate = 16; //Set this value to your desired refresh frequency
//Global variables
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
int irData[64]; //Contains the raw IR data from the sensor
float temperatures[64]; //Contains the calculated temperatures of each pixel in the array
float Tambient; //Tracks the changing ambient temperature of the sensor
byte eepromData[256]; //Contains the full EEPROM reading from the MLX (Slave 0x50)
//These are constants calculated from the calibration data stored in EEPROM
//See varInitialize and section 7.3 for more information
int v_th, a_cp, b_cp, tgc, b_i_scale;
float k_t1, k_t2, emissivity;
int a_ij[64], b_ij[64];
//These values are calculated using equation 7.3.3.2
//They are constants and can be calculated using the MLX90620_alphaCalculator sketch
float alpha_ij[64] = {
1.46447E-8, 1.50521E-8, 1.50521E-8, 1.34805E-8, 1.60126E-8, 1.62163E-8, 1.65946E-8, 1.48484E-8,
1.62163E-8, 1.65946E-8, 1.64200E-8, 1.50521E-8, 1.65946E-8, 1.73805E-8, 1.72058E-8, 1.60126E-8,
1.73805E-8, 1.77879E-8, 1.79625E-8, 1.62163E-8, 1.83700E-8, 1.85446E-8, 1.83700E-8, 1.67984E-8,
1.87483E-8, 1.93304E-8, 1.89521E-8, 1.70021E-8, 1.89521E-8, 1.97379E-8, 1.91558E-8, 1.77879E-8,
1.93304E-8, 1.93304E-8, 1.93304E-8, 1.79625E-8, 1.89521E-8, 1.95341E-8, 1.95341E-8, 1.79625E-8,
1.89521E-8, 1.95341E-8, 1.97379E-8, 1.79625E-8, 1.87483E-8, 1.89521E-8, 1.91558E-8, 1.73805E-8,
1.81663E-8, 1.85737E-8, 1.87483E-8, 1.70021E-8, 1.72058E-8, 1.81663E-8, 1.79625E-8, 1.72058E-8,
1.65946E-8, 1.75842E-8, 1.79625E-8, 1.65946E-8, 1.62163E-8, 1.72058E-8, 1.72058E-8, 1.58379E-8,
};
byte loopCount = 0; //Used in main loop
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//Begin Program code
void setup()
{
Serial.begin(115200);
Serial.println("MLX90620 Example");
i2c_init(); //Init the I2C pins
PORTC = (1 << PORTC4) | (1 << PORTC5); //Enable pull-ups
delay(5); //Init procedure calls for a 5ms delay after power-on
read_EEPROM_MLX90620(); //Read the entire EEPROM
setConfiguration(refreshRate); //Configure the MLX sensor with the user's choice of refresh rate
calculate_TA(); //Calculate the current Tambient
}
void loop()
{
if(loopCount++ == 16) //Tambient changes more slowly than the pixel readings. Update TA only every 16 loops.
{
calculate_TA(); //Calculate the new Tambient
if(checkConfig_MLX90620()) //Every 16 readings check that the POR flag is not set
{
Serial.println("POR Detected!");
setConfiguration(refreshRate); //Re-write the configuration bytes to the MLX
}
loopCount = 0; //Reset count
}
readIR_MLX90620(); //Get the 64 bytes of raw pixel data into the irData array
calculate_TO(); //Run all the large calculations to get the temperature data for each pixel
prettyPrintTemperatures(); //Print the array in a 4 x 16 pattern
//rawPrintTemperatures(); //Print the entire array so it can more easily be read by Processing app
}
//From the 256 bytes of EEPROM data, initialize
void varInitialization(byte calibration_data[])
{
v_th = 256 * calibration_data[VTH_H] + calibration_data[VTH_L];
k_t1 = (256 * calibration_data[KT1_H] + calibration_data[KT1_L]) / 1024.0; //2^10 = 1024
k_t2 = (256 * calibration_data[KT2_H] + calibration_data[KT2_L]) / 1048576.0; //2^20 = 1,048,576
emissivity = ((unsigned int)256 * calibration_data[CAL_EMIS_H] + calibration_data[CAL_EMIS_L]) / 32768.0;
a_cp = calibration_data[CAL_ACP];
if(a_cp > 127) a_cp -= 256; //These values are stored as 2's compliment. This coverts it if necessary.
b_cp = calibration_data[CAL_BCP];
if(b_cp > 127) b_cp -= 256;
tgc = calibration_data[CAL_TGC];
if(tgc > 127) tgc -= 256;
b_i_scale = calibration_data[CAL_BI_SCALE];
for(int i = 0 ; i < 64 ; i++)
{
//Read the individual pixel offsets
a_ij[i] = calibration_data[i];
if(a_ij[i] > 127) a_ij[i] -= 256; //These values are stored as 2's compliment. This coverts it if necessary.
//Read the individual pixel offset slope coefficients
b_ij[i] = calibration_data[0x40 + i]; //Bi(i,j) begins 64 bytes into EEPROM at 0x40
if(b_ij[i] > 127) b_ij[i] -= 256;
}
}
//Receives the refresh rate for sensor scanning
//Sets the two byte configuration registers
//This function overwrites what is currently in the configuration registers
//The MLX doesn't seem to mind this (flags are read only)
void setConfiguration(int irRefreshRateHZ)
{
byte Hz_LSB;
switch(irRefreshRateHZ)
{
case 0:
Hz_LSB = 0b00001111;
break;
case 1:
Hz_LSB = 0b00001110;
break;
case 2:
Hz_LSB = 0b00001101;
break;
case 4:
Hz_LSB = 0b00001100;
break;
case 8:
Hz_LSB = 0b00001011;
break;
case 16:
Hz_LSB = 0b00001010;
break;
case 32:
Hz_LSB = 0b00001001;
break;
default:
Hz_LSB = 0b00001110;
}
byte defaultConfig_H = 0b01110100; // x111.01xx, Assumes NA = 0, ADC low reference enabled, Ta Refresh rate of 2Hz
i2c_start_wait(MLX90620_WRITE);
i2c_write(0x03); //Command = configuration value
i2c_write((byte)Hz_LSB - 0x55);
i2c_write(Hz_LSB);
i2c_write(defaultConfig_H - 0x55); //Assumes NA = 0, ADC low reference enabled, Ta Refresh rate of 2Hz
i2c_write(defaultConfig_H);
i2c_stop();
}
//Read the 256 bytes from the MLX EEPROM and setup the various constants (*lots* of math)
//Note: The EEPROM on the MLX has a different I2C address from the MLX. I've never seen this before.
void read_EEPROM_MLX90620()
{
i2c_start_wait(MLX90620_EEPROM_WRITE);
i2c_write(0x00); //EEPROM info starts at location 0x00
i2c_rep_start(MLX90620_EEPROM_READ);
//Read all 256 bytes from the sensor's EEPROM
for(int i = 0 ; i <= 255 ; i++)
eepromData[i] = i2c_readAck();
i2c_stop(); //We're done talking
varInitialization(eepromData); //Calculate a bunch of constants from the EEPROM data
writeTrimmingValue(eepromData[OSC_TRIM_VALUE]);
}