Files
desklock/firmware/main/desklock.h
T
jpmschweitzerandClaude Fable 5 b462dff082
Test, Build and Push / test-gateway (push) Successful in 10s
Test, Build and Push / release (push) Skipped
Test, Build and Push / build-gateway (push) Skipped
Phase 5: hands-free "Computer" wake word (esp-sr WakeNet + AFE VAD)
Say "Computer" -> chime + listening -> speak -> AFE VAD detects you
stopped -> auto-sends. No taps. Touch still works as a manual override.

- esp-sr 2.4.6 added; wn9_computer_tts model packed into a new "model"
  flash partition (MODEL_IN_FLASH). App moved to 8M, model 4M.
- audio.c: replaced the on-demand capture_task with an AFE pipeline —
  feed_task is the SOLE mic reader (-> afe->feed); detect_task fetches,
  watches wakeup_state for the wake word and vad_state for end of
  speech, and forwards AFE-cleaned audio upstream during an utterance.
  One mic reader ever.
- short rising chime acknowledges the wake audibly.

Fixes from adversarial review before trusting it:
1. utterance framing (blocking WS sends) moved OFF the AFE fetch thread
   onto an app_task event queue (EV_TOUCH/EV_WAKE/EV_SPEECH_END) — a
   1.5s send could stall fetch and drop the first ~1.5s of speech.
2. app_task is now the single serializer of start/end -> no TOCTOU
   double-start (was: two utterance_start on a tap during wake).
3. VAD accounting resets on every streaming (re)start (wake OR tap),
   not just wake -> a tapped utterance can no longer end instantly on
   stale silence.
4. chime/reply set s_playing (+DMA tail hold) and detect_task skips the
   mic while s_playing -> our own audio no longer streams into STT or
   false-triggers the wake at a playback boundary (no AEC yet).
5. NULL-checked AFE create + feed buffer; tasks only start if AFE is up.

Verified on hardware: model loads, AFE inits with the Computer word,
boots and connects clean, no crash/wedge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 10:52:41 +02:00

59 lines
1.8 KiB
C

/* Shared app types and cross-module hooks. */
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
typedef enum {
FACE_BOOT, /* connecting — idle glyphs + status tag */
FACE_IDLE, /* clock, rare drips */
FACE_LISTENING, /* mic hot */
FACE_PENSIVE, /* transcribing / short waits */
FACE_EFFORT, /* Tatlock working: max rain + orbit + elapsed */
FACE_SPEAKING, /* mouth sync */
FACE_RAGE, /* request failed: table flip, auto-returns to idle */
FACE_ERROR, /* gateway unreachable */
} face_state_t;
/* face.c */
void face_init(void);
void face_set(face_state_t state); /* safe from any task */
face_state_t face_get(void);
void face_activity(void);
void face_status(const char *text); /* bottom status line (diag/boot) */ /* reset the power ladder to active */
/* audio.c */
void audio_init(void);
void audio_play_gong(void);
void audio_play_chime(void); /* short wake acknowledgement */
void audio_capture_start(void); /* begin streaming the utterance upstream */
void audio_capture_stop(void);
bool audio_capture_active(void);
void audio_playback_begin(void);
void audio_playback_feed(const uint8_t *data, size_t len);
void audio_playback_end(void);
bool audio_is_playing(void);
/* net.c */
void net_start(void);
/* gw_client.c */
void gw_start(void);
bool gw_connected(void);
void gw_send_text(const char *json);
void gw_send_bin(const uint8_t *data, size_t len);
/* wifi_diag.c */
void wifi_diag_start(void);
/* c6_ota.c */
void c6_ota_start(void);
/* desklock_main.c */
void app_on_touch(void);
void app_on_wake(void); /* wake word detected (from audio detect_task) */
void app_on_speech_end(void); /* AFE VAD detected end of utterance */
void app_on_disconnect(void);
void app_on_playback_done(void);