Skip to content

Commit 9b9e499

Browse files
committed
Reformat
1 parent 971814d commit 9b9e499

File tree

3 files changed

+265
-6
lines changed

3 files changed

+265
-6
lines changed
303 KB
Loading

content/hardware/03.nano/boards/nano-matter/tutorials/08.ml-magic-wand/content.md

Lines changed: 265 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ The Arduino Nano Matter acts as a digital magic wand 🪄, where sensor data fro
3939
- Modulino Pixels (x1)
4040
- Qwiic cables (x2)
4141
- [USB-C® cable](https://store.arduino.cc/products/usb-cable2in1-type-c) (x1)
42-
- Custom 3D printed parts
42+
- [Custom 3D printed parts](assets/3d-files.zip)
4343

4444
### Software Requirements
4545

@@ -81,20 +81,271 @@ The Modulino are daisy-chained leveraging the Qwiic I2C connection with the Nano
8181

8282
### Programming
8383

84+
Let's go through some important code sections to make this project fully operational, starting with the Protocol Stack setting and required libraries:
85+
8486
In the Arduino IDE upper menu, after selecting the **Nano Matter board** from the Silicon Labs core, navigate to **Tools > Protocol stack** and select **None**.
8587

8688
![Protocol stack configuration](assets/stack.png)
8789

8890
***The code will only compile if the Protocol Stack is set to None.***
8991

90-
You can download the complete project code from [here](assets/magic_wand_modulino.zip).
91-
92-
Let's go through some important code sections to make this application fully operational, starting with the required libraries:
93-
9492
Download the `Modulino.h` library from the Arduino IDE Library Manager. This will enable the support for the Modulino Pixels and the Modulino Movement.
9593

9694
![Modulino Library Installation](assets/modulino.png)
9795

96+
You can download the **complete project code** from [here](assets/magic_wand_modulino.zip) or copy and paste it from the snippet below:
97+
98+
```arduino
99+
#include "SilabsTFLiteMicro.h"
100+
#include "Modulino.h"
101+
102+
#define SEQUENCE_LENGTH 200
103+
#define SIGNAL_CHANNELS 3
104+
105+
#define GESTURE_COUNT 3
106+
#define WING_GESTURE 0
107+
#define RING_GESTURE 1
108+
#define NO_GESTURE 2
109+
110+
#define DETECTION_THRESHOLD 0.5f
111+
112+
static TfLiteTensor* model_input;
113+
static tflite::MicroInterpreter* interpreter;
114+
static int input_length;
115+
static TfLiteTensor *output;
116+
117+
typedef struct model_output {
118+
float gesture[GESTURE_COUNT];
119+
} model_output_t;
120+
121+
typedef float acc_data_t;
122+
123+
ModulinoColor OFF(0, 0, 0);
124+
ModulinoColor YELLOW(255, 255, 0);
125+
126+
ModulinoMovement imu;
127+
ModulinoPixels leds;
128+
129+
void setPixel(int pixel, ModulinoColor color) {
130+
leds.set(pixel, color, 25);
131+
leds.show();
132+
}
133+
134+
bool accelerometer_setup();
135+
void accelerometer_read(acc_data_t* dst, int n);
136+
137+
void setup() {
138+
Serial.begin(115200);
139+
Serial.println("Magic Wand - Silabs TensorFlowLite");
140+
Serial.println("init...");
141+
142+
// Init TFLite model
143+
sl_tflite_micro_init();
144+
145+
// Obtain pointer to the model's input tensor.
146+
model_input = sl_tflite_micro_get_input_tensor();
147+
interpreter = sl_tflite_micro_get_interpreter();
148+
output = sl_tflite_micro_get_output_tensor();
149+
150+
// Print model input parameters
151+
Serial.print("model_input->dims->size = ");
152+
Serial.println(model_input->dims->size);
153+
Serial.print("model_input->dims->data[0] = ");
154+
Serial.println(model_input->dims->data[0]);
155+
Serial.print("model_input->dims->data[1] = ");
156+
Serial.println(model_input->dims->data[1]);
157+
Serial.print("model_input->dims->data[2] = ");
158+
Serial.println(model_input->dims->data[2]);
159+
Serial.print("model_input->type = ");
160+
Serial.println(model_input->type);
161+
162+
// Check model input parameters
163+
if ((model_input->dims->size != 2) || (model_input->dims->data[0] != 1)
164+
|| (model_input->dims->data[1] != SEQUENCE_LENGTH * SIGNAL_CHANNELS)
165+
|| (model_input->type != kTfLiteFloat32)) {
166+
Serial.println("error: bad input tensor parameters in model");
167+
while(1) ;
168+
}
169+
170+
// Print model input length
171+
input_length = model_input->bytes / sizeof(float);
172+
Serial.print("input_length = ");
173+
Serial.println(input_length);
174+
175+
// Initialize accelerometer
176+
bool setup_status = accelerometer_setup();
177+
if (!setup_status) {
178+
Serial.println("error: accelerometer setup failed\n");
179+
while(1) ;
180+
}
181+
182+
// Init led to steady state (blue)
183+
leds.begin();
184+
for (int i = 0; i < 8; i++) {
185+
setPixel(i, BLUE);
186+
}
187+
188+
Serial.println("ready");
189+
}
190+
191+
void loop() {
192+
acc_data_t *dst = (acc_data_t *) model_input->data.f;
193+
194+
// Wait until a significant movement is detected
195+
bool movementDetected = false;
196+
Serial.println("Waiting for significant movement...");
197+
198+
while (!movementDetected) {
199+
uint8_t acceleroStatus;
200+
acceleroStatus = imu.available();
201+
202+
// Proceed only if new data is available
203+
if (acceleroStatus == 1) {
204+
float acceleration[3];
205+
imu.update();
206+
207+
acceleration[0] = imu.getX();
208+
acceleration[1] = imu.getY();
209+
acceleration[2] = imu.getZ();
210+
211+
// Calculate the absolute sum of the acceleration components
212+
float absSum = fabs(acceleration[0]) + fabs(acceleration[1]) + fabs(acceleration[2]);
213+
214+
// If the movement exceeds the threshold, update the state
215+
if (absSum > 1.8) {
216+
movementDetected = true;
217+
Serial.println("Movement detected: start collecting data!");
218+
}
219+
}
220+
}
221+
222+
// Turn off leds when movement is detected
223+
for (int i = 0; i < 8; i++) {
224+
setPixel(i, OFF);
225+
}
226+
227+
// Get accelerometer values
228+
accelerometer_read(dst, input_length);
229+
230+
// Run inference
231+
TfLiteStatus invoke_status = interpreter->Invoke();
232+
233+
if (invoke_status == kTfLiteOk) {
234+
// Analyze the results to obtain a prediction
235+
const model_output_t *output = (const model_output_t *)interpreter->output(0)->data.f;
236+
237+
// Print inference results (Gesture, probability)
238+
int max_i = -1;
239+
float max_val = 0;
240+
for (int i = 0; i < GESTURE_COUNT; i++) {
241+
switch(i) {
242+
case WING_GESTURE: Serial.print("W"); break;
243+
case RING_GESTURE: Serial.print("O"); break;
244+
case NO_GESTURE: Serial.print("No gesture"); break;
245+
}
246+
Serial.print(": ");
247+
Serial.print(interpreter->output(0)->data.f[i]);
248+
Serial.println();
249+
if (output->gesture[i] > max_val) {
250+
max_val = output->gesture[i];
251+
max_i = i;
252+
}
253+
}
254+
255+
// Print the graphical representation of the recognized gesture
256+
if (max_val >= DETECTION_THRESHOLD) {
257+
switch(max_i) {
258+
case WING_GESTURE:
259+
Serial.println("detection = wing (W)");
260+
Serial.println("* *");
261+
Serial.println("* *");
262+
Serial.println("* * *");
263+
Serial.println(" * * * * ");
264+
Serial.println(" * * ");
265+
266+
// Green leds for W gesture
267+
for (int i = 0; i < 10; i++) {
268+
for (int j = 0; j < 8; j++) {
269+
setPixel(j, GREEN);
270+
}
271+
delay(200);
272+
273+
for (int j = 0; j < 8; j++) {
274+
setPixel(j, OFF);
275+
}
276+
delay(200);
277+
}
278+
break;
279+
case RING_GESTURE:
280+
Serial.println("detection = ring (O)");
281+
Serial.println(" ***** ");
282+
Serial.println(" * * ");
283+
Serial.println("* *");
284+
Serial.println(" * * ");
285+
Serial.println(" ***** ");
286+
287+
// Yellow leds for O gesture
288+
for (int i = 0; i < 10; i++) {
289+
for (int j = 0; j < 8; j++) {
290+
setPixel(j, YELLOW);
291+
}
292+
delay(200);
293+
294+
for (int j = 0; j < 8; j++) {
295+
setPixel(j, OFF);
296+
}
297+
delay(200);
298+
}
299+
break;
300+
case NO_GESTURE:
301+
Serial.println("No gesture");
302+
break;
303+
}
304+
}
305+
306+
// reset LEDs to steady state (blue)
307+
for (int i = 0; i < 8; i++) {
308+
setPixel(i, BLUE);
309+
}
310+
} else {
311+
printf("error: inference failed");
312+
}
313+
}
314+
315+
bool accelerometer_setup() {
316+
Modulino.begin();
317+
bool status = imu.begin();
318+
319+
return status;
320+
}
321+
322+
void accelerometer_read(acc_data_t* dst, int n) {
323+
int i = 0;
324+
325+
while (i < n) {
326+
uint8_t acceleroStatus;
327+
acceleroStatus = imu.available();
328+
329+
if (acceleroStatus == 1) {
330+
float acceleration[3];
331+
332+
imu.update();
333+
334+
acceleration[0] = imu.getX();
335+
acceleration[1] = imu.getY();
336+
acceleration[2] = imu.getZ();
337+
338+
dst[i] = (acceleration[0]*1000+4000)/8000;
339+
dst[i+1] = (acceleration[1]*1000+4000)/8000;
340+
dst[i+2] = (acceleration[2]*1000+4000)/8000;
341+
342+
i+=SIGNAL_CHANNELS;
343+
}
344+
}
345+
}
346+
```
347+
***Be aware that your sketch needs the model header `sl_tflite_micro_model.c` that is included in the [complete code download file](assets/magic_wand_modulino.zip).***
348+
98349
After the libraries import, several variables and structures are declared that has to be with the Machine Learning implementation:
99350

100351
```arduino
@@ -182,7 +433,7 @@ void setup() {
182433
while(1) ;
183434
}
184435
185-
// Print model input lenght
436+
// Print model input length
186437
input_length = model_input->bytes / sizeof(float);
187438
Serial.print("input_length = ");
188439
Serial.println(input_length);
@@ -342,7 +593,15 @@ In addition, some helper functions are used:
342593
- `accelerometer_setup()`: This function initialize the Modulino Movement accelerometer and returns true if it did it successfully.
343594
- `accelerometer_read(acc_data_t* dst, int n)`: This function read the accelerometer data, normalize it and prepare the model input buffer.
344595

596+
### Upload the Project Sketch
597+
598+
You can download the code from [here](assets/magic_wand_modulino.zip) or by clicking on the image below:
599+
600+
[![ ](assets/download.png)](assets/magic_wand_modulino.zip)
601+
602+
In the Arduino IDE select the **Arduino Nano Matter** inside the _Silicon Labs_ board package and make sure the **Protocol Stack** is set to _None_.
345603

604+
[Nano Matter Sketch Upload](assets/code.png)
346605

347606

348607

0 commit comments

Comments
 (0)