Need help converting usbMIDI code to Serial MIDI

Hello,

The device that my Teensy 4.1 will be transmitting MIDI to has trouble taking anything except serial midi via UART. Not entirely sure how it works, but serial-only is the solution. I definitely need help with this one.

So what I have below is a modified version of the Mini Untztrument code, with the Trellis Test startup flash stuffed into it. It's a horrible beast of code and it's pretty telling of where I'm at. I'm aware that there are ways of condensing the code with the pots, but the logic is still a little over my head. Either way, it shows up in Midi Monitor when used with usbMIDI so all of the physical components definitely work.

In a previous incarnation of this sketch, I tried to add the MIDI library and create a default instance and build a bunch of duplicates of the analog reads around it. It was really ugly, worked for a second, and froze the controller. If I recall correctly, the MIDI and usbMIDI libraries are separate from each other and can work independently if used in the same sketch. That's a shot in the dark though, all of the extra midi code has been removed.

The device I'm plugging into , the Axoloti, has only responded to the midi output test found on the Teensy MIDI library page. I tried to program a single pot into that sketch and got no response, but likely because I programmed it wrong.

Thank you, and without further ado, the code:

#include <Wire.h>
#include <Adafruit_Trellis.h>
 
#define LED     13 // Pin for heartbeat LED (shows code is working)
#define CHANNEL 1 

Adafruit_Trellis trellis;
 
uint8_t       heart        = 0;  
unsigned long prevReadTime = 0L; 
uint8_t       pot1;
uint8_t       pot2;
uint8_t       pot3;
uint8_t       pot4;
uint8_t       pot5;
uint8_t       pot6;
uint8_t       pot7;
uint8_t       pot8;
uint8_t       pot9;
uint8_t       pot10;
uint8_t       pot11;
uint8_t       pot12;    
 
uint8_t note[] = {
  60, 61, 62, 63,
  56, 57, 58, 59,
  52, 53, 54, 55,
  48, 49, 50, 51
};
 
void setup() {
  
  pinMode(LED, OUTPUT);

 
  for (uint8_t i=0; i<60; i++) {
    trellis.setLED(i);
    trellis.writeDisplay();    
    delay(50);
  }

  for (uint8_t i=0; i<60; i++) {
    trellis.clrLED(i);
    trellis.writeDisplay();    
    delay(50);
  } // Pass I2C address
#ifdef __AVR__ // this part is optional in the code. not sure if there's any consequence to leaving it
  //TWBR = 12;
# endif
//  trellis.clear();
//  trellis.writeDisplay();
  pot1 = map(analogRead(24), 0, 1023, 0, 127);
  pot2 = map(analogRead(25), 0, 1023, 0, 127);
  pot3 = map(analogRead(26), 0, 1023, 0, 127);
  pot4 = map(analogRead(27), 0, 1023, 0, 127);
  pot5 = map(analogRead(38), 0, 1023, 0, 127);
  pot6 = map(analogRead(39), 0, 1023, 0, 127);
  pot7 = map(analogRead(40), 0, 1023, 0, 127);
  pot8 = map(analogRead(41), 0, 1023, 0, 127);
  pot9 = map(analogRead(14), 0, 1023, 0, 127);
  pot10 = map(analogRead(15), 0, 1023, 0, 127);
  pot11 = map(analogRead(16), 0, 1023, 0, 127);
  pot12 = map(analogRead(17), 0, 1023, 0, 127);
  usbMIDI.sendControlChange(22, pot1, CHANNEL);
  usbMIDI.sendControlChange(23, pot2, CHANNEL);
  usbMIDI.sendControlChange(24, pot3, CHANNEL);
  usbMIDI.sendControlChange(25, pot4, CHANNEL);
  usbMIDI.sendControlChange(26, pot5, CHANNEL);
  usbMIDI.sendControlChange(27, pot6, CHANNEL);
  usbMIDI.sendControlChange(28, pot7, CHANNEL);
  usbMIDI.sendControlChange(29, pot8, CHANNEL);
  usbMIDI.sendControlChange(30, pot9, CHANNEL);
  usbMIDI.sendControlChange(31, pot10, CHANNEL);
  usbMIDI.sendControlChange(32, pot11, CHANNEL);
  usbMIDI.sendControlChange(33, pot12, CHANNEL);
}
 
void loop() {
  unsigned long t = millis();
  if((t - prevReadTime) >= 20L) { // 20ms = min Trellis poll time
    if(trellis.readSwitches()) {  
      
      for(uint8_t i=0; i<16; i++) {
        if(trellis.justPressed(i)) {
          usbMIDI.sendNoteOn(note[i], 127, CHANNEL);

          
          trellis.setLED(i);
        } else if(trellis.justReleased(i)) {
          usbMIDI.sendNoteOff(note[i], 0, CHANNEL);

          trellis.clrLED(i);
        }
      }
      trellis.writeDisplay();
    }
    uint8_t newpot1 = map(analogRead(24), 0, 1023, 0, 127);
    if(pot1 != newpot1) {
      pot1 = newpot1;
      usbMIDI.sendControlChange(22, pot1, CHANNEL);

    }
    uint8_t newpot2 = map(analogRead(25), 0, 1023, 0, 127);
    if(pot2 != newpot2) {
      pot2 = newpot2;
      usbMIDI.sendControlChange(23, pot2, CHANNEL);

    }
    uint8_t newpot3 = map(analogRead(26), 0, 1023, 0, 127);
    if(pot3 != newpot3) {
      pot3 = newpot3;
      usbMIDI.sendControlChange(24, pot3, CHANNEL);

    }
    uint8_t newpot4 = map(analogRead(27), 0, 1023, 0, 127);
    if(pot4 !=newpot4) {
      pot4 = newpot4;
      usbMIDI.sendControlChange(25, pot4, CHANNEL);

    }
        uint8_t newpot5 = map(analogRead(38), 0, 1023, 0, 127);
    if(pot5 != newpot5) {
      pot5 = newpot5;
      usbMIDI.sendControlChange(26, pot5, CHANNEL);

    }
    uint8_t newpot6 = map(analogRead(39), 0, 1023, 0, 127);
    if(pot6 != newpot6) {
      pot6 = newpot6;
      usbMIDI.sendControlChange(27, pot6, CHANNEL);

    }
    uint8_t newpot7 = map(analogRead(40), 0, 1023, 0, 127);
    if(pot7 != newpot7) {
      pot7 = newpot7;
      usbMIDI.sendControlChange(28, pot7, CHANNEL);

    }
    uint8_t newpot8 = map(analogRead(41), 0, 1023, 0, 127);
    if(pot8 !=newpot8) {
      pot8 = newpot8;
      usbMIDI.sendControlChange(29, pot8, CHANNEL);

    }
    uint8_t newpot9 = map(analogRead(14), 0, 1023, 0, 127);
    if(pot9 != newpot9) {
      pot9 = newpot9;
      usbMIDI.sendControlChange(30, pot9, CHANNEL);

    }
    uint8_t newpot10 = map(analogRead(15), 0, 1023, 0, 127);
    if(pot10 != newpot10) {
      pot10 = newpot10;
      usbMIDI.sendControlChange(31, pot10, CHANNEL);

    }
    uint8_t newpot11 = map(analogRead(16), 0, 1023, 0, 127);
    if(pot11 != newpot11) {
      pot11 = newpot11;
      usbMIDI.sendControlChange(32, pot11, CHANNEL);

    }
    uint8_t newpot12 = map(analogRead(17), 0, 1023, 0, 127);
    if(pot12 !=newpot12) {
      pot12 = newpot12;
      usbMIDI.sendControlChange(33, pot12, CHANNEL);

    }
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
  }
  while(usbMIDI.read());

The device that my Teensy 4.1 will be transmitting MIDI to has trouble taking anything except serial midi via UART. Not entirely sure how it works, but serial-only is the solution.

The Teensy 4.1 has several serial ports, I can’t remember exactly how many as I am away from my desk. You need to use one of these ports and send / read MIDI from it.
First off you need to create the MIDI input / output interface like found in various MIDI shields.
Like this DIY project http://www.thebox.myzen.co.uk/Hardware/MIDI_Shield.html
That is designed for a 5V system so there are minor changes needed for a 3V3 system. So instead of going to 5V the output of the opto isolator goes to 3V3. The output signal needs boosting up to 5V without inversion before applying to the base resistor of the PNP transistor.

The issue doesn't seem to be the physical connection as the MIDI test script worked properly. The axoloti has a built in oscilloscope that responded to the notes coming from the Teensy.

The UART connections didn't respond when I used them in conjunction with the 47 ohm resistors (no optocoupler because I'm only doing MIDI out from the teensy, not MIDI in) To add to that, the conclusion on the Axoloti forum is that a direct connection without resistors works just fine so other people have made it work with the configuration I'm using (Teensy pin 1, the first TX pin to Axoloti RX pin) since the boards are both 3.3v logic. I'm using a ground cable at the moment since it's the one point of connection between the two boards. However, if there's something that I'm not aware of and I absolutely should be using the configuration you mentioned I'll try it again.

So I can (tentatively) prove that the physical setup works, when the code works. The problem is the actual code itself. Notice how the one here is serial MIDI only. If I could convert the Mini Untztrument code provided above to the same format as the Teensy midi code in this post, it should work in theory. I'm just real bad at coding and need some guidance.

The reason that I need to convert this code to serial only is because I think that the Axoloti firmware hasn't had an update for several years and (warning: me talking about things I don't understand -) can't read the midiUSB configuration correctly. Like, the way that the usbMIDI packet is sent confuses the Axoloti.

This is the code that the Axoloti successfully responded to. It's the code from the Teensy MIDI library page. Thanks!

#include <MIDI.h>

MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
const int channel = 1;

void setup() {
  MIDI.begin();
}

void loop() {
  int note;
  for (note=10; note <= 127; note++) {
    MIDI.sendNoteOn(note, 100, channel);
    delay(200);
    MIDI.sendNoteOff(note, 100, channel);
  }
  delay(1000);
}

The UART connections didn't respond when I used them in conjunction with the 47 ohm resistors (no optocoupler because I'm only doing MIDI out from the teensy, not MIDI in) To add to that, the conclusion on the Axoloti forum is that a direct connection without resistors works just fine so other people have made it work with the configuration I'm using (Teensy pin 1, the first TX pin to Axoloti RX pin) since the boards are both 3.3v logic.

You need to use another UART not pins 1 and 2. The Axoloti has an opto and you must be able to pass enough current to it to get a message through. Check the current rating on the Teensy output pins, it is not an Arduino and you will probably not be able to drive enough current through.

As to your software, forget the library for the moment and simply send the MIDI message through three Serial.write calls to the port you are using at normal MIDI speeds.

Grumpy_Mike:
You need to use another UART not pins 1 and 2. The Axoloti has an opto and you must be able to pass enough current to it to get a message through. Check the current rating on the Teensy output pins, it is not an Arduino and you will probably not be able to drive enough current through.

Thank you. What does it mean then that the code I provided is already working with the specs I have? I don't mean to pry I'm just confused why I would need to build a shield or change pins if it's already working. Will there be slow damage to my board? To clarify again, I'm only using the TX pin from the Teensy, it will not be receiving any MIDI data.

What does it mean then that the code I provided is already working with the specs I have?

I don’t understand the word “specs” in this context, can you please explain what that means.

I thought your problem was sending stuff into the Axoloti.
If you say it is working then I don’t understand what your problem is.

To clarify again, I'm only using the TX pin from the Teensy, it will not be receiving any MIDI data.

Then why
From your origional code are you doing this?

  while(usbMIDI.read());

Sorry I could have been a lot more clear.

To bring it back home and clarify the problem:

  • This code is taken from an open source Adafruit project and it's set up for usbMIDI

  • I would like to edit this code from usbMIDI to serial MIDI, and send it using the Teensy TX pin to the Axo RX pin. No need for the Teensy to receive MIDI at this time.

  • The hardware connection is established and works with the Teensy test code. (by the 'specs' i meant the hardware, but it was a poor choice of words). Pictured is the Axoloti oscilloscope responding to the test sketch from Teensy. Note that the 'test sketch' is the MIDI output test from the Teensy MIDI library page, not the code that I want for my project.

  • The reason I haven't gotten any other code to work using serial MIDI is because I don't know how to program and need some help.

Screen Shot 2020-09-30 at 12.23.20 PM.png

Screen Shot 2020-09-30 at 12.23.20 PM.png

Sorry I could have been a lot more clear.

Yes I think we have a way to go before I am up to speed on what you are doing.

In the original code you posted you used the Adafruit_Trellis.h library. Do you use one and how it is it wired up to the Teensey?

Is what you want to do, is receive button presses from a Trellis and send out MIDI messages to the Axoloti?
If not exactly what do you want to what is it.

Sorry but I don't have a clear idea of what you are trying to do, but I am becoming more convinced that the last thing you want to to is to use the 'usbMIDI' library, it is just the wrong thing to do.

I tried to program a single pot into that sketch and got no response,

And what have pots got to do with the Trellis?

When I try and compile this code i get the error message
'usbMIDI' was not declared in this scope
Which is quite understandable, as the code makes no attempt to actually include this library.
So is this the code that compiles for you? You never mentioned any error messages.

Hopefully I can quickly clear up just a couple issues with a little background info...

Grumpy_Mike:
Then why
From your origional code are you doing this?

  while(usbMIDI.read());

This code is recommended in all of Teensy's USB MIDI examples. Usually it is unnecessary but harmless, for the common case where you only transmit MIDI messages to your PC.

But in cases where software is running on the PC which automatically retransmits or echos incoming MIDI messages, reading & discarding the incoming messages prevents them from using up USB buffers.

Grumpy_Mike:
When I try and compile this code i get the error message
'usbMIDI' was not declared in this scope
Which is quite understandable, as the code makes no attempt to actually include this library.

Teensyduino doesn't use a library include for this. Instead it's an option in the Tools menu. Here's a screenshot which hopefully can give you a clear picture of how it works. When this menu item is selected, the include is done automatically (much like how Arduino.h is always automatically included).

Thank you @pjrc and thank you @grumpy_mike !!!

TLDR: I got it to work!

For closure, oh my goodness you're correct about all of those things. 12 pots sending midi CC, Trellis sending noteOn/Off messages transmitting serial MIDI only to the Axoloti. For visualization, they are all inside of one enclosure that serves as a desktop synth essentially. It works like a charm now.

In case it helps anyone in the very specific circumstance of having to convert usbMIDI code to MIDI in the future, I changed all of my usbMIDI messages into MIDI messages simply by removing the "usb" in front of all of the commands so that "usbMIDI.sendControlChange" just says "MIDI.sendControlChange".

Also made sure to add the MIDI_CREATE_DEFAULT_INSTANCE();, and MIDI.begin(); where appropriate.

Thank you for all of your help.

p.s. Bonus question if anybody can answer, since the Teensy 4.1 does not have a reset pin, would it damage the board if I were to add an on/off toggle switch to the pin it receives power from? I just need a crude and simple hardware solution because the Teensy freezes the Axoloti when switching programs which is fixed with a quick reset.

Well done on getting it going.

since the Teensy 4.1 does not have a reset pin, would it damage the board if I were to add an on/off toggle switch to the pin it receives power from?

It is a bad idea to remove the power from a board that has inputs still feeding into it. These inputs can cause parasitic powering of the board and can cause latch up, meaning that nothing will work until all the power from everything is removed. This latch up has been known to cause permanent damage.

You can reduce the current drawn by an active pin connected to an unpowered board by using a series resistor, about 1K, but that will reduce the noise immunity. I think the best way would be to fit a reset push button to one of the input pins and then use the interrupt capability to restart your code. However I am sure Paul could advise you better on this matter.

You did a lot better than I thought you would, sorry for underestimating you. But after my last post I prepared an example of how you could send CC messages from those pots in a more efficient way. So I will post it here if it is of use to anyone. It compiles but I have not tested it. It might not even work on a Teensey, but if not it is designed to work on a Uno / Nano as it just uses the serial output to send MIDI. If anyone has any trouble then please say what it is.

/* Midi pot controller - Mike Cook October 2020
 *
 * ----------------- 
 * send MIDI serial data containing CC messages from a pot reading
 * 
// Arduino pin assignments
*/
// set up to use MIDI channel 1
#define midiChannel (byte)0
int potValue[12];
int lastPotValue[12];
byte potPin[] = {24, 25, 26, 27, 38, 39, 40, 41, 14, 15, 16, 17 };
byte controller[] = { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33}; // list of CC messages for each pot 
/* Note this list of controllers is a consecutive list and so it could be omitted 
*  in the reactToChange() function, by changing "controller[i]" into "i+22" and removing the above byte array.
*  The list is left here if you want to use none consecutive numbers
*/
// Start of code
void setup() {
 //  Setup serial
   Serial.begin(31250);    // MIDI speed
}

//********************* MAIN LOOP ***********************************

void loop() {
   readPots(); // read all the pots
   reactToChange(); // react if they have changed enough to warrent sending another CC
   delay(300); // well we are not doing anything else so look at pots every 300 mS remove this if you are doing other stuff
    } // end loop function
    
//********************* Functions *********************************** 

// read all the pots
void readPots(){
  for(int i = 0; i<12; i++){
     potValue[i] = analogRead(potPin[i]); // get the full pot value
  }
}

void reactToChange(){ // send a CC message if it has changed enough
  int threshold = 4;  // amount the pot has to change before sending a CC message
  for(int i = 0; i<12; i++){
     if( abs(potValue[i] - lastPotValue[i]) > threshold) { // send the CC
        lastPotValue[i] = potValue[i]; // save the previous pot read for next time
        sendMIDImessage( 0xB0, controller[i], potValue[i] >> 3); // potValue >> 3 converts to an A/D reading to between 0 and 127         
     }
  }
}

//  sends a 3 byte MIDI message
 void sendMIDImessage(char cmd, char data1, char data2) {
  cmd = cmd | char(midiChannel);  // merge channel number and command
  Serial.write(cmd);
  Serial.write(data1);
  Serial.write(data2);
}

Grumpy_Mike:
You did a lot better than I thought you would, sorry for underestimating you. But after my last post I prepared an example of how you could send CC messages from those pots in a more efficient way.

Oh no problem at all (I kind of surprised myself). In fact, this code goes straight into the device and replaces that rat-king of pots that's in there now.

Thanks for the advice with the reset/interrupt pin. That's definitely what I'm going to do. Wonderful people, all.