Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

DIY Arduino RC-Sender

Im Tutorial lernen wir, wie man einen DIY Arduino RC-Sender baut. Sehr oft benötige ich für meine Projekte eine drahtlose Steuerung, deshalb habe ich diesen multifunktionalen Funkcontroller gebaut, der für so ziemlich alles verwendet werden kann.

Sie können sich das folgende Video ansehen oder das schriftliche Tutorial unten lesen.

Übersicht

Jetzt kann ich jedes Arduino-Projekt mit nur einigen kleinen Anpassungen auf der Empfängerseite drahtlos steuern. Dieser Sender kann auch als handelsüblicher RC-Sender zur Steuerung von RC-Spielzeugen, Autos, Drohnen usw. verwendet werden. Dazu braucht es nur einen einfachen Arduino-Empfänger, der dann die entsprechenden Signale zur Steuerung dieser handelsüblichen RC-Geräte generiert.

Ich werde in diesem Video erklären, wie alles funktioniert, anhand einiger Beispiele für die Steuerung eines Arduino-Roboterautos, die Steuerung des Arduino Ant-Roboters aus meinem vorherigen Video und die Steuerung eines bürstenlosen Gleichstrommotors mit einem ESC und einigen Servomotoren.

Die Funkkommunikation dieses Controllers basiert auf dem Transceiver-Modul NRF24L01, das bei Verwendung mit einer verstärkten Antenne eine stabile Reichweite von bis zu 700 Metern im offenen Raum haben kann. Es verfügt über 14 Kanäle, davon 6 analoge Eingänge und 8 digitale Eingänge.

Es hat zwei Joysticks, zwei Potentiometer, zwei Kippschalter, sechs Tasten und zusätzlich eine interne Messeinheit, bestehend aus einem Beschleunigungsmesser und einem Gyroskop, die auch zur Steuerung von Dingen verwendet werden kann, indem man einfach den Controller bewegt oder neigt.

Schaltplan des Arduino RC-Senders

Werfen wir zunächst einen Blick auf den Schaltplan. Das Gehirn dieses RC-Controllers ist ein Arduino Pro Mini, der mit 2 LiPo-Batterien betrieben wird, die etwa 7,4 Volt erzeugen. Wir können sie direkt an den RAW-Pin des Pro Mini anschließen, der über einen Spannungsregler verfügt, der die Spannung auf 5 V reduziert. Beachten Sie, dass es zwei Versionen des Arduino Pro Mini gibt, wie die, die ich habe, die mit 5 V arbeitet und die andere mit 3,3 V.

Andererseits benötigt das NRF24L01-Modul unbedingt 3,3 V und es wird empfohlen, es aus einer dedizierten Quelle zu beziehen. Daher müssen wir einen 3,3-V-Spannungsregler verwenden, der an die Batterien angeschlossen ist und die 7,4 V in 3,3 V umwandelt. Außerdem müssen wir direkt neben dem Modul einen Entkopplungskondensator verwenden, um die Spannung stabiler zu halten, wodurch auch die Funkkommunikation stabiler wird. Das NRF24L01-Modul kommuniziert mit dem Arduino über das SPI-Protokoll, während das MPU6050-Beschleunigungsmesser- und Kreiselmodul das I2C-Protokoll verwendet.

Die für dieses Arduino-Tutorial benötigten Komponenten erhalten Sie über die folgenden Links:

  • NRF24L01 Transceiver-Modul……….. 
  • NRF24L01 + PA + LNA …………………….
  • Potentiometer ……………………………….. 
  • Servomotor …………………………………… 
  • Kippschalter …………………………….….. 
  • Joystick ………………………………………….. – dieser Joystick wird mit einem Breakout Board geliefert, so dass Sie den Joystick davon ablöten müssen
  • Joystick ohne Breakoutboard ………… Ebay
  • Arduino Pro Mini…………………………….. – Für diese Boards benötigen Sie die PCB V2- oder V3-Version
  • Arduino Pro Mini wie das, das ich verwendet habe….. – PCB V1
  • HT7333 3,3 V Spannungsregler ……………. Aus dem örtlichen Elektrofachgeschäft – PCB V1 &PCB V2
  • AMS1117 3,3 V Spannungsregler …………… Amazon / Banggoood / AliExpress – Platine V3

PCB-Design

Ich habe tatsächlich alle analogen und digitalen Pins des Arduino Pro Mini verwendet. Wenn ich jetzt also versuche, alles mit Schaltdrähten miteinander zu verbinden, wird es ein ziemliches Durcheinander. Daher habe ich mit der kostenlosen Online-Schaltungsdesign-Software EasyEDA eine benutzerdefinierte Leiterplatte entworfen.

Hier habe ich die Ergonomie des Controllers berücksichtigt und ihn so gestaltet, dass er leicht mit zwei Händen gehalten werden kann, während sich alle Bedienelemente in Reichweite der Finger befinden. Ich habe die Kanten abgerundet und einige 3-mm-Löcher hinzugefügt, damit ich die Platine später auf etwas montieren kann. Ich habe die Pins zum Programmieren des Arduino Pro Mini auf der Oberseite des Controllers platziert, damit sie leicht zugänglich sind, falls wir den Arduino neu programmieren möchten. Wir können hier auch feststellen, dass ich die RX- und TX-Pins des Arduino für die Joystick-Tasten verwendet habe. Diese beiden Leitungen müssen jedoch von irgendetwas getrennt werden, während wir die Skizze auf den Arduino hochladen. Deshalb werden sie mit zwei Pins unterbrochen, die dann einfach mit einfachen Jumper-Kappen verbunden werden können.

Bitte beachten: Stellen Sie sicher, dass Sie die richtige Arduino Pro Mini-Version haben, um die Leiterplatte zu bearbeiten, oder ändern Sie das Leiterplattendesign entsprechend. Hier ist ein Vergleichsfoto zwischen den drei verschiedenen Versionen, abhängig von Ihrem Arduino und dem Spannungsregler.

Hier ist ein Link zu den Projektdateien dieses PCB-Designs. Dadurch werden die drei verschiedenen Versionen in separaten Registerkarten geöffnet, sodass Sie die gewünschte auswählen können.

Als ich also mit dem Design fertig war, generierte ich die Gerber-Datei, die für die Herstellung der Leiterplatte benötigt wurde.

Gerber-Datei:

Dann bestellte ich die Platine bei JLCPCB, die auch der Sponsor dieses Videos sind.

Hier können wir die Gerber-Datei einfach per Drag-and-Drop ziehen und nach dem Hochladen können wir unser PCB im Gerber-Viewer überprüfen. Wenn alles in Ordnung ist, können wir fortfahren und die gewünschten Eigenschaften für unsere Leiterplatte auswählen. Dieses Mal habe ich die PCB-Farbe schwarz gewählt. Und das war's, jetzt können wir unsere Leiterplatte einfach zu einem günstigen Preis bestellen. Beachten Sie, dass Sie bei Ihrer ersten Bestellung bei JLCPCB bis zu 10 PCBs für nur 2 $ erhalten können.

Und hier ist es. Ich liebe es einfach, wie diese Platine in dieser schwarzen Farbe herauskam. Die Qualität der Leiterplatte ist großartig und alles ist genau so wie im Design.

Montage der Leiterplatte

Ok, jetzt können wir mit der Bestückung der Platine fortfahren. Ich begann mit einem Löten der Stiftleisten des Arduino Pro Mini. Eine einfache und gute Möglichkeit, dies zu tun, besteht darin, sie auf ein Steckbrett zu legen, damit sie beim Löten fest an Ort und Stelle bleiben.

Der Pro Mini hat auch Stifte an den Seiten, aber beachten Sie, dass die Position dieser Stifte je nach Hersteller variieren kann.

Für das spezielle Modell, das ich habe, brauche ich 5 Pins für jede Seite, während ich einen GND-Pin leer lasse, weil ich seinen Bereich unten auf der Platine zum Ausführen einiger Spuren verwendet habe. Ich habe den Arduino Pro Mini direkt auf die Platine gelötet und die Execs-Länge der Header geschnitten. Gleich daneben befindet sich das MPU6050-Beschleunigungsmesser- und Gyroskopmodul.

Dann habe ich den 3,3-V-Spannungsregler mit einem Kondensator daneben und einem weiteren Kondensator in der Nähe des NRF24L01-Moduls gelötet. Dieses Modul hat drei verschiedene Versionen und wir können jede davon hier verwenden.

Weiter ging es mit den Pins zum Programmieren des Arduino, den RX- und TX-Pins, den Stromversorgungs-Pins und dem Netzschalter.

Als nächstes musste ich zum Löten der Potentiometer auf die Platine ihre Pins mit einigen Stiftleisten verlängern.

Wir können hier bemerken, dass ich vorher die Länge der Knöpfe geschnitten habe, damit ich einige Kappen richtig auf sie setzen kann. Allerdings werden wir die Potentiometer etwas später auf die Platine löten.

Dann habe ich die beiden Kippschalter und die beiden Joysticks eingesetzt und verlötet.

Zum Schluss müssen noch die vier Druckknöpfe verlötet werden. Allerdings haben sie nicht die richtige Höhe, also habe ich wieder Stiftleisten verwendet, um ihre Stifte ein wenig zu verlängern.

Und das ist es, unsere Leiterplatte ist jetzt fertig, sodass wir mit der Herstellung der Abdeckung dafür fortfahren können. Weil mir das Aussehen der Platine gefällt und ich gut sichtbar sein möchte, habe ich mich entschieden, transparentes Acryl für die Abdeckung zu verwenden.

Hier habe ich 4 mm dickes transparentes Acryl, das derzeit eine Schutzfolie hat und blau erscheint. Die Idee für die Abdeckung besteht darin, zwei Platten mit der Form der Leiterplatte herzustellen und eine davon an der Oberseite und die andere an der Unterseite der Leiterplatte zu befestigen.

Also habe ich die Leiterplattenform markiert und mit einer Metallhandsäge das Acryl entsprechend geschnitten.

Dann habe ich mit einer einfachen Raspel die Form des Acryls fein abgestimmt. Die beiden Platten sind super geworden und passen perfekt zur Platine.

Als nächstes markierte ich die Stellen, an denen ich Öffnungen machen muss, damit die Komponenten passieren können. Mit einem 3mm Bohrer habe ich zuerst die 4 Löcher gemacht, um die Platten an der Platine zu befestigen. Für diese Löcher habe ich auch Senken gemacht, damit die Bolzen blitzschnell mit den Platten platziert werden können.

Für die Öffnungen für die Kippschalter und die Potentiometer habe ich einen 6mm Bohrer verwendet, und für die Steuerknüppelöffnungen habe ich einen 25mm Forstnerbohrer verwendet. Auch hier habe ich mit einer Raspel alle Öffnungen verfeinert.

Vor dem Zusammenbau der Abdeckung noch eine kleine Anmerkung, dass ich die Stiftleiste für die Stromversorgung tatsächlich verkehrt herum gelötet habe, damit sie von der Rückseite erreicht werden kann, wo sich die Batterie befindet.

Ok, jetzt können wir mit der Montage der Abdeckung beginnen. Ich begann damit, die Schutzfolie vom Acryl abzuziehen, was, wie ich zugeben muss, ziemlich befriedigend war, weil das Acryl jetzt so sauber war. Also habe ich zuerst die beiden Potentiometer auf der oberen Platte befestigt, die 3-mm-Befestigungsschrauben eingesetzt und die 11-mm-Distanzringe platziert.

Dann habe ich sorgfältig die obere Platte und die Platine mit einigen Schrauben zusammengeführt und gesichert. An dieser Stelle habe ich schließlich die Potentiometer auf die Platine gelötet, weil ich vorher nicht genau wusste, auf welcher Höhe sie platziert werden.

Als nächstes befestigte ich auf der Rückplatte den Batteriehalter mit 2 Schrauben. Ich beendete die Abdeckungsmontage, indem ich die Rückplatte mit den vier Befestigungsschrauben an der Rückseite der Platine befestigte.

Schließlich können wir die Batterieleitungen an den Stromversorgungsstiften befestigen, die Knöpfe an den Potentiometern einsetzen und sichern, die Joystickknöpfe einsetzen und die Antenne am NRF24l01-Modul befestigen. Und das ist es, wir sind endlich fertig mit dem DIY Arduino RC-Sender.

Jetzt muss nur noch der Arduino programmiert werden. Zum Programmieren eines Pro Mini-Boards benötigen wir eine USB-zu-Seriell-UART-Schnittstelle, die an den Programmierkopf angeschlossen werden kann, der sich auf der Oberseite unseres Controllers befindet.

Dann müssen wir im Arduino IDE-Tools-Menü das Arduino Pro- oder Pro Mini-Board auswählen, die richtige Version des Prozessors auswählen, den Port auswählen und die Programmiermethode „USBasp“ auswählen.

Und jetzt können wir den Code auf den Arduino hochladen.

DIY Arduino-basierter RC-Sendercode

Lassen Sie uns erklären, wie der Sendercode funktioniert. Also müssen wir zuerst die SPI- und RF24-Bibliothek für die drahtlose Kommunikation und die I2C-Bibliothek für das Beschleunigungssensormodul einbinden. Dann müssen wir die digitalen Eingänge definieren, einige Variablen, die für das Programm unten benötigt werden, das Funkobjekt und die Kommunikationsadresse definieren.

#include <SPI.h> #include <nRF24L01.h> #include <RF24.h> #include <Wire.h> // Define the digital inputs #define jB1 1 // Joystick button 1 #define jB2 0 // Joystick button 2 #define t1 7 // Toggle switch 1 #define t2 4 // Toggle switch 1 #define b1 8 // Button 1 #define b2 9 // Button 2 #define b3 2 // Button 3 #define b4 3 // Button 4 const int MPU = 0x68; // MPU6050 I2C address float AccX, AccY, AccZ; float GyroX, GyroY, GyroZ; float accAngleX, accAngleY, gyroAngleX, gyroAngleY; float angleX, angleY; float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY; float elapsedTime, currentTime, previousTime; int c = 0; RF24 radio(5, 6); // nRF24L01 (CE, CSN) const byte address[6] = "00001"; // Address Codesprache:Arduino (arduino)

Dann müssen wir eine Struktur definieren, in der wir die 14 Eingangswerte des Controllers speichern. Die maximale Größe dieser Struktur kann 32 Bytes betragen, da dies die NRF24L01-Puffergrenze oder die Datenmenge ist, die das Modul auf einmal senden kann.

/ Max size of this struct is 32 bytes - NRF24L01 buffer limit
struct Data_Package {
  byte j1PotX;
  byte j1PotY;
  byte j1Button;
  byte j2PotX;
  byte j2PotY;
  byte j2Button;
  byte pot1;
  byte pot2;
  byte tSwitch1;
  byte tSwitch2;
  byte button1;
  byte button2;
  byte button3;
  byte button4;
};

Data_Package data; //Create a variable with the above structure Codesprache:Arduino (arduino) 

Im Setup-Abschnitt müssen wir das MPU6050-Modul initialisieren und wir können auch den IMU-Fehler berechnen, der ein Wert ist, der später bei der Berechnung der korrekten Winkel des Moduls verwendet wird.

void initialize_MPU6050() {
  Wire.begin();                      // Initialize comunication
  Wire.beginTransmission(MPU);       // Start communication with MPU6050 // MPU=0x68
  Wire.write(0x6B);                  // Talk to the register 6B
  Wire.write(0x00);                  // Make reset - place a 0 into the 6B register
  Wire.endTransmission(true);        //end the transmission
  // Configure Accelerometer
  Wire.beginTransmission(MPU);
  Wire.write(0x1C);                  //Talk to the ACCEL_CONFIG register
  Wire.write(0x10);                  //Set the register bits as 00010000 (+/- 8g full scale range)
  Wire.endTransmission(true);
  // Configure Gyro
  Wire.beginTransmission(MPU);
  Wire.write(0x1B);                   // Talk to the GYRO_CONFIG register (1B hex)
  Wire.write(0x10);                   // Set the register bits as 00010000 (1000dps full scale)
  Wire.endTransmission(true);
} Codesprache:Arduino (arduino) 

Weitere Einzelheiten zur Funktionsweise von MEMS-Beschleunigungsmessern und Kreiseln finden Sie hier. Ein spezielles Tutorial für die MPU6050 ist in Kürze verfügbar.

Dann müssen wir die Funkkommunikation initialisieren, die Arduino-internen Pull-up-Widerstände für alle digitalen Eingänge aktivieren und die anfänglichen Standardwerte für alle Variablen festlegen.

// Define the radio communication
  radio.begin();
  radio.openWritingPipe(address);
  radio.setAutoAck(false);
  radio.setDataRate(RF24_250KBPS);
  radio.setPALevel(RF24_PA_LOW);
  
  // Activate the Arduino internal pull-up resistors
  pinMode(jB1, INPUT_PULLUP);
  pinMode(jB2, INPUT_PULLUP);
  pinMode(t1, INPUT_PULLUP);
  pinMode(t2, INPUT_PULLUP);
  pinMode(b1, INPUT_PULLUP);
  pinMode(b2, INPUT_PULLUP);
  pinMode(b3, INPUT_PULLUP);
  pinMode(b4, INPUT_PULLUP); Codesprache:Arduino (arduino) 

Beginnen Sie im Schleifenabschnitt mit dem Lesen aller analogen Eingänge und ordnen Sie deren Werte von 0 bis 1023 Bytewerten von 0 bis 255 zu, da wir die Variablen in unserer Struktur bereits als Bytes definiert haben. Jede Eingabe wird in der jeweiligen Datenvariable aus der Struktur gespeichert.

// Read all analog inputs and map them to one Byte value
  data.j1PotX = map(analogRead(A1), 0, 1023, 0, 255); // Convert the analog read value from 0 to 1023 into a BYTE value from 0 to 255
  data.j1PotY = map(analogRead(A0), 0, 1023, 0, 255);
  data.j2PotX = map(analogRead(A2), 0, 1023, 0, 255);
  data.j2PotY = map(analogRead(A3), 0, 1023, 0, 255);
  data.pot1 = map(analogRead(A7), 0, 1023, 0, 255);
  data.pot2 = map(analogRead(A6), 0, 1023, 0, 255); Codesprache:Arduino (arduino) 

Wir sollten nur beachten, dass, weil wir die Pull-up-Widerstände verwenden, die digitalen Pins 0 sind, wenn die Tasten gedrückt werden.

// Read all digital inputs
  data.j1Button = digitalRead(jB1);
  data.j2Button = digitalRead(jB2);
  data.tSwitch2 = digitalRead(t2);
  data.button1 = digitalRead(b1);
  data.button2 = digitalRead(b2);
  data.button3 = digitalRead(b3);
  data.button4 = digitalRead(b4); Codesprache:Arduino (arduino) 

Mit der Funktion radio.write() senden wir also einfach die Werte von allen 14 Kanälen an den Empfänger.

// Send the whole data from the structure to the receiver
  radio.write(&data, sizeof(Data_Package)); Codesprache:Arduino (arduino) 

Falls der Kippschalter 1 eingeschaltet ist, verwenden wir stattdessen die Beschleunigungsmesser- und Kreiseldaten für die Steuerung.

if (digitalRead(t1) == 0) {
    read_IMU();    // Use MPU6050 instead of Joystick 1 for controling left, right, forward and backward movements
  } Codesprache:Arduino (arduino) 

Anstelle der Joystick 1 X- und Y-Werte verwenden wir also die Winkelwerte, die wir von der IMU erhalten, die wir zuvor von Werten von -90 bis +90 Grad in Byte-Werte von 0 bis 255 entsprechend umwandeln.

// Map the angle values from -90deg to +90 deg into values from 0 to 255, like the values we are getting from the Joystick
  data.j1PotX = map(angleX, -90, +90, 255, 0);
  data.j1PotY = map(angleY, -90, +90, 0, 255); Codesprache:Arduino (arduino) 

So also der Sendercode, das Wichtigste war, die Funkkommunikation zu definieren und die Daten an den Empfänger zu senden.

Hier ist der vollständige Arduino-Code für diesen DIY-Arduino-RC-Sender:

/*
        DIY Arduino based RC Transmitter
  by Dejan Nedelkovski, www.HowToMechatronics.com
  Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Wire.h>


// Define the digital inputs
#define jB1 1  // Joystick button 1
#define jB2 0  // Joystick button 2
#define t1 7   // Toggle switch 1
#define t2 4   // Toggle switch 1
#define b1 8   // Button 1
#define b2 9   // Button 2
#define b3 2   // Button 3
#define b4 3   // Button 4

const int MPU = 0x68; // MPU6050 I2C address
float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY;
float angleX, angleY;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY;
float elapsedTime, currentTime, previousTime;
int c = 0;


RF24 radio(5, 6);   // nRF24L01 (CE, CSN)
const byte address[6] = "00001"; // Address

// Max size of this struct is 32 bytes - NRF24L01 buffer limit
struct Data_Package {
  byte j1PotX;
  byte j1PotY;
  byte j1Button;
  byte j2PotX;
  byte j2PotY;
  byte j2Button;
  byte pot1;
  byte pot2;
  byte tSwitch1;
  byte tSwitch2;
  byte button1;
  byte button2;
  byte button3;
  byte button4;
};

Data_Package data; //Create a variable with the above structure

void setup() {
  Serial.begin(9600);
  
  // Initialize interface to the MPU6050
  initialize_MPU6050();

  // Call this function if you need to get the IMU error values for your module
  //calculate_IMU_error();
  
  // Define the radio communication
  radio.begin();
  radio.openWritingPipe(address);
  radio.setAutoAck(false);
  radio.setDataRate(RF24_250KBPS);
  radio.setPALevel(RF24_PA_LOW);
  
  // Activate the Arduino internal pull-up resistors
  pinMode(jB1, INPUT_PULLUP);
  pinMode(jB2, INPUT_PULLUP);
  pinMode(t1, INPUT_PULLUP);
  pinMode(t2, INPUT_PULLUP);
  pinMode(b1, INPUT_PULLUP);
  pinMode(b2, INPUT_PULLUP);
  pinMode(b3, INPUT_PULLUP);
  pinMode(b4, INPUT_PULLUP);
  
  // Set initial default values
  data.j1PotX = 127; // Values from 0 to 255. When Joystick is in resting position, the value is in the middle, or 127. We actually map the pot value from 0 to 1023 to 0 to 255 because that's one BYTE value
  data.j1PotY = 127;
  data.j2PotX = 127;
  data.j2PotY = 127;
  data.j1Button = 1;
  data.j2Button = 1;
  data.pot1 = 1;
  data.pot2 = 1;
  data.tSwitch1 = 1;
  data.tSwitch2 = 1;
  data.button1 = 1;
  data.button2 = 1;
  data.button3 = 1;
  data.button4 = 1;
}
void loop() {
  // Read all analog inputs and map them to one Byte value
  data.j1PotX = map(analogRead(A1), 0, 1023, 0, 255); // Convert the analog read value from 0 to 1023 into a BYTE value from 0 to 255
  data.j1PotY = map(analogRead(A0), 0, 1023, 0, 255);
  data.j2PotX = map(analogRead(A2), 0, 1023, 0, 255);
  data.j2PotY = map(analogRead(A3), 0, 1023, 0, 255);
  data.pot1 = map(analogRead(A7), 0, 1023, 0, 255);
  data.pot2 = map(analogRead(A6), 0, 1023, 0, 255);
  // Read all digital inputs
  data.j1Button = digitalRead(jB1);
  data.j2Button = digitalRead(jB2);
  data.tSwitch2 = digitalRead(t2);
  data.button1 = digitalRead(b1);
  data.button2 = digitalRead(b2);
  data.button3 = digitalRead(b3);
  data.button4 = digitalRead(b4);
  // If toggle switch 1 is switched on
  if (digitalRead(t1) == 0) {
    read_IMU();    // Use MPU6050 instead of Joystick 1 for controling left, right, forward and backward movements
  }
  // Send the whole data from the structure to the receiver
  radio.write(&data, sizeof(Data_Package));
}

void initialize_MPU6050() {
  Wire.begin();                      // Initialize comunication
  Wire.beginTransmission(MPU);       // Start communication with MPU6050 // MPU=0x68
  Wire.write(0x6B);                  // Talk to the register 6B
  Wire.write(0x00);                  // Make reset - place a 0 into the 6B register
  Wire.endTransmission(true);        //end the transmission
  // Configure Accelerometer
  Wire.beginTransmission(MPU);
  Wire.write(0x1C);                  //Talk to the ACCEL_CONFIG register
  Wire.write(0x10);                  //Set the register bits as 00010000 (+/- 8g full scale range)
  Wire.endTransmission(true);
  // Configure Gyro
  Wire.beginTransmission(MPU);
  Wire.write(0x1B);                   // Talk to the GYRO_CONFIG register (1B hex)
  Wire.write(0x10);                   // Set the register bits as 00010000 (1000dps full scale)
  Wire.endTransmission(true);
}

void calculate_IMU_error() {
  // We can call this funtion in the setup section to calculate the accelerometer and gury data error. From here we will get the error values used in the above equations printed on the Serial Monitor.
  // Note that we should place the IMU flat in order to get the proper values, so that we then can the correct values
  // Read accelerometer values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    AccX = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
    AccY = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
    AccZ = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
    // Sum all readings
    AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
    AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
    c++;
  }
  //Divide the sum by 200 to get the error value
  AccErrorX = AccErrorX / 200;
  AccErrorY = AccErrorY / 200;
  c = 0;
  // Read gyro values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x43);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 4, true);
    GyroX = Wire.read() << 8 | Wire.read();
    GyroY = Wire.read() << 8 | Wire.read();
    // Sum all readings
    GyroErrorX = GyroErrorX + (GyroX / 32.8);
    GyroErrorY = GyroErrorY + (GyroY / 32.8);
    c++;
  }
  //Divide the sum by 200 to get the error value
  GyroErrorX = GyroErrorX / 200;
  GyroErrorY = GyroErrorY / 200;
  // Print the error values on the Serial Monitor
  Serial.print("AccErrorX: ");
  Serial.println(AccErrorX);
  Serial.print("AccErrorY: ");
  Serial.println(AccErrorY);
  Serial.print("GyroErrorX: ");
  Serial.println(GyroErrorX);
  Serial.print("GyroErrorY: ");
  Serial.println(GyroErrorY);
}

void read_IMU() {
  // === Read acceleromter data === //
  Wire.beginTransmission(MPU);
  Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  //For a range of +-8g, we need to divide the raw values by 4096, according to the datasheet
  AccX = (Wire.read() << 8 | Wire.read()) / 4096.0; // X-axis value
  AccY = (Wire.read() << 8 | Wire.read()) / 4096.0; // Y-axis value
  AccZ = (Wire.read() << 8 | Wire.read()) / 4096.0; // Z-axis value

  // Calculating angle values using
  accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI) + 1.15; // AccErrorX ~(-1.15) See the calculate_IMU_error()custom function for more details
  accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * 180 / PI) - 0.52; // AccErrorX ~(0.5)

  // === Read gyro data === //
  previousTime = currentTime;        // Previous time is stored before the actual time read
  currentTime = millis();            // Current time actual time read
  elapsedTime = (currentTime - previousTime) / 1000;   // Divide by 1000 to get seconds
  Wire.beginTransmission(MPU);
  Wire.write(0x43); // Gyro data first register address 0x43
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 4, true); // Read 4 registers total, each axis value is stored in 2 registers
  GyroX = (Wire.read() << 8 | Wire.read()) / 32.8; // For a 1000dps range we have to divide first the raw value by 32.8, according to the datasheet
  GyroY = (Wire.read() << 8 | Wire.read()) / 32.8;
  GyroX = GyroX + 1.85; //// GyroErrorX ~(-1.85)
  GyroY = GyroY - 0.15; // GyroErrorY ~(0.15)
  // Currently the raw values are in degrees per seconds, deg/s, so we need to multiply by sendonds (s) to get the angle in degrees
  gyroAngleX = GyroX * elapsedTime;
  gyroAngleY = GyroY * elapsedTime;

  // Complementary filter - combine acceleromter and gyro angle values
  angleX = 0.98 * (angleX + gyroAngleX) + 0.02 * accAngleX;
  angleY = 0.98 * (angleY + gyroAngleY) + 0.02 * accAngleY;
  // Map the angle values from -90deg to +90 deg into values from 0 to 255, like the values we are getting from the Joystick
  data.j1PotX = map(angleX, -90, +90, 255, 0);
  data.j1PotY = map(angleY, -90, +90, 0, 255);
} Codesprache:Arduino (arduino) 

Empfängercode

Schauen wir uns nun an, wie wir diese Daten erhalten können. Hier ist ein einfaches Arduino- und NRF24L01-Empfängerschema. Natürlich können Sie auch jedes andere Arduino-Board verwenden.

Und hier ist ein einfacher Empfängercode, bei dem wir die Daten empfangen und einfach auf dem seriellen Monitor drucken, damit wir wissen, dass die Kommunikation ordnungsgemäß funktioniert. Wieder müssen wir die RF24-Bibliothek einbinden und die Objekte und die Struktur auf die gleiche Weise wie im Sendercode definieren. Im Setup-Abschnitt müssen wir beim Definieren der Funkkommunikation die gleichen Einstellungen wie beim Sender verwenden und das Modul mit der Funktion radio.startListening() als Empfänger festlegen.

/*
    DIY Arduino based RC Transmitter Project
              == Receiver Code ==

  by Dejan Nedelkovski, www.HowToMechatronics.com
  Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(10, 9);   // nRF24L01 (CE, CSN)
const byte address[6] = "00001";

unsigned long lastReceiveTime = 0;
unsigned long currentTime = 0;

// Max size of this struct is 32 bytes - NRF24L01 buffer limit
struct Data_Package {
  byte j1PotX;
  byte j1PotY;
  byte j1Button;
  byte j2PotX;
  byte j2PotY;
  byte j2Button;
  byte pot1;
  byte pot2;
  byte tSwitch1;
  byte tSwitch2;
  byte button1;
  byte button2;
  byte button3;
  byte button4;
};

Data_Package data; //Create a variable with the above structure

void setup() {
  Serial.begin(9600);
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setAutoAck(false);
  radio.setDataRate(RF24_250KBPS);
  radio.setPALevel(RF24_PA_LOW);
  radio.startListening(); //  Set the module as receiver
  resetData();
}
void loop() {
  // Check whether there is data to be received
  if (radio.available()) {
    radio.read(&data, sizeof(Data_Package)); // Read the whole data and store it into the 'data' structure
    lastReceiveTime = millis(); // At this moment we have received the data
  }
  // Check whether we keep receving data, or we have a connection between the two modules
  currentTime = millis();
  if ( currentTime - lastReceiveTime > 1000 ) { // If current time is more then 1 second since we have recived the last data, that means we have lost connection
    resetData(); // If connection is lost, reset the data. It prevents unwanted behavior, for example if a drone has a throttle up and we lose connection, it can keep flying unless we reset the values
  }
  // Print the data in the Serial Monitor
  Serial.print("j1PotX: ");
  Serial.print(data.j1PotX);
  Serial.print("; j1PotY: ");
  Serial.print(data.j1PotY);
  Serial.print("; button1: ");
  Serial.print(data.button1);
  Serial.print("; j2PotX: ");
  Serial.println(data.j2PotX); 
}

void resetData() {
  // Reset the values when there is no radio connection - Set initial default values
  data.j1PotX = 127;
  data.j1PotY = 127;
  data.j2PotX = 127;
  data.j2PotY = 127;
  data.j1Button = 1;
  data.j2Button = 1;
  data.pot1 = 1;
  data.pot2 = 1;
  data.tSwitch1 = 1;
  data.tSwitch2 = 1;
  data.button1 = 1;
  data.button2 = 1;
  data.button3 = 1;
  data.button4 = 1;
} Codesprache:Arduino (arduino) 

In der Hauptschleife prüfen wir mit der available()-Funktion, ob es eingehende Daten gibt. Wenn wahr, lesen wir einfach die Daten und speichern sie in den Variablen der Struktur. Jetzt können wir die Daten auf dem seriellen Monitor ausdrucken, um zu prüfen, ob die Übertragung richtig funktioniert. Auch mit der Funktion millis() und einer if-Anweisung prüfen wir, ob wir weiterhin Daten empfangen, oder wenn wir länger als 1 Sekunde keine Daten erhalten, setzen wir Variablen auf ihre Standardwerte zurück. Wir verwenden dies, um unerwünschtes Verhalten zu verhindern, zum Beispiel wenn eine Drohne Gas gibt und wir die Verbindung verlieren, kann sie weiter wegfliegen, es sei denn, wir setzen die Werte zurück.

Das war's. Jetzt können wir diese Methode zum Empfangen der Daten für jedes Arduino-Projekt implementieren. Hier zum Beispiel der Code zur Steuerung des Arduino-Roboterautos aus einem meiner vorherigen Videos.

Als Update für dieses Projekt habe ich einen dedizierten Arduino-basierten RC-Empfänger erstellt. Auch hier basiert es auf dem Arduino Pro Mini-Board und verfügt über mehrere gebrauchsfertige Servos und ESCs-Anschlüsse, die auf einer kompakten Platine platziert sind.

Arduino Robot Car Wireless Control mit RC-Sender

Arduino-Code:

Hier müssen wir die Bibliotheken, die Struktur und die Funkkommunikation wie zuvor erläutert definieren. Dann müssen wir in der Hauptschleife nur die eingehenden Daten lesen und sie für alles verwenden, was wir wollen. In diesem Fall verwende ich die Joystick 1-Werte zum Fahren des Autos.

Steuerung von Arduino Ant Robot/Hexapod mit Arduino RC Transmitter

Arduino-Code:

Genauso habe ich den Arduino Ant Robot aus meinem vorherigen Video gemacht, um mit diesem Arduino RC Transmitter drahtlos gesteuert zu werden. Wir müssen nur die Daten lesen und entsprechend die entsprechenden Funktionen ausführen, wie vorwärts, links, rechts, beißen, angreifen und so weiter.

ESC- und Servosteuerung mit RC-Sender

Lassen Sie uns zum Schluss einen Blick darauf werfen, wie dieser Sender zur Steuerung kommerzieller RC-Geräte verwendet werden kann.

Normalerweise müssen wir für diese Geräte ihre Servos oder bürstenlosen Motoren steuern. Nachdem wir also die Daten vom Sender erhalten haben, verwenden wir zur Steuerung des Servos einfach die Arduino Servo-Bibliothek und verwenden Werte von 0 bis 180 Grad. Zur Steuerung des bürstenlosen Motors mit ESC können wir wieder die Servobibliothek zur Erzeugung des 50-Hz-PWM-Signals verwenden, das zur Steuerung des ESC verwendet wird. Durch Variieren des Arbeitszyklus von 1000 bis 2000 Mikrosekunden steuern wir die Drehzahl des Motors von Null bis zum Maximum. Mehr zur Steuerung bürstenloser Motoren mit ESC in meinem nächsten Tutorial.

Bitte beachten Sie, dass wir mit diesem NRF24L01 2,4GHz-System eigentlich nicht das Standard-RC-Empfängersystem binden können. Stattdessen müssen wir unseren eigenen Empfänger modifizieren oder erstellen, der aus einem Arduino- und NRF24L01-Modul besteht. Von dort aus können wir dann die entsprechenden PWM- oder PPM-Signale zur Steuerung des RC-Geräts generieren.

/*
    DIY Arduino based RC Transmitter Project
   == Receiver Code - ESC and Servo Control ==

  by Dejan Nedelkovski, www.HowToMechatronics.com
  Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Servo.h>

RF24 radio(10, 9);   // nRF24L01 (CE, CSN)
const byte address[6] = "00001";

unsigned long lastReceiveTime = 0;
unsigned long currentTime = 0;

Servo esc;  // create servo object to control the ESC
Servo servo1;
Servo servo2;
int escValue, servo1Value, servo2Value;

// Max size of this struct is 32 bytes - NRF24L01 buffer limit
struct Data_Package {
  byte j1PotX;
  byte j1PotY;
  byte j1Button;
  byte j2PotX;
  byte j2PotY;
  byte j2Button;
  byte pot1;
  byte pot2;
  byte tSwitch1;
  byte tSwitch2;
  byte button1;
  byte button2;
  byte button3;
  byte button4;
};

Data_Package data; //Create a variable with the above structure

void setup() {
  Serial.begin(9600);
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setAutoAck(false);
  radio.setDataRate(RF24_250KBPS);
  radio.setPALevel(RF24_PA_LOW);
  radio.startListening(); //  Set the module as receiver
  resetData();
  esc.attach(9);
  servo1.attach(3);
  servo2.attach(4);
}
void loop() {
  // Check whether we keep receving data, or we have a connection between the two modules
  currentTime = millis();
  if ( currentTime - lastReceiveTime > 1000 ) { // If current time is more then 1 second since we have recived the last data, that means we have lost connection
    resetData(); // If connection is lost, reset the data. It prevents unwanted behavior, for example if a drone jas a throttle up, if we lose connection it can keep flying away if we dont reset the function
  }
  // Check whether there is data to be received
  if (radio.available()) {
    radio.read(&data, sizeof(Data_Package)); // Read the whole data and store it into the 'data' structure
    lastReceiveTime = millis(); // At this moment we have received the data
  }
  // Controlling servos
  servo1Value = map(data.j2PotX, 0, 255, 0, 180);
  servo2Value = map(data.j2PotY, 0, 255, 0, 180);
  servo1.write(servo1Value);
  servo2.write(servo2Value);
  // Controlling brushless motor with ESC
  escValue = map(data.pot1, 0, 255, 1000, 2000); // Map the receiving value form 0 to 255 to 0 1000 to 2000, values used for controlling ESCs
  esc.writeMicroseconds(escValue); // Send the PWM control singal to the ESC
}

void resetData() {
  // Reset the values when there is no radio connection - Set initial default values
  data.j1PotX = 127;
  data.j1PotY = 127;
  data.j2PotX = 127;
  data.j2PotY = 127;
  data.j1Button = 1;
  data.j2Button = 1;
  data.pot1 = 1;
  data.pot2 = 1;
  data.tSwitch1 = 1;
  data.tSwitch2 = 1;
  data.button1 = 1;
  data.button2 = 1;
  data.button3 = 1;
  data.button4 = 1;
} Codesprache:Arduino (arduino) 

So that’s it. I hope you enjoyed this video and learned something new. Feel free to ask any question in the comments section below and check my Arduino Projects Collection.


Herstellungsprozess

  1. Kazoo
  2. DIY LUMAZOID Arduino Music Visualiser
  3. Machen Sie eine DIY-Hausaufgaben-Schreibmaschine zu Hause
  4. DIY-Voltmeter mit Arduino und Smartphone
  5. Arduino + ESP Weather Box
  6. DIY Virtual Reality Skateboard
  7. DIY Arduino RADIONICS-Behandlungsmaschine
  8. DIY 2D Motion Racing Simulator
  9. DIY-Idee mit RFID
  10. DIY 3-Achsen-CNC-VMC