S3201865 Final Project - USB controller
From Physical Programming
Contents |
USB Controller
Initial Concept
My initial idea was to create a MIDI (musical instrument digital interface) controller that can control features on software such as Virtual DJ or Traktor and to give some sort of visual feedback.
However I had a look into ways of programming the arduino to become MIDI compatible but it turns out it's not the easiest of things to do and the arduino isnt all that good at doing it. There are many other microprocessors on the market that also use software such as Processing or Max MSP that do a much better job of this.
Secondly I would then need to map the MIDI controller to the respective software which again would require learning more coding ontop of Processing/arduino
after looking around for a bit i came across a virtual USB tutorial in the book "Practical Arduino"
The tutorial focus's on turning the arduino into a a device that the computer will recognize as a an USB device such as a mouse or keyboard.
this means that sensors, switches or potentiometers could be used in place of mouse clicks, movements and keyboard presses.
Both Traktor and Virtual DJ have mapping that allows for keyboard assignable shortcuts. With the Virtual USB it would be possible to replicate the relevant keys in a device more suited to use with the software.
Inspiration
Whilst trawling around midi controllers I came across a semi DIY controller called the midi fighter, it basically consists of a pre-programmed circuit and a kit that comprises of arcade style buttons.
http://www.djtechtools.com/2010/09/10/customized_controller-midi-fighter/
I'm going to try and create something similar to this but using an arduino as the brain.
Requirements
For the controller to work it will require the following
A second USB interface (the USB B port on the arduino sends serial data, a second usb port is required along with programming that makes the computer think the arduino is a usb device)
Inputs - I went through which controls i commonly use and narrowed it down to 9 these are as follows
Tab - Change which deck is active
Space - play/pause
ESC - loop in/out
cntrl - modifier
alt - modifier
6 - cue point set/play
7 - cue point set/play
8 - Shorten Loop
9 - Lengthen Loop
I will also need some form of feed back system which will most likely be light based. so far Im thinking an RGB LED that will return different colours for different actions.
Research
After scouring around i found two main sources that will cover most of the electrical and programming aspects of my project.
the first is from the book "practical Arduino" which details how to create the USB device. the example uses four buttons all going into the digital pins on the arduino. after adding the USB port there are only 10 available digital pins so if all 9 were to go to digital pins there would only be 1 free to control the LED
so I kept loooking and came across this [1] the article details how multiple switches can be connected to a single analog input and as long as only one is being pressed at once then the arduino can distinguish which button is being pressed. Using this method i can split my buttons into groups of buttons that will not be pushed together freeing up lots of pins for any outputs i may decide on later.
Hardware
So far here is a list of components required
- Arduino(obviously)
- 2 x 3.3 - 3.6V Zener Diodes operating at less than 0.5W (zener diode is used to limit the voltage to the stated voltage, this is required as USB ports operate at 3.3V)
- Selection of resistors (these are needed for a variety of things firstly for connecting the usb port to the circuit and also for distinguishing which switch is being pressed)
- 9x push bottons (preferably arcade style)
- USB B connector (to connect to computer)
- LEDS for feedback
- prototyping board to mount everything to
- Breadboard to test circuit.
getting the hardware
It turns out that finding a zener diode with a low enough wattage is quite difficult and after reading about a few projects that had used the same electronic components it is a crucial part of the circuit
I eventually found some on farnell electronics however the diode only cost 8c and they have a minimum order of $10 so I went ahead and ordered most of my other electrical components which i managed to get all my resistors plus spares (minimum order) a bunch of bright blue LED's and the diodes needed plus a few other bits and pieces.
I ordered my buttons separately from an overseas arcade spares site, so 9 arcade buttons are on their way in an assortment of colours to map out the functions of each one.
Update All my electrical components arrived today (at 7am, I didn't realize couriers started that early)
If you have parts you need I highly recommend farnell, apart from the minimum $10 order they ship REALLY fast I ordered my parts on sunday night and they arrived first thing tuesday morning. (make sure the parts are available in the aus warehouse)
when ordering my Zener diode I ordered a few different ones with differnt operating wattage's due to it being such a vital part of the circuit and now that they arrived I am really glad I did. here's why - I ordered two different kinds one that is very similar in appearance to a small resistor and the other without the long wire legs (only due to the fact that at lower W ratings its near impossible to find wire leg ones)
so here is what the diode looks like in a photo when ordering
and here it is in real life next to a bic pen for scale
it is insanely small!!!!
I really hope the wire leg diodes make the circuit work!!
Arcade buttons arrived today!! nice and colourful
Programming
The Code below is the code I intend to use and modify to suit my project, so far I'm using to different sets of code which i will combine to achieve the desired result.
Virtual USB Code
// Requires the use of the "UsbKeyboard" library available from // http://code.rancidbacon.com/ProjectLogArduinoUSB #include "UsbKeyboard.h"
// Define the inputs to use for buttons #define BUTTON_A 6 #define BUTTON_B 7 #define BUTTON_C 8 #define BUTTON_D 9 #define BUTTON_MSG 10 #define BUTTON_ENTER 11
// Use the on-board LED as an activity display int ledPin = 13;
Configure button inputs and set up the USB connection to the host
void setup()
{
// Set up the activity display LED
pinMode (ledPin, OUTPUT);
digitalWrite (ledPin, HIGH);
// Set the button pins to inputs pinMode (BUTTON_A, INPUT); pinMode (BUTTON_B, INPUT); pinMode (BUTTON_C, INPUT); pinMode (BUTTON_D, INPUT); pinMode (BUTTON_MSG, INPUT); pinMode (BUTTON_ENTER, INPUT);
// Enable the CPU's internal 20k pull-up resistors on the button // inputs so they default to a "high" state digitalWrite (BUTTON_A, HIGH); digitalWrite (BUTTON_B, HIGH); digitalWrite (BUTTON_C, HIGH); digitalWrite (BUTTON_D, HIGH); digitalWrite (BUTTON_MSG, HIGH); digitalWrite (BUTTON_ENTER, HIGH);
// Disable timer0 since it can mess with the USB timing. Note that // this means some functions such as delay() will no longer work. TIMSK0&=!(1<<TOIE0);
// Clear interrupts while performing time-critical operations cli();
// Force re-enumeration so the host will detect us usbDeviceDisconnect(); delayMs(250); usbDeviceConnect();
// Set interrupts again sei(); }
/**
* Main program loop. Scan for keypresses and send a matching keypress
* event to the host
* FIXME: currently repeats as fast as it can. Add transition detection
*/
void loop()
{
UsbKeyboard.update();
if (digitalRead(BUTTON_A) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_A);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
if (digitalRead(BUTTON_B) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_B);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
if (digitalRead(BUTTON_C) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_C);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
if (digitalRead(BUTTON_D) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_D);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
if (digitalRead(BUTTON_MSG) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_H, MOD_SHIFT_LEFT);
UsbKeyboard.sendKeyStroke(KEY_E);
UsbKeyboard.sendKeyStroke(KEY_L);
UsbKeyboard.sendKeyStroke(KEY_L);
UsbKeyboard.sendKeyStroke(KEY_O);
UsbKeyboard.sendKeyStroke(KEY_SPACE);
UsbKeyboard.sendKeyStroke(KEY_W, MOD_SHIFT_LEFT);
UsbKeyboard.sendKeyStroke(KEY_O);
UsbKeyboard.sendKeyStroke(KEY_R);
UsbKeyboard.sendKeyStroke(KEY_L);
UsbKeyboard.sendKeyStroke(KEY_D);
UsbKeyboard.sendKeyStroke(KEY_ENTER);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
if (digitalRead(BUTTON_ENTER) == LOW) {
UsbKeyboard.sendKeyStroke(KEY_ENTER);
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle status LED
}
}
* Define our own delay function so that we don't have to rely on * operation of timer0, the interrupt used by the internal delay()
void delayMs(unsigned int ms)
{
for (int i = 0; i < ms; i++) {
delayMicroseconds(1000);
}
}
5 buttons into one input code
const int buttonPin = 0; // the number of the pushbuttons pin const int ledPin = 13; // the number of the LED pin for testing
The above just sets up the pins used. Then you need to set up each button and the range of values for that button:
const int BUTTON1 = 1; ... const int BUTTON1LOW = 970; const int BUTTON1HIGH = 1024;
In the setup, we simply set the pin states and start the serial port (which button is pressed will be written to the serial output):
pinMode(buttonPin, INPUT); pinMode(ledPin, OUTPUT); Serial.begin(9600);
Then we get to the interesting part. The first part of the program loop is where the magic actually happens, but it simply checks which button was read based on the value we got from analogRead():
int reading = analogRead(buttonPin); int tmpButtonState = LOW; // the current reading from the input pin
if(reading>BUTTON5LOW && reading //Read switch 5 tmpButtonState = BUTTON5; }else if(reading>BUTTON4LOW && reading //Read switch 4 tmpButtonState = BUTTON4;
}else if ....
}else{ //No button is pressed; tmpButtonState = LOW;
}
The next part just debounces the button press. Basically, without this, pressing the button once would appear to the code as multiple presses. Usually this would allow you to use the button as a toggle switch as well, but I'm not doing that.
I'm planning on using the buttons as reset buttons, so I just need to detect when they are pushed and reset a specific variable.
if (tmpButtonState != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
buttonState = tmpButtonState;
} lastButtonState = tmpButtonState;
The last part of the program is just a switch statement that executes different code based on which button was pressed. For testing, they all just switch on the built in LED on pin 13.
switch(buttonState){
case LOW:
digitalWrite(ledPin, LOW);
break;
case BUTTON1:
digitalWrite(ledPin, HIGH);
break;
... }





