Skip to content

Commit b4c0b4b

Browse files
committed
LVGL, Sabo BootScreen
1 parent b732964 commit b4c0b4b

25 files changed

+1612
-559
lines changed

CMakeLists.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE
4646
# Add user defined library search paths
4747
)
4848

49+
# Add LVGL assets
50+
file(GLOB LVGL_ASSETS_SRC
51+
"src/drivers/ui/lvgl/assets/*.c"
52+
"src/drivers/ui/lvgl/assets/Orbitron/*.c"
53+
)
54+
4955
# Add sources to executable
5056
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
5157
boards/XCORE/board_utils.cpp
@@ -91,14 +97,13 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
9197
src/drivers/ui/SaboCoverUI/sabo_cover_ui_cabo_driver_base.cpp
9298
src/drivers/ui/SaboCoverUI/sabo_cover_ui_controller.cpp
9399
src/drivers/ui/SaboCoverUI/sabo_cover_ui_display_driver_uc1698.cpp
94-
src/drivers/ui/lvgl/assets/chassis_193x94x1.c
95-
src/drivers/ui/lvgl/assets/wheel_83x83x1.c
96100
# Raw driver debug interface
97101
src/debug/debug_tcp_interface.cpp
98102
src/debug/debug_udp_interface.cpp
99103
src/debug/debuggable_driver.cpp
100104
robots/src/robot.cpp
101105
${PLATFORM_SOURCES}
106+
${LVGL_ASSETS_SRC}
102107
)
103108

104109
# Add include paths

lv_conf.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@
178178
#define LV_DRAW_SW_SUPPORT_RGB565A8 0
179179
#define LV_DRAW_SW_SUPPORT_RGB888 0
180180
#define LV_DRAW_SW_SUPPORT_XRGB8888 0
181-
#define LV_DRAW_SW_SUPPORT_ARGB8888 0
181+
#define LV_DRAW_SW_SUPPORT_ARGB8888 1
182182
#define LV_DRAW_SW_SUPPORT_ARGB8888_PREMULTIPLIED 0
183183
#define LV_DRAW_SW_SUPPORT_L8 0
184184
#define LV_DRAW_SW_SUPPORT_AL88 0
@@ -204,7 +204,7 @@
204204
* - 0: Use a simple renderer capable of drawing only simple rectangles with gradient, images, text, and straight lines
205205
* only.
206206
* - 1: Use a complex renderer capable of drawing rounded corners, shadow, skew lines, and arcs too. */
207-
#define LV_DRAW_SW_COMPLEX 0
207+
#define LV_DRAW_SW_COMPLEX 1
208208

209209
#if LV_DRAW_SW_COMPLEX == 1
210210
/** Allow buffering some shadow calculation.
@@ -373,7 +373,7 @@
373373
* - LV_LOG_LEVEL_ERROR Log only critical issues, when system may fail.
374374
* - LV_LOG_LEVEL_USER Log only custom log messages added by the user.
375375
* - LV_LOG_LEVEL_NONE Do not log anything. */
376-
#define LV_LOG_LEVEL LV_LOG_LEVEL_INFO
376+
#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
377377

378378
/** - 1: Print log with 'printf';
379379
* - 0: User needs to register a callback with `lv_log_register_print_cb()`. */
@@ -616,7 +616,7 @@
616616

617617
/** Pixel perfect monospaced fonts */
618618
#define LV_FONT_UNSCII_8 1
619-
#define LV_FONT_UNSCII_16 1
619+
#define LV_FONT_UNSCII_16 0
620620

621621
/** Optionally declare custom fonts here.
622622
*

robots/include/sabo_robot.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ class SaboRobot : public MowerRobot {
4040
return 7.0f * 3.0;
4141
}
4242

43+
CHARGER_STATUS GetChargerStatus() {
44+
return charger_.getChargerStatus();
45+
}
46+
4347
private:
4448
BQ2576 charger_{};
4549
SaboCoverUIController cover_ui_{};

src/drivers/ui/SaboCoverUI/sabo_cover_ui_controller.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
#include <services.hpp>
99

10+
#include "chprintf.h"
1011
#include "hal.h"
12+
#include "robots/include/sabo_robot.hpp"
1113
#include "sabo_cover_ui_display.hpp"
1214
#include "sabo_cover_ui_display_driver_uc1698.hpp"
1315

@@ -127,6 +129,111 @@ const char* SaboCoverUIController::ButtonIDToString(const ButtonID id) {
127129
}
128130
}
129131

132+
/**
133+
* @brief Handle the boot sequence
134+
*
135+
*/
136+
void SaboCoverUIController::HandleBootSequence() {
137+
auto boot_screen = display_->GetBootScreen();
138+
if (boot_screen == nullptr) return;
139+
140+
// All boot steps done...
141+
if (current_boot_step_ >= boot_steps_.size()) {
142+
if (boot_screen->GetAnimationState() == SaboScreenBoot::AnimationState::NONE) {
143+
display_->SetBootStatus("Ready to mow", 100);
144+
boot_screen->StartForwardAnimation();
145+
} else if (boot_screen->GetAnimationState() == SaboScreenBoot::AnimationState::DONE) {
146+
display_->ShowMainScreen();
147+
}
148+
return;
149+
}
150+
151+
BootStep& step = boot_steps_[current_boot_step_];
152+
systime_t now = chVTGetSystemTimeX();
153+
uint32_t elapsed_ms = TIME_I2MS(now - step.last_action_time);
154+
155+
switch (step.state) {
156+
case BootStepState::WAIT:
157+
step.state = BootStepState::RUNNING;
158+
step.last_action_time = now;
159+
display_->SetBootStatus(step.name, (current_boot_step_ * 100) / boot_steps_.size());
160+
break;
161+
162+
case BootStepState::RUNNING:
163+
if (elapsed_ms >= 1000) {
164+
if (step.test_func()) {
165+
step.state = BootStepState::DONE;
166+
current_boot_step_++;
167+
boot_step_retry_count_ = 0;
168+
} else {
169+
boot_step_retry_count_++;
170+
if (boot_step_retry_count_ < BOOT_STEP_RETRIES) {
171+
etl::string<48> fail_msg;
172+
chsnprintf(fail_msg.data(), fail_msg.max_size(), "%s (%d/%d)", step.name, boot_step_retry_count_,
173+
BOOT_STEP_RETRIES);
174+
display_->SetBootStatus(fail_msg, (current_boot_step_ * 100) / boot_steps_.size());
175+
step.last_action_time = now;
176+
} else {
177+
etl::string<48> fail_msg = step.name;
178+
fail_msg += " failed!";
179+
display_->SetBootStatus(fail_msg, (current_boot_step_ * 100) / boot_steps_.size());
180+
step.state = BootStepState::ERROR;
181+
step.last_action_time = now;
182+
}
183+
}
184+
}
185+
break;
186+
187+
case BootStepState::ERROR:
188+
if (elapsed_ms >= 60000 || cabo_->IsAnyButtonPressed()) {
189+
step.state = BootStepState::DONE;
190+
current_boot_step_++;
191+
boot_step_retry_count_ = 0;
192+
}
193+
break;
194+
195+
case BootStepState::DONE:
196+
// NOP. Next step get handled in next tick
197+
break;
198+
}
199+
}
200+
201+
bool SaboCoverUIController::TestIMU() {
202+
return imu_service.IsFound();
203+
}
204+
205+
bool SaboCoverUIController::TestCharger() {
206+
Robot* robot = GetRobot();
207+
if (!robot) return false;
208+
209+
auto* sabo = static_cast<SaboRobot*>(robot);
210+
if (!sabo) return false;
211+
212+
const CHARGER_STATUS status = sabo->GetChargerStatus();
213+
// TODO: Why do I get COMMS_ERROR here?
214+
// return status != CHARGER_STATUS::FAULT && status != CHARGER_STATUS::COMMS_ERROR;
215+
return status != CHARGER_STATUS::FAULT;
216+
}
217+
218+
bool SaboCoverUIController::TestGPS() {
219+
return true;
220+
}
221+
222+
bool SaboCoverUIController::TestLeftESC() {
223+
auto esc_state = diff_drive.GetLeftESCState();
224+
return esc_state.status == MotorDriver::ESCState::ESCStatus::ESC_STATUS_OK;
225+
}
226+
227+
bool SaboCoverUIController::TestRightESC() {
228+
auto esc_state = diff_drive.GetRightESCState();
229+
return esc_state.status == MotorDriver::ESCState::ESCStatus::ESC_STATUS_OK;
230+
}
231+
232+
bool SaboCoverUIController::TestMowerESC() {
233+
auto esc_state = mower_service.GetESCState();
234+
return esc_state.status == MotorDriver::ESCState::ESCStatus::ESC_STATUS_OK;
235+
}
236+
130237
void SaboCoverUIController::ThreadFunc() {
131238
if (display_) {
132239
display_->Start();
@@ -139,6 +246,10 @@ void SaboCoverUIController::ThreadFunc() {
139246
if (cabo_->IsReady() && display_) {
140247
display_->Tick();
141248
if (cabo_->IsAnyButtonPressed()) display_->WakeUp();
249+
250+
if (current_boot_step_ <= boot_steps_.size() && display_->GetActiveScreen()->GetScreenId() == ScreenId::BOOT) {
251+
HandleBootSequence();
252+
}
142253
}
143254

144255
// ----- Debug -----

src/drivers/ui/SaboCoverUI/sabo_cover_ui_controller.hpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#ifndef OPENMOWER_SABO_COVER_UI_CONTROLLER_HPP
66
#define OPENMOWER_SABO_COVER_UI_CONTROLLER_HPP
77

8+
#include <etl/array.h>
9+
810
#include "ch.h"
911
#include "sabo_cover_ui_cabo_driver_v01.hpp"
1012
#include "sabo_cover_ui_cabo_driver_v02.hpp"
@@ -24,7 +26,7 @@ class SaboCoverUIController {
2426
static const char* ButtonIDToString(const ButtonID id); // Get string for ButtonID
2527

2628
private:
27-
THD_WORKING_AREA(wa_, 4096); // AH20250714 In use = 2912. Let's be save for LVGL GUI development
29+
THD_WORKING_AREA(wa_, 5120); // AH20250801 In use = 3808. Let's be save (+1k) for LVGL GUI development
2830
thread_t* thread_ = nullptr;
2931

3032
bool configured_ = false;
@@ -35,6 +37,39 @@ class SaboCoverUIController {
3537

3638
SaboCoverUIDisplay* display_ = nullptr; // Pointer to the Display driver
3739

40+
enum class BootStepState { WAIT, RUNNING, ERROR, DONE };
41+
struct BootStep {
42+
const char* name;
43+
bool (*test_func)();
44+
BootStepState state = BootStepState::WAIT;
45+
systime_t last_action_time = 0;
46+
};
47+
static constexpr size_t BOOT_STEP_COUNT_ = 7;
48+
etl::array<SaboCoverUIController::BootStep, BOOT_STEP_COUNT_> boot_steps_ = {{
49+
{"Motion Sensor", &TestIMU},
50+
{"Charger", &TestCharger},
51+
{"GPS", &TestGPS},
52+
{"Left Motor", &TestLeftESC},
53+
{"Right Motor", &TestRightESC},
54+
{"Mower Motor", &TestMowerESC},
55+
{"Flux Compensator", &TestFluxCompensator},
56+
}};
57+
size_t current_boot_step_ = 0;
58+
static constexpr size_t BOOT_STEP_RETRIES = 3;
59+
size_t boot_step_retry_count_ = 0;
60+
61+
void HandleBootSequence();
62+
63+
static bool TestIMU();
64+
static bool TestCharger();
65+
static bool TestGPS();
66+
static bool TestLeftESC();
67+
static bool TestRightESC();
68+
static bool TestMowerESC();
69+
static bool TestFluxCompensator() {
70+
return false; // Placeholder for a non-existent service
71+
}
72+
3873
static void ThreadHelper(void* instance);
3974
void ThreadFunc();
4075
};

0 commit comments

Comments
 (0)