| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- --- 模块功能:通话管理
- -- @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 result,true表示允许发送at命令拨号并且发送at,false表示不允许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.2K,AMR格式的数据
- -- @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 =
- pm.sleep('cc')
- end
- end
- end)
- --开启拨号音,忙音检测
- req("ATX4")
- --开启来电urc上报
- req("AT+CLIP=1")
- req("ATS7=60")
- req("AT+SETVOLTE=1")
|