Sunday, 25 March 2012

My DIY Arduino MIDI controller - 4. Finding more pins, part II + LDR Calibration

What I want to do at this point is to develop an Arduino sketch that allows me to:

  • Read from sensors and display the result on a serial monitor
  • Use a tact switch for each sensor to toggle between 3 different modes: continuous reading, freeze (so that I can take my hand off the LDR and its value remains the same), and LDR calibration, because I want to be able to use the controller under various ambient light conditions. To prevent accidental calibration, the switch will be have to be hold down for 1/2 second. Shorter presses will toggle between continuous reading and freeze.

I started by writing a short sketch that allows the user to toggle between 3 modes: reading, calibrating and serial out, for just one LDR, no matter how long the switch was pressed, it was just cycling through the different modes.

During the calibration mode, the LDR was read and its minimum and maximum values were stored until the user changed the mode. This is done by simply comparing the current reading to the minimum value, and each time the reading is lower than the minimum, the minimum is set to the current reading. Same logic for max.

The reading and serial out modes worked the same but used different displays: the value of the LDR was read, then mapped to a 0-255 range according to the calibration values. In reading mode, the reading was sent to an LED through a PWM pin. In serial out mode, it was sent to the serial monitor.

Things started to get a lot more complicated when I decided to add in the same time the freeze function (measuring as well the time each switch was pressed for entering calibration mode instead of just cycling through the 3 modes), and several sensors through a 4051 multiplexer. The code was well thought (for a beginner like me) but I stupidily forgot to set all the pin modes in the setup which caused some weird errors. Had to check the circuit with a voltmetre and spent numerous hours (I swear) trying to debug the code... Note to self: always check the basic stuff before checking the complex parts of a code!

This is the circuit:

The blue wires go to the select pins of the 4051. The orange wires go to the switches (for which I'm using a pull-down resistor). The yellow wires go to the LEDs through 220Ω resistors. The green wires connect the sensors (a tact switch, a LDR and a pot) to the 4051 y0, y1 and y2 pins. There is another green wire that connects the 4051 output to the Arduino analog pin 0.


And the final code, which I tried to comment as much as possible. This is not supposed to be a tutorial but if my efforts can help somebody else, I'll be happy:

/*
    This script calibrates and reads from sensors using a 4051
*/

// switch & sensor management
const int sensors_number = 3;   // number of sensors to use
boolean last_switch_state[] = {0, 0, 0};
boolean current_switch_state[sensors_number];
int sensor_min[] = {0, 0, 0};      // default min value
int sensor_max[] = {1023, 1023, 1023};// default max value
int sensor_value[sensors_number];            // needs to be global to work with switch{} below
int output_sensor_value[sensors_number];     // needs to be global if we want to freeze it
int last_sensor_value[sensors_number];       // because we will only send data if there is a change in the reading

// time management
long debounce_millis[sensors_number];
int debounce_threshold = 50;    // above 20ms the reading will be considered valid
long high_press_timer[sensors_number];
int calibration_trigger = 500;  // a 500ms press will activate calibration mode
int startup_delay = 1500;       // ms to wait at startup

//  LED management
long led_timer[sensors_number];
int led_blinking_time = 300;    // blinking time set to 300ms
boolean led_state[] = {0, 0, 0};  // default LED state

// modes
/* 0 = reading
   1 = freeze
   2 = calibration */
int sensor_mode[] = {0, 0, 0};

// Arduino pins
int switchPin[] = {2, 3, 4};
int ledPin[] = {8, 12, 13};
int commonPin = 0;       // the pin we will read from, connected to the 4051
int s0 = 5;              // these following 3 are connected to the 4051 select pins
int s1 = 6;
int s2 = 7;

// the bits we will write to select the 4051 to read from
boolean r0;
boolean r1;
boolean r2;

void setup() {
  // pins setting
  for (int i = 0; i < sensors_number; i++) {
    pinMode(ledPin[i], OUTPUT);
    pinMode(switchPin[i], INPUT);
  }
  pinMode(s0, OUTPUT);
  pinMode(s1, OUTPUT);
  pinMode(s2, OUTPUT);

  // initialize serial communication
  Serial.begin(9600);
  Serial.println(sensor_mode[0]);
  
  // LED check and delay before reading switches
  for (int i = 0; i < sensors_number; i++) {
    digitalWrite(ledPin[i], HIGH);
  }
  delay(startup_delay);
  for (int i = 0; i < sensors_number; i++) {
    digitalWrite(ledPin[i], led_state[i]);
  }
}


void loop() {
  
  // we will repeat the whole thing once per sensor
  for (int i = 0; i < sensors_number; i++) {
  
    // these bits are sent to the 4051 to tell it which of its pins we want to read
    // according to the value of 'i'
    r0 = bitRead(i,0); // we convert the sensor number into a 3 bit number
    r1 = bitRead(i,1); // if 0: r0 = 0, r1 = 0, r2 = 0
    r2 = bitRead(i,2); // if 1: r0 = 1, r1 = 0, r2 = 0
    digitalWrite(s0, r0);
    digitalWrite(s1, r1);
    digitalWrite(s2, r2);
  
    /*****************************************
        READING SWITCH AND SELECTING MODES
    *****************************************/
  
    // read switch
    current_switch_state[i] = digitalRead(switchPin[i]);
  
    // if state changed
    if (current_switch_state[i] != last_switch_state[i])
      debounce_millis[i] = millis();

    // if the counter is above the debouncing threshold
    if ((millis() - debounce_millis[i]) > debounce_threshold) {
       
      // if switch is HIGH (pressed)
      if (current_switch_state[i] == HIGH) {
        // if high press timer not set, set it
        if (high_press_timer[i] == 0) high_press_timer[i] = millis();
        //  if it is set, compare to calibration activation threshold. If over (and mode not already 2):
        else if ((millis() - high_press_timer[i] >= calibration_trigger) && (sensor_mode[i] != 2)) {
          // reset previous end values
          sensor_min[i] = analogRead(commonPin);
          sensor_max[i] = analogRead(commonPin);
          // activate calibration mode
          sensor_mode[i] = 2;
          // start led blink timer
          led_timer[i] = millis();
          led_state[i] = -led_state[i] + 1;  // toggle led_state to show we're entering calibration mode
        }
      }

      // if switch is LOW (released)
      else {
        // if we had a short press
        if (  millis()-high_press_timer[i] < calibration_trigger
              && millis()-high_press_timer[i] > 0) {
          // go from calibration to freeze mode if applicable
          if (sensor_mode[i] == 2) sensor_mode[i] = 1;  // will be toggled to 0 in the next line
          // and toggle anyway (so eventually we get from calibration to continuous reading mode)
          sensor_mode[i] = -sensor_mode[i] + 1;
          led_state[i] = sensor_mode[i];  // so if mode = 0, led is off, and if mode = 1 led is on
        }
        // we then reset the high press timer to enable calibration mode again
        high_press_timer[i] = 0;
      }
    }
  
    // save switch state
    last_switch_state[i] = current_switch_state[i];
  
    /***********
        MODES
    ***********/

    switch (sensor_mode[i]) {
  
      // continuous reading mode
      case 0:
        // read sensor
        sensor_value[i] = analogRead(commonPin);
        // in case the value is beyond the min or max
        sensor_value[i] = constrain(sensor_value[i], sensor_min[i], sensor_max[i]);
        // then map to calibration values
        output_sensor_value[i] = map(sensor_value[i], sensor_min[i], sensor_max[i], 0, 255);
    
      // freeze and continuous reading modes
      case 1:
        //Serial.println(String(i) + ": " + String(output_sensor_value[i]));
        Serial.print(String(i) + ": " + output_sensor_value[i]);
        if (i == sensors_number - 1) Serial.println();
        else Serial.print(" --- ");
        break;

      // calibration mode    
      case 2:
        // read sensor
        sensor_value[i] = analogRead(commonPin);
        // store new values if applicable
        if (sensor_value[i] < sensor_min[i]) sensor_min[i] = sensor_value[i];
        if (sensor_value[i] > sensor_max[i]) sensor_max[i] = sensor_value[i];
        // print current ends
        int p0 = r0;
        int p1 = r1;
        int p2 = r2;
        Serial.println(String(i)+" ("+String(p0)+String(p1)+String(p2)+") "+"> Current: "+String(sensor_value[i])+" - Min: " + String(sensor_min[i]) + " - Max: " + String(sensor_max[i]));
        // toggle the led if the time has come
        if (millis() - led_timer[i] > led_blinking_time) {
          led_state[i] = -led_state[i] + 1;
          // and reset the timer for the next blink
          led_timer[i] = millis();
        }
      
    }  // endswitch
  
    // print mode
    digitalWrite(ledPin[i], led_state[i]);
    
  }  // endfor
}

Oh, and I made small wire jumpers! That looks so much sexier!



<< Previous: 3. Finding more pins, part I: the 4051

Saturday, 10 March 2012

My DIY Arduino MIDI controller - 3. Finding more pins, part I: the 4051

The first thing I tried when I understood I would need more pins than what I have on my Arduino Uno, was the 4051 multiplexer. The way this chip works is rather simple, the Arduino playground explains it better than I could:

If you use the 4051 as a Multiplexer: You can choose between 8 different inputs and select just one you want to read at the time.
If you use the 4051 as a Demultiplexer you can choose between 8 different outputs and select just one you want to write at the time.

3 of its pins are connected to the Arduino, and by writing these pins HIGH or LOW you select which of the 4051 in-/output pins you are going to read or write. It's a simple binary to decimal conversion. For example if you want to select pin 5 of the 4051, you can refer to the following table:

000 => pin 0
001 => pin 1
010 => pin 2
011 => pin 3
100 => pin 4
101 => pin 5
110 => pin 6
111 => pin 7

That is to say you will write the select pin 1 HIGH, the second LOW and the third HIGH (101 is the binary for 5). Once you've done that you can write or read the common pin.

I first used the 4051 to connect 7 switches & pots to the 8 in-/outputs of the 4051, and I seemed to successfully read from them, but then I tried to use 4 switches (reading) and 4 LEDs (writing) with the same chip and it didn't work the way I expected.

This is the wiring...

The blue wires connect the Arduino to the 4051 select pins. By writing these HIGH or LOW we will tell the 4051 which of its 8 in-/outputs we want to play with.
The orange wires connect the switches to the pins 0-3 of the 4051.
The brown wires connect the red LEDs to the pins 4-7 of the 4051.
The white wire is the one we will use to read/write to the selected 4051 pin.
The green wires connect the yellow LEDs to Arduino pins.
In real life it looks like this



...and the code I used:

int r0;
int r1;
int r2;

//  4051 pins
int commonPin = 8;
int s0 = 2;
int s1 = 4;
int s2 = 7;

//  LEDs directly connected to Arduino pins
int regularLed[] = {9, 10, 11, 12};

boolean switchVal[] = {LOW, 0, 0, 0};
boolean lastSwitchVal[] = {0, 0, 0, 0};
boolean ledState[] = {0, 0, 0, 0};

//  setup
void setup() {
  pinMode(s0, OUTPUT);
  pinMode(s1, OUTPUT);
  pinMode(s2, OUTPUT);

  for (int i = 0; i <= 3; i++) {
    pinMode(regularLed[i], OUTPUT);
  }
  
  Serial.begin(9600);
}

//  loop
void loop () {

  for (int i=0; i<=7; i++) {

    // select the bit  
    r0 = bitRead(i,0);
    r1 = bitRead(i,1);
    r2 = bitRead(i,2);
    digitalWrite(s0, r0);
    digitalWrite(s1, r1);
    digitalWrite(s2, r2);

    //  Read from switches on pins 0 to 3
    pinMode(commonPin, INPUT);
    if (i <= 3) {
      
      //  Read switch
      switchVal[i] = digitalRead(commonPin);

      //  Light Arduino LEDs up accordingly to switch state
      digitalWrite(regularLed[i], switchVal[i]);

      //  If the state of the switch has changed
      if (switchVal[i] != lastSwitchVal[i]) {

        //  Toggle middle LED state if switch is pressed (not if released)
        if (switchVal[i] == 1) toggleLed(i);  //  1 = HIGH
        
        /*  Debug data
        Serial.println("s"+String(i)+" = "+String(int(lastSwitchVal[i]))+">"+String(int(switchVal[i])));
        for (int j = 0; j <= 3; j++) {
          if (j != 3)
            Serial.print(int(ledState[j]));
          else
            Serial.println(int(ledState[j]));
        }*/
        
        //  Save new switch state
        lastSwitchVal[i] = switchVal[i];
      }
    }

   //  As we will be reading from pins 4 to 7 we change the common pin state
   pinMode(commonPin, OUTPUT);

    //  Write to LEDs on pins 4 to 7
    if (i >= 4) {
      digitalWrite(commonPin, ledState[i - 4]);
      /*if (i != 7)
        Serial.print(String(int(ledState[i-4])));
      else
        Serial.println(String(int(ledState[i-4])));*/
      //delay(125);
    }
  }
}

//  toggles ledState[x] 0 / 1
void toggleLed(int l) {
  ledState[l] = -ledState[l] + 1; //  -0 + 1 = 1  |  -1 + 1 = 0
}

What this code does is that when a switch is pressed:
  • The corresponding yellow LED is on (and off when the switch is not pressed), I'm just writing the switch state to the LED.
  • The corresponding middle LED state is toggled on/off (nothing happens on switch release).


The yellow LEDs light up as I press switches 1 & 2 (works with all switches ;)

It worked fine at first when the code was sending serial data to the monitor, but when I commented the serial commands out, it started behaving strange. The reading seemed to be normal but the LEDs were lit in a strange fashion. When I pressed the first switch, both first & second LED turned on, but I knew from my previous try that the LED states were written correctly, because I had send them over the serial port. So it was not due to a mistake in my code.


Switch 1 has been pressed and LEDs 1 & 2 are lit, instead of only LED 1.

How come? I got the answer from the Arduino forums: Grumpy_Mike said
This is because you are writing the digital select lines one at a time and in the process you put false data briefly on the the chip. You need to write all these bits at the same time using direct port access or bit manipulation. But as I said it is totally the wrong chip anyway, use a shift register and don't put in that capacitor they have in the tutorial.
So I guess I'll try to use the 4051 for reading only (pots and LDRs), and I ordered a couple of 74HC595 shift registers from Sparkfun (along with pots, SPDT switches, RGB LEDs, MIDI connectors, resistors, a thumb joystick with its breakout board & solderless headers, wire & heat shrink!).

So next post will be about the 74HC595 8 bit shift register, unless they take too much time to arrive, in which case I could post about the LDR calibration.

<< Previous: 2. The first problems

Tuesday, 6 March 2012

My DIY Arduino MIDI controller - 2. The first problems

The first problems:
There's one thing I might find annoying with LDRs: If I want to increase for instance the filter cutoff frequency of a synthesizer by casting a shadow over a LDR with my hand, the frequency would suddenly drop down as I remove my hand, unlike a potentiometer which would remain at its position after tweaking. Although that sounds like a cool thing to be able to go instantly from high to low position (which you can't do with a rotary pot), I thought it would be even better to be able to "freeze" the reading of the LDR by pressing a button. To do that I will therefore need 4 switches, one per LDR, and 4 LEDs to tell if freeze is active for each LDR.

Another problem about LDRs is that they will return different values according to the ambient light, and I want to be able to use my controller under a maximum range of light conditions (daylight from the window, artificial lamp on the desk, or just the light from the computer screen...) Therefore I will have to calibrate it whenever the ambient light changes, which will require another button to toggle the calibration mode on/off. I will also have to tell the user when calibration is on and which LDR is being calibrated. I decided I would just turn the LEDs off when the sensor is in use, turn them on when the sensor is "frozen" and I'll make them blink during calibration. It might be a good thing to add a potentiometer to adjust their brightness so that they don't influence calibration.

The problem I'm facing now is that my Arduino board has only got 13 digital in/outs, and 6 analog inputs. The digital in/out pins can be used to detect the presses of switches, or light LEDs (you can think of them as switches that are either on or off). Pins 1 and 2 will be used for MIDI communication, so we have 11 pins left for LEDs & buttons/switches, each of which require 1 pin. The analog inputs allow us to connect a total of 6 sensors and potentiometers.

A rotary pot, a LDR and a few tact switches
My digital needs so far are:
- 5 switches/buttons for LDR options
- 1 "panic" button that would turn all notes off
- 1 reset button if possible
- 4 LEDs
- 4 (at least) triggering buttons (if I wanted to use velocity I would have to get analog sensors instead)

That's already a total of 15 digital pins and I only have 11 of them left. Same for analog inputs. I've only got 6, to which I want to connect 1 slider pot, 4 LDRs and some rotary pots. Running out of pins on the analog side too! That said, you cannot browse Youtube looking for Arduino videos without noticing many project with 8x8 LED matrices or many pots, motors or whatever, so there definitely is a solution, which is what I'm working on at the moment, see next post!

<< Previous: 1. The Arduino

Monday, 5 March 2012

My DIY Arduino MIDI controller - 1. The Arduino

This first post will try to sum up everything I had to learn to get where I'm now: shift registers.

The Arduino
Photo by the Arduino Team
First thing I had to do was to understand what an Arduino is and how it works, how hard it is to get it to send MIDI data out, or to read from various sensors etc.

If you have no idea what an Arduino is, it is a programmable electronic device that can receive or send voltages. Basically you can plug potentiometers, pressure sensors, light dependent resistors, audio signals, etc. to its inputs, and LEDs, motors, LCD displays etc. to its outputs. You can as well get it to communicate to computer programs such as Pure Data, Processing and many more, using its USB port. Of course you can get it to send or receive MIDI data. It is easy to program, the upload is done via USB with the minimal Arduino IDE (Integrated Development Environment).

Some Arduino projects I found interesting on Youtube, that made my brain go crazy and think of all I could do with this little device (actually I couldn't find all the videos I watched then, but I added some other ones that can be inspiring too):
http://www.youtube.com/watch?v=v56zLOqhjV4
http://www.youtube.com/watch?v=FGl-ba7qSic
http://www.youtube.com/watch?v=D48kgIr0CJI
http://www.youtube.com/watch?v=92VIEDtQKVI
http://www.youtube.com/watch?v=-KDd_YY7fTI

Knowing nothing about electronics I ordered an Arduino starter kit and followed the lessons on http://www.ladyada.net/learn/arduino/index.html. That is where you learn the "Hello world" of the Arduino: blinking an LED. That's also where you learn everything you need to get started when you know nothing about micro controllers, computer programming or electronics, and it only takes a few hours.

After spending a couple of hours reading the basic tutorials on the official Arduino site, I learned how to:
- read and display data from a potentiometer, a light dependent resistor, a piezo,
- use a tact switch to increment a counter (useful when you want to use a button to browse through different options),
- communicate with Processing (for a different project).

I also learnt how to solder components on a PCB (Printed Circuit Board) thanks to Bleeplab's Pico Paso, which I ordered as a kit, since soldering will definitely be required, though I might have to deal with wires more than PCBs but it's still good to know the basics of PCB through-hole soldering.

I still need now to learn how to send MIDI data (already did but forgot), and how to connect more sensors, LEDs and switches than what the Arduino normally allows. This is what I'm working on at the moment and I'll post soon about my failures and successes at multiplying the Arduino's ins and outs!

<< Previous: 0. Intro

Sunday, 4 March 2012

My DIY Arduino MIDI controller - 0. Intro

Hello there,

A few months ago I decided to get a new MIDI controller. I browsed Thomann, Audiofanzine and various sites but I couldn't find a controller satisfying my needs. During my search I stumbled upon many Arduino projects that I found interesting, and I thought I might give it a try, though I knew nothing about electronics, and very little about programming.

I will post here the various stages of the creation process. I have no idea whether I'll succeed in this task or not but I thought it might be interesting to share...


The project:
After spending some time on the Internet learning how to master the basics of the Arduino world, my idea has evolved quite a lot, I even forgot what I wanted to do in the first place but where I'm heading at at the moment is: I want to build a MIDI controller that uses different kinds of sensors rather than common potentiometers. It has to be playful though and I don't see myself playing music with a temperature sensor. So the only sensor that seemed easy enough to use for a first project, and still be playable in a live situation, was the light dependent resistor (LDR). I am planning to use 4 of them, a couple (or a bunch) of rotary potentiometers, a slider potentiometer (always good to have a fader for master volume), and buttons to act as pads to trigger sounds, and why not a Playstation-like thumb joystick, because they spring back to the middle like a pitch wheel.

Next: 1. The Arduino >>