diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 2157caecc..0e78328e5 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -550,6 +550,10 @@ elseif(CONFIG_BOARD_TYPE_AIPI_LITE) set(BUILTIN_TEXT_FONT font_puhui_basic_14_1) set(BUILTIN_ICON_FONT font_awesome_14_1) set(DEFAULT_EMOJI_COLLECTION twemoji_32) +elseif(CONFIG_BOARD_TYPE_HU_087) + set(BOARD_TYPE "hu-087") + set(BUILTIN_TEXT_FONT font_puhui_basic_14_1) + set(BUILTIN_ICON_FONT font_awesome_14_1) endif() file(GLOB BOARD_SOURCES diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 785412f38..de0a88b73 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -435,6 +435,9 @@ choice BOARD_TYPE config BOARD_TYPE_AIPI_LITE bool "AIPI-Lite" depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_HU_087 + bool "HU-087" + depends on IDF_TARGET_ESP32S3 endchoice choice @@ -468,7 +471,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_HU_087 prompt "OLED Type" default OLED_SSD1306_128X32 help diff --git a/main/boards/hu-087/README.md b/main/boards/hu-087/README.md new file mode 100644 index 000000000..8dfe0667e --- /dev/null +++ b/main/boards/hu-087/README.md @@ -0,0 +1,7 @@ + +[product](https://fr.aliexpress.com/item/1005010207331462.html) + +One of the cheapest kit in a watch form factor. + +## Programming ## +Before assembling the product, unglue the battery from the board to access the programming header. You'll need a ESP32 programming dongle with control of the EN and IO0 pins. The USB connector is not wired to the USB pins of the ESP32-S3 USB interface and can only be used for charging. diff --git a/main/boards/hu-087/config.h b/main/boards/hu-087/config.h new file mode 100644 index 000000000..dff8b3fc9 --- /dev/null +++ b/main/boards/hu-087/config.h @@ -0,0 +1,28 @@ +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#include + +#define AUDIO_INPUT_SAMPLE_RATE 16000 +#define AUDIO_OUTPUT_SAMPLE_RATE 24000 + +#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_4 +#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_5 +#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_6 +#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_7 +#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_15 +#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_16 +#define AUDIO_I2S_SPK_GPIO_CTLR GPIO_NUM_17 + + +#define TOUCH_BUTTON_GPIO GPIO_NUM_18 + +#define DISPLAY_SDA_PIN GPIO_NUM_41 +#define DISPLAY_SCL_PIN GPIO_NUM_42 +#define DISPLAY_WIDTH 128 +#define DISPLAY_HEIGHT 64 + +#define DISPLAY_MIRROR_X true +#define DISPLAY_MIRROR_Y true + +#endif // _BOARD_CONFIG_H_ diff --git a/main/boards/hu-087/config.json b/main/boards/hu-087/config.json new file mode 100644 index 000000000..761676641 --- /dev/null +++ b/main/boards/hu-087/config.json @@ -0,0 +1,9 @@ +{ + "target": "esp32s3", + "builds": [ + { + "name": "hu-087", + "sdkconfig_append": [] + } + ] +} diff --git a/main/boards/hu-087/hu_087_board.cc b/main/boards/hu-087/hu_087_board.cc new file mode 100644 index 000000000..e6d55bafb --- /dev/null +++ b/main/boards/hu-087/hu_087_board.cc @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include + +#include "application.h" +#include "assets/lang_config.h" +#include "boards/common/wifi_board.h" +#include "button.h" +#include "codecs/no_audio_codec.h" +#include "config.h" +#include "display/oled_display.h" +#include "lamp_controller.h" +#include "led/single_led.h" +#include "mcp_server.h" +#include "system_reset.h" + +#define TAG "Hu087Board" + +class Hu087Board : public WifiBoard { + private: + i2c_master_bus_handle_t display_i2c_bus_; + esp_lcd_panel_io_handle_t panel_io_ = nullptr; + esp_lcd_panel_handle_t panel_ = nullptr; + Display* display_ = nullptr; + Button touch_button_; + + void InitializeDisplayI2c() { + 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() { + // SSD1306 config + 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_LOGI(TAG, "Install SSD1306 driver"); + 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(DISPLAY_HEIGHT), + }; + panel_config.vendor_config = &ssd1306_config; + + ESP_ERROR_CHECK( + esp_lcd_new_panel_ssd1306(panel_io_, &panel_config, &panel_)); + ESP_LOGI(TAG, "SSD1306 driver installed"); + + // Reset the display + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_)); + if (esp_lcd_panel_init(panel_) != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize display"); + display_ = new NoDisplay(); + return; + } + + // Set the display to on + ESP_LOGI(TAG, "Turning display on"); + 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 initializeAmpCtrl() { + gpio_config_t io_conf = { + .pin_bit_mask = (1ULL << AUDIO_I2S_SPK_GPIO_CTLR), // Select GPIO 2 + .mode = GPIO_MODE_OUTPUT, // Set as output + .pull_up_en = GPIO_PULLUP_ENABLE, // Disable pull-up + .pull_down_en = GPIO_PULLDOWN_DISABLE, // Disable pull-down + .intr_type = GPIO_INTR_DISABLE // Disable interrupts + }; + gpio_config(&io_conf); + gpio_set_level(AUDIO_I2S_SPK_GPIO_CTLR, 1); + }; + + void InitializeButtons() { + touch_button_.OnClick([this]() { + auto& app = Application::GetInstance(); + if (app.GetDeviceState() == kDeviceStateStarting && + !WifiStation::GetInstance().IsConnected()) { + ResetWifiConfiguration(); + } + app.ToggleChatState(); + }); + + touch_button_.OnLongPress([this]() { + auto codec = GetAudioCodec(); + auto volume = codec->output_volume() + 10; + if (volume > 100) { + volume = 100; + } // Need to implement logic to lower volume + codec->SetOutputVolume(volume); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + + std::to_string(volume)); + }); + } + + public: + Hu087Board() : touch_button_(TOUCH_BUTTON_GPIO) { + InitializeDisplayI2c(); + InitializeSsd1306Display(); + InitializeButtons(); + initializeAmpCtrl(); // Could control the amp ctrl pin throught voice + // detection i guess + } + + 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, I2S_STD_SLOT_RIGHT, AUDIO_I2S_MIC_GPIO_SCK, + AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN, I2S_STD_SLOT_RIGHT); + return &audio_codec; + } + + virtual Display* GetDisplay() override { return display_; } +}; + +DECLARE_BOARD(Hu087Board);