trippy rgb brain machinuino

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

trippy rgb brain machinuino

Post by mtbf0 »

combined my arduino porkypine and orgonan projects and the trippy rgb brain machinuino was born. unfortunately, i have done something obscure to the sketch and building it now fails somewhere during the link.

just couldn't wait to post the pictures.

Image

Image

it's built with a resonatorless boarduino. the audio outputs are the OCR1A, (pin 9), and OCR1B, (pin 10) and the leds are connected to PORTC, (analog pins 1-6). i built it without a resonator so i could maybe build the thing into a sleep mask using a lilypad board from sparkfun. also figured i could port it to run on the minipov board which also lacks a resonator.

anyway, i'll post bits of the code when i figure out what i've done to it.

adafruit
 
Posts: 12151
Joined: Thu Apr 06, 2006 4:21 pm

Post by adafruit »

your stuff is -strange-!

lou
 
Posts: 91
Joined: Fri Jul 11, 2008 9:39 am

Re: trippy rgb brain machinuino

Post by lou »

How are you modulating the RGB? The original design is single-color, but the additional information has to come somewhere. Random? 3 simultaneous brainwave frequencies?

Does having RGB make any actual difference, or is it just pretty? Since the light is shining through your eyelids, can you even see anything besides red?

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Post by mtbf0 »

ladyada wrote:your stuff is -strange-!
well. uh, can't be helped.

this is, of course, a variant of mitch altman's project from make magazine #10.

anyway it generates a base frequency at 200Hz. offset frequencies of 201Hz-217Hz in 1Hz increments can be selected giving binaural beats in the range of 1Hz-17Hz. the leds are controlled by comparing the phases of the base and offset waveforms, so although the code's a little hairier than mitch's, adding new frequncies is easy, (see notestep table from orgonan).

still can't figure out what i did to the sketch, though. i was sticking in conditionals all over the place to try to get it to build for a minipov.

and, oh, yeah. i'm usingthislittle power supply from bodhilabs. it's like a surface mount mintyboost glued to the back of a 1aa cell holder. it cuts down on the weight of the project and gives me 3.3v so i can drive the blue and green leds.

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Re: trippy rgb brain machinuino

Post by mtbf0 »

lou wrote:How are you modulating the RGB? The original design is single-color, but the additional information has to come somewhere. Random? 3 simultaneous brainwave frequencies?

Does having RGB make any actual difference, or is it just pretty? Since the light is shining through your eyelids, can you even see anything besides red?
right now i've just got the blue and green fading in and out as the red flashes. the intensity of each is controlled with 7 bit pwm.

everything happens in an interrupt service that occurs at 15625Hz. divide that by 128, (7 bits), and you get a pwm frequency of ~122Hz which i hope is too fast to screw up the entrainment.

i'm using 3.3v boost power supply, so the green and blue are nice and bright and, yes, are visible through my eyelids. my motivation for using rgb leds was just to see if i could do it. it definitely adds something to the visual hallucinations, but i have yet to figure out exactly what i want to do with it. i figure being able to dial down the light intensity at lower frequencies will be nice.

i might add a third entry in the brainwave table to select a color.

first i have to figure out how i broke my code.

lou
 
Posts: 91
Joined: Fri Jul 11, 2008 9:39 am

Re: trippy rgb brain machinuino

Post by lou »

mtbf0 wrote:everything happens in an interrupt service that occurs at 15625Hz. divide that by 128, (7 bits), and you get a pwm frequency of ~122Hz which i hope is too fast to screw up the entrainment.
That ought to be fast enough. If it isn't, you could try smoothing the actual power delivered to the LEDs with capacitors (using open drain outputs if driving the LEDs directly from port pins) just as one would do with a DC supply to reduce ripple. Not so much as to affect response anywhere near 17Hz, of course.

Is that a different rate than the one used by the original brain machine?
mtbf0 wrote:i'm using 3.3v boost power supply, so the green and blue are nice and bright and, yes, are visible through my eyelids.
If you end up getting good effects with the additional colors, you might want to bump up the power on the green and blue (or reduce it on red). http://cat.inist.fr/?aModele=afficheN&cpsidt=2987732 It's up to you, of course.

I've had this idea before, but aborted it due to the reduced response to colors other than red.
mtbf0 wrote:my motivation for using rgb leds was just to see if i could do it.
That's often a legitimate reason for doing things like this. ;)
mtbf0 wrote: first i have to figure out how i broke my code.
Ah, yes. That would tend to get in the way. I hate when I do that.

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Post by mtbf0 »

aaaaarrrgh!

er, aha!

after staring at code for a day.

after determining that it would build for both a mega168 and a tiny2313 if i just borrowed the makefile from the original brain machine.

after staring at code for another day.

just moved the sketch from one machine to another and it built just fine.

arduino-0010 on both machines.

no time to test this morning. the post office beckons.

aaaaarrrgh!

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Post by mtbf0 »

aaaaarrrgh!

copied slm.pde to slm.c so i could try minipov build. arduino tried to link slm.pde with slm.c and, needless to say, found multiple definitions of everything.

never even noticed the separate tab for slm.c.

digital idjit... sniff. moan.

too stoopid too live.

collinmel
 
Posts: 58
Joined: Thu Aug 07, 2008 12:16 pm

Post by collinmel »

mtbf0 wrote:digital idjit... sniff. moan.

too stoopid too live.
Stop with such crazy talk! (at least until after you post the code)

seriously, I'd love to give it a go - I've been meaning to throw together a Theta-heavy brain machine for quite some time.
keep up with the awesomeness!

User avatar
jetcityorange
 
Posts: 5
Joined: Thu Aug 07, 2008 4:01 pm

binaural beat MP3 files

Post by jetcityorange »

If y'all wanna play with binaural beats as a way to "influence" specific brainwaves, I have a web page you may be interested in: JetCityOrange.com/meditation/binaural-beats.html

Free MP3 files to download and play with. There's a set of meditation timers too (with more in the works).

enjoy!

User avatar
jim
 
Posts: 106
Joined: Wed Feb 20, 2008 12:15 am

Post by jim »

this looked interesting and over my head. Using PWM for the audio...

http://www.tahina.priv.at/electronics.html#index3h1

the "orgonan" sounds interesting thou, i hope to check it out soon.

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Post by mtbf0 »

jim wrote:this looked interesting and over my head. Using PWM for the audio...

http://www.tahina.priv.at/electronics.html#index3h1

the "orgonan" sounds interesting thou, i hope to check it out soon.
yeah, i looked at that and at mitch altman's audio code for the mignonette. looking at the code just made me confused, so i grabbed a pad, a pen and my dirty clothes and went down to the laundromat and drew pictures 'til i figured out what i needed to do.

the really really cool thing in the code you cite is comparing the phase of the base and offset waves to control the leds. easiest part to code but truly inspired.

i've a bug in my pwm code for the leds. soon as it's fixed i'll post the code.

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Post by mtbf0 »

mtbf0 wrote:i've a bug in my pwm code for the leds. soon as it's fixed i'll post the code.
well, that was embarassing. made the make: blog and i lose a wire off my bodhilabs 3.3v boost supply and can't debug the color fading code. well, i could have, but i'd have been unable to test it. anyway, a little desoldering, a little soldering and a couple of volatile declarations later and all's well.

rather than try to explain how it works, i'll wait for questions.

so here's the code.

Code: Select all

#include "avr/io.h"             // i/o definitions
#include "avr/interrupt.h"      // definitions for interrupts
#include "avr/pgmspace.h"       // definitions for keeping data in program memory
#include "avr/sleep.h"          // definitions for idling mcu

//
//  mcu dependent definitions
//  for mega168 leds are attached to portc, for tiny2313 to portb
//
#ifdef __AVR_ATmega168__
#define LRED PC0
#define LGRN PC1
#define LBLU PC2
#define RRED PC3
#define RGRN PC4
#define RBLU PC5
#define Timer1Mask TIMSK1
#define SleepModeIdle() SMCR &= ~((1 << SM2) | (1 << SM1) | (1 << SM0))
#define SleepEnable() SMCR |= (1 << SE)
#elif defined __AVR_ATtiny2313__
#define LRED PB0
#define LGRN PB1
#define LBLU PB2
#define RRED PB5
#define RGRN PB6
#define RBLU PB7
#define SleepModeIdle() MCUCR &= ~((1 << SM1) | (1 << SM0))
#define SleepEnable() MCUCR |= (1 << SE)
#define Timer1Mask TIMSK
#else
#error "Target must be ATMEGA168 or ATTINY2313"
#endif

struct notestep {               // type for a 24 bit fixed point value
  uint8_t integer;
  uint16_t fraction;
};

//
// names for offset frequencies correspond to their positions in the following
// table, plus a value for the end of a tune
//
enum {DELTA2, DELTA3, THETA4, THETA5, THETA6, THETA7, ALPHA8, ALPHA9,
      ALPHA10, ALPHA11, ALPHA12, ALPHA13, BETA14, BETA15, BETA16, BWEND};
//
// the constants used to build the note table are calculated by multiplying
// the note frequency by the size of the sinewave table, (256), then shifting
// that result 16 bits to the left, (i.e. multiply by 65536), to move the integer
// part and make room for a 16 bit fraction, and finally dividing the whole thing
// by the frequency of the interrupt that does the audio output, (15625Hz).
//
// the interrupt frequency is F_CPU / (256 (timer TOP value) * 2) (because timer counts
// up then down between interrupts).
//
// that's (note_freq * 256 * 65536) / (F_CPU / 512)
//
// which is (note_freq * 8589934592) / F_CPU
//
// what we wind up with is a table of 24 bit fixed point values, (8 bit integer
// and 16 bit fraction), that can be used as an increment to index through the
// sine wave table while the frequency is being played.
//
#define notecalc(s) {(uint8_t)(s>>16),(uint16_t)(s&0xffff)}
const struct notestep notetab[] PROGMEM = {
  notecalc ((202ULL * 8589934592ULL) / (uint64_t)F_CPU),		// delta
  notecalc ((203ULL * 8589934592ULL) / (uint64_t)F_CPU),
  notecalc ((204ULL * 8589934592ULL) / (uint64_t)F_CPU),		// theta
  notecalc ((205ULL * 8589934592ULL) / (uint64_t)F_CPU),
  notecalc ((206ULL * 8589934592ULL) / (uint64_t)F_CPU),
  notecalc ((207ULL * 8589934592ULL) / (uint64_t)F_CPU),
  notecalc ((208ULL * 8589934592ULL) / (uint64_t)F_CPU),		// alpha
  notecalc ((209ULL * 8589934592ULL) / (uint64_t)F_CPU),
  notecalc ((210ULL * 8589934592ULL) / (uint64_t)F_CPU),
  notecalc ((211ULL * 8589934592ULL) / (uint64_t)F_CPU),
  notecalc ((212ULL * 8589934592ULL) / (uint64_t)F_CPU),
  notecalc ((213ULL * 8589934592ULL) / (uint64_t)F_CPU),		// beta
  notecalc ((214ULL * 8589934592ULL) / (uint64_t)F_CPU),
  notecalc ((215ULL * 8589934592ULL) / (uint64_t)F_CPU),
  notecalc ((216ULL * 8589934592ULL) / (uint64_t)F_CPU),
  notecalc ((217ULL * 8589934592ULL) / (uint64_t)F_CPU)
};

//
//  table of beat durations in 1/100 sec increments
//
enum {N1s, N5s, N10s, N15s, N20s, N25s, N30s, N60s};
#if F_CPU == 8000000
uint16_t durtab[] = { 100, 500, 1000, 1500, 2000, 2500, 3000, 6000 };
#elif F_CPU == 16000000
uint16_t durtab[] = { 200, 1000, 2000, 3000, 4000, 5000, 6000, 12000 };
#endif

#define BETA BETA14
#define ALPHA ALPHA11
#define THETA THETA7
#define DELTA DELTA2

uint8_t bwtab[] PROGMEM = {
  BETA,  N60s,  ALPHA, N10s,  BETA,  N20s,  ALPHA, N15s,  BETA,  N15s,  ALPHA, N20s,
  BETA,  N10s,  ALPHA, N30s,  BETA,  N5s,   ALPHA, N60s,  THETA, N10s,  ALPHA, N30s,
  THETA, N20s,  ALPHA, N30s,  THETA, N30s,  ALPHA, N15s,  THETA, N60s,  ALPHA, N15s,
  BETA,  N1s,   ALPHA, N15s,  THETA, N60s,  DELTA, N1s,   THETA, N10s,  DELTA, N1s,
  THETA, N10s,  DELTA, N1s,   THETA, N30s,  ALPHA, N15s,  BETA,  N1s,   ALPHA, N15s,
  THETA, N30s,  ALPHA, N15s,  BETA,  N1s,   ALPHA, N20s,  BETA,  N5s,   ALPHA, N20s,
  BETA,  N15s,  ALPHA, N15s,  BETA,  N20s,  ALPHA, N10s,  BETA,  N25s,  ALPHA, N5s,
  BETA,  N60s,  BWEND, BWEND
};

const uint8_t sinetab[] PROGMEM = {
  127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,
  176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,
  217,219,221,223,225,227,229,231,233,234,236,238,239,240,242,243,
  244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,
  254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,
  244,243,242,240,239,238,236,234,233,231,229,227,225,223,221,219,
  217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,
  176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,
  127,124,121,118,115,111,108,105,102, 99, 96, 93, 90, 87, 84, 81,
   78, 76, 73, 70, 67, 64, 62, 59, 56, 54, 51, 49, 46, 44, 42, 39,
   37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 16, 15, 14, 12, 11,
   10,  9,  7,  6,  5,  5,  4,  3,  2,  2,  1,  1,  1,  0,  0,  0,
    0,  0,  0,  0,  1,  1,  1,  2,  2,  3,  4,  5,  5,  6,  7,  9,
   10, 11, 12, 14, 15, 16, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
   37, 39, 42, 44, 46, 49, 51, 54, 56, 59, 62, 64, 67, 70, 73, 76,
   78, 81, 84, 87, 90, 93, 96, 99,102,105,108,111,115,118,121,124
};

//
// these data help us make our way through a song
//
volatile uint8_t* song_ptr;	  	// pointer to position in song
volatile uint8_t song_flags;            // some, er one flag, PLAY
#define S_PLAY 0
uint8_t song_note_tone;                 // note retrieved from song
uint8_t song_note_time;                 // whole note, half note, whatever from song
uint16_t song_note_duration;            // count down actual duration of note in .01 sec
//
// these data are used for tone generation
//
struct notestep basestep = notecalc ((200ULL * 8589934592ULL) / (uint64_t)F_CPU);
volatile struct notestep offsetstep;	// step size for offset frequency
struct notestep baseaccumulator;	// base frequency phase accumulator
struct notestep offsetaccumulator;	// offset frequency phase accumulator
uint8_t basephase;			// base frequency phase
uint8_t offsetphase;			// offset frequency phase
uint8_t basesample;			// base frequency sample
uint8_t offsetsample;			// offset frequency sample
//
// these data along with the base and offset phase values control the leds
//
uint8_t ledstate;
uint8_t laststate;
uint8_t portbuffer;
//
// colorfading datums
//
uint8_t iteration;                      // iteration counter for fading blue and green
uint8_t redcomp;
uint8_t greencomp;
uint8_t bluecomp;
volatile uint8_t red;                   // red intensity, 0-127, inclusive
volatile uint8_t green;                 // green intensity, 0-127, inclusive
volatile uint8_t blue;                  // blue intensity, 0-127, inclusive
//
// these data are used for time keeping
//
uint8_t intcount = 156;                 // count interrupts 156.25 = .01 sec
volatile uint8_t cents;                 // .01 sec elapsed, used to correct time

ISR (TIMER1_OVF_vect) {
  if (song_flags & (1 << S_PLAY)) {
//
// first output sample
//
    OCR1A = basesample;
    OCR1B = offsetsample;
#ifdef __AVR_ATtiny2313__
    PORTB = (PORTB & ((1 << PB3) | (1 << PB4))) | portbuffer;
#elif defined __AVR_ATmega168__
    PORTC = portbuffer;
#endif
//
// do timing stuff
//
    if (--intcount == 0) {              // count interrupts
      cents++;                          // increment elapsed .01 seconds
      intcount = (cents & 0x03) ? 156 : 157;// ludicrous attention to precision
      if (--song_note_duration == 0) {  // note finished ?
        song_note_tone = pgm_read_byte (song_ptr++) & 0x0f;
        song_note_time = pgm_read_byte (song_ptr++) & 0x07;
        song_note_duration = durtab[song_note_time];
        if (song_note_tone < BWEND) {
          offsetstep.integer = pgm_read_byte (&(notetab[song_note_tone].integer));
          offsetstep.fraction = pgm_read_word (&(notetab[song_note_tone].fraction));
        }
        else {
          song_flags &= ~(1 << S_PLAY);
          offsetstep.integer = 0;
          offsetstep.fraction = 0;
        }
      }
    }
//
// now get next offset frequency sample
//
    offsetaccumulator.fraction += offsetstep.fraction;
    if (SREG & (1 << SREG_C))	 	// on carry
      offsetaccumulator.integer++;	// add 1 to integer part
    offsetaccumulator.integer += offsetstep.integer;
    offsetphase = offsetaccumulator.integer;// move integer part of accumulator to
					// phase pointer
    if (offsetaccumulator.fraction & 0x8000)// if accumulator fraction > 1/2
      offsetphase++;			// round up
    offsetsample = pgm_read_byte(&(sinetab[offsetphase]));// get sample for next time.
//
// then get next base frequency sample
//
    baseaccumulator.fraction += basestep.fraction;
    if (SREG & (1 << SREG_C))	 	// on carry
      baseaccumulator.integer++;	// add 1 to integer part
    baseaccumulator.integer += basestep.integer;
    basephase = baseaccumulator.integer;// move integer part of accumulator to
					// phase pointer
    if (baseaccumulator.fraction & 0x8000)// if accumulator fraction > 1/2
      basephase++;			// round up
    basesample = pgm_read_byte(&(sinetab[basephase]));// get sample for next time.
//
// figure out next led state for red
//
    ledstate = (offsetphase - basephase) & 0x80;
    if (laststate ^ ledstate) {
      laststate = ledstate;
      portbuffer ^= (1 << LRED) | (1 << RRED);
    }
//
// do pwm for green and blue
//
    if (iteration == 128) {
      iteration = 0;
      redcomp = red;
      greencomp = green;
      bluecomp = blue;
      portbuffer |= (1 << LGRN) | (1 << LBLU) | (1 << RGRN) | (1 << RBLU);
    }
//    if (redcomp == iteration) portbuffer &= ~((1 << LRED) | (1 << RRED));
    if (greencomp == iteration) portbuffer &= ~((1 << LGRN) | (1 << RGRN));
    if (bluecomp == iteration) portbuffer &= ~((1 << LBLU) | (1 << RBLU));
    iteration += 1;
  }
}

void setup (void) {
//
// first turn off some stuff the arduino environment turns on by default
//
#ifdef Wiring_h
  TCCR0B = 0;				// stop timer 0
  TCCR2B = 0;				// stop timer 2
  TIMSK0 = 0;				// disable timer0 overflow interrupt
#endif
//
// then get the data direction registers set
//
  DDRD = (1 << PD1);			// PD0 is serial in, PD1 is serial out,
					// PD2 is wakeup button
#ifdef __AVR_ATmega168__
  DDRC = 0x3f;				// PC0-5 are outputs for leds
  DDRB = (1 << PB1) | (1 << PB2);	// OC1A, OC1B are audio outputs
#elif defined __AVR_ATtiny2313__
  DDRB = 0xff;				// all outputs
#endif
//
// now set up timer1 - phase and frequency correct pwm mode 8
// frequency will be 8MHz / 1 / 256 / 2 = 15625Hz
// that's FCPU / prescaler / (TOP + 1) and then we divide by 2 because mode 8
// gives dual slope operation
//
  TCCR1A = (1 << COM1A1) | (1 << COM1B1);// clear OC1A/B on upcount
					// set OC1A/B on douncount
  TCCR1B = (1 << WGM13) | (1 << CS10);	// phase and frequency correct
					// pwm, mode 8, prescale = 1
  ICR1 = 255;				// TOP = 255
//
// enable and disable some interrupts
//
  Timer1Mask = (1 << TOIE1);		// enable timer 1 overflow interrupt
}

uint8_t mycents;
uint8_t foo;
uint8_t inc;

#ifndef Wiring_h
int main (void) {
  setup ();
  sei ();
#else
void loop (void) {
#endif
  green = 63;                           // turn on blue and green at 50%
  blue = 0;
  song_ptr = &bwtab[0];		        // point to beginning of brainwave sequence
  song_note_duration = 1;               // kludge eliminates 655 second wait
  song_flags |= (1 << S_PLAY);          // start sequence
  while (song_flags & (1 << S_PLAY)) {  // let sequence run as long as it likes
    if (cents != mycents) {
      mycents = cents;
      foo = (foo + 1) & 15;
      if (foo == 0) {                   // this will update pwm values ca 6 times per second
        if (green == 0) inc = 1;
        else if (green == 63) inc = 255;
        green += inc;
        blue -= inc;
      }
    }
  }
  TCCR1B &= ~((1 << CS02) || (1 << CS01) || (1 << CS00));// stop timer 1
#ifdef __AVR_ATmega168__
  PORTC = 0;				// make sure leds are off
  DDRC = 0;				// make all PORTC pins inputs
#endif
  PORTB = 0;                            // all off
  DDRB = 0;				// make all PORTB pins inputs
  DDRD = 0;				// make all PORTD pins inputs
  SleepModeIdle();
  SleepEnable();
  sleep_cpu ();				// zzzzzzzzzz
}
// ... ad nauseum
Last edited by mtbf0 on Thu Sep 25, 2008 8:33 pm, edited 1 time in total.

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Post by mtbf0 »

some hardware pictures. click through to flickr to view notes.

Image

Image

100% organic version.

Image

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Post by mtbf0 »

i finally got around to building the minipov3 based version.

Image

when i loaded up the firmware for the first time nothing happened. no sound. no light. it seems that someone forgot to enable interrupts. if you don't want to be like someone change the following bit of code

Code: Select all

#ifndef Wiring_h
int main (void) {
  setup ();
#else
void loop (void) {
#endif 
to

Code: Select all

never mind.  editted the post with the code to reflect the change.  if you're still wondering, i added a call to sei () right after the call to setup ().
every once in a while it's nice to be able to solve a really stupid mistake in just a minute or two.

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”