Thermometer - 7segment, 1-wire, shift register, multiplex, wireless

Hi,

I’m starting new “project”. I want to build thermometer, maybe even weather station in future.

What it should do:

  • show temperature + sensor number
  • maybe PWM regulation of brightness (to fit ambient light)
    (- work on 433MHz wireless with sensors powered by solars - in future, not now)

How it should look:

  • Arduino (will buy mini/nano; have Duemilanove right now)
  • Dallas temperature sensors (1-wire bus)
  • 4x 7 segment LED display (so it can show ie. -12.4) + 2x 7 segment LED display for sensor number
  • powered with USB cable
  • sensors connected through some common connector (like computer fan, RJ11, …). Sensors should be easy to add

Problems:

  1. How to drive those 7 segment displays? I don’t like idea of 6 shift registers. How to use shift registers to multiplex?
  2. Is arduino (mini/nano, Duemilanove) fast enough to cover 6x 7 segment displays without visible flickering? (provided that code is written well, no delays and so on)
  3. Flickering, 1x per second, probably because of conversion in OneWire code that takes some time.
  4. How to get sensors count on OneWire bus? ANd how to advance to next one?

Answers to problems / Ideas:

  1. Use one shift register to drive 1x 7 segment + one shift register to drive common cathodes/anodes of displays. Arduino will shift-out 7 segment number + info on which 7 segment display it should be displayed.
  2. Arduino is fast enough, but 74HC595 shift registers need some delay and after adding this to code, numbers are dimmer and start to flicker a little. I will try to drive 2x 3 digits instead of 1x 6. This should half the time before it loops meaning less flicker and better brightness.
  3. Any ideas to this one?
  4. I can probably find this on my own… ?

Questions will probably pop-up during building this. I will ask them in reply and add them to this OP.

I would like to make it on my own, not by copying finished design by someone else.

Current state:
Adapted OneWire example code to get temperatures and displaying them. Two sensors right now.

Next goal:
Try to understand what OneWirecode does :slight_smile:

What to do after that:
Optimise whole sketch because there is flickering, possibly caused by computing OneWire variables (which taks some time). Also one temperature stays twice as long as the other, probably OneWire code problem of some sort.

Code so far:

#include <OneWire.h>

int latch_pin = 11;
int data_pin = 12;
int clk_pin = 10;
unsigned long time;
unsigned long time_temp;
unsigned long time_conversion;
int time_temp_delay = 1500; // How long before changing displayed temperature to another one?
int time_conversion_delay = 1000; // delay to let Dallas sensors do their work
int conversion = 1; // used to swith between initiating and reading in OneWire code

byte i;
byte present = 0;
byte type_s;
byte data[12];
byte addr[8];
float celsius, fahrenheit;
OneWire sensors(9);

// float temp_examples[6] = {15.4, 22.2, -3.6, -12.9, 0.7, -0.8}; // some examples to see how displays work
float actual_temp;
int actual_sensor = 0;

const byte numbers_lsb[] = {
  B11111100, // 0
  B01100000, // 1
  B11011010, // 2
  B11110010, // 3
  B01100110, // 4
  B10110110, // 5
  B10111110, // 6
  B11100000, // 7
  B11111110, // 8
  B11110110, // 9
  B00000000, // nothing
  B00000010  // minus/negative sign
};

const byte numbers_dot_lsb[] = {
  B11111101, // 0.
  B01100001, // 1.
  B11011011, // 2.
  B11110011, // 3.
  B01100111, // 4.
  B10110111, // 5.
  B10111111, // 6.
  B11100001, // 7.
  B11111111, // 8.
  B11110111, // 9.
};

const byte displays_lsb[] = {
  B10001000,
  B01000100,
  B00101000,
  B00010100,
  B11111100
};

void setup() {
  pinMode(latch_pin, OUTPUT);
  pinMode(data_pin, OUTPUT);
  pinMode(clk_pin, OUTPUT);
  
  time = millis();
  time_temp = time;
}

void loop() {    
  time = millis();
  
  get_temps();
  
  // This part is doing nothing at the moment
  if (time - time_temp >= time_temp_delay) { // rotate temperatures
    //actual_temp = temp_examples[actual_sensor];
    if (actual_sensor == sizeof(temp_examples)/sizeof(temp_examples[0])-1) { // go back to start
      actual_sensor = 0;
    }
    else {
      actual_sensor++;
    }
    time_temp = time;
  }
  
  display_out(actual_temp, actual_sensor); // displays output on 7 segment LED displays
}

void display_out(float value, int number) { // operates 7 segment displays
  int digits[5];
  digits[0] = abs(((int)value/10)%10); // parses digit (tens) from temperature
  digits[1] = abs(((int)value/1)%10); // parses digit (ones) from temperature
  digits[2] = abs((int)(value/0.1)%10); // parses digit (tenths) from temperature
  digits[3] = (number/10)%10; // parses digit (tens) from sensors number
  digits[4] = (number/1)%10; // parses digit (ones) from sensors number
  for (int i = 0; i < 4; i++) { // does 4 runs (because I have 4 digit multiplexing LED display
    digitalWrite(latch_pin, LOW);
    // Shifts out one digit of sensors umber
    if (i == 0 || i == 2) {
      shiftOut(data_pin, clk_pin, LSBFIRST, numbers_lsb[digits[4]]); // Outputs one digit of sensor number 1/4 and 3/4 of a loop
    }
    else {
      shiftOut(data_pin, clk_pin, LSBFIRST, numbers_lsb[digits[5]]); // Outputs one digit of sensor 2/4 and 4/4 of a loop
    }
    // Shifts out one digit of temperature (including negative sign)
    if (i == 0) { // writing on the first display (negative sign)
      if (value < 0) {
        shiftOut(data_pin, clk_pin, LSBFIRST, numbers_lsb[11]); // outputs negative sign
      }
      else {
        shiftOut(data_pin, clk_pin, LSBFIRST, numbers_lsb[10]); // outputs "nothing" (void display)
      }
    }
    else if (i == 1 && digits[i-1] == 0) { // writing on the second display (tens; if there are none, outputs nothing)
      shiftOut(data_pin, clk_pin, LSBFIRST, numbers_lsb[10]);
    }
    else if (i == 2) { // writing on the third display, where decimal point is needed
      shiftOut(data_pin, clk_pin, LSBFIRST, numbers_dot_lsb[digits[i-1]]);
    }
    else { // writing on "other" displays (in fact fourth display AND seecond display only if there are some tens)
      shiftOut(data_pin, clk_pin, LSBFIRST, numbers_lsb[digits[i-1]]);
    }
    // Shifts out which digits should be shown in this run through loop
    shiftOut(data_pin, clk_pin, LSBFIRST, displays_lsb[i]);
    delay(2);
    digitalWrite(latch_pin, HIGH);
  }
}

float get_temps() {
  if (time - time_conversion >= time_conversion_delay && conversion == 1) {
    if (!sensors.search(addr)) {
      sensors.reset_search();
      //delay(250);
    }
    // the first ROM byte indicates which chip
    switch (addr[0]) {
      case 0x10:
        type_s = 1;
        break;
      case 0x28:
        type_s = 0;
        break;
      case 0x22:
        type_s = 0;
        break;
      default:
        break;
    } 
    sensors.reset();
    sensors.select(addr);
    sensors.write(0x44);        // start conversion, no parasite power
    time_conversion = time;
    conversion = 0;
  }
  //delay(1000);     // maybe 750ms is enough, maybe not
  
  if (time - time_conversion >= time_conversion_delay && conversion == 0) {
    present = sensors.reset();
    sensors.select(addr);    
    sensors.write(0xBE);         // Read Scratchpad

    for ( i = 0; i < 9; i++) {           // we need 9 bytes
      data[i] = sensors.read();
    }

    // Convert the data to actual temperature
    // because the result is a 16 bit signed integer, it should
    // be stored to an "int16_t" type, which is always 16 bits
    // even when compiled on a 32 bit processor.
    int16_t raw = (data[1] << 8) | data[0];
    if (type_s) {
      raw = raw << 3; // 9 bit resolution default
      if (data[7] == 0x10) {
        // "count remain" gives full 12 bit resolution
        raw = (raw & 0xFFF0) + 12 - data[6];
      }
    }
    else {
      byte cfg = (data[4] & 0x60);
      // at lower res, the low bits are undefined, so let's zero them
      if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
      else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
      else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
      //// default is 12 bit resolution, 750 ms conversion time
    }
    time_conversion = time;
    celsius = (float)raw / 16.0;
    //fahrenheit = celsius * 1.8 + 32.0;
    actual_temp = celsius;
    conversion = 1;
  }
}

I have multiplexed 2x 7 segment display yesterday. The code is written for all six 7 segment displays, so I'm multiplexing all six at once but only 2 are visible (I don't have 6 at the moment).

Problem has arisen while doing this.

I need some delay to let shift registers "move". I look like 2 to 5 ms are required so that I don't see any "fade" on displays. If I add this delay, flickering starts to occur and brightness goes down (rapidly). I'm thinking about sing three 74HC595 registers. One for 3 displays, second for another 3 displays and third to trigger which two displays should work at one time. This method should reduce flickering and raise brightness, because I would be doing the loop on 2x 3 diplays at once, not 1x 6 displays like I did so far. I will probably add some basic schematics ~8 hours from posting this comment.

I'm gonna buy 4 digits display today so I can see more output. So far I'm using preset numbers, I will add OneWire code later.

I have added OneWire code to get actual temperatures. I’m using two sensors at the moment. I can see temperatures on 4 digit LED display, but one stays twice as long as the other, probably something in OneWire code. I will need to understand it.

There is also flickering, which is probably caused by conversion of OneWire variables. The same applies here - I have to understand it. Maybe redo my code for displaying output, so that Arduino first switches off all displays, then does conversion, then switches them on again. All done in ~100ms. User should notice it only like switching from one temperature to other. I will do video of this, so you can see it.

I have added my code so far in the OP. There are also two more questions/problems. I still think that flickering could be reduced by "display-off" before conversion. I should probably snychronize switching temperatures displayed and converting OneWIre.

What do you think?