Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions firmware/main/apps/app_setup/app_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ void AppSetup::onOpen()
_destroy_menu = true;
_need_warm_reset = true;
_worker = std::make_unique<XiaozhiPowerSavingWorker>();
}},
{"Idle Behavior",
[&]() {
_destroy_menu = true;
_need_warm_reset = true;
_worker = std::make_unique<IdleBehaviorWorker>();
}}},
},
{
Expand Down
175 changes: 175 additions & 0 deletions firmware/main/apps/app_setup/workers/ai_agent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,178 @@ void XiaozhiPowerSavingWorker::update_idle_label()
auto total_minutes = _config.idleShutdownTimeSeconds / 60;
_label_idle_value->setText(fmt::format("{} min", total_minutes));
}

static const char* frequency_label(uint8_t preset)
{
switch (preset) {
case 0: return "Sparse";
case 2: return "Frequent";
default: return "Normal";
}
}

static const char* intensity_label(uint8_t preset)
{
switch (preset) {
case 0: return "Calm";
case 2: return "Lively";
default: return "Normal";
}
}

IdleBehaviorWorker::IdleBehaviorWorker()
{
mclog::info("IdleBehaviorWorker start");

_config = GetHAL().getXiaozhiConfig();

_panel = std::make_unique<Container>(lv_screen_active());
_panel->setBgColor(lv_color_hex(0xEDF4FF));
_panel->align(LV_ALIGN_CENTER, 0, 0);
_panel->setBorderWidth(0);
_panel->setSize(320, 240);
_panel->setRadius(0);
_panel->setPadding(0, 50, 24, 18);
_panel->setScrollDir(LV_DIR_VER);
_panel->setScrollbarMode(LV_SCROLLBAR_MODE_ACTIVE);

_panel_enable = std::make_unique<Container>(_panel->get());
_panel_enable->setSize(296, 120);
_panel_enable->align(LV_ALIGN_TOP_MID, 0, 20);
_panel_enable->setBgColor(lv_color_hex(0xD2E3FF));
_panel_enable->setBorderWidth(0);
_panel_enable->setRadius(18);
_panel_enable->setPadding(0, 0, 0, 0);
_panel_enable->removeFlag(LV_OBJ_FLAG_SCROLLABLE);

_label_enable_title = std::make_unique<Label>(_panel_enable->get());
_label_enable_title->setText("Idle head motion:");
_label_enable_title->setTextFont(&lv_font_montserrat_16);
_label_enable_title->setTextColor(lv_color_hex(0x26206A));
_label_enable_title->setWidth(260);
_label_enable_title->setTextAlign(LV_TEXT_ALIGN_CENTER);
_label_enable_title->align(LV_ALIGN_TOP_MID, 0, 18);

_switch_enable = std::make_unique<Switch>(_panel_enable->get());
_switch_enable->setSize(64, 36);
_switch_enable->align(LV_ALIGN_TOP_MID, 0, 66);
_switch_enable->setBgColor(lv_color_hex(0xB8D3FD), LV_PART_MAIN);
_switch_enable->setBgColor(lv_color_hex(0x615B9E), LV_PART_INDICATOR | LV_STATE_CHECKED);
_switch_enable->setBgColor(lv_color_hex(0xFFFFFF), LV_PART_KNOB);
if (_config.idleMotionEnabled) {
_switch_enable->addState(LV_STATE_CHECKED);
}

_panel_frequency = std::make_unique<Container>(_panel->get());
_panel_frequency->setSize(296, 148);
_panel_frequency->align(LV_ALIGN_TOP_MID, 0, 160);
_panel_frequency->setBgColor(lv_color_hex(0xD2E3FF));
_panel_frequency->setBorderWidth(0);
_panel_frequency->setRadius(18);
_panel_frequency->setPadding(0, 0, 0, 0);
_panel_frequency->removeFlag(LV_OBJ_FLAG_SCROLLABLE);

_label_frequency_title = std::make_unique<Label>(_panel_frequency->get());
_label_frequency_title->setText("Frequency");
_label_frequency_title->setWidth(280);
_label_frequency_title->setTextAlign(LV_TEXT_ALIGN_CENTER);
_label_frequency_title->setTextFont(&lv_font_montserrat_16);
_label_frequency_title->setTextColor(lv_color_hex(0x26206A));
_label_frequency_title->align(LV_ALIGN_TOP_MID, 0, 18);

_label_frequency_value = std::make_unique<Label>(_panel_frequency->get());
_label_frequency_value->setTextFont(&lv_font_montserrat_24);
_label_frequency_value->setTextColor(lv_color_hex(0x26206A));
_label_frequency_value->align(LV_ALIGN_TOP_MID, 0, 64);

_slider_frequency = std::make_unique<Slider>(_panel_frequency->get());
_slider_frequency->align(LV_ALIGN_TOP_MID, 0, 106);
_slider_frequency->setRange(0, 2);
_slider_frequency->setSize(250, 18);
_slider_frequency->setBgColor(lv_color_hex(0x615B9E), LV_PART_KNOB);
_slider_frequency->setBgColor(lv_color_hex(0x615B9E), LV_PART_INDICATOR);
_slider_frequency->setBgColor(lv_color_hex(0xB8D3FD), LV_PART_MAIN);
_slider_frequency->setBgOpa(255);
_slider_frequency->setValue(_config.idleMotionFrequency);
_slider_frequency->onValueChanged().connect([this](int32_t value) { _pending_frequency_index = value; });

_panel_intensity = std::make_unique<Container>(_panel->get());
_panel_intensity->setSize(296, 148);
_panel_intensity->align(LV_ALIGN_TOP_MID, 0, 328);
_panel_intensity->setBgColor(lv_color_hex(0xD2E3FF));
_panel_intensity->setBorderWidth(0);
_panel_intensity->setRadius(18);
_panel_intensity->setPadding(0, 0, 0, 0);
_panel_intensity->removeFlag(LV_OBJ_FLAG_SCROLLABLE);

_label_intensity_title = std::make_unique<Label>(_panel_intensity->get());
_label_intensity_title->setText("Intensity");
_label_intensity_title->setWidth(280);
_label_intensity_title->setTextAlign(LV_TEXT_ALIGN_CENTER);
_label_intensity_title->setTextFont(&lv_font_montserrat_16);
_label_intensity_title->setTextColor(lv_color_hex(0x26206A));
_label_intensity_title->align(LV_ALIGN_TOP_MID, 0, 18);

_label_intensity_value = std::make_unique<Label>(_panel_intensity->get());
_label_intensity_value->setTextFont(&lv_font_montserrat_24);
_label_intensity_value->setTextColor(lv_color_hex(0x26206A));
_label_intensity_value->align(LV_ALIGN_TOP_MID, 0, 64);

_slider_intensity = std::make_unique<Slider>(_panel_intensity->get());
_slider_intensity->align(LV_ALIGN_TOP_MID, 0, 106);
_slider_intensity->setRange(0, 2);
_slider_intensity->setSize(250, 18);
_slider_intensity->setBgColor(lv_color_hex(0x615B9E), LV_PART_KNOB);
_slider_intensity->setBgColor(lv_color_hex(0x615B9E), LV_PART_INDICATOR);
_slider_intensity->setBgColor(lv_color_hex(0xB8D3FD), LV_PART_MAIN);
_slider_intensity->setBgOpa(255);
_slider_intensity->setValue(_config.idleMotionIntensity);
_slider_intensity->onValueChanged().connect([this](int32_t value) { _pending_intensity_index = value; });

_btn_confirm = std::make_unique<Button>(_panel->get());
apply_button_common_style(*_btn_confirm);
_btn_confirm->align(LV_ALIGN_TOP_MID, 0, 496);
_btn_confirm->setSize(290, 50);
_btn_confirm->label().setText("Confirm");
_btn_confirm->onClick().connect([this]() { _confirm_flag = true; });

update_frequency_label();
update_intensity_label();
}

void IdleBehaviorWorker::update()
{
if (_pending_frequency_index != -1) {
_config.idleMotionFrequency = static_cast<uint8_t>(_pending_frequency_index);
_pending_frequency_index = -1;
update_frequency_label();
}

if (_pending_intensity_index != -1) {
_config.idleMotionIntensity = static_cast<uint8_t>(_pending_intensity_index);
_pending_intensity_index = -1;
update_intensity_label();
}

if (_confirm_flag) {
_confirm_flag = false;
_config.idleMotionEnabled = _switch_enable->getValue();
GetHAL().setXiaozhiConfig(_config);
mclog::tagInfo(_tag,
"idle motion config updated: enabled={}, frequency={}, intensity={}",
_config.idleMotionEnabled,
_config.idleMotionFrequency,
_config.idleMotionIntensity);
_is_done = true;
}
}

void IdleBehaviorWorker::update_frequency_label()
{
_label_frequency_value->setText(frequency_label(_config.idleMotionFrequency));
}

void IdleBehaviorWorker::update_intensity_label()
{
_label_intensity_value->setText(intensity_label(_config.idleMotionIntensity));
}
37 changes: 37 additions & 0 deletions firmware/main/apps/app_setup/workers/workers.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,43 @@ class XiaozhiPowerSavingWorker : public WorkerBase {
bool _confirm_flag = false;
};

/**
* @brief
*
*/
class IdleBehaviorWorker : public WorkerBase {
public:
IdleBehaviorWorker();
void update() override;

private:
void update_frequency_label();
void update_intensity_label();

std::unique_ptr<uitk::lvgl_cpp::Container> _panel;

std::unique_ptr<uitk::lvgl_cpp::Container> _panel_enable;
std::unique_ptr<uitk::lvgl_cpp::Label> _label_enable_title;
std::unique_ptr<uitk::lvgl_cpp::Switch> _switch_enable;

std::unique_ptr<uitk::lvgl_cpp::Container> _panel_frequency;
std::unique_ptr<uitk::lvgl_cpp::Label> _label_frequency_title;
std::unique_ptr<uitk::lvgl_cpp::Label> _label_frequency_value;
std::unique_ptr<uitk::lvgl_cpp::Slider> _slider_frequency;

std::unique_ptr<uitk::lvgl_cpp::Container> _panel_intensity;
std::unique_ptr<uitk::lvgl_cpp::Label> _label_intensity_title;
std::unique_ptr<uitk::lvgl_cpp::Label> _label_intensity_value;
std::unique_ptr<uitk::lvgl_cpp::Slider> _slider_intensity;

std::unique_ptr<uitk::lvgl_cpp::Button> _btn_confirm;

XiaozhiConfig_t _config;
int32_t _pending_frequency_index = -1;
int32_t _pending_intensity_index = -1;
bool _confirm_flag = false;
};

/**
* @brief
*
Expand Down
12 changes: 12 additions & 0 deletions firmware/main/hal/board/hal_bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ static const char* _tag = "HAL_BRIDGE";
static constexpr std::string_view _xiaozhi_config_nvs_ns = "xiaozhi";
static constexpr std::string_view _xiaozhi_config_idle_shutdown_time_key = "idle_shutdown";
static constexpr std::string_view _xiaozhi_config_allow_shutdown_when_charging_key = "shutdown_charge";
static constexpr std::string_view _xiaozhi_config_idle_motion_enabled_key = "idle_motion";
static constexpr std::string_view _xiaozhi_config_idle_motion_frequency_key = "idle_freq";
static constexpr std::string_view _xiaozhi_config_idle_motion_intensity_key = "idle_intensity";

namespace hal_bridge {

Expand Down Expand Up @@ -126,6 +129,12 @@ XiaozhiConfig_t get_xiaozhi_config()
static_cast<int>(config.idleShutdownTimeSeconds));
config.allowShutdownWhenCharging =
settings.GetBool(_xiaozhi_config_allow_shutdown_when_charging_key.data(), config.allowShutdownWhenCharging);
config.idleMotionEnabled =
settings.GetBool(_xiaozhi_config_idle_motion_enabled_key.data(), config.idleMotionEnabled);
config.idleMotionFrequency = static_cast<uint8_t>(settings.GetInt(
_xiaozhi_config_idle_motion_frequency_key.data(), static_cast<int>(config.idleMotionFrequency)));
config.idleMotionIntensity = static_cast<uint8_t>(settings.GetInt(
_xiaozhi_config_idle_motion_intensity_key.data(), static_cast<int>(config.idleMotionIntensity)));

return config;
}
Expand All @@ -135,6 +144,9 @@ void set_xiaozhi_config(const XiaozhiConfig_t& config)
Settings settings(_xiaozhi_config_nvs_ns.data(), true);
settings.SetInt(_xiaozhi_config_idle_shutdown_time_key.data(), config.idleShutdownTimeSeconds);
settings.SetBool(_xiaozhi_config_allow_shutdown_when_charging_key.data(), config.allowShutdownWhenCharging);
settings.SetBool(_xiaozhi_config_idle_motion_enabled_key.data(), config.idleMotionEnabled);
settings.SetInt(_xiaozhi_config_idle_motion_frequency_key.data(), config.idleMotionFrequency);
settings.SetInt(_xiaozhi_config_idle_motion_intensity_key.data(), config.idleMotionIntensity);
}

void app_play_sound(const std::string_view& sound)
Expand Down
3 changes: 3 additions & 0 deletions firmware/main/hal/board/hal_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ struct Data_t {
struct XiaozhiConfig_t {
uint32_t idleShutdownTimeSeconds = 600;
bool allowShutdownWhenCharging = false;
bool idleMotionEnabled = true;
uint8_t idleMotionFrequency = 1; // 0=Sparse, 1=Normal, 2=Frequent
uint8_t idleMotionIntensity = 1; // 0=Calm, 1=Normal, 2=Lively
};

void lock();
Expand Down
14 changes: 12 additions & 2 deletions firmware/main/hal/board/stackchan_display.cc
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ void StackChanAvatarDisplay::SetEmotion(const char* emotion)
if (idle_motion_modifier_id_ >= 0) {
stackchan.removeModifier(idle_motion_modifier_id_);
idle_motion_modifier_id_ = -1;
}
if (idle_expression_modifier_id_ >= 0) {
stackchan.removeModifier(idle_expression_modifier_id_);
idle_expression_modifier_id_ = -1;
}
Expand Down Expand Up @@ -504,10 +506,16 @@ void StackChanAvatarDisplay::SetStatus(const char* status)
if (is_idle) {
// Start idle motion
ESP_LOGW(TAG, "Start idle motion");
if (idle_motion_modifier_id_ < 0) {
idle_motion_modifier_id_ = stackchan.addModifier(std::make_unique<IdleMotionModifier>());
if (idle_expression_modifier_id_ < 0) {
idle_expression_modifier_id_ = stackchan.addModifier(std::make_unique<IdleExpressionModifier>());
}
auto cfg = GetHAL().getXiaozhiConfig();
if (cfg.idleMotionEnabled && idle_motion_modifier_id_ < 0) {
auto interval = idleMotionFrequencyToIntervals(cfg.idleMotionFrequency);
auto factor = idleMotionIntensityToFactor(cfg.idleMotionIntensity);
idle_motion_modifier_id_ =
stackchan.addModifier(std::make_unique<IdleMotionModifier>(interval.min_ms, interval.max_ms, factor));
}

_is_xiaozhi_idle = true;
} else {
Expand All @@ -516,6 +524,8 @@ void StackChanAvatarDisplay::SetStatus(const char* status)
if (idle_motion_modifier_id_ >= 0) {
stackchan.removeModifier(idle_motion_modifier_id_);
idle_motion_modifier_id_ = -1;
}
if (idle_expression_modifier_id_ >= 0) {
stackchan.removeModifier(idle_expression_modifier_id_);
idle_expression_modifier_id_ = -1;
}
Expand Down
6 changes: 6 additions & 0 deletions firmware/main/hal/hal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ XiaozhiConfig_t Hal::getXiaozhiConfig()
return XiaozhiConfig_t{
.idleShutdownTimeSeconds = bridge_config.idleShutdownTimeSeconds,
.allowShutdownWhenCharging = bridge_config.allowShutdownWhenCharging,
.idleMotionEnabled = bridge_config.idleMotionEnabled,
.idleMotionFrequency = bridge_config.idleMotionFrequency,
.idleMotionIntensity = bridge_config.idleMotionIntensity,
};
}

Expand All @@ -216,6 +219,9 @@ void Hal::setXiaozhiConfig(XiaozhiConfig_t config)
hal_bridge::set_xiaozhi_config({
.idleShutdownTimeSeconds = config.idleShutdownTimeSeconds,
.allowShutdownWhenCharging = config.allowShutdownWhenCharging,
.idleMotionEnabled = config.idleMotionEnabled,
.idleMotionFrequency = config.idleMotionFrequency,
.idleMotionIntensity = config.idleMotionIntensity,
});
}

Expand Down
3 changes: 3 additions & 0 deletions firmware/main/hal/hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ struct UserAccountInfo_t {
struct XiaozhiConfig_t {
uint32_t idleShutdownTimeSeconds = 600;
bool allowShutdownWhenCharging = false;
bool idleMotionEnabled = true;
uint8_t idleMotionFrequency = 1; // 0=Sparse, 1=Normal, 2=Frequent
uint8_t idleMotionIntensity = 1; // 0=Calm, 1=Normal, 2=Lively
};

/**
Expand Down
Loading