Files
ESP32_ZACUS/plip_voice/main/phone.c
T
clemsail 309005360b
CI / platformio (pull_request) Failing after 4m52s
feat(plip_voice): ESP-IDF port of PLIP retro telephone annex
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.
2026-06-14 13:29:33 +02:00

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;
}