☰
使用taskBus串口操作LORA设备收发消息并获取LORA波形
2026/9/28 20:10:21 网站建设 项目流程

LORA是一种低功耗物联网协议,最近5年越来越得到业界的关注。正好最近在给taskBus添加串口、原始TCP\UDP三类接口,以便更好的和物联网设备进行M2M交互,这为LORA模块测试提供了便利。

下一篇,我们通过Octave研究数学结构,

2026年,我们根据数学模型,驱动AI实现了堪用的B210收发模块。

文章目录

  • 1. 硬件环境搭建
  • 2 在 taskBus 中操作LORA设备
  • 3. 传输速率测试
  • 4. 监测并获得波形和频谱
  • 5. 相关资料

1. 硬件环境搭建

LORA模块一般几十元到100多元不等。如果只是为了研究传输特性,比如距离、速率,则不需要购买很复杂的版本,或者中继、云服务。

笔者使用的是一款RS485的LORA模块,配合RS485转USB的连接线,构成了测试LORA的最小环境。当然,为了能够通信,一次要买至少2个。

由于是在室内临时使用,所以即使买的是支持上槽的,也没有和室外板箱结合。笔者简单的把旧诺基亚的充电器、RS485连接线连上模块,一起放在包装盒里,如下图所示:
具体接线:

  1. 旧诺基亚充电器引出+5v和GND,用万用表测一下极性,直接接到LORA模块的正负极上作为供电。供电一旦成功,指示灯就亮了。产品建议5-12V,为了室内小功率测试,5V足够了。需要注意的是,如果有条件用9V,还是更好,5V时,误码率比较高。
  2. RS485的T/R+和T/R-线连接LORA的A+,B-。这里有一个坑,笔者手头的485模块上面A+叫做T/R+,B-叫做T/R-,和公司的不一样。公司的名字也叫 A+、B-,和LORA模块一样。二者没有区别,只是叫法不同。
  3. 在windows下安装合适的RS485驱动。注意要咨询客服,RS485的芯片很多,同样外形的转接口,型号很杂,一定要安装正确。笔者询问客服后,试了4种芯片的驱动,才成功。笔者安装的是MS3020芯片的驱动。


全部连接正确后,串口调试助手登入,能够执行AT指令,就成功了。笔者测试的型号:CW-4A82

其测试状态截图(软件是老板附送的,写的很棒!):

2 在 taskBus 中操作LORA设备

taskBus的串口模块,可以直接连接键盘(console模块),我们迫不及待的在两个机器上测试消息收发了。

按照产品手册,最简便的方法是使用透明模式,连接键盘到LORA:

两台设备均能收发。

在初步测试时,感觉到延迟挺大的,应该是默认速率比较低。默认的配置:

参数单位取值
带宽KHz512
中心频率MHz433
比特率bit/s2400
串口速率bit/s9600

在测试中,LORA设备位于COM3,COM4,我们分别为两个串口连接键盘,这样构成了点到点的消息收发最简实验。

3. 传输速率测试

可以进一步开发一个发送数据包的小程序,测试LORA单路的时延和发送能力。

测试数据包采用2台设备,A设备发,B设备收。包由一个简单的C语言程序产生,具备一定的长度。通过统计每秒成功发送并接收的包数,来计算真正的通信流量。

#include<QCoreApplication>#include<QFile>#include<QVector>#include<QThread>#include<cstdio>#include<cstdlib>#include<thread>#include<atomic>#include<ctime>#include"cmdlineparser.h"#include"tb_interface.h"intmain(intargc,char*argv[]){QCoreApplicationa(argc,argv);//...ret=do_function(args);//...returnret;}intinstance=0;intsub_input=0,sub_output=0;std::atomic<int>send_count=0,lost_count=0;std::atomic<longlong>total_bytes;std::atomic<bool>recieved=true,finished=false;constintmsglen=64;voidsend_loop(){if(sub_output>0){intsleep_cnt=0;while(!finished){staticunsignedchardata[msglen]{0xc3u,0xa5u,0xe7u,0x96u};staticclock_t*clk=(clock_t*)&data[4];staticunsignedint*check=(unsignedint*)&data[msglen-4];if(recieved){recieved=false;sleep_cnt=0;*clk=clock();//Checksumunsignedintsum=0;for(inti=0;i<msglen-4;++i){if(i>=4+4)data[i]=i+8;sum+=(unsignedchar)(data[i]);intp=sum&0x80000000u?1:0;sum<<=1;sum^=p;}*check=sum;TASKBUS::push_subject(sub_output,instance,msglen,(unsignedchar*)data);++send_count;total_bytes+=msglen;}else{++sleep_cnt;QThread::msleep(10);//20 secondsif(sleep_cnt>=2000){sleep_cnt=0;++lost_count;recieved=true;}}}}}voidrecv_loop(){//Begin recievelonglongdelay=0;clock_t first_clk=-1;unsignedcharmsg[msglen]={0};intmsg_pos=0;longlongsucceed_cnt=0,failed_cnt=0;staticclock_t*clk=(clock_t*)&msg[4];staticlonglong*cnt=(longlong*)&msg[4+8];staticunsignedint*check=(unsignedint*)&msg[msglen-4];intfailed_header=0;while(false==finished){subject_package_header header;vector<unsignedchar>packagedta=pull_subject(&header);if(is_valid_header(header)==false){if(++failed_header>16)finished=true;continue;}if(is_control_subject(header)){//收到命令进程退出的广播消息,退出if(strstr(control_subject(header,packagedta).c_str(),"function=quit;")!=nullptr)finished=true;}elseif(header.subject_id==sub_input&&packagedta.size()){staticunsignedcharhead_msg[]={0xc3u,0xa5u,0xe7u,0x96u};intlen=packagedta.size();for(inti=0;i<len;++i){msg[msg_pos]=packagedta[i];switch(msg_pos){case0:case1:case2:case3:if(msg[msg_pos]==head_msg[msg_pos])++msg_pos;else{if(msg_pos)--i;msg_pos=0;}break;default:msg[msg_pos]=packagedta[i];++msg_pos;if(msg_pos==msglen){recieved=true;//Checksumunsignedintsum=0;for(intj=0;j<msglen-4;++j){sum+=(unsignedchar)(msg[j]);intp=sum&0x80000000u?1:0;sum<<=1;sum^=p;}if(*check==sum){++succeed_cnt;if(first_clk==-1)first_clk=*clk;delay+=clock()-*clk;}else{++failed_cnt;fprintf(stderr,"CHECKSUM Failed.\n");}longlongavgdelay=delay/(succeed_cnt+1e-13);doubletmCost=(*clk-first_clk)*1.0/CLOCKS_PER_SEC+1e-10;doublespeed=total_bytes*1.0/(tmCost+1e-13);fprintf(stderr,"Send:%lld, succeed:%lld, Average delay = %d clocks, total Speed = %.2lf B/s.\n",(longlong)send_count,succeed_cnt,(int)avgdelay,speed);fflush(stderr);msg_pos=0;}break;};}}}}intdo_function(constcmdlineParser&args){boolbInfo=false,finished=false;//1. parse cmdlineinstance=args.toInt("instance",0);sub_input=args.toInt("data_in",0);sub_output=args.toInt("data_out",0);if(instance<=0)return-1;srand(time(0)+instance*16);std::this_thread::sleep_for(std::chrono::milliseconds((instance*11711)%2000));//Begin send,recvstd::threadth_send(send_loop);std::threadth_recv(recv_loop);th_send.join();th_recv.join();//3.exitreturn0;}

测试表明:

  1. 默认状态下,LORA是一种半双工的IO通信。也就是说,终端在发送数据的时候,无法接收数据。同时,由于存在空闲率,如250ms,两个包之间不是完全连续的,是有空隙的,这些因素都导致实际速率远达不到2400bps, 实测约560~600bps左右。
  2. 被测试产品在默认出厂配置模式下,单个包最大64字节(512bit)。超过64字节的包会分片到下一次。从包送入串口,到从串口返回包,全路径延迟约900ms。
  3. 两台设备若同时进行双向收发,则碰撞后无法解析完整的包。这也从侧面说明了,LORA用于双终端乒乓通信并不是最佳的实践。这些设备更适合多发1收的情况。

4. 监测并获得波形和频谱

用taskBus可以直接驱动USRP B210采集433MHz的频谱。在5MHz采样率下,采用Audacity可以导入RAW格式的数据,降速设置采样率(如44100)后,可以听到每个符号的鸟叫频谱(chirp)和清晰的边界。这种鸟叫信号直接进行解调,即可获得线性的三角波。


我们用Octave直接处理上面的波形,求取角度和角度差分,而后得到频率:

clc;clear;fid=fopen("d:\\lora\\lora.wav","rb");%读取数据raw_iq_1d=fread(fid,'int16');fclose(fid);%组装为IQ复平面坐标raw_iq=raw_iq_1d(1:2:end)+1j*raw_iq_1d(2:2:end);%测相位角angles=angle(raw_iq);%求差分(瞬时频率,也就是转速)freqs=angle(raw_iq(2:end))-angle(raw_iq(1:end-1));% 2pi环绕去除freqs(freqs<=-pi)=freqs(freqs<=-pi)+2*pi;freqs(freqs>=pi)=freqs(freqs>=pi)-2*pi;% 画图plot(freqs);

前面规整的应该是同步序列,可以看到5MHz采样500KHz情况下,经过chirp线性调频扩频后的符号长度约为 (85984-65520 )/10=2046,也就是每个符号在500KHz采样率下占2048个点,符合 SF11的配置, 1个符号对应11比特,有2048种状态。

LORA的状态取决于线性调频的起点,但是由于存在收发的频率、时钟误差,在存在漂移的情况下,正确解析出符号不是简单的事情。

对于SF较小的情况,FFT的误差大一点,问题不大。对 SF10\11\12来说,如SF11,2048点FFT只要错位1个频率位置,符号就漂移了。下个月,我应该能学习完LORA的解析技术,在下一篇文章,将介绍如何还原本节的信息。

5. 相关资料

LORA的上位机COM测试用例参考

https://gitcode.com/colorEagleStdio/taskbus_course/tree/master/src/a4_lora

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询