Beginner project: MIDI, analog pot turned into MIDI CC, MIDI merge with incoming MIDI

Status
Not open for further replies.
And that code is pretty much what I was doing but will keep my mouth shut before I try it.

Good thing I saved all my stuff before reboot because every darn sketch I'd worked at for the last week or so had it's Whatever.ino deleted from its directory afterwards.
.
Will look at again after dark. I have a T2 with 3 buttons and two pots and Din Midi and use it as a workshop Controller/Merger/usb-Midi interface.

With Carlos's query in mind, I'd ducked into it's code as a refresher as I'd left a choice of lines to comment in or out for different modes and had worked out two different ways of doing Merge.
 
Sound like you've got way more DIN MIDI experience than I do... I have a Sparkfun MIDI board somewhere -- 5 volt only version I think but as I found my old T2.0 that would be perfect. Maybe I need to experiment a bit before I give out any more DIN MIDI advice.

Thanks for keeping this thread on track. :)

Do let us know if you get a chance to test this code (or fix it should it need it).
 
Hello again MatrixRat and oddson!
I have inserted the teensy into the footcontroller. The teensy gets the MIDI output from the footcontroller at TTL level (74LS05).
This is how I did it: I de-soldered the 220R resistor (that goes to DIN jack) at the inverter, pulled this end of the resistor out of its PCB hole.
I soldered a wire from the free hole in the PCB (the 74LS05 output) to pin 7 of the teensy which is serial receive (RX). Added a 220R pull-up resistor
Then I soldered a wire from pin 8 of the teeny which is serial transmit (TX) to the free leg of the 220R resistor and isolated it with shrink tube.

td_libs_MIDI_sch.gif
The follwoing problems still remain:

  • The teensy does send MIDI CC16 0-127 via USB-MIDI to MIDI-OX successfully, but NO programm changes (MIDI THRU)
  • The teensy does not send any MIDI via DIN at all, at least the MIDI devices connected do not react.


This is my code, I deleted any reference to digital buttons with the help of the compiler. Hope I didn't make any mistakes while deleting:

/* Use arrays to manage lists of knobs/pots and pushbuttons.

By Leif Oddson
https://forum.pjrc.com/threads/45376

This more complex example demonstrates how to use arrays to
manage a larger number of inputs, without duplicating your
code for every signal.

You must select MIDI from the "Tools > USB Type" menu

This example code is in the public domain.
*/


//************LIBRARIES USED**************
// include the ResponsiveAnalogRead library for analog smoothing
#include <MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
#include <ResponsiveAnalogRead.h>

//usbMIDI.h library is added automatically when code is compiled as a MIDI device

// ******CONSTANT VALUES********
// customize code behaviour here!
const int channel = 1; // MIDI channel
const int A_PINS = 6; // number of Analog PINS


// define the pins you want to use and the CC ID numbers on which to send them..
const int ANALOG_PINS[A_PINS] = {A0};
const int CCID[A_PINS] = {16};



//******VARIABLES***********
// a data array and a lagged copy to tell when MIDI changes are required
byte data[A_PINS];
byte dataLag[A_PINS]; // when lag and new are not the same then update MIDI CC value


//************INITIALIZE LIBRARY OBJECTS**************
// not sure if there is a better way... some way run a setup loop on global array??
// use comment tags to comment out unused portions of array definitions

// initialize the ReponsiveAnalogRead objects
ResponsiveAnalogRead analog[]{
{ANALOG_PINS[0],true},
{ANALOG_PINS[1],true},
{ANALOG_PINS[2],true},
{ANALOG_PINS[3],true},
{ANALOG_PINS[4],true},
{ANALOG_PINS[5],true}/*,
{ANALOG_PINS[6],true},
{ANALOG_PINS[7],true},*/
};



//************SETUP**************
void setup() {
MIDI.begin(MIDI_CHANNEL_OMNI);
}


//************LOOP**************
void loop() {
getAnalogData();
while (usbMIDI.read()) {
// controllers must call .read() to keep the queue clear even if they are not responding to MIDI
}
}


//************ANALOG SECTION**************
void getAnalogData(){
for (int i=0;i<A_PINS;i++){
// update the ResponsiveAnalogRead object every loop
analog.update();
// if the repsonsive value has change, print out 'changed'
if(analog.hasChanged()) {
data = analog.getValue()>>3;
if (data != dataLag){
dataLag = data;
usbMIDI.sendControlChange(CCID, data, channel);
}
}
}
}
 
Try this.

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

MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);

const int channel = 1; // MIDI channel
const int A_PINS  = 1; // number of Analog PINS
const int ANALOG_PINS[A_PINS] = {A0};
const int ANALOG_CCID[A_PINS] = {16};

byte data[A_PINS];
byte dataLag[A_PINS]; // when lag and new are not the same then update MIDI CC value

ResponsiveAnalogRead analog[]
{
  {ANALOG_PINS[0],true},
}; 

//************SETUP**************
void setup() {
  MIDI.begin(MIDI_CHANNEL_OMNI); 
}

//************LOOP**************
void loop() {
       mergeIncoming();
       getAnalogData();
            }
void mergeIncoming()
        {
   if (MIDI.read())
            {
        MIDI.send(MIDI.getType(),MIDI.getData1(),MIDI.getData2(),MIDI.getChannel());
            }
        }
//************ANALOG SECTION**************
void getAnalogData(){  
  for (int i=0;i<A_PINS;i++){
    // update the ResponsiveAnalogRead object every loop
    analog[i].update(); 
    // if the repsonsive value has change, print out 'changed'
    if(analog[i].hasChanged()) {
      data[i] = analog[i].getValue()>>3;
      if (data[i] != dataLag[i]){
        dataLag[i] = data[i];
        MIDI.sendControlChange(ANALOG_CCID[i], data[i], channel);
      }
    }
  }
}

Compiled, but not tested.
 
Thanks MatrixRat for your code,
program changes only get through intermittently before I touch the expression pedal which kind of freezes the teensy thru function. I get undefined status on CC and MIDI notes instead of MIDI program changes in MIDI-OX once in a while.
I used my CME U2MIDI USB-MIDI interface connected to the MIDI thu port of my Roland GP8. When I move the expression pedal which is defined as master volume on the GP8, there is one second of silence before the volume comes up exactly the same level as before ... I used audio to test as well.
I have to say that vintage MIDI equipment from the late 80s doesn't like too much MIDI data too fast ...

What's funny though is that the MIDI footcontroller is incapable of sending MIDI Note off data!
Maybe it would be helpful to filter/reduce the MIDI input to program changes only before merging with MIDI CC ...
 
Last edited:
I just tested it and you're totally right that Thru gets blocked when when I move the pot however all incoming Midi messages are passed thru correctly so perhaps the T2's RX is not getting a clean signal.

I've had faulty optocouplers, power supply issues and the circuit cause trouble here.

A T2 and the Midi I/O hardware takes very little current and the average wall wart can give you pretty dirty +5v when lightly loaded and cause Midi read errors.

As for Merge, I do all that in a software patchbay and don't really know how to do it in Teensy code yet but I think that filtering what you want might be a good place to start.

The good thing is that you're starting to get results.
 
Hi MatrixRat,
the incoming signal should be clean since it comes from the output of the footcontroller that used to work with my equipment, it's the output of a 74LS05 inverter plus 220R pull-up resistor, no optocoupler required because T2 and footcontroller share the same power supply (7805 with proper electrolytic and ceramic caps) so the VCC should be clean.

I realized the 7805 voltage regulator got pretty hot when the teensy was connected to my laptop via USB to program it. It was because voltage regulators do not like voltage at their outputs with no input voltage present (While programming I disconnected the footcontroller from my device that usually supplies DC for the footcontroller). So I swapped the 7805 for a fresh one and included a reverse diode across input and output of the voltage regulator.
I am going to sort this out and will come back here soon.
 
Hi MatrixRat,
you are right! The malfunction is caused by an unstable power supply or too much current draw.
The footcontroller is powered by a Roland GP8 which supplies unregulated 10VDC to the controller via a special cable (MIDI and DC). In the original Roland controller FC100 (Current draw 100mA max.) the VCC is regulated to +5VDC by a 7805 voltage regulator. Incidentally, within the Roland GP8 the 10VDC also supply the display and a 7805 voltage regulator to create 5VDC to supply MIDI and the digital circuits.

Now the alternative footcontroller that I want to modify with the T2 draws a much higher current than the orginal Roland footcontroller. Whooping 170mA! Whenever I connect the alternative footcontroller plus T2 to the Roland GP8, the display of the Roland GP8 gets darker. That's when I got out my multimeter.

The unreglated DC supply of originally 10V now drops down to below 7.5V. That is too low to operate a standard 7805 voltage regulator safely. So both the alternative footcontroller plus T2 and the receiving device do not see clean 5VDC and may create artefacts both sending and receiving.

I'm telling you all that in detail because I've got a hunch that your code would work in a normal environment. At least that's what I saw in MIDI-OX occasionally. Thanks for your coding!

So I need to lower the current draw somehow. I'm going to replace those vintage 80s 7segment LED numerical display in the alternative footcontroller with a modern high-effiency one that draws significantly less current.

Does the T2 draw less current with a supply voltage of 3.3V?
Can such a T2 at 3.3V operate safely in a TTL 5V environment?​

Once I've accomplished that I'll get back to you.
 
Last edited:
Cool. I had another look at that code and I gotta do a keyboard faceplant coz I'm darned if it works like I thought it did. Sorry for my bad.

Couple of thoughts:-

I've replaced many more 74LS05 to fix stuff than any other 74LSXX. Most common fail = noisy output. Good to eyeball it with a CRO. Also, the LS05 does not need to sink the full 20 or so Ma for the Led in the Opto so you might play with 1k0 or 1k5.

High efficiency Leds produce lots more light but may not draw less current unless you work at it.
 
I have ordered low drop voltage regulators 5V and some high efficiency 7 segment LEDS. I am going to raise the common anode resistors of the LED significantly. I hope these measures will lower the current draw so that there are no more power supply interferences.
 
Hello MatrixRat,
I'm back and I have to say you have been quite right.
I replaced the voltage regulator and 7-segment LEDs and current dropped considerably so that the unregulated input DC rose to healthy 8 volts.
The CC value changes as intended and is received. I can see the changing parameters in the display of my trusty Intellifex.
The program changes, however, of the original foot controller are not safely transmitted, once in a while the 90s Intellifex reacts, the 1987 Roland GP-8 doesn't.
(My basic motivation is to make great sounding vintage equipment with MIDI more user-friendly)
The pull-up resistor of the output of the foot controller (those pesky 7405) is 1K right now, I'm trying a 1k5 now. ... Doesn't work. I try find some spare 74LS05 here ...
Desoldered the old 74LS05 and put in a precision socket. No spares here right now, so I ordered a few new 74LS05.
 
Last edited:
Have just been investigating Midi input and output circuitry on the testbench looking at current, voltage, optocouplers etc. and I must say that it's a bad idea to increase the pullup on the 74LS05 output.

Any chance you might post a picture or an accurate schematic of your setup?
 
That‘s the schematics of the original foot controller. I highlighted the MIDI out path. X marks the spot where I inserted the Teensy. I desoldered the original R2 220R and soldered wires into each PCB hole. I just connected pin 8 of 74LS05 (plus 1k pullup) to Pin 7 (RX) of the Teensy 2.0 using one wire. (No optocoupler required since both devices share the same power supply.) Then I connected Pin 8 (TX) to a 220R resistor to Pin 2 of the DIN MIDI out jack using the PCB hole of the original R2 220R. The MIDI out is proprietary to Ibanez and includes phantom power to the foot controller.
I double-checked the wiring. It should be correct since MIDI CC16 is transmitted. It‘s the MIDI merge of the MIDI program changes that does not work.
541032D8-1765-48FD-915C-F6F526900600.jpg
F9E14896-89D7-498D-ACE2-B0BC45959D66.jpeg
 
Last edited:
Ok. Try dropping the pullup to 220R, then insert 150R from 74LS05 pin 8 to Teensy RX pin at the Teensy end.

It's past my bedtime and I've been writing code all day so will hook up the hardware and play with that Merge code in the morning.
 
Hi Carlos. Sorted the merge code and added ProgramChange filter. Works much better than previous attempt.

Code:
///************LIBRARIES USED**************
#include <MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);

// include the ResponsiveAnalogRead library for analog smoothing
#include <ResponsiveAnalogRead.h>

// ******CONSTANT VALUES******** 
const int channel = 1; // MIDI Out channel
const int A_PINS = 1; // number of Analog PINS

// define the pins you want to use and the CC ID numbers on which to send them..
const int ANALOG_PINS[A_PINS] = {A6};
const int CCID[A_PINS] = {16};

//******VARIABLES***********
// a data array and a lagged copy to tell when MIDI changes are required
byte data[A_PINS];
byte dataLag[A_PINS]; // when lag and new are not the same then update MIDI CC value

//************INITIALIZE LIBRARY OBJECTS**************
ResponsiveAnalogRead analog[]{
  {ANALOG_PINS[0],true}}; 

//************SETUP**************
void setup() {
  MIDI.begin(MIDI_CHANNEL_OMNI); 
  MIDI.setHandleControlChange(myProgramChange); //Telling MIDI.read() we want this message
  MIDI.turnThruOff();
}

//************LOOP**************
void loop() {
    MIDI.read(); // myProgramChange is called if we have incoming ProgramChange.
    getAnalogData();
}
//************  Processs incoming ProgramChange  **************
void myProgramChange(byte channel, byte program)
  {
    if(channel == 1) // Only listening to Ch 1
      {
      MIDI.sendProgramChange(program,channel); //Send it out
      }
  }

//************ANALOG SECTION**************
void getAnalogData(){  
  for (int i=0;i<A_PINS;i++){
    // update the ResponsiveAnalogRead object every loop
    analog[i].update(); 
    // if the repsonsive value has change, print out 'changed'
    if(analog[i].hasChanged()) {
      data[i] = analog[i].getValue()>>3;
      if (data[i] != dataLag[i]){
        dataLag[i] = data[i];
        MIDI.sendControlChange(CCID[i], data[i], channel);
      }
    }
  }
}
 
Hi everyone, hi MatrixRat,

I have installed the new 74LS05. If I bypass the T2 Midi in and out , program changes are sent and the devices connected change their patch numbers accordingly. Line check successful!

Once the T2 is inserted it neither transmits program changes (Midi merge) nor does it generate Midi CC16 anymore.

Midi in: 220R pull-up, the 120R series resistor between pull-up and T2 RX pin 7
Midi out: 220R series resistor at pin 8 TX

There was only one code that worked creating MIDI CC, I need to find out which one it was ...
 
Hi Carlos.

I'm working on a merger - controller for a Sequencer that runs on a Mega and it's pretty easy for me to setup a direct TX RX connect with a T2 to play with the idea.

Currently, Midi connections are managed with software patchbay but the plan is to stick it in one box so I wanna get direct connect working.

I'm happy to play with it and look at it with the CRO but have other work and will be a day or two.
 
I don't know if the baud rate should be set for Serial1?
#include <MIDI.h>

MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);

void setup() {
MIDI.begin(MIDI_CHANNEL_OMNI);
Serial1.begin(31250);
 
Hi Carlos11.

The Midi library sets up the baudrate.

Been playing with direct hookup eg. Midi keyboard > Mega2560 (as an Arpeggiator) TX pin to Teensy2 RX, then Teensy2 TX to Din midi out >Roland synth.

The T2 was configured as Merger/Controller with with 4 pots coded to send CC# 1,4,5 and 7 and an encoder to send ProgramChange, all on Channel 1. Everything works.

Heres the schematic:

Carlos11.jpg


And here's the code for the T2 with one pot:
Code:
///************LIBRARIES USED**************
#include <MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
#include <ResponsiveAnalogRead.h>

// ******CONSTANT VALUES******** 
const int channel = 1; // MIDI Out channel
const int A_PINS = 1; // number of Analog PINS

// define the pins you want to use and the CC ID numbers on which to send them..
const int ANALOG_PINS[A_PINS] = {A9}; //            Need to change this 
const int CCID[A_PINS] = {1};  //CC for Modulation. Need to change this

//******VARIABLES***********
// a data array and a lagged copy to tell when MIDI changes are required
byte data[A_PINS];
byte dataLag[A_PINS]; // when lag and new are not the same then update MIDI CC value

//************INITIALIZE LIBRARY OBJECTS**************
ResponsiveAnalogRead analog[]{{ANALOG_PINS[0],true}}; 

//************SETUP**************
void setup() {
  MIDI.begin(MIDI_CHANNEL_OMNI); 
}

//************LOOP**************
void loop() {
    MIDI.read();
    mergeIncoming();
    getAnalogData();
}
//*************Merge
void mergeIncoming()
        {
   if (MIDI.read())
            {
        MIDI.send(MIDI.getType(),MIDI.getData1(),MIDI.getData2(),MIDI.getChannel());
            }
        }
//************ANALOG SECTION**************
void getAnalogData(){  
  for (int i=0;i<A_PINS;i++){
    // update the ResponsiveAnalogRead object every loop
    analog[i].update(); 
    // if the repsonsive value has change, print out 'changed'
    if(analog[i].hasChanged()) {
      data[i] = analog[i].getValue()>>3;
      if (data[i] != dataLag[i]){
        dataLag[i] = data[i];
        MIDI.sendControlChange(CCID[i], data[i], channel);
      }
    }
  }
}


Note that no Midi filtering is used so Merge passes everything thru. I experimented with various filters which had no effect on throughput and don't see the need.

Hope you get it working.

Cheers.
 
Hi MatrixRat,

thanks a million! Your code works like a charm! I really appreciate your effort and your contribution!

I had to add a 1K pull-up resistor in front of the 420R series resistor, maybe because of the open collector output of the 704LS05.
I have got two devices in the MIDI chain, as I said. The Roland GP-8 provides the DC power via a propietary RRC cable, the next device is Rocktron Intellifex. The Intellifex now changes patches and parameters via CC accordingly, but the Roland GP-8 wouldn't react.

It was not until I compared my modded Ibanez footcontroller with the much larger Roland FC-100 foot controller that originally came with the Roland GP-8 in 1987 and hooked it up to MIDI-OX directly via my USB MIDI interface.

The Roland FC-100 footcontroller sends "Active Sensing" every 300ms to tell the RoLand GP-8 that there still is a footcontroller connected to the device. If there is no more "Active Sensing" anymore, the GP-8 won't change patches via this proprietary RRC MIDI input. This "active sensing" is not used anymore AFAIK.

Which code do I have to add to your code to send active sensing every 300ms?

https://forum.arduino.cc/index.php?topic=40882.0http://
 
Great news! Thanks for starting this thread coz I've learned something too.
Absolutely right that open collector needs a pullup.

A note about the 420R. My dodgy handwriting, I meant 470R although actually used 560R
The 420R is there as a safeguard in case of a code "Accident" eg the T2's RX pin can be configured as an output HIGH and let's imagine the 74LS05 tries to pull it LOW, something will break so serves as a current limit.

Also useful to clean up interference. Say you have a long wire for the TX -RX connection and the whole thing is stuck in a box with a noisy display. The resistor goes as close as possible to the T2's RX pin.

For activeSensing, I guess you'd use a timer so you might start by adding a variable to keep track of time :-

Code:
int previousTimer;

and a function:-
Code:
//******** Send Active sensing

void doActiveSensing()
{
   int currentTimer = millis();
      if(currentTimer - previousTimer > 300)// If 300Ms has passed
        {
          // Reset the counter
          previousTimer = currentTimer;
          // And send ActiveSensing
          // I think you use MIDI.sendRealTime();
          // and you need to find and put the number for activeSensing inside the brackets.
        }
}

and calling it in loop() by adding

Code:
doActiveSensing();

Which leaves you some homework. I've gotta go move some rocks. Cheers.
 
Hi MatrixRAT,
I actually used a 470R, because I didn't have a 420R.
Right now I can't compile this code, I haven't understood the overall structure of a sketch yet:
Code:
///************LIBRARIES USED**************
#include <MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
#include <ResponsiveAnalogRead.h>

// ******CONSTANT VALUES******** 
const int channel = 1; // MIDI Out channel
const int A_PINS = 1; // number of Analog PINS

// define the pins you want to use and the CC ID numbers on which to send them..
const int ANALOG_PINS[A_PINS] = {A0}; //            Need to change this 
const int CCID[A_PINS] = {16};  //CC for Modulation. Need to change this

//******VARIABLES***********
// a data array and a lagged copy to tell when MIDI changes are required
byte data[A_PINS];
byte dataLag[A_PINS]; // when lag and new are not the same then update MIDI CC value

//************INITIALIZE LIBRARY OBJECTS**************
ResponsiveAnalogRead analog[]{{ANALOG_PINS[0],true}}; 

int previousTimer;

//************SETUP**************
void setup() {
  MIDI.begin(MIDI_CHANNEL_OMNI); 
}

//************LOOP**************
void loop() {
    MIDI.read();
    mergeIncoming();
    getAnalogData();
    doActiveSensing();
}
//*************Merge
void mergeIncoming()
        {
   if (MIDI.read())
            {
        MIDI.send(MIDI.getType(),MIDI.getData1(),MIDI.getData2(),MIDI.getChannel());
            }
        }
//************ANALOG SECTION**************
void getAnalogData(){  
  for (int i=0;i<A_PINS;i++){
    // update the ResponsiveAnalogRead object every loop
    analog[i].update(); 
    // if the repsonsive value has change, print out 'changed'
    if(analog[i].hasChanged()) {
      data[i] = analog[i].getValue()>>3;
      if (data[i] != dataLag[i]){
        dataLag[i] = data[i];
        MIDI.sendControlChange(CCID[i], data[i], channel);
      }
    }
//******** Send Active sensing
void doActiveSensing()
     {
   int currentTimer = millis();
      if(currentTimer - previousTimer > 300)// If 300Ms has passed
        {
          // Reset the counter
          previousTimer = currentTimer;
          // And send ActiveSensing
          MIDI.sendRealTime(0xFE);
          // and you need to find and put the number for activeSensing inside the brackets.
        }
      }
  }
}
 
Dude, so close. I'm trying not to do all the hard work here. What happened is that when you pasted the void doActiveSensing() function into the sketch, it got inserted in between some curly braces and needed to have been inserted after the very last one.
 
Sorry, as I said, I'm a complete beginner to programming and have been trying to find a programming tutorial covering the braces and fomatting and so on.
Thanks for your advice!
 
Last edited:
Status
Not open for further replies.
Back
Top