Temperature sensor with Nano

Hello, I’ve used a Nano to build a 7-segment display that shows the temperature from a sensor, and I’m looking for a little help. I’ve got it mostly working, the temperature is read and displayed correctly, but the display is flickering. I’ve been able to use the display on its own to show numbers without flicker, so I’m suspecting its one of the temperature functions (read, value lookup, etc) taking long enough to cause the display to lag? I tried adding a delay timer to reduce the number of temperature reads, which sort of works, now it flickers about once/sec and is steady otherwise.

Any thoughts/suggestions? Is the Nano not powerful enough to do what I’m trying to do?

video: Imgur: The magic of the Internet

#include "SevSeg.h"
#include <DallasTemperature.h>
#include <OneWire.h>

int temp_sensor = 5; // Temp sensor on pin 5
float temperature = 0; // initialize temperature variable
int timer = 10000; // Timer to delay temp check by ~1sec
SevSeg sevseg; //Instantiate a seven segment controller object
OneWire oneWirePin(temp_sensor);
DallasTemperature sensors(&oneWirePin);

void setup(void){
  sensors.begin();
  
  byte numDigits = 2; // Number of digits in 7-seg display
  byte digitPins[] = {2, 3}; // digit pins from left to right
  byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12}; // segments in order: a,b,c,d,e,f,g
  bool resistorsOnSegments = false; // 'false' means resistors are on digit pins
  byte hardwareConfig = COMMON_ANODE; // See README.md for options
  bool updateWithDelays = false; // Default 'false' is Recommended
  bool leadingZeros = true; // Use 'true' if you'd like to keep the leading zeros
  bool disableDecPoint = true; // Use 'true' if your decimal point doesn't exist or isn't connected
  
  sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint);
  sevseg.setBrightness(90);
}

void loop(){
  sensors.requestTemperatures(); // read sensor
  temperature = sensors.getTempFByIndex(0); // convert sensor data to degrees F
  
  while (timer > 0) { // delay temp check for 1 sec
    timer -= 1;
    sevseg.setNumber(temperature, 1); // set number to be drawn on display
    sevseg.refreshDisplay(); // Must run repeatedly
  }
  timer = 10000; // reset countdown
}

Try this and see if it's better:

#include "SevSeg.h"
#include <DallasTemperature.h>
#include <OneWire.h>

int temp_sensor = 5;    // Temp sensor on pin 5
float temperature = 0;  // initialize temperature variable
int timer = 10000;      // Timer to delay temp check by ~1sec
SevSeg sevseg;          //Instantiate a seven segment controller object
OneWire oneWirePin(temp_sensor);
DallasTemperature sensors(&oneWirePin);

void setup(void) {
   sensors.begin();

   byte numDigits = 2;                               // Number of digits in 7-seg display
   byte digitPins[] = { 2, 3 };                      // digit pins from left to right
   byte segmentPins[] = { 6, 7, 8, 9, 10, 11, 12 };  // segments in order: a,b,c,d,e,f,g
   bool resistorsOnSegments = false;                 // 'false' means resistors are on digit pins
   byte hardwareConfig = COMMON_ANODE;               // See README.md for options
   bool updateWithDelays = false;                    // Default 'false' is Recommended
   bool leadingZeros = true;                         // Use 'true' if you'd like to keep the leading zeros
   bool disableDecPoint = true;                      // Use 'true' if your decimal point doesn't exist or isn't connected

   sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint);
   sevseg.setBrightness(90);
}

unsigned long lastUpdate = 0;

void loop() {
   unsigned long now = millis();
   
   if( now - lastUpdate >= 1000 ) {   
      sensors.requestTemperatures();             // read sensor
      temperature = sensors.getTempFByIndex(0);  // convert sensor data to degrees F
      sevseg.setNumber(temperature, 1);  // set number to be drawn on display
      lastUpdate = now;
   }
   sevseg.refreshDisplay();
}

Wow thanks for the fast reply! No change unfortunately

Looking at the header for DallasTemperature I noticed the following:

	// Get temperature for device index (slow)
	float getTempFByIndex(uint8_t);

Perhaps it's worth investigating if getTempF with the device's address might be quicker.

If your approach doesn't need constant temp readings, set the interval to one minute for example.
if( now - lastUpdate >= 60000 ) {

Yea a long delay is my backup plan, I’m just using this to display the temp of an aquarium, I just wanted to see if there was just something I was doing wrong first. Out of curiosity, would a different model Arduino have a faster processor?

It's just how this display works. Get non-multiplexed display instead.

Whatever your problem might be, it doesn't lie in the Nano.

Update display only when data changes.

  if (oldData != newData) {
     oldData = newData;
     updateDisplay(newData);
  }

So maybe a silly question, how would you compare the data in the sensor object without using the ‘getTempFByIndex’? Arduino is complaining about my ‘if’ statement:

“no match for 'operator!=' (operand types are 'DallasTemperature' and 'DallasTemperature')”

I guess you can’t compare the objects directly?

#include "SevSeg.h"
#include <DallasTemperature.h>
#include <OneWire.h>

int temp_sensor = 5;    // Temp sensor on pin 5
float temperature = 0;  // initialize temperature variable
int timer = 10000;      // Timer to delay temp check by ~1sec
SevSeg sevseg;          //Instantiate a seven segment controller object
OneWire oneWirePin(temp_sensor);
DallasTemperature sensor(&oneWirePin); // Sensor object
DallasTemperature oldsensor(&oneWirePin); // Sensor "old data" storage object

void setup(void) {
   sensor.begin();

   byte numDigits = 2;                               // Number of digits in 7-seg display
   byte digitPins[] = { 2, 3 };                      // digit pins from left to right
   byte segmentPins[] = { 6, 7, 8, 9, 10, 11, 12 };  // segments in order: a,b,c,d,e,f,g
   bool resistorsOnSegments = false;                 // 'false' means resistors are on digit pins
   byte hardwareConfig = COMMON_ANODE;               // See README.md for options
   bool updateWithDelays = false;                    // Default 'false' is Recommended
   bool leadingZeros = true;                         // Use 'true' if you'd like to keep the leading zeros
   bool disableDecPoint = true;                      // Use 'true' if your decimal point doesn't exist or isn't connected

   sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint);
   sevseg.setBrightness(90);
}

unsigned long lastUpdate = 0;

void loop() {
  unsigned long now = millis();
  
  if( now - lastUpdate >= 1000 ) {    // read sensor once every 1sec (1000ms) to work around flicker when temp is updated
    oldsensor = sensor; // backup sensor data
    sensor.requestTemperatures();     // read sensor
    if (oldsensor != sensor) { // if new data is different
      temperature = sensor.getTempFByIndex(0);  // convert sensor data to degrees F
      sevseg.setNumber(temperature, 1);  // set number to be drawn on display
    }   
    lastUpdate = now;
  }
  sevseg.refreshDisplay(); // refresh 7seg display
}

You can reduce the blanked time by switching to async operation (look at sensors.setWaitForConversion(false); and everything that goes with that). Reduce, but not eliminate. There's still a little flicker left.

A better solution might be to ditch SevSeg and look for a library that does the digit multiplexing in an interrupt. But that has potential conflicts with the OneWire library disabling interrupts at critical junctures.

An even better solution is probably to do things in hardware with a TM1637 or MAX7219 module.

Or use a couple of discrete digits and a shift register for the segments (74HC595?).

Or switch to a different temperature sensor that is quicker to read. TMP36/7 on an analog pin? DHT20 on I2C?

Food for thought.

Ok, snagged a pack of TMP36’s, let’s see if reading these on an analog pin is any faster. If it works, I guess I can just coat it in epoxy to waterproof it

I see you made two instances of DallasTemperature.

Use just one instance. Read the temperature. Compare oldTemp (which is "random" on the first compare) to newTemp. If different, save newTemp to oldTemp and update display.

float oldTemp, newTemp;
void setup() {
  // setup stuff
}
void loop() {
  // loop stuff
  .
  .
    sensor.requestTemperatures();         // read sensor
    newTemp = sensor.getTempFByIndex(0);  // convert sensor data to degrees F
    if (oldTemp != newTemp) {             // if new data is different
      oldTemp = newTemp; // store newTemp
      sevseg.setNumber(newTemp, 1);  // set number to be drawn on display
    }
}

So it does run, but still flickering unfortunately. At this point I suspect Van is correct and the lag is coming from either reading the sensor data or the index lookup, so switching out the sensor (or adding hardware to control the display) is probably the only solution

Even going with the hardware address and async the display still flickers a little (I tried it). It just takes too long to read the one wire sensor for POV to still work.

The display flickers without calling for a display refresh?

Yea, still causes flicker

#include "SevSeg.h"
#include <DallasTemperature.h>
#include <OneWire.h>

int temp_sensor = 5;    // Temp sensor on pin 5
float temperature = 0;  // initialize temperature variable
int timer = 10000;      // Timer to delay temp check by ~1sec
SevSeg sevseg;          //Instantiate a seven segment controller object
OneWire oneWirePin(temp_sensor);
DallasTemperature sensor(&oneWirePin); // Sensor object
// DallasTemperature oldsensor(&oneWirePin); // Sensor "old data" storage object

float oldTemp, newTemp;
void setup(void) {
   sensor.begin();

   byte numDigits = 2;                               // Number of digits in 7-seg display
   byte digitPins[] = { 2, 3 };                      // digit pins from left to right
   byte segmentPins[] = { 6, 7, 8, 9, 10, 11, 12 };  // segments in order: a,b,c,d,e,f,g
   bool resistorsOnSegments = false;                 // 'false' means resistors are on digit pins
   byte hardwareConfig = COMMON_ANODE;               // See README.md for options
   bool updateWithDelays = false;                    // Default 'false' is Recommended
   bool leadingZeros = true;                         // Use 'true' if you'd like to keep the leading zeros
   bool disableDecPoint = true;                      // Use 'true' if your decimal point doesn't exist or isn't connected

   sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint);
   sevseg.setBrightness(90);
}

unsigned long lastUpdate = 0;

void loop() {
  unsigned long now = millis();
  
  if( now - lastUpdate >= 1000 ) {    // read sensor once every 1sec (1000ms) to work around flicker when temp is updated
    sensor.requestTemperatures();         // read sensor
    newTemp = sensor.getTempFByIndex(0);  // convert sensor data to degrees F
    if (oldTemp != newTemp) {             // if new data is different
      oldTemp = newTemp; // store newTemp
      sevseg.setNumber(newTemp, 1);  // set number to be drawn on display
    }
    lastUpdate = now;
  }
  sevseg.refreshDisplay(); // refresh 7seg display
}

Does it flicker every 1000ms?

Yes, once every however long I set the delay for

While the code's off doing this, it can't be keeping the display refreshed. And with the default 9 bits it takes 94 ms to get the temperature. More than long enough to see.