Go Down

Topic: lire des valeurs sur Arduino en c / c++ (Read 7150 times) previous topic - next topic

Jean-François

Mar 21, 2008, 08:23 pm Last Edit: Mar 22, 2008, 11:00 am by jfs Reason: 1
Je cherchais un moyen de lire des valeurs sur l'Arduino avec mon Mac depuis quelques temps...

En furetant sur le net j'ai trouvé ça :

How to talk to the USB Arduino board with your  C++ Code and Xcode  (Comment communiquer via USB avec Aduino en C++ et avec Xcode)


Le problème c'est que ce code montre bien comment envoyer des données à l'Arduino, mais pas comment les lire.


Ensuite j'ai trouvé ça :

Création d'une centrale interactive de capteurs

Projet dans lequel j'ai récupéré un bout de code pour modifier le premier.

Ce qui donne :

Code: [Select]

#include <iostream>
#include <stdio.h>
#include <fcntl.h>  
#include <termios.h>
#include <unistd.h> //write(), read()

using namespace std;



int fd;


/******************************************************************************************************************
     Setting the serial port up to talk to the Arduino board
******************************************************************************************************************/
void init_port(int *fd, unsigned int baud)
{
   struct termios options;
   tcgetattr(*fd,&options);
   switch(baud)
   {
      case 9600: cfsetispeed(&options,B9600);
cfsetospeed(&options,B9600);
break;
      case 19200: cfsetispeed(&options,B19200);
cfsetospeed(&options,B19200);
break;
      case 38400: cfsetispeed(&options,B38400);
cfsetospeed(&options,B38400);
break;
 default:cfsetispeed(&options,B9600);
cfsetospeed(&options,B9600);
break;
   }
   options.c_cflag |= (CLOCAL | CREAD);
   options.c_cflag &= ~PARENB;
   options.c_cflag &= ~CSTOPB;
   options.c_cflag &= ~CSIZE;
   options.c_cflag |= CS8;
   tcsetattr(*fd,TCSANOW,&options);
}

/******************************************************************************************************************
     Main Function
******************************************************************************************************************/
int main()
{
     char USBPort[1000];
     
     cout << "what USB port is your Arduino Board pluged into?" << endl;
     cout << "Example:\n/dev/tty.usbserial-A70041zl\nor\n/dev/tty.usbserial-A1001N2Y" << endl;
     cin >> USBPort;
     
     fd = open(USBPort, O_RDWR | O_NOCTTY | O_NDELAY);
   if(fd == -1)
     perror("open_port: unable to open port");
     
     init_port(&fd,9600);         //set serial port to 9600,8,n,1
     
     
   
     fcntl(fd, F_SETFL, 0);

     char readport();


     int Temp,Temp1,Temp2 = 0;
     
     char table[5]={0};
     
     read(fd,&Val,5);

     cout<<Val<< endl;
     cout<<Val[2]<<Val[3]<<Val[4]<< endl;
     cout<<Val[0]<<Val[1]<<endl;
     
     Temp1 = Val[2]-'0';       // convert ASCII
     Temp2 = Val[3]-'0';       // to
     Temp3 = Val[4]-'0';       //decimal
     
     Temp = Temp1*100 + Temp2*10 + Temp3 ;      


     cout<<endl<<Temp<<endl;

     close (fd)     //close USBPort
     
     return 0;
     
     

}






Ainsi cela me permet de lire des données en ASCII envoyée par l'Arduino par ce code :

Code: [Select]

int Test1 = 0;

void setup()
{

 Serial.begin(9600);
}

void loop () {

Temp1 =123;
Serial.print("ok");
Serial.println(Test1);

}



Et voilà ce qui sort à la console :

Code: [Select]

[Session started at 2008-03-21 19:49:38 +0100.]
what USB port is your Arduino Board pluged into?
Example:
/dev/tty.usbserial-A70041zl
or
/dev/tty.usbserial-A1001N2Y
/dev/tty.usbserial-A1001N2Y
ok123
123
ok

Executable "digit" has exited with status 123.



C'est peut-être rudimentaire, mais comme je ne connais pas le langage C (je commence à apprendre  :)), c'est un grand pas pour moi.
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Jean-François

J'ai un peu modifié mes codes sur l'Arduino :

Code: [Select]

int CaptPin05 = 5;

void setup()
{
pinMode(CaptPin05,INPUT);
 Serial.begin(9600);
}


void loop ()
{
Serial.print("ok");
Serial.println(analogRead(CaptPin05));

}



J'utilise un capteur de luminosité sur un pin analogique, donc je devrais avoir une valeur comprise entre 0 et 1023
à lire.

J'ai deux problèmes, le premier est que la lecture n'est pas régulière et de temps en temps un chiffre manque ou alors c'est une lettre du "ok" qui sort et je ne comprend pas pourquoi.

Le deuxième problème est qu'avec la méthode que j'ai utilisé pour convertir mes "lectures" d'Ascii à Décimal, lorsque je dois afficher des valeurs en dessous du millier, la valeur s'affiche, mais elle est suivie d'un ou plusieurs chiffres incohérent et je n'arrive pas à résoudre cela.
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Skall

Salut,

a mon avis, ton probleme vient du fait que ton soft sur PC lit 5 caraecters puis s'arrete (je ne me trompe pas ? )
sauf que l'arduino, elle, crache en continue des choses sur son port série. si je regarde le code arduino, ca devrait ecrire :
'ok' suivi d'une valeur entre 0 et 1023, suivi d'un retour chariot.

une valeur entre 0 et 1023, ca correspond a 1, 2 3 ou 4 caracteres...

donc il faut que tu lises ton port serie juqu'a recevoir un retour chariot.

Ensuite, il y a des buffers sur la liaison serie, donc si tu lis systematiquement 5 caracteres, ben tu va avoir
un premier message plus le debut du suivant, puis a la lecture suivante, tu auras la fin du second message et le debut du 3eme, etc...
tous tes soucis viennent du fait que tu lis 5 caracteres, et non pas caracteres par caracteres en regardant si c'est le carac lu est le dernier d'un message...









Jean-François

OK Pascal, merci pour la réponse.


Donc si j'ai bien compris je suis obligé de lire le flux caractère par caractère jusqu'à trouver un \n ou \r ?

Est ce qu'en utilisant un fgets() à la place de read() cela marcherait, normalement le fgets devrait lire mon string en entier et s'arrêter à \n ?
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Skall

#4
Apr 10, 2008, 08:40 pm Last Edit: Apr 10, 2008, 08:41 pm by Skall Reason: 1
oui en theorie fgets doit suffire je pense...

Jean-François

#5
Apr 10, 2008, 09:24 pm Last Edit: Apr 10, 2008, 10:19 pm by jfs Reason: 1
J'ai fait des essais avec fgets, mais ça ne renvoit aucune valeurs du flux.
La communication avec l'arduino n'est même pas amorcée, les leds Rx/Tx restent éteintes.

Voici la façon dont j'essaye de l'utiliser :
Code: [Select]
     

     fd = open(USBPort, O_RDWR | O_NOCTTY | O_NDELAY);
   if(fd == -1)
     perror("open_port: unable to open port");

     init_port(&fd,9600);         //set serial port to 9600,8,n,1
     
   
     fcntl(fd, F_SETFL, 8);
     


     char readport();

     
     
     
     char table[100]={0};
           
     
     
     char fgets(table,99,fd);


     
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Skall

#6
Apr 11, 2008, 11:55 am Last Edit: Apr 11, 2008, 11:55 am by Skall Reason: 1
pourquoi tu passes 8 comme argument a ton fcntl ?

autre chose : tu n'as pas besoin de mettre char readport();,
readport(); suffit, pareil pour le fgets...
fgets(able,99,fd); suffit.


Jean-François

Je devrais mettre quoi pour F_SETFL ?


Si je ne mets pas les char ça plante , si j'ai de la chance ça me sort de l'application, autrement je suis obligé d'éteindre mon ordi de façon barbare... et j'ai la même chose lorsque la communication avec l'arduino est coupée au milieu d'un flux, je suis planté... planté.
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Jean-François

#8
Apr 13, 2008, 07:13 pm Last Edit: Apr 14, 2008, 01:31 pm by jfs Reason: 1
J'ai repris mes codes et j'ai essayé de rendre plus propre celui en C, j'ai rajouté une partie pour commencer la lecture et pour l'arrêter à la fin du string.
J'ai rajouté une ouverture de fenêtre (merci le site du zéro) pour avoir les valeurs imprimées dessus en parallèle à la console.
Le problème de la position des milliers, centaines et dizaines est traité directement sur l'arduino en ajoutant des "0" suivant l'échelle dans laquelle on se trouve, la lecture est ainsi stabilisée.
J'ai rajouter une fonction qui me permet de détecter le(s) port(s) USB utilisé(s), cela s'affiche dans la console.
L'application est toujours démarrée depuis la console.


Le main sur mon mac (Xcode) :

Code: [Select]

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL_ttf/SDL_ttf.h>



#include <fcntl.h>
#include <termios.h>  
//#include <memory.h>
#include <unistd.h> //write(), read()

using namespace std;

void Output();          // pour envoyer à l'arduino
void readport();        // pour recevoir de l'arduino
void init_port();       //initialise le portUSB
void Ouverture_port();  //ouvre le portUSB


char USBPort[1000];                  // pour le nom du portUSB
char temps[20] = "";            //pour déterminer le temps
char Temperature [5]={0};      //pour la valeur écrite dans la fenêtre
char table[7]={0};                  //pour les valeurs en sortie d'arduino

int fd = 0;
int continuer = 1;
long Temp,Temp1,Temp2,Temp3 ,Temp4,Temp5  = 0;




int main(int argc, char *argv[])
{
     /******************************************
     détection du port usb
     *******************************************/
     FILE* myPipe=NULL;
     char buffer[1000];
     myPipe = popen("ls /dev/tty.usbserial*", "r");
     if(myPipe==NULL){
           //errorstuff
     }
     while(fgets(buffer, 1000, myPipe)!=NULL){
           (void) printf("\n\nvotre usb est : %s \n", buffer);
     }      
     
     
     pclose(myPipe);
     
     /*******************************************
     fin de détection
     ********************************************/
                 

     
     cout << "what USB port is your Arduino Board pluged into?" << endl;
     cout << "Example:\n/dev/tty.usbserial-A70041zl\nor\n/dev/tty.usbserial-A1001N2Y" << endl;      
     
     cin >> USBPort;

     

   SDL_Surface *ecran = NULL, *texte = NULL;
   SDL_Rect position;
   SDL_Event event;
   TTF_Font *police = NULL;
   SDL_Color couleurNoire = {0, 0, 0}, couleurBlanche = {255, 255, 255};


   SDL_Init(SDL_INIT_VIDEO);
   TTF_Init();

   ecran = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
   SDL_WM_SetCaption("Mesure de luminosité", NULL);


   police = TTF_OpenFont("angelina.ttf", 65);

   tempsActuel = SDL_GetTicks();



   while (continuer)
           {
       SDL_PollEvent(&event);
       switch(event.type)
       {
           case SDL_QUIT:
               continuer = 0;
                       close(fd);
               break;
       }

       SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 255, 255, 255));

           
     Ouverture_port();
     Output();
     readport();
     close(fd);
           

                 sprintf(Temperature, "%ld%ld%ld%ld", Temp1, Temp2, Temp3, Temp4);      
     
     cout<<table[0]<<table[1]<<Temperature<<endl;
           
           sprintf(temps, "Temp : %s", Temperature);
           SDL_FreeSurface(texte);
           texte = TTF_RenderText_Shaded(police, temps, couleurNoire, couleurBlanche);
         

       position.x = 180;
       position.y = 210;
       SDL_BlitSurface(texte, NULL, ecran, &position); /* Blit du texte contenant le temps */
       SDL_Flip(ecran);

           }
     
     
     
           TTF_CloseFont(police);
           TTF_Quit();

           SDL_FreeSurface(texte);
           SDL_Quit();


   return EXIT_SUCCESS;
     }





Les fonctions :

Code: [Select]

void init_port(int *fd, unsigned int baud)
     {
   struct termios options;
   tcgetattr(*fd,&options);
   switch(baud)
   {
      case 9600: cfsetispeed(&options,B9600);
     cfsetospeed(&options,B9600);
     break;
      case 19200: cfsetispeed(&options,B19200);
     cfsetospeed(&options,B19200);
     break;
      case 38400: cfsetispeed(&options,B38400);
     cfsetospeed(&options,B38400);
     break;
     default:cfsetispeed(&options,B9600);
     cfsetospeed(&options,B9600);
     break;
   }
   options.c_cflag |= (CLOCAL | CREAD);
   options.c_cflag &= ~PARENB;
   options.c_cflag &= ~CSTOPB;
   options.c_cflag &= ~CSIZE;
   options.c_cflag |= CS8;
   tcsetattr(*fd,TCSANOW,&options);
     }
     
     
void Ouverture_port()
           {
           fd = open(USBPort, O_RDWR | O_NOCTTY | O_NDELAY);
   if(fd == -1)
     perror("open_port: unable to open port");

     init_port(&fd,9600);         //set serial port to 9600,8,n,1
     fcntl(fd, F_SETFL,3);
     }


void Output()
     {
     
     write(fd,"&",1);
     }
           
     
void readport()
     {
     char table[7]={0};
     {      
     while(table[6]!='$')
     {
     read(fd,&table,sizeof(table));
     
     Temp1 = table[2]-'0';
     Temp2 = table[3]-'0';
     Temp3 = table[4]-'0';
     Temp4 = table[5]-'0';

     }
     }

     }


     
     
     




Ainsi j'obtiens un renouvellement de l'affichage des mesures environ toutes les secondes.

J'aimerais savoir si c'est possible de rendre cela plus rapide ?
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Jean-François

#9
Apr 13, 2008, 07:15 pm Last Edit: Apr 13, 2008, 07:21 pm by jfs Reason: 1
Le .pde sur l'Arduino

Code: [Select]

int CaptPin05 = 5;
//int Temp1 = 0;
char incoming = 0;
void setup()
{
pinMode(CaptPin05,INPUT);
 Serial.begin(9600);
}



void loop () {
//Temp1 = analogRead(CaptPin05);

//Temp1 =123;

incoming=Serial.read();
if (incoming = '&')
{
Serial.print("ok");
//Serial.print("yes");
/* mise à niveau des chiffres */
if(analogRead(CaptPin05)<1000)
{
 Serial.print("0");
}
if(analogRead(CaptPin05)<100)
{
 Serial.print("0");
}
if(analogRead(CaptPin05)<10)
{
 Serial.print("0");
}
/* fin de mise à niveau */

Serial.print(analogRead(CaptPin05));
Serial.print("$");
incoming = 0 ;
}
}




Ainsi j'obtiens un renouvellement de l'affichage des mesures environ toutes les secondes.

J'aimerais savoir si c'est possible de rendre cela plus rapide ?
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Skall

oui, ca peut etre plus rapide,

Tout d'abord, tu peux augmenter la vitesse de ton port serie. Moi, je tourne en 19200.

Mais ca peut surtout etre plus efficace :

Perso, j'ai ajouté une condition  : je ne remonte la valeur au PC que si elle a changé.
Tu peux meme introduire une notion de seuil : ne remonter la valeur qui si elle a change de plus de XX%..
mais la, ca depend de ce que tu veux faire...

Jean-François

Merci pour ces réponses, je vais tester ça...
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Jean-François

Après essais ça ne change rien à la vitesse d'affichage des mesures.

Je pense que c'est le fait d'ouvrir et fermer le port usb à chaque boucle qui ralenti le tout.

Si je déplace la fonction d'ouverture et celle de fermeture ça plante.
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Jean-François

En mettant la lecture du port dans un thread et l'affichage dans le main, je peux ouvrir le port en début d'application et le fermer à la fin de l'application.
Sans l'affichage graphique (affiché dans la console) la fréquence de mesure est de 130 par seconde, cette fréquence tombe à 50 par seconde avec l'affichage dans une fenêtre graphique.

MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5
imac PPC G3 os X Pa

Skall

la, ca devient un probleme de programmation mac...  :-X

tu dois surement avoir un mecanisme de synchro entre ta thread de lecture de l'arduino, et la partie graphique qui fait que ca ralentit... (mutexs, sections critique ou autre).
si tu veux plus de vitesse, va falloir que tu t'embringues dans des traitements asynchrones, et je te souhaite bien du plaisir....

tu as vraiment besoin de lire si vite ?
et si tu ne remontes la valeur que si elle change, ca ne resoud pas ton probleme ?

au fait, c'est quoi le but final de ton projet ? (si c'est pas indiscret)

bonne chance.

Go Up