Unicorn Hybrid Black integration — research + proposal¶
Status: Path A (BrainFlow) VALIDATED on hardware 2026-07-07 — streams 8-ch @ 250 Hz, clean
link, with no g.tec software install and no paid API (BrainFlow bundles Unicorn.dll). Selectable
in the toolbox as a board, with a fixed built-in montage (Fz C3 Cz C4 Pz PO7 Oz PO8) the Design
tab auto-selects + locks when the device is chosen, and a GUI Detect-list entry (Bluetooth scan)
validated 2026-07-08 (open question #4 below → resolved). Remaining: reuse for impedance is open.
Aaron owns a Unicorn Hybrid Black + the paid Python API key, but the goal — future users must not
have to buy the Python API — is met by this path.
Validation result (2026-07-07, serial UN-2024.10.06)¶
python -m ssvep.runtime.openbci --board unicorn --check --debug →
use dyn lib: …\brainflow\lib\Unicorn.dll → Library is loaded
Use device with id UN-2024.10.06
OK: 1251 samples in 5.0s (~250.2 Hz vs nominal 250), 8 EEG channels. reliability: 0 dropped — CLEAN
brainflow (already a dep) + the headset paired at the Windows
level. The pairing was the whole battle — see below.
Pairing runbook (the part that isn't obvious)¶
- The Unicorn is not paired through Windows "Add device"; pair it via the Unicorn Suite → "My
Unicorn" panel, and complete the Windows pairing pop-up it triggers (no PIN is requested for
this device). A mere in-app connect does not leave a persistent pairing, and BrainFlow's discovery
only sees Windows-paired devices → the symptom is
[error] Unicorn not foundon every attempt. - Use the g.tec CSR8510 dongle ("Generic Bluetooth Radio" in Device Manager), disable the internal Bluetooth, and turn off power-saving on the dongle radio (Device Manager → Properties → Power Management). Headset blue-flashing = advertising.
- Once paired, BrainFlow auto-discovers the single device; pass
--serial-port UN-YYYY.MM.XXonly to pick among several. The board-specific failure checklist inopenbci._checklist("unicorn")now encodes all of this. - Pairing persists across a headset power-cycle (verified 2026-07-07 — powered the Unicorn off/on and it streamed again with the Suite closed, no re-pair). So the Unicorn Suite is a one-time pairing tool; it is never needed to run a session.
How to test Path A (Aaron, on the Windows acquisition PC)¶
- Ensure g.tec's free Unicorn.dll is present where BrainFlow can load it (it ships with the free Unicorn Suite / C API). Pair the headset over Bluetooth (see "Bluetooth adapter" below).
- Bounded connectivity check, mirroring how we validated the Cyton boards:
Expect 8 EEG ch @ 250 Hz, montage
python -m ssvep.runtime.openbci --board unicorn --check # or, to pin one of several units by serial number: python -m ssvep.runtime.openbci --board unicorn --serial-port UN-YYYY.MM.XX --checkFz C3 Cz C4 Pz PO7 Oz PO8, and the same dropped-sample / effective-rate reliability line we get for OpenBCI (the Unicorn has a usable packet counter). A green check means the whole existing bridge/retry/live-view stack works with the Unicorn as-is. - If the check fails, the likely cause is BrainFlow-on-Windows Unicorn support or the DLL location — that's exactly open question #1, and the point where we'd fall back to Path C/D.
Goal & constraints¶
- No purchase in the happy path. A lab that has a Unicorn but not the g.tec Python API must be able to run this toolbox with it. (The C API, .NET API, Recorder, and LSL interface come free with the device; only the Python API is a paid add-on — confirmed by Aaron, consistent with the sources below.)
- License hygiene (CLAUDE.md locked decision). Avoid strong copyleft (GPL). This rules out
vendoring
robertoostenveld/unicorn2xx(GPL-3.0), though we may read the public Bluetooth protocol spec it's built against and implement independently (a data format is not copyrightable). - Keep the array-API + LSL spine. Everything downstream already consumes either a plain
(n_ch, n_times)array +sfreq(decode/spectral/metrics) or an LSLEEGstream (RunRecorder.preflightresolves EEG by type). Any Unicorn path should terminate in one of those two seams and leave the rest of the toolbox untouched — exactly how OpenBCI plugs in today viaruntime/openbci.py's BrainFlow→LSL bridge.
The device, briefly¶
8 EEG channels @ 250 Hz, plus 3-axis accel (and gyro/battery/counter/validation), streamed over
Bluetooth. On Windows a paired Unicorn presents as a Bluetooth serial COM port (device name
like UN-YYYY.MM.XX) — i.e. it looks like a serial device, distinct from the OpenBCI FTDI dongle and
from the generic "Standard Serial over Bluetooth link" ports our discovery.py already rejects. The
raw link is a fixed 45-byte packet protocol, publicly documented (see unicorn2xx / unicorn-bi).
Candidate integration paths¶
| # | Path | New code | Purchase | License | Notes |
|---|---|---|---|---|---|
| B | UnicornLSL → LSL (g.tec's free LSL app streams; we just record the stream) | ~none | none (free w/ device) | proprietary app, but only run, not linked | Works today. Manual: operator launches a Windows GUI app per session. No impedance/board-control through LSL. |
| A | BrainFlow UNICORN_BOARD (add to the board map; reuse the existing bridge) |
tiny | none (needs free Unicorn.dll) |
BrainFlow MIT + proprietary (free) lib | Smallest change if BrainFlow's Unicorn works on Windows — the docs only promise Ubuntu/RPi. Must verify. |
| C | Free C API via ctypes (thin wrapper over Unicorn.dll/.lib + a unicorn_to_lsl bridge mirroring openbci.stream_to_lsl) |
moderate | none (free C API, bundled) | proprietary (free) lib, our wrapper is ours | Fully integrated, g.tec maintains device handling. Needs the free C API installed (it ships to Documents\gtec\Unicorn Suite\…\Unicorn CAPI). |
| D | Pure-Python protocol reader (implement the documented 45-byte BT packet over pyserial, + unicorn_to_lsl) |
most | none | fully ours (no proprietary bits) | Maximally independent & cross-platform; depends on nothing g.tec-proprietary. We own connection reliability. unicorn2xx proves feasibility (but is GPL — implement from the spec, don't copy). |
All four avoid the paid Python API, so all satisfy the core requirement. They differ in effort, how "seamless" the operator experience is, and how much proprietary/free-but-closed surface they pull in.
Recommendation — staged, cheapest-proof-first¶
- Phase 0 — validate end-to-end now, zero toolbox code (Path B). Install the free Unicorn Suite,
run UnicornLSL, and point the existing recorder at that LSL
EEGstream (set the stream name;RunRecorderalready resolves + verifies it against the manifest). This confirms the whole pipeline (markers, XDF, decode, report) works with Unicorn data and gives an always-available fallback for any lab that has the Suite. Also useful as ground truth to validate Phases A/C/D against. - Phase 1 — try the near-free integration (Path A). Drop the free
Unicorn.dllwhere BrainFlow can load it and tryBoardIds.UNICORN_BOARDon the Windows acquisition PC. If it streams, Unicorn support is ~5 lines: add"unicorn": BoardIds.UNICORN_BOARDtoopenbci._board_id, teach_make_boardthat it's a Bluetooth/serial-number device, and it inherits the entire existing bridge, retry, drop-metric, and live-view stack. This is the ideal outcome — verify first because BrainFlow's Unicorn support is documented for Linux, not Windows. - Phase 2 — the purchase-free default if Phase 1 is unreliable. Build a first-class in-toolbox
driver terminating in a
unicorn_to_lslbridge (same shape asopenbci.stream_to_lsl, so the runtime is unchanged). Choose: - Path C (free C API via ctypes) if we want g.tec to own device/BLE handling and are fine with an optional proprietary-but-free dependency (same posture as BrainFlow itself — a lazy import, never redistributed; the user installs the free Suite).
- Path D (pure-Python protocol) if we want zero proprietary surface and full cross-platform control, at the cost of owning the packet parser and connection robustness. Best fit for "future users install nothing extra" and for the toolbox's bare-numpy/pyserial ethos.
Lean C first (less code, g.tec-maintained), with D as the fully-independent option if the free C API's terms or platform coverage ever become a problem.
How it plugs into what we just built¶
- Discovery (
runtime/discovery.py). A paired Unicorn does not present as a COM port (unlike the FTDI dongle); it enumerates as a Windows Bluetooth device whose friendly name is its serial number.classify_bluetooth_device/discover_unicorndetect it as a third device class (board="unicorn",transport="bluetooth",target=the serial), anddiscover_devicesfolds it into the sameDetectpick-list as the dongles/shields. The "target" is the Unicorn serial number, carried through the sharedserial_portslot into BrainFlow'sserial_number. - Provenance (
Provenance.device). The Unicorn serial number (printed on the device / from the C APIGetDeviceInformation) is the stable per-unit identity — exactly the slot we just added. - Board/rate. Unicorn is fixed 8-ch @ 250 Hz; the manifest's
acquisition.boardgains aunicornvalue andsfreq_hz= 250. Theboard_mismatchwarning already covers designed-vs-actual.
Bluetooth adapter (a real reliability lever)¶
g.tec recommends disabling the PC's internal Bluetooth and using their supplied Logitech BT dongle — the internal radio is the usual cause of dropped/laggy Unicorn links (the Unicorn analogue of the WiFi-shield packet loss we already measured). Decisions:
- For now, test with the Logitech dongle so Bluetooth isn't a confound while we validate Path A.
This is an OS-level setting (disable the internal BT adapter in Windows Device Manager, plug in
the dongle) — no toolbox code changes: BrainFlow/
Unicorn.dlljust use whatever Windows BLE adapter is active. - Goal: not require the dongle. Whether the internal radio is good enough is an empirical
question we can already answer — our
dropped_from_counterreliability metric quantifies link loss per session, so we can bench internal-BT vs dongle the same way we compared the WiFi shields (3–6% loss on a bad link vs 0% clean) and set an evidence-based recommendation rather than a guess. - Provenance idea (later): optionally record which adapter was used, since it materially affects link quality — but adapter introspection on Windows is fiddly; defer unless it proves necessary.
Compliance¶
- An ethics amendment is required. The Unicorn is not covered by this deployment's approved
protocol (same situation as the OpenBCI Cyton). Adding it is a hardware/method change → flag
and amend before any collection (
docs/REB_AMENDMENTS.md, CLAUDE.md §4). No participant data on the Unicorn until amended. - License. Our code stays permissive. The free C API (
Unicorn.dll) is proprietary-but-free — treat it like BrainFlow's own third-party board libs: an optional dependency the user installs, never committed/redistributed here. Do not vendorunicorn2xx(GPL-3.0). - New deps. Path A adds nothing (BrainFlow already in the tree). Path C adds only stdlib
ctypes. Path D usespyserial(already present via BrainFlow). None needs Aaron's dep sign-off beyond the proprietaryUnicorn.dll(Path A/C), which isn't a Python package.
Hardware findings (2026-07-07, first bring-up)¶
- BrainFlow's Windows Unicorn path works and needs no g.tec software install. With
--debug, the bounded check logsuse dyn lib: …\brainflow\lib\Unicorn.dll→Library is loaded— i.e. thebrainflowwheel already bundlesUnicorn.dll. So Path A's only prerequisites arebrainflow(already a dep) + an OS-paired headset. This resolves open questions #1 and #2 and makes Path C (a separate ctypes wrapper over a user-installed C API) largely unnecessary — keep C/D only as the "drop the g.tec binary entirely" fallback. - The remaining blocker is OS-level Bluetooth pairing. BrainFlow discovery returns
Unicorn not foundbecause the headset was only selected inside the Unicorn Suite (an in-app BLE connect), not paired in Windows Bluetooth settings. The g.tec discovery scans Windows-paired devices, so the headset must be added via Settings → Bluetooth (UN-YYYY.MM.XX) on the Logitech dongle's radio. Not a firewall issue (the library is running and scanning — it just finds nothing paired). - Open: whether the Windows pairing persists across power-cycles or must be re-done per session (some BLE devices re-advertise); confirm once paired. If it doesn't persist, that's a workflow note, not a code blocker.
Open questions (still to verify)¶
- ~~Does the Windows pairing persist across headset power-cycles?~~ Resolved: yes (2026-07-07).
- Link reliability (
dropped_from_counter) on the g.tec CSR8510 dongle vs the internal Bluetooth — the "do we actually need the dongle" question. Bench both once with electrodes on a head. (First checks on the dongle: 0 dropped / clean.) - Impedance: the Unicorn has its own electrode-quality readout — is it reachable through BrainFlow so
we can reuse the impedance-check UI, or is that OpenBCI-lead-off-only? (The Cyton lead-off
config_boardpath does not apply to the Unicorn.) - ~~GUI Detect: our
discovery.pycan't yet enumerate a paired Unicorn.~~ Resolved (2026-07-08).discovery.discover_unicorn()enumerates paired Unicorns by their Bluetooth friendly name (UN-YYYY.MM.XX, which is the BrainFlowserial_number) viaGet-PnpDevice, using the Windows PnPPresentflag to put currently-connected units first. Wired intodiscover_devices()so the Set-up-Session-tab Detect button lists them alongside OpenBCI dongles/shields; confirmed on hardware against 3 paired headsets (2 connected, 1 idle). Theserial_numberflows through the existingserial_portslot intoopenbci._make_board's Unicorn branch. (Detection is independent of the Unicorn Suite; run a session with the Suite closed — the headset is single-client.)
Sources¶
- BrainFlow supported boards (Unicorn =
UNICORN_BOARD, 8ch/250Hz/BLE,libunicorn): https://brainflow.readthedocs.io/en/stable/SupportedBoards.html - unicorn-bi org (free C/.NET APIs, Recorder, LSL): https://github.com/unicorn-bi
- Free Windows APIs (C/.NET/Python docs; C API ships
Unicorn.lib/unicorn.h): https://github.com/unicorn-bi/Unicorn-Hybrid-Black-Windows-APIs - Unicorn Suite contents / LSL interface included: https://www.gtec.at/product/unicorn-suite/
robertoostenveld/unicorn2xx— GPL-3.0 CLI streamer proving the documented BT protocol path (Unicorn2lsl etc.): https://github.com/robertoostenveld/unicorn2xx- Oostenveld review (Unicorn as BT COM port, UnicornLSL, protocol notes): https://robertoostenveld.nl/unicorn-hybrid-black/
- FieldTrip realtime Unicorn (UnicornLSL is Windows-only): https://www.fieldtriptoolbox.org/development/realtime/unicorn/