Files
BR_YKC/4G/code/lib/cc.lua
2026-03-31 15:46:04 +08:00

248 lines
7.1 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--- 模块功能:通话管理
-- @module cc
-- @author openLuat
-- @license MIT
-- @copyright openLuat
-- @release 2017.11.2
module(..., package.seeall)
require"ril"
require"pm"
--- 通话中
CONNECTED = 0
--- 通话保持中
HOLD = 1
--- 正在呼出
DIALING = 2
ALERTING = 3
--- 正在呼入
INCOMING = 4
WAITING = 5
--- 正在挂断通话
DISCONNECTING = 98
--- 通话已挂断
DISCONNECTED = 99
local req = ril.request
local publish = sys.publish
--底层通话模块是否准备就绪true就绪false或者nil未就绪
local ccready = false
--通话列表
local call_list = {n= 0}
--通话断开原因
local discReason
--- 是否存在通话
-- @return bool result 存在通话返回true否则返回false
-- @usage result = cc.anyCallExist()
function anyCallExist()
return call_list.n ~= 0
end
--- 查询某个号码的通话状态
-- @string num 查询号码
-- @return number state 通话状态状态值参考本模块Fields定义
-- @usage state = cc.getState('10086')
function getState(num)
return call_list[num] or DISCONNECTED
end
--- 呼出电话
-- @string num 呼出号码
-- @number[opt=0] delay 延时delay毫秒后才发起呼叫
-- @return bool resulttrue表示允许发送at命令拨号并且发送atfalse表示不允许at命令拨号
-- @usage cc.dial('10086')
function dial(num, delay)
if num == "" or num == nil then return false end
pm.wake("cc")
req(string.format("%s%s;", "ATD", num), nil, nil, delay)
call_list[num] = DIALING
return true
end
--- 挂断通话
-- @string num 号码,若指定号码通话状态不对,则直接退出,不会执行挂断,若挂断时会挂断所有电话
-- @return nil
-- @usage cc.hangUp('10086')
function hangUp(num)
if call_list[num] == DISCONNECTING or call_list[num] == DISCONNECTED then return end
if audio and type(audio.stop)=="function" then audio.stop() end
req("AT+CHUP")
call_list[num] = DISCONNECTING
end
--- 接听电话
-- @string num 号码,若指定号码通话状态不对,则直接退出,不会接通
-- @return nil
-- @usage cc.accept('10086')
function accept(num)
if call_list[num] ~= INCOMING then return end
if audio and type(audio.stop)=="function" then audio.stop() end
req("ATA")
call_list[num] = CONNECTING
end
--- 通话中发送声音到对端,必须是12.2K AMR格式
-- @string data 12.2KAMR格式的数据
-- @bool[opt=nil] loop 是否循环发送true为循环其余为不循环
-- @bool[opt=nil] downLinkPlay 声音是否在本端播放true为播放其余为不播放
-- @return bool result true为成功false为失败
-- @usage
-- cc.transVoice("#!AMR\010\060*********")
-- cc.transVoice("#!AMR\010\060*********",true)
-- cc.transVoice("#!AMR\010\060*********",true,true)
function transVoice(data, loop, downLinkPlay)
local f = io.open("/RecDir/rec000", "wb")
if f == nil then
log.error("transVoice:open file error")
return false
end
-- 有文件头并且是12.2K帧
if string.sub(data, 1, 7) == "#!AMR\010\060" then
-- 无文件头且是12.2K帧
elseif string.byte(data, 1) == 0x3C then
f:write("#!AMR\010")
else
log.error('cc.transVoice', 'must be 12.2K AMR')
return false
end
f:write(data)
f:close()
req(string.format("AT+AUDREC=%d,%d,2,0,50000", downLinkPlay == true and 1 or 0, loop == true and 1 or 0))
return true
end
--- 设置dtmf检测是否使能以及灵敏度
-- @bool[opt=nil] enable true使能false或者nil为不使能
-- @number[opt=3] sens 灵敏度最灵敏为1
-- @return nil
-- @usage cc.dtmfDetect(true)
function dtmfDetect(enable, sens)
if enable == true then
if sens then
req("AT+DTMFDET=2,1," .. sens)
else
req("AT+DTMFDET=2,1,3")
end
end
req("AT+DTMFDET=" .. (enable and 1 or 0))
end
--- 发送dtmf到对端
-- @string str dtmf字符串仅支持数字、ABCD*#
-- @number[opt=100] playtime 每个dtmf播放时间单位毫秒
-- @number[opt=100] intvl 两个dtmf间隔单位毫秒
-- @return nil
-- @usage cc.sendDtmf("123")
function sendDtmf(str, playtime, intvl)
if string.match(str, "([%dABCD%*#]+)") ~= str then
log.error("sendDtmf: illegal string " .. str)
return false
end
playtime = playtime and playtime or 100
intvl = intvl and intvl or 100
--req("AT+SENDSOUND=" .. string.format("\"%s\",%d,%d", str, playtime, intvl))
req("AT+VTS=".. str)
end
local dtmfnum = { [71] = "Hz1000", [69] = "Hz1400", [70] = "Hz2300" }
local function parsedtmfnum(data)
local n = tonumber(string.match(data, "(%d+)"))
local dtmf
if (n >= 48 and n <= 57) or (n >= 65 and n <= 68) or n == 42 or n == 35 then
dtmf = string.char(n)
else
dtmf = dtmfnum[n]
end
if dtmf then
publish("CALL_DTMF_DETECT", dtmf) -- 通话中dtmf解码会产生消息AUDIO_DTMF_DETECT消息数据为DTMF字符
end
end
local function ccurc(data, prefix)
if data == "CALL READY" then --底层通话模块准备就绪
ccready = true
publish("CALL_READY")
req("AT+CCWA=1")
elseif prefix == "+DTMFDET" then
parsedtmfnum(data)
else
if data=="NO CARRIER" or data=="NO ANSWER" or data=="BUSY" then
discReason = data
end
req('AT+CLCC')
if data == "CONNECT" and audio and type(audio.stop)=="function" then audio.stop() end --先停止音频播放
end
end
local function ccrsp(cmd, success, response, intermediate)
if cmd=="AT+CHUP" then
discReason = "CHUP"
end
req('AT+CLCC')
end
--注册以下通知的处理函数
ril.regUrc("CALL READY", ccurc)
ril.regUrc("CONNECT", ccurc)
ril.regUrc("NO CARRIER", ccurc)
ril.regUrc("NO ANSWER", ccurc)
ril.regUrc("BUSY", ccurc)
ril.regUrc("+CLIP", ccurc)
ril.regUrc("+CCWA", ccurc)
ril.regUrc("+DTMFDET", ccurc)
--注册以下AT命令的应答处理函数
ril.regRsp("D", ccrsp)
ril.regRsp("A", ccrsp)
ril.regRsp("+CHUP", ccrsp)
ril.regRsp("+CHLD", ccrsp)
ril.regRsp("+CLCC", function(cmd, success, response, intermediate)
if success then
local new = {n = 0 }
if intermediate and intermediate:len() > 0 then
for id, dir, stat, num in intermediate:gmatch('%+CLCC:%s*(%d+),(%d),(%d),%d,%d,"([^"]*)".-\r\n') do
stat = tonumber(stat)
if stat == WAITING then
req('AT+CHLD=1' .. id)
return
end
if call_list[num] ~= stat then
if stat == INCOMING or stat == CONNECTED then
pm.wake('cc')
publish(stat == INCOMING and 'CALL_INCOMING' or 'CALL_CONNECTED', num)
end
end
new[num] = stat
new.n = new.n + 1
end
end
call_list = new
if new.n == 0 then
publish('CALL_DISCONNECTED',discReason)
discReason = nil
pm.sleep('cc')
end
end
end)
--开启拨号音,忙音检测
req("ATX4")
--开启来电urc上报
req("AT+CLIP=1")
req("ATS7=60")
req("AT+SETVOLTE=1")