
- Implémentation de la détection des mots d’activation à l’aide de WakeNet
- Construire la reconnaissance des commandes avec MultiNet
- Création d’une interface graphique tactile à l’aide de la bibliothèque LVGL
- Lecture de commentaires audio via l’interface I2S
- Contrôler le matériel (LED) via GPIO
| S. Non | Composant | Quantité |
| 1 | Carte de développement ESP32-S3-BOX-3 | 1 |
| 2 | Module LED RVB | 1 |
| 3 | Fils de liaison | Au besoin |
| 4 | Câble USB-C (pour la programmation et l’alimentation) | 1 |
- ESP-IDF v5.5.2 – Cadre de développement Espressif IoT
- Python3.12+ – Requis pour les outils ESP-IDF

Voici l’ESP32S3-Box-3 avec la LED attachée.

git clone https://github.com/Circuit-Digest/Voice-Activated-LED-Controller-with-Touch-Interface-Using-ESP32S3-Box-3
. $HOME/esp/esp-idf/export.sh
cd /path_to_your_project_directory
idf.py menuconfig
idf.py build
idf.py flash monitor
Microphone -> I2S -> AFE -> WakeNet -> Wake Detection Event

- WAKENET_DETECTED – Mot de réveil détecté ; commencez à écouter les commandes.
- WAKENET_CHANNEL_VERIFIED – Chaîne vérifiée ; prêt pour la reconnaissance des commandes.
- audio_feed_task() – Lit l’audio depuis I2S et le transmet à AFE
- audio_detect_task() – Traite la sortie AFE et détecte les mots de réveil
- app_sr_start() – Initialise les modèles AFE et WakeNet
| Mot de réveil | Langue | Clé de configuration |
| Salut ESP |
Anglais |
|
| Salut Lexin | Chinois |
|
| Alexa | Anglais |
|
| Xiao Ai Tong Xue | Chinois |
|
| Ni Hao Xiao Zhi | Chinois |
|
// In app_sr_set_language() function (line ~235)
char *wn_name = esp_srmodel_filter(models, ESP_WN_PREFIX,
(SR_LANG_EN == g_sr_data->lang ? "hiesp" : "hilexin"));
char *wn_name = esp_srmodel_filter(models, ESP_WN_PREFIX,
(SR_LANG_EN == g_sr_data->lang ? "alexa" : "hilexin"));
char *wn_name = esp_srmodel_filter(models, ESP_WN_PREFIX, "customword");
Wake Word Detected -> AFE Processing -> MultiNet -> Command ID -> Handler Action

- ESP_MN_STATE_DETECTING – Écoute d’une commande
- ESP_MN_STATE_DETECTED – Commande reconnue
- ESP_MN_STATE_TIMEOUT – Aucune commande détectée dans le délai d’attente
- Définition de commande (app_sr.c) – définit le texte et le phonème de chaque commande
- Structure de commande (app_sr.h) – structure contenant l’ID cmd, la langue, le texte et le phonème
- Processus de reconnaissance (audio_detect_task) – AFE traite l’audio, MultiNet analyse les morceaux, renvoie l’ID de commande via la file d’attente au gestionnaire
// Command definition array (app_sr.c)
static const sr_cmd_t g_default_cmd_info[] = {
{SR_CMD_LIGHT_ON, SR_LANG_EN, 0, "turn on light", "TkN nN LiT", {NULL}},
{SR_CMD_LIGHT_OFF, SR_LANG_EN, 0, "turn off light", "TkN eF LiT", {NULL}},
};
typedef enum {
SR_CMD_LIGHT_ON,
SR_CMD_LIGHT_OFF,
SR_CMD_MY_NEW_CMD, // Add your command enum
SR_CMD_MAX,
} sr_user_cmd_t;
static const sr_cmd_t g_default_cmd_info[] = {
{SR_CMD_LIGHT_ON, SR_LANG_EN, 0, "turn on light", "TkN nN LiT", {NULL}},
{SR_CMD_LIGHT_OFF, SR_LANG_EN, 0, "turn off light", "TkN eF LiT", {NULL}},
{SR_CMD_MY_NEW_CMD, SR_LANG_EN, 2, "my new command", "mI nU kMnd", {NULL}}, // Add
};
case SR_CMD_MY_NEW_CMD: // Add your handler
ESP_LOGI(TAG, "My new command executed!");
// Your action here
break;
idf.py build flash monitor
// app_sr.h - enum
SR_CMD_FAN_ON,
SR_CMD_FAN_OFF,
SR_CMD_SET_BRIGHTNESS_HIGH,
SR_CMD_SET_BRIGHTNESS_LOW,
// app_sr.c - command definitions
{SR_CMD_FAN_ON, SR_LANG_EN, 2, "turn on fan", "TkN nN fN", {NULL}},
{SR_CMD_FAN_OFF, SR_LANG_EN, 3, "turn off fan", "TkN eF fN", {NULL}},
{SR_CMD_SET_BRIGHTNESS_HIGH, SR_LANG_EN, 4, "brightness high", "brItns hI", {NULL}},
{SR_CMD_SET_BRIGHTNESS_LOW, SR_LANG_EN, 5, "brightness low", "brItns lO", {NULL}},
sr_cmd_t new_cmd = {
.cmd = SR_CMD_MY_NEW_CMD,
.lang = SR_LANG_EN,
.id = 10,
.str = "my command",
.phoneme = "mI kMnd"
};
app_sr_add_cmd(&new_cmd);
app_sr_update_cmds(); // Update MultiNet command list
- app_sr_add_cmd() – Ajouter une nouvelle commande
- app_sr_modify_cmd() – Modifier une commande existante
- app_sr_remove_cmd() – Supprime une commande
- app_sr_remove_all_cmd() – Efface toutes les commandes
- app_sr_update_cmds() – Mettre à jour MultiNet avec la liste de commandes actuelle
- Pilote d’affichage – Contrôleur LCD ILI9341 (320×240), interface SPI, format couleur RGB565, rendu accéléré matériellement.
- Pilote tactile – Contrôleur tactile capacitif GT911 via I2C, avec support multi-touch (une seule touche utilisée dans ce projet).
- Intégration LVGL – LVGL s’exécute dans une tâche dédiée avec double buffering pour un rendu fluide. Les événements tactiles sont gérés via le pilote d’entrée LVGL.
bsp_display_cfg_t cfg = {
.lvgl_port_cfg = ESP_LVGL_PORT_INIT_CONFIG(),
.buffer_size = BSP_LCD_H_RES * CONFIG_BSP_LCD_DRAW_BUF_HEIGHT,
.double_buffer = 0,
.flags = { .buff_dma = true }
};
bsp_display_start_with_config(&cfg);
bsp_board_init();
#include "lvgl.h"
#include "bsp/esp-bsp.h"
bsp_display_lock(0); // Lock for thread safety
lv_obj_t *scr = lv_scr_act(); // Get current screen
// Create a button
lv_obj_t *btn = lv_btn_create(scr);
lv_obj_set_size(btn, 100, 50);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
// Add label
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, "Click Me");
// Add click callback
lv_obj_add_event_cb(btn, on_button_click, LV_EVENT_CLICKED, NULL);
bsp_display_unlock();
static void on_touch_event(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *obj = lv_event_get_target(e);
switch (code) {
case LV_EVENT_PRESSED:
lv_obj_set_style_bg_color(obj, lv_color_hex(0x0000FF), 0);
break;
case LV_EVENT_RELEASED:
lv_obj_set_style_bg_color(obj, lv_color_hex(0x00FF00), 0);
break;
case LV_EVENT_CLICKED:
light_ctrl_toggle(); // Perform action
break;
default: break;
}
}
- LV_EVENT_CLICKED – Toucher relâché après avoir appuyé
- LV_EVENT_PRESSED – Toucher enfoncé
- LV_EVENT_RELEASED – Toucher relâché
- LV_EVENT_LONG_PRESSED – Appui long détecté
lv_img_set_src(img_obj, "/spiffs/image.bin");
// Screen 1: Main
lv_obj_t *main_screen = lv_obj_create(NULL);
// ... add widgets ...
// Screen 2: Settings
lv_obj_t *settings_screen = lv_obj_create(NULL);
// ... add widgets ...
// Navigate
void goto_settings(lv_event_t *e) { lv_scr_load(settings_screen); }
void goto_main(lv_event_t *e) { lv_scr_load(main_screen); }
WAV File -> Memory Buffer -> I2S Write -> Codec -> Speaker
- sr_echo_init() – Charge les fichiers WAV de SPIFFS vers la mémoire
- sr_echo_play() – Lit un segment audio via I2S
- bsp_i2s_write() – Écrit les données audio sur I2S (fonction BSP)
typedef enum {
AUDIO_WAKE, // Wake word detected tone
AUDIO_OK, // Command recognised tone
AUDIO_END, // Timeout / end tone
AUDIO_MAX,
} audio_segment_t;
// Load WAV from SPIFFS -> PSRAM
static esp_err_t load_wav_to_mem(audio_segment_t seg, const char *path)
{
FILE *fp = fopen(path, "rb");
if (!fp) return ESP_ERR_NOT_FOUND;
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0, SEEK_SET);
s_audio[seg].buf = heap_caps_malloc(sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
s_audio[seg].len = (size_t)sz;
fread(s_audio[seg].buf, 1, sz, fp);
fclose(fp);
return ESP_OK;
}
typedef enum {
AUDIO_WAKE,
AUDIO_OK,
AUDIO_END,
AUDIO_CUSTOM_1, // Add your segment
AUDIO_CUSTOM_2,
AUDIO_MAX,
} audio_segment_t;
spiffs/
├── echo_en_wake.wav
├── echo_en_ok.wav
├── echo_en_end.wav
├── custom_sound_1.wav Add here
└── custom_sound_2.wav
ESP_RETURN_ON_ERROR(
load_wav_to_mem(AUDIO_CUSTOM_1, "/spiffs/custom_sound_1.wav"),
TAG, "load custom1 wav failed");
sr_echo_play(AUDIO_CUSTOM_1);
idf.py build flash
| Paramètre | Valeur |
| Exemples de tarifs | 8, 16, 22,05, 44,1, 48 kHz |
| Profondeur de bits | 16 bits (recommandé) |
| Canaux | Mono ou Stéréo |
| Format | WAV PCM non compressé |
# Convert to 16 kHz, 16-bit, mono WAV
ffmpeg -i input.mp3 -ar 16000 -acodec pcm_s16le -ac 1 output.wav
# Convert to 16 kHz, 16-bit, stereo WAV
ffmpeg -i input.mp3 -ar 16000 -acodec pcm_s16le -ac 2 output.wav
| Fonction | Description |
|
Définir la fréquence d’échantillonnage du codec, la profondeur de bits et le mode de canal |
|
Régler le niveau de volume (0-100) |
|
Couper ou réactiver le codec audio |
|
Écrire le tampon de données audio sur la sortie I2S |
|
Arrêtez le périphérique codec |
|
Reprendre le périphérique codec |
- 16 kHz, 16 bits, mono -> ~32 Ko par seconde
- 16 kHz, 16 bits, stéréo -> ~64 Ko par seconde
- 44,1 kHz, 16 bits, stéréo -> ~ 176 Ko par seconde
- Utiliser PSRAM pour les tampons audio (MALLOC_CAP_SPIRAM)
- Précharger les sons fréquemment utilisés en mémoire
- Diffusez de longs fichiers audio à partir de SPIFFS en morceaux de 4 Ko
void play_long_audio_stream(const char *wav_path)
{
FILE *fp = fopen(wav_path, "rb");
if (!fp) return;
fseek(fp, 44, SEEK_SET); // Skip WAV header
uint8_t chunk[4096];
size_t bytes_read;
while ((bytes_read = fread(chunk, 1, sizeof(chunk), fp)) > 0) {
size_t bytes_written = 0;
bsp_i2s_write((char *)chunk, bytes_read, &bytes_written, portMAX_DELAY);
}
fclose(fp);
}
1. Ouvrez le fichier : main/app/app_led.c
2. Trouvez cette ligne (autour de la ligne 15) :
#define SINGLE_LED_GPIO GPIO_NUM_40
#define SINGLE_LED_GPIO GPIO_NUM_38
idf.py build flash monitor
cd /path/to/esp32-box3-voice-led-project
. $HOME/esp/esp-idf/export.sh
idf.py menuconfig
idf.py build
idf.py flash monitor
1. Prononcez le mot de réveil : « Salut ESP » (parlez clairement, à environ 1 mètre de l’appareil).
2. Attendez le retour audio – vous entendrez un son de confirmation.
3. Prononcez la commande : « Allumez la lumière » ou « Éteignez la lumière ».
4. Observez : la LED change d’état, l’écran se met à jour et le retour audio est joué.
- Parlez plus fort et plus clairement, à 0,5-1 mètre de l’appareil.
- Réduisez le bruit de fond.
- Vérifiez le moniteur série pour détecter les erreurs d’initialisation AFE.
- Vérifier la polarité des LED
- Vérifiez la connexion GPIO 40.
- Testez avec un multimètre : le GPIO doit lire 3,3 V lorsqu’il est allumé.
- Assurez-vous que ESP-IDF v5.5.2 est correctement installé.
- Courir . $HOME/esp/esp-idf/export.sh avant la construction.
- Effectuez une reconstruction complète : idf.py fullclean && idf.py build.
L’écran tactile ne répond pas
- Vérifiez le moniteur série pour les messages d’initialisation LVGL.
Aucun retour audio
- Assurez-vous que les fichiers WAV se trouvent dans le répertoire spiffs/ avant de créer.
- Vérifiez le volume du haut-parleur (un ajustement physique peut être nécessaire).
- Vérifiez l’initialisation I2S dans les journaux série.
Retrouvez l’histoire de Raspberry Pi dans cette vidéo :

-
ESP32-S3 Carte de Développement Écran Tactile LCD 4,3 pouces, Écran RGB 800×480 65K avec étui, Prise en charge Wi-Fi 2,4 GHz et Bluetooth 5, Processeur LX7 32 bits double cœur
-
TUOPUONE ESP32-S3 Smart 86 Box Carte de développement intégrée 4" 480 × 480 pixels, interface tactile RVB 5 points
