Warning: putenv() has been disabled for security reasons in /home/users4/d/debrouilloweb/www/wikidebrouillard/LocalSettings.php on line 193

Warning: putenv() has been disabled for security reasons in /home/users4/d/debrouilloweb/www/wikidebrouillard/LocalSettings.php on line 197

Warning: putenv() has been disabled for security reasons in /home/users4/d/debrouilloweb/www/wikidebrouillard/includes/parser/Parser.php on line 2338

Warning: putenv() has been disabled for security reasons in /home/users4/d/debrouilloweb/www/wikidebrouillard/includes/parser/Parser.php on line 2338

Warning: putenv() has been disabled for security reasons in /home/users4/d/debrouilloweb/www/wikidebrouillard/includes/parser/Parser.php on line 2338

Warning: putenv() has been disabled for security reasons in /home/users4/d/debrouilloweb/www/wikidebrouillard/includes/parser/Parser.php on line 2338

Warning: putenv() has been disabled for security reasons in /home/users4/d/debrouilloweb/www/wikidebrouillard/includes/parser/Parser.php on line 2338

Warning: putenv() has been disabled for security reasons in /home/users4/d/debrouilloweb/www/wikidebrouillard/includes/parser/Parser.php on line 2338
[ Wikidébrouillard ] Bras Robotisé piloté par nunchuk arduino

Bras Robotisé piloté par nunchuk arduino

De Wikidebrouillard.

(Schéma Fritzing)
 
(7 versions intermédiaires masquées)
Ligne 1 : Ligne 1 :
{{avertissement}}
{{avertissement}}
-
{{vidéo|numérovidéo = <videoflash type="mediaspip" num ="1">http://mediaspip.ptitdeb.infini.fr/IMG/flv/video_apdb_arduino_Vumetre_sapik-encoded.flv|400|300</videoflash>}}
+
{{vidéo|numérovidéo = <videoflash type="mediaspip" num ="1">http://mediaspip.ptitdeb.infini.fr/sites_ms/mediaspip.ptitdeb.infini.fr/IMG/flv/videotutorielarduino-encoded.flv|400|300</videoflash>}}
==Présentation du projet Arduino==
==Présentation du projet Arduino==
Ligne 21 : Ligne 21 :
Quand on bouge la nunchuk de haut en bas (avec le poignet), le servomoteur qui est collé à la paille se met en action.
Quand on bouge la nunchuk de haut en bas (avec le poignet), le servomoteur qui est collé à la paille se met en action.
Quand on bouge la nunchuk sur le même axe (avec le poignet, comme si on vissait), le servomoteur à la base se met à pivoter.
Quand on bouge la nunchuk sur le même axe (avec le poignet, comme si on vissait), le servomoteur à la base se met à pivoter.
 +
 +
Par défaut, on est en mode accéléromètre.
 +
Quand on appui sur la touche C, on passe en mode joystick.
 +
 +
Quand on décale le joystick de gauche à droite, on contrôle le servomoteur qui est à la base.
 +
Quand on decale le joystick de haut en bas, on contrôle le servomoteur qui tient la paille.
 +
 +
 +
Quand on appui sur la touche Z, on remet le "robot" en position de départ.
 +
 +
===Cablage sur le nunchuk===
 +
La photo qui suit nous montre à quoi correspond chaque broche du câble Nunchuck Wii.
 +
 +
[[Fichier:Nunchuck-cable-schema.png|200px]]
===Schéma Fritzing===
===Schéma Fritzing===
Ligne 28 : Ligne 42 :
===Code===
===Code===
<pre>
<pre>
-
#include <Servo.h>;
+
#include <Wire.h>
-
#include <Wire.h>;
+
#include <math.h>
 +
#include <Servo.h>
-
// Doit être ajusté en fonction de chaque nunchuck
+
// these may need to be adjusted for each nunchuck for calibration
-
#define ZEROX 530 
+
#define ZEROX 510
-
#define ZEROY 530
+
#define ZEROY 490
-
#define ZEROZ 530
+
#define ZEROZ 460
-
// adresse I2C du nunchuck
+
#define DEFAULT_ZERO_JOY_X 124
-
#define WII_NUNCHUK_I2C_ADDRESS 0x52
+
#define DEFAULT_ZERO_JOY_Y 132
-
// définition d'une variable Servo
+
class WiiChuck {
 +
private:
 +
  uint8_t cnt;
 +
  uint8_t status[6];      // array to store wiichuck output
 +
  uint8_t averageCounter;
 +
  int i;
 +
  int total;
 +
  uint8_t zeroJoyX;  // these are about where mine are
 +
  uint8_t zeroJoyY; // use calibrateJoy when the stick is at zero to correct
 +
  int lastJoyX;
 +
  int lastJoyY;
 +
  int angles[3];
 +
 
 +
  bool lastZ, lastC;
 +
 
 +
 
 +
public:
 +
 
 +
  uint8_t joyX;
 +
  uint8_t joyY;
 +
  bool buttonZ;
 +
  bool buttonC;
 +
  void begin()
 +
  {
 +
    Wire.begin();
 +
    cnt = 0;
 +
    averageCounter = 0;
 +
    // instead of the common 0x40 -> 0x00 initialization, we
 +
    // use 0xF0 -> 0x55 followed by 0xFB -> 0x00.
 +
    // this lets us use 3rd party nunchucks (like cheap $4 ebay ones)
 +
    // while still letting us use official oness.
 +
    // only side effect is that we no longer need to decode bytes in _nunchuk_decode_byte
 +
    Wire.beginTransmission(0x52);  // device address
 +
    Wire.write(0xF0);
 +
    Wire.write(0x55);
 +
    Wire.endTransmission();
 +
    delay(1);
 +
    Wire.beginTransmission(0x52);
 +
    Wire.write(0xFB);
 +
 
 +
    Wire.write(0x01);
 +
    Wire.write((uint8_t)0x00);
 +
 
 +
    Wire.endTransmission();
 +
    update();         
 +
    for (i = 0; i<3;i++) {
 +
      angles[i] = 0;
 +
    }
 +
    zeroJoyX = DEFAULT_ZERO_JOY_X;
 +
    zeroJoyY = DEFAULT_ZERO_JOY_Y;
 +
  }
 +
 
 +
 
 +
  void calibrateJoy() {
 +
    zeroJoyX = joyX;
 +
    zeroJoyY = joyY;
 +
  }
 +
 
 +
  void update() {
 +
 
 +
    Wire.requestFrom (0x52, 6); // request data from nunchuck
 +
    while (Wire.available ()) {
 +
      // receive byte as an integer
 +
      status[cnt] = _nunchuk_decode_byte (Wire.read()); //
 +
      cnt++;
 +
    }
 +
    if (cnt > 5) {
 +
      lastZ = buttonZ;
 +
      lastC = buttonC;
 +
      lastJoyX = readJoyX();
 +
      lastJoyY = readJoyY();
 +
      //averageCounter ++;
 +
      //if (averageCounter >= AVERAGE_N)
 +
      //    averageCounter = 0;
 +
 
 +
      cnt = 0;
 +
      joyX = (status[0]);
 +
      joyY = (status[1]);
 +
      for (i = 0; i < 3; i++)
 +
        //accelArray[i][averageCounter] = ((int)status[i+2] << 2) + ((status[5] & (B00000011 << ((i+1)*2) ) >> ((i+1)*2)));
 +
        angles[i] = (status[i+2] << 2) + ((status[5] & (B00000011 << ((i+1)*2) ) >> ((i+1)*2)));
 +
 
 +
      //accelYArray[averageCounter] = ((int)status[3] << 2) + ((status[5] & B00110000) >> 4);
 +
      //accelZArray[averageCounter] = ((int)status[4] << 2) + ((status[5] & B11000000) >> 6);
 +
 
 +
      buttonZ = !( status[5] & B00000001);
 +
      buttonC = !((status[5] & B00000010) >> 1);
 +
      _send_zero(); // send the request for next bytes
 +
 
 +
    }
 +
  }
 +
 
 +
  float readAccelX() {
 +
    // total = 0; // accelArray[xyz][averageCounter] * FAST_WEIGHT;
 +
    return (float)angles[0] - ZEROX;
 +
  }
 +
  float readAccelY() {
 +
    // total = 0; // accelArray[xyz][averageCounter] * FAST_WEIGHT;
 +
    return (float)angles[1] - ZEROY;
 +
  }
 +
  float readAccelZ() {
 +
    // total = 0; // accelArray[xyz][averageCounter] * FAST_WEIGHT;
 +
    return (float)angles[2] - ZEROZ;
 +
  }
 +
 
 +
  bool zPressed() {
 +
    return (buttonZ && ! lastZ);
 +
  }
 +
  bool cPressed() {
 +
    return (buttonC && ! lastC);
 +
  }
 +
 
 +
  int readJoyX() {
 +
    return (int) joyX - zeroJoyX;
 +
  }
 +
 
 +
  int readJoyY() {
 +
    return (int)joyY - zeroJoyY;
 +
  }
 +
 
 +
private:
 +
  uint8_t _nunchuk_decode_byte (uint8_t x)
 +
  {
 +
    //decode is only necessary with certain initializations
 +
    //x = (x ^ 0x17) + 0x17;
 +
    return x;
 +
  }
 +
 
 +
  void _send_zero()
 +
  {
 +
    Wire.beginTransmission (0x52);  // transmit to device 0x52
 +
    Wire.write ((uint8_t)0x00);    // sends one byte
 +
    Wire.endTransmission ();    // stop transmitting
 +
  }
 +
 
 +
};
 +
 
 +
 
 +
WiiChuck chuck = WiiChuck();
Servo servomoteur1, servomoteur2;
Servo servomoteur1, servomoteur2;
 +
int posMoteur1 = 90, posMoteur2 = 90;
 +
int mode=0;
-
// définition d'une variable counter
+
void mode_joystick(int a, int b){
-
int counter;
+
-
int pos = 0;
+
    if(a < -50){ // gauche
-
// définition d'un tableau de données
+
      if(posMoteur1 > 0){
-
uint8_t data[6];
+
        posMoteur1 -= 10;
 +
        servomoteur1.write(posMoteur1);
 +
      }
 +
    }
-
void setup()  
+
    if(a > 50){ //droite
-
{  
+
      if(posMoteur1 < 170){
-
  // on attache le servomoteur à la pin 11 (PWM)
+
        posMoteur1 += 10;
-
  servomoteur1.attach(9);
+
        servomoteur1.write(posMoteur1);
-
  servomoteur2.attach(8);
+
      }
 +
    }
 +
    if(b > 50){ //haut
 +
      if(posMoteur2 < 180){
 +
        posMoteur2 += 10;
 +
        servomoteur2.write(posMoteur2);
 +
      }
 +
    }
-
  // initialisation du nunchuck
+
    if(b < -50){ //bas
-
  Wire.begin();
+
      if(posMoteur2 > 0){
 +
        posMoteur2 -= 10;
 +
        servomoteur2.write(posMoteur2);
 +
      }
 +
    }
 +
}
-
  Wire.beginTransmission(WII_NUNCHUK_I2C_ADDRESS);
+
void mode_accelerometre(int a, int b){
-
   Wire.write(0xF0);
+
   if(a < -50){ // gauche
-
  Wire.write(0x55);
+
      if(posMoteur1 > 0){
-
  Wire.endTransmission();
+
        posMoteur1 -= 10;
 +
        servomoteur1.write(posMoteur1);
 +
      }
 +
    }
-
  Wire.beginTransmission(WII_NUNCHUK_I2C_ADDRESS);
+
    if(a > 50){ //droite
-
  Wire.write(0xFB);
+
      if(posMoteur1 < 170){
-
  Wire.write(0x00);
+
        posMoteur1 += 10;
-
  Wire.endTransmission();
+
        servomoteur1.write(posMoteur1);
 +
      }
 +
    }
 +
 
 +
    if(b > 50){ //haut
 +
      if(posMoteur2 < 180){
 +
        posMoteur2 += 10;
 +
        servomoteur2.write(posMoteur2);
 +
      }
 +
    }
 +
 
 +
    if(b < -50){ //bas
 +
      if(posMoteur2 > 0){
 +
        posMoteur2 -= 10;
 +
        servomoteur2.write(posMoteur2);
 +
      }
 +
    }
}
}
 +
void setup()
 +
{
 +
  Serial.begin(9600);
 +
  servomoteur1.attach(8);
 +
  servomoteur2.attach(9);
-
void loop()  
+
  servomoteur1.write(8);
-
{
+
  servomoteur2.write(8);
-
    // on demande 6 octets au nunchuck
+
-
    Wire.requestFrom(WII_NUNCHUK_I2C_ADDRESS, 6);
+
-
   
+
-
    counter = 0;
+
  chuck.begin();
-
    // tant qu'il y a des données
+
}
-
    while(Wire.available())
+
-
  {
+
-
      // on récupère les données
+
-
      data[counter++] = Wire.read();
+
-
  }
+
-
    // on réinitialise le nunchuck pour la prochaine demande
+
-
    Wire.beginTransmission(WII_NUNCHUK_I2C_ADDRESS);
+
-
    Wire.write(0x00);
+
-
    Wire.endTransmission();
+
-
    if(counter >= 5)
+
void loop()
-
    {
+
{
-
      // on extrait les données
+
  chuck.update(); // on actualise les données du nunchuk
-
      // dans mon exemple j'utilise uniquement les données d'accélération sur l'axe Y
+
-
      double accelX = ((data[2] << 2) + ((data[5] >> 2) & 0x03) - ZEROX);
+
-
      double accelY = ((data[3] << 2) + ((data[5] >> 4) & 0x03) - ZEROY);
+
-
      // on limite la valeur entre -180 et 180
+
  if(chuck.zPressed())  // on remet les moteurs à la position de départ
-
      int value = constrain(accelY, -180, 180);
+
  {
-
      // on mappe cette valeur pour le servomoteur soit entre 0 et 180
+
    posMoteur1 = 90;
-
       value = map(value, -180, 180, 0, 180);
+
    posMoteur2 = 90;
-
     
+
    servomoteur1.write(posMoteur1);
-
      // on limite la valeur entre -180 et 180
+
    servomoteur2.write(posMoteur2);
-
       int value2 = constrain(accelX, -180, 180);
+
  }
-
      // on mappe cette valeur pour le servomoteur soit entre 0 et 180
+
  if(chuck.cPressed())  // on change le mode
-
      value2 = map(value2, -180, 180, 0, 180);
+
  {
-
     
+
       if(mode == 1) mode = 0; // on le met en mode joystick
-
     
+
       else if(mode == 0) mode = 1;   // on le met en mode accelerometre
-
      // on écrit sur le servomoteur la valeur
+
  }
-
      servomoteur1.write(value);         // controle le servomoteur avec l'axe Y
+
 
-
      servomoteur2.write(value2);         // controle le servomoteur avec l'axe X
+
  if(mode == 0){  //mode joystick
-
     
+
    int a = chuck.readJoyX();
-
      // un petit delai pour pas saturer le servomoteur
+
    int b = chuck.readJoyY();
-
      delay(100);
+
    mode_joystick(a, b);
-
    }
+
  }
 +
 
 +
 
 +
  if(mode == 1){  //mode accelerometre
 +
    int x = chuck.readAccelX();  
 +
    int y = chuck.readAccelY();
 +
    mode_accelerometre(x,y);
 +
  }
 +
  //un petit delai d'attente pour ne pas saturer des servomoteurs
 +
  delay(50);
}
}
</pre>
</pre>
Ligne 118 : Ligne 306 :
==Liens avec d'autres projets arduino==
==Liens avec d'autres projets arduino==
chercher ici : http://wikidebrouillard.org/index.php/Catégorie:Arduino
chercher ici : http://wikidebrouillard.org/index.php/Catégorie:Arduino
-
 
-
==Pour aller plus loin==
 
==Liens avec le quotidien==
==Liens avec le quotidien==
-
  quelles peuvent être les applications technologique de ce montage, ou est-ce qu'on retrouve des programme qui y ressemble ?
+
  On pourrait controler plein de chose avec le nunchuk comme google earth ou encore le curseur de votre ordinateur.
[[Catégorie:Arduino]]
[[Catégorie:Arduino]]

Version actuelle en date du 19 janvier 2014 à 17:57

Article incomplet en cours de rédaction
Modèle:Vidéo

Sommaire

Présentation du projet Arduino

Contrôle d'un bras robotisé par un nunchuk.

On contrôle deux servomoteurs avec un nunchuk, tout cela piloté par la carte arduino.

Liste du matériel

réalisation du projet

Explication

Principe de fonctionnement

Quand on bouge la nunchuk de haut en bas (avec le poignet), le servomoteur qui est collé à la paille se met en action. Quand on bouge la nunchuk sur le même axe (avec le poignet, comme si on vissait), le servomoteur à la base se met à pivoter.

Par défaut, on est en mode accéléromètre. Quand on appui sur la touche C, on passe en mode joystick.

Quand on décale le joystick de gauche à droite, on contrôle le servomoteur qui est à la base. Quand on decale le joystick de haut en bas, on contrôle le servomoteur qui tient la paille.


Quand on appui sur la touche Z, on remet le "robot" en position de départ.

Cablage sur le nunchuk

La photo qui suit nous montre à quoi correspond chaque broche du câble Nunchuck Wii.

Schéma Fritzing

Code

#include <Wire.h>
#include <math.h>
#include <Servo.h>

// these may need to be adjusted for each nunchuck for calibration
#define ZEROX 510 
#define ZEROY 490
#define ZEROZ 460

#define DEFAULT_ZERO_JOY_X 124
#define DEFAULT_ZERO_JOY_Y 132

class WiiChuck {
private:
  uint8_t cnt;
  uint8_t status[6];      // array to store wiichuck output
  uint8_t averageCounter;
  int i;
  int total;
  uint8_t zeroJoyX;   // these are about where mine are
  uint8_t zeroJoyY; // use calibrateJoy when the stick is at zero to correct
  int lastJoyX;
  int lastJoyY;
  int angles[3];

  bool lastZ, lastC;


public:

  uint8_t joyX;
  uint8_t joyY;
  bool buttonZ;
  bool buttonC;
  void begin()
  {
    Wire.begin();
    cnt = 0;
    averageCounter = 0;
    // instead of the common 0x40 -> 0x00 initialization, we
    // use 0xF0 -> 0x55 followed by 0xFB -> 0x00.
    // this lets us use 3rd party nunchucks (like cheap $4 ebay ones)
    // while still letting us use official oness.
    // only side effect is that we no longer need to decode bytes in _nunchuk_decode_byte
    Wire.beginTransmission(0x52);   // device address
    Wire.write(0xF0);
    Wire.write(0x55);
    Wire.endTransmission();
    delay(1);
    Wire.beginTransmission(0x52);
    Wire.write(0xFB);

    Wire.write(0x01);
    Wire.write((uint8_t)0x00);

    Wire.endTransmission();
    update();           
    for (i = 0; i<3;i++) {
      angles[i] = 0;
    }
    zeroJoyX = DEFAULT_ZERO_JOY_X;
    zeroJoyY = DEFAULT_ZERO_JOY_Y;
  }


  void calibrateJoy() {
    zeroJoyX = joyX;
    zeroJoyY = joyY;
  }

  void update() {

    Wire.requestFrom (0x52, 6); // request data from nunchuck
    while (Wire.available ()) {
      // receive byte as an integer
      status[cnt] = _nunchuk_decode_byte (Wire.read()); //
      cnt++;
    }
    if (cnt > 5) {
      lastZ = buttonZ;
      lastC = buttonC;
      lastJoyX = readJoyX();
      lastJoyY = readJoyY();
      //averageCounter ++;
      //if (averageCounter >= AVERAGE_N)
      //    averageCounter = 0;

      cnt = 0;
      joyX = (status[0]);
      joyY = (status[1]);
      for (i = 0; i < 3; i++)
        //accelArray[i][averageCounter] = ((int)status[i+2] << 2) + ((status[5] & (B00000011 << ((i+1)*2) ) >> ((i+1)*2)));
        angles[i] = (status[i+2] << 2) + ((status[5] & (B00000011 << ((i+1)*2) ) >> ((i+1)*2)));

      //accelYArray[averageCounter] = ((int)status[3] << 2) + ((status[5] & B00110000) >> 4);
      //accelZArray[averageCounter] = ((int)status[4] << 2) + ((status[5] & B11000000) >> 6);

      buttonZ = !( status[5] & B00000001);
      buttonC = !((status[5] & B00000010) >> 1);
      _send_zero(); // send the request for next bytes

    }
  }

  float readAccelX() {
    // total = 0; // accelArray[xyz][averageCounter] * FAST_WEIGHT;
    return (float)angles[0] - ZEROX;
  }
  float readAccelY() {
    // total = 0; // accelArray[xyz][averageCounter] * FAST_WEIGHT;
    return (float)angles[1] - ZEROY;
  }
  float readAccelZ() {
    // total = 0; // accelArray[xyz][averageCounter] * FAST_WEIGHT;
    return (float)angles[2] - ZEROZ;
  }

  bool zPressed() {
    return (buttonZ && ! lastZ);
  }
  bool cPressed() {
    return (buttonC && ! lastC);
  }

  int readJoyX() {
    return (int) joyX - zeroJoyX;
  }

  int readJoyY() {
    return (int)joyY - zeroJoyY;
  }

private:
  uint8_t _nunchuk_decode_byte (uint8_t x)
  {
    //decode is only necessary with certain initializations
    //x = (x ^ 0x17) + 0x17;
    return x;
  }

  void _send_zero()
  {
    Wire.beginTransmission (0x52);  // transmit to device 0x52
    Wire.write ((uint8_t)0x00);     // sends one byte
    Wire.endTransmission ();    // stop transmitting
  }

};


WiiChuck chuck = WiiChuck();
Servo servomoteur1, servomoteur2;
int posMoteur1 = 90, posMoteur2 = 90;
int mode=0;

void mode_joystick(int a, int b){
 
    if(a < -50){ // gauche
      if(posMoteur1 > 0){
        posMoteur1 -= 10;
        servomoteur1.write(posMoteur1);
      }
    }

    if(a > 50){ //droite
      if(posMoteur1 < 170){
        posMoteur1 += 10;
        servomoteur1.write(posMoteur1);
      }
    }

    if(b > 50){ //haut
      if(posMoteur2 < 180){
        posMoteur2 += 10;
        servomoteur2.write(posMoteur2);
      }
    }

    if(b < -50){ //bas
      if(posMoteur2 > 0){
        posMoteur2 -= 10;
        servomoteur2.write(posMoteur2);
      }
    }
}

void mode_accelerometre(int a, int b){
  if(a < -50){ // gauche
      if(posMoteur1 > 0){
        posMoteur1 -= 10;
        servomoteur1.write(posMoteur1);
      }
    }

    if(a > 50){ //droite
      if(posMoteur1 < 170){
        posMoteur1 += 10;
        servomoteur1.write(posMoteur1);
      }
    }

    if(b > 50){ //haut
      if(posMoteur2 < 180){
        posMoteur2 += 10;
        servomoteur2.write(posMoteur2);
      }
    }

    if(b < -50){ //bas
      if(posMoteur2 > 0){
        posMoteur2 -= 10;
        servomoteur2.write(posMoteur2);
      }
    }
}

void setup()
{
  Serial.begin(9600);
  servomoteur1.attach(8);
  servomoteur2.attach(9);

  servomoteur1.write(8);
  servomoteur2.write(8);

  chuck.begin();
}

void loop()
{
  chuck.update(); // on actualise les données du nunchuk

  if(chuck.zPressed())  // on remet les moteurs à la position de départ
  {
    posMoteur1 = 90;
    posMoteur2 = 90;
    servomoteur1.write(posMoteur1);
    servomoteur2.write(posMoteur2);
  }
  if(chuck.cPressed())  // on change le mode
  {
      if(mode == 1) mode = 0;  // on le met en mode joystick
      else if(mode == 0) mode = 1;    // on le met en mode accelerometre
  }

  if(mode == 0){  //mode joystick
     int a = chuck.readJoyX();
     int b = chuck.readJoyY();
     mode_joystick(a, b);
  }
  
  
  if(mode == 1){   //mode accelerometre
    int x = chuck.readAccelX(); 
    int y = chuck.readAccelY();
    mode_accelerometre(x,y);
  }
  //un petit delai d'attente pour ne pas saturer des servomoteurs
  delay(50);
}

Liens avec d'autres projets arduino

chercher ici : http://wikidebrouillard.org/index.php/Catégorie:Arduino

Liens avec le quotidien

On pourrait controler plein de chose avec le nunchuk comme google earth ou encore le curseur de votre ordinateur.
Portail des ExplorateursWikidébrouillardLéon DitFLOGPhoto mystèreJ'ai FaitPortraits
AR
CO

Bras Robotisé piloté par nunchuk arduino

Rechercher

Page Discussion Historique
Powered by MediaWiki
Creative Commons - Paternite Partage a l

© Graphisme : Les Petits Débrouillards Grand Ouest (Patrice Guinche - Jessica Romero) | Développement web : Libre Informatique