millis and increment time lapse

Hello all,

I am trying to make it so that data on captured from sensors gets saved to SD card everything two minutes. I want a time elapsed.

h:m:s
The first save is at 0:0:0, the second save is at 0:1:0 ect

// LCD setup file
#include <LiquidCrystal_I2C.h>


LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display

// Sensors setup
#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS_1 2
#define ONE_WIRE_BUS_2 3

OneWire oneWire_in(ONE_WIRE_BUS_1);
OneWire oneWire_out(ONE_WIRE_BUS_2);

DallasTemperature sensor_inhouse(&oneWire_in);
DallasTemperature sensor_outhouse(&oneWire_out);

// Configuration SD card module

// SD card attached to SPI bus as follows:

// MOSI - pin 11
// MISO - pin 12
// CLK - pin 13
// CS - pin 10 

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

const int chipSelect = 10; // Chip select pin for selecting SD card module

// Delay for every two mins to save

const unsigned long interval = 60000 ; // 2 minutes is 120000 milli seconds
unsigned long previous = 0 ;

// Increment

unsigned long previousTime = 0;
byte seconds ;
byte minutes ;
byte hours ;

void setup()
{
  Serial.begin(9600); // Baud rate of 9600
pinMode(chipSelect, OUTPUT); // Set chip select as output
  sensor_inhouse.begin();
  sensor_outhouse.begin();

  // Time elapsed
 
  
  lcd.init();                      // initialize the lcd 
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Radiator");
  lcd.setCursor(2,1);
  lcd.print("RIOU GLASS!");
  delay(1000);
  lcd.setCursor(0,0);
  lcd.setCursor(0,1);  

  Serial.println(F("Radiator")); 
  Serial.println();
  
  while (!Serial) {
  }
  Serial.print("Starting SD card...");

  if (!SD.begin(chipSelect)) { // If SD module not working
    Serial.println("Card failed to start or not present");
    while (1);
  }
  Serial.println("   Card is now operational!"); // If SD module working
  Serial.println();
  
  File dataFile = SD.open("radiator.txt", FILE_WRITE); // Being to write in beehive.txt
  
// Printing column name for date, time, Temp_1, Temp_2, Temp_DHT and Humidity on serial terminal
  
  delay(100);
  Serial.print("Time elapsed      "); // Print header name for column
  Serial.print(" \t");        // Create tab/space
  Serial.print("Radiator 1 in °C"); // Print header name for column  
  Serial.print(" \t");  // Create tab/space   
  Serial.print("Radiator 2 in °C"); // Print header name for column  
  Serial.print(" \t"); // Create tab/space

  Serial.println();  // Insert a new line  
  
// Printing column name for date, time, Temp_1, Temp_2, Temp_DHT and Humidity on SD card
  
  dataFile.print("Timer"); // Print header name for column
//  dataFile.print("\t");        // Create tab/space
  dataFile.print(",");
  dataFile.print("Radiator 1 in °C"); // Print header name for column
//  dataFile.print("\t"); // Create tab/space
  dataFile.print(",");      
  dataFile.print("Radiator 2 in °C"); // Print header name for column  
//  dataFile.print("\t");  // Create tab/space
  dataFile.print(",");
  dataFile.println();  // Insert a new line 
  dataFile.close();
  delay(1000);
}


void loop()
{
  unsigned long current = millis(); // Setting delay for saving data on SD card every two minutes

previousTime = previousTime + 1000;  // use 100000 for uS
     seconds = seconds +1;
     if (seconds == 60)
     {
        seconds = 0;
        minutes = minutes +1;
     }
     if (minutes == 60)
     {
        minutes = 0;
        hours = hours +1;
     }
     if (hours == 13)
     {
        hours = 1;
     }
    
  if(current - previous >= interval) {
    
  File dataFile = SD.open("radiator.txt", FILE_WRITE);

  if (dataFile){ // If data is now ready to be written
    
// Save to SD card

//  dataFile.print("");                                                          
  dataFile.print(hours, DEC);  
  dataFile.print(":");     
  dataFile.print(minutes, DEC);  
  dataFile.print(":");  
  dataFile.print(seconds, DEC);                                                            
  dataFile.print(",");                                                                           
//  dataFile.print("\t"); // Create tab/space 
  dataFile.print(sensor_inhouse.getTempCByIndex(0)); // Print temperature from temperature sensor 1
//  dataFile.print("\t"); // Create tab/space
  dataFile.print(",");
  dataFile.print(sensor_outhouse.getTempCByIndex(0)); // Print temperature from temperature sensor 2
//  dataFile.print("\t"); // Create tab/space
  dataFile.println();
  dataFile.close();
  
// Print to serial

  Serial.print(hours, DEC); 
  Serial.print(":");
  Serial.print(minutes, DEC); 
  Serial.print(":");
  Serial.print(seconds, DEC);                                                                                                                                                                                        
  Serial.print("     \t"); // Create tab/space
  Serial.print(sensor_inhouse.getTempCByIndex(0)); // Print temperature from temperature sensor 1
  Serial.print("     \t"); // Create tab/space
  Serial.print(sensor_outhouse.getTempCByIndex(0)); // Print temperature from temperature sensor 2
  Serial.print("     \t"); // Create tab/space
  Serial.println();
  }

  else {
    Serial.println("There has been an error saving data"); // If the has been a problem
  }
  previous = current ; // Delay
  }
    
    sensor_inhouse.requestTemperatures();
    sensor_outhouse.requestTemperatures();

    lcd.setCursor(0, 0);
    lcd.print("Sens 1: ");
    lcd.print(sensor_inhouse.getTempCByIndex(0));
    
    lcd.setCursor(0, 1);
    lcd.print("Sens 2: ");
    lcd.print(sensor_outhouse.getTempCByIndex(0));

//    delay(5000);
  
}

I get this result in serial monitor

Radiator

Starting SD card... Card is now operational!

Time elapsed Radiator 1 in °C Radiator 2 in °C
0:0:37 23.50 23.62
0:1:15 23.44 23.56
0:1:53 23.50 23.56
0:2:31 23.37 23.56
0:3:9 23.44 23.56
0:3:47 23.50 23.62
0:4:25 23.44 23.62

Try this clock sketch; if it is good, you may add it with your sketch. The time is updated by 2-min (adjustable at any interval).

byte myClock[] = {0x00, 0x00, 0x00};
void setup()
{
  Serial.begin(9600);
  Serial.print(myClock[0]);
  Serial.print(':');
  Serial.print(myClock[1]);
  Serial.print(':');
  Serial.println(myClock[2]);
  
}

void loop()
{
  unsigned long prMillis = millis();
  while (millis() - prMillis <120000UL) //2 minute delay
  {
    ;
  }
  byte x = 120 + myClock[2];
  myClock[2] = x % 60;
 // Serial.print(myClock[2], DEC);
  x = x / 60;
  //-----------------------
  x = x + myClock[1];
  myClock[1] = x % 60;
  x = x / 60;
  //------------------------
  x = x + myClock[0];
  myClock[0] = x % 24;
  if (myClock[0] == 0x24)
  {
    myClock[0] = 0x00;
  }
  Serial.print(myClock[0]);
  Serial.print(':');
  //-------------------------------------
  Serial.print(myClock[1]);
  Serial.print(':');
  //-------------------------------------
  Serial.println(myClock[2]);
  //-------------------------------------

}

smQ.png

smQ.png

Delta_G:

 myClock[0] = x % 24;

if (myClock[0] == 0x24)
 {
   myClock[0] = 0x00;
 }




Why you keep switching back and forth to hexadecimal @Golam? If you modulo by 24, then myClock[0] is never ever going to equal 38. 

This is WAY easier than what @Golam has tried to show off.

I stand corrected that the comparison operand should be simply 24 and not 0x24.

I am not sure if you have used the phrase 'show off' to mean "boastfully display one's abilities or accomplishments"; if so, you have missed to see that OP's sketch does not show the elapsed time properly at 2 min interval and mine one is just an alternate solution demonstrating the use of % and / operators.

Delta_G:
There is no reason to be writing anything here in hexadecimal.

In this Arduino Forum man and machine are playing together and in many cases they get interchanged -- man is talking on behalf of machine and vice versa. Therefore, hexadecimal number might come in place of decimal without any harm; but, it should be in the correct form.

and this is also very far fetched

  unsigned long prMillis = millis();
  while (millis() - prMillis <120000UL) //2 minute delay
  {
    ;
  }

It's an active wait for 2 minutes, that's what the delay() function is for... why don't you just write delay(120000UL);

if you use millis(), then at least make it non blocking (a-la "blink without delay") and update the ticks only every 2 minutes.

GolamMostafa:
In this Arduino Forum man and machine are playing together and in many cases they get interchanged -- man is talking on behalf of machine and vice versa. Therefore, hexadecimal number might come in place of decimal without any harm; but, it should be in the correct form.

Code is for humans to read - as is this forum. if you use HexaDecimal, there must be a valid reason, like it's important to understand how bits are sets (and I would argue binary would be better for that) or something with a reason like the spec gives you a register address as HEX...

here there is not gain for the user and the compiler does not care... complexity is technical debt you carry forward... making things more complex than is necessary is not a good practice.
-> Think about the user first... and that will prevent you from making mistakes as you did....

Thank you but how would you replace that in the code I have written? What is the

{
;
}

mateuszriou123:
Thank you but how would you replace that in the code I have written? What is the

{
;
}

It's to make it absolutely clear to the reader that the author wants the processor to twiddle its metaphorical thumbs.

In plain english I would presume to insert the code in that location?

I am totally confused now...

No, you don't put code there.
The author apparently forgot what the delay() function does.

No - some programmers have coding rules that say you always use the { and } in a looping/conditional instruction for the statement and have them by themselves on an empty line and to show your intent is to have an empty block, use a ;

I prefer
while (condition) ; // active wait and you can also seewhile (condition) {} // active wait
but this is personal preference of sometimes imposed by your organization programing rules. So decide what rules you want and then stick to them.

but again, here the most readable code would be

delay(120000UL);

I think I understand your point and I will note this down for the future since you explaining it well.

How would you replace this in my code? I tried a few things and I messed up the ifs and whiles ect.

Your help is much appreciated!

J-M-L:
and this is also very far fetched

  unsigned long prMillis = millis();

while (millis() - prMillis <120000UL) //2 minute delay
 {
   ;
 }




It's an active wait for 2 minutes, that's what the delay() function is for... why don't you just write 


delay(120000UL);

Then the OP will have no more opportunity to update his variables until 2-min time has been elapsed. If the control program is monitoring fire hazards, then the suggested blocking codes could lead to disaster.

 unsigned long prMillis = millis();
while (millis() - prMillis <120000UL) //2 minute delay
{
     //insert codes to update variables like refreshing a multiplexed display unit
}
void loop()
{
  unsigned long current = millis(); // Setting delay for saving data on SD card every two minutes

previousTime = previousTime + 1000;  // use 100000 for uS
     seconds = seconds +1;
     if (seconds == 60)
     {
        seconds = 0;
        minutes = minutes +1;
     }
     if (minutes == 60)
     {
        minutes = 0;
        hours = hours +1;
     }
     if (hours == 13)
     {
        hours = 1;
     }
    
  if(current - previous >= interval) {

This code tells me that you don't know what you're doing with millis().

You should NOT be adding 1000 to previous every time that loop() runs.
You should ONLY be setting previous AFTER the timeout (current - previous >= interval) and THEN you should add the interval to previous and NOT set previous = current.

WHY do you have
seconds = seconds +1;
run every time through loop()? You do know that loop() runs over and over as fast as you let it?

You should have an easier time understanding millis() timing with simpler sketches, less to get confused over.
If you learn that lesson well, then it will be easier to make it work in this sketch. because you REALLY do not understand millis() timing.

.........................................

You can pack the millis() to HMS into a function that only runs at print time.

millis / 1000 will give you total seconds.

total seconds / 60 will give you total minutes then total seconds % 60 will give you the remaining seconds to print.

total minutes / 60 will give you total hours then total minutes % 60 will give you the remaining minutes to print.

and last,
if ( hours > 12 ) hours -= 12;

GolamMostafa:
Then the OP will have no more opportunity to update his variables until 2-min time has been elapsed. If the control program is monitoring fire hazards, then the suggested blocking codes could lead to disaster.

 unsigned long prMillis = millis();

while (millis() - prMillis <120000UL) //2 minute delay
{
    //insert codes to update variables like refreshing a multiplexed display unit
}

exactly my point - Your initial recommendation as it stands was not really good.

You can indeed suggest to insert code there but that's not a best practice, it's the wrong place to have that code...

The right answer (if there is a need for dynamic stuff to happen and delay() is not enough) is actually to make this non blocking.

unsigned long waitTime = 1000UL; // 1 second - could be 'const' if user cannot change it

void tickCheck()
{
  static unsigned long chrono = 0;
  if (millis() - chrono >= waitTime) {
    Serial.print(F("Tick #"));
    Serial.println(millis() / 1000);
    chrono += waitTime;
  }
}

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  // check if we need to update
  tickCheck();

  // here do other stuff, update variables, refreshing a multiplexed display unit etc..
}