-
Notifications
You must be signed in to change notification settings - Fork 5
Fun with modern Cpp
Here a few things from the more modern corners of c++ which can be useful in embedded applications.
If you have a class with a default constructor (parameter less constructor) you can easily declare arrays of objects of this class. Here an example showing how to do this with Servos
#include "Servo.h"
Servo servos[5]; // array of 5 Servo objects
void setup()
{
servos[0].attach(3); // attach pins to the Servos
servos[1].attach(7);
//...
}
However, if the constructor of your class needs parameters you need to pass them as shown below for the Encoder class. The Encoder constructor requires two pin numbers for phase A and phase B respectively.
#include "Encoder.h"
Encoder encoders[]{{1,2}, {4,7}, {0,15}}; // constructs 3 encoders at pins (1,2), (4,7) and (0,15)
constexpr int nrOfEncoders = sizeof(encoders) / sizeof(encoders[0]);
void setup(){}
void loop()
{
for (int i = 0; i < nrOfEncoders; i++)
{
Serial.println(encoders[i].read());
}
delay(200);
}
Initializer lists can be useful if you need to do something for a list of arbitrary objects. The following example shows how to set the pinMode of a bunch of pins to output:
void setup()
{
constexpr uint8_t pinA = 3, LED = 13, STP = 7;
for(uint8_t pin : {pinA, LED, STP}) // for each pin in the initializer list
{
pinMode(pin, OUTPUT);
}
}
You can also use initializer lists as parameters to functions. Let's define a enhancement for the pinMode function which takes a list of pins instead of only one.
void pinMode(std::initializer_list<uint8_t> pins, uint8_t mode){
for(uint8_t pin : pins){
pinMode(pin, mode);
}
}
// usage:
void setup(){
constexpr uint8_t pinA = 3, LED = 13, STP = 7;
pinMode({pinA, LED, STP}, OUTPUT);
}
In the Arduino ecosystem callbacks are often required to be of type void(*)()
. I.e., simple pointers to void functions. If, for example, you want to attach callbacks to pin interrupts you'd do something like:
void myCallback_0() {
Serial.println("pin0");
}
void myCallback_1(){
Serial.println("pin1");
}
void setup(){
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
attachInterrupt(0, myCallback_0, FALLING);
attachInterrupt(1, myCallback_1, FALLING);
}
void loop(){
}
While the code shown above works, it might get tedious if we need to attach a lot of interrupts. We can improve it by defining only one callback and passing information about the pin to the callback instead. However, since such a callback requires a parameter it is not compatible to the void(*f)()
functions required by attachInterrupt
anymore. Thus, it can not be attached to the pin interrupt directly.
However, we can use anonymous functions, aka lambda expressions to achieve our goal.
#include "Arduino.h"
void myCallback(int pin) {
Serial.print("pin");
Serial.println(pin);
}
void setup(){
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
attachInterrupt(0, [] { myCallback(0); }, FALLING);
attachInterrupt(1, [] { myCallback(1); }, FALLING);
}
void loop(){
}
Basically, this tells the compiler to generate code equivalent to this:
//...
void relay0(void){
myCallback(0);
}
void relay1(void){
myCallback(1);
}
//...
attachInterrupt(0,relay0, FALLING);
attachInterrupt(1,relay1, FALLING);
... To be continued...
Teensy is a PJRC trademark. Notes here are for reference and will typically refer to the ARM variants unless noted.