talk.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. --[[
  2. @module talk
  3. @summary Airtalk 对讲业务核心模块
  4. @date 2025.12.03
  5. @author 陈媛媛
  6. @usage
  7. 本demo演示的核心功能为:
  8. 1. 支持广播对讲(一对多)和一对一对讲;
  9. 2. 自动设备发现和管理;
  10. 3. 按一次Boot键选择指定设备,开始1对1对讲,再按一次Boot键或powerkey键结束对讲;
  11. 4. 按一次powerkey键开始一对多广播,再按一次Boot键或powerkey键结束广播;
  12. 5. 通过LED指示灯显示对讲状态(亮:对讲中,灭:空闲);
  13. 6. 支持目标设备ID指定呼叫,可配置TARGET_DEVICE_ID呼叫特定设备;
  14. 7. 支持4G和WiFi两种联网方式,默认使用4G网络。
  15. ]]
  16. local extalk = require "extalk"
  17. local audio_drv = require "audio_drv" -- 引入音频驱动模块
  18. -- 配置日志格式
  19. log.style(1)
  20. -- 常量定义
  21. local USER_TASK_NAME = "user_task" -- 用户任务名称
  22. local MSG_KEY_PRESS = 12 -- 按键消息类型
  23. local MSG_NETWORK_READY = 13 -- 网络就绪消息类型
  24. -- 目标设备ID,修改为你想要对讲的终端ID
  25. TARGET_DEVICE_ID = "78122397"
  26. -- 全局状态变量
  27. local g_dev_list = nil -- 设备列表,存储所有可用对讲设备
  28. local g_speech_active = false -- 对讲状态标记,true表示正在对讲中
  29. local g_network_ready = false -- 网络就绪标志
  30. -- 指示灯配置
  31. -- Air8000核心板:GPIO20(核心板板载LED指示灯)
  32. -- Air8000开发板:GPIO146(开发板上的LED指示灯)
  33. local LED_GPIO = 146
  34. local LED = nil
  35. -- ========================== 联网方式配置 ==========================
  36. -- WiFi连接参数(如果需要使用WiFi联网,请取消注释use_wifi函数调用)
  37. local WIFI_CONFIG = {
  38. ssid = "茶室-降功耗,找合宙!",
  39. password = "Air123456",
  40. }
  41. -- 低功耗模式配置
  42. local POWER_SAVE_CONFIG = {
  43. enable_wifi_low_power = true, -- 启用WiFi低功耗
  44. enable_4g_low_power = true, -- 启用4G低功耗
  45. pause_airlink = true, -- 暂停airlink通信
  46. }
  47. -- ========================== 联系人列表回调 ==========================
  48. -- 联系人列表回调函数
  49. local function contact_list_callback(dev_list)
  50. g_dev_list = dev_list
  51. if dev_list and #dev_list > 0 then
  52. log.info("联系人列表更新:")
  53. for i = 1, #dev_list do
  54. log.info(string.format(" %d. ID: %s, 名称: %s",
  55. i, dev_list[i]["id"], dev_list[i]["name"] or "未知"))
  56. end
  57. else
  58. log.info("联系人列表为空")
  59. end
  60. end
  61. -- ========================== 对讲状态回调 ==========================
  62. -- 对讲状态回调函数
  63. local function speech_state_callback(event_table)
  64. if not event_table then return end
  65. if event_table.state == extalk.START then
  66. log.info("对讲开始")
  67. if LED then LED(1) end -- LED亮
  68. g_speech_active = true
  69. elseif event_table.state == extalk.STOP then
  70. if LED then LED(0) end -- LED灭
  71. log.info("对讲结束")
  72. g_speech_active = false
  73. elseif event_table.state == extalk.UNRESPONSIVE then
  74. if LED then LED(0) end -- LED灭
  75. log.info("对端未响应")
  76. g_speech_active = false
  77. elseif event_table.state == extalk.ONE_ON_ONE then
  78. if LED then LED(1) end -- LED亮
  79. g_speech_active = true
  80. local dev_name = "未知设备"
  81. if g_dev_list then
  82. for i = 1, #g_dev_list do
  83. if g_dev_list[i]["id"] == event_table.id then
  84. dev_name = g_dev_list[i]["name"] or "未知设备"
  85. break
  86. end
  87. end
  88. end
  89. log.info(string.format("%s 来电", dev_name))
  90. elseif event_table.state == extalk.BROADCAST then
  91. if LED then LED(1) end -- LED亮
  92. g_speech_active = true
  93. local dev_name = "未知设备"
  94. if g_dev_list then
  95. for i = 1, #g_dev_list do
  96. if g_dev_list[i]["id"] == event_table.id then
  97. dev_name = g_dev_list[i]["name"] or "未知设备"
  98. break
  99. end
  100. end
  101. end
  102. log.info(string.format("%s 开始广播", dev_name))
  103. end
  104. log.info("当前对讲状态:", g_speech_active and "正在对讲" or "空闲")
  105. end
  106. -- ========================== extalk配置 ==========================
  107. -- extalk配置参数
  108. local extalk_configs = {
  109. key = PRODUCT_KEY, -- 产品密钥,从main.lua传入
  110. heart_break_time = 120, -- 心跳间隔(单位秒)
  111. contact_list_cbfnc = contact_list_callback,
  112. state_cbfnc = speech_state_callback,
  113. }
  114. -- ========================== 按键处理 ==========================
  115. -- Boot键回调函数
  116. local function boot_key_callback()
  117. log.info("boot_key_callback")
  118. sys.sendMsg(USER_TASK_NAME, MSG_KEY_PRESS, false) -- false表示Boot键
  119. end
  120. -- Power键回调函数
  121. local function power_key_callback()
  122. log.info("power_key_callback")
  123. sys.sendMsg(USER_TASK_NAME, MSG_KEY_PRESS, true) -- true表示Power键
  124. end
  125. -- 网络状态回调函数
  126. local function network_status_callback(net_type, adapter)
  127. log.info("网络切换至:", net_type)
  128. if net_type and not g_network_ready then
  129. log.info("网络已就绪,准备初始化对讲功能")
  130. g_network_ready = true
  131. sys.sendMsg(USER_TASK_NAME, MSG_NETWORK_READY, {net_type = net_type, adapter = adapter})
  132. end
  133. end
  134. -- WiFi STA连接事件回调函数
  135. local function wlan_sta_callback(evt, data)
  136. -- evt 可能的值有: "CONNECTED", "DISCONNECTED"
  137. -- 当evt=CONNECTED, data是连接的AP的ssid, 字符串类型
  138. -- 当evt=DISCONNECTED, data断开的原因, 整数类型
  139. log.info("WiFi STA事件", evt, data)
  140. if evt == "CONNECTED" then
  141. log.info("WiFi已连接,等待获取IP地址")
  142. end
  143. end
  144. -- IP就绪事件回调
  145. local function ip_ready_callback(ip, adapter)
  146. log.info("IP就绪事件", ip, adapter)
  147. if not g_network_ready and ip and ip ~= "0.0.0.0" then
  148. log.info("IP地址已获取,网络就绪")
  149. g_network_ready = true
  150. -- 获取网卡类型名称
  151. local adapter_names = {
  152. [socket.LWIP_ETH] = "以太网",
  153. [socket.LWIP_STA] = "WiFi",
  154. [socket.LWIP_GP] = "4G",
  155. [socket.LWIP_USER1] = "8101SPI以太网"
  156. }
  157. local adapter_name = adapter_names[adapter] or "未知"
  158. sys.sendMsg(USER_TASK_NAME, MSG_NETWORK_READY, {net_type = adapter_name, adapter = adapter, ip = ip})
  159. end
  160. end
  161. -- 初始化按键
  162. local function init_buttons()
  163. -- 配置Boot键 (GPIO0),下拉电阻,上升沿触发
  164. gpio.setup(0, boot_key_callback, gpio.PULLDOWN, gpio.RISING)
  165. gpio.debounce(0, 200, 1) -- 200ms去抖
  166. -- 配置Power键,上拉电阻,下降沿触发
  167. gpio.setup(gpio.PWR_KEY, power_key_callback, gpio.PULLUP, gpio.FALLING)
  168. gpio.debounce(gpio.PWR_KEY, 200, 1) -- 200ms去抖
  169. end
  170. -- 处理按键消息
  171. local function handle_key_press(is_power_key)
  172. if g_speech_active then
  173. -- 当前正在对讲,按任何键都结束对讲
  174. log.info("结束当前对讲")
  175. extalk.stop()
  176. if LED then LED(0) end -- 关闭LED
  177. g_speech_active = false
  178. else
  179. -- 当前未在对讲,根据按键类型开始不同对讲
  180. if is_power_key then
  181. -- Power键:开始一对多广播
  182. log.info("开始一对多广播")
  183. extalk.start() -- 不带参数表示广播
  184. else
  185. -- Boot键:开始一对一对讲
  186. -- 只呼叫指定的目标设备,不查找其他设备
  187. if TARGET_DEVICE_ID and TARGET_DEVICE_ID ~= "" then
  188. -- 直接呼叫指定设备
  189. log.info("开始一对一对讲,目标设备:", TARGET_DEVICE_ID)
  190. extalk.start(TARGET_DEVICE_ID)
  191. else
  192. log.error("无法开始一对一对讲,未配置目标设备ID")
  193. log.error("请在talk.lua中设置TARGET_DEVICE_ID变量")
  194. end
  195. end
  196. end
  197. end
  198. -- ========================== 联网功能 ==========================
  199. -- WiFi功能(可选)
  200. local function use_wifi()
  201. log.info("配置WiFi联网...")
  202. local exnetif = require("exnetif")
  203. -- 设置网络优先级,WiFi作为次优先级
  204. exnetif.set_priority_order({ {
  205. WIFI = {
  206. ssid = WIFI_CONFIG.ssid,
  207. password = WIFI_CONFIG.password,
  208. }
  209. }})
  210. -- 设置网络状态回调
  211. exnetif.notify_status(network_status_callback)
  212. -- 订阅WiFi STA连接事件
  213. sys.subscribe("WLAN_STA_INC", wlan_sta_callback)
  214. -- 订阅IP就绪事件(作为备份,以防exnetif回调不触发)
  215. sys.subscribe("IP_READY", ip_ready_callback)
  216. end
  217. -- 等待低功耗模式设置的函数
  218. local function wait_low_power_setup()
  219. -- 使用sys.wait等待低功耗模式设置生效
  220. -- 原因:需要等待硬件完成低功耗切换,通常需要几毫秒到几十毫秒
  221. -- 使用20ms的等待时间,确保设置生效
  222. sys.wait(20) -- 等待20ms,确保低功耗模式设置生效
  223. end
  224. -- 低功耗模式(可选)
  225. local function lower_enter()
  226. log.info("进入低功耗模式...")
  227. if POWER_SAVE_CONFIG.enable_wifi_low_power then
  228. -- WiFi模组进入低功耗模式
  229. pm.power(pm.WORK_MODE, 1, 1)
  230. log.info("WiFi低功耗模式已启用")
  231. end
  232. if POWER_SAVE_CONFIG.enable_4g_low_power then
  233. -- 4G模组进入低功耗模式
  234. pm.power(pm.WORK_MODE, 1)
  235. log.info("4G低功耗模式已启用")
  236. end
  237. -- 等待低功耗模式设置生效
  238. wait_low_power_setup()
  239. if POWER_SAVE_CONFIG.pause_airlink then
  240. -- 暂停airlink通信,进一步降低功耗
  241. airlink.pause(1)
  242. log.info("airlink通信已暂停")
  243. end
  244. log.info("低功耗模式配置完成")
  245. end
  246. -- ========================== 主任务 ==========================
  247. -- 检查网络状态
  248. local function check_network_status()
  249. log.info("主动检查网络状态...")
  250. -- 获取当前默认网卡
  251. local default_adapter = socket.dft()
  252. if default_adapter then
  253. local adapter_names = {
  254. [socket.LWIP_ETH] = "以太网",
  255. [socket.LWIP_STA] = "WiFi",
  256. [socket.LWIP_GP] = "4G",
  257. [socket.LWIP_USER1] = "8101SPI以太网"
  258. }
  259. local adapter_name = adapter_names[default_adapter] or "未知"
  260. log.info("当前默认网卡:", adapter_name, "适配器ID:", default_adapter)
  261. -- 获取当前默认网卡的IP地址
  262. local ip, mask, gw = socket.localIP()
  263. if ip and ip ~= "0.0.0.0" then
  264. log.info("IP地址:", ip, "子网掩码:", mask, "网关:", gw)
  265. return true, {net_type = adapter_name, adapter = default_adapter, ip = ip}
  266. else
  267. log.warn("默认网卡未获取到有效IP地址")
  268. end
  269. else
  270. log.warn("无法获取当前默认网卡")
  271. end
  272. return false
  273. end
  274. -- 初始化对讲功能
  275. local function init_extalk()
  276. log.info("初始化extalk对讲功能...")
  277. local extalk_init_ok = extalk.setup(extalk_configs)
  278. if not extalk_init_ok then
  279. log.error("extalk初始化失败")
  280. return false
  281. end
  282. log.info("extalk初始化成功")
  283. return true
  284. end
  285. -- 等待网络就绪
  286. local function wait_for_network()
  287. log.info("等待网络连接就绪...")
  288. -- 等待网络就绪消息,但也会主动检查
  289. local max_wait_time = 30000 -- 最长等待30秒
  290. local start_time = mcu.ticks()
  291. local network_ready = false
  292. while not network_ready and (mcu.ticks() - start_time < max_wait_time) do
  293. -- 等待网络就绪消息或主动检查
  294. local msg = sys.waitMsg(USER_TASK_NAME, MSG_NETWORK_READY, 2000)
  295. if msg and msg[1] == MSG_NETWORK_READY then
  296. log.info("收到网络就绪消息:", msg[2].net_type, "IP:", msg[2].ip or "未知")
  297. network_ready = true
  298. else
  299. -- 主动检查网络状态
  300. local status_ok, network_info = check_network_status()
  301. if status_ok and not network_ready then
  302. log.info("主动检查发现网络已就绪")
  303. network_ready = true
  304. sys.sendMsg(USER_TASK_NAME, MSG_NETWORK_READY, network_info)
  305. end
  306. end
  307. -- 超时检查
  308. if mcu.ticks() - start_time > max_wait_time then
  309. log.warn("网络连接超时,尝试强制初始化对讲")
  310. break
  311. end
  312. end
  313. return network_ready
  314. end
  315. -- 用户主任务
  316. local function user_main_task()
  317. log.info("启动对讲系统...")
  318. -- 初始化LED指示灯
  319. LED = gpio.setup(LED_GPIO, 1)
  320. if LED then
  321. LED(0) -- 初始状态关闭
  322. log.info("LED指示灯初始化完成 - GPIO"..LED_GPIO)
  323. else
  324. log.warn("LED初始化失败,GPIO"..LED_GPIO)
  325. end
  326. -- 初始化音频设备
  327. log.info("初始化音频设备...")
  328. if not audio_drv.init() then
  329. log.error("音频初始化失败")
  330. return
  331. end
  332. log.info("音频初始化成功")
  333. -- 【可选】使用WiFi联网(取消注释以启用)
  334. -- use_wifi()
  335. -- 【可选】进入低功耗模式(取消注释以启用)
  336. -- lower_enter()
  337. -- 等待网络就绪
  338. local network_ready = wait_for_network()
  339. -- 如果网络就绪,初始化对讲功能
  340. if network_ready then
  341. if not init_extalk() then
  342. log.error("对讲功能初始化失败,系统无法正常工作")
  343. return
  344. end
  345. else
  346. log.warn("网络未就绪,但尝试强制初始化对讲")
  347. if not init_extalk() then
  348. log.error("对讲功能初始化失败,系统无法正常工作")
  349. return
  350. end
  351. end
  352. log.info("对讲系统准备就绪,等待按键操作...")
  353. -- 主消息循环 - 等待和处理按键消息
  354. while true do
  355. local msg = sys.waitMsg(USER_TASK_NAME, MSG_KEY_PRESS)
  356. if msg and msg[1] == MSG_KEY_PRESS then
  357. handle_key_press(msg[2]) -- msg[2]区分Power键(true)和Boot键(false)
  358. end
  359. end
  360. end
  361. -- ========================== 初始化 ==========================
  362. -- 系统初始化
  363. local function init()
  364. log.info("对讲模块初始化...")
  365. init_buttons()
  366. -- 使用sys.taskInitEx创建支持waitMsg的任务
  367. sys.taskInitEx(user_main_task, USER_TASK_NAME)
  368. end
  369. -- 直接初始化,无需等待
  370. init()
  371. log.info("talk.lua加载完成")