cc.lua 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. --- 模块功能 : 通话管理
  2. -- @module cc
  3. -- @author 小强
  4. -- @license MIT
  5. -- @copyright openLuat.com
  6. -- @release 2017.11.2
  7. module(..., package.seeall)
  8. -- 通话状态
  9. CONNECTED = 0
  10. HOLD = 1
  11. DIALING = 2
  12. ALERTING = 3
  13. INCOMING = 4
  14. WAITING = 5
  15. DISCONNECTING = 98
  16. DISCONNECTED = 99
  17. local req = ril.request
  18. local publish = sys.publish
  19. --底层通话模块是否准备就绪,true就绪,false或者nil未就绪
  20. local ccready = false
  21. --通话列表
  22. local call_list = {n= 0}
  23. --- 是否存在通话
  24. -- @return result 存在通话返回true,否则返回false
  25. -- @usage result = cc.anycallexist()
  26. function anycallexist()
  27. return call_list.n ~= 0
  28. end
  29. --- 查询某个号码的通话状态
  30. -- @param num 查询号码
  31. -- @return state 通话状态,状态值参考本模块定义
  32. -- @usage state = cc.getState('10086')
  33. function getState(num)
  34. return call_list[num] or DISCONNECTED
  35. end
  36. --- 拨号
  37. -- @param number 号码
  38. -- @param delay 延时delay毫秒后,才发送at命令呼叫,默认不延时
  39. -- @return result true表示允许发送at命令拨号并且发送at,false表示不允许at命令拨号
  40. -- @usage cc.dial('10086')
  41. function dial(number, delay)
  42. if number == "" or number == nil then return false end
  43. pm.wake("cc")
  44. req(string.format("%s%s;", "ATD", number), nil, nil, delay)
  45. call_list[number] = DIALING
  46. return true
  47. end
  48. --- 挂断所有通话
  49. -- @param num 号码,若指定号码通话状态不对 则直接退出 不会执行挂断,若挂断时会挂断所有电话
  50. -- @return
  51. -- @usage cc.hangup('10086')
  52. function hangup(num)
  53. if call_list[num] == DISCONNECTING or call_list[num] == DISCONNECTED then return end
  54. audio.stop()
  55. req("AT+CHUP")
  56. call_list[num] = DISCONNECTING
  57. end
  58. --- 接听电话
  59. -- @param num 号码,若指定号码通话状态不对 则直接退出 不会接通
  60. -- @return
  61. -- @usage cc.accept('10086')
  62. function accept(num)
  63. if call_list[num] ~= INCOMING then return end
  64. audio.stop()
  65. req("ATA")
  66. call_list[num] = CONNECTING
  67. end
  68. --- 通话中发送声音到对端,必须是12.2K AMR格式
  69. -- @param data
  70. -- @param loop
  71. -- @param loop2
  72. -- @return result true为成功,false为失败
  73. -- @usage
  74. function transvoice(data, loop, loop2)
  75. local f = io.open("/RecDir/rec000", "wb")
  76. if f == nil then
  77. log_print("transvoice:open file error")
  78. return false
  79. end
  80. -- 有文件头并且是12.2K帧
  81. if string.sub(data, 1, 7) == "#!AMR\010\060" then
  82. -- 无文件头且是12.2K帧
  83. elseif string.byte(data, 1) == 0x3C then
  84. f:write("#!AMR\010")
  85. else
  86. log.error('cc.transvoice', 'must be 12.2K AMR')
  87. return false
  88. end
  89. f:write(data)
  90. f:close()
  91. req(string.format("AT+AUDREC=%d,%d,2,0,50000", loop2 == true and 1 or 0, loop == true and 1 or 0))
  92. return true
  93. end
  94. --- 设置dtmf检测是否使能以及灵敏度
  95. -- @param enable true使能,false或者nil为不使能
  96. -- @param sens 灵敏度,默认3,最灵敏为1
  97. -- @return
  98. -- @usage cc.dtmfdetect(true)
  99. function dtmfdetect(enable, sens)
  100. if enable == true then
  101. if sens then
  102. req("AT+DTMFDET=2,1," .. sens)
  103. else
  104. req("AT+DTMFDET=2,1,3")
  105. end
  106. end
  107. req("AT+DTMFDET=" .. (enable and 1 or 0))
  108. end
  109. --- 发送dtmf到对端
  110. -- @param str dtmf字符串
  111. -- @param playtime 每个dtmf播放时间,单位毫秒,默认100
  112. -- @param intvl 两个dtmf间隔,单位毫秒,默认100
  113. -- @return 无
  114. -- @usage cc.senddtmf("123")
  115. function senddtmf(str, playtime, intvl)
  116. if string.match(str, "([%dABCD%*#]+)") ~= str then
  117. log_print("senddtmf: illegal string " .. str)
  118. return false
  119. end
  120. playtime = playtime and playtime or 100
  121. intvl = intvl and intvl or 100
  122. req("AT+SENDSOUND=" .. string.format("\"%s\",%d,%d", str, playtime, intvl))
  123. end
  124. local dtmfnum = { [71] = "Hz1000", [69] = "Hz1400", [70] = "Hz2300" }
  125. local function parsedtmfnum(data)
  126. local n = tonumber(string.match(data, "(%d+)"))
  127. local dtmf
  128. if (n >= 48 and n <= 57) or (n >= 65 and n <= 68) or n == 42 or n == 35 then
  129. dtmf = string.char(n)
  130. else
  131. dtmf = dtmfnum[n]
  132. end
  133. if dtmf then
  134. publish("CALL_DTMF_DETECT", dtmf) -- 通话中dtmf解码会产生消息AUDIO_DTMF_DETECT,消息数据为DTMF字符
  135. end
  136. end
  137. local function ccurc(data, prefix)
  138. if data == "CALL READY" then --底层通话模块准备就绪
  139. ccready = true
  140. publish("CALL_READY")
  141. req("AT+CCWA=1")
  142. elseif prefix == "+DTMFDET" then
  143. parsedtmfnum(data)
  144. else
  145. req('AT+CLCC')
  146. if data == "CONNECT" then audio.stop() end --先停止音频播放
  147. end
  148. end
  149. local function ccrsp() req('AT+CLCC') end
  150. --注册以下通知的处理函数
  151. ril.regurc("CALL READY", ccurc)
  152. ril.regurc("CONNECT", ccurc)
  153. ril.regurc("NO CARRIER", ccurc)
  154. ril.regurc("NO ANSWER", ccurc)
  155. ril.regurc("BUSY", ccurc)
  156. ril.regurc("+CLIP", ccurc)
  157. ril.regurc("+CCWA", ccurc)
  158. ril.regurc("+DTMFDET", ccurc)
  159. --注册以下AT命令的应答处理函数
  160. ril.regrsp("D", ccrsp)
  161. ril.regrsp("A", ccrsp)
  162. ril.regrsp("+CHUP", ccrsp)
  163. ril.regrsp("+CHLD", ccrsp)
  164. ril.regrsp("+CLCC", function(cmd, success, response, intermediate)
  165. if success then
  166. local new = {n = 0 }
  167. if intermediate and intermediate:len() > 0 then
  168. for id, dir, stat, num in intermediate:gmatch('%+CLCC:%s*(%d+),(%d),(%d),%d,%d,"([^"]*)".-\r\n') do
  169. stat = tonumber(stat)
  170. if stat == WAITING then
  171. req('AT+CHLD=1' .. id)
  172. return
  173. end
  174. if call_list[num] ~= stat then
  175. if stat == INCOMING or stat == CONNECTED then
  176. pm.wake('cc')
  177. publish(stat == INCOMING and 'CALL_INCOMING' or 'CALL_CONNECTED', num)
  178. end
  179. end
  180. new[num] = stat
  181. new.n = new.n + 1
  182. end
  183. end
  184. call_list = new
  185. if new.n == 0 then
  186. publish('CALL_DISCONNECTED')
  187. pm.sleep('cc')
  188. end
  189. end
  190. end)
  191. --开启拨号音,忙音检测
  192. req("ATX4")
  193. --开启来电urc上报
  194. req("AT+CLIP=1")