03 LoRa
作者:王世豪 | 最后修改:2026-04-13
一、lora 概述
LuatOS 的 lora2 核心库,支持多设备挂载,提供完整的 lora 通信功能。仅支持 LLCC68、SX1268 两款主流 lora 芯片。
1.1 lora 通信原理
LoRa(Long Range)是 semtech 公司创建的低功耗、远距离、无线、广域网的标准。
LoRa 模块采用扩频通信技术,其核心原理是将信号的频谱扩展到一个很宽的频率范围内,从而提高信号的抗干扰能力和安全性。LoRa 模块主要采用的是基于线性调频(LFM)的扩频技术,即 Chirp Spread Spectrum(CSS)。
LoRa 模块在发送数据时,会将数据信号与一个高速的伪随机码(PN 序列)进行模二加运算,生成扩频信号。接收端使用相同的 PN 序列对接收到的扩频信号进行解扩,恢复出原始数据信号。
如果您需要权威参考,建议查阅 Semtech 官方(https://www.semtech.cn/lora)发布的 LoRa 技术文档或 LLCC68/SX1268 系列芯片数据手册。
1.2 工作模式
-
发送模式:用于无线发送数据,发送完成触发 "tx_done"事件。
-
接收模式:监听并接收无线信号,接收成功触发 "rx_done"事件 ,超时触发 "rx_timeout"事件。
-
待机模式:准备就绪状态,介于工作和睡眠之间,作为发送/接收操作前的过渡状态。
1.3 注意事项
由于 LoRa 设备的半双工特性,当设备 A 处于发送模式时,若设备 B 同时发送数据,设备 A 将无法接收,导致数据丢失。
二、演示功能概述
本文使用 Air8000 核心板 +lora 模块测试 lora 的数据发送和接收功能。
注意:需要两个 Air8000 核心板 + 两个 lora 模块,才能进行有效的通信测试。
三、准备硬件环境
3.1 Air8000 核心板
使用 Air8000 核心板,如下图所示:

此核心板的详细使用说明参考:硬件手册和证书 - product@air8000 - 合宙模组资料中心中的 Core_Air8000 核心板使用说明.pdf。
3.2 lora 模块

本文档演示使用的 LoRa 模块是 南京二五五物联科技 的 255MN-L03 模块,是一款基于射频芯片LLCC68设计的无线收发模组。该模组具有+22dBm的可调输出功率,最低4.2mA的接收电流,传输距离远,可靠性高,功耗低。模块提供了SPI通用接口,采用半双工通信方式。
二五五物联官网:https://255mesh.com/
资料下载:255MN-L03 模块资料
3.3 PC 电脑
WINDOWS 系统,其他暂无特别要求;
3.4 数据通信线
USB 数据线(其一端为 Type-C 接口,用于连接 Air8000 核心板)。
Air8000 核心板和数据线的硬件接线方式为
-
Air8000 核心板通过 TYPE-C USB 口连接 TYPE-C USB 数据线,数据线的另外一端连接电脑的 USB 口;
-
核心板正面的 供电/充电 拨动开关 拨到供电一端;
-
核心板背面的 USB ON/USB OFF 拨动开关 拨到 USB ON 一端;
3.5 Air8000 核心板和 lora 模块硬件接线

| Air8000核心板 | lora模块 |
| VDD_EXT | VCC |
| GND | GND |
| SPI1_CLK | SCK |
| SPI1_CS | CSS |
| SPI1_MISO | MISO |
| SPI1_MOSI | MOSI |
| GPIO1 | RST |
| GPIO16 | BUSY |
| GPIO17 | DIO1 |
硬件连接注意事项:
1、电源要求:
- 不同 lora 模块的供电电压范围可能存在差异,请根据具体模块规格确定合适的电源电压
2、控制信号连接:
-
RST 引脚:连接至核心板的 GPIO1,用于 lora 模块复位控制
-
BUSY 引脚:连接至核心板的 GPIO16,用于 lora 模块忙状态指示
-
DIO1 引脚:连接至核心板的 GPIO17,用于 lora 模块中断信号接收
3、连接确认:
-
上述 GPIO 引脚分配已在测试中验证可用
-
如有变更需求,需同步修改软件配置中的引脚定义
四、准备软件环境
4.1 软件环境
1. 烧录工具:Luatools 烧录调试工具;
2. 内核固件:本demo开发测试时使用的固件为Air8000 V2018 版本固件,本demo对固件版本没有什么特殊要求,所以你如果要测试本demo时,可以直接使用最新版本的内核固件;如果发现最新版本的内核固件测试有问题,可以使用我们开发本demo时使用的内核固件版本来对比测试;
3. 脚本文件:https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/demo/lora2
4. LuatOS 运行所需要的 lib 文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件。
准备好软件环境之后,接下来查看如何烧录项目文件到 Air8000 核心板中,将本篇文章中演示使用的项目文件烧录到 Air8000 核心板中。
4.2 API 介绍
lora2 核心库:https://docs.openluat.com/osapi/core/lora2/
五、程序结构
lora2/
├── main.lua
├── lora2_main.lua
├── lora2_receiver.lua
├── lora2_sender.lua
├── uart_app.lua
└── readme.md
5.1 文件说明
-
main.lua:主程序入口文件。 -
lora2_main.lua:LoRa 设备初始化和核心任务管理。 -
lora2_receiver.lua:接收数据处理和转发。 -
lora2_sender.lua:数据发送队列和传输管理。 -
uart_app.lua:串口应用模块。
六、代码详解
6.1 main.lua
主程序文件 main.lua 是整个项目的入口点。它负责初始化系统环境。
6.2 lora2_main.lua
lora2_main.lua 是 LoRa 主应用功能模块,负责 LoRa 设备的初始化、配置和核心任务管理。主要功能包括:
-
设备初始化与配置:通过 lora2_init()函数初始化 SPI 接口和 LoRa 设备,设置工作频率为 433MHz
-
参数配置:配置发送参数(功率、带宽、数据率等)和接收参数
-
事件回调处理:定义 callback()函数处理各种 LoRa 事件(tx_done、rx_done、rx_timeout、rx_error)
-
主任务管理:lora2_main_task_func()实现设备初始化、回调注册、数据收发管理的主循环
-
消息传递:通过 sys.sendMsg()实现与其他模块的通信,将设备就绪事件和操作结果通知相关模块
-- 加载依赖模块
local lora2_sender = require "lora2_sender"
local lora2_receiver = require "lora2_receiver"
local TASK_NAME = "lora2_task"
local spi_id = 1 -- SPI接口ID
local pin_cs = 12 -- 片选引脚
local pin_reset = 1 -- 复位控制引脚
local pin_busy = 16 -- 忙状态指示引脚
local pin_dio1 = 17 -- DIO1引脚
local RECEIVE_TIMEOUT = 3000 -- 接收超时时间3秒
--[[
event值有:
tx_done -- 发送完成:数据已成功发送
rx_done -- 接收完成:成功接收到数据
rx_timeout -- 接收超时:在指定时间内未收到数据
rx_error -- 接收错误:接收过程中发生错误
__]]
function callback(lora_device, event, data, size)
if event == "tx_done" then
sys.sendMsg(TASK_NAME, "LORA_EVENT", "tx_done")
elseif event == "rx_done" then
sys.sendMsg(TASK_NAME, "LORA_EVENT", "rx_done", data, size)
elseif event == "rx_timeout" then
sys.sendMsg(TASK_NAME, "LORA_EVENT", "rx_timeout")
elseif event == "rx_error" then
sys.sendMsg(TASK_NAME, "LORA_EVENT", "rx_error")
else
log.warn("未知事件类型:", event)
end
end
local function lora2_init()
-- 初始化SPI
spi_lora = spi_lora or spi.deviceSetup(spi_id,pin_cs,0,0,8,10*1000*1000,spi.MSB,1,0)
if not spi_lora then
log.error("spi_lora init failed")
return false
end
-- 初始化LORA2设备
-- 当前支持型号:llcc68, sx1262
lora_device = lora_device or lora2.init("llcc68",{res = pin_reset,busy = pin_busy,dio1 = pin_dio1},spi_lora)
if not lora_device then
log.error("lora_device init failed")
return false
end
log.info("lora_device",lora_device)
-- 设置频道频率为433MHz
lora_device:set_channel(433000000)
-- 配置 lora 设备的发送参数
lora_device:set_txconfig({
mode=1,
power=22,
fdev=0,
bandwidth=0,
datarate=9,
coderate=4,
preambleLen=8,
fixLen=false,
crcOn=true,
freqHopOn=0,
hopPeriod=0,
iqInverted=false
})
-- 配置 lora 设备的接收参数
lora_device:set_rxconfig({
mode=1,
bandwidth=0,
datarate=9,
coderate=4,
bandwidthAfc=0,
preambleLen=8,
symbTimeout=0,
fixLen=false,
payloadLen=0,
crcOn=true,
freqHopOn=0,
hopPeriod=0,
iqInverted=false,
rxContinuous=false
})
return true
end
local function lora2_main_task_func()
local result,msg
while true do
result = lora2_init()
if not result then
log.info("lora2_init error")
goto EXCEPTION_PROC
end
-- 注册回调
lora_device:on(callback)
-- 默认初始化后启动接收
lora_device:recv(RECEIVE_TIMEOUT)
--- 发送设备就绪事件,将lora_device传递给sender模块
sys.sendMsg(lora2_sender.TASK_NAME, "LORA_EVENT", "DEVICE_READY", lora_device)
while true do
msg = sys.waitMsg(TASK_NAME, "LORA_EVENT")
if msg[2]== "tx_done" then
log.info("lora2_main", "发送完成")
-- 通知sender模块发送完成
sys.sendMsg(lora2_sender.TASK_NAME, "LORA_EVENT", "TX_DONE")
-- 发送完成后启动接收
lora_device:recv(RECEIVE_TIMEOUT)
elseif msg[2]== "rx_done" then
log.info("lora2_main", "接收完成", "数据长度:", msg[4])
-- 交由receiver模块处理数据
lora2_receiver.proc(msg[3], msg[4], lora_device)
-- 处理完成后启动接收
lora_device:recv(RECEIVE_TIMEOUT)
elseif msg[2]== "rx_timeout" then
log.info("lora2_main", "接收超时")
-- 接收超时后继续接收
lora_device:recv(RECEIVE_TIMEOUT)
-- 接收过程中发生错误
elseif msg[2]== "rx_error" then
log.info("lora2_main", "接收错误")
-- 接收错误后继续接收
lora_device:recv(RECEIVE_TIMEOUT)
end
end
-- 出现异常
::EXCEPTION_PROC::
-- 清空此task绑定的消息队列中的未处理的消息
sys.cleanMsg(TASK_NAME)
sys.wait(5000)
end
end
sys.taskInitEx(lora2_main_task_func, TASK_NAME)
6.3 lora2_receiver.lua
lora2_receiver.lua 是 LoRa 数据接收应用功能模块,专注于处理接收到的 LoRa 数据。主要功能包括:
-
数据接收处理:提供 proc()函数作为对外接口,处理接收到的数据和大小信息
-
日志记录:记录接收到的数据长度和原始数据
-
数据转发:通过 sys.publish("LORA_RECV_DATA", data)将接收到的数据发布给其他订阅该消息的模块(如 uart_app.lua)
local lora2_receiver = {}
-- 处理接收到的lora数据
function lora2_receiver.proc(data, size)
log.info("lora2_receiver", "收到数据", size, data)
-- 发布数据给其他模块
sys.publish("LORA_RECV_DATA", data)
end
return lora2_receiver
6.4 lora2_sender.lua
lora2_sender.lua 是 LoRa 数据发送应用功能模块,负责管理数据发送队列和发送任务。主要功能包括:
-
发送队列管理:维护 send_queue 数据结构,存储待发送的数据和回调信息
-
消息订阅:订阅"SEND_DATA_REQ"消息,接收其他模块的数据发送请求
-
任务处理:通过 lora2_sender_task_func()任务函数处理设备就绪、发送请求和发送完成等事件
-
数据发送:send_item_func()按顺序处理队列中的数据项,调用 LoRa 设备发送功能
-
回调通知:数据发送完成后通过回调函数通知发送方
local lora2_sender = {}
--[[
数据发送队列,数据结构为:
{
[1] = {data="data1", cb={func=callback_function1, para=callback_para1}},
[2] = {data="data2", cb={func=callback_function2, para=callback_para2}},
}
data: 要发送的数据,string类型,必须存在;
cb.func: 数据发送结果的用户回调函数,可以不存在;
cb.para: 数据发送结果的用户回调函数参数,可以不存在;
]]
local send_queue = {}
lora2_sender.TASK_NAME = "lora2_sender"
-- "SEND_DATA_REQ"消息的处理函数
local function send_data_req_proc_func(tag, data, cb)
-- 将数据插入到发送队列send_queue中
table.insert(send_queue, {data=data, cb=cb})
-- 发送消息通知 lora sender task,有新数据等待发送
sys.sendMsg(lora2_sender.TASK_NAME, "LORA_EVENT", "SEND_REQ")
log.info("队列", #send_queue)
end
-- 按照顺序发送send_queue中的数据
-- 发送请求提交后,返回当前正在发送的数据项,等待发送完成事件
-- 如果设备未初始化,则通知回调函数发送失败,并继续处理下一条
local function send_item_func(lora_device)
local item
-- 如果发送队列中有数据等待发送
while #send_queue>0 do
-- 取出来第一条数据赋值给item
-- 同时从队列send_queue中删除这一条数据
item = table.remove(send_queue, 1)
-- 检查设备是否初始化
if not lora_device then
log.error("lora2_sender", "设备未初始化")
-- 通知回调函数发送失败
if item.cb and item.cb.func then
item.cb.func(false, item.cb.para)
end
return nil
end
-- 发送数据
lora_device:send(item.data)
-- 返回当前发送项,等待发送完成"TX_DONE"事件
return item
end
return nil
end
-- 处理发送结果的回调函数
local function send_item_cbfunc(item, result)
if item then
-- 如果当前发送的数据有用户回调函数,则执行用户回调函数
if item.cb and item.cb.func then
item.cb.func(result, item.cb.para)
end
end
end
-- lora client sender的任务处理函数
local function lora2_sender_task_func()
local lora_device
local send_item = nil
local msg
while true do
-- 等待"LORA_EVENT"消息
msg = sys.waitMsg(lora2_sender.TASK_NAME, "LORA_EVENT")
log.info("lora2_sender", "收到消息", msg[2])
-- 设备就绪事件
if msg[2] == "DEVICE_READY" then
lora_device = msg[3]
-- 如果当前没有正在发送的数据,则开始发送队列中的数据
if lora_device and not send_item then
send_item = send_item_func(lora_device)
end
-- 发送数据请求
elseif msg[2] == "SEND_REQ" then
-- 如果当前没有正在发送的数据,则开始发送队列中的数据
if lora_device and not send_item then
send_item = send_item_func(lora_device)
end
-- 发送完成事件
elseif msg[2] == "TX_DONE" then
-- 通知回调函数发送成功
send_item_cbfunc(send_item, true)
-- 清空当前发送项
send_item = nil
-- 继续处理队列中的下一条数据
send_item = send_item_func(lora_device)
end
end
end
-- 订阅"SEND_DATA_REQ"消息;
-- 其他应用模块如果需要发送数据,直接sys.publish这个消息即可,将需要发送的数据以及回调函数和回调参数一起publish出去;
-- 本demo项目中uart_app.lua中publish了这个消息;
sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)
--创建并且启动一个task
--运行这个task的处理函数lora2_sender_task_func
sysplus.taskInitEx(lora2_sender_task_func, lora2_sender.TASK_NAME)
return lora2_sender
6.5 uart_app.lua
uart_app.lua 是 UART 应用功能模块,实现串口通信和数据转发功能。主要功能包括:
1、串口初始化:配置 UART1,波特率 115200,数据位 8,停止位 1,无奇偶校验位
2、数据接收处理:实现 read()中断处理函数,非阻塞读取串口数据
3、数据拼接:通过 concat_timeout_func()和 50 毫秒定时器实现数据拼接,避免大数据包被拆分成多个小包
4、数据转发:
-
串口数据通过 sys.publish("SEND_DATA_REQ", "uart", read_buf)转发给 LoRa 发送模块
-
接收 LoRa 数据通过 uart.write(UART_ID, data.."\r\n")发送到 PC 端串口工具
5、消息订阅:订阅"LORA_RECV_DATA"消息,处理接收到的 LoRa 数据
- 该模块实现了 PC 端与 LoRa 设备之间的数据双向传输。
-- 使用UART1
local UART_ID = 1
-- 串口接收数据缓冲区
local read_buf = ""
-- 末尾增加回车换行两个字符,通过uart发送出去,方便在PC端换行显示查看
local function recv_data_from_lora_proc(data)
uart.write(UART_ID, data.."\r\n")
end
local function concat_timeout_func()
-- 如果存在尚未处理的串口缓冲区数据;
-- 将数据通过publish通知其他应用功能模块处理;
-- 然后清空本文件的串口缓冲区数据
if read_buf:len() > 0 then
sys.publish("SEND_DATA_REQ", "uart", read_buf)
read_buf = ""
end
end
-- UART1的数据接收中断处理函数,UART1接收到数据时,会执行此函数
local function read()
local s
while true do
-- 非阻塞读取UART1接收到的数据,最长读取1024字节
s = uart.read(UART_ID, 1024)
-- 如果从串口没有读到数据
if not s or s:len() == 0 then
-- 启动50毫秒的定时器,如果50毫秒内没收到新的数据,则处理当前收到的所有数据
-- 这样处理是为了防止将一大包数据拆分成多个小包来处理
-- 例如pc端串口工具下发1100字节的数据,可能会产生将近20次的中断进入到read函数,才能读取完整
-- 此处的50毫秒可以根据自己项目的需求做适当修改,在满足整包拼接完整的前提下,时间越短,处理越及时
sys.timerStart(concat_timeout_func, 50)
-- 跳出循环,退出本函数
break
end
log.info("uart_app.read len", s:len())
-- 将本次从串口读到的数据拼接到串口缓冲区read_buf中
read_buf = read_buf..s
end
end
-- 初始化UART1,波特率115200,数据位8,停止位1
uart.setup(UART_ID, 115200, 8, 1)
-- 注册UART1的数据接收中断处理函数,UART1接收到数据时,会执行read函数
uart.on(UART_ID, "receive", read)
-- 订阅"LORA_RECV_DATA"消息的处理函数recv_data_from_lora_proc
-- 收到"LORA_RECV_DATA"消息后,会执行函数recv_data_from_lora_proc
sys.subscribe("LORA_RECV_DATA", recv_data_from_lora_proc)
七、演示功能
下面将进行 Air8000 核心板搭配 lora 模块发送和接收数据的演示,Air8000 核心板 +lora 模块,下面统称为 lora 设备。
上电后,当初始化 LoRa 设备成功后,系统会输出 "LORA*: 0C7F5478" 这样的日志信息,表明 LoRa 设备已准备就绪并分配了相应的内存地址。
初始化完成后,lora 设备 A 和 B 会进入 lora 接收状态,等待接收数据; 若接收超时,会继续等待接收, 所以在没有数据传输时,luatools 会一直打印接收超时信息。

7.1 lora 设备 A 和 B 互传数据
通过下面的图片,可以看到:
LoRa 设备 A 通过串口发送"hello, i am A!",经 LoRa 无线传输至设备 B,B 接收后通过串口显示在 PC 工具界面;
同理,设备 B 通过串口发送"hello, i am B!",经 LoRa 无线传输至设备 A,A 接收后通过串口显示在 PC 工具界面。

八、总结
至此,我们已使用 Air8000 核心板实现了基于 LoRa2 模块的无线数据收发通信功能。