Initial commit

This commit is contained in:
2026-04-26 21:35:04 +08:00
commit da6ca1b09a
1483 changed files with 115719 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
## LILYGO T-CameraPlus-S3
T-CameraPlus-S3 is an intelligent camera module developed based on the ESP32S3 chip, equipped with a 240x240 TFT display, digital microphone, speaker, independent button, power control chip, SD card module, etc. It comes with a basic UI written based on LVGL, which can achieve functions such as file management, music playback, recording, and camera projection (if the factory does not write the program, you need to manually burn the UI program named "Lvgl_UI").
Official github: [T-CameraPlus-S3](https://github.com/Xinyuan-LilyGO/T-CameraPlus-S3)
## Configuration
**Set the compilation target to ESP32S3**
```bash
idf.py set-target esp32s3
```
**Open menuconfig**
```bash
idf.py menuconfig
```
**Select the board**
```
Xiaozhi Assistant -> Board Type -> LILYGO T-CameraPlus-S3_V1_0_V1_1
Or
Xiaozhi Assistant -> Board Type -> LILYGO T-CameraPlus-S3_V1_2
```
**Modify the psram configuration**
```
Component config -> ESP PSRAM -> SPI RAM config -> Mode (QUAD/OCT) -> Quad Mode PSRAM
```
**Select and set camera sensor**
```
Component config -> Espressif Camera Sensors Configurations -> Camera Sensor Configuration -> Select and Set Camera Sensor -> OV2640 -> Select default output format for DVP interface -> YUV422 240x240 25fps, DVP 8-bit, 20M input
```
**Build**
```bash
idf.py build
```

View File

@@ -0,0 +1,57 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#include "pin_config.h"
#define AUDIO_INPUT_SAMPLE_RATE 16000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#ifdef CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_0_V1_1
#define AUDIO_INPUT_REFERENCE true
#define AUDIO_MIC_I2S_GPIO_BCLK MSM261_BCLK
#define AUDIO_MIC_I2S_GPIO_WS MSM261_WS
#define AUDIO_MIC_I2S_GPIO_DATA MSM261_DATA
#elif defined CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_2
#define AUDIO_INPUT_REFERENCE false
#define AUDIO_MIC_I2S_GPIO_BCLK GPIO_NUM_NC
#define AUDIO_MIC_I2S_GPIO_WS MP34DT05TR_LRCLK
#define AUDIO_MIC_I2S_GPIO_DATA MP34DT05TR_DATA
#define AUDIO_MIC_SPKR_EN MP34DT05TR_MAX98357_EN
#endif
#define AUDIO_SPKR_I2S_GPIO_BCLK MAX98357A_BCLK
#define AUDIO_SPKR_I2S_GPIO_LRCLK MAX98357A_LRCLK
#define AUDIO_SPKR_I2S_GPIO_DATA MAX98357A_DATA
#define TOUCH_I2C_SDA_PIN TP_SDA
#define TOUCH_I2C_SCL_PIN TP_SCL
#define BUILTIN_LED_GPIO GPIO_NUM_NC
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define KEY1_BUTTON_GPIO KEY1
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC
#define DISPLAY_WIDTH LCD_WIDTH
#define DISPLAY_HEIGHT LCD_HEIGHT
#define DISPLAY_MOSI LCD_MOSI
#define DISPLAY_SCLK LCD_SCLK
#define DISPLAY_DC LCD_DC
#define DISPLAY_RST LCD_RST
#define DISPLAY_CS LCD_CS
#define DISPLAY_BL LCD_BL
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_Y false
#define DISPLAY_SWAP_XY false
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_BACKLIGHT_PIN DISPLAY_BL
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define AP1511B_GPIO AP1511B_FBC
#endif // _BOARD_CONFIG_H_

View File

@@ -0,0 +1,23 @@
{
"target": "esp32s3",
"builds": [
{
"name": "lilygo-t-cameraplus-s3",
"sdkconfig_append": [
"CONFIG_SPIRAM_MODE_QUAD=y",
"CONFIG_CAMERA_OV2640=y",
"CONFIG_CAMERA_OV2640_AUTO_DETECT_DVP_INTERFACE_SENSOR=y",
"CONFIG_CAMERA_OV2640_DVP_YUV422_240X240_25FPS=y"
]
},
{
"name": "lilygo-t-cameraplus-s3_v1_2",
"sdkconfig_append": [
"CONFIG_SPIRAM_MODE_QUAD=y",
"CONFIG_CAMERA_OV2640=y",
"CONFIG_CAMERA_OV2640_AUTO_DETECT_DVP_INTERFACE_SENSOR=y",
"CONFIG_CAMERA_OV2640_DVP_YUV422_240X240_25FPS=y"
]
}
]
}

View File

@@ -0,0 +1,44 @@
#ifndef __IR_FILTER_CONTROLLER_H__
#define __IR_FILTER_CONTROLLER_H__
#include "mcp_server.h"
class IrFilterController {
private:
bool enable_ = false;
gpio_num_t gpio_num_;
public:
IrFilterController(gpio_num_t gpio_num) : gpio_num_(gpio_num) {
gpio_config_t config = {
.pin_bit_mask = (1ULL << gpio_num_),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ESP_ERROR_CHECK(gpio_config(&config));
gpio_set_level(gpio_num_, 0);
auto& mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.camera.get_ir_filter_state", "Get the state of the camera's infrared filter", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
return enable_ ? "{\"enable\": true}" : "{\"enable\": false}";
});
mcp_server.AddTool("self.camera.enable_ir_filter", "Enable the camera's infrared filter", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
enable_ = true;
gpio_set_level(gpio_num_, 1);
return true;
});
mcp_server.AddTool("self.camera.disable_ir_filter", "Disable the camera's infrared filter", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
enable_ = false;
gpio_set_level(gpio_num_, 0);
return true;
});
}
};
#endif // __IR_FILTER_CONTROLLER_H__

View File

@@ -0,0 +1,350 @@
#include "wifi_board.h"
#include "tcamerapluss3_audio_codec.h"
#include "display/lcd_display.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "power_save_timer.h"
#include "i2c_device.h"
#include "sy6970.h"
#include "pin_config.h"
#include "esp_video.h"
#include "ir_filter_controller.h"
#include <esp_log.h>
#include <esp_lcd_panel_vendor.h>
#include <driver/i2c_master.h>
#define TAG "LilygoTCameraPlusS3Board"
class Cst816x : public I2cDevice {
public:
struct TouchPoint_t {
int num = 0;
int x = -1;
int y = -1;
};
Cst816x(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
uint8_t chip_id = ReadReg(0xA7);
ESP_LOGI(TAG, "Get chip ID: 0x%02X", chip_id);
read_buffer_ = new uint8_t[6];
}
~Cst816x() {
delete[] read_buffer_;
}
void UpdateTouchPoint() {
ReadRegs(0x02, read_buffer_, 6);
tp_.num = read_buffer_[0] & 0x0F;
tp_.x = ((read_buffer_[1] & 0x0F) << 8) | read_buffer_[2];
tp_.y = ((read_buffer_[3] & 0x0F) << 8) | read_buffer_[4];
}
const TouchPoint_t &GetTouchPoint() {
return tp_;
}
private:
uint8_t *read_buffer_ = nullptr;
TouchPoint_t tp_;
};
class Pmic : public Sy6970 {
public:
Pmic(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : Sy6970(i2c_bus, addr) {
uint8_t chip_id = ReadReg(0x14);
ESP_LOGI(TAG, "Get sy6970 chip ID: 0x%02X", (chip_id & 0B00111000));
WriteReg(0x00, 0B00001000); // Disable ILIM pin
WriteReg(0x02, 0B11011101); // Enable ADC measurement function
WriteReg(0x07, 0B10001101); // Disable watchdog timer feeding function
}
};
class LilygoTCameraPlusS3Board : public WifiBoard {
private:
i2c_master_bus_handle_t i2c_bus_;
Cst816x *cst816d_;
Pmic* pmic_;
LcdDisplay *display_;
Button boot_button_;
Button key1_button_;
PowerSaveTimer* power_save_timer_;
EspVideo* camera_;
void InitializePowerSaveTimer() {
power_save_timer_ = new PowerSaveTimer(-1, 60, -1);
power_save_timer_->OnEnterSleepMode([this]() {
GetDisplay()->SetPowerSaveMode(true);
GetBacklight()->SetBrightness(10);
});
power_save_timer_->OnExitSleepMode([this]() {
GetDisplay()->SetPowerSaveMode(false);
GetBacklight()->RestoreBrightness();
});
power_save_timer_->OnShutdownRequest([this]() {
pmic_->PowerOff();
});
power_save_timer_->SetEnabled(true);
}
void InitI2c(){
// Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_config = {
.i2c_port = I2C_NUM_0,
.sda_io_num = TOUCH_I2C_SDA_PIN,
.scl_io_num = TOUCH_I2C_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(&i2c_bus_config, &i2c_bus_));
}
void I2cDetect() {
uint8_t address;
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
for (int i = 0; i < 128; i += 16) {
printf("%02x: ", i);
for (int j = 0; j < 16; j++) {
fflush(stdout);
address = i + j;
esp_err_t ret = i2c_master_probe(i2c_bus_, address, pdMS_TO_TICKS(200));
if (ret == ESP_OK) {
printf("%02x ", address);
} else if (ret == ESP_ERR_TIMEOUT) {
printf("UU ");
} else {
printf("-- ");
}
}
printf("\r\n");
}
}
static void TouchpadDaemon(void *param) {
vTaskDelay(pdMS_TO_TICKS(2000));
auto &board = (LilygoTCameraPlusS3Board&)Board::GetInstance();
auto touchpad = board.GetTouchpad();
bool was_touched = false;
while (1) {
touchpad->UpdateTouchPoint();
if (touchpad->GetTouchPoint().num > 0){
// On press
if (!was_touched) {
was_touched = true;
Application::GetInstance().ToggleChatState();
}
}
// On release
else if (was_touched) {
was_touched = false;
}
vTaskDelay(pdMS_TO_TICKS(50));
}
vTaskDelete(NULL);
}
void InitCst816d() {
ESP_LOGI(TAG, "Init CST816x");
cst816d_ = new Cst816x(i2c_bus_, CST816_ADDRESS);
xTaskCreate(TouchpadDaemon, "tp", 2048, NULL, 5, NULL);
}
void InitSpi() {
spi_bus_config_t buscfg = {};
buscfg.mosi_io_num = DISPLAY_MOSI;
buscfg.miso_io_num = GPIO_NUM_NC;
buscfg.sclk_io_num = DISPLAY_SCLK;
buscfg.quadwp_io_num = GPIO_NUM_NC;
buscfg.quadhd_io_num = GPIO_NUM_NC;
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
}
void InitSy6970() {
ESP_LOGI(TAG, "Init Sy6970");
pmic_ = new Pmic(i2c_bus_, SY6970_ADDRESS);
}
void InitializeSt7789Display() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;
// 液晶屏控制IO初始化
ESP_LOGD(TAG, "Install panel IO");
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = LCD_CS;
io_config.dc_gpio_num = LCD_DC;
io_config.spi_mode = 0;
io_config.pclk_hz = 60 * 1000 * 1000;
io_config.trans_queue_depth = 10;
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io));
// 初始化液晶屏驱动芯片ST7789
ESP_LOGD(TAG, "Install LCD driver");
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = LCD_RST;
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
panel_config.bits_per_pixel = 16;
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel));
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY));
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y));
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, true));
display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
}
void InitializeButtons() {
boot_button_.OnClick([this]() {
power_save_timer_->WakeUp();
auto& app = Application::GetInstance();
// During startup (before connected), pressing BOOT button enters Wi-Fi config mode without reboot
if (app.GetDeviceState() == kDeviceStateStarting) {
EnterWifiConfigMode();
return;
}
app.ToggleChatState();
});
key1_button_.OnClick([this]() {
if (camera_) {
camera_->Capture();
}
});
}
void InitializeCamera() {
static esp_cam_ctlr_dvp_pin_config_t dvp_pin_config = {
.data_width = CAM_CTLR_DATA_WIDTH_8,
.data_io = {
[0] = Y2_GPIO_NUM,
[1] = Y3_GPIO_NUM,
[2] = Y4_GPIO_NUM,
[3] = Y5_GPIO_NUM,
[4] = Y6_GPIO_NUM,
[5] = Y7_GPIO_NUM,
[6] = Y8_GPIO_NUM,
[7] = Y9_GPIO_NUM,
},
.vsync_io = VSYNC_GPIO_NUM,
.de_io = HREF_GPIO_NUM,
.pclk_io = PCLK_GPIO_NUM,
.xclk_io = XCLK_GPIO_NUM,
};
esp_video_init_sccb_config_t sccb_config = {
#ifdef CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_0_V1_1
.init_sccb = false,
.i2c_handle = i2c_bus_,
#elif defined CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_2
.init_sccb = true,
.i2c_config = {
.port = 1,
.scl_pin = SIOC_GPIO_NUM,
.sda_pin = SIOD_GPIO_NUM,
},
#endif
.freq = 100000,
};
esp_video_init_dvp_config_t dvp_config = {
.sccb_config = sccb_config,
.reset_pin = RESET_GPIO_NUM,
.pwdn_pin = PWDN_GPIO_NUM,
.dvp_pin = dvp_pin_config,
.xclk_freq = XCLK_FREQ_HZ,
};
esp_video_init_config_t video_config = {
.dvp = &dvp_config,
};
camera_ = new EspVideo(video_config);
camera_->SetVFlip(1);
camera_->SetHMirror(1);
}
void InitializeTools() {
static IrFilterController irFilter(AP1511B_GPIO);
}
public:
LilygoTCameraPlusS3Board() : boot_button_(BOOT_BUTTON_GPIO), key1_button_(KEY1_BUTTON_GPIO) {
InitializePowerSaveTimer();
InitI2c();
InitSy6970();
InitCst816d();
I2cDetect();
InitSpi();
InitializeSt7789Display();
InitializeButtons();
InitializeCamera();
InitializeTools();
GetBacklight()->RestoreBrightness();
}
virtual AudioCodec *GetAudioCodec() override {
static Tcamerapluss3AudioCodec audio_codec(
AUDIO_INPUT_SAMPLE_RATE,
AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_MIC_I2S_GPIO_BCLK,
AUDIO_MIC_I2S_GPIO_WS,
AUDIO_MIC_I2S_GPIO_DATA,
AUDIO_SPKR_I2S_GPIO_BCLK,
AUDIO_SPKR_I2S_GPIO_LRCLK,
AUDIO_SPKR_I2S_GPIO_DATA,
AUDIO_INPUT_REFERENCE);
return &audio_codec;
}
virtual Display *GetDisplay() override{
return display_;
}
virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging) override {
static bool last_discharging = false;
charging = pmic_->IsCharging();
bool is_power_good = pmic_->IsPowerGood();
discharging = !charging && is_power_good;
if (discharging != last_discharging) {
power_save_timer_->SetEnabled(discharging);
last_discharging = discharging;
}
level = pmic_->GetBatteryLevel();
return true;
}
virtual void SetPowerSaveLevel(PowerSaveLevel level) override {
if (level != PowerSaveLevel::LOW_POWER) {
power_save_timer_->WakeUp();
}
WifiBoard::SetPowerSaveLevel(level);
}
virtual Backlight* GetBacklight() override {
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
return &backlight;
}
Cst816x *GetTouchpad() {
return cst816d_;
}
virtual Camera* GetCamera() override {
return camera_;
}
};
DECLARE_BOARD(LilygoTCameraPlusS3Board);

View File

@@ -0,0 +1,154 @@
/*
* @Description: None
* @Author: LILYGO_L
* @Date: 2024-11-11 11:36:49
* @LastEditTime: 2025-06-03 17:37:08
* @License: GPL 3.0
*/
#pragma once
#ifdef CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_0_V1_1
#define T_CameraPlus_S3_V1_0_V1_1
#elif defined CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_2
#define T_CameraPlus_S3_V1_2
#endif
#ifdef T_CameraPlus_S3_V1_0_V1_1
// SPI
#define SPI_SCLK GPIO_NUM_36
#define SPI_MOSI GPIO_NUM_35
#define SPI_MISO GPIO_NUM_37
// IIC
#define IIC_SDA GPIO_NUM_1
#define IIC_SCL GPIO_NUM_2
// MSM261
#define MSM261_BCLK GPIO_NUM_18
#define MSM261_WS GPIO_NUM_39
#define MSM261_DATA GPIO_NUM_40
// MAX98357A
#define MAX98357A_DATA GPIO_NUM_38
// FP-133H01D
#define LCD_CS GPIO_NUM_34
#define LCD_RST GPIO_NUM_33
// OV2640
#define OV2640_PWDN GPIO_NUM_NC
#define OV2640_RESET GPIO_NUM_3
#define OV2640_VSYNC GPIO_NUM_4
// CST816
#define TP_RST GPIO_NUM_48
// SY6970
#define SY6970_INT GPIO_NUM_47
#endif
#ifdef T_CameraPlus_S3_V1_2
// SPI
#define SPI_SCLK GPIO_NUM_35
#define SPI_MOSI GPIO_NUM_34
#define SPI_MISO GPIO_NUM_48
// IIC
#define IIC_SDA GPIO_NUM_33
#define IIC_SCL GPIO_NUM_37
// MP34DT05TR
#define MP34DT05TR_LRCLK GPIO_NUM_40
#define MP34DT05TR_DATA GPIO_NUM_38
#define MP34DT05TR_MAX98357_EN GPIO_NUM_18
// MAX98357A
#define MAX98357A_DATA GPIO_NUM_39
// FP-133H01D
#define LCD_CS GPIO_NUM_36
#define LCD_RST GPIO_NUM_NC
// OV2640
#define OV2640_PWDN GPIO_NUM_4
#define OV2640_RESET GPIO_NUM_NC
#define OV2640_VSYNC GPIO_NUM_3
// CST816
#define TP_RST GPIO_NUM_NC
#endif
// SD
#define SD_CS GPIO_NUM_21
#define SD_SCLK SPI_SCLK
#define SD_MOSI SPI_MOSI
#define SD_MISO SPI_MISO
// MAX98357A
#define MAX98357A_BCLK GPIO_NUM_41
#define MAX98357A_LRCLK GPIO_NUM_42
// FP-133H01D
#define LCD_WIDTH 240
#define LCD_HEIGHT 240
#define LCD_BL GPIO_NUM_46
#define LCD_MOSI SPI_MOSI
#define LCD_SCLK SPI_SCLK
#define LCD_DC GPIO_NUM_45
// SY6970
#define SY6970_SDA IIC_SDA
#define SY6970_SCL IIC_SCL
#define SY6970_ADDRESS 0x6A
// OV2640
#define OV2640_XCLK GPIO_NUM_7
#define OV2640_SDA GPIO_NUM_1
#define OV2640_SCL GPIO_NUM_2
#define OV2640_D9 GPIO_NUM_6
#define OV2640_D8 GPIO_NUM_8
#define OV2640_D7 GPIO_NUM_9
#define OV2640_D6 GPIO_NUM_11
#define OV2640_D5 GPIO_NUM_13
#define OV2640_D4 GPIO_NUM_15
#define OV2640_D3 GPIO_NUM_14
#define OV2640_D2 GPIO_NUM_12
#define OV2640_HREF GPIO_NUM_5
#define OV2640_PCLK GPIO_NUM_10
#define PWDN_GPIO_NUM OV2640_PWDN
#define RESET_GPIO_NUM OV2640_RESET
#define XCLK_GPIO_NUM OV2640_XCLK
#define SIOD_GPIO_NUM OV2640_SDA
#define SIOC_GPIO_NUM OV2640_SCL
#define Y9_GPIO_NUM OV2640_D9
#define Y8_GPIO_NUM OV2640_D8
#define Y7_GPIO_NUM OV2640_D7
#define Y6_GPIO_NUM OV2640_D6
#define Y5_GPIO_NUM OV2640_D5
#define Y4_GPIO_NUM OV2640_D4
#define Y3_GPIO_NUM OV2640_D3
#define Y2_GPIO_NUM OV2640_D2
#define VSYNC_GPIO_NUM OV2640_VSYNC
#define HREF_GPIO_NUM OV2640_HREF
#define PCLK_GPIO_NUM OV2640_PCLK
#define XCLK_FREQ_HZ 20000000
// CST816
#define CST816_ADDRESS 0x15
#define TP_SDA IIC_SDA
#define TP_SCL IIC_SCL
#define TP_INT GPIO_NUM_47
// AP1511B
#define AP1511B_FBC GPIO_NUM_16
// KEY
#define KEY1 GPIO_NUM_17

View File

@@ -0,0 +1,169 @@
#include "tcamerapluss3_audio_codec.h"
#include <esp_log.h>
#include <driver/i2c_master.h>
#include <driver/i2s_tdm.h>
#include <driver/i2s_pdm.h>
#include "config.h"
static const char TAG[] = "Tcamerapluss3AudioCodec";
Tcamerapluss3AudioCodec::Tcamerapluss3AudioCodec(int input_sample_rate, int output_sample_rate,
gpio_num_t mic_bclk, gpio_num_t mic_ws, gpio_num_t mic_data,
gpio_num_t spkr_bclk, gpio_num_t spkr_lrclk, gpio_num_t spkr_data,
bool input_reference) {
duplex_ = true; // 是否双工
input_reference_ = input_reference; // 是否使用参考输入,实现回声消除
input_channels_ = input_reference_ ? 2 : 1; // 输入通道数
input_sample_rate_ = input_sample_rate;
output_sample_rate_ = output_sample_rate;
CreateVoiceHardware(mic_bclk, mic_ws, mic_data, spkr_bclk, spkr_lrclk, spkr_data);
ESP_LOGI(TAG, "Tcamerapluss3AudioCodec initialized");
#ifdef CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_2
gpio_config_t config_mic_spkr_en;
config_mic_spkr_en.pin_bit_mask = BIT64(AUDIO_MIC_SPKR_EN);
config_mic_spkr_en.mode = GPIO_MODE_OUTPUT;
config_mic_spkr_en.pull_up_en = GPIO_PULLUP_ENABLE;
config_mic_spkr_en.pull_down_en = GPIO_PULLDOWN_DISABLE;
config_mic_spkr_en.intr_type = GPIO_INTR_DISABLE;
#if SOC_GPIO_SUPPORT_PIN_HYS_FILTER
config_mic_spkr_en.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE;
#endif
gpio_config(&config_mic_spkr_en);
gpio_set_level(AUDIO_MIC_SPKR_EN, 0);
#endif
}
Tcamerapluss3AudioCodec::~Tcamerapluss3AudioCodec() {
audio_codec_delete_codec_if(in_codec_if_);
audio_codec_delete_ctrl_if(in_ctrl_if_);
audio_codec_delete_codec_if(out_codec_if_);
audio_codec_delete_ctrl_if(out_ctrl_if_);
audio_codec_delete_gpio_if(gpio_if_);
audio_codec_delete_data_if(data_if_);
}
void Tcamerapluss3AudioCodec::CreateVoiceHardware(gpio_num_t mic_bclk, gpio_num_t mic_ws, gpio_num_t mic_data,
gpio_num_t spkr_bclk, gpio_num_t spkr_lrclk, gpio_num_t spkr_data) {
i2s_chan_config_t mic_chan_config = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
mic_chan_config.auto_clear = true; // Auto clear the legacy data in the DMA buffer
i2s_chan_config_t spkr_chan_config = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_MASTER);
spkr_chan_config.auto_clear = true; // Auto clear the legacy data in the DMA buffer
ESP_ERROR_CHECK(i2s_new_channel(&mic_chan_config, NULL, &rx_handle_));
ESP_ERROR_CHECK(i2s_new_channel(&spkr_chan_config, &tx_handle_, NULL));
#ifdef CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_0_V1_1
i2s_std_config_t mic_config = {
.clk_cfg = {
.sample_rate_hz = (uint32_t)input_sample_rate_,
.clk_src = I2S_CLK_SRC_DEFAULT,
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
#ifdef I2S_HW_VERSION_2
.ext_clk_freq_hz = 0,
#endif
},
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = mic_bclk,
.ws = mic_ws,
.dout = I2S_GPIO_UNUSED,
.din = mic_data,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = true // 默认右通道
}
}
};
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_, &mic_config));
#elif defined CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_2
i2s_pdm_rx_config_t mic_config = {
.clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(static_cast<uint32_t>(input_sample_rate_)),
/* The data bit-width of PDM mode is fixed to 16 */
.slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.clk = mic_ws,
.din = mic_data,
.invert_flags = {
.clk_inv = false,
},
},
};
ESP_ERROR_CHECK(i2s_channel_init_pdm_rx_mode(rx_handle_, &mic_config));
#endif
i2s_std_config_t spkr_config = {
.clk_cfg ={
.sample_rate_hz = static_cast<uint32_t>(11025),
.clk_src = I2S_CLK_SRC_DEFAULT,
.ext_clk_freq_hz = 0,
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
},
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg ={
.mclk = I2S_GPIO_UNUSED,
.bclk = spkr_bclk,
.ws = spkr_lrclk,
.dout = spkr_data,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false
}
}
};
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &spkr_config));
ESP_LOGI(TAG, "Voice hardware created");
}
void Tcamerapluss3AudioCodec::SetOutputVolume(int volume) {
volume_ = volume;
AudioCodec::SetOutputVolume(volume);
}
void Tcamerapluss3AudioCodec::EnableInput(bool enable) {
AudioCodec::EnableInput(enable);
}
void Tcamerapluss3AudioCodec::EnableOutput(bool enable) {
AudioCodec::EnableOutput(enable);
}
int Tcamerapluss3AudioCodec::Read(int16_t *dest, int samples) {
if (input_enabled_) {
size_t bytes_read;
i2s_channel_read(rx_handle_, dest, samples * sizeof(int16_t), &bytes_read, portMAX_DELAY);
// 麦克风接收音量放大20倍限制在 int16_t 范围内防止溢出)
int16_t *ptr = dest;
for (int i = 0; i < samples; i++) {
int32_t amplified = *ptr * 20;
*ptr++ = (amplified > 32767) ? 32767 : (amplified < -32768) ? -32768 : amplified;
}
}
return samples;
}
int Tcamerapluss3AudioCodec::Write(const int16_t *data, int samples){
if (output_enabled_){
size_t bytes_read;
auto output_data = (int16_t *)malloc(samples * sizeof(int16_t));
for (size_t i = 0; i < samples; i++){
output_data[i] = (float)data[i] * (float)(volume_ / 100.0);
}
i2s_channel_write(tx_handle_, output_data, samples * sizeof(int16_t), &bytes_read, portMAX_DELAY);
free(output_data);
}
return samples;
}

View File

@@ -0,0 +1,37 @@
#ifndef _TCIRCLES3_AUDIO_CODEC_H
#define _TCIRCLES3_AUDIO_CODEC_H
#include "audio_codec.h"
#include <esp_codec_dev.h>
#include <esp_codec_dev_defaults.h>
class Tcamerapluss3AudioCodec : public AudioCodec {
private:
const audio_codec_data_if_t *data_if_ = nullptr;
const audio_codec_ctrl_if_t *out_ctrl_if_ = nullptr;
const audio_codec_if_t *out_codec_if_ = nullptr;
const audio_codec_ctrl_if_t *in_ctrl_if_ = nullptr;
const audio_codec_if_t *in_codec_if_ = nullptr;
const audio_codec_gpio_if_t *gpio_if_ = nullptr;
uint32_t volume_ = 70;
void CreateVoiceHardware(gpio_num_t mic_bclk, gpio_num_t mic_ws, gpio_num_t mic_data,gpio_num_t spkr_bclk, gpio_num_t spkr_lrclk, gpio_num_t spkr_data);
virtual int Read(int16_t *dest, int samples) override;
virtual int Write(const int16_t *data, int samples) override;
public:
Tcamerapluss3AudioCodec(int input_sample_rate, int output_sample_rate,
gpio_num_t mic_bclk, gpio_num_t mic_ws, gpio_num_t mic_data,
gpio_num_t spkr_bclk, gpio_num_t spkr_lrclk, gpio_num_t spkr_data,
bool input_reference);
virtual ~Tcamerapluss3AudioCodec();
virtual void SetOutputVolume(int volume) override;
virtual void EnableInput(bool enable) override;
virtual void EnableOutput(bool enable) override;
};
#endif // _BOX_AUDIO_CODEC_H