2 timers arduino [ 2x millis() ] - c

I'm having a problem with this code. I know what the problem is, but I can't find a good solution.
I have two functions using millis() - clock() and time().
The function clock() returns date and time and the function time() returns the time that another action is ON.
The problem is that I'm not able to store the time that the time() function is counting. When I call the function 2 times the time starts on 0:00.
I know it's because I'm overwriting the variable every time I call the loop, but I can't find a good solution for it. It seems stupid, but I have this problem for a while.
PS: I would like to have the time information every loop.
Expected results:
Start
2s: press 1 - clock = 2s, time = 0s.
5s: press 2 - clock = 5s, time = 3s.
15s: press 1 - clock = 15s, time = 3s.
20s: press 2 - clock = 20s, time = 8s.
My code:
void clock() {
// ... function that returns date and time;
// ... using millis();
}
void time(){
if (first == 0){
sec_aux = 0;
min_aux = 0;
UtlTime = millis();
first = -1;
}
if(millis() - UtlTime < 0){
UtlTime = millis();
}
else{
sec_aux = int((millis() - UtlTime) / 1000);
}
if(sec_aux > 59){
sec_aux = 0;
min_aux++;
UtlTime = millis();
}
sec = sec_aux;
min = min_aux;
Serial.println("TIME");
Serial.print(min); Serial.print(":"); Serial.println(sec);
}
void loop() {
if (Serial.available()) {
inChar = Serial.read();
}
switch(inChar){
case '1':
time();
clock();
break;
case '2':
clock();
first = 0;
break;
default:
clock();
}
}

Ok, if I understood correctly you want a function that
1) you call once and it resets the counter
2) you call successively and tells you how much time passed
3) you can reset sometimes
If so, you have to ask yourself "how can I reset the timer?". IMHO the best solution is using a flag passed to the function.
And... avoid using the millis function twice ;)
And, BTW, I think you want to perform an action just once when you receive the serial data, so move the switch inside the test...
Here is a sample:
void time(bool reset){
unsigned long currMillis = millis();
if (reset){
sec_aux = 0;
min_aux = 0;
UtlTime = currMillis;
}
while ((currMillis - UtlTime) > 1000)
{
UtlTime += 1000;
sec_aux++;
}
while(sec_aux > 59){
sec_aux -= 60;
min_aux++;
}
// Why do you duplicate the data?
sec = sec_aux;
min = min_aux;
Serial.println("TIME");
Serial.print(min); Serial.print(":"); Serial.println(sec);
}
void loop() {
if (Serial.available()) {
inChar = Serial.read();
switch(inChar){
case '1':
// Show time measure
time(false);
clock();
break;
case '2':
clock();
// Start time measure
time(true);
break;
default:
clock();
}
}
}
EDIT:
Ok, then you want a "stoppable" stopwatch. What I suggest you is to detach the update from the serial interface, so you always have updated values. Or at least that's the way my head works :P
I modified some things. You should
init lastMillis to millis() in the setup
init timeIsRunning to the proper value
call resetTime() inside the setup
Here is the code:
void resetTime()
{
msec = 0;
sec = 0;
min = 0;
}
void updateTime()
{
unsigned long currStep = millis() - lastMillis;
lastMillis += currStep;
if (timeIsRunning)
{
msec += currStep;
while (msec > 1000)
{
msec -= 1000;
sec++;
}
while(sec > 59){
sec -= 60;
min++;
}
}
}
void printTime()
{
Serial.println("TIME");
Serial.print(min); Serial.print(":"); Serial.println(sec);
}
void loop()
{
updateTime();
if (Serial.available())
{
inChar = Serial.read();
switch(inChar)
{
case '1':
// Show time measure
timeIsRunning = false;
printTime();
clock();
break;
case '2':
clock();
// Start time measure
timeIsRunning = true;
break;
default:
clock();
}
}
}

Related

How do I use millis() in Arduino to get the time between two points in a graph?

I am writing a program where the Arduino will start a timer using millis() when the voltage on the analogue pin A2 rises and crosses a threshold and turns off the timer when threshold is crossed again (rising voltage). It will then calculate the time = t2-t1. I thought about using external interrupt and an op-amp to detect the threshold crossing but is there anyway I can accomplish this just with code, without the need for any external hardware??? An image is attached:
Thank you for helping!
Of course:
bool is_high = false;
int threshold = 600; // (or whatever)
unsigned long start_time = 0;
void loop()
{
int val = analogRead(A2);
if (!is_high)
{
if (val > threshold) {
is_high = true;
unsigned long now = millis();
if (start_time != 0) {
Serial.print("t=");
Serial.prinln(now - start_time);
}
start_time = now;
}
}
else {
if (val < threshold) {
is_high= false;
}
}

Arduino P10 display and Time count

everyone, I am using, P10 Dot Matrix Display with Arduino Uno. I am using P10 Library from this link. P10_LED and I need to display the one-hour countdown on the display module. The given library uses TimerOne library. So for countdown i am using MsTimer2 library which usese timer2 of arduino.
When I individually run both of the libraries, my scrolling on the display is perfect and my timer library also generates a pure 1sec interrupt. Now what I did is the added both the library in my project and I am doing the countdown. But now suddenly my MsTimer2 doesn't generate pure 1sec.
Here is the code.
#include <MsTimer2.h>
#include <TimerOne.h>
#include"SPI.h"
#include <ledP10.h>
LedP10 myled;
uint8_t minute = 0, second = 0, hour = 1;
volatile bool xIsCountDone = false;
volatile bool xIsInterruptOcuured = false;
char time_buff[100];
void setup()
{
Serial.begin(9600);
myled.init(3,4,8,9 ,3);
sprintf((char*)time_buff, " %d%d:%d%d:%d%d", (hour/10), (hour%10),(minute/10), (minute%10),(second/10), (second%10));
Serial.println((char*)time_buff);
myled.showmsg_single_static((char*)time_buff, 0);
xIsInterruptOcuured = false;
//myled.showmsg_single_scroll("this is single led test",2,8,0);
MsTimer2::set(1000, count);
MsTimer2::start();
}
void loop() {
if (xIsInterruptOcuured == true)
{
sprintf((char*)time_buff, " %d%d:%d%d:%d%d", (hour/10), (hour%10),(minute/10), (minute%10),(second/10), (second%10));
Serial.println((char*)time_buff);
myled.showmsg_single_static((char*)time_buff, 0);
xIsInterruptOcuured = false;
}
}
void count(){
second--;
if (second <= 0 || second > 59)
{
second = 59;
minute--;
if (minute <= 0 || minute > 59)
{
minute = 59;
hour--;
if (hour <= 0 || hour > 12)
{
xIsCountDone =true;
}
}
}
Serial.println(millis());
xIsInterruptOcuured = true;
}
In the interrupt routine, I am printing millis() to see at after how many ms the interrupt occurs. The results are something like this.
15:33:02.684 -> 1199
15:33:04.371 -> 2396
15:33:06.059 -> 3592
15:33:07.746 -> 4783
15:33:09.434 -> 5986
15:33:11.121 -> 7181
15:33:12.855 -> 8379
15:33:14.543 -> 9578
15:33:16.230 -> 10768
15:33:17.918 -> 11974
15:33:19.605 -> 13168
15:33:21.292 -> 14365
15:33:22.980 -> 15562
15:33:24.667 -> 16751
15:33:26.402 -> 17955
When I use only MsTimer2 library the results are something like this.
15:37:21.241 -> 998
15:37:22.226 -> 1998
15:37:23.257 -> 2998
15:37:24.241 -> 3998
15:37:25.226 -> 4998
15:37:26.257 -> 5998
15:37:27.241 -> 6998
15:37:28.225 -> 7998
15:37:29.257 -> 8998
15:37:30.241 -> 9998
15:37:31.225 -> 10998
15:37:32.256 -> 11998
15:37:33.241 -> 12998
15:37:34.225 -> 13998
15:37:35.256 -> 14998
My guess, it's happening because of the TimerOne library but I couldn't find the solution. In ledP10.cpp there is a callback method for timer1 and it contains loops and may line of code. But is timer1 interrupts priority is higher than timer2? But according to the ATmega328p datasheet, the vector no. for Timer2 is less than Timer1. Doesn't that mean Timer2 has a higher priority? My ultimate goal is to do the one-hour countdown. Any help with this problem or any additional information i am missing which will be useful or any other solution other than using timer2 interrupt will be appreciated.
Regards.
EDIT
Here is the code I used with millis() and gave me around 12min difference.
uint8_t new_buff[100];
unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
const unsigned long period = 1000; //the value is a number of milliseconds
uint8_t minute = 0, second = 0, hour = 1;
char time_buff[100];
void setup()
{
myled.init(3,4,8,9,3);
Serial.begin(9600);
sprintf((char*)time_buff, " %d%d:%d%d:%d%d", (hour/10), (hour%10),(minute/10), (minute%10),(second/10), (second%10));
//Serial.println((char*)time_buff);
myled.showmsg_single_static((char*)time_buff, 0);
startMillis = millis();
}
void loop() {
currentMillis = millis(); //get the current "time" (actually the number of milliseconds since the program started)
if (currentMillis - startMillis >= period) //test whether the period has elapsed
{
Serial.println(millis());
second--;
startMillis = currentMillis; //IMPORTANT to save the start time of the current LED state.
if (second <=0 || second > 59) {
second = 59;
minute--;
if (minute <=0 || minute > 59) {
minute = 59;
hour--;
if (hour <= 0 || hour > 12) {
hour = 0;
}
}
}
sprintf((char*)time_buff, " %d%d:%d%d:%d%d", (hour/10), (hour%10),(minute/10), (minute%10),(second/10), (second%10));
myled.showmsg_single_static((char*)time_buff, 0);
startMillis = currentMillis;
}
}
This answer targets your example using millis(). You can avoid accumulating errors over time, by not setting the next update relative to the current time. Rather just increment it by one second each time. This way, it does not matter if your main loop gets blocked by an interrupt for some milliseconds.
Also note that you don't have to save hours, minutes and seconds separately, you can just calculate them:
unsigned long nextMillis = 1000;
unsigned long targetTime = 1 * 60 * 60 * 1000; // 1 hour in milliseconds
void setup(){
myled.init(3,4,8,9,3);
Serial.begin(9600);
updateMyLed(0);
}
void updateMyLed( unsigned long elapsedTime ){
char buffer[100];
unsigned long timeLeftInSeconds = (targetTime - elapsedTime) / 1000;
uint8_t hour = timeLeftInSeconds / 3600;
timeLeftInSeconds -= hour * 3600;
uint8_t minute = timeLeftInSeconds / 60;
uint8_t second = timeLeftInSeconds - (minute * 60);
sprintf((char*)buffer, " %d%d:%d%d:%d%d", (hour/10), (hour%10), (minute/10), (minute%10), (second/10), (second%10));
myled.showmsg_single_static((char*)buffer, 0);
}
void loop() {
if( millis() >= nextMillis ){
updateMyLed(nextMillis);
nextMillis += 1000;
}
}
In the Arduino world, some libraries are disabling the interrupts.
This happens with all WS2812 LEDS and also yours. Without disabling interrupts, there would be a timing problem with the external devices.
So, you should never use interrupts or library with interrupts, if you use another one who will disable the interrupts.
Do you want use the P10 library? You can, but do not use interrupts in your code. Do not add other libraries like IR_remote, since it will not work correctly.
Back to your problem, just update your timer in the loop. And do not wait until the a second is over to update your time by 1 second! This will always be more than 1second.
You can for example convert the millis to seconds seconds = millis() / 1000;.
Your loop could be so:
void loop() {
currentSeconds = millis() / 1000; //get the current "time" (actually the number of milliseconds since the program started)
if (currentSeconds != savedSeconds) //test whether the period has elapsed
{
savedSeconds = currentSeconds;
Serial.println(millis());
second--;
if (second <=0 || second > 59) {
second = 59;
minute--;
if (minute <=0 || minute > 59) {
minute = 59;
hour--;
if (hour <= 0 || hour > 12) {
hour = 0;
}
}
}
sprintf((char*)time_buff, " %d%d:%d%d:%d%d", (hour/10), (hour%10),(minute/10), (minute%10),(second/10), (second%10));
myled.showmsg_single_static((char*)time_buff, 0);
}
}

Ardunio alarm clock without rtc projects error

i am currently working in a ardunio alarm clock project without and rtc for school. I have a problem with my hour variable not increasing and my conditionals that checks it.
This code mostly works i can set time in hr and minutes but when the timer reaches 59 minutes the hour doesnt increase by 1. The second code that has been posted is the bit where the problem lies. Any tips/help would be appreciated.
#include <Time.h>
#include <TimeLib.h>
#include <Wire.h>
#include <Adafruit_RGBLCDShield.h>
#include <utility/Adafruit_MCP23017.h>
#include <EEPROM.h>
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
const int numofMenus = 4;
int currentMenu = 0;
String menu[4][2] = {{"Set hour", "hr"}, {"Set minutes", "min"}, {"Set alarm", "o"}, {"Current Time", "ha"}};
int parameters[24];
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
lcd.noBlink();
}
void loop() {
inputAction();
if (currentMenu == 0)
{
printScreen();
}
if (currentMenu == 1)
{
printScreen();
}
if (currentMenu == 2)
{
printScreen();
}
if (currentMenu == 3)
{
int hr = parameters[0];
int mn = minute(parameters[1]);
int sec = second();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Time: ");
if (parameters[1] >= 0 && parameters[1] <= 59 && sec < 59) {
parameters[0];
printtime(parameters[0]);
}
else if (parameters[1] == 59 && second() == 59) {
if (parameters[0]>=0 && parameters[0]<24){
parameters[0];
printtime(parameters[0]++);
//delay(1000);}
/*else if (parameters[0] == 24){
parameters[0]= 0;
printtime(parameters[0]);
}*/
}
}
lcd.print(":");
if (sec >= 0 && sec < 59) {
printtime(parameters[1]);
}
else if (sec == 59) {
parameters[1];
printtime(parameters[1]++);
delay(1000);
}
lcd.print(":");
printtime(sec);
/* if (sec == 60) {
mn+1;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Time: ");
printtime(hr);
lcd.print(":");
printtime(mn);
lcd.print(":");
printtime(sec);
}
if (mn == 60) {
hr+1;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Time: ");
printtime(hr);
lcd.print(":");
printtime(mn);
lcd.print(":");
printtime(sec);
}*/
delay (100);
}
}
void inputAction() {
uint8_t buttons = lcd.readButtons();
if (buttons & BUTTON_UP) {
if (currentMenu == 0) {
currentMenu = numofMenus - 1;
} else {
currentMenu--;
}
} else if (buttons & BUTTON_DOWN) {
if (currentMenu == numofMenus - 1) {
currentMenu = 0;
} else {
currentMenu++;
}
} else if (buttons & BUTTON_RIGHT) {
parameters[currentMenu]++;
} else if (buttons & BUTTON_LEFT) {
parameters[currentMenu]--;
}
}
void printScreen() {
lcd.clear();
lcd.print(menu[currentMenu][0]);
lcd.setCursor(0, 1);
lcd.print(parameters[currentMenu]);
lcd.print(" ");
lcd.print(menu[currentMenu][1]);
delay(100);
}
void printtime(int data) {
if (data >= 0 && data < 10) {
lcd.print('0');
}
lcd.print(data);
}
I don't think it is a good strategy to throw all those delays in the code. If you want to work with an RTC, you need to be mindful of those delays as they will affect the functionality of your program. If you don't account for delay properly, they will accumulate as errors. An error of 100 msecs will be seen as error in seconds easily within days.
I did not go through your code because it is hard to understand. I propose you have a look at this code which should be placed in your loop() function:
delay(100);
parameters = parameters + 100;
long rtcSeconds = parameters / 1000;
int seconds = (int) (rtcSeconds % 60); // Range 0 . 59 units: Secs
int minutes = (int)(((rtcSeconds - seconds) / 60) % 60); // Range 0 . 59 units: Mins
int hours = (int) (((rtcSeconds - minutes*60 - seconds) / (3600)) % 24); // Range 0 . 23 units: Hrs
I am using your parameters[0] field to keep track of the time in your syste, in milliseconds units. The field increases every 100 msecs as you see. You can recover the different time values using a combination of the modulus (%) and division operations. Now, I know you need to increase the time in your program. To do so, it is easy:
If you want to increase one second, do this: parameters[0] += 1000;
If you want to increase one minute, do this: parameters[0] += 1000 * 60;
If you want to increase one hour, do this: parameters[0] += 1000 * 3600;
Now, you might need to work with long variables as int have a limited range. You also need to watch for the time roll over when you change your time. If you are decreasing the time, you need to watch pramaters[0] doesn't become negative. In case it does, you need to add 3600 * 24 * 1000 msecs to force it jump one day ahead (3600 * 24 is one day in seconds). I hope you understand this is not a solution to your problem but an alternative approach using a little bit of a simpler code structure.

Condition for cycling between a value each X milliseconds within a full second

I am working with a micro-controller that has an interrupt that counts every 1ms since start.
I have a variable which can be between 0 and 999 and I want to toggle another value if the time elapsed is less than x milliseconds (in following example it is 500ms). So between time 0 and 500 I want shouldActivate to be TRUE, and between 500 and 1000 it should be false, then between 1000 and 1500 it should be true and so on.
int activeTime = 500; // 500ms active
int shouldActivate = 0;
int elapsed = 0; //how many ticks we had so far
// This function gets automatically called every 1ms
void tick() {
if(elapsed < activeTime) {
elapsed++;
shouldActivate = 1;
} else {
shouldActivate = 0;
elapsed--;
}
}
The above code works when I just start, while elapsed goes over 500, I get problems as it just does the decrement operation only once.
What conditions should I put into my function to achive the desired result?
You have two solutions, make new variable "direction" (for specify if you are decrementing or incrementing) but I think better is something like:
void tick() {
elapsed++;
if(elapsed < activeTime) {
shouldActivate = 1;
} else {
shouldActivate = 0;
}
if(elapsed>999)
elapsed = 0;
}
As per my understanding your application should active for 500 ms and sleep or inactive for 500ms. You can achieve that like below:-
int activeTime = 500; // 500ms active
int shouldActivate = 0;
int elapsed = 0; //how many ticks we had so far
// This function gets automatically called every 1ms
void tick() {
if(elapsed < activeTime) {
elapsed++;
shouldActivate = 1;
} else {
shouldActivate = 0;
elapsed++;//keep increasing elapsed for another 500ms
}
// after sleeping for 500ms assign elapsed to zero so that it should active again
if(elapsed >= (activeTime + 500))
elapsed = 0
//with the above condition you can switch off and on every after 500ms
}
How about:
const int activeTime = 500; // 500ms active
int shouldActivate = 0;
int elapsed = 0; //how many ticks we had so far
// This function gets automatically called every 1ms
void tick() {
elapsed = (elapsed + 1) % 1000;
shouldActivate = (elapsed < activeTime);
// And possibly:
// if (elapsed == activeTime) { /* State change to Inactivate */}
// if (elapsed == 0) { /* State change to activate */}
}
So your code only decrements elapsed once then the next time through, elapsed < active, so it increments again.
Most answers so far reset elapsed when it gets to 500 or 1,000. However, if it is called elapsed I think it should contain the elapsed time since the start. Here's a solution that allows elapsed to go on increasing forever, or at least for just over 2 billion milliseconds for a 32 bit int.
void tick()
{
shouldActivate = elapsed % (2 * activeTime) < activeTime;
elapsed++;
}
What you are basically describing is a cycle, in which elapsed ranges from 0 to 2*activeTime-1 and the next ms it is back to 0. So in order to check in which part of the range you are at every moment, you can simply do
elapsed = (elapsed+1) % (2*activeTime);
Which will make the final code look like this:
int activeTime = ...;
int shouldActivate = 0;
int elapsed = 0; //how many ticks we had so far
// This function gets automatically called every 1ms
void tick() {
elapsed = (elapsed+1) % (2*activeTime);
if(elapsed < activeTime) {
shouldActivate = 1;
} else {
shouldActivate = 0;
}
}
The reason for why your code isn't working is: If the elapsed == activeTime, and else branch triggers, your elapsed-- runs, and after which, the condition of elapsed < activeTime is, once again, true, hence why it keeps going back-and-forth between branches.
Simplest solution, in my opinion, is just resetting elapsed to 0, and inverting shouldActivate, once the value of elapsed reaches activeTime.
void tick() {
if(elapsed < activeTime) {
elapsed++;
} else {
elapsed = 0;
shouldActivate = !shouldActivate;
}
}

Collecting from arduino pin for specific time interval

I want to run my Arduino for a specific amount of time (say 60 seconds) and collect
data from the analog pin with a specific sampling rate (say four samples a second).
I got the code working in matlab... but I want to use the arduino environment.
Please help me convert it.
a_pin = 0;
fs = 4; % sampling frequency (samplings per second)
mt = 20; % time for measurements
ind = 1;
nind = 1;
last_beep = 0;
tic;
while toc < mt
time(ind) = toc;
v(ind) = a.analogRead(a_pin);
% wait for appropriate time for next measurement
while( nind == ind )
nind = floor(toc*fs) + 1;
end
ind = nind;
end
Ok this is what i have so far in my sketch. Would this measure for 10 seconds taking
readings every 5?
int sensePin = 0;
unsigned long starttime = 0;
unsigned long endtime = 0;
int i = 0;
int n;
const int sizeofv = 50;
int v[sizeofv];
void setup(){
pinMode(sensePin, INPUT);
Serial.begin(9600);
}
void loop() {
starttime = millis();
endtime = starttime;
while ((endtime - starttime) <= 10000) // do this loop for up to 1000mS
{
i = i + 1;
v[i] = analogRead(sensePin);
endtime = millis();
delay(5000);
}
for(n=0; n < sizeofv; n++)
{
Serial.print(v[n]);
Serial.print('\n');
}
while(1) { }
}
I recommend using SimpleTimer library, it does all the work for you and is road proven.
There's one bug in your code, you're incrementing i prior to assigning the first value. So when you'll loop over the v[] array, you'll end up printing v[0] which will have an undefined value, and you'll never print the last recorded value of v (if you did not end up overflow the buffer and write outside its boundaries).
There's one glitch with your code, it's that you make a condition that you need to end it at 10000, and you make two delays of 5000. Theoretically it looks ok, but in practice, you may get a surprise having only one value. That's because code takes time to execute, and an analogRead() is particularly slow.
I think using loops within the loop() function make things more complicated, and add close to no flexibility. So here you have another take you could try, to use the loop() only by tracking states of the loop, based on some simple calculations:
#define TIME_WINDOW 10000
#define NB_STEPS 2
void setup() {
// ...
endtime = millis();
}
bool over = false;
void loop() {
if (!over) {
curtime = endtime - millis();
// get in this block for up to 1000mS
if (curtime <= TIME_WINDOW) {
v[i] = analogRead(sensePin);
// increments only once when 5000 has been reached.
if ((curtime / (TIME_WINDOW/NB_STEPS)) != i)
i = (curtime / (TIME_WINDOW/NB_STEPS));
} else {
for(n=0; n < NB_STEPS; n++)
Serial.println(v[n]);
over = true;
}
}
}
I did not test my code, so it may fail or have typos, but the idea is the following:
we define the time window and the number of measure steps
while the time window is not over:
store the measurement in the current array at current index
if integer division of spent time over time_window is different from current index
store the new value into the current index
e.g.: i = 4000/(10000/2) <=> i = 4000/5000 <=> i = 0 and i = 6000/(10000/2) <=> i = 1
when the time window is over:
print out each result
set over to true thus we're not entering the loop again
But a simpler (non flexible) solution for you would be:
void setup() {
int value1, value2, value3, value4;
value1 = analogRead(sensePin);
delay(2500);
value2 = analogRead(sensePin);
delay(2500);
value3 = analogRead(sensePin);
delay(2500);
value4 = analogRead(sensePin);
Serial.println("first value: %d", value1);
Serial.println("second value: %d", value2);
Serial.println("third value: %d", value3);
Serial.println("fourth value: %d", value4);
}
and there you got your solution!
HTH

Resources