ESP32: UART2 incompatible with OneWire/DallasTemperature?

With an ESP32, I want to read sensor data from an ultrasonic distance sensor A02YYUW and a temperature sensor DS18B20.

The following code works fine for the A02YYUW sensor:

// UART2 interface pins of the ESP32
#define RXD2 16
#define TXD2 17

unsigned char data_buffer[4] = {0};  // array to store incoming serial data
int distance = 0;  // [mm]
unsigned char cs;  // checksum

void setup() {
  Serial.begin(115200);

  // Set up UART2 serial connection
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);

  Serial.println("Setup completed...");
}

void loop() {
  // Process serial data if available
  if (Serial2.available() > 0) {
    delay(4);

    // check for packet header character 0xff
    if (Serial2.read() == 0xff) {
      data_buffer[0] = 0xff;  // adding header
      
      // reading remaining 3 characters of serial data
      for (int i = 1; i < 4; i++) {
        data_buffer[i] = Serial2.read();
      }

      // calculate checksum
      cs = data_buffer[0] + data_buffer[1] + data_buffer[2];

      // compare checksums and compose distance from serial data
      if (data_buffer[3] == cs) {
        distance = (data_buffer[1] << 8) + data_buffer[2];
        Serial.println("distance: " + String(distance) + " mm");
      }
    }
  }
}

But when I add code for the temperature sensor, the measurements of the distance sensor become incorrect. It almost looks like the value is frozen, but sometimes a change is shown:

// for the DS18B20 sensor
#include <OneWire.h>
#include <DallasTemperature.h>

// UART2 interface pins of the ESP32
#define RXD2 16
#define TXD2 17

// GPIO where the DS18B20 is connected to
const int oneWireBus_temp = 4;  

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire_temp(oneWireBus_temp);

// Pass our oneWire reference to Dallas Temperature sensor 
DallasTemperature temp_sensor(&oneWire_temp);

unsigned char data_buffer[4] = {0};  // array to store incoming serial data
int distance = 0;  // [mm]
unsigned char cs;  // checksum

void setup() {
  // Start the Serial Monitor
  Serial.begin(115200);

  // Set up UART2 serial connection
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);

  // Start the DS18B20 sensor
  temp_sensor.begin();

  Serial.println("Setup completed...");
}

void loop() {
  // Process serial data if available
  if (Serial2.available() > 0) {
    delay(4);

    Serial2.flush();

    // check for packet header character 0xff
    if (Serial2.read() == 0xff) {
      data_buffer[0] = 0xff;  // adding header
      
      // reading remaining 3 characters of serial data
      for (int i = 1; i < 4; i++) {
        data_buffer[i] = Serial2.read();
      }

      // calculate checksum
      cs = data_buffer[0] + data_buffer[1] + data_buffer[2];

      // compare checksums and compose distance from serial data
      if (data_buffer[3] == cs) {
        distance = (data_buffer[1] << 8) + data_buffer[2];
        Serial.println("distance: " + String(distance) + " mm");
      } else {
        Serial.println("checksum compare failed...");
      }
    }
  } else {
    Serial.println("Serial2 not available...");
  }

  temp_sensor.requestTemperatures(); 
  float temperatureC = temp_sensor.getTempCByIndex(0);
  Serial.print(temperatureC);
  Serial.println("ÂșC");

  delay(100);

}

If I comment out the code block starting with temp_sensor.requestTemperatures() the distance measurements are shown correctly, so I wonder if there is an incompatibility between the used protocols/libraries: UART2 for the distance sensor and OneWire/DallasTemperature for the temperature sensor.

Is there in fact an incompatibility or am I doing something else wrong?

You are getting about what you should. The Uart is an asynchronous 2 wire protocol with start and stop bits. The DS18B20 is a 1 wire protocol that uses a different protocol. It is not compatible, you need to use another pin. I suggest you get a copy of the arduino cookbook and skim it, then read the sections on serial communications.

you need to use another pin.

I don't understand. @againausername has the oneWireBus on pin4 not on either of the serial2 pins.

const int oneWireBus_temp = 4;  

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire_temp(oneWireBus_temp);

// Pass our oneWire reference to Dallas Temperature sensor 
DallasTemperature temp_sensor(&oneWire_temp);

@gilshultz, @cattledog: I think something else is playing here: if I adapt the working code for the A02YYUW, by adding a delay(500) at the end of the loop (goal: to read the distance only 2 times per second),

/* ESP32 pin allocations
 *
 * A02YYUW ultrasonic distance sensor
 * - A02YYUW, white  [4-TX]  - green:   GPIO16 [UART2 RX on ESP32]
 * - A02YYUW, yellow [3-RX]  - blue:    GPIO17 [UART2 TX on ESP32]
 * - A02YYUW, black  [2-GND] - black:   GND on ESP32
 * - A02YYUW, red    [1-VCC] - red:     3.3Vdc on ESP32
 *
 */


// UART2 interface pins of the ESP32
#define RXD2 16
#define TXD2 17

unsigned char data_buffer[4] = {0};  // array to store incoming serial data
unsigned char cs;  // checksum
int distance = 0;  // [mm]

void setup() {
  Serial.begin(115200);

  // Set up UART2 serial connection
  Serial.print("Setting up UART2 serial communication (for Y02YYUW ultrasonic distance sensor)... ");
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
  Serial.println("done.");

  Serial.println("Setup completed...");
}

void loop() {
  // Process serial data if available
  if (Serial2.available() > 0) {
    delay(4);

    // check for packet header character 0xff
    if (Serial2.read() == 0xff) {
      data_buffer[0] = 0xff;  // adding header
      
      // reading remaining 3 characters of serial data
      for (int i = 1; i < 4; i++) {
        data_buffer[i] = Serial2.read();
      }

      // calculate checksum
      cs = data_buffer[0] + data_buffer[1] + data_buffer[2];

      // compare checksums and compose distance from serial data
      if (data_buffer[3] == cs) {
        distance = (data_buffer[1] << 8) + data_buffer[2];
        Serial.println("distance: " + String(distance) + " mm");
      }
    }
  }

  delay(500);

}

I see the changes in the measured distance are shown in the Serial Monitor, but lagging several seconds


I would say that the execution of the loop part is effectively pausing half a second, but the sensor does not pause measuring (and ‘memorizing’) distances, and thus the shown distances in de Serial monitor are lagging like 0.5 seconds for each time loop is executed
 (but if so, where are all those values buffered?)

I don’t have enough knowledge (nor ICT background) of the specific details of the UART protocol on one side, and the time execution of the main program run on the ESP32 to even know where to start reading which documentation


Any clues?

According to the product documentation for the A02YYUW, the sensor is putting out serial values every every 100 to 300ms. The ESP32 has a serial input buffer which holds this output until it is read. I think the default buffer is 256 bytes but can be set to larger values. e.g. Serial2.setRxBufferSize(1024);
When the input buffer is full, new input characters are dropped.

The oneWire readings of the DS18B20 are relatively slow. You are reading every pass of loop. What happens if you read and print the temperature at five second intervals using a millis() timer?

1 Like

@cattledog: The reason for the delay in de loop is that in the final program will be measuring the status of a rain water reservoir (10 mÂł), so a distance reading each 15 min should be enough, which leads to the need for a measurement (possibly some kind of averaging over multiple measurements) on demand.

So I changed my initial naive strategy (copied from the internet), to using a function for doing the measurements which:

  1. starts the UART2 buffer
  2. log some measurements to a globaly defined array
  3. then stop the UART2 serial buffer.

Doing so, measuring distances and temperatures works fine:

/* ESP32 pin allocations
 *
 * A02YYUW ultrasonic distance sensor
 * - A02YYUW, white  [4-TX]  - green (after connector):   GPIO16 [UART2 RX on ESP32]
 * - A02YYUW, yellow [3-RX]  - blue (after connector):    GPIO17 [UART2 TX on ESP32]
 * - A02YYUW, black  [2-GND] - black (after connector):   GND on ESP32
 * - A02YYUW, red    [1-VCC] - red (after connector):     3.3Vdc on ESP32
 *
 * DS18B20 waterproof temperature sensor
 * - DS18B20, yellow [data line]:   GPIO04
 * - DS18B20, black [GND]:          GND on ESP32
 * - DS18B20, red [VCC]:            3.3Vdc on ESP32
 *
 */

// for the DS18B20 temperature sensor
#include <OneWire.h>
#include <DallasTemperature.h>

// connected pins
#define RXD2 16  // UART2 interface pins of the ESP32 (A02YYUW)
#define TXD2 17  // UART2 interface pins of the ESP32 (A02YYUW)
#define ONEWIREBUS_TEMP 4  // GPIO where the DS18B20 data line is connected

// Number of consequtive distance measuements to be taken by the A02YYUW sensor
#define N_DIST_MEASUREMENTS 7

// Timeout in milliseconds to get a series of valid distance measurements
#define TIMEOUT_DIST_MEASUREMENTS 3000

// Value for invalid distance measurement
#define DIST_MEASUREMENT_INVALID -1

// Setting up communication with DS18B20 temperature sensor
OneWire oneWire_temp(ONEWIREBUS_TEMP);
DallasTemperature temp_sensor(&oneWire_temp);



int measurements[N_DIST_MEASUREMENTS];  // [mm]
unsigned char data_buffer[4] = {0};  // array to store incoming serial data
unsigned char cs;  // checksum


void get_distance_readings(int timeOutMilliSec) {
  int t0 = millis();

  // Starting UART2 serial buffer
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);

  int dt = 0;  // [ms]
  char data_packet_header;
  int data_buffer[4];
  int distance = 0;  // [mm]

  for( int i = 1; i <= N_DIST_MEASUREMENTS; i++ ) {
    dt = millis()-t0;
    while( (Serial2.available() < 3) && (dt < timeOutMilliSec) ) { delay(100); dt = millis()-t0; }  // the A02YYUW produces 4 bytes of data every 100 to 300 ms
    // Serial.println(" ok.");
    if( dt >= timeOutMilliSec ) {
      for(int i = 0; i < N_DIST_MEASUREMENTS; i++) { measurements[i] = DIST_MEASUREMENT_INVALID; }
    }
    else {
      data_packet_header = Serial2.read(); // serialx.read() reads the first character (byte) and removes it from the serial buffer
      // check for packet header character 0xff
      dt = millis()-t0;
      while( (data_packet_header != 0xff) && (dt < timeOutMilliSec) ) { 
        Serial.println("/!\\ [get_distance_readings(int)] UART2: data packet header differs from '0xff': reading next byte...");
        data_packet_header = Serial2.read();
        dt = millis()-t0;
        }
      if( dt >= timeOutMilliSec ) {
        for(int i = 0; i < N_DIST_MEASUREMENTS; i++) { measurements[i] = DIST_MEASUREMENT_INVALID; }
      }
      else {
        data_buffer[0] = 0xff;  // (re)storing header (which was read and removed by Serial2.read())
        
        // reading remaining 3 characters of serial data
        dt = millis()-t0;
        while( (Serial2.available() < 2) && (dt < timeOutMilliSec) ) { delay(10); dt = millis()-t0; }  // the A02YYUW produces 4 bytes of data every 100 to 300 ms
        if( dt >= timeOutMilliSec ) {
          for(int i = 0; i < N_DIST_MEASUREMENTS; i++) { measurements[i] = DIST_MEASUREMENT_INVALID; }
        }
        else {
          for( int j = 1; j < 4; j++ ) {
            data_buffer[j] = Serial2.read();
          }
          // calculate checksum
          cs = data_buffer[0] + data_buffer[1] + data_buffer[2];
          // compare checksums and compose distance from serial data
          if (data_buffer[3] == cs) {
            distance = (data_buffer[1] << 8) + data_buffer[2];
            measurements[i-1] = distance;
          } else {
            Serial2.println("/!\\ [get_distance_readings(int)] A02YYUW: checksum compare failed.");
            measurements[i-1] = DIST_MEASUREMENT_INVALID;
          }
        }  // no timeout while waiting for min 3 bytes (remaining bytes in data message of A02YYUW) in serial buffer
      }  // no timeout while searching for header byte '0xff'
    }  // no timeout while waiting for min 4 bytes in serial buffer
  }  // for( int i = 1; i <= N_DIST_MEASUREMENTS; i++ )

  // Stopping UART2
  Serial2.end();
}

void printDistMeasurements() {
  Serial.print("[ ");
  for(int i = 0; i < N_DIST_MEASUREMENTS-1; i++) { Serial.print(String(measurements[i]) + ", "); }
  Serial.print(String(measurements[N_DIST_MEASUREMENTS-1]));
  Serial.println(" ]");
}

void setup() {
  Serial.begin(115200);

  // Start the DS18B20 sensor
  temp_sensor.begin();

  Serial.println("Setup completed...");
}

void loop() {
  get_distance_readings(TIMEOUT_DIST_MEASUREMENTS);
  Serial.print("* distances measured [mm]: ");
  printDistMeasurements();

  temp_sensor.requestTemperatures(); 
  float temperatureC = temp_sensor.getTempCByIndex(0);
  Serial.println("* temperature measured: " + String(temperatureC) + " ÂșC");

  Serial.println("| Waiting 2000 ms");
  delay(2000);
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.