I am using ESP-IDF to use UART to send and receive messages with an OBD-ii adapter. OBD-ii is a standard to read out information taken from automotive diagnostics. I am using this adapter. This adapter comes with an Arduino library. I tested it with this library and it works just fine. As I have to use ESP-IDF an Arduino library will not suffice so I translated it to C. I translated the UART-specific Arduino code to UART code of ESP-IDF. I then tested my library but unfortunately it did not work. It got into an infinite loop trying to read trying to process information and the problem appeared to be that it tried to process an empty value so the condition never exits because it has nothing to process. So, the problem was narrowed down to the fact that UART was not receiving any data. I thought this to be weird, as I had the exact same setting as the Arduino library and I followed the ESP-IDF UART guide to the bone. The mentioned Arduino library can be found here.
In examples/obd_uart_test/obd_uart_test.ino you see the following code:
Code: Select all
void setup()
{
mySerial.begin(115200);
while (!mySerial);
for (;;) {
delay(1000);
byte version = obd.begin();
mySerial.print("Freematics OBD-II Adapter ");
if (version > 0) {
mySerial.println("detected");
mySerial.print("OBD firmware version ");
mySerial.print(version / 10);
mySerial.print('.');
mySerial.println(version % 10);
break;
} else {
mySerial.println("not detected");
}
}
// some more setup code
}
Code: Select all
byte version = obd.begin();
Code: Select all
byte COBD::begin()
{
long baudrates[] = {115200, 38400};
byte version = 0;
for (byte n = 0; n < sizeof(baudrates) / sizeof(baudrates[0]); n++) {
#ifndef ESP32
OBDUART.begin(baudrates[n]);
#else
OBDUART.begin(baudrates[n], SERIAL_8N1, 16, 17);
#endif
version = getVersion();
if (version != 0) break;
OBDUART.end();
}
return version;
}
Code: Select all
OBDUART.begin(baudrates[n], SERIAL_8N1, 16, 17);
Code: Select all
version = getVersion();
Code: Select all
byte COBD::getVersion()
{
byte version = 0;
for (byte n = 0; n < 3; n++) {
char buffer[32];
if (sendCommand("ATI\r", buffer, sizeof(buffer), 200)) {
char *p = strchr(buffer, ' ');
if (p) {
p += 2;
version = (*p - '0') * 10 + (*(p + 2) - '0');
break;
}
}
}
return version;
}
Code: Select all
byte COBD::sendCommand(const char* cmd, char* buf, byte bufsize, int timeout)
{
write(cmd);
idleTasks();
return receive(buf, bufsize, timeout);
}
Code: Select all
int COBD::receive(char* buffer, int bufsize, unsigned int timeout)
{
unsigned char n = 0;
unsigned long startTime = millis();
char c = 0;
for (;;) {
if (OBDUART.available()) {
c = OBDUART.read();
if (!buffer) {
n++;
} else if (n < bufsize - 1) {
if (c == '.' && n > 2 && buffer[n - 1] == '.' && buffer[n - 2] == '.') {
// waiting siginal
n = 0;
timeout = OBD_TIMEOUT_LONG;
} else {
if (c == '\r' || c == '\n' || c == ' ') {
if (n == 0 || buffer[n - 1] == '\r' || buffer[n - 1] == '\n') continue;
}
buffer[n++] = c;
}
}
} else {
if (c == '>') {
// prompt char received
break;
}
if ((int)(millis() - startTime) > timeout) {
// timeout
break;
}
idleTasks();
}
}
if (buffer) {
buffer[n] = 0;
}
#ifdef DEBUG
DEBUG.print(">>>");
DEBUG.println(buffer);
#endif
return n;
}
Code: Select all
byte COBD::getVersion()
{
byte version = 0;
for (byte n = 0; n < 3; n++) {
char buffer[32];
if (sendCommand("ATI\r", buffer, sizeof(buffer), 200)) {
char *p = strchr(buffer, ' ');
if (p) {
p += 2;
version = (*p - '0') * 10 + (*(p + 2) - '0');
break;
}
}
}
return version;
}
Things to keep in mind
- I tried using the Arduino-esp32 codebase as a component to ESP-IDF, however, in my whole application I will also be using ESP-ADF together with ESP-IDF and the code from the ADF lacks compatibility with the Arduino library. I get errors because it refuses the C code ADF uses. Yes, I use extern c but it does not accept the code. One way I might have that fixed is to make everything extern to be used in cpp but I do not want to touch something I may broke so I will not do so.
- I will be setting pins with uart_set_pin, this is in another order than the Serial begin version, like rx first and tx after and the other way around.
- I will not be using a send buffer, I know this might block application but it is not neccesary for what I am trying to do, this may all be fixed later.
- I will not set the uart_mode assuming it will use the default, and if I do set it I will set if to UART_MODE. I tried with and without setting it with both the same result.
- Hardware control will be disabled, I do not need it.
- Serial configuration will be set according to SERIAL_8N1, which is 115200, 8 bit, no parity and 1 stop bit as the code in the Arduino library.
- I will be using UART port 2 as pin 16 and 17 seem to be used there according to HardwareSerial.cpp of arduino-esp32.
- I did not confuse the two pins I tried both ways of connecting.
I starting a new ESP-IDF project based on the "hello world" example because that is the most basic one. I emptied the app_main() and added one by one the following:
Code: Select all
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
Code: Select all
uart_param_config(UART_NUM_2, &uart_config);
uart_set_pin(UART_NUM_2, 17, 16, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
uart_driver_install(UART_NUM_2, 2048, 0, 0, NULL, 0);
Code: Select all
while (1)
{
uart_write_bytes(UART_NUM_2, "ATI\r", 32);
// Read data from the UART
int len = uart_read_bytes(UART_NUM_2, data, 1, 20 / portTICK_PERIOD_MS);
printf("Len is empty\n");
// Write data back to the UART
if(len > 0)
{
data[len] = "\0";
printf("There is data \n");
printf("%s \n", data);
fflush(stdout);
}
}
Do you by chance see any errors I made? I thank you in advance.