如何等待字节写入串行 GSM 调制解调器?
How to wait for byte to be written to serial GSM modem?
在此先感谢您的帮助。
使用下面的示例,我开始在C 中编写一些API 来发送一个AT 命令并通过串口接收响应。
我读取响应没有问题(通过轮询使用非阻塞读取)并且发现 "at command" 启用的设备没有问题。
我面临的问题是写入函数。大多数命令都有效(最小的命令,如 AT、ATI、ATI+CIMI 等)。有时像发送短信这样的命令会失败。
我认为问题是写入速度(比串行快)。
如果我在一次写入和下一次写入之间设置一个计时器,就不会出现所有问题。
下面是代码
int serial_write(int fd, char * command){
size_t len = strlen(command);
int wlen = write(fd, command, len);
if (wlen != len) {
return -1;
}
usleep(80*1000L);
if ( tcdrain(fd) != 0){
return -2;
}
return 0;
}
int open_tty(char *portname){
int fd;
/*Aperta NON bloccante, con la poll che aspetta 1 secondo*/
fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);
if (fd < 0) {
printf("Error opening %s: %s\n", portname, strerror(errno));
return -1;
}
/*baudrate 115200, 8 bits, no parity, 1 stop bit */
if (set_interface_attribs(fd, B115200) < 0 ){
printf("Error set_interface_attribs: %s\n", strerror(errno));
return -1;
}
return fd;
}
int set_interface_attribs(int fd, int speed){
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
if ( cfsetospeed(&tty, (speed_t)speed) < 0 ){
printf("Error from cfsetospeed: %s\n", strerror(errno));
return -1;
}
if ( cfsetispeed(&tty, (speed_t)speed) < 0 ){
printf("Error from cfsetispeed: %s\n", strerror(errno));
return -1;
}
tty.c_cflag |= CLOCAL | CREAD;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
tty.c_lflag |= ICANON | ISIG; /* canonical input */
tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
tty.c_iflag &= ~IGNCR; /* preserve carriage return */
tty.c_iflag &= ~INPCK;
tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
tty.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */
tty.c_oflag &= ~OPOST;
tty.c_cc[VEOL] = 0;
tty.c_cc[VEOL2] = 0;
tty.c_cc[VEOF] = 0x04;
tty.c_cc[VTIME] = 10;
tty.c_cc[VMIN] = 0;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
int read_response(int fd, char ** res){
int count=1; /* contatore realloc 1 per lo [=10=]*/
tcdrain(fd); /* waits until all of the data that has been written has been sent */
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN ;
do {
unsigned char buf[MAXBUF];
unsigned char *p;
int rdlen;
int n = poll( fds, 1, 1000);
if (n>0){
rdlen = read(fd, buf, sizeof(buf) - 1);
if (rdlen > 0) {
buf[rdlen] = 0;
for (p = buf; rdlen-- > 0; p++) {
if (*p < ' ')
*p = '[=10=]'; /* replace any control chars */
}
if ( (strcmp((char *)buf, "") != 0) || (buf[0] == '^') ){
count += (strlen((char *)buf)+1); /* 2 per ; e ' ' */
*res = realloc (*res, count);
strncat(*res, (char *)buf, strlen((char *)buf));
strcat(*res, ";");
}
if (strcmp((char *)buf, ATCMD_OK) == 0){
return 0;
}
if (strcmp((char *)buf, ATCMD_ERROR) == 0){
return -1;
}
} else if (rdlen < 0) {
return -2;
} else { /* rdlen == 0 */
return -3;
}
} else {
return -4;
}
/* repeat read */
} while (1);
}
int send_sms(int fd, char *tel, char *text){
int wlen = 0;
char *res = malloc(sizeof(char*));
char at_send[strlen(ATCMD_CMGS) + strlen(tel) + 3]; //3=2apici+"[=10=]"
strcpy(at_send, ATCMD_CMGS);
strcat(at_send, DL_QUOTE);
strcat(at_send, tel);
strcat(at_send, DL_QUOTE);
printf("Setting to sms text mode... ");
if ( (wlen = serial_write(fd, ATCMD_CMGF)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, C_R)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if (read_response(fd, &res) < 0 ) {
printf("FAIL\n");
}
else {
printf("OK, RES: %s\n",res);
}
free(res);
printf("Sending SMS...");
if ( (wlen = serial_write(fd, at_send)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, C_R)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, text)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, CTRL_Z)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if (read_response(fd, &res) < 0 ) {
printf("FAIL\n");
free(res);
return -1;
}
else {
printf("OK, RES: %s\n",res);
free(res);
return 0;
}
}
这些是有罪的功能。您可以看到,在 serial_write() 中,我正在使用 usleep() 并且一切正常。删除 usleep() 会导致问题(如果有 tcdrain)。
我们将不胜感激。
谢谢。
Sometimes a command like send SMS fails.
这不是对问题的帮助或详细描述。简单地说有问题然后期待解决方案是不合理的。
All the problems DO NOT occur if I set a timer between a write and the next write.
这表明您的程序在传输新 command/message 之前没有正确等待调制解调器的响应。接收响应(而不是等待传输完成,即调用 tcdrain()、and/or 延迟任意时间间隔,例如调用 usleep() ) 是调制解调器现在已准备好接收的正确指示。
您没有遇到问题的命令被描述为基本的命令和响应对话框。发送单行消息,并在短时间内接收单行消息作为响应。
但是使用 CMGS 命令发送 SMS 消息并不遵循这个简单的对话框。
然而,您的程序无论如何都试图强制执行单消息->单响应构造(结果显然是不可靠的结果)。
根据 GSM Technical Specification,CMGS 命令 can/should 被处理为
的 write/read 交换
[w]command -> [r]prompt_response -> [w]text -> [r]prompt_response ...
[w]text -> [r]prompt_response -> [w]text + ^Z -> [r]2-line response
其中 prompt_response
指定为
a four character sequence <CR><LF><greater_than><space>
请注意,此响应不适合规范读取(即常规行终止字符位于此序列的开头而不是结尾)。
另请注意,传输的消息文本中的每个回车 return 字符都会由调制解调器生成 prompt_response
的传输。
鉴于 CMGS 命令的复杂性,您的程序传输多行,然后期望只处理一个响应很容易产生不可靠的结果。
我发现您的代码存在其他问题,但 none 与 CMGS 命令的错误处理一样严重。
这个 "answer" 只会指出你程序中的这个主要缺陷(即提供 "help")而不是提供解决方案 and/or 重写。
总结:
How to wait for byte to be written to serial GSM modem?
您的程序应该等到它读取了对上一个传输的响应后才发送(下一个)message/command。该消息中的字节可以毫无延迟地发送。
请参阅 GSM 规范作为生成响应的内容。
在此先感谢您的帮助。
使用下面的示例
我面临的问题是写入函数。大多数命令都有效(最小的命令,如 AT、ATI、ATI+CIMI 等)。有时像发送短信这样的命令会失败。 我认为问题是写入速度(比串行快)。
如果我在一次写入和下一次写入之间设置一个计时器,就不会出现所有问题。
下面是代码
int serial_write(int fd, char * command){
size_t len = strlen(command);
int wlen = write(fd, command, len);
if (wlen != len) {
return -1;
}
usleep(80*1000L);
if ( tcdrain(fd) != 0){
return -2;
}
return 0;
}
int open_tty(char *portname){
int fd;
/*Aperta NON bloccante, con la poll che aspetta 1 secondo*/
fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);
if (fd < 0) {
printf("Error opening %s: %s\n", portname, strerror(errno));
return -1;
}
/*baudrate 115200, 8 bits, no parity, 1 stop bit */
if (set_interface_attribs(fd, B115200) < 0 ){
printf("Error set_interface_attribs: %s\n", strerror(errno));
return -1;
}
return fd;
}
int set_interface_attribs(int fd, int speed){
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
if ( cfsetospeed(&tty, (speed_t)speed) < 0 ){
printf("Error from cfsetospeed: %s\n", strerror(errno));
return -1;
}
if ( cfsetispeed(&tty, (speed_t)speed) < 0 ){
printf("Error from cfsetispeed: %s\n", strerror(errno));
return -1;
}
tty.c_cflag |= CLOCAL | CREAD;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
tty.c_lflag |= ICANON | ISIG; /* canonical input */
tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
tty.c_iflag &= ~IGNCR; /* preserve carriage return */
tty.c_iflag &= ~INPCK;
tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
tty.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */
tty.c_oflag &= ~OPOST;
tty.c_cc[VEOL] = 0;
tty.c_cc[VEOL2] = 0;
tty.c_cc[VEOF] = 0x04;
tty.c_cc[VTIME] = 10;
tty.c_cc[VMIN] = 0;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
int read_response(int fd, char ** res){
int count=1; /* contatore realloc 1 per lo [=10=]*/
tcdrain(fd); /* waits until all of the data that has been written has been sent */
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN ;
do {
unsigned char buf[MAXBUF];
unsigned char *p;
int rdlen;
int n = poll( fds, 1, 1000);
if (n>0){
rdlen = read(fd, buf, sizeof(buf) - 1);
if (rdlen > 0) {
buf[rdlen] = 0;
for (p = buf; rdlen-- > 0; p++) {
if (*p < ' ')
*p = '[=10=]'; /* replace any control chars */
}
if ( (strcmp((char *)buf, "") != 0) || (buf[0] == '^') ){
count += (strlen((char *)buf)+1); /* 2 per ; e ' ' */
*res = realloc (*res, count);
strncat(*res, (char *)buf, strlen((char *)buf));
strcat(*res, ";");
}
if (strcmp((char *)buf, ATCMD_OK) == 0){
return 0;
}
if (strcmp((char *)buf, ATCMD_ERROR) == 0){
return -1;
}
} else if (rdlen < 0) {
return -2;
} else { /* rdlen == 0 */
return -3;
}
} else {
return -4;
}
/* repeat read */
} while (1);
}
int send_sms(int fd, char *tel, char *text){
int wlen = 0;
char *res = malloc(sizeof(char*));
char at_send[strlen(ATCMD_CMGS) + strlen(tel) + 3]; //3=2apici+"[=10=]"
strcpy(at_send, ATCMD_CMGS);
strcat(at_send, DL_QUOTE);
strcat(at_send, tel);
strcat(at_send, DL_QUOTE);
printf("Setting to sms text mode... ");
if ( (wlen = serial_write(fd, ATCMD_CMGF)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, C_R)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if (read_response(fd, &res) < 0 ) {
printf("FAIL\n");
}
else {
printf("OK, RES: %s\n",res);
}
free(res);
printf("Sending SMS...");
if ( (wlen = serial_write(fd, at_send)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, C_R)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, text)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if ( (wlen = serial_write(fd, CTRL_Z)) < 0 ){
printf("Error from write: %d, %d\n", wlen, errno);
}
if (read_response(fd, &res) < 0 ) {
printf("FAIL\n");
free(res);
return -1;
}
else {
printf("OK, RES: %s\n",res);
free(res);
return 0;
}
}
这些是有罪的功能。您可以看到,在 serial_write() 中,我正在使用 usleep() 并且一切正常。删除 usleep() 会导致问题(如果有 tcdrain)。
我们将不胜感激。
谢谢。
Sometimes a command like send SMS fails.
这不是对问题的帮助或详细描述。简单地说有问题然后期待解决方案是不合理的。
All the problems DO NOT occur if I set a timer between a write and the next write.
这表明您的程序在传输新 command/message 之前没有正确等待调制解调器的响应。接收响应(而不是等待传输完成,即调用 tcdrain()、and/or 延迟任意时间间隔,例如调用 usleep() ) 是调制解调器现在已准备好接收的正确指示。
您没有遇到问题的命令被描述为基本的命令和响应对话框。发送单行消息,并在短时间内接收单行消息作为响应。
但是使用 CMGS 命令发送 SMS 消息并不遵循这个简单的对话框。
然而,您的程序无论如何都试图强制执行单消息->单响应构造(结果显然是不可靠的结果)。
根据 GSM Technical Specification,CMGS 命令 can/should 被处理为
[w]command -> [r]prompt_response -> [w]text -> [r]prompt_response ...
[w]text -> [r]prompt_response -> [w]text + ^Z -> [r]2-line response
其中 prompt_response
指定为
a four character sequence <CR><LF><greater_than><space>
请注意,此响应不适合规范读取(即常规行终止字符位于此序列的开头而不是结尾)。
另请注意,传输的消息文本中的每个回车 return 字符都会由调制解调器生成 prompt_response
的传输。
鉴于 CMGS 命令的复杂性,您的程序传输多行,然后期望只处理一个响应很容易产生不可靠的结果。
我发现您的代码存在其他问题,但 none 与 CMGS 命令的错误处理一样严重。
这个 "answer" 只会指出你程序中的这个主要缺陷(即提供 "help")而不是提供解决方案 and/or 重写。
总结:
How to wait for byte to be written to serial GSM modem?
您的程序应该等到它读取了对上一个传输的响应后才发送(下一个)message/command。该消息中的字节可以毫无延迟地发送。
请参阅 GSM 规范作为生成响应的内容。