feat(plip): incoming call + fast half-duplex
- /debug/ring?number=NN arms incoming call, starts ring - conversation_arm_incoming() + enter_incoming_greet() - offhook with armed call skips dial/ringback, goes to GREET - slic_is_offhook() used during ring (GPIO debounce unreliable) - CAPTURE_SILENCE_MS 800->600 ms, settles 250->120, 200->100 ms
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
#include "tones.h"
|
||||
#include "audio.h"
|
||||
#include "phone.h"
|
||||
#include "slic.h"
|
||||
#include "turn_client.h"
|
||||
#if CONFIG_PLIP_DIAL_DTMF
|
||||
#include "dtmf.h"
|
||||
@@ -46,7 +47,7 @@
|
||||
#define CAPTURE_MAX_IRAM (128 * 1024)
|
||||
#define CAPTURE_MAX_MS_PSRAM 8000
|
||||
#define CAPTURE_MAX_MS_IRAM 4000
|
||||
#define CAPTURE_SILENCE_MS 800
|
||||
#define CAPTURE_SILENCE_MS 600 /* end-of-speech VAD: shorter = snappier reply (was 800) */
|
||||
#define REPLY_POLL_MS 200 /* interval for checking hook during playback */
|
||||
#define REPLY_PLAYBACK_EXTRA_MS 500 /* safety margin added to computed WAV duration */
|
||||
#define BETWEEN_TURNS_MS 300 /* short pause between capture rounds */
|
||||
@@ -82,6 +83,13 @@ static char s_sid[32] = {0};
|
||||
* would 404 at the gateway. Capture the clean routed number here once. */
|
||||
static char s_number[16] = {0};
|
||||
|
||||
/* Incoming-call mode: an NPC "calls" the phone. conversation_arm_incoming()
|
||||
* stores the caller's persona number and rings; when the handset is then picked
|
||||
* up from IDLE, we skip the outgoing dialtone/dial/ringback and go straight to
|
||||
* GREET with this number (the NPC speaks first). */
|
||||
static char s_incoming_number[16] = {0};
|
||||
static volatile bool s_incoming_armed = false;
|
||||
|
||||
/* Known numbers: ringback when dialed */
|
||||
static const char *KNOWN[] = {
|
||||
"12", "3615", "15", "17", "18", "0142738200", NULL
|
||||
@@ -108,6 +116,24 @@ static void go_idle(void)
|
||||
ESP_LOGI(TAG, "-> IDLE");
|
||||
}
|
||||
|
||||
/* Begin an answered INCOMING call: stop ringing, lock the persona number, and
|
||||
* jump straight to GREET (the NPC speaks first). Drives the conversation's own
|
||||
* s_offhook directly because phone.c's debounced GPIO detection is unreliable
|
||||
* during ringing — we trust the SLIC's raw off-hook reading instead. */
|
||||
static void enter_incoming_greet(void)
|
||||
{
|
||||
s_incoming_armed = false;
|
||||
phone_ring_stop();
|
||||
tones_stop();
|
||||
dialer_reset();
|
||||
s_offhook = true;
|
||||
snprintf(s_number, sizeof(s_number), "%s", s_incoming_number);
|
||||
snprintf(s_sid, sizeof(s_sid), "%lld", (long long)esp_timer_get_time());
|
||||
audio_pa_set(true);
|
||||
s_state = STATE_GREET;
|
||||
ESP_LOGI(TAG, "INCOMING pickup -> GREET num=%s sid=%s", s_number, s_sid);
|
||||
}
|
||||
|
||||
static void conv_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
@@ -127,15 +153,21 @@ static void conv_task(void *arg)
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
/* Off-hook: start dial tone if we were idle */
|
||||
/* Off-hook from idle */
|
||||
if (s_state == STATE_IDLE) {
|
||||
dialer_reset();
|
||||
tones_dialtone_start();
|
||||
if (s_incoming_armed) {
|
||||
/* INCOMING call answered (phone.c detected the edge). */
|
||||
enter_incoming_greet();
|
||||
} else {
|
||||
/* Outgoing call: dial tone, wait for digits. */
|
||||
dialer_reset();
|
||||
tones_dialtone_start();
|
||||
#if CONFIG_PLIP_DIAL_DTMF
|
||||
dtmf_start();
|
||||
dtmf_start();
|
||||
#endif
|
||||
s_state = STATE_DIALTONE;
|
||||
ESP_LOGI(TAG, "off-hook -> DIALTONE");
|
||||
s_state = STATE_DIALTONE;
|
||||
ESP_LOGI(TAG, "off-hook -> DIALTONE");
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -144,7 +176,13 @@ static void conv_task(void *arg)
|
||||
/* State machine polling */
|
||||
switch (s_state) {
|
||||
case STATE_IDLE:
|
||||
break; /* nothing to do */
|
||||
/* Incoming-call pickup: phone.c's debounced edge detection is
|
||||
* unreliable while the bell rings, so poll the SLIC's raw off-hook
|
||||
* line directly — it reads the pickup cleanly mid-ring. */
|
||||
if (s_incoming_armed && slic_is_offhook()) {
|
||||
enter_incoming_greet();
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_DIALTONE:
|
||||
if (!s_offhook) {
|
||||
@@ -268,12 +306,12 @@ static void conv_task(void *arg)
|
||||
* the mic. Never capture while anything is playing, or the
|
||||
* playback feeds back and the line saturates. Wait for the
|
||||
* greeting/filler/reply to finish, then let the line settle. */
|
||||
vTaskDelay(pdMS_TO_TICKS(250)); /* let a just-queued clip start */
|
||||
vTaskDelay(pdMS_TO_TICKS(120)); /* let a just-queued clip start (was 250) */
|
||||
while (s_offhook && audio_is_playing()) {
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
if (!s_offhook) break;
|
||||
vTaskDelay(pdMS_TO_TICKS(200)); /* line settle after playback */
|
||||
vTaskDelay(pdMS_TO_TICKS(100)); /* line settle after playback (was 200) */
|
||||
|
||||
/* Capture player utterance — nothing is playing now. */
|
||||
int n = audio_capture_wav(cap_buf, cap_max,
|
||||
@@ -355,3 +393,13 @@ void conversation_on_hook_change(bool offhook)
|
||||
s_offhook = offhook;
|
||||
s_hook_changed = true;
|
||||
}
|
||||
|
||||
void conversation_arm_incoming(const char *number)
|
||||
{
|
||||
snprintf(s_incoming_number, sizeof(s_incoming_number), "%s",
|
||||
(number && number[0]) ? number : "17");
|
||||
s_incoming_armed = true;
|
||||
phone_ring_start();
|
||||
ESP_LOGI(TAG, "incoming call armed (num=%s) — ringing until pickup",
|
||||
s_incoming_number);
|
||||
}
|
||||
|
||||
@@ -3,3 +3,8 @@
|
||||
|
||||
void conversation_init(void);
|
||||
void conversation_on_hook_change(bool offhook);
|
||||
|
||||
/* Arm an INCOMING call from NPC persona `number` and start ringing. When the
|
||||
* handset is picked up from idle, the conversation jumps straight to the
|
||||
* greeting (persona speaks first) instead of the outgoing dial-tone path. */
|
||||
void conversation_arm_incoming(const char *number);
|
||||
|
||||
+12
-3
@@ -26,6 +26,7 @@
|
||||
#include "audio.h"
|
||||
#include "cmd_exec.h"
|
||||
#include "phone.h"
|
||||
#include "conversation.h"
|
||||
#include "es8388.h"
|
||||
#include "slic.h"
|
||||
#include "board_config.h"
|
||||
@@ -471,9 +472,17 @@ static esp_err_t handle_cmd_post(httpd_req_t *req)
|
||||
|
||||
static esp_err_t handle_debug_ring(httpd_req_t *req)
|
||||
{
|
||||
phone_ring_start();
|
||||
ESP_LOGI(TAG, "debug/ring: ring started");
|
||||
return send_json(req, "200 OK", "{\"ok\":true,\"ringing\":true}");
|
||||
/* /debug/ring[?number=NN] — arm an INCOMING call from persona NN (default
|
||||
* 17) and ring. On pickup the conversation jumps straight to the greeting. */
|
||||
char query[48] = {0}, number[16] = {0};
|
||||
httpd_req_get_url_query_str(req, query, sizeof(query));
|
||||
httpd_query_key_value(query, "number", number, sizeof(number));
|
||||
conversation_arm_incoming(number); /* defaults to "17" if empty; starts ringing */
|
||||
char resp[64];
|
||||
snprintf(resp, sizeof(resp), "{\"ok\":true,\"ringing\":true,\"incoming\":\"%s\"}",
|
||||
number[0] ? number : "17");
|
||||
ESP_LOGI(TAG, "debug/ring: incoming armed (num=%s), ringing", number[0] ? number : "17");
|
||||
return send_json(req, "200 OK", resp);
|
||||
}
|
||||
|
||||
/* ── GET /debug/ringstop (stop ring tone over HTTP) ─────────────────────── */
|
||||
|
||||
Reference in New Issue
Block a user