Fernsteuerung für den omnidirektionalen Roboter

In de blogpost van vandaag willen we een op afstand bestuurbare verkenningsrobot bouwen met behulp van de Acebott Smart Car Kit. De robot wordt bestuurd met een joystick en toont zijn omgeving in radaroptiek op een klein scherm.
De ultrasone sensor, die al gemonteerd is op een servomotor met een acrylhouder, wordt gebruikt om de omgeving te scannen.

Het besturingssysteem maakt gebruik van de PS2 joystick shield en ESP32 D1 R32 setup die je misschien al kent van de blogposts over het besturen van de Bionic Spider robot.

Hardware

Als je de aanpassingen aan het PS2 Joystick Shield in een eerdere blog uit de Bionic Spider Controller serie al hebt gedaan, kun je de volgende paragraaf overslaan.

 

Voor de realisatie van het project heb je nodig:

ESP32 NodeMCU D1 R32

PS2 joystick schild

(optioneel) 9V blokbatterij + Batterijclip

en natuurlijk een geassembleerd exemplaar Acebott robot auto

 

Aangezien de analoge ingangen van de twee assen van de joystick verbonden zijn met GPIO 2 en 4, die verbonden zijn met ADC2, kunnen deze niet gelijktijdig met de WLAN-module gebruikt worden. De aansluitingen IO 34 en IO35 (ADC1) bevinden zich hier echter direct naast, zodat de functionaliteit eenvoudig kan worden ingesteld via een draadjumper.

Afbeelding 1: Draadbrug tussen de aansluitingen

Sluit de aansluitingen aan zoals hierboven getoond.
Omdat de on-board LED is verbonden met GPIO 2, moet de pin hier worden verwijderd omdat de verbinding anders de waarde van de spanning op de X-as zou beïnvloeden.

Hetzelfde geldt voor pin D12, omdat deze het opstartproces verstoort en er dus niet opgestart kan worden met het shield aangesloten. De middelste knop van de joystick is verbonden met deze aansluiting en kan dus niet meer gebruikt worden. Als je dit toch wilt implementeren, moet het opnieuw verbonden worden met een vrije aansluiting.

 

Afbeelding 2: Schild met wijzigingen

 

De afscherming kan gewoon op de D1 R32 worden aangesloten. Zorg ervoor dat de kleine schakelaar in de linkerhoek is ingesteld op 3v3 Anders kan de ESP32 beschadigd raken door de te hoge spanning van 5V.

 

 

Uitbreiding weergeven

Omdat de controller de omgeving van het voertuig moet weergeven in de vorm van een radarscherm, zoals in veel films te zien is, is een display nodig.

Hier raden we de 1,3" OLED grafisch display (SH1106). Dit is niet alleen een groot, goed afleesbaar grafisch display, maar het heeft ook de juiste indeling voor de contactstrip van de HC05 module op het schild (rechtsboven). Dit betekent dat het display zonder grote aanpassingen kan worden ingeplugd.

Alleen de bovenste SMD-weerstand naast knop A moet worden verwijderd, omdat deze oorspronkelijk was ontworpen om de spanning op de RXD-pin van de HC05 te halveren, zodat de module niet beschadigd raakt wanneer deze wordt gebruikt met de UNO-microcontroller.

 

Afbeelding wijzigen

 

Aangezien de gebruikte microcontroller (ESP32) een logisch niveau van 3,3V heeft, zou communicatie met het display niet meer werken met de gehalveerde spanning.

 

Software

Voor de communicatie worden de gegevenspakketten uitgewisseld met UDP. Met dit communicatieprotocol worden de pakketten eenvoudig verzonden zonder te controleren of het pakket is ontvangen.

Hoewel dit de gegevensuitwisseling erg snel maakt, is het ook onveilig, omdat het niet mogelijk is om te bepalen of de ontvanger de gegevens daadwerkelijk ontvangt. Communicatie via een netwerk maakt ook een groter bereik mogelijk in vergelijking met ESPnow of Bluetooth, omdat het netwerk een groot gebied kan bestrijken met repeaters en toegangspunten.
In principe kunnen de twee apparaten met elkaar communiceren op elke locatie waar je ontvangst hebt met je smartphone.

 

De bibliotheek voor UDP-communicatie is al opgenomen in het uitgebreide bordpakket voor de ESP32.

 

Naast de UDP- en WiFi-bibliotheek zijn er nog andere externe bibliotheken nodig, die je nog moet installeren.

U8g2

ArduinoJson

Deze kunnen worden gedownload van GitHub als een .zip-bestand via de volgende links en kunnen worden geopend in de Arduino IDE onder

Sketch > Bibliotheek opnemen > .zip-bibliotheek toevoegen ...

kunnen worden geselecteerd en geïnstalleerd.

 

De ESP32Servo, ultrasoon en voertuig Bibliotheken zijn al opgenomen in het .zip-bestand van de instructies. Deze zijn ook al geïnstalleerd in de instructies voor geavanceerde gebruikers.

 

Code smart auto

#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");
  }
}

Uitleg:

Bij het opstarten maakt de ESP32 verbinding met het WLAN, opent een UDP-poort en initialiseert de motoren, ultrasone sensor en servo. In de hoofdlus wacht het op binnenkomende UDP-commando's en evalueert deze als tekstcommando's. Afhankelijk van het commando worden de motoren aangestuurd of speciale functies uitgevoerd. Afhankelijk van het commando worden de motoren aangestuurd of speciale functies uitgevoerd.

Met het BTN_A commando zwenkt de servo de ultrasone sensor in stappen, meet de afstand op elke positie en slaat de hoek en afstand op in een JSON array. Zodra de scan is voltooid, wordt de JSON geserialiseerd en via UDP teruggestuurd naar de zender, waarna de servo terugkeert naar de middelste positie.

Na elke uitgevoerde actie stuurt de ESP32 de feedback "RDY" om aan te geven dat hij klaar is voor de volgende opdracht.

 

U kunt de code hier download.

Zodra je het programma met de juiste WLAN-toegangsgegevens in de microcontroller hebt geladen, wordt het IP-adres van het apparaat weergegeven. Noteer dit omdat het nodig is voor de controller.

Code controller

#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);
}

Uitleg:

Na het opstarten maakt de ESP32 verbinding met het WLAN, initialiseert het OLED-display, kalibreert de joystick op zijn middenpositie en stelt de vereiste GPIO's in voor de knoppen. De assen van de joystick worden continu geanalyseerd in de hoofdlus. Als ze een drempelwaarde overschrijden, wordt een bewegingsrichting bepaald en via UDP als commando verzonden (bijv. vooruit, achteruit, links, rechts) of wordt de joystick gestopt als deze in de middenpositie staat. Daarnaast kunnen speciale commando's worden geactiveerd met de A-F-knoppen. Om meervoudige transmissies te voorkomen, wordt elk commando pas opnieuw verzonden na bevestiging of een time-out.

 

Inkomende UDP pakketten worden ook geanalyseerd. Als het antwoord "RDY" wordt ontvangen, geeft dit aan dat de ontvanger klaar is voor het volgende commando. Als het pakket in plaats daarvan een JSON object met meetgegevens bevat, wordt dit ontleed en weergegeven als een lijnweergave op de OLED. De hoek en afstand van de gemeten waarden stellen lijnen voor die vanuit het midden van het scherm beginnen. Dit creëert een eenvoudige grafische weergave van de omgeving die door de andere ESP32 is gedetecteerd.

 

De lijnen worden gemaakt met de functie drawLineAngle(). Hier worden de eindcoördinaten van de lijn berekend met behulp van de sinus en cosinus. Je kunt een gedetailleerde uitleg vinden in de blog Retro horloge met GC9A01A.

 

U kunt de code hier download.

Laad het programma met de juiste toegangsgegevens en het IP-adres van de ontvanger in de microcontroller.


Conclusie

Als je nu op knop A op de controller drukt, toont het display de omgeving in de volgende 60 cm grafisch als een lijn voor elke meting. Eén pixel komt overeen met ongeveer één centimeter. Als je een bredere weergaveradius wilt, kun je het besturingsprogramma dienovereenkomstig aanpassen, maar let wel op de maximale lengte van 60 pixels.

Natuurlijk legt dit project alleen de basis voor je eigen uitbreidingen en aanpassingen. Naast het scannen van de omgeving kunnen ook andere functies worden toegevoegd; de niet-toegewezen knoppen op het controller shield zijn hier beschikbaar.

Veel plezier met herbouwen :)

Esp32Esp8266Projekte für anfänger

Laat een reactie achter

Alle opmerkingen worden voor publicatie gecontroleerd door een moderator

Aanbevolen blogberichten

  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