分割从串口接收到的字符串

Posted

技术标签:

【中文标题】分割从串口接收到的字符串【英文标题】:Segmenting the strings received from serial port 【发布时间】:2015-10-29 14:14:14 【问题描述】:

我正在使用 Xbee 模块通过串口接收传感器数据。传感器连接到 Arduino,它们正在向连接到我的笔记本电脑的 zigbee 发送数据。在这里,是我用来显示接收到的数据的代码。

#include <fcntl.h>
#include <stdio.h>
#include <termios.h>
#include <stdlib.h>
#include <string.h>

/* Change to the baud rate of the port B2400, B9600, B19200 etc as per Arduino board is sending */
#define SPEED B9600

/* Change to the serial port you want to use /dev/ttyUSB0, /dev/ttyS0, etc. */
#define PORT "/dev/ttyS1"


/* Sensor raw datas */
int accLowX, accLowY, accLowZ;
int accHighX, accHighY, accHighZ;


int main( )
    int fd = open( PORT, O_RDONLY | O_NOCTTY );
    if (fd <0) perror(PORT); exit(-1); 
    struct termios options;

    bzero(&options, sizeof(options));
    options.c_cflag = SPEED | CS8 | CLOCAL | CREAD | IGNPAR;
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &options);

    int r;
    char buf[255];
    char sensorDatas[255];

    while( 1 )

        r = read( fd, buf, 255 );
        buf[r]=0;
        memcpy(sensorDatas, buf, strlen(buf)+1);
        printf( "%s", sensorDatas);

        // separate the strings for sensor datas




        //




    



我收到的数据的输出是:

56-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:341 AHZ:421
57-ALX:350 ALY:349 ALZ:351 AHX:354 AHY:341 AHZ:422
58-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
59-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
60-ALX:350 ALY:349 ALZ:351 AHX:352 AHY:342 AHZ:422
61-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:421
62-ALX:350 ALY:349 ALZ:351 AHX:354 AHY:342 AHZ:422
63-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
64-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
65-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:421
66-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:421
67-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
68-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
69-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:341 AHZ:421
70-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
71-ALX:350 ALY:349 ALZ:351 AHX:352 AHY:342 AHZ:422
72-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
73-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
74-ALX:350 ALY:349 ALZ:351 AHX:352 AHY:342 AHZ:422
75-ALX:350 ALY:349 ALZ:351 AHX:352 AHY:342 AHZ:422
76-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
77-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
78-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:421
79-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
80-ALX:350 ALY:350 ALZ:351 AHX:353 AHY:343 AHZ:422
81-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
82-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422

在这里我不能使用 strlen 函数,因为它给我输出 0,因为 r 设置为零。在不知道字符串长度的情况下,我无法继续对其进行分段以获取以下数据。 考虑接收到的字符串 56,我想在每次迭代中将以下值存储在以下变量中

accLowX = 350, accLowY = 349, accLowZ=351
accHighX =353, accHighY = 341, accHighZ =421;

我该怎么做?

【问题讨论】:

我实际上没有得到这部分:"因为它给了我输出 0,因为 r 设置为零" 如果 r 设置为零,则什么都没有读?见here。 否,如果 buf[r]=0,它将读取直到接收到的字节结束。否则,它还给了我很多垃圾价值。在 memcpy 之后,如果我使用 strlen(sensorDatas) 并打印它,它只会给我零 (0)。 但是,你可以使用strlen() 不是吗?如果 strlen() 结果为 0,这意味着 r 必须为零。 是的,很明显,r = 0,但 sensorDatas 不为零。它与我们收到的字符串完全相同。为什么 strlen(sensorDatas) 应该给零?什么时候打印如上所示的字符串? 【参考方案1】:

我将首先创建一个字符串数组并将每一行放入其中(可能是getline?)然后,您可以使用 boost 的 split 功能并使用空格作为分隔符。然后,将所有这些放在一个临时数组中。 http://www.cplusplus.com/faq/sequences/strings/split/ -> 提升字符串算法:拆分

之后,只需使用find (http://www.cplusplus.com/reference/string/string/find/) 来识别每个参数,并为每个参数使用一个 switch case。或者,获取数组并直接使用它,因为所有位置都是相同的(例如:[0] = ALX:(Number)、[1] = ALY:(Number) 等)。希望对您有所帮助。

【讨论】:

【参考方案2】:

你可以使用sscanf,像这样-

if(sscanf(s,"%*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d"
                   ,&accLowX,&accLowY,&accLowZ,&accHighX,&accHighY,&accHighZ))==6)
       // do something 
 

【讨论】:

@Student15 是的,使用printf 进行打印,或者如果您想对值进行任何操作,请执行该操作。 if(sscanf(sensorDatas,"%*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:% d %*[^:]:%d %*[^:]:%d" ,&accLowX,&accLowY,&accLowZ,&accHighX,&accHighY,&accHighZ)==6) // 做一些事情 printf( "accLowX : %d\ n", accLowX); printf("accLowY : %d\n", accLowY); printf("accLowZ : %d\n", accLowZ);它没有用 :( 它没有进入 If LOOP 我想让它像在每个字符串之后(收到一个完整的行),我将它们存储在 accLowX、accLowY...等中并处理它们。在处理完该迭代后,我将完成将在这些变量中存储新值并处理它们 @Student15 把它放在你读取值的循环中,这样就可以了。您 while 循环从文件中读取值,将其放入其中并进行处理 我完全按照你说的做了。但它没有显示 accLowX、accLowY、accLowZ 的 printf 值。我认为它不会进入 if 循环。你能看看这里吗? dropbox.com/s/lxpjro55cvxyl4y/code.txt?dl=0【参考方案3】:

经过您的宝贵建议,我找到了解决我问题的方法。这段代码的灵感来自CodeSource

修改代码:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BAUDRATE B9600  /* baudrate settings are defined in <asm/termbits.h>, which is included by <termios.h> */
#define MODEMDEVICE "/dev/ttyS1" /* change this definition for the correct port */
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE; 

int main()

    int fd,c, res;
    struct termios oldtio,newtio;
    char buf[255];
    char sensorDatas[255];
    /* Sensor raw datas */
    int accLowX, accLowY, accLowZ;
    int accHighX, accHighY, accHighZ;
    /* 
    Open modem device for reading and writing and not as controlling tty
    because we don't want to get killed if linenoise sends CTRL-C.
    */
    fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); 
    if(fd <0) 
        perror(MODEMDEVICE); 
        exit(-1); 
        

    tcgetattr(fd,&oldtio); /* save current serial port settings */
    //bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */

    /* 
    BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
    CRTSCTS : output hardware flow control (only used if the cable has
            all necessary lines. See sect. 7 of Serial-HOWTO)
    CS8     : 8n1 (8bit,no parity,1 stopbit)
    CLOCAL  : local connection, no modem contol
    CREAD   : enable receiving characters
    */
    newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

    /*
    IGNPAR  : ignore bytes with parity errors
    ICRNL   : map CR to NL (otherwise a CR input on the other computer
            will not terminate input)
    otherwise make device raw (no other input processing)
    */
    newtio.c_iflag = IGNPAR | ICRNL;

    /*
    Raw output.
    */
    newtio.c_oflag = 0;

    /*
    ICANON  : enable canonical input
    disable all echo functionality, and don't send signals to calling program
    */
    newtio.c_lflag = ICANON;

    /* 
    initialize all control characters 
    default values can be found in /usr/include/termios.h, and are given
    in the comments, but we don't need them here
    */
    newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */ 
    newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
    newtio.c_cc[VERASE]   = 0;     /* del */
    newtio.c_cc[VKILL]    = 0;     /* @ */
    newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
    newtio.c_cc[VTIME]    = 0;     /* inter-character timer unused */
    newtio.c_cc[VMIN]     = 1;     /* blocking read until 1 character arrives */
    newtio.c_cc[VSWTC]    = 0;     /* '\0' */
    newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */ 
    newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
    newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
    newtio.c_cc[VEOL]     = 0;     /* '\0' */
    newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
    newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
    newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
    newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
    newtio.c_cc[VEOL2]    = 0;     /* '\0' */

    /* 
    now clean the modem line and activate the settings for the port
    */
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd,TCSANOW,&newtio);

    /*
    terminal settings done, now handle input
    In this example, inputting a 'z' at the beginning of a line will 
    exit the program.
    */
    while (STOP==FALSE)      /* loop until we have a terminating condition */
    /* read blocks program execution until a line terminating character is 
    input, even if more than 255 chars are input. If the number
    of characters read is smaller than the number of chars available,
    subsequent reads will return the remaining chars. res will be set
    to the actual number of characters actually read */
    res = read(fd,buf,255); 
    memcpy(sensorDatas, buf, strlen(buf)+1);
    buf[res]=0;             /* set end of string, so we can printf */
    sensorDatas[res]=0;
    printf("%s", sensorDatas);

    /* Segmentation of Datas received from Arduino  */
    if(sscanf(sensorDatas,"%*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d"
                   ,&accLowX,&accLowY,&accLowZ,&accHighX,&accHighY,&accHighZ)==6) 
        // do something 

        printf( "accLowX : %d\n", accLowX);
        printf( "accLowY : %d\n", accLowY);
        printf( "accLowZ : %d\n", accLowZ);

    


    
    /* restore the old port settings */
    tcsetattr(fd,TCSANOW,&oldtio);

【讨论】:

以上是关于分割从串口接收到的字符串的主要内容,如果未能解决你的问题,请参考以下文章

单片机模拟串口通讯,将串口接收到的数据原样发送,收到的数据长度不同。怎样判断数据接收完毕?

易语言串口调试怎么判断串口数据接收完成。最好有源码每次接收到的数据长度不一样。(无自定义规约)

串口调试工具哪个好

Arduino 利用readString函数对串口数据进行接收和打印

stm32串口通讯时怎样接收字符串

Arduino 从串口接收到的数据中提取整型数据(parseInt)