// State shared between the main loop, the audio task and the network task. #pragma once #include #include #include enum AppState : uint8_t { ST_BOOT, ST_NO_SD, ST_IDLE, ST_RECORDING, ST_SAVED, ST_UPLOADING, // sending the WAV to the speech-to-text service ST_TRANSCRIBING, // waiting for / receiving the transcript ST_SUMMARIZING, // Claude is writing the notes ST_SYNCING, // copying the notes to Nextcloud ST_DONE, ST_ERROR, }; struct AppStatus { volatile AppState state = ST_BOOT; volatile uint32_t stateSince = 0; // millis() when the state was entered volatile uint8_t progress = 0; // 0-100 for ST_UPLOADING volatile uint32_t received = 0; // bytes of transcript / summary received volatile float micLevel = 0; // 0..1, updated by the audio task volatile uint32_t recordedBytes = 0; // PCM bytes in the current recording volatile uint32_t droppedBytes = 0; // audio lost because the SD card stalled volatile uint32_t maxSeconds = 0; // recording limit, from config and free space volatile uint8_t partIndex = 0; // transcription part being processed (1-based) volatile uint8_t partCount = 0; // estimated number of parts char lastRecording[64] = ""; // full path of the newest .wav char detail[96] = ""; // file name / error text shown on screen }; extern AppStatus app; // The LCD and the microSD share one SPI bus; anything touching either must hold this. extern SemaphoreHandle_t spiMutex; struct SpiLock { SpiLock() { xSemaphoreTake(spiMutex, portMAX_DELAY); } ~SpiLock() { xSemaphoreGive(spiMutex); } }; void setState(AppState s, const char *detail = nullptr); bool timeIsValid();