所有关于电路
项目

创建一个双通道遥控nRF24L01 +

2016年7月27日,通过马克·休斯

这篇文章会给你所需的构建块创建自己的双通道无线远程控制器。

学习如何连接一个nRF24L01 +两个arduino和操纵杆来创建您自己的双通道无线远程控制器。

nRF24L01 +是一个便宜的2.4 GHz无线收发器和许多微控制器接口。

这个项目将使用一个nRF24L01 +无线连接操纵杆两个伺服系统。我们将开始一个操纵杆控制两个伺服板,然后我们将电路划分为两个董事会。一个董事会将读取操纵杆数据传输到第二个板,接收数据,并控制伺服系统。

材料

成本 beplay无法取钱
电阻操纵杆 7美元 电位计操纵杆数据表
非接触式操纵杆 120美元 HF系列数据表
Arduino Uno R3 15美元 Arduino Uno数据表
nRF24L01 < 1美元 nRF24L01 +数据表
nRF24L01 +基地 2美元 基础数据表

操纵杆选项1 -可变电阻器操纵杆

大部分的双轴操纵杆爱好使用移动中心抽头电位计,将机械位移转换为可变电阻。

$ $ 10 \;kω\ $ $电阻器,集中位置相当于$ $ R = 5 \; kω\ $ $,极右派相当于$ $ R = 10 \; kω\ $ $和极左相当于$ $ R = 0 \; kω\ $ $。相似的价值观将对应于上下方向。

不幸的是,微控制器不直接测量电阻。他们直接解释电位差超过0 V参考输入行,所以一些小的努力必须使用可变电阻来创建一个变量潜在的差异。

创建一个已知常数电位差在可变电阻器的外针和中心之间的电位差测量雨刷和地面位移成正比,0 v和5之间的不同。

这些操纵杆是廉价和丰富。但接触操纵杆有局限性。灰尘、氧化、水分或身体接触磨损会引起读数波动。战斗机器人,这不是一个问题,但它确实是一个问题在安全至上或精密应用细微变化可以导致uncommanded运动。

双轴操纵杆,你需要两个可用模拟针阅读操纵杆,每个轴。

Arduino的操纵杆连接非常简单:接地,5 v, A0前/后,A1为左/右。预期的潜在差异的范围从非常接近0 V附近5 V。

操纵杆选项2 -非接触式操纵杆

电动轮椅和高端工业设备有很好的非接触式操纵杆,也可以用相同的四线:界面上的地面参考,从地面5伏特电位差,两个模拟电位差输出对应于远期/尾和左/右方向。输出影响磁铁的运动采用霍尔传感器附近。

没有计算机代码中的功能不同,也没有任何变化的数量输入使用。前面的操纵杆和这个的唯一区别是,decreases-however潜在差异范围,范围仍在0到5 V。

这些非接触式操纵杆没有接触部分腐蚀,磨损,或降低。他们可以在潮湿的环境和功能都是防弹的可靠性。然而,他们付出了成本超过20倍操纵杆和联系人。

要么操纵杆上可能有多个针接口的类型,但大多数操纵杆只需要四针Arduino的界面。

Arduino销 电阻操纵杆 非接触式操纵杆
接地 接地 接地
5伏直流 L / R + & U / D + 5伏直流
A0 U / D 前/后1
A1 L / R 左/右1

读取数据从一个操纵杆

每当我构建一个复杂的电路,我想做一下时间和记录的值小一些。我们的工作流程是读取数据的第一步从一个操纵杆。我使用一个非接触式操纵杆;如果您使用一个resistor-based操纵杆,你的价值观就会改变。

下面的代码应该跟踪操纵杆和发送数据的值通过Arduino Uno终端验证一切是否正常工作:

/ /代码来控制伺服系统与操纵杆int ForeAft_Pin = 0;/ /前/后输入Arduino销A0 int LeftRight_Pin = 1;/ /左/右输入Arduino销A1 int ForeAft_Input;/ /预期范围220 - 800采用霍尔操纵杆int LeftRight_Input;/ /预期范围220 - 800采用霍尔操纵杆空白设置主程序初始化()/ / {Serial.begin (9600);/ /准备调试}无效循环(){ForeAft_Input = analogRead (ForeAft_Pin);/ /读取前/后值LeftRight_Input = analogRead (LeftRight_Pin);/ /读左/右值并(LeftRight_Input);/ /左/右发送到终端并(“\ t”);以ForeAft_Input); // Send Fore/Aft to terminal delay( 1000 ); // Wait 1 second before looping }

我也想知道什么值是合理的和值都不可以给我重要的信息时,故障排除。合理值的范围规定使用和确定哪些类型的变量值应该导致计划认识到错误并停止执行。

注意,纠错并没有包含在程序。

创建伺服输出

你读过操纵杆的位置后,在调试终端验证它,你需要注意每个轴的限制和纳入未来的代码。这个步骤是必要的,以确保最左边操纵杆的位置对应于逆时针伺服的限制,和最右操纵杆的位置对应的全部顺时针限制伺服。

接下来,我们将添加两个伺服电路。连接地面线和5 vdc Arduino线。然后一个伺服的信号线连接到数字销销7 6和另一个数字。

接下来的代码将读取操纵杆的位置并把它转换成相应的角度$ $ 0 ^ \保监会< \θ< ^ \保监会180美元并发送相应的伺服信号:

# include <伺服。h > / /伺服。h的代码创建两个伺服变量——一个为每个轴操纵杆伺服ForeAft;/ /定义一个伺服伺服LeftRight正向和反向运动;/ /定义一个伺服运动对于左、右/ /决定你要去哪里把操纵杆到电路板。int ForeAft_Pin = 0;/ /操纵杆前/后插入模拟销0 int LeftRight_Pin = 1;/ /操纵杆左/右插入模拟销1 / /创建变量来读取操纵杆ForeAft_Input浮动值;/ /变量来存储数据的前/后输入操纵杆LeftRight_Input浮动;/ /左/右输入变量来存储数据从操纵杆/ /创建变量传递int ForeAft_Output伺服值;/ /预期范围0 - 180度int LeftRight_Output; // Expected range 0 - 180 degrees // These variables allow for math conversions and later error checking as the program evolves. int Fore_Limit = 800; // Joystick limit up int Aft_Limit = 220; // Joystick limit down int Right_Limit = 800; // Joystick limit right int Left_Limit = 226; // Joystick limit left void setup() // Main Program Initialization { Serial.begin(9600); // Send data back for debugging purposes ForeAft.attach(6); // Plug a servo signal line into digital output pin 6 LeftRight.attach(7); // Plug a servo signal line into digital output pin 7 } void loop() { ForeAft_Input = analogRead(ForeAft_Pin) ; // Read the Fore/Aft joystick value LeftRight_Input = analogRead(LeftRight_Pin) ; // Read the Left/Right joystick value ForeAft_Output = convertForeAftToServo(ForeAft_Input) ; // Convert the Fore/Aft joystick value to a Servo value (0-180) LeftRight_Output = convertLeftRightToServo(LeftRight_Input) ; // Convert the Left/Right joystick value to a Servo value (0-180) Serial.print(ForeAft_Output); //Debug Serial.print("\t"); //Debug Serial.println(LeftRight_Output); //Debug ForeAft.write(ForeAft_Output); // Command the Fore/Aft servo to a position LeftRight.write(LeftRight_Output); // Command the Left/Right servo to a position delay( 100 ); // Increase for debug, decrease to reduce servo jitter } // Functions to convert and scale the Fore/Aft and Left/Right data float convertForeAftToServo(float y) { int result; result = map(y, Aft_Limit, Fore_Limit, 0, 180); } float convertLeftRightToServo(float x){ int result; result = map(x, Left_Limit, Right_Limit, 0, 180); } // map() truncates data -- if you need a bit more accuracy for some reason, these // functions should give it to you. // // float convertForeAftToServo(float y) { // float result; // result = ((y - Aft_Limit) / (Fore_Limit - Aft_Limit) * 180); // return result; // } // // float convertLeftRightToServo(float x) { // float result; // result = ((x - Left_Limit) / (Right_Limit - Left_Limit) * 180); // return result; // } //

这里有一个视频操纵杆/伺服连接的行动:

nRF24L01_Gerber.Zip

nRF24L01 +

nRF24L01 +半双工收发器,你可以连接到你的Arduino,覆盆子π,或其他单片机发送双向信息。它工作在2.4 GHz的ISM(工业、科学和医疗)乐队。

摄谱仪的nRF24L01

获取图像与一个美国泰克MDO 3104混合域示波器

它的优点和它工作得很好,低功耗,易于使用,是一个极其廉价的方式来发送和接收信息。Arduino可以直接从权力的监管3.3 v输出,或5 v规范输出如果使用基地。

缺点是,它使用一些IO针功能,它不能同时发送/接收,2 x4 8位置头轻微不便使用,和$ $ V_ {DD} $ $不能超过3.6 V, 3.0建议,范围比较有限

优点远远大于缺点。买一把这些在eBay上或从addicore,添加无线项目几乎没有时间。

nRF24L01 +基地(左)与nRF24L01 +半双工收发器(右)

RF24见前面的基础模块

nRF24L01 +安装在基础模块

你会发现nRF24L01的基础模块+更容易成型时使用,因为它处理稳压和滤波电容器的nRF24L01 +

请注意,的基本单位需要5 v而nRF24L01 +需要3.3 v。

自制的模块用于界面arduino的基础模块

自制pin-swap董事会、nRF24L01基地和nRF24L01 +收发器

这篇文章是关于使用nRF24L01 +,您可以直接钩起来的Arduino下面的图。然而,我很快发现了多个电线非常不方便,知道如果我把这个项目放在一个抽屉里,把它回到工作之后,一半的线路会拔出这浪费时间,导致错误。所以我选择使用这个项目修改基础模块和自制pin-swap董事会。

我修改了基本模块通过添加完整的直通男性headers-my计划当时它直接插入Arduino Uno R3。然而,基本模块上的针不对应的销名称库。所以我还创建了一个自制的pin-swap板界面的基础模块和Arduino而不是改变的代码库。这使我方便plug-and-go联播同时与读者保持完整的兼容性不想做修改。

三个模块堆叠

nRF24L01 +安装在基本单位,安装在自制的适配器板,安装在Arduino Uno R3

注意:有很多伟大的文章在你的设计如何实现这些无线模块以及如何解决当事情出错。我没有什么值得添加到他们在这篇文章中,所以,我想请您留意这个页面故障排除和附加信息。

连接arduino

首先,下载和安装这些库Arduino。

然后下面的nRF24L01 +和Arduino之间的连接:

nRF24L0 + Arduino Uno R3
1 接地 接地
2 Vcc Vcc
3 CE 数字9
4 CSN 数字10
5 SCK 数字13
6 莫西人 数字11
7 味噌 数字12
8 硬中断请求优先级别 数字8


提供一致的力量nRF24L01 +载波板是至关重要的。如果你有麻烦你nRF24L01 +工作,你验证了电线和代码,试着用一个示波器跟踪3.3 V的输出是否一致。如果不是:

  • 添加滤波器电容电路。使用跳线,小鳄鱼剪辑,或者其他方式附加0.1µF - 10µF电容器引线附近的nRF24L01 +航空公司董事会。
  • 断开USB电源和使用4节AA电池电源你Arduino通过桶杰克连接器(4 x1.5 V = 6 V)。

接下来,您需要遵循以下步骤:

  1. 设置两个arduino nRF24L01 +有线。
  2. 下面的“入门代码”复制到一个草图,或者打开它从实例库(在你安装的库)。
  3. 改变行15”RF24广播(9、10);“
  4. 开始代码上传到第一Arduino第12行阅读“bool radioNumber = 0;
  5. 开始代码上传到第二Arduino第12行阅读“bool radioNumber = 1;
  6. 然后打开两个终端:一个连接到第一Arduino和一个连接到第二。“T”型指示在一个终端。数量将开始这两个接收器之间来回跳跃。这让你知道你已经正确连接一切,一切都是工作。
/ * *开始素描nRF24L01 +例子无线电*这是一个非常基本的例子,如何从一个节点发送数据到另一个*更新:2014年12月由TMRh20 * / # include < SPI。h > # include < RF24。h > / * * * * * * * * * * * * * * * * * *用户配置* * * * * * * * * * * * * * * * * * * * * * * * * * * / / * * *这个广播电台数量设置0或1 * * * / bool radioNumber = 0;/ *硬件配置:设置nRF24L01收音机SPI总线+针7 & 8 * / RF24电台(9、10);/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /字节地址[][6]={节点“1”,“2节点”};/ /用于控制该节点是否发送或接收bool角色= 0;无效的设置(){Serial.begin (115200);以(F (“RF24 /例子/ GettingStarted "));系列。println (F(“* * *新闻' T '开始传播到其他节点”));radio.begin (); // Set the PA Level low to prevent power supply related issues since this is a // getting_started sketch, and the likelihood of close proximity of the devices. RF24_PA_MAX is default. radio.setPALevel(RF24_PA_LOW); // Open a writing and reading pipe on each radio, with opposite addresses if(radioNumber){ radio.openWritingPipe(addresses[1]); radio.openReadingPipe(1,addresses[0]); }else{ radio.openWritingPipe(addresses[0]); radio.openReadingPipe(1,addresses[1]); } // Start the radio listening for data radio.startListening(); } void loop() { /****************** Ping Out Role ***************************/ if (role == 1) { radio.stopListening(); // First, stop listening so we can talk. Serial.println(F("Now sending")); unsigned long start_time = micros(); // Take the time, and send it. This will block until complete if (!radio.write( &start_time, sizeof(unsigned long) )){ Serial.println(F("failed")); } radio.startListening(); // Now, continue listening unsigned long started_waiting_at = micros(); // Set up a timeout period, get the current microseconds boolean timeout = false; // Set up a variable to indicate if a response was received or not while ( ! radio.available() ){ // While nothing is received if (micros() - started_waiting_at > 200000 ){ // If waited longer than 200ms, indicate timeout and exit while loop timeout = true; break; } } if ( timeout ){ // Describe the results Serial.println(F("Failed, response timed out.")); }else{ unsigned long got_time; // Grab the response, compare, and send to debugging spew radio.read( &got_time, sizeof(unsigned long) ); unsigned long end_time = micros(); // Spew it Serial.print(F("Sent ")); Serial.print(start_time); Serial.print(F(", Got response ")); Serial.print(got_time); Serial.print(F(", Round-trip delay ")); Serial.print(end_time-start_time); Serial.println(F(" microseconds")); } // Try again 1s later delay(1000); } /****************** Pong Back Role ***************************/ if ( role == 0 ) { unsigned long got_time; if( radio.available()){ // Variable for the received timestamp while (radio.available()) { // While there is data ready radio.read( &got_time, sizeof(unsigned long) ); // Get the payload } radio.stopListening(); // First, stop listening so we can talk radio.write( &got_time, sizeof(unsigned long) ); // Send the final one back. radio.startListening(); // Now, resume listening so we catch the next packets. Serial.print(F("Sent response ")); Serial.println(got_time); } } /****************** Change Roles via Serial Commands ***************************/ if ( Serial.available() ) { char c = toupper(Serial.read()); if ( c == 'T' && role == 0 ){ Serial.println(F("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK")); role = 1; // Become the primary transmitter (ping out) }else if ( c == 'R' && role == 1 ){ Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK")); role = 0; // Become the primary receiver (pong back) radio.startListening(); } } } // Loop

从发射机终端打印输出

从接收机终端打印输出

电路1 -操纵杆发射机

断开电路的功率和电脑,开始组装发射机电路。

将操纵杆连接到别针A0 A1 5 v,并接地。记住RF24附加到3.3 v供应和连接到5 v将摧毁它。

我在接收板有问题,我可以解决通过引入10µF 5 v供应管道和地面之间的电容,所以我把一个在两个电路实验。在后来的调查,问题与伺服系统的瞬时电流要求超过可用性大,同时驱动电脑USB端口。一个伺服的峰值电流画很容易超过一个什么USB 2.0端口是必须提供。

我们的代码将读取操纵杆价值观和传输通过nRF24L01 +与伺服系统接收板。延迟引入的代码用于故障诊断的目的。你需要大大减少他们在你最后的设计或你会紧张不安的运动。

/ *发射机*代码阅读操纵杆位置和传输用RF24L01 +接收器* / # include < SPI。h > # include < RF24。h > / /电台配置RF24电台(9、10);字节地址[][6]={节点“1”,“2节点”};bool radioNumber = 1;bool角色= 1;/ /控制传输1 /接收0 / /决定你要去哪里把操纵杆到电路板。int ForeAft_Pin = 0;/ /操纵杆前/后插入模拟销0 int LeftRight_Pin = 1;/ /操纵杆左/右插入模拟销1 / /创建变量来读取操纵杆ForeAft_Input浮动值;/ /变量来存储数据的前/后输入操纵杆LeftRight_Input浮动; // Variable to store data for for Left/Right input from joystick // Create variables to transmit servo value int ForeAft_Output; // Expected range 0 - 180 degrees int LeftRight_Output; // Expected range 0 - 180 degrees // These variables allow for math conversions and later error checking as the program evolves. int Fore_Limit = 800; // High ADC Range of Joystick ForeAft int Aft_Limit = 220; // Low ADC Range of Joystick ForeAft int Right_Limit = 800; // High ADC Range of Joystick LeftRight int Left_Limit = 226; // Low ADC Range of Joystick LeftRight void setup() { Serial.begin(9600); // Get ready to send data back for debugging purposes radio.begin(); // Get the transmitter ready radio.setPALevel(RF24_PA_LOW); // Set the power to low radio.openWritingPipe(addresses[1]); // Where we send data out radio.openReadingPipe(1,addresses[0]);// Where we receive data back } void loop() { ForeAft_Input = analogRead(ForeAft_Pin) ; // Read the Fore/Aft joystick value LeftRight_Input = analogRead(LeftRight_Pin) ; // Read the Left/Right joystick value ForeAft_Output = convertForeAftToServo(ForeAft_Input) ; // Convert the Fore/Aft joystick value to a Servo value (0-180) LeftRight_Output = convertLeftRightToServo(LeftRight_Input) ; // Convert the Left/Right joystick value to a Servo value (0-180) // Serial.print(ForeAft_Output); radio.stopListening(); // Stop listening and begin transmitting delay(500); // quite a long delay -- causes jittering of servo if(radio.write(&ForeAft_Output, sizeof(ForeAft_Output)),Serial.println("sent ForeAft")); //Send ForeAft data if(radio.write(&LeftRight_Output, sizeof(LeftRight_Output)),Serial.println("sent LeftRight")); //Send LeftRight data radio.startListening(); // Get ready to receive confirmation from receiver } // Function to convert and scale the Fore/Aft data float convertForeAftToServo(float y) { int result; result = ((y - Aft_Limit) / (Fore_Limit - Aft_Limit) * 180); return result; } // Function to convert and scale the Left / Right data // Can be replaced with Map function float convertLeftRightToServo(float x) { int result; result = ((x - Left_Limit) / (Right_Limit - Left_Limit) * 180); return result; }

供参考,这是紧张不安的运动是什么样子。再一次,这是造成的延误我们的代码:

电路2 -伺服接收器

/ *接收机从RF24L01 + *代码接收数据,并使用它来控制伺服* / # include <伺服。h > # include < SPI。h > # include < RF24。h > / /电台配置bool radioNumber = 0;RF24电台(9、10);字节地址[][6]={节点“1”,“2节点”};bool角色= 0;/ /控制传输/接收/ /创建变量来控制伺服伺服ForeAft价值;伺服LeftRight;unsigned int ForeAft_Output;/ /预期范围0 - 180度unsigned int LeftRight_Output; // Expected range 0 - 180 degrees void setup() { Serial.begin(9600); // Get ready to send data back for debugging purposes ForeAft.attach(6); // Plug a servo signal line into digital output pin 6 LeftRight.attach(7); // Plug a servo signal line into digital output pin 7 radio.begin(); // Initialize radio radio.setPALevel(RF24_PA_LOW); // Set the power output to low radio.openWritingPipe(addresses[0]); radio.openReadingPipe(1,addresses[1]); radio.startListening(); } void loop() { delay(500); //increase for debuggy, decrease to decrease jitter if(radio.available()){ radio.read(&ForeAft_Output,sizeof(ForeAft_Output)); radio.read(&LeftRight_Output,sizeof(ForeAft_Output)); } else {Serial.print("No radio"); } Serial.print(ForeAft_Output); Serial.print("\t"); Serial.println(LeftRight_Output); ForeAft.write(ForeAft_Output); // Command the Fore/Aft servo to a position LeftRight.write(LeftRight_Output); // Command the Left/Right servo to a position }

下一个步骤

相当多的特性可以被添加到这个代码使它更健壮一点:

  • 所有操纵杆有点不同和不同的操纵杆有不同的前/尾/左/右值。设置里面的限制以及期望是什么,然后可能引入一些代码在主程序循环推动操纵杆的门槛的限制。例如,如果foreaft_input < fore_limit,然后fore_limit - = 1。(提示:随着时间的推移慢慢扩大范围,所以用户可以适应变化。)
  • 一些来自接收者的反馈电路可以让操纵杆电路知道这是正确连接。或许你可以添加一个振动电机或LED,可以让用户知道操纵杆命令的接收器。
  • 你可以添加代码来决定当操纵杆是断开的。这个项目应该继续做它在做什么?应该中心伺服系统?应该完全停止传输?
  • 你可以添加代码的转换/规模函数检测和处理错误的输入,以防操纵杆故障和发送无效数据。
  • 你可以试一试我²C接口nRF24L01和让我们知道它是如何工作的。

现在,您应该有你自己的双通道遥控器!考虑使用它剑齿虎电动机控制器创建自己的机器人平台。

自己尝试这个项目!BOM。

21日的评论
  • ericgibbs 2016年7月28日,

    嗨,马克,
    国际海事组织的主要限制是短程,发射功率只是1 mwatt。
    我做了一个远程单位使用NRF24的基站和3。
    我可以得到的最好的范围使用机载PCB跟踪天线是大约10到15米。
    通过一个砖墙,房间,我只能得到~ 7米。

    接收到的信号被任何人“失去”移动路径的TX和RX单位
    我试着一些DIY偶极子的想法发表在网络上,一些稍微改进的范围。
    商业偶极子NRF24可用,这将给TX RX范围增加,而是增加了项目成本。

    NRF模块的另一个限制是程序的大小/复杂性需要为了得到一个简单的链接工作。

    我转向HC12收发器433 mhz乐队,[100 mwatt]现在我起床到100米。
    HC12是一个简单的RS232 I / O控制和数据传输程序。
    如果您添加可选的偶极子范围1000米是可能的。

    埃里克

    喜欢的。 回复
  • 亚当·克拉克1 2017年3月26日

    谢谢你的那样,在循环虽然有radio.stopListening ();还有不似乎是一个再把它吗?

    喜欢的。 回复
    • 马克·休斯 2017年3月26日
      推特)嗨@克拉克1,看起来就像你发现了一个错误。最后的代码部分添加额外的代码,在创建/编辑过程和持续的没有理由。我下载.ino文件(RF24toServo),我以前在项目的示范和线是缺席的。如果要我猜(它已经太长时间记住),我很可能实现一个错误检查,后来我决定消除为简洁的缘故。它也看起来像我没有正确逃脱反斜杠前t。我要编辑周一让修正。好点!顺便说一句,如果你需要更大的范围,查看我HC12文章——//www.vs-visa.com/projects/understanding-and-implementing-the-hc-12-wireless-transceiver-module/谢谢让我诚实!马克
      喜欢的。 回复