1

I am working on updating a project and I have a thread over in the Arduino forum which helped me out, but I'm a bit stuck, and there isn't much activity going on there.

This code is for use with a one wire temperature sensor, however, each time it retrieves data for the sensor, it has a 750ms delay as seen here:

ds.reset();
ds.select(addr);
ds.write(0x44, 1);        // start conversion, with parasite power on at the end

delay(750);     // maybe 750ms is enough, maybe not
// we might do a ds.depower() here, but the reset will take care of it.

present = ds.reset();
ds.select(addr);    
ds.write(0xBE);         // Read Scratchpad

Now, I'm going to be using a momentary button as a toggle, but for this I need to debounce it, using a delay. With the 750ms delay present for the temperature sensor, that just isn't feasible, it makes it too unpredictable and difficult to use.

I need to replace this delay with a millis counter, but I'm not sure if I can without hanging up the rest of the script. I gave it a try like so, but it completely screwed up the reading.

if (owcurrentMillis - owpreviousMillis >= owinterval) {
owpreviousMillis = owcurrentMillis;
present = ds.reset();
ds.select(addr);    
ds.write(0xBE);         // Read Scratchpad
}

I then tried it like so, and the readout does still work, however, I have an LED which lights up when the temperature has "Stabilized", which it's supposed to check every one second, but that has stopped working, and it's basically instant making it useless. I don't know why that millis loop does not work though, it was only working due to the 750ms delay.

if (owcurrentMillis - owpreviousMillis >= owinterval) {
owpreviousMillis = owcurrentMillis;
ds.reset();
ds.select(addr);
ds.write(0x44, 1);        // start conversion, with parasite power on at the end
}

Here is the bit of code controlling the LED illumination.

if (currentMillis - previousMillis >= interval) {  //If it has been 20 seconds, check to turn LED on
previousMillis = currentMillis;
if ((delta < 00.03) && (delta > -00.03)) {      //If temperature hasn't changed by +/- .05 turn led on.
  digitalWrite(A0, HIGH); 
}
}
if (delta >= 00.06) {                             //If temperature has changed by more than .1 turn LED off
digitalWrite(A0, LOW);
}
if (delta <= -00.06) {
digitalWrite(A0, LOW);                         // If temperature has changed by more than -.1 turn LED off
}

I think if I can get that one to work properly, instead of messing up like it is now I should be good, then I just need the 50ms delay to debounce the button which will trigger what I need, I just can't figure out why it will not work.

You can see the version which works best here (with the 750ms delay in place):

#include <OneWire.h>
#include <LiquidCrystal.h>
#include <SPI.h>
#include <SD.h>
const int chipSelect = 10;

OneWire  ds(9);                               //Onewire + lcd setup
LiquidCrystal lcd(6, 7, 5, 4, 3, 2);
float lasttemp;                              //Variable for last temperature
float delta;                                 //Variable for temperaturechange
long previousMillis = 0;
const long interval = 1000;                       //Check once every 20 seconds

const long sdinterval = 5000;
long sdpreviousMillis = 0;


File myFile;

void setup(void) {
  pinMode(A0, OUTPUT);
  pinMode(A3, OUTPUT);

  Serial.begin(115200);                       //Start serial + LCD/Light test
  lcd.begin(8, 2);
  lcd.setCursor(0,0);
  lcd.print("STARTING");
  lcd.setCursor(0, 1);
  lcd.print("VER. 1.2");
  digitalWrite(A0, HIGH);
  digitalWrite(A3, LOW);
  delay(1000);
  digitalWrite(A0, LOW);
  digitalWrite(A3, HIGH);
  lcd.clear();                                //End of LCD/Light test

  if (!SD.begin(10)) {                       // Check for SD card
    digitalWrite(A3, LOW);
    lcd.setCursor(0,0);
    lcd.print("SD CHECK");
    lcd.setCursor(0,1);
    lcd.print("  FAIL  ");
    delay(1000);
    lcd.clear();
    digitalWrite(A3, HIGH);
  } else {
    delay(1000);
    digitalWrite(A3, LOW);
    lcd.setCursor(0,0);
    lcd.print("SD CHECK");
    lcd.setCursor(0,1);
    lcd.print("  PASS  ");
    delay(1000);
    lcd.clear();
    digitalWrite(A3, HIGH);
  }
}



void loop(void) {

  unsigned long sdcurrentMillis = millis();
  unsigned long currentMillis = millis();
{
  }

  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;

  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }

  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

  if (OneWire::crc8(addr, 7) != addr[7]) {
    Serial.println("CRC is not valid!");
    return;
  }
  Serial.println();

  // the first ROM byte indicates which chip
  switch (addr[0]) {
  case 0x10:
    Serial.println("  Chip = DS18S20");  // or old DS1820
    type_s = 1;
    break;
  case 0x28:
    Serial.println("  Chip = DS18B20");
    type_s = 0;
    break;
  case 0x22:
    Serial.println("  Chip = DS1822");
    type_s = 0;
    break;
  default:
    Serial.println("Device is not a DS18x20 family device.");
    return;
  } 

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // start conversion, with parasite power on at the end

  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.

  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

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

  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");

  delta = celsius - lasttemp;                       //Create value to check how much temperature has changed

  if (currentMillis - previousMillis >= interval) {  //If it has been 20 seconds, check to turn LED on
    previousMillis = currentMillis;
    if ((delta < 00.03) && (delta > -00.03)) {      //If temperature hasn't changed by +/- .05 turn led on.
      digitalWrite(A0, HIGH); 
    }
  }
  if (delta >= 00.06) {                             //If temperature has changed by more than .1 turn LED off
    digitalWrite(A0, LOW);
  }
  if (delta <= -00.06) {
    digitalWrite(A0, LOW);                         // If temperature has changed by more than -.1 turn LED off
  }


    if (sdcurrentMillis - sdpreviousMillis >= sdinterval) {  //Write to SD card every 5 seconds.
    sdpreviousMillis = sdcurrentMillis;
    digitalWrite(A3, LOW);
    myFile = SD.open("Log.txt", FILE_WRITE);
    myFile.print(celsius);
    myFile.println(" C ");
    myFile.close();
    digitalWrite(A3, HIGH);
  }

  lcd.setCursor(0, 0);
  lcd.print(celsius);
  lcd.print(" C ");
  lcd.setCursor(0, 1);
  lcd.print(fahrenheit);
  lcd.print(" F ");
  lasttemp = celsius;
  }

and this is the one I'm working on as I write this, with the illumination delay messing up:

#include <OneWire.h>
#include <LiquidCrystal.h>
#include <SPI.h>
#include <SD.h>

unsigned long owpreviousMillis = 0;
const long owinterval = 750;
//---------------------------------------------------------------------------------- One wire temperature sensor stuff
OneWire  ds(9);                 //Setting one wire bus pin
byte i;                  
byte present = 0;
byte type_s;
byte data[12];
byte addr[8];
float celsius, fahrenheit;      //Variables for celsius and farenheit conversions
long previousMillis = 0;
float lasttemp;                 //Variable for last temperature
float delta;                    //Variable for temperaturechange
const long interval = 1000;     //Check once every 1 second

//---------------------------------------------------------------------------------- End of one wire temperature sensor stuff.

LiquidCrystal lcd(6, 7, 5, 4, 3, 2); //Defining pins for the LCD

//---------------------------------------------------------------------------------- SD card stuff
const int chipSelect = 10;              //Setting SD chip select pin (obviously)
File myFile;
long sdpreviousMillis = 0;
const long sdinterval = 5000;           //SD card write interval

//-----------------------------------------------------------------------------------End of pre-setup


void setup(void) {
  pinMode(A2, INPUT);
  pinMode(A0, OUTPUT);    //Green LED for temperature stability indication
  pinMode(A3, OUTPUT);    //Red LED for sd card activity indication
  Serial.begin(115200);
//------------------------- Testing LCD and LED's, displaying version number and checking for SD card.
  lcd.begin(8, 2);
  lcd.setCursor(0,0);
  lcd.print("STARTING");
  lcd.setCursor(0, 1);
  lcd.print("VER. 1.5");
  digitalWrite(A0, HIGH);
  digitalWrite(A3, LOW);
  delay(1000);
  digitalWrite(A0, LOW);
  digitalWrite(A3, HIGH);
  lcd.clear();

  if (!SD.begin(10)) {                    // Check for SD card
    digitalWrite(A3, LOW);
    lcd.setCursor(0,0);
    lcd.print("SD CHECK");
    lcd.setCursor(0,1);
    lcd.print("  FAIL  ");
    delay(1000);
    lcd.clear();
    digitalWrite(A3, HIGH);
  } else {
    delay(1000);
    digitalWrite(A3, LOW);
    lcd.setCursor(0,0);
    lcd.print("SD CHECK");
    lcd.setCursor(0,1);
    lcd.print("  PASS  ");
    delay(1000);
    lcd.clear();
    digitalWrite(A3, HIGH);
  }  
//----------------------------------- Setting up one wire temperature sensor.
  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }
  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }
  if (OneWire::crc8(addr, 7) != addr[7]) {
    Serial.println("CRC is not valid!");
    return;
  }
  Serial.println();

  // the first ROM byte indicates which chip
  switch (addr[0]) {
  case 0x10:
    Serial.println("  Chip = DS18S20");  // or old DS1820
    type_s = 1;
    break;
  case 0x28:
    Serial.println("  Chip = DS18B20");
    type_s = 0;
    break;
  case 0x22:
    Serial.println("  Chip = DS1822");
    type_s = 0;
    break;
  default:
    Serial.println("Device is not a DS18x20 family device.");
    return;
  } 

}
//----------------------------------------------------------------------------- End of setup. 

void loop(void) {

  unsigned long sdcurrentMillis = millis();
  unsigned long currentMillis = millis();
  unsigned long owcurrentMillis = millis();


  if (owcurrentMillis - owpreviousMillis >= owinterval) {
    owpreviousMillis = owcurrentMillis;
    ds.reset();
    ds.select(addr);
    ds.write(0x44, 1);        // start conversion, with parasite power on at the end
    }

  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

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

  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");

  delta = celsius - lasttemp;                       //Create value to check how much temperature has changed

  if (currentMillis - previousMillis >= interval) {  //If it has been 20 seconds, check to turn LED on
    previousMillis = currentMillis;
    if ((delta < 00.03) && (delta > -00.03)) {      //If temperature hasn't changed by +/- .05 turn led on.
      digitalWrite(A0, HIGH); 
    }
  }
  if (delta >= 00.06) {                             //If temperature has changed by more than .1 turn LED off
    digitalWrite(A0, LOW);
  }
  if (delta <= -00.06) {
    digitalWrite(A0, LOW);                         // If temperature has changed by more than -.1 turn LED off
  }


    if (sdcurrentMillis - sdpreviousMillis >= sdinterval) {  //Write to SD card every 5 seconds.
    sdpreviousMillis = sdcurrentMillis;
    digitalWrite(A3, LOW);
    myFile = SD.open("Log.txt", FILE_WRITE);
    myFile.print(celsius);
    myFile.println(" C ");
    myFile.close();
    digitalWrite(A3, HIGH);
  }

  lcd.setCursor(0, 0);
  lcd.print(celsius);
  lcd.print(" C ");
  lcd.setCursor(0, 1);
  lcd.print(fahrenheit);
  lcd.print(" F ");
  lasttemp = celsius;
  }

This is the post on the Arduino forum which sort of explains what I'm doing a bit better, if it does not make sense: Best way to set up a button to toggle a second function? (millis issue now).

Edit/update

Alright, so I think it has to do with how I structured that bit of the code, it seems like the arguments to turn the LED off weren't in the if statement which used the delay, so changed it to this, and unfortunately now it is on constantly, but I think I am on the right track.

if (currentMillis - previousMillis >= interval) {  //If it has been 20 seconds, check to turn LED on
previousMillis = currentMillis;
if ((delta < 00.03) && (delta > -00.03)) {       //If temperature hasn't changed by +/- .05 turn led on.
  digitalWrite(A0, HIGH);
} else {
  if ((delta >= 00.06) && (delta <= -00.06)) {
    digitalWrite(A0, LOW);
  }
}
}

It seems like this should work but perhaps there's an issue with having two if statements set up on top of each other like that. I also gave it a try like this, to no avail.

(currentMillis - previousMillis >= interval) {  //If it has been 20 seconds, check to turn LED on
previousMillis = currentMillis;
if ((delta < 00.03) && (delta > -00.03)) {       //If temperature hasn't changed by +/- .05 turn led on.
  digitalWrite(A0, HIGH);
} else {
  if (delta >= 00.06){
    digitalWrite(A0, LOW);
  } else {
  if (delta <= -00.06){
   digitalWrite(A0, LOW);
  }
 }
}
}

Edit/update

Quick clarification, I managed to get rid of the delay and use millis instead but it introduced an issue with the LED which indicates stable temperature not working (being lit most of the time, and then flashing off/on fairly quickly as the temperature changes, instead of remaining off until it stabilizes again). It seems to be ignoring the delay I have it waiting for.

Now, after going through it a lot, it seems like this is how I need it set up. It checks if the interval has been reached, if so, it checks if the temperature has changed less than x amount, if so it lights the LED.

However, if the temperature has changed more than that, it checks if it exceeds y amount, and if so turns the light off.

I have tried reversing these and it has had no effect, it just seems like the boolean arguments aren't working and I don't know why, but at least the delay issue is solved.

if (currentMillis - previousMillis >= interval) {  //If it has been 20 seconds, check to turn LED on
previousMillis = currentMillis;
if ((delta < 00.03) && (delta > -00.03)) {       //If temperature hasn't changed by +/- .05 turn led on.
  digitalWrite(A0, HIGH);
} else if ((delta >= 00.06) || (delta <= -00.06)) {
    digitalWrite(A0, LOW);
  }
}
  • 3
    Here? Best way to set up a button to toggle a second function? - you posted this issue there 30 minutes ago. there isn't much activity going on there - maybe everyone's eating dinner? Try to let a little longer elapse before running off cross-posting that "nothing is happening" on another site. Like, 12 hours. – Nick Gammon Mar 24 '16 at 10:42
  • There hasn't been activity on that thread much longer than that, and I wanted to get feelers out in multiple places, different sites are likely to be used by different people, who are in different places in the world, so why not try and maximize the chances of getting a response? Especially at this time of day when the majority of people who use this forums north america) will be asleep? Besides, this also puts out the issue as it's own thing relating to the millis function, which means that people searching for something similar will be able to find this more easily, what's wrong with that? – XOIIO Mar 24 '16 at 11:18
  • Besides, chances are most of the people who read that thread and did not reply were not interested in it or could not offer advice, so they will likely not visit it again, and I don't want to spam their forum. So, I'll just wait for an actual helpful reply on either of these, unfortunately this forum has turned out unhelpful in that regard thus far. – XOIIO Mar 24 '16 at 11:20
  • Looks like you got a good answer here from Edgar Bonet. Especially at this time of day when the majority of people who use this forums north america) will be asleep? However on the Arduino forum I note that you also got answers 49 mins, 83 mins, and 102 mins after the post I referred to. so they will likely not visit it again - apparently not. – Nick Gammon Mar 24 '16 at 21:36
  • I was referring to people who have previously looked at a thread, when it was in it's infancy, and were not interesting in the original problem. If that was the case, they would likely not revisit and see that there was a new problem. Apparently you are incapable of contributing though, and instead decide to just spit out useless and pointless comments. – XOIIO Mar 25 '16 at 7:03
3

Currently, your algorithm for reading the temperature in loop() looks like:

request_temperature_conversion();
delay(1000);
float temp = read_temperature();
report_temperature_to_user(temp);

If you take care to send the first request in setup(), then you can roll these pieces around so that your loop() code looks like:

delay(1000);
float temp = read_temperature();
report_temperature_to_user(temp);
request_temperature_conversion();

And that's the trick: you send the new request in the same loop iteration. Once you do that, it becomes quite simple to remove the delay and use millis() instead. Your compete loop code could then look like:

void loop() {
  uint32_t now = millis();

  if (now - last_temp_read >= temp_read_interval) {
    float temp = read_temperature();
    report_temperature_to_user(temp);
    request_temperature_conversion();  // make request now, will read next time
    last_temp_read = now;
  }

  if (now - last_led_update >= led_update_interval) {
    update_led_display();
    last_led_update = now;
  }

  if (now - last_SD_card_write >= SD_card_write_interval) {
    write_data_to_log_file();
    last_SD_card_write = now;
  }
}
  • Make sure to account for for the fact that the first temperature read is bogus. Other than that, an excellent technique. – Gerben Mar 24 '16 at 13:25
  • @Gerben: Could you explain why the first read is bogus? – Edgar Bonet Mar 24 '16 at 15:14
  • Sorry, not really the most accurate word here. The first time you start the code it will read the temperature before you started the first conversion. So the value you get back could be anything. – Gerben Mar 24 '16 at 15:33
  • Sorry again. It seems you already explained/fixed that: If you take care to send the first request in setup() .... – Gerben Mar 24 '16 at 15:35

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.