ESP32 - can't recieve serial data in Hairless Midi

Hi. I'm making a midi controller. It was all working on an Uno R3, but that board was too slow so I've just bought a Nano ESP32 (I think it's a legit one?) .

The midi controller sends data through USB Serial to my PC, Hairless midi receives it and sends it to Midi Loop, which picks it up in Ableton Live. This was all working fine on the Uno R3.

For the ESP32, it didn't work right away, so I've been searching and trying things out. I've uninstalled and reinstalled the VCP / FTDI drivers, then I saw that they aren't used for ESP32, so I installed Silicon Labs CP210x VCP drivers. I updated the firmware on my ESP32 using Zadig - to LibusbK.

I'm quite the newb, so this is all totally new for me. I naively thought my ESP32 would work right out the box!

Thanks, hope you can help. I want to get back to dialing in my air sensors!

I did check the baud rate in Hairless, I've set it to 115200 as per sketch.
I may have installed some incorrect drivers / firmware by accident.

I have made some updates to my code, which chat GPT recommended. This is my code:

#include <MIDI.h>

// Create a MIDI instance
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);

// Define pin numbers
const int button1Pin = 2;  // Valve 1
const int button2Pin = 3;  // Valve 2
const int button3Pin = 4;  // Valve 3
const int pitchButtonPin = 5; // Pitch change
const int pressureSensorPin = 36; // ADC1 channel 0, GPIO36

int currentNote = 60; // Middle C
const int velocity = 127; // Maximum velocity
bool notePlaying = false;

void setup() {
  // Initialize buttons as input
  pinMode(button1Pin, INPUT_PULLUP);
  pinMode(button2Pin, INPUT_PULLUP);
  pinMode(button3Pin, INPUT_PULLUP);
  pinMode(pitchButtonPin, INPUT_PULLUP);

  // Initialize MIDI communication
  MIDI.begin(MIDI_CHANNEL_OMNI);
  Serial.begin(115200); // USB Serial communication
  Serial1.begin(31250, SERIAL_8N1, -1, 17); // MIDI Serial communication on GPIO 17 for TX
}

void loop() {
  // Read button states
  bool button1State = digitalRead(button1Pin) == LOW;
  bool button2State = digitalRead(button2Pin) == LOW;
  bool button3State = digitalRead(button3Pin) == LOW;
  bool pitchButtonState = digitalRead(pitchButtonPin) == LOW;

  // Read pressure sensor
  int pressureValue = analogRead(pressureSensorPin);

  // Calculate base note based on valve combination
  int baseNote = 60; // Default to Middle C
  if (button1State) baseNote -= 2; // Valve 1
  if (button2State) baseNote -= 1; // Valve 2
  if (button3State) baseNote -= 3; // Valve 3

  // Adjust for pitch button
  if (pitchButtonState) baseNote += 7; // Perfect 5th

  // Determine if we should play the note
  if (pressureValue > 2048) { // Adjust threshold as needed (ESP32 ADC range is 0-4095)
    if (!notePlaying || currentNote != baseNote) {
      if (notePlaying) {
        sendMIDIMessage(0x80, currentNote, 0); // Note Off
      }
      sendMIDIMessage(0x90, baseNote, velocity); // Note On
      currentNote = baseNote;
      notePlaying = true;
    }
  } else {
    if (notePlaying) {
      sendMIDIMessage(0x80, currentNote, 0); // Note Off
      notePlaying = false;
    }
  }
  
  delay(10); // Small delay to debounce buttons and avoid overwhelming the serial communication
}

void sendMIDIMessage(byte command, byte pitch, byte velocity) {
  Serial1.write(command);
  Serial1.write(pitch);
  Serial1.write(velocity);
}

1 Like

you should decide what you will use: USB Serial or USB MIDI
bc:
if Serial then you need no MIDI.h
if USB MIDI then you need no Serial.begin(31250)

actually Serial.begin(31250) not needed in both cases, only if you connect DIY MIDI output or MIDI shield to Serial pins.

i decided to Serial. i will modify sketch for it.

// Define pin numbers
const int button1Pin = 2;  // Valve 1
const int button2Pin = 3;  // Valve 2
const int button3Pin = 4;  // Valve 3
const int pitchButtonPin = 5; // Pitch change
const int pressureSensorPin = 36; // ADC1 channel 0, GPIO36

int currentNote = 60; // Middle C
const int velocity = 127; // Maximum velocity
bool notePlaying = false;

void setup() {
  // Initialize buttons as input
  pinMode(button1Pin, INPUT_PULLUP);
  pinMode(button2Pin, INPUT_PULLUP);
  pinMode(button3Pin, INPUT_PULLUP);
  pinMode(pitchButtonPin, INPUT_PULLUP);

  Serial.begin(115200); // USB Serial communication
}

void loop() {
  // Read button states
  bool button1State = digitalRead(button1Pin) == LOW;
  bool button2State = digitalRead(button2Pin) == LOW;
  bool button3State = digitalRead(button3Pin) == LOW;
  bool pitchButtonState = digitalRead(pitchButtonPin) == LOW;

  // Read pressure sensor
  int pressureValue = analogRead(pressureSensorPin);

  // Calculate base note based on valve combination
  int baseNote = 60; // Default to Middle C
  if (button1State) baseNote -= 2; // Valve 1
  if (button2State) baseNote -= 1; // Valve 2
  if (button3State) baseNote -= 3; // Valve 3

  // Adjust for pitch button
  if (pitchButtonState) baseNote += 7; // Perfect 5th

  // Determine if we should play the note
  if (pressureValue > 2048) { // Adjust threshold as needed (ESP32 ADC range is 0-4095)
    if (!notePlaying || currentNote != baseNote) {
      if (notePlaying) {
        sendMIDIMessage(0x80, currentNote, 0); // Note Off
      }
      sendMIDIMessage(0x90, baseNote, velocity); // Note On
      currentNote = baseNote;
      notePlaying = true;
    }
  } else {
    if (notePlaying) {
      sendMIDIMessage(0x80, currentNote, 0); // Note Off
      notePlaying = false;
    }
  }

  delay(10); // Small delay to debounce buttons and avoid overwhelming the serial communication
}

void sendMIDIMessage(byte command, byte pitch, byte velocity) {
  Serial.write(command);
  Serial.write(pitch);
  Serial.write(velocity);
  Serial.flush();
}

Grüße aus Flensburg

Thankyou @kolaha for the help.

I tried you sketch, but I still can't get Hairless to recognice incoming data.

I have been uninstalling and reinstalling VCP FTDI drivers, and I also used Zadig to install the 'libusbK' fireware onto the ESP32. I feel like maybe something isn't quite right here, with my Windows10 drivers or my the ESP32 fireware.

I get this message in Arduino IDE (desktop) after upload:

"Sketch uses 292745 bytes (9%) of program storage space. Maximum is 3145728 bytes.
Global variables use 31100 bytes (9%) of dynamic memory, leaving 296580 bytes for local variables. Maximum is 327680 bytes.
dfu-util 0.11-arduino4

Opening DFU capable USB device...
Device ID 2341:0070
Device DFU version 0101
Claiming USB DFU Interface...
Setting Alternate Interface #0 ...
Determining device status...
DFU state(2) = dfuIDLE, status(0) = No error condition is present
DFU mode device DFU version 0101
Device returned transfer size 4096
Copying data from PC to DFU device
...
Download [=========================] 100% 293104 bytes
Download done.
DFU state(7) = dfuMANIFEST, status(0) = No error condition is present
DFU state(2) = dfuIDLE, status(0) = No error condition is present
Done!"

In Hairless Midi, I also get this error:
"FTDI Drivers appear not to be installed" - I did intall them when I used Uno R3, and it worked. I uninstalled and reinstalled with the ESP32, but it didn't stop the error. I'm a bit confused here, I read somewhere that ESP32 doesn't need the VCP drivers. That's why I installed the 'libusbK' fireware to the ESP32 (I think.. I'm very confused now!)

Could there be a problem here?
Thanks :slight_smile:

image

The Nano ESP32 has a native USB interface and doesn't need any of these drivers ( FTDI,CH340). Maybe you selected the wrong Com port.

I would not trust chatGPT ... at least not blindly. :thinking:

How did you connect your Nano to the PC???

Hi @MicroBahner - thanks for the reply.

At first I was just connecting through the USB-C port on the Nano, to USB port on my PC.

I was thinking about giving up on this and just using the TX midi serial out, into my audio interface..

RE Chat.GPT - started using it to quickly build some logic for three buttons that mimic trumpet valves, but yes - have ended up relying on it too much!

I'm sure the COM port is correct.. it say's 'Arduino Nano ESP32 COM5'

Hi @jammond.

The Nano ESP32 is different from the UNO R3 in that it doesn't start sending data from its USB CDC serial port until it receives a special signal from the computer (maybe RTS?).

Unfortunately Hairless MIDI<->Serial Bridge does not send that signal. This is likely the cause of your problem.

Arduino IDE Serial Monitor does send the necessary signal, so as a workaround we can open the port in Serial Monitor to get the communication started, then open the port in Hairless MIDI<->Serial Bridge:

  1. Select "(Not Connected)" from the "Serial port" menu in the Hairless MIDI<->Serial Bridge window.
    This is necessary because only one application can have the port open at a time, so we must close the port in Hairless MIDI<->Serial Bridge before opening it in Serial Monitor.
    I found that the alternative approach of using the "Serial<->MIDI Bridge On" checkbox can cause the Hairless MIDI<->Serial Bridge application to crash.
  2. Select the serial port of the Nano ESP32 from Arduino IDE's Tools > Port menu.
  3. Select Tools > Serial Monitor from the Arduino IDE menus.
    The "Serial Monitor" will open in the bottom panel of the Arduino IDE window.
  4. Click the X icon on the "Serial Monitor" tab in the bottom panel of the Arduino IDE window to close the tab:
    image
    This is necessary to allow the port to be opened by Hairless MIDI<->Serial Bridge
  5. Select the port of the board from the "Serial port" menu in the Hairless MIDI<->Serial Bridge window.

You should now see the MIDI messages being received by Hairless MIDI<->Serial Bridge as expected.

It is probably irrelevant. Please disregard it for now.

But you don't use it for your MIDI output:

Serial1 ist NOT the USB connection to your PC.

1 Like

I missed this. @jammond the information I shared is specific to using the USB CDC serial port of the Nano ESP32 (Serial), which is what I believe you are intending to do. It is not relevant when using the hardware UART on pins 0 and 1 (Serial1), which is what your sketch code is actually doing now.

N.B.

This is NOT an error, it is a hint, because Hairless MIDI tries to do some latency tricks if it detects an FTDI driver ( you must read everything in the output carefully :wink: )

1 Like

Thanks @ptillisch and everyone else for helping.

The procedure from @ptillisch (marked as solved - Post 8) has fixed it. I've confirmed it with this simple Hairless test sketch, which blinks a C note:

void setup() {
  Serial.begin(115200); // Set the baud rate to match Hairless MIDI
}

void loop() {
  // Send a test MIDI Note On message (Middle C, velocity 127)
  Serial.write(0x90); // Note On command (Channel 1)
  Serial.write(60);   // Note number (Middle C)
  Serial.write(127);  // Velocity (max)
  delay(1000);        // Wait 1 second
  
  // Send a test MIDI Note Off message (Middle C, velocity 0)
  Serial.write(0x80); // Note Off command (Channel 1)
  Serial.write(60);   // Note number (Middle C)
  Serial.write(0);    // Velocity (min)
  delay(1000);        // Wait 1 second
}

I can verify this is the problem, because I've tried to run the sketch without @ptillisch's procedure, and with, and Hairless only receives data when I did it.

Also, to confirm, I was always trying to send the midi information down via the Nano USB port, to my PC. I just got side tracked with other solutions as I couldn't figure this out, so please ignore my dodgy code.

I can now go back to learning about code, libraries, circuits etc, using my midi controller project.

I've learned about the TX pin today. Is it just better to use the TX pin, and solder up a MIDI 5 DIN plug? To avoid Hairless all together? (I have an audio interface).

Thanks!

I was hoping one of the other forum helpers who is knowledgeable about MIDI would respond, but I'll go ahead and throw in my opinion since nobody did:

I think it is better to use the hardware UART.

The primary reason I think that will be better is that the need to perform the Serial Monitor opening hack just to get the communication started is sure to be very annoying in any real MIDI project. You could definitely improve on it by finding a way to automate the opening and closing of the serial port (e.g., a Python script) so that you don't need to manually do it via Serial Monitor, but it is still a hack in the end.

The secondary reason is that, sadly, there hasn't been any development on the Hairless MIDI<->Serial Bridge application for six years and the developer even archived the project's GitHub repository a year ago (which is a signal that they have permanently abandoned work on the project). The application surely works fine as-is for many people, and that is great, but we can't hope for any fixes for problems like the one with the Nano ESP32 and even applications that work perfectly today tend to stop working at some point in the future if unmaintained as they become incompatible with newer versions of the operating system or hardware. So it is best to avoid a dependence on this application if you can avoid it. Unfortunately I haven't found a free open source equivalent replacement.


Since the time of my last reply here, I did a bit more investigation into the problem of Hairless MIDI<->Serial Bridge not starting the communication from the Nano ESP32 (I have also experienced this with the UNO R4 Minima and UNO R4 WiFi boards). I'll share my findings here in case they will be of interest to anyone who has a similar problem and finds this forum topic during their research.

I found I was able to reproduce the behavior of the communication not starting when using the PuTTY terminal emulator by configuring its Connection > Serial > Flow control setting to "DSR/DTR" before opening the Nano ESP32 board's port in a terminal session. The behavior did not occur when I used any of PuTTY's other Flow control options. I got the same results when I tried it with a couple of other standalone terminal programs.

This would imply that the problem could be fixed via the "Flow Control" setting in Hairless MIDI<->Serial Bridge. However, the fault still occurs no matter what you select from that setting (and in fact it doesn't even offer a "DSR/DTR" equivalent).

Why would you use Hairless with a Nano ESP32? It supports Midi over USB natively. See e.g. Control Surface: MIDI over USB.

@ptillisch Thank you for your detailed reply - it's good to know that the Hairless midi software is no longer developed/maintained. I see, a risky game to rely on it! UART looks like a good way to go if i ever need to connect directly to a 5 pin midi socket.

@PieterP Midi over USB is supported, okay that's great to hear! Short answer - the reason I was using Hairless midi was because I'm picking up Arduino again after about an 8 year break. Back then I was learning from absolute beginner stage (still am) and I was using Hairless to get signals into audio software.

I bought the Nano ESP32 and just carried on using those old workflows.

I'm going to read the ESP32 documentation before doing any more work on my projects!

Thanks all.
John

1 Like