Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Радиоуправляемый передатчик Arduino своими руками

<основной класс="главный сайт" id="главный">

В этом уроке мы узнаем, как собрать радиоуправляемый передатчик Arduino своими руками. Очень часто мне нужно беспроводное управление для проектов, которые я делаю, поэтому я создал этот многофункциональный радиоконтроллер, который можно использовать практически для всего.

Вы можете посмотреть следующее видео или прочитать письменное руководство ниже.

Обзор

Теперь я могу без проводов управлять любым проектом Arduino, просто немного изменив настройки на стороне приемника. Этот передатчик также можно использовать как любой коммерческий радиоуправляемый передатчик для управления игрушками, автомобилями, дронами и так далее. Для этого ему просто нужен простой приемник Arduino, который затем генерирует соответствующие сигналы для управления этими коммерческими RC-устройствами.

В этом видео я объясню, как все работает, на нескольких примерах управления автомобилем-роботом Arduino, управления роботом-муравьем Arduino из моего предыдущего видео и управления бесщеточным двигателем постоянного тока с помощью ESC и некоторых серводвигателей.

Радиосвязь этого контроллера основана на модуле приемопередатчика NRF24L01, который при использовании с усиленной антенной может иметь стабильный диапазон до 700 метров в открытом космосе. Он имеет 14 каналов, 6 из которых являются аналоговыми входами и 8 цифровыми входами.

Он имеет два джойстика, два потенциометра, два тумблера, шесть кнопок и дополнительно внутренний измерительный блок, состоящий из акселерометра и гироскопа, который также можно использовать для управления вещами, просто перемещая или наклоняя контроллер.

Схема передатчика Arduino RC

Для начала давайте посмотрим на принципиальную схему. Мозгом этого радиоуправляемого контроллера является Arduino Pro Mini, который питается от двух аккумуляторов LiPo, вырабатывающих около 7,4 вольт. Мы можем подключить их напрямую к выводу RAW Pro Mini, у которого есть регулятор напряжения, снижающий напряжение до 5 В. Обратите внимание, что существует две версии Arduino Pro Mini, одна из которых работает при напряжении 5 В, а другая — при напряжении 3,3 В.

С другой стороны, модулю NRF24L01 строго требуется 3,3 В, и рекомендуется подавать его от специального источника. Поэтому нам нужно использовать регулятор напряжения 3,3 В, который подключен к батареям, и преобразовать 7,4 В в 3,3 В. Также нам нужно использовать развязывающий конденсатор рядом с модулем, чтобы поддерживать более стабильное напряжение, таким образом, радиосвязь также будет более стабильной. Модуль NRF24L01 взаимодействует с Arduino по протоколу SPI, а модуль акселерометра и гироскопа MPU6050 использует протокол I2C.

Компоненты, необходимые для этого руководства по Arduino, можно получить по ссылкам ниже:

Дизайн печатной платы

На самом деле я использовал все аналоговые и цифровые контакты Arduino Pro Mini. Так что теперь, если я попытаюсь соединить все вместе с помощью соединительных проводов, это будет настоящий беспорядок. Поэтому я разработал собственную печатную плату, используя бесплатное онлайн-программное обеспечение для проектирования схем EasyEDA.

Здесь я учел эргономику контроллера и спроектировал его так, чтобы его можно было легко держать двумя руками, а все элементы управления находились в пределах досягаемости пальцев. Я сделал края круглыми и добавил несколько 3-миллиметровых отверстий, чтобы позже я мог установить плату на что-нибудь. Я разместил контакты для программирования Arduino Pro Mini в верхней части контроллера, чтобы к ним можно было легко получить доступ, если мы захотим перепрограммировать Arduino. Здесь также можно заметить, что я использовал контакты RX и TX Arduino для кнопок джойстика. Однако эти две линии должны быть отсоединены от чего-либо, пока мы загружаем скетч в Arduino. Поэтому они прерываются двумя контактами, которые затем можно легко соединить с помощью простых перемычек.

Обратите внимание: Убедитесь, что у вас есть правильная версия Arduino Pro Mini для обработки печатной платы или изменения дизайна печатной платы в соответствии с ней. Вот сравнительная фотография трех разных версий, в зависимости от вашего Arduino и регулятора напряжения.

Вот ссылка на файлы проекта этого дизайна печатной платы. Они открывают три разные версии в отдельных вкладках, так что вы можете выбрать ту, которая вам нужна.

Поэтому, закончив проектирование, я создал файл Gerber, необходимый для изготовления печатной платы.

Гербер-файл:

Затем я заказал печатную плату у JLCPCB, которая также является спонсором этого видео.

Здесь мы можем просто перетащить файл Gerber, и после загрузки мы можем просмотреть нашу плату в средстве просмотра Gerber. Если все в порядке, мы можем продолжить и выбрать свойства, которые мы хотим для нашей платы. На этот раз я выбрал черный цвет печатной платы. И все, теперь мы можем просто заказать нашу плату по разумной цене. Обратите внимание:если это ваш первый заказ в JLCPCB, вы можете получить до 10 печатных плат всего за 2 доллара США.

И вот оно. Мне просто очень нравится, как эта печатная плата получилась в этом черном цвете. Качество печатной платы отличное, и все точно так же, как и в дизайне.

Сборка печатной платы

Хорошо, теперь мы можем перейти к сборке печатной платы. Я начал с пайки контактов Arduino Pro Mini. Простой и хороший способ сделать это — разместить их на макетной плате, чтобы они надежно оставались на месте во время пайки.

Pro Mini также имеет контакты по бокам, но обратите внимание, что расположение этих контактов может различаться в зависимости от производителя.

Для конкретной модели, которая у меня есть, мне нужно 5 контактов для каждой стороны, а один контакт GND оставить пустым, потому что я использовал его область внизу на печатной плате для запуска некоторых дорожек. Я припаял Arduino Pro Mini прямо к печатной плате и обрезал длину разъемов. Рядом с ним находится модуль акселерометра и гироскопа MPU6050.

Затем я припаял стабилизатор напряжения 3,3В с конденсатором рядом с ним, и еще один конденсатор возле модуля NRF24L01. У этого модуля есть три разных версии, и мы можем использовать любую из них здесь.

Я продолжил с выводами для программирования Arduino, выводами RX и TX, выводами питания и выключателем питания.

Затем, чтобы припаять потенциометры к печатной плате, мне пришлось удлинить их контакты с помощью некоторых контактных разъемов.

Здесь мы можем отметить, что я предварительно обрезал ручки, чтобы я мог правильно установить на них некоторые колпачки. Однако потенциометры мы припаяем к плате чуть позже.

Затем я вставил и припаял два тумблера и два джойстика на место.

Наконец, осталось припаять четыре кнопки. Однако у них нет нужной высоты, поэтому я снова использовал разъемы для штифтов, чтобы немного удлинить их штифты.

Вот и все, наша печатная плата готова, так что мы можем продолжить делать для нее крышку. Поскольку мне нравится, как выглядит печатная плата, и я хочу, чтобы ее было видно, я решил использовать для крышки прозрачный акрил.

Здесь у меня есть прозрачный акрил толщиной 4 мм, который в настоящее время имеет защитную фольгу и кажется синим. Идея крышки состоит в том, чтобы сделать две пластины по форме печатной платы и закрепить одну из них на верхней стороне, а другую на нижней стороне печатной платы.

Поэтому я отметил форму печатной платы и с помощью ручной пилы по металлу вырезал акрил в соответствии с ней.

Затем с помощью простого рашпиля я откорректировал форму акрила. Две пластины вышли великолепными и идеально сочетаются с печатной платой.

Затем я отметил места, где мне нужно сделать отверстия для прохождения компонентов. Используя 3-миллиметровое сверло, я сначала сделал 4 отверстия для крепления пластин к печатной плате. Для этих отверстий я также сделал углубления, чтобы болты можно было вставить заподлицо с пластинами.

Для отверстий под тумблеры и потенциометры я использовал сверло на 6 мм, а для отверстий под джойстик использовал сверло Форстнера на 25 мм. Опять же, используя рашпиль, я подточил все отверстия.

Прежде чем собирать крышку, отмечу, что я на самом деле припаял контактную колодку блока питания вверх ногами, чтобы до нее можно было дотянуться с обратной стороны, где будет располагаться аккумулятор.

Хорошо, теперь мы можем приступить к сборке крышки. Я начал с того, что снял защитную пленку с акрила, что, должен признать, было весьма удовлетворительным, потому что теперь акрил был таким чистым. Итак, сначала я закрепил два потенциометра на верхней пластине, вставил 3-миллиметровые монтажные болты и установил на место 11-миллиметровые дистанционные кольца.

Затем я аккуратно соединил и закрепил верхнюю пластину и печатную плату несколькими болтами. На этом я наконец-то припаял потенциометры к плате, потому что раньше точно не знал, на какой высоте они будут располагаться.

Затем на задней панели я прикрепил держатель батареи с помощью 2 болтов. Я закончил сборку крышки, прикрепив заднюю пластину к задней стороне печатной платы с помощью четырех монтажных болтов.

Наконец, мы можем прикрепить линии батареи к контактам источника питания, вставить и закрепить ручки на потенциометрах, вставить ручки джойстиков и прикрепить антенну к модулю NRF24l01. Вот и все, мы наконец-то закончили с самодельным радиоуправляемым передатчиком Arduino.

Теперь осталось запрограммировать Arduino. Для программирования платы Pro Mini нам нужен USB-последовательный интерфейс UART, который можно подключить к разъему для программирования, расположенному на верхней стороне нашего контроллера.

Затем в меню инструментов Arduino IDE нам нужно выбрать плату Arduino Pro или Pro Mini, выбрать нужную версию процессора, выбрать порт и выбрать метод программирования «USBasp».

Итак, теперь мы можем загрузить код в Arduino.

Код радиоуправляемого передатчика на основе Arduino своими руками

Давайте объясним, как работает код передатчика. Итак, сначала нам нужно включить библиотеки SPI и RF24 для беспроводной связи и библиотеку I2C для модуля акселерометра. Затем нам нужно определить цифровые входы, некоторые переменные, необходимые для программы ниже, определить радиообъект и адрес связи.

#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"; // AddressCode language: Arduino (arduino)

Затем нам нужно определить структуру, в которой мы будем хранить 14 входных значений контроллера. Максимальный размер этой структуры может составлять 32 байта, потому что это ограничение буфера NRF24L01 или количество данных, которое модуль может отправить за один раз.

/ 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 structureCode language: Arduino (arduino)

В разделе настройки нам нужно инициализировать модуль MPU6050, и мы также можем рассчитать ошибку IMU, которая представляет собой значение, которое позже используется при расчете правильных углов модуля.

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);
}Code language: Arduino (arduino)

Подробнее о работе акселерометра и гироскопа MEMS можно узнать здесь. Скоро появится специальное руководство по MPU6050.

Затем нам нужно инициализировать радиосвязь, активировать внутренние подтягивающие резисторы Arduino для всех цифровых входов и установить начальные значения по умолчанию для всех переменных.

// 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);Code language: Arduino (arduino)

В разделе цикла начните с чтения всех аналоговых входов, сопоставьте их значения от 0 до 1023 с байтовыми значениями от 0 до 255, потому что мы уже определили переменные в нашей структуре как байты. Каждый ввод хранится в определенной переменной данных из структуры.

// 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);Code language: Arduino (arduino)

Мы должны просто отметить, что, поскольку мы используем подтягивающие резисторы, показания цифровых контактов равны 0 при нажатии кнопок.

// 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);Code language: Arduino (arduino)

Таким образом, используя функцию radio.write(), мы просто отправляем значения со всех 14 каналов на приемник.

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

Если тумблер 1 включен, то для управления вместо него используются данные акселерометра и гироскопа.

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

Таким образом, вместо значений X и Y с джойстика 1 мы используем значения углов, которые мы получаем от IMU, которые мы предварительно преобразуем из значений от -90 до +90 градусов в байтовые значения от 0 до 255 соответственно.

// 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);Code language: Arduino (arduino)

Так вот как код передатчика, наиболее важными вещами были определение радиосвязи и отправка данных на приемник.

Вот полный код Arduino для этого самодельного радиоуправляемого передатчика Arduino:

/*
        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);
}Code language: Arduino (arduino)

Код получателя

Теперь давайте посмотрим, как мы можем получить эти данные. Вот простая схема Arduino и приемника NRF24L01. Конечно, вы можете использовать любую другую плату Arduino.

А вот простой код приемника, где мы будем получать данные и просто печатать их на последовательном мониторе, чтобы знать, что связь работает правильно. Нам снова нужно подключить библиотеку RF24 и определить объекты и структуру так же, как в коде передатчика. В разделе настройки при определении радиосвязи нам нужно использовать те же настройки, что и для передатчика, и установить модуль в качестве приемника с помощью функции radio.startListening().

/*
    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;
}Code language: Arduino (arduino)

В основном цикле с помощью функции available() проверяем, есть ли входящие данные. Если это правда, мы просто читаем данные и сохраняем их в переменных структуры. Теперь мы можем распечатать данные на последовательном мониторе, чтобы проверить, правильно ли работает передача. Также с помощью функции millis() и оператора if мы проверяем, продолжаем ли мы получать данные, или если мы не получаем данные в течение периода, превышающего 1 секунду, то мы сбрасываем переменные к их значениям по умолчанию. Мы используем это, чтобы предотвратить нежелательное поведение, например, если дрон нажал на газ и мы потеряли связь, он может продолжать улетать, пока мы не сбросим значения.

Итак, это все. Теперь мы можем реализовать этот метод получения данных для любого проекта Arduino. Например, вот код для управления автомобилем-роботом Arduino из одного из моих предыдущих видео.

В качестве обновления этого проекта я сделал специальный радиоуправляемый приемник на базе Arduino. Опять же, он основан на мини-плате Arduino Pro и имеет несколько готовых к использованию разъемов для сервоприводов и регуляторов скорости, размещенных на компактной печатной плате.

Беспроводное управление роботом-автомобилем Arduino с помощью радиопередатчика

Код Arduino:

Здесь нам нужно определить библиотеки, структуру и радиосвязь, как объяснялось ранее. Затем в основном цикле нам просто нужно прочитать входящие данные и использовать их для чего угодно. В этом случае я использую значения джойстика 1 для управления автомобилем.

Управление роботом-муравьем/гексаподом Arduino с помощью радиоуправляемого передатчика Arduino

Код Arduino:

Точно так же я сделал робота-муравья Arduino из моего предыдущего видео для беспроводного управления с помощью этого радиоуправляемого передатчика Arduino. Нам просто нужно прочитать данные, и в соответствии с ними выполнить соответствующие функции, такие как движение вперед, влево, вправо, укус, атака и так далее.

ESC и сервоуправление с помощью RC-передатчика

Наконец, давайте посмотрим, как этот передатчик можно использовать для управления коммерческими радиоуправляемыми устройствами.

Обычно для этих устройств нам нужно управлять их сервоприводами или бесколлекторными двигателями. Итак, после получения данных от передатчика, для управления сервоприводом мы просто используем библиотеку Arduino Servo и используем значения от 0 до 180 градусов. Для управления бесщеточным двигателем с помощью ESC мы снова можем использовать библиотеку сервоприводов для генерации ШИМ-сигнала 50 Гц, используемого для управления ESC. Изменяя рабочий цикл от 1000 до 2000 микросекунд, мы управляем оборотами двигателя от нуля до максимума. Однако подробнее об управлении бесколлекторными двигателями с помощью ESC в моем следующем уроке.

Обратите внимание, что мы фактически не можем связать стандартную систему приемника RC с этой системой NRF24L01 2,4 ГГц. Вместо этого нам нужно изменить или создать собственный приемник, состоящий из Arduino и модуля NRF24L01. Оттуда мы можем генерировать соответствующие сигналы PWM или PPM для управления устройством RC.

/*
    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;
}Code language: Arduino (arduino)

Итак, это все. Надеюсь вам понравилось это видео и вы узнали что-то новое. Не стесняйтесь задавать любые вопросы в разделе комментариев ниже и проверять мою коллекцию проектов Arduino.


Производственный процесс

  1. Kazoo
  2. Музыкальный визуализатор DIY LUMAZOID для Arduino
  3. Сделайте машинку для письма домашнего задания дома
  4. Вольтметр своими руками с использованием Arduino и смартфона
  5. Погодное окно Arduino + ESP
  6. Скейтборд виртуальной реальности «Сделай сам»
  7. Сделай сам Arduino РАДИОНИКА Лечение MMachine
  8. Самодельный двухмерный гоночный симулятор
  9. Идея DIY с RFID
  10. DIY 3-осевой ЧПУ VMC