ES32, ESP-IDF and UART
Posted: Tue Oct 08, 2019 12:22 pm
Hello there,
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:
This code tries to retrieve the version of the OBD-ii adapter and when it does it says the adapter has been detected and prints out the version. But the method that actually gets the version (through UART) is this:
In OBD2UART.cpp the begin method has the following body:
OBDUART is a macro referring to Serial. So it says Serial.begin() there.
For the ESP32 it starts the serial like this. This pins are correct and corresponding to my rx (17) and tx (16) pins.
Then it tries to get the version by running the getVersion method, the method is as follows:
Here we see it uses the sendCommand function and that it sends "ATI\r" to the adapter. We go to the sendCommand function:
The write function just encapsulates Serial.write(); I do not know what idleTasks() is per say and I could not easily find it one the internet. After it writes the data using Serial.write(); it goes into the receive function which reads incoming data which the adapter should give now.
The data is put into the buffer that is supplied by the sendCommand method to the receive method. It then returns a value toe the sendCommand which does not represent the data per say. So, now we go back to the getVersion method:
In char buffer[32] now should be the retrieved data after sending the ATI command to the adapter and it processes this information and outputs it into the console. Now I explained this sequence and I want to implement the same thing in ESP-IDF but I could not get it to work.
Things to keep in mind
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:
Here I specify the configuration for UART as described.
Here I set the specified UART configuration to uart port 2, and set the corresponding pins. See uart_set_pin() here. As the default pins are 17 and 16 I could have just used UART_PIN_NO_CHANGE and I did with the same result. I then install the UART driver. I do not use a Queue, which is used for events and do not set any flags. That was the UART configuration part. Now for the communication.
Here it writes the ATI command to the adapter and then reads data given by the adapter continously. However, it never gets in the if (len > 0) condition.
Do you by chance see any errors I made? I thank you in advance.
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.