GSM модем отвечает слишком поздно

У меня есть модем GSM, что настройка:
1. BaudRate 9600
2. Базы данных 8
3. Нет паритета
4. Стопбит 1
5. Нет управления потоком
И мой Os - Ubuntu. После отправки AT-команды я написал sleep(2) секунды, чтобы получить ответ. Но почему это слишком поздно? И как я могу решить это?
это мой код для чтения данных:

string PDUSMS::readstring(int fd)
{
    int n = 0,
        spot = 0;
    char buf = '\0';
    /* Whole response*/
    char response[1024];
    memset(response, '\0', sizeof response);
    n=read(fd,&response,1024);
//---------------------------
    if (n < 0) {
        std::cout << "Error reading: " << strerror(errno) << std::endl;
    }
    else if (n == 0) {
        std::cout << "Read nothing!" << std::endl;
    }
    else {
        std::cout << "Response: " << response << std::endl;
    }
    string str(response);
    return str;
//---------------------------------------------------
}

как сделать быстрое чтение. прочитать всю строку ответа?
это весь мой код:

int fd; /* File descriptor for the port */
/*
    * 'open_port()' - Open serial port 1.
    *
    * Returns the file descriptor on success or -1 on error.
    */

    int openport(void)
    {

        fd=open("/dev/ttyS1",O_RDWR|O_NOCTTY|O_NDELAY);
        if (fd==-1)
        {
            perror("open_port: unable to open port\n");
            return -1;
        }
        else
        {
            printf("open_port: succesfully open port /dev/ttyUSB0\n");
            fcntl(fd,F_SETFL,0);
            return 1;
        }
    }
   //========================================================================

   void closeport(void)
   {
       close(fd);
   }

   void configport(void)
   {

       struct termios tty;
       struct termios tty_old;
       memset (&tty, 0, sizeof tty);

       /* Error Handling */
       if ( tcgetattr ( fd, &tty ) != 0 ) {
          std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl;
       }

       /* Save old tty parameters */
       tty_old = tty;

       /* Set Baud Rate */
       cfsetospeed (&tty, (speed_t)B9600);
       cfsetispeed (&tty, (speed_t)B9600);

       /* Setting other Port Stuff */
       tty.c_cflag     &=  ~PARENB;            // Make 8n1
       tty.c_cflag     &=  ~CSTOPB;
       tty.c_cflag     &=  ~CSIZE;
       tty.c_cflag     |=  CS8;

       tty.c_cflag     &=  ~CRTSCTS;           // no flow control
       tty.c_cc[VMIN]   =0;//  1;                  // read doesn't block
       tty.c_cc[VTIME]  = 2;// 5;                  // 0.5 seconds read timeout
       tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines

       /* Make raw */
       cfmakeraw(&tty);

       /* Flush Port, then applies attributes */
       tcflush( fd, TCIFLUSH );
       if ( tcsetattr ( fd, TCSANOW, &tty ) != 0) {
          std::cout << "Error " << errno << " from tcsetattr" << std::endl;
       }
   }
//------------------------------------------------------------  
string PDUSMS::SendandReciveData(string s,int fd)
{

  int i;
  string o,e,t;

  try
  {
    cout<<" we had sent:"<<s<<"\n";
    SendString(s,fd);


        sleep(1);
        o=readstring(fd);
//    for(int i=0;i<3;i++)
//     if (o.find(s)!=-1)
//     {
//         sleep(1.5);
//         o=readstring(fd);
//     }
    cout<< " we got :"<<o<<"\n";
    i = StateStr(o, s); //remove source command from the beging of string
    if (i >= 0)   //-becasause the command return back to us
      o = copy(o, s.length(), o.length() - s.length()); //return command to caller

  }
  catch(const std::exception&)
  {
    o = " ";
  }
  return o;

}

void PDUSMS::SendString(string s,int fd)
{
    char buf[255];
    strcpy(buf,s.c_str());
    write(fd, buf, s.length());
//    usleep(500);
}

string PDUSMS::readstring(int fd)
{
    int n = 0,
        spot = 0;
    char buf = '\0';

    /* Whole response*/
    char response[1024];
    memset(response, '\0', sizeof response);


    n=read(fd,&response,1024);
//---------------------------
    if (n < 0) {
        std::cout << "Error reading: " << strerror(errno) << std::endl;
    }
    else if (n == 0) {
        std::cout << "Read nothing!" << std::endl;
    }
    else {
        std::cout << "Response: " << response << std::endl;
    }
    string str(response);
    return str;
//---------------------------------------------------
}
bool PDUSMS::SendSMS(int fd,string Num,string Text,int MR,int CMR,int SMS_PART,int sms_id,int &sms_index,bool Delivery,bool MagicSMS,bool &Deliverd)
{

  string c, o, id;
  int i, l, Curr_PART, R_MR;
  string SNum, SDate, STime, PDU_Data, SMSC_Num, RTime, RDate, num1;
  ReceievedMessageKind PDU_Data_Type;
  bool sent, deliv;

  string Temp;
    MagicSMS=false;
    string result=" ";
    result=SendandReciveData("AT+CSMP=49,167,0,0\r",fd);
    result=SendandReciveData("AT+CNMI=2,2,0,1,0\r",fd);

    c = "AT+CMGS="; // at commmand for s} SMS
    o = EncodePDU(Num, Text, MR, CMR, SMS_PART, sms_id, Delivery, MagicSMS);

    c = c + IntToStr(o.length()/ 2 - 1); //Adding length of Pdu to at command
    c += "\r"; //adding <CR> to at comm &&
    Temp = SendandReciveData(c,fd); //send at command to phone
    o += (char)26; //add <CTRL-Z> to the PDU Text

    Temp = SendandReciveData(o,fd); //S} Text To The Phone

}

это мой вывод без сна:

open_port: успешно открыт порт /dev/ttyUSB0, который мы отправили: AT Ответ: AT мы получили: ATAT, который мы послали: AT Ответ:

у нас есть:

мы отправили: AT ответ: O мы получили: O OO мы отправили: AT ответ: K мы получили: K KK мы отправили: AT ответ:

у нас есть:

мы отправили: AT Ответ: A мы получили: A AA мы послали: AT Ответ: T мы получили: T TT мы отправили: AT Ответ: мы получили: мы отправили: AT Ответ: A мы получили: A AA мы отправил: AT Ответ: T мы получили: T TT мы отправили: AT Ответ: Awe получил: A мы отправили: AT Ответ: T мы получили: T TT мы отправили: ATssponse: ATe получили: мы отправили: Ответ AT: A, который мы получили: AA, который мы послали: AT Ответ: T, который мы получили: T, который мы послали: AT Ответ: OK

мы получили: ОК

Хорошо

ОК, мы отправили:AT+CSMP=49 167,0,0 Ответ: мы получили: мы отправили:AT+CNMI=2,2,0,1,0 Ответ:

у нас есть:

мы отправили:AT+CMGS=20 Ответ: ОК

ОК, мы получили: ОК

ОК, мы отправили:0031010c918939881454270000AA06f3701bce2e03 Ответ: мы получили: Ответ:

O Ответ: KA ATsponse: T Aesponse: AT Ответ: T ATsponse: T Ответ: T ATsponse: T ATsponse: Ответ: AT + CS Ответ: MP=49 Ответ:,167, Aesponse: 0,0 Ответ: T+ Ответ CN: MI=2, Ответ: 2,0,1 Ответ:,0 Ответ: +CMGS Ответ: =20 Ответ: 00310 Ответ: 10c91893 Ответ: 98 Ответ: 81454 Ответ: 2700 Ответ: 00AA0 Ответ: 6f370 Ответ: 1bce2 Ответ: e03 Ответ: ОК Ответ:

Ответ: ОК Ответ:

ОК Ответ:

Ответ: ОК

Ответ: ОК

Ответ: ОК Ответ:

ОК Ответ:

Ответ: ОК

Ответ: ОК

Ответ: ОК Ответ:

ОК Ответ:

Ответ: ОК

Ответ: ОК

Отклик:

Ответ: Ответ: +CUSD Ответ:: 0,"Ответ: Хазин Ответ: e SM Ответ: S: 2 Ответ: 0 Ответ: 9 Ria Ответ: 1. Et Ответ: ebar Ответ: asl Ответ: i Ответ:: 13623 Rial. Shegeftzad Ответ: eh sh Ответ: avid Ответ:! Ответ: Ответ: Ba s Ответ: homar Ответ: например Ответ: i Ответ: ry c Ответ: o Ответ: de*44 Ответ: 44*1# Ответ: tarh Ответ: e v Ответ: i Ответ: je kh Ответ: od r Ответ: a Ответ: dar Ответ: y Ответ: aft k Ответ: oni Ответ: d Ответ: ",15 Ответ:

Ответ: + CM Ответ: G Ответ: S: 21 Ответ: 8

O Ответ: K Ответ: Ответ:

Отклик:

Ответ: +CUSD: Ответ: 2

Отклик:

Ответ: +CDS: Ответ: 25

Ответ: 0 Ответ: 006D Ответ: A Ответ: 0C9 Ответ: 1 Ответ: 8939 Ответ: 8 Ответ: 8145 Ответ: 4 Ответ: 2751 Ответ: 1 Ответ: 16131 Ответ: 016 Ответ: 3 Ответ: 4151 Ответ: 1 Ответ: 1613 Ответ: 1 Ответ: 0183 Ответ: 4 Ответ: 100 Ответ:

1 ответ

Решение

Вывод, кажется, указывает на наличие команды echo. Либо отключите эхо на модеме, либо будьте готовы прочитать 2 строки на каждую написанную команду.


У вас есть настройка последовательного порта для неканонического (он же необработанного) режима. Необработанное чтение заканчивается количеством байтов и / или временем, которое ненадежно для чтения строки. Пока модем находится в командном режиме, модем будет отправлять свои ответы в виде строк.

Таким образом, ваша программа должна прочитать строку (которая является каноническим вводом) с модема. Либо (a) поместите read() в цикл, который объединяет входные данные, пока не будет получен разделитель строки, ИЛИ (b) настройте канонический ввод вместо raw.

Сырой режим

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

static char response[1024] = {0};
static int offset = 0;

string PDUSMS::readline(int fd)
{
    int n;
    char line[1024];
    char *nlp;

    while ((nlp = strpbrk(&response[offset], "\n\r")) == NULL) {
        n = read(fd, &response[offset], sizeof(response) - offset - 1);
        if (n < 0) {
            std::cout << "Error reading: " << strerror(errno) << std::endl;
            continue;
        }
        offset += n;
        response[offset] = '\0';
        if (offset >= sizeof(response) - 1) {
            nlp = &response[offset - 1];
            break;
        }
    }
    std::cout << "Response: " << response << std::endl;

    /* extract a line from the buffer */
    strncpy(line, response, nlp - response + 1);
    line[nlp - response + 1] = '\0';
    /* move remnant string to beginning */
    strcpy(response, nlp + 1);
    offset = strlen(response);

    string str(line);
    return str;
}

Примечание: код не проверен и по сути является C. Я не знаю C++.

Канонический режим

Согласно справочной странице по Linux для termios(3)

В каноническом режиме:

  • Ввод сделан доступным построчно. Строка ввода доступна, когда введен один из разделителей строки (NL, EOL, EOL2; или EOF в начале строки). За исключением случая EOF, разделитель строк включается в буфер, возвращаемый read(2).

  • Разрешено редактирование линии (ERASE, KILL; и если установлен флаг IEXTEN: WERASE, REPRINT, LNEXT). Read (2) возвращает максимум одну строку ввода; если read (2) запросил меньше байтов, чем доступно в текущей строке ввода, то будет прочитано только столько байтов, сколько запрошено, а остальные символы будут доступны для чтения в будущем (2).

Чтобы настроить последовательный порт для канонического режима для модема в командном режиме (вместо ввода терминала общего назначения), в вашем configport() удалите три оператора (с их комментариями):

    tty.c_cc[VMIN]   =0;//  1;                  // read doesn't block
    tty.c_cc[VTIME]  = 2;// 5;                  // 0.5 seconds read timeout

   /* Make raw */
   cfmakeraw(&tty);

(Обязательно сохраните настройки CREAD | CLOCAL.)
И вставить новые заявления:

    tty.c_iflag |= ICRNL | IGNBRK;
    tty.c_iflag &= ~(IXON | IXOFF | IXANY | INLCR);

    tty.c_lflag |= ICANON | ISIG  | IEXTEN;
    tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ECHOKE);

Вызов read() в вашей readstring() вернет полную строку ввода (включая символ '\n'). Если модем завершает свою строку и "\ n", и "\ r", имейте в виду, что эта конфигурация будет содержать пустую строку (потому что каждый "\ r" будет преобразован в "\ n").

Обратите внимание, что канонический режим может быть неуместным, когда ваша программа переводит модем из командного режима в прозрачный режим. Если данные не являются чистым текстом ASCII, но содержат двоичные значения, то программа должна переключить порт в необработанный режим, когда модем переключает режим.


Справочные руководства см. В Руководстве по последовательному программированию для операционных систем POSIX.
и установка режимов терминала правильно.

Несколько советов относительно AT-команд в целом, которые полезно знать перед написанием одной строки кода:

  1. У модемов по умолчанию включено эхо. Примите это во внимание при разработке приложения, особенно если вы решите читать построчно, а не выполнять обычное чтение, время ожидания которого истекло.

    Чтобы отключить эхо, подайте команду "ATE0" модему

  2. AT+CMGS имеет следующее поведение:

    • Хост отправляет "AT+CMGS="
    • Модем немедленно отправляет подсказку ('>')
    • Хост отправляет данные либо PDU, либо текст, оканчивающийся CTRL + Z char
    • !! Некоторое время в зависимости от сети!
    • Хорошо

Таким образом, не предполагайте, чтобы получить ОК "немедленно". Это может занять несколько десятков секунд!

  1. Вы тратите некоторые усилия на отправку SMS в режиме PDU, но также можно использовать режим TEXT:
    • AT + CMGF = 1 (для переключения в текстовый режим)
    • AT+CMGS=
      '>' текст для отправки
Другие вопросы по тегам