├── EEG_LCD_bar_graph.pde ├── EEG_LCD_bar_graph_smoothing.pde ├── EEG_serial_LCD_stream.pde ├── README.md ├── eeg_lucid_dream.fzz └── figures ├── eeg wiring diagram.png └── system_overview.png /EEG_LCD_bar_graph.pde: -------------------------------------------------------------------------------- 1 | // improved LCD display of MindFlex EEG data 2 | 3 | // include the LCD and Brain libraries 4 | #include 5 | #include 6 | 7 | // activate and configure the LCD 8 | LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2); 9 | int backLight = 13; 10 | 11 | // create the custom characters for the LCD bar-graphs 12 | uint8_t custom_0[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f}; //1 bar 13 | uint8_t custom_1[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f}; //2 bar 14 | uint8_t custom_2[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f}; //3 bar 15 | uint8_t custom_3[8] = { 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f}; //4 bar (half) 16 | uint8_t custom_4[8] = { 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f}; //5 bar 17 | uint8_t custom_5[8] = { 0x00, 0x00, 0x1f, 0x1F, 0x1F, 0x1F, 0x1F, 0x1f}; //6 bar 18 | uint8_t custom_6[8] = { 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1f}; //7 bar 19 | uint8_t custom_7[8] = { 0x1f, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1f}; //8 bar (full) 20 | 21 | // activate the Brain serial port connection 22 | Brain brain(Serial); 23 | 24 | // configure variables for the EEG data 25 | byte signalStrength = 0; 26 | unsigned long deltaEnergy = 0; 27 | unsigned long alphaEnergy = 0; 28 | unsigned long betaEnergy = 0; 29 | unsigned long thetaEnergy = 0; 30 | 31 | // configure remainders for bar graph 32 | int remSigStrength = 0; 33 | int remDelta = 0; 34 | int remAlpha = 0; 35 | int remBeta = 0; 36 | int remTheta = 0; 37 | 38 | void setup(){ 39 | // activate the LCD and set up the LCD backlight 40 | pinMode(backLight, OUTPUT); 41 | digitalWrite(backLight, HIGH); 42 | lcd.begin(16, 2); 43 | lcd.print("ready..."); 44 | 45 | // define the custom characters for the LCD controlled 46 | lcd.createChar(0, custom_0); //send the custom characters to the LCD 47 | lcd.createChar(1, custom_1); 48 | lcd.createChar(2, custom_2); 49 | lcd.createChar(3, custom_3); 50 | lcd.createChar(4, custom_4); 51 | lcd.createChar(5, custom_5); 52 | lcd.createChar(6, custom_6); 53 | lcd.createChar(7, custom_7); 54 | 55 | //active the serial connection stream 56 | Serial.begin(9600); 57 | Serial.println("connection established"); 58 | } 59 | 60 | void loop(){ 61 | if (brain.update()){ 62 | // stream the output to the PC via the serial connection 63 | Serial.println(brain.readCSV()); 64 | 65 | // draw the screen labels 66 | lcd.clear(); 67 | lcd.setCursor(0,0); 68 | lcd.print("S D A B T"); 69 | 70 | // fill in the wavelength energies from the EEG reading 71 | lcd.setCursor(1,0); 72 | lcd.print(brain.readSignalQuality()); 73 | lcd.setCursor(4,0); 74 | lcd.print(brain.readDelta()/75000); 75 | lcd.setCursor(7,0); 76 | lcd.print((brain.readHighAlpha()+brain.readLowAlpha())/10000); 77 | lcd.setCursor(10,0); 78 | lcd.print((brain.readHighBeta()+brain.readLowBeta())/10000); 79 | lcd.setCursor(13,0); 80 | lcd.print(brain.readTheta()/5000); 81 | 82 | // set the variables and correct for the bar graph 83 | byte signalStrength = (((brain.readSignalQuality())/10)/8); 84 | unsigned long deltaEnergy = (((brain.readDelta())/150000)/8); 85 | unsigned long alphaEnergy = (((brain.readHighAlpha()+brain.readLowAlpha())/15000)/8); 86 | unsigned long betaEnergy = (((brain.readHighBeta()+brain.readLowBeta())/15000)/8); 87 | unsigned long thetaEnergy = (((brain.readTheta())/10000)/8); 88 | int remSigStrength = (((brain.readSignalQuality())/10)%8); 89 | int remDelta = (((brain.readDelta())/150000)%8); 90 | int remAlpha = (((brain.readHighAlpha()+brain.readLowAlpha())/15000)%8); 91 | int remBeta = (((brain.readHighBeta()+brain.readLowBeta())/15000)%8); 92 | int remTheta = (((brain.readTheta())/10000)%8); 93 | 94 | // fill in the signal strength bar graph 95 | lcd.setCursor(0,1); 96 | for (int i=0; i 9 | #include 10 | 11 | // activate the Brain serial port connection 12 | Brain brain(Serial); 13 | 14 | // configure variables for the EEG data 15 | byte signalStrength = 0; 16 | long deltaEnergy = 0; 17 | long alphaEnergy = 0; 18 | long betaEnergy = 0; 19 | long thetaEnergy = 0; 20 | 21 | // create arrays for data smoothing 22 | const int numReadings = 3; 23 | int index = 0; 24 | long readingsDelta[numReadings]; 25 | long totalDelta = 0; 26 | long averageDelta = 0; 27 | long readingsAlpha[numReadings]; 28 | long totalAlpha = 0; 29 | long averageAlpha = 0; 30 | long readingsBeta[numReadings]; 31 | long totalBeta = 0; 32 | long averageBeta = 0; 33 | long readingsTheta[numReadings]; 34 | long totalTheta = 0; 35 | long averageTheta = 0; 36 | 37 | // create array for long duration monitoring 38 | 39 | // activate and configure the LCD pin 12 = pin 11 = pin 10 = pin 5 = 40 | LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2); // pin 4 = pin 3 = pin 2 = 41 | int backLight = 13; 42 | 43 | // create the custom characters for the LCD bar-graphs 44 | uint8_t custom_0[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f}; //1 bar 45 | uint8_t custom_1[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f}; //2 bar 46 | uint8_t custom_2[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f}; //3 bar 47 | uint8_t custom_3[8] = { 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f}; //4 bar (half) 48 | uint8_t custom_4[8] = { 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f}; //5 bar 49 | uint8_t custom_5[8] = { 0x00, 0x00, 0x1f, 0x1F, 0x1F, 0x1F, 0x1F, 0x1f}; //6 bar 50 | uint8_t custom_6[8] = { 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1f}; //7 bar 51 | uint8_t custom_7[8] = { 0x1f, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1f}; //8 bar (full) 52 | 53 | // configure remainders for bar graph 54 | int remSigStrength = 0; 55 | int remDelta = 0; 56 | int remAlpha = 0; 57 | int remBeta = 0; 58 | int remTheta = 0; 59 | 60 | void setup(){ 61 | // initialize the data smoothing array and set contents equal to zero 62 | for (int thisReading = 0; thisReading < numReadings; thisReading++) { 63 | readingsAlpha[thisReading] = 0; 64 | readingsDelta[thisReading] = 0; 65 | readingsBeta[thisReading] = 0; 66 | readingsTheta[thisReading] = 0; 67 | } 68 | 69 | // initialize the long term recording array and set contents equal to zero 70 | 71 | // active the serial connection to the computer 72 | Serial.begin(9600); 73 | Serial.println("connection established"); 74 | 75 | // activate the LCD and set up the LCD backlight 76 | pinMode(backLight, OUTPUT); 77 | digitalWrite(backLight, HIGH); 78 | lcd.begin(16, 2); 79 | lcd.print("ready..."); 80 | 81 | // define the custom characters for the LCD controlled 82 | lcd.createChar(0, custom_0); //send the custom characters to the LCD 83 | lcd.createChar(1, custom_1); 84 | lcd.createChar(2, custom_2); 85 | lcd.createChar(3, custom_3); 86 | lcd.createChar(4, custom_4); 87 | lcd.createChar(5, custom_5); 88 | lcd.createChar(6, custom_6); 89 | lcd.createChar(7, custom_7); 90 | } 91 | 92 | void loop(){ 93 | if (brain.update()){ 94 | // stream the output to the PC via the serial connection 95 | Serial.println(brain.readCSV()); 96 | 97 | // Serial.print("Raw values D"); 98 | // Serial.print(brain.readDelta()); 99 | // Serial.print(" A"); 100 | // Serial.print((brain.readLowAlpha() + brain.readHighAlpha())); 101 | // Serial.print(" B"); 102 | // Serial.print((brain.readLowBeta() + brain.readHighBeta())); 103 | // Serial.print(" T"); 104 | // Serial.println(brain.readTheta()); 105 | // 106 | // Serial.print("Averaged D"); 107 | // Serial.print(averageDelta); 108 | // Serial.print(" A"); 109 | // Serial.print(averageAlpha); 110 | // Serial.print(" B"); 111 | // Serial.print(averageBeta); 112 | // Serial.print(" T"); 113 | // Serial.println(averageTheta); 114 | 115 | 116 | // calculate values for the data smoothing array 117 | totalDelta = totalDelta - readingsDelta[index]; 118 | readingsDelta[index] = (brain.readDelta() + brain.readDelta()); 119 | totalDelta = totalDelta + readingsDelta[index]; 120 | 121 | totalAlpha = totalAlpha - readingsAlpha[index]; 122 | readingsAlpha[index] = (brain.readLowAlpha() + brain.readHighAlpha()); 123 | totalAlpha = totalAlpha + readingsAlpha[index]; 124 | 125 | totalBeta = totalBeta - readingsBeta[index]; 126 | readingsBeta[index] = (brain.readLowBeta() + brain.readHighBeta()); 127 | totalBeta = totalBeta + readingsBeta[index]; 128 | 129 | totalTheta = totalTheta - readingsTheta[index]; 130 | readingsTheta[index] = (brain.readTheta()); 131 | totalTheta = totalTheta + readingsTheta[index]; 132 | 133 | index = index + 1; 134 | 135 | // calculate 3 second averages based on the array 136 | averageDelta = totalDelta / numReadings; 137 | averageAlpha = totalAlpha / numReadings; 138 | averageBeta = totalBeta / numReadings; 139 | averageTheta = totalTheta / numReadings; 140 | 141 | // reset the position in the array 142 | if (index >= numReadings) 143 | index = 0; 144 | 145 | // draw the screen labels 146 | lcd.clear(); 147 | lcd.setCursor(0,0); 148 | lcd.print("S D A B T"); 149 | 150 | // fill in the wavelength energies from the EEG reading 151 | lcd.setCursor(1,0); 152 | lcd.print(brain.readSignalQuality()); 153 | lcd.setCursor(4,0); 154 | lcd.print(brain.readDelta()/75000); 155 | lcd.setCursor(7,0); 156 | lcd.print(averageAlpha/10000); 157 | lcd.setCursor(10,0); 158 | lcd.print((brain.readHighBeta()+brain.readLowBeta())/10000); 159 | lcd.setCursor(13,0); 160 | lcd.print(brain.readTheta()/5000); 161 | 162 | // normalize the variables and divide to create the bar graph values 163 | byte signalStrength = (((brain.readSignalQuality())/15)/8); 164 | unsigned long deltaEnergy = (((averageDelta)/150000)/8); 165 | unsigned long alphaEnergy = (((averageAlpha)/15000)/8); 166 | unsigned long betaEnergy = (((averageBeta)/15000)/8); 167 | unsigned long thetaEnergy = (((averageTheta)/10000)/8); 168 | int remSigStrength = (((brain.readSignalQuality())/15)%8); 169 | int remDelta = (((averageDelta)/150000)%8); 170 | int remAlpha = ((averageAlpha/15000)%8); 171 | int remBeta = (((averageBeta)/15000)%8); 172 | int remTheta = (((averageTheta)/10000)%8); 173 | 174 | // fill in the bar graphs 175 | lcd.setCursor(0,1); 176 | for (int i=0; i 6 | 7 | // Set up the brain parser, pass it the hardware serial object you want to listen on. 8 | Brain brain(Serial); 9 | 10 | void setup() { 11 | // Start the hardware serial. 12 | Serial.begin(9600); 13 | Serial.println("connection established"); 14 | } 15 | 16 | void loop() { 17 | // Expect packets about once per second. 18 | // The .readCSV() function returns a string (well, char*) listing the most recent brain data, in the following format: 19 | // "signal strength, attention, meditation, delta, theta, low alpha, high alpha, low beta, high beta, low gamma, high gamma" 20 | if (brain.update()) { 21 | Serial.println(brain.readErrors()); 22 | Serial.println(brain.readCSV()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # brain-eeg-graph 2 | An arduino EEG interface designed to enable lucid dreaming 3 | 4 | ### background 5 | After seeing the movie [_Inception_](https://www.imdb.com/title/tt1375666/), I was inspired to try and build a [lucid dreaming](https://en.wikipedia.org/wiki/Lucid_dream) device. I decided to take advantage of low cost, commercially available EEG chips which were being incorporated into toys, such as the [NeuroSky EEG module](http://neurosky.com/biosensors/eeg-sensor/) (pictured below). Using these off-the-shelf devices it is possible to extract low quality EEG signals and perform FFT processing to determine the dominant power spectrum and use that to (indirectly) monitor sleep stage. By detecting the change in EEG pattern it is theoretically possible to detect the onset of dreaming. If the system detects the onset of REM sleep, it plays an audible tone, which (with practice/training) can help the user recognize that they are dreaming, facilitating lucid dreaming. 6 | 7 | ![Neurosky EEG module](http://neurosky.com/wp-content/uploads/2014/11/TGAM_medium1.jpg) 8 | 9 | 10 | ### system overview 11 | The system consists of 12 | * a [Force Trainer](https://www.amazon.com/Star-Wars-Science-Force-Trainer/dp/B001UZHASY) or [Mindflex](https://www.amazon.com/Mattel-P2639-Mindflex-Game/dp/B001UEUHCG) EEG module is connected to an arduino via an [optoisolator](https://en.wikipedia.org/wiki/Opto-isolator). 13 | * The arduino uses the [brain library](https://github.com/kitschpatrol/Brain) to extract EEG data as it listens to the tx/rx pins. 14 | * the arduino displays the power spectrogram on the attached LCD display 15 | * when the EEG spectrum changes from delta wave predominant (suggesting slow wave sleep) to alpha wave predominant (suggesting either wakefullness or REM) the device emits a tone (or better yet plays the song [“Non, je ne Regrette Rien”](https://www.youtube.com/watch?v=Q3Kvu6Kgp88)) which can indicate to the user that they are dreaming 16 | * the device also [logs data to an SD card](https://www.adafruit.com/product/1141) for review 17 | 18 | ![system overview](https://github.com/nickmmark/brain-eeg-graph/blob/master/figures/system_overview.png) 19 | 20 | Definitions and interpretation of EEG power spectra: 21 | 22 | Phase | Frequency | Significance 23 | ----- | --------- | ------------- 24 | Delta | 1-3 Hz | sleep 25 | Theta | 4-7 Hz | relaxed 26 | Alpha | 8-12 Hz | eyes closed 27 | Beta | 13-30 Hz | eyes open 28 | Gamma | 31-50 Hz | concentration 29 | 30 | 31 | ### wiring details 32 | ![wiring diagram for system](https://github.com/nickmmark/brain-eeg-graph/blob/master/figures/eeg%20wiring%20diagram.png) 33 | * wiring diagram; green wires denote LCD connections, yellow denote EEG connection, red/black denote speaker connection * 34 | 35 | ### software overview 36 | The code uses the ```Brain``` and ```LiquidCrystal``` libraries. 37 | It collects EEG data for each power spectra and averages it over several seconds. It displays the average data on the LCD display using custom characters to form a bar graph. 38 | (in progress) It averages EEG data over 30 seconds to define an epoch and compares adjacent epochs to look for a change in sleep stage. 39 | (in progress) If a change in status from delta to beta wave predominant is identified suggesting the onset of REM sleep it generates a tone to alert the user and facilitate lucid dreaming. 40 | 41 | ```c++ 42 | // * 43 | // EEG analysis of Mindflex EEG data 44 | // v1.5 - with LCD bar graph output and rudimentary data smoothing 45 | // by Nick Mark 46 | // * 47 | 48 | // include the LCD and Brain libraries 49 | #include 50 | #include 51 | 52 | // activate the Brain serial port connection 53 | Brain brain(Serial); 54 | 55 | // configure variables for the EEG data 56 | byte signalStrength = 0; 57 | long deltaEnergy = 0; 58 | long alphaEnergy = 0; 59 | long betaEnergy = 0; 60 | long thetaEnergy = 0; 61 | 62 | // create arrays for data smoothing; numReadings defines the number of values to smooth together 63 | const int numReadings = 3; 64 | int index = 0; 65 | long readingsDelta[numReadings]; 66 | long totalDelta = 0; 67 | long averageDelta = 0; 68 | long readingsAlpha[numReadings]; 69 | long totalAlpha = 0; 70 | long averageAlpha = 0; 71 | long readingsBeta[numReadings]; 72 | long totalBeta = 0; 73 | long averageBeta = 0; 74 | long readingsTheta[numReadings]; 75 | long totalTheta = 0; 76 | long averageTheta = 0; 77 | 78 | // create array for long duration monitoring 79 | 80 | // activate and configure the LCD pin 12 = pin 11 = pin 10 = pin 5 = 81 | LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2); // pin 4 = pin 3 = pin 2 = 82 | int backLight = 13; 83 | 84 | // create the custom characters for the LCD bar-graphs 85 | uint8_t custom_0[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f}; //1 bar 86 | uint8_t custom_1[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f}; //2 bar 87 | uint8_t custom_2[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f}; //3 bar 88 | uint8_t custom_3[8] = { 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f}; //4 bar (half) 89 | uint8_t custom_4[8] = { 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f}; //5 bar 90 | uint8_t custom_5[8] = { 0x00, 0x00, 0x1f, 0x1F, 0x1F, 0x1F, 0x1F, 0x1f}; //6 bar 91 | uint8_t custom_6[8] = { 0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1f}; //7 bar 92 | uint8_t custom_7[8] = { 0x1f, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1f}; //8 bar (full) 93 | 94 | // configure remainders for bar graph 95 | int remSigStrength = 0; 96 | int remDelta = 0; 97 | int remAlpha = 0; 98 | int remBeta = 0; 99 | int remTheta = 0; 100 | 101 | void setup(){ 102 | // initialize the data smoothing array and set contents equal to zero 103 | for (int thisReading = 0; thisReading < numReadings; thisReading++) { 104 | readingsAlpha[thisReading] = 0; 105 | readingsDelta[thisReading] = 0; 106 | readingsBeta[thisReading] = 0; 107 | readingsTheta[thisReading] = 0; 108 | } 109 | 110 | // active the serial connection to the computer 111 | Serial.begin(9600); 112 | Serial.println("connection established"); 113 | 114 | // activate the LCD and set up the LCD backlight 115 | pinMode(backLight, OUTPUT); 116 | digitalWrite(backLight, HIGH); 117 | lcd.begin(16, 2); 118 | lcd.print("ready..."); 119 | 120 | // define the custom characters for the LCD controlled 121 | lcd.createChar(0, custom_0); //send the custom characters to the LCD 122 | lcd.createChar(1, custom_1); 123 | lcd.createChar(2, custom_2); 124 | lcd.createChar(3, custom_3); 125 | lcd.createChar(4, custom_4); 126 | lcd.createChar(5, custom_5); 127 | lcd.createChar(6, custom_6); 128 | lcd.createChar(7, custom_7); 129 | } 130 | 131 | void loop(){ 132 | if (brain.update()){ 133 | // calculate values for the data smoothing array 134 | totalDelta = totalDelta - readingsDelta[index]; 135 | readingsDelta[index] = (brain.readDelta() + brain.readDelta()); 136 | totalDelta = totalDelta + readingsDelta[index]; 137 | totalAlpha = totalAlpha - readingsAlpha[index]; 138 | readingsAlpha[index] = (brain.readLowAlpha() + brain.readHighAlpha()); 139 | totalAlpha = totalAlpha + readingsAlpha[index]; 140 | totalBeta = totalBeta - readingsBeta[index]; 141 | readingsBeta[index] = (brain.readLowBeta() + brain.readHighBeta()); 142 | totalBeta = totalBeta + readingsBeta[index]; 143 | totalTheta = totalTheta - readingsTheta[index]; 144 | readingsTheta[index] = (brain.readTheta()); 145 | totalTheta = totalTheta + readingsTheta[index]; 146 | 147 | index = index + 1; 148 | 149 | // calculate 3 second averages based on the array 150 | averageDelta = totalDelta / numReadings; 151 | averageAlpha = totalAlpha / numReadings; 152 | averageBeta = totalBeta / numReadings; 153 | averageTheta = totalTheta / numReadings; 154 | 155 | // reset the position in the array 156 | if (index >= numReadings) 157 | index = 0; 158 | 159 | // draw the screen labels 160 | lcd.clear(); 161 | lcd.setCursor(0,0); 162 | lcd.print("S D A B T"); 163 | 164 | // fill in the wavelength energies from the EEG reading 165 | lcd.setCursor(1,0); 166 | lcd.print(brain.readSignalQuality()); 167 | lcd.setCursor(4,0); 168 | lcd.print(brain.readDelta()/75000); 169 | lcd.setCursor(7,0); 170 | lcd.print(averageAlpha/10000); 171 | lcd.setCursor(10,0); 172 | lcd.print((brain.readHighBeta()+brain.readLowBeta())/10000); 173 | lcd.setCursor(13,0); 174 | lcd.print(brain.readTheta()/5000); 175 | 176 | // normalize the variables and divide to create the bar graph values 177 | byte signalStrength = (((brain.readSignalQuality())/15)/8); 178 | unsigned long deltaEnergy = (((averageDelta)/150000)/8); 179 | unsigned long alphaEnergy = (((averageAlpha)/15000)/8); 180 | unsigned long betaEnergy = (((averageBeta)/15000)/8); 181 | unsigned long thetaEnergy = (((averageTheta)/10000)/8); 182 | int remSigStrength = (((brain.readSignalQuality())/15)%8); 183 | int remDelta = (((averageDelta)/150000)%8); 184 | int remAlpha = ((averageAlpha/15000)%8); 185 | int remBeta = (((averageBeta)/15000)%8); 186 | int remTheta = (((averageTheta)/10000)%8); 187 | 188 | // fill in the bar graphs 189 | lcd.setCursor(0,1); 190 | for (int i=0; i