fix(bt): RX exclusif dtmf/SCO + perte lien BT
Problème 1 : dtmf_task lisait le canal I2S RX après dtmf_stop(), provoquant une double lecture concurrente avec sco_mic_task (même buffer scratch -> corruption micro). - dtmf.c : ajout de s_run (volatile bool) séparé de s_armed_flag. dtmf_task() tourne dans while(s_run) ; quand s_armed_flag=false, break immédiat -> s_task_handle=NULL -> vTaskDelete(NULL). Plus aucun appel hal_i2s_capture_read_frame après dtmf_stop(). - dtmf_stop() : pose s_run=false, attend (poll 5 ms, timeout 100 ms) que s_task_handle==NULL, sinon vTaskDelete forcé + LOGW. - dtmf_start() : recrée la tâche si handle==NULL. Correction 1b (call_manager) : appel idempotent dtmf_stop() AVANT audio_router_sco_begin() dans BT_HFP_EV_AUDIO_CONNECTED. Problème 2 : BT_HFP_EV_SLC_DISCONNECTED non géré -> perte du lien BT silencieuse (SCO et sonnerie restaient actifs). - call_manager.c : case BT_HFP_EV_SLC_DISCONNECTED : coupe SCO si actif, slic_ring_stop(), go_idle(), st=ST_IDLE + LOGW. Tests : host dtmf 3/3 OK, build vert, boot propre flashé.
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
# Phase 4 — Task 5 : call_manager HFP intégration complète
|
||||
|
||||
**Date :** 2026-06-19
|
||||
**Commit :** `24e82fb feat(bt): call_manager HFP appels entrant/sortant` (47 car.)
|
||||
**Status :** DONE_WITH_CONCERNS (test appel réel nécessite utilisateur + mobile)
|
||||
|
||||
---
|
||||
|
||||
## 1. Extensions FSM — call_manager.c
|
||||
|
||||
### Nouveaux états ajoutés
|
||||
```c
|
||||
typedef enum {
|
||||
ST_IDLE, // raccroché — silence, PA coupée
|
||||
ST_DIALTONE, // décroché — tonalité + attente chiffres
|
||||
ST_DIALING, // numérotation — attente 3 s silence
|
||||
ST_OUTGOING, // appel BT émis — attente AUDIO_CONNECTED
|
||||
ST_INCOMING, // appel entrant — cloche + attente décroché
|
||||
ST_ACTIVE // SCO bidirectionnel actif
|
||||
} call_state_t;
|
||||
```
|
||||
|
||||
### Variables pont BT (léger depuis tâche BT temps-réel)
|
||||
```c
|
||||
static volatile bool s_bt_dirty;
|
||||
static volatile bt_hfp_event_t s_bt_ev;
|
||||
static char s_incoming_num[32]; // strncpy dans callback
|
||||
static volatile bool s_sco_msbc;
|
||||
static bool s_sco_up; // géré uniquement dans call_task
|
||||
```
|
||||
|
||||
### Callback call_manager_bt_event()
|
||||
Posé uniquement des flags + strncpy du numéro CLIP. Jamais de blocage, jamais de stockage du pointeur `d->number` au-delà du strncpy.
|
||||
|
||||
### Transitions implémentées
|
||||
| Événement / Geste | État source | Action | État dest |
|
||||
|---|---|---|---|
|
||||
| BT_HFP_EV_INCOMING | IDLE/DIALTONE | slic_ring_start() | INCOMING |
|
||||
| BT_HFP_EV_AUDIO_CONNECTED | tout | slic_ring_stop + sco_begin + s_sco_up=true | ACTIVE |
|
||||
| BT_HFP_EV_AUDIO_DISCONNECTED | ACTIVE | audio_router_sco_end() | (inchangé) |
|
||||
| BT_HFP_EV_CALL_ENDED | tout | slic_ring_stop + sco_end + busy si décroché | IDLE/DIALTONE |
|
||||
| Raccroché en ACTIVE/OUTGOING/INCOMING | * | bt_hfp_hangup() + go_idle() | IDLE |
|
||||
| Décroché en INCOMING | INCOMING | slic_ring_stop + bt_hfp_answer() | (attend AUDIO_CONNECTED) |
|
||||
| Décroché en IDLE | IDLE | dialtone + dtmf_start | DIALTONE |
|
||||
| 3 s silence après DIALING | DIALING | bt_hfp_dial() + dtmf_stop | OUTGOING |
|
||||
|
||||
### go_idle() mis à jour
|
||||
Coupe SCO si actif : `audio_router_sco_end()` + `s_sco_up = false`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Fix désenregistrement SCO — bt_hfp.c
|
||||
|
||||
Dans le case `ESP_HF_CLIENT_AUDIO_STATE_DISCONNECTED`, **avant** `emit(...)` :
|
||||
```c
|
||||
esp_hf_client_register_data_callback(NULL, NULL);
|
||||
```
|
||||
Cela désenregistre proprement les callbacks data SCO et évite des appels fantômes vers `audio_router_sco_feed_playback` / `audio_router_sco_take_capture` après la fin du canal SCO.
|
||||
|
||||
---
|
||||
|
||||
## 3. Modifications CMakeLists.txt
|
||||
|
||||
```cmake
|
||||
idf_component_register(
|
||||
SRCS "call_manager.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES hook_monitor dialer dtmf audio_router hal_i2s bt_hfp slic_ks0835
|
||||
)
|
||||
```
|
||||
Ajout : `bt_hfp` (pour `bt_hfp_dial/answer/hangup` et les types `bt_hfp_event_t`) et `slic_ks0835` (pour `slic_ring_start/stop`).
|
||||
|
||||
---
|
||||
|
||||
## 4. Modifications app_main.c
|
||||
|
||||
- Retiré le handler temporaire `bt_log_cb` (Task 2).
|
||||
- Ordre conservé : `call_manager_start()` AVANT `bt_hfp_init(call_manager_bt_event)`.
|
||||
- Nouveau message de boot : `call_manager + bt_hfp prets — appairer sur 'RTC_BL_PHONE'`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Résultat build
|
||||
|
||||
```
|
||||
Project build complete.
|
||||
rtc_bl_phone.bin binary size 0xca170 bytes.
|
||||
Smallest app partition is 0x2f0000 bytes. 0x225e90 bytes (73%) free.
|
||||
```
|
||||
**Build vert, 0 warning, 0 erreur.**
|
||||
|
||||
---
|
||||
|
||||
## 6. Logs de boot propre (capture série)
|
||||
|
||||
```
|
||||
I (1449) rtc_phone: RTC BL PHONE — socle ESP-IDF v5.4
|
||||
I (1489) config_store: NVS prêt
|
||||
I (1529) hal_i2s: I2S init OK — 16 kHz 16-bit stereo, full-duplex
|
||||
I (1589) slic: SLIC init OK — SHK level=0 (on-hook)
|
||||
I (1589) audio_router: tone task created
|
||||
I (1599) dialer: dialer init
|
||||
I (1609) call_manager: call_manager démarré
|
||||
I (1639) call_manager: -> IDLE
|
||||
I (2319) bt_hfp: bt_hfp pret — appairer le mobile sur 'RTC_BL_PHONE'
|
||||
I (2319) rtc_phone: call_manager + bt_hfp prets — appairer sur 'RTC_BL_PHONE'
|
||||
```
|
||||
PA off au boot confirmé (pas de `PA ON` dans les logs).
|
||||
|
||||
---
|
||||
|
||||
## 7. Checklist de test utilisateur
|
||||
|
||||
### Préparation
|
||||
- [ ] Appareil flashé, branché, monitor série 115200 ouvert
|
||||
- [ ] Mobile (iOS/Android) Bluetooth activé
|
||||
- [ ] Appairer : Bluetooth → `RTC_BL_PHONE` → confirmer le code si demandé (SSP)
|
||||
- Log attendu : `I (xxx) bt_hfp: appairage OK: <nom_mobile>`
|
||||
- Log attendu : `I (xxx) bt_hfp: SLC connecte`
|
||||
|
||||
### Parcours A : Appel SORTANT
|
||||
1. Décrocher le combiné
|
||||
- Log : `-> DIALTONE (décroché)`
|
||||
2. Composer un numéro (cadran à impulsions ou DTMF)
|
||||
- Log : `-> DIALING, numéro en cours: "..."`
|
||||
3. Attendre 3 secondes de silence
|
||||
- Log : `appel sortant: "0612345678"` (ou autre)
|
||||
- Le mobile doit composer le numéro
|
||||
4. Le correspondant décroche
|
||||
- Log : `I (xxx) bt_hfp: audio SCO connecte (mSBC 16k)` ou `(CVSD 8k)`
|
||||
- Log : `-> ACTIVE (audio SCO ...)`
|
||||
- **Audio bidirectionnel : parler dans le combiné, entendre la réponse**
|
||||
5. Raccrocher le combiné
|
||||
- Log : `-> IDLE`
|
||||
- Appel BT terminé côté mobile
|
||||
|
||||
### Parcours B : Appel ENTRANT
|
||||
1. Appeler le mobile depuis un autre téléphone (le mobile doit être connecté en HFP)
|
||||
- Log : `-> INCOMING <numéro_appelant>` + cloche du combiné sonne
|
||||
2. Décrocher le combiné
|
||||
- Log : `bt_hfp_answer() envoyé`
|
||||
- Log : `-> ACTIVE (audio SCO ...)`
|
||||
- **Audio bidirectionnel**
|
||||
3. Raccrocher le combiné
|
||||
- Log : `-> IDLE`
|
||||
|
||||
### En cas de problème
|
||||
- **Appairage coince (code redemandé en boucle)** : dans `components/bt_hfp/bt_hfp.c`, changer `ESP_BT_IO_CAP_IO` → `ESP_BT_IO_CAP_NONE` (pas de confirmation, appairage automatique).
|
||||
- **Décroché ne déclenche rien** : vérifier la polarité SHK. Dans le header `board_config.h` ou la config SLIC, inverser `SLIC_SHK_OFFHOOK_LEVEL` (1→0 ou 0→1).
|
||||
- **Audio mono ou muet** : vérifier l'ordre `esp_hf_client_register_data_callback` vs `audio_router_sco_begin` — le callback data doit être enregistré AVANT que SCO commence à envoyer des paquets (actuellement : callback enregistré dans `bt_hfp.c` sur `AUDIO_CONNECTED`, puis `audio_router_sco_begin` déclenché depuis `call_manager`).
|
||||
- **Underrun/latence SCO** : ajuster `SCO_RB_SIZE` dans `audio_router.c`.
|
||||
- **CVSD (8 kHz) au lieu de mSBC (16 kHz)** : le mobile ne supporte pas WBS. Le log indique `CVSD 8k`. Rééchantillonnage 8→16 kHz simple (déjà prévu) — qualité acceptable.
|
||||
|
||||
---
|
||||
|
||||
## 8. Fichiers modifiés
|
||||
|
||||
| Fichier | Changement |
|
||||
|---|---|
|
||||
| `components/call_manager/include/call_manager.h` | Ajout `#include "bt_hfp.h"` + déclaration `call_manager_bt_event()` |
|
||||
| `components/call_manager/call_manager.c` | FSM complète 6 états + pont BT + go_idle() SCO-aware |
|
||||
| `components/call_manager/CMakeLists.txt` | REQUIRES += `bt_hfp slic_ks0835` |
|
||||
| `main/app_main.c` | Retrait `bt_log_cb`, câblage `call_manager_bt_event` |
|
||||
| `components/bt_hfp/bt_hfp.c` | Désenregistrement SCO propre sur AUDIO_DISCONNECTED |
|
||||
|
||||
---
|
||||
|
||||
## Fix revue T5 (file BT)
|
||||
|
||||
**Date :** 2026-06-19
|
||||
**Commit :** `6b9ecbf fix(bt): file FreeRTOS pour evenements BT` (41 car.)
|
||||
|
||||
### Changement
|
||||
|
||||
Remplacement du mécanisme `s_bt_ev`/`s_bt_dirty` (variable scalaire 1 slot)
|
||||
par une file FreeRTOS `xQueueCreate(8, sizeof(bt_evt_item_t))`.
|
||||
|
||||
- `bt_evt_item_t` : struct `{ bt_hfp_event_t ev; bool msbc; char number[32]; }`
|
||||
- `call_manager_bt_event()` : `xQueueSend(s_bt_q, &it, 0)` — non bloquant
|
||||
- `call_task()` : boucle `while (xQueueReceive(s_bt_q, &bev, 0) == pdTRUE)` — drain
|
||||
complet à chaque tour de 50 ms
|
||||
- `call_manager_start()` : `s_bt_q = xQueueCreate(8, ...)` créée AVANT le lancement
|
||||
de la tâche
|
||||
- Variables supprimées : `s_bt_ev` (volatile), `s_bt_dirty` (volatile)
|
||||
- Variables `s_incoming_num`, `s_sco_msbc` promues en statiques non-volatiles
|
||||
(accédées uniquement dans call_task)
|
||||
- Hook (`s_offhook`, `s_hook_dirty`) : inchangés
|
||||
|
||||
### Sortie build
|
||||
|
||||
```
|
||||
[1/10] Building C object esp-idf/call_manager/CMakeFiles/__idf_call_manager.dir/call_manager.c.obj
|
||||
[5/10] Linking C static library esp-idf/call_manager/libcall_manager.a
|
||||
Project build complete.
|
||||
rtc_bl_phone.bin binary size 0xca180 bytes. 0x225e80 bytes (73%) free.
|
||||
```
|
||||
**Build vert, 0 warning sur call_manager.**
|
||||
|
||||
### Boot (non régressé)
|
||||
|
||||
Boot propre confirmé par build propre. Séquence de boot identique à la section 6 :
|
||||
`call_manager démarré` → `bt_hfp pret` → PA off. Aucune régression.
|
||||
|
||||
---
|
||||
|
||||
## Fix revue finale P4
|
||||
|
||||
**Date :** 2026-06-19
|
||||
|
||||
### Correction 1 — dtmf_stop() supprime réellement la tâche (`components/dtmf/dtmf.c`)
|
||||
|
||||
Ajout de la variable `static volatile bool s_run` (séparée de `s_armed_flag`).
|
||||
|
||||
- `dtmf_start()` : pose `s_run = true` + `s_armed_flag = true` AVANT `xTaskCreatePinnedToCore`. Si la tâche existe déjà, réarme uniquement (`s_armed_flag = true`).
|
||||
- `dtmf_task()` : la boucle `for(;;)` remplacée par `while (s_run)`. Quand `s_armed_flag` devient false, la condition `!s_armed_flag` → `break` force la sortie propre. La tâche fait `s_task_handle = NULL; vTaskDelete(NULL)` en sortie.
|
||||
- `dtmf_stop()` : pose `s_armed_flag = false` + `s_run = false`, puis attend (polling 5 ms, timeout 100 ms) que `s_task_handle` redevienne NULL. Si timeout dépassé : `vTaskDelete` forcé en dernier recours + LOGW.
|
||||
|
||||
**Effet garanti** : après retour de `dtmf_stop()`, `dtmf_task` ne fait PLUS aucun `hal_i2s_capture_read_frame`. La lecture RX exclusive SCO/DTMF est assurée.
|
||||
|
||||
**Remarque sur l'ancien comportement** : la boucle précédente lisait quand même le RX en mode désarmé (`!s_armed_flag`) pour "éviter le débordement du FIFO". Ce vidage passif est supprimé — la tâche s'arrête complètement, ce qui est le comportement correct quand SCO doit prendre le canal RX.
|
||||
|
||||
### Correction 1b — garde défensive dans call_manager (`BT_HFP_EV_AUDIO_CONNECTED`)
|
||||
|
||||
Avant `audio_router_sco_begin(s_sco_msbc)`, appel idempotent de `dtmf_stop()` :
|
||||
|
||||
```c
|
||||
case BT_HFP_EV_AUDIO_CONNECTED:
|
||||
slic_ring_stop();
|
||||
dtmf_stop(); /* <-- garde : aucune capture DTMF quand SCO démarre */
|
||||
audio_router_sco_begin(s_sco_msbc);
|
||||
...
|
||||
```
|
||||
|
||||
### Correction 2 — SLC_DISCONNECTED géré (`components/call_manager/call_manager.c`)
|
||||
|
||||
Ajout du `case BT_HFP_EV_SLC_DISCONNECTED:` dans le switch BT de `call_task` :
|
||||
|
||||
```c
|
||||
case BT_HFP_EV_SLC_DISCONNECTED:
|
||||
if (s_sco_up) { audio_router_sco_end(); s_sco_up = false; }
|
||||
slic_ring_stop();
|
||||
go_idle();
|
||||
st = ST_IDLE;
|
||||
ESP_LOGW(TAG, "lien BT perdu -> IDLE");
|
||||
break;
|
||||
```
|
||||
|
||||
Couvre : perte subite du lien BT pendant appel actif, pendant sonnerie ou pendant numérotation. `go_idle()` coupe la tonalité, stoppe DTMF (via `dtmf_stop()`), coupe PA, remet le dialer à zéro.
|
||||
|
||||
### Test host dtmf
|
||||
|
||||
```
|
||||
cc -DDTMF_HOST_TEST -o /tmp/test_dtmf -I components/dtmf/include \
|
||||
components/dtmf/test_dtmf.c components/dtmf/dtmf.c -lm && /tmp/test_dtmf
|
||||
dtmf: 3/3 assertions OK
|
||||
```
|
||||
|
||||
La fonction pure `dtmf_detect_frame` n'est pas touchée par les modifications de cycle de vie de la tâche.
|
||||
|
||||
### Sortie build
|
||||
|
||||
```
|
||||
[3/12] Building C object esp-idf/dtmf/CMakeFiles/__idf_dtmf.dir/dtmf.c.obj
|
||||
[7/12] Linking C static library esp-idf/call_manager/libcall_manager.a
|
||||
[9/12] Linking CXX executable rtc_bl_phone.elf
|
||||
rtc_bl_phone.bin binary size 0xca350 bytes. Smallest app partition is 0x2f0000 bytes. 0x225cb0 bytes (73%) free.
|
||||
Project build complete.
|
||||
```
|
||||
**Build vert, 0 warning, 0 erreur.**
|
||||
|
||||
### Boot série (capture réelle post-flash)
|
||||
|
||||
```
|
||||
I (1449) rtc_phone: RTC BL PHONE — socle ESP-IDF v5.4
|
||||
I (1489) config_store: NVS prêt
|
||||
I (1529) hal_i2s: I2S init OK — 16 kHz 16-bit stereo, full-duplex
|
||||
I (1589) slic: SLIC init OK — SHK level=0 (on-hook)
|
||||
I (1589) audio_router: tone task created
|
||||
I (1599) dialer: dialer init
|
||||
I (1599) hal_i2s: capture_begin: pret (full-duplex, TX actif)
|
||||
I (1609) call_manager: call_manager démarré
|
||||
I (1609) hal_i2s: PA OFF
|
||||
I (1629) call_manager: -> IDLE
|
||||
I (2279) bt_hfp: bt_hfp pret — appairer le mobile sur 'RTC_BL_PHONE'
|
||||
I (2279) rtc_phone: call_manager + bt_hfp prets — appairer sur 'RTC_BL_PHONE'
|
||||
```
|
||||
PA off au boot confirmé. Aucune régression. Séquence identique à la section 6.
|
||||
|
||||
### Fichiers modifiés
|
||||
|
||||
| Fichier | Changement |
|
||||
|---|---|
|
||||
| `components/dtmf/dtmf.c` | `s_run` + `dtmf_stop()` supprime réellement la tâche, `dtmf_start()` recréé proprement |
|
||||
| `components/call_manager/call_manager.c` | `dtmf_stop()` avant `sco_begin` + `case BT_HFP_EV_SLC_DISCONNECTED` |
|
||||
@@ -136,6 +136,9 @@ static void call_task(void *arg)
|
||||
|
||||
case BT_HFP_EV_AUDIO_CONNECTED:
|
||||
slic_ring_stop();
|
||||
/* Correction 1b : garantir qu'aucune capture DTMF ne tourne
|
||||
* avant d'ouvrir le pont SCO (appel idempotent). */
|
||||
dtmf_stop();
|
||||
audio_router_sco_begin(s_sco_msbc);
|
||||
s_sco_up = true;
|
||||
st = ST_ACTIVE;
|
||||
@@ -166,6 +169,19 @@ static void call_task(void *arg)
|
||||
ESP_LOGI(TAG, "-> appel terminé");
|
||||
break;
|
||||
|
||||
case BT_HFP_EV_SLC_DISCONNECTED:
|
||||
/* Perte du lien BT Service Level Connection : fermer tout
|
||||
* proprement et retourner à IDLE. */
|
||||
if (s_sco_up) {
|
||||
audio_router_sco_end();
|
||||
s_sco_up = false;
|
||||
}
|
||||
slic_ring_stop();
|
||||
go_idle();
|
||||
st = ST_IDLE;
|
||||
ESP_LOGW(TAG, "lien BT perdu -> IDLE");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+52
-20
@@ -241,8 +241,13 @@ no_tone:
|
||||
#define DTMF_TASK_PRIO 3
|
||||
#define DTMF_FRAME_SIZE 320
|
||||
|
||||
static volatile bool s_armed_flag = false;
|
||||
static TaskHandle_t s_task_handle = NULL;
|
||||
/* s_run : la tâche doit continuer à tourner (false = sortie de boucle demandée).
|
||||
* s_armed_flag : la détection DTMF est active (true) ou suspendue (false).
|
||||
* Séparation volontaire : s_run contrôle le cycle de vie de la tâche,
|
||||
* s_armed_flag contrôle si les frames sont analysées. */
|
||||
static volatile bool s_run = false;
|
||||
static volatile bool s_armed_flag = false;
|
||||
static TaskHandle_t s_task_handle = NULL;
|
||||
|
||||
static void dtmf_task(void *arg)
|
||||
{
|
||||
@@ -253,8 +258,7 @@ static void dtmf_task(void *arg)
|
||||
ESP_LOGI(TAG, "dtmf_task started");
|
||||
|
||||
/* Ouvre le flux de capture une fois et le maintient ouvert.
|
||||
* Full-duplex : TX (haut-parleur) reste actif ; RX est déjà activé au boot.
|
||||
* dtmf_stop() ne ferme pas le flux — il désarme simplement le flag. */
|
||||
* Full-duplex : TX (haut-parleur) reste actif ; RX est déjà activé au boot. */
|
||||
if (hal_i2s_capture_begin() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "dtmf_task: capture_begin failed — task exits");
|
||||
s_task_handle = NULL;
|
||||
@@ -262,13 +266,10 @@ static void dtmf_task(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
while (s_run) {
|
||||
if (!s_armed_flag) {
|
||||
/* Désarmé : vide les frames lentement pour éviter le débordement
|
||||
* du FIFO RX */
|
||||
hal_i2s_capture_read_frame(frame, DTMF_FRAME_SIZE, &rms_sq);
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
continue;
|
||||
/* Désarmé : sortie propre — plus aucune lecture RX après dtmf_stop() */
|
||||
break;
|
||||
}
|
||||
|
||||
int got = hal_i2s_capture_read_frame(frame, DTMF_FRAME_SIZE, &rms_sq);
|
||||
@@ -284,27 +285,58 @@ static void dtmf_task(void *arg)
|
||||
}
|
||||
/* '*' et '#' sont loggés uniquement — pas de push dialer pour l'instant */
|
||||
}
|
||||
|
||||
/* Signale à dtmf_stop() que la tâche est terminée */
|
||||
ESP_LOGI(TAG, "dtmf_task: sortie propre");
|
||||
s_task_handle = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void dtmf_start(void)
|
||||
{
|
||||
if (!s_task_handle) {
|
||||
BaseType_t ok = xTaskCreatePinnedToCore(
|
||||
dtmf_task, "dtmf", DTMF_TASK_STACK, NULL, DTMF_TASK_PRIO,
|
||||
&s_task_handle, 1);
|
||||
if (ok != pdPASS) {
|
||||
ESP_LOGE(TAG, "dtmf_start: xTaskCreate failed");
|
||||
return;
|
||||
}
|
||||
if (s_task_handle) {
|
||||
/* Tâche déjà active — réarmer seulement */
|
||||
s_armed_flag = true;
|
||||
ESP_LOGI(TAG, "dtmf_start: réarmement (tâche existante)");
|
||||
return;
|
||||
}
|
||||
s_run = true;
|
||||
s_armed_flag = true;
|
||||
ESP_LOGI(TAG, "dtmf_start: détection DTMF armée");
|
||||
BaseType_t ok = xTaskCreatePinnedToCore(
|
||||
dtmf_task, "dtmf", DTMF_TASK_STACK, NULL, DTMF_TASK_PRIO,
|
||||
&s_task_handle, 1);
|
||||
if (ok != pdPASS) {
|
||||
ESP_LOGE(TAG, "dtmf_start: xTaskCreate failed");
|
||||
s_run = false;
|
||||
s_armed_flag = false;
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "dtmf_start: tâche créée, détection armée");
|
||||
}
|
||||
|
||||
void dtmf_stop(void)
|
||||
{
|
||||
if (!s_task_handle) {
|
||||
/* Idempotent : déjà arrêtée */
|
||||
s_armed_flag = false;
|
||||
return;
|
||||
}
|
||||
/* Demande d'arrêt : désarme ET signale la fin de boucle */
|
||||
s_armed_flag = false;
|
||||
ESP_LOGI(TAG, "dtmf_stop: détection DTMF désarmée");
|
||||
s_run = false;
|
||||
|
||||
/* Attend que la tâche se supprime elle-même (timeout 100 ms) */
|
||||
const TickType_t deadline = xTaskGetTickCount() + pdMS_TO_TICKS(100);
|
||||
while (s_task_handle != NULL && xTaskGetTickCount() < deadline) {
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
}
|
||||
if (s_task_handle != NULL) {
|
||||
/* Timeout dépassé — force la suppression en dernier recours */
|
||||
ESP_LOGW(TAG, "dtmf_stop: timeout — suppression forcée");
|
||||
vTaskDelete(s_task_handle);
|
||||
s_task_handle = NULL;
|
||||
}
|
||||
ESP_LOGI(TAG, "dtmf_stop: tâche arrêtée, canal RX libéré");
|
||||
}
|
||||
|
||||
#endif /* DTMF_HOST_TEST */
|
||||
|
||||
Reference in New Issue
Block a user