1 /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #if defined(ARDUINO) && !defined(ARDUINO_ARDUINO_NANO33BLE)
17 #define ARDUINO_EXCLUDE_CODE
18 #endif  // defined(ARDUINO) && !defined(ARDUINO_ARDUINO_NANO33BLE)
19 
20 #ifndef ARDUINO_EXCLUDE_CODE
21 
22 #include "tensorflow/lite/micro/examples/magic_wand/accelerometer_handler.h"
23 
24 #include <Arduino.h>
25 #include <Arduino_LSM9DS1.h>
26 
27 #include "tensorflow/lite/micro/examples/magic_wand/constants.h"
28 
29 // A buffer holding the last 200 sets of 3-channel values
30 float save_data[600] = {0.0};
31 // Most recent position in the save_data buffer
32 int begin_index = 0;
33 // True if there is not yet enough data to run inference
34 bool pending_initial_data = true;
35 // How often we should save a measurement during downsampling
36 int sample_every_n;
37 // The number of measurements since we last saved one
38 int sample_skip_counter = 1;
39 
SetupAccelerometer(tflite::ErrorReporter * error_reporter)40 TfLiteStatus SetupAccelerometer(tflite::ErrorReporter* error_reporter) {
41   // Switch on the IMU
42   if (!IMU.begin()) {
43     TF_LITE_REPORT_ERROR(error_reporter, "Failed to initialize IMU");
44     return kTfLiteError;
45   }
46 
47   // Make sure we are pulling measurements into a FIFO.
48   // If you see an error on this line, make sure you have at least v1.1.0 of the
49   // Arduino_LSM9DS1 library installed.
50   IMU.setContinuousMode();
51 
52   // Determine how many measurements to keep in order to
53   // meet kTargetHz
54   float sample_rate = IMU.accelerationSampleRate();
55   sample_every_n = static_cast<int>(roundf(sample_rate / kTargetHz));
56 
57   TF_LITE_REPORT_ERROR(error_reporter, "Magic starts!");
58 
59   return kTfLiteOk;
60 }
61 
ReadAccelerometer(tflite::ErrorReporter * error_reporter,float * input,int length)62 bool ReadAccelerometer(tflite::ErrorReporter* error_reporter, float* input,
63                        int length) {
64   // Keep track of whether we stored any new data
65   bool new_data = false;
66   // Loop through new samples and add to buffer
67   while (IMU.accelerationAvailable()) {
68     float x, y, z;
69     // Read each sample, removing it from the device's FIFO buffer
70     if (!IMU.readAcceleration(x, y, z)) {
71       TF_LITE_REPORT_ERROR(error_reporter, "Failed to read data");
72       break;
73     }
74     // Throw away this sample unless it's the nth
75     if (sample_skip_counter != sample_every_n) {
76       sample_skip_counter += 1;
77       continue;
78     }
79     // Write samples to our buffer, converting to milli-Gs and rotating the axis
80     // order for compatibility with model (sensor orientation is different on
81     // Arduino Nano BLE Sense compared with SparkFun Edge).
82     // The expected orientation of the Arduino on the wand is with the USB port
83     // facing down the shaft towards the user's hand, with the reset button
84     // pointing at the user's face:
85     //
86     //                  ____
87     //                 |    |<- Arduino board
88     //                 |    |
89     //                 | () |  <- Reset button
90     //                 |    |
91     //                  -TT-   <- USB port
92     //                   ||
93     //                   ||<- Wand
94     //                  ....
95     //                   ||
96     //                   ||
97     //                   ()
98     //
99     const float norm_x = -z;
100     const float norm_y = y;
101     const float norm_z = x;
102     save_data[begin_index++] = norm_x * 1000;
103     save_data[begin_index++] = norm_y * 1000;
104     save_data[begin_index++] = norm_z * 1000;
105     // Since we took a sample, reset the skip counter
106     sample_skip_counter = 1;
107     // If we reached the end of the circle buffer, reset
108     if (begin_index >= 600) {
109       begin_index = 0;
110     }
111     new_data = true;
112   }
113 
114   // Skip this round if data is not ready yet
115   if (!new_data) {
116     return false;
117   }
118 
119   // Check if we are ready for prediction or still pending more initial data
120   if (pending_initial_data && begin_index >= 200) {
121     pending_initial_data = false;
122   }
123 
124   // Return if we don't have enough data
125   if (pending_initial_data) {
126     return false;
127   }
128 
129   // Copy the requested number of bytes to the provided input tensor
130   for (int i = 0; i < length; ++i) {
131     int ring_array_index = begin_index + i - length;
132     if (ring_array_index < 0) {
133       ring_array_index += 600;
134     }
135     input[i] = save_data[ring_array_index];
136   }
137 
138   return true;
139 }
140 
141 #endif  // ARDUINO_EXCLUDE_CODE
142