Arduino - How to multithread this application - arduino

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

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.

Arduino command timeout

I'm currently working with a Arduino to forfill one of my DIY projects.
As it currently stands, I have my fingerprint scanner (GT-511C3) connected to my Arduino and that works great. I'm able to verify enrolled fingerprints.
The verifying of the finterprints happends via Raspberry Pi command (whom is initiated by a button press)
Logically, this means, when the button is pressed, the Raspberry Pi sends a 'validate' command to the Arduino, whom in return ask the Fingerprint scanner to run the validate command.
However, I would like to have a timeout after the validate command it sent. The timeout needs to make sure that if the button pressed (and the validate command is initated) but no one puts their finger on the machine it timeouts and reverts back to a state where it waits for the validate command.
I'm not able to complete this. This is the code I've tried:
#include "FPS_GT511C3.h"
#include "SoftwareSerial.h"
FPS_GT511C3 fps(4, 5);
int val = 0;
void setup()
{
Serial.begin(9600);
delay(100);
fps.Open();
fps.SetLED(false);
}
void loop(){
if (Serial.available() > 0) {
Continue:
if(Serial.find("validate")){
fps.SetLED(true);
do {
++val;
delay(100);
}
while(fps.IsPressFinger() == false || val > 150);
if(val <= 150){
fps.SetLED(false);
goto Continue;
}
if (fps.IsPressFinger()){
fps.CaptureFinger(false);
int id = fps.Identify1_N();
if (id <200)
{
Serial.print("Verified ID:");
Serial.println(id);
fps.SetLED(false);
}
else
{
Serial.println("Finger not found");
fps.SetLED(false);
}
}
else
{
Serial.println("Please press finger");
}
delay(100);
}
}
}
The code otherwise works fine, if the finger is placed and validated, it turns off and goes back to waiting for another validate command.
Any help would be greatly appreciated!
First, get rid of the label and goto. There is no justification for it here; it's considered bad programming practice and shouldn't be used unless you know exactly what you're doing. Only in Assembly is it okay to use goto (equivalent to JMPs) liberally.
Next, your while condition is wrong. If you try to interpret it, you'll notice it doesn't make any sense:
Wait for as long as nobody has placed a finger or if the timeout has expired.
What you probably want is:
Wait for as long as nobody has placed a finger and the timeout has not expired.
which translates to:
while(fps.IsPressFinger() == false && val < 150);
The IF condition that follows, is also wrong and should mean:
if the timeout has expired
translating to:
if(val >= 150){
fps.SetLED(false);
val = 0;
continue;
}
Notice the use of the continue keyword which restarts a loop. To make it legit, change if (Serial.available() > 0) to while (Serial.available() > 0).

Are interrupts the right thing to use for my robot?

So I have this old motorized wheelchair that I am trying to convert into a robot. I replaced the original motor driver with a sabertooth 2x12 and I’m using an Arduino micro to talk to it. The motors shaft goes all the way threw so I attached a magnet and a Hall Effect sensor to the back side to act as a rotary encoder. My current goal is to be able to tell the robot to move forward a certain amount of feet then stop. I wrote some code to do this linearly however this didn't work so well. Then I learned about interrupts and that sounded like exactly what I needed. So I tried my hand at that and things went wrong on several different levels.
LEVEL ONE: I have never seemed to be able to properly drive the motors it seems like any time I put the command to turn them on inside of a loop or if statement they decide to do what they want and move sporadically and unpredictably
LEVEL TWO: I feel like the interrupts are interrupting themselves and the thing I set in place to stop the wheels from moving forward because I can tell it to move 14 rotary encoder clicks forward and one wheel will continue moving way past 1000 clicks while the other stops
LEVEL THREE: couple times I guess I placed my interrupts wrong because when I uploaded the code windows would stop recognizing the Arduino and my driver would break until I uploaded the blink sketch after pressing the reset button which also reloaded and fixed my drivers. Then if I deleted one of my interrupts it would upload normally.
LEVEL FOUR: my Hall Effect sensors seem to not work right when the motors are on. They tend to jump from 1 to 200 clicks in a matter of seconds. This in turn floods my serial port and crashes the Arduino ide.
So as you can see there are several flaws somewhere in the system whether it’s hardware or software I don't know. Am I approaching this the right way or is there some Arduino secret I don’t know about that would make my life easier? If I am approaching this right could you take a look at my code below and see what I’m doing wrong.
#include <Servo.h>//the motor driver uses this library
Servo LEFT, RIGHT;//left wheel right wheel
int RclickNum=0;//used for the rotory encoder
int LclickNum=0;//these are the number of "clicks" each wheel has moved
int D =115;//Drive
int R =70;//Reverse
int B =90;//Break
int Linterrupt = 1;//these are the interrupt numbers. 0 = pin 3 and 1 = pin 2
int Rinterrupt = 0;
int clickConvert = 7;// how many rotery encoder clicks equal a foot
void setup()
{
Serial.begin(9600); //starting serial communication
LEFT.attach( 9, 1000, 2000);//attaching the motor controller that is acting like a servo
RIGHT.attach(10, 1000, 2000);
attachInterrupt(Linterrupt, LclickCounter, FALLING);//attaching the rotory encoders as interrupts that will
attachInterrupt(Rinterrupt, RclickCounter, FALLING);//trip when the encoder pins go from high to low
}
void loop()
{//This is for controling the robot using the standard wasd format
int input= Serial.read();
if(input == 'a')
left(2);
if(input == 'd')
right(2);
if(input == 'w')
forward(2);
if(input == 's')
backward(2);
if(input == 'e')
STOP();
}
void forward(int feet)//this is called when w is sent threw the serial port and is where i am testing all of my code.
{
interrupts(); //turn on the interrupts
while(RclickNum < feet * clickConvert || LclickNum < feet * clickConvert)// while either the left or right wheel hasnt made it to the desired distance
{
if(RclickNum < feet * clickConvert)//check if the right wheel has gone the distance
RIGHT.write(D); //make the right wheel move
else
RIGHT.write(B);//stop the right wheel
if(LclickNum < feet * clickConvert)
LEFT.write(D);
else
LEFT.write(B);
}
noInterrupts();//stop the interrupts
resetCount();//set the click counters back to zero
}
//once i have the forward function working i will implament it through out the other functions
//----------------------------------------------------------------------
void backward(int feet)
{
RIGHT.write(R);
LEFT.write(R);
}
void left(int feet)
{
RIGHT.write(D);
LEFT.write(R);
}
void right(int feet)
{
RIGHT.write(R);
LEFT.write(D);
}
void STOP()
{
resetCount();
RIGHT.write(B);
LEFT.write(B);
}
void LclickCounter()//this is called by the left encoder interrupt
{
LclickNum++;
Serial.print("L");
Serial.println(LclickNum);
}
void RclickCounter()//this is called by the right encoder interrupt
{
RclickNum++;
M Serial.print("R");
Serial.println(RclickNum);
}
void resetCount()
{
RclickNum=0;
LclickNum=0;
}
don't use interrupt() and nointerrupt() (or cli() and sei()) as they will stop timer and serial interrupt, breaking a lot of things. Just set to 0 the counting variable OR use detachInterrupt and attachInterrupt.
variable used inside interrupt AND normal execution flow should be declared as volatile, or their value my be unsyncornized. So declare them like volatile int RclickNum=0;
interrupt should be fast to execute, as by default other interrupt will NOT execute while inside an interrupt.
NEVER use Serial inside interrupt; if Serial buffer is full, it will call Serial.flush(), that will wait for Serial interrupt of byte written, but because you are alreadi inside an interrupt will never happen...dead lock aka you code hangs forever!
because your "moving" function use quite a long time to execute, if multiple command arrive to the serial, thay will remain isnode the buffer until readed. So if in the terminal you write "asd" and then "e", you will see robot go left, backward, right, stop (yes, actually the stop function is not usefull as it does nothing because your "moving" function are "blocking", that mean they won't return until they ended, so the loop() code (and the read of "e") will not execute until the buffer of serial has been processed.

QTime or QTimer wait for timeout

I'm writting a Qt application, and I have a four hour loop (in a seperate thread). In that four hour loop, I have to:
do stuff with the serial port;
wait a fixed period of time;
do some more stuff with the serial port;
wait an arbitrary amount of time.
when 500ms have past, do more stuff;
Go to 1. and repeat for four hours.
Currently, my method of doing this is really bad, and crashes some computers. I have a whole bunch of code, but the following snippet basically shows the problem. The CPU goes to ~100% and eventually can crash the computer.
void CRelayduinoUserControl::wait(int timeMS)
{
int curTime = loopTimer->elapsed();
while(loopTimer->elapsed() < curTime + timeMS);
}
I need to somehow wait for a particular amount of time to pass before continuing on with the program. Is there some function which will just wait for some arbitrary period of time while keeping all the timers going?
If you want to sleep in QT, use QTimer. Just connect the timeout signal from the timer to the slot that contains the code you want to do every x amount of time.
IMO you should use the signal/slot mechanism in spite of waiting inside a while. Code could be something like that:
#define STATE_1 0x01
#define STATE_2 0x02
#define STATE_3 0x03
.....
QTimer * timer = new QTimer();
connect(timer,SIGNAL(timeout()),SLOT(timerSlot()));
int state = STATE_1 ;
timer->timerSlot()
....
void timerSlot() {
switch(state) {
case STATE_1:
// do stuff with the serial port
state = STATE_2;
timer->start(time_to_wait_in_ms);
break;
case STATE_2:
// do more stuff with the serial port
state = STATE_3;
timer->start(500);
break;
case STATE_3:
// do more stuff
state = STATE_1;
timer->start(1000*60*60*4);
break;
}

Resources