• If you are citizen of an European Union member nation, you may not use this service unless you are at least 16 years old.

  • Get control of your email attachments. Connect all your Gmail accounts and in less than 2 minutes, Dokkio will automatically organize your file attachments. You can also connect Dokkio to Drive, Dropbox, and Slack. Sign up for free.


Final project- Theremin

Page history last edited by Rodrigo Ornelas 7 years, 10 months ago


For my final project I built a capacitive, digital Theremin that uses MIDI.

Inspiration : http://www.youtube.com/watch?v=K6KbEnGnymk


and many other youtube videos!

All required documentation and diagrams is in the photos and videos on the folder. (They are too big for this page) View "Final project documentation folder" 

  • Design point of view (what are you designing a player for?)- MIND MAP ✓.
  • Verplank diagram ✓.
  • Photos of your paper prototype ✓. (the paper plans, the cardboard prototype is in Stanford :( but it was printed with the exact same design as final Duron box )
  • Your state diagram ✓.
  • Your project code ✓.
  • video of the final working player in use✓.
  • diagram of the circuits and all pin assignments ✓.

 View "Final project documentation folder" 

Videos of the theremin in action!






 Most rewarding things-

When it worked for the first time :)

Being able to use MIDI and all of its capabilities- originally it was only going to have one instrument.

All the things I had to learn to make this project work.


Most frustrating things-

Not being able to configure Pitch Bend in MIDI because of some errors in the built in libraries.

Communication between Teensy and UNO not working until I changed the slave- master roles.



Possible additions for the future:

Configure LED lights and Matrixes

Add functionality to the buttons to select instrument directly in the instrument and not through the computer.

Calibrate antenna in a better way to maybe add analog note change (very unlikely since readings from antenna vary very much)



It was a great project on which I enjoyed working very much. 

Had to learn the MIDI protocol, how to use Wire and communication libraries, etc...

Best class! :)

Rodrigo Ornelas


I used a Teensy 2.0 and an Osepp UNO (Arduino clone)

//CODE ON TEENSY- based on Midi Code by Nathan Siedle -http://www.sparkfun.com/Code/MIDI_Example.pde


#include <SoftwareSerial.h>

#include <Wire.h>

SoftwareSerial mySerial(2, 8); //Soft TX on 3, we don't use RX in this code


byte note = 60; //The MIDI note value to be played

byte resetMIDI = 4; //Tied to VS1053 Reset line

byte ledPin = 13; //MIDI traffic inidicator

int instrument = 3;

int noteActivated=0;

int flag=0;


int currentNote=60;


void setup() {

  Wire.begin();                // join i2c bus with address #4




  //Setup soft serial for MIDI control



  //Reset the VS1053

  pinMode(resetMIDI, OUTPUT);

  digitalWrite(resetMIDI, LOW);


  digitalWrite(resetMIDI, HIGH);



  talkMIDI(0xB0, 0x07, 120);



void loop() {

  talkMIDI(0xC0, 81, 0);  // sets instrument

  Wire.requestFrom(2, 1);    // request 1 byte from slave device #2


  while(Wire.available())    // slave may send less than requested


    int c = Wire.read(); // receive a byte as character

   Serial.println(c);         // print the character



  int valor=analogRead(A1);


  int volume=map(valor, 720,300,0,127);

  if( valor<200)




  talkMIDI(0xB0, 0x07, volume);


void hazMusica(int notita)


  if(notita==currentNote|| notita>120)





    noteOff(0, currentNote, 127);


    noteOn(0, currentNote, 127);

   // Serial.println("ayudaaaaaaaa");






//Send a MIDI note-on message.  Like pressing a piano key

//channel ranges from 0-15

void noteOn(byte channel, byte note, byte attack_velocity) {

  talkMIDI( (0x90 | channel), note, attack_velocity);



//Send a MIDI note-off message.  Like releasing a piano key

void noteOff(byte channel, byte note, byte release_velocity) {

  talkMIDI( (0x80 | channel), note, release_velocity);



//Plays a MIDI note. Doesn't check to see that cmd is greater than 127, or that data values are less than 127

void talkMIDI(byte cmd, byte data1, byte data2) {

  digitalWrite(ledPin, HIGH);



  //Some commands only have one data byte. All cmds less than 0xBn have 2 data bytes 

  //(sort of: http://253.ccarh.org/handout/midiprotocol/)

  if( (cmd & 0xF0) <= 0xB0)



  digitalWrite(ledPin, LOW);




/* Code on UNO * 

 * Based on original code from  Martin Nawrath from Academy of Media Arts Cologne- Elektor Magazine, november 2009 edition


 #include <Wire.h>

#include <Stdio.h>

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))

#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))


//! Macro that clears all Timer/Counter1 interrupt flags.



int pinLed = 13;                 // LED connected to digital pin 13

int pinFreq = 5;

int tonito2=0;


void setup()


  pinMode(pinLed, OUTPUT);      // sets the digital pin as output

  pinMode(pinFreq, INPUT);

  pinMode(8, OUTPUT);




  Serial.begin(57600);        // connect to the serial port


  // hardware counter setup ( refer atmega168.pdf chapter 16-bit counter1)

  TCCR1A=0;                   // reset timer/counter1 control register A

  TCCR1B=0;                   // reset timer/counter1 control register A

  TCNT1=0;                    // counter value = 0

  // set timer/counter1 hardware as counter , counts events on pin T1 ( arduino pin 5)

  // normal mode, wgm10 .. wgm13 = 0

  sbi (TCCR1B ,CS10);         // External clock source on T1 pin. Clock on rising edge.

  sbi (TCCR1B ,CS11);

  sbi (TCCR1B ,CS12);


  // timer2 setup / is used for frequency measurement gatetime generation

  // timer 2 presaler set to 256 / timer 2 clock = 16Mhz / 256 = 62500 Hz

  cbi (TCCR2B ,CS20);

  sbi (TCCR2B ,CS21);

  sbi (TCCR2B ,CS22);


  //set timer2 to CTC Mode

  cbi (TCCR2A ,WGM20);

  sbi (TCCR2A ,WGM21);

  cbi (TCCR2B ,WGM22);

  OCR2A = 124;                  // CTC at top of OCR2A / timer2 interrupt when coun value reaches OCR2A value


  // interrupt control


  sbi (TIMSK2,OCIE2A);          // enable Timer2 Interrupt



volatile byte i_tics;

volatile byte f_ready ;

volatile byte mlt ;

unsigned int ww;

int cal;

int cal_max;

char st1[32];

long freq_in;

long freq_zero;

long freq_cal;

unsigned int dds;

int tune;

int cnt=0;

int tonito=60;

void loop()





  while (f_ready==0) {            // wait for period length end (100ms) by interrupt

    PORTB=((dds+=tune) >> 15);    // kind of DDS tonegenerator / connect speaker to portb.0 = arduino pin8


 tune = freq_in-freq_zero;



  // startup

  if (cnt==10) {




    Serial.print("** START **");


  // autocalibration

  if (cnt % 20 == 0) {   // try autocalibrate after n cycles


    if (cal_max <= 2) {


      Serial.print(" calibration");






  cal = freq_in-freq_cal;

  if ( cal < 0) cal*=-1;  // absolute value

  if (cal > cal_max) cal_max=cal;


  digitalWrite(pinLed,1);  // let LED blink


  Serial.print("  "); 


  if ( tune < 0) tune*=-1;  // absolute value

   sprintf(st1, " %04d",tune);


  Serial.print("  "); 



  Serial.print("  ");



  Serial.print("  ");







void f_meter_start() {

  f_ready=0;                      // reset period measure flag

  i_tics=0;                        // reset interrupt counter

  sbi (GTCCR,PSRASY);              // reset presacler counting

  TCNT2=0;                         // timer2=0

  TCNT1=0;                         // Counter1 = 0

  cbi (TIMSK0,TOIE0);              // dissable Timer0 again // millis and delay

  sbi (TIMSK2,OCIE2A);             // enable Timer2 Interrupt

  TCCR1B = TCCR1B | 7;             //  Counter Clock source = pin T1 , start counting now



// Timer2 Interrupt Service is invoked by hardware Timer2 every 2ms = 500 Hz

//  16Mhz / 256 / 125 / 500 Hz

// here the gatetime generation for freq. measurement takes place: 




  if (i_tics==50) {         // multiple 2ms = gate time = 100 ms

                            // end of gate time, measurement ready

    TCCR1B = TCCR1B & ~7;   // Gate Off  / Counter T1 stopped

    cbi (TIMSK2,OCIE2A);    // disable Timer2 Interrupt

    sbi (TIMSK0,TOIE0);     // ensable Timer0 again // millis and delay

    f_ready=1;              // set global flag for end count period


                            // calculate now frequeny value

    freq_in=0x10000 * mlt;  // mukt #ovverflows by 65636

    freq_in += TCNT1;       // add counter1 value




  i_tics++;                 // count number of interrupt events

  if (TIFR1 & 1) {          // if Timer/Counter 1 overflow flag

    mlt++;                  // count number of Counter1 overflows

    sbi(TIFR1,TOV1);        // clear Timer/Counter 1 overflow flag




void requestEvent()


  Wire.write(tonito); // respond with message of 1 byte

                       // as expected by master




Comments (1)

Benjamin Tee said

at 12:48 pm on Aug 22, 2012

Hi Rodrigo,

Very awesome Theremin! Great job on getting it to work the first time. This shows how much thought and planning you put into it. We love the videos you made showcasing your Theremin. We wished that you had included a more fleshed out design point of view. We also wished you had included your images directly into your report instead of separate files in your folder. It would be difficult for someone to understand them without any description or caption of what the images represent.

Keep making awesome stuff!

Yusi, Kai and Ben and David

You don't have permission to comment on this page.