System Architecture Overview

The SmartPi Assistant's software architecture is built around three core principles: modularity, protocol-driven communication, and edge-cloud hybrid intelligence. The system bridges cloud-based LLM processing with local embedded control, creating a responsive ambient intelligence platform.

High-Level Architecture

SmartPi system architecture diagram
SmartPi System Architecture: End-to-end data flow from cloud APIs through n8n workflow, MCP bridge, to embedded display
Design Rationale: The architecture separates concerns into three distinct layers: cloud intelligence (LLM processing), local gateway (protocol translation), and edge hardware (display control). This separation enables independent scaling, testing, and evolution of each component.

1. Firmware Architecture (CircuitPython)

Core Design Philosophy

The firmware is designed as a lightweight HTTP listener with a focus on reliability, modularity, and real-time display performance. Running on CircuitPython allows rapid prototyping while maintaining low-level hardware control through the RP2040's dual-core architecture.

Key Components

  • WiFi Manager — Handles connection lifecycle with auto-reconnect
  • HTTP Server — Lightweight web server (adafruit_httpserver)
  • Message Queue — Circular buffer with priority sorting
  • Display Renderer — HUB75 LED matrix control with themes
  • Audio Subsystem — I²S amplifier integration (optional)

Message Queue System

The message queue implements a priority-based circular buffer that automatically sorts incoming messages by urgency (urgent → important → normal) and rotates through them with configurable display time.

Queue Design Properties

Property Value Rationale
Max Size 50 messages Balances memory constraints (264KB RAM) with practical capacity
Rotation Time 5 seconds Optimal for readability without feeling rushed
Update Mode Replace entire queue Atomic updates prevent partial state issues
Priority Order Urgent (0) → Important (1) → Normal (2) Ensures critical information displays first

Display Rendering Pipeline

The display subsystem leverages the rgbmatrix library to drive the HUB75 interface with precise timing control. The rendering pipeline includes:

Text Input → Word Wrap → Theme Application → HUB75 Output

1. Text Processing:
   - Automatic word wrapping to fit 64×64 matrix
   - Maximum 5 lines per message
   - Truncation with ellipsis for overflow

2. Theme System:
   - 5 predefined color schemes (calendar, email, weather, slack, neutral)
   - Foreground, background, and accent colors
   - Dynamic color switching based on message type

3. Hardware Acceleration:
   - CPU overclocked to 250MHz for smooth refresh
   - Bit depth: 4-bit (16 color levels)
   - Serpentine scanning for uniform brightness
Why CircuitPython? While C/C++ offers better performance, CircuitPython provides:
  • Rapid prototyping and iteration
  • Rich library ecosystem (adafruit)
  • Easy debugging via serial console
  • Sufficient performance for 64×64 matrix at 60 FPS

HTTP Endpoints

The firmware exposes three REST endpoints for message management:

Endpoint Method Purpose
/messages POST Receive batch of messages with priority and theme
/status GET Query device state (queue size, WiFi status, IP)
/clear POST Clear all messages from queue

2. n8n Workflow with LLM Integration

Workflow Architecture

The n8n workflow orchestrates data collection, LLM-based summarization, and delivery to the SmartPi device. It operates on a scheduled trigger model, periodically pulling data from multiple sources and pushing processed results. The visual workflow editor makes it easy to understand and modify the data flow.

n8n workflow diagram
Complete n8n workflow showing trigger, data sources, processing nodes, and MCP integration

Understanding n8n Workflow Components

1. Trigger Nodes: Starting the Workflow

The workflow begins with a trigger node that determines when execution starts. The SmartPi workflow uses a manual trigger for testing and immediate updates.

Trigger Options Considered

Trigger Type Use Case Configuration
Manual Trigger Testing and on-demand updates Click button to execute
Schedule (Cron) Automatic periodic updates Every 15-30 minutes, or hourly
Webhook Event-driven execution React to external events (calendar changes)

For production deployment, a schedule trigger running every 15-30 minutes would keep the display continuously updated.

2. Data Source Nodes: Fetching Information

After the trigger fires, the workflow branches into three parallel paths:

  • Google Calendar nodes: Authenticate with OAuth2 and fetch upcoming events from three calendars. Return structured JSON with event titles, times, locations, and attendees.
  • Weather API node: Makes HTTP request to OpenWeatherMap API for Cambridge, MA. Returns current temperature, conditions, humidity, and forecast.
  • Gmail nodes: Connect via OAuth2 to Gmail API and fetch recent unread messages from three email accounts. Return sender, subject, snippet, and timestamp.

3. JavaScript Code Nodes: Data Processing

The JS Code nodes execute custom JavaScript to filter, transform, and format raw API responses—turning verbose API data into concise, actionable messages optimized for the 64×64 pixel display.

Example: Weather Branch JS Code

// Access weather data from OpenWeatherMap API
const temp = $input.item.json.main.temp;
const conditions = $input.item.json.weather[0].description;

// Add contextual advice based on temperature
let advice = "";
if (temp < 40) {
  advice = "Wear a heavy jacket!";
} else if (temp < 55) {
  advice = "Bring a light jacket.";
} else if (temp > 85) {
  advice = "Stay hydrated!";
} else {
  advice = "Enjoy the weather!";
}

// Format for SmartPi display (max 55 chars)
return {
  json: {
    text: `${Math.round(temp)}°F ${conditions} - ${advice}`,
    theme: "weather",
    priority: "normal"
  }
};

4. LLM Message Generation

The workflow uses Google Gemini 2.5 Flash for real-time summarization due to its:

  • Low latency (~2 seconds per request)
  • Cost efficiency (free tier: 15 RPM, 1M TPM)
  • Strong instruction-following capabilities
  • JSON output mode for structured data

Prompt Engineering Principles

Each prompt follows a structured format optimized for the 64×64 display constraints:

Calendar Events:
  • Prefix: "C " (for Calendar)
  • Urgency markers: URGENT (< 2 hours), IMP (today), none (later)
  • Time format: 12-hour (e.g., "2PM")
  • Max length: 55 characters
  • Focus: Action or person involved
Email Messages:
  • Prefix: "E " (for Email)
  • Priority: URGENT (time-sensitive), IMP (important), none (normal)
  • Include: Sender's first name only
  • Max length: 55 characters
  • Focus: Key action or information
Weather Data:
  • Prefix: "W " (for Weather)
  • Include: Temperature, condition, location
  • Actionable: What to wear/bring
  • Max length: 55 characters
Why Visual Workflows? n8n's visual programming makes complex orchestration accessible:
  • Easy to understand data flow at a glance
  • Quick iteration and debugging (see data at each step)
  • No deployment pipeline—changes go live immediately
  • Built-in error handling and retry logic
  • Extensive integration library (800+ pre-built nodes)

3. Model Context Protocol (MCP) Design

Why MCP?

The Model Context Protocol provides a standardized way for LLMs and agents to interact with external tools and hardware. By implementing MCP, SmartPi becomes a first-class peripheral for agentic systems, enabling:

  • Discoverability — Tools are self-describing via manifest endpoint
  • Extensibility — New capabilities can be added without changing client code
  • Interoperability — Works with any MCP-compatible client (n8n, LangChain, ChatGPT)
  • Future-proofing — Aligns with emerging agent-hardware communication patterns

MCP Bridge Server Architecture

The MCP bridge acts as a protocol translator between high-level tool calls and low-level device commands. Built with FastAPI, it provides both MCP-compliant endpoints and direct HTTP endpoints for testing.

MCP Bridge Server Architecture
MCP Bridge Server Architecture: Protocol translation from high-level tool calls to device commands

Defined MCP Tools

Tool Name Parameters Purpose
push_messages messages (array), timestamp (optional) Send batch of messages to display
get_status None Query device state and health
clear_queue None Clear all messages from device
Tool Design Philosophy: Each tool represents a complete action rather than a low-level command. For example, push_messages handles batch updates, priority sorting, and error recovery — the client doesn't need to understand queue management internals.

Multi-Message State Management

The bridge server maintains separate queues for each message theme, storing up to 10 messages per theme. This solves the problem of message loss when multiple messages arrive in a single batch.

# State Management (smartpi_mcp_server.py)
# In-memory state: lists of messages per theme
current_messages = {
    "email": [],     # Up to 10 email messages
    "weather": [],   # Up to 10 weather messages
    "calendar": []   # Up to 10 calendar messages
}

# Append new messages (not overwrite)
for msg in incoming_messages:
    theme = msg.get("theme")
    current_messages[theme].append(msg)
    
    # Limit per theme (FIFO queue)
    if len(current_messages[theme]) > MAX_MESSAGES_PER_THEME:
        current_messages[theme].pop(0)  # Remove oldest

# Flatten and send all messages to Pico W
all_messages = []
for theme_list in current_messages.values():
    all_messages.extend(theme_list)

send_to_device("/messages", {"messages": all_messages})

Error Handling & Resilience

The bridge implements multiple layers of error handling:

  • Network Timeouts — 5-second timeout with 3 retry attempts
  • Exponential Backoff — 2n seconds between retries
  • Graceful Degradation — Returns error status without crashing
  • Logging — Comprehensive logging for debugging

4. Cloud-to-Local Network Connectivity

The Challenge: Bridging Internet and LAN

A key architectural challenge is enabling n8n Cloud (running on n8n's servers) to communicate with the MCP bridge (running on a local network). Traditional HTTP cannot work because:

  • The bridge server runs on localhost:5055 (not publicly accessible)
  • NAT/firewall prevents inbound connections from the internet
  • Port forwarding requires router configuration and exposes security risks

Solution: ngrok Secure Tunneling

ngrok creates a secure tunnel from a public URL to the local bridge server, enabling cloud-to-local communication without firewall modifications.

Connectivity Architecture

Connectivity architecture showing ngrok tunnel from n8n Cloud through internet to local network
Connectivity Architecture: ngrok secure tunnel enables cloud-to-local communication bypassing NAT/firewall

ngrok Configuration

# Start ngrok tunnel
$ ngrok http 5055

# Output:
Forwarding  https://abc123.ngrok-free.app → http://localhost:5055

# In n8n HTTP Request node:
URL: https://abc123.ngrok-free.app/mcp
Method: POST
Body: {"tool": "push_messages", "args": {...}}
Security Considerations:
  • All traffic is TLS-encrypted (https://)
  • ngrok URL is randomly generated (hard to guess)
  • URL can be regenerated or use custom domains (paid plans)
  • Can add authentication layer in bridge server (future)

Alternative: Self-Hosted Tunnel

For production deployments, alternatives to ngrok include:

  • WireGuard/Tailscale — VPN-based mesh networking
  • Cloudflare Tunnel — Free alternative to ngrok
  • SSH Reverse Tunnel — DIY solution using SSH
  • Kubernetes Ingress — For containerized deployments
Production Note: ngrok free tier URLs change on restart. For production, either use a paid ngrok plan with custom domains, or deploy the bridge server to a cloud VPS with a static IP address.

5. Testing Environment & Strategy

Multi-Layer Testing Approach

The SmartPi system requires testing at multiple levels: hardware, firmware, network, and integration. The testing strategy isolates each layer for independent validation.

1. Device Endpoint Testing

Script: test_pico_endpoint.py
Method: Direct HTTP calls to Pico W

Tests:

  • Device reachability
  • HTTP endpoints (/messages, /status, /clear)
  • Theme variations
  • Priority sorting
  • Message wrapping
  • Error handling

2. Bridge Server Testing

Script: test_bridge.py
Method: API calls to localhost:5055

Tests:

  • Bridge health
  • MCP tool invocation
  • Error propagation
  • Retry logic
  • Manifest discovery

3. Integration Testing

Method: Manual or automated workflow execution via n8n

Tests:

  • End-to-end data flow
  • LLM summarization quality
  • Display rendering
  • Network resilience
  • Message deduplication

Test Script Architecture

Both test scripts follow a consistent pattern for reliability and clarity:

# Test Script Pattern

1. Setup:
   - Verify connectivity
   - Check prerequisites
   - Display test configuration

2. Execution:
   - Run tests sequentially
   - Catch and report exceptions
   - Log detailed output

3. Reporting:
   - Color-coded results (✓ green, ✗ red)
   - Summary statistics
   - Exit code (0 = pass, 1 = fail)

4. Cleanup:
   - Reset device state if needed
   - Save results to log file
Visual Verification: Many tests require visual confirmation of display output. The test scripts prompt the user to verify that:
  • Messages appear on the LED matrix
  • Colors match the expected themes
  • Text wrapping looks correct
  • Rotation timing is appropriate

Continuous Integration Potential

While currently manual, the test suite is designed for CI/CD integration:

  • Exit codes indicate pass/fail status
  • JSON output mode for machine parsing
  • Parameterized URLs for different environments
  • Dockerized test runner (future)

6. API Design & Integration Points

Three-Tier API Architecture

The SmartPi system exposes APIs at three levels, each serving a different audience and use case:

API Layer Endpoint Audience Protocol
Device API http://10.0.0.230:5000/* Local network clients REST/HTTP + JSON
Bridge API (Direct) http://localhost:5055/push Local scripts, testing REST/HTTP + JSON
Bridge API (MCP) http://localhost:5055/mcp LLM agents, n8n, automation MCP + JSON

Message Format Design

All APIs use a consistent message schema optimized for the display constraints:

{
  "messages": [
    {
      "text": "Meeting in 30 minutes",
      "theme": "calendar",           // calendar|email|weather|slack|neutral
      "priority": "urgent"             // urgent|important|normal
    },
    {
      "text": "Budget approval needed",
      "theme": "email",
      "priority": "important"
    }
  ],
  "timestamp": "2025-11-11T14:30:00Z"  // ISO 8601
}
Why This Schema?
  • Batch updates — Sending multiple messages atomically reduces network overhead
  • Explicit themes — Separates semantic meaning from visual rendering
  • Priority field — Enables automatic sorting without parsing text
  • Timestamp — Enables message freshness checks and logging

API Response Patterns

All APIs follow consistent response patterns for predictability:

Success Response:
{
  "status": "success",
  "result": {
    "message_count": 3,
    "device_response": {"status": "ok"}
  }
}
Error Response:
{
  "status": "error",
  "error": "Connection timeout",
  "retry_after": 5  // optional
}

Rate Limiting & Performance

Component Rate Limit Latency
SmartPi Device ~1 req/sec <100ms
MCP Bridge Unlimited (CPU-bound) <50ms + device latency
Google Gemini 15 RPM (free tier) ~2 seconds

Core Design Principles

1. Separation of Concerns

Each component has a single, well-defined responsibility. The firmware handles display control, the bridge handles protocol translation, and n8n handles orchestration.

2. Protocol-Driven Architecture

By standardizing on MCP and REST/HTTP, components can be swapped, upgraded, or replaced independently without breaking the system.

3. Edge Intelligence

The Pico W handles local decision-making (queue management, display refresh) while LLM processing happens in the cloud, optimizing for both performance and cost.

4. Fail-Safe Defaults

If WiFi fails, the device continues displaying cached messages. If the bridge is unreachable, n8n logs the error but doesn't crash the workflow.

5. Observable Systems

Every component exposes status endpoints, logging, and error reporting to enable easy debugging and monitoring in production.

6. Extensibility First

The MCP architecture enables future additions (voice control, sensors, multiple displays) without architectural changes.

7. Deployment & Configuration

Firmware Deployment

The CircuitPython firmware is deployed via USB mass storage:

  1. Connect Pico W via USB (appears as CIRCUITPY drive)
  2. Copy main.py and libraries to root directory
  3. Create secrets.py with WiFi credentials
  4. Reset device to start execution

Bridge Server Deployment

The MCP bridge server runs as a local service:

# Start bridge server
python smartpi_mcp_server.py

# Expose via NGROK for cloud access
ngrok http 5055

n8n Workflow Deployment

The workflow is imported and configured in n8n:

  1. Import workflow JSON file
  2. Configure OAuth2 credentials for Gmail and Calendar
  3. Set OpenWeather API key
  4. Update bridge server URL (NGROK tunnel)
  5. Activate workflow

8. Future Architecture Evolution

Phase 2: Voice Integration

  • ICS-43434 I²S MEMS microphone integration
  • Wake word detection (edge ML model)
  • Voice command processing via Gemini
  • Bidirectional interaction (pull mode)

Phase 3: Multi-Device Orchestration

  • Device registry in MCP bridge
  • Broadcast/multicast message delivery
  • Location-aware routing (home/office)
  • Synchronized displays

Phase 4: Local LLM Integration

  • Replace Gemini with local Llama model
  • Privacy-preserving on-device processing
  • Reduced cloud dependency
  • Faster response times

Phase 5: Agent Framework

  • LangChain/LlamaIndex integration
  • Multi-step reasoning workflows
  • Proactive notifications based on context
  • Learning user preferences over time

Conclusion

The SmartPi Assistant's software architecture demonstrates that ambient intelligence at the edge can be achieved through careful layering of protocols, modular design, and hybrid cloud-local processing. By adopting the Model Context Protocol, the system becomes more than just a display device—it becomes a first-class peripheral for LLM-powered agents.

The architecture balances multiple concerns: real-time performance on constrained hardware, cloud-scale intelligence through LLM integration, and network resilience through tunneling and retry logic. Each design decision prioritizes modularity and extensibility, enabling the system to evolve from a simple notification display to a comprehensive ambient intelligence platform.

Key Architectural Achievements:
  • ✓ Seamless cloud-to-local communication via ngrok tunneling
  • ✓ Protocol-driven design enabling future agent integrations
  • ✓ Real-time display performance (60 FPS) on $6 microcontroller
  • ✓ Modular, testable components with clear interfaces
  • ✓ Production-ready error handling and resilience