DIY Axe-Fx Foot Controller for $50 (Open Project)

It has one bug. I don't know why, but it only reads the Preset Name when pressing the switch two times or when selecting a scene.

Problem solved! Ver 6.9 posted.

I have isolated the part of the program that reads the SysEx from the Axe-Fx and debug by sending data to the Serial Monitor

Code:
#include <LiquidCrystal.h>
#include <MIDI.h>

MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);

LiquidCrystal lcd(8, 13, 9, 4, 5, 6, 7);       

// constants
static const unsigned ttt = 50;      // tempo led illumination time (ms)
static const unsigned ledPin = 50;   // tempoLED pin

unsigned long tt;      // time tempo sysex pulse detected
char pname[32];
byte pdata[12];
int initial = 0;
int preset = 0;
int scene = 0;
int ppreset = 0;
int blk = 0;
int cc = 0;
int byp = 0;
bool updLCD = false;
unsigned long ct;      // call time of FX request in millis

// sysex requests
byte RQSTNAME[6] = { 0x00, 0x01, 0x74, 0x03, 0x0F, 0x09 };
byte RQSTNUM[6] = { 0x00, 0x01, 0x74, 0x03, 0x14, 0x12 };
byte RQSTCC[6] = { 0x00, 0x01, 0x74, 0x03, 0x0E, 0x08 };
byte RQSTSCENE[6] = { 0x00, 0x01, 0x74, 0x03, 0x29, 0x2F };



void setup() {
    Serial.begin(115200);
    lcd.begin(16, 2);
    lcd.clear();
    pinMode(ledPin, OUTPUT);
    MIDI.begin();
    MIDI.turnThruOff();
    MIDI.setHandleSystemExclusive(HandleSysEx);
    MIDI.sendSysEx(6,RQSTNUM);
    delay(50);
    MIDI.sendSysEx(6,RQSTNAME);
    delay(50);
    updLCD = true;

}

void loop() {

    MIDI.read();
 
    // update tempo led
    if ((millis() - tt) > ttt) {
        digitalWrite(ledPin, LOW);
        tt = 0; // reset tempo pulse detected time
    }
 
    // check if FX block status needs updating
    if (ct > 0 && (ct + 100) < millis()) {
        MIDI.sendSysEx(6,RQSTCC);
        ct = 0;
        updLCD = true;
                Serial.println("FX block status needs updating");
    }

    // update LCD display
    if (updLCD == true) {   Serial.println("Updating LCD");
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print(pname);
        lcd.setCursor(0,1);
        lcd.print("Prog:");
        lcd.print(preset);
        lcd.setCursor(8,1);
        lcd.print("Scn:");
        lcd.print(scene);
        updLCD = false;
    }
}

// parse preset number from sysex data
int parseNum(const byte * sysex, int l) {Serial.println("// parse preset number from sysex data");
    int out = 0;
    if (sysex[6] == 0x00) {
        out = int(sysex[7]);
    } else {
        out = int(sysex[7]) + 128 * int(sysex[6]);
    }
    return out;

}

// parse preset name from sysex data
void parseName(const byte * sysex, int l) {   Serial.println("// parse preset NAME from sysex data");
    int out = 0;
    // reset char array to spaces
    for(byte i = 0x00; i < 0x14; i++) {
        pname[i] = 0x20;
    }
    // get sysex data into char array pname
    for(byte i = 0x00; i < 0x14; i++) {
        //char p = sysex[i + 6];
        pname[i] = sysex[i + 6];
    }
}

// callback -  handle sysex
void HandleSysEx(byte *SysExArray, unsigned int size) {
    int sizear = 0;
    if(SysExArray[0]==0xF0) {
        Serial.print("MIDI IN:");
        Serial.println(SysExArray[5],HEX);
        switch (SysExArray[5]) {
            case 0x0F: { ;// preset name
            Serial.println("case 0x0F // preset name");
                const byte *sys = MIDI.getSysExArray();
                sizear = MIDI.getSysExArrayLength();
                parseName(sys,sizear);
                updLCD = true;
                break;
            }
            case 0x29: { // scene
              Serial.println("case 0x29 // Scene");
                scene = SysExArray[6] + 1;
                updLCD = true;
                break;
            }
            case 0x21: { // MIDI event ACK??
              Serial.println("case 0x21 // MIDI event ACK?e");
                ct = millis();
                MIDI.sendSysEx(6,RQSTNUM);
                MIDI.sendSysEx(6,RQSTNAME);
                updLCD = true;
                break;
            }
            case 0x14: {  // preset num
              Serial.println("case 0x14 // preset num");
                const byte *sys = MIDI.getSysExArray();
                sizear = MIDI.getData1();
                preset = parseNum(sys,sizear);
             
    MIDI.sendSysEx(6,RQSTNAME);
    delay(50);
                    updLCD = true;
                break;
            }
            case 0x0E: { // FX data
              Serial.println("case 0x0E // FX data");
                const byte *sys = MIDI.getSysExArray();
                sizear = MIDI.getData1();
                break;
            }
            case 0x10: { // tempo
               Serial.println("case 0x10E // tempo");
                tt = millis();
                digitalWrite(ledPin, HIGH);
                break;
            }
        }
    }
}

I've found that when changing a preset I was receiving only 0x14 (presetNumber) but not 0x0F (presetName), so I've inserted this line inside case 0x14: { // preset num:

MIDI.sendSysEx(6,RQSTNAME);
delay(50);

I don't know if that is the most orthodox way to do it, but it works :)
Now It reads the PresetName when selecting the preset from both the controller or the Axe-FX front panel
 
Last edited:
i will start to do one, awesome project, there has been in my mind for years, ill try to do bidireccional comunication for the tuner.

is It possible make dual function (Click and Hold) for this type of buttons?
 
Last edited:
Im thinking in a "reveal mode" like MFC that it can change the funcion of the others buttons, i like this idea, What do you think about a bidirectional communication for tuner info on display?, i think it will be really cool.
 
Im thinking in a "reveal mode" like MFC that it can change the funcion of the others buttons, i like this idea, What do you think about a bidirectional communication for tuner info on display?, i think it will be really cool.

Several DIY'ers have already implemented the tuner at their arduino controllers, but not all are sharing the final version of their code
 
Coming soon...

The Calendar Axe-FX Analog Controller!


To tweak the Axe-FX parameters "feel-like-real-amp-button" without browsing menus


Axe_AnalogCont_01.jpg


So far I only have one set of rotary encoder and display working. I am waiting to receive a multiplexer to connect all the displays into one I2C bus.
 
Last edited:
Crazy cool DIY projects! I should reanimate my dusty Arduino Board! Thanks for sharing! Thread is on the watch list!

And Dude, i wish for a little video snippet .... demonstrate your units! So cool ...
 
Im thinking in a "reveal mode" like MFC that it can change the funcion of the others buttons, i like this idea, What do you think about a bidirectional communication for tuner info on display?, i think it will be really cool.
I have built a similar controller myself and borrowed the tuner code from this project (cant post links yet, just search 'FCBInfinity' on github).
It works really well.
 
Last edited:
I saw this thread yesterday and I ordered an arduino mega right away lol I had looked into building a midi controller a couple of years back so i kinda got an idea on how the arduino works, I like the idea of working with sysex, I have an AX8 and for what i see there's not sysex thru the midi jacks so it would have to be either over USB or no sysex. Thank you for starting this thread i will be sharing my code for the AX8 once in a few days once the board arrives!
 
Very cool project! I have to try building one.....

Two Question (for anyone):
1) Can the Ethernet MFC connection be used with (any) controller other than the MFC?
Is the data being sent through the Ethernet cable, just midi data? Or is it proprietary data?
It would be cool if you take the signal sent from the Ethernet output of the Axe FX, and rewire (like a 7 pin midi) to the Arduino.
I know a 7pin midi accomplishes the same thing....but using the Ethernet output on the Axe eliminates the need for a wall wart power supply to be bouncing around in the back of your rack.

2) Has anyone been able to display the tuner on a DIY controller like this?
 
This looks fantastic, I want to make something with rotaries for amp control and external modifiers(CC) with LCD or LED ring feedback from the axe which changes to show current values on preset load. I'll be following closely, keep up the good work! It'd be awesome to see some videos if you have the chance to make any :)
 
This looks fantastic, I want to make something with rotaries for amp control and external modifiers(CC) with LCD or LED ring feedback from the axe which changes to show current values on preset load. I'll be following closely, keep up the good work! It'd be awesome to see some videos if you have the chance to make any :)
Rotaries & displays?
Have you already check this?: https://forum.fractalaudio.com/threads/diy-midi-amp-knobs.127530/
Not completed, but almost there
 
Rotaries & displays?
Have you already check this?: https://forum.fractalaudio.com/threads/diy-midi-amp-knobs.127530/
Not completed, but almost there
Thanks, that looks like what I want. I'd like to find a way to only use one midi cable with two-way communication like the RJM mastermind does, otherwise I have to run more long cables on stage. Midi out from my rotary controller would go into midi in/thru on the mastermind and be powered from the auxiliary power supply. Then its just one 7-pin cable from the mastermind to the axe fx.
 
Back
Top Bottom