Saxophone midi controller, need help!

Sorry i was having a similar project open in a different instance of the IDE, and i may have gotten them mixed up. I'll correct the post #18 with the correct file. It is now.

Sketch uses 5556 bytes (19%) of program storage space. Maximum is 28672 bytes.
Global variables use 353 bytes (13%) of dynamic memory, leaving 2207 bytes for local variables. Maximum is 2560 bytes.

thank you a lot, code is working !
the bad news (and it's all my fault) is that the helix doesn't give power over usb, so i need to put an external source of power, and i don't want it too big ... have you counsels for that (regulator lipo battery and so on)
once again thank you so much for your time !

Pfff Oh i'm happy about that.
About the power source, how much do you think you need ? a helix ? don't really know what that is.
If 5v and up to 300mA is enough and you don't mind a cable i would just go for a USB-phonecharger or another small wall plug power supply. If you insist on batteries i can suggest 3 option

  • A LiPo or 2 but the circuitry to charge and monitor can be complex and it would be the least preferred choice.
  • A 9v Block ! If you don't need a lot of current a 9v Block can be a good solution, but for environmental reasons i prefer rechargeable ones. It is not to hard to find a decent charger (though they cost a bit) and if you buy 2 batteries, you can swap them out to charge and use one at a time.
  • Built a battery-pack out of AA-rechargeable batteries. This will provide you with enough power almost for sure, and you can charge the cells individually which will extend their lifetime considerably. Again you may want to double up on the total amount of batteries.

To give the best advice we need to know what amount of current and voltage you need.

by the way I don't really know how much power but I think it's really low, only the micro to power and the 2 four buttons pads ... batteries are a better option for me because I already have 1 usb cable to the helix (which is an amazing multi effect pedalboard, but unfortunately i can't use my feet anymore...) I have look to the lipo, but like you suggest, I already own rechargeable 9v blocks so if it is simple to use without a regulator that could be the solution. Is it just to plug + on raw and - on gnd ? is it as simple as that ?
thanks again

Adding an external power source is not going to help.
USB Hosts have to provide power to the bus. If the USB port doesn't provide power, it's probably a USB Device, not a Host.
The Arduino is a Device as well, you cannot connect a Device to a Device, one of them should be a Host.

Unless your device has a USB Host port, you cannot use it with an Arduino Micro/Leonardo. You can either use the 5-pin DIN MIDI connection, or get the necessary hardware to make your Arduino a USB Host. Even with the hardware sorted out, software is probably not straightforward, especially if your Helix is a composite USB device (not just MIDI USB), basically you have to find a driver for the device or write your own. On a computer you can rely on the drivers that come with your OS or that are supplied by the manufacturer of the device, but obviously they don't provide drivers for microcontrollers.

Looking at the Line6 Helix, it only has a USB B port. How did you connect it to your Arduino? This is not something you should be able to do with standard USB cables.
The easiest solution is probably to use the 5-pin DIN jacks.

You're right about the device to device, I didn't think about that, i was so happy to see the right CC in my DAW ^^
I connect the arduino via a micro-usb to usb female and with the usual usb B cable, but no power ... of course ... while the helix wass class compliant I think I didn't need driver, but device to device ... effectively not ...
Look like the 5-pin midi jack is the solution, but I still need something to power the arduino, and do you think I can keep the code or a big part of it with 5-din ?
thanks :slight_smile:

If I learned anything from reading the USB Host related threads on the Teensy forum, it's that manufacturers claiming “class compliance” is not usually a guarantee that it'll work with the USB Host libraries on microcontrollers ...

Sure, just fix your controlChange function to send MIDI to Serial1. You can use Control Surface, other MIDI libraries, or even just Serial1.write():

uint8_t msg[3] { uint8_t(0xB0 | channel), control, value };
Serial1.write(msg, 3);

Be sure to add Serial1.begin(31250) to your setup.

thanks a lot i will try that !

The original code was using MiDi.h
but instead of

MIDI_CREATE_DEFAULT_INSTANCE();

You can link it to Serial1

MIDI_CREATE_INSTANCE(HardwareSerial, Serial1,   MIDI);

Of course the 5 pin Din (of which 2 or 3 pins are used), does require a tad more juice than just the micro & buttons, but not a lot more though, it is actually powering the led of an Opto-coupler on the input of the helix through 3x 220R resistors. (2 of which are on the side of the controller)
I would go for the 9v block connected to the RAW (or Vin) .
Not an awful lot more though

Oh i had a look at the Line6 Helix, man those things are expensive. Used to own a Line6 delay, but gave that to my brother. And now it's waiting for me on the other side of the earth, so i can replace an IC in the internal power-supply.

you guys are amazing, !
i will return tomorrow if all is fine ^^

my project is still on the breadboard, and with an external usb battery, I'll can't stand testing ...
and ... IT'S WORKING !!!!! tomorrow, i will test with the 9v block (more convenient for me for fitting the sax), but wow you 2 guys, Deva_Rishi and PieterP are awesome (more than that) !!!!

Hi guys !
my project is done and assembled ! after some beginner faults ... I put once the 9v battery in the wrong way :frowning: so I burned a micro ... I put a diode on ground and a switch on raw ... more comfortable and secure LOL !




I'm so happy ! Thanks again for all the help !

Just have a last question ...
I would like to have one of the buttons be able to toggle switch (value 0 when released / value 127 when pressed), do you think it's possible to add that in the script ? the original one has it but with all the changes for my 4 buttons pads it is not there anymore ...

I think so yes, please post the full version of your current script, it shouldn't be to hard to do.

#include <MIDI.h>
#include "Controller.h"


int AD_PIN1 = A0;
int AD_PIN2 = A3;

byte NUMBER_BUTTONS = 8;

//Button (Analog Pin, KCButton, CC Number, Channel, Debounce Time)
//** Command parameter 0=NOTE  1=CC  2=Toggle CC **

Button BU1(A0, 1, 0, 61, 127, 1, 5 );
Button BU2(A0, 2, 0, 1, 127, 1, 5 );
Button BU3(A0, 3, 0, 52, 127, 1, 5 );
Button BU4(A0, 4, 0, 69, 8, 1, 5 );
Button BU5(A3, 1, 0, 60, 127, 1, 5 );
Button BU6(A3, 2, 0, 61, 0, 1, 5 );
Button BU7(A3, 3, 0, 53, 127, 1, 5 );
Button BU8(A3, 4, 0, 69, 9, 1, 5 );

//Add buttons used to array below like this->  Button *BUTTONS[] {&BU1, &BU2, &BU3, &BU4, &BU5, &BU6, &BU7, &BU8};
Button *BUTTONS[] {&BU1, &BU2, &BU3, &BU4, &BU5, &BU6, &BU7, &BU8};

void controlChange(byte control, byte value, byte channel) {
  uint8_t msg[3] { uint8_t(0xB0 | channel), control, value };
Serial1.write(msg, 3);
}

void updateButtons() {
  
  // Cycle through Button array
  for (int i = 0; i < NUMBER_BUTTONS; i = i + 1) {
    byte message = BUTTONS[i]->getValue();
    
    if (message == 1) {    //  Button is pressed
      controlChange(BUTTONS[i]->Bcc, BUTTONS[i]->Bvalue, BUTTONS[i]->Bchannel);
      break;
    }

    if (message == 0) {    //  Button is not pressed
      //controlChange(BUTTONS[i]->Bcc, 0, BUTTONS[i]->Bchannel);
    }
  }
}
void setup() {
  Serial1.begin(31250);
  Serial.begin(115200);
}

void loop() {
    int adValue = analogRead(AD_PIN1);
  Serial.println(buttonFromValue(adValue));
  int adValue2 = analogRead(AD_PIN2);
  Serial.println(buttonFromValue2(adValue2));
    if (NUMBER_BUTTONS != 0) updateButtons();
}
// Returns the button number based on the analog value
byte buttonFromValue(int adValue) {

  if (adValue > 300 && adValue < 500) {
    return 1;
  }

  if (adValue > 500 && adValue < 700) {
    return 2;
  }

  if (adValue > 700 && adValue < 900) {
    return 3;
  }

  if (adValue > 900) {
    return 4;
  }

  return 0;


}
byte buttonFromValue2(int adValue2) {

  if (adValue2 > 300 && adValue2 < 500) {
    return 1;
  }

  if (adValue2 > 500 && adValue2 < 700) {
    return 2;
  }

  if (adValue2 > 700 && adValue2 < 900) {
    return 3;
  }

  if (adValue2 > 900) {
    return 4;
  }

  return 0;

}

controller.cpp

#include "Controller.h"


//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
Button::Button(byte pin, byte kcbutton, byte command, byte cc, byte value, byte channel, byte debounce)
{
  _pin = pin;
  _kcbutton = kcbutton;    // add this line
  pinMode(_pin, INPUT);  // add this line
  _cc = cc;
  _value = value;
  _command = command;
  _debounce = debounce;
  _time = 0;
  _busy = false;
  _status = 0b00000010;
  _last = 1;
  Bcc = cc;
  Bcommand = command;
  Bvalue = value;
  Bchannel = channel;
  Btoggle = 0;
}

byte Button::buttonFromValue(int adValue) {   // add this whole function
  if (adValue > 300 && adValue < 500) {
    return 1;
  }
  if (adValue > 500 && adValue < 700) {
    return 2;
  }
  if (adValue > 700 && adValue < 900) {
    return 3;
  }
  if (adValue > 900) {
    return 4;
  }
  return 0;
}


byte Button::getValue()
{
  // If BUSY bit not set - read button
  if (bitRead(_status, 0) == false) { // If busy false
    
    if ((buttonFromValue(analogRead(_pin)) == _kcbutton) == _last) return 2; // If same as last state - exit
  }       // i think it is like this

  // If NEW Bit set - Key just pressed, record time
  if (bitRead(_status, 1) == true) { // If new is true
    bitSet(_status, 0); // Set busy TRUE
    bitClear(_status, 1); // Set New FALSE
    _time = millis();
    return 255;
  }

  // Check if debounce time has passed - If no, exit
  if (millis() - _time < _debounce) return 255;

  // Debounce time has passed. Read pin to see if still set the same
  // If it has changed back - assume false alarm
  if ((buttonFromValue(analogRead(_pin)) == _kcbutton) == _last) {
    bitClear(_status, 0); // Set busy false
    bitSet(_status, 1); // Set new true
    return 255;
  }

  // If this point is reached, event is valid. return event type
  else {
    bitClear(_status, 0); // Set busy false
    bitSet(_status, 1); // Set new true
    _last = ((~_last) & 0b00000001); // invert _last
    return _last;  // invert the logic here. 
  }
}

void Button::newValue(byte command, byte value, byte channel) {  
  Bvalue = value;
  Bcommand = command;
  Bchannel = channel;
}

controller.h

#ifndef Controller_h
#define Controller_h

#include <Arduino.h>


//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
class Button
{
  public:
    Button(byte pin, byte kcbutton, byte command, byte cc, byte value, byte channel, byte debounce);  // change this line
    //Button(Mux mux, byte muxpin, byte command, byte value, byte channel, byte debounce);
    byte getValue();
    //void muxUpdate();
    void newValue(byte command, byte value, byte channel);
    byte Bcc;
    byte Bcommand;
    byte Bvalue;
    byte Bchannel;
    byte Btoggle;

  private:
    byte buttonFromValue(int adValue);  // add this line
    byte _previous;
    byte _current;
    unsigned long _time;
    int _debounce;
    byte _pin;
    byte _kcbutton;  // and this one
    //byte _muxpin;
    //byte _numMuxPins;
    byte _cc;
    byte _value;
    byte _command;
    bool _busy;
    byte _status;
    byte _last;
    byte _enablepin;
};

#endif

It's for the BU2 with cc 1

Thank's a lot !

Ok i think that is actually still the same code i have, except for these :

int AD_PIN1 = A0;
int AD_PIN2 = A3;

that you never use after their declaration, but that doesn't matter much.

    if (message == 0) {    //  Button is not pressed
      //controlChange(BUTTONS[i]->Bcc, 0, BUTTONS[i]->Bchannel);
    }

It is still in your code, just commented out, but if you un-comment it, it will apply to all button, so you need to add an extra condition.

    if (message == 0)  {    //  Button is not pressed
      if (i == togglebutton) controlChange(BUTTONS[i]->Bcc, 0, BUTTONS[i]->Bchannel);
    }

and of course you have to declare or change 'togglebutton' into the button you want to have that ability.
I thought this was going to be a lot of work, but i was wrong.

Actually thinking about what i've been experiencing with my own midi-controller program, i am concerned that like this the buffer is going to get flooded. My controller is setup in a different way where the return value is either 0 (no change), 1 (pressed) & 2 (released), and this program does have that, but we can check to see if the message has actually changed, that means that you updateButtons() function will look like this

void updateButtons() {
  // Cycle through Button array
  static byte oldmsg[NUMBER_BUTTONS];
  int togglebutton = 4 // ?? the button you want
  for (int i = 0; i < NUMBER_BUTTONS; i = i + 1) {
    byte message = BUTTONS[i]->getValue();
    if (message != oldmsg[i]) {
      oldmsg[i] = message;
      if (message == 1) {    //  Button is pressed
        controlChange(BUTTONS[i]->Bcc, BUTTONS[i]->Bvalue, BUTTONS[i]->Bchannel);
        break;
      }

      if (message == 0) {    //  Button is not pressed
        if (i == togglebutton) controlChange(BUTTONS[i]->Bcc, 0, BUTTONS[i]->Bchannel);
      }
    }
  }
}

I was experiencing the flooding of the buffer when i was using one of the faders, which resulted in more messages being put in the buffer than could be sent. and something similar could have happened.

Hi !
I have an error on "oldmsg" : storage size of 'oldmsg' isn't constant
I tried to define it on controller.h, but not working ... Can we define it as a constant like BU2?, because it is the only button i need to toggle ...
What do you think ?
And of course thank you so much :slight_smile:

Oh that... ehm..
How about we make it constant

const byte NUMBER_BUTTONS = 8;

I hadn't paid attention, my naming convention dictates that i use 'Caps-only' for constants, normally actually just for defining literals.
so as the above would work just fine, i would actually do :

#define NUMBER_BUTTONS  8;

which also works.

you can just define 'togglebutton = 1' since 1 is the location of BU2 in the BUTTONS[] array, and it should do the trick. i don't really want to go through the trouble of adding an extra field to the object, enough messing in other peoples code really. If later you decide you want to have more 'toggle buttons you can simply add them to the list

 if (message == 0) {    //  Button is not pressed
        if ((i == 1) || (i == theotherbutton)) controlChange(BUTTONS[i]->Bcc, 0, BUTTONS[i]->Bchannel);
      }                                // etc.

the addition of 'oldmsg' is relevant since it prevents the program from creating repeated midi-messages, faster than they can be sent., which may result in strange artifacts, part messages etc. Midi protocol is set up in such a way that not to much can go wrong, but flooding the buffer should be prevented if possible (which it is)

Perfect !
It's working fine
Problem totally resolved ...
You're amazing !

1 Like