Reverse Engineering the EMOTIV EPOC+: Building a Custom Data Streamer
Learn how to bypass EMOTIV's SDK restrictions and build your own data streamer for the EPOC+ headset. We'll explore the device's security architecture and create a Python-based solution for direct data access.
Breaking Free from SDK Limitations
The EMOTIV EPOC+ is a powerful consumer-grade EEG headset, but its proprietary SDK and licensing restrictions can limit research and development. This guide will show you how to build a custom data streamer that bypasses these limitations, accessing your device's raw data directly.
Warning: This guide is for educational purposes only. Ensure you comply with all applicable laws and terms of service when working with EEG devices.
Understanding EEG Data
Before diving into the technical implementation, it's important to understand what we're capturing. The EPOC+ records electrical activity across different frequency bands:
- Delta (0.5-4 Hz): Deep sleep, unconscious processes
- Theta (4-8 Hz): Drowsiness, meditation
- Alpha (8-13 Hz): Relaxed awareness, closed eyes
- Beta (13-30 Hz): Active thinking, focus
- Gamma (30+ Hz): Complex problem solving, high-level processing
The EPOC+ samples at 128 Hz across 14 channels, providing comprehensive coverage of these frequency bands.
What You'll Need:
- EMOTIV EPOC+ headset
- Python 3.7+
hidapi
librarypycryptodome
library- Basic understanding of USB HID devices
Setting Up Your Development Environment
Before we start coding, let's set up our environment:
# Create a virtual environment
python -m venv emotiv_env
source emotiv_env/bin/activate # On Windows: emotiv_env\Scripts\activate
# Install required packages
pip install hidapi pycryptodome numpy pandas matplotlib
Understanding the EPOC+'s Security Architecture
The EPOC+ uses a simple encryption scheme to protect its data stream. Each device has a unique AES key derived from its serial number. While this might seem secure, the key generation algorithm is predictable and can be replicated.
The Key Generation Process
def generate_aes_key(serial_number, model):
if not serial_number or len(serial_number) < 4:
raise ValueError("Invalid serial number length.")
k = []
if model == 2: # Epoc::Standard
k = [serial_number[-1], '\0', serial_number[-2], 'T',
serial_number[-3], '\x10', serial_number[-4], 'B',
serial_number[-1], '\0', serial_number[-2], 'H',
serial_number[-3], '\0', serial_number[-4], 'P']
else:
k = [serial_number[-1], serial_number[-2], serial_number[-3],
serial_number[-4], 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L']
key = ''.join(k)
return key.ljust(16, '\0')[:16]
Security Note: This predictable key generation makes the device vulnerable to unauthorized access. Anyone with physical access to the device can read its serial number and generate the encryption key.
Building the Data Streamer
Our streamer consists of three main components:
- Device connection and identification
- Data decryption
- Real-time processing and storage
1. Connecting to the Device
The EPOC+ appears as a USB HID device with vendor ID 4660. We use the hidapi
library to establish a connection:
def connect_to_device():
print("Searching for Emotiv devices...")
for device in hid.enumerate():
if device['vendor_id'] == EMOTIV_VENDOR_ID:
serial_number = device['serial_number']
print(f"Found device with serial number: {serial_number}")
key = generate_aes_key(serial_number, MODEL)
return device['vendor_id'], device['product_id'], key
raise RuntimeError("No Emotiv device found.")
2. Decrypting the Data Stream
The EPOC+ sends encrypted 32-byte packets. We use AES in ECB mode to decrypt them:
cipher = AES.new(key.encode(), AES.MODE_ECB)
raw_data = hid_device.read(32)
decrypted_data = cipher.decrypt(bytes(raw_data))
Security Insight: The use of ECB mode is a significant weakness, as it doesn't provide proper cryptographic security for streaming data.
3. Processing and Storing Data
Once decrypted, we process the data into channel readings and save them to a CSV file:
def process_data(decrypted_data):
channels = [int.from_bytes(decrypted_data[i:i+2], 'big')
for i in range(0, len(decrypted_data), 2)]
return channels
def save_data_to_csv(data, filename="eeg_data.csv"):
with open(filename, 'a', newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(data)
Visualizing the Data Stream
To make sense of the captured data, we can add real-time visualization:
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
class EEGVisualizer:
def __init__(self):
self.fig, self.ax = plt.subplots()
self.lines = [self.ax.plot([], [])[0] for _ in range(14)]
self.data_buffer = [[] for _ in range(14)]
self.max_points = 500
def update(self, frame, new_data):
for i, line in enumerate(self.lines):
self.data_buffer[i].append(new_data[i])
if len(self.data_buffer[i]) > self.max_points:
self.data_buffer[i].pop(0)
line.set_data(range(len(self.data_buffer[i])), self.data_buffer[i])
self.ax.relim()
self.ax.autoscale_view()
return self.lines
def start(self):
plt.show()
Add this to your main loop:
visualizer = EEGVisualizer()
ani = FuncAnimation(visualizer.fig, visualizer.update, fargs=(channels,))
visualizer.start()
Enhanced Real-Time Visualization
While the basic visualizer works, we can create a more sophisticated solution that better handles continuous data streaming and provides a more professional look. Here's an enhanced version:
class RealtimeEEGVisualizer:
def __init__(self, buffer_size=1000, num_channels=14):
self.buffer_size = buffer_size
self.num_channels = num_channels
self.data_buffers = [deque(maxlen=buffer_size) for _ in range(num_channels)]
# EEG channel names for Emotiv EPOC+
self.channel_names = ['AF3', 'F7', 'F3', 'FC5', 'T7', 'P7', 'O1', 'O2',
'P8', 'T8', 'FC6', 'F4', 'F8', 'AF4']
# Initialize the plot with dark theme
plt.style.use('dark_background')
self.fig, self.ax = plt.subplots(figsize=(15, 10))
# Customize visualization parameters
self.display_window = 500 # Show 500 samples at a time
self.offset_factor = 50 # Offset between channels
self.scale_factor = 0.1 # Scale factor for signal amplitude
# Create color-coded channels
colors = plt.cm.viridis(np.linspace(0, 1, num_channels))
self.lines = [self.ax.plot([], [],
label=self.channel_names[i],
color=colors[i],
linewidth=0.8)[0]
for i in range(num_channels)]
# Customize the plot appearance
plt.xlabel("Sample Index", fontsize=12, color='white')
plt.ylabel("EEG Signal (µV)", fontsize=12, color='white')
plt.title("Real-time EEG Signals", fontsize=14, pad=20, color='white')
plt.legend(loc="center left", bbox_to_anchor=(1, 0.5))
plt.grid(True, alpha=0.3)
This enhanced visualizer includes several improvements:
- Proper Channel Labeling: Uses actual EPOC+ channel names (AF3, F7, etc.)
- Professional Styling: Dark theme with grid and proper scaling
- Buffer Management: Uses deque for efficient data storage
- Signal Scaling: Converts raw values to microvolts (µV)
- Channel Separation: Adds vertical offset between channels for clarity
Note: The complete implementation includes thread management and cleanup functions. You can find the full code in our GitHub repository.
Running the Complete System
Here's how to use the streamer:
- Install required packages:
pip install hidapi pycryptodome
- Connect your EPOC+ headset and run the script:
python emotiv_server.py
- The script will automatically:
- Find your EPOC+ device
- Generate the decryption key
- Start streaming and saving data
- Continue until you press 'q' and Enter
Security Implications
This implementation reveals several security concerns with the EPOC+:
- Predictable Encryption Keys: Keys are derived directly from serial numbers
- Weak Encryption Mode: ECB mode doesn't provide adequate security for streaming data
- No Authentication: The device doesn't verify who's accessing it
- Physical Access Risks: Anyone with the device can extract its serial number
Ethical Consideration: While these vulnerabilities make the device easier to work with for research, they also pose privacy risks for users.
Practical Applications
With direct access to the EPOC+'s data stream, you can build various applications:
- Real-time Focus Monitoring: Track beta wave activity to measure attention levels
- Meditation Assistant: Monitor alpha waves for meditation depth
- Sleep Analysis: Track delta and theta waves during rest
- Brain-Computer Interface: Use machine learning to classify thought patterns
Next Steps
Now that you have direct access to your EPOC+'s data stream, consider:
- Adding Filters: Implement bandpass filters to isolate specific frequency bands
- Machine Learning: Train models to recognize mental states
- Real-time Analysis: Build a web interface for live data visualization
- Data Logging: Create a database to store and analyze historical data
For advanced signal processing techniques, check out our guide on Advanced EEG Analysis with Python.
Conclusion
Building a custom data streamer for the EMOTIV EPOC+ is surprisingly straightforward once you understand its security architecture. While this opens up possibilities for research and development, it also highlights important security considerations in consumer EEG devices.
For those interested in more secure BCI implementations, consider checking out our guide on building your own BCI with open-source hardware.