A local, self-hosted J.A.R.V.I.S.-style AI assistant: a Flask web app that answers questions through a local LLM grounded with live web search, speaks its replies with a local text-to-speech engine, and drives a physical Arduino rig (LEDs + servo) in real time to visualize what it's doing — researching, debating, or done.
nvidia-smi), so the same install scales from a small laptop GPU to
a high-end card.Browser (chat UI)
│ POST /chat
▼
Flask app (app.py)
│
├─► DDGS web search ─┐
├─► CrewAI crew (opt.)│──► context ──► Ollama (local LLM) ──► reply
│ │
├─► Flask-SocketIO ───┴──► pushes live state updates to the browser
│
└─► Arduino (serial) ──► LEDs + servo react to the current state
│
▼
POST /tts ──► OmniVoice Studio ──► spoken audio streamed back to the browser
Every chat request walks through four visible states — idle → researching → debating → complete — each broadcast to the browser via WebSocket and mirrored on the Arduino via a one-character serial signal, so the physical hardware and the on-screen orb always agree.
| Signal | State | Hardware behavior |
|---|---|---|
0 | Idle | LEDs off, servo parked at 90° |
B | Researching | Blue LED (pin 12) pulses, servo sweeps |
Y | Debating | Yellow LED (pin 13) pulses faster, servo sweeps |
W | Complete | LEDs off, servo parks |
OLLAMA_URL)jarvis_haedware/jarvis_hardware.ino (optional — the app
runs fine without one, just without the physical light show)Install dependencies
pip install flask flask-socketio python-dotenv requests pyserial ddgs
Configure environment variables
Create a .env file in the project root:
# Flask
SECRET_KEY=<random_key>
FLASK_DEBUG=1 # 1 to enable debug/auto-reload, 0 for production
# Arduino serial (optional — auto-detected if omitted)
ARDUINO_PORT=COM3
# CrewAI (optional — falls back to direct web search if unset)
CREWAI_CREW_URL=
CREWAI_CREW_TOKEN=
CREWAI_INPUT_KEY=query
# Ollama (local LLM)
OLLAMA_URL=http://localhost:11434/api/chat
# OmniVoice Studio (local TTS)
OMNIVOICE_URL=http://localhost:3900/v1
OMNIVOICE_VOICE=alloy
OMNIVOICE_APP_PATH=C:\Program Files\OmniVoice Studio\omnivoice-studio.exe
(Optional) Flash the Arduino
Upload jarvis_haedware/jarvis_hardware.ino to your
board using the Arduino IDE or CLI. Wiring: blue LED on pin 12, yellow LED on pin 13, servo
signal on pin 8.
Run the app
python app.py
Then open http://localhost:5000 in your browser.
To test LED/servo wiring without booting the full Flask app:
python test_connection.py
This connects directly over serial and cycles through every state (4 seconds each) so you can confirm the board reacts correctly.
| Variable | Purpose | Default |
|---|---|---|
SECRET_KEY | Flask session secret | random, generated at startup |
FLASK_DEBUG | Enable Flask debug/reload mode | 1 |
ARDUINO_PORT | Force a specific serial port | auto-detected |
CREWAI_CREW_URL | Base URL of a deployed CrewAI crew | unset (uses direct search) |
CREWAI_CREW_TOKEN | Bearer token for the CrewAI API | unset |
CREWAI_INPUT_KEY | Input key your crew's tasks expect | query |
OLLAMA_URL | Ollama chat endpoint | http://localhost:11434/api/chat |
OMNIVOICE_URL | OmniVoice Studio API base URL | http://localhost:3900/v1 |
OMNIVOICE_VOICE | Voice name to use for synthesis | alloy |
OMNIVOICE_APP_PATH | Path to the OmniVoice executable, for auto-launch | Windows default install path |
CACHE_TTL | Response/audio cache lifetime, in seconds | 3600 |
OVERRIDE_MODEL | Force a specific Ollama model regardless of detected VRAM | unset |
| Issue | Likely cause | Fix |
|---|---|---|
| "No Arduino found" on startup | Cable disconnected or board in bootloader | Check the physical connection; restart the board |
| Hardware stops responding mid-session | Servo brownout on the board's own 5V rail | Give the servo a separate 5V supply |
| OmniVoice errors in the logs | TTS service not running yet | The app auto-launches it; check OMNIVOICE_APP_PATH |
| Requests always rate-limited | Rapid repeated requests from the same client | Rate limit is 10 requests/60s per IP by default |
| Stale-looking replies | Cache TTL too long | Lower CACHE_TTL or restart the app |
app.py Flask app, LLM/search/TTS orchestration, hardware control
test_connection.py Standalone Arduino connectivity check
jarvis_haedware/jarvis_hardware.ino Arduino firmware (LED + servo state machine)
templates/index.html Chat UI
static/script.js Client-side chat, TTS playback, WebSocket handling
static/style.css Styling
.env Local configuration (not committed)
See AGENTS.md for a deeper architectural walkthrough intended for AI coding agents working in this repo.
Python
53.0%
CSS
16.6%
JavaScript
16.4%
HTML
7.6%
C++
6.5%
A local, self-hosted J.A.R.V.I.S.-style AI assistant: a Flask web app that answers questions through a local LLM grounded with live web search, speaks its replies with a local text-to-speech engine, and drives a physical Arduino rig (LEDs + servo) in real time to visualize what it's doing — researching, debating, or done.
nvidia-smi), so the same install scales from a small laptop GPU to
a high-end card.Browser (chat UI)
│ POST /chat
▼
Flask app (app.py)
│
├─► DDGS web search ─┐
├─► CrewAI crew (opt.)│──► context ──► Ollama (local LLM) ──► reply
│ │
├─► Flask-SocketIO ───┴──► pushes live state updates to the browser
│
└─► Arduino (serial) ──► LEDs + servo react to the current state
│
▼
POST /tts ──► OmniVoice Studio ──► spoken audio streamed back to the browser
Every chat request walks through four visible states — idle → researching → debating → complete — each broadcast to the browser via WebSocket and mirrored on the Arduino via a one-character serial signal, so the physical hardware and the on-screen orb always agree.
| Signal | State | Hardware behavior |
|---|---|---|
0 | Idle | LEDs off, servo parked at 90° |
B | Researching | Blue LED (pin 12) pulses, servo sweeps |
Y | Debating | Yellow LED (pin 13) pulses faster, servo sweeps |
W | Complete | LEDs off, servo parks |
OLLAMA_URL)jarvis_haedware/jarvis_hardware.ino (optional — the app
runs fine without one, just without the physical light show)Install dependencies
pip install flask flask-socketio python-dotenv requests pyserial ddgs
Configure environment variables
Create a .env file in the project root:
# Flask
SECRET_KEY=<random_key>
FLASK_DEBUG=1 # 1 to enable debug/auto-reload, 0 for production
# Arduino serial (optional — auto-detected if omitted)
ARDUINO_PORT=COM3
# CrewAI (optional — falls back to direct web search if unset)
CREWAI_CREW_URL=
CREWAI_CREW_TOKEN=
CREWAI_INPUT_KEY=query
# Ollama (local LLM)
OLLAMA_URL=http://localhost:11434/api/chat
# OmniVoice Studio (local TTS)
OMNIVOICE_URL=http://localhost:3900/v1
OMNIVOICE_VOICE=alloy
OMNIVOICE_APP_PATH=C:\Program Files\OmniVoice Studio\omnivoice-studio.exe
(Optional) Flash the Arduino
Upload jarvis_haedware/jarvis_hardware.ino to your
board using the Arduino IDE or CLI. Wiring: blue LED on pin 12, yellow LED on pin 13, servo
signal on pin 8.
Run the app
python app.py
Then open http://localhost:5000 in your browser.
To test LED/servo wiring without booting the full Flask app:
python test_connection.py
This connects directly over serial and cycles through every state (4 seconds each) so you can confirm the board reacts correctly.
| Variable | Purpose | Default |
|---|---|---|
SECRET_KEY | Flask session secret | random, generated at startup |
FLASK_DEBUG | Enable Flask debug/reload mode | 1 |
ARDUINO_PORT | Force a specific serial port | auto-detected |
CREWAI_CREW_URL | Base URL of a deployed CrewAI crew | unset (uses direct search) |
CREWAI_CREW_TOKEN | Bearer token for the CrewAI API | unset |
CREWAI_INPUT_KEY | Input key your crew's tasks expect | query |
OLLAMA_URL | Ollama chat endpoint | http://localhost:11434/api/chat |
OMNIVOICE_URL | OmniVoice Studio API base URL | http://localhost:3900/v1 |
OMNIVOICE_VOICE | Voice name to use for synthesis | alloy |
OMNIVOICE_APP_PATH | Path to the OmniVoice executable, for auto-launch | Windows default install path |
CACHE_TTL | Response/audio cache lifetime, in seconds | 3600 |
OVERRIDE_MODEL | Force a specific Ollama model regardless of detected VRAM | unset |
| Issue | Likely cause | Fix |
|---|---|---|
| "No Arduino found" on startup | Cable disconnected or board in bootloader | Check the physical connection; restart the board |
| Hardware stops responding mid-session | Servo brownout on the board's own 5V rail | Give the servo a separate 5V supply |
| OmniVoice errors in the logs | TTS service not running yet | The app auto-launches it; check OMNIVOICE_APP_PATH |
| Requests always rate-limited | Rapid repeated requests from the same client | Rate limit is 10 requests/60s per IP by default |
| Stale-looking replies | Cache TTL too long | Lower CACHE_TTL or restart the app |
app.py Flask app, LLM/search/TTS orchestration, hardware control
test_connection.py Standalone Arduino connectivity check
jarvis_haedware/jarvis_hardware.ino Arduino firmware (LED + servo state machine)
templates/index.html Chat UI
static/script.js Client-side chat, TTS playback, WebSocket handling
static/style.css Styling
.env Local configuration (not committed)
See AGENTS.md for a deeper architectural walkthrough intended for AI coding agents working in this repo.
Python
53.0%
CSS
16.6%
JavaScript
16.4%
HTML
7.6%
C++
6.5%