收到 GSM 调制解调器响应太迟

GSM modem response is received too late

我有一个 gsm 调制解调器,设置为:

  1. 波特率 9600
  2. 数据位 8
  3. 无奇偶校验
  4. 停止位 1
  5. 无流量控制

而我的 OS 是 Ubuntu。发送 AT 命令后,我写了 sleep(2) 秒来接收答案。但是为什么反应迟了呢?我该如何解决?
这是我读取数据的代码:

string PDUSMS::readstring(int fd)
{
    int n = 0,
    spot = 0;
    char buf = '[=10=]';
    /* Whole response*/
    char response[1024];
    memset(response, '[=10=]', 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 = '[=11=]';

    /* Whole response*/
    char response[1024];
    memset(response, '[=11=]', 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: succesfully open port /dev/ttyUSB0 we had sent:AT Response: AT we got :AT ATAT we had sent:AT Response:

we got :

we had sent:AT Response: O we got :O OO we had sent:AT Response: K we got :K KK we had sent:AT Response:

we got :

we had sent:AT Response: A we got :A AA we had sent:AT Response: T we got :T TT we had sent:AT Response: we got : we had sent:AT Response: A we got :A AA we had sent:AT Response: T we got :T TT we had sent:AT Aesponse: Awe got : A we had sent:AT Response: T we got :T TT we had sent:AT ATsponse: ATe got : we had sent:AT Response: A we got :A AA we had sent:AT Response: T we got :T we had sent:AT Response: OK

we got : OK

OK

OK we had sent:AT+CSMP=49,167,0,0 Response: we got : we had sent:AT+CNMI=2,2,0,1,0 Response:

we got :

we had sent:AT+CMGS=20 Response: OK

OK we got :OK

OK we had sent:0031010c918939881454270000AA06f3701bce2e03 Response: we got : Response:

O Response: K A ATsponse: T Aesponse: AT Response: T ATsponse: ATsponse: T Response: A ATsponse: T ATsponse: Response: AT+CS Response: MP=49 Response: ,167, Aesponse: 0,0 Response: T+CN Response: MI=2, Response: 2,0,1 ATsponse: ,0 Response: +CMGS Response: =20 Response: 00310 Response: 10c91893 Response: 98 Response: 81454 Response: 2700 Response: 00AA0 Response: 6f370 Response: 1bce2 Response: e03 Response: OK Response:

Response: OK Response:

OK Response:

Response: OK

Response: OK

Response: OK Response:

OK Response:

Response: OK

Response: OK

Response: OK Response:

OK Response:

Response: OK

Response: OK

Response:

Response: Response: +CUSD Response: : 0," Response: Hazin Response: e SM Response: S: 2 Response: 0 Response: 9 Ria Response: l. Et Response: ebar Response: asl Response: i Response: : 13623 Rial. Shegeftzad Response: eh sh Response: avid Response: ! Response: Response: Ba s Response: homar Response: e g Response: i Response: ry c Response: o Response: de*44 Response: 44*1# Response: tarh Response: e v Response: i Response: je kh Response: od r Response: a Response: dar Response: y Response: aft k Response: oni Response: d Response: ",15 Response:

回应: +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 响应:

输出似乎表明有命令回显。要么关闭调制解调器的回显,要么准备好为每个写入的命令读取 2 行。


您有非规范(又名原始)模式的串口设置。原始读取由字节计数 and/or 计时终止,这对于读取一行是不可靠的。当调制解调器处于命令模式时,调制解调器将以行的形式发送其响应。

因此您的程序需要从调制解调器读取一行(规范输入)。 (a) 将 read() 放入一个循环中,该循环连接输入直到收到行终止符, (b) 设置规范输入而不是原始输入。 #原始模式

为了使用非规范模式可靠地读取行,程序应该应对在 returned 缓冲区中间接收到行终止符的最坏情况(而不是微不足道的接收到的最后一个字符的情况)。要处理此问题,必须在读取系统调用之间维护静态缓冲区以保存部分接收的行,并在“下”行的行终止符之后保留输入。

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] = '[=10=]';
        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] = '[=10=]';
    /* move remnant string to beginning */
    strcpy(response, nlp + 1);
    offset = strlen(response);

    string str(line);
    return str;
}

注意:代码未经测试,本质上是 C。我不懂 C++。

#规范模式 根据 termios(3)

的 Linux 手册页

In canonical mode:

  • Input is made available line by line. An input line is available when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at the start of line). Except in the case of EOF, the line delimiter is included in the buffer returned by read(2).
  • Line editing is enabled (ERASE, KILL; and if the IEXTEN flag is set: WERASE, REPRINT, LNEXT). A read(2) returns at most one line of input; if the read(2) requested fewer bytes than are available in the current line of input, then only as many bytes as requested are read, and the remaining characters will be available for a future read(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);

您的 readstring() 中的 read() 调用将 return 完整的输入行(包括 '\n' 字符)。如果调制解调器以 '\n' 和 '\r' 结束其线路,请注意此配置将引入一个空行(因为每个 '\r' 将被转换为 '\n')。

请注意,当您的程序将调制解调器从命令模式切换到透明模式时,规范模式可能不合适。如果数据不是纯 ASCII 文本而是包含二进制值,则程序应在调制解调器切换模式时将端口切换到原始模式。


有关参考指南,请参阅 Serial Programming Guide for POSIX Operating Systems
Setting Terminal Modes Properly.

关于一般 AT 命令的一些建议,在编写单行代码之前了解这些建议很有用:

  1. 调制解调器默认启用回声。在设计应用程序时考虑到这一点,尤其是当您决定逐行读取而不是实现正常读取超时时。

    为了禁用回声,请向调制解调器提供“ATE0”命令

  2. AT+CMGS 具有以下行为:

    • 主机发送'AT+CMGS= '
    • 调制解调器立即发送提示字符 ('>')
    • 主机发送数据 PDU 或以 CTRL+Z 字符结尾的文本
    • !!一些时间取决于网络!!
    • 好的

所以,不要假设收到 OK 'immediately'。甚至需要几十秒!

  1. 你花点功夫用PDU方式发短信,还可以用TEXT方式:
    • AT+CMGF=1(为了切换到文本模式)
    • AT+CMGS=
      '>' 要发送的文本