语音通话
一、音频应用-通话(VoLTE)概述
Air724UG 模块的 4G 通信功能,通过 VoLTE 技术实现高清语音通话。在通话过程中,声音信号通过 MIC 捕捉并转换为数字音频数据,经 4G 网络实时传输至对方设备。同时,Air724UG 支持扬声器(Speaker)输出、耳机(Headset)输出以及听筒(Handset)输出等多种音频输出模式,确保用户在不同场景下都能获得清晰的通话体验。这一应用广泛适用于物联网设备中的远程通信、语音交互等场景,为用户提供便捷、高效的通话服务。
二、准备硬件环境
“古人云:‘工欲善其事,必先利其器。’在深入介绍本功能示例之前,我们首先需要确保以下硬件环境的准备工作已经完成。”
2.1 Air724UG 开发板
本 demo 使用的是 Air724UG_A14 开发板,如下图所示:
点击链接购买:EVB_Air724UG_A14 开发板淘宝购买链接 ;
此开发板的详细使用说明参考:Air724UG 产品手册中的开发板硬件资料中《EVB_Air724UG_A14 开发板使用说明.pdf》;开发板使用过程中遇到任何问题,可以直接参考这份使用说明 pdf 文档。
2.2 SIM 卡
请准备一张可正常上网的 SIM 卡,该卡可以是物联网卡(一般不能支持)或您的个人手机卡。
特别提醒:请确保 SIM 卡未欠费且网络功能正常,以便顺利进行后续操作。
2.3 PC 电脑
请准备一台配备 USB 接口且能够正常上网的电脑。
电脑操作系统为:WIN7以及以上版本的WINDOWS系统。
2.4 小喇叭
点击链接购买:小喇叭 ;
2.5 数据通信线
请准备一根用于连接 EVB_Air724UG_A14 开发板和 PC 电脑的数据线,该数据线将实现业务逻辑的控制与交互。
- USB 数据线:此数据线不仅用于为测试板供电,还用于查看数据日志。其一端为 Micro-B 接口(俗称老安卓口),用于连接 EVB_Air724UG_A14 开发板;另一端为标准 USB 接口,连接 PC 电脑。
2.6 组装硬件环境
2.6.1 请按 SIM 卡槽指示方向正确插入 SIM 卡,避免插反损坏
通常,插入 SIM 卡的步骤如下:
- 将 SIM 卡的金属卡槽下滑打开。
- 平稳地将 SIM 卡放入卡槽。
- 上滑关闭卡槽。
2.6.2 USB 数据线,连接电脑和 EVB_Air724UG_A14 开发板,如下图所示:
2.6.3 小喇叭和 EVB_Air724UG_A14 开发板连接,如下图所示:
三、准备软件环境
“凡事预则立,不预则废。”在详细阐述本功能示例之前,我们需先精心筹备好以下软件环境。
3.1 Luatools 工具
要想烧录 LuatOS 固件到 4G 模组中,需要用到合宙的强大的调试工具:Luatools;
下载地址:Luatools v3 下载调试工具。
Luatools 工具集具备以下几大核心功能:
- 一键获取最新固件:自动连接合宙服务器,轻松下载最新的合宙模组固件。
- 固件与脚本烧录:便捷地将固件及脚本文件烧录至目标模组中。
- 串口日志管理:实时查看模组通过串口输出的日志信息,并支持保存功能。
- 串口调试助手:提供简洁的串口调试界面,满足基本的串口通信测试需求。
Luatools 下载之后, 无需安装,放入新建的文件夹后点击 Luatools_v3.exe 运行,出现如下界面,就代表 Luatools 安装成功了:
3.2 烧录代码
首先要说明一点: 脚本代码, 要和固件的 LuatOS-Air_V4030_RDA8910_TTS_NOLVGL_FLOAT.pac(注:支持 LCD,字库,图片,TTS,WIFI Scan,SD 卡,VOLTE) 文件一起烧录。
整体压缩文件:内含有四个文件,如图所示。
3.2.1 压缩文件:完整文件包
3.2.2 找到烧录的固件文件
官网下载,底层 core 下载地址:LuatOS 底层 core 注:本 demo 使用如图所示固件
3.2.3 正确连接电脑和 4G 模组电路板
使用带有数据通信功能的数据线,不要使用仅有充电功能的数据线;
3.2.4 识别 4G 模组的 BOOT 引脚
在下载之前,要用模组的 BOOT 引脚触发下载
具体到 EVB_Air724UG_A14 开发板,
- 当我们模块没开机时,按着下载模式键然后长按开机键开机。
- 当我们模块开机时,按着下载模式键然后点按重启键即可。
3.2.5 识别电脑的正确端口
判断是否进入 BOOT 模式:
- 模块上电,如果是正常开机运行(没有进入boot下载模式),此时在电脑的设备管理器中,查看串口设备,如下图所示(会出现3个或者4个端口):
- 先按下载模式再按一下重启,会出现一个端口表示进入了 BOOT 下载模式,如下图所示:
- 一旦进入了boot下载模式,表示硬件连接上已经处于就绪状态,此时就可以使用Luatools工具进行烧录了!
3.2.6 新建项目
首先,确保你的 Luatools 的版本大于或者等于 3.0.6 版本.
在 Luatools 的左上角上有版本显示的,如图所示:
Luatools 版本没问题的话, 就点击 Luatools 右上角的“项目管理测试”按钮,如下图所示:
这时会弹出项目管理和烧录管理的对话框,如下图:
3.2.7 开始烧录
- 选择 Air724ug 开发板对应的底层 core 和 main.lua 脚本文件。下载到板子中。
- 一直按下载模式按键,再按一下重启,然后点击下载底层和脚本,如图所示:
- 出现如图所示,表示已进入 BOOT 模式,可以松开下载模式按键,等待下载完成。
- 下载完成,如图所示
四、音频应用-通话(VoLTE)基本用法
4.1 本教程实现的功能定义:
- CC 库在 EVB_Air724UG_A14-LuatOS 中扮演了关键角色,它提供了一种高效、灵活且用户友好的呼叫控制解决方案。本次介绍的目标是帮助开发者迅速了解并精通 CC 库的 API 接口,以便在 VoLTE 通话中实现精确的呼叫管理和控制功能。
4.2 文章内容引用
- EVB_Air724UG_A14 开发板软硬件资料 : EVB_Air724UG_A14 产品手册
- 以下接口函数不做详细介绍,可通过此链接查看具体介绍:cc_API
4.3 API 接口详解
4.3.1 cc.anyCallExist()
解说: cc.anyCallExist
用于检查当前是否存在任何活动的通话。这个 API 不需要任何参数。
参数:
- 无
返回值:
- 布尔值(
true
或false
):如果存在活动的通话,则返回true
;否则返回false
。
举例:
-- 检查是否有活动的通话
local isCallExist = cc.anyCallExist()
if isCallExist then
print("存在活动的通话")
else
print("没有活动的通话")
end
4.3.2 cc.getState(num)
解说: cc.getState
用于获取指定通话的状态。它接收一个通话标识符(可能是通话句柄或电话号码,具体取决于实现)作为参数,并返回一个表示通话状态的值(通常是枚举或常量)。
参数:
num
:通话标识符(电话号码)。
返回值:
- 通话状态值(枚举或常量):表示指定通话的当前状态,如空闲、来电、正在拨打、已接通、保持等。
举例:
local CC_STATE_IDLE = 0 -- 无通话
local CC_STATE_INCOMING = 1 -- 来电
local CC_STATE_DIALING = 2 -- 正在拨打
local CC_STATE_CONNECTED = 3 -- 已接通
local CC_STATE_HELD = 4 -- 保持状态
-- 获取通话状态
local state = cc.getState(callHandle)
if state == CC_STATE_CONNECTED then
print("通话已接通")
elseif state == CC_STATE_INCOMING then
print("有来电")
else
print("通话状态:" .. state)
end
4.3.3 cc.dial(num, delay)
解说: cc.dial
用于拨打一个电话号码。它接收两个参数:要拨打的电话号码和一个可选的延迟时间(以秒为单位),在拨打之前可以等待一段时间。
参数:
num
:要拨打的电话号码。delay
(可选):可选参数,默认为0 延时 delay 毫秒后,才发起呼叫。
返回值:
- bool result,true 表示允许发送 at 命令拨号并且发送 at,false 表示不允许 at 命令拨号。
举例:
-- 立即拨打号码
cc.dial("110086")
-- 或在3秒后拨打号码
cc.dial("10086", 30)
4.3.4 cc.hangUp(num)
解说: cc.hangUp
用于挂断指定通话。它通常接收一个通话标识符(如通话句柄或电话号码)作为参数,尽管在某些实现中可能不需要参数(如果只有一个活动通话)。
参数:
num
(可选):号码,若指定号码通话状态不对,则直接退出,不会执行挂断,若挂断时会挂断所有电话。
返回值:
- 无。
举例:
-- 挂断通话
cc.hangUp("10086")
4.3.5 cc.dtmfDetect(enable, sens)
解说: cc.dtmfDetect
用于启用或禁用双音多频(DTMF)信号的检测。它接收两个参数:一个布尔值表示是否启用检测,以及一个可选的灵敏度值。
参数:
enable
:可选参数,默认为nil true 使能,false 或者 nil 为不使能。sens
(可选):可选参数,默认为3 灵敏度,最灵敏为 1。
返回值:
- 无。
举例:
cc.dtmfDetect(true, 2)
4.3.6 cc.accept(num)
解说: cc.accept
用于接听传入的通话。它通常接收一个通话标识符作为参数,尽管在某些实现中可能不需要(因为通常只有一个传入的通话等待接听)。
参数:
num
(可选):号码,若指定号码通话状态不对,则直接退出,不会接通。
返回值:
- 无
举例:
-- 接听来电
cc.accept("10086")
4.3.7 cc.transVoice(data, loop, downLinkPlay)
解说: cc.transVoice
通话中发送声音到对端,必须是 12.2K AMR 格式。
参数:
data
:12.2K,AMR 格式的数据。loop
:可选参数,默认为nil 是否循环发送,true 为循环,其余为不循环。downLinkPlay
:可选参数,默认为nil 声音是否在本端播放,true 为播放,其余为不播放。
返回值:
- 布尔值或状态码:表示传输操作是否成功启动。具体返回值可能因实现而异。
举例:
-- 传输语音数据(具体参数和用途可能因实现而异)
cc.transVoice(voiceData, true, false)
4.3.8 cc.sendDtmf(str, playtime, intvl)
解说: cc.sendDtmf
用于在通话中发送双音多频(DTMF)信号。它接收三个参数:一个包含 DTMF 字符的字符串,每个字符的播放时间,以及字符之间的间隔。
参数:
str
:dtmf 字符串,仅支持数字、ABCD*#。playtime
:可选参数,默认为100 每个 dtmf 播放时间,单位毫秒。intvl
:可选参数,默认为100 两个 dtmf 间隔,单位毫秒。
返回值:
- 无
举例
-- 发送DTMF信号“1234”,每个字符播放500毫秒,间隔100毫秒
cc.sendDtmf("1234", 500, 100)
五、音频应用整体演示
5.1 音频应用-录音成果演示与深度解析:视频 + 图文全面展示
5.1.1 成果运行精彩呈现
5.1.2 完整实例深度剖析
-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "call_demo"
VERSION = "1.0.0"
require "sim"
require "sys"
require"cc"
require"audio"
require"common"
require "net"
require"powerKey"
require "netLed"
--每1分钟查询一次GSM信号强度
--每1分钟查询一次基站信息
net.startQueryAll(60000, 60000)
--此处关闭RNDIS网卡功能
--否则,模块通过USB连接电脑后,会在电脑的网络适配器中枚举一个RNDIS网卡,电脑默认使用此网卡上网,导致模块使用的sim卡流量流失
--如果项目中需要打开此功能,把ril.request("AT+RNDISCALL=0,1")修改为ril.request("AT+RNDISCALL=1,1")即可
--注意:core固件:V0030以及之后的版本、V3028以及之后的版本,才以稳定地支持此功能
ril.request("AT+RNDISCALL=0,1")
--打开 VOLTE功能
ril.request("AT+SETVOLTE=1")
--加载网络指示灯和LTE指示灯功能模块
--根据自己的项目需求和硬件配置决定:1、是否加载此功能模块;2、配置指示灯引脚
--合宙官方出售的Air720U开发板上的网络指示灯引脚为pio.P0_1,LTE指示灯引脚为pio.P0_4
pmd.ldoset(2,pmd.LDO_VLCD)
netLed.setup(true,pio.P0_1,pio.P0_4)
--网络指示灯功能模块中,默认配置了各种工作状态下指示灯的闪烁规律,参考netLed.lua中ledBlinkTime配置的默认值
--如果默认值满足不了需求,此处调用netLed.updateBlinkTime去配置闪烁时长
powerKey.setup(3000,function() end, function() sys.publish("POWER_KEY_IND") end)
--来电铃声播放协程ID
local coIncoming
local function callVolTest()
local curVol = audio.getCallVolume()
curVol = (curVol>=7) and 1 or (curVol+1)
log.info("testCall.setCallVolume",curVol)
audio.setCallVolume(curVol)
end
-- audiocore.streamplay
--通话中向对方播放音频回调函数
local function testAudioStream(streamType)
sys.taskInit(
function()
while true do
tStreamType = streamType
log.info("AudioTest.AudioStreamTest", "AudioStreamPlay Start", streamType)
local tAudioFile =
{
[audiocore.AMR] = "tip.amr",
[audiocore.SPX] = "record.spx",
[audiocore.PCM] = "alarm_door.pcm",
[audiocore.MP3] = "sms.mp3"
}
local fileHandle = io.open("/lua/" .. tAudioFile[streamType], "rb")
if not fileHandle then
log.error("AudioTest.AudioStreamTest", "Open file error")
return
end
while true do
local data = fileHandle:read(streamType == audiocore.SPX and 1200 or 1024)
if not data then
fileHandle:close()
while audiocore.streamremain() ~= 0 do
sys.wait(20)
end
sys.wait(1000)
audiocore.stop() --添加audiocore.stop()接口,否则再次播放会播放不出来
log.warn("AudioTest.AudioStreamTest", "AudioStreamPlay Over")
return
end
local data_len = string.len(data)
local curr_len = 1
while true do
curr_len = curr_len + audiocore.streamplay(tStreamType,string.sub(data,curr_len,-1),audiocore.PLAY_VOLTE)
if curr_len>=data_len then
break
elseif curr_len == 0 then
log.error("AudioTest.AudioStreamTest", "AudioStreamPlay Error", streamType)
return
end
sys.wait(10)
end
sys.wait(10)
end
end
end
)
end
--- “通话已建立”消息处理函数
-- @string num,建立通话的对方号码
-- @return 无
local function connected(num)
log.info("testCall.connected")
coIncoming = nil
--通话中设置mic增益,必须在通话建立以后设置
--audio.setMicGain("call",7)
--通话中音量测试
sys.timerLoopStart(callVolTest,5000)
--通话中向对方播放TTS测试
audio.play(7,"TTS","通话中TTS测试",7,nil,true,2000)
--通话中向对方播放音频
--[[
audio.setVolume(2)
log.info("AudioTest.AudioStreamTest.AMRFilePlayTest", "Start")
testAudioStream(audiocore.AMR)
]]
--110秒之后主动结束通话
sys.timerStart(cc.hangUp,110000,num)
end
--- “通话已结束”消息处理函数
-- @string discReason,通话结束原因值,取值范围如下:
-- "CHUP"表示本端调用cc.hungUp()接口主动挂断
-- "NO ANSWER"表示呼出后,到达对方,对方无应答,通话超时断开
-- "BUSY"表示呼出后,到达对方,对方主动挂断
-- "NO CARRIER"表示通话未建立或者其他未知原因的断开
-- nil表示没有检测到原因值
-- @return 无
local function disconnected(discReason)
coIncoming = nil
log.info("testCall.disconnected",discReason)
sys.timerStopAll(cc.hangUp)
sys.timerStop(callVolTest)
audio.stop()
end
--- “来电”消息处理函数
-- @string num,来电号码
-- @return 无
local function incoming(num)
log.info("testCall.incoming:"..num)
if not coIncoming then
coIncoming = sys.taskInit(function()
--audio.play(1,"TTS","来电话啦",4,function() sys.publish("PLAY_INCOMING_RING_IND") end,true)
audio.play(1,"FILE","/lua/call.mp3",4,function() sys.publish("PLAY_INCOMING_RING_IND") end,true)
sys.waitUntil("PLAY_INCOMING_RING_IND")
end)
sys.subscribe("POWER_KEY_IND",function() audio.stop(function() cc.accept(num) end) end)
end
--[[
if not coIncoming then
coIncoming = sys.taskInit(function()
for i=1,7 do
--audio.play(1,"TTS","来电话啦",i,function() sys.publish("PLAY_INCOMING_RING_IND") end)
audio.play(1,"FILE","/lua/call.mp3",i,function() sys.publish("PLAY_INCOMING_RING_IND") end)
sys.waitUntil("PLAY_INCOMING_RING_IND")
end
--接听来电
--cc.accept(num)
end)
end]]
--接听来电
--cc.accept(num)
end
-- "此处填写自己要拨打的电话号码"
mynumber = "10086"
--- “通话功能模块准备就绪””消息处理函数
-- @return 无
local function ready()
log.info("tesCall.ready")
--呼叫10086
sys.timerStart(cc.dial,10000,mynumber)
end
--- “通话中收到对方的DTMF”消息处理函数
-- @string dtmf,收到的DTMF字符
-- @return 无
local function dtmfDetected(dtmf)
log.info("testCall.dtmfDetected",dtmf)
end
--订阅消息的用户回调函数
--sys.subscribe("CALL_READY",ready)
sys.subscribe("NET_STATE_REGISTERED",ready)
sys.subscribe("CALL_INCOMING",incoming)
sys.subscribe("CALL_CONNECTED",connected)
sys.subscribe("CALL_DISCONNECTED",disconnected)
cc.dtmfDetect(true)
sys.subscribe("CALL_DTMF_DETECT",dtmfDetected)
--启动系统框架
sys.init(0, 0)
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
六、总结
CC 库的通话管理 API 接口共同构成了通话控制的核心功能,使开发者能够高效地管理通话的启动、挂断、接听、参数配置以及附加的通话处理功能。通过合理利用这些接口,开发者可以构建出具备出色通话体验的应用程序,满足用户在多种通话场景下的需求。同时,也需关注接口之间的协同配合,以确保通话功能的流畅性和可靠性。
七、常见问题
- 打不了电话,确认能不能正常注册上网络,有没有欠费。确认卡是否开通 VOLTE 功能, 只有开通 VOLTE 功能才能进行语音通话。固件是否支持 VOLTE 功能,固件需要支持 VOLTE 功能。注:建议使用手机卡测试,普通物联网卡可能不支持 VOLTE 功能。
- audio.setMicGain()通话中设置 mic 增益,必须在通话建立以后设置。
- 暂不支持视频通话,且因为只有 30W 像素,效果较差。
八、扩展
通话过程中给对端播放音频文件:demo 有案例打开代码中注掉的部分即可进行测试,如图所示。
给读者的话
本篇文章由
永仔
开发;本篇文章描述的内容,如果有错误、细节缺失、细节不清晰或者其他任何问题,总之就是无法解决您遇到的问题;
请登录合宙技术交流论坛,点击文档找错赢奖金-Air724UG-LuatOS-软件指南-音频应用-语音通话;
我们会迅速核实并且修改文档;
同时也会为您累计找错积分,您还可能赢取月度找错奖金!