Raspberry Pi API Call Series: A Browser-Based AI Dashboard on a Pi 3

A local Flask server, an HTML interface in Chromium, file upload, AI processing, and results rendered as a table. All of it on a Raspberry Pi 3B+. Here is what we built.
The Full Stack: A Browser-Based AI Dashboard on a Pi 3 — Tech-Reader.blog
Tech-Reader.blog  ·  Raspberry Pi AI Series  ·  Part 4 of 4
Chromium Dashboard  ·  October 2026

The Full Stack: A Browser-Based AI Dashboard on a Pi 3

Flask in the background, Chromium up front, Groq in the cloud. Two files. Zero cost.
We started this series with twelve lines of bash and a curl command. We are finishing it with a web application — a local Flask server, an HTML dashboard in Chromium, file upload, AI processing, and results rendered as a formatted table. All of it running on a Raspberry Pi 3B+.

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

Terminal
pip3 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:

ai_dashboard/
├── app.py         ← the Flask server
└── templates/
    └── index.html ← the dashboard page

The Flask Server

app.py
from 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 &mdash; 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:

Terminal
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.

2 files The complete application — app.py and index.html. That is the entire stack.
· · ·

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.

Series Complete  ·  Tech-Reader.blog
What We Built — and What Comes Next

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.