Raspberry Pi API Call Series: A Browser-Based AI Dashboard on a Pi 3
The Full Stack: A Browser-Based AI Dashboard on a Pi 3
Flask is a lightweight Python web framework. It starts a local HTTP server on port 5000 that Chromium connects to at http://localhost:5000. The HTML page sends the CSV file to Flask via a form upload. Flask reads the file, calls the Groq API, and sends the JSON result back. JavaScript in the page renders it as a table.
The whole thing is local except for that one outbound API call to Groq. Your CSV file never leaves the Pi except to travel to the AI model for processing. Everything else — serving the page, receiving the upload, rendering the results — happens on the board.
The browser is the UI. Flask is the bridge. Groq is the engine. The Pi is the stage. It looks like a web app because it is one.
Setup: Two Libraries
Terminalpip3 install flask requests
That is the complete dependency list for this installment.
File Structure
Create a folder called ai_dashboard in your home directory. Inside it, you need two files:
├── app.py ← the Flask server
└── templates/
└── index.html ← the dashboard page
The Flask Server
app.pyfrom flask import Flask, request, jsonify, render_template
import requests as http_requests
import json
app = Flask(__name__)
GROQ_API_KEY = "your_api_key_here"
GROQ_URL = "https://api.groq.com/openai/v1/chat/completions"
@app.route("/")
def index():
return render_template("index.html")
@app.route("/process", methods=["POST"])
def process():
if "csv_file" not in request.files:
return jsonify({"error": "No file uploaded"}), 400
file = request.files["csv_file"]
csv_data = file.read().decode("utf-8")
prompt = f"""Here is a CSV file with product inventory data:
{csv_data}
Return a JSON array where each item has:
- product (string)
- price (number)
- category (reasonable category based on product name)
Return only valid JSON. No explanation."""
headers = {
"Authorization": f"Bearer {GROQ_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": "llama-3.3-70b-versatile",
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.2
}
try:
response = http_requests.post(GROQ_URL, headers=headers, json=payload)
response.raise_for_status()
content = response.json()["choices"][0]["message"]["content"]
result = json.loads(content)
return jsonify({"success": True, "data": result})
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000, debug=False)
The Dashboard Page
templates/index.html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AI CSV Dashboard</title>
<style>
body { font-family: sans-serif; background: #0f1923; color: #e0e0e0;
margin: 0; padding: 32px 24px; }
h1 { font-size: 22px; color: #a8d8f0; margin-bottom: 4px; }
.sub { font-size: 13px; color: #666; margin-bottom: 28px; }
.card { background: #1a2535; border-radius: 6px; padding: 24px; margin-bottom: 20px; }
input[type="file"] { color: #a8d8f0; margin-bottom: 16px; display: block; }
button { background: #1a5c8a; color: white; border: none;
padding: 10px 24px; border-radius: 4px; font-size: 14px; cursor: pointer; }
button:hover { background: #2075b0; }
button:disabled { background: #333; cursor: default; }
#status { font-size: 13px; color: #888; margin-top: 12px; }
table { width: 100%; border-collapse: collapse; margin-top: 8px; }
th { text-align: left; padding: 10px 12px; background: #111e2b;
color: #a8d8f0; font-size: 13px; }
td { padding: 10px 12px; border-bottom: 1px solid #1e2f40; font-size: 14px; }
tr:last-child td { border-bottom: none; }
#download-btn { background: #2e5e2e; margin-top: 16px; }
#download-btn:hover { background: #3a7a3a; }
</style>
</head>
<body>
<h1>AI CSV Dashboard</h1>
<p class="sub">Powered by Groq + Llama 3.3 — running on Raspberry Pi</p>
<div class="card">
<input type="file" id="csv-input" accept=".csv">
<button onclick="processCSV()">Send to AI</button>
<div id="status">Choose a CSV file to get started.</div>
</div>
<div class="card" id="results-card" style="display:none">
<table id="results-table"></table>
<button id="download-btn" onclick="downloadJSON()">Download JSON</button>
</div>
<script>
let lastResult = null;
async function processCSV() {
const input = document.getElementById("csv-input");
const status = document.getElementById("status");
if (!input.files[0]) { status.textContent = "Please select a CSV file first."; return; }
status.textContent = "Sending to Groq API...";
const formData = new FormData();
formData.append("csv_file", input.files[0]);
try {
const response = await fetch("/process", { method: "POST", body: formData });
const result = await response.json();
if (result.error) {
status.textContent = "Error: " + result.error;
} else {
lastResult = result.data;
renderTable(result.data);
status.textContent = "Done. " + result.data.length + " items processed.";
document.getElementById("results-card").style.display = "block";
}
} catch (err) {
status.textContent = "Network error: " + err.message;
}
}
function renderTable(data) {
const table = document.getElementById("results-table");
const headers = Object.keys(data[0]);
let html = "<tr>" + headers.map(h => `<th>${h.toUpperCase()}</th>`).join("") + "</tr>";
data.forEach(row => {
html += "<tr>" + headers.map(h => `<td>${row[h]}</td>`).join("") + "</tr>";
});
table.innerHTML = html;
}
function downloadJSON() {
if (!lastResult) return;
const blob = new Blob([JSON.stringify(lastResult, null, 2)], {type: "application/json"});
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "output.json";
a.click();
}
</script>
</body>
</html>
Running It
Navigate to your ai_dashboard folder in the terminal and start the server:
cd ~/ai_dashboard
python3 app.py
Open Chromium and go to http://localhost:5000. The dashboard loads. Select your CSV, click Send to AI, and the results render as a table in the browser. Click Download JSON to save the output.
A Raspberry Pi 3B+ just served a web application to its own browser, processed a file through a frontier AI model, and rendered the results as a formatted data table. The Pi handled the HTTP routing, the file reading, and the UI. Groq handled the intelligence. The whole thing cost nothing to run except the electricity to power the board.
That is the thesis of this series proven at every level. Shell script. Python CLI. Desktop app. Web dashboard. The hardware constraint is real — you cannot run a meaningful AI model locally on a Pi 3. But you can build AI-powered applications on one, today, for free. The Pi does not need to be the AI. It needs to be the builder. Those are different jobs. And the Pi 3 is very good at one of them.
Four platforms. Four installments. One free API key. The next series will expand the toolkit — different providers, different tasks, more complex workflows. All of it on the same affordable hardware, using the same approach: the Pi as builder, the cloud as engine.