muse-lsl
muse-lsl copied to clipboard
Muse S?
I'm thinking of getting a Muse S. Has anyone played with getting it to work with this yet?
Not me, but I'm curious to know if it works.
I have some experience reversing BLE stuff, and a lot of experience with BLE as a developer, so I'm pretty excited to see what's what. I just ordered one, but they're not shipping for a while...
On Sat, Feb 15, 2020 at 9:35 PM alexandre barachant < [email protected]> wrote:
Not me, but I'm curious to know if it works.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/alexandrebarachant/muse-lsl/issues/120?email_source=notifications&email_token=AAAIWYK6QKDOPGGFGINOEYLRDCYBJA5CNFSM4KV6VR3KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEL34YNA#issuecomment-586665012, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAIWYMRH7GH3LFN264ZCODRDCYBJANCNFSM4KV6VR3A .
Got one yesterday, working exactly as the Muse 2 from a bluepy perspective.
@RemyRamdam Fantastique, pioneer! Thanks for the information. Would you check how many EEG channels in Muse-S? Same four like the other bands?
Hello @iPsych,
As far as I can tell, they are the same
@Remy17 @RemyRamdam - just to confirm - were you able to steam all channels (accelerometer, gryo, PPG, EEG, telemetry, etc) without any modification of muse-lsl?
Sorry, I didn't notice that I had two accounts :D Actually, I use my own code, 99% inspired by MuseLSL but retailored to serve my purposes. This should totally work the same with Muse LSL.
What I obtained so far is this output for the LSL streams attached to the BLE data :
('Number of streams = ', 5)
<?xml version="1.0"?>
<info>
<name>anonymous</name>
<type>RAW_PPG</type>
<channel_count>3</channel_count>
<nominal_srate>64</nominal_srate>
<channel_format>float32</channel_format>
<source_id>00:55:DA:B9:09:99_PPG</source_id>
<version>1.1000000000000001</version>
<created_at>186968.33128078</created_at>
<uid>13fdd4ea-7337-45f5-9044-d6ccf96817e2</uid>
<session_id>default</session_id>
<hostname>laptop</hostname>
<v4address />
<v4data_port>16574</v4data_port>
<v4service_port>16574</v4service_port>
<v6address />
<v6data_port>16575</v6data_port>
<v6service_port>16575</v6service_port>
<desc>
<manufacturer>Muse</manufacturer>
<channels>
<channel>
<label>AMBIANT</label>
<unit>mmHg</unit>
<type>RAW_PPG</type>
</channel>
<channel>
<label>INFRARED</label>
<unit>mmHg</unit>
<type>RAW_PPG</type>
</channel>
<channel>
<label>RED</label>
<unit>mmHg</unit>
<type>RAW_PPG</type>
</channel>
</channels>
</desc>
</info>
<?xml version="1.0"?>
<info>
<name>anonymous</name>
<type>RAW_GYRO</type>
<channel_count>3</channel_count>
<nominal_srate>52</nominal_srate>
<channel_format>float32</channel_format>
<source_id>00:55:DA:B9:09:99_GYRO</source_id>
<version>1.1000000000000001</version>
<created_at>186968.53343392801</created_at>
<uid>432b0469-9c94-413b-9466-5ee4eafece50</uid>
<session_id>default</session_id>
<hostname>laptop</hostname>
<v4address />
<v4data_port>16576</v4data_port>
<v4service_port>16576</v4service_port>
<v6address />
<v6data_port>16577</v6data_port>
<v6service_port>16577</v6service_port>
<desc>
<manufacturer>Muse</manufacturer>
<channels>
<channel>
<label>X</label>
<unit>dps</unit>
<type>RAW_GYRO</type>
</channel>
<channel>
<label>Y</label>
<unit>dps</unit>
<type>RAW_GYRO</type>
</channel>
<channel>
<label>Z</label>
<unit>dps</unit>
<type>RAW_GYRO</type>
</channel>
</channels>
</desc>
</info>
<?xml version="1.0"?>
<info>
<name>anonymous</name>
<type>RAW_ACC</type>
<channel_count>3</channel_count>
<nominal_srate>52</nominal_srate>
<channel_format>float32</channel_format>
<source_id>00:55:DA:B9:09:99_ACCELERO</source_id>
<version>1.1000000000000001</version>
<created_at>186968.73676574699</created_at>
<uid>445e48c2-4547-4ec8-9c51-62b6d9b4a0bd</uid>
<session_id>default</session_id>
<hostname>laptop</hostname>
<v4address />
<v4data_port>16578</v4data_port>
<v4service_port>16578</v4service_port>
<v6address />
<v6data_port>16579</v6data_port>
<v6service_port>16579</v6service_port>
<desc>
<manufacturer>Muse</manufacturer>
<channels>
<channel>
<label>X</label>
<unit>g</unit>
<type>RAW_ACC</type>
</channel>
<channel>
<label>Y</label>
<unit>g</unit>
<type>RAW_ACC</type>
</channel>
<channel>
<label>Z</label>
<unit>g</unit>
<type>RAW_ACC</type>
</channel>
</channels>
</desc>
</info>
<?xml version="1.0"?>
<info>
<name>anonymous</name>
<type>RAW_EEG</type>
<channel_count>5</channel_count>
<nominal_srate>256</nominal_srate>
<channel_format>float32</channel_format>
<source_id>00:55:DA:B9:09:99_EEG</source_id>
<version>1.1000000000000001</version>
<created_at>186967.723528006</created_at>
<uid>5bd0b4ee-e634-4eb8-b4d9-adb794f7722a</uid>
<session_id>default</session_id>
<hostname>laptop</hostname>
<v4address />
<v4data_port>16572</v4data_port>
<v4service_port>16572</v4service_port>
<v6address />
<v6data_port>16573</v6data_port>
<v6service_port>16573</v6service_port>
<desc>
<manufacturer>Muse</manufacturer>
<channels>
<channel>
<label>TP9</label>
<unit>microvolts</unit>
<type>RAW_EEG</type>
</channel>
<channel>
<label>AF7</label>
<unit>microvolts</unit>
<type>RAW_EEG</type>
</channel>
<channel>
<label>AF8</label>
<unit>microvolts</unit>
<type>RAW_EEG</type>
</channel>
<channel>
<label>TP10</label>
<unit>microvolts</unit>
<type>RAW_EEG</type>
</channel>
</channels>
</desc>
</info>
<?xml version="1.0"?>
<info>
<name>anonymous</name>
<type>RAW_TELEMETRY</type>
<channel_count>4</channel_count>
<nominal_srate>0</nominal_srate>
<channel_format>float32</channel_format>
<source_id>00:55:DA:B9:09:99_TELEMETRY</source_id>
<version>1.1000000000000001</version>
<created_at>186968.94259854301</created_at>
<uid>74bdbd18-f08b-46f7-9fb0-bf39920cb49f</uid>
<session_id>default</session_id>
<hostname>laptop</hostname>
<v4address />
<v4data_port>16580</v4data_port>
<v4service_port>16580</v4service_port>
<v6address />
<v6data_port>16581</v6data_port>
<v6service_port>16581</v6service_port>
<desc>
<manufacturer>Muse</manufacturer>
<channels>
<channel>
<label>battery</label>
<unit>percentage</unit>
<type>RAW_TELEMETRY</type>
</channel>
<channel>
<label>fuel_gauge</label>
<unit>??</unit>
<type>RAW_TELEMETRY</type>
</channel>
<channel>
<label>adv_volt</label>
<unit>volt?</unit>
<type>RAW_TELEMETRY</type>
</channel>
<channel>
<label>temperature</label>
<unit>farenheit ?</unit>
<type>RAW_TELEMETRY</type>
</channel>
</channels>
</desc>
</info>
Also, I attached those files for you to reproduce the same things :
-
a video of the different channel streams (actually a gif)
-
a screenshot annoted to understand what you are looking in the video
The code I used :
# Main.py
from bluepy.btle import Peripheral, ADDR_TYPE_PUBLIC, AssignedNumbers, BTLEException
from pylsl import StreamInfo, StreamOutlet
import numpy as np
import time, struct, argparse, sys, datetime
import bitstring
from bitstring import BitArray
from constants import *
parser = argparse.ArgumentParser(description = 'Stream heart rate of bluetooth BLE compatible devices using LSL.')
parser.add_argument("-m", "--mac-address", help = "MAC address of the device.", default = "00:00:00:00:00:00", type = str)
parser.add_argument("-id", "--id", help = "id on the network, will be added to Muse_", default = "anonymous", type = str)
parser.add_argument("-e", "--eeg", default = False, action = 'store_true', help = "stream eeg data")
parser.add_argument("-p", "--ppg", default = False, action = 'store_true', help = "stream ppg data")
parser.add_argument("-g", "--gyro", default = False, action = 'store_true', help = "stream gyro data")
parser.add_argument("-a", "--accelero", default = False, action = 'store_true', help = "stream accelero data")
parser.add_argument("-t", "--telemetry", default = False, action = 'store_true', help = "stream telemetry data")
parser.add_argument("-v", "--verbose", action = 'store_true', help = "Print verbose information.")
parser.add_argument("-vv", "--ultraverbose", action = 'store_true', help = "Print more verbose information.")
parser.set_defaults(verbose=False)
parser.set_defaults(ultraverbose=False)
args = parser.parse_args()
eeg_info = []
eeg_outlet = []
ppg_info = []
ppg_outlet = []
gyro_info = []
gyro_outlet = []
accelero_info = []
accelero_outlet = []
telemetry_info = []
telemetry_outlet = []
data_eeg = np.zeros((MUSE_NB_EEG_CHANNELS, LSL_EEG_CHUNK))
data_ppg = np.zeros((MUSE_NB_PPG_CHANNELS, LSL_PPG_CHUNK))
data_gyro = np.zeros((MUSE_NB_GYRO_CHANNELS, LSL_GYRO_CHUNK))
data_accelero = np.zeros((MUSE_NB_ACC_CHANNELS, LSL_ACC_CHUNK))
data_telemetry = np.zeros((MUSE_NB_TELEMETRY_CHANNELS, LSL_TELEMETRY_CHUNK))
class Muse(Peripheral) :
def __init__(self, addr) :
if args.verbose :
print("connecting to device", addr)
Peripheral.__init__(self, addr, addrType = ADDR_TYPE_PUBLIC)
if args.verbose :
print("Connected to Muse", addr)
def print_data(cHandle, handle, data) :
aa = bitstring.Bits(bytes=data)
if handle == 0x001b-1 :
pattern = "uint:16,uint:16,uint:16,uint:16,uint:16"
elif handle == 0x0015-1 or handle == 0x0018-1 :
pattern = "uint:16,int:16,int:16,int:16,int:16,int:16,int:16,int:16,int:16,int:16"
elif handle == 0x0039-1 or handle == 0x003c-1 or handle == 0x003f-1 :
pattern = "uint:16,uint:24,uint:24,uint:24,uint:24,uint:24,uint:24"
else :
pattern = "uint:16,uint:12,uint:12,uint:12,uint:12,uint:12,uint:12,uint:12,uint:12,uint:12,uint:12,uint:12,uint:12"
res = aa.unpack(pattern)
data = res[1:]
if args.ultraverbose :
print("Handle = ", handle)
print("Data = ", data)
# EEG
if (handle == 0x0021-1) : # EEG TP9 #03
data = MUSE_EEG_SCALE_FACTOR * (np.array(data) - 2048)
data_eeg[2] = data
if (handle == 0x0024-1) : # EEG AF7 #04
data = MUSE_EEG_SCALE_FACTOR * (np.array(data) - 2048)
data_eeg[3] = data
timestamp = time.time()
timestamps = np.arange(timestamp - 1.0*LSL_EEG_CHUNK/MUSE_SAMPLING_EEG_RATE, timestamp, 1./MUSE_SAMPLING_EEG_RATE)
for ii in range(LSL_EEG_CHUNK) :
eeg_outlet.push_sample(data_eeg[:, ii], timestamps[ii])
if (handle == 0x0027-1) : # EEG AF8 #02
data = MUSE_EEG_SCALE_FACTOR * (np.array(data) - 2048)
data_eeg[1] = data
if (handle == 0x002a-1) : #EEG TP10 #01
data = MUSE_EEG_SCALE_FACTOR * (np.array(data) - 2048)
data_eeg[0] = data
# PPG
if (handle == 0x0039-1) : # PPG 1
data_ppg[0] = data
if (handle == 0x003c-1) : # PPG 2
data_ppg[1] = data
if (handle == 0x003f-1) : # PPG 3
data_ppg[2] = data
timestamp = time.time()
timestamps = np.arange(timestamp - 1.0*LSL_PPG_CHUNK/MUSE_SAMPLING_PPG_RATE, timestamp, 1./MUSE_SAMPLING_PPG_RATE)
for ii in range(LSL_PPG_CHUNK) :
ppg_outlet.push_sample(data_ppg[:, ii], timestamps[ii])
# GYRO
if (handle == 0x0015-1) : # GYRO
data_gyro = MUSE_GYRO_SCALE_FACTOR * np.array(data).reshape((3, 3), order='F')
timestamp = time.time()
timestamps = np.arange(timestamp - 1.0*LSL_GYRO_CHUNK/MUSE_SAMPLING_GYRO_RATE, timestamp, 1./MUSE_SAMPLING_GYRO_RATE)
for ii in range(LSL_GYRO_CHUNK) :
gyro_outlet.push_sample(data_gyro[:, ii], timestamps[ii])
# ACCELERO
if (handle == 0x0018-1) : # ACCELERO
data_accelero = MUSE_ACCELERO_SCALE_FACTOR * np.array(data).reshape((3, 3), order='F')
timestamp = time.time()
timestamps = np.arange(timestamp - 1.0*LSL_ACC_CHUNK/MUSE_SAMPLING_ACC_RATE, timestamp, 1./MUSE_SAMPLING_ACC_RATE)
for ii in range(LSL_ACC_CHUNK) :
accelero_outlet.push_sample(data_accelero[:, ii], timestamps[ii])
# TELEMETRY
if (handle == 0x001b-1) : # TELEMETRY >> battery, fuel_gauge, adc_volt, temperature
data_telemetry = [data[0]/512., data[1]*2.2, data[2], data[3]]
timestamp = time.time()
telemetry_outlet.push_sample(data_telemetry, timestamp)
print("Battery is ", data_telemetry[0])
if __name__=="__main__":
if args.ultraverbose :
args.verbose = True
muse = []
if (not args.eeg and not args.ppg and not args.gyro and not args.accelero and not args.telemetry) :
if args.verbose :
print("You did not subscribed to any data. Please choose at least one.")
sys.exit()
connected = False
while not connected :
try :
muse = Muse(args.mac_address)
except BTLEException :
if args.verbose:
print("Peripheral unavailable. Trying again in 2 seconds")
time.sleep(2)
else :
connected = True
if args.verbose :
print("Connected at ", datetime.datetime.now())
service, = [s for s in muse.getServices() if s.uuid == MUSE_GATT_CUSTOM_SERVICE]
ccc = service.getCharacteristics(forUUID=str(MUSE_GATT_ATTR_STREAM_TOGGLE))
ccc[0].write(S_ASK)
ccc[0].write(S_STREAM)
if args.eeg :
_handles_eeg = [0x0021, 0x0024, 0x0027, 0x002a]
for hnd in _handles_eeg :
muse.writeCharacteristic(hnd, b"\x01\x00")
if args.verbose :
print("subscribed to EEG handles", hnd)
time.sleep(.2)
eeg_info = StreamInfo('%s' % args.id, 'RAW_EEG', MUSE_NB_EEG_CHANNELS, MUSE_SAMPLING_EEG_RATE, 'float32', '%s_EEG' % args.mac_address)
eeg_info.desc().append_child_value("manufacturer", "Muse")
eeg_channels = eeg_info.desc().append_child("channels")
for c in ['TP9', 'AF7', 'AF8', 'TP10'] :
eeg_channels.append_child("channel").append_child_value("label", c).append_child_value("unit", "microvolts").append_child_value("type", "RAW_EEG")
eeg_outlet = StreamOutlet(eeg_info, LSL_EEG_CHUNK)
if args.verbose :
print("Created LSL outlet for Muse EEG")
if args.ppg :
_handles_ppg = [0x0039, 0x003c, 0x003f] # ambiant / infrared / red
for hnd in _handles_ppg :
muse.writeCharacteristic(hnd, b"\x01\x00")
if args.verbose :
print("subscribed to PPG handles", hnd)
time.sleep(.2)
ppg_info = StreamInfo('%s' % args.id, 'RAW_PPG', MUSE_NB_PPG_CHANNELS, MUSE_SAMPLING_PPG_RATE, 'float32', '%s_PPG' % args.mac_address)
ppg_info.desc().append_child_value("manufacturer", "Muse")
ppg_channels = ppg_info.desc().append_child("channels")
for c in ['AMBIANT', 'INFRARED', 'RED'] :
ppg_channels.append_child("channel").append_child_value("label", c).append_child_value("unit", "mmHg").append_child_value("type", "RAW_PPG")
ppg_outlet = StreamOutlet(ppg_info, LSL_PPG_CHUNK)
if args.verbose :
print("Created LSL outlet for Muse PPG")
if args.gyro :
_handles_gyro = [0x0015]
for hnd in _handles_gyro :
muse.writeCharacteristic(hnd, b"\x01\x00")
if args.verbose :
print("subscribed to GYRO handles", hnd)
time.sleep(.2)
gyro_info = StreamInfo('%s' % args.id, 'RAW_GYRO', MUSE_NB_GYRO_CHANNELS, MUSE_SAMPLING_GYRO_RATE, 'float32', '%s_GYRO' % args.mac_address)
gyro_info.desc().append_child_value("manufacturer", "Muse")
gyro_channels = gyro_info.desc().append_child("channels")
for c in ['X', 'Y', 'Z'] :
gyro_channels.append_child("channel").append_child_value("label", c).append_child_value("unit", "dps").append_child_value("type", "RAW_GYRO")
gyro_outlet = StreamOutlet(gyro_info, LSL_GYRO_CHUNK)
if args.verbose :
print("Created LSL outlet for Muse GYRO")
if args.accelero :
_handles_accelero = [0x0018]
for hnd in _handles_accelero :
muse.writeCharacteristic(hnd, b"\x01\x00")
if args.verbose :
print("subscribed to ACCELERO handles", hnd)
time.sleep(.2)
accelero_info = StreamInfo('%s' % args.id, 'RAW_ACC', MUSE_NB_ACC_CHANNELS, MUSE_SAMPLING_ACC_RATE, 'float32', '%s_ACCELERO' % args.mac_address)
accelero_info.desc().append_child_value("manufacturer", "Muse")
acc_channels = accelero_info.desc().append_child("channels")
for c in ['X', 'Y', 'Z'] :
acc_channels.append_child("channel").append_child_value("label", c).append_child_value("unit", "g").append_child_value("type", "RAW_ACC")
accelero_outlet = StreamOutlet(accelero_info, LSL_ACC_CHUNK)
if args.verbose :
print("Created LSL outlet for Muse ACC")
if args.telemetry :
_handles_telemetry = [0x001b]
for hnd in _handles_telemetry :
muse.writeCharacteristic(hnd, b"\x01\x00")
if args.verbose :
print("subscribed to TELEMETRY handles", hnd)
time.sleep(.2)
telemetry_info = StreamInfo('%s' % args.id, 'RAW_TELEMETRY', MUSE_NB_TELEMETRY_CHANNELS, MUSE_SAMPLING_TELEMETRY_RATE, 'float32', '%s_TELEMETRY' % args.mac_address)
telemetry_info.desc().append_child_value("manufacturer", "Muse")
tel_chans = telemetry_info.desc().append_child("channels")
tel_chans.append_child("channel").append_child_value("label", "battery").append_child_value("unit", "percentage").append_child_value("type", "RAW_TELEMETRY")
tel_chans.append_child("channel").append_child_value("label", "fuel_gauge").append_child_value("unit", "??").append_child_value("type", "RAW_TELEMETRY")
tel_chans.append_child("channel").append_child_value("label", "adv_volt").append_child_value("unit", "volt?").append_child_value("type", "RAW_TELEMETRY")
tel_chans.append_child("channel").append_child_value("label", "temperature").append_child_value("unit", "farenheit ?").append_child_value("type", "RAW_TELEMETRY")
telemetry_outlet = StreamOutlet(telemetry_info, LSL_TELEMETRY_CHUNK)
muse.delegate.handleNotification = muse.print_data
if args.verbose :
print("Launching infinite loop")
while connected :
try :
while True :
muse.waitForNotifications(1./MUSE_SAMPLING_EEG_RATE)
except KeyboardInterrupt :
if args.verbose :
print("Exited by KeyboardInterrupt event at ", datetime.datetime.now())
sys.exit()
except BTLEException :
if args.verbose :
print("BTLE exception at ", datetime.datetime.now(), ". Trying to reconnect.")
connected = False
except :
if args.verbose :
print("Don't know what happened at ", datetime.datetime.now())
print(sys.exc_info()[0])
connected = False
#constants.py
MUSE_GATT_CUSTOM_SERVICE = "0000fe8d-0000-1000-8000-00805f9b34fb"
MUSE_GATT_ATTR_STREAM_TOGGLE = '273e0001-4c4d-454d-96be-f03bac821358'
MUSE_GATT_ATTR_TP9 = '273e0003-4c4d-454d-96be-f03bac821358'
MUSE_GATT_ATTR_AF7 = '273e0004-4c4d-454d-96be-f03bac821358'
MUSE_GATT_ATTR_AF8 = '273e0005-4c4d-454d-96be-f03bac821358'
MUSE_GATT_ATTR_TP10 = '273e0006-4c4d-454d-96be-f03bac821358'
#MUSE_GATT_ATTR_RIGHTAUX = '273e0007-4c4d-454d-96be-f03bac821358' // 2c
#273e0008 ? 11
#273e0002 ? 1d
#273e000c ? 2f
#273e000d ? 32
#273e000e ? 35
#273e000f ? 38
#273e0010 ? 3b
#273e0011 ? 3e
#273e0012 ? 41
MUSE_GATT_ATTR_PPG1 = "273e000f-4c4d-454d-96be-f03bac821358"
MUSE_GATT_ATTR_PPG2 = "273e0010-4c4d-454d-96be-f03bac821358"
MUSE_GATT_ATTR_PPG3 = "273e0011-4c4d-454d-96be-f03bac821358"
MUSE_GATT_ATTR_GYRO = '273e0009-4c4d-454d-96be-f03bac821358'
MUSE_GATT_ATTR_ACCELEROMETER = '273e000a-4c4d-454d-96be-f03bac821358'
MUSE_GATT_ATTR_TELEMETRY = '273e000b-4c4d-454d-96be-f03bac821358'
S_ASK = b'\x02\x73\x0a'
S_STREAM = b'\x02\x64\x0a'
MUSE_EEG_SCALE_FACTOR = 0.48828125
MUSE_PPG_SCALE_FACTOR = 0.48828125
MUSE_ACCELERO_SCALE_FACTOR = 0.0000610352
MUSE_GYRO_SCALE_FACTOR = 0.0074768
MUSE_NB_EEG_CHANNELS = 5
MUSE_SAMPLING_EEG_RATE = 256
LSL_EEG_CHUNK = 12
MUSE_NB_PPG_CHANNELS = 3
MUSE_SAMPLING_PPG_RATE = 64
LSL_PPG_CHUNK = 6
MUSE_NB_ACC_CHANNELS = 3
MUSE_SAMPLING_ACC_RATE = 52
LSL_ACC_CHUNK = 1
MUSE_NB_GYRO_CHANNELS = 3
MUSE_SAMPLING_GYRO_RATE = 52
LSL_GYRO_CHUNK = 1
MUSE_NB_TELEMETRY_CHANNELS = 4
MUSE_SAMPLING_TELEMETRY_RATE = 0
LSL_TELEMETRY_CHUNK = 1
LSL_BUFFER = 360 # Buffer length.
And to read the streams :
from pylsl import ContinuousResolver, StreamInfo, StreamInlet
#from pylsl import LostError
import time, sys, argparse
parser = argparse.ArgumentParser(description='Two-ways connection to a remote tobecou device, using LSL for input / output.')
parser.add_argument("-t", "--type", help="LSL type of the stream", default="notype", type=str)
parser.add_argument("-v", "--verbose", action='store_true', help="Print more verbose information.")
parser.set_defaults(verbose=False)
args = parser.parse_args()
cr = ContinuousResolver(prop = "type", value = args.type, forget_after = 5)
streams = []
while (len(streams) == 0) :
streams = cr.results()
time.sleep(2)
print("looking for the first stream")
size_streams = len(streams)
print("initial number of streams = ", size_streams)
inlets = []
for s in streams :
inlets.append(StreamInlet(s))
while True :
streams = cr.results()
if len(streams) == 0 :
print("Lost every stream, exiting ...")
sys.exit()
if len(streams) != size_streams :
for inlet in inlets :
del inlet
size_streams = len(streams)
for s in streams :
inlets.append(StreamInlet(s))
for inlet in inlets :
sample, timestamp = inlet.pull_sample(timeout = [.1])
if sample != None :
last_timestamp = timestamp
print("Name : " + inlet.info().name() + ", type : " + inlet.info().type() + " at " + str(timestamp) + " >> ")
for s in sample :
print s,
print ""
Hope it helps ;)
Also, those are the outputs of gatttool :
gatttool -I -b 00:55:DA:B9:09:99 -t public
> primary
attr handle: 0x0001, end grp handle: 0x0004 uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x0005, end grp handle: 0x000b uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x0042 uuid: 0000fe8d-0000-1000-8000-00805f9b34fb
> char-desc 0x000c 0x0042
handle: 0x000c, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x000d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x000e, uuid: 273e0001-4c4d-454d-96be-f03bac821358
handle: 0x000f, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0010, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0011, uuid: 273e0008-4c4d-454d-96be-f03bac821358
handle: 0x0012, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0013, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0014, uuid: 273e0009-4c4d-454d-96be-f03bac821358
handle: 0x0015, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0016, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0017, uuid: 273e000a-4c4d-454d-96be-f03bac821358
handle: 0x0018, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0019, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x001a, uuid: 273e000b-4c4d-454d-96be-f03bac821358
handle: 0x001b, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x001c, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x001d, uuid: 273e0002-4c4d-454d-96be-f03bac821358
handle: 0x001e, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x001f, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0020, uuid: 273e0003-4c4d-454d-96be-f03bac821358
handle: 0x0021, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0022, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0023, uuid: 273e0004-4c4d-454d-96be-f03bac821358
handle: 0x0024, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0025, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0026, uuid: 273e0005-4c4d-454d-96be-f03bac821358
handle: 0x0027, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0028, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0029, uuid: 273e0006-4c4d-454d-96be-f03bac821358
handle: 0x002a, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x002b, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x002c, uuid: 273e0007-4c4d-454d-96be-f03bac821358
handle: 0x002d, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x002e, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x002f, uuid: 273e000c-4c4d-454d-96be-f03bac821358
handle: 0x0030, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0031, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0032, uuid: 273e000d-4c4d-454d-96be-f03bac821358
handle: 0x0033, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0034, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0035, uuid: 273e000e-4c4d-454d-96be-f03bac821358
handle: 0x0036, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0037, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0038, uuid: 273e000f-4c4d-454d-96be-f03bac821358
handle: 0x0039, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x003a, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x003b, uuid: 273e0010-4c4d-454d-96be-f03bac821358
handle: 0x003c, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x003d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x003e, uuid: 273e0011-4c4d-454d-96be-f03bac821358
handle: 0x003f, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0040, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0041, uuid: 273e0012-4c4d-454d-96be-f03bac821358
handle: 0x0042, uuid: 00002902-0000-1000-8000-00805f9b34fb
For the Muse2 they were
> primary
attr handle: 0x0001, end grp handle: 0x0004 uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x0005, end grp handle: 0x000b uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x003f uuid: 0000fe8d-0000-1000-8000-00805f9b34fb
> char-desc 0x000c 0x003f
handle: 0x000c, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x000d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x000e, uuid: 273e0001-4c4d-454d-96be-f03bac821358
handle: 0x000f, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0010, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0011, uuid: 273e0008-4c4d-454d-96be-f03bac821358
handle: 0x0012, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0013, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0014, uuid: 273e0009-4c4d-454d-96be-f03bac821358
handle: 0x0015, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0016, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0017, uuid: 273e000a-4c4d-454d-96be-f03bac821358
handle: 0x0018, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0019, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x001a, uuid: 273e000b-4c4d-454d-96be-f03bac821358
handle: 0x001b, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x001c, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x001d, uuid: 273e0002-4c4d-454d-96be-f03bac821358
handle: 0x001e, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x001f, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0020, uuid: 273e0003-4c4d-454d-96be-f03bac821358
handle: 0x0021, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0022, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0023, uuid: 273e0004-4c4d-454d-96be-f03bac821358
handle: 0x0024, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0025, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0026, uuid: 273e0005-4c4d-454d-96be-f03bac821358
handle: 0x0027, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0028, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0029, uuid: 273e0006-4c4d-454d-96be-f03bac821358
handle: 0x002a, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x002b, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x002c, uuid: 273e0007-4c4d-454d-96be-f03bac821358
handle: 0x002d, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x002e, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x002f, uuid: 273e000c-4c4d-454d-96be-f03bac821358
handle: 0x0030, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0031, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0032, uuid: 273e000d-4c4d-454d-96be-f03bac821358
handle: 0x0033, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0034, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0035, uuid: 273e000e-4c4d-454d-96be-f03bac821358
handle: 0x0036, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0037, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0038, uuid: 273e000f-4c4d-454d-96be-f03bac821358
handle: 0x0039, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x003a, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x003b, uuid: 273e0010-4c4d-454d-96be-f03bac821358
handle: 0x003c, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x003d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x003e, uuid: 273e0011-4c4d-454d-96be-f03bac821358
handle: 0x003f, uuid: 00002902-0000-1000-8000-00805f9b34fb
So MuseS - Muse2 is only
handle: 0x0040, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0041, uuid: 273e0012-4c4d-454d-96be-f03bac821358
handle: 0x0042, uuid: 00002902-0000-1000-8000-00805f9b34fb
But saying that, apart from those I use in my code, I don't know what others characteristics are for. When I tried to read the unknown characteristic, I had data coming from 0x0012, 0x0039, 0x003c, 0x003f (both Muse2 and S) and 0x0042 (just MuseS), but I don't know their meaning And nothing was coming from 0x0030, 0x0033 and 0x0036 (but this could come from a disabled parameter somewhere).
Question for people who've connected muse s. How is it the same? Are electrode column locations the same? Hasn't muse s scrapped mastoid/ear electrodes?
@apavlo89 No. same electrode positions, different material and mechanics.
FWIW, I've been able to stream EEG and PPG from the Muse S without any modifications. I'd expect ACC and GYRO to work as well, but haven't tested it.
sorry i missed this thread when i opened #142 . as mentioned there my notes are at https://github.com/xloem/pymuse/blob/master/muse_async.py including all gatt channels I found with names, a little ways down. I'm new to BLE and don't really understand how the uuids relate to the handles to fill in the handles remy17 describes but they're all there; there's a thermistor and another aux channel (and of course the drl channel, unsure why that one isn't in muselsl) in addition to the previously-known channels. #143 #144 preset 0x63 streams all the channels at once which is nice.
But saying that, apart from those I use in my code, I don't know what others characteristics are for. When I tried to read the unknown characteristic, I had data coming from 0x0012, 0x0039, 0x003c, 0x003f (both Muse2 and S) and 0x0042 (just MuseS), but I don't know their meaning
0x12 is DRL_REF. 0x39-0x3f you've already described. 0x42 appears to be thermistor.
And nothing was coming from 0x0030, 0x0033 and 0x0036 (but this could come from a disabled parameter somewhere).
I have magnetometer, pressure, ultraviolet for these. I haven't seen them output anything either.
I just recently noticed that the muse S appears to advertise its serial port over the usb cable attached to it. It looks like the muse S can be used without bluetooth at all, exchanging packets over its usb cable. I didn't know this. It would be good to eventually support direct serial access. EDIT: at first glance it seems the port might do commands but not data, unsure.
When I connect to MuseS, I can not get the extra electrode signal, which is Right AUX channel. Does anyone know how to fix this issue? How to modify the code? Thank you very much.