【问题标题】:GSM modem response is received too late接收到 GSM 调制解调器响应太晚
【发布时间】:2021-09-02 16:13:48
【问题描述】:

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

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

我的操作系统是 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 我们得到: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 Aesponse:Awe got :A 我们已发送:AT Response:T we got :T TT 我们已发送:AT ATsponse: ATe got : we have sent:AT 响应: A 我们得到:A AA 我们已发送:AT 响应:T 我们得到:T 我们已发送:AT 响应:好的

我们得到了:好的

好的

好的,我们已经发送:AT+CSMP=49,167,0,0 响应:我们得到:我们有 发送:AT+CNMI=2,2,0,1,0 响应:

我们得到了:

我们已经发送:AT+CMGS=20 响应:OK

好的,我们得到了:好的

好的,我们已经发送:0031010c918939881454270000AA06f3701bce2e03 响应: 我们得到了:响应:

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

响应:OK 响应:

OK 响应:

响应:好的

响应:好的

响应:OK 响应:

OK 响应:

响应:好的

响应:好的

响应:OK 响应:

OK 响应:

响应:好的

响应:好的

回复:

回应:回应: +CUSD 响应::0," 响应:Hazin 响应:e SM 响应:S:2 响应:0 响应:9 Ria 响应:l. Et 响应:ebar 响应: asl 响应:i 响应::13623 里亚尔。 Shegeftzad 回应:嗯 sh 回应:狂热 回应:!响应: 响应: Ba s 响应: homar 响应:e g 响应: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 响应:

【问题讨论】:

  • “为时已晚” 表示参考点或截止日期。那是什么?您的意思是您认为响应?为什么要睡2秒?如果根本没有睡眠会发生什么(假设您使用阻塞读取)?
  • 您发送的是哪个 AT 命令?你的串口有什么超时?睡眠在哪里? (因为它没有出现在您发布的代码中)
  • @sawdust 。是的,抱歉太慢了。当我评论睡眠时(2)。当我向我的方法发送“AT+CMGS=..”时。首先写入它,然后休眠,然后读取端口并返回,然后我发送另一个 AT 命令。但它响应第一种方法等等。并且响应有延迟..什么是阻塞读取?
  • @Mats Petersson:这些 AT 命令发送 PDU 短信:“AT+CSMP”、“AT+CNMI”、“AT+CMGS”和 pdu 格式..这是我的超时端口:tty。 c_cc[VMIN] =0;// 1; // 读取不会阻塞 tty.c_cc[VTIME] = 2;//2 秒。并且睡眠是在读方法之前的写方法之后。
  • 您已经为非规范(又名原始)模式设置了串行端口。原始读取由字节计数和/或计时终止,这对于读取一行是不可靠的。但是您实际上需要从调制解调器中读取一行(这是规范输入)。由于明显的命令回显,可能会读取 2 行。要么设置为规范输入而不是原始输入,要么在 readstring() 中将 read() 放在一个循环中,该循环连接输入直到收到行终止符。要么关闭调制解调器上的回显,要么准备每个写入的命令读取 2 行。

标签: c++ serial-port gsm


【解决方案1】:

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


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

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

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

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++。

#规范模式 根据 termios(3)

的 Linux 手册页

在规范模式下:

  • 输入逐行可用。输入行分隔符(NL、EOL、EOL2;或开头的 EOF)时,输入行可用 线)。除了 EOF 的情况外,行分隔符包含在 read(2) 返回的缓冲区中。
  • 行编辑已启用(ERASE、KILL;如果设置了 IEXTEN 标志:WERASE、REPRINT、LNEXT)。 read(2) 最多返回一行输入;如果 read(2) 请求的字节数少于当前输入行中可用的字节数,则只读取请求的字节数,剩余的字符将可用于未来的 read(2)。

要在命令模式(而不是通用终端输入)下为调制解调器配置规范模式的串行端口,请在 configport() 中删除三个语句(及其 cmets):

    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() 调用将返回完整的输入行(包括 '\n' 字符)。如果调制解调器以 '\n' 和 '\r' 终止其行,请注意此配置将引入一个空行(因为每个 '\r' 将转换为 '\n')。

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


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

【讨论】:

    【解决方案2】:

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

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

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

    2. AT+CMGS 有如下行为:

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

    所以,不要假设“立即”收到 OK。甚至可能需要几十秒!

    1. 你用PDU模式发短信费了一番功夫,也可以用TEXT模式:
      • AT+CMGF=1(为了切换到文本模式)
      • AT+CMGS=
        '>' 待发送文本

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 2014-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多