Help! Need to make a MIDI controller!

Hi there,

First up I'm very very new to Arduino and programming and so I'm positive I'll be making lots of mistakes along the way but I really want to learn so any help that can be thrown my way would be greatly appreciated.

What I would like to do is to make a MIDI controller using an Arduino or ESP8266 type board which to start with will have 1 potentiometer attached to it (I believe that all the boards I currently own [more on those in a minute] only have the one analog pin so I'm stuck with that at the moment but probably best to start small and work my way up with my limited knowledge anyway! :-D).

I currently have :

1 Wemos D1 Mini
1 ESP-01
1 Arduino Nano Compatible
1 Female MIDI connector
Resistors (various but the magic 220 ohms I have definitely got!)
Arduino IDE downloaded and working on Mac

I use Logic Pro X and would like to eventually make a controller which will have 3 potentiometers (buttons and more pots in the future when I'm better at what I'm doing) that will control dynamics, expression and master volume via MIDI.

I'd like to be able to have standard USB MIDI capability whereby I can just plug in a board to my computer via USB and it works with my DAW instead of having to use anything like Hairless MIDI and what-not. I'm working on an older 2009 Mac and so the less programmes running at once the better when it comes to my music writing!

Initially the faders will only need to transmit on MIDI channel 1 so simple in that regard.

My first questions are these :

Which of the boards I've listed above will allow me to start out by making a board with one fader which can control either dynamics, expression or volume in my DAW using USB MIDI?
If however none of them support USB MIDI then at this point I'm fine with using the female MIDI connector and resistors to create the connection as long as I won't need to use any other applications and the data will just go straight into my MIDI interface and then get recognised by the DAW.

Next up would be some very simple code that will allow me to read the value of the fader and convert it into a MIDI cc message to output via USB MIDI or the MIDI connector (dependent upon what I can do with the boards I currently have).

I've seen that there are many many libraries out there for MIDI and so if any of these would be better for me to use (even at my very early learning stage) then I'm all up for that too.

Lastly (at least I think so anyway until I think of something else I need to ask!) I would like to ask which would be the best board for me to get for future use (and for my larger controller I want to build) out of the myriad of boards that are available out there?

I've been reading various sites and all mention different boards all over the place and someone told me that perhaps an ESP-32 would be a better fit than an Arduino board however I'm very very new to all of this so not sure if that's correct or not?

I'd like to get one that has enough inputs for what I would like to end up doing, which is, creating the 3 faders initially but maybe still have some inputs left over incase I decide to add some more at some point in the future.

So basically in a nutshell (sorry this is such a long post with a lot in it) :

I'm wondering if any of the boards I currently own will allow me to make a simple MIDI controller with 1 fader to send CC messages to my DAW either via USB MIDI or failing that a MIDI cable. Then I'm looking for some simple code (or perhaps to use a library) to create the code needed to read the fader and convert to cc messages. Then also a very simple diagram of how to hook it all up electrically. After that just which board would be best for me to buy to be able to get up to 3 faders and maybe more but without breaking the bank. I definitely need 3 faders and that's ultimately what I'd like to build at the moment but in the future if I get any good at this then I may look at doing more.

Many thanks in advance for any help anyone can throw my way with this. I know this is a very very long post and I really hope someone can read this through to the end without falling asleep and maybe give me some help on all of this.

Many thanks in advance for any help.

Best wishes,

Mark

markbowendesign:
Which of the boards I've listed above will allow me to start out by making a board with one fader which can control either dynamics, expression or volume in my DAW using USB MIDI?

None of those boards support MIDI over USB. All of them can do regular DIN MIDI, using the right hardware. Do note that ESP8266 boards are 3.3V, so you need different resistors. You can find the schematic for both 5V and 3.3V on midi.org.
The ESP8266 boards have WiFi, so they can do AppleMIDI/rtpMIDI.

The problem with the ESP8266 is that its ADC is not really suitable for measuring ratiometric inputs such as potentiometers, because they have a fixed reference of 1.1V that's independent of the supply voltage.

markbowendesign:
Next up would be some very simple code that will allow me to read the value of the fader and convert it into a MIDI cc message to output via USB MIDI or the MIDI connector (dependent upon what I can do with the boards I currently have).

I've seen that there are many many libraries out there for MIDI and so if any of these would be better for me to use (even at my very early learning stage) then I'm all up for that too.

I maintain the Control Surface library, which aims to be easy to use, and supports all kinds of MIDI interfaces. The README contains a MIDI controller with one potentiometer in just 5 lines of code: GitHub - tttapa/Control-Surface: Arduino library for creating MIDI controllers and other MIDI devices.
There's also a more detailed tutorial.

You can of course also implement the same features yourself using other MIDI libraries, but I think Control Surface is a good place to start.

markbowendesign:
Lastly (at least I think so anyway until I think of something else I need to ask!) I would like to ask which would be the best board for me to get for future use (and for my larger controller I want to build) out of the myriad of boards that are available out there?

I've been reading various sites and all mention different boards all over the place and someone told me that perhaps an ESP-32 would be a better fit than an Arduino board however I'm very very new to all of this so not sure if that's correct or not?

I'd like to get one that has enough inputs for what I would like to end up doing, which is, creating the 3 faders initially but maybe still have some inputs left over incase I decide to add some more at some point in the future.

The ESP32 also doesn't support MIDI over USB. It has WiFi and Bluetooth, so it does support rtpMIDI and MIDI over BLE.

I try to maintain a list of Arduino-compatible boards and their MIDI capabilities here
I usually recommend a Teensy for building MIDI controllers.

You can always add multiplexers or other external hardware to increase the number of inputs, you're not limited to the number of pins the board has.

markbowendesign:
Then also a very simple diagram of how to hook it all up electrically.

Potentiometers are really easy to hook up: left terminal to ground, middle terminal (wiper) to an analog input of the Arduino, right terminal to Vcc (5V or 3.3V). On an ESP8266, it's a bit harder, because you'll destroy it if you apply more than around 1.8V on the analog input. Some boards have a voltage divider to transform the 0-1.1V range to a 0-3.3V range, IIRC the Wemos D1 mini has one, an ESP-01 doesn't break out the analog input pin, so you cannot use it to read a potentiometer directly.

You'll find plenty of tutorials for connecting a MIDI DIN output, you can find the official schematic here:

(MIDI DIN Electrical Specification is the old one, and Updated MIDI DIN Specification includes 3.3V as well.)

I hope this answers some of your questions,
Pieter

Hi Peiter,

Thank you so so much for the reply!

I had come across your library online (absolutely fantastic work you've done there) and I really would like to use that as my knowledge of coding just isn't really up to scratch at the moment and would really like to get something working as soon as I can just to make myself feel a little better during our lockdown.

To that end I decided to try a sketch with my Wemos D1 Mini.

This is what I have :

// Include the Control Surface library
#include <Control_Surface.h>

int baud = 9600;
HardwareSerialMIDI_Interface midi(Serial, MIDI_BAUD);

 
// Create a new instance of the class `CCPotentiometer`, called `potentiometer`,
// on pin A0, that sends MIDI messages with controller 7 (channel volume)
// on channel 1.
CCPotentiometer potentiometer = {
  A0, {MIDI_CC::Channel_Volume, CHANNEL_1}
};
 
// The filtered value read when potentiometer is at the 0% position
constexpr analog_t minimumValue = 255;
// The filtered value read when potentiometer is at the 100% position
constexpr analog_t maximumValue = 16383 - 255;
 
// A mapping function to eliminate the dead zones of the potentiometer:
// Some potentiometers don't output a perfect zero signal when you move them to
// the zero position, they will still output a value of 1 or 2, and the same
// goes for the maximum position.
analog_t mappingFunction(analog_t raw) {
    // make sure that the analog value is between the minimum and maximum
    raw = constrain(raw, minimumValue, maximumValue);
    // map the value from [minimumValue, maximumValue] to [0, 16383]
    return map(raw, minimumValue, maximumValue, 0, 16383);
    // Note: 16383 = 2¹⁴ - 1 (the maximum value that can be represented by
    // a 14-bit unsigned number
}
 
void setup() {
    // Add the mapping function to the potentiometer
    potentiometer.map(mappingFunction);
    // Initialize everything
    Control_Surface.begin();
    Serial.begin(115200);
}
 
void loop() {
    // Update the Control Surface (check whether the potentiometer's
    // input has changed since last time, if so, send the new value over MIDI).
    Control_Surface.loop();
    // Use this to find minimumValue and maximumValue: it prints the raw value
    // of the potentiometer, without the mapping function
    Serial.println(potentiometer.getRawValue());
}

I have a 10K potentiometer attached with the left wiper to GND on the board, the right one to +3.3V (wasn't too sure what the documentation meant by VCC as the board doesn't show one on the pins and then the middle wiper to A0.

When the code is uploaded and written to the board and I open the serial monitor I do get values being spat out ranging from 112 to 16400 however I also get a lot of weirdness too as shown below :

12:09:55.543 -> 16400
12:09:55.543 -> 16400
12:09:55.543 -> 16400
12:09:55.543 -> 16400
12:09:55.543 -> 16400
12:09:55.543 -> 16400
12:09:55.543 -> 16400
12:09:55.543 -> ⸮a16400
12:09:55.543 -> ⸮a16400
12:09:55.543 -> ⸮a716400
12:09:55.543 -> ⸮aJ16400
12:09:55.543 -> ⸮aX16400
12:09:55.543 -> ⸮ab16400
12:09:55.543 -> ⸮aj16400
12:09:55.543 -> ⸮ap16400
12:09:55.543 -> ⸮at16400

There are weird characters and garbage also being spat out so not too sure what all of that is?

I did see that you mentioned :

The problem with the ESP8266 is that its ADC is not really suitable for measuring ratiometric inputs such as potentiometers, because they have a fixed reference of 1.1V that's independent of the supply voltage.

so perhaps this is what is happening here?

Also if I start up Hairless Midiserial on my Mac then it does receive data however it's not what I believe it should be getting and seems to be getting all sorts of messages sent to it instead of just Volume changes? I've attached an image of what I'm getting to this forum post.

I'm also not sure at all that I'm creating the MIDI serial connection correctly in the code above?

int baud = 9600;
HardwareSerialMIDI_Interface midi(Serial, MIDI_BAUD);

I'm not sure if I need that int baud = 9600 in there before the midi interface function? Also not sure if I do then if the value is correct as I thought that MIDI was 31250 but if I upload that to the board and open up the serial monitor then I have to force quit the Arduino IDE app as it just goes really really slowly otherwise.

I'd love to get at least something up and running on the Wemos D1 Mini (or any of the other boards for that matter) just so that I can get some sort of proof to myself that I can some day manage to get done what I'd like to get done but at the moment I seem to be having all sorts of problems so just wondering if I could get a little more of a push in the right direction as it would be really appreciated.

Many thanks,

Mark

The problem is that you're using the Serial port for both MIDI and for printing text to the serial monitor. MIDI data is not human-readable, so that's why you're seeing the white rectangles/question marks in the serial monitor.

Vcc is the collector voltage, and is now synonymous for the supply voltage of a chip, even though most don't actually use bipolar transistors anymore. You'll find Vcc on many pinouts of the ESP8266, but the Wemos pinout just lists it as 3.3V.

The solution to your problem is to only use the Serial interface for a single purpose:

  1. If you want to debug using the Serial monitor, use a USBDebugMIDI_Interface instead of a HardwareSerialMIDI_Interface. The debug interface will print the MIDI messages in a human-readable format so you can view them in the serial monitor.

  2. If you want the microcontroller to work with Hairless, don't use the Serial port for anything else (remove all other uses of Serial like Serial.begin, Serial.println, etc.).
    You can use the HairlessMIDI_Interface instead of HardwareSerialMIDI_Interface, it'll use Hairless's standard baud rate of 115200.

The "int baud = 9600" in your code doesn't do anything, it just declares an integer variable with the name "baud" and initializes it to 9600. You don't seem to use it anywhere else in your code, so you can just delete it. If you want to specify the baud rate of the HardwareSerialMIDI_Interface, you pass it as the second (optional) constructor argument. In your code, this second argument is "MIDI_BAUD", which is a constant equal to the DIN MIDI baud rate of 31250.
If you wanted a baud rate of 115200, for example, you would use:

HardwareSerialMIDI_Interface midi(Serial, 115200);

or

HardwareSerialMIDI_Interface midi = {Serial, 115200};

(Both are equivalent, but the second is more consistent with the coding style used in most of the examples.)

Hi Pieter,

First of all, thank you so much for your kind help with all of this as it is very much appreciated.

I'm not really too sure why I placed baud there as I had meant to write MIDI_BAUD to go along with the function below it as I wasn't too sure if it already had a value. I'm guessing that as it's a constant then that must be coming from the Control Surface Library somewhere?

I've now changed the code to this instead :

// Include the Control Surface library
#include <Control_Surface.h>

// Setup a MIDI interface
HardwareSerialMIDI_Interface midi(Serial, MIDI_BAUD);

 
// Create a new instance of the class `CCPotentiometer`, called `potentiometer`,
// on pin A0, that sends MIDI messages with controller 7 (channel volume)
// on channel 1.
CCPotentiometer potentiometer = {
  A0, { MIDI_CC::Channel_Volume, CHANNEL_1 }
};
 
// The filtered value read when potentiometer is at the 0% position
constexpr analog_t minimumValue = 255;
// The filtered value read when potentiometer is at the 100% position
constexpr analog_t maximumValue = 16383 - 255;
 
// A mapping function to eliminate the dead zones of the potentiometer:
// Some potentiometers don't output a perfect zero signal when you move them to
// the zero position, they will still output a value of 1 or 2, and the same
// goes for the maximum position.
analog_t mappingFunction(analog_t raw) {
    // make sure that the analog value is between the minimum and maximum
    raw = constrain(raw, minimumValue, maximumValue);
    // map the value from [minimumValue, maximumValue] to [0, 16383]
    return map(raw, minimumValue, maximumValue, 0, 16383);
    // Note: 16383 = 2¹⁴ - 1 (the maximum value that can be represented by
    // a 14-bit unsigned number
}
 
void setup() {
    // Add the mapping function to the potentiometer
    potentiometer.map(mappingFunction);
    // Initialize everything
    Control_Surface.begin();
    Serial.begin(115200);
}
 
void loop() {
    // Update the Control Surface (check whether the potentiometer's
    // input has changed since last time, if so, send the new value over MIDI).
    Control_Surface.loop();
    // Use this to find minimumValue and maximumValue: it prints the raw value
    // of the potentiometer, without the mapping function
//    Serial.println(potentiometer.getRawValue());
}

However when I upload the code to the board and turn on Hairless MidiSerial I am still getting weird results. I've attached another image to this post showing what I'm now getting.

With my piano attached to my MIDI interface not a lot happened apart from the very end of travel of the fader at one end in that it was sending a programme change and changing the sound the piano was playing.

I'm not however getting volume changes sent though which is what I was hoping would happen :frowning:

Not too sure where I'm going wrong with it now though?

Also noticed that the values in Hairless are changing most of the time once the fader is set to past halfway but at the other half they only change when I change the fader. Also it's receiving all sorts of MIDI channels and controller values so not too sure why it's doing that?

Many thanks,

Mark

MIDI_BAUD is indeed a constant that comes with the library.

You have to be careful with the baud rates: you set the baud rate of the MIDI interface to MIDI_BAUD at the top of your sketch, but in your setup, you call Serial.begin(115200), which will override that, since your MIDI interface uses the Serial port. I'd recommend removing the Serial.begin(115200) altogether.

If you want to use a 5-pin DIN MIDI output to connect to a MIDI piano, you have to use MIDI_BAUD.
If you want to use Hairless, you cannot use MIDI_BAUD, because it is not a supported baud rate on most computers. In that case, you have to use the same baud rate as the one you have selected in the Hairless settings. The screenshot you posted indicates that the baud rate of the Arduino and the baud rate of Hairless do not match. When they are not the same, you just get random data.

#include <Control_Surface.h> // Include the Control Surface library

// Setup a MIDI interface
HardwareSerialMIDI_Interface midi(Serial, MIDI_BAUD);

// For Hairless, use 
//    USBSerialMIDI_Interface midi(115200);
// and set the baud rate to 115200 in the Hairless settings as well.
 
// Create a new instance of the class `CCPotentiometer`, called `potentiometer`,
// on pin A0, that sends MIDI messages with controller 7 (channel volume)
// on channel 1.
CCPotentiometer potentiometer = {
  A0, { MIDI_CC::Channel_Volume, CHANNEL_1 }
};
 
void setup() {
    // Initialize everything
    Control_Surface.begin();
}
 
void loop() {
    // Update the Control Surface (check whether the potentiometer's
    // input has changed since last time, if so, send the new value over MIDI).
    Control_Surface.loop();
}

Hi Pieter,

Thank you so so much!!!

Using the code you provided I was getting some weird sound at the end of travel of the potentiometer so I tried uncommenting the :

HardwareSerialMIDI_Interface midi(Serial, MIDI_BAUD);

line and using the :

    USBSerialMIDI_Interface midi(115200);

line instead (making sure to set Hairless to the same baud rate and that seemed to work better. I was also getting some weird transposing going on in my DAW when moving the fader however having said that I've just gone and changed them back to what you had originally posted above in your last post and now everything seems to be more settled down so not too sure what that was all about?

I can see in Hairless that I'm now indeed only getting CC7 messages being sent which is absolutely fantastic so thank you for that!
I'm not sure why I was getting the weird transposing going on though?

I do believe that the fader (it's quite an old one [10K linear]) has some weird values sending at the one end of travel though and do have some new ones that I will try later on with it instead which will hopefully fix that problem up.

I do believe however that there is a way to smooth out the values though or fix problems with faders that have gone like that though with your library maybe? I thought I saw something mentioned about that somewhere?

However it is indeed working and I can send volume messages to my DAW so thank you so so much for that!

I was just wondering with your code as it says to use the USBSerialMIDI_Interface function if I want to use Hairless but it seemed to work with both that and the other line (not both uncommented at the same time obviously) so wondering which one I should definitely be using?

Is HardwareSerialMIDI_Interface the one that you would use for outputting via the TX pin (I believe that's the right pin is it) to a MIDI connector?

If so then was just wondering why Hairless still picks up the information when using the USBSerialMIDI_Interface line instead?

Many thanks again though for getting me this far and I can't wait to really get stuck in and hopefully understand it all more and get what I want to make done now.

Best wishes,

Mark

Oops sorry meant to ask as well.

One other thing I noticed was that as soon as I turn on Hairless I get a load of MIDI notes sent through to my DAW or perhaps an overload of MIDI information which then plays a load of notes on CH1 which can be a little unsettling when it happens.

Just wondering if I'm perhaps not starting things up in the right order or if that's a normal side effect of these sorts of applications?

Many thanks again.

Mark

USBSerialMIDI_Interface sends MIDI over the Serial port that is connected to the USB interface of the Arduino. This is the serial port that you have access to in the serial monitor or Hairless.

HardwareSerialMIDI_Interface sends MIDI over a HardwareSerial interface, i.e. a hardware UART that you have access to through TX and RX pins on the Arduino.

The confusing thing here is that the ESP8266 doesn't have USB support, so to communicate over USB, you need a second chip (it's the black rectangular chip on the bottom of the Wemos D1 mini). The ESP8266 communicates with this USB chip over a hardware serial, the TX and RX pins are connected to this chip internally. That's why both MIDI interfaces can be used with Hairless, but this will not be the case on other boards that do have a true USB Serial port.

In general, use HardwareSerialMIDI_Interface if you want to use the TX and RX pins, and use USBSerialMIDI_Interface if you want to communicate with the computer over USB.

Old potentiometers can be noisy, you could probably fix it using some contact cleaner.
The library already filters the analog inputs and applies hysteresis to get rid of most of the noise you'd encounter under normal circumstances.
You can try increasing the amount of filtering in the settings: Control-Surface/Settings.hpp at 362566d1765c3c465c7c4a27aabfb2e3aad662bc · tttapa/Control-Surface · GitHub
Try 3 or 4, but keep in mind that this might introduce some latency. You could also try to change the ANALOG_FILTER_TYPE to uint32_t to reduce rounding errors in the filter, but I doubt that'll help much.

The potentiometer mapping code you posted in reply #2 allows you to compensate for potentiometers that don't reach 0 or 100% exactly, but it won't filter out noise. Do keep in mind that if you change the analog filter settings, you have to change the constants in the mapping function. The formula for the maximum value is 2B-1, where B = number of bits in the analog filter type - analog filter shift factor.
For the default values of ANALOG_FILTER_TYPE = uint16_t and ANALOG_FILTER_SHIFT_FACTOR = 2, B = 16 - 2 = 14, so the maximum value is 214-1 = 16383.
You can get this value programatically using FilteredAnalog<>::getMaxRawValue().

You might also get some noise because of unstable power rails, but there's not much you can do about that, except using a board with a ratiometric ADC.

The notes you get when you start Hairless are because the ESP8266 prints some information to the Serial port when it boots, it's not MIDI data, and probably at the wrong baud rate, so Hairless gets confused. This information is part of the bootloader, it's fixed in the chip, and there's no way to disable it.
The only solution I know of is not using the standard TX pin, but using an alternative TX pin, and then swapping the Serial pins in software. See Reference — ESP8266 Arduino Core 3.1.1-19-gbe02af05 documentation
That does mean you'll have to use an external USB to Serial adapter, because the Wemos's USB to Serial chip is connected to the primary TX pin, and that one always prints out boot information.
You can use this technique if you want to use DIN MIDI, though.

Until you get a more suitable Arduino board that doesn't have the same limitations as the ESP8266, maybe you could try using AppleMIDI over WiFi instead? I've never tried it on a Mac, though, but it seems to work alright on Linux, and should work out of the box on Mac (it's AppleMIDI, after all).
https://tttapa.github.io/Control-Surface-doc/Doxygen/dc/d7d/AppleMIDI_8ino-example.html

You could also try using your Arduino Nano.

Hi Pieter,

PieterP:
USBSerialMIDI_Interface sends MIDI over the Serial port that is connected to the USB interface of the Arduino. This is the serial port that you have access to in the serial monitor or Hairless.

HardwareSerialMIDI_Interface sends MIDI over a HardwareSerial interface, i.e. a hardware UART that you have access to through TX and RX pins on the Arduino.

The confusing thing here is that the ESP8266 doesn't have USB support, so to communicate over USB, you need a second chip (it's the black rectangular chip on the bottom of the Wemos D1 mini). The ESP8266 communicates with this USB chip over a hardware serial, the TX and RX pins are connected to this chip internally. That's why both MIDI interfaces can be used with Hairless, but this will not be the case on other boards that do have a true USB Serial port.

In general, use HardwareSerialMIDI_Interface if you want to use the TX and RX pins, and use USBSerialMIDI_Interface if you want to communicate with the computer over USB.

Thank you. I will definitely keep that in mind for the future.

PieterP:
Old potentiometers can be noisy, you could probably fix it using some contact cleaner.

Actually I've now moved to the Arduino Nano with the same code on and with a brand new potentiometer and that is having a problem however not that it's noisy at one end, rather that it does a couple of things.

The first is that it starts changing the values a little slowly on the low end then gets quicker in the middle and slows down a little again at the other end. Also it never gives out a MIDI CC value higher than 91 (I've now set it to CC11 [Expression]).

I'm not sure why it's getting stuck at 91 though and not going all the way to 127 though?

PieterP:
The library already filters the analog inputs and applies hysteresis to get rid of most of the noise you'd encounter under normal circumstances.
You can try increasing the amount of filtering in the settings: Control-Surface/src/AH/Settings/Settings.hpp at 362566d1765c3c465c7c4a27aabfb2e3aad662bc · tttapa/Control-Surface · GitHub
Try 3 or 4, but keep in mind that this might introduce some latency. You could also try to change the ANALOG_FILTER_TYPE to uint32_t to reduce rounding errors in the filter, but I doubt that'll help much.

I'll check that link out however I'm just about keeping up with what I've done so far and all that sounds pretty advanced to me :wink:

PieterP:
The potentiometer mapping code you posted in reply #2 allows you to compensate for potentiometers that don't reach 0 or 100% exactly, but it won't filter out noise. Do keep in mind that if you change the analog filter settings, you have to change the constants in the mapping function. The formula for the maximum value is 2B-1, where B = number of bits in the analog filter type - analog filter shift factor.
For the default values of ANALOG_FILTER_TYPE = uint16_t and ANALOG_FILTER_SHIFT_FACTOR = 2, B = 16 - 2 = 14, so the maximum value is 214-1 = 16383.
You can get this value programatically using FilteredAnalog<>::getMaxRawValue().

You might also get some noise because of unstable power rails, but there's not much you can do about that, except using a board with a ratiometric ADC.

That all definitely sounded a little NASA to me :slight_smile: Thank you for the unbelievably thorough replies though. I've never met anyone so knowledgeable on anything before!

PieterP:
The notes you get when you start Hairless are because the ESP8266 prints some information to the Serial port when it boots, it's not MIDI data, and probably at the wrong baud rate, so Hairless gets confused. This information is part of the bootloader, it's fixed in the chip, and there's no way to disable it.
The only solution I know of is not using the standard TX pin, but using an alternative TX pin, and then swapping the Serial pins in software. See Reference — ESP8266 Arduino Core 3.1.2-21-ga348833 documentation
That does mean you'll have to use an external USB to Serial adapter, because the Wemos's USB to Serial chip is connected to the primary TX pin, and that one always prints out boot information.
You can use this technique if you want to use DIN MIDI, though.

Ah, that's not too much of a problem. I'll just make sure that the DAW is the last thing turned on and that should sort it all out for me.

PieterP:
Until you get a more suitable Arduino board that doesn't have the same limitations as the ESP8266, maybe you could try using AppleMIDI over WiFi instead? I've never tried it on a Mac, though, but it seems to work alright on Linux, and should work out of the box on Mac (it's AppleMIDI, after all).
Control Surface: AppleMIDI.ino

You could also try using your Arduino Nano.

I am actually now using the Nano and it seems to be working nicely except for the Expression CC is only going to 91 for some reason.

Many thanks,

Mark

Just as a quick update I tried out the code on this page :

https://tttapa.github.io/Control-Surface-doc/Doxygen/d3/d26/CCPotentiometer-Map_8ino-example.html

and noted the values I was getting in the serial monitor. My potentiometer went up to about 11739 when at the full end of travel so I placed this code :

// The filtered value read when potentiometer is at the 0% position
constexpr analog_t minimumValue = 50;
// The filtered value read when potentiometer is at the 100% position
constexpr analog_t maximumValue = 11739 - 50;

into the sketch and it has now made it so that it still only goes from 0-91 but it's at 91 when the fader is fully faded. Is there any way to get it to go up to 128 though like it should do?

Many thanks,

Mark

Can you post the full sketch - snippets usually don't help...

markbowendesign:
The first is that it starts changing the values a little slowly on the low end then gets quicker in the middle and slows down a little again at the other end.

Are you using a linear taper potentiometer? Linear taper potentiometers are usually marked "B" on Asian or American products (confusingly, "A" using European conventions), while logarithmic/audio potentiometers are marked "A" ("B" or "C" European convention). There are other types as well, but they are less common.

markbowendesign:
Also it never gives out a MIDI CC value higher than 91 (I've now set it to CC11 [Expression]).

I'm not sure why it's getting stuck at 91 though and not going all the way to 127 though?

Did you connect the right pin of the potentiometer to 3.3V or 5V? The Arduino Nano is a 5V microcontroller, so Vcc is 5V, not 3.3V.

markbowendesign:
Just as a quick update I tried out the code on this page :

Control Surface: CCPotentiometer-Map.ino

and noted the values I was getting in the serial monitor. My potentiometer went up to about 11739 when at the full end of travel so I placed this code :

// The filtered value read when potentiometer is at the 0% position

constexpr analog_t minimumValue = 50;
// The filtered value read when potentiometer is at the 100% position
constexpr analog_t maximumValue = 11739 - 50;




into the sketch and it has now made it so that it still only goes from 0-91 but it's at 91 when the fader is fully faded. Is there any way to get it to go up to 128 though like it should do?

Just adding those 4 lines is not going to do anything, they're just two variable declarations, it doesn't affect the code if you don't use them. You need the rest of the sketch as well (the mapping function and potentiometer.map(mappingFunction) in the setup).
It might not be a bad idea to go through a C++ tutorial so you have an idea of the basic concepts of the language.

E.T.A. I did see the example where you have multiple pots in an array but that was for a multiplexer input and I wasn't really sure how to use an array and have them on different pins so not sure if you indeed can use an array for what I'm trying to do below.

Hi Pieter & MarkT,

So sorry for not posting my entire code above. I had meant to but got in a rush and forgot to. Apologies for that.

My entire code now is as follows :

#include <Control_Surface.h> // Include the Control Surface library

// Setup a MIDI interface
// HardwareSerialMIDI_Interface midi(Serial, MIDI_BAUD);
// HardwareSerialMIDI_Interface midi(Serial, 19200);


// For Hairless, use
   USBSerialMIDI_Interface midi(115200);
// and set the baud rate to 115200 in the Hairless settings as well.

// Create a new instance of the class `CCPotentiometer`, called `channel_volume_potentiometer`,
// on pin A0, that sends MIDI messages with controller 7 (channel volume)
// on channel 1.
//CCPotentiometer channel_volume_potentiometer = {
//  A0, { MIDI_CC::Channel_Volume, CHANNEL_1 }
//    A0, { 7, CHANNEL_1 }
//};

// Create a new instance of the class `CCPotentiometer`, called `expression_potentiometer`,
// on pin A1, that sends MIDI messages with controller 7 (channel volume)
// on channel 1.
CCPotentiometer expression_potentiometer = {
//  A1, { MIDI_CC::Expression_Controller, CHANNEL_1 }
    A1, { 11, CHANNEL_1 }
};

// Create a new instance of the class `CCPotentiometer`, called `modulation_potentiometer`,
// on pin A1, that sends MIDI messages with controller 1 (modulation)
// on channel 1.
//CCPotentiometer modulation_potentiometer = {
//  A2, { MIDI_CC::Modulation_Wheel, CHANNEL_1 }
//  A2, { 1, CHANNEL_1 }
//};




// The filtered value read when potentiometer is at the 0% position
constexpr analog_t minimumValue = 50;
// The filtered value read when potentiometer is at the 100% position
// constexpr analog_t maximumValue = 16383 - 50;
// constexpr analog_t maximumValue = 11739 - 50;
constexpr analog_t maximumValue = 11739 - 50;

// A mapping function to eliminate the dead zones of the potentiometer:
// Some potentiometers don't output a perfect zero signal when you move them to
// the zero position, they will still output a value of 1 or 2, and the same
// goes for the maximum position.
analog_t mappingFunction(analog_t raw) {
    // make sure that the analog value is between the minimum and maximum
    raw = constrain(raw, minimumValue, maximumValue);
    // map the value from [minimumValue, maximumValue] to [0, 16383]
    return map(raw, minimumValue, maximumValue, 0, 16383);
    // Note: 16383 = 2¹⁴ - 1 (the maximum value that can be represented by
    // a 14-bit unsigned number
}

void setup() {
    // Initialize everything
    Control_Surface.begin();
}

void loop() {
    // Update the Control Surface (check whether the potentiometer's
    // input has changed since last time, if so, send the new value over MIDI).
    Control_Surface.loop();
}

I had actually copied in the function for the mapping but sorry for not showing that above.

My problem as rightly pointed out by Pieter was that I was connected to the 3.3V rail instead of the 5V one. I thought I had read somewhere that you couldn't put that much voltage on the pins but that was probably for a different board (I'm possibly reading too much and definitely getting confused by all the different boards. Will try not to do that from now on!!).

So basically it is now working and going from 0-127 which is great!

The potentiometers are definitely Linear pots so hopefully they're the correct ones to be using here?

Only thing I'm wondering now is how do I attach more than one pot at the same time?

As you can see in my code above I tried making 3 different potentiometer calls, all named differently and sending out different CC messages however (disclaimer alert - I haven't connected the 3 pots yet although still thought it should work but just not send any [much] data along) it's not doing that. Currently if I uncomment those other pot calls then the same value from the pot that is connected (again only have the one connected to the board at the moment) gets sent out three times, once to each different CC message.

I wouldn't have expected that to happen though? At most I was thinking without the other pots connected then I might be getting noise on those pins without them being connected at all but I wasn't expecting the exact same value to be sent to all three messages so I think I've probably got something wrong with the code maybe?

Also with the mapping function, how do you set that up for the three pots if they were all different values? I'm fairly positive that these new pots don't have any problems with noise but from my exceptionally limited understanding of the code it looks like those two lines will only work for the one pot? Just wondering how you code it incase you need different values for each pot?

Thanks again for all the help as it's seriously appreciated. I'm not a coder (that's probably pretty obvious by now though :-D) so all the help you're sending my way is really appreciated.

Best wishes,

Mark

I wouldn't have expected that to happen though? At most I was thinking without the other pots connected then I might be getting noise on those pins without them being connected at all but I wasn't expecting the exact same value to be sent to all three messages so I think I've probably got something wrong with the code maybe?

No error in the code that is exactly what you will see with only one pot connected. What happens is that there is only one A/D inside the processor and the different inputs are connected to a multiplexer.

The input you are reading gets switched to the A/D where it charges up a tiny capacitor called the sample and hold capacitor. As the interference is so high an impedance on an unconnected input it will not overcome the charge already put on the sample and hold capacitor by the pot.

So the A/D just reads the same value again. In time this would decay but you keep charging it up every time you read the input connected to the pot.

markbowendesign:
E.T.A. I did see the example where you have multiple pots in an array but that was for a multiplexer input and I wasn't really sure how to use an array and have them on different pins so not sure if you indeed can use an array for what I'm trying to do below.

It doesn't matter whether you're using a multiplexer or not. If you want to use the standard analog inputs, you just replace mux.pin(0) by A0, for example. As long as it's a valid pin number, it's fine.

Also see Multiple-Control-Change-Potentiometers.ino, it doesn't use multiplexers.

markbowendesign:
My entire code now is as follows :

#include <Control_Surface.h> // Include the Control Surface library

// Setup a MIDI interface
// HardwareSerialMIDI_Interface midi(Serial, MIDI_BAUD);
// HardwareSerialMIDI_Interface midi(Serial, 19200);

// For Hairless, use
  USBSerialMIDI_Interface midi(115200);
// and set the baud rate to 115200 in the Hairless settings as well.

// Create a new instance of the class CCPotentiometer, called channel_volume_potentiometer,
// on pin A0, that sends MIDI messages with controller 7 (channel volume)
// on channel 1.
//CCPotentiometer channel_volume_potentiometer = {
//  A0, { MIDI_CC::Channel_Volume, CHANNEL_1 }
//    A0, { 7, CHANNEL_1 }
//};

// Create a new instance of the class CCPotentiometer, called expression_potentiometer,
// on pin A1, that sends MIDI messages with controller 7 (channel volume)
// on channel 1.
CCPotentiometer expression_potentiometer = {
//  A1, { MIDI_CC::Expression_Controller, CHANNEL_1 }
   A1, { 11, CHANNEL_1 }
};

// Create a new instance of the class CCPotentiometer, called modulation_potentiometer,
// on pin A1, that sends MIDI messages with controller 1 (modulation)
// on channel 1.
//CCPotentiometer modulation_potentiometer = {
//  A2, { MIDI_CC::Modulation_Wheel, CHANNEL_1 }
//  A2, { 1, CHANNEL_1 }
//};

// The filtered value read when potentiometer is at the 0% position
constexpr analog_t minimumValue = 50;
// The filtered value read when potentiometer is at the 100% position
// constexpr analog_t maximumValue = 16383 - 50;
// constexpr analog_t maximumValue = 11739 - 50;
constexpr analog_t maximumValue = 11739 - 50;

// A mapping function to eliminate the dead zones of the potentiometer:
// Some potentiometers don't output a perfect zero signal when you move them to
// the zero position, they will still output a value of 1 or 2, and the same
// goes for the maximum position.
analog_t mappingFunction(analog_t raw) {
   // make sure that the analog value is between the minimum and maximum
   raw = constrain(raw, minimumValue, maximumValue);
   // map the value from [minimumValue, maximumValue] to [0, 16383]
   return map(raw, minimumValue, maximumValue, 0, 16383);
   // Note: 16383 = 2¹⁴ - 1 (the maximum value that can be represented by
   // a 14-bit unsigned number
}

void setup() {
   // Initialize everything
   Control_Surface.begin();
}

void loop() {
   // Update the Control Surface (check whether the potentiometer's
   // input has changed since last time, if so, send the new value over MIDI).
   Control_Surface.loop();
}




I had actually copied in the function for the mapping but sorry for not showing that above.

You've defined a mapping function, but you never apply it to the potentiometers. You'll have to add expression_potentiometer.map(mappingFunction); to your setup, otherwise it won't have any effect.

markbowendesign:

CCPotentiometer expression_potentiometer = {

//  A1, { MIDI_CC::Expression_Controller, CHANNEL_1 }
   A1, { 11, CHANNEL_1 }
};

Both of these lines are equivalent, it doesn't matter whether you use MIDI_CC::Expression_Controller or 11, since Expresion_Controller is simply a constant equal to 11, see Control Surface: MIDI_CC Namespace Reference.

markbowendesign:
My problem as rightly pointed out by Pieter was that I was connected to the 3.3V rail instead of the 5V one. I thought I had read somewhere that you couldn't put that much voltage on the pins but that was probably for a different board (I'm possibly reading too much and definitely getting confused by all the different boards. Will try not to do that from now on!!).

So basically it is now working and going from 0-127 which is great!

Most boards will have a 5V and a 3.3V output, because they get 5V from the USB power, and use an on-board voltage regulator to get 3.3V.

The thing you have to watch out for is that the microcontrollers of some boards run at 5V, while others run at 3.3V. This should be clear from the product page of the board you're using.
If you apply 5V to the IO pins of a 3.3V microcontroller, you'll probably destroy it, so you have to be careful.
(Some 3.3V microcontrollers might have 5V-tolerant pins, but many modern ones don't, so you should always check the documentation for your specific board before connecting stuff.)

The ESP8266 is a 3.3V board, so you have to use 3.3V levels for the IO pins, and the Nano is a 5V board, so you use 5V levels for IO.

markbowendesign:
The potentiometers are definitely Linear pots so hopefully they're the correct ones to be using here?

Yes, you want linear pots.

markbowendesign:
Only thing I'm wondering now is how do I attach more than one pot at the same time?

Same procedure, left pin to ground, right pin to Vcc, wiper to an analog input of the Arduino (a different pin for each potentiometer, of course).

markbowendesign:
As you can see in my code above I tried making 3 different potentiometer calls, all named differently and sending out different CC messages however (disclaimer alert - I haven't connected the 3 pots yet although still thought it should work but just not send any [much] data along) it's not doing that. Currently if I uncomment those other pot calls then the same value from the pot that is connected (again only have the one connected to the board at the moment) gets sent out three times, once to each different CC message.

I wouldn't have expected that to happen though? At most I was thinking without the other pots connected then I might be getting noise on those pins without them being connected at all but I wasn't expecting the exact same value to be sent to all three messages so I think I've probably got something wrong with the code maybe?

Apart from the incorrect comments, the code looks alright.

The Arduino Nano only has a single ADC (analog to digital converter), it is multiplexed to the 8 analog input pins. The ADC has a sample-and-hold capacitor that is momentarily connected to the analog input pin to charge it to the voltage you apply to that pin. Then it measures the voltage over that S/H capacitor.
If you don't connect any signals to the analog input pins, no current can flow to charge or discharge the S/H capacitor, so the measurement won't change (much).

When reading the first potentiometer, the internal multiplexer connects pin A0 to the S/H capacitor, so the voltage on the capacitor will be equal to the voltage at the wiper of the potentiometer connected to that pin.
If the code then tries to measure A1, which doesn't have anything connected to it, the multiplexer connects the S/H capacitor to pin A1, but it doesn't change the voltage of the S/H capacitor, because there's nothing connected to that pin, so you measure the same voltage as the last measurement.

markbowendesign:
Also with the mapping function, how do you set that up for the three pots if they were all different values? I'm fairly positive that these new pots don't have any problems with noise but from my exceptionally limited understanding of the code it looks like those two lines will only work for the one pot? Just wondering how you code it incase you need different values for each pot?

You'll have to define different mapping functions in that case, and then call ***_potentiometer.map(***_mappingFunction) in the setup, where *** is the name of one of your potentiometers.

Defining all these different functions results in pretty long and repetitive code. A better alternative would be to write a single mapping function that takes the minimum and maximum values as parameters, and then use some lambda functions to pass different functions to the potentiometers.
If you want to understand how it works, you'll have to look up the syntax for lambda functions, and you'll have to have a basic understanding of how to use functions in C++.

// Include the Control Surface library
#include <Control_Surface.h>

// Instantiate a MIDI over USB interface.
USBMIDI_Interface midi;

// Instantiate an array of potentiometers.
CCPotentiometer potentiometers[] = {
  {A0, {MIDI_CC::Channel_Volume, CHANNEL_1}},
  {A1, {MIDI_CC::Channel_Volume, CHANNEL_2}},
  {A2, {MIDI_CC::Channel_Volume, CHANNEL_3}},
};

// A mapping function to eliminate the dead zones of the potentiometers:
// Some potentiometers don't output a perfect zero signal when you move them to
// the zero position, they will still output a value of 1 or 2, and the same
// goes for the maximum position.
analog_t mappingFunction(analog_t raw, analog_t minimumValue, analog_t maximumValue) {
    // make sure that the analog value is between the minimum and maximum
    raw = constrain(raw, minimumValue, maximumValue);
    // map the value from [minimumValue, maximumValue] to [0, 16383]
    return map(raw, minimumValue, maximumValue, 0, 16383);
    // Note: 16383 = 2¹⁴ - 1 (the maximum value that can be represented by
    // a 14-bit unsigned number
}

void setup() {
    // Add the mapping functions to the potentiometers
    potentiometers[0].map(+[](analog_t raw) { return mappingFunction(raw, 255, 16383 - 255); });
    potentiometers[1].map(+[](analog_t raw) { return mappingFunction(raw, 300, 16383 - 300); });
    potentiometers[2].map(+[](analog_t raw) { return mappingFunction(raw, 100, 16383 - 200); });
    // Initialize everything
    Control_Surface.begin();
}

void loop() {
    // Update the Control Surface
    Control_Surface.loop();
}

Hi Mike,

Thank you for the reply. I've seen your books mentioned a lot around the internet so very appreciative for your answer.

So if I'm right in my understanding of what you've said then when I connect the other pots I shouldn't have that problem any more?

Unfortunately I need to get some wires and what not to be able to add the other pots to the board but just wanted to check if I'm right in my understanding of all of that?

Many thanks,

Mark

Hi Pieter,

Looks like my page didn't update correctly and I've just noticed your replies after I'd just posted above.

Many thanks for all the information you've posted there.

Going to have a severe read of it all and see if I can get my head wrapped round it all.

Thank you again for all the utterly fantastic help you've been giving me on this as it's very much appreciated.

Best wishes,

Mark

if I'm right in my understanding of what you've said then when I connect the other pots I shouldn't have that problem any more?

Yes you are correct.