0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何使用RUI3制作一款用于电脑的多功能LoRa®适配器

瑞科慧联(RAK) 2022-11-29 14:33 次阅读

LoRa®和 LoRaWAN®已经成为了物联网世界的重要技术,也向人们提供了诸多易于使用的远程通信解决方案。在这过程中电脑设备却被忽略了,我们会发现带有 LoRa®模块的笔记本电脑很少见。

现在这种局面陆续得到了改善,在一些解决方案中,已经开始出现用于笔记本电脑的 LoRa®模块了。最近笔者利用瑞科慧联的低代码开发平台 RUI3 制作了一个 LoRa®USB 适配器,它可以直接连接到笔记本电脑或树莓派上。大多数时候,这个适配器可以作为收发器用于家居场景种;但它也作为一个方便测试的平台,比如:远程用笔记本电脑发送命令、记录结果等等。

poYBAGOFeB-AKC3yAABUGPoWbhU732.png

使用瑞科慧联的模块化硬件开发平台 WisBlock,让这样的应用开发变得更加简单。笔者通过 WisBlock 制作了两种适配器,一种是使用计算机上的自定义软件来管理 LoRa®模块的 AT 固件,另一种是直接在LoRa®模块上完成大部分工作。在这两种适配器中,电脑都是作为终端来使用。今天要介绍的是后一种适配器,主要就是使用 RUI3 为 LoRa®通信模块 RAK4631-R 制作一个简单的自定义固件。

一、前期准备

  • 硬件

1、选择 RAK4631-R(不同国家或地区对应频率的频段不同)。

poYBAGOFeB-Aa7o2AALW9vCoSKQ801.png(注意,这里我们也可以使用另一款通信模块 RAK3172,因为他们均支持 RUI3 编译,只要有自己所需要的功能就行。因为 RAK3172 不支持蓝牙和硬件加密,但该项目需要加密 LoRa® 数据包,而且将 AES128 添加到代码中也超出了本文的范围,所以这里我们选择了 RAK4631-R。)

2、底板:本例中,我们选择了 RAK19003,它具有最小的封装尺寸 30 mm x 35 mm。

3、USB 电缆(适用于 RAK19003 的 USBType-C)。

  • 软件

1、Arduino IDE。

2、终端应用程序,例如笔者最喜欢的 CoolTerm。当然 Arduino IDE 的串行终端,也能完成开发。

  • 工作模式

LoRa®适配器基本上需要两种工作模式:传输模式和设置模式。而 AT固件本质上是单模模式的,即它们总是处于设置模式。在设置模式中,甚至发送和接收都是命令。与此相反,默认的传输模式充当 LoRa®模块和 USB端口之间的桥梁:“无论一端输入任何内容,都将从另一端输出”。只有当用户发出特殊字符串时,适配器才会在传输和设置模式之间切换。 笔者见过一些 LoRa®模块为此提供一两个引脚来实现这一点,可以设置引脚高低电平从硬件上切换这两种模式,但这样的操作对电脑来说是不可能的。因此,用户可以使用不太可能出现的特殊字符串去切换这两种模式。然而在调制解调器时代,“$$$”经常作为特殊的字符串去使用,所以我们也可以使用该字符串实现。

二、工作流程

在常规的 LoRa®应用程序中,工作流程通常如下:

  • 初始化串口
  • 设置 Wire,然后设置 LoRa®模块(引脚分配等)
  • 设置 LoRa®配置(SF、BW、频率等)

本文使用到 RUI3,因此可直接去掉第二点,因为 API 已经配置完成、电池也配置好了。在RUI的 API中,LoRaWAN®是提供了LoRa选项区域帮助用户配置 LoRa®。并且LoRa®模块在 RAK4631-R 中是预先连通的,所以只需调用 LoRaWAN®的几行 API 设置所需的配置,就可以检查结果:

bool rslt = api.lorawan.nwm.set(0); if (!rslt) { // Do something } rslt = api.lorawan.pfreq.set(myFreq); if (!rslt) { // Do something } rslt = api.lorawan.psf.set(sf); if (!rslt) { // Do something } rslt = api.lorawan.pbw.set(bw); if (!rslt) { // Do something } // etc etc etc...

通过检查,已经设置完成了,结果与 API设定的配置是一致的。

然后设置 LoRa®回调:接收和传输。这里让用户能够以异步方式将“管理这些事件的代码”单独管理运行,而不是在主 loop() 代码中循环运行。

最后一行是为了将 LoRa®模块设置为了永久监听模式。

api.lorawan.registerPRecvCallback(recv_cb); api.lorawan.registerPSendCallback(send_cb); rslt = api.lorawan.precv(65534);

最后,就可以在 setup()中完成自己的需求了。例如:让 OLED 检查状态,或设置 LED的状态(威廉希尔官方网站 板上有 2 个可用,1 个绿色和 1 个蓝色)等。到这一步一切都准备好了,一起来看看接下来会发生什么?

三、loop()

在 loop() 中,循环检查串行端口是否有字符传入,并对其进行相应的操作。稍后我会详细介绍这一点。接着还需要检查 LoRa®模块,如果有接收到数据包,则将接收数据包中的内容打印到串口上。这是两个部分之间的桥梁。在其他框架中,这通常与串口相同。接着 LoRa®模块循环监听,如果有内容,直接读取。这个功能 RUI3中并不包含,需要在上面声明的 void recv_cb(rui_lora_p2p_recv_t data) 函数中自己实现并进行,在将LoRa®模块接收的原始数据发送到 Serial 之前,可以在这个函数中决定如何处理原始数据。例如:如果需要 JSON 数据,可以将其解析之后在打印到串口。同样,如果数据是加密的,或者希望它是加密的,就可以在进一步处理之前在那进行解密。回调函数代码如下所示:

void recv_cb(rui_lora_p2p_recv_t data) { uint16_t ln = data.BufferSize; char plainText[ln + 1] = {0}; char buff[92]; sprintf(buff, "Incoming message, length: %d, RSSI: %d, SNR: %d", data.BufferSize, data.Rssi, data.Snr); Serial.println(buff); if (needAES) { // Do we need to decrypt the data? int rslt = aes.Process((char*)data.Buffer, ln, myIV, myPWD, 16, plainText, aes.decryptFlag, aes.ecbMode); if (rslt < 0) { Serial.printf("Error %d in Process ECB Decrypt\n", rslt); return; } } else { // No? Just copy the data memcpy(plainText, data.Buffer, ln); } // The easiest way to know whether the data is a JSON packet is to try and decode it :-) StaticJsonDocument<200> doc; DeserializationError error = deserializeJson(doc, plainText); if (!error) { JsonObject root = doc.as(); // using C++11 syntax (preferred): for (JsonPair kv : root) { sprintf(buff, " * %s: %s", kv.key().c_str(), kv.value().as()); Serial.println(buff); } return; // End for JSON messages } // There was an error, so this is not a JSON packet – not well-formed anyway. // Print it as a plain message Serial.println("Message:"); Serial.println(plainText); }

四、Tx(发送)

发送同样也有一个回调函数,当数据发送完成时可调用。用户也可以在那里添加东西,但它在正常使用中基本上是为了确保LoRa®模块返回到监听模式中:

void send_cb(void) { // TX callback Serial.println("Tx done!"); isSending = false; // Flag used to determine whether we're still sending something or we're free to send. api.lorawan.precv(65534); }

该回调函数需要快速的执行并使 Lora®模块返回到监听模式,不需要在其中加入长延时等待。

五、设置模式

当用户发送 $$$(后缀为 \n)时,代码会切换到设置模式。这部分稍微复杂一些,发送命令这一段会重复被使用,所以为了使用方便,大部分都是复制粘贴后,对该段进行更改其函数名,并为每个命令添加合适的代码。因此我们需要一个统一的命令结构,如下所示:

int cmdCount = 0; struct myCommand { void (*ptr)(char *); // Function pointer char name[12]; char help[48]; };

(cmdCount 马上就会派上用场)。命令的结构由指针函数、函数名和命令描述三部分组成。

下图是声明了一个命令数组:

myCommand cmds[] = { {handleHelp, "help", "Shows this help."}, {handleP2P, "p2p", "Shows the P2P settings."}, {handleFreq, "fq", "Gets/sets the working frequency."}, {handleBW, "bw", "Gets/sets the working bandwidth."}, {handleSF, "sf", "Gets/sets the working spreading factor."}, {handleCR, "cr", "Gets/sets the working coding rate."}, {handleTX, "tx", "Gets/sets the working TX power."}, {handleAES, "aes", "Gets/sets AES encryption status."}, {handlePassword, "pwd", "Gets/sets AES password."}, {handleIV, "iv", "Gets/sets AES IV."}, {handleJSON, "json", "Gets/sets JSON sending status."}, };

到目前为止一切都顺利。所以在 setup() 函数启动时,会计算可用命令的数量,以便知道我们有多少个命令。cmdCount = sizeof (cmds)/ sizeof (myCommand):这在 evalCmd函数中用于遍历命令,cmdCount即为最终统计到的命令个数。

void evalCmd(char *str, string fullString) { uint8_t ix, iy = strlen(str); for (ix = 0; ix < iy; ix++) { char c = str[ix]; // lowercase the keyword if (c >= 'A' && c <= 'Z') str[ix] = c + 32; } Serial.print("Evaluating: `"); Serial.print(fullString.c_str()); Serial.println("`"); for (int i = 0; i < cmdCount; i++) { if (strcmp(str, cmds[i].name) == 0) { // call the function cmds[i].ptr((char*)fullString.c_str()); return; } } }

在此之后,添加命令和处理它们的调用就非常容易了。让我们来看看 handleHelp (char*)命令:

void handleHelp(char *param) { Serial.printf("Available commands: %d\n", cmdCount); for (int i = 0; i < cmdCount; i++) { sprintf(msg, " . %s: %s", cmds[i].name, cmds[i].help); Serial.println(msg); } }

char *param 参数可能需要也可能不需要,因此默认发送,每个命令都可以自由使用或者直接忽略它。例如:handleFreq() 命令便要使用该参数:

void handleFreq(char *param) { if (strcmp("fq", param) == 0) { // no parameters sprintf(msg, "P2P frequency: %.3f MHz\n", (myFreq / 1e6)); Serial.print(msg); sprintf(msg, "Fq: %.3f MHz\n", (myFreq / 1e6)); displayScroll(msg); return; } else { // fq xxx.xxx set frequency float value = atof(param + 2); if (value < 150.0 || value > 960.0) { // sx1262 freq range 150MHz to 960MHz // Your chip might not support all... sprintf(msg, "Invalid frequency value: %.3f\n", value); Serial.print(msg); return; } myFreq = value * 1e6; api.lorawan.precv(0); // turn off reception while we're doing setup sprintf(msg, "Set P2P frequency to %3.3f: %s MHz\n", (myFreq / 1e6), api.lorawan.pfreq.set(myFreq) ? "Success" : "Fail"); Serial.print(msg); api.lorawan.precv(65534); sprintf(msg, "New freq: %.3f", value); displayScroll(msg); return; } }

poYBAGOFeB-AEMkXAAFueGbNSLk032.png

一切操作之后有了现在的结果,编码历时几个小时,就得到了一个功能齐全的 LoRa®USB适配器。但实际上没有用这么多时间,因为笔者重用了以前项目中的 Commands.h 代码,并且暂时跳过 AES 加密部分,把它留在示例项目中是因为它相对比较复杂,且通常不是简单项目的一部分。通常可以在项目正常运行后再添加 AES,这样就不必担心其他东西会受影响。但是,就像 Commands.h 一样,笔者已经从其他项目准备好 AES 文件,所以对它的实现也只是复制粘贴工作。

pYYBAGOFeB-ARMFAAACe1v8dqBU954.png

六、扩展

功能蔓延(feature creep)一直都是困扰开发人员的问题,但现在我们暂时可以先忽略这一点。一起来看看这个项目可以有哪些扩展:

1、OLED显示屏

由于引脚配置,显示屏要在底板背面添加,但添加起来也是很方便。学习一些如何关闭屏幕的编程代码,可以帮助节省能源和保护屏幕;

2、RTC实时时钟

可以在 JSON 数据包或类似 Cayenne LPP 的格式中为数据包添加时间戳;

3、GNSS模块

用户可以将 GPS 坐标添加到数据包中,而且如果已经在家中设置了收发器的坐标,还可以使用它们的自动计算距离(Haversine 公式)的功能。

4、固件的BLE UART路由

添加这个功能很简单。一旦设置了 BLE,代码就与串行代码几乎相同了。这样操作之后,它就不仅仅是一个用于电脑的 USB LoRa®适配器了,加上电池它可以成为手机无线 LoRa®适配器。

以上这些,这个使用 RUI3 制作的项目都能实现、也都可以拥有这些功能。如果你们感兴趣,也可以自己动手试试!

pYYBAGOFeB-Ae_OBAACtuD2MIT0208.png

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 物联网
    +关注

    关注

    2909

    文章

    44625

    浏览量

    373228
  • LoRa
    +关注

    关注

    349

    文章

    1689

    浏览量

    231938
收藏 人收藏

    评论

    相关推荐

    电脑红外适配器有哪些

    电脑红外适配器种利用红外线技术实现电脑与其他电子设备之间进行数据交换和传输的设备。随着科技的不断发展,电脑红外
    的头像 发表于 10-14 10:56 279次阅读

    外接电源适配器的工作原理是什么

    外接电源适配器种将交流电(AC)转换为直流电(DC)的设备,广泛应用于各种电子设备中,如笔记本电脑、手机、平板电脑等。它通常由
    的头像 发表于 10-12 09:23 476次阅读

    电源适配器空载电压高怎么回事

    )的设备,广泛应用于各种电子设备中,如笔记本电脑、手机、平板电脑等。电源适配器的主要组成部分包括: 输入滤波器:用于滤除输入交流电中的高频噪
    的头像 发表于 09-27 15:32 756次阅读

    电源适配器电流大小对电器影响

    电源适配器电流大小对电器的影响是个复杂而重要的问题。 、电源适配器的工作原理 电源适配器
    的头像 发表于 08-02 09:40 3158次阅读

    适配器的电流大于原适配器可以吗

    适配器的电流大于原适配器是否可行,这是个涉及到电子设备电源适配器使用和安全性的问题。 适配器
    的头像 发表于 08-02 09:38 1579次阅读

    usb-c多口适配器

    面临的大问题。在这样的背景下,Type-C多口适配器应运而生,以其高效、便捷的特点,成为了现代充电与连接的新选择 概述 Type-C多口适配器,顾名思义,是指具有多个Type-C充电接口的
    的头像 发表于 07-01 09:13 375次阅读
    usb-c多口<b class='flag-5'>适配器</b>

    电源适配器有哪些作用

    电源适配器的作用有哪些 电源适配器,又称电源转换器或电源供应器,是种将电源输入转换为特定电压和电流输出的电子设备。它广泛应用于各种电子设备中,如手机、笔记本
    的头像 发表于 06-10 17:07 7637次阅读

    Type-C多功能适配器机多用的智能连接新选择

    Type-C多功能适配器种集多种功能身的电子设备,它采用了Type-C接口,可以连接多种不同的设备,并实现多种
    的头像 发表于 04-28 22:26 760次阅读
    Type-C<b class='flag-5'>多功能</b><b class='flag-5'>适配器</b>:<b class='flag-5'>一</b>机多用的智能连接新选择

    开关电源适配器和充电器的区别 开关电源适配器怎么用

    开关电源适配器和充电器是电子设备中常见的电源转换装置,它们在功能和用途上有些区别。下面将详细说明这两种设备的区别,并介绍开关电源适配器的使用。
    的头像 发表于 02-14 17:51 7822次阅读

    适配器的作用是什么?有哪些重要功能?

    适配器种软硬件组件,用于在不兼容的系统或设备之间建立连接,以实现数据或信号的传输和交换。适配器的作用是充当中间件,通过转换信号、协议、接口等,使得不同系统、设备之间能够互相通信和相
    的头像 发表于 02-03 09:54 5418次阅读

    什么是蓝牙适配器?它有哪些性能特点?

    什么是蓝牙适配器?它有哪些性能特点? 蓝牙适配器用于连接电脑、手机、平板电脑等设备的蓝牙设备。它的主要
    的头像 发表于 02-02 14:56 3032次阅读

    蓝牙适配器常见问题及解决

    蓝牙适配器种设备,它用于在不同的电子设备之间建立无线蓝牙连接。它可以将没有内置蓝牙功能的设备,如电脑、笔记本
    的头像 发表于 02-01 18:17 3570次阅读

    什么是电源适配器的转换效率?转换效率对电源适配器有何影响?

    什么是电源适配器的转换效率?转换效率对电源适配器有何影响?如何提高电源适配器的转换效率? 电源适配器的转换效率是指它所能输出的电能与输入的电能之间的转换效率。换句话说,转换效率越高,
    的头像 发表于 01-30 16:46 1432次阅读

    什么是电源适配器的浪涌电流?浪涌电流对电源适配器有何影响?

    什么是电源适配器的浪涌电流?浪涌电流对电源适配器有何影响? 电源适配器的浪涌电流是指在电源启动或断开时,由于电源电压的突变引起的瞬态电流。这种瞬态电流瞬间达到峰值,然后逐渐衰减到稳定状态。 浪涌电流
    的头像 发表于 01-30 16:38 1628次阅读

    什么是电源适配器?是干什么用的?电源适配器可以通用吗?

    什么是电源适配器?是干什么用的?电源适配器可以通用吗? 电源适配器种电子设备,用于将电源电压转换为适合其他设备使用的电压。它通常由
    的头像 发表于 12-29 11:28 3697次阅读