Hello ,
i am using arduino pro micro , and RTC DS3231 .
The task is to make 10 min timer ....
The code work like this :
If the button is pressed
take time now in unixtime .
end_time = time_now + 600 // time now + 600 seconds
loop
if time_now >= end_time
led off .
The problem is , that the led turn off about a minute earlier ...
Does this mean that RTC is not working properly and is incrementing the time faster than normal?
i don`t use delay() or millis() thats why I expected the time to be a bit more accurate ....
You don't need an RTC to do that, millis() is still enough for that interval with good precision (and with an RTC you don't have any precision better than one second, while millis have milliseconds...).
precisely because i had big inconsistencies using millis() i decided to use rtc.
But apparently the problem is elsewhere...
I read somewhere that if the crystal is not good, such a discrepancy can occur.
In theory, the error when using millis() for one hour should be 3.6 seconds. Which is quite enough for me. But now I have 1 minute every 10 minutes which is too much...
You need to debounce your button. Also, do you have a pull-up resistor connected to your button? You will need one based on your code. If not, it is better to connect one side of the button to ground and the other to your input pin and declare that pin as INPUT_PULLUP. It will reverse the logic of the button (LOW == pressed)
#include "RTClib.h"
const uint32_t SECONDS_PER_MINUTE = 60;
const int duration = 10 ;
const int BUTTON_PIN = 2;
const int LED_PIN = 5;
RTC_DS3231 rtc;
bool ledOn = false;
uint32_t end_time;
int buttonState;
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT);
if (!rtc.begin()) {
Serial.println("RTC is NOT running!");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
else {
Serial.println("RTC is running!");
}
buttonState = digitalRead(BUTTON_PIN);
}
void loop() {
int state = digitalRead(BUTTON_PIN);
if (state != buttonState) {
// button has changed state
if ( state == HIGH ) {
// button was pressed, start timer if not running, else ignore
if (!ledOn) {
switchOnLED(duration);
}
}
else {
// button was released, nothing to do
}
delay(20); // debounce
}
buttonState = state;
// check for expiration
if (ledOn && (uint32_t)rtc.now().unixtime() >= end_time) {
switchOffLED();
}
}
void switchOnLED(int duration) {
ledOn = true;
end_time = rtc.now().unixtime() + duration * SECONDS_PER_MINUTE;
digitalWrite(LED_PIN, HIGH);
}
void switchOffLED() {
ledOn = false;
digitalWrite(LED_PIN, LOW);
}
I don't know what kind of "inconsistences" you experienced, the only one you could see is whan the internal counter rolls to zero (being an "unsigned long" this happens once every 49 days of continuous power).
This can be taken in consideration avoiding to test millis() like this:
if (millis() >= startMillis + Interval)
this is the correct syntax:
if (millis() - startMillis >= Interval)
UNO (and others) has ceramic resonators with an accuracy of about ±0.5% and due to some other internal drifts. It means over 10 minutes you could have a precision of around ±5 ms, so this is almost irrilevant.
With micros() you could have a lower drift, unless the interval you need is below 70 minutes.
I'm not aware of any other drawbacks when using millis().
yes, you was right. that the problem is not in the crystal, because I wrote a program that, when the microcontroller is turned on, starts an LED for 10 minutes and the time was correct.
This means that I have an error in the calculations.
This code also has timing inaccuracies.
Can you direct me where I'm going wrong please:
Here i send sms with text ON and number in minutes .
Lets say if i send ON10 , the led should light for 10 minutes . But it go off earlier ...
void loop() {
if (SIM800.available()) {
String incomingMessage = SIM800.readString();
incomingMessage.toLowerCase();
if (incomingMessage.indexOf("on") != -1) {
int ledTime = incomingMessage.substring(incomingMessage.indexOf("on")+2).toInt() * 60;
digitalWrite(ledPin, HIGH);
ledOn = true;
unsigned long startTime = millis();
unsigned long endTime = startTime + (ledTime * 1000UL); //if (millis() - startMillis >= Interval)
while (millis() < endTime) {
if (SIM800.available()) {
String stopMessage = SIM800.readString();
stopMessage.toLowerCase();
if (stopMessage.indexOf("stop") != -1) {
digitalWrite(ledPin, LOW);
ledOn = false;
break;
}
}
}
if (ledOn) {
digitalWrite(ledPin, LOW);
ledOn = false;
}
}
}
}
When i use this for formating the text to lower case , i dont know how and why but it change the numbers too ....
I remove it from the code and now work very well ...
I must find a another solution for lower case ....
I can't see anything strange after toLowerCase(), and the resultimg time is correctly a 10 minutes delay (e.g. 600,000 milliseconds)...
What can you see if on your code try this below when receiving a message?
Meanwhile, I don't suggest you to test the end time like this:
while (millis() < endTime) {
because you'll have problems when millis() overlaps.
Always use the format I suggested you on post #8 (and you put inside comments but not applied), in this case something like:
// ledTime contains the time in minutes as read from the message
unsigned long ledTime = incomingMessage.substring(incomingMessage.indexOf("on")+2).toInt();
digitalWrite(ledPin, HIGH);
// Convert the time to milliseconds
ledTime = ledTime * 60000;
ledOn = true;
unsigned long startTime = millis();
while (millis() -startTime <= ledTime) {