Как ждать записи байта в последовательный модем GSM? - PullRequest
0 голосов
/ 08 апреля 2020

Заранее спасибо за помощь.

Используя следующий пример Канонический режим Linux Последовательный порт , я начинаю писать небольшой API на Cto для отправки AT-команды и получения ответа через последовательный порт. У меня нет проблем с чтением ответа (используется неблокирующее чтение с опросом) и нет проблем с обнаружением устройства с поддержкой команды «at command».

Проблема, с которой я сталкиваюсь, связана с функцией записи. Большинство команд работают (самая маленькая команда, как AT, ATI, ATI + CIMI и т. Д. c). Иногда такая команда, как отправить SMS, не работает. Я думаю, что проблема заключается в скорости записи (быстрее, чем последовательная).

Все проблемы НЕ возникают, если я устанавливаю таймер между записью и следующей записью.

код

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 \0*/
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 = '\0';   /* 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+"\0"

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).

Будем благодарны за любую помощь.

Спасибо.

1 Ответ

0 голосов
/ 09 апреля 2020

Иногда такая команда, как отправка SMS, не выполняется.

Это не является полезным или подробным описанием проблемы. Просто заявить, что есть проблема, а затем ожидать решения неразумно.

Все проблемы НЕ возникают, если я устанавливаю таймер между записью и следующей записью.

Это указывает на то, что ваша программа не ожидает должным образом ответа от модема, прежде чем она передаст новую команду / сообщение. Получение ответа (а не ожидание завершения передачи, т. Е. Вызов tcdrain () и / или задержка на произвольный интервал времени, например, вызов usleep () ) является правильным указанием что модем теперь готов к приему.

Команды, у которых нет проблем, характеризуются как базовый c диалог команд и ответов. Передается однострочное сообщение, и в коротком порядке однострочное сообщение принимается в качестве ответа.

Но отправка SMS-сообщения с использованием команды CMGS не следует этому простому диалоговому окну.
Тем не менее, ваша программа все равно пытается принудительно создать такую ​​конструкцию «один-один-ответ-один-ответ» (в результате получаются, по-видимому, ненадежные результаты).
Согласно технической спецификации GSM , команда CMGS может / должна обрабатываться как обмен записи / чтения

[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>

Обратите внимание, что этот ответ не подходит для канонического чтения (т. Е. Обычные символы окончания строки находятся в начале, а не в конце этой последовательности).
Также обратите внимание, что каждый символ возврата каретки в Переданный текст сообщения будет генерировать передачу prompt_response модемом.


Учитывая сложность команды CMGS, передача нескольких строк вашей программой, а затем ожидание обработки только одного ответа склонна к ненадежным результатам.
Я вижу другие проблемы с вашим кодом , но не такой серьезный, как это неправильное обращение с командой CMGS.
Этот «ответ» только укажет на этот серьезный недостаток в вашей программе (то есть предложит «help» ), а не предоставит решение и / или переписать


В итоге:

Как ожидать записи байта в последовательный GSM-модем?

Ваша программа должна ждать, пока она не прочитайте ответ на предыдущую передачу, прежде чем она отправит (следующее) сообщение / команду. Байты в этом сообщении могут быть отправлены без каких-либо задержек.
Обратитесь к спецификации GSM за информацией, которая генерирует ответы.

...