So far I have inched my way to making a Mind-Mirror compatible EEG in a theoretical way, but to make it work in real life I need a way of getting signals into the machine. You can buy a board made by Olimex for a reasonable £50, you get optoisolation and everything, and it’s probably the most cost-effective way. Trouble is I don’t know that EEGmir works yet, so I want to do it cheaper, and also now. A Microchip PIC16F88 will do the job here, and I have a few 🙂
I tinkered using this SPBRG calculator to find a suitable crystal to run the PIC16F88 at to match both the 256Hz sampling rate and the baud rate. The first run of EEGmir showed me nothing at all.
Inquiring further it seems the Raspberry Pi gets shirty about a 3% baudrate error at 57600 baud. I set up a test PIC to pump out an endless string of As, and when I brought up minicom they showed up as Ps. This is not good.
I needed to go find a 3.6864 MHz crystal, which lets you get down to 0% error at 57.6k, and by a fortunate stroke of luck fc/4 divides down integer-wise to 256Hz. Nice. So I did that, sending a bunch of As in the data frames to the Pi, after padding down the 5V TTL signal from the PIC.
Mincom showed the As OK from the test PIC, but it wouldn’t let go of the TTY until I rebooted. EEGmir comes up and shows me a load of gobby stuff about data errors. Pressing F12 shows it is assessing jitter
and telling me I have a sampling rate of 325Hz. The nice thing about hardware is you can get a second opinion. Sometimes it’s the smoke pouring out of something, but here it’s in the frame rate of the signal, as I gave myself a sync pulse on a spare PIC pin to synchronise my scope to. So I appeal the outrageous assertion that I am running too fast
and get handed down the verdict of guilty as charged, I did screw up. And I didn’t wait for the camera to focus.
Let’s look on the bright side. This PIC is sending out data at the right baud rate, sort of the right number of frames, too damn fast. And EEGmir is reading from the Pi serial port and struggling manfully to make some sense of it. The (256Hz) on the jitter display even gives me hope it might adapt if I choose to run at 128Hz. Oh and I find that the escape key is the quit command in EEGmir, which saves having to go find the PID and do a kill-9 PID on it, which always feels a bit bush league.
The sampling rate error is because I failed to wait for the TMR1 to time out which I was using to define the frame rate, doing that fixed the sampling rate, it’s now 256.04 according to EEGmir. Still hollering about data errors, so I probably failed to understand the OpenEEG2 protocol somehow.
testing different slots and values
So I create myself a test PIC 16F88 to read out to me all the slots, swapping sync0 for ‘Y’, sync1 is already ‘Z’, ‘P’ for the packet counter, then ‘S’ ‘0’ ‘s’ ‘1’ and end up with a CR in the switches, and add a gratuitous LF 18th packet.
Then stuff that into Mincom, just to ask the question, have I got all this rubbish in the right order? The answer is yes. So I check EEGmir.cfg, to see what protocol they expect.
# Note that there are currently two modEEG protocols in use. The # well-established "fmt modEEG-P2", and a newer "fmt modEEG-P3". Most # people have P2 installed, because it is supported by BioExplorer and # ElectricGuru. [unix-dev] #rawdump; #audio-sync; port /dev/ttyS0 57600; fmt modEEG-P2; rate 256; chan 6;
You can’t really argue with that. Looks like P2 to me, though I have the option of P3 should I so fancy. Scratches head. There’s clearly something in here I’m missing. Signed numbers perhaps – so I hit up the Big G on ‘openeeg signed integer’ and read this PDF, and go WTF? Were these openEEG guys doing acid as well as neurofeedback?
The modular OpenEEG digital boards output 256 EEG samples from the 6 possible channels per second in a “Firmware 2” format (as defined in the Modular EEG “Firmware 2” ). The samples are signed 10-bit values stored as 2 byte words (such that the upper 6 bits of the MSB are unused). Each sample is contained within a 17 byte data frame which has a 4 byte header (comprising a two byte synchronization value, a version byte and a frame number byte), 12 bytes of EEG values (2 bytes for each of 6 channels) and followed by a single (unused) button state byte (allowing the connection of four external buttons or switches should an application require them).
Now don’t get me wrong, there’s nothing wrong in accepting the 10-bit limitations of your hardware because you’re a cheapskate bum using a single successive approximation ADC behind an analogue switch, after all that’s what’s in my 16F88 too. The obvious way to pack that in your data format is to stick the MSB (or sign bit now) in the MSB of the 16-bit slot and pad the 6 LSBs with zero. Which is exactly what I told my PIC to do. That way, when you win the lottery and can afford a 16 bit ADC you’re all set and ready to go.
The way OpenEEG seem to have done it is to pad the 6 MSBs with zero, so when you win the lottery and have 16 bit ADCs I guess you get to invent a new data format, perhaps openEEGp3? It’s barmy as hell.
So. let’s have another go. At the moment my PIC puts the data in the MSBs and pads the LSBs, so from EEGmir’s point of view it sees the MSBs polluted with all sorts of guff. Let me make a PIC generating digital silence. Sadly the references in that PDF point to the deceased wiki and mailing lists of openEEG. So let me guess that the signed method was two’s complement because it’s the most popular. fortunately two’s complement 0 is still 0000000000 in 10-bit representation, so I can simply write 0 to all my signal bytes. what i would like to see there is openEEG not carping about data errors 😉 However I could make it more useful if I send digital 1s in the active bits, which should be -1.
And I take stick about data errors again. I am heartened by the indication it is tracking the packet count – ok it missed a lot more than 98 packets because that was a hook PIC out of board, reflash and try again, but it’s nice that it’s tracking the modulo-256 diff. This implies that frame synchronisation is running fine if it can parse the counter.
Hmm. Try all zeros. That works dandy, or at least no data errors but it can see my frame rate, 256.04Hz. Try 0xFF in all the LSBs. Nope – data errors. Try 0xFF in the MSBs and 0x00 in the LSBs. Now I get away with that, and I start to wonder if I really should be sending that LSB first. Now I do that because I got the idea from BrainBay’s Developer manual, but I should have been paying attention because the university final year projects PDF has this to say
Byte 1: Sync Value 0xa5
Byte 2: Sync Value 0x5a
Byte 3: Version (2)
Byte 4: Frame Number
Byte 5: Channel 1 High Byte
Byte 6: Channel 1 Low Byte
Byte 7: Channel 2 High Byte
Byte 8: Channel 2 Low Byte
Byte 9: Channel 3 High Byte
Byte 10: Channel 3 Low Byte
Byte 11: Channel 4 High Byte
Byte 12: Channel 4 Low Byte
Byte 13: Channel 5 High Byte
Byte 14: Channel 5 Low Byte
Byte 15: Channel 6 High Byte
Byte 16: Channel 6 Low Byte
Byte 17: Button States (b1-b4)
and anyway, if I use the source then exactly what part of big-endian format do I not understand?
So I now need some magic to convert my values into a twos-complement result. And send it through big-endian like the guy said in the code.
I have a hardware implementation of something that has the right baud rate, packet structure and frame rate for EEGmir to stop grousing about data errors and sync up to. I have found that the BrainBay developer’s manual is wrong and they really mean big-endian. And I have found out that for some bizarre reason OpenEEG p2 is limited to 10 bits. I am OK with that because Max Cade was running a 4-bit resolution display. And presumably p3 may be compatible with 16-bit resolution. As it is my 16F88 is only good for 10 bits.
To get this into OpenEEG format I need to subtract 32767 from the 16-bit padded 10-bit values I have. And then rotate right this 16 bit number six times. But that’s a job for another day.
Or not. After a cup of tea I realised that I could do a 16-bit add of -32767 with the existing 16-bit values, and the RRF these pairs of bytes through STATUS,C 6 times. So I fired it up and saw the signal come through. The funky sinewave pattern along the top is an artefact of the slow shutter speed, the signals look like an unlocked scope sine wave in real life. The bars respond well as I tune the frequency, faster in the higher frequencies and slower at the lower ones. Hard to really say, though, since my oscillator has lots of bounce at low frequencies due to the old HP trick of using a small bulb as a gain control that everybody copied for awhile.
More about that HP trick