hello everyone.
I have recently been fascinated with how NodeJS works. Well, it is interesting to see how it is possible to execute multiple tasks at the same time in a single thread. and I asked myself, is it possible to do this same thing on an Arduino UNO?
Well, I was doing a little research on how it works. and I realized that Nodejs uses 2 algorithms to achieve concurrency on a single thread.
-
Event Loop: is responsible for saving and executing a list of tasks, each of these functions could easily be a coroutine.
-
Coroutines: are a type of function that have the ability to suspend their execution, in order to resume them in the future.
So I decided to replicate this same concurrent system on an Arduino UNO.
I started with the easiest part. the coroutines. Since C/C++ doesn’t have built-in coroutine support, I have made my own coroutine system. To achieve this, I found a very useful algorithm called Duff’s Device, This algorithm is not directly related to Coroutines, but will help us to make them.
Ok, The main idea is to create a static state variable, which will allow us to remember the state of the coroutine. then using a switch statement, we have to split the function into chunks, and using the state variable, we can execute the part we need. If you don’t understand what I’m talking about? Well, it’s something like this:
int coroutine(){
static int state = 0;
switch(state){
case 0: do { printf("Hello World 0"); state=1; return 1; case 1:; } while(0);
do { printf("Hello World 1"); state=2; return 1; case 2:; } while(0);
do { printf("Hello World 2"); state=3; return 1; case 3:; } while(0);
do { printf("Hello World 3"); state=4; return 1; case 4:; } while(0);
do { printf("Hello World 4"); state=5; return 1; case 5:; } while(0);
do { printf("Hello World 5"); state=0; return 1; } while(0);
}
return -1;
}
void setup(){
Serial.begin( 9600 );
}
void loop(){
coroutine();
delay(1000);
}
It’s great, right? But imagine writing more complex coroutines using this method. Then it occurred to me to simplify the creation of Duff’s device using macros. Here are some:
#define coNext do { _state_ = _LINE_; return 1; case _LINE_:; } while (0)
#define coGoto(VALUE) do { _state_ = VALUE ; return 1; } while (0)
#define coYield(VALUE) do { _state_ = VALUE ; return 1; case VALUE:; } while (0)
/*──────────────────────────────────────────────────────*/
#define coStart static int _state_ = 0; { switch(_state_) { case 0:;
#define coEnd do { _state_ = 0; return -1; } while (0)
#define coStop } _state_ = 0; return -1; }
#define coSet(VALUE) _state_ = VALUE
#define coGet _state_
NOTE: I also added other very useful macros.
#define coDelay(VALUE) do { static auto tm = millis()+VALUE; while( millis() < tm ){ coNext; } tm = millis()+VALUE; break; } while (0)
#define coUDelay(VALUE) do { static auto tm = micros()+VALUE; while( micros() < tm ){ coNext; } tm = micros()+VALUE; break; } while (0)
If we replicate the previous example, we would have something like this:
int coroutine(){
coStart
printf("Hello World 0"); coNext;
printf("Hello World 2"); coNext;
printf("Hello World 3"); coNext;
printf("Hello World 4"); coNext;
printf("Hello World 5"); coNext;
printf("Hello World 6");
coGoto(0);
coStop
}
void setup(){
Serial.begin( 9600 );
}
void loop(){
coroutine();
delay(1000);
}
Now what about if we apply this thing in a real arduino project?
Suppose we want to create a program on Arduino UNO, which executes 3 asynchronous processes. which will allow us to turn on and off a series of LEDs. Well, here is the code:
int coroutine1(){
static bool b=0;
coStart
digitalWrite(7,b);
coDelay(300); b=!b;
coGoto(0);
coStop
}
int coroutine2(){
static bool b=0;
coStart
digitalWrite(6,b);
coDelay(1000); b=!b;
coGoto(0);
coStop
}
int coroutine3(){
static int x=0; static bool b=0;
unsigned char pin[] = { 13, 12, 11, 10, 9, 8 };
coStart
while( x-->0 ){
digitalWrite( pin[x], b );
coDelay(100);
} b=!b;
coGoto(0);
coStop
}
void setup(){
unsigned char pin[] = { 13, 12, 11, 10, 9, 8, 7, 6 };
for( auto &x: pin ) pinMode( pin, OUTPUT );
}
void loop(){
coroutine1();
coroutine2();
coroutine3();
}
As you can see, asynchronous programming in Arduino UNO is possible and not complicated at all. I was short of time, and I couldn’t implement an event loop for this demo. But the loop function is a good replacement. If you are interested in this article, I may make a second part, explaining the Event Loop. I hope you liked this project. and see you later.
Before closing this article, I want to introduce you to a framework created by me, called Nodepp. It is a C++ framework that will allow us to write asynchronous code in Arduino with a syntax very similar to NodeJS. I hope you enjoy it.
https://www.arduino.cc/reference/en/libraries/nodepp/
5 posts - 5 participants