5
5
6
6
#include " esp-box.hpp"
7
7
8
+ #include " kalman_filter.hpp"
9
+ #include " madgwick_filter.hpp"
10
+
8
11
using namespace std ::chrono_literals;
9
12
10
13
static constexpr size_t MAX_CIRCLES = 100 ;
@@ -80,16 +83,50 @@ extern "C" void app_main(void) {
80
83
return ;
81
84
}
82
85
86
+ // initialize the IMU
87
+ if (!box.initialize_imu ()) {
88
+ logger.error (" Failed to initialize IMU!" );
89
+ return ;
90
+ }
91
+
83
92
// set the background color to black
84
93
lv_obj_t *bg = lv_obj_create (lv_screen_active ());
85
94
lv_obj_set_size (bg, box.lcd_width (), box.lcd_height ());
86
95
lv_obj_set_style_bg_color (bg, lv_color_make (0 , 0 , 0 ), 0 );
87
96
88
97
// add text in the center of the screen
89
98
lv_obj_t *label = lv_label_create (lv_screen_active ());
90
- lv_label_set_text (label, " Touch the screen!\n Press the home button to clear circles." );
91
- lv_obj_align (label, LV_ALIGN_CENTER, 0 , 0 );
92
- lv_obj_set_style_text_align (label, LV_TEXT_ALIGN_CENTER, 0 );
99
+ static std::string label_text =
100
+ " \n\n\n\n Touch the screen!\n Press the home button to clear circles." ;
101
+ lv_label_set_text (label, label_text.c_str ());
102
+ lv_obj_align (label, LV_ALIGN_TOP_LEFT, 0 , 0 );
103
+ lv_obj_set_style_text_align (label, LV_TEXT_ALIGN_LEFT, 0 );
104
+
105
+ /* Create style*/
106
+ static lv_style_t style_line0;
107
+ lv_style_init (&style_line0);
108
+ lv_style_set_line_width (&style_line0, 8 );
109
+ lv_style_set_line_color (&style_line0, lv_palette_main (LV_PALETTE_BLUE));
110
+ lv_style_set_line_rounded (&style_line0, true );
111
+
112
+ // make a line for showing the direction of "down"
113
+ lv_obj_t *line0 = lv_line_create (lv_screen_active ());
114
+ static lv_point_precise_t line_points0[] = {{0 , 0 }, {box.lcd_width (), box.lcd_height ()}};
115
+ lv_line_set_points (line0, line_points0, 2 );
116
+ lv_obj_add_style (line0, &style_line0, 0 );
117
+
118
+ /* Create style*/
119
+ static lv_style_t style_line1;
120
+ lv_style_init (&style_line1);
121
+ lv_style_set_line_width (&style_line1, 8 );
122
+ lv_style_set_line_color (&style_line1, lv_palette_main (LV_PALETTE_RED));
123
+ lv_style_set_line_rounded (&style_line1, true );
124
+
125
+ // make a line for showing the direction of "down"
126
+ lv_obj_t *line1 = lv_line_create (lv_screen_active ());
127
+ static lv_point_precise_t line_points1[] = {{0 , 0 }, {box.lcd_width (), box.lcd_height ()}};
128
+ lv_line_set_points (line1, line_points1, 2 );
129
+ lv_obj_add_style (line1, &style_line1, 0 );
93
130
94
131
// add a button in the top left which (when pressed) will rotate the display
95
132
// through 0, 90, 180, 270 degrees
@@ -129,6 +166,7 @@ extern "C" void app_main(void) {
129
166
},
130
167
.task_config = {
131
168
.name = " lv_task" ,
169
+ .stack_size_bytes = 6 * 1024 ,
132
170
}});
133
171
lv_task.start ();
134
172
@@ -143,6 +181,112 @@ extern "C" void app_main(void) {
143
181
// set the display brightness to be 75%
144
182
box.brightness (75 .0f );
145
183
184
+ // make a task to read out the IMU data and print it to console
185
+ espp::Task imu_task (
186
+ {.callback = [&label, &line0, &line1](std::mutex &m, std::condition_variable &cv) -> bool {
187
+ // sleep first in case we don't get IMU data and need to exit early
188
+ {
189
+ std::unique_lock<std::mutex> lock (m);
190
+ cv.wait_for (lock, 10ms);
191
+ }
192
+ static auto &box = espp::EspBox::get ();
193
+ static auto imu = box.imu ();
194
+
195
+ auto now = esp_timer_get_time (); // time in microseconds
196
+ static auto t0 = now;
197
+ auto t1 = now;
198
+ float dt = (t1 - t0) / 1'000'000 .0f ; // convert us to s
199
+ t0 = t1;
200
+
201
+ std::error_code ec;
202
+ // get accel
203
+ auto accel = imu->get_accelerometer (ec);
204
+ if (ec) {
205
+ return false ;
206
+ }
207
+ auto gyro = imu->get_gyroscope (ec);
208
+ if (ec) {
209
+ return false ;
210
+ }
211
+ auto temp = imu->get_temperature (ec);
212
+ if (ec) {
213
+ return false ;
214
+ }
215
+
216
+ // with only the accelerometer + gyroscope, we can't get yaw :(
217
+ float roll = 0 , pitch = 0 ;
218
+ static constexpr float angle_noise = 0 .001f ;
219
+ static constexpr float rate_noise = 0 .1f ;
220
+ static espp::KalmanFilter<2 > kf;
221
+ kf.set_process_noise (rate_noise);
222
+ kf.set_measurement_noise (angle_noise);
223
+ static constexpr float beta = 0 .1f ; // higher = more accelerometer, lower = more gyro
224
+ static espp::MadgwickFilter f (beta);
225
+
226
+ f.update (dt, accel.x , accel.y , accel.z , gyro.x * M_PI / 180 .0f , gyro.y * M_PI / 180 .0f ,
227
+ gyro.z * M_PI / 180 .0f );
228
+ float yaw; // ignore / unused since we only have 6-axis
229
+ f.get_euler (roll, pitch, yaw);
230
+ pitch *= M_PI / 180 .0f ;
231
+ roll *= M_PI / 180 .0f ;
232
+
233
+ std::string text = fmt::format (" {}\n\n\n\n\n " , label_text);
234
+ text += fmt::format (" Accel: {:02.2f} {:02.2f} {:02.2f}\n " , accel.x , accel.y , accel.z );
235
+ text += fmt::format (" Gyro: {:03.2f} {:03.2f} {:03.2f}\n " , gyro.x * M_PI / 180 .0f ,
236
+ gyro.y * M_PI / 180 .0f , gyro.z * M_PI / 180 .0f );
237
+ text +=
238
+ fmt::format (" Angle: {:03.2f} {:03.2f}\n " , roll * 180 .0f / M_PI, pitch * 180 .0f / M_PI);
239
+ text += fmt::format (" Temp: {:02.1f} C\n " , temp);
240
+
241
+ // use the pitch to to draw a line on the screen indiating the
242
+ // direction from the center of the screen to "down"
243
+ int x0 = box.lcd_width () / 2 ;
244
+ int y0 = box.lcd_height () / 2 ;
245
+
246
+ float vx = sin (pitch);
247
+ float vy = -cos (pitch) * sin (roll);
248
+ float vz = -cos (pitch) * cos (roll);
249
+
250
+ int x1 = x0 + 50 * vx;
251
+ int y1 = y0 + 50 * vy;
252
+
253
+ static lv_point_precise_t line_points0[] = {{x0, y0 }, {x1, y1 }};
254
+ line_points0[1 ].x = x1;
255
+ line_points0[1 ].y = y1 ;
256
+
257
+ // Apply Kalman filter
258
+ float accelPitch = atan2 (-accel.x , sqrt (accel.y * accel.y + accel.z * accel.z ));
259
+ float accelRoll = atan2 (accel.y , accel.z );
260
+ kf.predict ({float (gyro.x * M_PI / 180 .0f ), float (gyro.y * M_PI / 180 .0f )}, dt);
261
+ kf.update ({accelPitch, accelRoll});
262
+ std::tie (pitch, roll) = kf.get_state ();
263
+
264
+ vx = sin (pitch);
265
+ vy = -cos (pitch) * sin (roll);
266
+ vz = -cos (pitch) * cos (roll);
267
+
268
+ x1 = x0 + 50 * vx;
269
+ y1 = y0 + 50 * vy;
270
+
271
+ static lv_point_precise_t line_points1[] = {{x0, y0 }, {x1, y1 }};
272
+ line_points1[1 ].x = x1;
273
+ line_points1[1 ].y = y1 ;
274
+
275
+ std::lock_guard<std::recursive_mutex> lock (lvgl_mutex);
276
+ lv_label_set_text (label, text.c_str ());
277
+ lv_line_set_points (line0, line_points0, 2 );
278
+ lv_line_set_points (line1, line_points1, 2 );
279
+
280
+ return false ;
281
+ },
282
+ .task_config = {
283
+ .name = " IMU" ,
284
+ .stack_size_bytes = 6 * 1024 ,
285
+ .priority = 10 ,
286
+ .core_id = 0 ,
287
+ }});
288
+ imu_task.start ();
289
+
146
290
// loop forever
147
291
while (true ) {
148
292
std::this_thread::sleep_for (1s);
0 commit comments