Posted on Leave a comment

Katduino: Part 5 – wiring notes

There’s a been a bit of interest in how I put this amp switcher together and I realised I hadn’t really shared any details about exactly how it’s all wired together so here’s some gutshots and some notes about how it’s all plumbed together.

The guts of the Katduino pedal
The front of the switch control board
The back of the switch control board
The relay board wired to the stereo socket

I’m using a standard Boss style 2.1mm DC socket so wiring this is straight forward – the positive terminal goes to the Raw V+ pin on the Arduino and the ground terminal goes to a GND pin.

The LCD screen is wired in with VCC and GND on the screen going to VCC and GND pins on the Arduino. The SDA and SCL screen pins go to A4 and A5 on the Arduino although you should double check the right pins for your screen and Arduino model. I also used i2c scanner programme to make sure I had the right HEX address for the screen.

// Set up the LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);

By switch vero board takes its voltage straight from a VCC pin on the Arduino and is grounded using a GND pin. Each footswitch is wired to the respective input pin on the Arduino as well.

// Define footswitch pins
#define S1 10
#define S2 11
#define S3 12
#define S4 13

The relay board connects to a couple of places and you’ll probably want to experiment here to make sure you get this right. If you get it wrong you’ll end up with a mismatch between the channel the Arduino thinks your amp is on and the actual channel.

The relay takes its voltage from a VCC pin on the Arduino and its GND connects to a spare GND pin. The two input terminals go to the correct Arduino pins that are specified in the code.

// Define the relay pins
#define CH1 2
#define CH2 3

Finally the relay board needs to connect to the 6.35mm stereo socket that I’ll be using for the TRS cable that runs between the pedal and the amp. For my relay, I have the following connections:

  • NO1 – ring (the central part of the jack)
  • COM1 – sleeve (part of the jack furthest from the tip)
  • NC1 – no connection
  • NO2 – tip
  • COM2 – sleeve (part of the jack furthest from the tip)
  • NC2 – no connection
Posted on Leave a comment

Katduino: Part Four – putting it all together

The final part of the plan was to get everything boxed up and permanently wired. This was pretty simple and a hooked all of the footswitches to a simple stripboard layout that let me feed a single 5v to all 4 switches, return a single ground and keep all of the resistors firmly in place.

And that’s about it – simple yet effective 🙂

Posted on Leave a comment

Katduino: Part Three – the code

I went through a number of different iterations of the code before I got it right – lots of fun with button press detection and debouncing to make sure we were only triggering actions based on committed button presses rather than any flutter or noise as the mechanics of the button were being converted to digital signals.

I also gradually added more functionality, tailoring an tweaking things to get them just how I wanted and then extracting some of the settings into variables making it easier for others to set their pedals up when writing code to their own Arduinos.

One feature I was particularly pleased with was the creation of some custom glyphs for the LCD that would show the Boss Katana logo when the pedal was fired up – this is all included in the v0.3 code 🙂

You can access the full code on Github, including all of the utilities and libraries I used – https://github.com/d13design/katduino

// Include libraries
#include <Wire.h>
#include <EEPROM.h>
#include <LiquidCrystal_I2C.h>

/* Here we have a bunch of tailorable settings to get your footswitch working just how you'd like */

// Should we display amp channels as 1, 2, 3 & 4 rather than A1, A2, B1, B2?
const bool numericDisplay = true;

// Define the switches that have to be held down to enter and exit programming mode
// Create a string with 4 characters, each character is a footswitch from 1-4. Use a "1" if you want that button to be pressed and a "0" for unpressed
// Default to enter program mode is switches 1 and 4 together ("1001")
// Default to exit program mode (and turn off momentary) is switches 2 and 4 together ("0101")
const String enterProgramModeCode = "1001";
const String exitProgramModeCode = "0101";
unsigned long timeToWaitForMomentary = 600; // time (in ms) to hold these buttons down for before entering / exiting momentary programming mode

/* End of tailorable settings */

// Define app name and version
const String APP_NAME = "KATDUINO";
const String APP_VERSION = "0.3";

// Define footswitch pins
#define S1 10
#define S2 11
#define S3 12
#define S4 13

// Define the relay pins
#define CH1 2
#define CH2 3

// Define relay voltages
#define H HIGH
#define L LOW

// Define required memory addresses
#define S_ADDR 0
#define S1_MOM_ADDR 10
#define S2_MOM_ADDR 15
#define S3_MOM_ADDR 20
#define S4_MOM_ADDR 25

// Set up the LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Define some LCD characters for the Boss Katana logo
uint8_t boss[8]     = {0x17,0x13,0x11,0x1B,0x1F,0x00,0x00,0x00};
uint8_t boss_b[8]   = {0x16,0x11,0x16,0x11,0x1E,0x00,0x00,0x00};
uint8_t boss_o[8]   = {0x0E,0x11,0x11,0x11,0x0E,0x00,0x00,0x00};
uint8_t boss_s[8]   = {0x1F,0x10,0x1F,0x01,0x1F,0x00,0x00,0x00};
uint8_t katana[8]   = {0x1F,0x09,0x09,0x19,0x17,0x00,0x00,0x00};

// Define variables for later use
int currState, prevState, currMom; // current amp state and last amp state
unsigned long timeSinceLastPress = 0; // timer to use for debounce testing
unsigned long debounceTime = 100; // time (in ms) to wait before counting a press as an actual press
unsigned long askedForMomentary = 0; // timer to use for momentary programming mode detection
bool inProgrammingMode = false; // are we in programming mode for setting a momentary button
String pressed = "0000"; String lastPressed = "0000"; // strings to use for comparing button states
bool s1Mom, s2Mom, s3Mom, s4Mom; // booleans to track if a button is set to be momentary
int stateBeforeMom = 0; // when the momentary button is down, what should we go back to when it's released
bool momentaryOn = false; // boolean to track if we're currently playing a momentary channel

// Function set up LCD loading screen
void initDisplay(){
  lcd.createChar(0, boss);
  lcd.createChar(1, boss_b);
  lcd.createChar(2, boss_o);
  lcd.createChar(3, boss_s);
  lcd.createChar(4, katana);
  lcd.setCursor(4, 0);
  lcd.write(0);
  lcd.print(" ");
  lcd.write(1);
  lcd.write(2);
  lcd.write(3);
  lcd.write(3);
  lcd.print(" ");
  lcd.write(4);
  lcd.setCursor(2, 1);
  String t = APP_NAME + " " + APP_VERSION;
  lcd.print(t);
}

// Function to count how many buttons are currently being pressed
int countSwitches(bool a, bool b, bool c, bool d){
  return ( (a?1:0)+(b?1:0)+(c?1:0)+(d?1:0) );
}

// Function to create comparable strings of button states
String stringPresses(bool a, bool b, bool c, bool d){
  String t = String(a)+String(b)+String(c)+String(d);
  return(t);
}

// Function to update the display
void setDisplay(){
  // Reset the display
  lcd.clear();

  // Display the amp channel
  lcd.print("AMP CHANNEL:");
  String channel = "";
  if(numericDisplay){
    // We're doing a numerical display
    channel = channel + currState;
    lcd.setCursor(15,0);
    lcd.print(channel);
  }else{
    // Were doing an "A1" style display
    if(currState < 3){
      channel = channel + "A" + currState;
    }else{
      channel = channel + "B" + (currState-2);
    }
    lcd.setCursor(14,0);
    lcd.print(channel);
  }
  // Display momentary switch info
  lcd.setCursor(0,1);
  lcd.print("MOMENTARY:");
  if(currMom == 0){
    lcd.setCursor(13,1);
    lcd.print("OFF");
  }else{
    if(currMom == currState){
      lcd.setCursor(14,1);
      lcd.print("ON");
    }else{
      lcd.setCursor(15,1);
      lcd.print(currMom);
    }
  }
  
}

// Function to set the amp channel
void setChannel(int val){
  switch(val){
    case 1:
      digitalWrite(CH1, L);
      digitalWrite(CH2, L);
      break;
    case 2:
      digitalWrite(CH1, H);
      digitalWrite(CH2, L);
      break;
    case 3:
      digitalWrite(CH1, L);
      digitalWrite(CH2, H);
      break;
    case 4:
      digitalWrite(CH1, H);
      digitalWrite(CH2, H);
      break;
    default:
      return;
  }
  // Update state variables and commit to memory
  prevState = currState;
  currState = val;
  EEPROM.write(S_ADDR, currState);
  // Update the display
  setDisplay();
}

void setup() {
  // Set up pin modes
  pinMode(S1, INPUT);
  pinMode(S2, INPUT);
  pinMode(S3, INPUT);
  pinMode(S4, INPUT);
  pinMode(CH1, OUTPUT);
  pinMode(CH2, OUTPUT);

  // Set up the LCD
  lcd.begin();
  lcd.backlight();
  initDisplay();
  delay(3000);
  lcd.clear();

  // Load stored settings from memory
  currState = EEPROM.read(S_ADDR);
  if(currState > 4) currState = 1;
  prevState = currState;
  currMom = 0;
  if(EEPROM.read(S1_MOM_ADDR) == 1){
    s1Mom = true;
    currMom = 1;
  }else{
    s1Mom = false;
  }
  if(EEPROM.read(S2_MOM_ADDR) == 1){
    s2Mom = true;
    currMom = 2;
  }else{
    s2Mom = false;
  }
  if(EEPROM.read(S3_MOM_ADDR) == 1){
    s3Mom = true;
    currMom = 3;
  }else{
    s3Mom = false;
  }
  if(EEPROM.read(S4_MOM_ADDR) == 1){
    s4Mom = true;
    currMom = 4;
  }else{
    s4Mom = false;
  }
  
  // Update memory settings just in case they weren't set before
  EEPROM.write(S_ADDR, currState);
  EEPROM.write(S1_MOM_ADDR, s1Mom);
  EEPROM.write(S2_MOM_ADDR, s2Mom);
  EEPROM.write(S3_MOM_ADDR, s3Mom);
  EEPROM.write(S4_MOM_ADDR, s4Mom);
  
  // Set the starting amp channel (which also updates the display)
  stateBeforeMom = currState;
  setChannel(currState);
  
  // Start the debounce timer
  timeSinceLastPress = millis();
}

void loop() {
  // Get the latest state of all buttons
  bool s1Pressed = digitalRead(S1) == H;
  bool s2Pressed = digitalRead(S2) == H;
  bool s3Pressed = digitalRead(S3) == H;
  bool s4Pressed = digitalRead(S4) == H;
  pressed = stringPresses(s1Pressed, s2Pressed, s3Pressed, s4Pressed);
    
  // If the switch has changed, reset the timer
  if(pressed != lastPressed){
    timeSinceLastPress = millis();
    if(pressed == enterProgramModeCode || pressed == exitProgramModeCode){
      // The "enter momentary programming mode" switches are being pressed
      askedForMomentary = millis();
    }else{
      askedForMomentary = 0;
    }
  }

  // Check to see if we should enter momentary programming mode or not
  if(askedForMomentary == 0 && !inProgrammingMode){ // Not waiting for the momentary time to elapse / Not already in program mode -- Business as usual
    // We want to have had the buttons in the same state for {debounceTime} milliseconds before counting it as an actual press
    if((millis() - timeSinceLastPress) > debounceTime){
      // Yay! We should count this as an actual button press!
      if(pressed == "0000" && momentaryOn){
        // User has released all buttons and is in momentary mode - time to reset
        momentaryOn = false;
        setChannel(stateBeforeMom);
      }else{
        if(countSwitches(s1Pressed, s2Pressed, s3Pressed, s4Pressed)==1){ // Only one button is being pressed
          // Work out which button has been pressed
          int whichButton;
          if(s1Pressed) whichButton = 1;
          if(s2Pressed) whichButton = 2;
          if(s3Pressed) whichButton = 3;
          if(s4Pressed) whichButton = 4;
          // If button is different to current state, switch channels
          if(whichButton != currState){
            // If a momentary button is set, and is being pressed we should switch on momentary mode
            if(whichButton == currMom){
              stateBeforeMom = currState;
              momentaryOn = true;
            }
            setChannel(whichButton);
          }
        }
      }
    }
  }

  // In program mode but not pressing the exit buttons
  if(askedForMomentary == 0 && inProgrammingMode){
    // We're trying to set a button to act as a momentary switch
    if(countSwitches(s1Pressed, s2Pressed, s3Pressed, s4Pressed)==1){ // Only one button is being pressed
      // Work out which button has been pressed
      int whichButton;
      if(s1Pressed) whichButton = 1;
      if(s2Pressed) whichButton = 2;
      if(s3Pressed) whichButton = 3;
      if(s4Pressed) whichButton = 4;
      // If the momentary channel has changed...
      if(currMom != whichButton){
        // Update the current momentary
        currMom = whichButton;
        // Store the current momentary
        s1Mom = false;
        s2Mom = false;
        s3Mom = false;
        s4Mom = false;
        if(currMom == 1) s1Mom = true;
        if(currMom == 2) s2Mom = true;
        if(currMom == 3) s3Mom = true;
        if(currMom == 4) s4Mom = true;
        EEPROM.write(S1_MOM_ADDR, s1Mom);
        EEPROM.write(S2_MOM_ADDR, s2Mom);
        EEPROM.write(S3_MOM_ADDR, s3Mom);
        EEPROM.write(S4_MOM_ADDR, s4Mom);
        // Update the programming mode display
        lcd.setCursor(15,1);
        lcd.print(currMom);
        delay(1500);
        // Exit programming mode
        inProgrammingMode = false;
        // Just in case we've set the momentary to the current channel
        if(currMom == currState){
          if(currMom == 1) setChannel(2);
          if(currMom != 1) setChannel(1);
        }else{
          // Reset the display
          setDisplay();
        }
      }
    }
  }
  
  if(askedForMomentary != 0){ // We're being timed for entering or exiting momentary setting mode
    if((millis() - askedForMomentary) > timeToWaitForMomentary && !inProgrammingMode && pressed==enterProgramModeCode){ // We want to enter programming mode and we're not already in it
      // We've held the "enter programming mode" or "exit programming mode" buttons down for long enough
      inProgrammingMode = true;
      askedForMomentary = 0;
      lcd.clear();
      lcd.print("PROGRAMMING MODE");
      lcd.setCursor(0,1);
      lcd.print("SET MOMENTARY: ");
      lcd.blink();
      delay(1500);
    }
    if((millis() - askedForMomentary) > timeToWaitForMomentary && inProgrammingMode && pressed==exitProgramModeCode){ // We want to exit programming mode and we're already in it
      // RESET MOMENTARY STUFF
      s1Mom = false;
      s2Mom = false;
      s3Mom = false;
      s4Mom = false;
      currMom = 0;
      EEPROM.write(S1_MOM_ADDR, s1Mom);
      EEPROM.write(S2_MOM_ADDR, s2Mom);
      EEPROM.write(S3_MOM_ADDR, s3Mom);
      EEPROM.write(S4_MOM_ADDR, s4Mom);
      // Update the programming mode display
      lcd.setCursor(0,1);
      lcd.print(" MOMENTARY OFF! ");
      delay(1500);
      // Exit programming mode
      inProgrammingMode = false;
      // Reset the display
      setDisplay();
    }
  }

  lastPressed = pressed;
}
Posted on Leave a comment

Katduino: Part Two – learning the ropes

It feels like ages since I built this pedal now and I’ve finally gotten around to writing up how I got it done.

My starting ambitions were fairly simple – a small Arduino, a couple of momentary switches and a relay to control the signal sent to the amp through the TRS cable. I’d planned to add in a 7 segment LCD to provide an update of the currently selected channel.

I structured my experiments to tackle a small bit of functionality at a time, gradually planning to combine each experiment into the finished set of code:

  1. Have the Arduino connect to the relay and cycle through each switch configuration
  2. Hook the relay up to a mono socket and cycle the amp through each of its 4 channels
  3. Hook the LCD to the Arduino and display a “Hello World” message
  4. Use the relay script to send specific values to the LCD to reflect the selected channel
  5. Combine the relay and LCD programmes to actually switch the amp channels and display the correct info on the LCD
  6. Have the Arduino detect button presses using the onboard LED
  7. Have the Arduino convert button presses into relay switch configurations
  8. Combine everything so the button presses trigger relay switches and the LCD updates

Each of these tests worked really well and I was impressed with how intuitive the coding process was, especially using the examples bundled into the IDE. I was contemplating getting this boxed up and being done with it but I got ambitious and decided to take things even further.

So, the new plan is to extend what I’ve currently got to give me:

  • 4 momentary switches – one for each channel of the amp
  • A 16×2 LCD display module to show current settings
  • A snazzy little loading screen with customised BOSS artwork
  • A memory of what the last setting was before the pedal was switched off
  • The ability to set a channel as a momentary option
Posted on Leave a comment

Katduino: Part One – the goals

I’ve had my Boss Katana 50 for a little while now and have been playing about with the different settings available in Tone Studio a bit more in recent months. I can tweak my sounds as much as I want, playing with EQ settings and effects chains to get things just right before committing them to one of the four different channels the amp has.

While this is great, the buttons to switch channels are on top of the amp – a short distance from my guitar playing stool – and it can be a bit fiddly to switch between them. This is especially apparent when you want to play a song that needs a combination of sounds – think Battery by Metallica!

What I really need is a footswitch that will let me seamlessly switch between each of the four onboard channels. Being a bit of a DIY pedal builder I’m pretty sure this is something I can do without too much trouble. A quick bit of research landed me at https://www.vguitarforums.com/smf/index.php?topic=22174.0 where al3xandr3 on Youtube has done a lot of the hard work already – BONUS!

Based on al3xandr3’s examples I’m planning to build a pedal with:

I’ll be building all of this with an Arduino Pro Mini (to help save some space), a two channel relay and a 7-segment four digit display module – all from https://www.bitsbox.co.uk/. Once all of these have arrived I’ll get them hooked up and start to test some code samples.