Go Down

Topic: UNTZtrument, how to prog buttons w. midi note number, velocity, ch, latch mode  (Read 5193 times) previous topic - next topic

Flintholm

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:

https://soundcloud.com/flintholm/sets/zero_gravity

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




Grumpy_Mike

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
Code: [Select]
boolean notePlaying[8][8]; // this is an array to store if a note is playing

add this to your setup function:-
Code: [Select]
// 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:-
Code: [Select]
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

Flintholm

Dear Mike

Thank you very much for yor patience :-)

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 :-(

This is the full original BLE_UNTZrument_Hello_World:

Code: [Select]
// 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

Flintholm

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


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


Code: [Select]
// 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 :-)

Flintholm

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:

Code: [Select]
// 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

Grumpy_Mike

Sorry got an extra closing bracket on both those if statements, try this:-
Code: [Select]
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
  }
}


Quote
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
Well when I download the Hello World from the example folder that comes with the Adafruit library it not in the setup function. I did this for this post, so it is the latest one.

Quote
have i inserted/replaced your cod the right places ?
Yes the cod is fine.

Functions like the loop function can be placed anywhere in the code as well.

Flintholm

Dear Mike - I have now tried your last suggestions, but im sad to say that i cant get it to work. It´s really sad that im so close to what i wanted and now im just stuck.

I understand that you cant keep helping me and im very thankfull for all you have done already. Im just very fustraited because i may be very close to a solution and it could be that i have just missed a very simple thing, maybe something with a bracket, so if you have the time to have a last look at the code i would be very happy.

First i had a lot of problems with the IDE not seeing my arduino at all. After i had no problems to load different example sketches which all worked and also your earlier suggestions then after uploading the code with missing brackets there was no connection at all, the board was not responsing. I had an error "butterfly_recev(): programmer is not responding. I have tried a lot also arduino (uno) with the blink ex. and it worked. So i tried to reinstall the ide, i installed different ch340 & ch341 drivers and removing them again also i installed FTDI drivers but nothing worked until i found the tip to reset the Leonardo board shortly after uploading the code and then suddenly the board was responding again. Apparently the Leonardo becomes unresponsive after uploading buggy code - here is the link i found:

https://stackoverflow.com/questions/49283074/arduino-leonardo-avrdude-butterfly-recv-programmer-is-not-responding

So now when the board came back to life i have tried to do your edits more times, but im stuck here. When i make an error in the code, the board gets unresponsive and i have to do the reset, it´s not that easy because i have to try 10-20 times pressing upload and then press the reset button on Leonardo at the exact right moment before it recieves the code. I have this working sketch where i can: change the midi ch for each row, change the velocity for all buttons globally, change the note-number for each button, all buttons light up when pressed but all buttons are non-latch:


Code: [Select]
// 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() {
  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
  }
}



If you could have a last look at the code and maybe put in your edits the right place i would be very  thankfull - also if you cant help me, if you know where i could search for further help. This is just very fustraiting when im so close. Maybe i can donate something to you for helping me ?

best regards

Flintholm

Grumpy_Mike

Just seen the post and am about to go to bed, so just as a quick one replace
Code: [Select]
if(untztrument.justPressed(i) && notePlaying[y][x] == true) {
With
Code: [Select]
else if(untztrument.justPressed(i) && notePlaying[y][x] == false) {

Flintholm

Dear Mike

Thank you.

I cant find the line you want me to replace ???

Code: [Select]
if(untztrument.justPressed(i) && notePlaying[y][x] == true) {

I can only find something similar in the void loop section:

Code: [Select]
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
  }
}


Im sorry that im not fit in programming but im really lost here. I try to follow your suggestions but i  am really confused.

Please take take your time, but if you could help me to a finish it would be very very nice.




Grumpy_Mike

Quote
I cant find the line you want me to replace
In the loop function in the code in reply#20.

Grumpy_Mike

OK, on my laptop so I can tidy up that fix I gave you. Here is the loop function you need in your code:-
Code: [Select]
void loop() {
  uint8_t x, y, note;
  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
        untztrument.i2xy(i, &x, &y);
        note = noteArray[y][x];
        // if a key is pressed and a note is playing turn it off
        if(untztrument.justPressed(i)) {
            if(notePlaying[y][x]) {
               noteOff(CHANNEL, note, 0);
               notePlaying[y][x] = false;
               untztrument.clrLED(i);
            }
            else { // if a key is pressed and a note is not playing turn it on
              noteOn(CHANNEL, note, 100);
              notePlaying[y][x] = true;
              untztrument.setLED(i);
            }
      untztrument.writeDisplay(); // update only if there has been a change
      }
    } // end of for loop reading all the buttons
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
    } // end of has button changed state
  } // end of pole time
} // end of loop function

Flintholm

Dear Mike

Thanx - I have tried, but the same situation. After i add/replace the code you suggest and uploaded the board becomes unresponsive and i´ll have to do the "reset board shortly after upload of a known working patch"   the reset press will have to be at a very exact time after the upload and i must try 10-20 times before i get it right and can try again.
I dont know what im doing wrong - maybe im inserting the code edits at a wrong place or maybe before/after a bracket ???

It was all good up to the point where we started with the latching thing. This is your edit suggestion from post #16

add this after the note array defenition:

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


add this to the setup function:

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


then this last edit post #25

replace the loop function with this:

Code: [Select]
void loop() {
  uint8_t x, y, note;
  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
        untztrument.i2xy(i, &x, &y);
        note = noteArray[y][x];
        // if a key is pressed and a note is playing turn it off
        if(untztrument.justPressed(i)) {
            if(notePlaying[y][x]) {
               noteOff(CHANNEL, note, 0);
               notePlaying[y][x] = false;
               untztrument.clrLED(i);
            }
            else { // if a key is pressed and a note is not playing turn it on
              noteOn(CHANNEL, note, 100);
              notePlaying[y][x] = true;
              untztrument.setLED(i);
            }
      untztrument.writeDisplay(); // update only if there has been a change
      }
    } // end of for loop reading all the buttons
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
    } // end of has button changed state
  } // end of pole time
} // end of loop function



This is the working code before these edits:

Code: [Select]
// 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
  }
}


But im stuck here and i dont know what to do. Could i ask you to to the edits on the above working code and insert them at the right place so i know it´s done correct ? Then i would only have to upload the code and not do the trial and error with my very reduced skills. Furthemore i have lost the oversight from doing so many edits and different versions.

I know that this must be irritating and i would also suggest that i could send you the hardware so you could test it and you could send it back when it it working - i would pay you for doing this. Im fustrated being so close and cant stand the thought of giving up now.

best regards

Flintholm





Grumpy_Mike

The big big problem here is that I am writing code for hardware I don't have. It could be that it is behaving in a way I don't expect. I am reliant on you to give me feedback on what is happening. This feedback has been somewhat minimal so far. For example "the board becomes unresponsive" is a bit vague. I need details like
Is a note produced?
Do the LEDs go on and off when the buttons are pushed?
Does the heart beat LED flash?

This is my best guess at what should work:-
Code: [Select]
// 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() {
  uint8_t x, y, note;
  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       
        untztrument.i2xy(i, &x, &y);
        note = noteArray[y][x];       
        if(untztrument.justPressed(i)) {
            if(notePlaying[y][x]) {
               noteOff(CHANNEL, note, 0);
               notePlaying[y][x] = false;
               untztrument.clrLED(i);
            }
            else { // if a key is pressed and a note is not playing turn it on
              noteOn(CHANNEL, note, 100);
              notePlaying[y][x] = true;
              untztrument.setLED(i);
            }
      untztrument.writeDisplay();
      }
    }
    prevReadTime = t;
    digitalWrite(LED, ++heart & 32); // Blink = alive
    }
  }
} // end of loop function


If it does not then I will need you to report back what happens and then do changes to the code and report back again.
Just so we are on the same page you do have a Leonardo and the board is set to compile for the Arduino Leonardo. 

Flintholm

Hi Mike - I know it´s not optimal not to have the hardware and im glad that you are helping out - thanx a lot.

I have tried your last code. It verifyied ok in the Arduino Software v.1.8.10 but after uploading the board became unresponsive again :-(

I was not quite clear, but i will try to make it as clear as possible here.

The board is a Leonardo and i have chosen the Leonardo board in Arduino Software, confirmed by the succesfull upload of the earlier sketches. The board became unresponsive after uploading buggy code (as what i´ve found, the Leonardo is very sensetive to buggy code and need the restart procedure)

With unresponsive i mean:

The board does nothing afterwards, no leds are lighting up on USB connection, no heartbeat, 2 board led are constant lit (orange and green), the buttons does not response to pressure, doesnt send out midi notes, is not recogniced by my midi monitor software "Midi Monitor" i cant upload another sketch, i get the error message "couldnt find a board on the selected port". I have to do the tedious restart procedure as described earlier and only after a succecfull restart, the board will recieve another working sketch and work again until the next buggy sketch upload.

Grumpy_Mike

OK, this throws up a few things.
Quote
2 board led are constant lit (orange and green),
I thought these things were just white or do you have the neo-trellis  version.

Quote
I have to do the tedious restart procedure as described earlier and only after a succecfull restart,
When the board does not show up have you tried opening the serial monitor on the Arduino? This sometimes gets things going. In fact leaving the thing open often makes matters simpler.

Quote
I know it´s not optimal not to have the hardware
Optimal is hardly the word, it is dang near impossible. Especially when the feedback I am getting makes little sense to me.

Quote
The board does nothing afterwards, no leds are lighting up on USB connection, no heartbeat,
I think that means there is something else wrong other than the code I made. I can't see how the code changes would stop the heartbeat LED.

Maybe the way forward is posting that last code on the Adafruit forum and asking someone where it is wrong. You are more likely to get people with that hardware on there.



Go Up