atoms3r_echo_base.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. #include "wifi_board.h"
  2. #include "codecs/es8311_audio_codec.h"
  3. #include "display/lcd_display.h"
  4. #include "application.h"
  5. #include "button.h"
  6. #include "config.h"
  7. #include "i2c_device.h"
  8. #include "assets/lang_config.h"
  9. #include <esp_log.h>
  10. #include <driver/i2c_master.h>
  11. #include <wifi_station.h>
  12. #include <esp_lcd_panel_io.h>
  13. #include <esp_lcd_panel_ops.h>
  14. #include <esp_lcd_gc9a01.h>
  15. #define TAG "AtomS3R+EchoBase"
  16. #define PI4IOE_ADDR 0x43
  17. #define PI4IOE_REG_CTRL 0x00
  18. #define PI4IOE_REG_IO_PP 0x07
  19. #define PI4IOE_REG_IO_DIR 0x03
  20. #define PI4IOE_REG_IO_OUT 0x05
  21. #define PI4IOE_REG_IO_PULLUP 0x0D
  22. LV_FONT_DECLARE(font_puhui_16_4);
  23. LV_FONT_DECLARE(font_awesome_16_4);
  24. class Pi4ioe : public I2cDevice {
  25. public:
  26. Pi4ioe(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
  27. WriteReg(PI4IOE_REG_IO_PP, 0x00); // Set to high-impedance
  28. WriteReg(PI4IOE_REG_IO_PULLUP, 0xFF); // Enable pull-up
  29. WriteReg(PI4IOE_REG_IO_DIR, 0x6E); // Set input=0, output=1
  30. WriteReg(PI4IOE_REG_IO_OUT, 0xFF); // Set outputs to 1
  31. }
  32. void SetSpeakerMute(bool mute) {
  33. WriteReg(PI4IOE_REG_IO_OUT, mute ? 0x00 : 0xFF);
  34. }
  35. };
  36. class Lp5562 : public I2cDevice {
  37. public:
  38. Lp5562(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
  39. WriteReg(0x00, 0B01000000); // Set chip_en to 1
  40. WriteReg(0x08, 0B00000001); // Enable internal clock
  41. WriteReg(0x70, 0B00000000); // Configure all LED outputs to be controlled from I2C registers
  42. // PWM clock frequency 558 Hz
  43. auto data = ReadReg(0x08);
  44. data = data | 0B01000000;
  45. WriteReg(0x08, data);
  46. }
  47. void SetBrightness(uint8_t brightness) {
  48. // Map 0~100 to 0~255
  49. brightness = brightness * 255 / 100;
  50. WriteReg(0x0E, brightness);
  51. }
  52. };
  53. class CustomBacklight : public Backlight {
  54. public:
  55. CustomBacklight(Lp5562* lp5562) : lp5562_(lp5562) {}
  56. void SetBrightnessImpl(uint8_t brightness) override {
  57. if (lp5562_) {
  58. lp5562_->SetBrightness(brightness);
  59. } else {
  60. ESP_LOGE(TAG, "LP5562 not available");
  61. }
  62. }
  63. private:
  64. Lp5562* lp5562_ = nullptr;
  65. };
  66. static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
  67. // {cmd, { data }, data_size, delay_ms}
  68. {0xfe, (uint8_t[]){0x00}, 0, 0},
  69. {0xef, (uint8_t[]){0x00}, 0, 0},
  70. {0xb0, (uint8_t[]){0xc0}, 1, 0},
  71. {0xb2, (uint8_t[]){0x2f}, 1, 0},
  72. {0xb3, (uint8_t[]){0x03}, 1, 0},
  73. {0xb6, (uint8_t[]){0x19}, 1, 0},
  74. {0xb7, (uint8_t[]){0x01}, 1, 0},
  75. {0xac, (uint8_t[]){0xcb}, 1, 0},
  76. {0xab, (uint8_t[]){0x0e}, 1, 0},
  77. {0xb4, (uint8_t[]){0x04}, 1, 0},
  78. {0xa8, (uint8_t[]){0x19}, 1, 0},
  79. {0xb8, (uint8_t[]){0x08}, 1, 0},
  80. {0xe8, (uint8_t[]){0x24}, 1, 0},
  81. {0xe9, (uint8_t[]){0x48}, 1, 0},
  82. {0xea, (uint8_t[]){0x22}, 1, 0},
  83. {0xc6, (uint8_t[]){0x30}, 1, 0},
  84. {0xc7, (uint8_t[]){0x18}, 1, 0},
  85. {0xf0,
  86. (uint8_t[]){0x1f, 0x28, 0x04, 0x3e, 0x2a, 0x2e, 0x20, 0x00, 0x0c, 0x06,
  87. 0x00, 0x1c, 0x1f, 0x0f},
  88. 14, 0},
  89. {0xf1,
  90. (uint8_t[]){0x00, 0x2d, 0x2f, 0x3c, 0x6f, 0x1c, 0x0b, 0x00, 0x00, 0x00,
  91. 0x07, 0x0d, 0x11, 0x0f},
  92. 14, 0},
  93. };
  94. class AtomS3rEchoBaseBoard : public WifiBoard {
  95. private:
  96. i2c_master_bus_handle_t i2c_bus_;
  97. i2c_master_bus_handle_t i2c_bus_internal_;
  98. Pi4ioe* pi4ioe_ = nullptr;
  99. Lp5562* lp5562_ = nullptr;
  100. Display* display_ = nullptr;
  101. Button boot_button_;
  102. bool is_echo_base_connected_ = false;
  103. void InitializeI2c() {
  104. // Initialize I2C peripheral
  105. i2c_master_bus_config_t i2c_bus_cfg = {
  106. .i2c_port = I2C_NUM_1,
  107. .sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
  108. .scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
  109. .clk_source = I2C_CLK_SRC_DEFAULT,
  110. .glitch_ignore_cnt = 7,
  111. .intr_priority = 0,
  112. .trans_queue_depth = 0,
  113. .flags = {
  114. .enable_internal_pullup = 1,
  115. },
  116. };
  117. ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_));
  118. i2c_bus_cfg.i2c_port = I2C_NUM_0;
  119. i2c_bus_cfg.sda_io_num = GPIO_NUM_45;
  120. i2c_bus_cfg.scl_io_num = GPIO_NUM_0;
  121. ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_internal_));
  122. }
  123. void I2cDetect() {
  124. is_echo_base_connected_ = false;
  125. uint8_t echo_base_connected_flag = 0x00;
  126. uint8_t address;
  127. printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
  128. for (int i = 0; i < 128; i += 16) {
  129. printf("%02x: ", i);
  130. for (int j = 0; j < 16; j++) {
  131. fflush(stdout);
  132. address = i + j;
  133. esp_err_t ret = i2c_master_probe(i2c_bus_, address, pdMS_TO_TICKS(200));
  134. if (ret == ESP_OK) {
  135. printf("%02x ", address);
  136. if (address == 0x18) {
  137. echo_base_connected_flag |= 0xF0;
  138. } else if (address == 0x43) {
  139. echo_base_connected_flag |= 0x0F;
  140. }
  141. } else if (ret == ESP_ERR_TIMEOUT) {
  142. printf("UU ");
  143. } else {
  144. printf("-- ");
  145. }
  146. }
  147. printf("\r\n");
  148. }
  149. is_echo_base_connected_ = (echo_base_connected_flag == 0xFF);
  150. }
  151. void CheckEchoBaseConnection() {
  152. if (is_echo_base_connected_) {
  153. return;
  154. }
  155. // Pop error page
  156. InitializeLp5562();
  157. InitializeSpi();
  158. InitializeGc9107Display();
  159. InitializeButtons();
  160. GetBacklight()->SetBrightness(100);
  161. display_->SetStatus(Lang::Strings::ERROR);
  162. display_->SetEmotion("sad");
  163. display_->SetChatMessage("system", "Echo Base\nnot connected");
  164. while (1) {
  165. ESP_LOGE(TAG, "Atomic Echo Base is disconnected");
  166. vTaskDelay(pdMS_TO_TICKS(1000));
  167. // Rerun detection
  168. I2cDetect();
  169. if (is_echo_base_connected_) {
  170. vTaskDelay(pdMS_TO_TICKS(500));
  171. I2cDetect();
  172. if (is_echo_base_connected_) {
  173. ESP_LOGI(TAG, "Atomic Echo Base is reconnected");
  174. vTaskDelay(pdMS_TO_TICKS(200));
  175. esp_restart();
  176. }
  177. }
  178. }
  179. }
  180. void InitializePi4ioe() {
  181. ESP_LOGI(TAG, "Init PI4IOE");
  182. pi4ioe_ = new Pi4ioe(i2c_bus_, PI4IOE_ADDR);
  183. pi4ioe_->SetSpeakerMute(false);
  184. }
  185. void InitializeLp5562() {
  186. ESP_LOGI(TAG, "Init LP5562");
  187. lp5562_ = new Lp5562(i2c_bus_internal_, 0x30);
  188. }
  189. void InitializeSpi() {
  190. ESP_LOGI(TAG, "Initialize SPI bus");
  191. spi_bus_config_t buscfg = {};
  192. buscfg.mosi_io_num = GPIO_NUM_21;
  193. buscfg.miso_io_num = GPIO_NUM_NC;
  194. buscfg.sclk_io_num = GPIO_NUM_15;
  195. buscfg.quadwp_io_num = GPIO_NUM_NC;
  196. buscfg.quadhd_io_num = GPIO_NUM_NC;
  197. buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
  198. ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
  199. }
  200. void InitializeGc9107Display() {
  201. ESP_LOGI(TAG, "Init GC9107 display");
  202. ESP_LOGI(TAG, "Install panel IO");
  203. esp_lcd_panel_io_handle_t io_handle = NULL;
  204. esp_lcd_panel_io_spi_config_t io_config = {};
  205. io_config.cs_gpio_num = GPIO_NUM_14;
  206. io_config.dc_gpio_num = GPIO_NUM_42;
  207. io_config.spi_mode = 0;
  208. io_config.pclk_hz = 40 * 1000 * 1000;
  209. io_config.trans_queue_depth = 10;
  210. io_config.lcd_cmd_bits = 8;
  211. io_config.lcd_param_bits = 8;
  212. ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &io_handle));
  213. ESP_LOGI(TAG, "Install GC9A01 panel driver");
  214. esp_lcd_panel_handle_t panel_handle = NULL;
  215. gc9a01_vendor_config_t gc9107_vendor_config = {
  216. .init_cmds = gc9107_lcd_init_cmds,
  217. .init_cmds_size = sizeof(gc9107_lcd_init_cmds) / sizeof(gc9a01_lcd_init_cmd_t),
  218. };
  219. esp_lcd_panel_dev_config_t panel_config = {};
  220. panel_config.reset_gpio_num = GPIO_NUM_48; // Set to -1 if not use
  221. panel_config.rgb_endian = LCD_RGB_ENDIAN_BGR;
  222. panel_config.bits_per_pixel = 16; // Implemented by LCD command `3Ah` (16/18)
  223. panel_config.vendor_config = &gc9107_vendor_config;
  224. ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(io_handle, &panel_config, &panel_handle));
  225. ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
  226. ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
  227. ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
  228. display_ = new SpiLcdDisplay(io_handle, panel_handle,
  229. DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
  230. {
  231. .text_font = &font_puhui_16_4,
  232. .icon_font = &font_awesome_16_4,
  233. .emoji_font = font_emoji_32_init(),
  234. });
  235. }
  236. void InitializeButtons() {
  237. boot_button_.OnClick([this]() {
  238. auto& app = Application::GetInstance();
  239. if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
  240. ResetWifiConfiguration();
  241. }
  242. app.ToggleChatState();
  243. });
  244. }
  245. public:
  246. AtomS3rEchoBaseBoard() : boot_button_(BOOT_BUTTON_GPIO) {
  247. InitializeI2c();
  248. I2cDetect();
  249. CheckEchoBaseConnection();
  250. InitializePi4ioe();
  251. InitializeLp5562();
  252. InitializeSpi();
  253. InitializeGc9107Display();
  254. InitializeButtons();
  255. GetBacklight()->RestoreBrightness();
  256. }
  257. virtual AudioCodec* GetAudioCodec() override {
  258. static Es8311AudioCodec audio_codec(
  259. i2c_bus_,
  260. I2C_NUM_1,
  261. AUDIO_INPUT_SAMPLE_RATE,
  262. AUDIO_OUTPUT_SAMPLE_RATE,
  263. AUDIO_I2S_GPIO_MCLK,
  264. AUDIO_I2S_GPIO_BCLK,
  265. AUDIO_I2S_GPIO_WS,
  266. AUDIO_I2S_GPIO_DOUT,
  267. AUDIO_I2S_GPIO_DIN,
  268. AUDIO_CODEC_GPIO_PA,
  269. AUDIO_CODEC_ES8311_ADDR,
  270. false);
  271. return &audio_codec;
  272. }
  273. virtual Display* GetDisplay() override {
  274. return display_;
  275. }
  276. virtual Backlight *GetBacklight() override {
  277. static CustomBacklight backlight(lp5562_);
  278. return &backlight;
  279. }
  280. };
  281. DECLARE_BOARD(AtomS3rEchoBaseBoard);