Попытка записи AT-команд на кабель USB-OBDii, использующий FT232R для чтения данных ECU (ISO 9141-2)

Я пытаюсь прочитать данные из ECU, используя протокол ISO 9141-2. Я использую кабель 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;
}

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 читает beyon границы строкового литерала. Было бы лучше сделать:

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

тогда вы бы написали правильный объем данных.

Как я уже говорил в комментариях, я провел поиск в Google по ISO 9141-2 и не смог найти достоверной информации о том, использует ли он AT-команды. Поэтому, пожалуйста, проверьте руководство по чипу OBDII, который вы пытаетесь прочитать.

Википедия говорит

ISO 9141-2. Этот протокол имеет асинхронную последовательную скорость передачи данных 10,4 кбит / с. Это несколько похоже на RS-232; однако уровни сигналов различны, и связь происходит по одной двунаправленной линии без дополнительных сигналов квитирования. ISO 9141-2 в основном используется в автомобилях Chrysler, европейских и азиатских.

Мне кажется, что этот протокол похож только на RS232, возможно, вам нужен другой конвертер (кроме FT232R), который поддерживает эту скорость передачи данных. На этой странице также указано, что скорость передачи данных составляет 10400 бит / с, что не поддерживается по умолчанию. Я нашел этот пост, в котором также предлагается использовать два преобразователя: один для связи между OBD2 и преобразователем со скоростью 10,4 кбит / с, а другой - для преобразователя и вашего ПК со стандартной скоростью передачи данных, например 19,2 кбит / с.

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

Для адаптера OBD2 совершенно нормально отправлять вам ту же команду, которую вы отправили. Это называется режимом эха и его можно отключить, отправив ATE0\r как первая команда. Если вы прочитаете больше ответа, вы должны увидеть результат выполнения вашей команды после повторного запроса.

Также обратите внимание, что AT-команды обрабатываются OBD2 без отправки данных в какие-либо ECU, по шине будут отправляться только PID (команды, состоящие из цифр).

Итак, сначала: я не знаю 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. Надеюсь, это помогло!

Другие вопросы по тегам