Showing posts with label linux. Show all posts
Showing posts with label linux. Show all posts

Monday, April 18, 2011

So my buddy over at Forshee Media was asking me the other day if I could build him a DVD duplicator on the cheap. I said “Challenge Accepted”. Here's how I did it.

The Hardware

Being a guy that works in the IT field, I get a lot of junk or old systems donated to me. I had plenty of parts sitting around to build a decently running, purpose built appliance like this, but I wouldn't want to use systems with their specs for a main desktop. Here are the specs:

  • Pentium 4 processor
  • 512MB PC2-3200
  • 1 – 80GB PATA Drive for the OS
  • Motherboard with 4 SATA ports
  • 2 PATA connectors
  • Case with at least 4-5.25” drive bays

Sounds pretty simple right? I also had a few DVD burners sitting around that I could use to test the software. I didn't have the 4 burners that he wanted but the software would scale.

The Software

I installed Ubuntu 10.10 Server Edition on it because it takes up very few resources. This device will have nothing but a power cord and a network cable hooked to it. As such, it will be remotely controlled via ssh.

The network card has everything statically assigned. I got some of my buddy's network specs ahead of time. How this can be accomplished is edit /etc/network/interfaces and here is what I added to his configuration:

iface eth0 inet static
address 192.168.1.200
netmask 255.255.255.0
gateway 192.168.1.1

Pretty simple. With an “apt-get install ssh”, I unhooked all of the peripherals and ran this thing like the headless drone it is supposed to be.

Up next was finding out what DVD drives are being recognized. This is important as the dev assignments are statically coded into the coming scripts. To find out what drives are available “udisks --enumerate | grep sr”. Normally optical drives are specified with an sr#. Your distro might vary.

The Code

Now we're getting to the good part. The meat and potatoes of this operation. Here is a script that works for me with 4 drives:

#!/bin/bash

dvd1=/dev/sr0
dvd2=/dev/sr1
dvd3=/dev/sr2
dvd4=/dev/sr3

dvd_prep(){
    local dvd_to_load=$1
    local state=$2
    dvd_load=0
    sleep 2
    while [ $dvd_load -eq 0 ]
    do
        dvd_load=`udisks --show-info $dvd_to_load | grep -c "blank: *$state"`
    done
}

echo "Load DVD into top tray"
eject $dvd1
dvd_prep $dvd1 0
echo "Copying DVD to image"
dd if=$dvd1 of=dvdimage.iso bs=512
eject $dvd1

echo "How many copies?"
read copies
count=1
while [ $count -le $copies ]
do
    remaining=0
    if [ $copies -gt 1 ]
    then
        remaining=$((copies-count))
    fi

    if [ $copies -eq 1 -o $remaining -eq 0 ]
    then
        dvd_prep $dvd1 1
        echo "Number $count"
        eject $dvd1
        growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd1=dvdimage.iso > duplicator_log 
        eject $dvd1
        let "count+=1"

    elif [ $copies -eq 2 -o $remaining -eq 1 ]
        eject $dvd1
        eject $dvd2
       
        dvd_prep $dvd1 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd2 1
        echo "Number $count"
        let "count+=1"
        growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd1=dvdimage.iso  > duplicator_log  && eject $dvd1 & growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd2=dvdimage.iso  > duplicator_log && eject $dvd2 

    elif [ $copies -eq 3 -o $remaining -eq 2 ]
        eject $dvd1
        eject $dvd2
        eject $dvd3
       
        dvd_prep $dvd1 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd2 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd3 1
        echo "Number $count"
        let "count+=1"

        growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd1=dvdimage.iso  > duplicator_log  && eject $dvd1 & growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd2=dvdimage.iso  > duplicator_log && eject $dvd3 & growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd3=dvdimage.iso  > duplicator_log && eject $dvd3    
    else
        eject $dvd1
        eject $dvd2
        eject $dvd3
        eject $dvd4
       
        dvd_prep $dvd1 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd2 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd3 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd4 1
        echo "Number $count"
        let "count+=1"

        growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd1=dvdimage.iso  > duplicator_log  && eject $dvd1 & growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd2=dvdimage.iso  > duplicator_log && eject $dvd3 & growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd3=dvdimage.iso  > duplicator_log && eject $dvd3  & growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd4=dvdimage.iso  > duplicator_log && eject $dvd4 
    fi
done
echo "Done"
rm dvdimage.iso


All This does is copy DVDs. If it's worth doing, it's worth doing overkill. That being said...

LightScribe

Since the drives I bought for this project are LightScribe compatible, I decided to write a bit more in this script in order to turn this not only into a DVD toaster, but also make mass LightScribe operations possible. Doing more than, say, 5 of the same LS label is tedious.

Get this label here
I found that you need to have the LightScribe System software installed as well as the Lacie LS Utilities (because they have a scriptable CLI implementation). It is a bit of a process to get this working, but worth it.

How this works is when the DVD is done being written, the user is prompted to turn the DVD over to etch a bitmap label on the top. The LightScribe bits of this code are activated when the script is fed an argument of an image name without an extension (ex. "./duplicator label" not "./duplicator label.bmp")

Le Code in red is what was changed from the previous code:


#!/bin/bash

LS=$1
dvd1=/dev/sr0
dvd2=/dev/sr1
dvd3=/dev/sr2
dvd4=/dev/sr3

dvd_prep(){
    local dvd_to_load=$1
    local state=$2
    dvd_load=0
    sleep 2
    while [ $dvd_load -eq 0 ]
    do
        dvd_load=`udisks --show-info $dvd_to_load | grep -c "blank: *$state"`
    done
}

ls_prep(){
    local dvd_to_load=$1
    dvd_load=0
    sleep 2
    while [ $dvd_load -eq 0 ]
    do
        dvd_load=`4L-cli mediainfo $dvd_to_load | grep Oriented | cut -d' ' -f4`
    done
}


echo "Load DVD into top tray"
eject $dvd1
dvd_prep $dvd1 0
echo "Copying DVD to image"
dd if=$dvd1 of=dvdimage.iso bs=512
eject $dvd1

echo "How many copies?"
read copies
count=1
while [ $count -le $copies ]
do
    remaining=0
    if [ $copies -gt 1 ]
    then
        remaining=$((copies-count))
    fi

    if [ $copies -eq 1 -o $remaining -eq 0 ]
    then
        dvd_prep $dvd1 1
        echo "Number $count"
        eject $dvd1
        growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd1=dvdimage.iso > duplicator_log

    if [ $LS ]
    then    
        eject $dvd1
        echo "Flip the DVD over and close the drive tray."
        ls_prep $dvd1
        4L-cli print $dvd1 labels/$LS.bmp
    fi

        eject $dvd1
        let "count+=1"

    elif [ $copies -eq 2 -o $remaining -eq 1 ]
        eject $dvd1
        eject $dvd2
     
        dvd_prep $dvd1 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd2 1
        echo "Number $count"
        let "count+=1"
        growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd1=dvdimage.iso  > duplicator_log  && eject $dvd1 &
    growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd2=dvdimage.iso  > duplicator_log && eject $dvd2
  
if [ $LS ]
    then    
        eject $dvd1
        eject $dvd2
        echo "Flip the DVDs over and close the drive trays."
        
        ls_prep $dvd1
        ls_prep $dvd2

        4L-cli print $dvd1 labels/$LS.bmp &
        4L-cli print $dvd2 labels/$LS.bmp
    fi

    elif [ $copies -eq 3 -o $remaining -eq 2 ]
        eject $dvd1
        eject $dvd2
        eject $dvd3
     
        dvd_prep $dvd1 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd2 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd3 1
        echo "Number $count"
        let "count+=1"

        growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd1=dvdimage.iso  > duplicator_log  && eject $dvd1 &
    growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd2=dvdimage.iso  > duplicator_log && eject $dvd3 &
    growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd3=dvdimage.iso  > duplicator_log && eject $dvd3  

    if [ $LS ]
    then    
        eject $dvd1
        eject $dvd2
        eject $dvd3
        echo "Flip the DVDs over and close the drive trays."
        
        ls_prep $dvd1
        ls_prep $dvd2
        ls_prep $dvd3

        4L-cli print $dvd1 labels/$LS.bmp &
        4L-cli print $dvd2 labels/$LS.bmp &
        4L-cli print $dvd3 labels/$LS.bmp
    fi

    else
        eject $dvd1
        eject $dvd2
        eject $dvd3
        eject $dvd4
     
        dvd_prep $dvd1 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd2 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd3 1
        echo "Number $count"
        let "count+=1"

        dvd_prep $dvd4 1
        echo "Number $count"
        let "count+=1"

        growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd1=dvdimage.iso  > duplicator_log  && eject $dvd1 &
    growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd2=dvdimage.iso  > duplicator_log && eject $dvd2 &
    growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd3=dvdimage.iso  > duplicator_log && eject $dvd3
    & growisofs -dvd-compat -use-the-force-luke=bufsize:32m -use-the-force-luke=notray -Z $dvd4=dvdimage.iso  > duplicator_log && eject $dvd4

    if [ $LS ]
    then    
        eject $dvd1
        eject $dvd2
        eject $dvd3
        eject $dvd4
        echo "Flip the DVDs over and close the drive trays."
        
        ls_prep $dvd1
        ls_prep $dvd2
        ls_prep $dvd3
        ls_prep $dvd4

        4L-cli print $dvd1 labels/$LS.bmp &
        4L-cli print $dvd2 labels/$LS.bmp &
        4L-cli print $dvd3 labels/$LS.bmp &
        4L-cli print $dvd4 labels/$LS.bmp
    fi
    fi
done
echo "Done"
rm dvdimage.iso


If you just want a script that will handle Lightscribe and nothing else:

#!/bin/bash

LS=$1
dvd1=/dev/sr0
dvd2=/dev/sr2
dvd3=/dev/sr1
dvd4=/dev/sr3

ls_prep(){
    local dvd_to_load=$1
    media_ready=0
    while [ $media_ready -eq 0 ]   
    do
        media_ready=`4L-cli mediainfo $dvd_to_load | grep media | cut -d' ' -f3`
    done
}

echo "How many copies?"
read copies
count=1
while [ $count -le $copies ]
do
    remaining=0
    if [ $copies -gt 1 ]
    then
        remaining=$((copies-count))
    fi

    if [ $copies -eq 1 -o $remaining -eq 0 ]
    then
        eject $dvd1
        ls_prep $dvd1
        4L-cli print -q best $dvd1 "/home/ftp/$LS.bmp" && eject $dvd1
        echo "Number $count"
        let "count+=1"

    elif [ $copies -eq 2 -o $remaining -eq 1 ]
    then
        eject $dvd1
        eject $dvd2
        ls_prep $dvd1
        echo "Number $count"
        let "count+=1"
        ls_prep $dvd2
        echo "Number $count"
        let "count+=1"
        4L-cli print -q best $dvd1 "/home/ftp/$LS.bmp" && eject $dvd1 &
        4L-cli print -q best $dvd2 "/home/ftp/$LS.bmp" && eject $dvd2

    elif [ $copies -eq 3 -o $remaining -eq 2 ]
    then
        eject $dvd1
        eject $dvd2
        eject $dvd3
        ls_prep $dvd1
        echo "Number $count"
        let "count+=1"
        ls_prep $dvd2
        echo "Number $count"
        let "count+=1"
        ls_prep $dvd3
        echo "Number $count"
        let "count+=1"
        4L-cli print -q best $dvd1 "/home/ftp/$LS.bmp" && eject $dvd1 &
        4L-cli print -q best $dvd2 "/home/ftp/$LS.bmp" && eject $dvd2 &
        4L-cli print -q best $dvd3 "/home/ftp/$LS.bmp" && eject $dvd3

    else
        eject $dvd1
        eject $dvd2
        eject $dvd3
        eject $dvd4

        ls_prep $dvd1
        echo "Number $count"
        let "count+=1"
        ls_prep $dvd2
        echo "Number $count"
        let "count+=1"
        ls_prep $dvd3
        echo "Number $count"
        let "count+=1"
        ls_prep $dvd4
        echo "Number $count"
        let "count+=1"

        4L-cli print -q best $dvd1 "/home/ftp/$LS.bmp" && eject $dvd1 &
        4L-cli print -q best $dvd2 "/home/ftp/$LS.bmp" && eject $dvd2 &
        4L-cli print -q best $dvd3 "/home/ftp/$LS.bmp" && eject $dvd3 &
        4L-cli print -q best $dvd4 "/home/ftp/$LS.bmp" && eject $dvd4

    fi
done





Notes
  • For maximum possible happiness, run the script with Lightscribe as root. 4L-cli is unnecessarily root bound for printing.
  • Remember to "chmod 777 duplicator_script" or "chmod a+x duplicator_script" for maximum possible happiness
  • This is a multi threaded application. Remember the rule of thumb: max 2 threads per processor core... for maximum possible happiness.

Monday, January 24, 2011


Extending the functionality of my hack job is necessary. I mean, when was the last time you saw someone spend hours building something and just wanted to hit them when they stopped at just good enough? I have some parts on order from the good folks at Sparkfun Electronics for phantom power and panel mount USB hookup (w00t!) and also a little surprise that I will be adding in as soon as I figure out how to mount it.

I had a thought the other day. If I can control volume and wah with the expression pedals, what else can I control? According to the manual (page 17) I can control quite a few things with them (not all are useful though). Then it was just a matter for code and how to shift between modes of operation.

Here is my logic: if you step on two of the effects pedals at once, the program sends the expression pedals into a different mode depending on which two were pressed.

First off, there has to be a global array variable that can be changed depending on what mode is desired. I put “int expression_choice[] = {1,2};” at the top of the code. I arbitrarily assigned wah the value of one and volume the value of two. Array index 0 is on the left and index 1 is on the right for left and right pedals respectively.




Secondly, there has to be something in the loop() function to catch when the two buttons are pressed simultaneously. For this it is necessary to put it BEFORE the effects change buttons in the if statement within the loop(). Here's what I did in order to get Volume/Wah, Double Wah, Reverb/Volume and Delay/Volume.

void loop() {
  //////////////////////
  //Expression Options//
  //////////////////////
  if (digitalRead(23) == LOW && digitalRead(25) == LOW)//1 AND 2
  {
    expression_choice[0] = 1;  //Wah
    expression_choice[1] = 2;  //Volume
  }
  else if (digitalRead(25) == LOW && digitalRead(29) == LOW)//2 AND 3
  {
    expression_choice[0] = 1;  //Wah
    expression_choice[1] = 1;  //Double Wah!
  }
  else if (digitalRead(29) == LOW && digitalRead(27) == LOW)//3 AND 4
  {
    expression_choice[0] = 3;  //Reverb
    expression_choice[1] = 2;  //Volume

  }
  else if (digitalRead(27) == LOW && digitalRead(33) == LOW)//4 AND 5
  {
    expression_choice[0] = 4;  //Delay
    expression_choice[1] = 2;  //Volume

  }
  ///////////
  //EFFECTS//
  ///////////

  //
  //Lower Button Bank
  //

  //Button 1
  else if(digitalRead(23) == LOW)....

Beyond that, I did a bit of a shakeup at the bottom of the code. Since the left and right pedals are no longer strictly wah and volume, I renamed the changeVolume and wahCharacter functions to reflect that. Also in those functions, I added some extra if logic so that based on what value expression_choice is it will reflect that by sending the proper Control Change codes for whatever mode I want. Here's the bottom of the code.

void rightPedal(byte channel, byte volume)
{
  Serial.print(0xB0 | (channel & 0x0F), BYTE); //Control Change
  if(expression_choice[1] == 2){
    Serial.print(0x07, BYTE);//Volume CC number
  }
  else if(expression_choice[0] == 1)
  {
    Serial.print(0x1B, BYTE);//Wah character CC number
  }
  Serial.print(volume & 0x7F, BYTE);//Value (0-127)
}


void leftPedal(byte channel, byte wah)
{
  Serial.print(0xB0 | (channel & 0x0F), BYTE);//Control Change

  if(expression_choice[0] == 1)
  {
    Serial.print(0x1B, BYTE);//Wah character CC number
  }
  else if(expression_choice[0] == 3)
  {
    Serial.print(0x12, BYTE);//Reverb character CC number
  }
  else if(expression_choice[0] == 4)
  {
    Serial.print(0x32, BYTE);//Delay timing CC number
  }

  Serial.print(wah & 0x7F, BYTE);//Value (0-127)
}

void expression(int pin)
{
  static int s_nLastPotValue = 0;
  static int s_nLastMappedValue = 0;
  int nCurrentPotValue = analogRead(pin);
  if(abs(nCurrentPotValue - s_nLastPotValue) < pot_threshold)     return;
  s_nLastPotValue = nCurrentPotValue;
  int nMappedValue = map(nCurrentPotValue, 0, 1023, 0, 127); // Map the value to 0-127
  if(nMappedValue == s_nLastMappedValue)
    return;
  s_nLastMappedValue = nMappedValue;
  if(nMappedValue >= 10)
  {
    if (pin == volume_pin)
    {
     rightPedal(midi_channel, nMappedValue);
    }
    else
    {
      leftPedal(midi_channel, nMappedValue);
    }
  }
  else//Turn it off
  {
    if (pin == volume_pin)
    {
     rightPedal(midi_channel, 0);
    }
    else
    {
      leftPedal(midi_channel, 0);
    }
  }
}

So if all goes according to plan, you should have something that works like this:

Saturday, January 22, 2011

Well, it's all come down to this: a clone of the FCB1010 based on the Arduino MEGA2560 microcontroller.

I got the MEGA2560 in the mail the other day and it is a real pain to use at first. NewSoftSerial.h is a necessary include. On a side note: I would like to thank Arduino for NOT pointing that out anywhere in the documentation and let me assume my brand new microcontroller was bricked for about 3 hours. Jeeze!

Hardware

Before hooking everything up, I had to first do some soldering. Well, not some. A ton. Everything hooked to the resistor packs in the image of my hardware setup below had to be hooked into a daughter board.


And here is the finished daughter board.
Top

Bottom


And here is the Arduino with everything hooked up to it and everything being insulated/held-on by good old-fashioned electrical tape.

I opted for pin/ribbon cable connections between the boards to avoid any spaghetti wiring. It keeps everything organized and neat. And I think it looks cool.

Software

OK so I'm not an experienced electronics engineer or claim to know very much about the topic. As such, I wasn't sure which switch went to which digital pin after I soldered them all in. So I wrote some code to easily tell me. I knew the input pins were 22-33 so I wrote this code, uploaded, opened the serial monitor and started pressing buttons on the footswitch boards.

#include <NewSoftSerial.h>

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  if(digitalRead(22) == LOW){
    Serial.println("22 pressed.");
  }
  if(digitalRead(23) == LOW){
    Serial.println("23 pressed.");
  }
  if(digitalRead(24) == LOW){
    Serial.println("24 pressed.");
  }
  if(digitalRead(25) == LOW){
    Serial.println("25 pressed.");
  }
  if(digitalRead(26) == LOW){
    Serial.println("26 pressed.");
  }
  if(digitalRead(27) == LOW){
    Serial.println("27 pressed.");
  }
  if(digitalRead(28) == LOW){
    Serial.println("28 pressed.");
  }
  if(digitalRead(29) == LOW){
    Serial.println("29 pressed.");
  }
  if(digitalRead(30) == LOW){
    Serial.println("30 pressed.");
  }
  if(digitalRead(31) == LOW){
    Serial.println("31 pressed.");
  }
  if(digitalRead(32) == LOW){
    Serial.println("32 pressed.");
  }
  if(digitalRead(33) == LOW){
    Serial.println("33 pressed.");
  }
}



That step was quick. Figuring out the LED pins was a trail and error test that took a while longer.

The next step was to marry the test effect change code with the wah/volume code. This was no easy task and took hours of troubleshooting with a sniper rifle to get it right. Here is the final functional code to produce a clone of the FCB1010.


#include <NewSoftSerial.h>

int bank = 0x00;//Default bank of 0
int number_in_bank = 0x0A;//10 effects per bank
const int volume_pin = 0;// Pot connected to analog pin 0
const int wah_pin = 1;// Pot connected to analog pin 1
const int pot_threshold = 7;// Threshold amount to guard against false values
const int midi_channel = 0;

void setup() {
  for(int i = 34; i <= 43; i++)//Set up the LED pins and make them float high right off the bat
  {
    pinMode(i, OUTPUT);
    digitalWrite(i, HIGH);
  }

  Serial.begin(31250);//  Set MIDI baud rate:

  changeProgram(0x00,43);//Homed to program 1
}

void loop() {
  ///////////
  //EFFECTS//
  ///////////

  //
  //Lower Button Bank
  //

  //Button 1
  if(digitalRead(23) == LOW)
  {
    changeProgram(0x00,43);
  }
  //Button 2
  if(digitalRead(25) == LOW)
  {
    changeProgram(0x01,41);
  }
  //Button 3
  if(digitalRead(29) == LOW)
  {
    changeProgram(0x02,39);
  }
  //Button 4
  if(digitalRead(27) == LOW)
  {
    changeProgram(0x03,37);
  }
  //Button 5
  else if(digitalRead(33) == LOW)
  {
    changeProgram(0x04,35);
  }
  //Bank Down
  else if(digitalRead(31) == LOW)
  {
    if(bank != 0x01)
    {
      bank -= number_in_bank;
      changeProgram(0x00,43);
      delay(250);
    }
  }

  //
  //Upper Bank
  //

  //Button 6
  else if(digitalRead(22) == LOW)
  {
    changeProgram(0x05,42);
  }
  //Button 7
  else if(digitalRead(24) == LOW)
  {
    changeProgram(0x06,40);
  }
  //Button 8
  else if(digitalRead(26) == LOW)
  {
    changeProgram(0x07,38);
  }
  //Button 9
  else if(digitalRead(28) == LOW)
  {
    changeProgram(0x08,36);
  }
  //Button 10 (0)
  else if(digitalRead(30) == LOW)
  {
    changeProgram(0x09,34);
  }
  //Bank Up
  else if(digitalRead(32) == LOW)
  {
    if(bank != 0x19)
    {
      bank += number_in_bank;
      changeProgram(0x00,43);
      delay(250);
    }
  }
  //////////
  //VOLUME//
  //////////
  expression(volume_pin);
  ///////
  //WAH//
  ///////
  expression(wah_pin);
}

void changeProgram(int effect_number, int LED) {
  int program = bank + effect_number;//Program 1 would be bank(0)+effect_button(0) so 0x00 with 10 effects in the bank
  Serial.print(0xC0, BYTE);//Program Change
  Serial.print(program, BYTE);
  digitalWrite(LED,LOW);  //Light the associated LED

  for(int i =34; i <= 43; i++)//Turn off all the rest of the LEDs
  {
    if(i != LED)
    {
      digitalWrite(i, HIGH);
    }
  }
  delay(250);//Some debounce
}

void changeVolume(byte channel, byte volume)
{
  Serial.print(0xB0 | (channel & 0x0F), BYTE); //Control Change
  Serial.print(0x07, BYTE);//Volume CC number        
  Serial.print(volume & 0x7F, BYTE);//Volume level (0-127)
}


void wahCharacter(byte channel, byte wah)
{
  Serial.print(0xB0 | (channel & 0x0F), BYTE);//Control Change
  Serial.print(0x1B, BYTE);//Wah character CC number
  Serial.print(wah & 0x7F, BYTE);//Wah tone character (0-127)
}

void expression(int pin)
{
  static int s_nLastPotValue = 0;
  static int s_nLastMappedValue = 0;
  int nCurrentPotValue = analogRead(pin);
  if(abs(nCurrentPotValue - s_nLastPotValue) < pot_threshold)
    return;
  s_nLastPotValue = nCurrentPotValue;
  int nMappedValue = map(nCurrentPotValue, 0, 1023, 0, 127); // Map the value to 0-127
  if(nMappedValue == s_nLastMappedValue)
    return;
  s_nLastMappedValue = nMappedValue;
  if(nMappedValue >= 15){
    if (pin == volume_pin)
    { 
     changeVolume(midi_channel, nMappedValue);
    }
    else
    {
      wahCharacter(midi_channel, nMappedValue);
    }
  }
}


Extra

While messing with the code, I discovered that you can run 2 wah pedals off of the same controller. The effects processor takes the setpoints from both of the expression pedals and bounces in between them. It sounds like this [link]

Just change void expression to this:


void expression(int pin)
{
  static int s_nLastPotValue = 0;
  static int s_nLastMappedValue = 0;
  int nCurrentPotValue = analogRead(pin);
  if(abs(nCurrentPotValue - s_nLastPotValue) < pot_threshold)
    return;
  s_nLastPotValue = nCurrentPotValue;
  int nMappedValue = map(nCurrentPotValue, 0, 1023, 0, 127); // Map the value to 0-127
  if(nMappedValue == s_nLastMappedValue)
    return;
  s_nLastMappedValue = nMappedValue;
  if(nMappedValue >= 15){
    wahCharacter(midi_channel, nMappedValue);
  }
}


Still to Come

I have an LCD display on the way. This should be fun.

Possibly messing with shift keys.

<-Part 3   Part 5->

Thursday, January 20, 2011

Since my Arduino Mega just shipped today, I only have volume and wah to test really.

The FCB1010 chassis comes with 2 expression pedals that can be used to vary a plethora of things. For me, I just want things to be the way they used to be: wah on the left, volume on the right.

What makes volume and wah unique from the effects discussed previously are three things: 1. They are variable and work via potentiometers instead of pushbuttons, 2. They are analog inputs and not digital and 3. In the software, they are controlled by a Control Change instead of a Program Change(effects).

The hardware setup is as in the image below if you wish to test.

The code for both is the same. I'll admit I grabbed the code from this site. No shame in not re-inventing the wheel.

The control code for volume is 0x07 and the wah control is 0x11. It took some trial and error to find the wah control code since there is a typo in the documentation. “Wah off/position” should be “Wah on/position”.

Here's the code to enable wah. Just change 0x11 to 0x07 for volume.
// Constant
const int POT_PIN = 0;              // Pot connected to analog pin 0
const int POT_THRESHOLD = 7;        // Threshold amount to guard against false values
const int MIDI_CHANNEL = 0;         // MIDI Channel 1

void setup()
{
    Serial.begin(31250);     // Starts communication with the serial port
}

void loop()
{
    static int s_nLastPotValue = 0;
    static int s_nLastMappedValue = 0;

    int nCurrentPotValue = analogRead(POT_PIN);
    if(abs(nCurrentPotValue - s_nLastPotValue) < POT_THRESHOLD)
        return;
    s_nLastPotValue = nCurrentPotValue;

    int nMappedValue = map(nCurrentPotValue, 0, 1023, 0, 127); // Map the value to 0-127
    if(nMappedValue == s_nLastMappedValue)
        return;
    s_nLastMappedValue = nMappedValue;
    if(nMappedValue >= 15){
    
      MidiVolume(MIDI_CHANNEL, nMappedValue);
    }
}

void MidiVolume(byte channel, byte volume)
{
    Serial.print(0xB0 | (channel & 0x0F), BYTE);    // Control change command
    Serial.print(0x11, BYTE);                       // Volume command
    Serial.print(volume & 0x7F, BYTE);              // Volume 0-127
}


Still to Come


Beyond the breadboard: PCB and wiring
10 effect bank: 2 footswitch boards
Awesome eye candy: LCD screen
<- Part 2       Part 4 ->

Wednesday, January 19, 2011

So I threw out all of the code from the last post. Can't take the bull by the horns like that.

Testing


First off, I set the V-Amp to communicate through the MIDI in on channel 1. Consult the manual on how to do this. Editing the settings on that piece of gear is like working a Chinese puzzle box.

Upon examining the existing hardware, it was determined that the code has to use reverse HIGH/LOW logic. So when a switch goes low, instead of being normally high, it will change effects/effect banks and make the negative end of an LED go low when necessary. Get all that?

Test Program 1 – Bank Up/Bank Down


Like the title suggests, this code just changes the number (bank number) on the V-Amp's 7 segment displays using 2 momentary buttons. There are 5 effects per bank.
See image for hardware required.


int program = 0x00;
int bank = 0x01;
int number_in_bank = 0x05;

void setup() {
  for(int i = 2; i <= 3; i++)
  {
    pinMode(i, INPUT);
  }
 
  //  Set MIDI baud rate:
  Serial.begin(31250);
  changeProgram(program);
}

void loop() {
 
  if(digitalRead(2) == LOW){
    program += number_in_bank;
    changeProgram(program);
    delay(500);
  }
  if(digitalRead(3) == LOW){
    program -= number_in_bank;
    changeProgram(program);
    delay(500);
  }
}

void changeProgram(int program) {
  Serial.print(0xC0, BYTE);
  Serial.print(program, BYTE);
}


Verdict: WORKS!

Test Program 2 – One whole switchboard


This code will utilize one whole switchboard from the FCB1010. The button hooked into pin 2 of the Arduino will move the effects up one bank (add 5).
See image for hardware required.



int program = 0x00;
int bank = 0x00;
int number_in_bank = 0x05;

void setup() {
  for(int i = 2; i <= 7; i++)
  {
    pinMode(i, INPUT);
  }
 
  //  Set MIDI baud rate:
  Serial.begin(31250);
  changeProgram(program);
}

void loop() {
 
  if(digitalRead(2) == LOW)
  {
    program += number_in_bank;
    bank += number_in_bank;
    changeProgram(program);
    delay(500);
  }
  else if(digitalRead(7) == LOW)
  {
    program = bank + 0x00;
    changeProgram(program);
    delay(500);
  }
  else if(digitalRead(6) == LOW)
  {
    program = bank + 0x01;
    changeProgram(program);
    delay(500);
  }
  else if(digitalRead(5) == LOW)
  {
    program = bank + 0x02;
    changeProgram(program);
    delay(500);
  }
  else if(digitalRead(4) == LOW)
  {
    program = bank + 0x03;
    changeProgram(program);
    delay(500);
  }
  else if(digitalRead(3) == LOW)
  {
    program = bank + 0x04;
    changeProgram(program);
    delay(500);
  }
}

void changeProgram(int program) {
  Serial.print(0xC0, BYTE);
  Serial.print(program, BYTE);
}


Verdict: I tried factoring out the repetitive code into an elegant loop. It just plain didn't work. As written: WORKS!

Test 3 – LEDs


Factored-out some code. Also, the FCB1010 has LEDs that light up that correspond with whatever effect is active. This has to be accounted for in the code.

See image for hardware required.

int program = 0x00;
int bank = 0x00;
int number_in_bank = 0x05;

void setup() {
  for(int i = 2; i <= 7; i++)
  {
    pinMode(i, INPUT);
  }
 for(int i = 8; i <= 12; i++)
 {
   pinMode(i, OUTPUT);
   digitalWrite(i, HIGH);
 }
  //  Set MIDI baud rate:
  Serial.begin(31250);
  changeProgram(program,0x00,0);
}

void loop() {
 
  if(digitalRead(2) == LOW)
  {
    program += number_in_bank;
    bank += number_in_bank;
    changeProgram(program,0x00,0);
    delay(500);
  }
  else if(digitalRead(7) == LOW)
  {
    changeProgram(program,0x00,12);
  }
  else if(digitalRead(6) == LOW)
  {
    changeProgram(program, 0x01,11);
  }
  else if(digitalRead(5) == LOW)
  {
    changeProgram(program,0x02,10);
  }
  else if(digitalRead(4) == LOW)
  {
    changeProgram(program, 0x03, 9);
  }
  else if(digitalRead(3) == LOW)
  {
    changeProgram(program,0x04,8);
  }
}

void changeProgram(int program, int effect_number, int LED) {
  program = bank + effect_number;
  Serial.print(0xC0, BYTE);
  Serial.print(program, BYTE);
  digitalWrite(LED,LOW);
 
  for(int i = 8; i <= 12; i++){
    if(i != LED)
    {
      digitalWrite(i, HIGH);
    }
  }
  delay(500);
}


Verdict: WORKS!!!!!!1!!one!!!

Future Installments


I ran out of I/O and I lack the know-how to build a multiplexing circuit. As such, I will have to wait until the Arduino MEGA256 arrives to add more stuff. (Sad face)

Still to go: add another 5 effects changing switches and a down bank switch (Another footswitch board), expression pedals and (optional) an LED/LCD screen telling me what effect is active.
<- Part 1       Part 4 ->

Sunday, January 16, 2011

So I'm a guitarist that has been playing for right around 12 years. As such, I've amassed some equipment that is quite pricey.

Last week a power surge took out my Behringer FCB1010 midi foot controller that I use to control my Behringer V-Amp Pro multieffects processor. Double damn it!

I was sad and pissed off. I think I went through the seven stages of grief when I saw a bunch of surface mount resistors and transistors let the magic smoke out on the foot controller's main circuit board. Then a thought occurred to me. The controller board is nothing more than a crude microcontroller like a BASIC Stamp or an Arduino. All of the actual physical controls were in tact (pushbuttons and rheostats). I could build a better foot controller with an Arduino.

Planning

I have all of the original manuals for both the V-Amp and the FCB1010 that show the MIDI implementation and control codes. So those values just need plugged into the code.

I want this new controller to have the following features:
  1. Run on phantom power through the MIDI out port
  2. USB port mounted where the mains power used to come into the chassis for programming without tearing the thing apart.
  3. Fully-functional volume and wah expression pedals
  4. All selector switches function as labeled.

Reverse Engineering

All drawings are the circuit boards unmodified. Some changes may be necessary.
The footswitch PCB Both identical to this:
The MIDI out PCB (MIDI in won't be used so omitted):

The Code

I'm really crappy at coding for a microcontroller that I haven't even received in the mail yet. As such, the code so far is just my thought process as to how it will work. This is just for the changing of banks and changing of programs via MIDI. Any help would be appreciated.
const byte BANKSELECT = 0x00;        //0-31
const byte CONTROL_CHANGE = 0xB0;    //For Volume and Wah
const byte PROGRAMCHANGE = 0xC0;    //0-127
const int MIDI_CHANNEL = 0;;  
const int SERIAL_PORT_RATE = 31250;
const int UPPERLIMIT = 127;
const int LOWERLIMIT = 0;
const int UPPER_BANK = 25;
const int LOWER_BANK = 1;
const int LED[] = {1,2,3,4,5,6,7,8,9,10};
const int SWITCH[] = {11,12,13,14,15,16,17,18,19,20,21,22};
int ACTIVE = 0;

void setup(){
  int BANK_PROGS[][5] = {};
  //Populate bank_progs array
  int i,j;
   int COUNTER = 0;
  for(i=1; i<26; i++)
  {
    for(j=0; j<5; i++)
      BANK_PROGS[i][j] == COUNTER;
      COUNTER++;
    {
    
    }
  }

  int x;
  for(x=0; x<10; x++){
    pinMode(LED[x], OUTPUT);
  }
  for(x=0; x<13; x++){
    pinMode(SWITCH[x], INPUT);
  }
  byte BANK = 0x01;            //Default Bank is homed
  Serial.begin(SERIAL_PORT_RATE);
}

void loop(){
  //Search for a high switch
  int x;
  for(x=0; x<10; x++){
    int SWITCHSTATE = digitalRead(SWITCH[x]);
    if(SWITCHSTATE == HIGH){
      if(x=5)
      {
        BANK_CHANGE(1);
      }else if(x=11)
      {
        BANK_CHANGE(0);
      }
      else
      {
        int program = 0;
        EFFECT_CHANGE(x);
      }
      ACTIVE = x;
    }
  }
  //TURN THE ACTIVE LED ON
  digitalWrite(LED[ACTIVE], HIGH);

}

void BANK_CHANGE(int BANKDIFF){
  int BANK=0;
  if(BANKDIFF==1){
  //Bank Up  
    if(BANK!=UPPER_BANK){
      BANK = BANK+2;
    }else{
      BANK = 0x00;
    }
  
  }else{
    //Bank Down
    if(BANK != 0){
      BANK = BANK-2;
    }else{
      BANK = UPPER_BANK;      
    }
  }
  Serial.print(CONTROL_CHANGE, BYTE);
  Serial.print(BANKSELECT, BYTE);
  Serial.print(BANK, BYTE);
}

void EFFECT_CHANGE(byte program){
    Serial.print(PROGRAMCHANGE, BYTE);
    Serial.print(program, BYTE);
}

Stay tuned for more as this project develops.
Part 2 ->

Thursday, October 7, 2010

*EDIT: To see a version that doesn't suck, click here*
I could not find a ready-made module for monitoring my KMS server with Nagios. All I wanted to know was what the current count of activations handed out by the server.

On the KMS Server

First off, some jury rigging. You have to set up a shared folder on your KMS server. In my case it was C:\nagios.

Next, create a batch file (kms_status.bat) with the following line:
cscript C:\windows\system32\slmgr.vbs -dli > C:\nagios\kms_status.txt

Next, set kms_status.bat to run as a scheduled task. I set it to run every hour. You can set it to whatever frequency you want.

On the Nagios Server

First, because I'm lazy, I added the following to rc.local instead of editing fstab in order to mount the Windows share from the KMS server.
mount -t cifs //[KMSSERVERADDRESS]/[SHARENAME] /media/net -o domain=[DOMAIN],username=[USERNAME],password=[PASSWORD]

Next, create a file called check_kms in /usr/local/nagios/bin and copy/paste the following code into it.
#!/bin/bash

COUNT=`grep -i "count" /media/net/kms_status.txt | awk '{print $3}' | tr '\r' ' '`

if [ $COUNT -le 24 ]
then
      echo "CRITICAL: Current Count: "$COUNT". The count is too low for KMS to work."
     exit 2
elif [ $COUNT -ge 25 ]
then     echo "OK: Current Count: "$COUNT". You can go have another cup of coffee now."
     exit 0
else
     echo "UNKNOWN: Something is wrong. kms_status.txt might not be available."
     exit 3
fi


You'll have to chmod +x check_kms

Add the following to /usr/local/nagios/etc/objects/commands.
define command{
     command_name check_kms
     command_line ./usr/local/nagios/bin/check_kms
}


The last thing you have to do is go to the configuration for your KMS server in Nagios and add the following.
define service{
     use generic-service
      host_name [KMS SERVER NAME]
      service_description KMS
     check_command check_kms
}


Lastly, restart Nagios. For Ubuntu users: /etc/init.d/nagios restart

If all goes according to plan, you should be up and running and should look like this.
Subscribe to RSS Feed Follow me on Twitter!