В этой статье мы научимся делать приемник для пульта дистанционного управления (RC-приемник) на основе платы Arduino и модуле NRF24L01. Ранее на нашем сайте мы рассматривали создание RC-передатчика на основе платы Arduino. Теперь эти два устройства могут легко взаимодействовать, и мы можем использовать их для управления многими вещами по беспроводной связи. Мы рассмотрим как это все работает на нескольких примерах.
В первом примере мы будем использовать этот RC-приемник на Arduino для управления простой машиной, состоящей из двух двигателей постоянного тока. Во втором примере я покажу вам, как управлять бесколлекторными двигателями (BLDC) и сервоприводами, которые являются обычными компонентами, встречающимися во многих коммерческих самолетах с дистанционным управлением, лодках, автомобилях и так далее. Если мы знаем, как ими управлять, мы можем легко модифицировать и управлять многими моделями с дистанционным управлением с помощью нашего собственного передатчика Arduino.
В качестве третьего примера я покажу вам, как я модифицировал и использовал эту систему радиоуправления на базе Arduino для управления коммерческим радиоуправляемым автомобилем.
Необходимые компоненты
- Плата Arduino Pro Mini (купить на AliExpress).
- Модуль nRF24L01 + PA + LNA (купить на AliExpress).
- Регулятор напряжения AMS1117 3,3 В (купить на AliExpress).
- Штыревые разъемы «папа» + «мама».
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Схема RC-приемника на Arduino
Для начала рассмотрим принципиальную схему данной системы. Радиосвязь в ней реализована на базе приемопередающих модулей NRF24L01.
Передатчик постоянно отправляет данные со своих контроллеров, джойстиков, кнопок, потенциометров и тумблеров, и мы получаем эти данные с помощью приемника. Мы также можем отметить, что этот RC-приемник не обязательно работает только с этим конкретным передатчиком, который мы рассматривали на нашем сайте. Он может работать с любой другой подобной установкой, состоящей из платы Arduino и модуля NRF24L01.
Тем не менее, мозг этого RC-приемника — плата Arduino Pro Mini. Для питания мы можем использовать либо вывод VCC, к которому мы можем подключить 5 В, либо вывод RAW, к которому мы можем подключить от 6 до 12 В. Обратите внимание, что есть две версии Arduino Pro Mini, например, та, которую я использую здесь, которая работает при 5 В, а другая — при 3,3 В. С другой стороны, модуль NRF24L01 работает при 3,3 В, поэтому нам нужен регулятор напряжения. На этот раз я использую регулятор напряжения AMS1117, который выдает 3,3 В с входов, которые могут находиться в диапазоне от 5 В до 12 В.
Для связи с Arduino модуль NRF24L01 использует протокол SPI, а также два дополнительных цифровых контакта. Это означает, что у нас остается 9 цифровых контактов, которые можно использовать как выходные каналы, два из которых — контакты RX и TX. Стоит отметить, что эти контакты должны быть отключены от чего-либо, пока мы загружаем скетч на плату Arduino, поэтому я сделал возможным подключение или отключение через отдельные разъемы контактов. На самом деле, мы также можем использовать аналоговые входы как цифровые выходы, поэтому, хотя эта плата Arduino довольно мала, у нас есть много доступных выходов или каналов.
Проектирование печатной платы
Тем не менее, чтобы сохранить компактность этой схемы, я сделал собственную печатную плату с помощью бесплатного онлайн-программного обеспечения для проектирования схем EasyEDA. Здесь я разместил 8 каналов прямо рядом с шиной 5 В и заземлением, и поэтому мы можем напрямую подключать к ним сервоприводы и ECS. Канал номер 9 расположен в отдельной позиции, рядом с выводом VCC Arduino, поэтому мы можем использовать, например, ESC для питания Arduino с его функцией Battery Eliminator Circuit, которая обеспечивает 5 В. Конечно, мы могли бы использовать любые другие каналы для этой цели, поскольку вывод VCC также подключен к этим шинам 5 В.
Что касается каналов номер 7 и 8, мы можем видеть здесь, как они прерываются этими штыревыми разъемами. Если мы хотим их использовать, нам просто нужно соединить два штырька вместе. Разъем программирования расположен в правом верхнем углу, а конденсатор 100 мкФ служит как для регулятора напряжения, так и для модуля NRF24L01. В левом нижнем углу печатной платы я разместил аналоговые штырьки.
Здесь мы можем отметить еще один момент: некоторые платы Arduino Pro Mini могут иметь различное расположение контактов, поэтому я включил еще одну версию печатной платы, чтобы вы могли выбрать ту, которая подходит для вашей платы Arduino Pro Mini.
Вот ссылка на файлы проекта этой печатной платы. Поэтому, как только я закончил проектирование, я сгенерировал файл Gerber, необходимый для изготовления печатной платы.
Файлы Gerber:
Вы можете заказать изготовление печатной платы в любом месте, где вам это удобно сделать.
Сборка печатной платы
У нас качество изготовленных печатных плат оказалось отличное, и все точно такое же, как в проекте.
Теперь мы можем двигаться дальше и собирать печатную плату. Сначала нам нужно припаять штыревые разъемы платы Arduino. Удобный способ сделать это — использовать макетную плату, чтобы вставить в нее штыревые разъемы, и тогда плата будет надежно закреплена во время пайки. Как я уже говорил ранее, в зависимости от вашей платы, штыревые разъемы могут немного отличаться, так что имейте это в виду при их пайке.
Также есть несколько заземляющих штырьков, которые нам нужно оставить свободными, так как под ними на печатной плате проходят некоторые дорожки. После того, как я припаял плату Arduino, я отрезал лишнюю длину штырьков.
Далее я разместил все остальные штыревые разъемы на плате. Нам нужны как штыревые, так и гнездовые штыревые разъемы, или вам решать, какие штыревые разъемы вы выберете. Однако хорошей идеей будет использовать штыревые штыревые разъемы для цифровых каналов, поскольку серводвигатели и соединения ESC являются гнездовыми, поэтому мы можем легко их подключить.
Регулятор напряжения — это компонент поверхностного монтажа, поэтому я использовал клей Blue-Tack, чтобы закрепить его на месте во время пайки. Наконец, как только мы припаяем два конденсатора, мы можем прикрепить модуль NRF24L01 к соответствующим штыревым разъемам.
В зависимости от приложения или необходимого нам диапазона мы можем использовать либо обычный модуль с бортовой антенной, либо тот, к которому мы можем прикрепить большую антенну и достичь беспроводной связи на расстоянии до 700 метров на открытом пространстве. Вот и все, наш RC-приемник на Arduino теперь готов, и мы можем использовать его для чего угодно.
Для программирования приемника или подключения Arduino Pro Mini к компьютеру можно использовать интерфейс USB-последовательный UART, который можно подключить к программному разъему.
В меню инструментов Arduino IDE нам необходимо выбрать плату Arduino Pro или Pro Mini, выбрать нужную версию процессора, выбрать порт и выбрать метод программирования «USBasp».
Итак, теперь мы можем загружать коды в плату Arduino.
Пример 1 – Машинка с дистанционным управлением на базе Arduino
Хорошо, теперь мы можем двигаться дальше и рассмотреть первый пример.
Это простая машина, состоящая из двух двигателей постоянного тока напряжением 12 В. На этот раз мы будем использовать наш новый RC приемник на Arduino для управления ей. Для управления двигателями постоянного тока мы используем драйвер двигателя L298N, а для питания мы используем 3 литий-ионных аккумулятора, которые обеспечивают около 12 В.
Необходимые для этого примера компоненты:
- Драйвер L298N
- 12 В двигатель постоянного тока с высоким крутящим моментом
- Двигатель постоянного тока с пластиковой шиной колеса
- Макетная плата и соединительные провода
Итак, соединения действительно просты, 12 В, поступающие от батарей, идут на контакт 12 В на нашем приемнике, а шесть контрольных контактов драйвера идут на 6 каналов. Здесь мы должны отметить, что для того, чтобы иметь возможность управлять скоростью двигателей, нам нужно подать сигнал ШИМ на контакты Enable A и Enable B драйвера. В нашем приемнике каналы номер 2, 3, 6 и 9 могут выводить сигналы ШИМ, поэтому в этом случае я подключил контакты Enable драйвера к каналам номер 2 и 6.
Давайте теперь посмотрим на код Arduino.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
/* Arduino RC Receiver - Car Example by Dejan, www.HowToMechatronics.com Library: TMRh20/RF24, https://github.com/tmrh20/RF24/ */ #include <SPI.h> #include <nRF24L01.h> #include <RF24.h> #define enA 9 // Arduino pin D9 - CH6 on PCB board - PWM output #define in1 8 // D8 - CH5 #define in2 7 // D7 - CH4 #define in3 6 // D6 - CH3 #define in4 4 // D4 - CH1 #define enB 5 // D5 - CH2 - PWM output RF24 radio(3, 2); // nRF24L01 (CE, CSN) const byte address[6] = "00001"; unsigned long lastReceiveTime = 0; unsigned long currentTime = 0; // Max size of this struct is 32 bytes 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 int steering, throttle; int motorSpeedA = 0; int motorSpeedB = 0; void setup() { pinMode(enA, OUTPUT); pinMode(enB, OUTPUT); pinMode(in1, OUTPUT); pinMode(in2, OUTPUT); pinMode(in3, OUTPUT); pinMode(in4, OUTPUT); //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 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 } // Parse the data from the Joystic 1 to the throttle and steering variables throttle = data.j1PotY; steering = data.j1PotX; // Throttle used for forward and backward control // Joystick values: 0 to 255; down = 0; middle = 127; up = 255 if (throttle < 110) { // Set Motor A backward digitalWrite(in1, HIGH); digitalWrite(in2, LOW); // Set Motor B backward digitalWrite(in3, HIGH); digitalWrite(in4, LOW); // Convert the declining throttle readings for going backward from 110 to 0 into 0 to 255 value for the PWM signal for increasing the motor speed motorSpeedA = map(throttle, 110, 0, 0, 255); motorSpeedB = map(throttle, 110, 0, 0, 255); } else if (throttle > 140) { // Set Motor A forward digitalWrite(in1, LOW); digitalWrite(in2, HIGH); // Set Motor B forward digitalWrite(in3, LOW); digitalWrite(in4, HIGH); // Convert the increasing throttle readings for going forward from 140 to 255 into 0 to 255 value for the PWM signal for increasing the motor speed motorSpeedA = map(throttle, 140, 255, 0, 255); motorSpeedB = map(throttle, 140, 255, 0, 255); } // If joystick stays in middle the motors are not moving else { motorSpeedA = 0; motorSpeedB = 0; } // Steering used for left and right control if (steering < 110) { // Convert the declining steering readings from 140 to 255 into increasing 0 to 255 value int xMapped = map(steering, 110, 0, 0, 255); // Move to left - decrease left motor speed, increase right motor speed motorSpeedA = motorSpeedA - xMapped; motorSpeedB = motorSpeedB + xMapped; // Confine the range from 0 to 255 if (motorSpeedA < 0) { motorSpeedA = 0; } if (motorSpeedB > 255) { motorSpeedB = 255; } } if (steering > 140) { // Convert the increasing steering readings from 110 to 0 into 0 to 255 value int xMapped = map(steering, 140, 255, 0, 255); // Move right - decrease right motor speed, increase left motor speed motorSpeedA = motorSpeedA + xMapped; motorSpeedB = motorSpeedB - xMapped; // Confine the range from 0 to 255 if (motorSpeedA > 255) { motorSpeedA = 255; } if (motorSpeedB < 0) { motorSpeedB = 0; } } // Prevent buzzing at low speeds (Adjust according to your motors. My motors couldn't start moving if PWM value was below value of 70) if (motorSpeedA < 70) { motorSpeedA = 0; } if (motorSpeedB < 70) { motorSpeedB = 0; } analogWrite(enA, motorSpeedA); // Send PWM signal to motor A analogWrite(enB, motorSpeedB); // Send PWM signal to motor B } 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; } |
Описание работы кода: Итак, сначала нам нужно включить SPI и библиотеку RF24, определить несколько пинов, радиообъект и структуру данных, где мы будем хранить входящие данные от передатчика. В разделе настройки нам нужно определить выводы пинов и инициализировать радиосвязь. Для получения более подробной информации о том, как это работает и что делает каждая из этих линий, вы можете ознакомиться с моим подробным руководством по NRF24L01.
В разделе цикла мы постоянно проверяем, получаем ли мы данные, и если да, то считываем эти входящие данные. Если мы быстро взглянем на код передатчика, то увидим, какие данные он отправляет приемнику. Он считывает данные со всех своих контроллеров, джойстиков, потенциометров и кнопок, и отправляет эти данные как единый пакет приемнику.
Итак, как только мы прочитаем эти данные, мы можем делать с ними все, что захотим. В этом случае мы будем использовать значение оси Y джойстика 1 для управления дроссельной заслонкой и значение оси X для управления рулевым управлением. Я поместил эти данные в отдельные переменные дроссельной заслонки и рулевого управления. Значения, которые мы получаем от джойстиков, находятся в диапазоне от 0 до 255. Таким образом, если мы опустим джойстик вниз, мы соответствующим образом установим управляющие контакты водителя, чтобы автомобиль двигался назад, и будем использовать значение дроссельной заслонки для управления скоростью движения. Тот же принцип применим для движения вперед, влево и вправо. Внизу кода мы можем отметить пользовательскую функцию resetData(), которая сбрасывает все значения до их начальных значений по умолчанию, поэтому в случае потери радиосвязи машина замедлит движение.
Пример 2 – Управление сервоприводами и бесколлекторными двигателями
Хорошо, теперь мы можем перейти ко второму примеру — управлению сервоприводами и бесколлекторными двигателями с помощью этого радиоуправляемого приемника на Arduino.
Для управления бесколлекторными (BLDC) двигателями нам нужен ESC, или электронный контроллер скорости. Arduino взаимодействует с ESC с помощью всего одного контакта. Для управления ESC Arduino посылает определенный сигнал ШИМ на ESC, и с его помощью ESC управляет скоростью двигателя. ESC с тем же подключением обеспечивает 5 В через свою функцию схемы выпрямителя батареи, поэтому мы можем питать наш приемник с его помощью.
Что касается серводвигателей, то они имеют тот же тип соединений, что и ESC, и мы можем просто подключить их к любому из доступных каналов.
Необходимые для этого примера компоненты можно получить по ссылкам ниже:
- Бесщеточный двигатель
- ESC 30A (купить на AliExpress)
- Li-Po аккумулятор
- Серводвигатель MG996R (купить на AliExpress).
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Входные сигналы для управления как сервоприводами, так и бесколлекторными двигателями с использованием ESC практически одинаковы. Они используют специальный сигнал ШИМ 50 Гц, который можно легко сгенерировать с помощью библиотеки Arduino Servo.
Примечание: При использовании сервоприводов MG996R с этой настройкой они могут вызвать проблему в схеме и сжечь Arduino Pro Mini. Они могут потреблять более высокий ток, что может вызвать скачки напряжения на шине 5 В. Arduino Pro Mini должен выдерживать до 5,5 В на выводе 5 В, но когда происходят эти скачки, это может сжечь Arduino. У меня была эта проблема при тестировании схемы. Решением этой проблемы может быть установка больших развязывающих конденсаторов на сервоприводах, но я не совсем уверен и не проверял это. Поэтому, пожалуйста, имейте в виду эту проблему.
С другой стороны, в примере я использовал два других сервопривода MG996R, которые не сожгли Arduino, я полагаю, потому что они не вызывали таких высоких скачков. Также я использовал этот пример с 4 меньшими сервоприводами S90, и у него не было никаких проблем.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
/* DIY RC Receiver - Servos and Brushless motors control by Dejan, 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(3, 2); // 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(10); // Arduino digital pin D10 - CH9 on PCB board servo1.attach(4); // D4 - CH1 servo2.attach(5); // D5 - CH2 } 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); // Map the receiving value form 0 to 255 to 0 to 180(degrees), values used for controlling servos servo2Value = map(data.j2PotY, 0, 255, 0, 180); servo1.write(servo1Value); servo2.write(servo2Value); // Controlling brushless motor with ESC escValue = map(data.j1PotY, 127, 255, 1000, 2000); // Map the receiving value form 127 to 255 to 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; } |
Итак, после получения данных от передатчика, мы преобразуем значения от 0 до 255 в значения от 0 до 180 для управления сервоприводами с помощью функции write(). Аналогичным образом мы преобразуем данные для управления ESC в значения от 1000 до 2000. В этом примере мы управляем этим ESC из средней точки джойстика номер 1, в верхнее положение, поэтому мы преобразуем значения из середины, 127 до 255, в значения от 1000 до 2000. С помощью функции Servo Library writeMicroseconds() мы отправляем сигнал ШИМ на ESC и таким образом можем управлять скоростью бесщеточного двигателя от минимума до максимума.
Итак, подобным образом мы можем управлять радиоуправляемыми самолетами , автомобилями, лодками и т. д., поскольку они обычно используют этот тип двигателей, сервоприводов и бесщеточных двигателей.
Пример 3 – Управление моделью радиоуправляемой машины
Радиоуправляемая машинка оснащена собственным контроллером, который позволяет управлять передними колесами, чтобы они двигались влево и вправо, а также двигать машину вперед и назад.
Однако, поскольку это дешевая RC-машинка, управление цифровое, или включено или выключено, до максимального положения влево и вправо и максимальной скорости. Независимо от этого, я разобрал машинку, чтобы посмотреть, что внутри и как я могу реализовать RC-приемник Arduino для управления ею.
Как только я раскрыл электронные компоненты, я заметил, что два двигателя на самом деле являются простыми двигателями постоянного тока, работающими от 5 В. Даже передний двигатель, который управляет ограниченными движениями рулевого управления, является простым двигателем постоянного тока непрерывного вращения.
Итак, судя по всему, мы уже знаем, как управлять двигателями постоянного тока, поэтому заменить эту плату на наш самодельный приемник Arduino будет довольно просто. В дополнение к нашему приемнику нам просто нужен драйвер двигателя, способный управлять двумя двигателями одновременно. Для этой цели есть множество вариантов, даже тот, который мы использовали в первом примере, драйвер L298N.
Однако он слишком большой для этого приложения, поэтому я выбрал драйвер двигателя MX1508. Это простой драйвер двух двигателей постоянного тока с управлением по схеме H-bridge и ШИМ. Он имеет 4 управляющих входных контакта, 4 контакта для двигателей и 2 контакта для питания.
Я отпаял соединения моторов от платы RC-машины и припаял их к драйверу. С обратной стороны я припаял контакты питания, и теперь осталось подключить этот драйвер к приемнику. Питание для этой RC-машины поступает от Ni-Cd аккумулятора 4,8 В, расположенного в нижней части машины.
Итак, используя соединительные провода, я подключил эти контакты к контакту VCC Arduino, а также подключил 4 входных контакта управления драйвера к 4 цифровым каналам. Как я уже сказал, этот драйвер поддерживает управление ШИМ, поэтому для двигателя B, или заднего двигателя, я использовал каналы ШИМ номер 2 и 3.
Код для этой радиоуправляемой машины очень похож на первый пример.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
/* Arduino RC Receiver - RC Model control by Dejan , www.HowToMechatronics.com Library: TMRh20/RF24, https://github.com/tmrh20/RF24/ */ #include <SPI.h> #include <nRF24L01.h> #include <RF24.h> #define in3 5 // D5 - CH2 - PWM output #define in4 6 // D6 - CH3 - PWM output #define in1 7 // D7 - CH4 #define in2 8 // D8 - CH5 RF24 radio(3, 2); // nRF24L01 (CE, CSN) const byte address[6] = "00001"; unsigned long lastReceiveTime = 0; unsigned long currentTime = 0; // Max size of this struct is 32 bytes 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 int steering, throttle; int motorSpeedA = 0; int motorSpeedB = 0; void setup() { pinMode(in1, OUTPUT); pinMode(in2, OUTPUT); pinMode(in3, OUTPUT); pinMode(in4, OUTPUT); 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 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 } // Parse the data from the Joystic 1 to the steering and throttle variables steering = data.j2PotX; throttle = data.j1PotY; // Throttle used for forward and backward control if (throttle < 110) { // Convert the declining throttle readings for going backward from 110 to 0 into 0 to 255 value for the PWM signal for increasing the motor speed motorSpeedB = map(throttle, 110, 0, 0, 255); // Set Motor B backward analogWrite(in3, motorSpeedB); digitalWrite(in4, LOW); } else if (throttle > 140) { // Convert the increasing throttle readings for going forward from 140 to 255 into 0 to 255 value for the PWM signal for increasing the motor speed motorSpeedB = map(throttle, 140, 255, 0, 255); // Set Motor B forward digitalWrite(in3, LOW); analogWrite(in4, motorSpeedB); } // If joystick stays in middle the motors are not moving else { digitalWrite(in3, HIGH); digitalWrite(in4, HIGH); } // steering used for left and right control if (steering < 110) { digitalWrite(in1, HIGH); digitalWrite(in2, LOW); } if (steering > 140) { digitalWrite(in1, LOW); digitalWrite(in2, HIGH); } // If joystick stays in middle the motors are not moving else { digitalWrite(in1, HIGH); digitalWrite(in2, HIGH); } } 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; } |
Мы используем данные, поступающие от джойстиков, для управления дроссельной заслонкой и рулевым управлением радиоуправляемой машины. Для движения назад мы используем функцию analogWrite(), чтобы отправить сигнал ШИМ водителю на вывод Input3, в то время как вывод input4 удерживаем на низком уровне. Для движения вперед мы делаем это наоборот. Если джойстик остается посередине, мы даем команду водителю затормозить или остановить двигатели. Тот же принцип используется для рулевого двигателя, хотя здесь нам не нужно использовать функцию analogWrite(), так как нам не нужно контролировать скорость этого двигателя.
После загрузки этого скетча в Arduino мне пришлось пересобрать радиоуправляемую машинку. Я поместил маленький драйвер во внутренний корпус и закрепил остальные детали винтами. Я снова соединил приемник с драйвером и поместил его под внешний корпус машинки, в котором было достаточно места для приемника.
Итак, мы не только модифицировали эту RC-машину для управления с помощью нашего DIY RC-передатчика, но и улучшили ее, добавив управление PWM, так что теперь мы также можем контролировать скорость машины. В случае, если RC-модель, которую вы хотите модифицировать, имеет сервоприводы и бесщеточные двигатели вместо двигателей постоянного тока, вы можете следовать методу, описанному во втором примере.