air720u.c 16 KB

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