Hardware
Devices
ardEEG
Specification

Specification

ardEEG shield

Hardware

Channels8 biosignal inputs
Extra inputsReference (shared by all channels) and bias
Analog front endTexas Instruments ADS1299
Resolution24-bit, delta-sigma, all channels sampled at the same moment
Sample rate250 SPS as set by the sketch (the ADS1299 supports 250 SPS to 16 kSPS)
Gain1 as set by the sketch (the ADS1299 supports 1, 2, 4, 6, 8, 12, 24)
Reference voltage4.5 V internal
Internal noiseAbout 0.5 µV RMS per channel (measured, Signals & Datasets)
SignalsEEG, EMG, ECG
ElectrodesDry or wet Ag/AgCl, 2.54 mm header
HostArduino UNO R4 WiFi
InterfaceSPI (D13, D12, D11, chip select D10) and DRDY on D7
Power5 V from a small power bank: up to 1 A, up to 1,000 mAh
IsolationNone. Battery operation is required.
LicenceSoftware and SDK: MIT

SPI settings

SettingValue
Clock600 kHz
Mode1 (CPOL 0, CPHA 1)
Bit orderMSB first
Chip selectD10

Data format

Each sample is 27 bytes, read from the ADS1299 when DRDY (D7) goes low:

BytesContent
1–3ADS1299 status word
4–6Channel 1
……
25–27Channel 8

Each channel is a 24-bit, big-endian, two's-complement value.

Over Wi-Fi, the sketch sends UDP packets of 1,350 bytes = 50 samples to your computer on port 13900. At 250 SPS that is 5 packets, or 6,750 bytes, per second.

Converting to microvolts

The conversion used by the PiEEG scripts:

raw = (b[0] << 16) | (b[1] << 8) | b[2]   # 3 bytes of one channel
if raw & 0x800000:                       # negative value
    raw -= 16777214
uV = raw * 4.5 / 16777215 * 1_000_000    # about 0.268 µV per count

Minimal Python receiver

import socket
 
UDP_IP, UDP_PORT = "192.168.1.241", 13900   # this computer's IP
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))
 
def to_uv(b):
    raw = (b[0] << 16) | (b[1] << 8) | b[2]
    if raw & 0x800000:
        raw -= 16777214
    return raw * 4.5 / 16777215 * 1e6
 
while True:
    packet, _ = sock.recvfrom(1350)
    for s in range(0, len(packet), 27):                  # 50 samples per packet
        sample = packet[s:s + 27]
        ch = [to_uv(sample[3 + 3*i: 6 + 3*i]) for i in range(8)]
        print([round(v, 1) for v in ch])

ADS1299 settings used by the sketch

Start-up: WAKEUP (0x02), STOP (0x0A), RESET (0x06), SDATAC (0x11), write the registers below, then RDATAC (0x10) and START (0x08).

AddressRegisterValueMeaning
0x01CONFIG10x96250 SPS
0x02CONFIG20xD4Internal test-signal settings (not used in normal mode)
0x03CONFIG30xFFInternal reference buffer on, internal bias reference, bias buffer on
0x04LOFF0x00Lead-off detection off
0x05–0x0CCH1SET–CH8SET0x00Normal electrode input, gain 1
0x0D–0x11BIAS_SENS, LOFF_SENS, LOFF_FLIP0x00
0x14GPIO0x80
0x15MISC10x20Reference electrode shared by all channels (SRB1)
0x17CONFIG40x00Continuous conversion

Write a register with three bytes: [0x40 | address, 0x00, value].

Noise self-test: set every CHnSET to 0x01 (input shorted) in the sketch. The signal should stay within a few µV. See Noise elimination.