Investigating brainwaves using a low-cost Brain-Computer Interface (and some Python programming)

Investigation of neurological activity has possible applications in preventing neurological diseases along with management of neurological conditions. Recent advancement of brain-computer interfaces has allowed experimentation by a larger number of people outside research organisations.

Investigation of neurological activity has possible applications in preventing neurological diseases such as Alzheimer's (a disease that affects approximately 1.8% of the population), along with the management of other neurological conditions such as epilepsy (a condition that affects 1% of the population).


Electroencephalography is a technique used to infer the aggregate neural activity that occurs within the brain. The resultant electrical 'signature' of brain activity acquired is usually a complex waveform that varies highly across time (Figure 1). This signature acquired is part of a more complex device that incorporates signal processing (along with other technology) that can be classified as brain-computer interfaces.

Figure 1: EEG translation of neuro-activity showing highly-dynamical electrical values (the sensor is placed at a single location).

Brain-computer interfaces

The recent advances in manufacturing (leading to much lower cost) has allowed more access to a wider range of people (that once remained solely the property of research organisations). One example of such a BCI device is made by OpenBCI [1], which can capture multiple sensor locations simultaneously (Figure 2).

Figure 2: a brain-computer interface developed by OpenBCI. The EEG sensors (held on the white structure) are plugged into the BCI (the blue-coloured 'board'). 

Signal Processing on BCIs

Turning the raw signal from the sensor into data that can be represented on a computer screen requires additional signal processing. Namely, after the sensors acquire the signal amplifiers increase the EEG signal, which is then digitalised by A/D converters while maintaining accuracy. More details can be found by consulting learning material developed by NeuroTechEDU [2].

Signal acquisition

Getting the signal from a BCI usually requires custom software written for that particular platform. However, due to the 'open-source' nature of OpenBCI, they have attracted independent software developers to contribute to their endeavours.

BrainFlow [3] is a package that is aimed at encouraging BCI device-agnostic applications using their code (developers can switch BCI devices with minimal code refactoring, while rewriting at a higher level of abstraction).

BrainFlow has signal acquisition of OpenBCI devices available. This can be used to get data from OpenBCI's boards, which can be saved to your hard drive for later analysis. Below is an example of such code, which retrieves data from a Ganglion BCI and then saves the data in JSON format.

import argparse
import time
import numpy as np

import brainflow
from brainflow.board_shim import BoardShim, BoardIds, 
from brainflow.data_filter import DataFilter

import matplotlib.pyplot as plt
from datetime import datetime
import json

# display development messages

# CONSTANTS: modify each recording
note = "Notes of experiment."
channel_name = 'Fp1'

# setup for my board
Id = BoardIds.GANGLION_BOARD.value
sfreq = BoardShim.get_sampling_rate(Id)
params = BrainFlowInputParams()
params.serial_port = 'COM3'
board = BoardShim(Id, params)

# get data from board
dataRaw = board.get_board_data()  

# clean-up

# write a JSON file
data = dataRaw[1] # Ganglion's EEG channels are [1: 4]
dateTime ="20%y-%m-%d_%H-%M")
fileName = 'data-eeg-ganglion_' + dateTime + '.json'
file = open(fileName, "w")
dataList = data.tolist()
JSON = {"note": note, channel_name: dataList}
json.dump(JSON, file)

print("data saved.")

Data pre-processing & analysis

Once the signal has been digitised into the computer, pre-processing the data is usually done to remove artefacts (including noise such as 'Mains electricity'). Within the Python ecosystem, the package MNE [4] can be used for these types of tasks.

MNE  advertises their package allows 'exploring, visualizing, and analyzing human neurophysiological data'[5], thus can also be used for analysis-type tasks that we may be interested in. An example of code using MNE to both remove artefacts, and also represent the data in the frequency domain, is shown below.

# general requirements
import json
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

# brainflow requirements
import brainflow
from brainflow.board_shim import BoardShim, BoardIds
from brainflow.data_filter import DataFilter

# MNE requirements
import os
import numpy as np
import mne

# get JSON data
fileName = 'data-eeg-ganglion_2022-02-22_15-18.json'
Id = BoardIds.GANGLION_BOARD.value
file = open(fileName, "r")
JSON = json.load(file)

# convert data into MNE data structure
data = JSON['Fp1']
dataMNE = [] 
for dataI in data: # BrainFlow returns uV, convert to V for MNE
    dataINorm = dataI / 1000000
dataMNE = np.array([dataMNE]) # required MNE structure
ch_names = ['Fp1']
sfreq = BoardShim.get_sampling_rate(Id)
info = mne.create_info(ch_names=ch_names, sfreq=sfreq)
raw =, info)

# plot frequency representation of the brain's time-series data. 
# PSD allows comparison across differing time-series duration, where 
# an FFT would have dependency on this duration (and thus show 
# different results for the same signal but of different time 
# duration).
from mne.time_frequency import psd_welch
import scipy.signal as signal

# remove mains electricity's frequency artifact (noise) from signal
rawNotched = raw.copy().notch_filter(freqs=50, picks='Fp1', 

psdMNE, freqMNE = psd_welch(rawNotched, picks='Fp1', 
							fmin=1, fmax=100.0, n_fft=300, 
# Convert power to dB scale
psdMNEDb = 10 * np.log10(psdMNE)

data = rawNotched.get_data()
data = data[0]
freqScipy, psdScipy = signal.welch(data, fs=200, 
psdScipyDb = 10 * np.log10(psdScipy)

fig, ax = plt.subplots(figsize=(12, 8))
ax.plot(freqMNE, psdMNEDb[0], label='MNE')
ax.set_xlim(1.0, 95)
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('Power [dB]')


The combination of low-cost brain-computer interfaces in combination with the low-complexity of Python syntax allows software developers - and perhaps even non-developers - access to real-time neurological data.

Such access could allow greater scientific exploration and also perhaps encourage greater artistic representation. Both 'categories' of endeavour we deem to enrich the world, both our understanding of it and also our expression within it.


[1] OpenBCI creates open-source tools for biosensing and neuroscience. OpenBCI’s mission is to lower the barrier to entry for brain-computer interfacing, while ensuring that these technologies are adopted into the consumer landscape in an ethical way that protects user agency and mental health. Read more at

[2] NeuroTechX brings hackers, enthusiasts, researchers and experts together to drive innovation and foster collaboration at local and international scales. Our core mission is to build a strong global neurotech community by providing key resources, learning opportunities, and by being leaders in local and worldwide technological initiatives. Read more at

[3] BrainFlow is a library intended to obtain, parse and analyze EEG, EMG, ECG and other kinds of data from biosensors. Read more at

[4] MNE is an open-source Python package for exploring, visualizing, and analyzing human neurophysiological data: MEG, EEG, sEEG, ECoG, NIRS, and more. Read more at