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
5 changes: 5 additions & 0 deletions main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,11 @@ elseif(CONFIG_BOARD_TYPE_WIRELESS_TAG_WTP4C5MP07S)
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_SEEEDSTUDIO_S3_WIFI)
set(BOARD_TYPE "seeedstudio-s3-wifi")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
endif()

file(GLOB BOARD_SOURCES
Expand Down
5 changes: 4 additions & 1 deletion main/Kconfig.projbuild
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,9 @@ choice BOARD_TYPE
config BOARD_TYPE_WIRELESS_TAG_WTP4C5MP07S
bool "Wireless-Tag WTP4C5MP07S"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_SEEEDSTUDIO_S3_WIFI
bool "Seeed Studio XIAO ESP32-S3"
depends on IDF_TARGET_ESP32S3
endchoice

choice
Expand Down Expand Up @@ -457,7 +460,7 @@ choice ESP_S3_LCD_EV_Board_Version_TYPE
endchoice

choice DISPLAY_OLED_TYPE
depends on BOARD_TYPE_BREAD_COMPACT_WIFI || BOARD_TYPE_BREAD_COMPACT_ML307 || BOARD_TYPE_BREAD_COMPACT_ESP32
depends on BOARD_TYPE_BREAD_COMPACT_WIFI || BOARD_TYPE_BREAD_COMPACT_ML307 || BOARD_TYPE_BREAD_COMPACT_ESP32 || BOARD_TYPE_SEEEDSTUDIO_S3_WIFI
prompt "OLED Type"
default OLED_SSD1306_128X32
help
Expand Down
26 changes: 26 additions & 0 deletions main/boards/seeedstudio-s3-wifi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Seeed Studio XIAO ESP32-S3
参照Bread-compact-wifi改造的适配XIAO ESP32-S3开发板的面包板版本。

## 🛠️ 编译指南
**开发环境**:ESP-IDF v5.4.1

### 编译步骤:
默认Windows已安装ESP-IDF环境
1. 双击打开ESP-IDF 5.5CMD桌面图标,并 cd 进入项目文件夹
2. 输入 idf.py fullclean, 回车
3. 输入 idf.py set-target esp32s3,回车
4. 输入 idf.py menuconfig
5. 进入 Serial flasher config -> Flash size -> 8MB
6. 进入 Partition Table -> Custom partition CSV file -> 复制粘贴此路径:partitions/v2/8m.csv
7. 进入 Xiaozhi Assistant -> Board Type -> Seeed Studio XIAO ESP32-S3
8. 进入 OLED Type -> SSD1306 128*32
8. 按S保存,Q退出。
9. 输入 idf.py build
10. 等待编译结束后开始烧录

## 🔌 烧录步骤
1. 连接开发板到电脑
2. Windows: 此电脑右键找到管理,进入设备管理器查看端口
3. 输入 idf.py -p COM(你的端口号) flash
4. 烧录完成后可以输入 idf.py -p COM(你的端口号) monitor 打开串口监视器查看开发板状态
5. 祝你玩儿的开心~~~
56 changes: 56 additions & 0 deletions main/boards/seeedstudio-s3-wifi/board_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#ifndef BOARD_CONFIG_H_
#define BOARD_CONFIG_H_

#include <driver/gpio.h>

// 音频速率
#define AUDIO_INPUT_REFERENCE true
#define AUDIO_INPUT_SAMPLE_RATE 16000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000

// 开 Simplex(两套总线)
#define AUDIO_I2S_METHOD_SIMPLEX

// 扬声器(TX, MASTER)—— 总线#0
#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_7
#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_4
#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_2

// 麦克风(RX, MASTER)—— 总线#1
#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_44
#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_9
#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_1

// OLED I2C (SDA/SCL)
// D4 -> GPIO5 : SDA
// D5 -> GPIO6 : SCL
#define DISPLAY_SDA_PIN GPIO_NUM_5
#define DISPLAY_SCL_PIN GPIO_NUM_6
#define DISPLAY_WIDTH 128

#if CONFIG_OLED_SSD1306_128X32
#define DISPLAY_HEIGHT 32
#elif CONFIG_OLED_SSD1306_128X64
#define DISPLAY_HEIGHT 64
#elif CONFIG_OLED_SH1106_128X64
#define DISPLAY_HEIGHT 64
#define SH1106
#else
#error "未选择 OLED 屏幕类型"
#endif

#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y true

// 板载LED
#define BUILTIN_LED_GPIO GPIO_NUM_21

// 按钮
#define BOOT_BUTTON_GPIO GPIO_NUM_0 // 板载 Boot
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_3 // D2
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_8 // D9

// 触摸键暂时不用,但为了兼容代码构造函数我们给个占位
#define TOUCH_BUTTON_GPIO GPIO_NUM_3

#endif // BOARD_CONFIG_H_
23 changes: 23 additions & 0 deletions main/boards/seeedstudio-s3-wifi/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"target": "esp32s3",
"builds": [
{
"name": "seeedstudio-s3-wifi",
"sdkconfig_append": [
"CONFIG_BOARD_SEEEDSTUDIO_S3_WIFI=y",
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB_y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\"",
"CONFIG_OLED_SSD1306_128X64=y"
]
},
{
"name": "seeedstudio-s3-wifi",
"sdkconfig_append": [
"CONFIG_BOARD_SEEEDSTUDIO_S3_WIFI=y",
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB_y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\"",
"CONFIG_OLED_SSD1306_128X32=y"
]
}
]
}
190 changes: 190 additions & 0 deletions main/boards/seeedstudio-s3-wifi/seeedstudio_s3_wifi_board.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#include "wifi_board.h"
#include "codecs/no_audio_codec.h"
#include "display/oled_display.h"
#include "system_reset.h"
#include "application.h"
#include "button.h"
#include "assets/lang_config.h"
#include "led/single_led.h"

#include "board_config.h" // <- 很重要,来自当前目录
#include <wifi_station.h>

#include <esp_log.h>
#include <driver/i2c_master.h>
#include <esp_lcd_panel_ops.h>
#include <esp_lcd_panel_vendor.h>
#ifdef SH1106
#include <esp_lcd_panel_sh1106.h>
#endif

#define TAG "SeeedStudioS3WifiBoard"

// 这个类专门代表 XIAO ESP32-S3 这块板
class SeeedStudioS3WifiBoard : public WifiBoard {
private:
i2c_master_bus_handle_t display_i2c_bus_ = nullptr;
esp_lcd_panel_io_handle_t panel_io_ = nullptr;
esp_lcd_panel_handle_t panel_ = nullptr;
Display* display_ = nullptr;

Button boot_button_;
Button touch_button_;
Button volume_up_button_;
Button volume_down_button_;

void InitDisplayI2C() {
i2c_master_bus_config_t bus_config = {
.i2c_port = (i2c_port_t)0,
.sda_io_num = DISPLAY_SDA_PIN,
.scl_io_num = DISPLAY_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, &display_i2c_bus_));
}

void InitializeSsd1306Display() {
esp_lcd_panel_io_i2c_config_t io_config = {
.dev_addr = 0x3C,
.on_color_trans_done = nullptr,
.user_ctx = nullptr,
.control_phase_bytes = 1,
.dc_bit_offset = 6,
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
.flags = { .dc_low_on_data = 0, .disable_control_phase = 0 },
.scl_speed_hz = 400 * 1000,
};

ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c_v2(display_i2c_bus_, &io_config, &panel_io_));

esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = -1;
panel_config.bits_per_pixel = 1;

esp_lcd_panel_ssd1306_config_t ssd1306_config = {
.height = static_cast<uint8_t>(DISPLAY_HEIGHT),
};
panel_config.vendor_config = &ssd1306_config;

#ifdef SH1106
ESP_ERROR_CHECK(esp_lcd_new_panel_sh1106(panel_io_, &panel_config, &panel_));
#else
ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(panel_io_, &panel_config, &panel_));
#endif

ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_));
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_, false));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true));

display_ = new OledDisplay(
panel_io_,
panel_,
DISPLAY_WIDTH,
DISPLAY_HEIGHT,
DISPLAY_MIRROR_X,
DISPLAY_MIRROR_Y
);
}

void InitButtons() {
// Boot 按钮:切换聊天 / 复位 WiFi
boot_button_.OnClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting &&
!WifiStation::GetInstance().IsConnected()) {
ResetWifiConfiguration();
}
app.ToggleChatState();
});

// touch_button_:兼容语音按住说话逻辑(目前硬件上可以不接)
touch_button_.OnPressDown([this]() {
Application::GetInstance().StartListening();
});
touch_button_.OnPressUp([this]() {
Application::GetInstance().StopListening();
});

// 音量+
volume_up_button_.OnClick([this]() {
auto codec = GetAudioCodec();
auto volume = codec->output_volume() + 10;
if (volume > 100) volume = 100;
codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
});
volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
});

// 音量-
volume_down_button_.OnClick([this]() {
auto codec = GetAudioCodec();
auto volume = codec->output_volume() - 10;
if (volume < 0) volume = 0;
codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
});
volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification(Lang::Strings::MUTED);
});
}

public:
SeeedStudioS3WifiBoard() :
boot_button_(BOOT_BUTTON_GPIO),
touch_button_(TOUCH_BUTTON_GPIO),
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) {

ESP_LOGI(TAG, "Init SeeedStudioS3WifiBoard");
InitDisplayI2C();
InitializeSsd1306Display();
InitButtons();
}

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

virtual AudioCodec* GetAudioCodec() override {
#ifndef AUDIO_I2S_METHOD_SIMPLEX
static NoAudioCodecDuplex audio_codec(
AUDIO_INPUT_SAMPLE_RATE,
AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_I2S_GPIO_BCLK,
AUDIO_I2S_GPIO_WS,
AUDIO_I2S_GPIO_DOUT,
AUDIO_I2S_GPIO_DIN
);
#else
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
);
#endif
return &audio_codec;
}

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

// 把这个板注册出去,供系统使用
DECLARE_BOARD(SeeedStudioS3WifiBoard);