最近需要在Arduino之间,以及Arduino和上位机(树莓派)之间传输数据,
原有APC220设备虽然可用,使用也方便,但成本太高,不容易批量,遂寻求其他方案。
一、方案选择
根据搜索的结果和前人经验,有如下几种可行方案:
- nRF24L01+(RF)
- ESP8266(WIFI)
- XBee (ZigBee)
- ENC28J60(LAN)
- W5100,W5500(LAN)
其中,Xbee为最优选,但成本太高。
LAN方案不方便,WIFI方案功耗高,蓝牙方案传输距离短,
故考虑RF方案,成本和效果平衡较好。
nRF24L01+价格便宜(5块左右,做工好带天线的15左右),编程简单,
且存在能同时支持树莓派、Arduino、Linux的RF24库。
Git连接为:https://github.com/TMRh20/RF24.git
便宜的(做工一般的)nRF24L01开发板:
二、接线
1、nRF24L01+引脚图
– 1:地
– 2:3.3V(切不可接5V,烧片)
– 3:CE(RF读写控制引脚)
– 4:CSN(选片引脚)
– 5:SCK(SPI时钟)
– 6:MOSI(SPI主出从入)
– 7:MISO(SPI主入从出)
– 8:IRQ(外部中断)
2、接线方法
编号 | nRF24L01 | Arduino Mega | Arduino UNO | Rpi(物理管脚) |
---|---|---|---|---|
1 | GND | 9 | ||
2 | VCC | 1 | ||
3 | CE | 7(可自定义) | 7(可自定义) | 15(GPIO22) |
4 | CSN | 8(可自定义) | 8(可自定义) | 24(SPI0CS1) |
5 | SCK | 52 | 13 | 23 |
6 | MOSI | 51 | 11 | 19 |
7 | MISO | 53 | 12 | 21 |
8 | IRQ | – | – | – |
接线示意图:
Arduino UNO
Arduino Mega
RaspberryPi3
三、代码 & 运行
RF24库中自带的GettingStarted例子非常方便,其代码包含发送端和接收端两种类型,
默认为接受模式,输入T时切换为发送,输入R则切为接受模式,并有简单的超时判断。
为了易于理解,可简单修正代码,让接收端返回一自增数字。
1、 Arduino
1)RF24库安装
从https://github.com/TMRh20/RF24.git下载RF24后,
将其复制到Arduino安装目录下的libraries目录下,启动ArduinoIDE后,从例子中选择RF24->GettingStarted。
2)代码修改
发送端不必修改,直接编译上传即可。(注意UNO和Mega的选择和串口选择)
接收端将代码中的radioNumber从默认的0修改为1。如下:
bool radioNumber = 0; (自身为2Node,发送给1Node) -> bool radioNumber = 1;(自身为1Node,发送给2Node)
简言之,1Node为接收端,2Node为发送端。
建议:原代码中的got_time不易观察理解,
可将接收端中的got_time发送前赋值为一静态可增计数值。
3)运行
发送端启动后,输入T,使其进入发送模式。
接受端启动即可,无需输入R。(默认为R接收模式)
如上述配置接线正常,可在Serial Monitor中看到发送方和接收方的输出,大致如下:
发送方图(静态自增变量):
2、 树莓派(RaspberryPi)
本文中使用的树莓派为 16年新发布的RPi3 B型,其管脚如下:
1)RF库安装
将RF24库复制到树莓派(或通过git直接获取)。进入RF24目录后执行如下命令,进行编译和安装(选择SPI方式)
./configure --driver=SPIDEV sudo make install -B
2) 修改系统配置
修改/etc/modprobe.d/raspi-blacklist.conf,如果其中存在 blacklist spi-bcm2708,将其注释。 修改/etc/modules文件,在其中追加一行,开启SPI。 spidev
reboot重启树莓派后,/dev下会新增spidev0.0和spidev0.1两个设备文件。
3)代码修改
修改RF24/example_linux/GettingStarted.cpp文件,
同上面的Arduino一样,发送端不必修改,
接收端将radioNumber从默认的0修改为1,并建议吧回送的时戳数据改为自增数字。
在当前目录下执行make后,生成GettingStarted的二进制文件。
4)运行
使用sudo ./ GettingStarted执行,并输入0进入接收模式。
如Arduino的发送端配置、运行正常,则正常发送回应包。大致如下(自增变量版):
四、注意&体会
便宜版本的nRF24L01效果一般,很容易受到干扰。带天线的会好些,真做项目不可图便宜。
接线要准确,SPI要理解下原理。CE、CSN其实是可以任意指定的,只是要修改下RF24的初始化代码。
五、RH24例子代码简单说明
以下是RH24(TMRh20)自带的Arduino例子,简单说明一下,
树莓派上为C语言实现的版本,变量、语法略有区别,但逻辑是基本一致的。
- 变量定义
bool radioNumber = 1; // RF节点名称决定Flag // 指定CE用GPIO7,CSN用GPIO8,需要和接线一致 // 如接线不采用7,8,代码这里需要修改。 RF24 radio(7, 8); byte addresses[][6] = {"1Node", "2Node"}; // 两个节点名 bool role = 0; // 发送&接收模式Flag
- 初始化函数
void setup() { Serial.begin(115200); Serial.println(F("RF24/examples/GettingStarted")); Serial.println(F("*** PRESS 'T' to begin transmitting to the other node")); radio.begin(); 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]); } // 默认为监听模式,开始监听 radio.startListening(); }
- 执行逻辑
void loop() { if (role == 1) { // 发送模式 radio.stopListening(); // 发送数据前要停止监听 Serial.println(F("Now sending")); unsigned long start_time = micros(); // 待发送的时戳 // RF24内部会自动处理payload和发送数据不等长的问题 if (!radio.write( &start_time, sizeof(unsigned long) )){ // 发送数据 Serial.println(F("failed")); } radio.startListening(); // 数据发送完,需要监听回应数据的到来 unsigned long started_waiting_at = micros(); boolean timeout = false; while ( ! radio.available() ){ // 超时判断 if (micros() - started_waiting_at > 200000 ){ timeout = true; break; } } if ( timeout ){ Serial.println(F("Failed, response timed out.")); }else{ // 读数据并显示数据和间隔时间 unsigned long got_time; 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); } if ( role == 0 ) { // 接收模式 static long count = 1; // 自增计数 unsigned long got_time = 0; if( radio.available()){ while (radio.available()) { // 读数据 radio.read( &got_time, sizeof(unsigned long) ); } got_time = count++; // 为便于理解,回送自增计数值 radio.stopListening(); radio.write( &got_time, sizeof(unsigned long) ); // 写回应 radio.startListening(); Serial.print(F("Sent response ")); Serial.println(got_time); } } 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; }else if ( c == 'R' && role == 1 ){ //接受模式 Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK")); role = 0; radio.startListening(); } } } // Loop
五、参考URL
详细参考(英语):
http://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-HowTo
其他人的成功经验:
http://www.cnblogs.com/hangxin1940/archive/2013/05/01/3053467.html
http://www.cnblogs.com/hangxin1940/archive/2013/05/01/3048315.html
注意:其所使用的RF24库并不相同
转载自 http://blog.csdn.net/ydogg/article/details/53307365