Fernsteuerung für den omnidirektionalen Roboter - AZ-Delivery

Dans l'article de blog d'aujourd'hui, nous voulons construire un robot d'exploration télécommandé à partir du kit Smart Car d'Acebott. Le robot doit être contrôlé par un joystick et afficher son environnement en optique radar sur un petit écran.
Pour scanner l'environnement, on utilise le capteur à ultrasons, qui est déjà monté sur un servomoteur avec un support en acrylique.

Pour la commande, on utilise la structure du PS2-Joystick Shield et de l'ESP32 D1 R32, que vous connaissez peut-être déjà grâce aux articles de blog sur la commande du robot Bionic Spider.

Matériel informatique

Si vous avez déjà effectué des modifications sur le PS2 Joystick Shield dans un blog précédent de la série Bionic Spider Controller, vous pouvez sauter le paragraphe suivant.

 

Pour la réalisation du projet, vous avez besoin de

ESP32 NodeMCU D1 R32

Bouclier de joystick PS2

(en option) pile monobloc 9V + Clip de batterie

et bien sûr un assemblé Voiture robot Acebott

 

Comme les entrées analogiques des deux axes du joystick sont connectées aux GPIO 2 et 4, qui sont reliés au CAN2, elles ne peuvent pas être utilisées simultanément avec le module WLAN. Les connexions IO 34 et IO35 (ADC1) se trouvent cependant directement à côté de celles-ci, ce qui permet d'établir très facilement la fonctionnalité via un pont de fil.

Figure 1 : Pont de fil entre les connexions

Reliez les connecteurs comme indiqué ci-dessus.
Comme la LED on-board est connectée au GPIO 2, la broche doit être retirée ici, sinon la connexion influencerait la valeur de la tension de l'axe des X.

Il en va de même pour la broche D12, car elle perturbe le processus de boot et ne permettrait donc pas de booter avec le shield enfiché. Le bouton-poussoir central du joystick se trouve sur cette connexion, il n'est donc plus utilisable. Si vous souhaitez tout de même l'implémenter, il doit être à nouveau relié à un port libre.

 

Figure 2 : Bouclier avec modifications

 

Le shield peut être facilement enfiché sur le D1 R32. Veillez à ce que le petit interrupteur dans le coin gauche soit positionné sur 3v3 sinon l'ESP32 pourrait être endommagé par une tension trop élevée de 5V.

 

 

Extension de l'écran

Comme le contrôleur doit représenter l'environnement du véhicule sous la forme d'un écran radar, tel qu'on le voit dans de nombreux films, un écran est nécessaire.

Dans ce cas, il est recommandé d'utiliser le Écran graphique OLED 1,3" (SH1106). Il s'agit non seulement d'un grand écran graphique très lisible, mais il a aussi une configuration adaptée au connecteur du module HC05 sur le shield (en haut à droite). L'écran peut donc être branché sans grande modification.

Seule la résistance SMD supérieure à côté du bouton A doit être retirée, car elle a été conçue à l'origine pour réduire de moitié la tension sur la broche RXD du HC05, afin que le module ne soit pas endommagé lors de son utilisation avec le microcontrôleur UNO.

 

Modification de l'image

 

Comme le microcontrôleur utilisé (ESP32) a un niveau logique de 3,3V, la communication avec l'écran ne fonctionnerait plus avec une tension réduite de moitié.

 

Logiciel

Pour la communication, les paquets de données sont échangés via UDP. Avec ce protocole de communication, les paquets sont simplement envoyés sans savoir si le paquet a été reçu.

Ainsi, l'échange de données est certes très rapide, mais aussi peu sûr, car il n'est pas possible de déterminer si le destinataire reçoit effectivement les données. La communication via un réseau permet également une plus grande portée par rapport à ESPnow ou Bluetooth, car le réseau peut couvrir une grande zone avec des répéteurs et des points d'accès.
En principe, les deux appareils peuvent ainsi communiquer à n'importe quel endroit où vous recevez avec votre smartphone.

 

La bibliothèque pour la communication UDP est déjà incluse dans le package complet de la carte ESP32.

 

En plus de la bibliothèque UDP et WiFi, d'autres bibliothèques externes sont nécessaires et doivent encore être installées par vos soins.

U8g2

ArduinoJson

Vous pouvez les télécharger sur GitHub au format .zip en cliquant sur les liens suivants et les utiliser dans l'IDE Arduino sous

Sketch > include Library > Add .zip Library ...

être sélectionné et installé.

 

Le site ESP32Servo, ultrasonic et vehicle Les bibliothèques sont déjà incluses dans le fichier .zip du guide. Elles ont également déjà été installées dans le guide pour utilisateurs avancés.

 

Code smart car

#include <vehicle.h>
#
include <ultrasonic.h>
#
include <ESP32Servo.h>
#
include <ArduinoJson.h>
#
include <WiFi.h>
#
include <WiFiUdp.h>

ultrasonic myUltrasonic;
vehicle myCar;
Servo myServo;
JsonDocument doc;
WiFiUDP udp;

const char* ssid = "xxx";
const char* password = "xxx";
String cmd;
String msg;
int v = 255//speed

void setup() {
  
Serial.begin(115200);
  
WiFi.begin(ssid, password);
  
while (WiFi.status() != WL_CONNECTED) {
    
delay(500);
    
Serial.print(".");
    
if(millis() > 30000) ESP.restart();
  }

  udp.
begin(4210);

  
Serial.println("ESP8266 ready (Reciever)");
  
Serial.print("IP:");
  
Serial.println(WiFi.localIP());
  myCar.Init();
//Initialize all motors
  myUltrasonic.Init(
13,14);
  myServo.
attach(25);//initialize
}

void loop() {
  
//Serial.print("IP:");
  
//Serial.println(WiFi.localIP());

  
int pSize = udp.parsePacket();
  
if (pSize) {
    
char buf[20];
    
int len = udp.read(buf, 20);
    buf[len] = 
0;

    cmd = 
String(buf);
    
Serial.println(cmd);
  }

  
if(cmd.length() > 0) {
    
if(cmd.indexOf("CMD_FWD") >= 0) {
      
Serial.println("FWD");
      myCar.Move(Forward, v);
    }
    
else if(cmd.indexOf("CMD_BWD") >= 0) {
      
Serial.println("BWD");
      myCar.Move(Backward, v);
    }
    
else if(cmd.indexOf("CMD_RGT") >= 0) {
      
Serial.println("RGT");
      myCar.Move(Move_Right, v);
    }
    
else if(cmd.indexOf("CMD_LFT") >= 0) {
      
Serial.println("LFT");
      myCar.Move(Move_Left, v);
    }
    
else if(cmd.indexOf("CMD_STOP") >= 0) {
      
Serial.println("Stop");
      myCar.Move(Stop,
0);
    }
    
else if(cmd.indexOf("BTN_A") >= 0) {
      
Serial.println("A");
      
int j = 0;
      
for(int i=30; i<=150; i+=5) {
        myServo.
write(i);
        
Serial.println(myUltrasonic.Ranging());
        doc[
"vals"][j]["angle"] = i + 180;
        doc[
"vals"][j]["dist"] = myUltrasonic.Ranging();
        
delay(150);
        j++;
      }
      doc[
"count"] = j;
      serializeJson(doc, msg);
      
Serial.println(msg);
      udp.
beginPacket(udp.remoteIP(), udp.remotePort());
      udp.
print(msg);
      udp.
endPacket();
      myServo.
write(90);
    }
    
else if(cmd.indexOf("BTN_B") >= 0) {
      
Serial.println("B");
    }
    
else if(cmd.indexOf("BTN_C") >= 0) {
      
Serial.println("C");
    }
    
else if(cmd.indexOf("BTN_D") >= 0) {
      
Serial.println("D");
    }
    
else if(cmd.indexOf("BTN_E") >= 0) {
      
Serial.println("E");
      myCar.Move(Clockwise,v);
    }
    
else if(cmd.indexOf("BTN_F") >= 0) {
      
Serial.println("F");
      myCar.Move(Contrarotate,v);
    }

    cmd = 
"";
    
// send ready to controller to recieve new commands
    udp.
beginPacket(udp.remoteIP(), udp.remotePort());
    udp.
print("RDY");
    udp.
endPacket();
    
Serial.println("ACK gesendet");
  }
}

Explication :

Au démarrage, l'ESP32 se connecte au WLAN, ouvre un port UDP et initialise les moteurs, le capteur à ultrasons et le servo. Dans la boucle principale, il attend les commandes UDP entrantes et les évalue sous forme de commandes textuelles. Selon la commande, les moteurs sont commandés ou des fonctions spéciales sont exécutées.

Lors de la commande BTN_A, le servo fait pivoter le capteur à ultrasons pas à pas, mesure la distance à chaque position et enregistre l'angle et la distance dans un tableau JSON. Une fois le balayage terminé, le JSON est sérialisé et renvoyé à l'expéditeur par UDP, puis le servo revient à sa position centrale.

Après chaque action exécutée, l'ESP32 envoie le message de retour "RDY" pour signaler qu'il est prêt pour la prochaine commande.

 

Vous pouvez utiliser le code ici télécharger.

Après avoir chargé le programme sur le microcontrôleur avec les données d'accès WLAN correctes, l'adresse IP de l'appareil s'affiche. Notez-la, car elle est nécessaire au contrôleur.

Contrôleur de code

#include <math.h>
#
include <Arduino.h>
#
include <U8g2lib.h>
#
include <Wire.h>
#
include <WiFi.h>
#
include <esp_wifi.h>
#
include <WiFiUdp.h>
#
include <ArduinoJson.h>

#
define X 34
#
define Y 35

#
define A 26
#
define B 25
#
define C 17
#
define D 16
#
define E 27
#
define F 14

const char* ssid = "xxx";
const char* password = "xxx";
const char* rcvIP = "192.168.178.xxx";


int calibY, calibX;
bool status = true;
long sentTime;
String msg;

WiFiUDP udp;
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, U8X8_PIN_NONE, 
31);

JsonDocument doc;

void drawLineAngle(float angleDeg, float length) {
  
float angleRad = angleDeg * DEG_TO_RAD;

  
int x1 = 64 - cos(angleRad) * length;
  
int y1 = 63 + sin(angleRad) * length;

  u8g2.drawLine(
6463, x1, y1);
}

void setup() {
  
Serial.begin(115200);
  
delay(2000);

  
WiFi.begin(ssid, password);
  
while (WiFi.status() != WL_CONNECTED) {
    
delay(500);
    
Serial.print(".");
    
if(millis() > 7000) ESP.restart();
  }

  u8g2.
begin();
  u8g2.clearBuffer();

  
Serial.println("ESP32 ready (Controller)");

  calibX = 
analogRead(X); //calibrating current Position to zero
  calibY = 
analogRead(Y);

  
pinMode(A, INPUT_PULLUP);
  
pinMode(B, INPUT_PULLUP);
  
pinMode(C, INPUT_PULLUP);
  
pinMode(D, INPUT_PULLUP);
  
pinMode(E, INPUT_PULLUP);
  
pinMode(F, INPUT_PULLUP);
}

void loop() {
  
int valX = analogRead(X) - calibX;
  
int valY = analogRead(Y) - calibY;
  
//Serial.println(valX);
  
//Serial.println(valY);

//no acknowledgement recieved after 10 sec. -> set marker to true; reenable sending
  
if((millis() - sentTime) > 10000) {
    status = true;
  }
  
if(status) {
    
//threshold values
    
if(valX < -50 || valY < -50 || valX > 50 || valY > 50) {
      
Serial.println("trig");
      
//choose dominant coordinate
      
if(abs(valX) > abs(valY)) {
        
if(valX < 0) {
          
Serial.println("LEFT");
          msg = 
"CMD_LFT";
        }
        
else {
          
Serial.println("Right");
          msg = 
"CMD_RGT";
        }
      }
      
else {
        
if(valY < 0) {
          
Serial.println("BWD");
          msg = 
"CMD_BWD";
        }
        
else {
          
Serial.println("FWD");
          msg = 
"CMD_FWD";
        }
      }
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
    
else {
      msg = 
"CMD_STOP";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }

    
if(!digitalRead(A)) {
      
Serial.println("A");
      msg = 
"BTN_A";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation +3000
    }
    
else if(!digitalRead(B)) {
      
Serial.println("B");
      msg = 
"BTN_B";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
    
else if(!digitalRead(C)) {
      
Serial.println("C");
      msg = 
"BTN_C";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
    
else if(!digitalRead(D)) {
      
Serial.println("D");
      msg = 
"BTN_D";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
    
else if(!digitalRead(E)) {
      
Serial.println("E");
      msg = 
"BTN_E";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
    
else if(!digitalRead(F)) {
      
Serial.println("F");
      msg = 
"BTN_F";
      status = false; 
//marking sent
      sentTime = 
millis(); //store time for limitation
    }
   
    udp.
beginPacket(rcvIP, 4210);
    udp.
print(msg);
    udp.
endPacket();
    msg = 
"";
  }
 
  
int pSize = udp.parsePacket();
  
if (pSize) {
    
char buf[pSize];
    
int len = udp.read(buf, pSize);
    buf[len] = 
0//termination

    
if (String(buf) == "RDY") {
      status = true;
      
Serial.println("ACK");
     
    }
    
else {
      
Serial.println(buf);
      deserializeJson(doc, buf);
      
float l;
      u8g2.clearBuffer();
      
for(int i = 0; i<doc["count"]; i++) {
        l = doc[
"vals"][i]["dist"];
        
if(l>60) l = 60;
        drawLineAngle(doc[
"vals"][i]["angle"] , l);
      }
      u8g2.sendBuffer();
    }
  }
  
delay(100);
}

Explication :

Après le démarrage, l'ESP32 se connecte au WLAN, initialise l'écran OLED, calibre le joystick sur sa position centrale et définit les GPIO nécessaires pour les boutons-poussoirs. Dans la boucle principale, les axes du joystick sont évalués en continu. S'ils dépassent une valeur seuil, une direction de déplacement est déterminée à partir de celle-ci et envoyée par UDP sous forme de commande (par ex. avant, arrière, gauche, droite), ou d'arrêt si le joystick se trouve en position centrale. En outre, des commandes spéciales peuvent être déclenchées via les touches A-F. Pour éviter les envois multiples, chaque commande n'est retransmise que lorsqu'une confirmation ou un timeout a eu lieu.

 

Les paquets UDP entrants sont également évalués. Si le message de retour "RDY" est reçu, cela signale que le récepteur est prêt pour la prochaine commande. Si le paquet contient plutôt un objet JSON avec des données de mesure, celles-ci sont analysées et affichées sous forme de lignes sur l'OLED. L'angle et la distance des valeurs de mesure représentent des lignes qui partent du centre de l'écran. On obtient ainsi une représentation graphique simple de l'environnement détecté par l'autre ESP32.

 

Les lignes sont créées avec la fonction drawLineAngle(). Ici, les coordonnées finales de la ligne sont calculées avec le sinus et le cosinus. Vous trouverez une explication détaillée dans le blog Montre rétro avec GC9A01A.

 

Vous pouvez utiliser le code ici télécharger.

Chargez le programme sur le microcontrôleur avec les données d'accès et l'adresse IP correctes du récepteur.


Conclusion

Si vous appuyez maintenant sur le bouton A du contrôleur, l'environnement dans les 60 cm à venir est représenté graphiquement à l'écran sous forme de ligne pour chaque mesure. Ici, un pixel correspond à environ un centimètre. Si vous souhaitez un rayon de représentation plus large, vous pouvez adapter le programme du contrôleur en conséquence, mais vous devez tenir compte de la longueur maximale de 60 pixels.

Bien entendu, ce projet ne fait que poser les bases de vos propres extensions et modifications. Outre le balayage de l'environnement, d'autres fonctions peuvent être ajoutées, vous disposez ici des boutons inoccupés sur le Controller Shield.

Amuse-toi bien à les reproduire :)

Esp32Esp8266Projekte für anfänger

Laisser un commentaire

Tous les commentaires sont modérés avant d'être publiés

Articles de blog recommandés

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery