UNTZtrument, how to prog buttons w. midi note number, velocity, ch, latch mode

Hi, I hope to find some help here to modify my UNTZtrument 64 and im not very good with programming so please excuse me.

My goal is to control a multi-midi-channel patch on my Waldorf Blofeld, with sustaining notes to make drones with individual notes (latch mode) on different midi channels

I would like to modify the basic patch Hello World or make a patch which does the job. As it is, it functions like a keyboard, sending out midi notes on the same midi channel.

I need to be able to specify every button with a midi note number, velocity, midi-channel, and in latch mode (first press note on, second press note on)

An example could be:

Row 1: 8 notes, C major scale, Midi Ch 1
Row 2: 8 notes, C major scale, Midi Ch 2
.
.
.
Row 8: 8 notes, C major scale, Midi Ch 8

Is this easy to do ?

Im greatfull for any help

best regards Henning Flintholm

Please read the how to use this forum sticky post, it will tell you how to ask a question.

First off what is a “ UNTZtrument 64 ”
When we know that we might stand a chance of being able to say if it is easy or not.

Hi - i will try to explain what i know about this instrument.

For what i know this is a open source adafruit 64 button midi controller built with a Arduino Leonardo Board

On this side these are 2 example libs, one sequencer and a simple Hello World example. The last one is putting out midi notes, but i cant find where to edit the notenumber, midi channel or latch function.

I hope this info will help

Thanks, it looks like he note is set at this line

note = LOWNOTE + y * WIDTH + x;

So at this point you just have to put in the MIDI note number you want. It seems to be calculated from the X-Y coordinates of the key you pressed.

So for a test try putting just a simple number in here for the value of the note. Then if I am right all keys should produce the same note.

Then change it so you just

note = 30 + y ;

And all columns should be he same set of notes.

To make any note map to any key simply have a two dimensional matrix addressed by the X and Y values and fill the array with the note mapping you want.

Or try replacing the constants LOWNOTE and WIDTH with simple numbers and have a play.

What changes do you want to make to the notes?

Hi Grumpy Mike

Thank you very much for your answer.

I have tried your suggestion and it works very good and simple.

If i change LOWNOTE with a number i will get the lowest note on the top-left button and all other buttons from here will be the next notenumber

If i change the WIDTH with a number every row will start with this offset - so far so good.

My next problem here is that every button in a row will be playing succesive notenumber which will give me a cromatic scale, but it would be nice to be able to have non-succesive numbers to make e scale or intervalvs. ex. row 1:

C1, F1, G1, G#1,C2, D#2, F2, G2

Next problem is that it would be important to have diffent midi channels for each row. I can change the midi channel on this line:

#define CHANNEL 3 // MIDI channel number

but it changes midi ch for all buttons

And it would also be important to have Latch mode for the buttons and not Momentary mode

Do you think it´s possible with this sketch at all or do i need an other sketch ?


I have found some more info on the UNZTrument:

https://docs-emea.rs-online.com/webdocs/1534/0900766b815343b5.pdf

https://wikifactory.com/@botler/diy-midi-controller

https://portal.cca.edu/learning/shops/hybrid-lab/tutorials/untztrument-trellis-midi-instrument/

I also have been able to use this sketch from “Notes and Volts” making a simple Midi Bass Pedal with Arduino Uno:

I feel comfortable using this sketch and im looking for something similar, maybe a rewrite of this sketch, but im not fit in programming.

On this site i found some interesting info:

https://portal.cca.edu/learning/shops/hybrid-lab/tutorials/untztrument-trellis-midi-instrument/

Here is the sketch:

Use the code below for testing your UNTZtrument matrix rather than the code in the Trellis guide; it’s already set up for the tiled matrix.

For the HELLA UNTZtrument, change NUMTRELLIS to 8.

/***********************************************************

 This is a test example for the Adafruit Trellis w/HT16K33.

 Reads buttons and sets/clears LEDs in a loop.

 "momentary" mode lights only when a button is pressed.

 "latching" mode toggles LED on/off when pressed.

 4 or 8 matrices can be used.  #define NUMTRELLIS to the

 number in use.



 Designed specifically to work with the Adafruit Trellis

 ----> https://www.adafruit.com/products/1616

 ----> https://www.adafruit.com/products/1611



 Adafruit invests time and resources providing this

 open source code, please support Adafruit and open-source

 hardware by purchasing products from Adafruit!



 Written by Limor Fried/Ladyada for Adafruit Industries.  

 MIT license, all text above must be included in any redistribution

***********************************************************/



#include <Wire.h>

#include "Adafruit_Trellis.h"



#define NUMTRELLIS 4        // **** SET # OF TRELLISES HERE



#define MOMENTARY 0

#define LATCHING  1



#define MODE      LATCHING  // **** SET MODE HERE



Adafruit_Trellis matrix[NUMTRELLIS] = {

 Adafruit_Trellis(), Adafruit_Trellis(),

 Adafruit_Trellis(), Adafruit_Trellis()

#if NUMTRELLIS > 4

,Adafruit_Trellis(), Adafruit_Trellis(),

 Adafruit_Trellis(), Adafruit_Trellis()

#endif

};



Adafruit_TrellisSet trellis = Adafruit_TrellisSet(

 &matrix[0], &matrix[1], &matrix[2], &matrix[3]

#if NUMTRELLIS > 4

,&matrix[4], &matrix[5], &matrix[6], &matrix[7]

#endif

);



#define numKeys (NUMTRELLIS * 16)



// Connect Trellis Vin to 5V and Ground to ground.

// Connect I2C SDA pin to your Arduino SDA line.

// Connect I2C SCL pin to your Arduino SCL line.

// All Trellises share the SDA, SCL and INT pin! 

// Even 8 tiles use only 3 wires max.



void setup() {

 Serial.begin(9600);

 Serial.println("Trellis Demo");



 // begin() with the addresses of each panel.

 // I find it easiest if the addresses are in order.

 trellis.begin(

   0x70, 0x71, 0x72, 0x73

#if NUMTRELLIS > 4

  ,0x74, 0x75, 0x76, 0x77

#endif

 );



 // light up all the LEDs in order

 for (uint8_t i=0; i<numKeys; i++) {

   trellis.setLED(i);

   trellis.writeDisplay();    

   delay(50);

 }

 // then turn them off

 for (uint8_t i=0; i<numKeys; i++) {

   trellis.clrLED(i);

   trellis.writeDisplay();    

   delay(50);

 }

}



void loop() {

 delay(30); // 30ms delay is required, dont remove me!

 

 if (MODE == MOMENTARY) {

   // If a button was just pressed or released...

   if (trellis.readSwitches()) {

     // go through every button

     for (uint8_t i=0; i<numKeys; i++) {

// if it was pressed, turn it on

if (trellis.justPressed(i)) {

Serial.print("v"); Serial.println(i);

trellis.setLED(i);

} 

// if it was released, turn it off

if (trellis.justReleased(i)) {

Serial.print("^"); Serial.println(i);

trellis.clrLED(i);

}

     }

     // tell the trellis to set the LEDs we requested

     trellis.writeDisplay();

   }

 }



 if (MODE == LATCHING) {

   // If a button was just pressed or released...

   if (trellis.readSwitches()) {

     // go through every button

     for (uint8_t i=0; i<numKeys; i++) {

       // if it was pressed...

if (trellis.justPressed(i)) {

Serial.print("v"); Serial.println(i);

// Alternate the LED

if (trellis.isLED(i))

  trellis.clrLED(i);

else

  trellis.setLED(i);

       } 

     }

     // tell the trellis to set the LEDs we requested

     trellis.writeDisplay();

   }

 }

}

This sketch seem to work in latch mode. Buttons are turned on/off with each succesive press. But i doesnt send out midi. I noticed that it was´nt recogniced on my computer and saw that the line from the other sketch :

#include "MIDIUSB.h"

was missing. after adding this line the UNTXintrument was now recogniced, but it doesnt send out midi

OK time to read the rules about posting code here. Please read this:-
How to use this forum
Go back and modify that last post so you use code tabs.

I noticed that it was´nt recogniced on my computer and saw that the line from the other sketch :

#include "MIDIUSB.h"

was missing. after adding this line the UNTXintrument was now recogniced, but it doesnt send out midi

I think you are best sticking with your original modifications of the "hello world" sketch. The MIDIUSB library sends out it's messages in a different way to the direct connection's MIDI library. That is it uses a different call and its parameters are in a different order.

However your problems are the same, how to generate note numbers from key presses. Basically you have two ways to do this.

  1. Work out a series of calculations that will calculate the note number from the X-Y readings of the button press.
  2. Define a two dimensional byte array with the note numbers in, so you just select a note by using:-
note = noteArray[x][y];

Where noteArray is the array you defined.

Next problem is that it would be important to have diffent midi channels for each row. I can change the midi channel on this line:
#define CHANNEL 3 // MIDI channel number
but it changes midi ch for all buttons

Yes that does. What the #define does is simply replace a text string with the number. So every time the compiler sees "CHANNEL" it replaces it with the number 3.

And it would also be important to have Latch mode for the buttons and not Momentary mode
Do you think it´s possible with this sketch at all or do i need an other sketch ?

Yes you can do all that with this sketch.

For the channel number replace you #define macro with a variable

byte channel = 3;

By convention constants use all upper case, but now you have a variable.

Then like I said above work out a series of calculations that will calculate the channel number. In this case it is simple. If you want a different channel for each row, simply use the y coordinate to change the channel number.

channel = y +1;

Depending on what library you use you might not have to add one to this if it uses channel 0.

The latching mode is simply a matter of when you send the note off command. If, like at the moment, you send it when the button is released, you have a momentary note. To make it latched simply send the note off when the button is pressed a second time.

An example of this for a single button is shown in the IDE example "state change detection". You have to extend the idea to your situation where you have many numbers. This is not as daunting as it seems if again you use an array to hold the last value of each switch and compare with the incoming value. So you detect when a key is pressed not if it is currently being held down. You use this to "count" the number of key presses from 0 to 2. When you see a 1 in the updated count, turn the note on and when you see a 2, turn the note off and reset the count to zero.

You are pushing you coding skills here but it is not that much of a big step.

Dear Grumpy Mike

Thank you very much for you feedback and taking your time helping me :slight_smile: I think i will be able to make the changes, but i will have to spend some more time understanding your directions. I will get back when i have tried it out. best regards Flintholm

Hi again Grumpy Mike

I have now had some more time to try out some of your suggestions and i am happy that i´ve managed some of the stuff, but as you said, this will defenitly push my coding skills and i have a lot to learn.

I have managed the following:

changing the midi channels on each row

changing the startnote on each row (still chromatic)

changing the velocity (globally for all notes)

i still have to:

find out how to get the latching function - i will look into the IDE example "state change detection" later

find a way to specify the notes so the will not be chromatic - i will have to find out how to do this NoteArray

In order to make it understandable for me, i need to clarify things - Is it following correct:

The buttons in the Array are starting in the topleft corner and ascending in a normal reading direction (left to right) ?

Numbers in programming always start with a 0 (zero) for number 1 ? like midi-channel 4, the number should be 3 ?

Is the buttons X and the rows Y ?

Is the first button and first row 0 (zero) ?

Is the second button and second row 1 ?

In the Hello World example the notes are defined like this (for the chromatic ascending notenumbers):

note = LOWNOTE + y * WIDTH + x;

Should i replace this line with the NoteArray which is defined like this:

note = noteArray[x][y];

as an example i would like to have these notes on the rows (in order to just get the understanding, i just take the first 3 notes and 3 rows, and i set a global velocity and i change the midi channel as earlier suggested)

Row 1, Button 1: C2/Note-number 48 (Midi Ch 9)
Row 1, Button 2: D2/Note-number 50 (Midi Ch 9)
Row 1, Button 3: E2/Note-number 52 (Midi Ch 9)

Row 2, Button 1: C3/Note-number 60 (Midi Ch 10)
Row 2, Button 2: D3/Note-number 62 (Midi Ch 10)
Row 2, Button 3: E3/Note-number 64 (Midi Ch 10)

Row 3, Button 1: C4/Note-number 72 (Midi Ch 11)
Row 3, Button 2: D4/Note-number 74 (Midi Ch 11)
Row 3, Button 3: E4/Note-number 76 (Midi Ch 11)

3 rows with 3 notes each in an Array:

{48, 50, 52}
{60, 62, 64}
{72, 74, 76}

Is this correct:

{ {48, 50, 52}, {60, 62, 64}, {72, 74, 76} };

Im sorry for the newbee questions, but im not so much into programming.

I think this is all i can cope with for now - I would like to make small steps and get the notes right before i jump into the Latching problem and the IDE example "state change detection"

best regards Flintholm

You are close with the array but you have not actually declared an array with those examples.

byte noteArray[3][3] = { {48, 50, 52}, {60, 62, 64}, {72, 74, 76} };
// or to understand it better lay it out like this:-
byte noteArray[3][3] = { {48, 50, 52}, 
                                     {60, 62, 64}, 
                                     {72, 74, 76} 
                                    };

Then you can use:-

note = noteArray[y][x];

when you declare the array the first number is the number of groups and the second number how far into that group you have, so I would swap the X and Y if you define the array like you said.

As to your questions:-

The buttons in the Array are starting in the topleft corner and ascending in a normal reading direction (left to right) ?

I don't know, I don't have that hardware but I would guess the buttons are ordered left to right.

Numbers in programming always start with a 0 (zero) for number 1 ? like midi-channel 4, the number should be 3 ?

It depends on the library you are using, some require the "true" value 0 to 15, while others require the "musicians" value of 1 to 16. I call it the true value because the channel number is in the lower four bits of the command byte of a MIDI message. Four bits can have a value between 0 and 15, but musicians can't cope with that so we say we have channels 1 to 16. It is easy enough to correct for and so some libraries correct for it and others don't. It is not always easy to find in the documentation, so send stuff on channel 3 and monitor the MIDI and see what channel it shows up as.

Is the buttons X and the rows Y ?

That is my understanding.

Is the first button and first row 0 (zero) ?

Is the second button and second row 1 ?

It would be astonishing if they were not.

In order to make it understandable for me, i need to clarify things - Is it following correct:

If you need to clarify something then it is best doing a test. Write a short code that is like the hello world example but don't do any of the MIDI stuff just print out variables to the serial monitor using a sensible baud rate and see for yourself how things are are arranged.

Hi Mike

Thanx - i will be back soon and check out your helpfull answers :slight_smile:

Flintholm

Dear Grumpy Mike

I was away from this stuff because i had to do some stupid tax-return paperwork (i hate it) i will have to start over again and try to work with your suggestions.

I think i understood and can work out the Array

note = noteArray[y][x];

If i want 8 rows with 8 notes each in an Array:

{2, 4, 6}
{8, 10, 12}
{14, 16, 18}
{20, 22, 24}
{26, 28, 30}
{32, 34, 36}
{38, 40, 42}
{44, 46, 48}

This is just an example with simple wholenote-steps, making it easy to type - i will adjust the final notes for my needs when i have the understanding of it.

should the code look like this:

byte noteArray[8][8] = { {2, 4, 6}, {8, 10, 12}, {14, 16, 18}, {20, 22, 24}, {26, 28, 30}, {32, 34, 36}, {38, 40, 42}, {44, 46, 48} };

if this is right - is it also right that i should use the line:

note = noteArray[y][x];

instead of the line:

note = LOWNOTE + y * WIDTH + x;

in the Hello world example ?

Is this right:

void loop() {
  unsigned long t = millis();
  if((t - prevReadTime) >= 20L) { // 20ms = min Trellis poll time
    if(untztrument.readSwitches()) { // Button state change?
      for(uint8_t i=0; i<N_BUTTONS; i++) { // For each button...
        // Get column/row for button, convert to MIDI note number
        uint8_t x, y, note;
        untztrument.i2xy(i, &x, &y);
        note = noteArray[y][x];
        if(untztrument.justPressed(i)) {
          noteOn(CHANNEL, note, 127);
          untztrument.setLED(i);
        } else if(untztrument.justReleased(i)) {
          noteOff(CHANNEL, note, 0);
          untztrument.clrLED(i);
        }
      }
      untztrument.writeDisplay();
    }
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
  }

If this is right, where should i put the part with the Array:

byte noteArray[8][8] = { {2, 4, 6}, {8, 10, 12}, {14, 16, 18}, {20, 22, 24}, {26, 28, 30}, {32, 34, 36}, {38, 40, 42}, {44, 46, 48} };

I hope to get this solved sometime - Now x-mas is comming with a lot of other timecomsuming stuff, but maybe ill get it together with you help. Im very greatfull - Thanks :slight_smile:

should the code look like this:

byte noteArray[8][8] = { {2, 4, 6}, {8, 10, 12}, {14, 16, 18}, {20, 22, 24}, {26, 28, 30}, {32, 34, 36}, {38, 40, 42}, {44, 46, 48} };

Yes apart from the obvious that there should be 8 numbers in each brace {} and 8 brace pairs. I would put each 8 number brace on a new line to make it more readable.

if this is right - is it also right that i should use the line:

note = noteArray[y][x];

Yes.

If this is right, where should i put the part with the Array:

Just before the setup function is defined.

Thank You Mike :slight_smile:

It´was ofcourse a fault that i only had 3 notenumbers in each brace. Also the listing you suggested is much better. I was not quite sure what the setup function was, but i supposed this was the right place:

byte noteArray[8][8] = { 
{2, 4, 6, 8, 10, 12, 14, 16}, 
{8, 10, 12, 14,16, 18, 20, 22}, 
{14, 16, 18, 20, 22, 24, 26, 28}, 
{20, 22, 24, 26, 28, 30, 32, 34}, 
{26, 28, 30, 32, 34, 36, 38, 40}, 
{32, 34, 36, 38, 40, 42, 44, 46}, 
{38, 40, 42, 44, 46, 48, 50, 52}, 
{44, 46, 48, 48, 50, 52, 54, 56} };

void setup() {
  pinMode(LED, OUTPUT);
#ifndef HELLA
  untztrument.begin(addr[0], addr[1], addr[2], addr[3]);
#else
  untztrument.begin(addr[0], addr[1], addr[2], addr[3],
                    addr[4], addr[5], addr[6], addr[7]);
#endif // HELLA
  // Default Arduino I2C speed is 100 KHz, but the HT16K33 supports
  // 400 KHz.  We can force this for faster read & refresh, but may
  // break compatibility with other I2C devices...so be prepared to
  // comment this out, or save & restore value as needed.
#ifdef ARDUINO_ARCH_SAMD
  Wire.setClock(400000L);
#endif
#ifdef __AVR__
  TWBR = 12; // 400 KHz I2C on 16 MHz AVR
#endif
  untztrument.clear();
  untztrument.writeDisplay();
}

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
  MidiUSB.flush();
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
  MidiUSB.flush();
}

void loop() {
  unsigned long t = millis();
  if((t - prevReadTime) >= 20L) { // 20ms = min Trellis poll time
    if(untztrument.readSwitches()) { // Button state change?
      for(uint8_t i=0; i<N_BUTTONS; i++) { // For each button...
        // Get column/row for button, convert to MIDI note number
        uint8_t x, y, note;
        untztrument.i2xy(i, &x, &y);
        note = noteArray[y][x];
        if(untztrument.justPressed(i)) {
          noteOn(CHANNEL, note, 100);
          untztrument.setLED(i);
        } else if(untztrument.justReleased(i)) {
          noteOff(CHANNEL, note, 0);
          untztrument.clrLED(i);
        }
      }
      untztrument.writeDisplay();
    }
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
  }
}

Anyway i can confirm that it works :slight_smile:

Thats very nice - thanks Mike.

Now the last step is to make the pads latch instead of momentary.

You wrote earlier this:

The latching mode is simply a matter of when you send the note off command. If, like at the moment, you send it when the button is released, you have a momentary note. To make it latched simply send the note off when the button is pressed a second time.
An example of this for a single button is shown in the IDE example "state change detection". You have to extend the idea to your situation where you have many numbers. This is not as daunting as it seems if again you use an array to hold the last value of each switch and compare with the incoming value. So you detect when a key is pressed not if it is currently being held down. You use this to "count" the number of key presses from 0 to 2. When you see a 1 in the updated count, turn the note on and when you see a 2, turn the note off and reset the count to zero.

I will have to check up on this, but i think this will be the last hurdle for this project.

I proberly will come back for help on this after i have tried, but thanks very much for this so far :slight_smile:

best regards

Henning

Well done.

I was not quite sure what the setup function was,

there is at least a setup function and a loop function defined in every Arduino sketch. A function definition starts with the variable type it will return. These two functions never return a value so the function definition starts with the key word void, which means no type of variable. Beginners often say this is the void loop function but it is not, it is simply the loop function.

You can define other functions if you want. This is very useful for splitting the code into logical parts or where you want the same bit of code to run in various points in the code.

Read about functions here:- https://www.arduino.cc/en/Reference/FunctionDeclaration

Dear Mike

I have tried to look at your suggestions for making my switches from momentary to latch. You have suggested that i look at the IDE example "state change detection" - i did, but i must accept that this is way over my code skills and as you very correct wrote "You are pushing you coding skills here but it is not that much of a big step" i can only confirm that it is though a big step for me.

For what i understand i need to make the state for each button/switch in an array/matrix just as the midi note defenition, but i dont know how to do that. I must accept that im not a programmer. Could you maybe give me a hint or a example that could help me ?

My original goal was to make the UNZTrument with an 8x8 matrix of latching notes on 8 different midi channels for making sustaining notes/chords on a multitimbral synth, Waldorf Blofeld. With the proper synth programing this would allow me to build soundscapes with 8 different patches of sustaining notes, which was programmed to compliment each other in tonal and timbral complexivity. For this to happen it´s very important that the notes are latching.

If you want to have an idea of what kind of music i make you can have a listen on my my upcoming ambient album "zero gravity" here on soundcloud:

I really hope to solve this last hurdle before i go on to the next hurdle - I need to convert the midi output from UNZtrument (which is USB) to standard 5 pin din-midi signals so i can address a standard multitimbral midi synth (Waldorf Blofeld). In order to use the setup live and direct from UNZtrument to Synth without a computer i need a Arduino USB host to midi converter. I want to use a Arduino Nano Chinese Clone and a HW-244 Mini USB Host Shield. So if you have a links for such a project this would be super, otherwise i belive this is not so much the big programming problem, but rather find a project thats already made.

Best regards

Flintholm

OK you have given me a lot of trouble with this sketch because you have not included all the code and my attempts to fill in what you have missed out don't seam to be working. Why have you included all the #def stuff inside the setup function? It should not be their because the things it defines are not global. Are you actually copying and pasting from a working program or from some other source?

The changes you need to make are simple:-
add this after the note array definition

boolean notePlaying[8][8]; // this is an array to store if a note is playing

add this to your setup function:-

// clear note playing array
  for(byte i=0; i<8; i++){
    for(byte j=0; i<8; j++){
       notePlaying[i][j] = false;
    }
  }

and make this your loop function:-

void loop() {
  unsigned long t = millis();
  if((t - prevReadTime) >= 20L) { // 20ms = min Trellis poll time
    if(untztrument.readSwitches()) { // Button state change?
      for(uint8_t i=0; i<N_BUTTONS; i++) { // For each button...
        // Get column/row for button, convert to MIDI note number
        uint8_t x, y, note;
        untztrument.i2xy(i, &x, &y);
        note = noteArray[y][x];
        // if a key is pressed and a note is not playing turn it on 
        if(untztrument.justPressed(i) && notePlaying[y][x]) == false) {
          noteOn(CHANNEL, note, 100);
          notePlaying[y][x] = true;
          untztrument.setLED(i);
        }
        // if a key is pressed and a note is already playing turn it off 
        if(untztrument.justPressed(i) && notePlaying[y][x]) == true) {
          noteOff(CHANNEL, note, 0);
          notePlaying[y][x] = false;
          untztrument.clrLED(i);
        }
      }
      untztrument.writeDisplay();
    }
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
  }
}

I couldn't test this because I do not have a untztrument.

You will see a small change to the function, this looks for a key being pressed for the first time.

If it is, and a note is not currently playing associated with that key it turns on that note.
If it is, and a note is currently playing associated with that key it turns off that note.

That is all there is to it, it is not complex.

Please in future post all your code if you are having further trouble with this.

Other programmers note:- I know I am using 8 times more memory for the notePlaying array than I need to, but I have done this to make it easy to follow

Dear Mike

Thank you very much for yor patience :slight_smile:

I will look into you suggestions later but for now i will show you the code i have been modifying.

I cant say why some lines isn´t in the right place :frowning:

This is the full original BLE_UNTZrument_Hello_World:

// Super-basic Bluefruit Feather UNTZtrument MIDI example.
// Maps buttons to MIDI noteon/off events;
// this is NOT a sequencer or anything fancy.
// Requires an Adafruit Bluefruit LE Feather, the Adafruit 
// BluefruitLE nRF51 library, and a MIDI software synth on your
// host computer or mobile device.
//
// Copyright (c) 2014-2016 Adafruit Industries
// Author: Phil Burgess
// BLE Modifications: Todd Treece
// Licensed under the Modified BSD License.

#include <Wire.h>
#include <Adafruit_Trellis.h>
#include <Adafruit_UNTZtrument.h>

#include "Adafruit_BLE.h"
#include "Adafruit_BluefruitLE_SPI.h"
#include "Adafruit_BLEMIDI.h"

#include "BluefruitConfig.h"

#define FACTORYRESET_ENABLE         1
#define MINIMUM_FIRMWARE_VERSION    "0.7.0"

Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);
Adafruit_BLEMIDI midi(ble);

#define LED     13 // Pin for heartbeat LED (shows code is working)
#define CHANNEL 1  // MIDI channel number

#ifndef HELLA
// A standard UNTZtrument has four Trellises in a 2x2 arrangement
// (8x8 buttons total).  addr[] is the I2C address of the upper left,
// upper right, lower left and lower right matrices, respectively,
// assuming an upright orientation, i.e. labels on board are in the
// normal reading direction.
Adafruit_Trellis     T[4];
Adafruit_UNTZtrument untztrument(&T[0], &T[1], &T[2], &T[3]);
const uint8_t        addr[] = { 0x70, 0x71,
                                0x72, 0x73 };
#else
// A HELLA UNTZtrument has eight Trellis boards...
Adafruit_Trellis     T[8];
Adafruit_UNTZtrument untztrument(&T[0], &T[1], &T[2], &T[3],
                                 &T[4], &T[5], &T[6], &T[7]);
const uint8_t        addr[] = { 0x70, 0x71, 0x72, 0x73,
                                0x74, 0x75, 0x76, 0x77 };
#endif // HELLA

// For this example, MIDI note numbers are simply centered based on
// the number of Trellis buttons; each row doesn't necessarily
// correspond to an octave or anything.
#define WIDTH     ((sizeof(T) / sizeof(T[0])) * 2)
#define N_BUTTONS ((sizeof(T) / sizeof(T[0])) * 16)
#define LOWNOTE   ((128 - N_BUTTONS) / 2)

uint8_t       heart        = 0;  // Heartbeat LED counter
unsigned long prevReadTime = 0L; // Keypad polling timer
bool isConnected = false;

void setup() {
  pinMode(LED, OUTPUT);
 
  Serial.begin(115200);
  Serial.print(F("Bluefruit Feather: "));

  if ( !ble.begin(VERBOSE_MODE) ) {
    error(F("Couldn't find Bluefruit, check wiring?"));
  }
  Serial.println( F("OK!") );

  if ( FACTORYRESET_ENABLE ) {
    /* Perform a factory reset to make sure everything is in a known state */
    Serial.println(F("Performing a factory reset: "));
    if ( ! ble.factoryReset() ) {
      error(F("Couldn't factory reset"));
    }
  }

  ble.echo(false);

  Serial.println("Requesting Bluefruit info:");
  /* Print Bluefruit information */
  ble.info();
  
  /* Set BLE callbacks */
  ble.setConnectCallback(connected);
  ble.setDisconnectCallback(disconnected);
  Serial.println(F("Enable MIDI: "));
  
  if ( ! midi.begin(true) ) {
    error(F("Could not enable MIDI"));
  }
    
  ble.verbose(false);
  Serial.print(F("Waiting for a connection..."));
  while(!isConnected) {
    ble.update(500);
  }

#ifndef HELLA
  untztrument.begin(addr[0], addr[1], addr[2], addr[3]);
#else
  untztrument.begin(addr[0], addr[1], addr[2], addr[3],
                    addr[4], addr[5], addr[6], addr[7]);
#endif // HELLA
  // Default Arduino I2C speed is 100 KHz, but the HT16K33 supports
  // 400 KHz.  We can force this for faster read & refresh, but may
  // break compatibility with other I2C devices...so be prepared to
  // comment this out, or save & restore value as needed.
#ifdef ARDUINO_ARCH_SAMD
    Wire.setClock(400000L);
#endif
#ifdef __AVR__
      TWBR = 12; // 400 KHz I2C on 16 MHz AVR
#endif
  untztrument.clear();
  untztrument.writeDisplay();
}

void noteOn(byte channel, byte pitch, byte velocity) {
  midi.send(0x90 | channel, pitch, velocity);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midi.send(0x80 | channel, pitch, velocity);
}

void loop() {
  ble.update(1);
 
  if(! isConnected) {
    return;
  }
 
  unsigned long t = millis();
  
  if((t - prevReadTime) >= 20L) { // 20ms = min Trellis poll time
    if(untztrument.readSwitches()) { // Button state change?
      for(uint8_t i=0; i<N_BUTTONS; i++) { // For each button...
        // Get column/row for button, convert to MIDI note number
        uint8_t x, y, note;
        untztrument.i2xy(i, &x, &y);
        note = LOWNOTE + y * WIDTH + x;
        if(untztrument.justPressed(i)) {
          noteOn(CHANNEL, note, 127);
          untztrument.setLED(i);
        } else if(untztrument.justReleased(i)) {
          noteOff(CHANNEL, note, 0);
          untztrument.clrLED(i);
        }
      }
      untztrument.writeDisplay();
    }
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
  }
  
}

void error(const __FlashStringHelper*err) {
  Serial.println(err);
  while (1);
}

void connected(void) {
  isConnected = true;
  Serial.println(F(" CONNECTED!"));
}

void disconnected(void) {
  Serial.println("disconnected");
  isConnected = false;
}

best regards

Flintholm

becaus of a 9000 character max i´ve had to split up my answer - here is part 2 :slight_smile:

I have ben using the original (above) with you helpfull suggestions to make this version:

// Super-basic UNTZtrument MIDI example.  Maps buttons to MIDI note
// on/off events; this is NOT a sequencer or anything fancy.
// Requires an Arduino Leonardo w/TeeOnArdu config (or a PJRC Teensy),
// software on host computer for synth or to route to other devices.

#include <Wire.h>
#include <Adafruit_Trellis.h>
#include <Adafruit_UNTZtrument.h>
#include "MIDIUSB.h"

#define LED     13 // Pin for heartbeat LED (shows code is working)
#define CHANNEL y +8// MIDI channel number

#ifndef HELLA
// A standard UNTZtrument has four Trellises in a 2x2 arrangement
// (8x8 buttons total).  addr[] is the I2C address of the upper left,
// upper right, lower left and lower right matrices, respectively,
// assuming an upright orientation, i.e. labels on board are in the
// normal reading direction.
Adafruit_Trellis     T[4];
Adafruit_UNTZtrument untztrument(&T[0], &T[1], &T[2], &T[3]);
const uint8_t        addr[] = { 0x70, 0x71,
                                0x72, 0x73 };
#else
// A HELLA UNTZtrument has eight Trellis boards...
Adafruit_Trellis     T[8];
Adafruit_UNTZtrument untztrument(&T[0], &T[1], &T[2], &T[3],
                                 &T[4], &T[5], &T[6], &T[7]);
const uint8_t        addr[] = { 0x70, 0x71, 0x72, 0x73,
                                0x74, 0x75, 0x76, 0x77 };
#endif // HELLA

// For this example, MIDI note numbers are simply centered based on
// the number of Trellis buttons; each row doesn't necessarily
// correspond to an octave or anything.
#define WIDTH     ((sizeof(T) / sizeof(T[0])) * 2)
#define N_BUTTONS ((sizeof(T) / sizeof(T[0])) * 16)
#define LOWNOTE   ((128 - N_BUTTONS) / 2)

uint8_t       heart        = 0;  // Heartbeat LED counter
unsigned long prevReadTime = 0L; // Keypad polling timer

byte noteArray[8][8] = { 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58} };

void setup() {
  pinMode(LED, OUTPUT);
#ifndef HELLA
  untztrument.begin(addr[0], addr[1], addr[2], addr[3]);
#else
  untztrument.begin(addr[0], addr[1], addr[2], addr[3],
                    addr[4], addr[5], addr[6], addr[7]);
#endif // HELLA
  // Default Arduino I2C speed is 100 KHz, but the HT16K33 supports
  // 400 KHz.  We can force this for faster read & refresh, but may
  // break compatibility with other I2C devices...so be prepared to
  // comment this out, or save & restore value as needed.
#ifdef ARDUINO_ARCH_SAMD
  Wire.setClock(400000L);
#endif
#ifdef __AVR__
  TWBR = 12; // 400 KHz I2C on 16 MHz AVR
#endif
  untztrument.clear();
  untztrument.writeDisplay();
}

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
  MidiUSB.flush();
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
  MidiUSB.flush();
}

void loop() {
  unsigned long t = millis();
  if((t - prevReadTime) >= 20L) { // 20ms = min Trellis poll time
    if(untztrument.readSwitches()) { // Button state change?
      for(uint8_t i=0; i<N_BUTTONS; i++) { // For each button...
        // Get column/row for button, convert to MIDI note number
        uint8_t x, y, note;
        untztrument.i2xy(i, &x, &y);
        note = noteArray[y][x];
        if(untztrument.justPressed(i)) {
          noteOn(CHANNEL, note, 100);
          untztrument.setLED(i);
        } else if(untztrument.justReleased(i)) {
          noteOff(CHANNEL, note, 0);
          untztrument.clrLED(i);
        }
      }
      untztrument.writeDisplay();
    }
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
  }
}

This gives m a working 8x8 matrix with 8 C-minor Pentatonic notes on midi channel 9-16

I will have to be off for work and come back when i have had some time to check out you last suggestions.

Im so gratefull that you take the time to help me :slight_smile:

Hi Mike

I have tried your suggestions and trying to eject the suggested code - the first 2 blocks seemed to pass without errors on verification, but on the last one (the replace loop function i get this error

"expected primary-expression before "==" token

Maybe i have not placed the lines corretly regarding to the brackets ?

This is the code that fails the last block:

// Super-basic UNTZtrument MIDI example.  Maps buttons to MIDI note
// on/off events; this is NOT a sequencer or anything fancy.
// Requires an Arduino Leonardo w/TeeOnArdu config (or a PJRC Teensy),
// software on host computer for synth or to route to other devices.

#include <Wire.h>
#include <Adafruit_Trellis.h>
#include <Adafruit_UNTZtrument.h>
#include "MIDIUSB.h"

#define LED     13 // Pin for heartbeat LED (shows code is working)
#define CHANNEL y +8// MIDI channel number

#ifndef HELLA
// A standard UNTZtrument has four Trellises in a 2x2 arrangement
// (8x8 buttons total).  addr[] is the I2C address of the upper left,
// upper right, lower left and lower right matrices, respectively,
// assuming an upright orientation, i.e. labels on board are in the
// normal reading direction.
Adafruit_Trellis     T[4];
Adafruit_UNTZtrument untztrument(&T[0], &T[1], &T[2], &T[3]);
const uint8_t        addr[] = { 0x70, 0x71,
                                0x72, 0x73 };
#else
// A HELLA UNTZtrument has eight Trellis boards...
Adafruit_Trellis     T[8];
Adafruit_UNTZtrument untztrument(&T[0], &T[1], &T[2], &T[3],
                                 &T[4], &T[5], &T[6], &T[7]);
const uint8_t        addr[] = { 0x70, 0x71, 0x72, 0x73,
                                0x74, 0x75, 0x76, 0x77 };
#endif // HELLA

// For this example, MIDI note numbers are simply centered based on
// the number of Trellis buttons; each row doesn't necessarily
// correspond to an octave or anything.
#define WIDTH     ((sizeof(T) / sizeof(T[0])) * 2)
#define N_BUTTONS ((sizeof(T) / sizeof(T[0])) * 16)
#define LOWNOTE   ((128 - N_BUTTONS) / 2)

uint8_t       heart        = 0;  // Heartbeat LED counter
unsigned long prevReadTime = 0L; // Keypad polling timer

byte noteArray[8][8] = { 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58}, 
{36, 43, 48, 50, 51, 53, 55, 58} };

boolean notePlaying[8][8]; // this is an array to store if a note is playing

void setup() 

{

  // clear note playing array
  for(byte i=0; i<8; i++){
    for(byte j=0; i<8; j++){
       notePlaying[i][j] = false;
    }
  }
  pinMode(LED, OUTPUT);
#ifndef HELLA
  untztrument.begin(addr[0], addr[1], addr[2], addr[3]);
#else
  untztrument.begin(addr[0], addr[1], addr[2], addr[3],
                    addr[4], addr[5], addr[6], addr[7]);
#endif // HELLA
  // Default Arduino I2C speed is 100 KHz, but the HT16K33 supports
  // 400 KHz.  We can force this for faster read & refresh, but may
  // break compatibility with other I2C devices...so be prepared to
  // comment this out, or save & restore value as needed.
#ifdef ARDUINO_ARCH_SAMD
  Wire.setClock(400000L);
#endif
#ifdef __AVR__
  TWBR = 12; // 400 KHz I2C on 16 MHz AVR
#endif
  untztrument.clear();
  untztrument.writeDisplay();
}

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
  MidiUSB.flush();
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
  MidiUSB.flush();
}

void loop() {
  unsigned long t = millis();
  if((t - prevReadTime) >= 20L) { // 20ms = min Trellis poll time
    if(untztrument.readSwitches()) { // Button state change?
      for(uint8_t i=0; i<N_BUTTONS; i++) { // For each button...
        // Get column/row for button, convert to MIDI note number
        uint8_t x, y, note;
        untztrument.i2xy(i, &x, &y);
        note = noteArray[y][x];
        // if a key is pressed and a note is not playing turn it on 
        if(untztrument.justPressed(i) && notePlaying[y][x]) == false) {
          noteOn(CHANNEL, note, 100);
          notePlaying[y][x] = true;
          untztrument.setLED(i);
        }
        // if a key is pressed and a note is already playing turn it off 
        if(untztrument.justPressed(i) && notePlaying[y][x]) == true) {
          noteOff(CHANNEL, note, 0);
          notePlaying[y][x] = false;
          untztrument.clrLED(i);
        }
      }
      untztrument.writeDisplay();
    }
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
  }
}

have i inserted/replaced your cod the right places ?

Regarding all the #def stuff inside the setup function - i can only say that i was in the origional Hello World example from which this all is build (see earlier post)

best regards

Henning