esp_spot_s3_board.cc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #include "wifi_board.h"
  2. #include "codecs/es8311_audio_codec.h"
  3. #include "application.h"
  4. #include "button.h"
  5. #include "config.h"
  6. #include "sdkconfig.h"
  7. #include <wifi_station.h>
  8. #include <esp_log.h>
  9. #include <driver/i2c_master.h>
  10. #include <driver/spi_common.h>
  11. #include "esp_adc/adc_oneshot.h"
  12. #include "esp_adc/adc_cali.h"
  13. #include "esp_adc/adc_cali_scheme.h"
  14. #include <driver/gpio.h>
  15. #include "esp_timer.h"
  16. #include "led/circular_strip.h"
  17. #define TAG "esp_spot_s3"
  18. bool button_released_ = false;
  19. bool shutdown_ready_ = false;
  20. esp_timer_handle_t shutdown_timer;
  21. class EspSpotS3Bot : public WifiBoard {
  22. private:
  23. i2c_master_bus_handle_t i2c_bus_;
  24. Button boot_button_;
  25. Button key_button_;
  26. adc_oneshot_unit_handle_t adc1_handle;
  27. adc_cali_handle_t adc1_cali_handle;
  28. bool do_calibration = false;
  29. bool key_long_pressed = false;
  30. int64_t last_key_press_time = 0;
  31. static const int64_t LONG_PRESS_TIMEOUT_US = 5 * 1000000ULL;
  32. void InitializeI2c() {
  33. // Initialize I2C peripheral
  34. i2c_master_bus_config_t i2c_bus_cfg = {
  35. .i2c_port = I2C_NUM_0,
  36. .sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
  37. .scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
  38. .clk_source = I2C_CLK_SRC_DEFAULT,
  39. .glitch_ignore_cnt = 7,
  40. .intr_priority = 0,
  41. .trans_queue_depth = 0,
  42. .flags = {
  43. .enable_internal_pullup = 1,
  44. },
  45. };
  46. ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_));
  47. }
  48. void InitializeADC() {
  49. adc_oneshot_unit_init_cfg_t init_config1 = {
  50. .unit_id = ADC_UNIT_1
  51. };
  52. ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
  53. adc_oneshot_chan_cfg_t chan_config = {
  54. .atten = ADC_ATTEN,
  55. .bitwidth = ADC_WIDTH,
  56. };
  57. ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, VBAT_ADC_CHANNEL, &chan_config));
  58. adc_cali_handle_t handle = NULL;
  59. esp_err_t ret = ESP_FAIL;
  60. #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
  61. adc_cali_curve_fitting_config_t cali_config = {
  62. .unit_id = ADC_UNIT_1,
  63. .atten = ADC_ATTEN,
  64. .bitwidth = ADC_WIDTH,
  65. };
  66. ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
  67. if (ret == ESP_OK) {
  68. do_calibration = true;
  69. adc1_cali_handle = handle;
  70. ESP_LOGI(TAG, "ADC Curve Fitting calibration succeeded");
  71. }
  72. #endif // ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
  73. }
  74. void InitializeButtons() {
  75. boot_button_.OnClick([this]() {
  76. auto& app = Application::GetInstance();
  77. ResetWifiConfiguration();
  78. });
  79. key_button_.OnClick([this]() {
  80. auto& app = Application::GetInstance();
  81. app.ToggleChatState();
  82. key_long_pressed = false;
  83. });
  84. key_button_.OnLongPress([this]() {
  85. int64_t now = esp_timer_get_time();
  86. auto* led = static_cast<CircularStrip*>(this->GetLed());
  87. if (key_long_pressed) {
  88. if ((now - last_key_press_time) < LONG_PRESS_TIMEOUT_US) {
  89. ESP_LOGW(TAG, "Key button long pressed the second time within 5s, shutting down...");
  90. led->SetSingleColor(0, {0, 0, 0});
  91. gpio_hold_dis(MCU_VCC_CTL);
  92. gpio_set_level(MCU_VCC_CTL, 0);
  93. } else {
  94. last_key_press_time = now;
  95. BlinkGreenFor5s();
  96. }
  97. key_long_pressed = true;
  98. } else {
  99. ESP_LOGW(TAG, "Key button first long press! Waiting second within 5s to shutdown...");
  100. last_key_press_time = now;
  101. key_long_pressed = true;
  102. BlinkGreenFor5s();
  103. }
  104. });
  105. }
  106. void InitializePowerCtl() {
  107. InitializeGPIO();
  108. gpio_set_level(MCU_VCC_CTL, 1);
  109. gpio_hold_en(MCU_VCC_CTL);
  110. gpio_set_level(PERP_VCC_CTL, 1);
  111. gpio_hold_en(PERP_VCC_CTL);
  112. }
  113. void InitializeGPIO() {
  114. gpio_config_t io_pa = {
  115. .pin_bit_mask = (1ULL << AUDIO_CODEC_PA_PIN),
  116. .mode = GPIO_MODE_OUTPUT,
  117. .pull_up_en = GPIO_PULLUP_DISABLE,
  118. .pull_down_en = GPIO_PULLDOWN_DISABLE,
  119. .intr_type = GPIO_INTR_DISABLE
  120. };
  121. gpio_config(&io_pa);
  122. gpio_set_level(AUDIO_CODEC_PA_PIN, 0);
  123. gpio_config_t io_conf_1 = {
  124. .pin_bit_mask = (1ULL << MCU_VCC_CTL),
  125. .mode = GPIO_MODE_OUTPUT,
  126. .pull_up_en = GPIO_PULLUP_DISABLE,
  127. .pull_down_en = GPIO_PULLDOWN_DISABLE,
  128. .intr_type = GPIO_INTR_DISABLE
  129. };
  130. gpio_config(&io_conf_1);
  131. gpio_config_t io_conf_2 = {
  132. .pin_bit_mask = (1ULL << PERP_VCC_CTL),
  133. .mode = GPIO_MODE_OUTPUT,
  134. .pull_up_en = GPIO_PULLUP_DISABLE,
  135. .pull_down_en = GPIO_PULLDOWN_DISABLE,
  136. .intr_type = GPIO_INTR_DISABLE
  137. };
  138. gpio_config(&io_conf_2);
  139. }
  140. void BlinkGreenFor5s() {
  141. auto* led = static_cast<CircularStrip*>(GetLed());
  142. if (!led) {
  143. return;
  144. }
  145. led->Blink({50, 25, 0}, 100);
  146. esp_timer_create_args_t timer_args = {
  147. .callback = [](void* arg) {
  148. auto* self = static_cast<EspSpotS3Bot*>(arg);
  149. auto* led = static_cast<CircularStrip*>(self->GetLed());
  150. if (led) {
  151. led->SetSingleColor(0, {0, 0, 0});
  152. }
  153. },
  154. .arg = this,
  155. .dispatch_method = ESP_TIMER_TASK,
  156. .name = "blinkGreenFor5s_timer"
  157. };
  158. esp_timer_handle_t blink_timer = nullptr;
  159. ESP_ERROR_CHECK(esp_timer_create(&timer_args, &blink_timer));
  160. ESP_ERROR_CHECK(esp_timer_start_once(blink_timer, LONG_PRESS_TIMEOUT_US));
  161. }
  162. public:
  163. EspSpotS3Bot() : boot_button_(BOOT_BUTTON_GPIO), key_button_(KEY_BUTTON_GPIO, true) {
  164. InitializePowerCtl();
  165. InitializeADC();
  166. InitializeI2c();
  167. InitializeButtons();
  168. }
  169. virtual Led* GetLed() override {
  170. static CircularStrip led(LED_PIN, 1);
  171. return &led;
  172. }
  173. virtual AudioCodec* GetAudioCodec() override {
  174. static Es8311AudioCodec audio_codec(i2c_bus_, I2C_NUM_0,
  175. AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK,
  176. AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN, AUDIO_CODEC_PA_PIN,
  177. AUDIO_CODEC_ES8311_ADDR, false);
  178. return &audio_codec;
  179. }
  180. virtual bool GetBatteryLevel(int &level, bool &charging, bool &discharging) {
  181. if (!adc1_handle) {
  182. InitializeADC();
  183. }
  184. int raw_value = 0;
  185. int voltage = 0;
  186. ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, VBAT_ADC_CHANNEL, &raw_value));
  187. if (do_calibration) {
  188. ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_handle, raw_value, &voltage));
  189. voltage = voltage * 3 / 2; // compensate for voltage divider
  190. ESP_LOGI(TAG, "Calibrated voltage: %d mV", voltage);
  191. } else {
  192. ESP_LOGI(TAG, "Raw ADC value: %d", raw_value);
  193. voltage = raw_value;
  194. }
  195. voltage = voltage < EMPTY_BATTERY_VOLTAGE ? EMPTY_BATTERY_VOLTAGE : voltage;
  196. voltage = voltage > FULL_BATTERY_VOLTAGE ? FULL_BATTERY_VOLTAGE : voltage;
  197. // 计算电量百分比
  198. level = (voltage - EMPTY_BATTERY_VOLTAGE) * 100 / (FULL_BATTERY_VOLTAGE - EMPTY_BATTERY_VOLTAGE);
  199. charging = gpio_get_level(MCU_VCC_CTL);
  200. ESP_LOGI(TAG, "Battery Level: %d%%, Charging: %s", level, charging ? "Yes" : "No");
  201. return true;
  202. }
  203. };
  204. DECLARE_BOARD(EspSpotS3Bot);