Communication MAX/MSP=>Arduino via Serial, envoyer une liste ?

Bonjour à tous.

Je suis débutant en code, je comprends quelques commandes mais je travail de manière très empirique. Je voulais pouvoir contrôler un 6 canaux d’un Potentiomètre Numérique AD5206 depuis MAX/MSP et j’ai réussi à le faire.
J’ai modifié le sketch de Greg Surges pour son Octomod (http://cycling74.com/2010/10/19/making-connections-control-voltage-output-using-osc-processing-and-a-microcontroller/) qui permet d’envoyer des données SPI depuis MAXMSP vers Processing via OSC puis Arduino via Serial

Par soucis de simplicité, j’ai essayé de ne pas utiliser Processing et OSC mais de passer directement de MAXMSP à Arduino via Serial.
Tout fonctionne, les 6 channels répondent bien mais je suis obligé d’envoyer les info Channel par Channel tout les 10ms. Du coup, quand je veux contrôler les 6 Channels cela me prends 60ms…

A l’origine, le code de Greg Surges envoie une seule liste de valeur (ex 0 234 138 12 9 0 0) depuis MAX, et Arduino (ou Processing ?) s’occupe de les répartir pour qu’elles correspondent au bon Channel.

Comment peut on faire ça ? Est-ce possible de le faire via Serial ? Dois-je utiliser Processing et OSC pour le faire ?

Voilà mon code :

// inslude the SPI library:
#include <SPI.h>


// set pin 10 as the slave select for the digital pot:
const int slaveSelectPin = 10;
int channel;
byte firstByte, secondByte, thirdByte;
boolean action;

void setup() {
  // set the slaveSelectPin as an output:
  pinMode (slaveSelectPin, OUTPUT);
  // initialize SPI:
  Serial.begin(115200);
  SPI.begin();
 
}

void loop(){
  pollAndWrite();
} 


void pollAndWrite(){
  action = false;
  while(!action){
   // check if data has been sent from the computer:
  if (Serial.available() > 1) {
    // read the most recent byte (which will be from 0 to 255):
    firstByte = Serial.read();
    delayMicroseconds(100);
    if (firstByte == 0) {
      secondByte = Serial.read();
      delayMicroseconds(100);
      thirdByte = Serial.read();
     // take the SS pin low to select the chip:
      digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
      SPI.transfer(secondByte);
      SPI.transfer(thirdByte);
      delayMicroseconds(10);
  // take the SS pin high to de-select the chip:
      digitalWrite(slaveSelectPin,HIGH);
      action = true;
   } 
if (firstByte == 1) {
      secondByte = Serial.read();
      delayMicroseconds(100);
      thirdByte = Serial.read();
   // take the SS pin low to select the chip:
      digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
      SPI.transfer(secondByte);
      SPI.transfer(thirdByte);
      delayMicroseconds(10);
  // take the SS pin high to de-select the chip:
      digitalWrite(slaveSelectPin,HIGH); 
     }
   }
  }
}

Up..

Bon MSP tout ça, c'est pas ma tasse de thé mais de ce que je comprend : tout ce qui te manque en fait c'est une routine Arduino pour pouvoir réceptionner une liste via port série ? Exemple : côté PC je tape dans le moniteur série "6,24,0,87,98,54" et le channel 1 passe à 6, le 2 à 24, le 3 à 0 ...

B@tto: Bon MSP tout ça, c'est pas ma tasse de thé mais de ce que je comprend : tout ce qui te manque en fait c'est une routine Arduino pour pouvoir réceptionner une liste via port série ? Exemple : côté PC je tape dans le moniteur série "6,24,0,87,98,54" et le channel 1 passe à 6, le 2 à 24, le 3 à 0 ...

Merci pour ta réponse, oui c'est exactement ca !

En gros, j'envoie un message dans le port série du style "0 0 0 0 0 0" puis 10ms plus tard "0 0 255 0 0 0". J'aimerai un code qui puisse déterminer quel numéro correspond à quel channel, et aussi si la valeur assigné au channel a changé ou non, de manière à alleger la masse de donnée SPI.

EX: 1_ "0 0 0 0 0 0" => Serial => Channel 1 à 6 = 0 10ms 2_ "0 0 255 0 0 0" => Serial => Channel 3 = 255 10ms 3_ "255 0 255 0 0 255" => Serial => Channel 1=255 et Channel 6=255

etc..

“La masse de données” ==> avec une vitesse > 1mhz impossible de ressentir une latence :wink:

Tu peux utiliser strtol : strtol - C++ Reference

char Buffer[50];
char *pEnd;
long Channel[6];
int i;
....

if(Serial.available()){

delay(20);

for(i=0;i<50;i++) Buffer[i]=NULL;

i=0;
while (Serial.available()) Buffer[i++]=Serial.read();

Channel[0] = strtol (Buffer,&pEnd,10);
for (i=1;i<6;i++)  Channel[i] = strtol (pEnd,&pEnd,10);

// et voila Channel[] est rempli de tes valeurs de canaux

}

Intéressant ! Merci pour la piste !

Je précise que je suis vraiment newb en C++, mais à priori je comprends que Channel[] est une inconue, donc dans ma liste "0 0 255 0 0 0", Channel[3]=255 c'est ça ?

Du coup mon code donnerait quelque chose de ce genre :

Channel1 = 1 Value1 = Channel[1] Trasfert SPI Channel1, Value1

Channel2 = 2 Value2 = Channel[2] Transfert SPI Channel2, Value2

Channel3 = 3 ETC....

Du coup, à chaques fois transferer les 6 channel toutes les 10ms ?

Ya peut être une méthode plus élegante j'imagine ?

Merci en tout cas pour la piste :)

Oui c'est bien ça. Channel[] est un tableau de variable de type char (inconnue c'est pour les math ;) )

Je comprends pas par contre pourquoi tu tiens à tes 10 ms. Tu les sors d'où ?

Après je n'ai pas compris ce qui est connecté en SPI : juste 6 DAC ? Tu as la datasheet ?

EDIT : trouvée http://www.pucktronix.com//media/pucktronix/USB-Octomod_02_schem.pdf http://datasheets.maximintegrated.com/en/ds/MAX5250.pdf

mais du coup je vois pas où tu te situes : tu recodes la teensy ?

Arf biensur j'aurais du commencer par le commencement... !

Je contrôle un potard digital 6 channels (AD5206) pour contrôler des volumes.

Je lui envoie actuellement un message comme ça : 0 255 puis 1 255 puis 2 0 etc.. Les valeurs sont envyés toutes les 10ms dans max (sinon la puce réponds mal, peut être due au delay du sketch Arduino). Ce qui me fait au final 60ms pour que toutes les valeures s'actualisent..

Et du coup, j'aimerai actualisé toutes les 10ms en ne lui envoyé qu'un message avec toutes les channels..

C'est plus clair ?

Merci de tes réponses !

Oui mais que viens faire l'Arduino la dedans ? Sur le site il est dit que l'Octomod est mu par une Teensy

Oui il utilise le teensy par soucis pratique, mais son programme marche très bien avec Arduino avec quelques modifications.

Nan c’est certain mais c’est pas l’IDE Arduino :wink: Même s’il est directement inspiré. C’est juste que pour l’instant tu es dans ton projet donc toi tu sais de quoi tu causes, il est nullement mention depuis le début de ton topic d’une teensy. Pour bien aider il faut savoir déjà de quoi l’on parle :wink:

Bref. Du coup je ne vois pas toujours pas pourquoi il faut 10 ms. Si je suis la datasheet, ceci devrait fonctionner (pour le premier Max5250) :

byte Byte1,Byte2;

....

for(int i=0;i<4;i++) {

Byte1 = Byte1<<3 & 1;
Byte1 = Byte1<<4 & (Channel[i]>>6 & 0b1111);

Byte2=Channel[i] & 0b111111;
Byte2=Byte2<<2;

digitalWrite(CS_1,LOW);
SPI.tranfer(Byte1);
SPI.tranfer(Byte2);
digitalWrite(CS_1,HIGH);

}

digitalWrite(CS_1,LOW);
SPI.tranfer(0b01000000);
SPI.tranfer(0);
digitalWrite(CS_1,HIGH);

Oui, c’est sur que c’est pas forcement très clair quand on a pas tous les élements :smiley:

Voilà donc mon projet :

J’utilise le potentiometre numérique AD5206 (http://www.analog.com/static/imported-files/data_sheets/AD5204_5206.pdf)

J’utilise les 6 potentiometre en diviseur de tension pour contrôler 6 volumes. J’ai utilisé l’Octomod de Greg Surge comme piste de travail mais j’ai cru comprendre que la communication SPI avec le MAX5250 n’était pas tout à fait au même format que pour l’AD5206…

Je contrôle la bête depuis MAX/MSP via serial en lui envoyant un message type “channelX value”. Mon code ne permet pas l’envoie de toutes les valeurs dans un même message, et il semble que le temps d’actualisation ne peux pas aller en dessous de 10ms sinon la puce ne répond plus.
Ca ne me poserai pas de soucis si je pouvais rafraichir TOUS les canaux toutes les 10ms, or avec mon système il me faut 60ms pour faire le tour des canaux, c’est bien trôp long pour moi…

J’ai essayé une alternative en dupliquant le système Serial.read() => transfert SPI canal par canal. Voilà ce que ça doonne :

// inslude the SPI library:
#include <SPI.h>


// set pin 10 as the slave select for the digital pot:
const int slaveSelectPin = 10;
byte channel1, channel2, channel3, channel4, channel5, channel6;
byte firstByte, secondByte, thirdByte, fourthByte, fifthByte, sixthByte, seventhByte;
boolean action;

void setup() {
  // set the slaveSelectPin as an output:
  pinMode (slaveSelectPin, OUTPUT);
  // initialize SPI:
  Serial.begin(115200);
  SPI.begin();
 
}

void loop(){
  pollAndWrite();
} 


void pollAndWrite(){
  action = false;
  while(!action){
   // check if data has been sent from the computer:
  if (Serial.available() > 1) {
    // read the most recent byte (which will be from 0 to 255):
    firstByte = Serial.read();
    delayMicroseconds(100);
    if (firstByte == 0) {
      secondByte = Serial.read();
      channel1 = byte(0);
      delayMicroseconds(10);
     // take the SS pin low to select the chip:
      digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
      SPI.transfer(channel1);
      SPI.transfer(secondByte);
      delayMicroseconds(10);
  // take the SS pin high to de-select the chip:
      digitalWrite(slaveSelectPin,HIGH);
      thirdByte = Serial.read();
      channel2 = byte(1);
      delayMicroseconds(10);
     // take the SS pin low to select the chip:
      digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
      SPI.transfer(channel2);
      SPI.transfer(thirdByte);
      delayMicroseconds(10);
  // take the SS pin high to de-select the chip:
      digitalWrite(slaveSelectPin,HIGH);
      fourthByte = Serial.read();
      channel3 = byte(2);
      delayMicroseconds(10);
     // take the SS pin low to select the chip:
      digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
      SPI.transfer(channel3);
      SPI.transfer(fourthByte);
      delayMicroseconds(10);
  // take the SS pin high to de-select the chip:
      digitalWrite(slaveSelectPin,HIGH);
      fifthByte = Serial.read();
      channel4 = byte(3);
      delayMicroseconds(10);
     // take the SS pin low to select the chip:
      digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
      SPI.transfer(channel4);
      SPI.transfer(fifthByte);
      delayMicroseconds(10);
  // take the SS pin high to de-select the chip:
      digitalWrite(slaveSelectPin,HIGH);
      sixthByte = Serial.read();
      channel5 = byte(4);
      delayMicroseconds(10);
     // take the SS pin low to select the chip:
      digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
      SPI.transfer(channel5);
      SPI.transfer(sixthByte);
      delayMicroseconds(10);
  // take the SS pin high to de-select the chip:
      digitalWrite(slaveSelectPin,HIGH);
      seventhByte = Serial.read();
      channel6 = byte(5);
      delayMicroseconds(10);
     // take the SS pin low to select the chip:
      digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
      SPI.transfer(channel6);
      SPI.transfer(seventhByte);
      delayMicroseconds(10);
  // take the SS pin high to de-select the chip:
      digitalWrite(slaveSelectPin,HIGH);
      action = true;
   } 
if (firstByte == 1) {
      secondByte = Serial.read();
      delayMicroseconds(100);
      thirdByte = Serial.read();
   // take the SS pin low to select the chip:
      digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
      SPI.transfer(secondByte);
      SPI.transfer(thirdByte);
      delayMicroseconds(10);
  // take the SS pin high to de-select the chip:
      digitalWrite(slaveSelectPin,HIGH); 
     }
   }
  }
}

J’envoie donc un message du style “255 138 245 0 255 12” en pensant qu’avec serial.read() j’aurai accès à toutes les valeurs. EX:

Serial.read() = 255
transfert SPI Chan1, Serial.read
Serial.read() = 138
transfert SPI Chan2, Serial.read
Serial.read() = 245
transfert SPI Chan3, Serial read
Serial.read() = 0
transfert SPI Chan4, Serial read
etc etc…

Or ca fonctionne à partir du môment ou je ne duplique ce schema que 2 fois pour les deux premières channel (2 fois Serial.read()=>TransfertSPI). Dès que j’en rajoute un troisième, la puce ne comprends répond mais ne comprends n’assigne plus les valeurs aux bon canaux.

De toute manière, cette méthode n’est pas très élegante :). Revenons donc aux pistes que tu m’a données :

Donc si j’ai bien compris ton code, voilà ce que cela donnerai :

// inslude the SPI library:
#include <SPI.h>


// set pin 10 as the slave select for the digital pot:
const int CS_1 = 10;
byte Byte1,Byte2;
char Buffer[50];
char *pEnd;
long Channel[6];
int i;

void setup() {
  // set the slaveSelectPin as an output:
  pinMode (CS_1, OUTPUT);
  // initialize SPI:
  SPI.begin(); 
}

void loop() {
  
  if(Serial.available()){

delay(20);

for(i=0;i<50;i++) Buffer[i]=NULL;

i=0;
while (Serial.available()) Buffer[i++]=Serial.read();

Channel[0] = strtol (Buffer,&pEnd,10);
for (i=1;i<6;i++)  Channel[i] = strtol (pEnd,&pEnd,10);

// et voila Channel[] est rempli de tes valeurs de canaux

  for(int i=0;i<4;i++) {

Byte1 = Byte1<<3 & 1;
Byte1 = Byte1<<4 & (Channel[i]>>6 & 0b1111);

Byte2=Channel[i] & 0b111111;
Byte2=Byte2<<2;

digitalWrite(CS_1,LOW);
SPI.transfer(Byte1);
SPI.transfer(Byte2);
digitalWrite(CS_1,HIGH);

}
}
}

Mais, si je n’ai pas fais d’erreurs, vu que tu considerai que la puce était une MAX5202 je ne suis pas sur que ça marche avec l’AD5206.
Je vais faire le test rapidement et te dire ce qu’il en est.

Merci de ta patience et de ton aide !

Ah oui effectivement la communication change. Au passage je te conseille d’utiliser des fonctions, c’est plus clair et si jamais tu dois t’en resservir ailleurs c’est beaucoup plus commode :

// include the SPI library:
#include <SPI.h>

// set pin 10 as the slave select for the digital pot:
#define CS_1 = 10;

char Buffer[50];
char *pEnd;
long Channel[6];
int i;

void setup() {
  // set the slaveSelectPin as an output:
  pinMode (CS_1, OUTPUT);
  // initialize SPI:
  SPI.begin(); 
}

void loop() {
  
  if(Serial.available()){

ReceptionSerie();
TransferSPI();

            }

}

void ReceptionSerie() {

delay(20);

for(i=0;i<50;i++) Buffer[i]=NULL;

i=0;

while (Serial.available()) Buffer[i++]=Serial.read();

Serial.print("J'ai reçu : ");

Channel[0] = strtol (Buffer,&pEnd,10);
Serial.print(Channel[0]);

for (i=1;i<6;i++)  {
Serial.print(",");
Channel[i] = strtol (pEnd,&pEnd,10);
Serial.print(Channel[i]);
}

Serial.println("");

}


void TranferSPI() {

for(int i=0;i<6;i++) {

digitalWrite(CS_1,LOW);
SPI.transfer(i);
SPI.transfer(Channel[i]);
digitalWrite(CS_1,HIGH);

// delayMicroseconds(100); // à décommenter si jamais ça ne marche pas

}

Je viens d’essayer ton code, tout semble bien marcher quand j’envoie des données depuis le Serial Monitor, la puce réponds bien et pas de latence apparente !
Mais par contre je n’arrive pas à communiquer depuis MAX/MSP alors que j’envoie les même messages !

Faut il les convertir dans un systèmes numérique autre ? (style hexadecimal, binaire… ?)

voilà ton code tel que je l’utilise :

// include the SPI library:
#include <SPI.h>

// set pin 10 as the slave select for the digital pot:
#define CS_1 10

// const int CS_1 = 10;
char Buffer[50];
char *pEnd;
long Channel[6];
int i;

void setup() {
  // set the slaveSelectPin as an output:
  pinMode(CS_1, OUTPUT);
  // initialize SPI:
  Serial.begin(115200);
  SPI.begin(); 
}

void loop() {
  
  if(Serial.available()){

ReceptionSerie();
TransferSPI();

            }

}

void ReceptionSerie() {

delay(20);

for(i=0;i<50;i++) Buffer[i]=NULL;

i=0;

while (Serial.available()) Buffer[i++]=Serial.read();

Serial.print("J'ai reçu : ");

Channel[0] = strtol (Buffer,&pEnd,10);
Serial.print(Channel[0]);

for (i=1;i<6;i++)  {
Serial.print(",");
Channel[i] = strtol (pEnd,&pEnd,10);
Serial.print(Channel[i]);
}

Serial.println("");

}


void TransferSPI() {

for(int i=0;i<6;i++) {

digitalWrite(CS_1,LOW);
SPI.transfer(i);
SPI.transfer(Channel[i]);
digitalWrite(CS_1,HIGH);

delayMicroseconds(100); // à décommenter si jamais ça ne marche pas

}
}

Dans mon code il n'y aucune latence, c'est normal, mis à part le delayMicroseconds() normalement inutile (au pire j'ai mis 100 mais même à 10 ça devrait marcher).

Pour MSP comme je t'ai dit, jamais touché. Mais vraissemblablement il émet en binaire, il faut utiliser Spell pour envoyer en ASCII : http://www.cycling74.com/docs/max5/refpages/max-ref/spell.html

Dans tous les cas c'est pas dramatique, on peut modifier le code côté Arduino pour recevoir du binaire.

Avec spell ça communique bien, merci !

par contre, la communication Serial fait planter MAX au bout de quelques secondes de transfert ! J'ai essayé plusieurs methodes : envoyer la liste toutes les 10ms 20, envoyer la liste que quand elle change et ce tout les 10 ou 20ms.. Rien n'y fait, le problème n'a pas l'air de venir de MAX...

Je vais continuer à faire quelques test, mais ca a l'air prometteur !

Encore merci de ton aide preciseuse

Bonjour,

j’ai fais quelques test et le résultat a l’air prometteur ( :D)!

Ca communique bien, mais il y a un soucis de répartitions des channel. Le fait de changer la valeur d’un channel peut en affecter un autre, assez chaotiquement. Je pense que cela est du à la conversion int => ASCII via spell.
En effet, la longeur du message envoyé varie beaucoup vu que j’envoie 6 valeures numériques de 0 à 255, valeure qui peux prendre la place d’un, deux ou trois character ASCII. Je me retrouve donc avec une liste qui peux faire de 11 à 23 caractères (en comptant les espaces)

Exemple de message envoyé:

0 0 0 0 0 0 => spell => 48 32 48 32 48 32 48 32 48 32 48 => Serial

0 0 255 0 0 0 => spell=> 48 32 48 32 50 53 53 32 48 32 48 32 48 => Serial

le code arduino

// include the SPI library:
#include <SPI.h>

// set pin 10 as the slave select for the digital pot:
#define CS_1 10

// const int CS_1 = 10;
char Buffer[50];
char *pEnd;
long Channel[6];
int i;

void setup() {
  // set the slaveSelectPin as an output:
  pinMode(CS_1, OUTPUT);
  // initialize SPI:
  Serial.begin(9600);
  SPI.begin(); 
}

void loop() {
  
  if(Serial.available()){

ReceptionSerie();
TransferSPI();

            }

}

void ReceptionSerie() {

delay(20);

for(i=0;i<50;i++) Buffer[i]=NULL;

i=0;

while (Serial.available()) Buffer[i++]=Serial.read();

//Serial.print("J'ai reçu : ");

Channel[0] = strtol (Buffer,&pEnd,10);
//Serial.print(Channel[0]);

for (i=1;i<6;i++)  {
//Serial.print(",");
Channel[i] = strtol (pEnd,&pEnd,10);
//Serial.print(Channel[i]);
}

//Serial.println("");

}


void TransferSPI() {

for(int i=0;i<6;i++) {

digitalWrite(CS_1,LOW);
SPI.transfer(i);
SPI.transfer(Channel[i]);
digitalWrite(CS_1,HIGH);

//delayMicroseconds(10); // à décommenter si jamais ça ne marche pas

}
delayMicroseconds(100);
}

Je pense qu’il serait peut être plus pratique de convertir les entiers en ASCII dans Arduino plutôt que dans MAX. Comment peut on faire ça ?

Merci de votre aide !

Salut,

Il faut utiliser les espaces pour séparer les différentes consignes

Ok, ça me semblait aussi être la piste à emprunter. Cela dit, de la à coder ça en C, c'est une autre paire de manche pour moi !

Pourrais tu m'indiquer les fonctions à utiliser ?

Merci encore de ton aide précieuse !

En reprenant un des premiers code que je t’avais donné :

char Buffer[50];
char *P;
long Channel[6];
int i;
....

if(Serial.available()){

delay(20);

for(int i=0;i<50;i++) Buffer[i]={'\0'};

int y=0;

while (Serial.available()) Buffer[y++]=Serial.read();

for (int g=0;g<6;g++)   {

P = strtok (BufferSerie, " ");
Channel[g] = strtol (P,NULL,10);

}


}