Add VoiceNotes firmware and CDM customer service SQL project
VoiceNotes is an Arduino sketch for the Waveshare ESP32-C6-LCD-0.85 that records meetings and voice memos to microSD and turns them into notes: - Records 16 kHz mono WAV (up to 4 h) from the ES7210 dual mics, with a boosted preamp and soft-limited digital gain - Transcribes via an OpenAI-compatible speech-to-text API in ~10 min parts cut at quiet points, with retries and resume after failures - Summarizes the transcript with Claude into meeting notes (decisions, action items, topics with timestamps), streamed to and from the card - Copies the notes to Nextcloud over WebDAV - Shows recording/processing state on the 0.85" LCD and the LED ring - Works around the board's SD card failing multi-sector reads Credentials live in VoiceNotes/secrets.h, which is git-ignored; see secrets.example.h. Also adds the Microsoft CDM customer service SQL implementation (DDL, sample data, reporting queries and docs). Its README is renamed to CDM_README.md so the repository README stays at the top level. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ef38a502d8
commit
3b371cdb28
@@ -0,0 +1,149 @@
|
||||
# VoiceNotes — voice recorder + AI notes for the Waveshare ESP32-C6-LCD-0.85
|
||||
|
||||
Record meetings (up to 4 hours) or voice memos to the microSD card. Then, with one
|
||||
button press, the device transcribes the newest recording and has Claude turn it into
|
||||
meeting notes: decisions, action items and topics. The notes are saved on the card.
|
||||
|
||||
## Buttons
|
||||
|
||||
The board has **two** usable buttons (BOOT and PLUS). The PWR key is wired to the
|
||||
power-switch chip, so the firmware can't read it. "Button 3" is a long-press:
|
||||
|
||||
| Action | Press | What happens |
|
||||
|---|---|---|
|
||||
| Button 1 – record | **PLUS** click | Starts a new recording |
|
||||
| Button 2 – stop | **BOOT** click | Stops and saves the WAV file |
|
||||
| Button 3 – AI notes | **PLUS** hold 1 s | Transcribes the latest recording and writes a summary |
|
||||
|
||||
If you'd rather have a real third button, wire a push-button from a free GPIO to
|
||||
GND and set `PIN_BTN_EXTRA` in `config.h`.
|
||||
|
||||
## Files on the microSD card
|
||||
|
||||
```
|
||||
/recordings/REC_2026-10-02_14-30-05.wav 16 kHz mono 16-bit WAV
|
||||
/recordings/last.txt remembers the newest recording across reboots
|
||||
/notes/REC_2026-10-02_14-30-05_transcript.txt transcript, with [hh:mm:ss] markers every ~10 min
|
||||
/notes/REC_2026-10-02_14-30-05_transcript.progress only while a transcription is unfinished (used to resume)
|
||||
/notes/REC_2026-10-02_14-30-05_summary.md Claude's notes: summary, decisions, action items, topics
|
||||
```
|
||||
|
||||
The timestamp comes from NTP over Wi-Fi (the ESP32-C6 has no battery-backed
|
||||
clock). If Wi-Fi isn't up when you record, the file is named `REC_nodate_xxxxxxxx.wav`.
|
||||
|
||||
## Nextcloud
|
||||
|
||||
When the notes are ready, the summary and transcript are also copied to your Nextcloud over
|
||||
WebDAV, into `NEXTCLOUD_FOLDER` (the folder is created if it doesn't exist). Set it up in `secrets.h`:
|
||||
|
||||
```c
|
||||
#define NEXTCLOUD_HOST "nc.example.com"
|
||||
#define NEXTCLOUD_USER "you@example.com"
|
||||
#define NEXTCLOUD_PASSWORD "xxxxx-xxxxx-xxxxx-xxxxx-xxxxx" // an app password
|
||||
#define NEXTCLOUD_FOLDER "secondbrain"
|
||||
```
|
||||
|
||||
Use an **app password**, not your login password. Create one in Nextcloud under
|
||||
**Settings → Security → Devices & sessions → Create new app password**. It works even with
|
||||
two-factor authentication on, and you can revoke it without changing your main password.
|
||||
|
||||
If the copy fails, the notes are still on the SD card. Holding PLUS again retries only the
|
||||
Nextcloud step; it doesn't redo the transcription or the summary. To regenerate a summary, delete
|
||||
its `_summary.md` from the card first.
|
||||
|
||||
## What the screen and LED ring show
|
||||
|
||||
| State | Screen | LED ring |
|
||||
|---|---|---|
|
||||
| Starting | Mic logo, "Connecting Wi-Fi…" | Amber spinner |
|
||||
| Ready | "Ready", time of the last recording | Slow blue breathing (amber if Wi-Fi is down) |
|
||||
| Recording | Blinking ● REC, timer (h:mm:ss after an hour), scrolling level meter | Red VU meter that moves with your voice |
|
||||
| Saved | ✓, date/time, length and size | Green flash |
|
||||
| Uploading | Progress bar, "Part 3 of 12" and % | Blue ring fills up with overall progress |
|
||||
| Transcribing | Cyan spinner, part number | Cyan spinning comet |
|
||||
| Summarizing | Purple spinner, characters received | Two purple comets |
|
||||
| Saving to Nextcloud | Green spinner | Green spinning comet |
|
||||
| Done | ✓ "Notes saved", file name or "Also saved to Nextcloud" | Green pulse |
|
||||
| Error | ✗ and the reason | Red blinking |
|
||||
| No microSD | Red card icon | Slow red blink |
|
||||
|
||||
The status bar shows the clock, the SD card, and Wi-Fi bars.
|
||||
|
||||
## Setup
|
||||
|
||||
1. **Arduino IDE 2.x** with the **esp32 core 3.x** by Espressif (Boards Manager).
|
||||
2. Install these libraries from Library Manager:
|
||||
*GFX Library for Arduino* (moononournation), *FastLED*, *OneButton*, *U8g2*, *ArduinoJson* (v7).
|
||||
3. Copy `secrets.example.h` to `secrets.h` and fill in:
|
||||
- your Wi-Fi SSID and password, and your timezone,
|
||||
- a speech-to-text API key (`STT_*`). Any OpenAI-compatible
|
||||
`/audio/transcriptions` endpoint works: OpenAI (`whisper-1`, `gpt-4o-transcribe`) or Groq.
|
||||
- an Anthropic API key for the summary (`ANTHROPIC_API_KEY`).
|
||||
4. Board settings (**Tools** menu):
|
||||
- Board: **ESP32C6 Dev Module**
|
||||
- USB CDC On Boot: **Enabled** (so Serial Monitor works over USB-C)
|
||||
- Flash Size: **8MB**
|
||||
- Partition Scheme: **8M with spiffs (3MB APP/1.5MB SPIFFS)**
|
||||
5. Put a **FAT32** microSD card in the slot, then upload.
|
||||
|
||||
From the command line:
|
||||
|
||||
```bash
|
||||
arduino-cli compile --upload -p /dev/cu.usbmodem* -b esp32:esp32:esp32c6:CDCOnBoot=cdc,FlashSize=8M,PartitionScheme=default_8MB .
|
||||
```
|
||||
|
||||
Open the Serial Monitor at 115200 baud to see state changes and any API errors. You can also send:
|
||||
`p` (same as holding PLUS), `n` (print the latest transcript and summary), `d` (list recordings),
|
||||
`s` (SD read test), and `u<file name>` (choose which recording `p` and `n` use).
|
||||
|
||||
## How it works
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `VoiceNotes.ino` | Setup, buttons, state machine |
|
||||
| `audio.cpp` | ES7210 mic codec setup over I2C, I2S capture task, WAV writer |
|
||||
| `cloud.cpp` | HTTPS: streams the WAV to speech-to-text, then streams Claude's reply into `summary.md` |
|
||||
| `ui.cpp` | 128×128 screens (drawn off-screen, then pushed to the LCD) and LED ring animations |
|
||||
| `config.h` | Pin map and limits |
|
||||
|
||||
- **Audio:** the two onboard mics go through the ES7210 ADC at 16 kHz and are mixed to mono.
|
||||
A high-priority task fills a 2-second RAM buffer, and the main loop writes that buffer to the card.
|
||||
This way a slow SD write can't make the recording skip.
|
||||
- **Shared SPI bus:** the LCD and the microSD use the same SPI pins, so every access takes `spiMutex`.
|
||||
- **Long recordings:** the WAV header is updated every 10 s, so a power cut mid-meeting still leaves a
|
||||
playable file. Recording stops at 4 hours (`MAX_RECORD_SECONDS`), or earlier if the card is nearly full.
|
||||
- **Transcription in parts:** speech-to-text APIs accept at most 25 MB per file, so the recording is sent
|
||||
in ~10-minute parts (`STT_SEGMENT_SECONDS`). Each part is cut at the quietest moment in the 15 s before
|
||||
the target, so no word is split. The end of the previous part's text is sent as a hint, so names and
|
||||
spellings stay consistent. Each part is streamed straight from the card.
|
||||
- **Resume:** each part is retried up to 3 times. If it still fails (for example, Wi-Fi drops), the
|
||||
finished parts are kept, and holding PLUS again continues from where it stopped.
|
||||
- **SD reads** use 512-byte (single-sector) reads. On this board the microSD fails multi-sector reads.
|
||||
- **Summary:** `POST /v1/messages` to `claude-opus-5-5` with streaming. A 2-hour transcript (~100 KB) is too
|
||||
big for the board's RAM, so the request body is streamed from the card, and the reply is written to the
|
||||
card as it arrives.
|
||||
The request sets `fallbacks: "default"` (beta `server-side-fallback-2026-07-01`). If the model declines,
|
||||
the API retries on a fallback model instead of failing.
|
||||
- If the transcript was saved but the summary step failed, a long-press again reuses the
|
||||
saved transcript and doesn't upload the audio a second time.
|
||||
|
||||
## Limits and notes
|
||||
|
||||
- Recordings use about **115 MB per hour** (16 kHz mono WAV). A 2-hour meeting is ~230 MB.
|
||||
- Processing a 2-hour meeting takes roughly **15–25 minutes**: 12 parts, each about a minute to upload at
|
||||
~300 KB/s plus 20–40 s to transcribe, then a minute or two for Claude. The device can't record while it's
|
||||
processing.
|
||||
- For multi-hour meetings, run the device from USB power. A small battery won't last.
|
||||
- Privacy: your audio goes to the speech-to-text provider, and the transcript goes to Anthropic.
|
||||
- TLS certificates are verified with the ESP32 core's built-in CA bundle.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Fix |
|
||||
|---|---|
|
||||
| LED colours wrong (red shows green) | Set `LED_COLOR_ORDER` to `GRB` in `config.h` |
|
||||
| Recordings are quiet or noisy | Change `MIC_GAIN_STEP` in `config.h` (10 = 30 dB) |
|
||||
| "Microphone init failed" | The ES7210 didn't answer at I2C 0x40. Power-cycle the board |
|
||||
| `STT HTTP 401` / `Claude HTTP 401` | Check the API keys in `secrets.h` |
|
||||
| File names say `nodate` | Wi-Fi/NTP wasn't ready yet. Wait for the clock in the status bar |
|
||||
| Serial warns that audio was dropped | Use a faster (Class 10 / U1) microSD card |
|
||||
Reference in New Issue
Block a user