The first open-source BLE protocol implementation for Muse S athena headsets
(note, current implementation is not yet complete, data is still scrambled and I'm still reverse engineering this)
Finally! Direct BLE connection to Muse S without proprietary SDKs. We're quite amused that we cracked the protocol nobody else has published online!
We reverse-engineered the BLE communication from scratch to provide researchers with full control over their Muse S devices.
Key breakthrough: The dc001 command must be sent TWICE to start streaming - a critical detail not in any documentation!
- EEG Streaming: 7 channels at 256 Hz (TP9, AF7, AF8, TP10, FPz, AUX_R, AUX_L)
- PPG Heart Rate: Real-time HR and HRV from photoplethysmography sensors
- IMU Motion: 9-axis accelerometer + gyroscope
- Binary Recording: 10x more efficient than CSV with replay capability
- Real-time Visualization: Multiple visualization options including band powers
- No SDK Required: Pure Python with BLE - no proprietary libraries!
pip install amusedOr from source:
git clone https://github.com/nexon33/amused.git
cd amused
pip install -e .# For PyQtGraph visualizations
pip install pyqtgraph PyQt5
# For all visualization features
pip install -r requirements-viz.txtimport asyncio
from muse_stream_client import MuseStreamClient
from muse_discovery import find_muse_devices
async def stream():
# Find Muse devices
devices = await find_muse_devices()
if not devices:
print("No Muse device found!")
return
device = devices[0]
print(f"Found: {device.name}")
# Create streaming client
client = MuseStreamClient(
save_raw=True, # Save to binary file
decode_realtime=True # Decode in real-time
)
# Stream for 30 seconds
await client.connect_and_stream(
device.address,
duration_seconds=30,
preset='p1035' # Full sensor mode
)
summary = client.get_summary()
print(f"Collected {summary['packets_received']} packets")
asyncio.run(stream())The main streaming client for real-time data collection:
- Connects to Muse S via BLE
- Streams all sensor data (EEG, PPG, IMU)
- Optional binary recording
- Real-time callbacks for data processing
Binary data storage and retrieval:
- Efficient binary format (10x smaller than CSV)
- Fast read/write operations
- Packet-level access with timestamps
Real-time packet decoding:
- Decodes BLE packets on-the-fly
- Extracts EEG, PPG, IMU data
- Calculates heart rate from PPG
- Minimal latency
Replay recorded sessions:
- Play back binary recordings
- Variable speed playback
- Same callback interface as live streaming
# See examples/01_basic_streaming.py
from muse_stream_client import MuseStreamClient
from muse_discovery import find_muse_devices
client = MuseStreamClient(
save_raw=False, # Don't save, just stream
decode_realtime=True,
verbose=True
)
devices = await find_muse_devices()
if devices:
await client.connect_and_stream(
devices[0].address,
duration_seconds=30,
preset='p1035'
)# See examples/02_full_sensors.py
client = MuseStreamClient(
save_raw=True, # Enable binary saving
data_dir="muse_data"
)
# Records all sensors to binary file
await client.connect_and_stream(
device.address,
duration_seconds=60,
preset='p1035'
)# See examples/03_parse_data.py
from muse_raw_stream import MuseRawStream
from muse_realtime_decoder import MuseRealtimeDecoder
stream = MuseRawStream("muse_data/recording.bin")
stream.open_read()
decoder = MuseRealtimeDecoder()
for packet in stream.read_packets():
decoded = decoder.decode(packet.data, packet.timestamp)
if decoded.eeg:
print(f"EEG data: {decoded.eeg}")
if decoded.heart_rate:
print(f"Heart rate: {decoded.heart_rate:.0f} BPM")# See examples/04_stream_with_callbacks.py
def process_eeg(data):
channels = data['channels']
# Process EEG data in real-time
print(f"Got EEG from {len(channels)} channels")
def process_heart_rate(hr):
print(f"Heart Rate: {hr:.0f} BPM")
client = MuseStreamClient()
client.on_eeg(process_eeg)
client.on_heart_rate(process_heart_rate)
await client.connect_and_stream(device.address)# See examples/07_lsl_style_viz.py
# Shows Delta, Theta, Alpha, Beta, Gamma bands
# Stable bar graphs without jumpy waveforms# See examples/09_frequency_display.py
# Just shows dominant frequency (Hz) for each channel
# Clean, large numbers - no graphs# See examples/06_heart_monitor.py
# Dedicated heart rate display with zones
# Shows current BPM, trend, and historyThe Muse S uses Bluetooth Low Energy (BLE) with a custom protocol:
- Connect to device
- Enable notifications on control characteristic
- Send halt command (
0x02680a) - Set preset (
p1035for full sensors) - Enable sensor notifications
- Send start command (
dc001) TWICE - Stream data
p21: Basic EEG onlyp1034: Sleep mode preset 1p1035: Full sensor mode (recommended)
0xDF: EEG + PPG combined0xF4: IMU (accelerometer + gyroscope)0xDB,0xD9: Mixed sensor data
- Ensure
dc001is sent twice (critical!) - Check Bluetooth is enabled
- Make sure Muse S is in pairing mode
- Try preset
p1035for full sensor access
- Heart rate requires ~2 seconds of PPG data
- Check PPG sensor contact with skin
- Use preset
p1035which enables PPG
- Install PyQt5:
pip install PyQt5 pyqtgraph - On Windows, the library handles Qt/asyncio conflicts automatically
- Try examples 06 or 09 for simpler visualizations
The examples/ folder contains working examples:
01_basic_streaming.py- Simple EEG streaming02_full_sensors.py- Record all sensors to binary03_parse_data.py- Parse binary recordings04_stream_with_callbacks.py- Real-time processing05_save_and_replay.py- Record and replay sessions06_heart_monitor.py- Clean heart rate display07_lsl_style_viz.py- LSL-style band power visualization09_frequency_display.py- Simple Hz display for each channel
This is the first open implementation! Areas to explore:
- Additional sensor modes
- Machine learning pipelines
- Mobile apps
- Advanced signal processing
MIT License - see LICENSE file
If you use Amused in research:
@software{amused2025,
title = {Amused: A Muse S Direct BLE Implementation},
author = {Adrian Tadeusz Belmans},
year = {2025},
url = {https://github.com/nexon33/amused}
}
Note: Research software for educational purposes. Probably not for medical use.