init: Air780EPM 官方 LuatOS 项目代码基线
- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码 - 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等) - 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
--[[
|
||||
@module fota_file
|
||||
@summary 文件系统FOTA升级功能模块
|
||||
@version 1.0
|
||||
@date 2025.10.24
|
||||
@author 孟伟
|
||||
@usage
|
||||
-- 文件系统FOTA升级功能
|
||||
-- 提供从文件系统直接读取升级包进行固件升级的功能
|
||||
-- 可以使用luatools工具的烧录系统文件功能将升级包直接烧录到文件系统中,
|
||||
|
||||
本文件没有对外接口,直接在main.lua中require "fota_file"就可以加载运行;
|
||||
]]
|
||||
|
||||
local function fileUpgradeTask()
|
||||
-- 等待系统稳定后再开始升级
|
||||
sys.wait(10000)
|
||||
|
||||
log.info("FOTA_FILE", "=== 开始文件系统升级 ===")
|
||||
|
||||
-- 步骤1: 初始化FOTA流程
|
||||
log.info("FOTA_FILE", "初始化FOTA...")
|
||||
if not fota.init() then
|
||||
log.error("FOTA_FILE", "FOTA初始化失败")
|
||||
return
|
||||
end
|
||||
|
||||
-- 步骤2: 等待底层准备就绪
|
||||
log.info("FOTA_FILE", "等待底层准备...")
|
||||
while not fota.wait() do
|
||||
sys.wait(100)
|
||||
end
|
||||
log.info("FOTA_FILE", "底层准备就绪")
|
||||
|
||||
-- 步骤3: 从文件系统读取升级包并启动升级
|
||||
local filePath = "/update.bin"
|
||||
log.info("FOTA_FILE", "开始读取升级文件:", filePath)
|
||||
local result, isDone, cache = fota.file(filePath)
|
||||
log.info("FOTA_FILE", "升级文件写入flash中的fota分区结果", result, isDone, cache)
|
||||
|
||||
-- 步骤4: 结束写入fota分区
|
||||
log.info("FOTA_FILE", "结束写入fota分区...")
|
||||
local result, isDone = fota.isDone()
|
||||
log.info("FOTA_FILE", "写入fota分区状态", "结果:", result, "完成:", isDone)
|
||||
if result then
|
||||
-- 步骤5: 处理写入结果
|
||||
if isDone then
|
||||
-- 升级文件成功写入flash中的fota分区,准备重启设备;
|
||||
-- 设备重启后,在初始化阶段的运行过程中会自动应用fota分区中的数据完成升级,最终升级结果可以通过观察日志中的版本号来区分。
|
||||
log.info("FOTA_FILE", "升级成功,准备重启设备")
|
||||
-- 调用fota.finish(true)结束升级流程,参数true表示正确走完流程。
|
||||
fota.finish(true)
|
||||
-- 可选:删除升级包文件
|
||||
-- os.remove("/update.bin")
|
||||
sys.wait(2000)
|
||||
rtos.reboot()
|
||||
else
|
||||
log.error("FOTA_FILE", "升级失败")
|
||||
-- -- 调用fota.finish(false)结束升级流程,参数false表示升级流程失败。
|
||||
fota.finish(false)
|
||||
end
|
||||
else
|
||||
log.error("FOTA_FILE", "升级失败:检查写入状态失败")
|
||||
-- -- 调用fota.finish(false)结束升级流程,参数false表示升级流程失败。
|
||||
fota.finish(false)
|
||||
end
|
||||
end
|
||||
|
||||
-- 启动文件升级任务
|
||||
sys.taskInit(fileUpgradeTask)
|
||||
@@ -0,0 +1,217 @@
|
||||
--[[
|
||||
@module fota_uart
|
||||
@summary 串口FOTA升级功能模块
|
||||
@version 1.0
|
||||
@date 2025.10.24
|
||||
@author 孟伟
|
||||
@usage
|
||||
-- 串口FOTA升级功能
|
||||
-- 提供通过串口分段接收升级包数据进行固件升级的功能
|
||||
|
||||
用法:
|
||||
1. 先把脚本和固件烧录到模块里, 并确认开机
|
||||
2. 按下Power键启动串口升级模式(设备会等待升级数据)
|
||||
3. 在电脑端操作:进入命令行程序,执行 `python main.py` 进行升级,需要保证升级文件名字为 `fota_uart.bin`,并且和 `main.py` 在同一目录下
|
||||
注意:运行`python main.py`需要确保电脑安装了Python环境。
|
||||
4. 观察luatools的输出和main.py的输出
|
||||
5. 模块接收正确的升级数据后,会提示1秒后重启
|
||||
6. 本demo自带的脚本升级包,仅加了一条打印和修改版本号
|
||||
|
||||
串口通讯过程说明
|
||||
串口升级采用简单的文本协议进行握手和数据传输控制:
|
||||
协议流程:
|
||||
1. 上位机发送:#FOTA\n
|
||||
2. 设备回复:#FOTA RDY\n
|
||||
3. 上位机发送:256字节数据包
|
||||
4. 设备回复:#FOTA NEXT\n(请求下一包)
|
||||
5. 重复步骤3-4直到所有数据发送完成
|
||||
6. 设备回复:#FOTA OK\n(升级成功)
|
||||
7. 设备自动重启
|
||||
|
||||
注意:
|
||||
- 本demo默认是走虚拟串口进行交互, 如需改成物理串口, 修改uart_id和main.py
|
||||
- 升级过程中如果发生错误,串口会自动关闭,需要重新按Power键开启
|
||||
- 升级成功设备会自动重启
|
||||
|
||||
本文件没有对外接口,直接在main.lua中require "fota_uart"就可以加载运行;
|
||||
]]
|
||||
|
||||
-- 定义所需要的UART编号
|
||||
-- uart_id = 1 -- UART1, 通常也是MAIN_UART
|
||||
local uart_id = uart.VUART_0 -- 虚拟USB串口
|
||||
|
||||
-- 全局变量
|
||||
local uart_zbuff = nil
|
||||
local uart_fota_state = 0
|
||||
local uart_rx_counter = 0
|
||||
local uart_fota_writed = 0
|
||||
local upgrade_active = false -- 升级是否激活标志
|
||||
|
||||
-- 按键回调函数 - Power键
|
||||
local function power_key_callback()
|
||||
if not upgrade_active then
|
||||
log.info("FOTA_UART", "Power键按下,启动串口升级模式")
|
||||
-- 初始化串口和缓冲区
|
||||
uart_zbuff = zbuff.create(1024)
|
||||
uart.setup(uart_id, 115200)
|
||||
uart.on(uart_id, "receive", uart_cbfun)
|
||||
upgrade_active = true
|
||||
uart_fota_state = 0
|
||||
uart_rx_counter = 0
|
||||
uart_fota_writed = 0
|
||||
-- 发布事件,唤醒升级任务
|
||||
sys.publish("UART_UPGRADE_START")
|
||||
else
|
||||
log.info("FOTA_UART", "升级模式已激活,请等待当前升级完成")
|
||||
end
|
||||
end
|
||||
|
||||
-- 配置Power键
|
||||
gpio.setup(gpio.PWR_KEY, power_key_callback, gpio.PULLUP, gpio.FALLING)
|
||||
gpio.debounce(gpio.PWR_KEY, 200, 1) -- 200ms去抖
|
||||
|
||||
-- 清理资源的函数
|
||||
local function cleanup_resources()
|
||||
if uart_zbuff then
|
||||
uart_zbuff:del()
|
||||
uart_zbuff = nil
|
||||
end
|
||||
uart.close(uart_id) -- 关闭串口
|
||||
upgrade_active = false
|
||||
uart_fota_state = 0
|
||||
log.info("FOTA_UART", "资源已清理,串口已关闭")
|
||||
end
|
||||
|
||||
-- 串口接收回调函数
|
||||
function uart_cbfun(id, len)
|
||||
if not upgrade_active or not uart_zbuff then
|
||||
return
|
||||
end
|
||||
|
||||
-- 防御缓冲区超标的情况
|
||||
if uart_zbuff:used() > 8192 then
|
||||
log.warn("fota", "uart_zbuff待处理的数据太多了,强制清空")
|
||||
uart_zbuff:del()
|
||||
end
|
||||
|
||||
while true do
|
||||
local len = uart.rx(id, uart_zbuff)
|
||||
if len <= 0 then
|
||||
break
|
||||
end
|
||||
uart_rx_counter = uart_rx_counter + len
|
||||
log.info("uart", "收到数据", len, "累计", uart_rx_counter)
|
||||
-- 首次收到数据即发布事件,唤醒升级任务
|
||||
if uart_fota_state == 0 then
|
||||
sys.publish("UART_FOTA")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 串口升级任务
|
||||
local function uartUpgradeTask()
|
||||
local fota_state = 0 -- 0还没开始, 1进行中
|
||||
|
||||
while true do
|
||||
-- 等待升级启动信号
|
||||
sys.waitUntil("UART_UPGRADE_START")
|
||||
log.info("FOTA_UART", "升级任务已启动,等待数据...")
|
||||
|
||||
while upgrade_active do
|
||||
-- 等待升级数据到来
|
||||
sys.waitUntil("UART_FOTA", 1000)
|
||||
if not upgrade_active then break end
|
||||
|
||||
local used = uart_zbuff and uart_zbuff:used() or 0
|
||||
if used > 0 then
|
||||
if fota_state == 0 then
|
||||
-- 等待FOTA的状态
|
||||
if used > 5 then
|
||||
local data = uart_zbuff:query()
|
||||
uart_zbuff:del()
|
||||
-- 如果接受到 #FOTA\n 代表数据要来了
|
||||
if data:startsWith("#FOTA") and data:endsWith("\n") then
|
||||
fota_state = 1
|
||||
log.info("fota", "检测到fota起始标记,进入FOTA状态", data)
|
||||
if fota.init() then
|
||||
-- 固件数据发送端应该在收到#FOTA RDY\n之后才开始发送数据
|
||||
uart.write(uart_id, "#FOTA RDY\n")
|
||||
else
|
||||
log.error("FOTA_UART", "FOTA初始化失败")
|
||||
cleanup_resources()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- 已进入升级状态:把收到的数据喂给fota.run
|
||||
uart_fota_writed = uart_fota_writed + used
|
||||
log.info("准备写入fota包", used, "累计写入", uart_fota_writed)
|
||||
local result, isDone, cache = fota.run(uart_zbuff)
|
||||
log.debug("fota.run", result, isDone, cache)
|
||||
uart_zbuff:del() -- 清空缓冲区
|
||||
|
||||
if not result then
|
||||
-- 写入失败,退出升级状态并通知上位机
|
||||
log.error("fota", "出错了", result, isDone, cache)
|
||||
uart.write(uart_id, "#FOTA ERR\n")
|
||||
-- 调用fota.finish(false)结束升级流程,参数false表示升级流程失败。
|
||||
fota.finish(false)
|
||||
cleanup_resources()
|
||||
fota_state = 0
|
||||
break
|
||||
elseif isDone then
|
||||
-- 全部数据写入完成,等待底层校验结束
|
||||
local success = false
|
||||
for i = 1, 30 do -- 最多等待3秒
|
||||
sys.wait(100)
|
||||
local succ, fotaDone = fota.isDone()
|
||||
if not succ then
|
||||
log.error("fota", "校验过程出错")
|
||||
uart.write(uart_id, "#FOTA ERR\n")
|
||||
fota.finish(false)
|
||||
cleanup_resources()
|
||||
fota_state = 0
|
||||
break
|
||||
end
|
||||
if fotaDone then
|
||||
uart_fota_state = 1
|
||||
-- 升级文件成功写入flash中的fota分区,准备重启设备;
|
||||
log.info("fota", "已完成,1s后重启")
|
||||
-- 调用fota.finish(true)结束升级流程,参数true表示正确走完流程。
|
||||
fota.finish(true)
|
||||
-- 反馈给上位机
|
||||
uart.write(uart_id, "#FOTA OK\n")
|
||||
sys.wait(1000)
|
||||
success = true
|
||||
rtos.reboot()
|
||||
break
|
||||
end
|
||||
end
|
||||
if not success then
|
||||
log.error("fota", "校验超时")
|
||||
uart.write(uart_id, "#FOTA ERR\n")
|
||||
fota.finish(false)
|
||||
cleanup_resources()
|
||||
fota_state = 0
|
||||
end
|
||||
break
|
||||
else
|
||||
-- 单包写入成功,通知上位机继续下发
|
||||
log.info("fota", "单包写入完成", used, "等待下一个包")
|
||||
uart.write(uart_id, "#FOTA NEXT\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 重置状态,等待下次升级
|
||||
fota_state = 0
|
||||
uart_fota_state = 0
|
||||
uart_rx_counter = 0
|
||||
uart_fota_writed = 0
|
||||
end
|
||||
end
|
||||
|
||||
-- 启动串口升级任务
|
||||
sys.taskInit(uartUpgradeTask)
|
||||
@@ -0,0 +1,88 @@
|
||||
--[[
|
||||
@module main
|
||||
@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
|
||||
@version 1.0
|
||||
@date 2025.10.24
|
||||
@author 孟伟
|
||||
@usage
|
||||
Air780EPM模块的两种FOTA升级方式:文件系统直接升级和串口分段升级;
|
||||
|
||||
分两种不同的应用场景来演示固件升级的实现方法:
|
||||
|
||||
1、文件系统直接升级:通过模组文件系统中的文件直接升级,代码演示通过luatools的烧录文件系统功能将升级包文件直接烧录到文件系统然后升级;
|
||||
|
||||
2、分段升级:通过串口将升级包文件分多个片段发送,每个片段接收并写入,代码演示使用usb虚拟串口分段写入升级包升级;
|
||||
|
||||
适用场景:
|
||||
- 非标准数据传输 -> 串口、TCP、MQTT等自定义通道升级
|
||||
|
||||
- 流程精细控制 -> 需要自定义升级前后处理逻辑
|
||||
|
||||
更多说明参考本目录下的readme.md文件
|
||||
]]
|
||||
|
||||
--[[
|
||||
必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
|
||||
PROJECT:项目名,ascii string类型
|
||||
可以随便定义,只要不使用,就行
|
||||
VERSION:项目版本号,ascii string类型
|
||||
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
|
||||
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
|
||||
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为999
|
||||
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
|
||||
]]
|
||||
PROJECT = "fota_test"
|
||||
VERSION = "1.0.0" --不同于使用libfota2扩展库来升级必须是xxx.xxx.xxx的格式,这里可以自定义版本号格式。
|
||||
|
||||
|
||||
-- 在日志中打印项目名和项目版本号
|
||||
log.info("main", PROJECT, VERSION)
|
||||
|
||||
|
||||
|
||||
|
||||
-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
|
||||
-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
|
||||
-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
|
||||
-- 启动errDump日志存储并且上传功能,600秒上传一次
|
||||
-- if errDump then
|
||||
-- errDump.config(true, 600)
|
||||
-- end
|
||||
|
||||
|
||||
-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
|
||||
-- 可以使用合宙的iot.openluat.com平台进行远程升级
|
||||
-- 也可以使用客户自己搭建的平台进行远程升级
|
||||
-- 远程升级的详细用法,可以参考fota的demo进行使用
|
||||
|
||||
|
||||
-- 启动一个循环定时器
|
||||
-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
|
||||
-- 方便分析内存使用是否有异常
|
||||
-- sys.timerLoopStart(function()
|
||||
-- log.info("mem.lua", rtos.meminfo())
|
||||
-- log.info("mem.sys", rtos.meminfo("sys"))
|
||||
-- end, 3000)
|
||||
|
||||
|
||||
|
||||
-- 循环打印版本号, 方便看版本号变化, 非必须
|
||||
function print_version()
|
||||
while true do
|
||||
sys.wait(1000)
|
||||
log.info("fota", "version", VERSION)
|
||||
-- log.info("fota1111122222222222")
|
||||
end
|
||||
end
|
||||
sys.taskInit(print_version)
|
||||
|
||||
-- 方式1: 文件系统直接升级
|
||||
-- require("fota_file")
|
||||
|
||||
-- 方式2: 分段写入升级,以串口来分段写入升级包
|
||||
require("fota_uart")
|
||||
|
||||
-- 用户代码已结束---------------------------------------------
|
||||
-- 结尾总是这一句
|
||||
sys.run()
|
||||
-- sys.run()之后后面不要加任何语句!!!!!
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import serial, serial.tools.list_ports, sys, argparse
|
||||
|
||||
def find_virtual_port():
|
||||
for port in serial.tools.list_ports.comports():
|
||||
if port.vid == 0x19d1 and port.pid == 0x0001 and port.location and "x.6" in port.location:
|
||||
return port.device
|
||||
return None
|
||||
|
||||
# 解析命令行参数
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-p', '--port', help='串口名称,如COM1或/dev/ttyS0')
|
||||
args = parser.parse_args()
|
||||
|
||||
# 确定使用的串口
|
||||
port = args.port or find_virtual_port()
|
||||
if not port:
|
||||
print("错误: 未找到虚拟串口且未指定串口")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"使用串口: {port}")
|
||||
|
||||
try:
|
||||
with serial.Serial(port, 115200, timeout=1) as ser:
|
||||
ser.write(b"#FOTA\n")
|
||||
data = ser.read(128)
|
||||
if data and data.startswith(b"#FOTA"):
|
||||
print("设备响应", data)
|
||||
with open("fota_uart.bin", "rb") as f:
|
||||
while fdata := f.read(256):
|
||||
print("发送升级包数据", len(fdata))
|
||||
ser.write(fdata)
|
||||
if resp := ser.read(128):
|
||||
print("设备响应", resp)
|
||||
print("发送完毕,退出")
|
||||
else:
|
||||
print("设备没响应", data)
|
||||
except Exception as e:
|
||||
print(f"错误: {e}")
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,375 @@
|
||||
## 功能模块介绍
|
||||
|
||||
1、main.lua:主程序入口;
|
||||
|
||||
2、fota_file.lua:介绍了使用文件系统进行FOTA升级功能的实现模块,包括完整的升级流程;
|
||||
|
||||
3、fota_uart.lua:介绍了使用串口分段进行FOTA升级功能的实现模块,包括完整的升级流程;
|
||||
|
||||
4、main.py:Python脚本工具,用于通过串口分段发送升级包,演示分段升级的流程;
|
||||
|
||||
5、fota_uart.bin:演示串口分段升级的升级包文件,升级内容仅升级版本号以及添加几行打印;
|
||||
|
||||
## 演示功能概述
|
||||
|
||||
FOTA是固件远程升级的简称,用于设备固件的远程更新和维护;
|
||||
|
||||
本demo演示的核心功能为:
|
||||
|
||||
Air780EPM模块的两种FOTA升级方式:文件系统直接升级和串口分段升级;
|
||||
|
||||
分两种不同的应用场景来演示固件升级的实现方法:
|
||||
|
||||
1、文件系统直接升级:通过模组文件系统中的文件直接升级,代码演示通过luatools的烧录文件系统功能将升级包文件直接烧录到文件系统然后升级;
|
||||
|
||||
2、分段升级:通过串口将升级包文件分多个片段发送,每个片段接收并写入,代码演示使用usb虚拟串口分段写入升级包升级;
|
||||
|
||||
适用场景:
|
||||
|
||||
非标准数据传输 -> 串口、TCP、MQTT等自定义通道升级
|
||||
|
||||
流程精细控制 -> 需要自定义升级前后处理逻辑
|
||||
|
||||
## 演示硬件环境
|
||||
|
||||

|
||||
|
||||
1、Air780EPM V1.3版本开发板一块
|
||||
|
||||
2、TYPE-C USB数据线一根 + USB转串口数据线一根,Air780EPM V1.3版本开发板和数据线的硬件接线方式为:
|
||||
|
||||
- Air780EPM V1.3版本开发板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
|
||||
|
||||
- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
|
||||
|
||||
## 演示软件环境
|
||||
|
||||
1、Luatools下载调试工具
|
||||
|
||||
2、[Air780EPM V2016版本固件)](https://docs.openluat.com/air780epm/luatos/firmware/version/)
|
||||
|
||||
3、Python 3 环境(用于运行main.py发送升级包)
|
||||
|
||||
## 演示操作步骤
|
||||
|
||||
### 方式1:文件系统直接升级
|
||||
|
||||
1、搭建好演示硬件环境
|
||||
|
||||
2、修改demo脚本代码,取消`main.lua`中`require("fota_file")`的注释,注释掉`require("fota_uart")`
|
||||
|
||||
3、使用Luatools制作升级包,先把新旧版本分别生成量产文件,然后再制作升级包,工具上栏 luatOS->固件工具->差分包/整包升级包制作,将制作好的升级包放到luatools工具上面的烧录文件系统功能。
|
||||
|
||||
4、Luatools烧录内核固件和修改前的demo脚本代码,以及升级包文件。烧录成功后,自动开机运行。
|
||||
|
||||
5、可以看到如下日志:
|
||||
日志分析如下:
|
||||
|
||||
1. 开始升级,读取文件系统目录下的升级包文件/update.bin
|
||||
|
||||
2. FOTA初始化 → 底层就绪 → 文件写入 → MD5校验通过
|
||||
|
||||
3. 升级完成,版本验证成功
|
||||
|
||||
4. 设备自动重启
|
||||
|
||||
5. 新版本 1.0.1 正常运行,新增日志确认升级成功
|
||||
|
||||
结果:文件系统FOTA升级完全成功,版本从1.0.0升级到1.0.1
|
||||
|
||||
```lua
|
||||
[2025-10-24 17:56:47.892][000000005.840] D/mobile bearer act 0, result 0
|
||||
[2025-10-24 17:56:47.902][000000005.841] D/mobile NETIF_LINK_ON -> IP_READY
|
||||
[2025-10-24 17:56:47.911][000000005.890] D/mobile TIME_SYNC 0
|
||||
[2025-10-24 17:56:48.203][000000006.204] I/user.fota version 1.0.0
|
||||
[2025-10-24 17:56:49.208][000000007.204] I/user.fota version 1.0.0
|
||||
[2025-10-24 17:56:50.205][000000008.204] I/user.fota version 1.0.0
|
||||
[2025-10-24 17:56:51.205][000000009.205] I/user.fota version 1.0.0
|
||||
[2025-10-24 17:56:52.199][000000010.205] I/user.fota version 1.0.0
|
||||
[2025-10-24 17:56:52.207][000000010.210] I/user.FOTA_FILE === 开始文件系统升级 ===
|
||||
[2025-10-24 17:56:52.217][000000010.210] I/user.FOTA_FILE 初始化FOTA...
|
||||
[2025-10-24 17:56:52.224][000000010.226] I/user.FOTA_FILE 等待底层准备...
|
||||
[2025-10-24 17:56:52.234][000000010.226] I/user.FOTA_FILE 底层准备就绪
|
||||
[2025-10-24 17:56:52.242][000000010.227] I/user.FOTA_FILE 开始读取升级文件: /update.bin
|
||||
[2025-10-24 17:56:52.251][000000010.230] I/fota write common data
|
||||
[2025-10-24 17:56:52.309][000000010.311] I/fota common data done, now checking 0
|
||||
[2025-10-24 17:56:52.317][000000010.312] I/fota common data md5 ok
|
||||
[2025-10-24 17:56:52.325][000000010.312] I/fota only common data
|
||||
[2025-10-24 17:56:52.358][000000010.362] I/fota fota type 0 ok!, wait reboot
|
||||
[2025-10-24 17:56:52.370][000000010.362] I/user.FOTA_FILE 升级文件写入flash中的fota分区结果 true true 0
|
||||
[2025-10-24 17:56:52.380][000000010.363] I/user.FOTA_FILE 结束写入fota分区...
|
||||
[2025-10-24 17:56:52.389][000000010.363] I/user.FOTA_FILE 写入fota分区状态 结果: true 完成: true
|
||||
[2025-10-24 17:56:52.402][000000010.363] I/user.FOTA_FILE 升级成功,准备重启设备
|
||||
[2025-10-24 17:56:53.199][000000011.205] I/user.fota version 1.0.0
|
||||
[2025-10-24 17:56:54.197][000000012.205] I/user.fota version 1.0.0
|
||||
[2025-10-24 17:56:55.575][000000000.000] main_entry 708:SDK base line V017_pb18.002
|
||||
[2025-10-24 17:56:55.589][000000000.008] am_service_init 1154:Air780EPM_A11
|
||||
[2025-10-24 17:56:55.604][000000000.008] am_get_chip_type 635:6bef6,19,24,c7,0,EC718
|
||||
[2025-10-24 17:56:55.618][000000000.049] bsp_user_init_io 312:io volt 3.3v 21
|
||||
[2025-10-24 17:56:55.630][000000000.050] BSP_CustomInit 558:hardfault mode init 4
|
||||
[2025-10-24 17:56:55.643][000000000.050] Uart_ChangeBR 1338:uart0, 6000000 6028985 26000000 69
|
||||
[2025-10-24 17:56:55.654][000000000.072] I/pm poweron: Power/Reset
|
||||
[2025-10-24 17:56:55.668][000000000.181] self_info 127:model Air780EPM_A11 imei 862419074066563
|
||||
[2025-10-24 17:56:55.682][000000000.181] self_info 129:firmware[1] BASIC
|
||||
[2025-10-24 17:56:55.692][000000000.181] self_info 131:zone(kbytes) fs 168 script 256
|
||||
[2025-10-24 17:56:55.704][000000000.181] I/main LuatOS@Air780EPM base 25.03 bsp V2016 32bit
|
||||
[2025-10-24 17:56:55.720][000000000.181] I/main ROM Build: Oct 9 2025 21:32:15
|
||||
[2025-10-24 17:56:55.734][000000000.183] W/pins /luadb/pins_AIR780EPM.json not exist!!
|
||||
[2025-10-24 17:56:55.749][000000000.186] D/main loadlibs luavm 1048568 14888 14888
|
||||
[2025-10-24 17:56:55.762][000000000.186] D/main loadlibs sys 2375432 53100 58844
|
||||
[2025-10-24 17:56:55.772][000000000.186] D/main loadlibs psram 2375432 53184 58844
|
||||
[2025-10-24 17:56:56.105][000000001.206] I/user.fota version 1.0.1
|
||||
[2025-10-24 17:56:56.118][000000001.206] I/user.fota123456789
|
||||
[2025-10-24 17:56:57.105][000000002.206] I/user.fota version 1.0.1
|
||||
[2025-10-24 17:56:57.111][000000002.207] I/user.fota123456789
|
||||
[2025-10-24 17:56:58.108][000000003.207] I/user.fota version 1.0.1
|
||||
[2025-10-24 17:56:58.116][000000003.208] I/user.fota123456789
|
||||
```
|
||||
|
||||
### 方式2:串口分段升级
|
||||
|
||||
1、搭建好演示硬件环境
|
||||
|
||||
2、修改demo脚本代码,确保`main.lua`中已注释`require("fota_file")`,取消`require("fota_uart")`的注释
|
||||
|
||||
3、使用Luatools制作升级包,先把新旧版本分别生成量产文件,然后再制作升级包,工具上栏 luatOS->固件工具->差分包/整包升级包制作,将制作好的升级包放在main.py同级目录下
|
||||
|
||||
4、Luatools烧录内核固件和修改前的demo脚本代码,烧录成功后,自动开机运行。
|
||||
|
||||
5、确认设备连接到电脑的串口(虚拟USB串口)
|
||||
|
||||
6、按一下核心板上的Powerkey键,然后运行Python脚本发送升级包:
|
||||
|
||||
```lua
|
||||
python main.py
|
||||
```
|
||||
|
||||
7、脚本会自动寻找设备虚拟串口,发送升级命令并传输`fota_uart.bin`文件
|
||||
|
||||
8、设备接收并验证升级包,升级成功后会自动重启
|
||||
|
||||
9、可以看到如下日志:
|
||||
|
||||
串口分段升级日志解读:
|
||||
|
||||
1. USB虚拟串口连接,收到#FOTA起始指令
|
||||
|
||||
2. 开始分段接收升级包,每次256字节,累计5751字节
|
||||
|
||||
3. 所有数据包写入成功,MD5校验通过
|
||||
|
||||
4. 升级完成,重启
|
||||
|
||||
5. 重启后新版本1.0.2运行,新增日志确认升级成功
|
||||
|
||||
结果:串口FOTA升级完全成功,版本从1.0.0升级到1.0.2。
|
||||
```lua
|
||||
[2025-10-24 19:04:42.229][000000130.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:43.235][000000131.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:44.239][000000132.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:44.896][000000132.888] luat_usb_recv_cb 447:usb serial connected
|
||||
[2025-10-24 19:04:44.909][000000132.889] I/user.uart 收到数据 6 累计 6
|
||||
[2025-10-24 19:04:44.921][000000132.891] I/user.fota 检测到fota起始标记,进入FOTA状态 #FOTA
|
||||
|
||||
[2025-10-24 19:04:45.238][000000133.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:45.916][000000133.894] I/user.uart 收到数据 256 累计 262
|
||||
[2025-10-24 19:04:45.931][000000133.895] I/user.准备写入fota包 256 累计写入 256
|
||||
[2025-10-24 19:04:45.939][000000133.896] I/fota write common data
|
||||
[2025-10-24 19:04:45.957][000000133.896] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:45.968][000000133.896] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:46.227][000000134.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:46.929][000000134.906] I/user.uart 收到数据 256 累计 518
|
||||
[2025-10-24 19:04:46.942][000000134.907] I/user.准备写入fota包 256 累计写入 512
|
||||
[2025-10-24 19:04:46.953][000000134.908] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:46.964][000000134.908] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:47.227][000000135.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:47.940][000000135.917] I/user.uart 收到数据 256 累计 774
|
||||
[2025-10-24 19:04:47.950][000000135.918] I/user.准备写入fota包 256 累计写入 768
|
||||
[2025-10-24 19:04:47.963][000000135.919] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:47.971][000000135.919] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:48.238][000000136.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:48.956][000000136.933] I/user.uart 收到数据 256 累计 1030
|
||||
[2025-10-24 19:04:48.965][000000136.933] I/user.准备写入fota包 256 累计写入 1024
|
||||
[2025-10-24 19:04:48.976][000000136.934] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:48.986][000000136.934] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:49.237][000000137.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:49.968][000000137.945] I/user.uart 收到数据 256 累计 1286
|
||||
[2025-10-24 19:04:49.977][000000137.946] I/user.准备写入fota包 256 累计写入 1280
|
||||
[2025-10-24 19:04:49.987][000000137.946] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:49.995][000000137.947] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:50.231][000000138.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:50.981][000000138.957] I/user.uart 收到数据 256 累计 1542
|
||||
[2025-10-24 19:04:50.990][000000138.958] I/user.准备写入fota包 256 累计写入 1536
|
||||
[2025-10-24 19:04:51.008][000000138.958] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:51.016][000000138.959] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:51.229][000000139.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:51.992][000000139.969] I/user.uart 收到数据 256 累计 1798
|
||||
[2025-10-24 19:04:52.002][000000139.970] I/user.准备写入fota包 256 累计写入 1792
|
||||
[2025-10-24 19:04:52.013][000000139.970] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:52.020][000000139.971] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:52.228][000000140.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:53.007][000000140.984] I/user.uart 收到数据 256 累计 2054
|
||||
[2025-10-24 19:04:53.019][000000140.985] I/user.准备写入fota包 256 累计写入 2048
|
||||
[2025-10-24 19:04:53.029][000000140.985] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:53.037][000000140.985] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:53.226][000000141.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:54.005][000000141.997] I/user.uart 收到数据 256 累计 2310
|
||||
[2025-10-24 19:04:54.019][000000141.998] I/user.准备写入fota包 256 累计写入 2304
|
||||
[2025-10-24 19:04:54.034][000000141.998] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:54.044][000000141.999] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:54.225][000000142.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:55.022][000000142.998] I/user.uart 收到数据 256 累计 2566
|
||||
[2025-10-24 19:04:55.032][000000142.999] I/user.准备写入fota包 256 累计写入 2560
|
||||
[2025-10-24 19:04:55.042][000000143.000] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:55.050][000000143.000] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:55.240][000000143.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:56.035][000000144.012] I/user.uart 收到数据 256 累计 2822
|
||||
[2025-10-24 19:04:56.044][000000144.013] I/user.准备写入fota包 256 累计写入 2816
|
||||
[2025-10-24 19:04:56.054][000000144.013] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:56.060][000000144.014] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:56.237][000000144.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:57.048][000000145.024] I/user.uart 收到数据 256 累计 3078
|
||||
[2025-10-24 19:04:57.063][000000145.025] I/user.准备写入fota包 256 累计写入 3072
|
||||
[2025-10-24 19:04:57.076][000000145.026] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:57.088][000000145.026] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:57.235][000000145.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:58.057][000000146.034] I/user.uart 收到数据 256 累计 3334
|
||||
[2025-10-24 19:04:58.065][000000146.035] I/user.准备写入fota包 256 累计写入 3328
|
||||
[2025-10-24 19:04:58.076][000000146.035] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:58.085][000000146.035] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:58.228][000000146.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:04:59.070][000000147.048] I/user.uart 收到数据 256 累计 3590
|
||||
[2025-10-24 19:04:59.079][000000147.049] I/user.准备写入fota包 256 累计写入 3584
|
||||
[2025-10-24 19:04:59.085][000000147.049] D/user.fota.run true false 1
|
||||
[2025-10-24 19:04:59.094][000000147.049] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:04:59.227][000000147.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:05:00.083][000000148.061] I/user.uart 收到数据 256 累计 3846
|
||||
[2025-10-24 19:05:00.096][000000148.062] I/user.准备写入fota包 256 累计写入 3840
|
||||
[2025-10-24 19:05:00.109][000000148.062] D/user.fota.run true false 1
|
||||
[2025-10-24 19:05:00.121][000000148.062] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:05:00.240][000000148.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:05:01.093][000000149.070] I/user.uart 收到数据 256 累计 4102
|
||||
[2025-10-24 19:05:01.102][000000149.071] I/user.准备写入fota包 256 累计写入 4096
|
||||
[2025-10-24 19:05:01.112][000000149.071] D/user.fota.run true false 1
|
||||
[2025-10-24 19:05:01.119][000000149.071] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:05:01.233][000000149.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:05:02.090][000000150.082] I/user.uart 收到数据 256 累计 4358
|
||||
[2025-10-24 19:05:02.102][000000150.083] I/user.准备写入fota包 256 累计写入 4352
|
||||
[2025-10-24 19:05:02.136][000000150.116] D/user.fota.run true false 1
|
||||
[2025-10-24 19:05:02.144][000000150.116] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:05:02.229][000000150.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:05:03.117][000000151.094] I/user.uart 收到数据 256 累计 4614
|
||||
[2025-10-24 19:05:03.144][000000151.095] I/user.准备写入fota包 256 累计写入 4608
|
||||
[2025-10-24 19:05:03.153][000000151.095] D/user.fota.run true false 1
|
||||
[2025-10-24 19:05:03.162][000000151.096] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:05:03.226][000000151.216] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:05:04.118][000000152.095] I/user.uart 收到数据 256 累计 4870
|
||||
[2025-10-24 19:05:04.128][000000152.096] I/user.准备写入fota包 256 累计写入 4864
|
||||
[2025-10-24 19:05:04.138][000000152.097] D/user.fota.run true false 1
|
||||
[2025-10-24 19:05:04.152][000000152.097] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:05:04.227][000000152.216] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:05:05.133][000000153.108] I/user.uart 收到数据 256 累计 5126
|
||||
[2025-10-24 19:05:05.147][000000153.109] I/user.准备写入fota包 256 累计写入 5120
|
||||
[2025-10-24 19:05:05.167][000000153.109] D/user.fota.run true false 1
|
||||
[2025-10-24 19:05:05.179][000000153.110] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:05:05.226][000000153.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:05:06.126][000000154.117] I/user.uart 收到数据 256 累计 5382
|
||||
[2025-10-24 19:05:06.138][000000154.118] I/user.准备写入fota包 256 累计写入 5376
|
||||
[2025-10-24 19:05:06.150][000000154.119] D/user.fota.run true false 1
|
||||
[2025-10-24 19:05:06.161][000000154.119] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:05:06.233][000000154.216] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:05:07.155][000000155.132] I/user.uart 收到数据 256 累计 5638
|
||||
[2025-10-24 19:05:07.167][000000155.133] I/user.准备写入fota包 256 累计写入 5632
|
||||
[2025-10-24 19:05:07.181][000000155.133] D/user.fota.run true false 1
|
||||
[2025-10-24 19:05:07.192][000000155.133] I/user.fota 单包写入完成 256 等待下一个包
|
||||
[2025-10-24 19:05:07.234][000000155.216] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:05:08.170][000000156.147] I/user.uart 收到数据 113 累计 5751
|
||||
[2025-10-24 19:05:08.189][000000156.148] I/user.准备写入fota包 113 累计写入 5745
|
||||
[2025-10-24 19:05:08.209][000000156.177] I/fota common data done, now checking 0
|
||||
[2025-10-24 19:05:08.232][000000156.179] I/fota common data md5 ok
|
||||
[2025-10-24 19:05:08.256][000000156.179] I/fota only common data
|
||||
[2025-10-24 19:05:08.268][000000156.213] I/fota fota type 0 ok!, wait reboot
|
||||
[2025-10-24 19:05:08.278][000000156.213] D/user.fota.run true true 0
|
||||
[2025-10-24 19:05:08.287][000000156.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:05:08.324][000000156.314] I/user.fota 已完成,1s后重启
|
||||
[2025-10-24 19:05:09.227][000000157.217] I/user.fota version 1.0.0
|
||||
[2025-10-24 19:05:10.822][000000000.000] main_entry 708:SDK base line V017_pb18.002
|
||||
[2025-10-24 19:05:10.835][000000000.008] am_service_init 1154:Air780EPM_A11
|
||||
[2025-10-24 19:05:10.847][000000000.008] am_get_chip_type 635:6bef6,19,24,c7,0,EC718
|
||||
[2025-10-24 19:05:10.858][000000000.051] bsp_user_init_io 312:io volt 3.3v 21
|
||||
[2025-10-24 19:05:10.870][000000000.051] BSP_CustomInit 558:hardfault mode init 4
|
||||
[2025-10-24 19:05:10.879][000000000.052] Uart_ChangeBR 1338:uart0, 6000000 6028985 26000000 69
|
||||
[2025-10-24 19:05:10.894][000000000.073] I/pm poweron: Power/Reset
|
||||
[2025-10-24 19:05:10.908][000000000.185] self_info 127:model Air780EPM_A11 imei 862419074066563
|
||||
[2025-10-24 19:05:10.920][000000000.186] self_info 129:firmware[1] BASIC
|
||||
[2025-10-24 19:05:10.935][000000000.186] self_info 131:zone(kbytes) fs 168 script 256
|
||||
[2025-10-24 19:05:10.946][000000000.186] I/main LuatOS@Air780EPM base 25.03 bsp V2016 32bit
|
||||
[2025-10-24 19:05:10.956][000000000.186] I/main ROM Build: Oct 9 2025 21:32:15
|
||||
[2025-10-24 19:05:10.972][000000000.188] W/pins /luadb/pins_AIR780EPM.json not exist!!
|
||||
[2025-10-24 19:05:10.987][000000000.191] D/main loadlibs luavm 1048568 14888 14888
|
||||
[2025-10-24 19:05:11.002][000000000.191] D/main loadlibs sys 2375432 53100 58844
|
||||
[2025-10-24 19:05:11.018][000000000.191] D/main loadlibs psram 2375432 53184 58844
|
||||
[2025-10-24 19:05:11.257][000000001.213] I/user.fota version 1.0.2
|
||||
[2025-10-24 19:05:11.273][000000001.213] I/user.fota1111122222222222
|
||||
[2025-10-24 19:05:12.083][000000002.214] I/user.fota version 1.0.2
|
||||
[2025-10-24 19:05:12.094][000000002.214] I/user.fota1111122222222222
|
||||
[2025-10-24 19:05:13.079][000000003.215] I/user.fota version 1.0.2
|
||||
[2025-10-24 19:05:13.088][000000003.215] I/user.fota1111122222222222
|
||||
|
||||
```
|
||||
|
||||
main.py 日志:
|
||||
```lua
|
||||
D:\gitee_hz\fota>python main.py
|
||||
COM59
|
||||
设备响应 b'#FOTA RDY\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 256
|
||||
设备响应 b'#FOTA NEXT\n'
|
||||
发送升级包数据 113
|
||||
设备响应 b'#FOTA OK\n'
|
||||
发送完毕,退出
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user