Просмотр исходного кода

fix: 修改air8000 的 airtalk 为正式协议

梁健 7 месяцев назад
Родитель
Сommit
d6b86e776b

+ 7 - 2
luat/demo/airtalk/audio_config.lua

@@ -12,15 +12,20 @@ function audio_init()
     --air8000 core开发版+音频小板配置
     local voice_vol = 60 --音频小板喇叭太容易失真了,不能太大
     local i2c_id = 0
-    local pa_pin = 26
+    local pa_pin = 162           -- 喇叭pa功放脚
     local pa_on_level = 1
     local pa_delay = 200
-    local dac_power_pin = 28
+    local dac_power_pin = 164
     local dac_power_on_level = 1
     local dac_power_off_delay = 600
     gpio.setup(24, 1)   --air8000的I2C0需要拉高gpio24才能用
     gpio.setup(26, 0)
     i2c.setup(0, i2c.FAST)
+    gpio.setup(24, 1, gpio.PULLUP)          -- i2c工作的电压域
+    sys.wait(100)
+    gpio.setup(dac_power_pin, 1, gpio.PULLUP)   -- 打开音频编解码供电
+    gpio.setup(pa_pin, 1, gpio.PULLUP)      -- 打开音频放大器
+    audio.on(0, audio_callback)
 
     i2s.setup(i2s_id, i2s_mode, i2s_sample_rate, i2s_bits_per_sample, i2s_channel_format, i2s_communication_format,i2s_channel_bits)
 

+ 0 - 77
module/Air8000/demo/airtalk/airaudio.lua

@@ -1,77 +0,0 @@
---[[
-@module  airaudio
-@summary 初始化codec测试功能模块
-@version 001.000.000
-@date    2025.07.11
-@author  李源龙
-@usage
-使用Air8000整机板,初始化codec的配置:
-
-]]
-
-local airaudio = {}
-
-local i2c_id = 0            -- i2c_id 0
-
-local pa_pin = 162           -- 喇叭pa功放脚,需要根据自己的硬件配置
-local power_pin = 164         -- es8311电源脚,需要根据自己的硬件配置
-
-
-local i2s_id = 0            -- i2s_id 0
-local i2s_mode = 0          -- i2s模式 0 主机 1 从机
-local i2s_sample_rate = 16000   -- 采样率
-local i2s_bits_per_sample = 16  -- 数据位数
-local i2s_channel_format = i2s.MONO_R   -- 声道, 0 左声道, 1 右声道, 2 立体声
-local i2s_communication_format = i2s.MODE_LSB   -- 格式, 可选MODE_I2S, MODE_LSB, MODE_MSB
-local i2s_channel_bits = 16     -- 声道的BCLK数量
-
-local multimedia_id = 0         -- 音频通道 0
-local pa_on_level = 1           -- PA打开电平 1 高电平 0 低电平
-local power_delay = 3           -- 在DAC启动前插入的冗余时间,单位100ms
-local pa_delay = 100            -- 在DAC启动后,延迟多长时间打开PA,单位1ms
-local power_on_level = 1        -- 电源控制IO的电平,默认拉高
-local power_time_delay = 600    -- 音频播放完毕时,PA与DAC关闭的时间间隔,单位1ms
-local taskName = "task_tts"
-
-local play_string = "降功耗,找合宙"
-local voice_vol = 60        -- 喇叭音量
-local mic_vol = 80          -- 麦克风音量
-
-function audio_setup()
-    sys.wait(100)
-
-    i2c.setup(i2c_id,i2c.FAST)
-    i2s.setup(i2s_id, i2s_mode, i2s_sample_rate, i2s_bits_per_sample, i2s_channel_format, i2s_communication_format,i2s_channel_bits)
-
-    audio.config(multimedia_id, pa_pin, pa_on_level, power_delay, pa_delay, power_pin, power_on_level, power_time_delay)
-    audio.setBus(multimedia_id, audio.BUS_I2S,{chip = "es8311",i2cid = i2c_id , i2sid = i2s_id, voltage = audio.VOLTAGE_1800})	--通道0的硬件输出通道设置为I2S
-
-    audio.vol(multimedia_id, voice_vol)
-    audio.micVol(multimedia_id, mic_vol)
-
-end
-
-local function audio_callback(id, event)
-    local succ,stop,file_cnt = audio.getError(0)
-    if not succ then
-        if stop then
-            log.info("用户停止播放")
-        else
-            log.info("第", file_cnt, "个文件解码失败")
-        end
-    end
-    sysplus.sendMsg(taskName, MSG_PD)
-end
-
-
-function airaudio.init()
-    gpio.setup(24, 1, gpio.PULLUP)          -- i2c工作的电压域
-    sys.wait(100)
-    gpio.setup(power_pin, 1, gpio.PULLUP)   -- 打开音频编解码供电
-    gpio.setup(pa_pin, 1, gpio.PULLUP)      -- 打开音频放大器
-    audio_setup()
-    audio.on(0, audio_callback)
-end
-
-
-return airaudio

+ 339 - 0
module/Air8000/demo/airtalk/airtalk_dev_ctrl.lua

@@ -0,0 +1,339 @@
+local g_state = SP_T_NO_READY   --device状态
+local g_mqttc = nil             --mqtt客户端
+local g_local_id                  --本机ID
+local g_remote_id                 --对端ID
+local g_s_type                  --对讲的模式,字符串形式的
+local g_s_topic                 --对讲用的topic
+local g_s_mode                  --对讲的模式
+local g_dev_list                --对讲列表
+
+
+
+
+local function auth()
+    if g_state == SP_T_NO_READY then
+        g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0001", json.encode({["key"] = PRODUCT_KEY, ["device_type"] = 1}))
+    end
+end
+
+local function heart()
+    if g_state == SP_T_CONNECTED then
+        g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0005", json.encode({["from"] = g_local_id, ["to"] = g_remote_id}))
+    end
+end
+
+local function wait_speech_to()
+    log.info("主动请求对讲超时无应答")
+    speech_off(true, false)
+end
+
+--对讲开始,topic,ssrc,采样率(8K或者16K)这3个参数都有了之后就能进行对讲了,可以通过其他协议传入
+local function speech_on(ssrc, sample)
+    g_state = SP_T_CONNECTED
+    g_mqttc:subscribe(g_s_topic)
+    airtalk.set_topic(g_s_topic)
+    airtalk.set_ssrc(ssrc)
+    log.info("对讲模式", g_s_mode)
+    airtalk.speech(true, g_s_mode, sample)
+    sys.sendMsg(AIRTALK_TASK_NAME, MSG_SPEECH_ON_IND, true) 
+    sys.timerLoopStart(heart, 150000)
+    sys.timerStopAll(wait_speech_to)
+    log.info("对讲接通,可以说话了")
+end
+--对讲结束
+local function speech_off(need_upload, need_ind)
+    if g_state ==  SP_T_CONNECTED then
+        g_mqttc:unsubscribe(g_s_topic)
+        airtalk.speech(false)
+        g_s_topic = nil
+    end
+    g_state = SP_T_IDLE
+    sys.timerStopAll(auth)
+    sys.timerStopAll(heart)
+    sys.timerStopAll(wait_speech_to)
+    log.info("对讲断开了")
+    if need_upload then
+        g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0004", json.encode({["to"] = g_remote_id}))
+    end
+    if need_ind then
+        sys.sendMsg(AIRTALK_TASK_NAME, MSG_SPEECH_OFF_IND, true)
+    end
+end
+
+
+local function analyze_v1(cmd, topic, obj)
+    if cmd == "8005" or cmd == "8004" then       -- 对讲心跳保持和结束对讲的应答不做处理
+        return
+    end
+    if cmd == "8003" then       -- 请求对讲应答
+        if g_state ~= SP_T_CONNECTING then  --没有发起对讲请求
+            log.error("state", g_state, "need", SP_T_CONNECTING)
+            return
+        else
+            if obj and obj["result"] == SUCC and g_s_topic == obj["topic"]then  --完全正确,开始对讲
+                speech_on(obj["ssrc"], obj["audio_code"] == "amr-nb" and 8000 or 16000)
+                return
+            else
+                log.info(obj["result"], obj["topic"], g_s_topic)
+                sys.sendMsg(AIRTALK_TASK_NAME, MSG_SPEECH_ON_IND, false)   --有异常,无法对讲
+            end
+            
+        end
+        g_s_topic = nil
+        g_state = SP_T_IDLE
+        return
+    end
+    local new_obj = nil
+    if cmd == "0102" then       -- 对端打过来
+        if obj and obj["topic"] and obj["ssrc"] and obj["audio_code"] and obj["type"] then
+            if g_state ~= SP_T_IDLE then    -- 空闲状态下才可以进入对讲状态
+                log.error("state", g_state, "need", SP_T_IDLE)
+                new_obj = {["result"] = "failed", ["topic"] = obj["topic"], ["info"] = "device is busy"}
+            else
+                if obj["type"] == "one-on-one" then -- 1对1对讲
+                    local from = string.match(obj["topic"], "audio/.*/(.*)/.*")
+                    if from then
+                        log.info("remote id ", from)
+                        g_s_topic = obj["topic"]
+                        g_remote_id = from
+                        new_obj = {["result"] = SUCC, ["topic"] = obj["topic"], ["info"] = ""}
+                        g_s_type = "one-on-one"
+                        g_s_mode = airtalk.MODE_PERSON
+                        speech_on(obj["ssrc"], obj["audio_code"] == "amr-nb" and 8000 or 16000)
+                    else
+                        new_obj = {["result"] = "failed", ["topic"] = obj["topic"], ["info"] = "topic error"}
+                    end
+                elseif obj["type"] == "broadcast" then  -- 1对多对讲
+                    g_s_topic = obj["topic"]
+                    new_obj = {["result"] = SUCC, ["topic"] = obj["topic"], ["info"] = ""}
+                    g_s_mode = airtalk.MODE_GROUP_LISTENER
+                    g_s_type = "broadcast"
+                    speech_on(obj["ssrc"], obj["audio_code"] == "amr-nb" and 8000 or 16000)
+                end
+            end
+        else
+            new_obj = {["result"] = "failed", ["topic"] = obj["topic"], ["info"] = "json info error"}
+        end
+        g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/8102", json.encode(new_obj))
+        return
+    end
+
+    if cmd == "0103" then   --对端挂断
+        if g_state == SP_T_IDLE then
+            new_obj = {["result"] = "failed", ["info"] = "no speech"}
+        else
+            if obj and obj["type"] == g_s_type then
+                new_obj = {["result"] = SUCC, ["info"] = ""}
+                speech_off(false, true)
+            else
+                new_obj = {["result"] = "failed", ["info"] = "type mismatch"}
+            end
+        end
+        g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/8103", json.encode(new_obj))
+        return
+    end
+
+    if cmd == "0101" then                        --更新设备列表
+        if obj then
+            g_dev_list = obj["dev_list"]
+            -- for i=1,#g_dev_list do
+            --     log.info(g_dev_list[i]["id"],g_dev_list[i]["name"])
+            -- end
+            new_obj = {["result"] = SUCC, ["info"] = ""}
+        else
+            new_obj = {["result"] = "failed", ["info"] = "json info error"}
+        end
+        g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/8101", json.encode(new_obj))
+        return
+    end
+    if cmd == "8001" then
+        if obj and obj["result"] == SUCC then
+            g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0002","")  -- 更新列表
+        else
+            sys.sendMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, false, "鉴权失败" .. obj["info"]) 
+        end
+        return
+    end
+    if cmd == "8002" then
+        if obj and obj["result"] == SUCC then   --收到设备列表更新应答,才能认为相关网络服务准备好了
+            g_dev_list = obj["dev_list"]
+            -- for i=1,#g_dev_list do
+            --     log.info(g_dev_list[i]["id"],g_dev_list[i]["name"])
+            -- end
+            g_state = SP_T_IDLE
+            sys.sendMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, true)  --完整登录流程结束
+        else
+            sys.sendMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, false, "更新设备列表失败") 
+        end
+        return
+    end
+end
+
+local function mqtt_cb(mqttc, event, topic, payload)
+    log.info(event, topic)
+    local msg,data,obj
+    if event == "conack" then
+        sys.sendMsg(AIRTALK_TASK_NAME, MSG_CONNECT_ON_IND) --mqtt连上了,开始自定义的鉴权流程
+        g_mqttc:subscribe("ctrl/downlink/" .. g_local_id .. "/#")--单主题订阅
+    elseif event == "suback" then
+        if g_state == SP_T_NO_READY then
+            if topic then
+                auth()
+            else
+                sys.sendMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, false, "订阅失败" .. "ctrl/downlink/" .. g_local_id .. "/#") 
+            end
+        elseif g_state == SP_T_CONNECTED then
+            if not topic then
+                speech_off(false, true)
+            end
+        end
+    elseif event == "recv" then
+        local result = string.match(topic, g_dl_topic)
+        if result then 
+            local obj,res,err = json.decode(payload)
+            analyze_v1(result, topic, obj)
+        end
+        result = nil
+        data = nil
+        obj = nil
+        
+    elseif event == "sent" then
+        -- log.info("mqtt", "sent", "pkgid", data)
+    elseif event == "disconnect" then
+        speech_off(false, true)
+        g_state = SP_T_NO_READY
+    elseif event == "error" then
+
+    end
+end
+
+local function task_cb(msg)
+    if msg[1] == MSG_SPEECH_CONNECT_TO then
+        speech_off(true,false)
+    else
+        log.info("未处理消息", msg[1], msg[2], msg[3], msg[4])
+    end
+end
+
+local function airtalk_event_cb(event, param)
+    log.info("airtalk event", event, param)
+    if event == airtalk.EVENT_ERROR then
+        if param == airtalk.ERROR_NO_DATA then
+            log.error("长时间没有收到音频数据")
+            speech_off(true, true)
+        end
+    end
+end
+
+local function airtalk_mqtt_task()
+    local msg,data,obj,online,num,res
+    --g_local_id也可以自己设置
+    g_local_id = mobile.imei()
+    g_dl_topic = "ctrl/downlink/" .. g_local_id .. "/(%w%w%w%w)"
+    sys.timerLoopStart(next_auth, 900000)
+
+    g_mqttc = mqtt.create(nil, "mqtt.airtalk.luatos.com", 1883, false, {rxSize = 32768})
+    airtalk.config(airtalk.PROTOCOL_MQTT, g_mqttc, 200) -- 缓冲至少200ms播放
+    airtalk.on(airtalk_event_cb)
+    airtalk.start()
+
+    g_mqttc:auth(g_local_id,g_local_id,mobile.muid()) -- g_local_id必填,其余选填
+    g_mqttc:keepalive(240) -- 默认值240s
+    g_mqttc:autoreconn(true, 15000) -- 自动重连机制
+    g_mqttc:debug(false)
+    g_mqttc:on(mqtt_cb)
+    log.info("设备信息", g_local_id, mobile.muid())
+    -- mqttc自动处理重连, 除非自行关闭
+    g_mqttc:connect()
+    online = false
+    while true do
+        msg = sys.waitMsg(AIRTALK_TASK_NAME, MSG_CONNECT_ON_IND)   --等服务器连上
+        log.info("connected")
+        while not online do
+            msg = sys.waitMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, 30000)   --登录流程不应该超过30秒
+            if type(msg) == 'table' then
+                online = msg[2]
+                if online then
+                    sys.timerLoopStart(auth, 3600000) --鉴权通过则60分钟后尝试重新鉴权
+                else
+                    log.info(msg[3])
+                    sys.timerLoopStart(auth, 300000)       --5分钟后重新鉴权
+                end
+            else
+                auth()  --30秒鉴权无效后重新鉴权
+            end
+        end
+        log.info("对讲管理平台已连接")
+        while online do
+            msg = sys.waitMsg(AIRTALK_TASK_NAME)
+            if type(msg) == 'table' and type(msg[1]) == "number" then
+                if msg[1] == MSG_PERSON_SPEECH_TEST_START then
+                    if g_state ~= SP_T_IDLE then
+                        log.info("正在对讲无法开始")
+                    else
+                        log.info("测试一下主动1对1对讲功能,找一个有效的IMEI")
+
+                        for i=1,#g_dev_list do
+                            res = string.match(g_dev_list[i]["id"], "(%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w)")
+                            if res and res ~= g_local_id then
+                                break
+                            end
+                        end
+                        if res then
+                            log.info("向", res, "主动发起对讲")
+                            g_state = SP_T_CONNECTING
+                            g_remote_id = res
+                            g_s_mode = airtalk.MODE_PERSON
+                            g_s_type = "one-on-one"
+                            g_s_topic = "audio/" .. g_local_id .. "/" .. g_remote_id .. "/" .. (string.sub(tostring(mcu.ticks()), -4, -1))
+                            g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0003", json.encode({["topic"] = g_s_topic, ["type"] = g_s_type}))
+                            sys.timerStart(wait_speech_to, 15000)
+                        else
+                            log.info("找不到有效的设备ID")
+                        end
+                    end
+                elseif msg[1] == MSG_GROUP_SPEECH_TEST_START then
+                    if g_state ~= SP_T_IDLE then
+                        log.info("正在对讲无法开始")
+                    else
+                        log.info("测试一下1对多对讲功能")
+                        g_remote_id = "all"
+                        g_state = SP_T_CONNECTING
+                        g_s_mode = airtalk.MODE_GROUP_SPEAKER
+                        g_s_type = "broadcast"
+                        g_s_topic = "audio/" .. g_local_id .. "/all/" .. (string.sub(tostring(mcu.ticks()), -4, -1))
+                        g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0003", json.encode({["topic"] = g_s_topic, ["type"] = g_s_type}))
+                        sys.timerStart(wait_speech_to, 15000)
+                    end
+                elseif msg[1] == MSG_SPEECH_STOP_TEST_END then
+                    if g_state ~= SP_T_CONNECTING and g_state ~= SP_T_CONNECTED then
+                        log.info("没有对讲", g_state)
+                    else
+                        log.info("主动断开对讲")
+                        speech_off(true, false)
+                    end
+                elseif msg[1] == MSG_SPEECH_ON_IND then
+                    if msg[2] then
+                        log.info("对讲接通")
+                    else
+                        log.info("对讲断开")
+                    end
+                elseif msg[1] == MSG_CONNECT_OFF_IND then
+                    log.info("connect", msg[2])
+                    online = msg[2]
+                end
+                obj = nil
+            else
+                log.info(type(msg), type(msg[1]))
+            end
+            msg = nil
+        end
+        online = false
+    end
+end
+
+function airtalk_mqtt_init()
+    sys.taskInitEx(airtalk_mqtt_task, AIRTALK_TASK_NAME, task_cb)
+end
+
+

+ 37 - 0
module/Air8000/demo/airtalk/audio_config.lua

@@ -0,0 +1,37 @@
+function audio_init()
+    pm.ioVol(pm.IOVOL_ALL_GPIO, 3300)
+    local multimedia_id = 0
+
+    local i2s_id = 0
+    local i2s_mode = 0
+    local i2s_sample_rate = 16000
+    local i2s_bits_per_sample = 16
+    local i2s_channel_format = i2s.MONO_R
+    local i2s_communication_format = i2s.MODE_LSB
+    local i2s_channel_bits = 16
+    --air8000 core开发版+音频小板配置
+    local voice_vol = 60 --音频小板喇叭太容易失真了,不能太大
+    local i2c_id = 0
+    local pa_pin = 162           -- 喇叭pa功放脚
+    local pa_on_level = 1
+    local pa_delay = 200
+    local dac_power_pin = 164
+    local dac_power_on_level = 1
+    local dac_power_off_delay = 600
+    gpio.setup(24, 1)   --air8000的I2C0需要拉高gpio24才能用
+    gpio.setup(26, 0)
+    i2c.setup(0, i2c.FAST)
+    gpio.setup(24, 1, gpio.PULLUP)          -- i2c工作的电压域
+    sys.wait(100)
+    gpio.setup(dac_power_pin, 1, gpio.PULLUP)   -- 打开音频编解码供电
+    gpio.setup(pa_pin, 1, gpio.PULLUP)      -- 打开音频放大器
+    audio.on(0, audio_callback)
+
+    i2s.setup(i2s_id, i2s_mode, i2s_sample_rate, i2s_bits_per_sample, i2s_channel_format, i2s_communication_format,i2s_channel_bits)
+
+    audio.config(multimedia_id, pa_pin, pa_on_level, 0, pa_delay, dac_power_pin, dac_power_on_level, dac_power_off_delay)
+    audio.setBus(multimedia_id, audio.BUS_I2S,{chip = "es8311",i2cid = i2c_id , i2sid = i2s_id})	--通道0的硬件输出通道设置为I2S
+
+    audio.vol(multimedia_id, voice_vol)
+    audio.micVol(multimedia_id, 75)
+end

+ 27 - 0
module/Air8000/demo/airtalk/demo_define.lua

@@ -0,0 +1,27 @@
+
+AIRTALK_TASK_NAME = "airtalk_task"
+USER_TASK_NAME = "user"
+
+MSG_CONNECT_ON_IND = 0
+MSG_CONNECT_OFF_IND = 1
+MSG_AUTH_IND = 2
+MSG_SPEECH_ON_IND = 3
+MSG_SPEECH_OFF_IND = 4
+MSG_SPEECH_CONNECT_TO = 5
+
+MSG_PERSON_SPEECH_TEST_START = 20
+MSG_GROUP_SPEECH_TEST_START = 21
+MSG_SPEECH_STOP_TEST_END = 22
+
+
+MSG_READY = 10
+MSG_NOT_READY = 11
+MSG_KEY_PRESS = 12
+
+SP_T_NO_READY = 0           -- 离线状态无法对讲
+SP_T_IDLE = 1               -- 对讲空闲状态
+SP_T_CONNECTING = 2         -- 主动发起对讲
+SP_T_CONNECTED = 3          -- 对讲中
+
+
+SUCC = "success"

+ 65 - 57
module/Air8000/demo/airtalk/main.lua

@@ -1,60 +1,68 @@
+--演示airtalk基本功能
+--按一次boot,开始1对1对讲,再按一次boot,结束对讲
+--按一次powerkey,开始1对多对讲,再按一次powerkey或者boot,结束对讲
+PROJECT = "airtalk_demo"
+VERSION = "1.0.1"
+PRODUCT_KEY = "s1uUnY6KA06ifIjcutm5oNbG3MZf5aUv" -- 到 iot.openluat.com 创建项目,获取正确的项目id
+_G.sys=require"sys"
+log.style(1)
+require "demo_define"
+require "airtalk_dev_ctrl"
+require "audio_config"
 
---[[
-@module  main
-@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
-@version 001.000.000
-@date    2025.07.11
-@author  李源龙
-@usage
-本demo演示的功能为:
-使用Air8000整机板,通过airtalk库,通过MQTT实现对讲功能,对讲网页地址:https://airtalk.openluat.com/
-]]
-
---[[
-必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
-PROJECT:项目名,ascii string类型
-        可以随便定义,只要不使用,就行
-VERSION:项目版本号,ascii string类型
-        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
-            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
-            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
-        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
-]]
-PROJECT = "airtalk"
-VERSION = "001.000.000"
-
--- 如果内核固件支持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)
-
-talk=require "talk"
-
---运行talk.run(),启动talk
+--errDump.config(true, 600, "airtalk_test")
+mcu.hardfault(0)
+local function boot_key_cb()
+    sys.sendMsg(USER_TASK_NAME, MSG_KEY_PRESS, false)
+end
+
+local function power_key_cb()
+    sys.sendMsg(USER_TASK_NAME, MSG_KEY_PRESS, true)
+end
+
+--按下boot开始上传,再按下停止,加入了软件去抖,不需要长按了
+gpio.setup(0, boot_key_cb, gpio.PULLDOWN, gpio.RISING)
+gpio.debounce(0, 200, 1)
+gpio.setup(gpio.PWR_KEY, power_key_cb, gpio.PULLUP, gpio.FALLING)
+gpio.debounce(gpio.PWR_KEY, 200, 1)
+
+local test_ready = false
+local function task_cb(msg)
+    log.info("未处理消息", msg[1], msg[2], msg[3], msg[4])
+    if msg[1] == MSG_SPEECH_IND then
+
+    elseif msg[1] == MSG_NOT_READY then
+        test_ready = false
+        msg = sys.waitMsg(USER_TASK_NAME, MSG_KEY_PRESS)
+    end
+end
+
+local function user_task()
+    audio_init()
+    airtalk_mqtt_init()
+    local msg
+    while true do
+        msg = sys.waitMsg(USER_TASK_NAME, MSG_KEY_PRESS)
+        if msg[2] then  -- true powerkey false boot key
+            sys.sendMsg(AIRTALK_TASK_NAME, MSG_GROUP_SPEECH_TEST_START)   --测试阶段自动给一个device打
+        else
+            sys.sendMsg(AIRTALK_TASK_NAME, MSG_PERSON_SPEECH_TEST_START)   --测试阶段自动给一个device打
+        end 
+        msg = sys.waitMsg(USER_TASK_NAME, MSG_KEY_PRESS)
+        sys.sendMsg(AIRTALK_TASK_NAME, MSG_SPEECH_STOP_TEST_END)        --再按一次就自动挂断
+    end
+end
+
+sys.taskInitEx(user_task, USER_TASK_NAME, task_cb)
+
+--定期检查ram使用情况,及时发现内存泄露
 sys.taskInit(function()
-    log.info("start")
-    talk.run()
+    while true do
+        sys.wait(60000)
+        log.info("time", os.time())
+        log.info("lua", rtos.meminfo("lua"))
+        log.info("sys", rtos.meminfo("sys"))
+        log.info("psram", rtos.meminfo("psram"))
+    end
 end)
-
--- 用户代码已结束---------------------------------------------
--- 结尾总是这一句
-sys.run()
--- sys.run()之后后面不要加任何语句!!!!!
+sys.run()

+ 0 - 123
module/Air8000/demo/airtalk/talk.lua

@@ -1,123 +0,0 @@
---[[
-@module  talk
-@summary airtalk测试功能模块
-@version 001.000.000
-@date    2025.07.11
-@author  李源龙
-@usage
-使用Air8000整机板,连接MQTT服务器,订阅对应的主题和平台进行对讲功能,还支持使用powerkey按键进行对讲功能
-]]
-local talk = {}
-
-local run_state = false
-local airaudio  = require "airaudio"
-local input_key = false
-
--- 初始化fskv
---speech_topic是topic,自己设定,需要和平台的topic一致,mqtt_host是mqtt服务器地址,mqtt_port是mqtt服务器端口,
---mqtt_isssl是是否使用ssl连接,client_id是mqtt客户端id,user_name是mqtt用户名,password是mqtt密码
-local speech_topic = "12345678910"
-local mqtt_host = "lbsmqtt.openluat.com"
-local mqtt_port = 1886
-local mqtt_isssl = false
-local client_id = nil
-local user_name = "mqtt_hz_test_1"
-local password = "Ck8WpNCp"
-local mqttc = nil
-local message = ""
-local event = ""
-local talk_state = ""
-
-local function airtalk_event_cb(event, param)
-    log.info("talk event", event, param)
-    event  = event
-end
-
-
--- MQTT回调函数
-local function mqtt_cb(mqtt_client, event, data, payload)
-    log.info("mqtt", "event", event, mqtt_client, data, payload)
-    -- 连接成功时订阅主题
-end
-
-
---初始化airtalk,连接MQTT
-local function init_talk()
-    log.info("init_call")
-    --初始化codec
-    airaudio.init() 
-    client_id = mobile.imei()
-
-    mqttc = mqtt.create(nil, mqtt_host, mqtt_port, mqtt_isssl, {rxSize = 4096})
-    airtalk.config(airtalk.PROTOCOL_DEMO_MQTT_8K, mqttc, 200) -- 缓冲至少200ms播放
-    airtalk.on(airtalk_event_cb)
-    airtalk.start(client_id, speech_topic)
-
-    mqttc:auth(client_id,user_name,password) -- client_id必填,其余选填
-    mqttc:keepalive(240) -- 默认值240s
-    mqttc:autoreconn(true, 3000) -- 自动重连机制
-    mqttc:debug(false)
-    mqttc:on(mqtt_cb)
-
-    -- mqttc自动处理重连, 除非自行关闭
-    mqttc:connect()
-
-end
-
--- 重新初始化对讲函数
-local function reinit_talk()
-    log.info("talk", "重新初始化对讲")
-    
-    -- 安全停止对讲
-    if airtalk and airtalk.stop then
-        airtalk.stop()
-    end
-    if mqttc then
-        mqttc:close()
-    end
-    
-    -- 重新初始化对讲
-    sys.taskInit(init_talk)
-end
-
---初始化airtalk
-function talk.run()
-    log.info("talk.run")
-    -- lcd.setFont(lcd.font_opposansm12_chinese)
-    run_state = true
-    init_talk()
-    speech_topic = fskv.get("talk_channel")
-    log.info("get  speech_topic",speech_topic)
-end
-
---停止语音采集
-local function stop_talk()
-    talk_state = "语音停止采集"
-    airtalk.uplink(false)
-    log.info("STATE", talk_state)
-end
-
---开启语音采集
-local function start_talk()
-    talk_state = "语音采集上传中"
-    airtalk.uplink(true)
-    log.info("STATE", talk_state)
-end
-
-
---设置防抖
-gpio.debounce(gpio.PWR_KEY,1000)
-gpio.setup(gpio.PWR_KEY,function(val)
-    if val == 1 then
-        log.info("talk", "暂停",val)
-        stop_talk()
-    else
-        log.info("talk2", "录音上传",val)
-        start_talk()
-
-    end
-end,gpio.PULLUP)
-
-
-
-return talk

+ 3 - 3
module/Air8000/project/整机开发板出厂工程/user/talk.lua

@@ -46,7 +46,7 @@ local function init_talk()
     client_id = mobile.imei()
 
     mqttc = mqtt.create(nil, mqtt_host, mqtt_port, mqtt_isssl, {rxSize = 4096})
-    airtalk.config(airtalk.PROTOCOL_DEMO_MQTT_8K, mqttc, 200) -- 缓冲至少200ms播放
+    airtalk.config(airtalk.PROTOCOL_DEMO_MQTT_16K, mqttc, 200) -- 缓冲至少200ms播放
     airtalk.on(airtalk_event_cb)
     airtalk.start(client_id, speech_topic)
 
@@ -92,11 +92,11 @@ end
 
 
 function talk.run()
-    log.info("talk.run")
+    log.info("talk.run",airtalk.PROTOCOL_DEMO_MQTT_16K)
     lcd.setFont(lcd.font_opposansm12_chinese)
     run_state = true
     
-    speech_topic = fskv.get("talk_channel")
+    speech_topic = "toy/device/SXD7250500000001/amrPackage"
     log.info("get  speech_topic",speech_topic)
     if  speech_topic   then
         sys.taskInit(init_talk)