Blink: turn the built-in LED on and off.Sensor stream: read accelerometer, gyroscope, and magnetometer data from the Nano 33 BLE Sense IMU.setup() runs once.loop() runs repeatedly.#include statements;setup() and loop();if statements;while loops;struct values for organizing sensor data;
1
2
3
4
5
6
7
8
9
10
11
12
#include <Arduino.h>
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
.ino extension in the Arduino IDE, or it may be written as a .cpp file in PlatformIO.#include statement tells the compiler to bring in declarations from another file. For this sketch, Arduino.h gives us access to names such as: pinModedigitalWritedelayLED_BUILTINOUTPUTHIGHLOWmain():
1
2
3
4
5
6
7
void setup() {
// runs once
}
void loop() {
// runs repeatedly
}
1
2
3
4
5
6
7
8
9
int main() {
initArduinoHardware();
setup();
while (true) {
loop();
}
}
A microcontroller program maintains a continuous with hardware:
- configure pins and sensors;
- check whether new data is available;
- read inputs;
- compute something small;
- update outputs;
- repeat. That is the
setup()/loop()model.
setup() is for one-time configuration
1
2
3
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
loop() is for repeated behavior
1
2
3
4
5
6
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
loop() repeatedly turns the LED on and off by write (send) a HIGH voltage value and a LOW voltage value to the LED_BUILTIN pin, with a delay of 1000ms between sends.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <Arduino.h>
#include <Arduino_BMI270_BMM150.h>
void setup() {
Serial.begin(9600);
while (!Serial) {
; // wait for serial monitor
}
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1) {
delay(1000);
}
}
Serial.println("Serial ready. Initializing IMU...");
Serial.println("IMU ready.");
Serial.println("Ax Ay Az | Gx Gy Gz | Mx My Mz");
}
void loop() {
float ax, ay, az;
float gx, gy, gz;
float mx, my, mz;
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(ax, ay, az);
}
if (IMU.gyroscopeAvailable()) {
IMU.readGyroscope(gx, gy, gz);
}
if (IMU.magneticFieldAvailable()) {
IMU.readMagneticField(mx, my, mz);
}
char line[160];
snprintf(line,sizeof(line),
"A:%.3f,%.3f,%.3f | G:%.3f,%.3f,%.3f | M:%.3f,%.3f,%.3f",
ax, ay, az,
gx, gy, gz,
mx, my, mz
);
Serial.println(line);
delay(200);
}
For the IMU sensor sketch, we include another library:
1 2 #include <Arduino.h> #include <Arduino_BMI270_BMM150.h>The second include gives us access to the
IMUobject and its sensor-reading functions.
1
2
3
float ax, ay, az;
float gx, gy, gz;
float mx, my, mz;
| Type | Meaning | Example use |
|---|---|---|
int | whole number | pin number, counter |
float | decimal number | acceleration, gyroscope, magnetometer reading |
bool | true/false | whether data is available |
char | single character | simple serial command |
unsigned long | large non-negative integer | time from millis() |
Sensor, sensor readings are not usually integers, so float is appropriate.if statements.ax, ay, and az.
1
2
3
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(ax, ay, az);
}
1
2
3
4
5
6
7
if (IMU.gyroscopeAvailable()) {
IMU.readGyroscope(gx, gy, gz);
}
if (IMU.magneticFieldAvailable()) {
IMU.readMagneticField(mx, my, mz);
}
The setup code uses a while loop:
1
2
3
while (!Serial) {
; // wait for serial monitor
}
This means while the serial connection is not ready, keep waiting.
1
readMagneticField(mx, my, mz)
readMagneticField is the function name.mx, my, mz are the variables where readMagneticField write the contents of the magnetic sensors into.Setup a pothole detector: If the sensor detects bounciness (
vertical acceleration) that is greater than a certain value, change the LED light toRED. Otherwise, keep it asGREEN.
1
2
3
4
5
6
7
8
9
10
11
12
void setup() {
initialize LED with OUTPUT using LEDR, LEDG, and LEDB;
initialize IMU;
possibly initialize Serial for debugging purposes;
}
void loop() {
capture accelerometer values into ax, ay, az;
if az is greaer than a certain value (1 means stationary), change LED to RED;
else change LED to GREEN;
delay
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <Arduino.h>
#include <Arduino_BMI270_BMM150.h>
void setup() {
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1) {
delay(1000);
}
}
pinMode(LEDR, OUTPUT);
pinMode(LEDG, OUTPUT);
pinMode(LEDB, OUTPUT);
}
void loop() {
float ax, ay, az;
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(ax, ay, az);
}
if (az > 2) {
// Red
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);
} else {
// Green
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, HIGH);
}
delay(200);
}
void, which means the function does not return a value.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <Arduino.h>
#include <Arduino_BMI270_BMM150.h>
void setred() {
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);
}
void setgreen() {
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, HIGH);
}
void beginLED() {
pinMode(LEDR, OUTPUT);
pinMode(LEDG, OUTPUT);
pinMode(LEDB, OUTPUT);
}
void setup() {
IMU.begin();
beginLED();
}
void loop() {
float ax, ay, az;
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(ax, ay, az);
}
if (az > 1.5) {
setred();
} else {
setgreen();
}
delay(200);
}
loop code detect which color, then run a single emitColor function whose parameter is the integer code for coloring 0: green1: yellow2: red
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <Arduino.h>
#include <Arduino_BMI270_BMM150.h>
void setred() {
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);
}
void setyellow() {
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, HIGH);
}
void setgreen() {
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, HIGH);
}
void beginLED() {
pinMode(LEDR, OUTPUT);
pinMode(LEDG, OUTPUT);
pinMode(LEDB, OUTPUT);
}
void emitColor(int color) {
if (color == 0) {
setgreen();
} else if (color == 1) {
setyellow();
} else if (color == 2) {
setred();
}
}
void setup() {
IMU.begin();
beginLED();
}
void loop() {
float ax, ay, az;
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(ax, ay, az);
}
if (az < 1.2) {
emitColor(0);
} else if (az > 1.2 && az < 1.8) {
emitColor(1);
} else {
emitColor(2);
}
delay(200);
}
main.cpp file in version 2 is getting lengthy.src called led_controller.cpp.include to let PlatformIO knows about the new structure.
1
2
3
4
5
void beginLED();
void emitColor(int color);
void setred();
void setyellow();
void setgreen();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <Arduino.h>
#include "led_controller.h"
void setred() {
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);
}
void setyellow() {
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, HIGH);
}
void setgreen() {
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, HIGH);
}
void beginLED() {
pinMode(LEDR, OUTPUT);
pinMode(LEDG, OUTPUT);
pinMode(LEDB, OUTPUT);
}
void emitColor(int color) {
if (color == 0) {
setgreen();
} else if (color == 1) {
setyellow();
} else if (color == 2) {
setred();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <Arduino.h>
#include <Arduino_BMI270_BMM150.h>
#include "led_controller.h"
void setup() {
IMU.begin();
beginLED();
}
void loop() {
float ax, ay, az;
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(ax, ay, az);
}
if (az < 1.2) {
emitColor(0);
} else if (az > 1.2 && az < 1.8) {
emitColor(1);
} else {
emitColor(2);
}
delay(200);
}
While modern C++ has added many new features that are not really C-like, historically, C++ began as C with Classes, and therefore, it inherited much of C’s syntax and builtin structures, including struct.
A struct groups related data together. Going back to the Sensors sketch, instead of keeping x, y, and z as separate variables, we can define a vector-like struct:
1
2
3
4
5
struct Vec3D {
float x;
float y;
float z;
};
Then we can create 3 variables representing the three sensors, rather than the 9 variables.
1
2
3
Vec3D acceleration = {0.0F, 0.0F, 0.0F};
Vec3D gyroscope = {0.0F, 0.0F, 0.0F};
Vec3D magnetometer = {0.0F, 0.0F, 0.0F};
The fields of a a struct can be accessed using the dot operator:
1
2
3
Serial.print(acceleration.x);
Serial.print(acceleration.y);
Serial.println(acceleration.z);
We can group all sensor values into one sample:
1
2
3
4
5
6
7
8
9
10
11
struct Vec3D {
float x;
float y;
float z;
};
struct ImuSample {
Vec3 acceleration;
Vec3 gyroscope;
Vec3 magnetometer;
};
Then the loop can work with one ImuSample variable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void loop() {
ImuSample sample = {
{0.0F, 0.0F, 0.0F},
{0.0F, 0.0F, 0.0F},
{0.0F, 0.0F, 0.0F}
};
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(sample.acceleration.x,
sample.acceleration.y,
sample.acceleration.z);
}
if (IMU.gyroscopeAvailable()) {
IMU.readGyroscope(sample.gyroscope.x,
sample.gyroscope.y,
sample.gyroscope.z);
}
if (IMU.magneticFieldAvailable()) {
IMU.readMagneticField(sample.magnetometer.x,
sample.magnetometer.y,
sample.magnetometer.z);
}
delay(200);
}
This is longer at first, but the organization becomes valuable when programs grow.
struct is one of the first tools students can use to represent structured sensor data cleanly.instantiated from a class.Serial is an object representing serial communication;IMU is an object representing the inertial measurement unit;begin, println, and readAcceleration are member functions.
1
2
3
4
Serial.begin(9600);
Serial.println("IMU ready.");
IMU.begin();
IMU.readAcceleration(ax, ay, az);