File size: 9,272 Bytes
0dc7ce9 8b80b7f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | ---
license: mit
tags:
- esp32
- audio
- speech-to-text
- real-time
- deepgram
- gemini
- fastapi
- iot
---
# ποΈ ESP32 Real-Time Audio Transcriber
A real-time speech-to-text system that captures audio from an **ESP32 + INMP441** microphone, streams it to a cloud-hosted **FastAPI** server, transcribes it via **Deepgram Nova-3**, and generates conversational AI responses using **Google Gemini 2.5 Flash** β all viewable in a sleek live web dashboard.
---
## πΈ Product Images
<p align="center">
<img src="product-media/product-image-1.jpeg" alt="Product Image 1" width="400"/>
<img src="product-media/product-image-2.jpeg" alt="Product Image 2" width="400"/>
</p>
<p align="center"><em>ESP32 + INMP441 assembly housed in a custom 3D printed case</em></p>
### π₯ Demo Video
[βΆ Watch the product demo video](product-media/product-demo.mp4)
---
## β¨ Features
- **Real-time transcription** β Live interim + final transcripts powered by Deepgram Nova-3
- **AI conversation** β Press **Q** or click *Ask Gemini* to get a conversational response from Gemini 2.5 Flash with multi-turn context
- **WiFi captive portal** β No hardcoded credentials; configure WiFi via phone/laptop on first boot
- **One-click deploy** β Server deploys to [Railway](https://railway.app) with zero configuration
- **Live status dashboard** β Monitor WebSocket, Deepgram, ESP32, and Gemini connection states in real time
- **Debug panel** β Built-in debug log overlay for troubleshooting
---
## ποΈ Architecture
```
βββββββββββββββ WSS (binary audio) ββββββββββββββββββββ WSS βββββββββββββ
β ESP32 + β βββββββββββββββββββββββββββββββΆ β FastAPI Server β βββββββββββββββΆ β Deepgram β
β INMP441 β β (Railway) β βββββββββββββββ β Nova-3 β
βββββββββββββββ β β transcripts βββββββββββββ
β β
β β REST βββββββββββββ
βββββββββββββββ WSS (text events) β β βββββββββββββββΆ β Gemini β
β Browser β βββββββββββββββββββββββββββββββ β β βββββββββββββββ β 2.5 Flashβ
β Dashboard β βββββββββββββββββββββββββββββββΆ ββββββββββββββββββββ AI response βββββββββββββ
βββββββββββββββ "PROCESS" / "CLEAR"
```
---
## π Project Structure
```
audio_transcriber/
βββ server.py # FastAPI server (transcription + LLM + web UI)
βββ esp32_firmware/
β βββ esp32_firmware.ino # Arduino sketch for ESP32 + INMP441
βββ requirements.txt # Python dependencies
βββ Procfile # Railway process definition
βββ runtime.txt # Python version for Railway
βββ .gitignore # Ignored files (venv, cache, etc.)
βββ README.md # This file
```
---
## π§ Hardware Requirements
| Component | Description |
|---|---|
| **ESP32 Dev Board** | Any ESP32-WROOM-32 based board |
| **INMP441** | I2S MEMS microphone module |
| **Jumper wires** | 5 connections (see wiring below) |
| **USB cable** | For flashing and serial monitor |
### Wiring Diagram
| ESP32 Pin | INMP441 Pin | Function |
|---|---|---|
| `GPIO 26` | `SCK` | Bit Clock |
| `GPIO 32` | `WS` | Word Select (LRCLK) |
| `GPIO 33` | `SD` | Serial Data Out |
| `3.3V` | `VDD` | Power |
| `GND` | `GND` + `L/R` | Ground (L/R β GND for left channel) |
---
## π Getting Started
### 1. Deploy the Server to Railway
1. Push this repository to GitHub
2. Go to [railway.app](https://railway.app) β **New Project** β **Deploy from GitHub Repo**
3. Railway auto-detects the `Procfile` and `runtime.txt` β no extra config needed
4. Note your deployed URL (e.g. `audio-transcriber.up.railway.app`)
### 2. Flash the ESP32 Firmware
1. Open `esp32_firmware/esp32_firmware.ino` in **Arduino IDE**
2. Install the required libraries via **Library Manager**:
- `WiFiManager` by tzapu (β₯ 2.0)
- `WebSockets` by Markus Sattler (β₯ 2.4)
3. Update `SERVER_HOST` in the sketch if your Railway URL differs:
```cpp
const char* SERVER_HOST = "your-app-name.up.railway.app";
```
4. Select board: **ESP32 Dev Module**
5. Flash and open the Serial Monitor at **115200 baud**
### 3. Connect to WiFi
1. On first boot, the ESP32 creates a WiFi AP named **`Transcriber-Setup`**
2. Connect to it from your phone or laptop
3. Select your home WiFi network and enter the password
4. The ESP32 saves the credentials and auto-connects on subsequent boots
> π‘ **Reset WiFi:** Hold the **BOOT** button (GPIO 0) while powering on to clear saved credentials.
### 4. View the Dashboard
Open your Railway URL in a browser:
```
https://audio-transcriber.up.railway.app
```
You'll see live transcription appear as the ESP32 streams audio.
---
## π§ Using Gemini AI
Once transcription lines appear:
1. Press **Q** on your keyboard or click the **⬑ Ask Gemini** button
2. Gemini reads the buffered transcript and responds in a casual, conversational tone
3. The conversation supports **multi-turn context** β keep talking and asking
4. Click **Reset Chat** to clear the conversation history
---
## βοΈ Configuration
### Server Environment
| Variable | Default | Description |
|---|---|---|
| `PORT` | `8000` | HTTP port (auto-set by Railway) |
### API Keys
API keys are currently embedded in `server.py`. For production, move them to environment variables:
```python
DEEPGRAM_API_KEY = os.environ.get("DEEPGRAM_API_KEY")
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
```
### ESP32 Firmware
| Constant | Default | Description |
|---|---|---|
| `SERVER_HOST` | `audio-transcriber.up.railway.app` | Railway domain |
| `SERVER_PORT` | `443` | HTTPS/WSS port |
| `WS_PATH` | `/ws/audio` | WebSocket endpoint |
| `SAMPLE_RATE` | `16000` | Audio sample rate (Hz) |
| `I2S_READ_LEN` | `256` | Samples per I2S read cycle |
---
## π οΈ Local Development
```bash
# Clone the repo
git clone https://github.com/<your-username>/audio_transcriber.git
cd audio_transcriber
# Create a virtual environment
python -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
# Run the server locally
python server.py
# β Server starts at http://localhost:8000
```
> Update `SERVER_HOST` in the ESP32 sketch to your local IP for local testing.
---
## π¦ Dependencies
### Python (Server)
| Package | Purpose |
|---|---|
| `fastapi` | Async web framework & WebSocket handling |
| `uvicorn` | ASGI server |
| `websockets` | Async WebSocket client for Deepgram |
| `langchain-google-genai` | LangChain integration for Gemini |
### Arduino (ESP32)
| Library | Purpose |
|---|---|
| `WiFiManager` | Captive portal for WiFi provisioning |
| `WebSockets` | WebSocket client with SSL support |
---
## π API Endpoints
| Endpoint | Type | Description |
|---|---|---|
| `GET /` | HTTP | Serves the live transcription dashboard |
| `WS /ws` | WebSocket | Browser β Server (transcripts + LLM events) |
| `WS /ws/audio` | WebSocket | ESP32 β Server (raw 16-bit PCM audio) |
### Browser WebSocket Messages
| Direction | Message | Description |
|---|---|---|
| Browser β Server | `PROCESS` | Trigger Gemini on buffered transcript |
| Browser β Server | `CLEAR_HISTORY` | Reset conversation history |
| Server β Browser | `__FINAL__:<text>` | Final transcript line |
| Server β Browser | `__INTERIM__:<text>` | Interim (partial) transcript |
| Server β Browser | `__LLM_START__` | Gemini processing started |
| Server β Browser | `__LLM_TOKEN__:<text>` | Gemini response text |
| Server β Browser | `__LLM_DONE__` | Gemini processing complete |
| Server β Browser | `__LLM_ERROR__:<msg>` | Gemini error message |
| Server β Browser | `__STATUS__:<state>` | Connection state change |
---
## π€ Contributing
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
---
## π License
This project is open source and available under the [MIT License](LICENSE).
---
<p align="center">
Built with β€οΈ using ESP32 Β· FastAPI Β· Deepgram Β· Gemini
</p>
|