Circuitbenders Forum

Please login or register.

Login with username, password and session length
Advanced search  

News:

Author Topic: Triggers using 2$ Arduino Nano & MIDI Input circuit (step by step guide + code)  (Read 9727 times)

Pieterv1

  • Newbie
  • *
  • Karma: 0
  • Offline Offline
  • Posts: 3

Hello builders & benders,

I built a CB-55 for school last year and since I didn't have any modular gear or sequencer at the time, I went looking for a cheap and compact way to control the CB-55 with standard MIDI.
I stumbled across quite a few solutions which were still a bit above my budget without being 100% sure they would fit my needs and most of them were too big to build in with my voiceboard.

That's why I went with a tiny Arduino Nano clone (RobotDyn v3.0) which cost me around 2$ each:
- The Arduino can run at 6v - 12v unregulated power and the onboard regulator makes it 5v --> it can share the power supply with the voiceboard (*)
- Instead of buying a MIDI shield which only fits on a regular, full size Arduino Uno, a very simple MIDI-in circuit can be made using an optocoupler
- Both the Arduino Nano and the MIDI input circuit are small enough to be built in next to the voiceboard
- Any other Arduino Nano clone will work. I recommend these because they're great to fit in the same case with the voiceboard. It fits nicely on a prototyping board (I put mine on a small self-adhesive breadboard). + It has a usb interface built-in --> no need for an additional serial programmer.
(*) The voice sections of the CB-55 run off the 6v regulator so 9v input is sufficient to run the voices triggered by the Arduino. It is only the conditioning circuit that needs 12v input, but my code uses 10ms short triggers so there's no need for the conditioning circuit. Long story short: use a 9v power supply! I fried one of my Arduino's using 12v  ;)

1) Preparation:
- Download the Arduino IDE for your operating system (Windows users: I'd recommend the "installer" version instead of the app)
- Build the Arduino MIDI input circuit following this tutorial: http://www.notesandvolts.com/2015/02/midi-and-arduino-build-midi-input.html

2) Test the MIDI input circuit
Make sure you download the Arduino MIDI Library first: https://github.com/FortySevenEffects/arduino_midi_library/releases (the .zip file)
Test your circuit using the example test program of the tutorial. It shows you how to add the MIDI library to the Arduino IDE, explains the basics of the IDE in case you're not familiar with Arduino and goes through the code example step by step: http://www.notesandvolts.com/2015/02/midi-for-arduino-input-test.html  The program will make the on board LED of the Arduino light up when any MIDI note-on message is received. For now Arduino Nano can just be powered over USB. The MIDI input circuit draws power from the VCC (constant 5v) output of the Arduino.
When you got it working, I'd say replicate the circuit on stripboard :)

3) Explanation, editing and uploading of the MIDI to trigger code
In my code there's a few variables which can be edited to your needs. The lines of code with a double dash at the beginning are comments which might give you a better understanding of the overall functionality of the code.
By default the trigger time is set to 10ms, but you can change "10" to any (useful) number you want, depending on your application. (10ms works best with the CB-55 as I've been told by Paul)
Code: [Select]
#define TRIGGER_TIME 10  // this defines the trigger length in ms (edit as needed)
The first Pin of the Arduino Nano is named Pin2 and I'm using all 12 pins in total as CV triggers. Because the CB-55 needs a positive +5v trigger, Pin2 to Pin12 are set to "LOW" (0v) upon initialization. A trigger is created by setting the pins to "HIGH" for the specified number of ms, according to the incoming MIDI notes.
Pins are assigned in ascending order per semi tone starting with C of the specified octave for Pin2. For example: if set to 1 --> Pin2 = C1, Pin3 = C#1, Pin4 = D1 etc. (standard for most drum sequencers).
You can change the octave at the following line of code (change the "1"):
Code: [Select]
int octave = 1;
The code also allows you to specify the MIDI channel to receive. Using "MIDI_CHANNEL_OMNI", my code currently listens to all MIDI channels, but this can be replaced with 1 to 16 (between brackets) to specify a single channel. Note: start counting at (0) like this:
Code: [Select]
MIDI.begin(MIDI_CHANNEL_OMNI)   // Receive all midi channels
MIDI.begin(0)   // Receive only MIDI channel 1
MIDI.begin(5)   // Receive only MIDI channel 6
...

Copy and paste the full code below in a new Arduino Project to edit and upload it to your Arduino just like with the sample code :). You can test my code by setting the trigger time to 1000ms, connecting a MIDI keyboard and playing a B1. This will set Pin13 to "HIGH" for one second and since Pin13 is hardwired to the built-in LED of an Arduino you should see the LED light up for a second. You can also grab a multimeter and test if the other Pins come up for the right notes. Don't forget to set your trigger time back to 10ms or the value you need of course ;)

4) Connection
The Arduino Nano can run at 6v to 12v unregulated power through it's "RAW" power input thanks to its onboard regulator. So I'm powering my Arduino in parallel with the CB-55 voiceboard at 9v. This is plenty for the voices and allows the use of any guitar pedal power supply :)

Now it's only a matter of connecting the output pins to the trigger INs of the voiceboard. I believe it's best to follow the standard MIDI mapping for drums so you can easily use your CB-55 with any drum sequencer.
MIDI Drum mapping:     
- BD = C1
- Snare = D1
- Rim = C#1
- CHH = F#1
- OHH = A#1
Connection from Arduino Pins to trigger INs
- Arduino Pin 2 (=C1) --> Trig in 4 (BD)
- Arduino Pin 4 (=D1) --> Trig in 1 (Snare)
- Arduino Pin 3 (=C#1) --> Trig in 3 (Rim)
- Arduino Pin 8 (=F#1) --> Trig in 2 (CHH)
- Arduino Pin 12 (=A#1) --> Trig in 5 (Accent)

I hope this helps for now! Here's a few pictures: https://www.flickr.com/gp/151239360@N03/16L655
Thanks to Paul (Circuit Benders) and Gert from the forum.
Credits to Travis T. who shared is code for use with his CR-5000. I mainly simplified and adapted this for use with the CB-55. (https://github.com/TravisThatcher/MIDI_TO_TRIGGER_SYNC)

Code: [Select]
// Original Roland CR-5000 Midi Sync and Midi Control Rev 02. 12-14 by Travis Thatcher
// Simplified for MIDI note to positive going triggers only using all 12 digital pins by Pieter Vochten (May-2016)

// Include the MIDI library (make sure it is installed in your Arduino IDE!
// Downloadable at: http://playground.arduino.cc/Main/MIDILibrary
#include "MIDI.h"
MIDI_CREATE_DEFAULT_INSTANCE(); // Create a default, nameless MIDI instance for handling MIDI events

#define NUMTRIGS 12 // This code will use all 12 digital output pins

#define TRIGGER_TIME 10  // this defines the trigger length in ms (edit as needed)

// This code uses 12 pins starting from Pin2 up to Pin13
// Pin 13 can be used for debugging because on most Arduino boards, it is connected to a built-in LED
int triggerPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};   // Create an array of 12 integers resembling the Pin numbers
// Create an array in which the hold times (timestamps in ms) for every trigger PIN will be stored (initially all set to zero)
int triggerTimes[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// Create an array containing the trigger state for every trigger PIN (initially all set to LOW, idle)
// Triggers will be set to HIGH (= +5v)
// Replace LOW / HIGH throughout the whole code to create inverted, negative going triggers (+5v idle, 0v triggers)
int triggerStates[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};

int currentTime = 0;
int octave = 1;

byte incomingByte;
byte velocity;

void setup() {    // The following code is executed once for initialization
  MIDI.turnThruOff();
  MIDI.begin(MIDI_CHANNEL_OMNI);  // this sets the midi channel, (0)= midi channel 1, (1)= midi channel 2 etc... or MIDI_CHANNEL_OMNI

  // Specify the octave; The octave can range from -2 to 8, depending on your needs.
  // Pins are assigned in ascending order per semi tone starting with C of the specified octave for Pin2
  // For example: if set to 1 --> Pin2 = C1, Pin3 = C#1, Pin4 = D1 etc. (standard for most drum sequencers)
  octave = 1;

  // Initialize the output pins. The Arduino uses + triggers (5v) so we set their initial state to Low (0v)

  pinMode(2, OUTPUT);
  digitalWrite(2, LOW);
  pinMode(3, OUTPUT);
  digitalWrite(3, LOW);
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
  pinMode(5, OUTPUT);
  digitalWrite(5, LOW);
  pinMode(6, OUTPUT);
  digitalWrite(6, LOW);
  pinMode(7, OUTPUT);
  digitalWrite(7, LOW);
  pinMode(8, OUTPUT);
  digitalWrite(8, LOW);
  pinMode(9, OUTPUT);
  digitalWrite(9, LOW);
  pinMode(10, OUTPUT);
  digitalWrite(10, LOW);
  pinMode(11, OUTPUT);
  digitalWrite(11, LOW);
  pinMode(12, OUTPUT);
  digitalWrite(12, LOW);
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);

  // The built-in MIDI library is capable of filtering different types of MIDI events such as notes, control, sync...
  // This line specifies how to handle MIDI Note ON messages, referring to the function "void HandleNoteOn(..."
  MIDI.setHandleNoteOn(HandleNoteOn); // Set the handler for NoteOn events
}

void loop() {     // The following code is executed continuously after the setup
  MIDI.read();
  // Read in the current timestamp in ms and store it in currentTime
  currentTime = millis();

  // Check the state of the 12 triggers in a loop
  for (int i = 0; i < NUMTRIGS; i++) {  // "While i = 0 to i < 12 (number of triggers), do... for Array Position[i]" --> running through the arrays
    // For every HIGH trigger: compare its timestamp with the currentTime
    // Set the trigger state to LOW if the difference exceeds the specified TRIGGER_TIME
    if (triggerStates[i] == HIGH) {
      if (currentTime - triggerTimes[i] > TRIGGER_TIME) {
        digitalWrite(triggerPins[i], LOW);
        triggerStates[i] = LOW;
      }
    }
  }
}

void HandleNoteOn(byte channel, byte pitch, byte velocity) {  // this code is executed when MIDI notes are received
  // triggers!
  for (int i = 0; i < NUMTRIGS; i++) {
    if (pitch - 24 - (12 * octave) == i) {  // Calculate the PIN to match the incoming Pitch and the specified octave by subtracting a plural of "12"
      digitalWrite(triggerPins[i], HIGH);   // Set the actual matching PIN to HIGH (=> trigger active)
      triggerStates[i] = HIGH;              // Save the triggerState for this PIN in the triggerStates array
      triggerTimes[i] = currentTime;        // Save the current timestamp for this PIN in the triggerTimes array
    }
  }
}
« Last Edit: January 24, 2017, 02:35:42 PM by Pieterv1 »
Logged

Circuitbenders

  • crustypaul
  • Admin
  • This person is dangerously insane.
  • *****
  • Karma: 1102
  • Offline Offline
  • Posts: 2451
    • Circuitbenders.co.uk

excellent work!

Have you checked how close simultaneous triggers are actually output on a scope? You often find on midi to trigger interfaces that if you trigger several outputs simultaneously they will actually output pulses several milliseconds apart instead of at exactly the same time. If you use output 5 for the accent and trigger 5 outputs at once, you might find that output 5 actually pulses with a noticable lag after output 1, which might produce a weird effect if the accent triggers after the start of a bass drum instead of dead on it.

I think a better way to do it might be to have a dedicated accent output that triggers whenever the interface receives any note with 127 velocity, and then process the velocity in the code before anything else. that way the accent can never be triggered late.
I'm sure i have a couple of Arduino Nano's somewhere. I'll have a go at putting this project together when i get a chance.
Logged
i am not paid to listen to this drivel, you are a terminal fool

Pieterv1

  • Newbie
  • *
  • Karma: 0
  • Offline Offline
  • Posts: 3

Haven't thought about that! Maybe that's because I didn't hear such artefacts in the first place. But yes, the Arduino probably handles everything one by one. Maybe the accent trigger can be a littlebit longer, 20ms? I'll try it out later, I'm still rebuilding my voiceboard ;)

Also I forgot to mention that the Arduino produces some slightly audible noise whenever it receives MIDI data. It is recommended to turn of sync output of the sequencer you're using. Sync generetes a constant tone, every MIDI note a short click, but it's very silent compared to the drum voices. I guess (hope) it has something to do with the fact that I'm running them of the same power supply. I'm going to try running the Arduino of battery once, maybe that solves it.
Logged

cmcpress

  • Newbie
  • *
  • Karma: 0
  • Offline Offline
  • Posts: 1

N00b question here - I've built the bulk of the board, and I'm trying to include in the case a MIDI-trigger converter as posted by Pieter here.  I've built the breadboard / notes and volts part and it works fine - now turning to implementing it on veroboard - but it's unclear how you've implemented this here.

@pieterv1 - Are you able to show the bottom of the board / a bit more detail / a tagboard plan about how you've connected this up? It's unclear which orientation the stripboard is in.

Question 2 - how have you wired up the converter / CB-55 from the power input? Just connecting the two sets of wire by soldering together, so the two boards are in parallel?
Logged

Pieterv1

  • Newbie
  • *
  • Karma: 0
  • Offline Offline
  • Posts: 3

Hi cmcpress, I had to cut some traces / solder some jumpers.. I'm not even sure anymore if it was a stripboard, but it's a fairly simple circuit - just follow the tutorials I linked to.

And yes, the arduino is powered in parallel with the voice board. But that could be introducing some interference on my voiceboard
Logged