55
66#include " esp-box.hpp"
77
8+ #include " kalman_filter.hpp"
9+ #include " madgwick_filter.hpp"
10+
811using namespace std ::chrono_literals;
912
1013static constexpr size_t MAX_CIRCLES = 100 ;
@@ -80,16 +83,50 @@ extern "C" void app_main(void) {
8083 return ;
8184 }
8285
86+ // initialize the IMU
87+ if (!box.initialize_imu ()) {
88+ logger.error (" Failed to initialize IMU!" );
89+ return ;
90+ }
91+
8392 // set the background color to black
8493 lv_obj_t *bg = lv_obj_create (lv_screen_active ());
8594 lv_obj_set_size (bg, box.lcd_width (), box.lcd_height ());
8695 lv_obj_set_style_bg_color (bg, lv_color_make (0 , 0 , 0 ), 0 );
8796
8897 // add text in the center of the screen
8998 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 );
93130
94131 // add a button in the top left which (when pressed) will rotate the display
95132 // through 0, 90, 180, 270 degrees
@@ -129,6 +166,7 @@ extern "C" void app_main(void) {
129166 },
130167 .task_config = {
131168 .name = " lv_task" ,
169+ .stack_size_bytes = 6 * 1024 ,
132170 }});
133171 lv_task.start ();
134172
@@ -143,6 +181,112 @@ extern "C" void app_main(void) {
143181 // set the display brightness to be 75%
144182 box.brightness (75 .0f );
145183
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+
146290 // loop forever
147291 while (true ) {
148292 std::this_thread::sleep_for (1s);
0 commit comments