The idea of building a toy car that can be controlled using brain signals came to me as a way to learn signal analysis. In a perfect world, I would have loved to do something like “A colony of neurons plays Doom”, but I had neither the required skills, nor the right equipment - building a whole wet-lab in my office was out of question.

An obvious choice would be to download some existing neural-recording dataset and do some analyses on it. However, the vision of spending hundreds of hours on a random dataset that I have no relation to was demotivating. Instead, I decided that the best approach would be to buy an electroencephalography (EEG) headset and learn signal analysis on EEG data instead of direct neural recording. Though the idea of creating a toy car was a fun end goal, the whole project started for one purpose - build a machine learning model that can read my mind.

Source of inspiration

One day I accidentally saw some streamer play Dark Souls hands-free using a consumer-grade EEG headset. It looked like it took a stack of skills I did not have and wanted, so I decided to build something similar.

Hardware

The headset from the stream was the Emotiv EPOC X (14 channels, research-grade, proprietary stack). For exploratory work it was too expensive, so I bought the cheapest hobby-grade option I could find instead - a NeuroSky MindWave Mobile 2. One dry electrode at Fp1, an ear-clip reference, raw output at 512 Hz. At that point I did not even know what I would do with it, so a single channel was enough to start.

EEG Theory

The number coming off the MindWave is a voltage in microvolts (μV), sampled 512 times a second from one electrode at Fp1 - left forehead - referenced against an ear clip. Scalp EEG is the summed electrical activity of large populations of neurons, smeared by the skull and scalp before it reaches the electrode.

MindWave Mobile 2

Getting the raw data off the device took the first real effort. No official Python SDK was published for it, so I pulled a community library off GitHub (pymindwave2) and used it to stream raw voltage into Python. Everything downstream depends on that stream, so it had to work before anything else did.

The Plan

The idea was simple. The target stack, as I envisioned it: wear the headset, stream raw data to the PC over Python, run a real-time algorithm that classifies brain activity into a small set of states, and send each state as a command to an Arduino. The Arduino would be the automation layer that actually drives the car, with three commands wired in:

  1. forward
  2. left
  3. right

The classification target came from the literature. There is an established paradigm for exactly this, called motor imagery. The idea: if I imagine clenching my left hand without moving it, motor cortex produces a distinct, repeatable pattern of activity. Train a model to recognize that pattern, map it to “left”. Imagine the right hand for “right”, imagine both hands at once for “forward”. Three imagined movements, three commands, and the car drives on intent alone. That was the dream version.

A frontal electrode for a motor task

I knew Fp1 is a frontal site, not over the motor cortex, which is the obvious place to read motor imagery from. However, I found some literature suggesting motor-related activity can still be picked up from frontal regions, so I decided to go ahead with that assumption.

First Implementation

Before touching motor imagery, I wanted the whole loop working end to end with a signal I could not miss. The strongest, most obvious signal in a forehead recording is not EEG at all - it is the electromyography (EMG) burst - an artifact from clenching your jaw.

The classifier was deliberately trivial. Take the last 50 raw samples (about 100 ms at 512 Hz), take the maximum, compare it to a fixed 300 μV threshold. Above it, send “forward”; below it - don’t send anything. A jaw clench artifact has an order of magnitude higher voltage compared to the resting baseline, so a simple threshold-based activation was enough.

Then, I bought a cheap RC car from a toy store, disassembled the controller and connected my own input source via Arduino. At the time it was a single pin that, when pulled active, made the car go forward. While the logic was easy, this setup provided me a platform where I could potentially experiment with more complex signals. At that time, it was enough - I clenched my jaw and the car drove forward.

RC car, disassembled

Controller, hijacked

Limitations

Unfortunately, this implementation was not really a brain-to-car loop, it was a jaw muscle-to-car loop. While it worked well, this kind of signal was able to give me only two possible states: go vs no-go. From a neuroscience perspective, no brain signal analysis was done.

Next part: Trying to collect some real brain data and testing hypotheses