pieeg-server·Python 3.10+ · MIT · :1616 / :1617

Self-Hosted Biosignal Streaming Server

One process acquires 8 to 32 channels over SPI, Bluetooth LE, or USB serial and emits one JSON object per sample on WebSocket :1616. A React dashboard is served on :1617. Optional LSL, OSC, and HTTP webhooks attach to the same loop. MIT licensed.

SPI (PiEEG-8 / 16, JNEEG) BLE (IronBCI-8) USB Serial (IronBCI-32) LSL · OSC · Webhooks MIT · optional --auth
ws://raspberrypi.local:1616
250 Hz · n=18,420
Fp1
Fp2
C3
O1
Frame contract
{ "t": 1711234567.123, "n": 18420, "channels": [12.40, -5.11, 8.02, 15.60, …] }
t1711234567.123
channelsµV array
bind:1616 WS
UI:1617 HTTP
Simulated 4s windowInspect path

[01 · SERVER ARCHITECTURE]

Trace the sample from ADC to JSON, LSL, and OSC

Pick a subsystem to inspect how acquisition, the WebSocket contract, the dashboard, and optional outlets share one loop. Hover any specification row to highlight its nodes.

Highlight
Data path through pieeg-server

Live output preview

Values in the live panel are simulated to illustrate the runtime contract of each server surface.

[02 · INSTALL, DEVICES & WEBSOCKET]

One package. A device flag. JSON on :1616.

Python 3.10+ on Raspberry Pi, Jetson, Linux, macOS, or Windows. The dashboard is already inside the wheel. Node is not required unless you are editing dashboard/src.

Server docs
Install
any OS · Python 3.10+
Command
pip install pieeg-server
pieeg-server --mock
:1616WebSocket JSON
:1617Dashboard HTTP
doctorpieeg-server doctor
Device flag

PiEEG-16

16 ch · SPI

Default device. Raspberry Pi shield over SPI. First boot: enable SPI, then reboot.

Start
$ pieeg-server

WebSocket contract

stream_eeg.py
import asyncio, json, websockets

async def main():
    async with websockets.connect("ws://raspberrypi.local:1616") as ws:
        async for msg in ws:
            frame = json.loads(msg)
            # frame = {"t": 1711234567.123, "n": 42, "channels": [12.34, -5.67, ...]}
            print(f"#{frame['n']}: {len(frame['channels'])} ch @ {frame['t']}")

asyncio.run(main())
stream.js
const ws = new WebSocket("ws://raspberrypi.local:1616");

ws.onmessage = (e) => {
  const frame = JSON.parse(e.data);
  // frame.channels = µV per channel
  // frame.t = Unix time (seconds)
  // frame.n = monotonic sample index
  console.log(`#${frame.n}: ${frame.channels.length} channels`);
};
t

Unix timestamp (seconds, float)

n

Monotonic sample index

channels

Array of microvolts, one per ADC channel

Filter

Butterworth bandpass, live toggle

{ "cmd": "set_filter", "enabled": true, "lowcut": 1.0, "highcut": 40.0 }

Record

CSV start / stop

{ "cmd": "start_record" }
{ "cmd": "stop_record" }

ADS1299

Registers and noise test

{ "cmd": "reg_read" }
{ "cmd": "reg_write", "regs": { "0x05": "0x00" } }
{ "cmd": "noise_test", "duration": 3 }

LSL

Outlet and channel groups

{ "cmd": "lsl_start" }
{ "cmd": "lsl_groups_set", "groups": [
  { "name": "EEG", "channels": [0, 1, 2, 3] }
] }

OSC

VRChat chatbox / parameters

{ "cmd": "osc_configure", "host": "127.0.0.1", "port": 9000, "mode": "both" }
{ "cmd": "osc_start" }

Webhooks

HTTP callbacks on EEG events

{ "cmd": "webhook_create", "rule": { "trigger_type": "amplitude_above", "params": { "channel": 0, "threshold": 150 } } }

[03 · MIT LICENSE & ECOSYSTEM]

Software is MIT.

pieeg-server, the dashboard source, and pieeg.js are published under the MIT license. You may use, modify, and redistribute that code, including in commercial products, subject to the license text in the repository.

What MIT covers

The Python package, WebSocket protocol, committed dashboard build, pieeg.js, and examples in pieeg-club/PiEEG-server. LICENSE file in that repo is the source of truth.

pieeg.js

Zero-dependency browser library in the same repo. connectBLE() / connectSerial() must run from a user gesture. CDN: jsDelivr PiEEG-server@main/pieeg.js.

Local by default

Acquisition, filtering, recording, and the dashboard run on the machine that started the process. Cloud relay, session upload, and Buddy keys are separate, opt-in surfaces. --auth is a local 6-digit gate, not a cloud account.

Lab Streaming Layer

Raw samples on the LAN. OpenViBE, MNE-LSL, LabRecorder, BCI2000, NeuroPype.

--lsl

OSC

Band powers to UDP :9000. Chatbox and /avatar/parameters, including per-region groups.

--osc

Webhooks

HTTP POST when a band, ratio, or amplitude rule matches. Dashboard wizard for IFTTT and Zapier.

webhooks.json

Jupyter

CSV on disk, or stream live. notebooks/ in the repo. Agent can generate notebooks over LSL.

notebooks/
Self-hosted server vs browser-native Cloud
Same React dashboard UI · MIT licensed
Layerpieeg-serverPiEEG Cloud
Installationpip install pieeg-server (Python 3.10+)Open cloud.pieeg.com in Chrome, Edge, Brave, or Opera
Hardware inputSPI shields (PiEEG-8/16, JNEEG) plus BLE and USB serial boardsWeb Bluetooth (IronBCI 8/16, XR, Octopus-16); Web Serial (IronBCI-32, Kraken-48)
Where DSP runsOptional server-side IIR; dashboard still does client-side FFTClient-side in the tab (Hampel, Butterworth IIR, Worker FFT)
Local app outputNative LSL (--lsl), OSC (--osc), HTTP webhooksWebRTC P2P to Local Bridge → UDP OSC
AI copilotPiEEG Agent over LSL + MCP (pieeg-server --lsl, then pieeg-agent mcp)Buddy at buddy.pieeg.com (in-tab, read-only tools)
LicenseMIT (pieeg-server, pieeg.js, dashboard source)Dashboard UI MIT; hosted relay is opt-in

Start the process, then open :1617

No board: pieeg-server --mock. Hardware: pass --device. The dashboard and the WebSocket share that process.