В этой статье мы научимся делать приемник для пульта дистанционного управления (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-модель, которую вы хотите модифицировать, имеет сервоприводы и бесщеточные двигатели вместо двигателей постоянного тока, вы можете следовать методу, описанному во втором примере.
























спасибо
а можете пожалуйста помочь с кодом а то я просто новичок ни понимаю как писать эти коды
Попробуйте следующий код (библиотеки пропишите как в статье + библиотека Servo.h - сюда просто невозможно скопировать корректно фрагмент кода с подключением библиотек)
#include#include
#include
#include
// ==================== НАСТРОЙКА РАДИО ====================
RF24 radio(9, 10); // CE, CSN
const byte address[6] = "00001";
// ==================== СТРУКТУРА ДАННЫХ ====================
struct Data_Package {
byte joy1_x;
byte joy1_y;
byte joy2_x;
byte joy2_y;
byte pot1;
byte pot2;
byte button1;
byte button2;
byte button3;
byte button4;
byte switch1;
byte switch2;
};
Data_Package data;
// ==================== ПИНЫ ДЛЯ МОТОРОВ (MX1508) ====================
// Мотор A (левый)
#define MOTOR_A_IN1 2 // ШИМ
#define MOTOR_A_IN2 3 // ШИМ
// Мотор B (правый)
#define MOTOR_B_IN3 4 // ШИМ
#define MOTOR_B_IN4 5 // ШИМ
// ==================== ПИНЫ ДЛЯ СЕРВО ====================
#define SERVO_BASE_PIN 6 // Серво основания (поворот)
#define SERVO_SHOULDER_PIN 7 // Серво плеча
#define SERVO_ELBOW_PIN 8 // Серво локтя
#define SERVO_GRIPPER_PIN 9 // Серво захвата
// Создаем объекты для серво
Servo servoBase;
Servo servoS holder;
Servo servoElbow;
Servo servoGripper;
// ==================== ПЕРЕМЕННЫЕ ====================
int motorSpeed = 0;
int steering = 0;
// Переменные для хранения значений серво
int baseAngle = 90;
int shoulderAngle = 90;
int elbowAngle = 90;
int gripperAngle = 30; // Закрыто
void setup() {
Serial.begin(9600);
// Настройка пинов моторов
pinMode(MOTOR_A_IN1, OUTPUT);
pinMode(MOTOR_A_IN2, OUTPUT);
pinMode(MOTOR_B_IN3, OUTPUT);
pinMode(MOTOR_B_IN4, OUTPUT);
// Подключение серво
servoBase.attach(SERVO_BASE_PIN);
servoS holder.attach(SERVO_SHOULDER_PIN);
servoElbow.attach(SERVO_ELBOW_PIN);
servoGripper.attach(SERVO_GRIPPER_PIN);
// Установка начальных положений
servoBase.write(90);
servoS holder.write(90);
servoElbow.write(90);
servoGripper.write(30); // Захват закрыт
// Настройка радио
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.setDataRate(RF24_250KBPS);
radio.startListening();
// Сброс данных при старте
resetData();
}
void loop() {
if (radio.available()) {
radio.read(&data, sizeof(Data_Package));
// --- УПРАВЛЕНИЕ ГУСЕНИЧНОЙ ПЛАТФОРМОЙ (Левый стик) ---
// Ось Y левого стика (вперед/назад)
int throttle = data.joy1_y;
// Ось X левого стика (поворот)
int turn = data.joy1_x;
// Преобразуем значения (0-255) в скорость (-255 до 255)
int leftSpeed = 0;
int rightSpeed = 0;
// Расчет скоростей для гусениц (дифференциальный привод)
if (throttle > 127) {
// Движение вперед
leftSpeed = map(throttle, 128, 255, 0, 255);
rightSpeed = map(throttle, 128, 255, 0, 255);
} else if (throttle < 127) { // Движение назад leftSpeed = map(throttle, 0, 126, -255, 0); rightSpeed = map(throttle, 0, 126, -255, 0); } // Корректировка поворота if (turn > 137) {
// Поворот направо
int turnValue = map(turn, 138, 255, 0, 255);
leftSpeed = constrain(leftSpeed + turnValue, -255, 255);
rightSpeed = constrain(rightSpeed - turnValue, -255, 255);
} else if (turn < 117) { // Поворот налево int turnValue = map(turn, 0, 116, 255, 0); leftSpeed = constrain(leftSpeed - turnValue, -255, 255); rightSpeed = constrain(rightSpeed + turnValue, -255, 255); } // Управление моторами через драйвер MX1508 setMotor(MOTOR_A_IN1, MOTOR_A_IN2, leftSpeed); setMotor(MOTOR_B_IN3, MOTOR_B_IN4, rightSpeed); // --- УПРАВЛЕНИЕ РУКОЙ (Правый стик) --- // Ось Y правого стика (плечо) shoulderAngle = map(data.joy2_y, 0, 255, 0, 180); // Ось X правого стика (основание) baseAngle = map(data.joy2_x, 0, 255, 0, 180); // --- УПРАВЛЕНИЕ ЗАХВАТОМ (Правый потенциометр) --- // Потенциометр 1 (правый) управляет захватом gripperAngle = map(data.pot1, 0, 255, 0, 180); // Применяем значения к серво servoBase.write(baseAngle); servoS holder.write(shoulderAngle); servoGripper.write(gripperAngle); // Дополнительно: можно добавить управление локтем с помощью кнопок или второго потенциометра // Для примера используем среднее значение // elbowAngle = map(data.pot2, 0, 255, 0, 180); // servoElbow.write(elbowAngle); // Вывод в монитор порта для отладки Serial.print("Base: "); Serial.print(baseAngle); Serial.print(" | Shoulder: "); Serial.print(shoulderAngle); Serial.print(" | Gripper: "); Serial.println(gripperAngle); } } // ==================== ФУНКЦИЯ УПРАВЛЕНИЯ МОТОРОМ ==================== void setMotor(int pin1, int pin2, int speed) { // Ограничиваем скорость speed = constrain(speed, -255, 255); if (speed >= 0) {
// Вперед
analogWrite(pin1, speed);
analogWrite(pin2, 0);
} else {
// Назад
analogWrite(pin1, 0);
analogWrite(pin2, -speed);
}
}
// ==================== СБРОС ДАННЫХ ====================
void resetData() {
data.joy1_x = 127;
data.joy1_y = 127;
data.joy2_x = 127;
data.joy2_y = 127;
data.pot1 = 127;
data.pot2 = 127;
data.button1 = 0;
data.button2 = 0;
data.button3 = 0;
data.button4 = 0;
data.switch1 = 0;
data.switch2 = 0;
}
Схема подключения компонентов
Подключение драйвера моторов MX1508:
MX1508 Arduino Pro Mini Примечание
IN1 D2 (ШИМ) Левый мотор (вперед)
IN2 D3 (ШИМ) Левый мотор (назад)
IN3 D4 (ШИМ) Правый мотор (вперед)
IN4 D5 (ШИМ) Правый мотор (назад)
VCC (питание) Внешний источник 5-12В Не подключать к Arduino!
GND GND Arduino Общая земля
Подключение сервоприводов:
Серво Arduino Pro Mini Питание
Основание (поворот) D6 Внешний источник 5В
Плечо D7 Внешний источник 5В
Локоть D8 Внешний источник 5В
Захват D9 Внешний источник 5В
Все GND GND Arduino Общая земля
⚠️ ВАЖНО: Питание моторов и сервоприводов должно идти ОТДЕЛЬНО от Arduino! Не подключайте их к пину VCC Arduino — это может сжечь плату. Используйте внешний источник питания 5В для серво и аккумулятор для моторов.
Подключение радио модуля NRF24L01:
NRF24L01 Arduino Pro Mini
VCC 3.3V
GND GND
CE D9
CSN D10
SCK D13
MOSI D11
MISO D12
Описание управления:
Левый стик (джойстик 1):
Вверх/вниз = движение вперед/назад
Влево/вправо = поворот гусеничной платформы
Правый стик (джойстик 2):
Вверх/вниз = подъем/опускание плеча
Влево/вправо = поворот основания
Правый потенциометр (pot1):
Управление захватом (открыть/закрыть)
Дополнительно: левый потенциометр можно использовать для управления локтем (раскомментируйте соответствующие строки в коде).
здравствуйте можно ли к этому приёмнику подключить 2 мотора и 4 серво и если да то как
Добрый вечер.
Общие принципы подключения
Каналы приемника: Ваш приемник имеет 9 цифровых выходных каналов (выводы D2-D10). Любой из них можно использовать для управления сервоприводом. Для двигателей постоянного тока вам понадобится драйвер моторов, который будет подключаться к нескольким каналам.
Питание — самый важный момент: Это ключевой аспект, который может вывести устройство из строя. Сервоприводы (особенно мощные) и моторы потребляют большой ток.
Arduino Pro Mini не может питать их напрямую через свой встроенный стабилизатор — он сгорит.
Правильный способ: Подключите все сервоприводы и драйвер моторов к общей шине питания 5В, которая запитана от внешнего мощного источника (например, аккумулятора). Ваш приемник и управляющая логика (Arduino) должны получать питание отдельно (через вывод RAW) или от этого же источника, но через хороший стабилизатор.
Схема подключения
Вот как нужно соединить все компоненты:
1. Подключение 4 сервоприводов
Сигнальный провод (обычно желтый/оранжевый) каждого серво подключите к свободным цифровым каналам приемника. Например:
Серво 1 → Канал 3 (D3)
Серво 2 → Канал 4 (D4)
Серво 3 → Канал 5 (D5)
Серво 4 → Канал 7 (D7)
Провод питания (красный, +5В) и земли (черный/коричневый, GND) всех четырех сервоприводов подключите к общей шине питания, которая идет от вашего внешнего источника питания на 5В.
Важное предупреждение из статьи: мощные сервоприводы (например, MG996R) могут создавать скачки напряжения и сжечь Arduino Pro Mini. Он рекомендует устанавливать большие развязывающие конденсаторы параллельно питанию сервоприводов или использовать отдельный стабилизатор для них. Маломощные серво (SG90) обычно проблем не вызывают.
2. Подключение 2 моторов
Для управления моторами вам понадобится драйвер двигателей (например, L298N или более компактный MX1508, как в третьем примере автора).
Подключение к драйверу:
Питание драйвера подключите напрямую к вашей батарее (например, 7.4В или 12В).
Входы управления (IN1, IN2, IN3, IN4) драйвера подключите к цифровым каналам приемника. Обязательно используйте каналы с поддержкой ШИМ, чтобы регулировать скорость. В статье указаны каналы №2, №3, №6 и №9.
Например:
IN1 (Мотор A, вперед) → Канал 2 (ШИМ)
IN2 (Мотор A, назад) → Канал 9 (ШИМ)
IN3 (Мотор B, вперед) → Канал 6 (ШИМ)
IN4 (Мотор B, назад) → Канал 3 (ШИМ)
Заземление (GND): ОБЯЗАТЕЛЬНО соедините землю драйвера, землю приемника (Arduino) и землю вашего источника питания в одной точке. Без этого схема работать не будет.
3. Питание приемника
Вы можете запитать Arduino Pro Mini через вывод RAW (6-12В) от основной батареи.
Или, если ваш драйвер моторов имеет встроенный стабилизатор 5В (BEC, как у ESC), вы можете подать 5В на вывод VCC Arduino. Главное, чтобы общая земля была соединена.
Как это будет работать в коде
Вам нужно будет модифицировать код из примера. Основная логика:
Получить данные от передатчика.
Считать значения джойстиков (0-255).
Преобразовать эти значения:
Для сервоприводов: map(значение_джойстика, 0, 255, 0, 180) и использовать myservo.write(угол).
Для моторов (через ШИМ): написать логику управления драйвером (как в первом примере), используя analogWrite(пин, значение_скорости).
Сводная таблица подключения (пример)
Компонент Контакт на компоненте Пин на приемнике (Arduino) Примечание
Серво 1 Сигнал D3 Управление
Серво 2 Сигнал D4 Управление
Серво 3 Сигнал D5 Управление
Серво 4 Сигнал D7 Управление
Серво 1-4 Питание (5В) и GND Внешний источник 5В Не подключать к VCC Arduino!
Драйвер (L298N) IN1 (Мотор A) D2 (ШИМ) Скорость/направление
Драйвер (L298N) IN2 (Мотор A) D9 (ШИМ) Скорость/направление
Драйвер (L298N) IN3 (Мотор B) D6 (ШИМ) Скорость/направление
Драйвер (L298N) IN4 (Мотор B) D3 (ШИМ) Скорость/направление
Драйвер (L298N) GND GND Arduino Обязательно!
Драйвер (L298N) Питание мотора (Vcc) Внешний источник (батарея) Для питания моторов
Arduino Pro Mini RAW Внешний источник (6-12В) Или от BEC драйвера на VCC
В итоге, подключение возможно. Главное — внимательно отнестись к разделению питания: силовая часть (моторы и серво) должна питаться от внешнего источника, а управляющая часть (Arduino и логика) — через свой стабилизатор. Удачи в реализации вашего проекта
спасибо
а можете пожалуйста помочь с кодом а то я просто новичок ни понимаю как писать эти коды
Опишите словами что должен делать необходимый вам код
мне нужно чтобы этот пульт (https://microkontroller.ru/arduino-projects/pult-distanczionnogo-upravleniya-na-arduino-i-nrf24l01/) чтоб левый стик управлял гусеничной платформой (2 мотора), а правый стик управлял рукой (3 серво и 1 серво захват) захват должен управляется правым Потенциометрам
и можете пожалуйста указать куда и как подключить 4 серво и драйвер моторов MX1508