Bar-graph visualisation demo of reading data from Neurosky's Mindwave Mobile headset, reading via BlueSMIRF Silver bluetooth modem and displaying via MikroElektronika's TFT Proto 320x240 touchscreen.
Dependencies: SPI_TFT TFT_fonts mbed
main.cpp
00001 /* Mindwave Mobile demo - Bob Stone June 2013 00002 * Visual demo of reading data packets from Neurosky's Mindwave Mobile headset 00003 * via BlueSMIRF Silver bluetooth modem and displaying a simple visualisation on 00004 * MikroElektronika's ProtoTFT screen. 00005 * Adapted from http://developer.neurosky.com/docs/doku.php?id=mindwave_mobile_and_arduino 00006 * Display library from Peter Drescher: http://mbed.org/cookbook/SPI-driven-QVGA-TFT 00007 * 00008 * Connect pin9 to BlueSMIRF's RX and pin10 to BlueSMIRF's TX, 00009 * also hook up GND to GND and VCC to the mbed's 3V3 supply. 00010 * 00011 * To prepare the BlueSMIRF to auto-connect to the Mindwave Mobile: 00012 * 00013 * First wire it up and power it using the mbed's power pins. Once it's powered, 00014 * pair your computer to the Mindwave Mobile headset to you can 00015 * find out its MAC address. Write that down as you will need it. 00016 * 00017 * Next pair your computer to the BlueSMIRF so we can configure it. It will 00018 * appear as RN-42-5922 or similar (it's a Roving Networks RN-42 unit). 00019 * 00020 * Now we can use a terminal program to connect to the serial modem created on 00021 * your computer that connects wirelessly to the BlueSMIRF - by default it's at 00022 * 115200 baud, 8 N 1 - when you're connected the light will go green. 00023 * 00024 * If you've got a successful serial connection, put it into command mode by 00025 * typing three dollar signs $$$ - if successful you should see a prompt saying 00026 * 'CMD'. If not, power it down and try again till you get a CMD prompt. 00027 * 00028 * At that prompt we need to change some defaults so that the BlueSMIRF is set to 00029 * master mode, 57600 baud and sends a pincode of '0000', and seeks to connect to 00030 * the headset's MAC address. To do this, type: 00031 * SP,0000 00032 * SM,3 00033 * SR,<the 12 digit MAC address of the headset written down earlier> 00034 * SU,57.6 00035 * D 00036 * You should see AOK after each line, and after the D it will print out its 00037 * settings so you can check it's now AUTO, 57600, using 0000 and the right MAC 00038 * address. All being well, type three minuses '---' to exit command mode. 00039 * 00040 * To check it's working, close the terminal you've been using, reboot the 00041 * BlueSMIRF (flashing red light) and switch on the Mindwave Mobile headset 00042 * - the light on the BlueSMIRF should go green when it connects and the 00043 * flashing blue light on the Mindwave Mobile headset should go steady blue. 00044 */ 00045 #include "mbed.h" 00046 #include "SPI_TFT.h" 00047 #include "Arial12x12.h" 00048 Serial blueSmirf(p9, p10); //for bluetooth comms (TX, RX) 00049 SPI_TFT screen(p11, p12, p13, p14, p15, "TFT"); 00050 int quality=0; 00051 00052 //***************************** 00053 //User routines to process data 00054 //***************************** 00055 00056 /** Maps a value from one scale to another 00057 * 00058 * @param value Value we're trying to scale 00059 * @param min,max The range that value came from 00060 * @param newMin,newMax The new range we're scaling value into 00061 * @returns value mapped into new scale 00062 */ 00063 int map(int value, int min, int max, int newMin, int newMax) { 00064 return newMin + (newMax-newMin) * (value-min) / (max-min); 00065 } 00066 00067 /** Returns a 16-bit RGB565 colour from three 8-bit component values. 00068 * 00069 * @param red,green,blue primary colour channel values expressed as 0-255 each 00070 * @returns 16-bit RGB565 colour constructed as RRRRRGGGGGGBBBBB 00071 */ 00072 int RGBColour(int red, int green, int blue) { 00073 //take most-significant parts of red, green and blue and bit-shift into RGB565 positions 00074 return ((red & 0xf8) << 8) | ((green & 0xfc) << 3) | ((blue & 0xf8) >> 3); 00075 } 00076 00077 /** Returns a colour mapped on a gradient from one colour to another. 00078 * 00079 * @param value Value we're trying to pick a colour for 00080 * @param min,max Scale that value belongs in 00081 * @param minColour,maxColour start and end colours of the gradient we're choosing from (16-bit RGB565) 00082 * @returns colour that's as far along the gradient from minColour to maxColour as value is between min and max (16-bit RGB565) 00083 */ 00084 int getMappedColour(int value, int min, int max, int minColour, int maxColour) 00085 { 00086 // TFT screen colours are 16-bit RGB565 i.e. RRRRRGGGGGGBBBBB 00087 int minRed = (minColour & 0xf800) >> 11; //bitmask for 5 bits red 00088 int maxRed = (maxColour & 0xf800) >> 11; 00089 int minGreen = (minColour & 0x7e0) >> 5; //bitmask for 6 bits green 00090 int maxGreen = (maxColour & 0x7e0) >> 5; 00091 int minBlue = minColour & 0x1f; // bitmask for 5 bits blue 00092 int maxBlue = maxColour & 0x1f; 00093 int valRed = map(value, min, max, minRed, maxRed); 00094 int valGreen = map(value, min, max, minGreen, maxGreen); 00095 int valBlue = map(value, min, max, minBlue, maxBlue); 00096 int valColour = ((valRed & 0x1F) << 11) | ((valGreen & 0x3F) << 5) | (valBlue & 0x1F); 00097 return valColour; 00098 } 00099 00100 /** 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'. 00101 * 00102 * @param x0,y0 coordinates of the 'min' end of the bargraph 00103 * @param x1,y1 coordinates of the 'max' end of the bargraph 00104 * @param isHorizontal If true, bar graph will be drawn with horizontal bars 00105 * @param value Value of the bar, with bars drawn from min up to value, remaining 'backColour' from there to max 00106 * @param min,max Scale of the bar graph that value should be found within 00107 * @param minColour,maxColour colours at the min and max ends of the bar, drawn in a gradient between the two (16-bit RGB565) 00108 * @param backColour background colour of the bar graph (16-bit RGB565) 00109 */ 00110 void displayBarGraph(int x0, int y0, int x1, int y1, bool isHorizontal, int value, int min, int max, int minColour, int maxColour, int backColour) 00111 { 00112 int valColour; 00113 if (isHorizontal) { 00114 if (x1>x0) 00115 { 00116 for (int i = x0; i < x1; i+=5) 00117 { 00118 if (map(i, x0, x1, min, max) > value) 00119 valColour = backColour; 00120 else 00121 valColour = getMappedColour(i, x0, x1, minColour, maxColour); 00122 screen.fillrect(i, y0, i+3, y1, valColour); 00123 } 00124 } else { 00125 for (int i = x1; i < x0; i+=5) 00126 { 00127 if (map(i, x0, x1, min, max) > value) 00128 valColour = backColour; 00129 else 00130 valColour = getMappedColour(i, x0, x1, minColour, maxColour); 00131 screen.fillrect(i-3, y0, i, y1, valColour); 00132 } 00133 } 00134 } else { 00135 if (y1>y0) 00136 { 00137 for (int i = y0; i < y1; i+=5) 00138 { 00139 if (map(i, y0, y1, min, max) > value) 00140 valColour = backColour; 00141 else 00142 valColour = getMappedColour(i, y0, y1, minColour, maxColour); 00143 screen.fillrect(x0, i, x1, i+3, valColour); 00144 } 00145 } else { 00146 for (int i = y1; i < y0; i+=5) 00147 { 00148 if (map(i, y0, y1, min, max) > value) 00149 valColour = backColour; 00150 else 00151 valColour = getMappedColour(i, y0, y1, minColour, maxColour); 00152 screen.fillrect(x0, i-3, x1, i, valColour); 00153 } 00154 } 00155 } 00156 } 00157 00158 /** This will be called if you blink. 00159 */ 00160 void blinked(void) 00161 { 00162 if (quality == 0) { 00163 screen.locate(0,0); 00164 printf("Blink!"); 00165 } 00166 } 00167 00168 /** This will be called when processed eSense data comes in, about once a second. 00169 * 00170 * @param poorQuality will be 0 if connections are good, 200 if connections are useless, and somewhere in between if connection dodgy. 00171 * @param attention processed percentage denoting focus and attention. 0 to 100 00172 * @param meditation processed percentage denoting calmness and serenity. 0 to 100 00173 * @param timeSinceLastPacket time since last packet processed, in milliseconds. 00174 */ 00175 void eSenseData(int poorQuality, int attention, int meditation, int timeSinceLastPacket) 00176 { 00177 quality=poorQuality; 00178 if (poorQuality == 200) 00179 screen.fillrect(313, 3, 317, 7, Red); 00180 else if (poorQuality == 0) 00181 screen.fillrect(313, 3, 317, 7, Green); 00182 else 00183 screen.fillrect(313, 3, 317, 7, Yellow); 00184 00185 if (attention > 0) { 00186 displayBarGraph(50, 210, 75, 30, false, attention, 0, 100, RGBColour(0x10,0x00,0x00), RGBColour(0xFF,0x00,0x00), 0x00); 00187 screen.locate(50, 225); 00188 screen.foreground(Red); 00189 screen.printf("%d ",attention); 00190 screen.foreground(White); // set chars to white 00191 } 00192 if (meditation > 0) { 00193 displayBarGraph(85, 210, 110, 30, false, meditation, 0, 100, RGBColour(0x00,0x10,0x00), RGBColour(0x00,0xFF,0x00), 0x00); 00194 screen.locate(85, 225); 00195 screen.foreground(Green); 00196 screen.printf("%d ",meditation); 00197 screen.foreground(White); // set chars to white 00198 } 00199 } 00200 00201 /** This will be called when processed meter reading data arrives, about once a second. 00202 * This is a breakdown of frequencies in the wave data into 8 named bands, these are: 00203 * 0: Delta (0.5-2.75 Hz) 00204 * 1: Theta (3.5-6.75 Hz) 00205 * 2: Low-Alpha (7.5-9.25 Hz) 00206 * 3: High-Alpha (10-11.75 Hz) 00207 * 4: Low-Beta (13-16.75 Hz) 00208 * 5: High-Beta (18-29.75 Hz) 00209 * 6: Low-Gamma (31-39.75 Hz) 00210 * 7: High-Gamma (41-49.75 Hz) 00211 * 00212 * @param meter array of meter data for different frequency bands 00213 * @param meterMin array of minimum recorded samples of each band 00214 * @param meterMax arrat if naximum recorded samples of each band 00215 */ 00216 void meterData(int meter[8], int meterMin[8], int meterMax[8]) 00217 { 00218 for (int j=0; j<8; j++) { 00219 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); 00220 } 00221 screen.locate(0,0); 00222 printf(" "); 00223 } 00224 00225 /** This will be called when wave data arrives. 00226 * There will be a lot of these, 512 a second, so if you're planning to do anything 00227 * here, don't let it take long. Best not to printf this out as it will just choke. 00228 * 00229 * param wave Raw wave data point 00230 */ 00231 void waveData(int wave) 00232 { 00233 } 00234 00235 //***************** 00236 //End User routines 00237 //***************** 00238 00239 //System routines to obtain and parse data 00240 00241 /** Simplify serial comms 00242 */ 00243 unsigned char ReadOneByte() 00244 { 00245 int ByteRead; 00246 00247 while(!blueSmirf.readable()); 00248 ByteRead = blueSmirf.getc(); 00249 00250 return ByteRead; 00251 } 00252 00253 /** Main loop, sets up and keeps listening for serial 00254 */ 00255 int main() 00256 { 00257 //Video setup 00258 screen.claim(stdout); // send stdout to the TFT display 00259 screen.background(Black); // set background to black 00260 screen.foreground(White); // set chars to white 00261 screen.cls(); // clear the screen 00262 screen.set_font((unsigned char*) Arial12x12); 00263 screen.set_orientation(1); 00264 00265 screen.locate(50, 213); 00266 screen.foreground(Red); 00267 screen.printf("Att"); 00268 screen.locate(85, 213); 00269 screen.foreground(Green); 00270 screen.printf("Med"); 00271 for (int j = 0; j < 8; j++) 00272 { 00273 screen.locate(160 + j * 15, 213); 00274 screen.foreground(RGBColour(0, j*32, 0xFF)); 00275 screen.printf("%d", j); 00276 } 00277 screen.foreground(White); // set chars to white 00278 00279 Timer t; //packet timer 00280 t.start(); 00281 Timer blinkTimer; //used for detecting blinks 00282 int time; 00283 int generatedChecksum = 0; 00284 int checksum = 0; 00285 int payloadLength = 0; 00286 int payloadData[64] = {0}; 00287 int poorQuality = 0; 00288 int attention = 0; 00289 int meditation = 0; 00290 int wave = 0; 00291 int meter[8] = {0}; 00292 int meterMin[8]; 00293 int meterMax[8]; 00294 for (int j = 0; j < 8; j++) 00295 { 00296 meterMin[j]=99999999; 00297 meterMax[j]=-99999999; 00298 } 00299 bool eSensePacket = false; 00300 bool meterPacket = false; 00301 bool wavePacket = false; 00302 00303 blueSmirf.baud(57600); 00304 blinkTimer.reset(); 00305 00306 while(1) { 00307 // Look for sync bytes 00308 if(ReadOneByte() == 170) { 00309 if(ReadOneByte() == 170) { 00310 //Synchronised to start of packet 00311 payloadLength = ReadOneByte(); 00312 if(payloadLength > 169) //Payload length can not be greater than 169 00313 return; 00314 00315 generatedChecksum = 0; 00316 for(int i = 0; i < payloadLength; i++) { 00317 payloadData[i] = ReadOneByte(); //Read payload into memory 00318 generatedChecksum += payloadData[i]; 00319 } 00320 00321 checksum = ReadOneByte(); //Read checksum byte from stream 00322 generatedChecksum = 255 - (generatedChecksum & 0xFF); //Take one's compliment of generated checksum 00323 00324 if(checksum == generatedChecksum) { 00325 //Packet seems OK 00326 poorQuality = 200; 00327 attention = 0; 00328 meditation = 0; 00329 wave = 0; 00330 for(int i = 0; i < payloadLength; i++) { // Parse the payload 00331 switch (payloadData[i]) { 00332 case 2: //quality 00333 i++; 00334 poorQuality = payloadData[i]; 00335 eSensePacket = true; 00336 break; 00337 case 4: //attention 00338 i++; 00339 attention = payloadData[i]; 00340 eSensePacket = true; 00341 break; 00342 case 5: //meditation 00343 i++; 00344 meditation = payloadData[i]; 00345 eSensePacket = true; 00346 break; 00347 case 0x80: //wave 00348 wave = payloadData[i+2] * 256 + payloadData[i+3]; 00349 //We also want to try to detect blinks via analysing wave data 00350 time = blinkTimer.read_ms(); 00351 if (wave > 32767) wave -= 65535; //cope with negatives 00352 if (wave>200 && time == 0) { 00353 blinkTimer.start(); 00354 } else if (wave<-90 && time > 10 && time < 350) { 00355 blinkTimer.stop(); 00356 blinkTimer.reset(); 00357 blinked(); 00358 } else if (time>500) { 00359 blinkTimer.stop(); 00360 blinkTimer.reset(); 00361 } 00362 i = i + 3; 00363 wavePacket = true; 00364 break; 00365 case 0x83: //meter readings for different frequency bands 00366 for (int j=0; j<8; j++) { 00367 //documentation is inconsistent about whether these values are big-endian or little-endian, 00368 //and claims both in different places. But wave data is big-endian so assuming that here. 00369 meter[j] = payloadData[i+j*3+2]*65536 + payloadData[i+j*3+3]*256 + payloadData[i+j*3+4]; 00370 if (meter[j]<meterMin[j]) 00371 meterMin[j]=meter[j]; 00372 if (meter[j]>meterMax[j]) 00373 meterMax[j]=meter[j]; 00374 } 00375 meterPacket = true; 00376 i = i + 25; 00377 break; 00378 default: 00379 break; 00380 } // switch 00381 } // for loop 00382 00383 //Call routines to process data 00384 if(eSensePacket) { 00385 eSenseData(poorQuality, attention, meditation, t.read_ms()); 00386 eSensePacket = false; 00387 } 00388 if (meterPacket) { 00389 meterData(meter, meterMin, meterMax); 00390 t.reset(); 00391 meterPacket=false; 00392 } 00393 if (wavePacket) { 00394 waveData(wave); 00395 wavePacket=false; 00396 } 00397 } else { 00398 // Checksum Error 00399 } // end if else for checksum 00400 } // end if read 0xAA byte 00401 } // end if read 0xAA byte 00402 } 00403 }
Generated on Tue Jul 12 2022 21:03:27 by 1.7.2