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

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

3天内不再提示

工业场景通话录音:LuatOS 开源方案

青山老竹农 来源:jf_82863998 作者:jf_82863998 2026-04-14 12:36 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

近期有较多用户咨询通话录音功能的实现方案及相关参考案例。

AirUI 可视化方案目前仍在优化中,具备轻量化、低成本、高可靠性等特性,在工业场景具备一定应用价值。

针对用户对通话录音功能的需求,本文提供一套可直接落地的实现方案,支持自动接听与自动录音,适用于暂不使用可视化 UI 的开发场景参考。

一、方案功能

基于LuatOS开发,适配多种型号核心板,可实现低成本通话留痕,适用于客服系统、会议记录、远程问诊、话务工单追溯等场景。
主要功能特色如下:

自动接听: 来电响铃2声后自动接听,无需手动操作。

自动录音: 通话接通后自动开始录音,对方挂断后自动停止。

SD卡存储: 录音文件以PCM格式保存到SD卡,支持自动挂载和空间检测。

数据优化: 只保存上行数据,避免下行数据造成的回声问题。

跨模组兼容: Air780EHM、Air780EGH、Air780EHV、Air8000系列核心板,均可通过外挂音频配件板和SD卡存储配件板实现。

简而言之:一套方案搞定多个硬件型号,极大提升开发效率。

二、主要硬件准备

方案一:Air8000/Air780EHV开发板

开发板提供了丰富的音频接口资源,可通过开发板上的音频接口进行连接和测试。

如Air8000/Air780EHV开发板:
wKgZO2ncfY6AYoGSABp3sKCAJA0239.png

wKgZO2ncfY6AYoGSABp3sKCAJA0239.png

方案二:核心板+配件板

如果没有Air780EHV和Air8000系列Turnkey开发板,那么可使用Air780EHM、Air780EGH、Air780EHV、Air8000系列核心板,通过外挂音频配件板和存储配件板来实现通话录音功能。

AirAUDIO_1010音频配件板: 负责音频输入输出;

AirMICROSD_1010存储配件板: 提供SD卡存储功能。

连接小贴士: 如果搭配AirAUDIO_1010扩展板测试,需将扩展板中PA开关拨到OFF,让软件控制PA,避免pop音。

本文以Air780EGH为例:

接线方式参照下方图表对应连接(注意不同型号核心板具体引脚号差异):

wKgZO2ncfcaADN0oABUG9Ev8m6w734.png

wKgZPGncfeCAfg0dAAUthHXjVAo797.png
三、开源示例与教程
基于LuatOS开发的通话录音示例已上传Gitee开源仓库,即便是新接触LuatOS开发的朋友,也可以根据合宙资料中心提供配套实操教程快速上手。

核心功能模块包括SD卡挂载、通话状态机、录音数据回调等,完整示例代码详见源码仓库最新文件。

--[[

录音功能特性:
- 录音文件保存为PCM格式:/sd/record_call.pcm
- 只保存上行数据(包含本地声音和网络回声)
- 下行数据自动跳过,避免重复存储
- 支持SD卡自动挂载和空间检测
   
-- ====================== 录音功能 ======================

-- 创建音频数据缓冲区
local up1 = zbuff.create(BUFFER_SIZE,0)      -- 上行数据保存区1
local up2 = zbuff.create(BUFFER_SIZE,0)      -- 上行数据保存区2
local down1 = zbuff.create(BUFFER_SIZE,0)    -- 下行数据保存区1
local down2 = zbuff.create(BUFFER_SIZE,0)    -- 下行数据保存区2

-- 打开录音文件
local function open_record_file()
    -- 先挂载SD卡
    if not mount_sd_card() then
        log.error("录音文件", "SD卡挂载失败,无法进行录音")
        return false
    end

    log.info("录音文件", "SD卡挂载成功,录音文件将保存到SD卡")

    -- 关闭已打开的文件
    if record_file then
        record_file:close()
        record_file = nil
    end

    -- 删除旧录音文件
    if io.exists(RECORD_FILE_PATH) then
        os.remove(RECORD_FILE_PATH)
        log.info("录音文件", "删除旧录音文件:", RECORD_FILE_PATH)
    end

    -- 创建录音文件
    record_file = io.open(RECORD_FILE_PATH, "wb")

    if record_file then
        log.info("录音文件", "创建录音文件成功:", RECORD_FILE_PATH)
        record_start_time = mcu.ticks()
        is_recording_to_file = true
        return true
    else
        log.error("录音文件", "创建录音文件失败:", RECORD_FILE_PATH)
        return false
    end
end

-- 关闭录音文件
local function close_record_file()
    if record_file then
        record_file:close()
        record_file = nil

        local file_size = io.fileSize(RECORD_FILE_PATH)
        record_duration = (mcu.ticks() - record_start_time) / 1000  -- 转换为秒

        log.info("录音文件", "录音完成", "文件大小:", file_size, "字节", "录音时长:", string.format("%.1f", record_duration), "秒", "路径:", RECORD_FILE_PATH)

        is_recording_to_file = false
        record_start_time = 0
        record_duration = 0
    end
end

-- 写入录音数据到文件
local function write_record_data(buff, is_downlink)
    if not record_file or not is_recording_to_file then
        return false
    end

    -- 保存数据
    if not is_downlink then
        local data_size = buff:used()
        if data_size > 0 then
            local start_time = mcu.ticks()

            -- 写入数据到文件
            record_file:write(buff:query())

            local end_time = mcu.ticks()
            local write_time = end_time - start_time
            local write_speed = data_size / (write_time / 1000)  -- 字节/秒

            log.info("录音写入", 
                    "数据大小:", data_size, "字节,", 
                    "写入耗时:", string.format("%.2f", write_time), "ms,",
                    "写入速度:", string.format("%.2f", write_speed / 1024), "KB/s")

            return true
        end
    else
        -- 下行数据不保存,只记录日志
        -- 写入下行数据会导致文件内有回声
        local data_size = buff:used()
        if data_size > 0 then
            log.info("录音写入", "下行数据跳过", "数据大小:", data_size, "字节")
        end
    end
    return false
end

-- 音频数据回调函数
local function recordCallback(is_dl, point)
    if is_dl then
        log.info("录音", "下行数据,位于缓存", point+1, "缓存1数据量", down1:used(), "缓存2数据量", down2:used())

        -- 处理下行数据
        if point == 0 then
            write_record_data(down1, true)
            down1:del()  -- 清空缓冲区
        else
            write_record_data(down2, true)
            down2:del()  -- 清空缓冲区
        end
    else
        log.info("录音", "上行数据,位于缓存", point+1, "缓存1数据量", up1:used(), "缓存2数据量", up2:used())

        -- 处理上行数据
        if point == 0 then
            write_record_data(up1, false)
            up1:del()  -- 清空缓冲区
        else
            write_record_data(up2, false)
            up2:del()  -- 清空缓冲区
        end
    end
    log.info("通话质量", cc.quality())
end

-- 启用通话录音
local function enableRecording()
    cc.record(true, up1, up2, down1, down2)
    cc.on("record", recordCallback)
    log.info("cc_app", "通话录音已启用")
end

-- 开始通话录音到文件
local function start_call_recording()
    if open_record_file() then
        log.info("通话录音", "开始录音到文件:", RECORD_FILE_PATH)
        return true
    else
        log.error("通话录音", "无法开始录音到文件,请检查SD卡")
        return false
    end
end

-- 停止通话录音到文件
local function stop_call_recording()
    close_record_file()
    log.info("通话录音", "停止录音到文件")
end

-- 获取所有缓冲区
local function getRecordingBuffers()
    return {
        up1 = up1,
        up2 = up2,
        down1 = down1,
        down2 = down2
    }
end

-- 获取录音文件信息
local function get_record_file_info()
    if io.exists(RECORD_FILE_PATH) then
        local file_size = io.fileSize(RECORD_FILE_PATH)
        return {
            path = RECORD_FILE_PATH,
            size = file_size,
            duration = record_duration,
            exists = true
        }
    else
        return {
            path = RECORD_FILE_PATH,
            size = 0,
            duration = 0,
            exists = false
        }
    end
end

-- 呼入自动接听,等待对方挂断
local function handle_scenario(status)
    if status == "INCOMINGCALL" then
        -- 获取来电号码
        caller_number = cc.lastNum() or "未知号码"
        call_counter = call_counter + 1

        log.info("收到来电,号码:", caller_number, "响铃次数:", call_counter)

        -- 响铃2声后自动接听
        if call_counter >= 2 then
            log.info("自动接听来电")
            cc.accept(0)
            call_counter = 0  -- 重置计数器
        end
    elseif status == "SPEECH_START" then
        -- 语音通话真正开始
        log.info("电话已接通,电话号码:", caller_number)

        -- 开始通话录音到文件
        start_call_recording()
    elseif status == "DISCONNECTED" then
        -- 对方挂断通话
        log.info("通话结束对方挂断")

        -- 停止通话录音到文件
        stop_call_recording()

        call_counter = 0  -- 重置计数器
    end
end

-- ====================== 主事件处理器 ======================
sys.subscribe("CC_IND", function(status)
    log.info("CC状态", status)
    handle_scenario(status)

    -- 需要处理的通用状态
    if status == "READY" then
        sys.publish("CC_READY")  -- 发布系统就绪事件
    elseif status == "HANGUP_CALL_DONE" or status == "MAKE_CALL_FAILED" or status == "DISCONNECTED" then
        exaudio.pm(audio.SHUTDOWN)   --主动进入低功耗模式
    end
end)

-- ====================== 电话系统初始化 ======================
local function init_cc()
    -- 先尝试挂载SD卡
    mount_sd_card()

    -- 初始化音频设备
    audio_drv.initAudioDevice()

    -- 等待电话系统就绪
    sys.waitUntil("CC_READY")

    -- 初始化电话功能
    cc.init(audio_drv.getMultimediaId())

    -- 启用通话录音(录音功能在cc_app中)
    enableRecording()

    log.info("cc_app", "电话系统初始化完成")
end

-- 启动初始化任务
sys.taskInit(init_cc)

四、使用注意事项

必须插入SD卡才能使用录音功能,因为录音文件较大无法存入内存;缓冲区大小必须是640的倍数,否则可能导致录音异常。

录音文件保存在SD卡的/sd/record_call.pcm路径下,可以通过读卡器在电脑上查看;录音文件为原始PCM格式,需要使用专用播放器(如Audacity )播放。
401NEW.png
402 播放器.png

审核编辑 黄宇

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

    关注

    3

    文章

    4325

    浏览量

    46428
  • LuatOS
    +关注

    关注

    0

    文章

    169

    浏览量

    2745
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    WiFi 7 工业远距离无线新标杆 ——Wallystech 全场景解决方案

    /DR5424 :工业级 AP / 路由,金属机身,宽温稳定,面向智能工厂、仓储、高密接入场景。 DR9274 系列模块 :QCN9274/QCN6274 方案,M.2 形态,便于嵌入式集成,支持
    发表于 04-14 11:30

    通话录音功能实现:自动接听 + 自动录音开源方案

    AirUI 可视化方案仍在优化中,其轻量化、低成本、高可靠的特性在工业场景具备一定应用价值。针对用户对通话录音功能的需求,本文提供一套可直接落地的实现
    的头像 发表于 04-13 13:36 467次阅读
    <b class='flag-5'>通话录音</b>功能实现:自动接听 + 自动<b class='flag-5'>录音</b><b class='flag-5'>开源</b><b class='flag-5'>方案</b>

    LuatOS中Modbus RTU通信开发指南

     在实际工业控制场景中,Modbus RTU常以主从结构实现多设备协同工作。LuatOS不仅支持标准串口通信,更提供了灵活的任务调度与内存管理机制,便于开发者在单一设备上实现主站或从站功能。本文将
    的头像 发表于 02-03 19:34 771次阅读
    <b class='flag-5'>LuatOS</b>中Modbus RTU通信开发指南

    LuatOS框架的使用(上)

    在资源受限的物联网终端设备中,如何实现快速开发与稳定运行是关键挑战。LuatOS框架通过将Lua语言与底层硬件抽象层深度融合,提供了一套简洁高效的开发范式。本文将围绕LuatOS框架的使用展开,从
    的头像 发表于 01-27 19:38 324次阅读
    <b class='flag-5'>LuatOS</b>框架的使用(上)

    LuatOS-Air转LuatOS常见故障排查手册

    LuatOS-Air脚本在LuatOS环境中运行失败,问题往往集中在几个关键模块:任务调度、外设驱动、网络配置和固件版本匹配。本文以故障排查的逻辑为主线,列出常见报错现象、可能原因及快速修复方案
    的头像 发表于 01-13 19:20 252次阅读
    <b class='flag-5'>LuatOS</b>-Air转<b class='flag-5'>LuatOS</b>常见故障排查手册

    五大技术路径,重塑格局:唯创电子录音芯片方案引领智能录音新纪元

    风雨声中完整记录自然之声,抑或通过蓝牙无损保存手机通话原音——这些对传统录音设备而言的奢望,正被广州唯创电子五大专业录音芯片方案变为现实。它们如同五把精准的手术刀,
    的头像 发表于 12-26 09:10 674次阅读
    五大技术路径,重塑格局:唯创电子<b class='flag-5'>录音</b>芯片<b class='flag-5'>方案</b>引领智能<b class='flag-5'>录音</b>新纪元

    数字工牌是什么?数字工牌录音方案设计

    数字工牌使用场景还是非常多的,主要使用场景集中在各种需要进行部门对接和客户对接以及会议的场景当中。以展会为例,参展员工带着数字工牌开启录音功能去和客户沟通,在回到公司以后如果不
    的头像 发表于 12-01 16:41 530次阅读
    数字工牌是什么?数字工牌<b class='flag-5'>录音</b><b class='flag-5'>方案</b>设计

    2025年新录音芯片方案解决录音设备三大难题

    录音笔市场三大核心痛点 在深入介绍我们的解决方案之前,让我们先来看看当前录音笔市场面临的核心痛点。 1.录音不清晰的烦恼:如果你坐在会议室后排,距离发言人有好几米远,传统
    的头像 发表于 11-13 16:55 1592次阅读
    2025年新<b class='flag-5'>录音</b>芯片<b class='flag-5'>方案</b>解决<b class='flag-5'>录音</b>设备三大难题

    多封装长录音:WT2003H系列MP3录音芯片满足全场景音频需求

    在音频技术快速发展的今天,广州唯创电子WT2003H系列以70秒录音时长和三种封装选择,为智能设备提供专业的音频解决方案01核心技术突破:70秒录音时长的卓越表现1.1专业级录音性能W
    的头像 发表于 10-16 09:12 1068次阅读
    多封装长<b class='flag-5'>录音</b>:WT2003H系列MP3<b class='flag-5'>录音</b>芯片满足全<b class='flag-5'>场景</b>音频需求

    教程来啦!LuatOS中的消息通信机制详解及其应用场景

    在资源受限的嵌入式环境中,LuatOS采用消息机制实现模块间解耦与高效通信。通过预定义消息名称(如“new_msg”),开发者可轻松构建响应式程序结构。接下来我们将深入剖析其实现原理与典型使用方法
    的头像 发表于 09-26 18:59 546次阅读
    教程来啦!<b class='flag-5'>LuatOS</b>中的消息通信机制详解及其应用<b class='flag-5'>场景</b>

    广州唯创电子录音语音芯片IC:工作原理与应用场景全解析

    录放音芯片解决方案,广泛应用于智能家居、医疗设备、工业控制等领域。本文将深入解析其工作原理和典型应用场景。01录音芯片的核心作用与技术价值录音
    的头像 发表于 09-24 09:39 960次阅读
    广州唯创电子<b class='flag-5'>录音</b>语音芯片IC:工作原理与应用<b class='flag-5'>场景</b>全解析

    揭秘LuatOS Task:多任务管理的“智能中枢”

    Task任务作为LuatOS的核心组成部分,通过智能化的任务管理机制,实现任务的创建、调度与协同运行,让复杂应用得以高效并行处理,满足实时场景下的严苛需求。 sys核心库是LuatOS运行框架库
    的头像 发表于 08-28 13:48 771次阅读
    揭秘<b class='flag-5'>LuatOS</b> Task:多任务管理的“智能中枢”

    唯创录音芯片五大技术方案,重塑智能录音市场新格局:自动识别环境噪音、智能调节录音参数、实现多路并

    2025,智能录音市场正从基础功能满足转向音质体验升级。企业正面临哪些技术选型难点?唯创知音一文为您解读录音芯片行业技术演进之路!三大诉求录音市场正从功能满足转向品质升级传统录音设备音
    的头像 发表于 08-16 12:03 1224次阅读
    唯创<b class='flag-5'>录音</b>芯片五大技术<b class='flag-5'>方案</b>,重塑智能<b class='flag-5'>录音</b>市场新格局:自动识别环境噪音、智能调节<b class='flag-5'>录音</b>参数、实现多路并

    多种录音笔录音芯片方案推荐

    多种录音笔录音芯片方案推荐 一、引言 随着信息技术的飞速发展,录音笔作为一种重要的音频记录设备,在会议记录、采访、学习等众多场景中得到广泛应用。其核心的
    的头像 发表于 08-06 16:48 1478次阅读

    芯资讯|广州唯创电子语音芯片录音采样率解析:为多元场景打造声音解决方案

    的采样率设计可满足不同场景下的音质与存储需求。本文将重点解析其主流录音芯片的采样率特性及适用场景。一、WTR096芯片:经济型录音方案(8-
    的头像 发表于 05-23 08:46 796次阅读
    芯资讯|广州唯创电子语音芯片<b class='flag-5'>录音</b>采样率解析:为多元<b class='flag-5'>场景</b>打造声音解决<b class='flag-5'>方案</b>