Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: SPI_TFT TFT_fonts mbed
Revision 0:b36d13636379, committed 2013-06-04
- Comitter:
- RorschachUK
- Date:
- Tue Jun 04 17:55:00 2013 +0000
- Commit message:
- First commit of screen version of Mindwave demo
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SPI_TFT.lib Tue Jun 04 17:55:00 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/dreschpe/code/SPI_TFT/#2efcbb2814fa
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TFT_fonts.lib Tue Jun 04 17:55:00 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/dreschpe/code/TFT_fonts/#419d21bfc20c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Jun 04 17:55:00 2013 +0000 @@ -0,0 +1,403 @@ +/* Mindwave Mobile demo - Bob Stone June 2013 + * Visual demo of reading data packets from Neurosky's Mindwave Mobile headset + * via BlueSMIRF Silver bluetooth modem and displaying a simple visualisation on + * MikroElektronika's ProtoTFT screen. + * Adapted from http://developer.neurosky.com/docs/doku.php?id=mindwave_mobile_and_arduino + * Display library from Peter Drescher: http://mbed.org/cookbook/SPI-driven-QVGA-TFT + * + * Connect pin9 to BlueSMIRF's RX and pin10 to BlueSMIRF's TX, + * also hook up GND to GND and VCC to the mbed's 3V3 supply. + * + * To prepare the BlueSMIRF to auto-connect to the Mindwave Mobile: + * + * First wire it up and power it using the mbed's power pins. Once it's powered, + * pair your computer to the Mindwave Mobile headset to you can + * find out its MAC address. Write that down as you will need it. + * + * Next pair your computer to the BlueSMIRF so we can configure it. It will + * appear as RN-42-5922 or similar (it's a Roving Networks RN-42 unit). + * + * Now we can use a terminal program to connect to the serial modem created on + * your computer that connects wirelessly to the BlueSMIRF - by default it's at + * 115200 baud, 8 N 1 - when you're connected the light will go green. + * + * If you've got a successful serial connection, put it into command mode by + * typing three dollar signs $$$ - if successful you should see a prompt saying + * 'CMD'. If not, power it down and try again till you get a CMD prompt. + * + * At that prompt we need to change some defaults so that the BlueSMIRF is set to + * master mode, 57600 baud and sends a pincode of '0000', and seeks to connect to + * the headset's MAC address. To do this, type: + * SP,0000 + * SM,3 + * SR,<the 12 digit MAC address of the headset written down earlier> + * SU,57.6 + * D + * You should see AOK after each line, and after the D it will print out its + * settings so you can check it's now AUTO, 57600, using 0000 and the right MAC + * address. All being well, type three minuses '---' to exit command mode. + * + * To check it's working, close the terminal you've been using, reboot the + * BlueSMIRF (flashing red light) and switch on the Mindwave Mobile headset + * - the light on the BlueSMIRF should go green when it connects and the + * flashing blue light on the Mindwave Mobile headset should go steady blue. + */ +#include "mbed.h" +#include "SPI_TFT.h" +#include "Arial12x12.h" +Serial blueSmirf(p9, p10); //for bluetooth comms (TX, RX) +SPI_TFT screen(p11, p12, p13, p14, p15, "TFT"); +int quality=0; + +//***************************** +//User routines to process data +//***************************** + +/** Maps a value from one scale to another + * + * @param value Value we're trying to scale + * @param min,max The range that value came from + * @param newMin,newMax The new range we're scaling value into + * @returns value mapped into new scale + */ +int map(int value, int min, int max, int newMin, int newMax) { + return newMin + (newMax-newMin) * (value-min) / (max-min); +} + +/** Returns a 16-bit RGB565 colour from three 8-bit component values. + * + * @param red,green,blue primary colour channel values expressed as 0-255 each + * @returns 16-bit RGB565 colour constructed as RRRRRGGGGGGBBBBB + */ +int RGBColour(int red, int green, int blue) { + //take most-significant parts of red, green and blue and bit-shift into RGB565 positions + return ((red & 0xf8) << 8) | ((green & 0xfc) << 3) | ((blue & 0xf8) >> 3); +} + +/** Returns a colour mapped on a gradient from one colour to another. + * + * @param value Value we're trying to pick a colour for + * @param min,max Scale that value belongs in + * @param minColour,maxColour start and end colours of the gradient we're choosing from (16-bit RGB565) + * @returns colour that's as far along the gradient from minColour to maxColour as value is between min and max (16-bit RGB565) + */ +int getMappedColour(int value, int min, int max, int minColour, int maxColour) +{ + // TFT screen colours are 16-bit RGB565 i.e. RRRRRGGGGGGBBBBB + int minRed = (minColour & 0xf800) >> 11; //bitmask for 5 bits red + int maxRed = (maxColour & 0xf800) >> 11; + int minGreen = (minColour & 0x7e0) >> 5; //bitmask for 6 bits green + int maxGreen = (maxColour & 0x7e0) >> 5; + int minBlue = minColour & 0x1f; // bitmask for 5 bits blue + int maxBlue = maxColour & 0x1f; + int valRed = map(value, min, max, minRed, maxRed); + int valGreen = map(value, min, max, minGreen, maxGreen); + int valBlue = map(value, min, max, minBlue, maxBlue); + int valColour = ((valRed & 0x1F) << 11) | ((valGreen & 0x3F) << 5) | (valBlue & 0x1F); + return valColour; +} + +/** Displays a bar graph showing 'value' on a scale 'min' to 'max', where coords (x0,y0) are at 'min' and (x1,y1) are at 'max'. + * + * @param x0,y0 coordinates of the 'min' end of the bargraph + * @param x1,y1 coordinates of the 'max' end of the bargraph + * @param isHorizontal If true, bar graph will be drawn with horizontal bars + * @param value Value of the bar, with bars drawn from min up to value, remaining 'backColour' from there to max + * @param min,max Scale of the bar graph that value should be found within + * @param minColour,maxColour colours at the min and max ends of the bar, drawn in a gradient between the two (16-bit RGB565) + * @param backColour background colour of the bar graph (16-bit RGB565) + */ +void displayBarGraph(int x0, int y0, int x1, int y1, bool isHorizontal, int value, int min, int max, int minColour, int maxColour, int backColour) +{ + int valColour; + if (isHorizontal) { + if (x1>x0) + { + for (int i = x0; i < x1; i+=5) + { + if (map(i, x0, x1, min, max) > value) + valColour = backColour; + else + valColour = getMappedColour(i, x0, x1, minColour, maxColour); + screen.fillrect(i, y0, i+3, y1, valColour); + } + } else { + for (int i = x1; i < x0; i+=5) + { + if (map(i, x0, x1, min, max) > value) + valColour = backColour; + else + valColour = getMappedColour(i, x0, x1, minColour, maxColour); + screen.fillrect(i-3, y0, i, y1, valColour); + } + } + } else { + if (y1>y0) + { + for (int i = y0; i < y1; i+=5) + { + if (map(i, y0, y1, min, max) > value) + valColour = backColour; + else + valColour = getMappedColour(i, y0, y1, minColour, maxColour); + screen.fillrect(x0, i, x1, i+3, valColour); + } + } else { + for (int i = y1; i < y0; i+=5) + { + if (map(i, y0, y1, min, max) > value) + valColour = backColour; + else + valColour = getMappedColour(i, y0, y1, minColour, maxColour); + screen.fillrect(x0, i-3, x1, i, valColour); + } + } + } +} + +/** This will be called if you blink. + */ +void blinked(void) +{ + if (quality == 0) { + screen.locate(0,0); + printf("Blink!"); + } +} + +/** This will be called when processed eSense data comes in, about once a second. + * + * @param poorQuality will be 0 if connections are good, 200 if connections are useless, and somewhere in between if connection dodgy. + * @param attention processed percentage denoting focus and attention. 0 to 100 + * @param meditation processed percentage denoting calmness and serenity. 0 to 100 + * @param timeSinceLastPacket time since last packet processed, in milliseconds. + */ +void eSenseData(int poorQuality, int attention, int meditation, int timeSinceLastPacket) +{ + quality=poorQuality; + if (poorQuality == 200) + screen.fillrect(313, 3, 317, 7, Red); + else if (poorQuality == 0) + screen.fillrect(313, 3, 317, 7, Green); + else + screen.fillrect(313, 3, 317, 7, Yellow); + + if (attention > 0) { + displayBarGraph(50, 210, 75, 30, false, attention, 0, 100, RGBColour(0x10,0x00,0x00), RGBColour(0xFF,0x00,0x00), 0x00); + screen.locate(50, 225); + screen.foreground(Red); + screen.printf("%d ",attention); + screen.foreground(White); // set chars to white + } + if (meditation > 0) { + displayBarGraph(85, 210, 110, 30, false, meditation, 0, 100, RGBColour(0x00,0x10,0x00), RGBColour(0x00,0xFF,0x00), 0x00); + screen.locate(85, 225); + screen.foreground(Green); + screen.printf("%d ",meditation); + screen.foreground(White); // set chars to white + } +} + +/** This will be called when processed meter reading data arrives, about once a second. + * This is a breakdown of frequencies in the wave data into 8 named bands, these are: + * 0: Delta (0.5-2.75 Hz) + * 1: Theta (3.5-6.75 Hz) + * 2: Low-Alpha (7.5-9.25 Hz) + * 3: High-Alpha (10-11.75 Hz) + * 4: Low-Beta (13-16.75 Hz) + * 5: High-Beta (18-29.75 Hz) + * 6: Low-Gamma (31-39.75 Hz) + * 7: High-Gamma (41-49.75 Hz) + * + * @param meter array of meter data for different frequency bands + * @param meterMin array of minimum recorded samples of each band + * @param meterMax arrat if naximum recorded samples of each band + */ +void meterData(int meter[8], int meterMin[8], int meterMax[8]) +{ + for (int j=0; j<8; j++) { + displayBarGraph(160 + j * 15, 210, 170 + j * 15, 100, false, meter[j], meterMin[j], meterMax[j], RGBColour(0, j*2, 0x10), RGBColour(0, j*32, 0xFF), 0x00); + } + screen.locate(0,0); + printf(" "); +} + +/** This will be called when wave data arrives. + * There will be a lot of these, 512 a second, so if you're planning to do anything + * here, don't let it take long. Best not to printf this out as it will just choke. + * + * param wave Raw wave data point + */ +void waveData(int wave) +{ +} + +//***************** +//End User routines +//***************** + +//System routines to obtain and parse data + +/** Simplify serial comms + */ +unsigned char ReadOneByte() +{ + int ByteRead; + + while(!blueSmirf.readable()); + ByteRead = blueSmirf.getc(); + + return ByteRead; +} + +/** Main loop, sets up and keeps listening for serial + */ +int main() +{ + //Video setup + screen.claim(stdout); // send stdout to the TFT display + screen.background(Black); // set background to black + screen.foreground(White); // set chars to white + screen.cls(); // clear the screen + screen.set_font((unsigned char*) Arial12x12); + screen.set_orientation(1); + + screen.locate(50, 213); + screen.foreground(Red); + screen.printf("Att"); + screen.locate(85, 213); + screen.foreground(Green); + screen.printf("Med"); + for (int j = 0; j < 8; j++) + { + screen.locate(160 + j * 15, 213); + screen.foreground(RGBColour(0, j*32, 0xFF)); + screen.printf("%d", j); + } + screen.foreground(White); // set chars to white + + Timer t; //packet timer + t.start(); + Timer blinkTimer; //used for detecting blinks + int time; + int generatedChecksum = 0; + int checksum = 0; + int payloadLength = 0; + int payloadData[64] = {0}; + int poorQuality = 0; + int attention = 0; + int meditation = 0; + int wave = 0; + int meter[8] = {0}; + int meterMin[8]; + int meterMax[8]; + for (int j = 0; j < 8; j++) + { + meterMin[j]=99999999; + meterMax[j]=-99999999; + } + bool eSensePacket = false; + bool meterPacket = false; + bool wavePacket = false; + + blueSmirf.baud(57600); + blinkTimer.reset(); + + while(1) { + // Look for sync bytes + if(ReadOneByte() == 170) { + if(ReadOneByte() == 170) { + //Synchronised to start of packet + payloadLength = ReadOneByte(); + if(payloadLength > 169) //Payload length can not be greater than 169 + return; + + generatedChecksum = 0; + for(int i = 0; i < payloadLength; i++) { + payloadData[i] = ReadOneByte(); //Read payload into memory + generatedChecksum += payloadData[i]; + } + + checksum = ReadOneByte(); //Read checksum byte from stream + generatedChecksum = 255 - (generatedChecksum & 0xFF); //Take one's compliment of generated checksum + + if(checksum == generatedChecksum) { + //Packet seems OK + poorQuality = 200; + attention = 0; + meditation = 0; + wave = 0; + for(int i = 0; i < payloadLength; i++) { // Parse the payload + switch (payloadData[i]) { + case 2: //quality + i++; + poorQuality = payloadData[i]; + eSensePacket = true; + break; + case 4: //attention + i++; + attention = payloadData[i]; + eSensePacket = true; + break; + case 5: //meditation + i++; + meditation = payloadData[i]; + eSensePacket = true; + break; + case 0x80: //wave + wave = payloadData[i+2] * 256 + payloadData[i+3]; + //We also want to try to detect blinks via analysing wave data + time = blinkTimer.read_ms(); + if (wave > 32767) wave -= 65535; //cope with negatives + if (wave>200 && time == 0) { + blinkTimer.start(); + } else if (wave<-90 && time > 10 && time < 350) { + blinkTimer.stop(); + blinkTimer.reset(); + blinked(); + } else if (time>500) { + blinkTimer.stop(); + blinkTimer.reset(); + } + i = i + 3; + wavePacket = true; + break; + case 0x83: //meter readings for different frequency bands + for (int j=0; j<8; j++) { + //documentation is inconsistent about whether these values are big-endian or little-endian, + //and claims both in different places. But wave data is big-endian so assuming that here. + meter[j] = payloadData[i+j*3+2]*65536 + payloadData[i+j*3+3]*256 + payloadData[i+j*3+4]; + if (meter[j]<meterMin[j]) + meterMin[j]=meter[j]; + if (meter[j]>meterMax[j]) + meterMax[j]=meter[j]; + } + meterPacket = true; + i = i + 25; + break; + default: + break; + } // switch + } // for loop + + //Call routines to process data + if(eSensePacket) { + eSenseData(poorQuality, attention, meditation, t.read_ms()); + eSensePacket = false; + } + if (meterPacket) { + meterData(meter, meterMin, meterMax); + t.reset(); + meterPacket=false; + } + if (wavePacket) { + waveData(wave); + wavePacket=false; + } + } else { + // Checksum Error + } // end if else for checksum + } // end if read 0xAA byte + } // end if read 0xAA byte + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Tue Jun 04 17:55:00 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/b3110cd2dd17 \ No newline at end of file