Go Down

Topic: Melexis Infrared Thermometer Speed (Read 900 times) previous topic - next topic

zalo

Jun 17, 2011, 03:09 am Last Edit: Jun 17, 2011, 04:50 pm by zalo Reason: 1
Recently, I purchased an Arduino and some other various assorted parts from Sparkfun, including a Melexis Infrared Thermometer ( http://www.sparkfun.com/products/9570 )

I wired up the unit according to these instructions, using this arduino code (posted below): http://bildr.org/2011/02/mlx90614-arduino/

After a little bit of farting around, I managed to get some usable data from the sensor into Processing.
My dilemma is that the temperature data only comes in a few times a second.

What can I do to squeeze more data samples out of this nifty little sensor in a given timeframe?

I'm a newbie to electronics, but I'm pretty familiar with programming.  Here's the arduino code:
Code: [Select]
#include <i2cmaster.h>


void setup(){
   Serial.begin(19200);
   Serial.println("Setup...");
   
   i2c_init(); //Initialise the i2c bus
   PORTC = (1 << PORTC4) | (1 << PORTC5);//enable pullups
}

void loop(){
   int dev = 0x5A<<1;
   int data_low = 0;
   int data_high = 0;
   int pec = 0;
   
   i2c_start_wait(dev+I2C_WRITE);
   i2c_write(0x07);
   
   // read
   i2c_rep_start(dev+I2C_READ);
   data_low = i2c_readAck(); //Read 1 byte and then send ack
   data_high = i2c_readAck(); //Read 1 byte and then send ack
   pec = i2c_readNak();
   i2c_stop();
   
   //This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
   double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
   double tempData = 0x0000; // zero out the data
   int frac; // data past the decimal point
   
   // This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
   tempData = (double)(((data_high & 0x007F) << 8) + data_low);
   tempData = (tempData * tempFactor)-0.01;
   
   float celcius = tempData - 273.15;
   float fahrenheit = (celcius*1.8) + 32;

//    Serial.print("Celcius: ");
//    Serial.println(celcius);

   Serial.println(fahrenheit);
//    Serial.println(fahrenheit);

//    delay(10); // wait a second before printing again
}


And here's the processing script:

Code: [Select]
import processing.serial.*;

Serial myPort;        // The serial port
int xPos = 0;         // horizontal position of the graph
int yPos = 0;         // vertical position of the graph
String nString = "lol";
float pinByte = 0;
void setup () {
 // set the window size:
 size(100, 100);        
strokeWeight(1);
 // List all the available serial ports
 println(Serial.list());
 // I know that the first port in the serial list on my mac
 // is always my  Arduino, so I open Serial.list()[0].
 // Open whatever port is the one you're using.
 myPort = new Serial(this, Serial.list()[1], 19200);
 // don't generate a serialEvent() unless you get a newline character:
 myPort.bufferUntil('\n');
 // set inital background:
 background(0);
}
void draw () {
 // everything happens in the serialEvent()
}

void serialEvent (Serial myPort) {
 // get the ASCII string:
 String inString = myPort.readStringUntil('\n');

 if ((inString != null)&&(inString.equals(nString)!=true)){
   nString = inString;
   // trim off any whitespace:
   inString = trim(inString);
   // convert to an int and map to the screen height:
   float inByte = float(inString);
   print(inByte+"\n");
   inByte = map(inByte, 79, 90, 0, 255);

   // draw the line:
   stroke(inByte);
   line(xPos, yPos, xPos, yPos);
//    background(inByte);
   // at the edge of the screen, go back to the beginning:
   if (xPos >= width) {
     xPos = 0;
     yPos++;
   }
   else {
     // increment the horizontal position:
     xPos++;
     pinByte = inByte;
   }
 }
}


Basically, the arduino sends the temperature in Fahrenheit through a serial cable at 19200 baud (increasing it does nothing) to the processing application which takes the temperature (ignoring duplicates) and graphs them in a scan-line pattern across the domain.  Unfortunately, this could take a while at the current speed of transmissions.

I heard that this thermometer could do PWM?  How would I be able to take advantage of that for speed?

If the arduino can't extract the data fast enough alone, would this be more suitable?
http://www.sparkfun.com/products/9813

Alternatively, can I have the arduino recieve data really fast from the sensor, store it in a buffer, and then dump the data later?

Thanks.

robtillaart


what you can do is rewrite to something like:

Code: [Select]
setup()
{
  ...
  start_sensor();
 
}
loop()
{
  wait until ready;
  t = fetch temperature();
  start_sensor();
  write to processing
}

 
This (re)starts the sensor directly after you read it.
While the sensor do its work you process the new arrived data, so when you're finished the sensor is allready halfway a new value.

Hope this helps
Rob

Please use the # button for code (that gives the scrollable window).
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

robtillaart

To optimize you could try code below:
* removed the floating point math => long math

Code: [Select]

unsigned long tempData = word(data_high, data_low) & 0x7FFF;

tempData *= 100;  // to keep it in int range 
tempData *= 2;     // tempFactor
tempData -= 1;
long celsius = tempData - 27315;
long fahrenheit = celsius * 18 + 320;

Serial.print(celsius / 100, DEC);
Serial.print(".");
Serial.println(celsius % 100, DEC);

Serial.print(fahrenheit / 100, DEC);
Serial.print(".");
Serial.println(fahrenheit % 100, DEC);


you might compact it to a oneliner:

Code: [Select]
long celsius = (word(data_high, data_low) & 0x7FFF) * 200L - 27316;
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

MarkT

Well I've looked at the datasheet for 10 minutes now and still can't find what maximum sample rate the sensor supports - badly written datasheet... PWM might give more readings a second, but its hard to know without more study (I note it has a 17bit ADC on board - these low power sensor ADCs tend to be slow.

But a general observation:  its a temperature sensor, its inherently low bandwidth, you probably won't get any useful extra information by reading its output any faster.
[ I won't respond to messages, use the forum please ]

wildbill

What is your project doing that 'a few times a second' isn't sufficient for?

AWOL

Quote
What is your project doing that 'a few times a second' isn't sufficient for
I'm guessing "trying to build a thermal imager".
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

zalo

Thanks robtillart and MarkT for your help

I implemented the code from your second post, robtillart, but it had a negligible effect on sample rate.  :(

I had a little more trouble reading into the first post you made.  Considering that I am new to microcontroller-
programming, and I filched the code straight from the bildr post, I was unable to discern which lines set it and reset it.
A pointer or two in that direction would be greatly appreciated :)

MarkT, thanks for taking the time to look through the datasheet.  I too am frustrated at the lack of some of the information.  The Sparkfun eval board said it could update at 38400bps, though, that's gibberish to me. :(

AWOL, you are right on the money.  What makes the project I'm attempting slightly different from the usual servo-scanner imager, is that I've figured out a way to do ultra-high speed scanning using a motor, a servo, and some polar coordinate processing.  Similar in principle to this:
http://www.youtube.com/watch?v=WNq4x1eE-EQ
Though, my implementation would have an adjustable angle on the mirror.
This thermometer here is the only thing is vaguely unknown territory.   :(

Thanks for all of your help so far!

robtillaart


OK I will give it a try based upon your code (and my integer math variant)

(code not compiled or tested)
Code: [Select]

#include <i2cmaster.h>

void setup()
{
    Serial.begin(115200);
    Serial.println("Setup...");
   
    i2c_init(); //Initialise the i2c bus
    PORTC = (1 << PORTC4) | (1 << PORTC5);//enable pullups

   // START CONVERSION
    i2c_start_wait(dev+I2C_WRITE);
    i2c_write(0x07);
}

int dev = 0x5A<<1;
int data_low = 0;
int data_high = 0;
int pec = 0;

void loop()
{
    // READ VALUE
    i2c_rep_start(dev+I2C_READ);
    data_low = i2c_readAck(); //Read 1 byte and then send ack
    data_high = i2c_readAck(); //Read 1 byte and then send ack
    pec = i2c_readNak();
    i2c_stop();

    // REQUEST NEW VALUE
    i2c_start_wait(dev+I2C_WRITE);
    i2c_write(0x07);
   
    // PROCESS "PREVIOUS"
    long celsius = (word(data_high, data_low) & 0x7FFF) * 200L - 27316;
    long fahrenheit = (celsius * 18 + 32000)/10; 

    Serial.print(celsius / 100, DEC);
    Serial.print(".");
    Serial.println(celsius % 100, DEC);

    Serial.print(fahrenheit / 100, DEC);
    Serial.print(".");
    Serial.println(fahrenheit % 100, DEC);
}

As you follow the code you will see that before processing a new request is send, and instead of waiting for the answer we use the time to process the prev value.

Is it so more clear?
Rob
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

AWOL

Quote
Serial.println(fahrenheit % 100, DEC);

Won't that make 3.5 and 3.05 print the same value?
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

robtillaart

Definitely true (as I said not tested),  - the purpose of the sketch is to show the reversal of the START and READ cycle, not to be 100% robust
Think the OP can fix ;)




Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

zalo

#10
Jun 18, 2011, 01:03 am Last Edit: Jun 18, 2011, 01:05 am by zalo Reason: 1
Alright, I see how it works, and it worked after a little bit of tinkering.

It seems the arduino is processing the temperatures fast enough on it's own though...

It's sending data at an extremely high speed, but it often reports duplicate values.  

This is the raw data graphed (between 81 degrees and 91 degrees Fahrenheit) using rob's latest code:

The code increments the graph 1 pixel to the right every time it receives a sample.  The stair-step pattern is caused by duplicate samples.
On the plus side, it filled up extremely fast, so I know the arduino is capable of sending them that fast.
It gets faster with the increased baud rate.

This is what the graph looks like when filtered to ignore the duplicate samples:

It took almost a minute to acquire this much data (the fluctuations are me moving my hand around).
It fills up at the same speed, whether its 300 baud or 115200 baud, when I filter the duplicates (less duplicates at 300).

EDIT: Oh, and to make the code work, I had to add:
Code: [Select]
int dev = 0x5A<<1;
before the first time it was called in the setup(), And a second time after the setup before the loop()

Go Up