Un contrôleur midi 100 basé sur Arduino
- 90 réponses
- 20 participants
- 22 437 vues
- 26 followers
secmast
Suite a la discussion sur ce contrôleur:https://fr.audiofanzine.com/surface-de-controle-midi/cme/BitStream-3X/forums/t.541700,mais-ou-le-trouver.html
Je viens jeter ici les bases pour la construction d'un contrôleur midi basé sur Arduino.
Certains pensaient que vu le nombre d'entrée analogique limitée sur une carte Arduino elle ne permettait pas l'utilisation de cette plateforme pour ce genre d'application. Ce n'est évidement pas le cas, il suffit de connaître un peu l'électronique et la programmation pour arriver a un résultat ma fois pas trop mal et peu coûteux.
Comme le montre l'image ci-dessous en utilisant des multiplexer il est possible de multiplier le nombre d'entrée/sortie avec en théorie un maximum de 1024 E/S sur une Arduino Mega.
Donc l'idée de ce fil est de jeter sur le papier le minimum requis pour une interface Midi
Sachant que l'arduino est limité en terme de mémoire (SRAM 2Kb au max) ce qui limite les variables (on en discute ?)
Sachant que l'Arduino est limité en terme de puissance "électrique", 40ma par PIN (on en discute ?)
Liste "A faire"
- Gestion EEPROM
- Alimentation externe
- Calcul d'un boucle sur 80 contrôle
[ Dernière édition du message le 13/09/2013 à 13:00:28 ]
secmast
int selection0MuxPrimaire = 3;
int selection1MuxPrimaire = 4;
int selection2MuxPrimaire = 5;
int selection0MuxSecondaire = 6;
int selection1MuxSecondaire = 7;
int selection2MuxSecondaire = 8;
int sel0;
int sel1;
int sel2;
int bouclePrimaire;
int boucleSecondaire;
int boutonEntre = 9;
int bouton[4];
int boutonEtat[4]={
0, 0, 0, 0};
int boutonCC[4]={
10, 20, 30, 40};
int potentiometreEntre = 0;
int potentiometre[8];
int potentiometreCC[8]={
11, 21, 31, 41, 51, 61, 71, 81};
// Canal midi de 176 à 191
int midiChannel = 176;
void setup(){
// Ouverture du port serie proche de la vitesse du Midi(31250) pour debut seulement
Serial.begin(31250);
// Configuration des ports Arduino E/S
pinMode(selection0MuxPrimaire, OUTPUT);
pinMode(selection1MuxPrimaire, OUTPUT);
pinMode(selection2MuxPrimaire, OUTPUT);
pinMode(selection0MuxSecondaire, OUTPUT);
pinMode(selection1MuxSecondaire, OUTPUT);
pinMode(selection2MuxSecondaire, OUTPUT);
pinMode(boutonEntre, INPUT);
}
void loop(){
// Scan et lecture des boutons
//Boucle de selection du Mux B1 pour 4 boutons
for (boucleSecondaire = 0; boucleSecondaire < 4; boucleSecondaire++){
// Transformation en binaire de la variable boucleSecondaire
sel0 =bitRead(boucleSecondaire, 0) ;
sel1 =bitRead(boucleSecondaire, 1) ;
sel2 =bitRead(boucleSecondaire, 2) ;
// Selection du port sur le 4051 Mux B3
digitalWrite(selection0MuxSecondaire, sel0);
digitalWrite(selection1MuxSecondaire, sel1);
digitalWrite(selection2MuxSecondaire, sel2);
// Delay d'attente avant capture du port Data, valeur à mettre au plus bas possible.
//delayMicroseconds(90);
// Echange entre 0 ou 127
if (boutonEtat[bouclesecondaire] == 0 && digitalRead(boutonEntre) == HIGH) {
boutonEtat[bouclesecondaire]= 1;
bouton[bouclesecondaire]= !bouton[bouclesecondaire];
// Envois vers le port Serie pour debug, sera remplacé par le code MIDI
Serial.write(midiChannel);
Serial.write(boutonCC[bouclesecondaire]);
Serial.write(bouton[bouclesecondaire]*127);
}
// Si bouton relacher, pret a capturer une nouvelle valeur
if (boutonEtat[bouclesecondaire] == 1 && digitalRead(boutonEntre) == LOW){
boutonEtat[bouclesecondaire]=0;
}
}
// Scan et lecture des potentiometres
// Boucle de selection du Mux B2 ou Mux B3
for (bouclePrimaire = 0; bouclePrimaire < 2; bouclePrimaire++){
// Transformation en binaire de la variable bouclePrimaire
sel0 =bitRead(bouclePrimaire, 0) ;
sel1 =bitRead(bouclePrimaire, 1) ;
sel2 =bitRead(bouclePrimaire, 2) ;
// Selection du port sur le 4051 Mux A
digitalWrite(selection0MuxPrimaire, sel0);
digitalWrite(selection1MuxPrimaire, sel1);
digitalWrite(selection2MuxPrimaire, sel2);
// Boucle de lecture Mux Bn pour 4 entrees
for (boucleSecondaire = 0; boucleSecondaire < 4; boucleSecondaire++){
// Transformation en binaire de la variable boucleSecondaire
sel0 =bitRead(boucleSecondaire, 0) ;
sel1 =bitRead(boucleSecondaire, 1) ;
sel2 =bitRead(boucleSecondaire, 2) ;
// Selection du port sur le 4051 Mux Bn
digitalWrite(selection0MuxSecondaire, sel0);
digitalWrite(selection1MuxSecondaire, sel1);
digitalWrite(selection2MuxSecondaire, sel2);
// Delay d'attente avant capture du port Data, valeur à mettre au plus bas possible.
//delayMicroseconds(75);
// Lecture de la valeur du potentiometre(n)
// Verification de la valeur ancienne et valeur actuelle base sur 4 potentiometre par mux
if (analogRead(potentiometreEntre)/8 != potentiometre[bouclesecondaire + (bouclePrimaire * 4)]){
potentiometre[bouclesecondaire + (bouclePrimaire * 4)] = analogRead(potentiometreEntre)/8;
Serial.write(midiChannel);
Serial.write(potentiometreCC[bouclesecondaire + (bouclePrimaire * 4)]);
Serial.write(potentiometre[bouclesecondaire + (bouclePrimaire * 4)]);
}
}
}
}
En tournant un potentiomètre très vite j'obtiens des valeurs de timing plus qu'honnête.
Entre 2 et 5 millisecondes c'est vraiment pas mal.
Sans la fonction "delay" je n'ai aucune erreur de lecture des potentiomètres ou des boutons.
En l'état cette maquette fonctionne parfaitement.
09:50:54.878 From UltraLite mk3 Hybrid MIDI Port Control 1 81 4
09:50:54.880 From UltraLite mk3 Hybrid MIDI Port Control 1 81 3
09:50:54.884 From UltraLite mk3 Hybrid MIDI Port Control 1 81 2
09:50:54.888 From UltraLite mk3 Hybrid MIDI Port Control 1 81 1
09:50:54.893 From UltraLite mk3 Hybrid MIDI Port Control 1 81 0
Comme je n'ai pas de processus qui permet de retenir la position des potentiomètres a l'extinction, si je fais un reset ou bien je fais un cycle éteindre/allumer. L'interface MIDI envois vers le canal choisis la valeur actuelle des potentiomètres. Et là c'est de l'ordre de la milliseconde.
10:03:06.634 From UltraLite mk3 Hybrid MIDI Port Control 1 11 23
10:03:06.635 From UltraLite mk3 Hybrid MIDI Port Control 1 21 11
10:03:06.636 From UltraLite mk3 Hybrid MIDI Port Control 1 31 2
10:03:06.636 From UltraLite mk3 Hybrid MIDI Port Control 1 51 19
10:03:06.637 From UltraLite mk3 Hybrid MIDI Port Control 1 61 35
10:03:06.638 From UltraLite mk3 Hybrid MIDI Port Control 1 71 55
10:03:06.639 From UltraLite mk3 Hybrid MIDI Port Control 1 81 12
secmast
int selection0MuxPrimaire = 3;
int selection1MuxPrimaire = 4;
int selection2MuxPrimaire = 5;
int selection0MuxSecondaire = 6;
int selection1MuxSecondaire = 7;
int selection2MuxSecondaire = 8;
Cette première partie sélectionne les ports de l'Arduino où sont connectés les ports de sélection des ports d'entrés des 4051.
Vous remarquerez que pour ma part j'ai du commencer au port 3 alors que le schéma montre le port 2.
Le miens, de port 2, est mort, je me suis donc adapté.
int sel0;
int sel1;
int sel2;
Les trois variables ou seront stockés la valeurs des ports de sélections.
int bouclePrimaire;
int boucleSecondaire;
Les variables pour les deux boucles.
int boutonEntre = 9;
int bouton[4];
int boutonEtat[4]={
0, 0, 0, 0};
int boutonCC[4]={
10, 20, 30, 40};
Les variables pour les boutons.
BoutonEntre est le port ou les données arrive sur l'Arduino en provenance du port de sortie du 4051.
Bouton[] et le tableau des valeurs actuelle des boutons. Ici de 1 à 4.
BoutonsEtat et une valeur indicative sur le dernier état du bouton pour éviter un phénomène de rebond.
BoutonCC est la valeur CC de chaque bouton.
int potentiometreEntre = 0;
int potentiometre[8];
int potentiometreCC[8]={
11, 21, 31, 41, 51, 61, 71, 81};
Les variables pour les potentiomètres.
Potentiomètre[] est la valeur actuelle du potentiomètre[n]
potentiomètreCC est le valeur CC pour chaque potentiomètre.
int midiChannel = 176;
Numéro du canal MIDI de destination des CC. 176 à 191 pour 1 à 16.
void setup(){
Serial.begin(31250);
Ouverture du port série avec une vitesse de 31250 kb/s qui est la vitesse d'un port MIDI.
pinMode(selection0MuxPrimaire, OUTPUT);
pinMode(selection1MuxPrimaire, OUTPUT);
pinMode(selection2MuxPrimaire, OUTPUT);
pinMode(selection0MuxSecondaire, OUTPUT);
pinMode(selection1MuxSecondaire, OUTPUT);
pinMode(selection2MuxSecondaire, OUTPUT);
pinMode(boutonEntre, INPUT);
}
Les ports logique d'un arduino doivent être configurer soit en sortie, soit en entré.
PinMode en est la commande. Les ports de sélections sont des sorties pour l'Arduino alors que BoutonEntre est un port d'entré.
void loop(){
Boucle principale du programme
// Scan et lecture des boutons
for (boucleSecondaire = 0; boucleSecondaire < 4; boucleSecondaire++){
Cette boucle sert a scanner les ports d'entrés du 4051 pour les boutons (Mux B1).
Comme il y a 4 boutons, cette boucle se fait 4 fois.
// Transformation en binaire de la variable boucleSecondaire
sel0 =bitRead(boucleSecondaire, 0) ;
sel1 =bitRead(boucleSecondaire, 1) ;
sel2 =bitRead(boucleSecondaire, 2) ;
Stockage de la valeur en binaire.
digitalWrite(selection0MuxSecondaire, sel0);
digitalWrite(selection1MuxSecondaire, sel1);
digitalWrite(selection2MuxSecondaire, sel2);
Ouverture du port par selection sur les pin du 4051.
if (boutonEtat[bouclesecondaire] == 0 && digitalRead(boutonEntre) == HIGH) {
boutonEtat[bouclesecondaire]= 1;
Par défaut boutEtat[n] est à zéro. Si la valeur du port est différente alors on considére que le bouton est poussé et on continue. Sinon on passe a la ligne suivante (après la fermeture du braquet plus bas)
bouton[bouclesecondaire]= !bouton[bouclesecondaire];
On inverse la valeur du bouton: si il est à 0 alors 1, si il est à 1 alors 0.
Serial.write(midiChannel);
Serial.write(boutonCC[bouclesecondaire]);
Serial.write(bouton[bouclesecondaire]*127);
Ici on envois sur le port série les trois valeurs : le canal midi, la valeur CC du bouton pressé et la valeur 0 ou 1 mulitplié par 127.
Serial.write écrit en mode binaire et c'est ce qui est attendu par le port d'entré de votre interface midi que vous voulez commander.
Serial.print écrit en mode ASCII pour pouvoir être lu par un humain.
}
Braquet de fermeture de la condition bouton pousser.
if (boutonEtat[bouclesecondaire] == 1 && digitalRead(boutonEntre) == LOW){
boutonEtat[bouclesecondaire]=0;
Si le bouton est relaché alors on passe le bouton Etat à Zero permettant l'entré d'une nouvelle valeur 0 ou 1. Sinon on passe a la ligne suivante (après le braquet de fermeture de cette condition)
}
Braquet de fermeture de la condition bouton relaché
}
Braquet de fermeture de la boucle
for (bouclePrimaire = 0; bouclePrimaire < 2; bouclePrimaire++){
sel0 =bitRead(bouclePrimaire, 0) ;
sel1 =bitRead(bouclePrimaire, 1) ;
sel2 =bitRead(bouclePrimaire, 2) ;
digitalWrite(selection0MuxPrimaire, sel0);
digitalWrite(selection1MuxPrimaire, sel1);
digitalWrite(selection2MuxPrimaire, sel2);
for (boucleSecondaire = 0; boucleSecondaire < 4; boucleSecondaire++){
sel0 =bitRead(boucleSecondaire, 0) ;
sel1 =bitRead(boucleSecondaire, 1) ;
sel2 =bitRead(boucleSecondaire, 2) ;
digitalWrite(selection0MuxSecondaire, sel0);
digitalWrite(selection1MuxSecondaire, sel1);
digitalWrite(selection2MuxSecondaire, sel2);
Comme nous avons deux 4051 connectés sur un autre 4051 il faut deux boucles.
Une première boucle pour sélectionner le port d’écoute du premier 4051 et a l’intérieur de cette boucle, une seconde qui écoute les ports du second étage 4051.
Donc :
Premier passage:
MUX A port 1
Mux B2 port 1
Mux B2 port 2
Mux B2 port 3
Mux B2 port 4
Second passage:
Mux A port 2
Mux B3 port 1
Mux B3 port 2
Mux B3 port 3
Mux B3 port 4
Si il y avait un ou deux 4051 supplémentaire il faut alors changer la valeur bouclePrimaire < 2 en bouclePrimaire < 4
Si il y avait 8 potentiomètres par 4051 il faut alors changer la valeur boucleSecondaire < 4 en boucleSecondaire < 8
if (analogRead(potentiometreEntre)/8 != potentiometre[bouclesecondaire + (bouclePrimaire * 4)]){
Par défaut et au démarrage, la valeur des potentiomètres est de Zéro, nous vérifions ici la valeur du potentiomètre et si elle est différente alors on procède sinon on passe au suivant.
potentiometre[bouclesecondaire + (bouclePrimaire * 4)] = analogRead(potentiometreEntre)/8;
On écrit la valeur du potentiomètre dans la variable potentiomètre[n] le calcul est le suivant :
Pour le premier potentiomètre qui est connecté sur le MUX B2 nous avons comme valeur :
boucleSecondaire = 0
BouclePrimaire = 0
Ce qui donne 0+(0*4) = 0 ce qui correspond au premier potentiomètre.
Pour le 6ème qui lui est connecté sur le port 2 du mux B3 nous aurons les valeurs :
boucleSecondaire = 2
BouclePrimaire = 1
Ce qui donne 1+(1*4) = 5 ce qui correspond au sixième potentiomètre, vous aurez deviné que nous comptons a partir de ZERO et non de UN.
Serial.write(midiChannel);
Serial.write(potentiometreCC[bouclesecondaire + (bouclePrimaire * 4)]);
Serial.write(potentiometre[bouclesecondaire + (bouclePrimaire * 4)]);
Envois des données vers le port série.
Et on retourne a ligne Void Loop(){
Rémy M. (chimimic)
Formateur en techniques sonores ; électronicien ; auteur @ sonelec-musique.com
secmast
secmast
adrienf
Je joins des cours pour la programmation de l'Arduino : http://fr.openclassrooms.com/sciences/cours/arduino-pour-bien-commencer-en-electronique-et-en-programmation
Et un projet pour un controleur MIDI via USB : https://2froblog.wordpress.com/2012/07/19/controleur-midi-via-lusb-avec-arduino-49/
oudi
super intéressant...
bon c'est un peu dur à comprendre pour moi ..mais bon...grâce à vous je commence à entre-apercevoir l’intérêt d'arduino.
D'où ma question.
Serait ce possible ( et pas trop galére) de créer un patch contrôlé par arduino.
en gros pouvoir faire un routing classique de patch mais sans les câbles.(avec des relais j'imagine)
j’espère être un peu clair.
merci.
jf
[ Dernière édition du message le 19/09/2013 à 21:34:36 ]
secmast
Maintenant les relais sont électriquement bruyant.
Il faudrait regarder du coté des 74HC4066 ou 67 pour le routage, mais là ça dépasse encore ma sphère de compétence.
Un petit dessin de ce que tu veux faire ?
Rémy M. (chimimic)
Serait ce possible ( et pas trop galére) de créer un patch contrôlé par arduino.
en gros pouvoir faire un routing classique de patch mais sans les câbles.(avec des relais j'imagine)
Tout à fait !
Relais possibles pour de l'audio ou du MIDI : exemple pour une grille / patch 4*4
(ici pilotée par un PIC, mais Arduino ou autre uC conviennent)
Commutateurs analogiques pour audio :
- CD4016 / CD4066 / CD4067 / CD4097 pour "qualité standard";
- autres spécialisés style MAX335 pour "qualité supérieure"
Formateur en techniques sonores ; électronicien ; auteur @ sonelec-musique.com
adrienf
- < Liste des sujets
- Charte