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);
}
}
Related
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;
}
}
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.
I am trying to use the arduino yun to output the number of parts a machine has produced to a google spreadsheet. With the setup I have, I am able to output data to the spreadsheet temporarily. However, after some time, the yun stops working. The red LED I have on to indicate the code is running turns off, and I can no longer see the arduino in the port list. A reset of the 32u4 chip causes the LED to turn back on, indicating the code is running, but the board still does not appear in the port menu.
I have tested it at our workshop and it has run for 7-12 hours with no problems at all. It is only when we take it on to the production floor that we start experiencing these problems. Does anyone have any idea what the issue could be? Here is the most relevant part of the code:
#include <elapsedMillis.h>
#include <Process.h>
#include <Bridge.h>
#include "TimeLib.h"
// On Arduino: 0 - 1023 maps to 0 - 5 voltsf
#define VOLTAGE_MAX 5.0
#define VOLTAGE_MAXCOUNTS 1023.0
unsigned int buttonCount = 0;
float voltage = 0;
elapsedMillis timeSinceLastCycle = 0;
elapsedMillis transmitData = 0;
int pressFlag = 0;
Process date;
int hours, minutes, seconds;
int lastSecond = -1;
Process sendData;
String printDate() {
// String currTime = String(hours) + ":" + String(minutes) + ":" + String(seconds);
if (lastSecond != seconds) { // if a second has passed
// print the time:
if (hours <= 9) {
Console.print("0"); // adjust for 0-9
}
Console.print(hours);
Console.print(":");
if (minutes <= 9) {
Console.print("0"); // adjust for 0-9
}
Console.print(minutes);
Console.print(":");
if (seconds <= 9) {
Console.print("0"); // adjust for 0-9
}
Console.println(seconds);
// restart the date process:
if (!date.running()) {
date.begin("date");
date.addParameter("+%T");
date.run();
}
}
//if there's a result from the date process, parse it:
while (date.available() > 0) {
// get the result of the date process (should be hh:mm:ss):
String timeString = date.readString();
// find the colons:
int firstColon = timeString.indexOf(":");
int secondColon = timeString.lastIndexOf(":");
// get the substrings for hour, minute second:
String hourString = timeString.substring(0, firstColon);
String minString = timeString.substring(firstColon + 1, secondColon);
String secString = timeString.substring(secondColon + 1);
// convert to ints,saving the previous second:
hours = hourString.toInt();
minutes = minString.toInt();
lastSecond = seconds; // save to do a time comparison
seconds = secString.toInt();
String currTime = hourString + ":" + minString + ":" + String(seconds);
return currTime;
}
}
void checkVoltage() {
int sensorValue = analogRead(A0);
voltage = sensorValue * (VOLTAGE_MAX / VOLTAGE_MAXCOUNTS);
Console.println(voltage);
delay(50);
if (voltage >= 4.9 && pressFlag == 0) {
Console.println("Delaying");
sensorValue = analogRead(A0);
voltage = sensorValue * (VOLTAGE_MAX / VOLTAGE_MAXCOUNTS);
if (pressFlag == 0 && voltage >= 4.9) {
unsigned long int intCycleTime = timeSinceLastCycle;
timeSinceLastCycle = 0;
digitalWrite(LED_BUILTIN, LOW); // turn the LED on (HIGH is the voltage level)
printDate();
String pressTime = printDate();
Console.print("PressTime is ");
Console.println(pressTime);
buttonCount++;
Console.println(buttonCount);
pressFlag = 1;
String part1 = "curl -X POST -H \"Content-Type: application/json\" -d '{\"value1\":\"";
String timeString = pressTime;
Console.print(timeString + " seconds");
String part2 = "\",\"value2\":\"";
String numParts = String(buttonCount);
String part3 = "\",\"value3\":\"";
String strCycleTime = String(intCycleTime / 1000); // + " seconds";
String part4 = "\"}' https://maker.ifttt.com/trigger/arduino2Request/with/key/gL8YmxeaUChOMJvmwpdXp -k";
//curl -X POST -H "Content-Type: application/json" -d '{"value1":"1","value2":"2","value3":"3"}' https://maker.ifttt.com/trigger/arduino2Request/with/key/gL8YmxeaUChOMJvmwpdXp
String curlString = part1 + timeString + part2 + numParts + part3 + strCycleTime + part4;
// The curl string sends data to oue excel spreadhsheet using the IFTTT web service
sendData.runShellCommandAsynchronously(curlString);
elapsedMillis breakTimer = 0;
/*while(sendData.running()){
if(breakTimer > 5*1000){
break;
}
} */
Console.print("Data Available: "); // A value of 32 indicates a successful transmission of data, 0 also works if run asynchronously.
Console.println(sendData.available());
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
}
}
else if (voltage < 2.5) {
pressFlag = 0;
}
}
void setup() {
Bridge.begin();
Console.begin();
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
// run an initial date process. Should return:
// hh:mm:ss :
if (!date.running()) {
date.begin("date");
date.addParameter("+%T");
date.run();
}
}
void loop() {
checkVoltage();
if ((timeSinceLastCycle > 300000) && (transmitData > 300000)) { // If 5 minutes have elapsed without a part being produced, output that the arduino is transmitting even if not part is available
sendData.runShellCommand("curl -X POST -H \"Content-Type: application/json\" -d '{\"value1\":\"1\",\"value2\":\"Arduino Transmitting\"}' https://maker.ifttt.com/trigger/transmitData/with/key/gKRo-zSur5rj6rD5rviCaV2RHI5g56Dy0Vc0S_XJ-oO -k");
transmitData = 0;
}
}
UPDATE: I added a series of print statements to the checkVoltage function. The code above has been updated to reflect this. I found that it hung when trying to use sendData.runShellCommandAsynchronously. The output looks like
1.58
1.54
5.00
Delaying
PressTime is n" -d
3
Could the network connection cutting out when trying to runShellCommand cause this issue?
So after following user3629249's solution, this issue appears to be resolved. Have now had multiple weeks of error free runtime. user3629249, if you want to repost your advice as an answer, I will accept it. Thank you to user3629249 and Patrick Trentin for your help!
My update ProcessDate function now contains:
if(date.available() >0){
while (date.available() > 0) {
// get the result of the date process (should be hh:mm:ss):
String timeString = date.readString();
// find the colons:
int firstColon = timeString.indexOf(":");
int secondColon = timeString.lastIndexOf(":");
// get the substrings for hour, minute second:
String hourString = timeString.substring(0, firstColon);
String minString = timeString.substring(firstColon + 1, secondColon);
String secString = timeString.substring(secondColon + 1);
// convert to ints,saving the previous second:
lastSecond = seconds; // save to do a time comparison
lastMinute = minutes;
lastHour = hours;
hours = hourString.toInt();
minutes = minString.toInt();
seconds = secString.toInt();
String currTime = hourString + ":" + minString + ":" + String(seconds);
return currTime;
}
else{
String currTime = ""; // If there is no result from the date process, return nothing. Should not happen.
return currTime;
}
}
This code invokes a function for every 40 seconds inside a loop. Since time_t is signed in my linux system, I am doubtful about this condition:
if ((time_left <= 0 || time_left > interval))
Is only checking (time_left <= 0) fine? I referred to some websites and they have also added a check for time_left > interval. Is this check required?
int print_timed_op()
{
time_t time_now;
time_t time_left;
time_t time_next_interval;
int interval = 40, hit_count =10; //40 second interval, 10 times
time_next_interval = time(0) + interval;
//tight loop
while (1) {
sleep(1);
time_now = time(0);
time_left = time_next_interval - time_now;
/* here time_left > interval check required ? */
if ((time_left <= 0 || time_left > interval)) {
call_my_fuc();
time_next_interval = time(0) + interval;
time_left = interval;
hit_count--;
}
if(hit_count <= 0)
break;
}
return 0;
}
Note: I don't want to use a Linux timer system call or any other method to invoke the function periodically.
The check for time_left > interval is to deal with clock changes or synchronization. If your interval is 40 seconds, and someone comes along and updates the system clock to shift it ahead by one hour, you will miss 90 updates if you don't have that check.
You can overcome this in a more precise way by using clock_gettime() with the CLOCK_MONOTONIC option. Then the time will never skip forward or backward, and your function calls will always be evenly spaced.
I have an Arduino Due that I'm doing a lot of really neat stuff with in one section of code I'm handeling a flowsensor and getting readings from it. the flowsensor is hooked up to an interrupt as it is a hallsensor device. The problem I'm having is that my math is based off of a single seconds worth of collection data, when I turn on the interrupt and sleep for a second do the calculations for flowrate in gp/m it works great. For My purposes however I can't afford to pause and collect data, so instead I calculate a change in time of greater than a thousand milliseconds. then I take the amount of time passed since the last loop, devide it by 1000 milliseconds or 1 second then I have the percentage of 1 second stored in a variable, I multiply the count by the percentage and should arrive at the number of counts per second. This however results in an ever growing number regardless of the fact that I zero the numbers prior to the next loop. Below I've included the way I formatted the expressions and the loop code for the flowsensor, I'd post the whole code but I don't want to waste space on stackoverflow and I know that portion of code already works with the sleep method.
long currentMillis = 0;
long lastMillis = 0;
int checkMillis = 0;
volatile int NbTopsFan; //measuring the rising edges of the signal
int newNbTopsFan;
float realNbTopsFan;
float realMillis;
float Calc;
float Calcd;
int Calcf;
int hallsensor = 7;
void loop()
{
if (lastMillis = 0) {
lastMillis = millis();
}
currentMillis = millis();
checkMillis = (currentMillis - lastMillis);
if (checkMillis >= 1000) {
realMillis = (checkMillis / 1000);
realNbTopsFan = (NbTopsFan * realMillis);
newNbTopsFan = realNbTopsFan;
Calc = (newNbTopsFan * 60 / 7.5); //(Pulse frequency x 60) / 7.5Q, = flow rate
Calc = (Calc / 60);
Calc = (Calc / 3.78541);
Calc = (Calc * 1.47);
Calcd = (Calc * 100);
Calcf = Calcd;
NbTopsFan = 0;
lastMillis = 0;
checkMillis = 0;
realMillis = 0;
realNbTopsFan = 0;
newNbTopsFan = 0;
}
}
Your first line should read:
if ( lastMillis == 0 ) {
lastMillis = millis();
}
You need the double = to evaluate the variable