Hardware
Devices
Octopus_16
Specification

Specification

Everything you need to know about the hardware, plus the Bluetooth protocol for writing your own software.

Octopus 16

Signal acquisition

Converters2 × Texas Instruments ADS131M08
Type24-bit, 8-channel, simultaneous-sampling, delta-sigma
Channels16 (8 per converter)
Gain4 on all channels (set in firmware; the ADS131M08 supports 1 to 128)
DC blockADS131M08 built-in DC-block filter on (about 1 Hz)
Reference voltage1.2 V internal
ClockExternal 8.192 MHz oscillator
Oversampling ratio16384
Sample rate250 SPS (8.192 MHz ÷ (2 × 16384))
InterfaceShared SPI bus, a chip select per converter, shared RESET, DRDY from converter 1 as the sync master

Sample rate on some boards: boards fitted with an 8.000 MHz clock run at about 244 SPS (4 MHz ÷ 16384), even though the stream is labelled 250. That is fine for viewing and band power. Correct for it if you need precise timing (ERP, P300).

Electrode array

ContactsSpring-loaded pogo pins, ⌀ 4 × 11 mm
Total pins18: 16 signal, 1 reference (AINREF), 1 ground (GND)
PitchAbout 5.38 mm, hexagonal packing
Array spanAbout 20.3 × 20.3 mm
Reference / groundSelectable by jumpers for single-board or stacked use

Board and controller

BoardRound, 26.0 mm diameter, 4 layers
ControllerSeeed Studio XIAO ESP32-S3 (not included)
RadioBluetooth LE 5, 2M PHY
Power5 V power bank via the XIAO's USB-C, no more than 2 A. Battery only while worn.
IsolationNone
3D modelsThingiverse (opens in a new tab)

BLE protocol

Advertised nameOctopus-16-XXXX (official firmware)
Service4fafc201-1fb5-459e-8fcc-c5c9c331914b
Data characteristic (notify)beb5483e-36e1-4688-b7f5-ea07361b26a8
Command characteristic (write + notify)c0de0001-36e1-4688-b7f5-ea07361b26a8: register bridge to read and write ADC registers

Packet: 51 bytes, one sample per notification

BytesContent
[0]0xA0 start marker
[1]Sample counter (0–255, wraps)
[2..25]ADC 1, channels 1–8 (8 × 3 bytes, 24-bit big-endian two's complement)
[26..49]ADC 2, channels 9–16
[50]0xC0 end marker

Use the sample counter to spot lost packets.

Converting to microvolts

1.2 V reference, gain 4, 24-bit:

raw = (b[0] << 16) | (b[1] << 8) | b[2]   # 3 bytes of one channel
if raw & 0x800000:                       # negative value
    raw -= 1 << 24
uV = raw * (1.2 / 4.0 / 8388607) * 1e6   # about 0.0358 µV per count

Minimal Python reader

import asyncio
from bleak import BleakScanner, BleakClient
 
SERVICE = "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
DATA    = "beb5483e-36e1-4688-b7f5-ea07361b26a8"
SCALE   = 1.2 / 4.0 / 8388607 * 1e6
 
def on_data(_, p):
    if len(p) != 51 or p[0] != 0xA0 or p[50] != 0xC0:
        return
    uv = []
    for c in range(16):
        b = p[2 + 3*c: 5 + 3*c]
        raw = (b[0] << 16) | (b[1] << 8) | b[2]
        if raw & 0x800000:
            raw -= 1 << 24
        uv.append(raw * SCALE)
    print(p[1], [round(v, 1) for v in uv[:4]], "...")
 
async def main():
    dev = await BleakScanner.find_device_by_filter(
        lambda d, adv: SERVICE in [u.lower() for u in adv.service_uuids])
    async with BleakClient(dev) as client:
        await client.start_notify(DATA, on_data)
        await asyncio.sleep(10)
 
asyncio.run(main())

In a browser, the easiest path is the JavaScript SDK: eeg.connectBLE({ device: 'octopus-16' }).