跳转至

10 AirCAMERA_1050

作者:沈园园 | 最后修改:2026-04-21

一、概述

AirCAMERA _1050 是合宙推出的一款SPI接口30W像素的摄像头的配件板,其中:

  1. 驱动IC:KC6045-V1.2,GC0310;
  2. 30万像素拍照;

    1. 适用于Air780系列/Air8000系列模组;

二、演示模块概述

本文主要是展示 AirCAMERA_1050 的使用,演示了拍照上传和扫码两种应用。

1、main.lua:主程序入口

2、take_photo_http_post.lua:本地执行拍照后通过 httpplus 扩展库将图片上传至 air32.com

3、netdrv_4g.lua:联网状态检测模块

4、scan_code.lua:扫码应用DEMO,支持一维码和二维码扫描

注意事项:

  • 拍照或者扫描模式需要在摄像头初始化时确定
  • 如使用拍照模式就无法使用扫描模式,扫描模式同理
  • 需要拍照后执行扫描的话需要重新初始化
  • 所以拍照和扫描不可同时使用,如需切换模式需重新初始化

三、演示功能概述

1、主程序入口模块(main.lua)

  • 初始化项目信息和版本号
  • 初始化看门狗,并定时喂狗
  • 启动一个循环定时器,每隔 3 秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况方便分析内存使用是否有异常
  • 加载 netdrv_4g 4G联网状态检测模块
  • 根据需要加载对应模块:
  • 加载 take_photo_http_post 模块(通过 require "take_photo_http_post"),拍照后通过httpplus上传到air32.cn
  • 加载 photo_to_aircloud 模块(通过 require "photo_to_aircloud"),拍照后通过excloud上传到合宙IOT平台
  • 加载 scan_code 模块(通过 require "scan_code"),扫码应用

2、4G联网状态检测模块(netdrv_4g.lua)

  • 订阅"IP_READY"消息,收到消息后打印联网成功日志
  • 订阅"IP_LOSE"消息,收到消息后打印联网失败日志

3、拍照上传业务模块(take_photo_http_post.lua)

  • 每 30 秒触发一次拍照:AirCAMERA_1050_func()
  • 每 3 秒打印一次系统和 LUA 的内存信息:memory_check()
  • 配置摄像头信息表:spi_camera_param
  • 初始化摄像头:excamera.open()
  • 执行拍照:excamera.photo()
  • 上传照片:httpplus.request()
  • 关闭摄像头:excamera.close()

4、扫码应用业务模块(scan_code.lua)

  • 每 30 秒触发一次拍照:AirCAMERA_1050_func()
  • 每 3 秒打印一次系统和 LUA 的内存信息:memory_check()
  • 配置摄像头信息表:spi_camera_param
  • 初始化摄像头:excamera.open()
  • 执行扫描:excamera.scan()
  • 关闭摄像头:excamera.close()

5、拍照上传合宙IOT平台业务模块(photo_to_aircloud.lua)

  • 每 60 秒触发一次拍照:AirCAMERA_1050_func()
  • 每 3 秒打印一次系统和 LUA 的内存信息:memory_check()
  • 配置摄像头信息表:spi_camera_param
  • 初始化摄像头:excamera.open()
  • 执行拍照:excamera.photo()
  • 上传照片:通过 excloud 扩展库上传至合宙IOT平台
  • 关闭摄像头:excamera.close()

四、准备硬件环境

1、Air8000开发板一块

2、TYPE-C USB数据线一根

3、Air8000开发板和数据线的硬件接线方式为

  • Air8000开发板通过TYPE-C USB口连接TYPE-C USB 数据线,数据线的另外一端连接电脑的USB口;
  • 开发板正面的 USB供电/外部供电 拨动开关 拨到USB供电一端;

4、合宙标准配件 AirCAMERA_1050 一个

  • AirCAMERA_1050 配件板插入Air8000开发板的SPI摄像头座子中

五、准备软件环境

5.1 软件环境

在开始实践本示例之前,先筹备一下软件环境:

1、烧录工具:Luatools 下载调试工具

2、内核固件:

本demo开发测试时使用的固件为[Air8000 V2032 版本固件](https://docs.openluat.com/air8000/luatos/firmware/),本demo对固件版本没有什么特殊要求,所以你如果要测试本demo时,可以直接使用最新版本的内核固件;如果发现最新版本的内核固件测试有问题,可以使用我们开发本demo时使用的内核固件版本来对比测试;

3、脚本文件:https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/demo/accessory_board/AirCAMERA_1050

4、lib脚本文件:使用Luatools烧录时,勾选 添加默认lib 选项,使用默认lib脚本文件

准备好软件环境之后,接下来查看如何烧录项目文件到Air8000开发板,将本篇文章中演示使用的项目文件烧录到Air8000开发板中。

5.2 API 介绍

excamera 扩展库:https://docs.openluat.com/osapi/ext/excamera/

excloud 扩展库:https://docs.openluat.com/osapi/ext/exmux/

六、程序结构

AirCAMERA_1050/
│── main.lua
│── netdrv_4g.lua
│── scan_code.lua
│── take_photo_http_post.lua
│── photo_to_aircloud.lua
│── readme.md

6.1 文件说明

  1. main.lua:主程序入口文件。
  2. netdrv_4g.lua:联网状态检测模块。
  3. scan_code.lua:扫描码应用DEMO。
  4. take_photo_http_post.lua:执行拍照后上传照片至 air32.com。
  5. photo_to_aircloud.lua:拍照后通过 excloud 扩展库上传图片至合宙IOT平台。

七、代码详解

7.1 main.lua

主程序文件 main.lua 是整个项目的入口点。它负责初始化系统环境。

7.2 netdrv_4g.lua

本文件为4G网卡驱动模块,核心业务逻辑为:监听"IP_READY"和"IP_LOSE",在日志中进行打印。

local function ip_ready_func(ip, adapter)
    if adapter == socket.LWIP_GP then
        log.info("netdrv_4g.ip_ready_func", "IP_READY", socket.localIP(socket.LWIP_GP))
    end
end

local function ip_lose_func(adapter)
    if adapter == socket.LWIP_GP then
        log.warn("netdrv_4g.ip_lose_func", "IP_LOSE")
    end
end


-- 此处订阅"IP_READY"和"IP_LOSE"两种消息
-- 在消息的处理函数中,仅仅打印了一些信息,便于实时观察4G网络的连接状态
-- 也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
sys.subscribe("IP_READY", ip_ready_func)
sys.subscribe("IP_LOSE", ip_lose_func)


-- 设置默认网卡为socket.LWIP_GP
-- 在Air8000上,内核固件运行起来之后,默认网卡就是socket.LWIP_GP
-- 在单4G网卡使用场景下,下面这一行代码加不加都没有影响,为了和其他网卡驱动模块的代码风格保持一致,所以加上了
socket.dft(socket.LWIP_GP)

7.3 scan_code.lua

使用AirCAMERA_1050 gc0310摄像头完成扫描码任务,每30S触发一次扫描。

注意:如果使用的Air8000开发板的为V2.0版本,需要把摄像头pwdn开关脚5改为153。

-- 摄像头扩展库模块
-- 功能:提供摄像头初始化、扫描和资源管理功能
-- 引入excamera扩展库模块
local excamera = require "excamera"
-- 引入exmux扩展库模块
local exmux = require "exmux"

-- 硬件I2C/SPI配置,当您使用合宙开发板时,请根据具体的开发板版本选择对应的变量,
-- exmux库将会自动处理开发板上的I2C/SPI外设,确保总线通讯正常
-- 当您使用自己的制作的板子,请参考exmux库的文档,配置对应的变量:https://docs.openluat.com/osapi/ext/exmux/
local HARDWARE_ENV = "DEV_BOARD_8000_V2.0"
-- local HARDWARE_ENV = "DEV_BOARD_780_V1.2"
-- local HARDWARE_ENV = "DEV_BOARD_780_V1.3"

-- 扫描功能函数
-- 作用:循环监听扫描事件,执行摄像头初始化、扫描和资源释放
local function scan_code_func()
    -- 定义变量用于存储操作结果和数据
    local result, data
    -- 初始化外设分组开关状态
    exmux.setup(HARDWARE_ENV)
    -- 无限循环,持续等待扫描事件
    while true do
        -- 配置gc0310摄像头参数表
        local spi_camera_param = {
            id = "gc0310", -- SPI摄像头仅支持"gc032a"、"gc0310"、"bf30a2",请带引号填写
            i2c_id = 0, -- 模块上使用的I2C编号
            work_mode = 1, -- 工作模式,0为拍照模式,1为扫描模式
            save_path = nil, -- 拍照结果存储路径,可用"ZBUFF"交由excamera库内部管理
            camera_pwr = 147, -- 摄像头使能管脚,填写GPIO号即可,无则填nil
            camera_pwdn = 5, -- 摄像头pwdn开关脚,填写GPIO号即可,无则填nil
            camera_light = nil -- 摄像头补光灯控制管脚,填写GPIO号即可,无则填nil
        }
        -- 等待外部触发扫描事件(SCAN_CODE)
        sys.waitUntil("SCAN_CODE")
        -- 打开外设分组
        exmux.open("i2c0")
        -- 初始化摄像头,传入配置参数
        result = excamera.open(spi_camera_param)
        -- 记录摄像头初始化状态
        log.info("初始化状态", result)
        -- 判断摄像头初始化是否成功,不成功则直接关闭,成功则启动扫描
        if result then
            -- 执行扫描操作,5秒超时
            result, data = excamera.scan(5000)
            -- 扫描执行完成则上传,否则关闭摄像头
            if result then
                log.info("Scan result :", data)
            end
        end
        -- 关闭摄像头,释放资源
        excamera.close()
        -- 关闭外设分组
        exmux.close("i2c0")
    end
end

-- 内存检查函数
-- 作用:定期监控系统内存使用情况
local function memory_check()
    -- 无限循环,定期检查内存
    while true do
        -- 等待3秒
        sys.wait(3000)
        -- 打印系统内存使用信息
        log.info("sys ram", rtos.meminfo("sys"))
        -- 打印Lua虚拟机内存使用信息
        log.info("lua ram", rtos.meminfo("lua"))
    end
end

-- AirCAMERA_1050 DEMO应用触发函数,每30S触发一次扫描
local function AirCAMERA_1050_func()
    while true do
        sys.publish("SCAN_CODE")
        sys.wait(30000)
    end
end

-- 创建扫描功能任务
-- 作用:在单独的任务中运行扫描逻辑
sys.taskInit(scan_code_func)

-- 创建内存监控任务
-- 作用:在单独的任务中运行内存监控逻辑
sys.taskInit(memory_check)

-- 创建扫描触发任务
-- 作用:每30秒触发一次扫描二维码业务
sys.taskInit(AirCAMERA_1050_func)

7.4 take_photo_http_post.lua

执行拍照后上传照片至 air32.com,每30S触发一次拍照。

注意:如果使用的Air8000开发板的为V2.0版本,需要把摄像头pwdn开关脚5改为153。

-- 功能:提供摄像头初始化、拍照和资源管理功能
-- 引入excamera扩展库模块
local excamera = require "excamera"
-- 引入httpplus扩展库模块
local httpplus = require "httpplus"
-- 引入exmux扩展库模块
local exmux = require "exmux"

-- 硬件I2C/SPI配置,当您使用合宙开发板时,请根据具体的开发板版本选择对应的变量,
-- exmux库将会自动处理开发板上的I2C/SPI外设,确保总线通讯正常
-- 当您使用自己的制作的板子,请参考exmux库的文档,配置对应的变量:https://docs.openluat.com/osapi/ext/exmux/
local HARDWARE_ENV = "DEV_BOARD_8000_V2.0"
-- local HARDWARE_ENV = "DEV_BOARD_780_V1.2"
-- local HARDWARE_ENV = "DEV_BOARD_780_V1.3"

-- 定义照片保存方式,有三种类型:
-- 1、ZBUFF保存,输入"ZBUFF"即可,excamera库会自动处理ZBUFF
-- 2、保存到内存文件系统中,路径名需指向/ram/文件夹
-- 3、保存到内置FLASH文件系统中
-- 选择其中一个即可,注释另两个路径变量
local save_method = "ZBUFF"
-- local save_method = "/ram/test.jpg"
-- local save_method = "/test.jpg"

-- 拍照功能函数
-- 作用:循环监听拍照事件,执行摄像头初始化、拍照和资源释放
local function capture_func()
    -- 定义变量用于存储操作结果和数据
    local result, data
    -- 初始化外设分组开关状态
    exmux.setup(HARDWARE_ENV)
    -- 无限循环,持续等待拍照事件
    while true do
        -- 配置gc0310摄像头参数表
        local spi_camera_param = {
            id = "gc0310", -- SPI摄像头仅支持"gc032a"、"gc0310"、"bf30a2",请带引号填写
            i2c_id = 0, -- 模块上使用的I2C编号
            work_mode = 0, -- 工作模式,0为拍照模式,1为扫描模式
            save_path = save_method, -- 拍照结果存储路径,可用"ZBUFF"交由excamera库内部管理
            camera_pwr = 147, -- 摄像头使能管脚,填写GPIO号即可,无则填nil
            camera_pwdn = 5, -- 摄像头pwdn开关脚,填写GPIO号即可,无则填nil
            camera_light = nil -- 摄像头补光灯控制管脚,填写GPIO号即可,无则填nil
        }
        -- 等待外部触发拍照事件(ONCE_CAPTURE)
        sys.waitUntil("ONCE_CAPTURE")
        -- 打开外设分组
        exmux.open("i2c0")
        -- 初始化摄像头,传入配置参数
        result = excamera.open(spi_camera_param)
        -- 记录摄像头初始化状态
        log.info("初始化状态", result)
        -- 判断摄像头初始化是否成功,不成功则直接关闭,成功则启动拍照
        if result then
            -- 执行拍照操作
            result, data = excamera.photo()
            -- 拍照执行完成则上传,否则关闭摄像头
            if result then
                -- 通过网卡状态判断WIFI是否连接成功,WIFI连接成功后再运行照片上传任务。
                while not socket.adapter(socket.dft()) do
                    -- 在此处阻塞等待WIFI连接成功的消息"IP_READY",避免联网过快,丢失了"IP_READY"信息而导致一直被卡住。
                    -- 或者等待30秒超时退出阻塞等待状态
                    log.warn("tcp_client_main_task_func", "wait IP_READY")
                    sys.waitUntil("IP_READY", 30000)
                end
                if type(data) == "userdata" then
                    data = data:query()
                else
                    data = io.readFile(data)
                end
                -- 通过网卡(本demo使用的是socket.LWIP_STA网卡)将拍摄到的照片数据result上传到服务器air32.cn
                -- 如果上传成功,电脑上浏览器打开https://www.air32.cn/upload/jpg/,打开对应的测试日期目录,点击具体的测试时间照片,可以查看摄像头拍照上传的照片
                -- 执行httpplus.request后,等待服务器的http应答,此处会阻塞当前task,等待整个过程成功结束或者出现错误异常结束
                -- code表示结果,number类型,详细说明参考API手册,一般来说:
                --             200表示成功
                --             小于0的值表示出错,例如-8表示超时错误
                --             其余结果值参考API手册
                local code = httpplus.request({
                    url = "http://upload.air32.cn/api/upload/jpg",
                    method = "POST",
                    body = data
                })
                -- 打印http传输状态
                log.info("http_upload_photo_task_func", "httpplus.request", code)
            end
        end
        -- 判断是否ZBUFF存储方式,如果是文件系统保存则删除本地文件
        if save_method ~= "ZBUFF" then
            os.remove(spi_camera_param.save_path)
        end
        -- 关闭摄像头,释放资源
        -- 使用ZBUFF存储方式时,close传入true后,excamera内部创建的ZBUFF会缩减至0字节,放出内存但是不释放ZBUFF,便于下次拍照时调用;
        -- 重复申请和释放ZBUFF会导致垃圾内存堆积,影响系统内存;
        excamera.close(true)
        -- 关闭外设分组
        exmux.close("i2c0")
    end
end

-- 内存检查函数
-- 作用:定期监控系统内存使用情况
local function memory_check()
    -- 无限循环,定期检查内存
    while true do
        -- 等待3秒
        sys.wait(3000)
        -- 打印系统内存使用信息
        log.info("sys ram", rtos.meminfo("sys"))
        -- 打印Lua虚拟机内存使用信息
        log.info("lua ram", rtos.meminfo("lua"))
    end
end

-- AirCAMERA_1050 DEMO应用触发函数,每30S触发一次拍照
local function AirCAMERA_1050_func()
    while true do
        sys.publish("ONCE_CAPTURE")
        sys.wait(30000)
    end
end

-- 创建拍照功能任务
-- 作用:在单独的任务中运行拍照逻辑
sys.taskInit(capture_func)

-- 创建内存监控任务
-- 作用:在单独的任务中运行内存监控逻辑
sys.taskInit(memory_check)

-- 创建拍照触发任务
-- 作用:每30秒触发一次拍照上传业务
sys.taskInit(AirCAMERA_1050_func)

7.5 photo_to_aircloud.lua

使用AirCAMERA_1050 gc0310摄像头完成拍照,通过excloud扩展库上传到合宙IOT平台。

注意:如果使用的Air8000开发板的为V2.0版本,需要把摄像头pwdn开关脚5改为153。

 -- 摄像头拍照模块
-- 功能:提供摄像头初始化、拍照和资源管理功能
-- 引入excamera扩展库模块
local excamera = require "excamera"
-- 引入httpplus扩展库模块
local httpplus = require "httpplus"
-- 引入exmux扩展库模块
local exmux = require "exmux"
-- 导入excloud库
local excloud = require "excloud"

-- 硬件I2C/SPI配置,当您使用合宙开发板时,请根据具体的开发板版本选择对应的变量,
-- exmux库将会自动处理开发板上的I2C/SPI外设,确保总线通讯正常
-- 当您使用自己的制作的板子,请参考exmux库的文档,配置对应的变量:https://docs.openluat.com/osapi/ext/exmux/
local HARDWARE_ENV = "DEV_BOARD_8000_V2.0"
-- local HARDWARE_ENV = "DEV_BOARD_780_V1.2"
-- local HARDWARE_ENV = "DEV_BOARD_780_V1.3"

-- 定义照片保存方式,有三种类型:
-- 1、ZBUFF保存,输入"ZBUFF"即可,excamera库会自动处理ZBUFF
-- 2、保存到内存文件系统中,路径名需指向/ram/文件夹
-- 3、保存到内置FLASH文件系统中
-- 选择其中一个即可,注释另两个路径变量
local save_method = "ZBUFF"
-- local save_method = "/ram/test.jpg"
-- local save_method = "/test.jpg"

--[[
excloud事件回调函数
参数:
    event: 事件类型字符串
    data: 事件数据,根据事件类型不同而不同

事件类型说明:
    connect_result: 连接结果
    auth_result: 认证结果
    disconnect: 断开连接
    reconnect_failed: 重连失败
]]
function on_excloud_event(event, data)
    -- 打印事件信息
    log.info("用户回调函数", event, json.encode(data))

    -- 处理连接结果事件
    if event == "connect_result" then
        if data.success then
            log.info("连接成功")
            -- 发布连接成功消息,通知其他任务
            sys.publish("aircloud_connected")
        else
            log.info("连接失败: " .. (data.error or "未知错误"))
        end
        -- 处理认证结果事件
    elseif event == "auth_result" then
        if data.success then
            log.info("认证成功")
        else
            log.info("认证失败: " .. data.message)
        end
        -- 处理断开连接事件
    elseif event == "disconnect" then
        log.warn("与服务器断开连接")
        -- 处理重连失败事件
    elseif event == "reconnect_failed" then
        log.info("重连失败,已尝试 " .. data.count .. " 次")
    end
end

-- 注册excloud事件回调函数
excloud.on(on_excloud_event)

--[[
excloud任务函数
功能:
    1. 等待网络连接就绪
    2. 配置excloud参数
    3. 初始化并开启excloud服务
    4. 启动自动心跳
]]
local function excloud_task_func()
    -- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
    while not socket.adapter(socket.dft()) do
        log.warn("excloud_task_func", "wait IP_READY", socket.dft())
        -- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
        -- 或者等待1秒超时退出阻塞等待状态;
        -- 注意:此处的1000毫秒超时不要修改的更长;
        -- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
        -- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
        -- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
        sys.waitUntil("IP_READY", 1000)
    end

    -- 配置excloud参数
    local ok, err_msg = excloud.setup({
        use_getip = true, -- 使用getip服务
        device_type = 1, -- 4G设备
        auth_key = "sh5g0OTP7ThOSlGKmE5jiEMbOBqQWyw9", -- 认证密钥
        transport = "tcp", -- 使用TCP传输
        auto_reconnect = true, -- 自动重连
        reconnect_interval = 10, -- 重连间隔(秒)
        max_reconnect = 5, -- 最大重连次数
        mtn_log_enabled = true, -- 启用运维日志
        mtn_log_blocks = 2, -- 日志文件块数
        mtn_log_write_way = excloud.MTN_LOG_CACHE_WRITE -- 缓存写入方式
    })

    -- 检查初始化是否成功
    if not ok then
        log.info("初始化失败: " .. err_msg)
        return
    end
    log.info("excloud初始化成功")

    -- 开启excloud服务
    local ok, err_msg = excloud.open()
    if not ok then
        log.info("开启excloud服务失败: " .. err_msg)
        return
    end
    log.info("excloud服务已开启")

    -- 启动自动心跳,默认5分钟一次的心跳
    excloud.start_heartbeat()
    log.info("自动心跳已启动")
end

--[[
照片上传任务函数
功能:
    1. 等待excloud连接建立
    2. 等待图片数据
    3. 上传图片到云端
    4. 处理上传结果
]]
function upload_image_fun(image)

    -- 连接成功,等待图片数据
    if excloud.status().is_connected then
        log.info("开始上传图片")
        if image then
            local ok, err = excloud.upload_image(image, "test.jpg")
            if ok then
                log.info("图片上传成功")
                return true
            else
                log.error("图片上传失败:", err)
                return false
            end
        else
            log.warn("测试图片文件不存在")
            return false
        end
    end
    -- 连接断开,等待重连
    log.info("excloud连接已断开,等待重连")
    return false
end

-- 拍照功能函数
-- 作用:循环监听拍照事件,执行摄像头初始化、拍照和资源释放
local function capture_func()
    -- 定义变量用于存储操作结果和数据
    local result, data, err
    -- 初始化开发板
    exmux.setup(HARDWARE_ENV)
    -- 无限循环,持续等待拍照事件
    while true do
        -- 配置gc0310摄像头参数表
        local spi_camera_param = {
            id = "gc0310", -- SPI摄像头仅支持"gc032a"、"gc0310"、"bf30a2",请带引号填写
            i2c_id = 0, -- 模块上使用的I2C编号
            work_mode = 0, -- 工作模式,0为拍照模式,1为扫描模式
            save_path = save_method, -- 拍照结果存储路径,可用"ZBUFF"交由excamera库内部管理
            camera_pwr = 147, -- 摄像头使能管脚,填写GPIO号即可,无则填nil
            camera_pwdn = 5, -- 摄像头pwdn开关脚,填写GPIO号即可,无则填nil
            camera_light = nil -- 摄像头补光灯控制管脚,填写GPIO号即可,无则填nil
        }
        -- 等待外部触发拍照事件(ONCE_CAPTURE)
        sys.waitUntil("ONCE_CAPTURE")
        -- 打开外设分组
        exmux.open("i2c0")
        -- 初始化摄像头,传入配置参数
        result = excamera.open(spi_camera_param)
        -- 记录摄像头初始化状态
        log.info("初始化状态", result)
        -- 判断摄像头初始化是否成功,不成功则直接关闭,成功则启动拍照
        if result then
            -- 执行拍照操作
            result, data = excamera.photo()
            -- 拍照执行完成则上传,否则关闭摄像头
            if result then
                -- 通过网卡状态判断WIFI是否连接成功,WIFI连接成功后再运行照片上传任务。
                while not socket.adapter(socket.dft()) do
                    -- 在此处阻塞等待WIFI连接成功的消息"IP_READY",避免联网过快,丢失了"IP_READY"信息而导致一直被卡住。
                    -- 或者等待30秒超时退出阻塞等待状态
                    log.warn("tcp_client_main_task_func", "wait IP_READY")
                    sys.waitUntil("IP_READY", 30000)
                end
                upload_image_fun(data)
            end
        end
        -- 判断是否ZBUFF存储方式,如果是文件系统保存则删除本地文件
        if save_method ~= "ZBUFF" then
            os.remove(spi_camera_param.save_path)
        end
        -- 关闭摄像头,释放资源
        -- 使用ZBUFF存储方式时,close传入true后,excamera内部创建的ZBUFF会缩减至0字节,放出内存但是不释放ZBUFF,便于下次拍照时调用;
        -- 重复申请和释放ZBUFF会导致垃圾内存堆积,影响系统内存;
        excamera.close(true)
        -- 关闭外设分组
        exmux.close("i2c0")
    end
end

-- 内存检查函数
-- 作用:定期监控系统内存使用情况
local function memory_check()
    -- 无限循环,定期检查内存
    while true do
        -- 等待3秒
        sys.wait(3000)
        -- 打印系统内存使用信息
        log.info("sys ram", rtos.meminfo("sys"))
        -- 打印Lua虚拟机内存使用信息
        log.info("lua ram", rtos.meminfo("lua"))
    end
end

-- AirCAMERA_1050 DEMO应用触发函数,每30S触发一次拍照
local function AirCAMERA_1050_func()
    while true do
        sys.publish("ONCE_CAPTURE")
        sys.wait(30000)
    end
end

-- 启动excloud连接任务
sys.taskInit(excloud_task_func)

-- 创建拍照功能任务
-- 作用:在单独的任务中运行拍照逻辑
sys.taskInit(capture_func)

-- 创建内存监控任务
-- 作用:在单独的任务中运行内存监控逻辑
sys.taskInit(memory_check)

-- 创建拍照触发任务
-- 作用:每30秒触发一次拍照上传业务
sys.taskInit(AirCAMERA_1050_func)

八、运行结果展示

8.1 拍照后上传

1、搭建硬件环境;

2、烧录 DEMO 代码;

3、等待自动拍照完成后上传平台,LUATOOLS会有如下打印;

[2025-12-16 11:12:47.182][000000060.769] I2C_MasterSetup 426:I2C0, Total 65 HCNT 22 LCNT 40
[2025-12-16 11:12:47.265][000000060.837] I/user.初始化状态 true
[2025-12-16 11:12:47.276][000000060.838] CSPI_Rx 2000:block len 7680, total block 80
[2025-12-16 11:12:47.280][000000060.838] I/user.照片存储路径 ZBUFF*: 0C7B97B8
[2025-12-16 11:12:47.296][000000060.839] luat_camera_capture_config 682:0,0,0,0
[2025-12-16 11:12:47.309][000000060.840] I/user.sys ram 3207056 1589112 1901108
[2025-12-16 11:12:47.313][000000060.840] I/user.lua ram 4194296 289176 289384
[2025-12-16 11:12:47.497][000000061.080] I/user.摄像头数据 60386
[2025-12-16 11:12:47.500][000000061.081] I/user.拍照完成
[2025-12-16 11:12:47.512][000000061.083] D/socket connect to upload.air32.cn,80
[2025-12-16 11:12:47.520][000000061.084] dns_run 676:upload.air32.cn state 0 id 3 ipv6 0 use dns server2, try 0
[2025-12-16 11:12:47.684][000000061.267] dns_run 693:dns all done ,now stop
[2025-12-16 11:12:47.742][000000061.329] I/user.httpplus 等待服务器完成响应
[2025-12-16 11:12:48.434][000000062.017] I/user.httpplus 等待服务器完成响应
[2025-12-16 11:12:48.440][000000062.020] I/user.httpplus 服务器已完成响应,开始解析响应
[2025-12-16 11:12:48.457][000000062.041] I/user.http_upload_photo_task_func httpplus.request 200
[2025-12-16 11:12:48.479][000000062.042] I/user.剩余内存 3207056 367460 1902228
[2025-12-16 11:12:50.257][000000063.841] I/user.sys ram 3207056 368076 1902228
[2025-12-16 11:12:50.259][000000063.841] I/user.lua ram 4194296 354656 354656

4、登录 https://www.air32.cn/upload/jpg/ 查看拍摄的照片;

8.2 扫码功能

1、搭建硬件环境;

2、在main.lua做如下修改,根据需要导入对应模块,只保留一个需要的模块,注释其他两个;

-- 只能选择一个模块使用,取消注释对应模块即可
-- 拍照上传到air32.cn应用DEMO
--require "take_photo_http_post"
-- 拍照上传到合宙IOT平台应用DEMO(通过excloud扩展库)
--require "photo_to_aircloud"
-- 导入scan_code扫描二维码应用DEMO
 require "scan_code"

3、烧录 DEMO 代码

4、二维码扫描,打开 https://cli.im/text 网址,生成一个二维码。

等待自动扫描任务完成后,LUATOOLS会有如下打印;

[2025-12-16 11:17:02.332][000000030.473] I2C_MasterSetup 426:I2C0, Total 65 HCNT 22 LCNT 40
[2025-12-16 11:17:02.386][000000030.528] I/user.初始化状态 true
[2025-12-16 11:17:02.388][000000030.528] CSPI_Rx 2000:block len 7680, total block 40
[2025-12-16 11:17:02.599][000000030.742] I/user.扫码结果 scan test
[2025-12-16 11:17:02.602][000000030.743] I/user.扫描完成,扫描结果为: scan test
[2025-12-16 11:17:02.604][000000030.743] I/user.Scan result : scan test

5、一维码扫描,打开https://yours.tools/zh/barcode.html 网址,生成一个一维码。

等待自动扫描任务完成后,LUATOOLS会有如下打印;

[2025-12-11 15:02:16.255][000000030.707] I/user.扫码结果 yours.tools
[2025-12-11 15:02:16.255][000000030.708] I/user.扫描完成,扫描结果为: yours.tools
[2025-12-11 15:02:16.255][000000030.709] I/user.Scan result : yours.tools

九、总结

通过本文学习,你可以学习到使用Air8000开发板 +AirCAMERA_1050的扫码和拍照并上传到http服务器功能。