Contrôle servomoteur avec XBEE et Arduino

Bonjour tout le monde :slight_smile: !!!!

Je suis actuellement entrain de bosser sur le projet d'une aile volante.

J'aimerais par communication zigbee pouvoir contrôler un servo moteur depuis mon ordinateur.

En gros je m'explique : d'un côté l'arduino, un module xbee et le servo moteur

et de l'autre mon ordinateur avec les touches haut, bas, gauche, droite et un xbee.

J'ai déjà effectué quelques tutos pour savoir comment se servir d'un servo moteur avec Arduino, également je sais me servir du logiciel XCTU pour configurer les XBEE, mais je ne sais pas comment transmettre depuis mes touches d'ordi mes ordres au moteur.

Quelqu'un pour m'aider s'il vous plait??

Je vous remercie d'avance pour votre réponse

Il faut faire un programme sur votre Mac/PC pour lire le clavier et ensuite envoyer des commandes à votre zigbee local

Le plus simple serait un second arduino avec 4 boutons pour vos flèches et le zigbee plutôt que votre ordinateur à mon avis comme ça vous faites tout dans le même langage de programmation

Bonjour,

Tout d'abord je vous remercie pour votre réponse.

Mon professeur ne souhaite pas que j'utilise deux arduino.
J'ai déjà commencé à écrire mon code émetteur/récepteur (l'un en python, l'autre en c sur arduino).

Voici mon code émetteur :

#!/usr/bin/env python
# -*-coding:Utf-8 -*
 
import Tkinter as tk
import serial
from serial import SerialException
 
#détection d'évènements sur le clavier
def onKeyPress1(event):
    ser=0
    try:
        ser = serial.Serial("/dev/ttyACM0", 9600)
    except serial.SerialException:
        print("Communication non possible")
        exit();
    print("Envoi au " + "/dev/ttyACM0 " + "à " + "9600 bauds" + "\n")
        ser.write('u');
    print "up"
 
 
def onKeyPress2(event):
    ser=0
    try:
        ser = serial.Serial("/dev/ttyACM0", 9600)
    except serial.SerialException:
        print("Communication non possible")
        exit();
    print("Envoi au " + "/dev/ttyACM0 " + "à " + "9600 bauds" + "\n")
    ser.write('d');
    print "down"
 
 
def onKeyPress3(event):
    ser=0
    try:
        ser = serial.Serial("/dev/ttyACM0", 9600)
    except serial.SerialException:
        print("Communication non possible")
        exit();
    print("Envoi au " + "/dev/ttyACM0 " + "à " + "9600 bauds" + "\n")
    ser.write('l');
    print "left"
 
 
def onKeyPress4(event):
    ser=0
    try:
        ser = serial.Serial("/dev/ttyACM0", 9600)
    except serial.SerialException:
        print("Communication non possible")
        exit();
    print("Envoi au " + "/dev/ttyACM0 " + "à " + "9600 bauds" + "\n")
    ser.write('r');
    print "right"
 
 
 
 
root = tk.Tk()
root.bind("<Up>", onKeyPress1) # Flèche haut
root.bind("<Down>", onKeyPress2) # Flèche Bas
root.bind("<Left>", onKeyPress3) # Flèche Gauche
root.bind("<Right>", onKeyPress4) # Flèche Droite
root.mainloop()

et voici mon code récepteur :

#include <Servo.h>

Servo monServo;

int pinServo=8;

void setup()
{
Serial.begin(9600);
monServo.attach(pinServo);
delay(500);
}

void loop()
{
while(Serial.available()==0);
char data = Serial.read();
int pos = map(data,0,127,0,179);

Serial.println(pos);
monServo.write(pos);
Serial.flush();
}

Qu'en pensez vous ? :slight_smile:

cela pose plusieurs questions

1/ comment votre zigbee est attaché à votre ordinateur et comment l'avez vous configuré? vous supposez dans votre code Python que vous parlez au Xbee local à 9600 bauds et qu'il retransmet fidèlement ce qu'il reçoit sur une entrée série vers les ondes. est-ce que cela est fonctionnel? avez vous testé?

petit commentaire en python - pourquoi construire une String avec des concaténation (+) alors que tout est statique dans votre message?

    print("Envoi au " + "/dev/ttyACM0 " + "à " + "9600 bauds" + "\n")

2/ côté récepteur vous utilisez la libraire standard du port série. est-ce que votre xbee est connecté et configuré à 9600 bauds sur ce port (0 et 1 de votre arduino) et retransmet-il fidèlement uniquement ce qu'il reçoit des ondes?

3/ while(Serial.available()==0);

ce n'est pas une bonne pratique de faire de l'attente active. vous bloquez votre arduino qui ne peut rien faire d'autre en attendant une commande de votre ordinateur. l'idéal est plutôt de faire un if. Si vous avez reçu un caractère alors faire une action, sinon passez cette étape, faire autre chose et laissez la boucle boucler. vous en aurez besoin par exemple si vous voulez faire autre chose que de bouger le servo, par exemple vérifier la batterie de votre aile volante, faire un calcul d'ajustement de trajectoire si vous avez un GPS, modifier la vitesse des moteurs en cas de coup de vent si vous avez un accéléromètre/un capteur qui mesure la position de votre aile volante, couper les moteurs en cas de choc (crash au sol), détection de la distance au sol pour se poser, etc...

4/ int pos = map(data,0,127,0,179);
vous avez lu 1 caractères qui est soit 'u', 'd', 'l' ou 'r'. la valeur de data est le code ASCII de ce caractère, soit 117, 100, 108 ou 114 respectivement. vous effectuez donc un calcul de fonction linéaire sur ce code d'entrée pour l'étirer un peu. 117, 100, 108 ou 114 vont devenir 164, 140, 152 et 160

je le sais parce que j'ai fait un petit code de test

void setup() {
Serial.begin(115200);
Serial.println(map('u',0,127,0,179));
Serial.println(map('d',0,127,0,179));
Serial.println(map('l',0,127,0,179));
Serial.println(map('r',0,127,0,179));
}

void loop() {}

donc vous allez demander à votre servo d'aller en 164, 140, 152 et 160 en fonction de la touche appuyée. la position est fixe. est-ce vraiment ce que vous voulez faire?

Olala merci pour cette super réponse !!!

Donc pour répondre à vos questions :

1/ D'un coté j'ai un xbee routeur AT branché sur un parallax pour faire la laison avec l'ordinateur et de l'autre mon xbee coordinateur AT branché sur un support compatible avec l'arduino et mon servo moteur cablé sur arduino.
Je n'ai pas touché à grand chose pour les paramètres des xbee (peut être que je devrais?)
J'ai testé et dans le moniteur série de l'arduino j'observe bien des valeurs d'angle en fonction des touches sur lesquelles j'appuie et l'axe de mon moteur se déplace.
En python j'avais fait un copier/coller d'un de mes autres projets et effectivement je peux enlever les concaténations, merci pour la remarque.

2/Oui mon xbee est bien configuré à 9600, j'ai vérifié avec le logiciel XCTU, et effectivement le xbee est bien connecté au port 0 et 1 de l'arduino pour la communication série

3/ vous avez raison pour le while , j'avais d'ailleur déjà changé ça en if(Serial.available()){...}
4/ C'était juste un essai pour vérifier que la communication se faisait bien et justement je n'arrive pas à savoir comment utiliser le servomoteur pour le bon fonctionnement de l'aile volante.
A savoir que l'aile volante est composée de deux servomoteurs sur les ailerons et un moteur cc qui active une hélice au centre.

4/ C'était juste un essai pour vérifier que la communication se faisait bien et justement je n'arrive pas à savoir comment utiliser le servomoteur pour le bon fonctionnement de l'aile volante.

Dans l'absolu il faudrait plus d'un seul servo pour piloter votre aile volante qui va se déplacer sur 3 degrés de liberté (roulis, lacet, tangage). regardez ce petit document.

ensuite pour chaque servo il faut mémoriser la position actuelle et quand vous appuyez sur une touche du clavier et qu'elle est reconnue de l'autre côté, alors vous augmentez ou diminuez cette valeur (en vérifiant des bornes max et min) et c'est votre nouvelle valeur de position que vous passez au servo.

par exemple l'axe de lacet gouverne la direction de votre aile volante: Si vous recevez 'l' alors diminuez de 1 la valeur du servo de lacet. si vous recevez 'r' alors augmentez de 1 la valeur de position du servo. en pressant plusieurs fois sur une touche vous allez augmenter ou diminuer la position qui permet de braquer une gouverne. généralement on n'utilise pas des touches parce que c'est long de revenir au point mort et qu'on ne se souvient pas toujours du nombre de click à faire... on utilise un joystick qui se recentre tout seul :slight_smile:

C'est prévu que je n'aurais pas qu'un seul servo, mais deux pour le pilotage :slight_smile:
Donc si j'ai bien compris dans le cas de mon aile volante seul intervient l'axe de lacet et il me faudrait une fonction capable d'incrémenter et de décrémenter l'angle de mes servo en fonction de la touche de mon clavier pressée, c'est bien ça?

oui -enfin tous les degrés de liberté sont importants pour le pilotage - si vous n'avez que 2 moteurs vous ne commanderez que 2 des 3 axes .

Je suppose que vous avez un servo standard qui est capable de prendre un angle entre 0 et 180° pour définir son orientation.

Dans votre code arduino définissez une variable entière globale posServoLacet dont la valeur initiale est celle pour laquelle votre servo met la gouverne de direction au neutre - c'est à dire pour laquelle vote avion va voler tout droit. L'idéal serait que ce soit au milieu de la course du servo (90°) ce qui vous donne un débattement maximum des 2 côtés pour tourner à droite ou à gauche.

essayez ce code connecté à votre aile:

#include <Servo.h> 

#define SERVO_LACET_NEUTRE 90
const int pinServoLacet=8;
Servo servoLacet;
int posServoLacet = SERVO_LACET_NEUTRE;

void setup() 
{ 
  servoLacet.attach(pinServoLacet);
  servoLacet.write(posServoLacet);  // normalement le servo est au milieu à 90°
} 

void loop() {}

et regardez ce que ça fait à la gouverne de direction. Si votre montage physique fait que ce n'est pas pile 90 qui met la gouverne au neutre, trouvez la valeur de SERVO_LACET_NEUTRE qui va bien et conservez là comme référence.

Il y a un bon exemple à lire pour tester la position d'un servo en fonction d'une entrée variable, vous pouvez l'utiliser pour régler finement votre gouverne au neutre et afficher quelle est la bonne valeur à passer au servo pour cela

Ensuite vous faites un switch case sur la valeur reçue par votre Xbee. quand vous recevez une flèche à gauche, vous augmentez (ou diminuez selon le sens du montage du moteur) de 1 l'angle de posServoLacet et inversement quand vous recevez la flèche droite.

Je vous suggère d'autoriser l'envoi d'une lettre en plus par l'ordinateur, par exemple 'n' pour "neutre" qui va réaffecter à posServoLacet = SERVO_LACET_NEUTRE; pour revenir au vol tout droit sans avoir à taper 100 fois sur les flèches.

Explications supers claires merci !!!
J'ai effectué la manip avec le potentiomètre pour afiner ma valeur neutre, j'ai écris mon switch avec l'incrémentation et la décrémentation. Je touche au but!!!!! :slight_smile:
Maintenant, avez vous une idée pour crypter les informations entre deux xbee?
mon professeur souhaite qu'on utilise le chiffrement basique de césar avec une checksum de vérif mais je n'y connais rien là dedans entre deux xbee..

Le chiffre de César consiste simplement à décaler d'un nombre prédéfini de caractère le caractère que vous voulez envoyer.

par exemple 'a' devient 'd', 'b' devient 'e', 'c' devient 'f'

comme vous avez des valeurs en ASCII sur un octet, c'est assez simple; définissez de combien vous voulez décaler votre alphabet et avant d'envoyer votre lettre ajoutez ce décalage

par exemple côté python au lieu de faire ser.write('u'); vous devez envoyer ser.write(ord('u')+[color=blue][b]5[/b][/color])

la fonction python ord('x') vous retournant la valeur ASCII de 'x'.
si vous voulez faire l'inverse et que vous avez un code ASCII vous faites en python chr(117) et vous obtenez le caractère 'u'

n'oubliez pas que vous travaillez sur des octets, donc avec des valeurs entre 0 et 255.... si vous travaillez avec des entiers vous allez déborder de 255 si votre décalage est trop grand et vous n'allez pas envoyer un octet compréhensible. (généralement on utilise la fonction modulo pour rester entre 0 et 255 ou des bytes qui sont limités automatiquement)

Vous devez donc définir de combien de caractères décaler l'alphabet de votre envoi d'ordre et bien sûr côté arduino avant de faire votre switch vous allez devoir soustraire ce même décalage pour retrouver la vraie commande. (de même si le résultat de la soustraction est négative il faut penser que c'est un truc à faire sur un seul octet, non signé).

un checksum - ou somme de contrôle - c'est un nombre qu'on ajoute à un message à transmettre pour permettre au récepteur de vérifier que le message reçu est bien celui qui a été envoyé. Comme vous envoyez des messages de 1 seul octet - une méthode classique est le bit de parité. je vous suggère d'explorer cette piste, la littérature est abondante sur internet. et si vous voulez être efficace vous pouvez même greffer le bit de parité dans la partie haute de votre caractère à envoyer pour ne pas envoyer 2 octets. Pour cela il suffit que les commandes que vous envoyez soient comprises entre à et 127, ce qui laisse le 8ème bit pour calculer la parité. ça vous fera jouer avec des masques sur les bits de l'octet que vous voulez envoyer, c'est un exercice sympa. :slight_smile:

(et la raison de n'envoyer qu'un octet c'est que vous communiquez avec votre avion 2 fois plus rapidement que si vous ordres étaient sur 2 octets... ce qui peut être important si votre avion doit réagir vite...)

Encore une fois c'est super clair !!
ça ne me parait pas être un exercice trop difficile je vais essayer :slight_smile:
Merci pour votre aide

partagez votre code ou une photo du résultat quand ça fonctionnera!

Pardonnez moi pour ma réponse tardive!

petite question : deux xbee entre eux communiquent avec des trames bien précises contenant plusieurs bits correspondant au début de la trame, la taille de la trame ...etc Les deux derniers bits sont réservés à la checksum. Si le xbee génère sa propre checksum il me suffit juste de vérifier dans la partie arduino si je reçois ce que je veux non? Je ne sais pas si j'ai été claire.

exemple d'une trame :

7E 00 0A 01 01 50 01 00 48 65 6C 6C 6F B8

7E Start Delimeter
00 0A Length Bytes
01 API Identifier
01 API Frame ID
50 01 Destination Address low
00 Option Byte
48 65 6C 6C 6F Data Packet
B8 Checksum

Tout à fait claire et tout à fait. Ensuite voir si c'est ce que veut votre prof

Je suppose que vous êtes en XBee API Mode alors, pas en AT mode. vous utilisez une librairie côté Python?

qu'utilisez vous côté client arduino? en pratique si la checksum est fausse votre côté client aura peut-être rejeté la trame et ne vous la donne pas? à vérifier et pour celles qu'ils vous donne, elles seront toutes OK...

à voir donc en fonction du niveau d'abstraction auquel vous travaillez

Mon prof souhaite juste que j'envoie une consigne à mon servo (dans mon cas les touches), et tant que le servo n'a pas reçu la bonne consigne (checksum) je continue à l'envoyer.

Côté python j'utilise une librairie pour la gestion des touches du clavier mais c'est tout. Effectivement je suis en API mode entre mes deux xbee. Je ne sais pas comment m'y prendre en faite niveau client ... :confused:

est-ce que vous utilisez une librairie sur l'arduino?