Introduction

Biophysics is an integral part of biology, even though it is not a standard part of the K-12 curriculum. That is why the Biophysical Society provides additional information and lesson plans. Teaching biophysics allows for a large number of demonstration experiments and laboratory work for students to complete in lessons.

We would like to present a simple approach to support teaching challenging curriculum dealing with neural tissue in hands-on experiments. Unlike electrocardiography (ECG) and electromyography (EMG) measurements, where voltages measured on the human body are in the order of millivolts, electroencephalography (EEG) is measured on the head and voltages are in the order of microvolts. This makes ECG and EMG measurements a much bigger challenge. Although the construction of an EEG amplifier is not impossible, it is challenging in a typical school environment. Therefore, experiments with a commercially available, but relatively cheap, EEG headset are described below.


EEG headset

The Neurosky MindWave Mobile headset was chosen because of the low price and Bluetooth capability. There are many instructions for interfacing with the headset, for example using Arduino. For our purposes, we used a personal computer. The headset can be paired with a computer over Bluetooth and a program called ThinkGear Connector allows other software to receive data from the headset. Of course, the measured data are not comparable to commercial medical EEG devices because only one differential electrode is used instead of the many used in medical devices. However, even this one measured curve is sufficient for demonstration in the classroom and for simple tasks that students can perform by themselves.

This particular headset have to be paired with the computer via BlueTooth (technically speaking, headset acts as a virtual serial port while data from the serial port can be handled very easily in software). We learned that if there is a problem, fresh pair of batteries efficiently solves the problem.


Software

For the lecture demonstrations and motivation purposes, MindWave Mobile Starter Kit bundled with the headset is absolutely sufficient. MindWave Mobile Core application shows not only the raw data stream in form of graph (right upper part of the picture) as well as Fast Fourier transformation (FFT) spectrum (color bars), which can be used to estimate the spectrum components of the EEG data.

Students can see how their brain activity affects the displayed values of attention and the shape of the measured EEG curve, for example, while they try to solve a mathematical problem. The program window can be presented to the classroom via a data projector and EEG curve changes representing changes in voltage, measured on the volunteer's head, can be easily seen.


Because there are more frequency components than one, left part of the appliaction window shows most prevalent EEG rythms. Bottom-right part shows calculated values of "Attenttion" and "Meditation". These values are calculated according to the FTT spectrum of the EEG data and are calculated on the headset microchip, the MindWave Mobile Core application gets them from the headset to display it on the screen.

For the junior college level, EEG data analysis can be done. For this exercise, raw data have to be obtained from the headset. There are several methods, for example, if the matlab software is available, matlab script can be used.

To further ease the data collection, we used small appliacation called ThinkGear Connector. This app runs as a background process on the computer and can direct headset data from the serial port to an open network socket. This network socket can be for example written in the Processing programming language, which is a PC counterpart of the Arduino programming language called Wiring. Processing is very easy programming language taught in a beginning high school or university computer science classes.

We have modified the Processing sketch by Yang, by adding the capability of saving the raw data to the text file. The final sketch (mindwave.pde) can be downloaded here. Data can be then visualised and analyzed in any spreadsheet editor (for example Microsfot Excel).

These data correspond to the measured electrical voltage on the head of the pupil, i.e., they are a record of the electrical activity of the brain. Even if the Fast Fourier transformation is not known to students, straightforward estimation of prevalent EEG rhythms can be made. The most significant minima (circled in the left subfigure) are approximately 100 to 200 milliseconds apart, which means that the most significant frequencies will roughly be in the 5 to 10 Hz range. This simple exercise can be done with a ruler and printed sheet of data and verified by computer calculations made by the teacher (right panel).



Lab handouts



Here you can download lab handouts and customize it for your needs. In the left panel, there is information about nerve tissue that students can fill out during a lecture. In the right panel, there is information about EEGs. Several EEG curves that students can analyze independently (normal EEG, deep sleep, and EEG during an epileptic seizure) can be downloaded here.







Conclusion



We believe that laboratory exercises like this are not beneficial only for future medical doctors, but for all students that study biology. Biophysical measurements, or laboratory exercises involving medical devices, with first-hand experience, are especially popular because the students can be direct participants. If these laboratory exercises strengthen students’ operational data processing skills, they are doubly useful. Although it may seem that these exercises are only usable at the college level, they can act as substantial motivational factors at the secondary school level. The cost of the EEG headset is within the means of most public schools.


Source code: File mindwave.pde



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
// Sample sketch to illustrate MindWave support in Processing

// for graphing
import processing.serial.*;
import java.util.Iterator;

eegPort eeg;
Serial serialPort;
String portName;
PFont font;
String portNames[];

// for data saving
output = createWriter("data.txt");   

// Application state
final int APP_SERIAL_SELECT = 1;
final int APP_CONNECTING = 2;
final int APP_CONNECTED = 3;

int appState = APP_SERIAL_SELECT;
int selected = -1;

int width = 800, height = 600;

void setup() {
  size(800, 600);
//  font = 'Arial';
 // textFont();
  
  portNames = Serial.list();
  for (int i = 0; i < portNames.length; i++) {
    println(portNames[i]);
  }
  
  smooth();
}

void draw() {
  switch (appState) {
    case APP_SERIAL_SELECT:
      drawSerialSelect();
      break;
    case APP_CONNECTING:
      drawConnecting();
      break;
    case APP_CONNECTED:
      drawConnected();
      break;
  }
}

// Drawing when we're connected
void drawConnected() {
  int lastEventInterval = millis() - eeg.lastEvent;
  
  background(255);
  
  if (mousePressed) {
    eeg.refresh();
  }
  
  textAlign(LEFT);
  fill(0);
  text("Port: " + portName, 5, 20);
  text("Dongle state: " + eeg.portDongleState, 5, 40);
  text("Poor signal: " + eeg.poorSignal, 5, 60);
  text("Attention: " + eeg.attention, 5, 80);
  text("Meditation: " + eeg.meditation, 5, 100);
  text("Last event: " + lastEventInterval + " ms ago", 5, 120);
  text("Raw buffer size: " + eeg.rawDataBuffer.size(), 5, 140);
  text("Raw data sequence: " + eeg.rawSequence, 5, 160);
  text("Vector sequence: " + eeg.vectorSequence, 5, 180);
  text("Vector buffer size: " + eeg.vectorBuffer.size(), 5, 200);
  text("Serial read state: " + eeg.portReadState, 5, 220);
  text("Failed checksum count: " + eeg.failedChecksumCount, 5, 240);
  text("Click mouse for a second to reset", 5, 260);
  
  // Draw signal
  noStroke();
  if (eeg.poorSignal < 50 && lastEventInterval < 500) {
    // good signal
    fill(0, 255, 0);
    ellipse(150, 320, 100, 100);
  } else {
    // bad signal
    fill(255, 0, 0);
    ellipse(150, 320, 100, 100);
  }
  
  textAlign(CENTER);
  fill(0);
  text("Attention", 400, 20);
  text("Meditation", 600, 20);
  
  if (eeg.lastAttention > 0) {
    text(millis() - eeg.lastAttention + " ms old", 400, 180);
  }

  if (eeg.lastMeditation > 0) {
    text(millis() - eeg.lastMeditation + " ms old", 600, 180);
  }

  // Draw attention
  noFill();
  stroke(0);
  ellipse(400, 90, 127, 127);
  fill(204, 102, 0);
  noStroke();
  ellipse(400, 90, eeg.attention, eeg.attention);
  
  
  // Draw meditation
  noFill();
  stroke(0);
  ellipse(600, 90, 127, 127);
  fill(108, 102, 240);
  noStroke();
  ellipse(600, 90, eeg.meditation, eeg.meditation);
  
  // Draw signal
  
  
  // Chart vector values
  // first get maximum value
  int maxValue = 0;
  Iterator<eegPort.vectorObs> iterator;
  iterator = eeg.vectorBuffer.iterator();
  int vectorCount = eeg.vectorBuffer.size();

  int skip = 0;
  if (vectorCount > 200) {
    skip = vectorCount - 200;
  }
  
  int i = -1;
  
  while (iterator.hasNext()) {
    eegPort.vectorObs vobs = iterator.next();
    if (++i < skip) {
      continue;
    }
    
    if (vobs.vectorValue > maxValue) {
      maxValue = vobs.vectorValue;
    }
  }
  
  iterator = eeg.vectorBuffer.iterator();

  // we are interested in the last 400 observations
  
  i = -1;
  int j = 0;
  int prevValue = 0;
  int x = 0, y = 0;
  int prevX = 0, prevY = 0;
  
  stroke(0);
  
  // we are drawing between 0 and 800 in width, and between 400 and 600 in height
  while (iterator.hasNext()) {
    eegPort.vectorObs vobs = iterator.next();
    if (++i < skip) {
      continue;
    }
    
    x = j*4;
    y = (int)(580 - 200.0*vobs.vectorValue/maxValue);
    if (j > 0) {
      line(prevX, prevY, x, y);
    }
    
    prevValue = vobs.vectorValue;
    prevX = x;
    prevY = y;
    j++;
  }

  // save last value to file
  output.println(vobs.vectorValue);     
  
  // chart attention
  int attentionCount = eeg.attentionBuffer.size();

  skip = 0;
  if (attentionCount > 200) {
    skip = attentionCount - 200;
  }
  
  Iterator<Integer> attentionIterator = eeg.attentionBuffer.iterator();

  // we are interested in the last 200 observations
  
  i = -1;
  j = 0;
  prevValue = 0;
  x = 0; y = 0;
  prevX = 0; prevY = 0;
  
  stroke(204, 102, 0);
  
  // we are drawing between 0 and 800 in width, and between 400 and 600 in height
  while (attentionIterator.hasNext()) {
    int attention = attentionIterator.next();
    if (++i < skip) {
      continue;
    }
    
    x = j*4;
    y = (int)(580 - 200.0*attention/255);
    if (j > 0) {
      line(prevX, prevY, x, y);
    }
    
    prevValue = attention;
    prevX = x;
    prevY = y;
    j++;
  }

  // chart meditation
  int meditationCount = eeg.meditationBuffer.size();

  skip = 0;
  if (meditationCount > 200) {
    skip = meditationCount - 200;
  }
  
  Iterator<Integer> meditationIterator = eeg.meditationBuffer.iterator();

  // we are interested in the last 200 observations
  
  i = -1;
  j = 0;
  prevValue = 0;
  x = 0; y = 0;
  prevX = 0; prevY = 0;
  
  stroke(108, 102, 240);
  
  // we are drawing between 0 and 800 in width, and between 400 and 600 in height
  while (meditationIterator.hasNext()) {
    int meditation = meditationIterator.next();
    if (++i < skip) {
      continue;
    }
    
    x = j*4;
    y = (int)(580 - 200.0*meditation/255);
    if (j > 0) {
      line(prevX, prevY, x, y);
    }
    
    prevValue = meditation;
    prevX = x;
    prevY = y;
    j++;
  }
}

void drawConnecting() {
  background(255);
  
  text("Connecting to " + portName + ", please wait...", 5, 20);
}

// Serial selection
void drawSerialSelect() {
  background(255);
  
  int hover = (int)Math.round(Math.floor(mouseY/20));
  
  if (mousePressed) {
    selected = hover;
  }
  
  for (int i = 0; i < portNames.length; i++) {
    if (i == selected) {
      fill(0);
      rect(0, i*20, width, 20);
      fill(255);
      
      portName = portNames[i];
      println("selected " + portName);
      serialPort = new Serial(this, portName, 115200);
      appState = APP_CONNECTING;
      eeg = new eegPort(this, serialPort);
      delay(500);
      eeg.refresh();
    } else if (i == hover) {
      fill(200, 200, 240);
      noStroke();
      rect(0, i*20, width, 20);
      fill(0);
    } else {
      fill(0);
    }
    text(portNames[i], 5, (i+1)*20);
  }
}

void serialEvent(Serial p) {
  while (p.available() > 0) {
    int inByte = p.read();
    eeg.serialByte(inByte);
    if (inByte == 170 && appState < APP_CONNECTED) {
      println("Connected");
      appState = APP_CONNECTED;
      frameRate(10);
    }
  }
}

 void keyPressed() {
  output.flush();      // on keypress...
  output.close();      // ...we close the file...
  exit();              // ...and terminate program.
}

If you have any questions or comments, please contact the authors at .