Why getLocalTime implementation needs delay - arduino

I noticed a weird behavior of my application on an ESP32.
After some "debugging" I think the issue is due to this function:
bool getLocalTime(struct tm * info, uint32_t ms)
{
uint32_t start = millis();
time_t now;
while((millis()-start) <= ms) {
time(&now);
localtime_r(&now, info);
if(info->tm_year > (2016 - 1900)){
return true;
}
delay(10);
}
return false;
}
By the way, ms defaults to 5000.
In my code I use getLocalTime in a finite-state-machine in order to execute actions at specific time, i.e.:
void Irrigation::fsm()
{
time_t now = timing.getTimestamp();
switch (state)
{
case Running:
if (now - lastExecution)
{
// do something
}
break;
}
}
where:
time_t Timing::getTimestamp()
{
struct tm tm;
getLocalTime(&tm);
return mktime(&tm);
}
It seemed to me the application hangs (the fsm is called every second).
Actually, looking at the getLocalTime implementation I don't understand what it does. Why it needs a while cycle and a delay of 10 ms every cycle, just to retrieve the current time?
I'm looking for the epoch time in second. Is my approach wrong?

Thank you for raising this. I just found the same issue when using the ESP32Time by fbiego. Which as you have identified (Thanks) is that delay when fetching the time.
Looking the code, it is always having that 5000ms delay while the time is set before 2016.
I fixed mine by setting the rtc to the start of 2022.
This gets rid of the delay for me until the GPS I am using gets a lock and gives me the correct time.

How to fix that:
Just pass the 2nd parameter to the function. It will limit the delay:
getLocalTime(&timeinfo, 5);
It will finish in 10ms max (if current year is < 2016).
Why it is implemented that way:
Just sharing my guess:
Function returns immediately if the evaluated year is > 2016. And in this case it returns true, i.e. success.
In case if the evaluated year is < 2016, then such result is considered as failed result. So, function waits 10ms and does another attempt to calculate the time.
It runs in that loops for 5 sec (5000 ms).
I think, the assumption here is that the correct date/time might be retrieved from NTP during this delay.
(surely it won't happen automatically. But technically there might be started request to the NTP server, running on the other core)

Related

Alarms on an ESP32

I am using an ESP32 connected to Google Cloud IoT Core to control lights with a raspberry pi as a hub to send messages to the esp32 through IoT core to turn them on and off at set times of the day.
What I want to do now is just eliminate the raspberry pi and have the esp32 manage when it needs to turn them on or off but I cant see to find a way to set alarms or something like alarms that trigger at specific times of days.
The time value coming in is in milliseconds from UTC
Does something like that exists or how could I accomplish alarms on the esp32?
I don't know if you are using the ESP IDF or some sort of Arduino framework for the ESP32 (with which I am not familiar). This answer is for the ESP IDF. If you are not using the ESP-IDF and don't have access to the relevant header files and libraries, some of the ideas might still be relevant.
Activate SNTP
Ensure that the clock on the controller is updated by following the SNTP example at https://github.com/espressif/esp-idf/tree/master/examples/protocols/sntp .
Use localtime()
If you are using the ESP-IDF you will have access to the localtime_r() function. Pass this (a pointer to) the number of 'seconds since the Epoch' - as returned by time(NULL) - and it will fill a struct tm with the day of the week, day of the month, hour, minute and second.
The general procedure is (error handling ommitted) ...
#include <time.h>
// ...
struct tm now = {};
time_t tnow = time(NULL);
localtime_r(&tnow, &now);
// Compare current day, hour, minute, etc. against pre-defined alarms.
Note: You can use gmtime_r() instead of localtime_r() if you prefer to use UTC.
You can invoke this periodically from your main while() loop as outlined below.
Alarm structures
Define a structure for your alarm information. You could use something like the following - tweaking according to the level of granularity you need. Note that it includes a callback member - to specify what the program should actually do when the alarm is triggered.
typedef struct {
int week_day; // -1 for every day of week
int hour; // -1 for every hour
int minute; // -1 for every minute
void (*callback)(void);
void *callback_arg;
} alarm_t;
Define alarms and callbacks
Define your array of alarms, callback functions and arguments.
static void trigger_relay(const void *arg) {
uint32_t num = *(uint32_t *)arg;
printf("Activating relay %u ...\n", num);
// Handle GPIO etc.
}
static uint32_t relay_1 = 0;
static uint32_t relay_2 = 1;
static alarm_t alarms[] = {
// Trigger relay 1 at 9:00 on Sunday
{0, 9, 0, trigger_relay, (void *)&relay_1},
// Relay 2 at 7:30 every day
{-1, 7, 30, trigger_relay, (void *)&relay_2},
};
Periodic scan
Define a check_alarms() routine which loads a struct tm structure with the current time and compares this with each of the alarms you have defined. If the current time matches that of the alarm, the callback is invoked.
static void check_alarms(void) {
time_t tnow = time(NULL);
struct tm now = {};
localtime_r(&tnow, &now);
ESP_LOGI(TAG, "Time: %u:%u:%u", now.tm_hour, now.tm_min, now.tm_sec);
size_t n_alarms = sizeof(alarms) / sizeof(alarms[0]);
for (int i = 0; i < n_alarms; i++) {
alarm_t *alrm = &alarms[i];
if (alrm->week_day != -1 && alrm->week_day != now.tm_wday) {
continue;
}
if (alrm->hour != -1 && alrm->hour != now.tm_hour) {
continue;
}
if (alrm->minute != -1 && alrm->minute != now.tm_min) {
continue;
}
alrm->callback(alrm->callback_arg);
}
}
This would then be called from your main while() loop with a frequency depending on the granularity of your alarm. Since the alarm_t defined above has a granularity of 1 minute, that is how often I will call check_alarms(). This example is for FreeRTOS but could be adapted as needs be.
while (1) {
TickType_t tick = xTaskGetTickCount();
if (tick - g_time_last_check > pdMS_TO_TICKS(60000)) {
g_time_last_check = tick;
check_alarms();
}
}
Efficiency
The above is not very efficient if you have a lot of alarms and / or each alarm should be triggered at high frequency. In such a case, you might consider algorithms which sort the alarms based on time until elapsed, and - instead of iterating over the entire array every time - instead employ a one-shot software timer to invoke the relevant callback. It probably isn't the worth the hassle unless you have a very large number of timers though.
Alternatives
Both FreeRTOS and the ESP-IDF include APIs for software timers - but these can not be (easily) used for alarms at a specific time of the day - which you state as a requirement. On the other hand - as you may already know - the ESP32 system-on-a-chip does have a built-in RTC, but I think it is best to use a higher-level interface than communicating with it directly.
A real-time clock is what you are looking for. I've never worked with the ESP32 but apparently it has one and it is bad. However, 5% error might be good enough for you. If it's an option, I would use an external one.

Arduino Function Execution Time

I have an Arduino and an APC220 wireless transceiver. I'm writing a library that reads in data from the APC using the SoftwareSerial class. I originally started with the (incorrect) code below that was causing a seg fault because the i variable is incremented even when there is no data available to read. In cases where it happened to work (by chance when the data was immediately available), this function took approximately 6 milliseconds to execute. When I put the i++; statement in its proper place (above the closing brace immediately above it), the function takes over 270 ms to run. Speed is crucial for this function, so I'm wondering what it is about that statement's placement that causes such a dramatic increase in time.
For the code below, buff is declared as char buff[10]; and sSerial is an instance of SoftwareSerial
unsigned long updateLocation(Marker* marker) {
this->sSerial->print('~');
//initiate request from vision system
this->sSerial->flush();
this->sSerial->print('#');
this->sSerial->print(marker->num);
this->sSerial->print('*');
this->sSerial->flush();
unsigned long start = millis();
int state = 0, i = 0;
while((millis() - start) < 600) {
if(this->sSerial->available()) {
buff[i] = this->sSerial->read();
if(buff[i] == ',') {
buff[i] = 0;
switch(state) {
case 0:
i = -1;
state++;
break;
case 1:
marker->x = atof(buff);
i = -1;
state++;
break;
case 2:
marker->y = atof(buff);
i = -1;
state++;
break;
case 3:
marker->theta = atof(buff);
i = -1;
return (millis() - start);
break;
default:
return 0;
break;
}
}
// Correct location for i++; takes 270 ms to execute
}
// Incorrect location for i++; Takes 6 ms to execute
i++;
}
this->sSerial->print('~');
this->sSerial->flush();
return 0;
}
Assuming that there's data ready and waiting from sSerial, there's no effective difference in the placement of i++.
You said yourself, in most cases the data isn't ready. With the incorrect placement of i++, i quickly grows to greater than the size of buff which causes the segfault.
With the correct placement, your code blocks for up to 600ms waiting for enough data to come in to reach case 3 and return. On average, you're seeing that it takes 270ms for that to happen.
You can test this theory yourself by timing the same function operating directly on a string rather than reading in from serial.
You may want to a) increase your baud rate b) check to see if there's a more efficient software serial implementation you can use c) switch to hardware serial. If you are only using hardware serial currently for debugging output, you can switch that around. Use an FTDI adapter ($6-10 on eBay) to pipe software serial to USB and reserve the hardware serial for your time sensitive function.
You might also be able to reconfigure your code so it doesn't block the whole time waiting. You can read in what's available, store it in a global and go back to your main loop to do something else until there's data available again.
edit: I see now that the APC220 is 9600 baud max. That's pretty slow, so the bottle neck may not be software serial (but you should test it). If the bottle neck is simply the baud rate, you will need to look at optimizing your code not to block waiting for input if there are other things your system can work on while it's waiting.

How to make Arduino toggle a variable after a completion of UART transmission?

I would like to have a bit of code executed (like toggling a flag variable),
after a completion of UART transmission issued by Serial.write(buf, len).
I tried several things to no success. Could someone suggest what would be the best way?
Thanks.
m
According to the code on the Arduino repository (you can see it here, particularly the HardwareSerial, Stream and Print classes) the Serial class functions are not blocking: they use an internal buffer which is emptied by the ISR.
Looking at the code, you have two possibilities.
The first and easiest one is to use the built-in Serial.flush function. This function waits for the uC to complete the sending and returns only when everything has been sent.
The second way is to mimic the flush behavior; this is what you need if you don't want the program to stop (i.e. perform other tasks) and just check if the board is sending data or not. If bit UDRIE0 (register UCSR0B) is set then the library has some data in its queue. At the end, however, you have to wait for the final byte to be sent, so you also have to check that bit TXC0(register UCSR0A) is cleared.
To implement and test this, I wrote this simple program:
unsigned long thisMillis = -1;
unsigned long lastMillis = -1;
char mybuffer[256];
void setup() {
Serial.begin(9600);
}
bool isSending()
{
return bit_is_set(UCSR0B, UDRIE0) || bit_is_clear(UCSR0A, TXC0);
}
void loop() {
int printed = snprintf(mybuffer, 256, "time: %lu\r\n", millis());
Serial.write(mybuffer, printed);
// Just enable ONE of the following methods
//Serial.flush();
//while(isSending());
lastMillis = millis();
}
Then I simulated it.
When neither the flush nor the while are enabled, the board buffers the string until it can. Then, when the buffer becomes full, starts waiting for some chars to go away before filling it again. Consequently we will see a lot of strings in the first milliseconds, then almost equally spaced strings (when the buffer is full). And in fact the output of the program is this one:
time: 0
time: 0
time: 1
time: 1
time: 1
time: 1
time: 2
time: 2
time: 7
time: 17
time: 27
When, however, we uncomment the Serial.flush function, the loop will wait for all the data to get out of the uC before looping again. This leads to equally spaced timestamps just from the beginning, and in fact this is the output
time: 0
time: 9
time: 19
time: 29
time: 39
time: 51
When enabling the while loop the behavior is exactly the same:
time: 0
time: 9
time: 19
time: 29
time: 39
time: 51
So, summing up: if you want to wait for the board to send just go with Serial.flush(); if you want to just test it (and then do something else) test the two bits reported above.
Try this:
void setup() {
Serial.begin(9600);
_SFR_BYTE(UCSR0B) |= _BV(TXCIE0); //Enable TXCIE0 interrupts
Serial.print("When UART finishes to send this text, it will call the routine ISR(USART0_TX_vect) ");
}
void loop() {
}
ISR(USART1_TX_vect)
{
//do whatever you want, but as quick as you can.
}

Basic PIC Programming issues

I am writing PIC code in C and encountered the following problems:
When I write my delay as _delay_ms(500), my code doesn't compile, it says it didn't recognize this instruction. I am using MPLAB.
I want to write a program that would count how many time the push button is pressed then return that value and display it using LED's. I know how to display it, but not how to make the program to wait for the push of the push button on the pickit.
main()
{
TRISA=0;//Sets all ports on A to be outputs
TRISB=1;//Sets all ports on B to be inputs
for(;;){
if(PORTBbits.RB0==1){//When the button is pressed the LED is off
PORTAbits.RA1 =0;
count=count+1;
}
else{
PORTAbits.RA1=1;
count = count +1;
}
if (count > 20){//if count =20 aka 20 button presses the LED turns on
PORTAbits.RA0=1;
}
else{
PORTAbits.RA0=0;
}
}
}
There are a few issues:
Assuming you're using a PIC24 or a dsPIC, you need to include libpic30.h
Before you include libpic30.h you need to #define FCY to be your instruction rate so that the delay takes the correct number of cycles. See the comments in the libpic30.h file for details.
The function is __delay_ms not _delay_ms. Note that there are two underscores at the beginning.
The name is all lower case, not Delay_ms as in your comment.
You need to add delay in your code when you detect a key is pressed. As you are saying the _delay_ms(500) is not recognized, You can try something like following:
unsigned char x;
// Just waste a few cycles to create delay
for (x = 0; x < 100; x++)
{
// No operation instruction
Nop();
}
You can create your own delay function with specific number of iterations of this for loop. Measure the exact delay created by this function using a profiler if you need. IMO any arbitrary delay, like say 100 iterations as stated above shall work.

Arduino - How to multithread this application

I'm writing a garage door opener and monitor.
The monitor receives the door status via another Arduino over RF (315 MHz). The code below works, but I feel that I shouldn't need to check the status every time I make a request to the server to open the door. Is there a way to split the code out so that I check the door status every 20 seconds, and the opening and closing of the garage are on demand?
Here is the code:
https://github.com/dhysong/ArduinoGarageOpener/blob/master/src/GarageDoorOpener/GarageDoorOpener.ino
Based on this post: http://arduino.cc/forum/index.php/topic,5686.0.html
I was able to add a mult-threading like capability to my app. Source code has been updated to reflect the change.
Here's the pertinent piece:
boolean cycleCheck(unsigned long *lastMillis, unsigned int cycle)
{
unsigned long currentMillis = millis();
if(currentMillis - *lastMillis >= cycle)
{
*lastMillis = currentMillis;
return true;
}
else
return false;
}
Here's the github code for anyone that might benefit: https://github.com/dhysong/ArduinoGarageOpener/blob/master/src/GarageDoorOpener/GarageDoorOpener.ino

Resources