尝试将AT命令写入USB到OBDii电缆,使用FT232R读取ECU数据(ISO 9141-2)

问题描述 投票:0回答:3

我正在尝试使用ISO 9141-2协议从ECU读取数据。我使用的电缆是OBD2到USB,使用FT232R芯片。我正在运行的程序在C中。

当我向串行端口(ttyUSB0)写入命令时,它会写入很好但当它读回接收到的数据时,它只返回相同的数据。而不是从ECU返回数据,我不确定它为什么这样做。

任何帮助都很棒,谢谢。

示例代码 - 尝试设置波特率等,但也没有运气。

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    char *port = "/dev/ttyUSB0";
    char receivedData[100];

    int n, fd; 

    fd = open(port, O_RDWR | O_NOCTTY);

    if(fd > 0){
        n = write(fd, "AT I\r\n", 10);
        read(fd, receivedData, 10);
        printf("%s\n", receivedData);

        close(fd);
    }
    else{
        printf("failed to open device\n");
    }

    return 0;
}
c serial-port usb at-command obd-ii
3个回答
1
投票

即使这不是问题的直接答案,但这段代码不适合评论部分。

我使用这些函数来初始化串行线:

#include <stdio.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>


struct baud_map {
    int baud;
    speed_t speed;
};

struct baud_map baudmap[] = {
    { 50        , B50 },
    { 75        , B75 },
    { 110       , B110 },
    { 134       , B134 },
    { 150       , B150 },
    { 200       , B200 },
    { 300       , B300 },
    { 600       , B600 },
    { 1200      , B1200 },
    { 1800      , B1800 },
    { 2400      , B2400 },
    { 4800      , B4800 },
    { 9600      , B9600 },
    { 19200     , B19200 },
    { 38400     , B38400 },
    { 57600     , B57600 },
    { 115200    , B115200 },
    { 230400    , B230400 },
    { 0,      0 }
};

int dbits_map[] = { 0, 0, 0, 0, 0, CS5, CS6, CS7, CS8 };

enum parity_t {
    PARITY_NO_PARITY,
    PARITY_ODD,
    PARITY_EVEN
};

int baud_to_speed(int baud, speed_t *speed)
{
    if(speed == NULL)
        return 0;

    struct baud_map *map = baudmap;
    while(map->baud)
    {
        if(map->baud == baud)
        {
            *speed = map->speed;
            return 1;
        }

        map++;
    }

    return 0;
}

/*
 * tty: "/dev/ttyUSB0"
 * baud: baudrate, for example 9600
 * parity: see enum parity_t
 * stop_bits: 1 or 2
 * data_bits: [5-8]
 *
 * return the fd on success, -1 on failure
 */
int openSerial_long(const char *tty, int baud, enum parity_t parity, int stop_bits, int data_bits)
{
    int fd;

    speed_t speed;

    if(baud_to_speed(baud, &speed) == 0)
    {
        fprintf(stderr, "Invalid baudrate %d\n", baud);
        return 0;
    }

    fd = open(tty, O_RDWR | O_NOCTTY  | O_NONBLOCK);

    if(fd == -1)
    {
        fprintf(stderr, "Could not open %s as a tty: %s\n", tty, strerror(errno));
        return -1;
    }

    struct termios termios;

    if(tcgetattr(fd, &termios) == -1)
    {
        fprintf(stderr, "Could not get tty attributes from %s: %s\n", tty, strerror(errno));
        close(fd);
        return -1;
    }

    // setting common values
    termios.c_iflag &= ~ICRNL;           // do not translate \r into \n
    termios.c_oflag &= ~OPOST;           // do not map \n to \r\n
    termios.c_cflag |= (CREAD | CLOCAL); // enable receiver & ignore model ctrl lines
    termios.c_lflag |= (ISIG | ICANON);  // enable signals and noncanonical mode
    termios.c_lflag &= ~ECHO;             // disable echo


    cfsetispeed(&termios, speed);
    cfsetospeed(&termios, speed);

    switch(parity)
    {
        case PARITY_NO_PARITY:
            termios.c_cflag &= ~PARENB;
            break;
        case PARITY_ODD:
            termios.c_cflag |= PARENB;
            termios.c_cflag |= PARODD;
            break;
        case PARITY_EVEN:
            termios.c_cflag |= PARENB;
            termios.c_cflag &= ~PARODD;
            break;
        default:
            fprintf(stderr, "invalid parity\n");
            break;
    }

    if(stop_bits == 1)
        termios.c_cflag &= ~CSTOPB;
    else if(stop_bits == 2)
        termios.c_cflag |= CSTOPB;
    else
        fprintf(stderr, "Invalid stop bit\n");

    int bits;

    switch(data_bits)
    {
        case 5:
        case 6:
        case 7:
        case 8:
            bits = dbits_map[data_bits];
            break;

        default:
            bits = -1;

    }

    if(bits != -1)
    {
        termios.c_cflag &= ~CSIZE;
        termios.c_cflag |= bits;
    } else
        fprintf(stderr, "Invalid data size\n");


    if(tcsetattr(fd, TCSANOW, &termios) == -1)
    {
        fprintf(stderr, "Could not get tty attributes from %s: %s\n", tty, strerror(errno));
        close(fd);
        return -1;
    }


    return fd;
}

/**
 * tty: "/dev/ttyUSB0"
 * baud: baudrate, for example 9600
 * mode: a string like 8N1 where 
 *       the first character is the number of data bits (range from 5-8)
 *       the second character is N (no parity), O (odd), E (even)
 *       the third character is the number of stop bits (1 or 2)
 */
int openSerial(const char *tty, int baud, const char *mode)
{
    if(tty == NULL || mode == NULL)
        return -1;

    if(strlen(mode) != 3)
    {
        fprintf(stderr, "invalid mode\n");
        return -1;
    }

    int stop_bits = mode[2];
    if(stop_bits != '1' && stop_bits != '2')
    {
        fprintf(stderr, "Invalid stop bits\n");
        return -1;
    }

    stop_bits -= '0';

    enum parity_t parity;
    switch(mode[1])
    {
        case 'n':
        case 'N':
            parity = PARITY_NO_PARITY;
            break;
        case 'o':
        case 'O':
            parity = PARITY_ODD;
            break;
        case 'e':
        case 'E':
            parity = PARITY_EVEN;
            break;
        default:
            fprintf(stderr, "Invalid parity\n");
            return -1;
    }

    int data_bits = mode[0] - '0';

    if(data_bits < 5 || data_bits > 8)
    {
        fprintf(stderr, "invalid data bits\n");
        return -1;
    }

    return openSerial_long(tty, baud, parity, stop_bits, data_bits);
}

最常见的模式是"8N1"(8位数据,无奇偶校验,1个停止位),我打开串行线路

int fd = open("/dev/ttyUSB0", 9600, "8N1");

if(fd == -1)
{
    fprintf(stderr, "Error, could not initialize serial line\n");
    exit(EXIT_FAILURE);
}

我不知道您必须使用哪些串行设置,请在手册中查找。

还要注意像这样的东西

n = write(fd, "AT I\r\n", 10);

这是错误的,它是未定义的行为,因为write正在读取超出字符串文字的边界。最好这样做:

const char *cmd = "AT I\r\n";
n = write(fd, cmd, strlen(cmd));

然后你会写出正确数量的数据。

就像我在评论中说的那样,我已经在ISO 9141-2上进行了谷歌搜索,但我找不到任何关于它是否使用AT命令的可靠信息。因此,请查看您尝试阅读的OBDII芯片的手册。

Wikipedia

ISO 9141-2。该协议的异步串行数据速率为10.4 kbit / s。它有点类似于RS-232;然而,信号电平是不同的,并且通信发生在单个双向线路上而没有额外的握手信号。 ISO 9141-2主要用于克莱斯勒,欧洲和亚洲车辆。

在我看来,这个协议只是类似于RS232,也许你需要另一个支持这种波特率的转换器(FT232R除外)。 This页面还指出波特率为10400位/秒,默认情况下不支持。我发现这个post也建议你应该使用两个转换器,一个用于OBD2和转换器之间的通信,速率为10.4 kbit / s,另一个转换器和PC之间的标准波特率,如19.2 kbits / s。

或者,您可以尝试设置自定义波特率,如herehere所述。我从来没有使用自定义波特率,所以我不知道这是否有效。


0
投票

好吧首先关闭:我不知道C.但是,你做了n =(无论发送AT命令)和你收到的读数据,然后打印。为什么不将读取的结果附加到变量并打印出来?或者,您可以使用基于bash的命令与串口进行通信,如“minicom”,我知道还有其他类似的。

还有其他一些注释,因为我最近一直在使用OBD2:AT命令发送给读者,而不是ECU。您可以使用minicom查看何时重置适配器(ATZ),ECU什么都不做。但是,发送03(或任何模式是ECU代码重置)它将清除您的代码。有关标题的更多信息以及多个ECU的更多信息,请参阅https://mechanics.stackexchange.com/questions/22982/received-frames-from-vehicles-with-multiple-ecu-chips

最后一点:不要忘记你有2种不同的波特率 - 一种用于USB串口到FT232R芯片,另一种用于从芯片到ECU。使用ELM327,后者通过AT命令完成,以更改原型。无论如何,请使用来自计算机的FT232R波特率,必要时在minicom中进行测试。希望这有帮助!


0
投票

OBD2适配器将您发送的命令发送回去是完全正常的。这称为回声模式,可以通过发送ATE0\r作为第一个命令来关闭。如果您阅读了更多答案,您应该会在回显的请求之后看到命令的结果。

另请注意,OBD2处理AT命令而不向任何ECU发送数据,只有PID(由数字组成的命令)将通过总线发送。

© www.soinside.com 2019 - 2024. All rights reserved.