Explaining how MetroEEG works

Some developers may wish to not include MetroEEG directly but include lower-level code. In this sample we'll endeavor to show a standalone example of getting information from a Mindwave Mobile headset into WP8.

Connecting to a Mindset Mobile Headset
Using the new Bluetooth support in WP8, it's possible to iterate over all paired Bluetooth devices and open a BT-SPP socket to them.
        private StreamSocket socket;
        private async void ConnectToMindset(object sender, RoutedEventArgs e)
        {
            PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";

            var peers = await PeerFinder.FindAllPeersAsync();

            if (!peers.Any(p => p.DisplayName.Contains("MindWave")))
            {
                Print("MindWave not found. Is bluetooth on? Is MindWave paired?");
            }
            else
            {
                PeerInformation spheroPeer = peers.First(p => p.DisplayName.Contains("MindWave"));

                socket = new StreamSocket();
                await socket.ConnectAsync(spheroPeer.HostName, "1");

                Print("Connected to " + peers[0].DisplayName + " bluetooth service.");

                ListenToResult();
            }
First, we get the list of all connected Bluetooth paired devices.
            PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";

            var peers = await PeerFinder.FindAllPeersAsync();

Next we iterate over that collection attempting to find one with the word "Mindwave" in it. If we don't find one, that could be for multiple reasons: Phone Bluetooth is off, mindwave is off, both devices aren't paired, etc.
            if (!peers.Any(p => p.DisplayName.Contains("MindWave")))
            {
                Print("MindWave not found. Is bluetooth on? Is MindWave paired?");
            }
            else
            {
                PeerInformation spheroPeer = peers.First(p => p.DisplayName.Contains("MindWave"));
            }

And finally we open up a socket that'll be used later to retrieve information from.
                PeerInformation spheroPeer = peers.First(p => p.DisplayName.Contains("MindWave"));

                socket = new StreamSocket();
                await socket.ConnectAsync(spheroPeer.HostName, "1");

                Print("Connected to " + peers[0].DisplayName + " bluetooth service.");

                ListenToResult();
            }

Reading BT-SSP Packet from Mindwave
Mindwave tends to be very chatty and has over 500+ messages every seconds. Those messages are received over as Bytes by C#. We can see the message format at Mindset Communications Protocol.

We'll use socket.InputStream.ReadAsync to read the data sent over by Mindwave.
        private async Task<byte[]> GetNextBuffer(uint length = 512)
        {
            var buffer = await socket.InputStream.ReadAsync(new Windows.Storage.Streams.Buffer(length), length,
                                                            InputStreamOptions.None);

            var resultArray = GetBytesFromBuffer(buffer);
            return resultArray;
        }

        private byte GetByteFromBuffer(IBuffer buffer)
        {
            using (var dr = DataReader.FromBuffer(buffer))
            {
                return dr.ReadByte();
            }
        }

We'll have to continuously read that data in a while (true) loop.
        private async void ListenToResult()
        {
            while (true)
            {
                var resultArray = await GetNextBuffer();
                Debug.WriteLine(string.Join(",", resultArray.Select(b => b.ToString())));
             }
        }

Mindwave sends back A LOT of data. It's quite easy to see a pattern of using 2 bytes of 170, 170 as a separator between message
Cannot resolve image macro, invalid image name or id.

We're interested in a particular packet that start with 3 bytes of 170,170,32 and has an overall length of 36 bytes. for example:
170,170,32,2,25,131,24,0,0,132,0,0,53,0,0,22,0,0,12,0,0,10,0,0,5,0,0,1,0,0,1,4,0,5,0,84

So, we'll check if the 512 bytes we just read has that 170,170,32 header in it.
        private const int PARSER_SYNC = 0xAA;
        private const int UsefulDataPacketLength = 32;
        private const int LengthOfUsefulPacket =
            /*Syncs Length*/ 2 +
            /* Packet Legnth */ 1 +
            UsefulDataPacketLength +
            /* checksum */ 1;
        private int? GetUsefulDataHeaderIndex(byte[] resultArray)
        {
            for (int i = 0; i < resultArray.Length - 2; i++)
            {
                if (resultArray[i] == PARSER_SYNC
                    && resultArray[i + 1] == PARSER_SYNC
                    && resultArray[i + 2] == UsefulDataPacketLength)
                {
                    return i;
                }
            }
            return null;
        }

If our code has those values then we found the one packet out of 500 packets every second that interested us. Otherwise if that header doesn't exist we can just ignore all 512 bytes.
private async void ListenToResult()
{
    while (true)
    {
        var resultArray = await GetNextBuffer();

        int? indexOfUsefulDataHeader = GetUsefulDataHeaderIndex(resultArray);
        if (indexOfUsefulDataHeader.HasValue == false)
        {
            // ignore data and just dump it
        }
        else
        {

        }
    }
}
One unfortunate thing that might happen is that we get 512 bytes that have the header for our 36 bytes packet, but only a part of it. And we might need to read another 512 bytes to get the full 36 byte packet. Once we have that we'll take the length of the data packet (and 2 more bytes to see the next header).
private async void ListenToResult()
{
    while (true)
    {
        var resultArray = await GetNextBuffer();
        // Debug.WriteLine(string.Join(",", resultArray.Select(b => b.ToString())));

        int? indexOfUsefulDataHeader = GetUsefulDataHeaderIndex(resultArray);
        if (indexOfUsefulDataHeader.HasValue == false)
        {
            // ignore data and just dump it
        }
        else
        {
            // Check if enough data exists to finalize this useful data packet, if not, get another
            if (indexOfUsefulDataHeader.Value  + LengthOfUsefulPacket
                > resultArray.Length)
            {
                var nextResultsArray = await GetNextBuffer();
                resultArray = resultArray.Concat(nextResultsArray).ToArray();
            }

            var usefulDataPacket =
                resultArray
                    .Skip(indexOfUsefulDataHeader.Value)
                    .Take(LengthOfUsefulPacket + 4)
                    .ToArray();

            Debug.Writeline(string.Join(",", usefulDataPacket.Select(b => b.ToString())));
        }
    }
}
When we run this code snippet we can see the following print out with useful data packets.
170,170,32,2,25,131,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,5,0,64,170,170,4,128
170,170,32,2,25,131,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,5,0,64,170,170,4,128
170,170,32,2,25,131,24,5,76,181,1,109,49,0,165,255,0,67,142,0,72,239,0,29,197,0,20,50,0,14,11,4,0,5,0,174,170,170,4,128
170,170,32,2,0,131,24,0,54,21,0,59,230,0,13,123,0,2,35,0,7,190,0,4,121,0,1,134,0,1,18,4,0,5,0,100,170,170,4,128
170,170,32,2,0,131,24,9,131,218,3,187,65,0,25,6,0,188,221,0,190,75,0,61,189,0,9,6,0,39,19,4,0,5,0,240,170,170,4,128
170,170,32,2,0,131,24,16,167,140,0,232,31,0,184,100,0,70,183,0,92,99,0,88,228,0,27,190,0,16,81,4,0,5,0,193,170,170,4,128
170,170,32,2,0,131,24,21,60,117,3,90,220,0,69,112,0,70,240,0,124,10,0,41,106,0,100,249,0,53,199,4,30,5,30,193,170,170,4,128
170,170,32,2,51,131,24,9,47,134,0,37,83,0,6,210,0,12,53,0,6,211,0,15,83,0,4,124,0,2,232,4,30,5,30,246,170,170,4,128
170,170,32,2,25,131,24,5,224,15,3,239,254,2,229,125,0,53,105,0,63,75,0,116,127,0,157,83,0,131,10,4,30,5,30,36,170,170,4,128
170,170,32,2,0,131,24,10,187,109,1,120,31,0,196,103,0,143,57,0,84,87,0,104,26,0,45,53,0,121,65,4,43,5,44,252,170,170,4,128
170,170,32,2,0,131,24,4,47,171,4,68,202,0,55,217,0,128,94,0,37,85,0,94,14,0,36,34,0,59,103,4,43,5,38,92,170,170,4,128
170,170,32,2,0,131,24,5,222,176,0,39,237,0,19,9,0,8,159,0,19,186,0,8,23,0,6,29,0,4,195,4,51,5,44,186,170,170,4,128
170,170,32,2,0,131,24,18,191,112,1,97,97,1,29,246,0,29,133,0,9,8,0,58,54,0,16,23,0,25,175,4,63,5,67,173,170,170,4,128

Last edited Sep 25, 2012 at 10:44 PM by JustinJosefAngel, version 1

Comments

No comments yet.