| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- #include "ec200u.h"
- #include "../swapi/subjects/serial/serial.h"
- // 模块名称
- static const char MODULE_NAME[] = "EC200U";
- #define MAX_CMD_CHARS 65535 // 单条指令最大字符数
- #define MAX_ACK_CHARS 65535 // 指令应答最大字符数
- #define WAIT_ACK_TIMEOUT 9000 // 等待应答超时时间ms
- #define R_SILENT_TIMEOUT 50 // 应答静默超时时间ms
- // 关闭命令回显模式
- static const char *AT_USE_ECHO = "ATE0" ;
- // 获取模块的IMEI号
- static const char *AT_GET_IMEI = "AT+CGSN" ;
- // 查看网络注册状态
- static const char *AT_GET_CREG = "AT+CREG?";
- // 获取当前信号强度
- static const char *AT_GET_CSQ = "AT+CSQ" ;
- // 获取SIM-PIN状态
- static const char *AT_GET_PINSTA = "AT+CPIN?";
- // 获取SIM-ICCID号
- static const char *AT_GET_ICCID = "AT+ICCID";
- // 激活USB网络模式
- static const char *AT_QCFG_CMD1 = "AT+QCFG=\"usbnet\",1";
- static const char *AT_QCFG_CMD2 = "AT+qnetdevctl=1,1,1" ;
- // AT指令请求和应答
- typedef struct
- {
- // 指令
- struct
- {
- char data[MAX_CMD_CHARS+1]; // 指令数据(字符串)
- int len; // 指令长度
- } cmd;
- // 等待执行结束的超时ms
- int timeout;
- // 等待执行结束的信号量
- void *hEndSgl;
- // 应答
- struct
- {
- int ret; // 返回值: >0时执行成功, 表明应答数据的长度; <0时出错(-1超时)
- char data[MAX_ACK_CHARS+1]; // 应答数据(字符串)
- } ack;
- } SATCmdTrans;
- // 与模块的串口通讯
- typedef struct
- {
- void *h; // 打开的串口句柄
- void *do_cmd_mutex; // 指令执行互斥量, 同一时刻正在执行的指令只能有一条
- SATCmdTrans *pTrans; // 执行的指令及其应答结果: 空闲时为空, 工作时不为空
- } SEC200UCom;
- static SEC200UCom s_myCom;
- // 定义AT指令格式符
- #define CR '\r' // 回车
- #define LF '\n' // 换行
- #define CRLF "\r\n" // 回车换行
- #define ATOK "\r\nOK\r\n" // 应答成功
- #define ATER "ERROR" // 应答失败
- // 执行一条指令, 返回: 大于0成功; 小于0失败(-1命令超时)
- static int comio_doCmd(SATCmdTrans *pTrans/*要执行的指令(IN&OUT)*/, int timeout/*等待应答的超时时间, 单位是毫秒*/)
- {
- SEC200UCom *pComIO = &s_myCom; const char *log_prefix = serial_get_log_prefix(pComIO->h);
- unsigned long stime, wtime, etime; char *pAtCmdStr; int atCmdLen, ret;
- xgettickcount(&stime);
- ret = sw_mutex_lock(pComIO->do_cmd_mutex, timeout);
- if(ret != 0) { pTrans->ack.ret = -1; goto end_p; } // 互斥量上锁超时
- xgettickcount(&etime); wtime = etime - stime;
- pTrans->hEndSgl = sw_signal_create();
- if(!pTrans->hEndSgl) { pTrans->ack.ret = -2; goto end_p; }
- pAtCmdStr = (char *)sw_heap_malloc(pTrans->cmd.len+2);
- if(!pAtCmdStr) { pTrans->ack.ret = -3; goto end_p; }
- memcpy(pAtCmdStr, pTrans->cmd.data, pTrans->cmd.len);
- pAtCmdStr[pTrans->cmd.len] = CR; pAtCmdStr[pTrans->cmd.len+1] = '\0';
- atCmdLen = strlen((const char *)pAtCmdStr);
- serial_recvThrd_pause(pComIO->h); sw_thrd_delay(THRDWAIT_DELAY); // "pComIO->pTrans"的控制权转移前, 先暂停串口数据的接收
- ret = serial_send_data(pComIO->h, (unsigned char *)pAtCmdStr, atCmdLen); // 发送一条指令
- sw_heap_free(pAtCmdStr); // 释放AT指令字符串内存
- if(ret == atCmdLen)
- {
- sw_log_trace("[%s] %s send a cmd(%d bytes): %s", MODULE_NAME, log_prefix, pTrans->cmd.len, pTrans->cmd.data);
- }
- else
- {
- 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);
- serial_recvThrd_resume(pComIO->h); pTrans->ack.ret = -4; goto end_p;
- }
- pTrans->timeout = (wtime > timeout ? 0 : (timeout - wtime));
- pComIO->pTrans = pTrans; serial_recvThrd_resume(pComIO->h); // 恢复串口数据接收, "pComIO->pTrans"的控制权转移给接收线程.
- sw_signal_wait(pTrans->hEndSgl, WAIT_FOREVER); // 等待指令执行结束, "pComIO->pTrans"的控制权收回
- end_p:
- if(pTrans->hEndSgl) sw_signal_destroy(pTrans->hEndSgl);
- pComIO->pTrans = NULL;
- sw_mutex_unlock(pComIO->do_cmd_mutex);
- return pTrans->ack.ret;
- }
- // 接收、处理来自EC200U模块的数据报文帧, 串口-线程回调
- static int comio_data_recv_proc(unsigned long wParam/*传递打开的串口句柄*/, unsigned long lParam/*保留暂未使用*/)
- {
- SEC200UCom *pComIO = &s_myCom; void *pSerial = pComIO->h; SATCmdTrans *pTrans = pComIO->pTrans;
- const unsigned char *pRecvBuf = serial_get_recv_buffer(pSerial); int nRecvBytes = serial_get_recv_buffer_bytes(pSerial);
- const char *log_prefix = serial_get_log_prefix(pSerial); bool bPrintRecvBuf = true;
- static unsigned long enteredWaitAckTime = 0, now; static unsigned long enteredSilentTime = 0; static int nSilentRecvBytes = 0;
- if(!pTrans) { goto ret_p2; } // 无主动指令时
- // 1, 计算命令应答超时
- if(enteredWaitAckTime == 0) xgettickcount(&enteredWaitAckTime);
- xgettickcount(&now);
- // 2, 分析应答回复内容
- if(strstr((const char *)pRecvBuf, ATOK) != NULL || strstr((const char *)pRecvBuf, ATER) != NULL)
- { // 已应答
- if(nRecvBytes > nSilentRecvBytes) { enteredSilentTime = now; nSilentRecvBytes = nRecvBytes; }
- if((now-enteredSilentTime) < R_SILENT_TIMEOUT) { goto ret_p3; } // 等待静默期结束(延期多接收一会数据, 避免"内容"在"ATOK"或"ATER"之后)
- memcpy(pTrans->ack.data, pRecvBuf, nRecvBytes);
- pTrans->ack.ret = nRecvBytes; pTrans->ack.data[pTrans->ack.ret] = '\0'; bPrintRecvBuf = false;
- sw_log_trace("[%s] %s received a cmd ack(%d bytes): %s", MODULE_NAME, log_prefix, pTrans->ack.ret, pTrans->ack.data);
- goto ret_p1;
- }
- else if((now-enteredWaitAckTime) >= pTrans->timeout)
- { // 已超时
- sw_log_error("[%s] %s timeout occurred while waiting for the \"%s\" command's ack!!", MODULE_NAME, log_prefix, pTrans->cmd.data);
- pTrans->ack.ret = -1; pTrans->ack.data[0] = '\0';
- goto ret_p1;
- }
- else
- { // 继续收
- goto ret_p3;
- }
- // 3, 缓存数据接收控制
- ret_p1: // 点亮信号量, 表明本指令执行结束
- sw_signal_give(pComIO->pTrans->hEndSgl); enteredWaitAckTime = 0; enteredSilentTime = 0; nSilentRecvBytes = 0;
- goto ret_p2;
- ret_p2: // 清接收缓存, 重新开始一轮新指令
- if(nRecvBytes > 0 && bPrintRecvBuf)
- {
- serial_printf_recv_buffer(pSerial, LEVEL_TRACE); // 十六进制打印当前串口接收数据缓存
- sw_log_warn("[%s] %s discarded %d bytes of meaningless data!", MODULE_NAME, log_prefix, nRecvBytes);
- }
- if(nRecvBytes > 0) serial_clear_recv_buffer(pSerial);
- goto ret_p3;
- ret_p3: return 1; // 持续累加的接收新数据
- }
- // 打开与模块的通讯, 返回: 0成功, <0时失败
- int EC200U_ComInit()
- {
- s_myCom.do_cmd_mutex = sw_mutex_create();
- if(!s_myCom.do_cmd_mutex) { sw_log_error("[%s] error to create a mutex!!", MODULE_NAME); return -1; }
- s_myCom.pTrans = NULL;
- const char *serialName = "/dev/ttyUSB0"; int baudrate = 115200;
- const char *parityCheck = "none"; // 无校检
- s_myCom.h = serial_open(serialName, baudrate, parityCheck, \
- comio_data_recv_proc, comio_data_recv_proc, NULL);
- if(!s_myCom.h)
- {
- sw_log_error("[%s] failed to open the \"%s:%d(%s parity)\" device!!", \
- MODULE_NAME, serialName, baudrate, parityCheck);
- EC200U_ComExit(); return -1;
- }
- SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
- strcpy(trans.cmd.data, AT_USE_ECHO); trans.cmd.len = strlen(trans.cmd.data);
- int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
- if(!(ret > 0 && strstr(trans.ack.data, ATOK)))
- {
- sw_log_error("[%s] failed to close echo mode!!", MODULE_NAME);
- EC200U_ComExit(); return -2+ret;
- }
- memset(&trans, 0, sizeof(SATCmdTrans));
- strcpy(trans.cmd.data, AT_QCFG_CMD1); trans.cmd.len = strlen(trans.cmd.data);
- ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
- if(!(ret > 0 && strstr(trans.ack.data, ATOK)))
- {
- sw_log_error("[%s] failed to configure USB network mode!!", MODULE_NAME);
- EC200U_ComExit(); return -3+ret;
- }
- memset(&trans, 0, sizeof(SATCmdTrans));
- strcpy(trans.cmd.data, AT_QCFG_CMD2); trans.cmd.len = strlen(trans.cmd.data);
- ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
- if(!(ret > 0 && strstr(trans.ack.data, ATOK)))
- {
- sw_log_error("[%s] failed to activate USB network mode!!", MODULE_NAME);
- EC200U_ComExit(); return -4+ret;
- }
- return 0;
- }
- // 关闭与模块的通讯, 返回: 0成功, <0时失败
- int EC200U_ComExit()
- {
- if(s_myCom.h) serial_close(s_myCom.h, WAITTHRD_SAFEEXIT_TIMEOUT);
- if(s_myCom.do_cmd_mutex) sw_mutex_destroy(s_myCom.do_cmd_mutex);
- memset(&s_myCom, 0, sizeof(s_myCom));
- return 0;
- }
- // 改写sysfs虚拟文件, 返回: 0成功, <0失败
- static int sysfs_write(const char *path, const char *value)
- {
- int fd = open(path, O_WRONLY);
- if (fd < 0) return -1;
- ssize_t len = write(fd, value, strlen(value));
- if(len != strlen(value)) { close(fd); return -2; }
- close(fd); return 0;
- }
- // 控制模块断电重启, 返回: 0成功, <0时失败
- // 138脚 -- gpio4.IO[20] (4-1)*32+20 = 116 -- 4G_POWER
- int EC200U_Repower()
- {
- sysfs_write("/sys/class/gpio/unexport", "116"); // 撤销原导出
- if(sysfs_write("/sys/class/gpio/export", "116") < 0) return -2; // 重设新导出
- if(sysfs_write("/sys/class/gpio/gpio116/direction", "out") < 0) return -3; // 设置为输出
- if(sysfs_write("/sys/class/gpio/gpio116/value", "1") < 0) return -4; // 输出高电平-断电
- sw_thrd_delay(5000); // 等一段时间-毫秒
- if(sysfs_write("/sys/class/gpio/gpio116/value", "0") < 0) return -5; // 输出低电平-上电
- return 0;
- }
- // 获取模块的标识号, 返回: =15成功, <0失败(-1命令超时)
- int EC200U_GetIMEI(char buf[16]/*固定长15个十进制数字组成*/)
- {
- SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
- strcpy(trans.cmd.data, AT_GET_IMEI);
- trans.cmd.len = strlen(trans.cmd.data);
- int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
- if(ret < 0) return ret; // 执行超时或出错
- else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
- const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
- lsa_p: // 行扫描, 逐行分析应答结果
- if((*p) == '\0') goto end_p;
- pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
- if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
- if(pLineE) { lineLen = pLineE - pLineS; }
- if(lineLen == 0)
- { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
- if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
- else goto end_p;
- }
- //// 示例: "359759002514931"
- if(lineLen == 15)
- {
- bool isHex = true;
- for(int i = 0; i < lineLen; i++) if(!xisxdigit(pLineS[i])) { isHex = false; break; }
- if(isHex) { memcpy(buf, pLineS, lineLen); buf[lineLen] = '\0'; return lineLen; } // 成功读到模块的IMEI号
- }
- p = pLineE + crlfLen;
- goto lsa_p; // 继续分析下一行
- end_p:
- return -5;
- }
- // 获取网络注册状态, 返回: >=0成功, <0失败(-1命令超时)
- int EC200U_GetCregState()
- {
- SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
- strcpy(trans.cmd.data, AT_GET_CREG);
- trans.cmd.len = strlen(trans.cmd.data);
- int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
- if(ret < 0) return ret; // 执行超时或出错
- else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
- const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
- const char *p1, *p2; char statBuf[3]; int statLen;
- lsa_p: // 行扫描, 逐行分析应答结果
- if((*p) == '\0') goto end_p;
- pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
- if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
- if(pLineE) { lineLen = pLineE - pLineS; }
- if(lineLen == 0)
- { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
- if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
- else goto end_p;
- }
- //// 示例: "+CREG: <n>,<stat>"
- //// 说明: <n>注册状态报告模式, <stat>当前网络注册状态
- p1 = strstr(pLineS, "+CREG:"); p2 = NULL;
- if(p1 && p1 < pLineE) { p1 += 6; p2 = strchr(p1, ','); }
- if(p2 && p2 < pLineE)
- {
- p2++; while(*p2 == 0x20 && p2 < pLineE) { p2++; } // 跳过空格
- statLen = (pLineE - p2); if(statLen != 1 && statLen != 2) return -5; // 状态值范围: 1或2个数字
- memcpy(statBuf, p2, statLen); statBuf[statLen] = '\0';
- bool isDigit = true;
- for(int i = 0; i < strlen(statBuf); i++) if(!xisdigit(statBuf[i])) { isDigit = false; break; }
- if(isDigit) return atoi(statBuf); // 成功读到模块的网络注册状态值
- else return -6;
- }
- p = pLineE + crlfLen;
- goto lsa_p; // 继续分析下一行
- end_p:
- return -7;
- }
- // 获取当前信号强度, 返回: >=0成功, <0失败(-1命令超时)
- int EC200U_GetRSSIFromCSQ()
- {
- SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
- strcpy(trans.cmd.data, AT_GET_CSQ);
- trans.cmd.len = strlen(trans.cmd.data);
- int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
- if(ret < 0) return ret; // 执行超时或出错
- else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
- const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
- const char *p1, *p2; char rssiBuf[3]; int rssiLen;
- lsa_p: // 行扫描, 逐行分析应答结果
- if((*p) == '\0') goto end_p;
- pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
- if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
- if(pLineE) { lineLen = pLineE - pLineS; }
- if(lineLen == 0)
- { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
- if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
- else goto end_p;
- }
- //// 示例: "+CSQ: 15,99"
- //// 说明: 逗号前第一个数值是RSSI的值
- p1 = strstr(pLineS, "+CSQ:"); p2 = NULL;
- if(p1 && p1 < pLineE) { p1 += 5; p2 = strchr(p1, ','); }
- if(p2 && p2 < pLineE)
- {
- while(*p1 == 0x20 && p1 < p2) { p1++; } // 跳过空格
- rssiLen = (p2 - p1); if(rssiLen != 1 && rssiLen != 2) return -5; // 强度值范围: 0-99, 1或2个数字
- memcpy(rssiBuf, p1, rssiLen); rssiBuf[rssiLen] = '\0';
- bool isDigit = true;
- for(int i = 0; i < strlen(rssiBuf); i++) if(!xisdigit(rssiBuf[i])) { isDigit = false; break; }
- if(isDigit) return atoi(rssiBuf); // 成功读到模块的接收信号强度值
- else return -6;
- }
- p = pLineE + crlfLen;
- goto lsa_p; // 继续分析下一行
- end_p:
- return -7;
- }
- // 返回电话卡的状态, 返回: 1已可用, <0失败(-1命令超时)
- int EC200U_IsSimCardReady()
- {
- SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
- strcpy(trans.cmd.data, AT_GET_PINSTA);
- trans.cmd.len = strlen(trans.cmd.data);
- int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
- if(ret < 0) return ret; // 执行超时或出错
- else if(strstr(trans.ack.data, ATOK) && strstr(trans.ack.data, "+CPIN: READY")) return 1;
- else return -5;
- }
- // 获取电话卡标识号, 返回: =20成功, <0失败(-1命令超时)
- int EC200U_GetSimICCID(char buf[21]/*一般由20个十进制数字组成*/)
- {
- SATCmdTrans trans; memset(&trans, 0, sizeof(SATCmdTrans));
- strcpy(trans.cmd.data, AT_GET_ICCID);
- trans.cmd.len = strlen(trans.cmd.data);
- int ret = comio_doCmd(&trans, WAIT_ACK_TIMEOUT);
- if(ret < 0) return ret; // 执行超时或出错
- else if(strstr(trans.ack.data, ATOK) == NULL) goto end_p; // 未收到成功应答
- const char *p = trans.ack.data, *pLineS, *pLineE; int lineLen, crlfLen = strlen(CRLF);
- const char *p1, *p2; int iccidLen;
- lsa_p: // 行扫描, 逐行分析应答结果
- if((*p) == '\0') goto end_p;
- pLineS = strstr(p, CRLF); pLineE = NULL; lineLen = 0;
- if(pLineS) { pLineS += crlfLen; pLineE = strstr(pLineS, CRLF); }
- if(pLineE) { lineLen = pLineE - pLineS; }
- if(lineLen == 0)
- { // 连续两个CRLF, 上面的逻辑保证了"lineLen"只可能大于或等于0, 不会是小于0
- if(pLineE) { p = pLineE; goto lsa_p; } // 跳第一个CRLF, 继续分析下一行
- else goto end_p;
- }
- //// 示例: "+ICCID: 89860117831003134201"
- p1 = strstr(pLineS, "+ICCID:"); p2 = NULL;
- if(p1 && p1 < pLineE) { p1 += 7; p2 = pLineE; }
- if(p2)
- {
- while(*p1 == 0x20 && p1 < p2) { p1++; } // 跳过空格
- iccidLen = (p2 - p1); if(iccidLen != 20) return -5; // 长度不对
- bool isHex = true;
- for(int i = 0; i < iccidLen; i++) if(!xisxdigit(p1[i])) { isHex = false; break; }
- if(isHex) { memcpy(buf, p1, iccidLen); buf[iccidLen] = '\0'; return iccidLen; } // 成功读到SIM卡的ICCID号
- else return -6;
- }
- p = pLineE + crlfLen;
- goto lsa_p; // 继续分析下一行
- end_p:
- return -7;
- }
|