前段时间我在做低功耗蓝牙的时候看到了这个网页
https://wiki.bitcraze.io/misc:hacks:hackrf
但是我当时没有成功解调nrf24l01,反而成功搞定蓝牙了。
后来我用了我自己新买的小四轴也没解调成功,所以我打算老老实实地重现那个网页上的步骤。
正好我以前买过crazyflie的产品(小四轴和crazyradio模块),我就找出了这个crazyradio模块来重复实验。
期间我还看到了几个好的资源。
https://www.bitcraze.io/2015/06/sniffing-crazyflies-radio-with-hackrf-blue/
这个也是同一网站的相关内容,但是更详细。
http://blog.cyberexplorer.me/2014/01/sniffing-and-decoding-nrf24l01-and.html
这是nrf24l01和btle的解码程序作者的文章。从这篇文章,我发现nrf24l01解码程序的输入必须是2M的数据。
然后根据nrf24l01的发射机硬件选择的速率模式来决定降采样率。如果nrf24l01设置的速度是2M那么降采样率就是1,如果是1M那么降采样率是2,如果是256K那么降采样率就是8。这个值不能自动判断,需要根据发射机事先指定好。
另外在实验过程中,我还发现一个坑。gnuradio流图输出的fifo,在每次实验前都需要清空再新建,否则里面的数据就是老的。无法以此来判断是否准确解码。
我的开发计划分为4步:
1.重复实验crazyflie网页上的步骤,使用crazyradio来发射,然后用gnuradio流图来做fm解调输出给fifo,再用nrf解码程序来解码。
2.合并gnuradio的fm解调程序和nrf解码程序,这样在电脑上用一个单独的程序就能解调crazyradio发射的数据了。
3.把这个独立的程序搬到portapack里,解调crazyradio的数据。
4.用portapack去解调小四轴的遥控器指令。
目前我第一步已经实现了。
首先,我下载了crazyflie的库,这样就能用python语言来控制它,并发出自己想要的数据。
https://github.com/bitcraze/crazyradio-firmware
不用编译安装,但是需要安装一些依赖
sudo apt install python-usb
然后到lib下,可以看到crazyradio.py,这就是我们自己写的Python程序要调用的库了。
然后在同一个文件夹下新建我们自己的python程序try.py
1
2
3
4
5
6
7
8
9
10from crazyradio import Crazyradio import time cr = Crazyradio() cr.set_channel(26) cr.set_data_rate(cr.DR_250KPS) while True: cr.send_packet((0,1,2,3,4)) time.sleep(0.1)
然后运行
sudo python try.py
这样就开始发射了。
接下来画一个流图
如果你不想自己画可以下载下面的流图,不过它的参数和我不太一样,你要把参数改成和我一样的。
https://wiki.bitcraze.io/_media/misc:hacks:nrf24_demod.grc.zip
再然后就可以参照我以前的文章,编译NRF24-BTLE-Decoder程序,就是我前面说的nrf解码程序。
搞定以后的运行顺序是:
sudo rm /tmp/fifo
mkfifo /tmp/fifo
然后运行gnuradio流图
cat /tmp/fifo | ./nrf24-btle-decoder -d 8
这样你就能看到终端里的这样的输出了
你可以看到大多数数据包的地址是E7E7E7E7E7,就是我们发的数据包了,数据是00 01 02 03 04,这正好是我们前面的python程序里发射的0 1 2 3 4了。
少数数据包不对可能是干扰导致的。
这样第一步就做完了。
第二步:
我尝试了合并gnuradio解调和nrf解码
hackrf_nrf.cpp:
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375#include <signal.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <math.h> #include <pthread.h> #include <libhackrf/hackrf.h> #include <iostream> #include <stdint.h> #include <time.h> #include <sys/time.h> #include <stdbool.h> #include <inttypes.h> #define MAXIMUM_BUF_LENGTH (16 * 16384) /* Global variables */ int32_t g_threshold = 0; // Quantization threshold int g_srate = 8; uint8_t channel_number = 38; int skipSamples = 1000; /* Ring Buffer */ #define RB_SIZE 1000 int rb_head=-1; int16_t *rb_buf; using namespace std; static volatile bool do_exit = false; hackrf_device *device; uint32_t freq; uint32_t hardware_sample_rate; uint16_t buf16[MAXIMUM_BUF_LENGTH]; int16_t buffer[MAXIMUM_BUF_LENGTH]; int lp_len; int rate_in; int16_t result_demod[MAXIMUM_BUF_LENGTH]; int result_demod_len; int pre_r, pre_j; void sigint_callback_handler(int signum) { cout << "Caught signal" << endl; do_exit = true; } void multiply(int ar, int aj, int br, int bj, int *cr, int *cj) { *cr = ar*br - aj*bj; *cj = aj*br + ar*bj; } int polar_discriminant(int ar, int aj, int br, int bj) { int cr, cj; double angle; multiply(ar, aj, br, -bj, &cr, &cj); angle = atan2((double)cj, (double)cr); return (int)(angle / 3.14159 * (1<<14)); } int rx_callback(hackrf_transfer* transfer) { for (int i = 0; i < transfer->valid_length; i++) { double sample = (int8_t)(transfer->buffer[i]) + 1; buf16[i] = (int16_t)sample; //s->buf16[i] = (int16_t)buf[i] - 127; s->buf16[i] -127~128 uint16_t, unsigned for negative? } memcpy(buffer, buf16, 2*transfer->valid_length); lp_len = transfer->valid_length; //fm demod //rate = rate_in = 2M int i, pcm; pcm = polar_discriminant(buffer[0], buffer[1], pre_r, pre_j); result_demod[0] = (int16_t)pcm; for (i = 2; i < (lp_len-1); i += 2) { pcm = polar_discriminant(buffer[i], buffer[i+1], buffer[i-2], buffer[i-1]); result_demod[i/2] = (int16_t)pcm; } pre_r = buffer[lp_len - 2]; pre_j = buffer[lp_len - 1]; result_demod_len = lp_len/2; int i4; for (i4 = 0; i4 < result_demod_len; i4 += 1) { int16_t cursamp = (int16_t) (result_demod[i4]); rb_head++; rb_head=(rb_head)%RB_SIZE; rb_buf[(rb_head)%RB_SIZE]=(int)cursamp; skipSamples = skipSamples - 1; if (skipSamples<1) { int32_t threshold_tmp=0; for (int c=0;c<8*g_srate;c++) { threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE]; } g_threshold = (int32_t)threshold_tmp/(8*g_srate); int transitions=0; if (rb_buf[(rb_head + 9*g_srate)%RB_SIZE] > g_threshold) { for (int c=0;c<8;c++) { if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] > rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE]) transitions = transitions + 1; } } else { for (int c=0;c<8;c++) { if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] < rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE]) transitions = transitions + 1; } } bool packet_detected=false; if (transitions==4 && abs(g_threshold)<15500) { int packet_length = 0; uint8_t tmp_buf[10]; uint8_t packet_data[500]; uint8_t packet_packed[50]; uint16_t pcf; uint32_t packet_crc; uint32_t calced_crc; uint64_t packet_addr_l; /* extract address */ packet_addr_l=0; for (int t=0;t<5;t++) { bool current_bit; uint8_t byte=0; for (int c=0;c<8;c++) { if (rb_buf[(rb_head+(1*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold) current_bit = true; else current_bit = false; byte |= current_bit << (7-c); } tmp_buf[t]=byte; } for (int t=0;t<5;t++) packet_addr_l|=((uint64_t)tmp_buf[t])<<(4-t)*8; /* extract pcf */ for (int t=0;t<2;t++) { bool current_bit; uint8_t byte=0; for (int c=0;c<8;c++) { if (rb_buf[(rb_head+(6*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold) current_bit = true; else current_bit = false; byte |= current_bit << (7-c); } tmp_buf[t]=byte; } pcf = tmp_buf[0]<<8 | tmp_buf[1]; pcf >>=7; /* extract packet length, avoid excessive length packets */ if(packet_length == 0) packet_length=(int)pcf>>3; if (packet_length>32) packet_detected = false; /* extract data */ for (int t=0;t<packet_length;t++) { bool current_bit; uint8_t byte=0; for (int c=0;c<8;c++) { if (rb_buf[(rb_head+(6*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold) current_bit = true; else current_bit = false; byte |= current_bit << (7-c); } packet_data[t]=byte; } /* Prepare packed bit stream for CRC calculation */ uint64_t packet_header=packet_addr_l; packet_header<<=9; packet_header|=pcf; for (int c=0;c<7;c++){ packet_packed[c]=(packet_header>>((6-c)*8))&0xFF; } for (int c=0;c<packet_length;c++){ packet_packed[c+7]=packet_data[c]; } /* calculate packet crc */ const uint8_t* data = packet_packed; size_t data_len = 7+packet_length; bool bit; uint8_t cc; uint_fast16_t crc=0x3C18; while (data_len--) { cc = *data++; for (uint8_t i = 0x80; i > 0; i >>= 1) { bit = crc & 0x8000; if (cc & i) { bit = !bit; } crc <<= 1; if (bit) { crc ^= 0x1021; } } crc &= 0xffff; } calced_crc = (uint16_t)(crc & 0xffff); /* extract crc */ for (int t=0;t<2;t++) { bool current_bit; uint8_t byte=0; for (int c=0;c<8;c++) { if (rb_buf[(rb_head+((6+packet_length)*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold) current_bit = true; else current_bit = false; byte |= current_bit << (7-c); } tmp_buf[t]=byte; } packet_crc = tmp_buf[0]<<8 | tmp_buf[1]; /* NRF24L01+ packet found, dump information */ if (packet_crc==calced_crc) { printf("NRF24, Threshold:%"PRId32", Address: 0x%08"PRIX64" ",g_threshold,packet_addr_l); printf("length:%d, pid:%d, no_ack:%d, CRC:0x%04X data:",packet_length,(pcf&0b110)>>1,pcf&0b1,packet_crc); for (int c=0;c<packet_length;c++) printf("%02X ",packet_data[c]); printf("n"); packet_detected = true; } else packet_detected = false; } if (packet_detected) { skipSamples=20; } } } return 0; } int main(int argc, char **argv) { signal(SIGINT, &sigint_callback_handler); int result; rb_buf = (int16_t *)malloc(RB_SIZE*2); freq = 2426e6; rate_in = 2000000; hardware_sample_rate = (uint32_t)(rate_in); result = hackrf_init(); if( result != HACKRF_SUCCESS ) { cout << "hackrf_init() failed" << endl; return EXIT_FAILURE; } result = hackrf_open(&device); if( result != HACKRF_SUCCESS ) { cout << "hackrf_open() failed" << endl; return EXIT_FAILURE; } result = hackrf_set_lna_gain(device, 14); if( result != HACKRF_SUCCESS ) { cout << "hackrf_set_lna_gain() failed" << endl; return EXIT_FAILURE; } result = hackrf_set_vga_gain(device, 20); if( result != HACKRF_SUCCESS ) { cout << "hackrf_set_vga_gain() failed" << endl; return EXIT_FAILURE; } /* Set the frequency */ result = hackrf_set_freq(device, freq); if( result != HACKRF_SUCCESS ) { cout << "hackrf_set_freq() failed" << endl; return EXIT_FAILURE; } /* Set the sample rate */ result = hackrf_set_sample_rate(device, hardware_sample_rate); if( result != HACKRF_SUCCESS ) { cout << "hackrf_set_sample_rate() failed" << endl; return EXIT_FAILURE; } result = hackrf_set_baseband_filter_bandwidth(device, hardware_sample_rate); if( result != HACKRF_SUCCESS ) { cout << "hackrf_baseband_filter_bandwidth_set() failed" << endl; return EXIT_FAILURE; } fprintf(stderr, "Output at %u Hz.n", rate_in); usleep(100000); result = hackrf_start_rx(device, rx_callback, NULL); while ((hackrf_is_streaming(device) == HACKRF_TRUE) && (do_exit == false)) { usleep(100000); } if (do_exit) { fprintf(stderr, "nUser cancel, exiting...n"); } else { fprintf(stderr, "nLibrary error, exiting...n"); } result = hackrf_close(device); if(result != HACKRF_SUCCESS) { cout << "hackrf_close() failed" << endl; } else { cout << "hackrf_close() done" << endl; } hackrf_exit(); cout << "hackrf_exit() done" << endl; return 0; }
1
2
3g++ hackrf_nrf.cpp -o hackrf_nrf -lhackrf -pthread ./hackrf_nrf
记得增益不能太高,否则反而收不到
第三步:
我把合并完的代码仿照btle加入到portapack里了,本来需要的是8MHz降采样4倍到2MHz这样才能解调2MPS的信号,但是实际发现不行,只能4MHz降采样4倍,这样理论上只能解调1MPS和250KPS,实际我发现连1MPS都不行,所以暂时只能解调250KPS。
红圈里就是我的crazyradio发射的数据包被解出来了。
后续我可能会解调一下实际小四轴的遥控器。
代码已经上传github了,改的部分和btle差不多,主要是ui_nrf_rx.cpp ui_nrf_rx.hpp proc_nrfrx.cpp proc_nrfrx.hpp这四个文件。
第四步:
下载安装crazyflie-clients-python,它会给你安装一个小飞机地面站,可以用crazyradio来和crazyflie双向通信,观察数传信息,如果有操纵杆也可以控制飞机。
1
2
3
4
5
6
7
8
9
10
11git clone https://github.com/bitcraze/crazyflie-clients-python.git #如果pip3命令安装这个包的时候,提示internal之类的错误,要运行下面两行 wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate sudo python3 get-pip.py #然后再次用pip3安装 pip3 install -e . --user #装完就能运行了,记得加sudo,否则无法访问crazyradio sudo ./cfclient
另外,如果想深入研究这个地面站如何调用nrf24l01来发数据,建议看一下
https://github.com/bitcraze/crazyflie-lib-python
这里的代码会生成cflib,在装crazyflie-clients-python的时候会自动安装编译好的版本。所以这个crazyflie-lib-python仅仅供观察代码,不要重复安装,如果想要自己编译这个包来安装,需要在安装crazyflie-clients-python之前来做,步骤也不太一样。
视频:https://www.bilibili.com/video/BV1vT4y1G7S7
我这里暂时没找到游戏操纵杆,所以今天演示的只是数传数据,控制使用的是Iphone经过蓝牙连接手机,而不是usb操纵杆。
最后
以上就是美好眼神最近收集整理的关于Portapack应用开发教程(七)nrf24l01解调的全部内容,更多相关Portapack应用开发教程(七)nrf24l01解调内容请搜索靠谱客的其他文章。
发表评论 取消回复