Arduino Brain Machine

Arduino Brain MachineI got an Arduino UNO for Christmas. Arduino is an open-source electronics prototyping platform. It can be used to create anything from a simple web server to a hand-held game. With a few additional inexpensive electronic components, you can create a working prototype of a machine that actually does something useful or fun.

For this particular article, I’ve chosen to emulate a fun electronic toy
– a ‘brain machine’ – that has already been created using a similar platform. A brain machine, also known as a ‘light and sound’ machine, is a meditation or relaxation device that takes
advantage of the brain’s own circuitry to help stimulate deeper or more
interesting meditation or relaxation. A typical hand-held brain machine will play anywhere from six to twelve different ‘sound and light’ shows. Each show might be ten minutes to an hour long. Just like an MP3 player, you choose which ‘show’ you want to play and press ‘play’. A typical show might be optimized for sleep, meditation, relaxation or conscious dreaming. A typical commercial brain machine today costs between one hundred and five hundred dollars depending on the capabilities included. We’re building ours for considerably less.

Arduino UNO’s architecture revolves around the ATMega328 chip, a microcontroller that is capable of receiving information from the environment and reacting to it. In 2007, Mitch Altman used the ATtiny2313 to create a brain machine that was featured in Make Magazine 10. (For more information about brain machines and what they’re about, download the pdf documentation from the above link.) The ATtiny2313 is in the same family of microcontrollers, but is made for smaller applications than the Arduino is capable of handling, so the Arduino should have no trouble with an adaptation of Mr. Altman’s example.

Some of the coding in the original example needed to be modified to work with the Arduino. Just for clarification, the coding used in the ATtiny is called ‘Firmware’ created using a C++ compiler and development environment. The Arduino uses an Integrated Development Environment (IDE) with a simplified form of C++. For this reason, conversion was not too difficult. Rounding off the background information, the firmware coding in Arduino culture is called a ‘sketch’.

One thing that makes things a little easier for me is that I have a twenty-year-old brain machine called a Synetic “Esprit” ‘Computerized Relaxation and Mental Fitness System’ that already has a pair of eyephones and earphones I can re-use. The eyephones have eight LED’s, four per eye, already attached to a standard 3.5mm stereo plug. The earphones are similarly standard.

The photo at the top of the article shows an early prototype. There is now a breadboard patch guide at the bottom of the article clearly diagramming the components.  Besides the Arduino UNO, I also needed these items:

2
3.5mm Stereo Jacks
1
10K ohm dual audio wheel potentiometer
3
220 ohm resistors
2
4.7K ohm resistors
18
wire leads in varying lengths
1
pair of compatible eyephones with stereo 3.5mm plug
1
earphones/headphones with standard stereo 3.5mm plug
1
small bread board

Here is Altman’s coding:

/*
Sound & Light Machine
Firmware
for use with ATtiny2313
Make Magazine issue #10
Mitch Altman
19-Mar-07
*/

#include              // this contains all the IO port definitions
#include       // definitions for interrupts
#include           // definitions for power-down modes
#include        // definitions or keeping constants in program memory

#define TIMER0_PRESCALE_1 1
#define TIMER0_PRESCALE_8 2
#define TIMER0_PRESCALE_64 3
#define TIMER0_PRESCALE_256 4
#define TIMER0_PRESCALE_1024 5
#define TIMER1_PRESCALE_1 1
#define TIMER1_PRESCALE_8 2
#define TIMER1_PRESCALE_64 3
#define TIMER1_PRESCALE_256 4
#define TIMER1_PRESCALE_1024 5

/*
The hardware for this project is very simple:
     ATtiny2313 has 20 pins:
       pin 1   connects to serial port programming circuitry
       pin 10  ground
       pin 12  PB0 - Left eye LED1
       pin 13  PB1 - Right eye LED1
       pin 14  OC0A - Left ear speaker (base-frequency)
       pin 15  OC1A - Right ear speaker (Offset Frequencies for binaural beats)
       pin 17  connects to serial port programming circuitry
       pin 18  connects to serial port programming circuitry
       pin 19  connects to serial port programming circuitry
       pin 20  +3v
    All other pins are unused

    This firmware requires that the clock frequency of the ATtiny 
      is the default that it is shipped with:  8.0MHz
*/

/*
The C compiler creates code that will transfer all constants into RAM when the microcontroller
resets.  Since this firmware has a table (brainwaveTab) that is too large to transfer into RAM,
the C compiler needs to be told to keep it in program memory space.  This is accomplished by
the macro PROGMEM (this is used, below, in the definition for the brainwaveTab).  Since the
C compiler assumes that constants are in RAM, rather than in program memory, when accessing
the brainwaveTab, we need to use the pgm_read_byte() and pgm_read_dword() macros, and we need
to use the brainwveTab as an address, i.e., precede it with "&".  For example, to access 
brainwaveTab[3].bwType, which is a byte, this is how to do it:
     pgm_read_byte( &brainwaveTab[3].bwType );
And to access brainwaveTab[3].bwDuration, which is a double-word, this is how to do it:
     pgm_read_dword( &brainwaveTab[3].bwDuration );
*/

// table of values for meditation 
//   start with lots of Beta (awake / conscious)
//   add Alpha (dreamy / trancy to connect with subconscious Theta that'll be coming up)
//   reduce Beta (less conscious)
//   start adding Theta (more subconscious)
//   pulse in some Delta (creativity)
//   and then reverse the above to come up refreshed
struct brainwaveElement {
  char bwType;  // 'a' for Alpha, 'b' for Beta, 't' for Theta, or 'd' for Delta ('0' signifies last entry in table
  unsigned long int bwDuration;  // Duration of this Brainwave Type (divide by 10,000 to get seconds)
} const brainwaveTab[] PROGMEM = {
  { 'b', 600000 },
  { 'a', 100000 },
  { 'b', 200000 },
  { 'a', 150000 },
  { 'b', 150000 },
  { 'a', 200000 },
  { 'b', 100000 },
  { 'a', 300000 },
  { 'b',  50000 },
  { 'a', 600000 },
  { 't', 100000 },
  { 'a', 300000 },
  { 't', 200000 },
  { 'a', 300000 },
  { 't', 300000 },
  { 'a', 150000 },
  { 't', 600000 },
  { 'a', 150000 },
  { 'b',  10000 },
  { 'a', 150000 },
  { 't', 600000 },
  { 'd',  10000 },
  { 't', 100000 },
  { 'd',  10000 },
  { 't', 100000 },
  { 'd',  10000 },
  { 't', 300000 },
  { 'a', 150000 },
  { 'b',  10000 },
  { 'a', 150000 },
  { 't', 300000 },
  { 'a', 150000 },
  { 'b',  10000 },
  { 'a', 200000 },
  { 'b',  50000 },
  { 'a', 200000 },
  { 'b', 150000 },
  { 'a', 150000 },
  { 'b', 200000 },
  { 'a', 100000 },
  { 'b', 250000 },
  { 'a',  50000 },
  { 'b', 600000 },
  { '0',      0 }
};

// This function delays the specified number of 1/10 milliseconds
void delay_one_tenth_ms(unsigned long int ms) {
  unsigned long int timer;
  const unsigned long int DelayCount=87;  // this value was determined by trial and error

  while (ms != 0) {
    // Toggling PD0 is done here to force the compiler to do this loop, rather than optimize it away
    for (timer=0; timer <= DelayCount; timer++) {PIND |= 0b0000001;};
    ms--;
  }
}

// This function blinks the LEDs (connected to PB0, PB1 - for Left eye, Right eye, respectively)
//   at the rate determined by onTime and offTime
//   and keeps them blinking for the Duration specified (Duration given in 1/10 millisecs)
// This function also acts as a delay for the Duration specified
void blink_LEDs( unsigned long int duration, unsigned long int onTime, unsigned long int offTime) {
  for (int i=0; i<(duration/(onTime+offTime)); i++) {
    PORTB |= 0b00000011;          // turn on LEDs at PB0, PB1
    delay_one_tenth_ms(onTime);   //   for onTime
    PORTB &= 0b11111100;          // turn off LEDs at PB0, PB1
    delay_one_tenth_ms(offTime);  //   for offTime
  }
}

// This function starts the Offset Frequency audio in the Right ear through output OC1A  (using Timer 1)
//   to create a binaural beat (between Left and Right ears) for a Brainwave Element
//   (the base-frequency of 400.641Hz is already assumed to be playing in the Left ear before calling this function)
//   and blinks the LEDs at the same frequency for the Brainwave Element
//   and keeps it going for the Duration specified for the Brainwave Element
// The timing for the Right ear is done with 16-bit Timer 1 (set up for CTC Mode, toggling output on each compare)
//   Output frequency = Fclk / (2 * Prescale * (1 + OCR1A) ) = 8,000,000 / (2 * (1 + OCR1A) )
void do_brainwave_element(int index) {
    char brainChr = pgm_read_byte(&brainwaveTab[index].bwType);
    if (brainChr == 'b') {
         // PORTB &= 0b00001100;  // (for debugging purposes only -- commented out for SLM)
         // PORTB |= 0b10000000;
      // Beta
      // start Timer 1 with the correct Offset Frequency for a binaural beat for the Brainwave Type
      //   to Right ear speaker through output OC1A (PB3, pin 15)
      OCR1A = 9637;  // T1 generates 415.024Hz, for a binaural beat of 14.4Hz
      // delay for the time specified in the table while blinking the LEDs at the correct rate
      //   onTime = 34.7ms, offTime = 34.7ms --> 14.4Hz
      blink_LEDs( pgm_read_dword(&brainwaveTab[index].bwDuration), 347, 347 );
      return;   // Beta
    }

    else if (brainChr == 'a') {
         // PORTB &= 0b00001100;  // (for debugging purposes only -- commented out for SLM)
         // PORTB |= 0b01000000;
      // Alpha
      // start Timer 1 with the correct Offset Frequency for a binaural beat for the Brainwave Type
      //   to Right ear speaker through output OC1A (PB3, pin 15)
      OCR1A = 9714;  // T1 generates 411.734Hz, for a binaural beat of 11.1Hz
      // delay for the time specified in the table while blinking the LEDs at the correct rate
      //   onTime = 45.1ms, offTime = 45.0ms --> 11.1Hz
      blink_LEDs( pgm_read_dword(&brainwaveTab[index].bwDuration), 451, 450 );
      return;   // Alpha
    }

    else if (brainChr == 't') {
         // PORTB &= 0b00001100;  // (for debugging purposes only -- commented out for SLM)
         // PORTB |= 0b00100000;
      // Theta
      // start Timer 1 with the correct Offset Frequency for a binaural beat for the Brainwave Type
      //   to Right ear speaker through output OC1A (PB3, pin 15)
      OCR1A = 9836;  // T1 generates 406.628Hz, for a binaural beat of 6.0Hz
      // delay for the time specified in the table while blinking the LEDs at the correct rate
      //   onTime = 83.5ms, offTime = 83.5ms --> 6.0Hz
      blink_LEDs( pgm_read_dword(&brainwaveTab[index].bwDuration), 835, 835 );
      return;   // Theta
    }

    else if (brainChr == 'd') {
         // PORTB &= 0b00001100;  // (for debugging purposes only -- commented out for SLM)
         // PORTB |= 0b00010000;
      // Delta
      // start Timer 1 with the correct Offset Frequency for a binaural beat for the Brainwave Type
      //   to Right ear speaker through output OC1A (PB3, pin 15)
      OCR1A = 9928;  // T1 generates 402.860Hz, for a binaural beat of 2.2Hz
      // delay for the time specified in the table while blinking the LEDs at the correct rate
      //   onTime = 225.3ms, offTime = 225.3ms --> 2.2Hz
      blink_LEDs( pgm_read_dword(&brainwaveTab[index].bwDuration), 2253, 2253 );
      return;   // Delta
    }

    // this should never be executed, since we catch the end of table in the main loop
    else {
         // PORTB &= 0b00001100;  // (for debugging purposes only -- commented out for SLM)
         // PORTB |= 0b00000010;
      return;      // end of table
    }
}

int main(void) {

  TIMSK = 0x00;  // no Timer interrupts enabled
  DDRB = 0xFF;   // set all PortB pins as outputs
  PORTB = 0x00;  // all PORTB output pins Off

  // start up Base frequency = 400.641Hz on Left ear speaker through output OC0A (using Timer 0)
  //   8-bit Timer 0 OC0A (PB2, pin 14) is set up for CTC mode, toggling output on each compare
  //   Fclk = Clock = 8MHz
  //   Prescale = 256
  //   OCR0A = 38
  //   F = Fclk / (2 * Prescale * (1 + OCR0A) ) = 400.641Hz
  TCCR0A = 0b01000010;  // COM0A1:0=01 to toggle OC0A on Compare Match
                        // COM0B1:0=00 to disconnect OC0B
                        // bits 3:2 are unused
                        // WGM01:00=10 for CTC Mode (WGM02=0 in TCCR0B)
  TCCR0B = 0b00000100;  // FOC0A=0 (no force compare)
                        // F0C0B=0 (no force compare)
                        // bits 5:4 are unused
                        // WGM2=0 for CTC Mode (WGM01:00=10 in TCCR0A)
                        // CS02:00=100 for divide by 256 prescaler
  OCR0A = 38;  // to output 400.641Hz on OC0A (PB2, pin 14)

  // set up T1 to accept Offset Frequencies on Right ear speaker through OC1A (but don't actually start the Timer 1 here)
  //   16-bit Timer 1 OC1A (PB3, pin 15) is set up for CTC mode, toggling output on each compare
  //   Fclk = Clock = 8MHz
  //   Prescale = 1
  //   OCR0A = value for Beta, Alpha, Theta, or Delta (i.e., 9520, 9714, 9836, or 9928)
  //   F = Fclk / (2 * Prescale * (1 + OCR0A) )
  TCCR1A = 0b01000000;  // COM1A1:0=01 to toggle OC1A on Compare Match
                        // COM1B1:0=00 to disconnect OC1B
                        // bits 3:2 are unused
                        // WGM11:10=00 for CTC Mode (WGM13:12=01 in TCCR1B)
  TCCR1B = 0b00001001;  // ICNC1=0 (no Noise Canceller)
                        // ICES1=0 (don't care about Input Capture Edge)
                        // bit 5 is unused
                        // WGM13:12=01 for CTC Mode (WGM11:10=00 in TCCR1A)
                        // CS12:10=001 for divide by 1 prescaler
  TCCR1C = 0b00000000;  // FOC1A=0 (no Force Output Compare for Channel A)
                        // FOC1B=0 (no Force Output Compare for Channel B)
                        // bits 5:0 are unused

  // loop through entire Brainwave Table of Brainwave Elements
  //   each Brainwave Element consists of a Brainwave Type (Beta, Alpha, Theta, or Delta) and a Duration
  // Seeing the LEDs blink and hearing the binaural beats for the sequence of Brainwave Elements
  //   synchs up the user's brain to follow the sequence (hopefully it is a useful sequence)
  int j = 0;
  while (pgm_read_byte(&brainwaveTab[j].bwType) != '0') {  // '0' signifies end of table
    do_brainwave_element(j);
    j++;
  }

  // Shut down everything and put the CPU to sleep
  TCCR0B &= 0b11111000;  // CS02:CS00=000 to stop Timer0 (turn off audio in Right ear speaker)
  TCCR1B &= 0b11111000;  // CS12:CS10=000 to stop Timer1 (turn off audio in Left ear speaker)
  MCUCR |= 0b00100000;   // SE=1 (bit 5)
  MCUCR |= 0b00010000;   // SM1:0=01 to enable Power Down Sleep Mode (bits 6, 4)
  delay_one_tenth_ms(10000);  // wait 1 second
  PORTB = 0x00;          // turn off all PORTB outputs
  DDRB = 0x00;           // make PORTB all inputs
  sleep_cpu();           // put CPU into Power Down Sleep Mode
}

In order for me to understand how it worked, I divided up my project into different bits of code — one for the eyephones, one for the earphones and one for series of blinks and tones that would eventually come out of both. This is the code I used for testing the earphones:

Update: 2011.02.15
This has changed.
Please visit the git repository instead!
https://github.com/LaughterOnWater/Arduino-Brain-Machine

This is the code I used to test my eyephones in order to be sure they would work with the final sketch:

Update: 2011.02.15
This has changed.
Please visit the git repository instead!
https://github.com/LaughterOnWater/Arduino-Brain-Machine

This is the adapted code for Arduino:

Update: 2011.02.15
This has changed.
Please visit the git repository instead!
https://github.com/LaughterOnWater/Arduino-Brain-Machine

Because Altman’s brain machine used a common cathode for his LED’s and my eyephones use a common anode, I had to reverse values for the HIGH and LOW settings to the LED pins. Likewise, analogWrite value ‘255’ means ‘off’ and value ‘0’ means ‘bright’. Also, I used a different method for turning off the machine after it had finished playing its tones and blinks, plus a restart button that automatically starts at the beginning once the machine has stopped playing.

This brain machine could be enhanced in a number of ways:

  1. I don’t own an oscilloscope, so I have no way of verifying whether the durations for beta, alpha, etc., are correct. If someone who has an oscilloscope wants to verify, feel free to contact me with a more correct number for the variable ‘DelayCount’.
  2. Currently sounds and lights are simultaneous. Both ears and eyes experience a blink or a tone at the same time. There is no reason with the arduino that we couldn’t alternate blinks and tones from one side to the other at various frequencies. Changing the ‘do_brainwave_element’ function to allow alternating sides for light and sound might be an excellent addition to this sketch.
  3. I would probably change the ‘do_brainwave_element’ function from ‘else if’ clauses to ‘case’ format for ease of construction of the first suggestion.
  4. The arduino has enough pins so that multiple tones and binaural beats could be achieved simultaneously. This could also be an interesting addition for a future version of the arduino brain machine.
  5. Make more than one sound and light show available in the same sketch. We could use some LED’s on the bread board to designate which light show is currently being played.
  6. Make the button so you can stop pause or stop in the middle of a session. Currently, the button is only functional when the machine has gone into sleep mode.
  7. Some people do not have stereo hearing, so it might be nice to offer an alternative to the binaural experience using pulsed tones.
  8. Also, it might be nice to add stereo percussive entrainment as an alternative to binaural beat tones.
  9. It could use a better volume limiter on the earphone volume, probably through an opamp. Currently the upper limit is too high and could be potentially dangerous to the eardrums.
  10. Last, it be interesting to try hooking this up to a localized magnetic field resonator synced with the LED’s.

Caveats:
Do not plug your earphones directly into your arduino without at least a 10K dual audio potentiometer of some sort. Check the volume first with the earphones off your ears. You can break your eardrums, break your arduino or break your earphones without something to limit the volume. If you make this machine, you take full responsibility for anything you damage, including your own health.

Below is a video of the Arduino Brain Machine at work. Turn down the volume – it’s a little loud. The sound you’re hearing is the binaural beat of the tones in each ear. In the original code, the light and sound show lasts for about 14 and a half minutes. I’ve reduced it to a few seconds in this example to show that the sleep mode works. Have fun!

I might add updates below here as I find out more useful things to come.

Update: February 15, 2011 –
All documents are now available in this github.com repository:
https://github.com/LaughterOnWater/Arduino-Brain-Machine

Arduino Brain Machine Breadboard Patch GuideUpdate: February 12, 2011 –
In front of the 10K ohm potentiometer, I’ve added to 4.7K ohm resistors to further reduce the highest audio volume to merely-annoyingly loud from ear-drum-destroyingly loud. I’ve also added a breadboard patch diagram in png format.

Update: February 10, 2011 –
The light and sound show for
ATtiny used a different timer than the Arduino. For that reason, a 14.25
minute show lasted about 6 minutes when DelayCount equalled 87. I
cranked DelayCount up to 200 and did a ratio to find that a delaycount
of 196 should give me about 14.25 minutes for my arduino. With testing,
this proved to be the case. While this may be mathematically correct, it
is not a replacement for direct measurement of frequency – but it
should be darned close.

Update: Januery 2013 –
The Tone Library, as it is, is not working properly with the current version of the Arduino IDE. This video helps fix that:

[youtube_sc url=”https://www.youtube.com/watch?v=fQqrKUHB8QQ” title=”Adruino%20Tone%20Library%20Fix” autohide=”1″ fs=”1″]

 

Good luck making your own Arduino Brain Machine!

7 Replies to “Arduino Brain Machine”

    1. Sorry for the extremely late reply. Please click the popup image next to the Update: February 12, 2011. You should see the image called “Arduino Brain Machine Breadboard Patch Guide”. It’s not a schematic, but it should be reasonably clear.

      Best of luck!
      Chris

Leave a Reply

Your email address will not be published. Required fields are marked *