1

I have two questions want to ask

  1. I'm using Arduino Uno with 16*2 LCD Blue ( also I have temp. sensor and two motors also 3 limit switches) ,, my code is working good, but sometimes my lcd stop printing the values of Temperature and time [print nothing i.e. print all the leds on] \\ then when I reboot it everything works good .... why this problem happened ?

  2. Is there any way to save the value of millis function when the arduino was rebooted ?

thanks for your help regards


My code is for an egg incubator , it consists of

  1. Dht22 sensor [humidity + Temp]
  2. three limit sw.
  3. one motor
  4. 16*2 lcd
  5. lamp [acting as heaters]

As I said previously, millis function that I'm using is for controlling the motor movement every 3 hours and stop the movement after 18 days of working

The main problem I faced is POINT 1 that I mentioned previously (I don't need to lose the time when I reboot the LCD)

If any one can help by giving me a short code as an example who to deal with EEPROM or any other idea.

Here is my code

#include "DHT.h"  //includes lib for the temperature-humdity sensor
#include <LiquidCrystal.h> //lcd lib

#define DHTPIN 13 //pin that DHT is connected to 
#define DHTTYPE DHT22 //Sensor we are using

DHT dht(DHTPIN, DHTTYPE); //defines location for DHT

int sw1,sw2,sw3;
const int LM1=0;
const int LM2=1;
const int LM3=6;
const int LAMP=8;
const int CW=10;
const int CCW= 9;
const int Buzzer=7;
long previousMillis = 0;        // will store last time LED was updated
unsigned long RealTime = 0;  
float MOTORTime =0;// This is the real time that present on LCD now.
unsigned long MOTORSTOP = 0;// store the valve after 18 days of ranning ,, 

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 10800000;           // interval at which to blink (milliseconds)(3 hours waiting)

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {

  lcd.begin(16, 2);   // set up the LCD's number of rows and columns: [LCD that we have (Colomn,Row)]
  pinMode(LAMP,OUTPUT);
  pinMode(LM1,INPUT);
  pinMode(LM2,INPUT);
  pinMode(LM3,INPUT);
  pinMode(CW,OUTPUT);
  pinMode(CCW,OUTPUT);

 }
void loop() {
  sw3=digitalRead(LM3);
      unsigned long currentMillis = millis();
MOTORSTOP = currentMillis;
if (MOTORSTOP < 1555200000){ // 18 days
  float h = dht.readHumidity(); //defines the variable for humidity
  float t = dht.readTemperature(); //defines the variable for temperature
  // set the cursor to column 0, line 0
  lcd.setCursor(0,0);
  // print the humidity 
  lcd.print("H ");
  lcd.print(h);
  lcd.print("% ");
  //move line down
  lcd.setCursor(0,1);
  lcd.print("T ");
  lcd.print(t);
  lcd.print("C ");

  lcd.setCursor(10,0);
  lcd.print("MOTOR"); // to print motor working time


if (t==37.65)digitalWrite(LAMP,LOW);// Lamp OFF
else if (t>37.65)digitalWrite (LAMP,LOW); // Lamp OFF
else { digitalWrite(LAMP,HIGH);}// Lamp ON


  delay(2000); //wait two sec. before taking another reading.
  ///////////////stepper motor control and delay time 2.45 hours. 
 sw1= digitalRead(LM1);
  sw2= digitalRead(LM2);


  RealTime = currentMillis - previousMillis;
  MOTORTime=((RealTime/(1000))/60); //////////////////////////////////////////// 
  MOTORTime = 180 - MOTORTime;

  if (MOTORTime >= 1){
   lcd.setCursor(10,1);
  lcd.print(MOTORTime); }// this will print the ACTUAL Time Remaining on LCD
  else {
    lcd.setCursor(10,1);
   lcd.print("<1 MIN");}


  if(RealTime > interval) {
    // save the last time you blinked the LED 
    previousMillis = currentMillis;   

  if (sw1==LOW && sw2==HIGH){
    digitalWrite (CW,LOW);
    digitalWrite (CCW,LOW);
    do {
      digitalWrite(Buzzer,HIGH);
      digitalWrite (CW,HIGH);// change the direction of rotation (DOWN Movement)
      digitalWrite (CCW,LOW);

      sw2= digitalRead(LM2);}
      while(sw2==HIGH);}

      else if (sw1==HIGH && sw2==LOW){

      digitalWrite(CW,LOW);
      digitalWrite(CCW,LOW);

        do {
          digitalWrite(Buzzer,HIGH);
          digitalWrite(CW,LOW);// change the direction of rotation (UP Movement)
          digitalWrite(CCW,HIGH);

          sw1= digitalRead(LM1);}
          while(sw1 == HIGH);}}
}
//----------------------------------------------------------------------------------------------------------------------
else if (((MOTORSTOP/10800000)>= 144 )&& (LM3== LOW)&&(MOTORSTOP >= 1555200000)){
  digitalWrite (CW,LOW);
    digitalWrite (CCW,LOW);

   float h = dht.readHumidity(); //defines the variable for humidity
  float t = dht.readTemperature(); //defines the variable for temperature
  // set the cursor to column 0, line 0
  lcd.setCursor(0,0);
  // print the humidity 
  lcd.print("H ");
  lcd.print(h);
  lcd.print("% ");
  //move line down
  lcd.setCursor(0,1);
  lcd.print("T ");
  lcd.print(t);
  lcd.print("C ");

  lcd.setCursor(10,0);
  lcd.print("MOTOR"); // to print motor working time
  lcd.setCursor(10,1);
  lcd.print("OFF");

if (t==36.70)digitalWrite(LAMP,LOW);// Lamp OFF
else if (t>36.70)digitalWrite (LAMP,LOW); // Lamp OFF
else { digitalWrite(LAMP,HIGH);}// Lamp ON


  delay(2000); //wait two sec. before taking another reading.

}
}
  • 1
    You can save things permanently in the EEPROM. Just store your counter there, and it will be available after a reset. Just don't use it too often, it's only good for maybe 1 million writes, which can be used up quite quickly. – tomnexus Apr 26 '15 at 11:44
3

You might try a real-time clock module instead of the millis() function. They keep time in external hardware and are battery backed so continue through a power down, you get calendar date and clock time instead of milliseconds since restart, and they don't overflow every 50 days.

| improve this answer | |
3

It has been suggested to you to save the millis value in the EEPROM. Although this can work, there is a big issue with the frequency at which you save it. If you save too often, you will burn your EEPROM, as it is only rated to 105 writes. If you do not save often enough, you will have most of the time a very outdated value in EEPROM. The problem can be mitigated by adopting a wear-leveling strategy, but this adds significant complexity.

There is a simpler solution to this problem: instead of using the EEPROM, save the value in RAM. Actually, the millis value already lives in RAM, and since the RAM is not initialized by hardware on reset, you will find, when your program starts, that the last millis is right where you left it! It is then cleared by your program, in a function called __do_clear_bss that belongs to the C runtime and runs before setup().

In order to preserve millis across a reset, you can do the following:

  • Write a function that copies the millis value into some other global variable. This function must run early in the startup sequence, before __do_clear_bss clears millis. This can be achieved by putting the function in the .init3 section of the program, as explained in the avr-libc documentation on memory sections. And the global variable must be in the .noinit section in order for the C runtime not to mess with it.
  • In setup(), restore the millis value from that global variable.

This way you will have a continuous millis across resets, with one caveat: on a cold boot, it will start with an unpredictable value. This should be no issue if you take care to never rely on absolute time but only on time differences, which is a good practice if only to avoid issues when millis rolls over. If you nevertheless want to reset millis on a cold boot, you have several options:

  1. Connect a "reset time" button to your Arduino and hold the button pressed when you power it up. setup() would then know it should not restore the millis value.
  2. Write some magic number in RAM. If you find this number at startup, it is extremely likely that the program is starting from a warm reset.
  3. If your Arduino has a fairly recent version of optiboot, you will find the reset flags in the CPU register r2 when your program starts. These flags tell you whether you come from a cold boot or a warm reset.

The program below is an example that implements strategy 2. It prints the millis value on the serial port roughly every second. On reset, it prints “-- starting --” and continues from where it left.

// If this is in RAM at startup, we come from a warm reset.
volatile const unsigned long magic = 0x12345678;

// This is where Arduino core keeps the millis() value.
extern volatile unsigned long timer0_millis;

// Here we will save a copy of timer0_millis.
unsigned long __attribute__((section(".noinit"))) savedMillis;

// Save the millis value at startup.
void __attribute__((section(".init3"),naked)) saveMillis()
{
    if (magic == 0x12345678)  // very likely a reset
        savedMillis = timer0_millis;
    else                      // cold boot
        savedMillis = 0;
}

void setup()
{
    // DO THIS FIRST: Restore the saved value of millis.
    noInterrupts();
    timer0_millis = savedMillis;
    interrupts();

    // Other initializations can be done now.
    Serial.begin(9600);
    Serial.println("-- starting --");
}

void loop()
{
    Serial.println(millis());
    delay(1000);
}
| improve this answer | |
1

Summary:

You have an incorrect variable type assignment for previousMillis ('long' instead of 'unsigned long') resulting in illegal mixing of types in a subtraction.

It is not certain that this is causing your problems but, if not, it will cause problems at some stage if millis() is never reset - if only when millis() passes its halfway point.

It is not possible to prove with certainty that the mixing of types is not causing a problem without examining the compiled code as the action is 'illegal' and so no assurance is possible.
(We hope that the compiler writer 'got it right' :-) ).


Details:

Where you now use

long previousMillis = 0;

You should use

unsigned long previousMillis = 0;

Both 'long' and 'unsigned long' are 32 bit data types but long is signed and effectively "wraps around" at half value.

Apart from having funny things happen after about 25 days, if you mix data types in a calculation as you do in

RealTime = currentMillis - previousMillis;
where

  • RealTime = unsigned long
  • currentMillis = unsigned long , but
  • previousMillis = long

then there is no certainty as to what the code will do when subtracting a long' from an 'unsigned long'.

We know what we would like it to do, and we hope that the compiler writer made the same assumptions as we do, but there is no guarantee.

Interestingly, while it does no harm for RealTime to be of type 'unsigned long', this is usually not essential, as we know that

up to about 25 days the result will fit in the lower 31 bits of a 'long' and
up to 65.535 seconds the result will fit in the 16 bits of an unsigned int and
up to 32.767 seconds the result will fit in the lower 15 bits of an int.

So, if your period between millis() tests is guaranteed to fit in the above ranges then the lower range variables may be used.

While the mixing of types in the actual subtraction produces a technically undefined result, the use of a different type for the result than is used in the calculation is acceptable as the calculation result is well defined as long as the subtraction is performed correctly.


millis() wrap-around - NOT a problem:

People have expressed concern about incorrect results when millis() overflows after about 50 days, but this need not be a concern if correct data typing is used. When two variables or 'counters' (in this case eg millis() and previousMillis), both of type 'unsigned long' are subtracted or compared, the result is arithmetically valid, even if the "upper" variable ( millis() ) has reached the top of its range and 'wrapped around'. This result is true for any two unsigned integer counters which are both of the same length.
ie the result of eg

delay = millis() - previousMillis

will ALWAYS be valid if millis() and previousMillis are both 'unsigned long's.
This result is not intuitively obvious to most people and there are many "solutions" to this "problem" on web which seek to correct a problem that does not exist. If you are interested in making use of this fact in other situations it is worth taking pencil and paper (or your favoured equivalent) and using eg 3 or 4 bit variables, working through how this works and why. What happens is that the overflowing / wrapping around higher counter (millis() in this case) "looks like" a longer unsigned variable with additional bits above its MSb. When the subtraction is carried out an MSb "bowwow" may occur - this is being "borrowed" from data bits that do not exist but this matters not when only the lower N bits (32 in this case) are of importance.


Variable (including the state of millis() ) can be saved in EEROM memory prior to a reboot. IF the data has to be validly written when a power outage occurs the system must detect the outage and continue to run long enough to validly write to EEROM. This requires proper design of power supply holdup time - without which EEROM corruption may occur due to power loss while writing to memory. As well as internal EEROM, it is possible to use low cost I^2C EEROM memory if non volatile storage requirements exceed uC EEROM capacity.


More detail on any of the above available if required.

| improve this answer | |
  • +1 for your explanation of the millis() rollover issue. I too have come across a few misguided attempts to fix this non-problem. OTOH, (unsigned long) - (long), although certainly a bad idea, is well-defined by the rules of usual arithmetic conversion: the signed argument is implicitly converted to unsigned. – Edgar Bonet May 27 '15 at 18:25
  • @EdgarBonet You may be right re the (UL - L) result. In C++ certainly, in Arduino code, probably and hopefully. In a language that isn't exactly the same as any formally defined one, and which also includes a GUI of sorts, programming code and more, all rolled together and implemented by a team of competent enthusiasts, where things like this are not explicitly covered in the 'reference' you'd hope the results were what you'd expect from the nearest relevant language(s) - but if it didn't work the way expected, you'd have no real comeback. But, as you say, it's probably "standardised". – Russell McMahon May 28 '15 at 10:27
0
  1. You could be running out of memory. Posting your code would be helpful.

2. Not without some external memory such as an SD card.

  1. As David points out in the comments the Arduino does have EEPROM memory (512bytes or 1024bytes depending on which version you have). See here for the library: http://www.arduino.cc/en/Reference/EEPROM
| improve this answer | |
  • Many microcontrollers have EEPROM or self-writable flash that can be used to store values internally in a non-volatile way. – David Apr 26 '15 at 11:50

Your Answer

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