I agree about i2c being too slow for keyboard scanning. I did use the macetech I/O expanders for years with my PIC encoders where a single MCU ran an entire console including up to 128 stops. Each macetech expander had four of the Microchip 23017 i2c chips. So by using four macetechs on two i2c channels I had 128 inputs and 128 outputs which was enough to handle the largest Allen organs I encountered.
Scanning the stops at 1/8 or even 1/16th the speed of the keys was plenty fast and the key to doing so much with a single 80 Mhz MCU. The same applies when scanning expression potentiometers where scanning one of eight potentiometers on each scan of the keys was a nice round robin time share that avoided needing a wasteful RTOS (real time operating system).
I'm now using SPI for both keys and stops as it's so much faster than i2c. With most but not all Allen manuals being a matrix the matrix approach is the obvious way to read the keys. A matrix comes with its own speed limitations, primarily the cable capacitance. The original Allen circuits have a remarkably slow matrix that introduces more latency than we normally accept for Hauptwerk. Just saying that sometimes we lose track of how well we are doing.
Arduino to convert an Allen MOS-2 to a MIDI controller
-
- Member
- Posts: 1210
- Joined: Thu May 07, 2009 9:43 am
Re: Arduino to convert an Allen MOS-2 to a MIDI controller
Thanks Nick and John. I actually had two 220 ohm resistors for the output, but my Steinberg UR22 had trouble reading the midi signals (none were getting through). When I desoldered the resistor to the Tx port, everything worked fine. I’m guessing it should be safe, since the input on the UR22 should have a photocoupler? Any other recommendations would be appreciated. Thanks John for your input. I wasn’t aware of the speed difference, so that is good to know about! I will look into SPI boards for a future project.
Re: Arduino to convert an Allen MOS-2 to a MIDI controller
I would just adding more information using Arduino Mega for scanning keyboards and pedals.
The experience I’ve made shows a strange behavior :
- because the wiring of the Johannus pedals matrix is a little inverted in comparison of keyboards matrix I have inverted the definition of the columns pins of the matrix(s!) just because I don’t want to redo the wiring.
Here are the matrix :
NoteButtonMatrix<8, 4> pedButtonmatrix = {
{22, 24, 26, 28, 30, 32, 34, 36}, // row pins brochage connecteur Johannus
{23, 25, 27, 29}, // column pins
pedAddresses, // address matrix
CHANNEL_1, // channel and cable number
};
and
NoteButtonMatrix<8, 8> kb1Buttonmatrix = { // great
{53, 51, 49, 47, 45, 43, 41, 39}, // row pins brochage connecteur Johannus
{37, 33, 29, 25, 35, 31, 27, 23}, // column pins
kbAddresses, // address matrix
CHANNEL_2, // channel and cable number
};
NoteButtonMatrix<8, 8> kb2Buttonmatrix = { // swell
{52, 50, 48, 46, 44, 42, 40, 38}, // row pins brochage connecteur Johannus
{37, 33, 29, 25, 35, 31, 27, 23}, // column pins
kbAddresses, // address matrix
CHANNEL_3, // channel and cable number
};
Every things seem correct and I played the organs without any troubles for hours until a chord with 3 notes:
Pedal F1#, right hand D3 and A3… and a strange sound like a big beating !
I have investigated a lot for finding the reason of this. Finally I find that the strange behavior occurs itself only playing the F1# at pedals and the same note on the keyboard without pulling any stops and with St Anne !
Finally with ‘Midi-OX’ I have find that with the two notes F1# on pedal and keyboard a lot of messages NoteOn NoteOff was generated with G1 and this at the rate of the matrix’s scanning !
The conclusion is you can use the same pins for different matrix but not to change the order of the pins !
Yes it’s not a problem from Hauptwerk !
The experience I’ve made shows a strange behavior :
- because the wiring of the Johannus pedals matrix is a little inverted in comparison of keyboards matrix I have inverted the definition of the columns pins of the matrix(s!) just because I don’t want to redo the wiring.
Here are the matrix :
NoteButtonMatrix<8, 4> pedButtonmatrix = {
{22, 24, 26, 28, 30, 32, 34, 36}, // row pins brochage connecteur Johannus
{23, 25, 27, 29}, // column pins
pedAddresses, // address matrix
CHANNEL_1, // channel and cable number
};
and
NoteButtonMatrix<8, 8> kb1Buttonmatrix = { // great
{53, 51, 49, 47, 45, 43, 41, 39}, // row pins brochage connecteur Johannus
{37, 33, 29, 25, 35, 31, 27, 23}, // column pins
kbAddresses, // address matrix
CHANNEL_2, // channel and cable number
};
NoteButtonMatrix<8, 8> kb2Buttonmatrix = { // swell
{52, 50, 48, 46, 44, 42, 40, 38}, // row pins brochage connecteur Johannus
{37, 33, 29, 25, 35, 31, 27, 23}, // column pins
kbAddresses, // address matrix
CHANNEL_3, // channel and cable number
};
Every things seem correct and I played the organs without any troubles for hours until a chord with 3 notes:
Pedal F1#, right hand D3 and A3… and a strange sound like a big beating !
I have investigated a lot for finding the reason of this. Finally I find that the strange behavior occurs itself only playing the F1# at pedals and the same note on the keyboard without pulling any stops and with St Anne !
Finally with ‘Midi-OX’ I have find that with the two notes F1# on pedal and keyboard a lot of messages NoteOn NoteOff was generated with G1 and this at the rate of the matrix’s scanning !
The conclusion is you can use the same pins for different matrix but not to change the order of the pins !
Yes it’s not a problem from Hauptwerk !
Re: Arduino to convert an Allen MOS-2 to a MIDI controller
Just wanted to let everyone know that I have updated my Arduino Hub page. I have now included a schematic and code for bussed keyboards, as well as the included potentiometer inputs in the schematics, and code to make them work for both Matrixed and Bussed keyboards.
https://create.arduino.cc/projecthub/La ... ans-f3756c
https://create.arduino.cc/projecthub/La ... ans-f3756c
Re: Arduino to convert an Allen MOS-2 to a MIDI controller
Hello Larason2, thank you for sharing your program, I have used it just for encoding pedalboard on a small "KeeYees Pro Micro ATmega32U4 Module de Développement 5V 16MHz Micro USB pour Arduino IDE Leonardo Bootloader".
(Having some problems with the length of the wire (near 3 meters) and parasites for some notes, I choose to separate functions, my Arduino Mega just converting MidiSerial from the keyboards to MidiUSB and the I2C Bus for buttons and pistons and their Leds). I just modifying it for less writing, using loops and array and that save a lot of codes and add a debouncing function. I think you could use the same method and save a lot lines of code...
----------------------------------------------------------
// Name: Pedal_encoder
// Created: 16/04/2021
// Author: JMG
// Ce programme fonctionne au choix avec un Arduino Mega ou avec un équivalent Leonardo moyennant quelques
// ajustement au niveau de l'affectation des pins et de la commande Serial. Il prévu pour un pédalier de 30 marches
// matricé en 4x8. (Deux notes manquent !) Le matricage est de type diode montée en cathode commune c'est à dire que
// toutes les diodes d'une même colonne ont leur cathode relièe au meme potentiel pendant la scrutation de chaque anode
// (que l'interrupteur soit d'un coté ou de l'autre de la diode ne change qrien quant au résultat de la mesure.)
// Pour plus d'infos voir les commentaires dans le code.
// En décommantant #define DEBUG 1 on peut debugger sur le moniteur série .
//#define DEBUG 1
unsigned long debounceTimeStamp;
int debounceDelay = 10;
byte keysC[4][8]; //Pedalier: Tableau contenant l'état des touches O=enfoncée ou 1=relachée; [Col=Mx][Row=P]
byte lastC[4][8]; //Pedalier: Tableau contenant l'état des touches lors de précédente boucle
// Definition des constantes
uint8_t channel = 0; // canal midi pédalier
const uint8_t ped_Note[4][8] = {
{0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B},
{0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33},
{0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B},
{0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43},
};
/*
// Déclaration Pin pour Arduino Mega
const uint8_t pinRow[8] = {22, 24, 26, 28, 30, 32, 34, 36};
const uint8_t pinCol[4] = {23, 25, 27, 29};
*/
// Déclaration Pin pour Leonardo utilise Serial1 au lieu de Serial
const uint8_t pinRow[8] = {21, 20, 19, 18, 15, 14, 16, 10};
const uint8_t pinCol[4] = {6, 7, 8, 9};
int noteOn = 0x90;
int noteOff = 0x80;
int velocity = 0x64;
void setup() {
noteOn = noteOn + channel;
noteOff = noteOff + channel;
// Start Serial
#ifdef DEBUG
Serial.begin(38400);
#else
Serial1.begin(31250);
#endif
// Initialise lastC
for (int Mx=0; Mx < 4; Mx++) {
for (int P=0; P < 8; P++) {
lastC[Mx][P] = 1;
}
}
//Initialize Keyboard C
for (int Mx = 0; Mx < 4; Mx++) {
pinMode(pinCol[Mx], INPUT);
}
for (int P = 0; P < 8; P++) {
pinMode(pinRow[P], INPUT);
}
}
void loop() {
if (millis() - debounceTimeStamp >= debounceDelay) {
// Read Keyboard C <=> Pedalier
//Set Row pins to read
for (int P = 0; P < 8; P++) {
pinMode(pinRow[P], INPUT_PULLUP);
}
// Scrute dans chaque colonne (Mx) les rangées (P)
for (int Mx = 0; Mx < 4; Mx++) {
// met la cathode de la diode à la masse
pinMode(pinCol[Mx], OUTPUT);
digitalWrite(pinCol[Mx], LOW);
for (int P = 0; P < 8; P++) {
// lit valeur sur la rangée si inter fermé = note enfoncée => ==0
keysC[Mx][P] = digitalRead(pinRow[P]);
}
// Remet la colonne en mode INPUT
pinMode(pinCol[Mx], INPUT);
}
/*
// Envoi des messages midi correspondant à l'état des notes
// Si Note enfoncée alors keysC[Col=Mx][Row=P] = 0
// si note enfoncée (==0) et précédent ==1 alors envoi NoteON
// si note relachée (==1) et précédent ==0 alors envoi NoteOff
// si pas de changement on fait rien
*/
for (int Mx = 0; Mx < 4; Mx++) {
debounceTimeStamp = millis();
for (int P = 0; P < 8; P++) {
if ((keysC[Mx][P] == 0) and (lastC[Mx][P] == 1)) {
MidiSend(noteOn,ped_Note[Mx][P], velocity);
lastC[Mx][P] = 0;
}
if ((keysC[Mx][P] == 1) and (lastC[Mx][P] == 0)) {
MidiSend(noteOff,ped_Note[Mx][P], velocity);
lastC[Mx][P] = 1;
}
} // fin for P
} // fin for Mx
} // fin du if
}
#ifdef DEBUG
void MidiSend(int cmd, int note, int vel) {
Serial.print(cmd, HEX);
Serial.print(" ");
Serial.print(note, HEX);
Serial.print(" ");
Serial.println(vel, HEX);
}
#else
void MidiSend(int cmd, int note, int vel) {
Serial1.write(cmd);
Serial1.write(note);
Serial1.write(vel);
}
#endif
(Having some problems with the length of the wire (near 3 meters) and parasites for some notes, I choose to separate functions, my Arduino Mega just converting MidiSerial from the keyboards to MidiUSB and the I2C Bus for buttons and pistons and their Leds). I just modifying it for less writing, using loops and array and that save a lot of codes and add a debouncing function. I think you could use the same method and save a lot lines of code...
----------------------------------------------------------
// Name: Pedal_encoder
// Created: 16/04/2021
// Author: JMG
// Ce programme fonctionne au choix avec un Arduino Mega ou avec un équivalent Leonardo moyennant quelques
// ajustement au niveau de l'affectation des pins et de la commande Serial. Il prévu pour un pédalier de 30 marches
// matricé en 4x8. (Deux notes manquent !) Le matricage est de type diode montée en cathode commune c'est à dire que
// toutes les diodes d'une même colonne ont leur cathode relièe au meme potentiel pendant la scrutation de chaque anode
// (que l'interrupteur soit d'un coté ou de l'autre de la diode ne change qrien quant au résultat de la mesure.)
// Pour plus d'infos voir les commentaires dans le code.
// En décommantant #define DEBUG 1 on peut debugger sur le moniteur série .
//#define DEBUG 1
unsigned long debounceTimeStamp;
int debounceDelay = 10;
byte keysC[4][8]; //Pedalier: Tableau contenant l'état des touches O=enfoncée ou 1=relachée; [Col=Mx][Row=P]
byte lastC[4][8]; //Pedalier: Tableau contenant l'état des touches lors de précédente boucle
// Definition des constantes
uint8_t channel = 0; // canal midi pédalier
const uint8_t ped_Note[4][8] = {
{0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B},
{0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33},
{0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B},
{0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43},
};
/*
// Déclaration Pin pour Arduino Mega
const uint8_t pinRow[8] = {22, 24, 26, 28, 30, 32, 34, 36};
const uint8_t pinCol[4] = {23, 25, 27, 29};
*/
// Déclaration Pin pour Leonardo utilise Serial1 au lieu de Serial
const uint8_t pinRow[8] = {21, 20, 19, 18, 15, 14, 16, 10};
const uint8_t pinCol[4] = {6, 7, 8, 9};
int noteOn = 0x90;
int noteOff = 0x80;
int velocity = 0x64;
void setup() {
noteOn = noteOn + channel;
noteOff = noteOff + channel;
// Start Serial
#ifdef DEBUG
Serial.begin(38400);
#else
Serial1.begin(31250);
#endif
// Initialise lastC
for (int Mx=0; Mx < 4; Mx++) {
for (int P=0; P < 8; P++) {
lastC[Mx][P] = 1;
}
}
//Initialize Keyboard C
for (int Mx = 0; Mx < 4; Mx++) {
pinMode(pinCol[Mx], INPUT);
}
for (int P = 0; P < 8; P++) {
pinMode(pinRow[P], INPUT);
}
}
void loop() {
if (millis() - debounceTimeStamp >= debounceDelay) {
// Read Keyboard C <=> Pedalier
//Set Row pins to read
for (int P = 0; P < 8; P++) {
pinMode(pinRow[P], INPUT_PULLUP);
}
// Scrute dans chaque colonne (Mx) les rangées (P)
for (int Mx = 0; Mx < 4; Mx++) {
// met la cathode de la diode à la masse
pinMode(pinCol[Mx], OUTPUT);
digitalWrite(pinCol[Mx], LOW);
for (int P = 0; P < 8; P++) {
// lit valeur sur la rangée si inter fermé = note enfoncée => ==0
keysC[Mx][P] = digitalRead(pinRow[P]);
}
// Remet la colonne en mode INPUT
pinMode(pinCol[Mx], INPUT);
}
/*
// Envoi des messages midi correspondant à l'état des notes
// Si Note enfoncée alors keysC[Col=Mx][Row=P] = 0
// si note enfoncée (==0) et précédent ==1 alors envoi NoteON
// si note relachée (==1) et précédent ==0 alors envoi NoteOff
// si pas de changement on fait rien
*/
for (int Mx = 0; Mx < 4; Mx++) {
debounceTimeStamp = millis();
for (int P = 0; P < 8; P++) {
if ((keysC[Mx][P] == 0) and (lastC[Mx][P] == 1)) {
MidiSend(noteOn,ped_Note[Mx][P], velocity);
lastC[Mx][P] = 0;
}
if ((keysC[Mx][P] == 1) and (lastC[Mx][P] == 0)) {
MidiSend(noteOff,ped_Note[Mx][P], velocity);
lastC[Mx][P] = 1;
}
} // fin for P
} // fin for Mx
} // fin du if
}
#ifdef DEBUG
void MidiSend(int cmd, int note, int vel) {
Serial.print(cmd, HEX);
Serial.print(" ");
Serial.print(note, HEX);
Serial.print(" ");
Serial.println(vel, HEX);
}
#else
void MidiSend(int cmd, int note, int vel) {
Serial1.write(cmd);
Serial1.write(note);
Serial1.write(vel);
}
#endif
Re: Arduino to convert an Allen MOS-2 to a MIDI controller
Thanks JmG! I actually tried to simplify my code along the lines of what you suggest, but I couldn’t figure it out! I think you’re a better programmer than I am! Thanks for sharing your code. Not sure what you mean by parasites, but when I was troubleshooting my setup, I sometimes got notes triggered with other notes. I fixed that with making sure none of my soldered contacts were touching, and having one pin for each contact wire. Congratulations on your project!
Re: Arduino to convert an Allen MOS-2 to a MIDI controller
By parasites I mean that sometimes many notes were "speaking" together, sometimes a note was not speaking at all and with a shorter flat wire connecting only the the group of that note there was no problems. I spent a lot of time with midi-ox and a scopemeter for investigating! All these problems appear after I put a 4-Channel relay card "https://www.amazon.com/AC100V-250V-4-Channel-High-low-Trigger-Arduino/dp/B077YLQ6DL" for powering with some different delay the accessory plug, the loudspeakers plug and finally the PC when the MOTU AO24 is ready,( a another "KeeYees Pro Micro ATmega32U4 Module" is doing the trick.) This card has a self powering +5V and when it was connected with the Arduino Mega by the ground there was a lot of electrical noise which was disturbing the reading of the diodes matrix.
Now with three different Arduino it's more simple for investigating if a another problem occurs !
Now with three different Arduino it's more simple for investigating if a another problem occurs !
Re: Arduino to convert an Allen MOS-2 to a MIDI controller
I just updated the Arduino Hub site with some updated code. This adds a piston matrix, adjustable debounce and Midi Thru. Just wanted to let everyone know!
https://create.arduino.cc/projecthub/La ... ans-f3756c
https://create.arduino.cc/projecthub/La ... ans-f3756c