0

Following configuration:

  • Arduino UNO R3
  • Real Time Clock DS1307RTC
  • LCD1602 Shield with buttons
  • Photo resistor
  • DHT11 (Temp & Humidity Sensor)

I need the function delay(1000) to display the seconds properly from the RTC to the LCD. But, that way I always have to keep the buttons pressed for up to 1 second until they react.

How can I workaround or solve this problem?

Idea 1: Multitasking would come in handy here :-) pseudocode:

task 1: listen for LCD buttons
task 2: display seconds with a delay of 1000ms

But there isn’t a way Arduino could multitask, isn’t it?

Idea 2: As far as I saw in the header files for the RTC, the smallest readable unit is seconds. Otherwise I could have used something like a millisecond reader in a while condition to listen for the buttons; pseudocode:

while (RTC-millisecond < 1000ms) {
    // listen for buttons
    // so a second passes, which gives the RTC time to display the seconds values
}

-

EDIT

updated code, thanks to @NickGammon and Gerben:

#include <LiquidCrystal.h>
#include <Wire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <DHT.h>

// initialize the library with the numbers of the interface pins
//                RS  E  D4 D5 D6 D7 (maybe wrong order)
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// pins on which the sensor is connected
#define DHTPIN A1 
#define PHOTOPIN A2

// initialize DHT sensor
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
//#define btnRIGHT  0
//#define btnUP     1
//#define btnDOWN   2
//#define btnLEFT   3
//#define btnSELECT 4
//#define btnNONE   5

// FSM
unsigned long previousMillis1 = 0; 
unsigned long previousMillis2 = 0; 
const long interval = 3000; 

// prototypes
void print2digits(int number);
int read_LCD_buttons();


void setup() {
  // open a serial connection to display values on PC
  Serial.begin(9600);

  // set up the number of columns and rows on the LCD
  lcd.begin(16, 2);

  // start DHT sensor
  dht.begin();
}


void loop() {
  // set variable from struct tmElements_t
  tmElements_t tm;


/********* Reading from sensors *********/  
  /*** DHT sensor ***/
  // Reading temp or RH takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float humid = dht.readHumidity();
  float tempC = dht.readTemperature();  
  // Check if any reads failed and exit early (to try again).
  if (isnan(humid) || isnan(tempC)) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("DHT reading failed!");
    Serial.println("DHT reading failed!");
    return;
  }
  // Compute heat index in Celsius (isFahreheit = false)
  float hiC = dht.computeHeatIndex(tempC, humid, false);  

  /*** Photo resistor ***/
  int lightReading = analogRead(PHOTOPIN);


/********* Print to PC over serial connection *********/ 
  // ...


/********* Print values to LCD *********/ 
  /* --- first line on LCD --- */
  /*** RTC ***/  
  // set the cursor to LCD column 4, line 0 to center the clock
  lcd.setCursor(4, 0);

  // starts RTC reading and checks if it's working
  if (RTC.read(tm)) {
    static byte oldSecond = 60; // 60, because there is no 60'th second in a clock
    // display only if time changes
    if (tm.Second != oldSecond) {
      oldSecond = tm.Second;
      print2digits(tm.Hour);
      lcd.print(':');
      print2digits(tm.Minute);
      lcd.print(':');
      print2digits(tm.Second);
    }
  }
  else {
    lcd.clear();
    lcd.setCursor(0, 0);
    if (RTC.chipPresent()) {
      lcd.print("Please run SetTime function");
    } else {
      lcd.print("DS1307 read error!");
    }
    delay(9000);
  }


  /* --- second line on LCD according to button --- */
  lcd_key = ((lcd_key + read_LCD_buttons()) %3);  // call button function, modulo 3 to cycle through
    /** // Test buttons:
    lcd.setCursor(1, 1);
    lcd.print("lcd var:");
    lcd.print(lcd_key); */

  unsigned long currentMillis = millis(); // FSM used for displaying DHT and photo values without flikering

  switch (lcd_key) {
    case 0:
      /*** Weekday and Date ***/
      // display only if day changed
      static byte oldDay = 99;
      if (tm.Day != oldDay) {
        oldDay = tm.Day; 
        lcd.setCursor(0, 1);
        lcd.print("                "); // 16 spaces to clear second line before printing
        lcd.setCursor(0, 1);
        //lcd.print(dayShortStr((weekday() + 1) % 7 )); // (day+1)%7 calculation for Swiss layout
        lcd.print(tm.Wday); // TEST
        lcd.print(weekday()); // TEST
        lcd.print(", ");
        print2digits(tm.Day);
        lcd.print('.');
        print2digits(tm.Month);
        lcd.print('.');
        lcd.print(tmYearToCalendar(tm.Year));
      }
      break;

    case 1:
      /*** DHT Sensor ***/
      if (currentMillis - previousMillis1 >= interval) {
        previousMillis1 = currentMillis;   
        lcd.setCursor(0, 1);
        lcd.print("                "); // 16 spaces to clear second line before printing
        lcd.setCursor(1, 1);
        lcd.print(hiC);
        lcd.print("*C ");  
        lcd.setCursor(10, 1);
        lcd.print(humid); 
        lcd.setCursor(12, 1); 
        lcd.print("%RH ");
      }
      break;

    case 2:
      /*** Photo resistor ***/
      if (currentMillis - previousMillis2 >= interval) {
        previousMillis2 = currentMillis;    
        lcd.setCursor(0, 1);
        lcd.print("                "); // 16 spaces to clear second line before printing
        lcd.setCursor(0, 1);
        lcd.print("Light ");  
        lcd.setCursor(6, 1);
        lcd.print(lightReading);
      }
      break;
  }
}


// used to print 2 digits in the time values
void print2digits(int number) {
  if (number >= 0 && number < 10) {
    lcd.print('0'); 
  }
  lcd.print(number);
}

// read the buttons
int read_LCD_buttons() {
  adc_key_in = analogRead(0); 
  //if (adc_key_in > 1000) return btnNONE; // 1st option since it will be the most likely result (speed reasons)
  //if (adc_key_in < 50)   return btnRIGHT;  
  //if (adc_key_in < 195)  return btnUP; // replaced by below one
  if (adc_key_in < 195)  return 1; // my button up !
  //if (adc_key_in < 380)  return btnDOWN; 
  //if (adc_key_in < 555)  return btnLEFT; 
  //if (adc_key_in < 790)  return btnSELECT;   
  //return btnNONE;  // when all others fail, return this...
  return 0;
}
10
  • Try leaving out the delay entirely and see what happens. You'd be surprised. – Gerben Aug 12 '15 at 19:57
  • I tried that. The problem is, that seconds are then updated too frequently which makes the characters spin like crazy on the LCD. Also using a delay of 500 does not work properly: seconds aren't displayed smoothly. – Lex Aug 12 '15 at 20:04
  • 2
    The lcd flickers, because you are constantly updating it, but the time itself is shown correctly. That's what I was trying to show. Using delay(1000) would also give problems as the rest of the code also takes some time. So it's a bit like delay(1008). So every now and then the seconds will skip a number. What you could do it store the previous time in a variable, and only update the display is the new time is different from the time in the previous loop. That way you don't need the delay, and also don't get the flickering. – Gerben Aug 12 '15 at 20:15
  • What @Gerben suggested is by far the simplest. Since you need a delay anyway to stop the flickering, and you have a clock, use that to get yourself a nice one-second delay. Only update the display if the time differs from the previous one. Testing the seconds only would probably do it, as the minutes aren't going to change without the seconds changing. – Nick Gammon Aug 13 '15 at 5:31
  • Gerben and Nick Gammon: Thanks for the answers, I'll try them out in the next few hours and then tackle an answer as solution. I like the FSM approach; But one Q about millis(): will it count until infinity and then overflow the variable after 50 days (long = 32bit = 4Billion ms = ca 50days)? – Lex Aug 13 '15 at 6:55
1
  // clean up screen before printing a new reply
  lcd.clear();

  /* --- first line on LCD --- */
  /*** RTC ***/  
  // set the cursor to LCD column 4, line 0 to center the clock
  lcd.setCursor(4, 0);

Straight away, that will cause flicker, right? For a few milliseconds the screen will go blank, and then get gradually redrawn. Why not omit the clear? You could put it in if you are changing display types (eg. from date to temperature) but you don't need to clear the screen every time. So:

  • Don't call delay()

  • Don't clear the screen unless changing to displaying a different type of thing

  • Only update the display at all if the new number (or time, or temperature, etc.) is different from what you just displayed.

  • Just do the setCursor() to position the cursor and overwrite the information there previously.

  • Preferably follow by a space or two in case a large number is replaced by a smaller one.


Side-benefit: Since you have removed the delay, your project will now be much more responsive to key presses. Win win!


References


I am not quite sure how to do it

 if (RTC.read(tm)) 
  {
  static byte oldSecond = 99;
  // display if time changes
  if (tm.Second != oldSecond)  
    {
    oldSecond = tm.Second;
    print2digits(tm.Hour);
    lcd.print(':');
    print2digits(tm.Minute);
    lcd.print(':');
    print2digits(tm.Second);
    }
  }

Similarly further down where you display the date / sensor.

2
  • Due to your help, nothing flickers anymore. Hope you're still patient with me :-) I updated the code above, please have a look. Strange thing happened now: Weekday only shows NULL or Error, after implementing the change-check. When removing the check, it works again. Problem 2: without delay the buttons are now too fast :-) It's a LCD Shield with buttons, which when pressed constantly send a signal. Giving a delay of between 100 or 300ms doesn't help. Tested in a separate code, shows that my switch case is spinning; though here in this code the button works intermittend. I'm on since hours :-/ – Lex Aug 14 '15 at 16:36
  • For the weekday issue, I don't see where you are providing the time to the weekday function. As for the buttons, now it is fast, you are reading them too quickly. One simple thing is to look for a state change. That is, is the new button different to the old button? So for example, if it is btnRIGHT but was btnNONE then you have a state change. More information: Switches tutorial. – Nick Gammon Aug 14 '15 at 22:01
1

You need to learn a different way of programming.

Two things you need to understand:

  1. Non-blocking code. Read and understand the BlinkWithoutDelay example sketch (in the Arduino IDE's File -> Examples menu)
  2. Finite State Machines. Stop thinking purely linear, and start thinking about the different states your program can be in at any one time.

Once you have those cracked you'll be an Elite Arduino Programmer (EAP™).

3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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