NeuroFocus Mark 3 firmware
Modular Arduino / PlatformIO firmware for:
Board
Netlist
Role in firmware
Main
nf-mark3-main.tel
Full: ADS1220 + LEDs + button + battery
AFE
nf-mark3-afe.tel
OUT0 path (preset p0)
Expansion
nf-mark3-expansion.tel
Same SPI master code, external strip
firmware/
├── platformio.ini # seeed_xiao_esp32s3 + native tests
├── include/nf/ # public headers (documented)
│ ├── AppConfig.h # defaults & why they exist
│ ├── BoardPins.h # pin ↔ netlist map
│ ├── ChannelMap.h # OUT* → ADS1220 mux plans
│ ├── Voltage.h # raw → V / µV / battery
│ ├── StreamFormat.h # plotter / CSV / JSON
│ ├── CommandParser.h # CLI parse (unit-tested)
│ ├── Ads1220Driver.h # SPI ADC
│ ├── StreamEngine.h # capture + print
│ ├── CommandShell.h # serial shell
│ ├── StatusLeds.h
│ ├── UserButton.h
│ └── BatterySense.h
├── src/main.cpp # wiring only
└── test/ # pio test -e native
├── test_channel_map/
├── test_commands/
├── test_voltage/
└── test_stream_format/
cd firmware
pio run -t upload # flash XIAO
pio device monitor -b 115200
pio test -e native # host unit tests (no board)
Flash and open serial @ 115200
Confirm ADS1220 OK
Press b (or SW2)
Open Serial Plotter — one raw count per line
s to stop
Input
Action
Why it exists
b / s / v
Start / stop / soft-reset
OpenBCI-compatible hosts
p0
OUT0 vs AVSS
Default AFE / single-ended demo
p1
OUT0 vs OUT3
Differential with ref on AIN3
p2
Scan 4× SE
Exercise all H8/H9 nets
p3
Scan ref-to-OUT3
Legacy multi-channel montage
p4
OUT0 vs OUT1
Tight differential pair
f0 f1 f2 f3
raw / csv / µV / json
Plotter vs logging vs science
r0–r6
ADS1220 data-rate level
Speed vs noise trade
g1…g128
PGA gain
Only if you disable bypass
pga0 / pga1
Bypass PGA on/off
AFE already gains — default bypass
bat
Pack voltage
LiPo safety / logging
temp
ADS1220 die °C
Sanity / thermal
test
AVDD monitor self-test
SPI bring-up
blink
LED check
Carrier assembly
i / status
Dump config
Debug without recompile
? / help
This list
—
SW2 toggles stream without a keyboard.
Config variables (AppConfig.h)
Symbol
Default
Why
kVrefVolts
3.3
REFP0≈VCC on carrier
kBypassPgaDefault
true
AD8422 is the gain stage
kDefaultDataRateLevel
2
~90 SPS, plotter-friendly
kDefaultPreset
OUT0_SE
Reliable single-channel demo
kDefaultFormat
RawLine
Serial Plotter
kAfeGainDefault
1.0
Report ADC-pin µV until Rg known
kDrdyTimeoutMs
50
Never hang CLI on dead ADC
kBatRTop/Bottom
220k/220k
Match R3/R4 netlist
Bench OUT0 — continuous ADC, plotter
4-ch scan — p2/p3 + f1 CSV to a host logger
Differential pair — p4 or p1
Science units — f2 with real afeGain
Structured log — f3 JSON lines
Hands-free — SW2 start/stop
Field power — bat before long capture
Expansion strip — same SPI, external board
Self-test — test after assembly
Rate/gain sweep — r* / g* without reflash
Open OUT0 may sit mid/full-scale — connect AFE or a known voltage for a real wave.
Single-ended p0 uses AIN0–AVSS ; true bipolar EEG montages prefer p1/p3.
Expansion has no battery net / button — those APIs no-op or float if pins unconnected.