Tuesday, January 27, 2015

Part 3, Arduino Sketch, BeatBuddy Tempo Pedal / MIDI Beat Clock Generator / OPEN SOURCE


See Part 1 for hardware design and schematic.


See Part 2, for parts list and build pictures.


This is Part 3, Arduino sketch.


See Part 4 for software design notes.


Sketch:

// send MIDI beat clocks to BeatBuddy
// beat clocks are 24x faster than BPM
// sent in real time as a constant MIDI command: 0xf8
//    via SoftSerial at MIDI baud rate: 31250 Hz
// BeatBuddy operating range: 40 to 300 BPM
//   which is 16 Hz to 120 Hz beat clock
//     40 BPM * 24 / 60 = 16 Hz
//    300 BPM * 24 / 60 = 120 Hz
// SoftwareSerial used for:
//    MIDI outout
//    7 segment display (pedal BPM)
// Serial Debug Monitor settings: 115200 baud, No line ending

#include <TimerOne.h>        // 16 bit counter with uS resolution
#include <SoftwareSerial.h>

// MIDI Tx via software serial port
const int MMCtx = 8;              // D8 Tx: Midi Clock output
const int MMCrx = 7;              // D7 Rx (not used)
const int MIDIbaud = 31250;        
SoftwareSerial MMC(MMCrx, MMCtx); 

// serial 7 segment display BPM via software serial port
const int s7sTx = 10;             // D10 Tx to display
const int s7sRx = 9;              //  D9 Rx (not used)
const int s7sBrightness = 255;    //  0 to 255
SoftwareSerial s7s(s7sRx, s7sTx);

const int POT = A1;      // Expression Pedal, 50K pot wiper, 0 to 3.3V
const int Enable = 11;   // ~Enable Clock Out
const int LowRange = 12; // ~LowRange
const int HighRange = 13;// ~HighRange
const int LowMin = 40;   // Low range min BPM
const int LowMax = 160;  // Low range max BPM 
const int MidMin = 120;  // Mid range min BPM
const int MidMax = 240;  // Mid range max BPM
const int HighMin = 180; // High range min BPM
const int HighMax = 300; // High range max BPM
const int rLED = 3;      // ~Red
const int gLED = 5;      // ~Green
const int bLED = 6;      // ~Blue

// MIDI commands
const int CLOCK = 0xf8;  // MIDI Tempo Clock
//const int START = 0xfa; // MIDI Start command
//const int STOP = 0xFC;  // MIDI Stop command

boolean Enabled; 
int BPM;             // current beat clock rate
int newBPM;          // reading from pedal
long PERIOD;         // microseconds, setting Timer1
char tempString[10]; // for sprintf

void MIDI_CLOCK() // ISR: send MIDI CLock
{
  MMC.write(CLOCK);
}

void setTimer()  // called when BPM changed
{
  BPM = newBPM;
  // 1e6 / (BPM*24/60) is period in microseconds
  // reduced for efficient coding:
  PERIOD = 2.5e6 / BPM;  // period in microseconds, 24x beat clock
  Timer1.initialize(PERIOD);
}

void  setup() 
{
  pinMode(Enable, INPUT_PULLUP);    // Low: enable MIDI clock output
  pinMode(LowRange, INPUT_PULLUP);  // Low: 40 to 200 BPM
  pinMode(HighRange, INPUT_PULLUP); // Low: 140 to 300 BPM
  pinMode(rLED, OUTPUT);
  pinMode(gLED, OUTPUT);
  pinMode(bLED, OUTPUT);
  setRGB(HIGH, HIGH, HIGH);  // RGB LED off
  
  s7s.begin(9600);              // Tx to s7s at default baud rate
  clearDisplay();               // Clears display, resets cursor
  setBrightness(s7sBrightness); 
  s7s.print("-HI-");
  delay(1000);
  //setDecimals(0b111111); // Turn on all decimals, colon, apos
  //s7s.print("8888");
  //delay(1000);
  clearDisplay();
  
  MMC.begin(MIDIbaud);   // MIDI baud = 31250
  
  Serial.begin(115200);  // serial monitor
  Serial.println("BeatBuddyTempoClock,v1.2, 01/21/2015");
  Serial.println("Enable Switch On to output MIDI clock");
  Serial.println("Select BPM range switch Low, Med, High");
  Serial.println("Then adjust BPM using Expression Pedal: Pot 10K ohms or greater");

  Timer1.attachInterrupt( MIDI_CLOCK ); // set interrupt handler
}

void loop() 
{
  Enabled = (0==digitalRead(Enable));  // read enable switch
  if(Enabled) 
    Timer1.attachInterrupt( MIDI_CLOCK );  // enable interrupt, set handler 
  else
  {
    Timer1.detachInterrupt();  // disable interrupt
    setRGB(LOW, HIGH, HIGH);   // red
  }
  if(0==digitalRead(LowRange))  // read low range contact
  { // low range   
    newBPM = map(analogRead(POT), 1, 1010, LowMin, LowMax);
    newBPM = constrain(newBPM, LowMin, LowMax); 
    if (Enabled)
      setRGB(HIGH, HIGH, LOW);  // blue  
  }
  else 
  {  
    if(0==digitalRead(HighRange)) // read high range contact 
    { // high range  
      newBPM = map(analogRead(POT), 1, 1010, HighMin, HighMax);
      newBPM = constrain(newBPM, HighMin, HighMax);
      if (Enabled)
        setRGB(HIGH, LOW, HIGH);  // green
    }   
    else
    { // mid range
      newBPM = map(analogRead(POT), 1, 1010, MidMin, MidMax);
      newBPM = constrain(newBPM, MidMin, MidMax); 
      if (Enabled)
        setRGB(HIGH, LOW, LOW);  // green + blue
    }
  }
  
  if (newBPM != BPM)  // was the pedal changed?
  {
    setTimer();
    sprintf(tempString, "%4d", BPM);
    s7s.print(tempString);
    Serial.print(BPM);
    Serial.print(" BPM, ");
    Serial.print(PERIOD);
    Serial.println(" Period, microseconds");
  } 
  else 
    delay(20);
}

void setRGB(boolean r, boolean g, boolean b)
{
  digitalWrite(rLED, r);
  digitalWrite(gLED, g);
  digitalWrite(bLED, b);  
}

void clearDisplay()
{
  s7s.write(0x76);  // Clear display and reset cursor
}

void setBrightness(byte value)
{
  s7s.write(0x7A);  // Set brightness command byte
  s7s.write(value);  // brightness data byte, 0 to 255
}

// Turn on any, none, or all of the decimals.
//  The six lowest bits in the decimals parameter sets a decimal 
//  (or colon, or apostrophe) on or off. A 1 indicates on, 0 off.
//  [MSB] (X)(X)(Apos)(Colon)(Digit 4)(Digit 3)(Digit2)(Digit1)
void setDecimals(byte decimals)
{
  s7s.write(0x77);
  s7s.write(decimals);
}