Being unable to post in the Eyewire forum, I’m using this place to host images I have questions about.

Animated GIF

Animated GIF

A.K.A. "W.T.F."

A.K.A. “W.T.F.”

My DIY Arduino MIDI controller – 5. Finding more pins, part II: the shift register

As I said in a previous post, I might need to expand the digital outs as well as the analog ins, so I followed Grumpy_Mike’s advice on the Arduino forums and got a 74HC595 shift register to control my LEDs instead of the 4051. The 8 bit shift register is a very convenient way to drive 8 LEDs with only 3 digital pins: you just send it a series of 8 HIGHs or LOWs and it lights on/off the LEDs accordingly when you tell it so. Looks exactly like what I need. I followed this ShiftOut tutorial to learn how to handle the chip, then I modified the second code example from the tutorial to make something like this:

This is the circuit:

MDAMC04-shift register circuit

And the code I came up with, slightly modified from the tutorial:

/*
Shift Register Example
for 74HC595 shift register

This sketch turns reads serial input and uses it to set the pins
of a 74HC595 shift register.

Hardware:
* 74HC595 shift register attached to pins 2, 3, and 4 of the Arduino,
as detailed below.
* LEDs attached to each of the outputs of the shift register

Created 22 May 2009
Created 23 Mar 2010
by Tom Igoe

Modified 1 Apr 2012 by Yann Guillermou

*/

//Pin connected to latch pin (ST_CP) of 74HC595
const int latchPin = 8;
//Pin connected to clock pin (SH_CP) of 74HC595
const int clockPin = 12;
////Pin connected to Data in (DS) of 74HC595
const int dataPin = 11;

// the bits you want to send (a byte is 8 bits, one per LED)
byte bitsToSend = 0;

void setup() {
//set pins to output because they are addressed in the main loop
pinMode(latchPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}

void loop() {
for (int i = 0; i < 8; i++) {
registerToggle(i);
delay(100);
}
}

// This method sends bits to the shift register:
void registerToggle(int pin) {
// turn of the output so the pins don't light up
// while we're shifting bits
digitalWrite(latchPin, LOW);

// read bit state
int state = bitRead(bitsToSend, pin);

// change bit state
state = -state + 1;

// toggle the bit in bitsToSend:
bitWrite(bitsToSend, pin, state);

// shift the bits out:
shiftOut(dataPin, clockPin, MSBFIRST, bitsToSend);

// turn on the output so the LEDs can light up:
digitalWrite(latchPin, HIGH);
}

The mod’ consisted in making the bitsToSend variable global because I wanted to be able to toggle an LED without affecting the others, and above all keep track of each LED state. I also replaced the registerWrite() function by this registerToggle() function, just to check if I had understood well enough how to do.

This code only loops and toggles the LEDs on and off but with the registerToggle() and registerWrite() functions you can easily tell the chip which LED to turn on or off, this way it is as easy as a digitalWrite().

Next post will be about MIDI out, as I got a couple of breadboard friendly MIDI connectors!

My DIY Arduino MIDI controller – 4. 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 held down for 1/2 second. Shorter presses will toggle between continuous reading and freeze.

I started with 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 is read and its minimum and maximum values are stored until the user leaves the calibration 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 work the same but use different displays: the value of the LDR is read, then mapped to a 0-255 range where 0 and 255 correspond respectively to the min and max values set during calibration. In reading mode, the reading is sent to an LED through a PWM pin. In serial out mode, it is sent to the serial monitor.

Things started to get more complicated when I decided to add the freeze function (measuring how long each switch was pressed 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 section 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.
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. Again, 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!

Wire jumper :)

JUMP! :)

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.

4 of its pins are connected to the Arduino. 1 of them is the common pin, from which we will read/write, the 3 other pins are the “select pins” and by writing these 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 which you can refer to the following table:

Select pins Output
0, 0, 0 0
0, 0, 1 1
0, 1, 0 2
0, 1, 1 3
1, 0, 0 4
1, 0, 1 5
1, 1, 0 6
1, 1, 1 7

So if you want to read or write what’s connected to the pin #5 of the 4051, you write the three select pins respectively 1, 0, 1 (HIGH, LOW, HIGH). Once you’ve done that you can read or write 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… I simply wanted to light an LED whenever the corresponding switch was pressed. This was the wiring:

caption

The blue wires connect the Arduino to the 4051 select pins.
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 (for troubleshooting).


In real life, with coloured LED's replacing the red ones on the above circuit.

In real life, with coloured LED’s replacing the red ones in the above circuit.

…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 LED's light up as I press switches 1 & 2

The yellow LEDs light up as I press switches 1 & 2

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.

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 get here, in which case I could post about the LDR calibration.

My DIY Arduino MIDI controller – 2. 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.

1 pot, 1 LDR, 3 tact switches

1 pot, 1 LDR, 3 tact switches

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 8×8 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!

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

Arduino UNO

Arduino UNO

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):

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!