diff --git a/README.md b/README.md new file mode 100644 index 0000000..1f956cb --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# STM32 Audio Analyzer + +STM32 Audio Analyzer — система реального времени для измерения уровня звука и доминантной частоты на базе STM32F103C8T6 (Blue Pill) с веб‑дашбордом и хранением истории в TimescaleDB. + +Микроконтроллер оцифровывает сигнал с MAX4466, считает RMS (dBFS) и FFT (512 точек), отсылает метрики по USB CDC; на ПК коллектор пишет данные в PostgreSQL/TimescaleDB, FastAPI отдаёт REST/WS, React‑фронтенд показывает текущие значения и историю. + +## Архитектура + +- **MCU (firmware)** + - STM32F103C8T6, FreeRTOS, TinyUSB, CMSIS‑DSP. + - ADC1 + DMA (circular, double‑buffer 2×512) с триггером от TIM3, частота дискретизации 22.05 кГц. + - Обработка: удаление DC, RMS в dBFS, Hann‑окно, RFFT 512, поиск пика 100–8000 Гц. + - Передача каждые 100 мс пакетом 12 байт по USB CDC. + +- **Collector (Python)** + - Читает бинарный протокол с /dev/ttyACM0, ресинхронизация по SOF=0xAA. + - Проверка CRC‑8/ATM, диапазонов, подсчёт статистики ошибок. + - Запись в TimescaleDB (`audio_data`), параллельно пушит JSON по WebSocket (`ws://…/ws/live`). + +- **API (FastAPI)** + - REST `/api/v1/audio/latest|range|export/csv`, `/api/v1/stats/summary`, `/api/v1/events/loud`. + - База: `audio_data` + непрерывный агрегат `audio_data_1min` (avg/max/min/доминирующая частота, доля тишины). + +- **Frontend (React + Vite)** + - Live‑дашборд: вертикальный аудио‑метр, peak‑hold за 3 секунды, история частоты, текущая нота. + - Источник данных — WebSocket `VITE_WS_URL`, REST для исторических запросов при необходимости. + +## Структура репозитория + +```text +. +├── README.md +├── docker-compose.yml +├── db/ +│ └── init.sql # схема TimescaleDB + агрегаты/retention +├── firmware/ # прошивка STM32 (FreeRTOS + TinyUSB + CMSIS-DSP) +└── services/ + ├── collector/ # serial→WS→DB + ├── api/ # FastAPI REST + DB + └── frontend/ # React/Vite дашборд +``` + +## Быстрый старт + +``` +git clone --recurse-submodules +``` +```bash +cp .example.env .env +docker compose up --build +``` + +- БД: TimescaleDB на `localhost:5432` (`DB_NAME=audio_analyzer` по умолчанию). +- API: `http://localhost:8000/api/v1`. +- WebSocket коллектора: `ws://localhost:8001/ws/live`. +- Frontend: `http://localhost:3000` (dev‑режим Vite). diff --git a/firmware/README.md b/firmware/README.md new file mode 100644 index 0000000..2c38964 --- /dev/null +++ b/firmware/README.md @@ -0,0 +1,74 @@ +# firmware/ + +## Цель + +Прошивка для Blue Pill, которая снимает аудио с MAX4466, считает уровень в dBFS и доминантную частоту, мигает LED с частотой звука и публикует метрики по USB CDC в формате фиксированного бинарного пакета. + +## Железо + +- STM32F103C8T6 (Blue Pill, 72 МГц, 64К Flash, 20К RAM). +- Микрофон: MAX4466 (выход → **PA1 / ADC1_IN1**, питание 3.3 В). +- USB FS через TinyUSB CDC (Virtual COM). +- Светодиод: PC13 (on = 0, off = 1). + +## Обработка аудио (DSP) + +- Частота дискретизации: 22050 Гц (`AUDIO_SAMPLE_RATE`). +- DMA‑буфер: 2×512 выборок `uint16_t` (двойная буферизация через half/full interrupt). +- RMS считается по формуле \(\mathrm{RMS} = \sqrt{\frac{1}{N}\sum\_{i=1}^{N} x_i^2}\) после удаления среднего и нормализации к диапазону около \([-1, 1]\). +- Перевод в dBFS: \(\mathrm{dBFS} = 20 \log\_{10} (\mathrm{RMS} + \varepsilon)\), где \(\varepsilon\) — малый стабилизатор для нуля. +- FFT: `arm_rfft_fast_f32` на 512 точках, Hann‑окно рассчитывается “на лету” через `arm_cos_f32`. +- Поиск пика по модулю `mag[k]` в диапазоне частот 100–8000 Гц, шаг частоты \( \Delta f = \frac{22050}{512} \approx 43 \text{ Гц} \). + +## Протокол + +Пакет фиксированного размера 12 байт: + +```c +// firmware/App/Inc/protocol.h + protocol.c +[SOF=0xAA][TYPE=0x02][LEN=0x08] +[timestamp_ms: uint32] +[rms_db_x10: int16] // dBFS * 10, ожидается -50.0 .. 0.0 +[frequency_hz: uint16] // 100 .. 8000 +[crc8: uint8] // CRC-8/ATM +``` + +- CRC: CRC‑8/ATM (poly=0x07, init=0x00, без отражения), считается по байтам с индексами 1..10. + +## FreeRTOS задачи + +- `audio_process_task` + - Ждёт уведомление от ISR DMA, обрабатывает последний буфер (512 сэмплов). + - Вычисляет метрики, обновляет режим LED, отправляет метрику в очередь для USB‑задачи. + +- `cdc_task` + - Читает из очереди, упаковывает в `protocol_pack_v1`, пишет в USB CDC, целевая частота ~10 Гц. + +- `health_led_task` + - Настраивает PC13 и мигает в зависимости от частоты/уровня сигнала, плюс подкармливает watchdog. + +## Сборка и прошивка + +- Зависимости: `arm-none-eabi-gcc`, `make`, `st-flash`. +- Сборка: + +```bash +cd firmware +make +``` + +- Прошивка (через ST‑Link): + +```bash +make flash +``` + +## Зависимости + +| Библиотека | Путь | Репозиторий | +| ------------------- | -------------------------------------------- | ------------------------------------------------------------------- | +| **CMSIS-Core** | `firmware/Drivers/CMSIS/Core` | [ST GitHub](https://github.com/STMicroelectronics/cmsis_core) +| **CMSIS-Device F1** | `firmware/Drivers/CMSIS/Device/ST/STM32F1xx` | [ST GitHub](https://github.com/STMicroelectronics/cmsis_device_f1) | +| **FreeRTOS** | `firmware/Middlewares/FreeRTOS` | [FreeRTOS-Kernel](https://github.com/FreeRTOS/FreeRTOS-Kernel) | +| **TinyUSB** | `firmware/Middlewares/TinyUSB` | [hathach/tinyusb](https://github.com/hathach/tinyusb) | +| **CMSIS-DSP** | `firmware/Middlewares/CMSIS-DSP` | [ARM-software/CMSIS-DSP](https://github.com/ARM-software/CMSIS-DSP) | diff --git a/services/api/README.md b/services/api/README.md new file mode 100644 index 0000000..3a55207 --- /dev/null +++ b/services/api/README.md @@ -0,0 +1,40 @@ +# services/api + +## Назначение + +FastAPI‑сервис поверх TimescaleDB, который предоставляет REST‑доступ к аудио‑метрикам и агрегированным статистикам. + +## Основные endpoint’ы + +- `GET /api/v1/audio/latest?limit=100` + - Возвращает последние N точек `audio_data`. + +- `GET /api/v1/audio/range?from=&to=` + - Данные за заданный интервал, проверка `from < to`. + +- `GET /api/v1/stats/summary?period=1h` + - Агрегированная статистика (avg/max/min по dB, доминантная частота, процент тишины) по окну (`10s|1m|1h|6h|24h|7d|30d`). + +- `GET /api/v1/events/loud?threshold=-35&from=&to=` + - “Громкие” события, где `rms_db` выше порога, опционально с интервалом времени. + +- `GET /api/v1/export/csv?from=&to=` + - Экспорт диапазона в CSV: `time,rms_db,frequency_hz,is_silence`. + +- `GET /api/v1/health/live` / `ready` + - Примитивный healthcheck и проверка доступности БД. + +## Модель данных + +Таблица `audio_data` в TimescaleDB: + +```sql +CREATE TABLE audio_data ( + time TIMESTAMPTZ NOT NULL, + rms_db REAL CHECK (rms_db >= -50.0 AND rms_db <= 0.0), + frequency_hz INTEGER CHECK (frequency_hz >= 100 AND frequency_hz <= 8000), + is_silence BOOLEAN NOT NULL DEFAULT FALSE +); +``` + +Плюс continuous aggregate `audio_data_1min` и политики retention/refresh. diff --git a/services/collector/README.md b/services/collector/README.md new file mode 100644 index 0000000..3c7449f --- /dev/null +++ b/services/collector/README.md @@ -0,0 +1,34 @@ +# services/collector + +## Назначение + +Сервис, который читает бинарные пакеты с STM32 через USB CDC, валидирует и логирует их, пишет данные в TimescaleDB и рассылает в реальном времени по WebSocket. + +## Основные компоненты + +- `serial_reader.py` — асинхронное чтение порта `SERIAL_PORT` (`/dev/ttyACM0` по умолчанию), non‑blocking read, reconnect‑логика. +- `protocol_parser.py` — парсер протокола: поиск SOF, проверка длины, CRC‑8/ATM, конвертация `rms_db_x10` → float. +- `audio_validator.py` — проверка диапазонов: `rms_db ∈ [-50.0, 0.0]`, `frequency_hz ∈ [100, 8000]`, детекция и пометка тишины. +- `db_writer.py` — batch‑запись в PostgreSQL/TimescaleDB (`audio_data`), батч по количеству/таймеру. +- `ws_app.py` + `ws_manager.py` — WebSocket сервер, который бродкастит последние метрики всем подписчикам. +- `monitor.py` — вывод статистики: пакетов/с, CRC‑ошибки, length‑ошибки, range‑ошибки. + +## Формат данных в WebSocket + +```json +{ + "time": "2025-12-25T19:00:00Z", + "rms_db": -18.5, + "freq_hz": 440 +} +``` + +Время берётся по хосту при приёме пакета, а не из MCU‑таймстампа, чтобы быть в одной временной зоне с БД. + +## Конфигурация + +Через переменные окружения (см. `.example.env` и `docker-compose.yml`): + +- `SERIAL_PORT`, `BAUDRATE`. +- `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`. +- `WS_HOST`, `WS_PORT` (по умолчанию `0.0.0.0:8000`, наружу проброшен как `8001`).