Please feel free to contact us throught contact form or directly with email

Boitier Control Commande A Distance

17 posts / 0 new
Last post
Raiden
Boitier Control Commande A Distance
French

Le AsIs/ToBe graphique:

schema_explicatif_20.03.03_01.01.jpg

 

Les tensions revelées sur les sorties (couleurs) du Joystick "Penny+Giles" connecté au boitier "Dupont" :

Couleur Position Tension point mort Tension Devant Tension Derrière Tension Droite Tension Gauche
Violet 2 1,4 1,4 1,4 1,4 1,4
Bleu 3 3,64 3,64 3,64 3,64 3,64
Vert 4 3,64 3,64 3,64 3,64 3,64
Orange 6 2,53 3,63 1,9 1,75 3,27
Marron 8 2,55 3,16 1,88 3,4 1,57

Joystick_PennyGiles_Photo_Soudures.jpg

 

La fiche technique du Joystick "Penny+Giles" avec les codes couleurs annotés (pour faire le lien avec le tableau ci-dessus):

joystick_pennygiles_fichetechniqued40972_annotations_2020.03.03_01.01.jpg

admin

Le module PWM -> Voltage:

Convertisseur_De_Signal_Pwm_1-3Khz_En_Tension_0-10V_branchement.jpg

admin

Et les programmes Arduino.

(todo)

Raiden

Les modules HC-05 ont été achetés ici : https://www.amazon.fr/gp/product/B071HBF22P

 

felix

Bonsoir,

du coup, j'ai fais les premiers tests avec le HC-05 (envois de texte entre mon téléphone portable et l'arduino), ce qui marche bien.

 

Du coup, j'aurais quelques questions pour la suite :

- est-ce que tu veux une communication arduino à arduino (via 2 modules HC-05) ou est-ce que tu veux juste le coté récepteur?

- pour le format des communications, tu as une préférence? (texte, binaire, quelles vérifications, ...)? Ou est-ce que je fais comme je pense. Dans ce cas, si je pars sur arduino à arduino, ce serait probablement en binaire, avec 1 octet de début de message fixe, puis le message (format à définir selon ce qu'on voudra transmettre), puis une checksum pour s'assurer d'ignorer les messages corrompus. Éventuellement avec une confirmation de réception si tu le souhaites. Si veux juste le coté récepteur, alors du text sera probablement plus pratique.

- qu'est-ce que tu veux envoyer comme informations? La tension souhaitée sur chaque fil? Les pourcentages avant et coté du joystick? La vitesse de chaque roue? La vitesse d'avance du robot et la vitesse de rotation?

 

Enfin, tu veux que j'assemble un circuit final soudé et que je te l'envois, ou tu veux que je t'envois les instructions de comment le faire toi?

 

Bonne soirée

Félix

Raiden

Salut Félix,

pour répondre à tes questions:

1- Aujourd'hui je fais déjà une communication Nano (émetteur) -> Uno (récepteur). J'ai mesuré une certaine latence en faisant des serial print dans des programmes Arduino sur les 2 cartes, j'obtiens très souvent 1 à 2 secondes de latence ce qui n'est pas génant pour l'ouverture d'une porte de garage mais pourrait être inconfortable dans le cadre du pilotage d'un robot "à distance" (quelques metres). Ce sujet n'est pas prioritaire car non bloquant.

2- Actuellement j'envoie la position X,Y du joystick (donnée entre 0 et 255) sous la forme d'une string type json. (je vais poster à la suite de cette réponse une des versions des programmes Arduino, ce qui m'embete c'est de ne pas avoir retrouvé les dernières versions, du coup ce n'est pas exploitable en l'état mais je vais prendre le temps de refaire des programmes qu'on pourra partager poru continuer d'avancer).

3- le point le plus important car je suis bloqué dessus, c'est de transformer ces données reçues (INT X,Y) en intensité (V) suivant le tableau du 1er post. Donc délivrer une tension entre 1,4 V et 3.64V qui correspond à la valeur entre 0 et 255 reçue pour chaque axe.

Enfin, je n'ai pas besoin que tu m'envoies quelquechose pour le moment, mon objectif c'est de me débloquer la situation, soit par manque de connaissance soit par vision trop étriquée, donc j'ai surtout besoin de conseil pour éviter de perdre trop de temps sur des points de blocage.

Je poste une des versions des programmes, à ne pas prendre en l'état car si ce n'est pas la dernière version ca ne doit pas bien marcher, mais je vais prendre du temsp ce WE pour les refaire fonctionnel.

@+
Adrien

Raiden

le programme émetteur:

#include <RH_ASK.h>
#include <SPI.h>

RH_ASK driver;

const int SW_pin = 2;
const int X_pin = A0;
const int Y_pin = A1;

void setup() {
  pinMode(SW_pin, INPUT);
  digitalWrite(SW_pin, HIGH);

  Serial.begin(115200);

  if (!driver.init())
    Serial.println("init failed");
}

void loop() {
  int swh = digitalRead(SW_pin);
  int xpi = analogRead(X_pin);
  int ypi = analogRead(Y_pin);

  char s;
  sprintf(s, "s:%04d,", swh);
  Serial.print("s = ");
  Serial.println(s);

  char x;
  sprintf(x, "x:%04d,", xpi);
  Serial.print("x = ");
  Serial.println(x);

  char y;
  sprintf(y, "y:%04d,", ypi);
  Serial.print("y = ");
  Serial.println(y);

  String txt = s;
  Serial.println(txt);
  txt += x;
  Serial.println(txt);
  txt += y;
  Serial.println(txt);

  char ftxt;
  String strxpi;
  strxpi = String(txt);
  strxpi.toCharArray(ftxt, 19);

  Serial.print(txt);
  Serial.print("\n");

  Serial.print("Switch:  ");
  Serial.print(swh);
  Serial.print("\n");

  Serial.print("X-axis: ");
  Serial.print(xpi);
  Serial.print("\n");

  Serial.print("Y-axis: ");
  Serial.println(ypi);

  const char *msg = ftxt;
  driver.send((uint8_t *)msg, strlen(msg));
  driver.waitPacketSent();
  Serial.println(msg);
  delay(500);

}

 

Raiden

et le programme récepteur:

#include <RH_ASK.h>
#include <SPI.h>

RH_ASK driver;

void setup()
{
    Serial.begin(9600);
    if (!driver.init())
         Serial.println("init failed");
}

void loop()
{
    uint8_t buf;
    uint8_t buflen = sizeof(buf);

    if (driver.recv(buf, &buflen))
    {
      int i;
      Serial.print("Message: ");
      Serial.println((char*)buf);         
    }
}

 

felix

Bonjour,

1) J'ai pas testé la latence pour le moment, vu que je n'avais que testé le mode récepteur (et pour une bonne mesure de latance, il faut soit deux systèmes parfaitement synchronisés, soit faire faire un aller retour au message (donc programmer un arduino en maitre et l'autre en esclave).

A noter que plusieurs éléments peuvent jouer :

- la librairie RH_ASK.h, si c'est bien celle d'ici (http://www.airspayce.com/mikem/arduino/RadioHead/classRH__ASK.html) n'est pas explicitement compatible avec le HC-05

- la librairie, par défaut, utilise une vitesse de transmission de seulement 2000 bits/seconde

- utiliser du JSON fait que tu envois beaucoup de caractères (19 à première vue), sans compter les bits supplémentaires envoyés par la librairie (72 bits je crois + 50% de ce que tu envois). ça ferait donc un total de 300 bits par message  (ça n'explique pas encore 2 secondes, mais ça nous fait une latence d'au moins 300/2000=0.15 secondes

Je vais essayer de creuser un peu la question.

D'ailleurs tu as obtenu cette latence de quelle manière (si tu passes par le moniteur série, il faut ajouter la latence de la communication arduin-PC, le temps de préparer les message, la latence le temps que ce soit au tour du thread du serial monitor, plus le temps d'affichage)

 

2) ok, comme tu veux. Préviens moi si au final tu préfères que j'avance sans attendre ton programme.

 

3) ça c'est assez facile. A noter que ce qui t'intéresse c'est une tension (en volts : V) et pas une intensité (en ampères: A).

Pour créer une tension 0<=U<=5V  à partir de l'arduino, c'est pas dificile :

- sur un pin PWM, tu mets un PWM=255*U/5 : tu aura donc un créneau avec un temps une proportion U/5V à l'état haut (5V), et le reste à l'état bas (0V). La tension moyenne est donc Umoy=(U/5V)*5V+(1-U/5V)*0V=U.

- on a donc un créneau qui a la bonne tension moyenne (mais qui ondule en plus) : il suffit donc de supprimer les ondulations avec un filtre passe bas : un simple filtre RC suffit.

Pour le choix de R et C, ainsi que de la fréquence de PWM, ça dépends de ce que tu veux :

- quelle erreur maximale sur la tension

- quelle temps de réponse à un changement brutal de consigne

La fréquence de coupure du filtre (en Hz) est fc=1/(2*pi*R*C). Si les ondulations. Si la fréquence du PWM (fPWM) est plus grande que fc, alors les ondulations restantes auront une amplitude de l'ordre de 3.2V*fc/fPWM. Donc si tu veux moins de 10mV d'ondulation, il te faut fPWM > 300*fc.

Le temps caractéristique (n secondes) est tau=2*pi*R*C : ça correspond à la durée qu'il faut pour faire 1/e=63% du changement. Plus précisément, au bout d'un temps t, il te reste exp(-t/tau) du changement à faire. Si t=4*tau, tu à fait 98% du changement. Si t=5taux, tu as fait 99.3%

 

Ma suggestion :

- Utiliser les pins PWM n°9 et 10 (ils permettront si besoin d’obtenir une résolution de 16 bits au lieu de 8 bits). Nb : on va modifier la fréquence du Timer1 pour changer celle du PWM, du coup la librarie Servo ne marchera plus (si c’est un problème, on peut utiliser le Timer2 à la place, mais il n’a que 8 bits de résolution).

- utiliser la fréquence maximale pour ces pins, soit 31.25 kHz

- on veut qu’un changement de tension soit fait à 99 % en 0.1 s (donc fait à 63 % en 0.02s). Par conséquent, on a t=5*tau=0.05s, donc 2*pi*R*C=tau=0.01s. On a donc R*C=0.01/(2*pi)=6.4 ms

- on a donc une fréquence de coupure fc=1/(2*pi*R*C)=1/tau=100 Hz. Par conséquent, les oscillations seront de l’ordre deltaU=3.2V*fc/fPWM=3.2*100/31250=1mV

- pour le choix de R et C, tu fais selon ce que tu as, tant que le produit R*C fait environ 6.4*10^-3.

Par exemple, si tu as un condentateur C=10µF=10^-5 F, alors il te faut une résistance R=6.4*10^-3/10^-5 = 640 ohms environ (par exemple R=560 ou 680 ohms, pour prendre des valeurs standard).

Raiden

Salut Félix,

aie ca y est je commence à peiner ^^

Bon oui je parle de tension (V) j'ai honte d'avoir écrit intensité !

par contre je vais être clair, je ne comprends rien à ta suggestion (et tu comprends maintenant pourquoi j'ai besoin de toi !).

As-tu la possibilité de mettre 2 arduino en mode master/slave ?

De me proposer 2 programmes Arduino (si possible le plus simple possible) pour transmettre 3 valeurs du master au slave (X, Y, et switch joystick) ?

De me faire un schéma de montage + liste des composants pour faire afficher sur un multimètre 2 bornes de tension (correspondant aux 2 fils Orange + Marron pour Droite /  Gauche) en plus de la borne de masse ? (suis-je clair ?)

 

Encore merci, @+ Adrien

Raiden

Hello,

bon en regardant de plus près les moniteur séries je pense que je me suis bien planté sur les programmes, je te colle donc ceux que je pense avoir uploadé:

Master:

#include <SoftwareSerial.h>

// HC05 Pinout
SoftwareSerial ArduinoMaster(10, 11);

String answer;
String msg;
String txt;
String dat;

// Joystick: Arduino pin numbers
const int SW_pin = 2; // digital pin connected to switch output
const int X_pin = A0; // analog pin connected to X output
const int Y_pin = A1; // analog pin connected to Y output

void setup() {
  // Joytsick switch
  pinMode(SW_pin, INPUT);
  digitalWrite(SW_pin, HIGH);

  Serial.begin(9600);
  Serial.println("ENTER Commands:");
  ArduinoMaster.begin(9600);
}

void loop() {

  // Joystick reading
  String swh = String(digitalRead(SW_pin));
  String xpi = String(analogRead(X_pin));
  String ypi = String(analogRead(Y_pin));

/*
  Serial.print("Switch:  ");
  Serial.println(swh);

  Serial.print("X-axis: ");
  Serial.println(xpi);

  Serial.print("Y-axis: ");
  Serial.println(ypi);
*/

  String dat = String("x:") + String(xpi) + String(",y:") + String(ypi) + String(",s:") + String(swh);

  //Read command from monitor
  readSerialPort();


  //Read answer from slave
  while (ArduinoMaster.available()) {
    delay(10);
    if (ArduinoMaster.available() > 0) {
      char c = ArduinoMaster.read();  //gets one byte from serial buffer
      answer += c; //makes the string readString
    }
  }

  //Send data to slave
  if ((msg != "") || (dat != "")) {

    Serial.print("Master sent msg: ");


    if (msg != "") {
      //String txt = String(msg);
      Serial.print("msg: ");
      Serial.println(msg);
      ArduinoMaster.print(msg);
    }
    else if (dat != "") {
      //String txt = String(dat);
      Serial.print("dat: ");
      Serial.println(dat);
      ArduinoMaster.print(dat);
    }
    else
    {
      //String txt = "ok";
      Serial.print("txt: ");
      Serial.println("ok");
      ArduinoMaster.print("ok");
    }


    //Serial.println(txt);

    txt = "";
    msg = "";
    dat = "";
  }

  //Send answer to monitor
  if (answer != "") {
    Serial.print("Slave recieved : ");
    Serial.println(answer);
    answer = "";
  }
}
void readSerialPort() {
  while (Serial.available()) {
    delay(10);
    if (Serial.available() > 0) {
      char c = Serial.read();  //gets one byte from serial buffer
      msg += c; //makes the string readString
    }
  }
  Serial.flush();
  delay(500);
}

 

Slave:

#include <SoftwareSerial.h>
SoftwareSerial ArduinoSlave(10, 11);
String msg;
void setup() {
  Serial.begin(9600);
  ArduinoSlave.begin(9600);
}
void loop() {
  readSerialPort();
  // Send answer to master
  if (msg != "") {
    Serial.print("Master sent : " );
    Serial.println(msg);
    ArduinoSlave.print(msg);
    msg = "";
  }
}
void readSerialPort() {
  while (ArduinoSlave.available()) {
    delay(10);
    if (ArduinoSlave.available() > 0) {
      char c = ArduinoSlave.read();  //gets one byte from serial buffer
      msg += c; //makes the string readString
    }
  }
  ArduinoSlave.flush();
}

par contre je vois rien qui inclut l'envoi d'info vers le DAC coté slave, bizarre !

Je ne comprends toujours pas comment j'ai pu perdre ces scripts mais je vais devoir me faire une raison, faut recommencer ^^

Raiden

Je te confirme j'ai rechargé ces 2 scripts dans les 2 arduino (Nano = master, Uno = slave), ça fonctionne.

Dans mon dernier update j'avais mis en forme les data au format json, ce qui n'est pas le cas là mais rien d'important.

Je pense que j'avais aussi fait un lien entre les données reçues (par la Uno slave) et le module DAC, mais de ce que je comprends c'est que ce module DAC n'a pas de raison d'être si on utilise le PWM pour sortir une tension en gomant les "creux", c'est bien ça ?

Raiden

Pour faire suite, voici les screenshots des consoles séries, avec highlight sur les données au moment où je bouge le joystick vers l'avant de la télécommande.

J'ai activé l'horodatage après avoir up les 2 systèmes par NTP (pour info, Ubuntu 18 pour la Nano et Windows 7 pour la Uno).

Screenshot du master (Arduino Nano):

et screenshot du slave (Arduino Uno):


l'écran est juste plus grand et je n'ai pas redimmensionné lors de la capture.

On voit bien que les 6 commandes (il en apparait 12 sur le screenshot du master car j'affiche 2 lignes à chaque envoi = "master sent" + "slave received"), sont bien transmises.

 

voici la transcription en txt (j'ai viré les lignes "slave received" qui n'apportent rien):

Master:

14:55:39.407 -> Master sent msg: dat: x:1022,y:502,s:1

14:55:40.137 -> Master sent msg: dat: x:1022,y:502,s:1

14:55:40.867 -> Master sent msg: dat: x:1023,y:502,s:1

14:55:41.630 -> Master sent msg: dat: x:1023,y:502,s:1

14:55:42.360 -> Master sent msg: dat: x:1020,y:502,s:1

14:55:43.089 -> Master sent msg: dat: x:1023,y:502,s:1

14:55:43.819 -> Master sent msg: dat: x:498,y:502,s:0

Slave:

14:55:39.584 -> Master sent : x:1022,y:502,s:1

14:55:40.333 -> Master sent : x:1022,y:502,s:1

14:55:41.035 -> Master sent : x:1023,y:502,s:1

14:55:41.784 -> Master sent : x:1023,y:502,s:1

14:55:42.533 -> Master sent : x:1020,y:502,s:1

14:55:43.281 -> Master sent : x:1023,y:502,s:1

14:55:43.983 -> Master sent : x:498,y:502,s:0
 

Jusque là je dirais que tout va bien.

En regardant les scripts d'un peu plus près c'est vraiment de la merde, j'ai remis le nez dedans je me demande comment j'ai pu pondre ça.

Donc je les ai un peu nettoyé (je vais encore les revoir):

 

Master:

#include <SoftwareSerial.h>

// HC05 Pinout
SoftwareSerial ArduinoMaster(10, 11);

String answer; //reply from HC-05 slave (receiver)
String dat; //data to send to slave (receiver)
String msg; //string value from serial reading
int countUp; //counter to follow data sent

// Joystick: Arduino pin numbers
const int SW_pin = 2; // digital pin connected to switch output
const int X_pin = A0; // analog pin connected to X output
const int Y_pin = A1; // analog pin connected to Y output

void setup() {

  //init counter to 0
  int countUp = 0; 
  
  // Joytsick switch
  pinMode(SW_pin, INPUT);
  digitalWrite(SW_pin, HIGH);

  //start Serial + HC-05 communication
  Serial.begin(9600);
  ArduinoMaster.begin(9600);
}

void loop() {

  //up 1 to counter
  countUp++;
  
  // read joystick values
  String swh = String(digitalRead(SW_pin));
  String xpi = String(analogRead(X_pin));
  String ypi = String(analogRead(Y_pin));

//debug joystick value to serial console
/*
  Serial.print("Switch:  ");
  Serial.println(swh);

  Serial.print("X-axis: ");
  Serial.println(xpi);

  Serial.print("Y-axis: ");
  Serial.println(ypi);
*/

  //concat values to string variable "dat"
  String dat = String("#") + String(countUp) + String("=") + String("x:") + String(xpi) + String(",y:") + String(ypi) + String(",s:") + String(swh);

  //Read command from monitor
  readSerialPort();

  //Read answer from slave
  while (ArduinoMaster.available()) {
    if (ArduinoMaster.available() > 0) {
      char c = ArduinoMaster.read();  //gets one byte from serial buffer
      answer += c; //makes the string readString
    }
    delay(10);
  }

  //Send data to slave
  if ((msg != "") || (dat != "")) {

    Serial.print("Master sent: ");

    if (dat != "") {
      Serial.println(dat);
      ArduinoMaster.print(dat);
    }
    else
    {
      Serial.print("txt: ");
      ArduinoMaster.print("ok");
    }

    //Reset variables to null
    dat = "";
    msg = "";
  }

  //Send answer to monitor
  if (answer != "") {
    /*
    Serial.print("Slave received : ");
    Serial.println(answer);
    */
    answer = "";
  }else{
    Serial.println("No answer from Slave");
  }

}

void readSerialPort() {
  while (Serial.available()) {
    if (Serial.available() > 0) {
      char c = Serial.read();  //gets one byte from serial buffer
      msg += c; //makes the string readString
    }
    delay(10);
  }
  
  Serial.flush();
  delay(500);
}

 

Slave:

#include <SoftwareSerial.h>

SoftwareSerial ArduinoSlave(10, 11);

String msg;

void setup() {
  Serial.begin(9600);
  ArduinoSlave.begin(9600);
}

void loop() {
  readSerialPort();
  // Send answer to master
  if (msg != "") {
    Serial.print("Slave received : " );
    Serial.println(msg);
    ArduinoSlave.print(msg);
    msg = "";
  }else{
    Serial.println("Nothing received from Master");
  }
}

void readSerialPort() {
  while (ArduinoSlave.available()) {
    delay(10);
    if (ArduinoSlave.available() > 0) {
      char c = ArduinoSlave.read();  //gets one byte from serial buffer
      msg += c; //makes the string readString
    }
  }
  ArduinoSlave.flush();
}

 

Raiden

Salut,

un test avec des logs:

Master:
09:44:24.702 -> Master sent: #70=x:498,y:502,s:0
09:44:25.430 -> Master sent: #71=x:498,y:502,s:0
09:44:26.193 -> Master sent: #72=x:498,y:502,s:0
09:44:26.922 -> Master sent: #73=x:1014,y:502,s:0
09:44:27.651 -> Master sent: #74=x:498,y:502,s:0
09:44:28.413 -> Master sent: #75=x:0,y:502,s:0
09:44:28.944 -> Master sent: #76=x:0,y:502,s:0
09:44:29.540 -> Master sent: #77=x:0,y:502,s:0
09:44:30.270 -> Master sent: #78=x:498,y:403,s:1
09:44:31.032 -> Master sent: #79=x:498,y:411,s:0
09:44:31.762 -> Master sent: #80=x:498,y:1022,s:0
09:44:32.490 -> Master sent: #81=x:498,y:502,s:1
09:44:33.220 -> Master sent: #82=x:498,y:332,s:0
09:44:33.949 -> Master sent: #83=x:498,y:502,s:0
09:44:34.678 -> Master sent: #84=x:498,y:502,s:0
09:44:35.407 -> Master sent: #85=x:498,y:502,s:1
09:44:36.136 -> Master sent: #86=x:498,y:502,s:1
09:44:36.866 -> Master sent: #87=x:498,y:502,s:0
09:44:37.596 -> Master sent: #88=x:498,y:502,s:0
09:44:38.325 -> Master sent: #89=x:498,y:502,s:1
09:44:39.021 -> Master sent: #90=x:1023,y:503,s:1

Slave:
09:44:26.547 -> Slave received : #70=x:498,y:502,s:0
09:44:26.922 -> Slave received : ⸮
09:44:27.296 -> Slave received : #71=x:498,y:502,s:0⸮
09:44:27.998 -> Slave received : #72=x:498,y:502,s:0
09:44:28.185 -> Slave received : ⸮
09:44:28.747 -> Slave received : #73=x:1014,y:502,s:0
09:44:29.496 -> Slave received : #74=x:498,y:502,s:0⸮
09:44:29.823 -> Slave received : ⸮⸮
09:44:30.291 -> Slave received : ⸮
09:44:30.712 -> Slave received : ⸮⸮⸮
09:44:30.993 -> Slave received : ⸮⸮
09:44:31.368 -> Slave received : #77=x:0,y:502,s:0
09:44:31.555 -> Slave received : ⸮
09:44:31.602 -> Slave received : ⸮⸮
09:44:32.116 -> Slave received : #78=x:498,y:403,s:1⸮⸮
09:44:32.865 -> Slave received : #79=x:498,y:411,s:0
09:44:33.333 -> Slave received : ⸮
09:44:33.427 -> Slave received : #
09:44:33.614 -> Slave received : 80=x:498,y:1022,s:0
09:44:34.316 -> Slave received : #81=x:498,y:502,s:1
09:44:35.065 -> Slave received : #82=x:498,y:332,s:0
09:44:35.814 -> Slave received : #83=x:498,y:502,s:0
09:44:36.516 -> Slave received : #84=x:498,y:502,s:0
09:44:37.264 -> Slave received : #85=x:498,y:502,s:1
09:44:37.966 -> Slave received : #86=x:498,y:502,s:1
09:44:38.715 -> Slave received : #87=x:498,y:502,s:0
09:44:39.417 -> Slave received : #88=x:498,y:502,s:0
09:44:40.166 -> Slave received : #89=x:498,y:502,s:1
09:44:40.868 -> Slave received : #90=x:1023,y:503,s:1
 

On peut voir quelques erreurs, je ne connais pas l'origine, mais cela signifie qu'il faut que sur le récepteur (slave Uno) je programme une reconnaissance de la chaine et ne prendre en compte que les chaines bien reçues.

felix

Bonjour,

désolé d'avoir tardé un peu à te répondre.

Du coup, pour le DAC, on n'en a plus besoin, on le fera nous même avec un PWM, un condensateur et une résistance.

 

Sinon, j'ai fait quelques tests sur la transmission, et mis au point un algo assez fiable.

Tout d'abord, pouquoi y a-t-il des erreurs de transmissions :

- une cause est une erreur de lecture d'un bit, du à du bruit électromagnétique. A priori c'est assez rare, et ça change un seul bit

- le buffer de réception est plein (car on n'a pas fini de traiter les données avant que de nouvelles arrivent) : dans ce cas, il y a des octets qui disparraissent, et du coup on risque de mal interpréter les données (par exemple penser qu'on est au début du message alors qu'en fait le début manque et qu'on est au milieu. C'est de loin l'erreur la plus fréquente. Elle survient principalement quand on envoit les données "trop vite" et que le récepteur n'arrives plus a suivre

 

Du coup, j'ai écris les programmes de tests suivant :

master :

#include <SoftwareSerial.h>

#define COMMAND_FRAME_PAYLOAD_INT16 3


SoftwareSerial ArduinoSlave(10,11);
String answer;
String msg;
void setup(){
 Serial.begin(115200);
 Serial.println("ENTER Commands:");
 ArduinoSlave.begin(9600);          
}


void loop(){
  static int i=0;
  int16_t data;
  data=10*i+0;
  data=10*i+1;
  data=10*i+2;
  sendFrame(data);
  delay(1);
  i++;
}

void sendFrame(int16_t *data) //nb : sends the values of the data array, along with metadata. Nb : the number of elements read from data is COMMAND_FRAME_PAYLOAD_INT16 
{
  uint8_t send_buffer;
  send_buffer=42;  //fixed start of frame byte
  memcpy ( send_buffer+1, (uint8_t *)data, 2*COMMAND_FRAME_PAYLOAD_INT16);

  uint8_t checksum=bsdChecksum(send_buffer,2*COMMAND_FRAME_PAYLOAD_INT16+1);

  send_buffer=checksum;
  ArduinoSlave.write(send_buffer,2*COMMAND_FRAME_PAYLOAD_INT16+2);
}

uint8_t bsdChecksum(uint8_t *data, uint8_t len)
{
    uint8_t checksum = 0;
    for(int i=0; i<len; i++)
    {
        checksum = (checksum >> 1) + ((checksum & 1) << 7);
        checksum += data;
    }
    return checksum;
}

slave:

#include <SoftwareSerial.h>

#define COMMAND_FRAME_PAYLOAD_INT16 3
#define COMMAND_FRAME_SIZE (2*COMMAND_FRAME_PAYLOAD_INT16+2)

SoftwareSerial ArduinoMaster(10,11);
String msg;
void setup(){
 Serial.begin(115200);
 Serial.println("slave stating");
 ArduinoMaster.begin(9600);    
}
void loop(){
 readSerialPort();
}

uint32_t nbr_read_bytes=0;

void readSerialPort(){
  static uint8_t index=0;
  static uint8_t input_buffer;

  while (ArduinoMaster.available()>0)
  {
    uint8_t new_byte=ArduinoMaster.read();
    nbr_read_bytes++;
    if(index==0)  //we are waiting for a valide start of frame
    {
      if(new_byte!=42)  //invalide start of frame byte
      {
        continue; //stop this iteration of while loop (we therefore jst ignore the invalid byte
      }
      else  //we get the right value for a start of frame, so it might be the start of a frame
      {
        input_buffer=new_byte;
        index=1;
      }
    }
    else //we have already a start of frame, and we are waiting for the rest of the frame
    {
      input_buffer=new_byte;
      index++;
      
      if(index==COMMAND_FRAME_SIZE) //we have the right amount of data to form a full frame, let's verify if it is valide
      {
        uint8_t checksum=bsdChecksum(input_buffer,COMMAND_FRAME_SIZE-1);

        if(checksum==input_buffer) //valid frame
        {
          //process frame
          uint8_t raw_payload;
          memmove(raw_payload, input_buffer+1,COMMAND_FRAME_SIZE-2);
          process_payload(raw_payload);

          //empty buffer
          index=0;
        }
        else  //invalid frame : either the frame is corrupted, or it was a coincidence that the start of frame byte appeared.
        {
          //look if we have a potential start of frame in the middle of what we believed to be a frame
          int i=1;
          while (i<COMMAND_FRAME_SIZE && input_buffer!=42)
          {
            i++;
          }

          if(i==COMMAND_FRAME_SIZE) //no potential start of frame byte found
          {
            index=0;  //we trash all data in the input buffer
          }
          else  //we found a potential start of frame at position i : we trash everything before
          {
            memmove(input_buffer, input_buffer+i,index-i);  //copy te start byte and all bytes that followed
            index=index-i;
          }
        }
      }
    }
  }
}

uint8_t bsdChecksum(uint8_t *data, uint8_t len)
{
    uint8_t checksum = 0;
    for(int i=0; i<len; i++)
    {
        checksum = (checksum >> 1) + ((checksum & 1) << 7);
        checksum += data;
    }
    return checksum;
}

void process_payload(uint8_t *raw_payload)
{
  static uint32_t nbr_frames_appearing_correct=0;
  static uint32_t nbr_frames_appearing_correct_that_are_corrupted=0;

  nbr_frames_appearing_correct++;
  int16_t *data=(int16_t *)raw_payload; //cast back to int16_t* to interpret the payload as COMMAND_FRAME_PAYLOAD_INT16 int16_t values

  if(data!=data+1 || data!=data+1)
    nbr_frames_appearing_correct_that_are_corrupted++;

  Serial.print("bytes : ");
  Serial.print(nbr_read_bytes);
  Serial.print("\teq_frames : ");
  Serial.print(nbr_read_bytes/COMMAND_FRAME_SIZE);
  Serial.print("\tnegatives : ");
  Serial.print(nbr_read_bytes/COMMAND_FRAME_SIZE-nbr_frames_appearing_correct);
  Serial.print(" ( : ");
  Serial.print(100.*(nbr_read_bytes/COMMAND_FRAME_SIZE-nbr_frames_appearing_correct)/(nbr_read_bytes/COMMAND_FRAME_SIZE));
  Serial.print("%)\tpositives : ");
  Serial.print(nbr_frames_appearing_correct);
  Serial.print("\tfalse positives among pos:");
  Serial.print(nbr_frames_appearing_correct_that_are_corrupted);
  Serial.print("\t error_rate:");
  Serial.print(100.*nbr_frames_appearing_correct_that_are_corrupted/nbr_frames_appearing_correct);
  Serial.println("%");
}

 

A noter que j'ai laissé les modules HC-05 sur leur vitesse par défaut (9600)

Avec le "delay(1)" dans le loop, le slave n'arrives pas a suivre. Du coup un nombre important de frames arrivent corrompues (42%). Néanmoins, la vérification des frames permet de détecter les frames invalides dans 99.8% des cas (ie seul 0.2% des frames invalides sont interprétée comme valides).

 

Si on remplace le "delay(1)" par "delay(10)", le slave arrive de nouveau à suivre. Du coup, sur 10^5 frames, je n'ai observé aucune frame corrompue (mais s'il y en avait une, il y aurait 99.8% de chance quelle soit rejetée).

 

 

 

A noter que je me suis permis de remplacer la transmission par texte par une transmission par octots directement.

Un message est donc de la forme :

42; nombre_1_sur_2_octets ; nombre_2_sur_2_octets ; nombre_3_sur_2_octets ; checksum_sur_1_octet

soit 8 octets d'envoyés pour transmettre 3 nombres (des int16_t).

 

 

Pour te finaliser le programme (ie l'adapter précisément à ton joystic), j'aurais quelques questions :

1) c'est quoi exactement que tu veux transmettre du master (télécomande) au slave? La valeur du potentiomètre? La tension à envoyer sur chaque fil en sortie? La vitesse en % en rotation et en avance? La vitesse en % de chaque roue? ...

NB : si tout ce qui t'intéresse c'est de commander avec la télécommande, alors c'est peu importe (dans ce cas je prendrais la tension en mV). Si en revanche tu veux déjà préparer la suite, quand la télécomande sera remplacée par un ordinateur, alors tu as peut-être une préférence.

2) ton joystick, tu le montes "droit" (ie axes avant-arrière et gauche-droite) ou à 45° comme celui du fauteil roulant

3) Est-ce que le bouton du joystick sert à quelque chose ou je l'ignore?

4) Tu veux transmettre à quelle fréquence?

5) Est-ce que tu as besoin de savoir si un envoi a échoué?

 

Bon weekend

Félix

Raiden

Salut,

alors je viens de voir 5 mois passer sans m'en rendre compte, ma réponse est plus que tardive.

j'ai remis les mains dans le camboui, j'applique ta réponse et je reviens dans quelques jours.

@+

Raiden

Salut Felix,

pour le master, j'obtiens cette erreur à la compilation:

 

arduino_remote-controller_master_05_Felix:36:65: error: no matching function for call to 'write(uint8_t&, int)'

   ArduinoSlave.write(send_buffer,2*COMMAND_FRAME_PAYLOAD_INT16+2);

                                                                 ^

In file included from D:\arduino-1.8.12\hardware\arduino\avr\cores\arduino/Stream.h:26:0,

                 from D:\arduino-1.8.12\hardware\arduino\avr\cores\arduino/HardwareSerial.h:29,

                 from D:\arduino-1.8.12\hardware\arduino\avr\cores\arduino/Arduino.h:233,

                 from sketch\arduino_remote-controller_master_05_Felix.ino.cpp:1:

D:\arduino-1.8.12\hardware\arduino\avr\cores\arduino/Print.h:57:12: note: candidate: size_t Print::write(const char*, size_t) <near match>

     size_t write(const char *buffer, size_t size) {

            ^~~~~

D:\arduino-1.8.12\hardware\arduino\avr\cores\arduino/Print.h:57:12: note:   conversion of argument 1 would be ill-formed:

D:\arduino-1.8.12\hardware\arduino\avr\cores\arduino/Print.h:56:20: note: candidate: virtual size_t Print::write(const uint8_t*, size_t) <near match>

     virtual size_t write(const uint8_t *buffer, size_t size);

                    ^~~~~

D:\arduino-1.8.12\hardware\arduino\avr\cores\arduino/Print.h:56:20: note:   conversion of argument 1 would be ill-formed:

exit status 1
no matching function for call to 'write(uint8_t&, int)'

 

2020.10.10_01.jpg

 

A noter que je me suis permis de remplacer la transmission par texte par une transmission par octots directement.

Un message est donc de la forme :

42; nombre_1_sur_2_octets ; nombre_2_sur_2_octets ; nombre_3_sur_2_octets ; checksum_sur_1_octet

soit 8 octets d'envoyés pour transmettre 3 nombres (des int16_t).

donc 3 nombres d'une valeur max chacun de 65536 ?

A quoi sert le "42" au début ?

 

Pour te finaliser le programme (ie l'adapter précisément à ton joystic), j'aurais quelques questions :

1) c'est quoi exactement que tu veux transmettre du master (télécomande) au slave? La valeur du potentiomètre? La tension à envoyer sur chaque fil en sortie? La vitesse en % en rotation et en avance? La vitesse en % de chaque roue? ...

NB : si tout ce qui t'intéresse c'est de commander avec la télécommande, alors c'est peu importe (dans ce cas je prendrais la tension en mV). Si en revanche tu veux déjà préparer la suite, quand la télécomande sera remplacée par un ordinateur, alors tu as peut-être une préférence.

oui peu importe la technique du moment qu'elle permet de "piloter" le slave, donc d'envoyer un indicateur de direction et de vitesse (si on considère que l'angle du joystick represente la direction et la distance depuis le centre, la vitesse).

 

2) ton joystick, tu le montes "droit" (ie axes avant-arrière et gauche-droite) ou à 45° comme celui du fauteil roulant

je l'ai mis droit en raison de la morphologie de la carte electronique du joytsick qui s'intègre mieux en position "droite".

 

3) Est-ce que le bouton du joystick sert à quelque chose ou je l'ignore?

pour le moment il n'est pas utile mais il pourrait être un élément de sécurité, par exemple initier de nouvelles commandes après un délai "de veille", ainsi ca pourrait éviter un accident si le joytsick était déplacé par inadvertance. Au bout de 5s par exemple, tu doit appuyer sur le bouton pour que son déplacement soit pris en compte. Aucune commande pendant 5s force l'utilisation du bouton du joystick pour prise en compte de nouvelle commande.

 

4) Tu veux transmettre à quelle fréquence?

Qu'importe du moment que ca marche :)

 

5) Est-ce que tu as besoin de savoir si un envoi a échoué?

pour le moment non.

Je préfère m'assurer que les réceptions sont justes et fiables.