Files
Ravi ReddyandClaude Opus 5.5 3b371cdb28 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>
2026-10-03 22:22:10 +05:30

150 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |