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
}


Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s