Skip to content

添加新板子支持‘腕宝助手’ #480

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
2 changes: 2 additions & 0 deletions main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ elseif(CONFIG_BOARD_TYPE_DOIT_S3_AIBOX)
set(BOARD_TYPE "doit-s3-aibox")
elseif(CONFIG_BOARD_TYPE_ESP32_CGC)
set(BOARD_TYPE "esp32-cgc")
elseif(CONFIG_BOARD_TYPE_ESP32_S3_WRIST_GEM)
set(BOARD_TYPE "esp32-s3-wrist-gem")
endif()
file(GLOB BOARD_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc
Expand Down
2 changes: 2 additions & 0 deletions main/Kconfig.projbuild
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ choice BOARD_TYPE
bool "无名科技星智1.54(ML307)"
config BOARD_TYPE_SENSECAP_WATCHER
bool "SenseCAP Watcher"
config BOARD_TYPE_ESP32_S3_WRIST_GEM
bool "腕宝助手"
config BOARD_TYPE_DOIT_S3_AIBOX
bool "四博智联AI陪伴盒子"
endchoice
Expand Down
38 changes: 38 additions & 0 deletions main/boards/esp32-s3-wrist-gem/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# 一、腕宝开源链接
- [小智 AI 聊天机器人百科全书](https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb)
- [OSHWHub | 腕宝项目页](https://oshwhub.com/dotnfc/esp32-s3-wrist-gem-xiaoszhi-ai)
- [B站 | 腕宝功能说明](https://www.bilibili.com/opus/1054903427441623049)


# 二、编译配置命令

**配置编译目标为 ESP32S3:**

```bash
idf.py set-target esp32s3
```

**打开 menuconfig:**

```bash
idf.py menuconfig
```

**选择板子:**

```
Xiaozhi Assistant -> Board Type -> 腕宝助手
```

**编译:**

```bash
idf.py build
```

**合并固件:**
```bash
idf.py merge-bin -o merged-xiaozhi-wg.bin -f raw
```

---
60 changes: 60 additions & 0 deletions main/boards/esp32-s3-wrist-gem/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_

#include <driver/gpio.h>

#define AUDIO_INPUT_SAMPLE_RATE 16000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000

/**< Pixel-Led */
#define BUILTIN_LED_GPIO GPIO_NUM_2

/**< Audio */
#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_38
#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_39
#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_40
#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_42
#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_41
#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_48

/**< Button */
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC
#define RESET_NVS_BUTTON_GPIO GPIO_NUM_NC
#define RESET_FACTORY_BUTTON_GPIO GPIO_NUM_NC

/**< Board I2C Bus */
#define BOARD_I2C_NUM I2C_NUM_0
#define BOARD_SCL_PIN GPIO_NUM_5
#define BOARD_SDA_PIN GPIO_NUM_4

/**< Display P169H002-CTP */
#define DISPLAY_MOSI GPIO_NUM_47
#define DISPLAY_SCK GPIO_NUM_21
#define DISPLAY_DC GPIO_NUM_13
#define DISPLAY_CS GPIO_NUM_14
#define DISPLAY_REST GPIO_NUM_NC
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 280
#define DISPLAY_SWAP_XY false
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_Y false
#define BACKLIGHT_INVERT false
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 20
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_1
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false

/**< Display P169H002-CTP-Touch */
#define DISPLAY_TOUCH_AS_LISTEN_BUTTON 1
#define DISPLAY_TOUCH_SDA BOARD_SDA_PIN
#define DISPLAY_TOUCH_SCL BOARD_SCL_PIN
#define DISPLAY_TOUCH_INT GPIO_NUM_NC // GPIO_NUM_18

/**< PMIC */
#define AXP2101_I2C_ADDR 0x34

#endif // _BOARD_CONFIG_H_

10 changes: 10 additions & 0 deletions main/boards/esp32-s3-wrist-gem/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"target": "esp32s3",
"builds": [
{
"name": "esp32-s3-wrist-gem",
"sdkconfig_append": []
}
]
}

249 changes: 249 additions & 0 deletions main/boards/esp32-s3-wrist-gem/esp32-s3-wrist-gem.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
#include "wifi_board.h"
#include "audio_codecs/no_audio_codec.h"
// #include "audio_codecs/box_audio_codec.h"

#include "display/lcd_display.h"
#include "system_reset.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "power_save_timer.h"
#include "iot/thing_manager.h"
#include "led/single_led.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <esp_lcd_panel_vendor.h>
#include <wifi_station.h>

#include <driver/rtc_io.h>
#include <esp_sleep.h>

#include "axp2101.h"
#include <driver/gpio.h>
#include <driver/i2c_master.h>
#include "assets/lang_config.h"
#include "font_awesome_symbols.h"
#include "lcd_touch.h"

#define TAG "WRIST-GEM"

LV_FONT_DECLARE(font_puhui_20_4);
LV_FONT_DECLARE(font_awesome_20_4);

class Pmic : public Axp2101 {
public:
Pmic(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : Axp2101(i2c_bus, addr) {
// ** EFUSE defaults **
WriteReg(0x22, 0b110); // PWRON > OFFLEVEL as POWEROFF Source enable
WriteReg(0x27, 0x10); // hold 4s to power off

WriteReg(0x92, 0x1C); // 配置 aldo1 输出为 3.3V

uint8_t value = ReadReg(0x90); // XPOWERS_AXP2101_LDO_ONOFF_CTRL0
value = value | 0x01; // set bit 1 (ALDO1)
WriteReg(0x90, value); // and power channels now enabled

WriteReg(0x64, 0x03); // CV charger voltage setting to 4.2V

WriteReg(0x61, 0x05); // set Main battery precharge current to 125mA
WriteReg(0x62, 0x08); // set Main battery charger current to 200mA ( 0x08-200mA, 0x09-300mA, 0x0A-400mA )
WriteReg(0x63, 0x15); // set Main battery term charge current to 125mA

WriteReg(0x14, 0x00); // set minimum system voltage to 4.1V (default 4.7V), for poor USB cables
WriteReg(0x15, 0x00); // set input voltage limit to 3.88v, for poor USB cables
WriteReg(0x16, 0x05); // set input current limit to 2000mA

WriteReg(0x24, 0x01); // set Vsys for PWROFF threshold to 3.2V (default - 2.6V and kill battery)
WriteReg(0x50, 0x14); // set TS pin to EXTERNAL input (not temperature)
}
};


class WristGemBoard : public WifiBoard {
private:
Button boot_button_;
SpiLcdDisplayEx* display_;
Pmic* pmic_ = nullptr;
i2c_master_bus_handle_t i2c_bus_;
PowerSaveTimer* power_save_timer_;

void InitializePowerSaveTimer() {
power_save_timer_ = new PowerSaveTimer(240, 60, 300);
power_save_timer_->OnEnterSleepMode([this]() {
ESP_LOGI(TAG, "Enabling sleep mode");

auto display = GetDisplay();
display->SetChatMessage("system", "");
display->SetEmotion("sleepy");

auto codec = GetAudioCodec();
codec->EnableInput(false);

GetBacklight()->SetBrightness(10);
});
power_save_timer_->OnExitSleepMode([this]() {
auto codec = GetAudioCodec();
codec->EnableInput(true);

auto display = GetDisplay();
display->SetChatMessage("system", "");
display->SetEmotion("neutral");

GetBacklight()->RestoreBrightness();
});
power_save_timer_->OnShutdownRequest([this]() {
pmic_->PowerOff();
});
power_save_timer_->SetEnabled(true);
}

void InitializeSpi() {
spi_bus_config_t buscfg = {};
buscfg.mosi_io_num = DISPLAY_MOSI;
buscfg.miso_io_num = GPIO_NUM_NC;
buscfg.sclk_io_num = DISPLAY_SCK;
buscfg.quadwp_io_num = GPIO_NUM_NC;
buscfg.quadhd_io_num = GPIO_NUM_NC;
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
}

void InitializeI2c() {
i2c_master_bus_config_t bus_config = {
.i2c_port = (i2c_port_t)BOARD_I2C_NUM,
.sda_io_num = BOARD_SDA_PIN,
.scl_io_num = BOARD_SCL_PIN,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.intr_priority = 0,
.trans_queue_depth = 0,
.flags = {
.enable_internal_pullup = 1,
},
};
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &i2c_bus_));
}

void InitializeButtons() {
boot_button_.OnClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
ResetWifiConfiguration();
}

app.ToggleChatState();
// if (app.GetDeviceState() == kDeviceStateSpeaking) {
// app.AbortSpeaking(kAbortReasonNone);
// }
});
}

void InitializeTouchDisplay() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;

// 液晶屏控制IO初始化
ESP_LOGD(TAG, "Install panel IO");
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = DISPLAY_CS;
io_config.dc_gpio_num = DISPLAY_DC;
io_config.spi_mode = 3;
io_config.pclk_hz = 80 * 1000 * 1000;
io_config.trans_queue_depth = 10;
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io));

// 初始化液晶屏驱动芯片ST7789
ESP_LOGD(TAG, "Install LCD driver");
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = DISPLAY_REST;
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
panel_config.bits_per_pixel = 16;
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel));
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY));
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y));
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, true));

display_ = new SpiLcdDisplayEx(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y,
DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y,
DISPLAY_SWAP_XY,
{
.text_font = &font_puhui_20_4,
.icon_font = &font_awesome_20_4,
.emoji_font = font_emoji_64_init(),
});

display_->InitializeTouch(i2c_bus_);
}

// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker")); // 音量
thing_manager.AddThing(iot::CreateThing("Lamp")); // 灯光
thing_manager.AddThing(iot::CreateThing("Screen")); // 背光
thing_manager.AddThing(iot::CreateThing("Battery"));
}

public:
WristGemBoard() :

boot_button_(BOOT_BUTTON_GPIO) {
ESP_LOGI(TAG, "Initializing Wrist Gem Board");

InitializeI2c();
pmic_ = new Pmic(i2c_bus_, AXP2101_I2C_ADDR);

InitializeSpi();
InitializeButtons();
InitializeTouchDisplay();
InitializeIot();
InitializePowerSaveTimer();
GetBacklight()->RestoreBrightness();
}

virtual Led* GetLed() override {
static SingleLed led(BUILTIN_LED_GPIO);
return &led;
}

virtual AudioCodec *GetAudioCodec() override {
static NoAudioCodecSimplex audio_codec(
AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_I2S_SPK_GPIO_BCLK, AUDIO_I2S_SPK_GPIO_LRCK, AUDIO_I2S_SPK_GPIO_DOUT,
AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN
);
return &audio_codec;
}

virtual Display *GetDisplay() override {
return display_;
}

virtual Backlight* GetBacklight() override {
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
return &backlight;
}

virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging) override {
static bool last_discharging = false;
charging = pmic_->IsCharging();
discharging = pmic_->IsDischarging();
if (discharging != last_discharging) {
power_save_timer_->SetEnabled(discharging);
last_discharging = discharging;
}

level = pmic_->GetBatteryLevel();
return true;
}
};

DECLARE_BOARD(WristGemBoard);


Loading