air720u.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #include "air720u.h"
  2. #include "../swapi/subjects/serial/serial.h"
  3. // 模块名称
  4. static const char MODULE_NAME[] = "Air720U";
  5. #define MAX_CMD_CHARS 65535 // 单条指令最大字符数
  6. #define MAX_ACK_CHARS 65535 // 指令应答最大字符数
  7. #define WAIT_ACK_TIMEOUT 9000 // 等待应答超时时间ms
  8. #define R_SILENT_TIMEOUT 50 // 应答静默超时时间ms
  9. // 关闭命令回显模式
  10. static const char *AT_ECHO_OFF = "ATE0" ;
  11. // 获取4G模块的型号
  12. static const char *AT_GET_CGMM = "AT+CGMM" ;
  13. // 获取模块的IMEI号
  14. static const char *AT_GET_IMEI = "AT+CGSN" ;
  15. // 查看网络注册状态
  16. static const char *AT_GET_CREG = "AT+CREG?";
  17. // 获取当前信号强度
  18. static const char *AT_GET_CSQ = "AT+CSQ" ;
  19. // 获取SIM-PIN状态
  20. static const char *AT_GET_PINSTA = "AT+CPIN?";
  21. // 获取SIM-ICCID号
  22. static const char *AT_GET_ICCID = "AT+ICCID";
  23. // AT指令请求和应答
  24. typedef struct
  25. {
  26. // 指令
  27. struct
  28. {
  29. char data[MAX_CMD_CHARS+1]; // 指令数据(字符串)
  30. int len; // 指令长度
  31. } cmd;
  32. // 等待执行结束的超时ms
  33. int timeout;
  34. // 等待执行结束的信号量
  35. void *hEndSgl;
  36. // 应答
  37. struct
  38. {
  39. int ret; // 返回值: >0时执行成功, 表明应答数据的长度; <0时出错(-1超时)
  40. char data[MAX_ACK_CHARS+1]; // 应答数据(字符串)
  41. } ack;
  42. } SATCmdTrans;
  43. // 与模块的串口通讯
  44. typedef struct
  45. {
  46. void *h; // 打开的串口句柄
  47. void *do_cmd_mutex; // 指令执行互斥量, 同一时刻正在执行的指令只能有一条
  48. SATCmdTrans *pTrans; // 执行的指令及其应答结果: 空闲时为空, 工作时不为空
  49. } SAir720UCom;
  50. static SAir720UCom s_myCom;
  51. // 定义AT指令格式符
  52. #define CR '\r' // 回车
  53. #define LF '\n' // 换行
  54. #define CRLF "\r\n" // 回车换行
  55. #define ATOK "\r\nOK\r\n" // 应答成功
  56. #define ATER "ERROR" // 应答失败
  57. // 执行一条指令, 返回: 大于0成功; 小于0失败(-1命令超时)
  58. static int comio_doCmd(SATCmdTrans *pTrans/*要执行的指令(IN&OUT)*/, int timeout/*等待应答的超时时间, 单位是毫秒*/)
  59. {
  60. SAir720UCom *pComIO = &s_myCom; const char *log_prefix = serial_get_log_prefix(pComIO->h);
  61. unsigned long stime, wtime, etime; char *pAtCmdStr; int atCmdLen, ret;
  62. xgettickcount(&stime);
  63. ret = sw_mutex_lock(pComIO->do_cmd_mutex, timeout);
  64. if(ret != 0) { pTrans->ack.ret = -1; goto end_p; } // 互斥量上锁超时
  65. xgettickcount(&etime); wtime = etime - stime;
  66. pTrans->hEndSgl = sw_signal_create();
  67. if(!pTrans->hEndSgl) { pTrans->ack.ret = -2; goto end_p; }
  68. pAtCmdStr = (char *)sw_heap_malloc(pTrans->cmd.len+2);
  69. if(!pAtCmdStr) { pTrans->ack.ret = -3; goto end_p; }
  70. memcpy(pAtCmdStr, pTrans->cmd.data, pTrans->cmd.len);
  71. pAtCmdStr[pTrans->cmd.len] = CR; pAtCmdStr[pTrans->cmd.len+1] = '\0';
  72. atCmdLen = strlen((const char *)pAtCmdStr);
  73. serial_recvThrd_pause(pComIO->h); sw_thrd_delay(THRDWAIT_DELAY); // "pComIO->pTrans"的控制权转移前, 先暂停串口数据的接收
  74. ret = serial_send_data(pComIO->h, (unsigned char *)pAtCmdStr, atCmdLen); // 发送一条指令
  75. sw_heap_free(pAtCmdStr); // 释放AT指令字符串内存
  76. if(ret == atCmdLen)
  77. {
  78. sw_log_trace("[%s] %s send a cmd(%d bytes): %s", MODULE_NAME, log_prefix, pTrans->cmd.len, pTrans->cmd.data);
  79. }
  80. else
  81. {
  82. sw_log_error("[%s] %s failed to send a cmd(%d bytes, ret=%d): %s!!", MODULE_NAME, log_prefix, pTrans->cmd.len, ret, pTrans->cmd.data);
  83. serial_recvThrd_resume(pComIO->h); pTrans->ack.ret = -4; goto end_p;
  84. }
  85. pTrans->timeout = (wtime > timeout ? 0 : (timeout - wtime));
  86. pComIO->pTrans = pTrans; serial_recvThrd_resume(pComIO->h); // 恢复串口数据接收, "pComIO->pTrans"的控制权转移给接收线程.
  87. sw_signal_wait(pTrans->hEndSgl, WAIT_FOREVER); // 等待指令执行结束, "pComIO->pTrans"的控制权收回
  88. end_p:
  89. if(pTrans->hEndSgl) sw_signal_destroy(pTrans->hEndSgl);
  90. pComIO->pTrans = NULL;
  91. sw_mutex_unlock(pComIO->do_cmd_mutex);
  92. return pTrans->ack.ret;
  93. }
  94. // 接收、处理来自Air720U模块的数据报文帧, 串口-线程回调
  95. static int comio_data_recv_proc(unsigned long wParam/*传递打开的串口句柄*/, unsigned long lParam/*保留暂未使用*/)
  96. {
  97. SAir720UCom *pComIO = &s_myCom; void *pSerial = pComIO->h; SATCmdTrans *pTrans = pComIO->pTrans;
  98. const unsigned char *pRecvBuf = serial_get_recv_buffer(pSerial); int nRecvBytes = serial_get_recv_buffer_bytes(pSerial);
  99. const char *log_prefix = serial_get_log_prefix(pSerial); bool bPrintRecvBuf = true;
  100. static unsigned long enteredWaitAckTime = 0, now; static unsigned long enteredSilentTime = 0; static int nSilentRecvBytes = 0;
  101. if(!pTrans) { goto ret_p2; } // 无主动指令时
  102. // 1, 计算命令应答超时
  103. if(enteredWaitAckTime == 0) xgettickcount(&enteredWaitAckTime);
  104. xgettickcount(&now);
  105. // 2, 分析应答回复内容
  106. if(strstr((const char *)pRecvBuf, ATOK) != NULL || strstr((const char *)pRecvBuf, ATER) != NULL)
  107. { // 已应答
  108. if(nRecvBytes > nSilentRecvBytes) { enteredSilentTime = now; nSilentRecvBytes = nRecvBytes; }
  109. if((now-enteredSilentTime) < R_SILENT_TIMEOUT) { goto ret_p3; } // 等待静默期结束(延期多接收一会数据, 避免"内容"在"ATOK"或"ATER"之后)
  110. memcpy(pTrans->ack.data, pRecvBuf, nRecvBytes);
  111. pTrans->ack.ret = nRecvBytes; pTrans->ack.data[pTrans->ack.ret] = '\0'; bPrintRecvBuf = false;
  112. sw_log_trace("[%s] %s received a cmd ack(%d bytes): %s", MODULE_NAME, log_prefix, pTrans->ack.ret, pTrans->ack.data);
  113. goto ret_p1;
  114. }
  115. else if((now-enteredWaitAckTime) >= pTrans->timeout)
  116. { // 已超时
  117. sw_log_error("[%s] %s timeout occurred while waiting for the \"%s\" command's ack!!", MODULE_NAME, log_prefix, pTrans->cmd.data);
  118. pTrans->ack.ret = -1; pTrans->ack.data[0] = '\0';
  119. goto ret_p1;
  120. }
  121. else
  122. { // 继续收
  123. goto ret_p3;
  124. }
  125. // 3, 缓存数据接收控制
  126. ret_p1: // 点亮信号量, 表明本指令执行结束
  127. sw_signal_give(pComIO->pTrans->hEndSgl); enteredWaitAckTime = 0; enteredSilentTime = 0; nSilentRecvBytes = 0;
  128. goto ret_p2;
  129. ret_p2: // 清接收缓存, 重新开始一轮新指令
  130. if(nRecvBytes > 0 && bPrintRecvBuf)
  131. {
  132. serial_printf_recv_buffer(pSerial, LEVEL_TRACE); // 十六进制打印当前串口接收数据缓存
  133. sw_log_warn("[%s] %s discarded %d bytes of meaningless data!", MODULE_NAME, log_prefix, nRecvBytes);
  134. }
  135. if(nRecvBytes > 0) serial_clear_recv_buffer(pSerial);
  136. goto ret_p3;
  137. ret_p3: return 1; // 持续累加的接收新数据
  138. }
  139. // 打开与模块的通讯, 返回: 0成功, <0时失败
  140. int Air720U_ComInit()
  141. {
  142. s_myCom.do_cmd_mutex = sw_mutex_create();
  143. if(!s_myCom.do_cmd_mutex) { sw_log_error("[%s] error to create a mutex!!", MODULE_NAME); return -1; }
  144. s_myCom.pTrans = NULL;
  145. const char *serialName = "/dev/ttyUSB0"; int baudrate = 115200;
  146. const char *parityCheck = "none"; // 无校检
  147. s_myCom.h = serial_open(serialName, baudrate, parityCheck, \
  148. comio_data_recv_proc, comio_data_recv_proc, NULL);
  149. if(!s_myCom.h)
  150. {
  151. sw_log_error("[%s] failed to open the \"%s:%d(%s parity)\" device!!", \
  152. MODULE_NAME, serialName, baudrate, parityCheck);
  153. Air720U_ComExit(); return -1;
  154. }
  155. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  156. strcpy(trans.cmd.data, AT_ECHO_OFF); trans.cmd.len = strlen(trans.cmd.data);
  157. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  158. if(!(ret > 0 && strstr(trans.ack.data, ATOK)))
  159. {
  160. sw_log_error("[%s] failed to close echo mode!!", MODULE_NAME);
  161. Air720U_ComExit(); return -2+ret;
  162. }
  163. memset(&trans, 0, sizeof(SATCmdTrans));
  164. strcpy(trans.cmd.data, AT_GET_CGMM); trans.cmd.len = strlen(trans.cmd.data);
  165. ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  166. if(!(ret > 0 && strstr(trans.ack.data, ATOK) && xstrcasestr(trans.ack.data, "left", "Air72")))
  167. {
  168. if(ret != 0) sw_log_error("[%s] failed to get module model!!", MODULE_NAME);
  169. Air720U_ComExit(); return -99+ret;
  170. }
  171. return 0;
  172. }
  173. // 关闭与模块的通讯, 返回: 0成功, <0时失败
  174. int Air720U_ComExit()
  175. {
  176. if(s_myCom.h) serial_close(s_myCom.h, WAITTHRD_SAFEEXIT_TIMEOUT);
  177. if(s_myCom.do_cmd_mutex) sw_mutex_destroy(s_myCom.do_cmd_mutex);
  178. memset(&s_myCom, 0, sizeof(s_myCom));
  179. return 0;
  180. }
  181. // 改写sysfs虚拟文件, 返回: 0成功, <0失败
  182. static int sysfs_write(const char *path, const char *value)
  183. {
  184. int fd = open(path, O_WRONLY);
  185. if (fd < 0) return -1;
  186. ssize_t len = write(fd, value, strlen(value));
  187. if(len != strlen(value)) { close(fd); return -2; }
  188. close(fd); return 0;
  189. }
  190. // 控制模块断电重启, 返回: 0成功, <0时失败
  191. // 138脚 -- gpio4.IO[20] (4-1)*32+20 = 116 -- 4G_POWER
  192. int Air720U_Repower()
  193. {
  194. sysfs_write("/sys/class/gpio/unexport", "116"); // 撤销原导出
  195. if(sysfs_write("/sys/class/gpio/export", "116") < 0) return -2; // 重设新导出
  196. if(sysfs_write("/sys/class/gpio/gpio116/direction", "out") < 0) return -3; // 设置为输出
  197. if(sysfs_write("/sys/class/gpio/gpio116/value", "1") < 0) return -4; // 输出高电平-断电
  198. sw_thrd_delay(5000); // 等一段时间-毫秒
  199. if(sysfs_write("/sys/class/gpio/gpio116/value", "0") < 0) return -5; // 输出低电平-上电
  200. return 0;
  201. }
  202. // 获取模块的标识号, 返回: =15成功, <0失败(-1命令超时)
  203. int Air720U_GetIMEI(char buf[16]/*固定长15个十进制数字组成*/)
  204. {
  205. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  206. strcpy(trans.cmd.data, AT_GET_IMEI);
  207. trans.cmd.len = strlen(trans.cmd.data);
  208. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  209. if(ret < 0) return ret; // 执行超时或出错
  210. else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
  211. const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
  212. lsa_p: // 行扫描, 逐行分析应答结果
  213. if((*p) == '\0') goto end_p;
  214. pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
  215. if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
  216. if(pLineE) { lineLen = pLineE - pLineS; }
  217. if(lineLen == 0)
  218. { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
  219. if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
  220. else goto end_p;
  221. }
  222. //// 示例: "359759002514931"
  223. if(lineLen == 15)
  224. {
  225. bool isHex = true;
  226. for(int i = 0; i < lineLen; i++) if(!xisxdigit(pLineS[i])) { isHex = false; break; }
  227. if(isHex) { memcpy(buf, pLineS, lineLen); buf[lineLen] = '\0'; return lineLen; } // 成功读到模块的IMEI号
  228. }
  229. p = pLineE + crlfLen;
  230. goto lsa_p; // 继续分析下一行
  231. end_p:
  232. return -5;
  233. }
  234. // 获取网络注册状态, 返回: >=0成功, <0失败(-1命令超时)
  235. int Air720U_GetCregState()
  236. {
  237. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  238. strcpy(trans.cmd.data, AT_GET_CREG);
  239. trans.cmd.len = strlen(trans.cmd.data);
  240. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  241. if(ret < 0) return ret; // 执行超时或出错
  242. else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
  243. const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
  244. const char *p1, *p2; char statBuf[3]; int statLen;
  245. lsa_p: // 行扫描, 逐行分析应答结果
  246. if((*p) == '\0') goto end_p;
  247. pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
  248. if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
  249. if(pLineE) { lineLen = pLineE - pLineS; }
  250. if(lineLen == 0)
  251. { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
  252. if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
  253. else goto end_p;
  254. }
  255. //// 示例: "+CREG: <n>,<stat>"
  256. //// 说明: <n>注册状态报告模式, <stat>当前网络注册状态
  257. p1 = strstr(pLineS, "+CREG:"); p2 = NULL;
  258. if(p1 && p1 < pLineE) { p1 += 6; p2 = strchr(p1, ','); }
  259. if(p2 && p2 < pLineE)
  260. {
  261. p2++; while(*p2 == 0x20 && p2 < pLineE) { p2++; } // 跳过空格
  262. statLen = (pLineE - p2); if(statLen != 1 && statLen != 2) return -5; // 状态值范围: 1或2个数字
  263. memcpy(statBuf, p2, statLen); statBuf[statLen] = '\0';
  264. bool isDigit = true;
  265. for(int i = 0; i < strlen(statBuf); i++) if(!xisdigit(statBuf[i])) { isDigit = false; break; }
  266. if(isDigit) return atoi(statBuf); // 成功读到模块的网络注册状态值
  267. else return -6;
  268. }
  269. p = pLineE + crlfLen;
  270. goto lsa_p; // 继续分析下一行
  271. end_p:
  272. return -7;
  273. }
  274. // 获取当前信号强度, 返回: >=0成功, <0失败(-1命令超时)
  275. int Air720U_GetRSSIFromCSQ()
  276. {
  277. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  278. strcpy(trans.cmd.data, AT_GET_CSQ);
  279. trans.cmd.len = strlen(trans.cmd.data);
  280. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  281. if(ret < 0) return ret; // 执行超时或出错
  282. else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
  283. const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
  284. const char *p1, *p2; char rssiBuf[3]; int rssiLen;
  285. lsa_p: // 行扫描, 逐行分析应答结果
  286. if((*p) == '\0') goto end_p;
  287. pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
  288. if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
  289. if(pLineE) { lineLen = pLineE - pLineS; }
  290. if(lineLen == 0)
  291. { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
  292. if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
  293. else goto end_p;
  294. }
  295. //// 示例: "+CSQ: 15,99"
  296. //// 说明: 逗号前第一个数值是RSSI的值
  297. p1 = strstr(pLineS, "+CSQ:"); p2 = NULL;
  298. if(p1 && p1 < pLineE) { p1 += 5; p2 = strchr(p1, ','); }
  299. if(p2 && p2 < pLineE)
  300. {
  301. while(*p1 == 0x20 && p1 < p2) { p1++; } // 跳过空格
  302. rssiLen = (p2 - p1); if(rssiLen != 1 && rssiLen != 2) return -5; // 强度值范围: 0-99, 1或2个数字
  303. memcpy(rssiBuf, p1, rssiLen); rssiBuf[rssiLen] = '\0';
  304. bool isDigit = true;
  305. for(int i = 0; i < strlen(rssiBuf); i++) if(!xisdigit(rssiBuf[i])) { isDigit = false; break; }
  306. if(isDigit) return atoi(rssiBuf); // 成功读到模块的接收信号强度值
  307. else return -6;
  308. }
  309. p = pLineE + crlfLen;
  310. goto lsa_p; // 继续分析下一行
  311. end_p:
  312. return -7;
  313. }
  314. // 返回电话卡的状态, 返回: 1已可用, <0失败(-1命令超时)
  315. int Air720U_IsSimCardReady()
  316. {
  317. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  318. strcpy(trans.cmd.data, AT_GET_PINSTA);
  319. trans.cmd.len = strlen(trans.cmd.data);
  320. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  321. if(ret < 0) return ret; // 执行超时或出错
  322. else if(strstr(trans.ack.data, ATOK) && strstr(trans.ack.data, "+CPIN: READY")) return 1;
  323. else return -5;
  324. }
  325. // 获取电话卡标识号, 返回: =20成功, <0失败(-1命令超时)
  326. int Air720U_GetSimICCID(char buf[21]/*一般由20个十进制数字组成*/)
  327. {
  328. SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
  329. strcpy(trans.cmd.data, AT_GET_ICCID);
  330. trans.cmd.len = strlen(trans.cmd.data);
  331. int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
  332. if(ret < 0) return ret; // 执行超时或出错
  333. else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
  334. const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
  335. const char *p1, *p2; int iccidLen;
  336. lsa_p: // 行扫描, 逐行分析应答结果
  337. if((*p) == '\0') goto end_p;
  338. pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
  339. if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
  340. if(pLineE) { lineLen = pLineE - pLineS; }
  341. if(lineLen == 0)
  342. { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
  343. if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
  344. else goto end_p;
  345. }
  346. //// 示例: "+ICCID: 89860117831003134201"
  347. p1 = strstr(pLineS, "+ICCID:"); p2 = NULL;
  348. if(p1 && p1 < pLineE) { p1 += 7; p2 = pLineE; }
  349. if(p2)
  350. {
  351. while(*p1 == 0x20 && p1 < p2) { p1++; } // 跳过空格
  352. iccidLen = (p2 - p1); if(iccidLen != 20) return -5; // 长度不对
  353. bool isHex = true;
  354. for(int i = 0; i < iccidLen; i++) if(!xisxdigit(p1[i])) { isHex = false; break; }
  355. if(isHex) { memcpy(buf, p1, iccidLen); buf[iccidLen] = '\0'; return iccidLen; } // 成功读到SIM卡的ICCID号
  356. else return -6;
  357. }
  358. p = pLineE + crlfLen;
  359. goto lsa_p; // 继续分析下一行
  360. end_p:
  361. return -7;
  362. }