hybridgroup.gobot/drivers/serial/neurosky/mindwave_driver.go

197 lines
5.0 KiB
Go

package neurosky
import (
"bytes"
"log"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/serial"
)
type mindWaveSerialAdaptor interface {
gobot.Adaptor
serial.SerialReader
}
const (
// mindWaveBTSync is the sync code
mindWaveBTSync byte = 0xAA
// mindWaveCodeEx Extended code
mindWaveCodeEx byte = 0x55
// mindWaveCodeSignalQuality POOR_SIGNAL quality 0-255
mindWaveCodeSignalQuality byte = 0x02
// mindWaveCodeAttention ATTENTION eSense 0-100
mindWaveCodeAttention byte = 0x04
// mindWaveCodeMeditation MEDITATION eSense 0-100
mindWaveCodeMeditation byte = 0x05
// mindWaveCodeBlink BLINK strength 0-255
mindWaveCodeBlink byte = 0x16
// mindWaveCodeWave RAW wave value: 2-byte big-endian 2s-complement
mindWaveCodeWave byte = 0x80
// mindWaveCodeAsicEEG ASIC EEG POWER 8 3-byte big-endian integers
mindWaveCodeAsicEEG byte = 0x83
ExtendedEvent = "extended"
SignalEvent = "signal"
AttentionEvent = "attention"
MeditationEvent = "meditation"
BlinkEvent = "blink"
WaveEvent = "wave"
EEGEvent = "eeg"
ErrorEvent = "error"
)
// MindWaveDriver is the Gobot driver for the Neurosky MindWave Sensor
type MindWaveDriver struct {
*serial.Driver
gobot.Eventer
}
// MindWaveEEGData is the EEG raw data returned from the sensor
type MindWaveEEGData struct {
Delta int
Theta int
LoAlpha int
HiAlpha int
LoBeta int
HiBeta int
LoGamma int
MidGamma int
}
// NewMindWaveDriver creates a driver for Neurosky MindWave
// and adds the following events:
//
// extended - user's current extended level
// signal - shows signal strength
// attention - user's current attention level
// meditation - user's current meditation level
// blink - user's current blink level
// wave - shows wave data
// eeg - showing eeg data
func NewMindWaveDriver(a mindWaveSerialAdaptor, opts ...serial.OptionApplier) *MindWaveDriver {
d := &MindWaveDriver{
Eventer: gobot.NewEventer(),
}
d.Driver = serial.NewDriver(a, "MindWave", d.initialize, nil, opts...)
d.AddEvent(ExtendedEvent)
d.AddEvent(SignalEvent)
d.AddEvent(AttentionEvent)
d.AddEvent(MeditationEvent)
d.AddEvent(BlinkEvent)
d.AddEvent(WaveEvent)
d.AddEvent(EEGEvent)
d.AddEvent(ErrorEvent)
return d
}
// initialize creates a go routine to listen from serial port and parse buffer readings
// TODO: stop the go routine gracefully on Halt()
func (d *MindWaveDriver) initialize() error {
go func() {
for {
buff := make([]byte, 1024)
_, err := d.adaptor().SerialRead(buff)
if err != nil {
d.Publish(d.Event("error"), err)
} else {
if err := d.parse(bytes.NewBuffer(buff)); err != nil {
panic(err)
}
}
}
}()
return nil
}
// parse converts bytes buffer into packets until no more data is present
func (d *MindWaveDriver) parse(buf *bytes.Buffer) error {
for buf.Len() > 2 {
b1, _ := buf.ReadByte()
b2, _ := buf.ReadByte()
if b1 == mindWaveBTSync && b2 == mindWaveBTSync {
length, _ := buf.ReadByte()
payload := make([]byte, length)
if _, err := buf.Read(payload); err != nil {
return err
}
// checksum, _ := buf.ReadByte()
buf.Next(1)
if err := d.parsePacket(bytes.NewBuffer(payload)); err != nil {
panic(err)
}
}
}
return nil
}
// parsePacket publishes event according to data parsed
func (d *MindWaveDriver) parsePacket(buf *bytes.Buffer) error {
for buf.Len() > 0 {
b, _ := buf.ReadByte()
switch b {
case mindWaveCodeEx:
d.Publish(d.Event(ExtendedEvent), nil)
case mindWaveCodeSignalQuality:
ret, _ := buf.ReadByte()
d.Publish(d.Event(SignalEvent), ret)
case mindWaveCodeAttention:
ret, _ := buf.ReadByte()
d.Publish(d.Event(AttentionEvent), ret)
case mindWaveCodeMeditation:
ret, _ := buf.ReadByte()
d.Publish(d.Event(MeditationEvent), ret)
case mindWaveCodeBlink:
ret, _ := buf.ReadByte()
d.Publish(d.Event(BlinkEvent), ret)
case mindWaveCodeWave:
buf.Next(1)
ret := make([]byte, 2)
if _, err := buf.Read(ret); err != nil {
return err
}
d.Publish(d.Event(WaveEvent), int16(ret[0])<<8|int16(ret[1]))
case mindWaveCodeAsicEEG:
ret := make([]byte, 25)
i, _ := buf.Read(ret)
if i == 25 {
d.Publish(d.Event(EEGEvent), d.parseEEG(ret))
}
}
}
return nil
}
// parseEEG returns data converted into EEG map
func (d *MindWaveDriver) parseEEG(data []byte) MindWaveEEGData {
return MindWaveEEGData{
Delta: d.parse3ByteInteger(data[0:3]),
Theta: d.parse3ByteInteger(data[3:6]),
LoAlpha: d.parse3ByteInteger(data[6:9]),
HiAlpha: d.parse3ByteInteger(data[9:12]),
LoBeta: d.parse3ByteInteger(data[12:15]),
HiBeta: d.parse3ByteInteger(data[15:18]),
LoGamma: d.parse3ByteInteger(data[18:21]),
MidGamma: d.parse3ByteInteger(data[21:25]),
}
}
func (d *MindWaveDriver) parse3ByteInteger(data []byte) int {
return ((int(data[0]) << 16) |
(((1 << 16) - 1) & (int(data[1]) << 8)) |
(((1 << 8) - 1) & int(data[2])))
}
func (d *MindWaveDriver) adaptor() mindWaveSerialAdaptor {
if a, ok := d.Connection().(mindWaveSerialAdaptor); ok {
return a
}
log.Printf("%s has no Neurosky serial connector\n", d.Name())
return nil
}