If you are lucky

then it might survive the criticism in the living room, but maybe I have to find a different plug, like a rectangular one.

img_1442

 

So today it happened, this creation had to leave our living room, the acceptance window was 2 days.

Advertisements

Seven segment LED vintage component IR thermometer

Bringing 4 seven segment LEDs back to life, think I got them when I was 18. There are just enough digital lines on an uno to multiplex four of them, the repeat cycle is 4*8 = 32 milliseconds, this thing draws around 70 milliamps, the thermopile doesn’t draw more than 2 to 3 milliamps, but is it the LED display and the arduino that take the lead here. The thermopile is good in picking up your presence up to about 3 meter away.

Multiplexing is a simple technique to reduce power consumption, rather than turning all LEDs on all the time (all my pre 1990 projects did this because it was too difficult to implement without a microprocessor) you simply cycle one digit after another every so many milliseconds (here 5 milliseconds). You will still see all 4 digits at the same time because of the latency of your eyes. Beyond a refresh rate of approximately 15 images per second your brain starts to interpolate, and this is how sequences of images become movies. A phenomenon called aliasing could occur when you film multiplexing LED displays, and that is seen in this movie that I found on youtube:

If you’re interested in rebuilding this gadget then here is the source code. The LED schematics is explained here, I pretty much followed this except that I use different transistor and resistors. There are also maxim IC that does the LED or dot matrix translation for you, this movie shows how to do that with an atmega 2560 from #arduino (It wasn’t fun soldering all those LEDs, but you can buy the matrices of course). The maxim IC is however hard to get, and a simple atmel 328p is cheaper in my opinion. But I must admit that maxim did some fine engineering on the brightness control.

The sketch is here, updated it on 19-10-2014 to improve the stability of the displayed numbers, that goes now through sprintf and decoding the output, also I compute averages in 300 milliseconds. And, if there is a dot in the LED matrix then why no simply use it?

// reads out the MLX90614 infrared thermometer

#include <Arduino.h>
#define SDA_PORT PORTC   // this maps to analog ports
#define SDA_PIN 4
#define SCL_PORT PORTC   // this maps to analog ports
#define SCL_PIN 5
#include <SoftI2CMaster.h>
#define DEVICE (0x5A<<1)

const int digit1 = 2;  // assigns the digits (1 is leftmost)
const int digit2 = 3;
const int digit3 = 4;
const int digit4 = 5;
const int segA = 6;    // assigns the segments
const int segB = 7;
const int segC = 8;
const int segD = 9;
const int segE = 10;
const int segF = 11;
const int segG = 12;
const int segH = 13;

int cc;
unsigned long int ctimer,ptimer;
int inumber;
float numerator,denominator;

void BCDWrite( int code, int dot ) // translated codes into LEDs to turn on, dot is an extra
{
  byte bits=0;  
  switch (code) {
    case  0: { bits = B01111110;  break; }
    case  1: { bits = B00010010;  break; }
    case  2: { bits = B10111100;  break; }
    case  3: { bits = B10110110;  break; }
    case  4: { bits = B11010010;  break; }
    case  5: { bits = B11100110;  break; }
    case  6: { bits = B11101110;  break; }
    case  7: { bits = B00110010;  break; }
    case  8: { bits = B11111110;  break; }
    case  9: { bits = B11110110;  break; }
    case 10: { bits = B10000000;  break; }
    case -1: { bits = B00000000;  break; }
  }
  if (dot == 1) bits = bits | B00000001;
  for (int seg=0; seg<8; seg++) {
    if (bitRead(bits,seg) == 1) {
      digitalWrite(segA+seg,HIGH);
    }  
  }
}

void BCDClear()
{
  for (int seg=0; seg<8; seg++) {
    digitalWrite(segA+seg,LOW);
  }
}

//--------------------------------------------------------------------------------------------------

void ShowNumber( int input )  // a way to convert number to LED sequences
{
  int dly = 5;
  char buff[20];
  if ((cc > 9999) || (cc < -999)) {
    sprintf(buff,"----");
  } else {
    if (abs(input) > 10) {
      sprintf(buff,"%4d",input );
    } else {
      if (input >= 0) {
        sprintf(buff,"  %2.2d",input ); 
      } else {
        sprintf(buff," %2.2d",input ); 
      }
    } 
  }
  int len = strlen(buff);
  //Serial.println(buff);
  for (int i=0; i<len; i++) {
    int digit = 0;
    switch (buff[i]) {
      case ' ':  { digit = -1; break; }
      case '0':  { digit =  0; break; }
      case '1':  { digit =  1; break; }
      case '2':  { digit =  2; break; }
      case '3':  { digit =  3; break; }
      case '4':  { digit =  4; break; }
      case '5':  { digit =  5; break; }
      case '6':  { digit =  6; break; }
      case '7':  { digit =  7; break; }
      case '8':  { digit =  8; break; }
      case '9':  { digit =  9; break; }
      case '-':  { digit = 10; break; }
      default:   { digit = -1; break; }      
    }
    BCDClear(); 
    digitalWrite(digit1+i,HIGH);
    int dot = 0; if (i==2) dot = 1; BCDWrite( digit,dot ); 
    delay(dly); 
    digitalWrite(digit1+i,LOW);
  }
}

uint8_t GetEmissivity( uint16_t &result, uint8_t *buf ) // just get the emissivity correction in case
{
  uint8_t data_low = 0;
  uint8_t data_high = 0;
  uint8_t data_pec = 0;
  //
  if (!i2c_start(DEVICE+I2C_WRITE)) return false;
  i2c_write(0x24);                // emissivity correction stored in EEPROM
  i2c_rep_start(DEVICE+I2C_READ);
  data_low = i2c_read(false);     // write low byte and then send ack
  data_high = i2c_read(false);    // write high byte and then send ack
  data_pec = i2c_read(true);          // write pec byte and terminate
  i2c_stop();
  result = ((data_high << 8) + data_low);
  buf[0] = data_low; buf[1] = data_high; buf[2] = data_pec;
  return true;
}

//----------------------------------------------------------------------------------------------------------------------

float GetTemp(uint8_t which) // see earlier projects
{
  //
  // Get the object temperature here
  //
  int dev = DEVICE;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;    
  i2c_start(dev+I2C_WRITE);
  if (which == 0) i2c_write(0x07); // read (for the object temperature)
  if (which == 1) i2c_write(0x06); // read (for the die temperature)
  i2c_rep_start(dev+I2C_READ);
  data_low = i2c_read(false); //Read 1 byte and then send ack
  data_high = i2c_read(false); //Read 1 byte and then send ack
  pec = i2c_read(true);
  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 celsius = tempData - 273.15;
  return celsius;
}

void setup()  // essentially assign everything, and print the emissivity correction once
{
  Serial.begin(115200);
  i2c_init();
  Serial.println("Hi there");
  pinMode( digit1, OUTPUT );
  pinMode( digit2, OUTPUT );
  pinMode( digit3, OUTPUT );
  pinMode( digit4, OUTPUT );
  pinMode( segA, OUTPUT );
  pinMode( segB, OUTPUT );
  pinMode( segC, OUTPUT );
  pinMode( segD, OUTPUT );
  pinMode( segE, OUTPUT );
  pinMode( segF, OUTPUT );
  pinMode( segG, OUTPUT );
  pinMode( segH, OUTPUT );
  cc = 0; ctimer = 0; ptimer = 0;
  uint16_t result; uint8_t buf[5];
  if (GetEmissivity(result,buf)) {
    Serial.print("E1 = "); Serial.print(result,HEX);
    Serial.print(" : ");  Serial.print(buf[0],HEX);
    Serial.print(" ");    Serial.print(buf[1],HEX);
    Serial.print(" ");    Serial.println(buf[2],HEX);
  }
  float temp = GetTemp(0);       // not sure how long this takes, probably 10 msec
  inumber = round(temp * 10.0);  // so that it shows something the first time
  numerator = 0;
  denominator = 0;
  //
  // but does it work? the inevitable question, yes it works I thought
  //
  //for (int number = -199; number < 350; number = number + 1) {
  //  //Serial.println(number);
  //  for (int k=0; k<2; k++) {
  //    ShowNumber(number);
  //  }
  //}
}

void loop() // this does the multiplexing
{
  ctimer = millis();
  if (ctimer < ptimer) ptimer = ctimer;
  long timer = ctimer - ptimer;
  if (timer > 75) {  // every one fifth of a second we change the inumber
    cc++; 
    ptimer = millis();
    float temp = GetTemp(0); // not sure how long this takes, probably 10 msec
    int jnumber = round(temp * 10.0);
    numerator = numerator + jnumber; 
    denominator = denominator + 1;
    if ((cc % 4) == 0) {
      float average = float(numerator) / float(denominator);
      inumber = round(average);
      numerator = 0;
      denominator = 0;
      cc = 0; // since it has no use to count on and on
    }
  }
  ShowNumber(inumber); // this takes 4 times dly milliseconds, dly=5 so it is 20 milliseconds
}


A wireless non-contact thermometer

So far I showed that NTC’s measure the contact temperature and that you can easily integrate them into an Moteino project. In this blog I want to show that temperatures can also be measured with a non-contact device, called a thermopile. A thermopile is a sensor that converts heat into a tiny voltage, the measurement is based on the Seebeck effect that is documented here.

The main advantage of non-contact thermometers is that one measures an integrated temperature directly, and that no contact is required between the detector and the object. The detector has an opening angle, for this project I’ve chosen a low cost thermopile with an angle of 90 degrees. More expensive thermopiles include optics consisting of Germanium lenses so that the detector area can be reduced down to as little as 5 degrees.

The relation between temperature and long-wave infrared radiation from an object is described by the Stephan Boltzmann law which says that the heat energy received is proportional to detector area times sigma times T to the power four where T is the temperature of black body, and where sigma is a fundamental constant. When the flux is measured you can back out T but only if one compensates for the self-temperature of the detector and shape factors.

Although the latency of the thermopile is short, the actual latency of the measurement is affected by the NTC which measures the detector self-temperature. As a result it can take a couple of minutes when there are extreme changes in the ambient temperature of the detector. IR thermometers are best used away from heat components, and in case of changes in the ambient temperature you should allow some time for them to adjust.

Melexis makes a low-cost IC called the 90614 which does all this for you, and it comes in a TO-39 case. It can either run as an I2C device or a PWM device. I have chosen for the first option and ordered a couple of them through futurelectronics. They are easy to integrate in a project, the 90614 is calibrated in front of a black body radiator and assumes an emissivity of 1.

The schematic below shows how you can integrate the thermopile in your microprocessor project. The detector including the 100nF and two 4k7 resistors are best placed on a separate PCB in my opinion. The window of the detector should not be covered or touched; in fact, it should stay as clean as possible. If you decide to protect the detector with a piece of thin plastic then a calibration procedure should be followed to determine an emissivity correction factor, which you either upload in the 90614’s eeprom, or you apply it in your own software. See also the attached source code.

The only drawback so far I’ve encountered so far is that it is hard to put the 90614 in sleep mode, so there is a constant current drain of about 1,5 milliamp. This is the driving factor for the battery consumption at this moment, at 1,5 mA the 800mAh battery live for about a month, maybe I should revise this at some point in the future.

inside-box

Temperature record in the living room over the last three days.
Temperature record in the living room over the last three days.

schematic

sensorhead

to39

Source code

I gathered the arduino code from various sources on the internet, the CRC calculation is messy but it works I believe. The only reason to use i2c master is that it allows for repeated starts which you can’t do with the wire library. It is all explained on the Arduino forum. Still it took me a weekend or so to comprehend this all.

// reads out the MLX90614 infrared thermometer

#include <Arduino.h>
#define SDA_PORT PORTC   // this maps to analog ports
#define SDA_PIN 4
#define SCL_PORT PORTC   // this maps to analog ports
#define SCL_PIN 5
#include <SoftI2CMaster.h>
#define DEVICE (0x5A<<1)
#include <RFM69.h>
#include <SPI.h>
#include <SPIFlash.h>
#include <Wire.h>
#include <LowPower.h>
//
#define NODEID        94                 // unique for each node on same network
#define NETWORKID     50                 // the same on all nodes that talk to each other
#define GATEWAYID     1
#define FREQUENCY     RF69_868MHZ
#define ENCRYPTKEY    "thisIsEncryptKey" // exactly the same 16 characters/bytes on all nodes!
#define ACK_TIME      30                 // max # of ms to wait for an ack
//
// Leave this in here
//
#ifdef __AVR_ATmega1284P__
  #define LED           15 // Moteino MEGAs have LEDs on D15
  #define FLASH_SS      23 // and FLASH SS on D23
#else
  #define LED           9 // Moteinos have LEDs on D9
  #define FLASH_SS      8 // and FLASH SS on D8
#endif

#define SERIAL_BAUD   115200

#define BEEPER 3  // give a short beep after power on

char buff[61];
byte sendSize=0;
boolean requestACK = false;
SPIFlash flash(FLASH_SS, 0xEF30); //EF30 for 4mbit  Windbond chip (W25X40CL)
RFM69 radio;

//-----------------------------------------------------------------------------------------------------------------------

long int cc;
float numerator,denominator,minimum,maximum,average;
const int nsamples0 = 30;  // take  30 samples every 2 seconds, then we radio the data back (so every minute)
const int nsamples1 = 150; // take 150 samples every 2 seconds, then we radio the data back (every 5 minutes)
const long int maxsec = 300;
const int stepsize = 0;    // not relevant at this (see code)
unsigned long int ctimer,ptimer;
long int sec;
float celsius;
int nsamples;

//----------------------------------------------------------------------------------------------------------------------
//
// http://stackoverflow.com/questions/20554869/implementing-crc8-on-arduino-to-write-to-mlx90614
//
//----------------------------------------------------------------------------------------------------------------------

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;
}

//----------------------------------------------------------------------------------------------------------------------

uint8_t CRC8(const uint8_t *data, uint8_t len)         // computes crc value
{
  int i,j;
  int c;
  int CRC=0;
  int genPoly = 0x07;
  for (j=0; j<len; j++)
  {
    c = data[j];
    CRC ^= c;
    for(i = 0; i<8; i++)
        if(CRC & 0x80 )
          CRC = (CRC << 1) ^ genPoly;
        else
          CRC <<= 1;
    CRC &= 0xff;
  }
  return CRC;
}

//----------------------------------------------------------------------------------------------------------------------

void Blink(byte PIN, int DELAY_MS)
{
  pinMode(PIN, OUTPUT);
  digitalWrite(PIN,HIGH);
  delay(DELAY_MS);
  digitalWrite(PIN,LOW);
}

uint8_t ResetEmissivity() 
{
  uint8_t pec0; 
  //
  {
    uint8_t writeArray0[] = {DEVICE + I2C_WRITE, 0x20 + 0x04, 0, 0}; 
    i2c_start_wait(writeArray0[0]);
    i2c_write(writeArray0[1]); //Register Address to write to
    i2c_write(writeArray0[2]); //Erase low byte (write 0)
    i2c_write(writeArray0[3]); //Erase high byte (write 0)
    i2c_write(0xE8); //Send PEC
    i2c_stop(); 
  } delay(100); {
    uint8_t writeArray0[] = {DEVICE + I2C_WRITE, 0x20 + 0x04, 0xff, 0xff}; 
    pec0 = CRC8(writeArray0, 4);
    i2c_start_wait(writeArray0[0]);
    i2c_write(writeArray0[1]); //Register Address to write to
    i2c_write(writeArray0[2]); //Erase low byte (write 0)
    i2c_write(writeArray0[3]); //Erase high byte (write 0)
    i2c_write(0xCC); //Send PEC
    i2c_stop(); 
  } delay(100); 
}

//----------------------------------------------------------------------------------------------------------------------

uint8_t SetEmissivity( uint16_t setting, uint8_t *buf )
{
  uint8_t data_low = setting;
  uint8_t data_high = setting >> 8;
  buf[0] = 0x24; buf[1] = data_low; buf[2] = data_high; 
  uint8_t data_pec1 = CRC8(buf,3); // just avoid some space, in doubt compare crcr8 and CRC8
  uint8_t data_pec2 = CRC8(buf,3);
  //  
  buf[0] = data_low; buf[1] = data_high; buf[2] = data_pec1;  buf[3] = data_pec2;
  //
  // first erase it 
  //
  uint8_t writeArray0[4] = {DEVICE + I2C_WRITE, 0x20 + 0x04, 0, 0};
  uint8_t pec0 = CRC8(writeArray0, 4); 
  i2c_start_wait(writeArray0[0]);
  i2c_write(writeArray0[1]); //Register Address to write to
  i2c_write(writeArray0[2]); //Erase low byte (write 0)
  i2c_write(writeArray0[3]); //Erase high byte (write 0)
  i2c_write(pec0); //Send PEC
  i2c_stop(); 
  //
  // then write it
  //
  uint8_t writeArray1[4] = {DEVICE + I2C_WRITE, 0x20 + 0x04, data_low, data_high};
  uint8_t pec1 = CRC8(writeArray1, 4); 
  i2c_start_wait(writeArray1[0]);
  i2c_write(writeArray1[1]); //Register Address to write to
  i2c_write(writeArray1[2]); //Erase low byte (write 0)
  i2c_write(writeArray1[3]); //Erase high byte (write 0)
  i2c_write(pec1); //Send PEC
  i2c_stop();   
  return true;
}

//----------------------------------------------------------------------------------------------------------------------

uint8_t GetEmissivity( uint16_t &result, uint8_t *buf )
{
  uint8_t data_low = 0;
  uint8_t data_high = 0;
  uint8_t data_pec = 0;
  //
  if (!i2c_start(DEVICE+I2C_WRITE)) return false;
  i2c_write(0x24);                // emissivity correction stored in EEPROM
  i2c_rep_start(DEVICE+I2C_READ);
  data_low = i2c_read(false);     // write low byte and then send ack
  data_high = i2c_read(false);    // write high byte and then send ack
  data_pec = i2c_read(true);          // write pec byte and terminate
  i2c_stop();
  result = ((data_high << 8) + data_low);
  buf[0] = data_low; buf[1] = data_high; buf[2] = data_pec;
  return true;
}

//----------------------------------------------------------------------------------------------------------------------

void setup()
{
  Serial.begin(115200);
  i2c_init();
  //
  // initialize the radiomodem, and just say hello to the gateway
  //
  radio.initialize(FREQUENCY,NODEID,NETWORKID);
  radio.encrypt(ENCRYPTKEY);
  sprintf(buff, "\nTransmitting at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
  Serial.println(buff);
  if (flash.initialize()) {
    Serial.print("SPI Flash Init OK ... UniqueID (MAC): ");
    flash.readUniqueId();
    for (byte i=0;i<8;i++)
    {
      Serial.print(flash.UNIQUEID[i], HEX);
      Serial.print(' ');
    }
    Serial.println();
  } else {
    Serial.println("SPI Flash Init FAIL! (is chip present?)");
  }
  sprintf(buff,"IR thermometer");
  radio.sendWithRetry( GATEWAYID,buff,strlen(buff) );
  uint16_t value1,value2; uint8_t buf[10];
  //
  // If you need to reset/set/verify the emissivity correctionthen do that here
  //
  //Serial.println("Reset emissivity correction");
  //ResetEmissivity();
  //
  //value1 = 0xffff;
  //SetEmissivity(value1,buf);
  //
  // get the emissivity 
  //
  if (GetEmissivity(value1,buf)) {
    Serial.print("E1 = "); Serial.print(value1,HEX);
    Serial.print(" : ");  Serial.print(buf[0],HEX);
    Serial.print(" ");    Serial.print(buf[1],HEX);
    Serial.print(" ");    Serial.println(buf[2],HEX);
    sprintf(buff,"EMISSIVITY %4.4X %2.2X",value1,buf[2]);
      radio.sendWithRetry( GATEWAYID,buff,strlen(buff) );
  }
  // set the emissivity (becomes effective when you do a hard restart)
  /*
  double emissivity = 0.85; emissivity = emissivity * 65535.0;
  value2 = uint16_t(emissivity);
  if (SetEmissivity(value2,buf)) {
    Serial.print("E2 = "); Serial.print(value2,HEX);
    Serial.print(" : ");   Serial.print(buf[0],HEX);
    Serial.print(" ");     Serial.print(buf[1],HEX);
    Serial.print(" ");     Serial.print(buf[2],HEX);
    Serial.print(" (");    Serial.print(buf[3],HEX);  Serial.println(")");
  }
  */
  cc = 0;
  //
  // and beep that we are ready to start
  //
  pinMode(BEEPER, OUTPUT);
  digitalWrite(BEEPER,HIGH);
  delay(30);
  digitalWrite(BEEPER,LOW);
  //
  float celsius;
  long major1,minor1,major2,minor2;
  celsius = GetTemp(0); d2i( celsius,major1,minor1 );
  celsius = GetTemp(1); d2i( celsius,major2,minor2 );
  sprintf(buff,"TO %ld.%2.2ld TA %ld.%2.2ld LOOPS %ds/%ds",major1,minor1,major2,minor2,nsamples0*2,nsamples1*2);
  if (radio.sendWithRetry( GATEWAYID,buff,strlen(buff) ) ) {
    delay(60);
    digitalWrite(BEEPER,HIGH);
    delay(30);
    digitalWrite(BEEPER,LOW);
  }
}

//----------------------------------------------------------------------------------------------------------------------

void d2i( float CC, long &major0, long &minor0 ) 
{
  float copy = CC; copy = copy * 100.0; copy = abs(copy); long icopy = copy;
  major0 = icopy / 100L; 
  minor0 = icopy % 100L;
  if (CC < 0.0) major0 = -major0;
  // Serial.print(major0); Serial.print(" ");  Serial.println(minor0); 
}

//----------------------------------------------------------------------------------------------------------------------

float GetTemp(uint8_t which)
{
  //
  // Get the object temperature here
  //
  int dev = DEVICE;
  int data_low = 0;
  int data_high = 0;
  int pec = 0;    
  i2c_start(dev+I2C_WRITE);
  if (which == 0) i2c_write(0x07); // read (for the object temperature)
  if (which == 1) i2c_write(0x06); // read (for the die temperature)
  i2c_rep_start(dev+I2C_READ);
  data_low = i2c_read(false); //Read 1 byte and then send ack
  data_high = i2c_read(false); //Read 1 byte and then send ack
  pec = i2c_read(true);
  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 celsius = tempData - 273.15;
  return celsius;
}

//----------------------------------------------------------------------------------------------------------------------
    
void loop()
{   
  sec = cc * 2;
  if (sec < maxsec) {
    nsamples = nsamples0;
  } else {
    nsamples = nsamples1;
  }
  celsius = GetTemp(0); 
  if ((celsius > -20.0) && (celsius < +70.0)) { // cancel the most obvious errors
    if ((cc % nsamples) == 0 ) { 
      numerator = 0; denominator = 0; minimum = 99999; maximum = -minimum;
    }
    numerator += celsius; denominator += 1.0; 
    average = numerator / denominator; 
    if (celsius < minimum) minimum = celsius;  
    if (celsius > maximum) maximum = celsius;  
    if ((cc++ % nsamples) == (nsamples-1)) { 
      Blink( LED,10 ); 
      float myavera = average;
      float dietemp = GetTemp(1);
      long number1 = round(myavera * 100.0);
      long number2 = round(minimum * 100.0);
      long number3 = round(maximum * 100.0);
      long number4 = round(dietemp * 100.0);
      sprintf(buff,"TEMP %5ld %5ld %5ld %5ld %ld",number1,number2,number3,number4,sec);
      Serial.println(buff);
      radio.sendWithRetry( GATEWAYID,buff,strlen(buff) );
      ptimer = ctimer;
     }
  }
  LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF); 
}

Last update: 13-oct-2014