309005360b
CI / platformio (pull_request) Failing after 4m52s
New IDF project targeting AI-Thinker ESP32-A1S Audio Kit (ESP32, ES8388
codec). Replaces the Arduino PLIP_FIRMWARE with a proper IDF component
structure aligned on box3_voice / scenario_mesh infrastructure.
Phases implemented and hardware-verified:
- A: ES8388 I2C init + I2S TX; 440 Hz test tone exits the speaker
- B: WiFi STA + httpd :80 (GET /status, POST /game/scenario, /game/file)
- C: ESP-NOW CMD receiver via shared scenario_mesh lib; op=play dispatched
to audio; screen/evt/led logged (no display on PLIP)
- D: Off-hook GPIO monitor (GPIO4=BOOT button stand-in); ring tone cadence;
POST /voice/hook to master on pickup/hangup (200 OK confirmed)
MAC (first observed): a0:b7:65:e7:f6:44
IP (DHCP): 192.168.0.138
Files:
plip_voice/CMakeLists.txt - project root; EXTRA_COMPONENT_DIRS=../lib/scenario_mesh
plip_voice/partitions.csv - 4MB OTA dual-bank + SPIFFS (matches PLIP_FIRMWARE)
plip_voice/sdkconfig.defaults - ESP32 target, 4MB flash DIO, ch=11 hint (no creds)
plip_voice/main/Kconfig.projbuild - PLIP_ menu (SSID/pwd/channel/master_url/hook_gpio)
plip_voice/main/board_config.h - A1S pin map (I2C 32/33, I2S 0/27/25/26/35, PA=21)
plip_voice/main/es8388.[ch] - ES8388 I2C register init sequence (legacy i2c driver)
plip_voice/main/audio.[ch] - I2S TX + async play queue + ring task
plip_voice/main/net.[ch] - WiFi STA + httpd (NVS creds with Kconfig fallback)
plip_voice/main/cmd_exec.[ch] - D5 CMD executor (play→audio; screen/evt/led→log)
plip_voice/main/hook_client.[ch] - POST /voice/hook via esp_http_client
plip_voice/main/phone.[ch] - Off-hook GPIO ISR + debounce + ring control
plip_voice/main/main.c - app_main boot sequence (all four phases)
Build: IDF 5.4.4, target esp32, clean.
Flash: /dev/cu.usbserial-0001, 4MB flash verified.
104 lines
3.0 KiB
C
104 lines
3.0 KiB
C
/*
|
|
* phone.c — Off-hook GPIO debounce + ring control (Phase D).
|
|
*
|
|
* Mirrors PLIP_FIRMWARE/src/phone_task.cpp logic, ported to IDF C.
|
|
* ISR sets a flag; the task reads the GPIO level after a 30 ms debounce
|
|
* window and reports the transition via hook_client_report().
|
|
*/
|
|
|
|
#include "phone.h"
|
|
#include "audio.h"
|
|
#include "hook_client.h"
|
|
|
|
#include "driver/gpio.h"
|
|
#include "esp_log.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
|
|
#define TAG "phone"
|
|
|
|
#define DEBOUNCE_MS 30
|
|
#define TASK_STACK 4096
|
|
#define TASK_PRIO 5
|
|
|
|
static volatile bool s_edge_pending = false;
|
|
static volatile bool s_ringing = false;
|
|
|
|
/* IRAM_ATTR: ISR must live in IRAM on original ESP32. */
|
|
static void IRAM_ATTR on_hook_isr(void *arg)
|
|
{
|
|
(void)arg;
|
|
s_edge_pending = true;
|
|
}
|
|
|
|
void phone_ring_start(void)
|
|
{
|
|
if (s_ringing) return;
|
|
s_ringing = true;
|
|
ESP_LOGI(TAG, "ring start");
|
|
audio_ring_start();
|
|
}
|
|
|
|
void phone_ring_stop(void)
|
|
{
|
|
if (!s_ringing) return;
|
|
s_ringing = false;
|
|
ESP_LOGI(TAG, "ring stop");
|
|
audio_stop();
|
|
}
|
|
|
|
static void phone_task(void *arg)
|
|
{
|
|
(void)arg;
|
|
const int hook_gpio = CONFIG_PLIP_HOOK_GPIO;
|
|
|
|
gpio_config_t io_conf = {
|
|
.pin_bit_mask = (1ULL << hook_gpio),
|
|
.mode = GPIO_MODE_INPUT,
|
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
|
.intr_type = GPIO_INTR_ANYEDGE,
|
|
};
|
|
ESP_ERROR_CHECK(gpio_config(&io_conf));
|
|
ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
|
ESP_ERROR_CHECK(gpio_isr_handler_add(hook_gpio, on_hook_isr, NULL));
|
|
|
|
/* Read and report initial level so master state machine is in sync. */
|
|
int last_level = gpio_get_level(hook_gpio);
|
|
const char *init_state = (last_level == 0) ? "off" : "on";
|
|
ESP_LOGI(TAG, "phone task ready, hook GPIO=%d level=%d (%s)",
|
|
hook_gpio, last_level, init_state);
|
|
hook_client_report(init_state, "boot");
|
|
|
|
for (;;) {
|
|
if (s_edge_pending) {
|
|
s_edge_pending = false;
|
|
vTaskDelay(pdMS_TO_TICKS(DEBOUNCE_MS));
|
|
int level = gpio_get_level(hook_gpio);
|
|
if (level != last_level) {
|
|
last_level = level;
|
|
if (level == 0) {
|
|
/* Off-hook: handset picked up. Stop ringing if active. */
|
|
ESP_LOGI(TAG, "off-hook (pickup) detected");
|
|
phone_ring_stop();
|
|
hook_client_report("off", "pickup");
|
|
} else {
|
|
/* On-hook: handset hung up. */
|
|
ESP_LOGI(TAG, "on-hook (hangup) detected");
|
|
audio_stop();
|
|
hook_client_report("on", "hangup");
|
|
}
|
|
}
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(20));
|
|
}
|
|
}
|
|
|
|
esp_err_t phone_init(void)
|
|
{
|
|
BaseType_t ok = xTaskCreatePinnedToCore(phone_task, "phone",
|
|
TASK_STACK, NULL,
|
|
TASK_PRIO, NULL, 1);
|
|
return (ok == pdPASS) ? ESP_OK : ESP_ERR_NO_MEM;
|
|
}
|