add config files for known boards

This commit is contained in:
Terrence 2024-10-29 00:22:29 +08:00
parent fe05a039a2
commit 33518dca2b
35 changed files with 1000 additions and 3181 deletions

View File

@ -4,7 +4,7 @@
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
set(PROJECT_VER "0.4.1")
set(PROJECT_VER "0.5.0")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(xiaozhi)

View File

@ -1,6 +1,4 @@
#include <BuiltinLed.h>
#include <TcpTransport.h>
#include <TlsTransport.h>
#include <Ml307SslTransport.h>
#include <WifiConfigurationAp.h>
#include <WifiStation.h>
@ -10,6 +8,7 @@
#include <esp_log.h>
#include <cJSON.h>
#include <driver/gpio.h>
#include <arpa/inet.h>
#include "Application.h"
@ -17,29 +16,20 @@
Application::Application()
: boot_button_((gpio_num_t)CONFIG_BOOT_BUTTON_GPIO),
volume_up_button_((gpio_num_t)CONFIG_VOLUME_UP_BUTTON_GPIO),
volume_down_button_((gpio_num_t)CONFIG_VOLUME_DOWN_BUTTON_GPIO),
#ifdef CONFIG_USE_DISPLAY
display_(CONFIG_DISPLAY_SDA_PIN, CONFIG_DISPLAY_SCL_PIN),
#endif
#ifdef CONFIG_USE_ML307
ml307_at_modem_(CONFIG_ML307_TX_PIN, CONFIG_ML307_RX_PIN, 4096),
http_(ml307_at_modem_),
#else
http_(),
#endif
firmware_upgrade_(http_)
: boot_button_((gpio_num_t)BOOT_BUTTON_GPIO),
volume_up_button_((gpio_num_t)VOLUME_UP_BUTTON_GPIO),
volume_down_button_((gpio_num_t)VOLUME_DOWN_BUTTON_GPIO),
display_(DISPLAY_SDA_PIN, DISPLAY_SCL_PIN)
{
event_group_ = xEventGroupCreate();
opus_encoder_.Configure(16000, 1);
opus_decoder_ = opus_decoder_create(opus_decode_sample_rate_, 1, NULL);
if (opus_decode_sample_rate_ != CONFIG_AUDIO_OUTPUT_SAMPLE_RATE) {
output_resampler_.Configure(CONFIG_AUDIO_OUTPUT_SAMPLE_RATE, opus_decode_sample_rate_);
if (opus_decode_sample_rate_ != AUDIO_OUTPUT_SAMPLE_RATE) {
output_resampler_.Configure(AUDIO_OUTPUT_SAMPLE_RATE, opus_decode_sample_rate_);
}
if (16000 != CONFIG_AUDIO_INPUT_SAMPLE_RATE) {
input_resampler_.Configure(CONFIG_AUDIO_INPUT_SAMPLE_RATE, 16000);
if (16000 != AUDIO_INPUT_SAMPLE_RATE) {
input_resampler_.Configure(AUDIO_INPUT_SAMPLE_RATE, 16000);
}
firmware_upgrade_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
@ -47,18 +37,29 @@ Application::Application()
}
Application::~Application() {
if (update_display_timer_ != nullptr) {
esp_timer_stop(update_display_timer_);
esp_timer_delete(update_display_timer_);
}
if (ws_client_ != nullptr) {
delete ws_client_;
}
if (opus_decoder_ != nullptr) {
opus_decoder_destroy(opus_decoder_);
}
if (audio_encode_task_stack_ != nullptr) {
free(audio_encode_task_stack_);
}
if (audio_device_ != nullptr) {
delete audio_device_;
}
vEventGroupDelete(event_group_);
}
void Application::CheckNewVersion() {
// Check if there is a new firmware version available
firmware_upgrade_.SetBoardJson(Board::GetInstance().GetJson());
firmware_upgrade_.CheckVersion();
if (firmware_upgrade_.HasNewVersion()) {
// Wait for the chat state to be idle
@ -67,11 +68,9 @@ void Application::CheckNewVersion() {
}
SetChatState(kChatStateUpgrading);
firmware_upgrade_.StartUpgrade([this](int progress, size_t speed) {
#ifdef CONFIG_USE_DISPLAY
char buffer[64];
snprintf(buffer, sizeof(buffer), "Upgrading...\n %d%% %zuKB/s", progress, speed / 1024);
display_.SetText(buffer);
#endif
});
// If upgrade success, the device will reboot and never reach here
ESP_LOGI(TAG, "Firmware upgrade failed...");
@ -81,138 +80,19 @@ void Application::CheckNewVersion() {
}
}
#ifdef CONFIG_USE_DISPLAY
#ifdef CONFIG_USE_ML307
static std::string csq_to_string(int csq) {
if (csq == -1) {
return "No network";
} else if (csq >= 0 && csq <= 9) {
return "Very bad";
} else if (csq >= 10 && csq <= 14) {
return "Bad";
} else if (csq >= 15 && csq <= 19) {
return "Fair";
} else if (csq >= 20 && csq <= 24) {
return "Good";
} else if (csq >= 25 && csq <= 31) {
return "Very good";
}
return "Invalid";
}
#else
static std::string rssi_to_string(int rssi) {
if (rssi >= -55) {
return "Very good";
} else if (rssi >= -65) {
return "Good";
} else if (rssi >= -75) {
return "Fair";
} else if (rssi >= -85) {
return "Poor";
} else {
return "No network";
}
}
#endif
void Application::UpdateDisplay() {
while (true) {
if (chat_state_ == kChatStateIdle) {
#ifdef CONFIG_USE_ML307
std::string network_name = ml307_at_modem_.GetCarrierName();
int signal_quality = ml307_at_modem_.GetCsq();
if (signal_quality == -1) {
network_name = "No network";
} else {
ESP_LOGI(TAG, "%s CSQ: %d", network_name.c_str(), signal_quality);
display_.SetText(network_name + "\n" + csq_to_string(signal_quality) + " (" + std::to_string(signal_quality) + ")");
}
#else
auto& wifi_station = WifiStation::GetInstance();
int8_t rssi = wifi_station.GetRssi();
display_.SetText(wifi_station.GetSsid() + "\n" + rssi_to_string(rssi) + " (" + std::to_string(rssi) + ")");
#endif
}
vTaskDelay(pdMS_TO_TICKS(10 * 1000));
}
}
#endif
void Application::Start() {
auto& builtin_led = BuiltinLed::GetInstance();
#ifdef CONFIG_USE_ML307
builtin_led.SetBlue();
builtin_led.StartContinuousBlink(100);
ml307_at_modem_.SetDebug(false);
ml307_at_modem_.SetBaudRate(921600);
// Print the ML307 modem information
std::string module_name = ml307_at_modem_.GetModuleName();
ESP_LOGI(TAG, "ML307 Module: %s", module_name.c_str());
#ifdef CONFIG_USE_DISPLAY
display_.SetText(std::string("Wait for network\n") + module_name);
#endif
ml307_at_modem_.ResetConnections();
ml307_at_modem_.WaitForNetworkReady();
std::string imei = ml307_at_modem_.GetImei();
std::string iccid = ml307_at_modem_.GetIccid();
ESP_LOGI(TAG, "ML307 IMEI: %s", imei.c_str());
ESP_LOGI(TAG, "ML307 ICCID: %s", iccid.c_str());
auto& board = Board::GetInstance();
board.Initialize();
// If low power, the material ready event will be triggered by the modem because of a reset
ml307_at_modem_.OnMaterialReady([this]() {
ESP_LOGI(TAG, "ML307 material ready");
Schedule([this]() {
SetChatState(kChatStateIdle);
});
});
// Set the board type for OTA
std::string carrier_name = ml307_at_modem_.GetCarrierName();
int csq = ml307_at_modem_.GetCsq();
std::string board_json = std::string("{\"type\":\"compact.4g\",");
board_json += "\"revision\":\"" + module_name + "\",";
board_json += "\"carrier\":\"" + carrier_name + "\",";
board_json += "\"csq\":\"" + std::to_string(csq) + "\",";
board_json += "\"imei\":\"" + imei + "\",";
board_json += "\"iccid\":\"" + iccid + "\"}";
firmware_upgrade_.SetBoardJson(board_json);
#else
// Try to connect to WiFi, if failed, launch the WiFi configuration AP
auto& wifi_station = WifiStation::GetInstance();
#ifdef CONFIG_USE_DISPLAY
display_.SetText(std::string("Connect to WiFi\n") + wifi_station.GetSsid());
#endif
builtin_led.SetBlue();
builtin_led.StartContinuousBlink(100);
wifi_station.Start();
if (!wifi_station.IsConnected()) {
builtin_led.SetBlue();
builtin_led.Blink(1000, 500);
auto& wifi_ap = WifiConfigurationAp::GetInstance();
wifi_ap.SetSsidPrefix("Xiaozhi");
#ifdef CONFIG_USE_DISPLAY
display_.SetText(wifi_ap.GetSsid() + "\n" + wifi_ap.GetWebServerUrl());
#endif
wifi_ap.Start();
return;
}
// Set the board type for OTA
std::string board_json = std::string("{\"type\":\"compact.wifi\",");
board_json += "\"ssid\":\"" + wifi_station.GetSsid() + "\",";
board_json += "\"rssi\":" + std::to_string(wifi_station.GetRssi()) + ",";
board_json += "\"channel\":" + std::to_string(wifi_station.GetChannel()) + ",";
board_json += "\"ip\":\"" + wifi_station.GetIpAddress() + "\",";
board_json += "\"mac\":\"" + SystemInfo::GetMacAddress() + "\"}";
firmware_upgrade_.SetBoardJson(board_json);
#endif
audio_device_.Initialize();
audio_device_.OnInputData([this](std::vector<int16_t>&& data) {
if (16000 != CONFIG_AUDIO_INPUT_SAMPLE_RATE) {
if (audio_device_.input_channels() == 2) {
audio_device_ = board.CreateAudioDevice();
audio_device_->Initialize();
audio_device_->OnInputData([this](std::vector<int16_t>&& data) {
if (16000 != AUDIO_INPUT_SAMPLE_RATE) {
if (audio_device_->input_channels() == 2) {
auto left_channel = std::vector<int16_t>(data.size() / 2);
auto right_channel = std::vector<int16_t>(data.size() / 2);
for (size_t i = 0, j = 0; i < left_channel.size(); ++i, j += 2) {
@ -268,7 +148,7 @@ void Application::Start() {
}, "play_audio", 4096 * 4, this, 4, NULL);
#ifdef CONFIG_USE_AFE_SR
wake_word_detect_.Initialize(audio_device_.input_channels(), audio_device_.input_reference());
wake_word_detect_.Initialize(audio_device_->input_channels(), audio_device_->input_reference());
wake_word_detect_.OnVadStateChange([this](bool speaking) {
Schedule([this, speaking]() {
auto& builtin_led = BuiltinLed::GetInstance();
@ -317,7 +197,7 @@ void Application::Start() {
});
wake_word_detect_.StartDetection();
audio_processor_.Initialize(audio_device_.input_channels(), audio_device_.input_reference());
audio_processor_.Initialize(audio_device_->input_channels(), audio_device_->input_reference());
audio_processor_.OnOutput([this](std::vector<int16_t>&& data) {
Schedule([this, data = std::move(data)]() {
if (chat_state_ == kChatStateListening) {
@ -361,45 +241,37 @@ void Application::Start() {
volume_up_button_.OnClick([this]() {
Schedule([this]() {
auto volume = audio_device_.output_volume() + 10;
auto volume = audio_device_->output_volume() + 10;
if (volume > 100) {
volume = 100;
}
audio_device_.SetOutputVolume(volume);
#ifdef CONFIG_USE_DISPLAY
audio_device_->SetOutputVolume(volume);
display_.ShowNotification("Volume\n" + std::to_string(volume));
#endif
});
});
volume_up_button_.OnLongPress([this]() {
Schedule([this]() {
audio_device_.SetOutputVolume(100);
#ifdef CONFIG_USE_DISPLAY
audio_device_->SetOutputVolume(100);
display_.ShowNotification("Volume\n100");
#endif
});
});
volume_down_button_.OnClick([this]() {
Schedule([this]() {
auto volume = audio_device_.output_volume() - 10;
auto volume = audio_device_->output_volume() - 10;
if (volume < 0) {
volume = 0;
}
audio_device_.SetOutputVolume(volume);
#ifdef CONFIG_USE_DISPLAY
audio_device_->SetOutputVolume(volume);
display_.ShowNotification("Volume\n" + std::to_string(volume));
#endif
});
});
volume_down_button_.OnLongPress([this]() {
Schedule([this]() {
audio_device_.SetOutputVolume(0);
#ifdef CONFIG_USE_DISPLAY
audio_device_->SetOutputVolume(0);
display_.ShowNotification("Volume\n0");
#endif
});
});
@ -416,14 +288,8 @@ void Application::Start() {
vTaskDelete(NULL);
}, "check_new_version", 4096 * 2, this, 1, NULL);
#ifdef CONFIG_USE_DISPLAY
// Launch a task to update the display
xTaskCreate([](void* arg) {
Application* app = (Application*)arg;
app->UpdateDisplay();
vTaskDelete(NULL);
}, "update_display", 4096, this, 1, NULL);
#endif
chat_state_ = kChatStateIdle;
display_.UpdateDisplay();
}
void Application::Schedule(std::function<void()> callback) {
@ -450,6 +316,7 @@ void Application::MainLoop() {
void Application::SetChatState(ChatState state) {
const char* state_str[] = {
"unknown",
"idle",
"connecting",
"listening",
@ -457,13 +324,14 @@ void Application::SetChatState(ChatState state) {
"wake_word_detected",
"testing",
"upgrading",
"unknown"
"invalid_state"
};
chat_state_ = state;
ESP_LOGI(TAG, "STATE: %s", state_str[chat_state_]);
auto& builtin_led = BuiltinLed::GetInstance();
switch (chat_state_) {
case kChatStateUnknown:
case kChatStateIdle:
builtin_led.TurnOff();
break;
@ -557,7 +425,7 @@ void Application::AudioEncodeTask() {
continue;
}
if (opus_decode_sample_rate_ != CONFIG_AUDIO_OUTPUT_SAMPLE_RATE) {
if (opus_decode_sample_rate_ != AUDIO_OUTPUT_SAMPLE_RATE) {
int target_size = output_resampler_.GetOutputSamples(frame_size);
std::vector<int16_t> resampled(target_size);
output_resampler_.Process(packet->pcm.data(), frame_size, resampled.data());
@ -580,7 +448,7 @@ void Application::HandleAudioPacket(AudioPacket* packet) {
}
// This will block until the audio device has finished playing the audio
audio_device_.OutputData(packet->pcm);
audio_device_->OutputData(packet->pcm);
if (break_speaking_) {
skip_to_end_ = true;
@ -589,7 +457,7 @@ void Application::HandleAudioPacket(AudioPacket* packet) {
int frame_size = opus_decode_sample_rate_ / 1000 * opus_duration_ms_;
std::vector<int16_t> silence(frame_size);
bzero(silence.data(), silence.size() * sizeof(int16_t));
audio_device_.OutputData(silence);
audio_device_->OutputData(silence);
}
break;
}
@ -643,9 +511,9 @@ void Application::SetDecodeSampleRate(int sample_rate) {
opus_decoder_destroy(opus_decoder_);
opus_decode_sample_rate_ = sample_rate;
opus_decoder_ = opus_decoder_create(opus_decode_sample_rate_, 1, NULL);
if (opus_decode_sample_rate_ != CONFIG_AUDIO_OUTPUT_SAMPLE_RATE) {
ESP_LOGI(TAG, "Resampling audio from %d to %d", opus_decode_sample_rate_, CONFIG_AUDIO_OUTPUT_SAMPLE_RATE);
output_resampler_.Configure(opus_decode_sample_rate_, CONFIG_AUDIO_OUTPUT_SAMPLE_RATE);
if (opus_decode_sample_rate_ != AUDIO_OUTPUT_SAMPLE_RATE) {
ESP_LOGI(TAG, "Resampling audio from %d to %d", opus_decode_sample_rate_, AUDIO_OUTPUT_SAMPLE_RATE);
output_resampler_.Configure(opus_decode_sample_rate_, AUDIO_OUTPUT_SAMPLE_RATE);
}
}
@ -657,15 +525,7 @@ void Application::StartWebSocketClient() {
std::string url = CONFIG_WEBSOCKET_URL;
std::string token = "Bearer " + std::string(CONFIG_WEBSOCKET_ACCESS_TOKEN);
#ifdef CONFIG_USE_ML307
ws_client_ = new WebSocket(new Ml307SslTransport(ml307_at_modem_, 0));
#else
if (url.find("wss://") == 0) {
ws_client_ = new WebSocket(new TlsTransport());
} else {
ws_client_ = new WebSocket(new TcpTransport());
}
#endif
ws_client_ = Board::GetInstance().CreateWebSocket();
ws_client_->SetHeader("Authorization", token.c_str());
ws_client_->SetHeader("Protocol-Version", std::to_string(PROTOCOL_VERSION).c_str());
ws_client_->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());

View File

@ -4,9 +4,6 @@
#include <OpusEncoder.h>
#include <OpusResampler.h>
#include <WebSocket.h>
#include <Ml307AtModem.h>
#include <Ml307Http.h>
#include <EspHttp.h>
#include <opus.h>
#include <resampler_structs.h>
@ -16,8 +13,9 @@
#include <list>
#include <condition_variable>
#include "BoxAudioDevice.h"
#include "AudioDevice.h"
#include "Display.h"
#include "Board.h"
#include "FirmwareUpgrade.h"
#ifdef CONFIG_USE_AFE_SR
@ -59,6 +57,7 @@ struct AudioPacket {
enum ChatState {
kChatStateUnknown,
kChatStateIdle,
kChatStateConnecting,
kChatStateListening,
@ -75,6 +74,10 @@ public:
}
void Start();
ChatState GetChatState() const { return chat_state_; }
Display& GetDisplay() { return display_; }
void Schedule(std::function<void()> callback);
void SetChatState(ChatState state);
// 删除拷贝构造函数和赋值运算符
Application(const Application&) = delete;
@ -87,23 +90,11 @@ private:
Button boot_button_;
Button volume_up_button_;
Button volume_down_button_;
#ifdef CONFIG_AUDIO_CODEC_ES8311_ES7210
BoxAudioDevice audio_device_;
#else
AudioDevice audio_device_;
#endif
#ifdef CONFIG_USE_DISPLAY
AudioDevice* audio_device_ = nullptr;
Display display_;
#endif
#ifdef CONFIG_USE_AFE_SR
WakeWordDetect wake_word_detect_;
AudioProcessor audio_processor_;
#endif
#ifdef CONFIG_USE_ML307
Ml307AtModem ml307_at_modem_;
Ml307Http http_;
#else
EspHttp http_;
#endif
FirmwareUpgrade firmware_upgrade_;
std::mutex mutex_;
@ -111,9 +102,10 @@ private:
std::list<std::function<void()>> main_tasks_;
WebSocket* ws_client_ = nullptr;
EventGroupHandle_t event_group_;
volatile ChatState chat_state_ = kChatStateIdle;
volatile ChatState chat_state_ = kChatStateUnknown;
volatile bool break_speaking_ = false;
bool skip_to_end_ = false;
esp_timer_handle_t update_display_timer_ = nullptr;
// Audio encode / decode
TaskHandle_t audio_encode_task_ = nullptr;
@ -127,7 +119,7 @@ private:
OpusDecoder* opus_decoder_ = nullptr;
int opus_duration_ms_ = 60;
int opus_decode_sample_rate_ = CONFIG_AUDIO_OUTPUT_SAMPLE_RATE;
int opus_decode_sample_rate_ = AUDIO_OUTPUT_SAMPLE_RATE;
OpusResampler input_resampler_;
OpusResampler output_resampler_;
@ -136,13 +128,10 @@ private:
StackType_t* check_new_version_task_stack_ = nullptr;
void MainLoop();
void Schedule(std::function<void()> callback);
BinaryProtocol* AllocateBinaryProtocol(const uint8_t* payload, size_t payload_size);
void SetDecodeSampleRate(int sample_rate);
void SetChatState(ChatState state);
void StartWebSocketClient();
void CheckNewVersion();
void UpdateDisplay();
void AudioEncodeTask();
void AudioPlayTask();

View File

@ -1,12 +1,15 @@
#include "AudioDevice.h"
#include "Board.h"
#include <esp_log.h>
#include <cstring>
#include <cmath>
#define TAG "AudioDevice"
AudioDevice::AudioDevice()
: input_sample_rate_(CONFIG_AUDIO_INPUT_SAMPLE_RATE),
output_sample_rate_(CONFIG_AUDIO_OUTPUT_SAMPLE_RATE) {
: input_sample_rate_(AUDIO_INPUT_SAMPLE_RATE),
output_sample_rate_(AUDIO_OUTPUT_SAMPLE_RATE) {
}
AudioDevice::~AudioDevice() {
@ -22,7 +25,7 @@ AudioDevice::~AudioDevice() {
}
void AudioDevice::Initialize() {
#ifdef CONFIG_AUDIO_I2S_METHOD_SIMPLEX
#ifdef AUDIO_I2S_METHOD_SIMPLEX
CreateSimplexChannels();
#else
CreateDuplexChannels();
@ -30,7 +33,7 @@ void AudioDevice::Initialize() {
}
void AudioDevice::CreateDuplexChannels() {
#ifdef CONFIG_AUDIO_I2S_METHOD_DUPLEX
#ifndef AUDIO_I2S_METHOD_SIMPLEX
duplex_ = true;
i2s_chan_config_t chan_cfg = {
@ -65,10 +68,10 @@ void AudioDevice::CreateDuplexChannels() {
},
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_BCLK,
.ws = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_LRCK,
.dout = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_DOUT,
.din = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_DIN,
.bclk = (gpio_num_t)AUDIO_I2S_GPIO_BCLK,
.ws = (gpio_num_t)AUDIO_I2S_GPIO_LRCK,
.dout = (gpio_num_t)AUDIO_I2S_GPIO_DOUT,
.din = (gpio_num_t)AUDIO_I2S_GPIO_DIN,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
@ -85,7 +88,7 @@ void AudioDevice::CreateDuplexChannels() {
}
void AudioDevice::CreateSimplexChannels() {
#ifdef CONFIG_AUDIO_I2S_METHOD_SIMPLEX
#ifdef AUDIO_I2S_METHOD_SIMPLEX
// Create a new channel for speaker
i2s_chan_config_t chan_cfg = {
.id = I2S_NUM_0,
@ -119,9 +122,9 @@ void AudioDevice::CreateSimplexChannels() {
},
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_SPK_GPIO_BCLK,
.ws = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_SPK_GPIO_LRCK,
.dout = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_SPK_GPIO_DOUT,
.bclk = (gpio_num_t)AUDIO_I2S_SPK_GPIO_BCLK,
.ws = (gpio_num_t)AUDIO_I2S_SPK_GPIO_LRCK,
.dout = (gpio_num_t)AUDIO_I2S_SPK_GPIO_DOUT,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
@ -136,10 +139,10 @@ void AudioDevice::CreateSimplexChannels() {
chan_cfg.id = I2S_NUM_1;
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, nullptr, &rx_handle_));
std_cfg.clk_cfg.sample_rate_hz = (uint32_t)input_sample_rate_;
std_cfg.gpio_cfg.bclk = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_MIC_GPIO_SCK;
std_cfg.gpio_cfg.ws = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_MIC_GPIO_WS;
std_cfg.gpio_cfg.bclk = (gpio_num_t)AUDIO_I2S_MIC_GPIO_SCK;
std_cfg.gpio_cfg.ws = (gpio_num_t)AUDIO_I2S_MIC_GPIO_WS;
std_cfg.gpio_cfg.dout = I2S_GPIO_UNUSED;
std_cfg.gpio_cfg.din = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_MIC_GPIO_DIN;
std_cfg.gpio_cfg.din = (gpio_num_t)AUDIO_I2S_MIC_GPIO_DIN;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));

4
main/Board.cc Normal file
View File

@ -0,0 +1,4 @@
#include "Board.h"
#include <esp_log.h>
// static const char *TAG = "Board";

43
main/Board.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef BOARD_H
#define BOARD_H
#include "config.h"
#include <Http.h>
#include <WebSocket.h>
#include <AudioDevice.h>
#include <string>
void* create_board();
class Board {
public:
static Board& GetInstance() {
static Board* instance = nullptr;
if (nullptr == instance) {
instance = static_cast<Board*>(create_board());
}
return *instance;
}
virtual void Initialize() = 0;
virtual ~Board() = default;
virtual AudioDevice* CreateAudioDevice() = 0;
virtual Http* CreateHttp() = 0;
virtual WebSocket* CreateWebSocket() = 0;
virtual bool GetNetworkState(std::string& network_name, int& signal_quality, std::string& signal_quality_text) = 0;
virtual std::string GetJson() = 0;
protected:
Board() = default;
private:
Board(const Board&) = delete; // 禁用拷贝构造函数
Board& operator=(const Board&) = delete; // 禁用赋值操作
};
#define DECLARE_BOARD(BOARD_CLASS_NAME) \
void* create_board() { \
return new BOARD_CLASS_NAME(); \
}
#endif // BOARD_H

View File

@ -1,4 +1,6 @@
#include "BoxAudioDevice.h"
#include "Board.h"
#include <esp_log.h>
#include <cassert>
@ -25,14 +27,14 @@ BoxAudioDevice::~BoxAudioDevice() {
void BoxAudioDevice::Initialize() {
duplex_ = true; // 是否双工
input_reference_ = CONFIG_AUDIO_CODEC_INPUT_REFERENCE; // 是否使用参考输入,实现回声消除
input_reference_ = AUDIO_INPUT_REFERENCE; // 是否使用参考输入,实现回声消除
input_channels_ = input_reference_ ? 2 : 1; // 输入通道数
// Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_cfg = {
.i2c_port = I2C_NUM_0,
.sda_io_num = (gpio_num_t)CONFIG_AUDIO_CODEC_I2C_SDA_PIN,
.scl_io_num = (gpio_num_t)CONFIG_AUDIO_CODEC_I2C_SCL_PIN,
.sda_io_num = (gpio_num_t)AUDIO_CODEC_I2C_SDA_PIN,
.scl_io_num = (gpio_num_t)AUDIO_CODEC_I2C_SCL_PIN,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.intr_priority = 0,
@ -70,7 +72,7 @@ void BoxAudioDevice::Initialize() {
es8311_cfg.ctrl_if = out_ctrl_if_;
es8311_cfg.gpio_if = gpio_if_;
es8311_cfg.codec_mode = ESP_CODEC_DEV_WORK_MODE_DAC;
es8311_cfg.pa_pin = CONFIG_AUDIO_CODEC_PA_PIN;
es8311_cfg.pa_pin = AUDIO_CODEC_PA_PIN;
es8311_cfg.use_mclk = true;
es8311_cfg.hw_gain.pa_voltage = 5.0;
es8311_cfg.hw_gain.codec_dac_voltage = 3.3;
@ -160,10 +162,10 @@ void BoxAudioDevice::CreateDuplexChannels() {
.bit_order_lsb = false
},
.gpio_cfg = {
.mclk = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_MCLK,
.bclk = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_BCLK,
.ws = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_LRCK,
.dout = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_DOUT,
.mclk = (gpio_num_t)AUDIO_I2S_GPIO_MCLK,
.bclk = (gpio_num_t)AUDIO_I2S_GPIO_BCLK,
.ws = (gpio_num_t)AUDIO_I2S_GPIO_LRCK,
.dout = (gpio_num_t)AUDIO_I2S_GPIO_DOUT,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
@ -196,11 +198,11 @@ void BoxAudioDevice::CreateDuplexChannels() {
.total_slot = I2S_TDM_AUTO_SLOT_NUM
},
.gpio_cfg = {
.mclk = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_MCLK,
.bclk = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_BCLK,
.ws = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_LRCK,
.mclk = (gpio_num_t)AUDIO_I2S_GPIO_MCLK,
.bclk = (gpio_num_t)AUDIO_I2S_GPIO_BCLK,
.ws = (gpio_num_t)AUDIO_I2S_GPIO_LRCK,
.dout = I2S_GPIO_UNUSED,
.din = (gpio_num_t)CONFIG_AUDIO_DEVICE_I2S_GPIO_DIN,
.din = (gpio_num_t)AUDIO_I2S_GPIO_DIN,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,

137
main/BuiltinLed.cc Normal file
View File

@ -0,0 +1,137 @@
#include "BuiltinLed.h"
#include "Board.h"
#include <cstring>
#include <esp_log.h>
#define TAG "builtin_led"
BuiltinLed::BuiltinLed() {
mutex_ = xSemaphoreCreateMutex();
blink_event_group_ = xEventGroupCreate();
xEventGroupSetBits(blink_event_group_, BLINK_TASK_STOPPED_BIT);
if (BUILTIN_LED_GPIO == GPIO_NUM_NC) {
ESP_LOGI(TAG, "Builtin LED not connected");
return;
}
Initialize();
SetGrey();
}
BuiltinLed::~BuiltinLed() {
StopBlinkInternal();
if (led_strip_ != nullptr) {
led_strip_del(led_strip_);
}
if (mutex_ != nullptr) {
vSemaphoreDelete(mutex_);
}
if (blink_event_group_ != nullptr) {
vEventGroupDelete(blink_event_group_);
}
}
BuiltinLed& BuiltinLed::GetInstance() {
static BuiltinLed instance;
return instance;
}
void BuiltinLed::Initialize() {
led_strip_config_t strip_config = {};
strip_config.strip_gpio_num = BUILTIN_LED_GPIO;
strip_config.max_leds = 1;
strip_config.led_pixel_format = LED_PIXEL_FORMAT_GRB;
strip_config.led_model = LED_MODEL_WS2812;
led_strip_rmt_config_t rmt_config = {};
rmt_config.resolution_hz = 10 * 1000 * 1000; // 10MHz
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip_));
led_strip_clear(led_strip_);
}
void BuiltinLed::SetColor(uint8_t r, uint8_t g, uint8_t b) {
r_ = r;
g_ = g;
b_ = b;
}
void BuiltinLed::TurnOn() {
if (led_strip_ == nullptr) {
return;
}
StopBlinkInternal();
xSemaphoreTake(mutex_, portMAX_DELAY);
led_strip_set_pixel(led_strip_, 0, r_, g_, b_);
led_strip_refresh(led_strip_);
xSemaphoreGive(mutex_);
}
void BuiltinLed::TurnOff() {
if (led_strip_ == nullptr) {
return;
}
StopBlinkInternal();
xSemaphoreTake(mutex_, portMAX_DELAY);
led_strip_clear(led_strip_);
xSemaphoreGive(mutex_);
}
void BuiltinLed::BlinkOnce() {
Blink(1, 100);
}
void BuiltinLed::Blink(int times, int interval_ms) {
StartBlinkTask(times, interval_ms);
}
void BuiltinLed::StartContinuousBlink(int interval_ms) {
StartBlinkTask(BLINK_INFINITE, interval_ms);
}
void BuiltinLed::StartBlinkTask(int times, int interval_ms) {
if (led_strip_ == nullptr) {
return;
}
StopBlinkInternal();
xSemaphoreTake(mutex_, portMAX_DELAY);
blink_times_ = times;
blink_interval_ms_ = interval_ms;
should_blink_ = true;
xEventGroupClearBits(blink_event_group_, BLINK_TASK_STOPPED_BIT);
xEventGroupSetBits(blink_event_group_, BLINK_TASK_RUNNING_BIT);
xTaskCreate([](void* obj) {
auto this_ = static_cast<BuiltinLed*>(obj);
int count = 0;
while (this_->should_blink_ && (this_->blink_times_ == BLINK_INFINITE || count < this_->blink_times_)) {
xSemaphoreTake(this_->mutex_, portMAX_DELAY);
led_strip_set_pixel(this_->led_strip_, 0, this_->r_, this_->g_, this_->b_);
led_strip_refresh(this_->led_strip_);
xSemaphoreGive(this_->mutex_);
vTaskDelay(this_->blink_interval_ms_ / portTICK_PERIOD_MS);
if (!this_->should_blink_) break;
xSemaphoreTake(this_->mutex_, portMAX_DELAY);
led_strip_clear(this_->led_strip_);
xSemaphoreGive(this_->mutex_);
vTaskDelay(this_->blink_interval_ms_ / portTICK_PERIOD_MS);
if (this_->blink_times_ != BLINK_INFINITE) count++;
}
this_->blink_task_ = nullptr;
xEventGroupClearBits(this_->blink_event_group_, BLINK_TASK_RUNNING_BIT);
xEventGroupSetBits(this_->blink_event_group_, BLINK_TASK_STOPPED_BIT);
vTaskDelete(NULL);
}, "blink", 2048, this, tskIDLE_PRIORITY, &blink_task_);
xSemaphoreGive(mutex_);
}
void BuiltinLed::StopBlinkInternal() {
should_blink_ = false;
xEventGroupWaitBits(blink_event_group_, BLINK_TASK_STOPPED_BIT, pdFALSE, pdTRUE, portMAX_DELAY);
}

52
main/BuiltinLed.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef _BUILTIN_LED_H_
#define _BUILTIN_LED_H_
#include <led_strip.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <freertos/event_groups.h>
#include <atomic>
#define BLINK_INFINITE -1
#define BLINK_TASK_STOPPED_BIT BIT0
#define BLINK_TASK_RUNNING_BIT BIT1
#define DEFAULT_BRIGHTNESS 16
class BuiltinLed {
public:
static BuiltinLed& GetInstance();
void BlinkOnce();
void Blink(int times, int interval_ms);
void StartContinuousBlink(int interval_ms);
void TurnOn();
void TurnOff();
void SetColor(uint8_t r, uint8_t g, uint8_t b);
void SetWhite(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(brightness, brightness, brightness); }
void SetGrey(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(brightness, brightness, brightness); }
void SetRed(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(brightness, 0, 0); }
void SetGreen(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(0, brightness, 0); }
void SetBlue(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(0, 0, brightness); }
private:
BuiltinLed();
~BuiltinLed();
BuiltinLed(const BuiltinLed&) = delete;
BuiltinLed& operator=(const BuiltinLed&) = delete;
SemaphoreHandle_t mutex_;
EventGroupHandle_t blink_event_group_;
TaskHandle_t blink_task_ = nullptr;
led_strip_handle_t led_strip_ = nullptr;
uint8_t r_ = 0, g_ = 0, b_ = 0;
int blink_times_ = 0;
int blink_interval_ms_ = 0;
std::atomic<bool> should_blink_{false};
void Initialize();
void StartBlinkTask(int times, int interval_ms);
void StopBlinkInternal();
};
#endif // _BUILTIN_LED_H_

View File

@ -4,6 +4,9 @@
static const char* TAG = "Button";
Button::Button(gpio_num_t gpio_num) : gpio_num_(gpio_num) {
if (gpio_num == GPIO_NUM_NC) {
return;
}
button_config_t button_config = {
.type = BUTTON_TYPE_GPIO,
.long_press_time = 1000,
@ -27,6 +30,9 @@ Button::~Button() {
}
void Button::OnPress(std::function<void()> callback) {
if (button_handle_ == nullptr) {
return;
}
on_press_ = callback;
iot_button_register_cb(button_handle_, BUTTON_PRESS_DOWN, [](void* handle, void* usr_data) {
Button* button = static_cast<Button*>(usr_data);
@ -37,6 +43,9 @@ void Button::OnPress(std::function<void()> callback) {
}
void Button::OnLongPress(std::function<void()> callback) {
if (button_handle_ == nullptr) {
return;
}
on_long_press_ = callback;
iot_button_register_cb(button_handle_, BUTTON_LONG_PRESS_START, [](void* handle, void* usr_data) {
Button* button = static_cast<Button*>(usr_data);
@ -47,6 +56,9 @@ void Button::OnLongPress(std::function<void()> callback) {
}
void Button::OnClick(std::function<void()> callback) {
if (button_handle_ == nullptr) {
return;
}
on_click_ = callback;
iot_button_register_cb(button_handle_, BUTTON_SINGLE_CLICK, [](void* handle, void* usr_data) {
Button* button = static_cast<Button*>(usr_data);
@ -57,6 +69,9 @@ void Button::OnClick(std::function<void()> callback) {
}
void Button::OnDoubleClick(std::function<void()> callback) {
if (button_handle_ == nullptr) {
return;
}
on_double_click_ = callback;
iot_button_register_cb(button_handle_, BUTTON_DOUBLE_CLICK, [](void* handle, void* usr_data) {
Button* button = static_cast<Button*>(usr_data);

View File

@ -3,18 +3,50 @@ set(SOURCES "AudioDevice.cc"
"SystemInfo.cc"
"SystemReset.cc"
"Application.cc"
"Display.cc"
"Button.cc"
"BuiltinLed.cc"
"Display.cc"
"Board.cc"
"main.cc"
)
set(INCLUDE_DIRS ".")
# BOARD_TYPE
if(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI)
# add all files from boards/bread-compact-wifi
set(BOARD_TYPE "bread-compact-wifi")
file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc)
list(APPEND SOURCES ${BOARD_SOURCES} "WifiBoard.cc")
list(APPEND INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE})
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ML307)
# add all files from boards/bread-compact-ml307
set(BOARD_TYPE "bread-compact-ml307")
file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc)
list(APPEND SOURCES ${BOARD_SOURCES} "Ml307Board.cc")
list(APPEND INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE})
elseif(CONFIG_BOARD_TYPE_ESP_BOX_3)
# add all files from boards/esp-box-3
set(BOARD_TYPE "esp-box-3")
file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc)
list(APPEND SOURCES ${BOARD_SOURCES} "WifiBoard.cc")
list(APPEND INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE})
list(APPEND SOURCES "BoxAudioDevice.cc")
elseif(CONFIG_BOARD_TYPE_KEVIN_BOX_0)
# add all files from boards/kevin-box-0
set(BOARD_TYPE "kevin-box-0")
file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc)
list(APPEND SOURCES ${BOARD_SOURCES} "Ml307Board.cc")
list(APPEND INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE})
list(APPEND SOURCES "BoxAudioDevice.cc")
endif()
if(CONFIG_USE_AFE_SR)
list(APPEND SOURCES "AudioProcessor.cc" "WakeWordDetect.cc")
endif()
if(CONFIG_AUDIO_CODEC_ES8311_ES7210)
list(APPEND SOURCES "BoxAudioDevice.cc")
endif()
idf_component_register(SRCS ${SOURCES}
INCLUDE_DIRS "."
INCLUDE_DIRS ${INCLUDE_DIRS}
)
# 使 target_compile_definitions BOARD_TYPE
target_compile_definitions(${COMPONENT_LIB} PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\")

View File

@ -1,6 +1,3 @@
#include "Display.h"
#include <esp_log.h>
#include <esp_err.h>
#include <esp_lcd_panel_ops.h>
@ -9,11 +6,17 @@
#include <string>
#include <cstdlib>
#include "Display.h"
#include "Board.h"
#include "Application.h"
#define TAG "Display"
#ifdef CONFIG_USE_DISPLAY
Display::Display(int sda_pin, int scl_pin) : sda_pin_(sda_pin), scl_pin_(scl_pin) {
if (sda_pin_ == GPIO_NUM_NC || scl_pin_ == GPIO_NUM_NC) {
ESP_LOGI(TAG, "Display not connected");
return;
}
ESP_LOGI(TAG, "Display Pins: %d, %d", sda_pin_, scl_pin_);
i2c_master_bus_config_t bus_config = {
@ -55,7 +58,7 @@ Display::Display(int sda_pin, int scl_pin) : sda_pin_(sda_pin), scl_pin_(scl_pin
panel_config.bits_per_pixel = 1;
esp_lcd_panel_ssd1306_config_t ssd1306_config = {
.height = CONFIG_DISPLAY_HEIGHT
.height = DISPLAY_HEIGHT
};
panel_config.vendor_config = &ssd1306_config;
@ -68,50 +71,72 @@ Display::Display(int sda_pin, int scl_pin) : sda_pin_(sda_pin), scl_pin_(scl_pin
ESP_LOGE(TAG, "Failed to initialize display");
return;
}
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_, true, true));
ESP_LOGI(TAG, "Initialize LVGL");
lvgl_port_cfg_t port_cfg = ESP_LVGL_PORT_INIT_CONFIG();
lvgl_port_init(&port_cfg);
const lvgl_port_display_cfg_t display_cfg = {
.io_handle = panel_io_,
.panel_handle = panel_,
.buffer_size = 128 * CONFIG_DISPLAY_HEIGHT,
.double_buffer = true,
.hres = 128,
.vres = CONFIG_DISPLAY_HEIGHT,
.monochrome = true,
.rotation = {
.swap_xy = 0,
.mirror_x = 0,
.mirror_y = 0,
},
.flags = {
.buff_dma = 0,
.buff_spiram = 0,
},
};
disp_ = lvgl_port_add_disp(&display_cfg);
lv_disp_set_rotation(disp_, LV_DISP_ROT_180);
// Set the display to on
ESP_LOGI(TAG, "Turning display on");
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true));
ESP_LOGI(TAG, "Adding LCD screen");
const lvgl_port_display_cfg_t display_cfg = {
.io_handle = panel_io_,
.panel_handle = panel_,
.control_handle = nullptr,
.buffer_size = DISPLAY_WIDTH * DISPLAY_HEIGHT,
.double_buffer = false,
.trans_size = 0,
.hres = DISPLAY_WIDTH,
.vres = DISPLAY_HEIGHT,
.monochrome = true,
.rotation = {
.swap_xy = false,
.mirror_x = true,
.mirror_y = true,
},
.flags = {
.buff_dma = 1,
.buff_spiram = 0,
.sw_rotate = 0,
.full_refresh = 0,
.direct_mode = 0,
},
};
disp_ = lvgl_port_add_disp(&display_cfg);;
ESP_LOGI(TAG, "Display Loading...");
if (lvgl_port_lock(0)) {
label_ = lv_label_create(lv_disp_get_scr_act(disp_));
// lv_obj_set_style_text_font(label_, font_, 0);
lv_label_set_text(label_, "Initializing...");
lv_obj_set_width(label_, disp_->driver->hor_res);
lv_obj_set_height(label_, disp_->driver->ver_res);
notification_ = lv_label_create(lv_disp_get_scr_act(disp_));
// lv_obj_set_style_text_font(notification_, font_, 0);
lv_label_set_text(notification_, "Notification\nTest");
lv_obj_set_width(notification_, disp_->driver->hor_res);
lv_obj_set_height(notification_, disp_->driver->ver_res);
lv_obj_set_style_opa(notification_, LV_OPA_MIN, 0);
lvgl_port_unlock();
}
// Create a timer to update the display every 10 seconds
esp_timer_create_args_t update_display_timer_args = {
.callback = [](void *arg) {
Display* display = static_cast<Display*>(arg);
display->UpdateDisplay();
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
.name = "UpdateDisplay",
.skip_unhandled_events = false,
};
ESP_ERROR_CHECK(esp_timer_create(&update_display_timer_args, &update_display_timer_));
ESP_ERROR_CHECK(esp_timer_start_periodic(update_display_timer_, 10 * 1000000));
}
Display::~Display() {
@ -119,6 +144,10 @@ Display::~Display() {
esp_timer_stop(notification_timer_);
esp_timer_delete(notification_timer_);
}
if (update_display_timer_ != nullptr) {
esp_timer_stop(update_display_timer_);
esp_timer_delete(update_display_timer_);
}
lvgl_port_lock(0);
if (label_ != nullptr) {
@ -127,6 +156,10 @@ Display::~Display() {
}
lvgl_port_unlock();
if (font_ != nullptr) {
lv_font_free(font_);
}
if (disp_ != nullptr) {
lvgl_port_deinit();
esp_lcd_panel_del(panel_);
@ -176,4 +209,20 @@ void Display::ShowNotification(const std::string &text) {
}
}
#endif
void Display::UpdateDisplay() {
auto chat_state = Application::GetInstance().GetChatState();
if (chat_state == kChatStateIdle || chat_state == kChatStateConnecting || chat_state == kChatStateListening) {
std::string text;
auto& board = Board::GetInstance();
std::string network_name;
int signal_quality;
std::string signal_quality_text;
if (!board.GetNetworkState(network_name, signal_quality, signal_quality_text)) {
text = "No network";
} else {
ESP_LOGI(TAG, "%s CSQ: %d", network_name.c_str(), signal_quality);
text = network_name + "\n" + signal_quality_text + " (" + std::to_string(signal_quality) + ")";
}
SetText(text);
}
}

View File

@ -17,6 +17,8 @@ public:
void SetText(const std::string &text);
void ShowNotification(const std::string &text);
void UpdateDisplay();
private:
int sda_pin_;
int scl_pin_;
@ -26,9 +28,11 @@ private:
esp_lcd_panel_io_handle_t panel_io_ = nullptr;
esp_lcd_panel_handle_t panel_ = nullptr;
lv_disp_t *disp_ = nullptr;
lv_font_t *font_ = nullptr;
lv_obj_t *label_ = nullptr;
lv_obj_t *notification_ = nullptr;
esp_timer_handle_t notification_timer_ = nullptr;
esp_timer_handle_t update_display_timer_ = nullptr;
std::string text_;
};

View File

@ -1,5 +1,7 @@
#include "FirmwareUpgrade.h"
#include "SystemInfo.h"
#include "Board.h"
#include <cJSON.h>
#include <esp_log.h>
#include <esp_partition.h>
@ -15,7 +17,7 @@
#define TAG "FirmwareUpgrade"
FirmwareUpgrade::FirmwareUpgrade(Http& http) : http_(http) {
FirmwareUpgrade::FirmwareUpgrade() {
}
FirmwareUpgrade::~FirmwareUpgrade() {
@ -38,16 +40,18 @@ void FirmwareUpgrade::CheckVersion() {
return;
}
auto http = Board::GetInstance().CreateHttp();
for (const auto& header : headers_) {
http_.SetHeader(header.first, header.second);
http->SetHeader(header.first, header.second);
}
http_.SetHeader("Content-Type", "application/json");
http_.SetContent(GetPostData());
http_.Open("POST", check_version_url_);
http->SetHeader("Content-Type", "application/json");
http->SetContent(GetPostData());
http->Open("POST", check_version_url_);
auto response = http_.GetBody();
http_.Close();
auto response = http->GetBody();
http->Close();
delete http;
// Response: { "firmware": { "version": "1.0.0", "url": "http://" } }
// Parse the JSON response and check if the version is newer
@ -123,15 +127,17 @@ void FirmwareUpgrade::Upgrade(const std::string& firmware_url) {
bool image_header_checked = false;
std::string image_header;
if (!http_.Open("GET", firmware_url)) {
auto http = Board::GetInstance().CreateHttp();
if (!http->Open("GET", firmware_url)) {
ESP_LOGE(TAG, "Failed to open HTTP connection");
delete http;
return;
}
size_t content_length = http_.GetBodyLength();
size_t content_length = http->GetBodyLength();
if (content_length == 0) {
ESP_LOGE(TAG, "Failed to get content length");
http_.Close();
delete http;
return;
}
@ -139,10 +145,10 @@ void FirmwareUpgrade::Upgrade(const std::string& firmware_url) {
size_t total_read = 0, recent_read = 0;
auto last_calc_time = esp_timer_get_time();
while (true) {
int ret = http_.Read(buffer, sizeof(buffer));
int ret = http->Read(buffer, sizeof(buffer));
if (ret < 0) {
ESP_LOGE(TAG, "Failed to read HTTP data: %s", esp_err_to_name(ret));
http_.Close();
delete http;
return;
}
@ -174,13 +180,13 @@ void FirmwareUpgrade::Upgrade(const std::string& firmware_url) {
auto current_version = esp_app_get_description()->version;
if (memcmp(new_app_info.version, current_version, sizeof(new_app_info.version)) == 0) {
ESP_LOGE(TAG, "Firmware version is the same, skipping upgrade");
http_.Close();
delete http;
return;
}
if (esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle)) {
esp_ota_abort(update_handle);
http_.Close();
delete http;
ESP_LOGE(TAG, "Failed to begin OTA");
return;
}
@ -192,11 +198,11 @@ void FirmwareUpgrade::Upgrade(const std::string& firmware_url) {
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write OTA data: %s", esp_err_to_name(err));
esp_ota_abort(update_handle);
http_.Close();
delete http;
return;
}
}
http_.Close();
delete http;
esp_err_t err = esp_ota_end(update_handle);
if (err != ESP_OK) {

View File

@ -5,11 +5,9 @@
#include <string>
#include <map>
#include <Http.h>
class FirmwareUpgrade {
public:
FirmwareUpgrade(Http& http);
FirmwareUpgrade();
~FirmwareUpgrade();
void SetBoardJson(const std::string& board_json);
@ -21,7 +19,6 @@ public:
void MarkCurrentVersionValid();
private:
Http& http_;
std::string check_version_url_;
bool has_new_version_ = false;
std::string firmware_version_;

View File

@ -18,218 +18,25 @@ config WEBSOCKET_ACCESS_TOKEN
help
Access token for websocket communication.
config AUDIO_INPUT_SAMPLE_RATE
int "Audio Input Sample Rate"
default 16000
choice BOARD_TYPE
prompt "Board Type"
default BOARD_TYPE_BREAD_COMPACT_WIFI
help
Audio input sample rate.
config AUDIO_OUTPUT_SAMPLE_RATE
int "Audio Output Sample Rate"
default 24000
help
Audio output sample rate.
choice AUDIO_CODEC
prompt "Audio Codec"
default AUDIO_CODEC_NONE
help
Audio codec.
config AUDIO_CODEC_ES8311_ES7210
bool "Box: ES8311 + ES7210"
config AUDIO_CODEC_NONE
bool "None"
Board type. 开发板类型
config BOARD_TYPE_BREAD_COMPACT_WIFI
bool "面包板新版接线WiFi"
config BOARD_TYPE_BREAD_COMPACT_ML307
bool "面包板新版接线ML307 AT"
config BOARD_TYPE_ESP_BOX_3
bool "ESP BOX 3"
config BOARD_TYPE_KEVIN_BOX_0
bool "Kevin Box 0"
endchoice
menu "Box Audio Codec I2C and PA Control"
depends on AUDIO_CODEC_ES8311_ES7210
config AUDIO_CODEC_I2C_SDA_PIN
int "Audio Codec I2C SDA Pin"
default 39
help
Audio codec I2C SDA pin.
config AUDIO_CODEC_I2C_SCL_PIN
int "Audio Codec I2C SCL Pin"
default 38
help
Audio codec I2C SCL pin.
config AUDIO_CODEC_PA_PIN
int "Audio Codec PA Pin"
default 40
help
Audio codec PA pin.
config AUDIO_CODEC_INPUT_REFERENCE
bool "Audio Codec Input Reference"
default y
help
Audio codec input reference.
endmenu
choice AUDIO_I2S_METHOD
prompt "Audio I2S Method"
default AUDIO_I2S_METHOD_SIMPLEX if AUDIO_CODEC_NONE
default AUDIO_I2S_METHOD_DUPLEX if AUDIO_CODEC_ES8311_ES7210
help
Audio I2S method.
config AUDIO_I2S_METHOD_SIMPLEX
bool "Simplex"
help
Use I2S 0 as the audio input and I2S 1 as the audio output.
config AUDIO_I2S_METHOD_DUPLEX
bool "Duplex"
help
Use I2S 0 as the audio input and audio output.
endchoice
menu "Audio I2S Simplex"
depends on AUDIO_I2S_METHOD_SIMPLEX
config AUDIO_DEVICE_I2S_MIC_GPIO_WS
int "I2S MIC GPIO WS"
default 4
help
GPIO number of the I2S MIC WS.
config AUDIO_DEVICE_I2S_MIC_GPIO_SCK
int "I2S MIC GPIO BCLK"
default 5
help
GPIO number of the I2S MIC SCK.
config AUDIO_DEVICE_I2S_MIC_GPIO_DIN
int "I2S MIC GPIO DIN"
default 6
help
GPIO number of the I2S MIC DIN.
config AUDIO_DEVICE_I2S_SPK_GPIO_DOUT
int "I2S SPK GPIO DOUT"
default 7
help
GPIO number of the I2S SPK DOUT.
config AUDIO_DEVICE_I2S_SPK_GPIO_BCLK
int "I2S SPK GPIO BCLK"
default 15
help
GPIO number of the I2S SPK BCLK.
config AUDIO_DEVICE_I2S_SPK_GPIO_LRCK
int "I2S SPK GPIO WS"
default 16
help
GPIO number of the I2S SPK LRCK.
endmenu
menu "Audio I2S Duplex"
depends on AUDIO_I2S_METHOD_DUPLEX
config AUDIO_DEVICE_I2S_GPIO_MCLK
int "I2S GPIO MCLK"
default -1
help
GPIO number of the I2S WS.
config AUDIO_DEVICE_I2S_GPIO_LRCK
int "I2S GPIO LRCK"
default 4
help
GPIO number of the I2S LRCK.
config AUDIO_DEVICE_I2S_GPIO_BCLK
int "I2S GPIO BCLK / SCLK"
default 5
help
GPIO number of the I2S BCLK.
config AUDIO_DEVICE_I2S_GPIO_DIN
int "I2S GPIO DIN"
default 6
help
GPIO number of the I2S DIN.
config AUDIO_DEVICE_I2S_GPIO_DOUT
int "I2S GPIO DOUT"
default 7
help
GPIO number of the I2S DOUT.
endmenu
config BOOT_BUTTON_GPIO
int "Boot Button GPIO"
default 0
help
GPIO number of the boot button.
config VOLUME_UP_BUTTON_GPIO
int "Volume Up Button GPIO"
default 40
help
GPIO number of the volume up button.
config VOLUME_DOWN_BUTTON_GPIO
int "Volume Down Button GPIO"
default 39
help
GPIO number of the volume down button.
config USE_AFE_SR
bool "Use Espressif AFE SR"
default y
help
Use AFE SR for wake word detection.
config USE_ML307
bool "Use ML307"
default n
help
Use ML307 as the modem.
config ML307_RX_PIN
int "ML307 RX Pin"
default 11
depends on USE_ML307
help
GPIO number of the ML307 RX.
config ML307_TX_PIN
int "ML307 TX Pin"
default 12
depends on USE_ML307
help
GPIO number of the ML307 TX.
config USE_DISPLAY
bool "Use Display"
default n
help
Use Display.
config DISPLAY_HEIGHT
int "Display Height"
default 32
depends on USE_DISPLAY
help
Display height in pixels.
config DISPLAY_SDA_PIN
int "Display SDA Pin"
default 41
depends on USE_DISPLAY
help
GPIO number of the Display SDA.
config DISPLAY_SCL_PIN
int "Display SCL Pin"
default 42
depends on USE_DISPLAY
help
GPIO number of the Display SCL.
endmenu

98
main/Ml307Board.cc Normal file
View File

@ -0,0 +1,98 @@
#include "Ml307Board.h"
#include "Application.h"
#include <esp_log.h>
#include <Ml307Http.h>
#include <Ml307SslTransport.h>
#include <WebSocket.h>
#include <esp_timer.h>
static const char *TAG = "Ml307Board";
static std::string csq_to_string(int csq) {
if (csq == -1) {
return "No network";
} else if (csq >= 0 && csq <= 9) {
return "Very bad";
} else if (csq >= 10 && csq <= 14) {
return "Bad";
} else if (csq >= 15 && csq <= 19) {
return "Fair";
} else if (csq >= 20 && csq <= 24) {
return "Good";
} else if (csq >= 25 && csq <= 31) {
return "Very good";
}
return "Invalid";
}
Ml307Board::Ml307Board() : modem_(ML307_TX_PIN, ML307_RX_PIN, 4096) {
}
void Ml307Board::StartModem() {
auto& application = Application::GetInstance();
auto& display = application.GetDisplay();
modem_.SetDebug(false);
modem_.SetBaudRate(921600);
// Print the ML307 modem information
std::string module_name = modem_.GetModuleName();
ESP_LOGI(TAG, "ML307 Module: %s", module_name.c_str());
display.SetText(std::string("Wait for network\n") + module_name);
modem_.ResetConnections();
modem_.WaitForNetworkReady();
std::string imei = modem_.GetImei();
std::string iccid = modem_.GetIccid();
ESP_LOGI(TAG, "ML307 IMEI: %s", imei.c_str());
ESP_LOGI(TAG, "ML307 ICCID: %s", iccid.c_str());
// If low power, the material ready event will be triggered by the modem because of a reset
modem_.OnMaterialReady([&application]() {
ESP_LOGI(TAG, "ML307 material ready");
application.Schedule([&application]() {
application.SetChatState(kChatStateIdle);
});
});
}
void Ml307Board::Initialize() {
ESP_LOGI(TAG, "Initializing Ml307Board");
StartModem();
}
AudioDevice* Ml307Board::CreateAudioDevice() {
return new AudioDevice();
}
Http* Ml307Board::CreateHttp() {
return new Ml307Http(modem_);
}
WebSocket* Ml307Board::CreateWebSocket() {
return new WebSocket(new Ml307SslTransport(modem_, 0));
}
bool Ml307Board::GetNetworkState(std::string& network_name, int& signal_quality, std::string& signal_quality_text) {
network_name = modem_.GetCarrierName();
signal_quality = modem_.GetCsq();
signal_quality_text = csq_to_string(signal_quality);
return signal_quality != -1;
}
std::string Ml307Board::GetJson() {
// Set the board type for OTA
std::string board_type = BOARD_TYPE;
std::string module_name = modem_.GetModuleName();
std::string carrier_name = modem_.GetCarrierName();
std::string imei = modem_.GetImei();
std::string iccid = modem_.GetIccid();
int csq = modem_.GetCsq();
std::string board_json = std::string("{\"type\":\"" + board_type + "\",");
board_json += "\"revision\":\"" + module_name + "\",";
board_json += "\"carrier\":\"" + carrier_name + "\",";
board_json += "\"csq\":\"" + std::to_string(csq) + "\",";
board_json += "\"imei\":\"" + imei + "\",";
board_json += "\"iccid\":\"" + iccid + "\"}";
return board_json;
}

23
main/Ml307Board.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef ML307_BOARD_H
#define ML307_BOARD_H
#include "Board.h"
#include <Ml307AtModem.h>
class Ml307Board : public Board {
protected:
Ml307AtModem modem_;
void StartModem();
public:
Ml307Board();
virtual void Initialize() override;
virtual AudioDevice* CreateAudioDevice() override;
virtual Http* CreateHttp() override;
virtual WebSocket* CreateWebSocket() override;
virtual bool GetNetworkState(std::string& network_name, int& signal_quality, std::string& signal_quality_text) override;
virtual std::string GetJson() override;
};
#endif // ML307_BOARD_H

View File

@ -3,11 +3,18 @@
class SystemReset {
public:
SystemReset();
static SystemReset& GetInstance() {
static SystemReset instance;
return instance;
}
void CheckButtons();
private:
SystemReset(); // 构造函数私有化
SystemReset(const SystemReset&) = delete; // 禁用拷贝构造
SystemReset& operator=(const SystemReset&) = delete; // 禁用赋值操作
void ResetNvsFlash();
void ResetToFactory();
void RestartInSeconds(int seconds);

View File

@ -1,5 +1,6 @@
#include <esp_log.h>
#include <model_path.h>
#include <arpa/inet.h>
#include "WakeWordDetect.h"
#include "Application.h"

95
main/WifiBoard.cc Normal file
View File

@ -0,0 +1,95 @@
#include "WifiBoard.h"
#include "Application.h"
#include "WifiStation.h"
#include "WifiConfigurationAp.h"
#include "SystemInfo.h"
#include "BuiltinLed.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <EspHttp.h>
#include <TcpTransport.h>
#include <TlsTransport.h>
#include <WebSocket.h>
#include <esp_log.h>
static const char *TAG = "WifiBoard";
static std::string rssi_to_string(int rssi) {
if (rssi >= -55) {
return "Very good";
} else if (rssi >= -65) {
return "Good";
} else if (rssi >= -75) {
return "Fair";
} else if (rssi >= -85) {
return "Poor";
} else {
return "No network";
}
}
void WifiBoard::StartWifi() {
auto& application = Application::GetInstance();
auto& display = application.GetDisplay();
auto& builtin_led = BuiltinLed::GetInstance();
// Try to connect to WiFi, if failed, launch the WiFi configuration AP
auto& wifi_station = WifiStation::GetInstance();
display.SetText(std::string("Connect to WiFi\n") + wifi_station.GetSsid());
wifi_station.Start();
if (!wifi_station.IsConnected()) {
builtin_led.SetBlue();
builtin_led.Blink(1000, 500);
auto& wifi_ap = WifiConfigurationAp::GetInstance();
wifi_ap.SetSsidPrefix("Xiaozhi");
display.SetText(wifi_ap.GetSsid() + "\n" + wifi_ap.GetWebServerUrl());
wifi_ap.Start();
// Wait until the WiFi configuration is finished
while (true) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
}
void WifiBoard::Initialize() {
ESP_LOGI(TAG, "Initializing WifiBoard");
StartWifi();
}
Http* WifiBoard::CreateHttp() {
return new EspHttp();
}
WebSocket* WifiBoard::CreateWebSocket() {
std::string url = CONFIG_WEBSOCKET_URL;
if (url.find("wss://") == 0) {
return new WebSocket(new TlsTransport());
} else {
return new WebSocket(new TcpTransport());
}
}
bool WifiBoard::GetNetworkState(std::string& network_name, int& signal_quality, std::string& signal_quality_text) {
auto& wifi_station = WifiStation::GetInstance();
if (!wifi_station.IsConnected()) {
return false;
}
network_name = wifi_station.GetSsid();
signal_quality = wifi_station.GetRssi();
signal_quality_text = rssi_to_string(signal_quality);
return signal_quality != -1;
}
std::string WifiBoard::GetJson() {
// Set the board type for OTA
auto& wifi_station = WifiStation::GetInstance();
std::string board_type = BOARD_TYPE;
std::string board_json = std::string("{\"type\":\"" + board_type + "\",");
board_json += "\"ssid\":\"" + wifi_station.GetSsid() + "\",";
board_json += "\"rssi\":" + std::to_string(wifi_station.GetRssi()) + ",";
board_json += "\"channel\":" + std::to_string(wifi_station.GetChannel()) + ",";
board_json += "\"ip\":\"" + wifi_station.GetIpAddress() + "\",";
board_json += "\"mac\":\"" + SystemInfo::GetMacAddress() + "\"}";
return board_json;
}

18
main/WifiBoard.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef WIFI_BOARD_H
#define WIFI_BOARD_H
#include "Board.h"
class WifiBoard : public Board {
protected:
virtual void StartWifi();
public:
virtual void Initialize() override;
virtual Http* CreateHttp() override;
virtual WebSocket* CreateWebSocket() override;
virtual bool GetNetworkState(std::string& network_name, int& signal_quality, std::string& signal_quality_text) override;
virtual std::string GetJson() override;
};
#endif // WIFI_BOARD_H

View File

@ -0,0 +1,18 @@
#include "Ml307Board.h"
#include <esp_log.h>
#define TAG "CompactMl307Board"
class CompactMl307Board : public Ml307Board {
public:
virtual void Initialize() override {
ESP_LOGI(TAG, "Initializing CompactMl307Board");
Ml307Board::Initialize();
}
virtual AudioDevice* CreateAudioDevice() override {
return new AudioDevice();
}
};
DECLARE_BOARD(CompactMl307Board);

View File

@ -0,0 +1,45 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#define AUDIO_INPUT_SAMPLE_RATE 16000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_I2S_METHOD_SIMPLEX
#ifdef AUDIO_I2S_METHOD_SIMPLEX
#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
#else
#define AUDIO_I2S_GPIO_LRCK GPIO_NUM_4
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
#endif
#define BUILTIN_LED_GPIO GPIO_NUM_48
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_40
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_39
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 32
#define DISPLAY_SDA_PIN GPIO_NUM_41
#define DISPLAY_SCL_PIN GPIO_NUM_42
#define ML307_RX_PIN GPIO_NUM_11
#define ML307_TX_PIN GPIO_NUM_12
#endif // _BOARD_CONFIG_H_

View File

@ -0,0 +1,19 @@
#include "WifiBoard.h"
#include <esp_log.h>
#define TAG "CompactWifiBoard"
class CompactWifiBoard : public WifiBoard {
public:
virtual void Initialize() override {
ESP_LOGI(TAG, "Initializing CompactWifiBoard");
WifiBoard::Initialize();
}
virtual AudioDevice* CreateAudioDevice() override {
return new AudioDevice();
}
};
DECLARE_BOARD(CompactWifiBoard);

View File

@ -0,0 +1,40 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#define AUDIO_INPUT_SAMPLE_RATE 16000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_I2S_METHOD_SIMPLEX
#ifdef AUDIO_I2S_METHOD_SIMPLEX
#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
#else
#define AUDIO_I2S_GPIO_LRCK GPIO_NUM_4
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
#endif
#define BUILTIN_LED_GPIO GPIO_NUM_48
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_40
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_39
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 32
#define DISPLAY_SDA_PIN GPIO_NUM_41
#define DISPLAY_SCL_PIN GPIO_NUM_42
#endif // _BOARD_CONFIG_H_

View File

@ -0,0 +1,20 @@
#include "WifiBoard.h"
#include "BoxAudioDevice.h"
#include <esp_log.h>
#define TAG "EspBox3Board"
class EspBox3Board : public WifiBoard {
public:
virtual void Initialize() override {
ESP_LOGI(TAG, "Initializing EspBox3Board");
WifiBoard::Initialize();
}
virtual AudioDevice* CreateAudioDevice() override {
return new BoxAudioDevice();
}
};
DECLARE_BOARD(EspBox3Board);

View File

@ -0,0 +1,32 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#define AUDIO_INPUT_SAMPLE_RATE 16000
#define AUDIO_OUTPUT_SAMPLE_RATE 16000
#define AUDIO_INPUT_REFERENCE true
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_2
#define AUDIO_I2S_GPIO_LRCK GPIO_NUM_45
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_17
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_16
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_15
#define AUDIO_CODEC_PA_PIN GPIO_NUM_46
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_8
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_18
#define BUILTIN_LED_GPIO GPIO_NUM_NC
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 64
#define DISPLAY_SDA_PIN GPIO_NUM_NC
#define DISPLAY_SCL_PIN GPIO_NUM_NC
#endif // _BOARD_CONFIG_H_

View File

@ -0,0 +1,48 @@
#include "Ml307Board.h"
#include "BoxAudioDevice.h"
#include <esp_log.h>
#include <esp_spiffs.h>
#include <driver/gpio.h>
static const char *TAG = "KevinBoxBoard";
class KevinBoxBoard : public Ml307Board {
private:
void MountStorage() {
// Mount the storage partition
esp_vfs_spiffs_conf_t conf = {
.base_path = "/storage",
.partition_label = "storage",
.max_files = 5,
.format_if_mount_failed = true,
};
esp_vfs_spiffs_register(&conf);
}
void Enable4GModule() {
// Make GPIO15 HIGH to enable the 4G module
gpio_config_t ml307_enable_config = {
.pin_bit_mask = (1ULL << 15),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&ml307_enable_config);
gpio_set_level(GPIO_NUM_15, 1);
}
public:
virtual void Initialize() override {
ESP_LOGI(TAG, "Initializing KevinBoxBoard");
MountStorage();
Enable4GModule();
Ml307Board::Initialize();
}
virtual AudioDevice* CreateAudioDevice() override {
return new BoxAudioDevice();
}
};
DECLARE_BOARD(KevinBoxBoard);

View File

@ -0,0 +1,35 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_INPUT_REFERENCE true
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_0
#define AUDIO_I2S_GPIO_LRCK GPIO_NUM_47
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_48
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_45
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_21
#define AUDIO_CODEC_PA_PIN GPIO_NUM_40
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_39
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_38
#define BUILTIN_LED_GPIO GPIO_NUM_8
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_6
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_7
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 64
#define DISPLAY_SDA_PIN GPIO_NUM_4
#define DISPLAY_SCL_PIN GPIO_NUM_5
#define ML307_RX_PIN GPIO_NUM_17
#define ML307_TX_PIN GPIO_NUM_16
#endif // _BOARD_CONFIG_H_

View File

@ -1,14 +1,14 @@
## IDF Component Manager Manifest File
dependencies:
78/esp-builtin-led: "^1.0.2"
78/esp-wifi-connect: "^1.2.0"
78/esp-opus-encoder: "^1.0.2"
78/esp-ml307: "^1.2.1"
espressif/led_strip: "^2.4.1"
espressif/esp_codec_dev: "^1.3.1"
espressif/esp-sr: "^1.9.0"
espressif/button: "^3.3.1"
lvgl/lvgl: "^8.4.0"
esp_lvgl_port: "^1.4.0"
esp_lvgl_port: "^2.4.1"
## Required IDF version
idf:
version: ">=5.3"

View File

@ -13,22 +13,8 @@
extern "C" void app_main(void)
{
#ifdef CONFIG_AUDIO_CODEC_ES8311_ES7210
// Make GPIO15 HIGH to enable the 4G module
gpio_config_t ml307_enable_config = {
.pin_bit_mask = (1ULL << 15),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&ml307_enable_config);
gpio_set_level(GPIO_NUM_15, 1);
#endif
// Check if the reset button is pressed
SystemReset system_reset;
system_reset.CheckButtons();
SystemReset::GetInstance().CheckButtons();
// Initialize the default event loop
ESP_ERROR_CHECK(esp_event_loop_create_default());

View File

@ -4,6 +4,7 @@ nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
model, data, spiffs, 0x10000, 0xF0000,
storage, data, spiffs, 0x100000, 1M,
factory, app, factory, 0x200000, 4M,
ota_0, app, ota_0, 0x600000, 4M,
ota_1, app, ota_1, 0xA00000, 4M,

1 # ESP-IDF Partition Table
4 otadata, data, ota, 0xd000, 0x2000,
5 phy_init, data, phy, 0xf000, 0x1000,
6 model, data, spiffs, 0x10000, 0xF0000,
7 storage, data, spiffs, 0x100000, 1M,
8 factory, app, factory, 0x200000, 4M,
9 ota_0, app, ota_0, 0x600000, 4M,
10 ota_1, app, ota_1, 0xA00000, 4M,

File diff suppressed because it is too large Load Diff

View File

@ -57,7 +57,7 @@ def get_board_name(folder):
basename = os.path.basename(folder)
if basename.startswith("v0.2"):
return "simple"
if basename.startswith("v0.3") or basename.startswith("v0.4"):
if basename.startswith("v0.3") or basename.startswith("v0.4") or basename.startswith("v0.5"):
if "ML307" in basename:
return "compact.4g"
else: