UART based ultrasonic sensor

I had some issues with my sensor and these pins worked for me but my sensor is really bad.
What pins are you using?

So you changed line 0x55 to 0xFF, right?

You can try adding a few lines to see what’s going on.
Add this line:

        frame[pos] = read();
        ESP_LOGD("Hex", "Hex: %X", frame[pos]);  // <<<<<<<<<< THIS LINE

What board are you using? I’m using a node mcu.

It’s transmitting and receiving but the receiving is slower.

ESP32

How long does it take until the next reading?

Did you change the PollingComponent?

PollingComponent(1000),

Feels right, must return 1.638 / 10 = 163 cm

Post the yaml and .h file codes so we can see what you changed

#include "esphome.h"

class AJ_SR04M_Sensor : public PollingComponent, public UARTDevice, public Sensor {
  public:

    AJ_SR04M_Sensor(UARTComponent *parent) : PollingComponent(1000), UARTDevice(parent) {}

    void update() override {

      byte frame[5];
      int pos = 0;
      float value = 0.0;

      write(0xff); // Try this
     //delay(1);
     // write(0x00);
    // delay(1);
     //write(0xFF);
     delay(86);
     
      while (available()) {

        frame[pos] = read();
        ESP_LOGD("Hex", "Hex: %X", frame[pos]); 

        pos++;

        if (pos == 4) {

	  if ((frame[0] == 0xFF) && (((frame[0] + frame[1] + frame[2]) & 0x00FF) == frame[3])){
            value = ((frame[1] << 8) + frame[2])/10.0;
            publish_state(value);
          }
          break;


        }
      }
    }
};

esphome:

name: esphome-web-37188b

includes:

- AJ_SR04M_Sensor.h

esp8266:

board: esp01_1m

Enable logging

logger:

Enable Home Assistant API

api:

ota:

wifi:

ssid: !secret wifi_ssid

password: !secret wifi_password

Enable fallback hotspot (captive portal) in case wifi connection fails

ap:

ssid: "Esphome-Web-37188B"

password: "T2MCrnwGgqO2"

captive_portal:

uart:

id: uart_bus

tx_pin: GPIO2

rx_pin: GPIO0

baud_rate: 9600

stop_bits: 1

rx_buffer_size: 4

debug:

dummy_receiver: True

sensor:

  • platform: custom

    lambda: |-

    auto my_sensor = new AJ_SR04M_Sensor(id(uart_bus));

    App.register_component(my_sensor);

    return {my_sensor};

    sensors:

    unit_of_measurement: cm

    accuracy_decimals: 2

    name: “Distance”

    force_update: true

“Distance is still unknown”

Remove:

If you start getting constant readings play around with the PollingComponent, always greater than 85

I’m getting continuous output now but distance is still unknown

Remove this to

Still nothing. It takes a lot of output (0xFF) to get a single input.

I don’t know why yours is sending so many outputs.

The only thing I can think of is that it’s not passing validation on the line:

	  if ((frame[0] == 0xFF) && (frame[4] == 0x00) && (((frame[0] + frame[1] + frame[2]) & 0x00ff) == frame[3])){


but it should pass

Add these two lines to confirm:

        if (pos == 4) {
        ESP_LOGD("Validation", "pos == 4"); // ADD THIS LINE

	  if ((frame[0] == 0xFF) && (frame[4] == 0x00) && (((frame[0] + frame[1] + frame[2]) & 0x00ff) == frame[3])){
         ESP_LOGD("Validation", "SUM"); // ADD THIS LINE
           value = ((frame[1] << 8) + frame[2]) / 10.0;

I couldn’t make it work on the esphome but now I made it work on a rpi using python script, in thonny. I made some changes to the code for A02 ultrasonic sensor from dfrobot
Library file


'''!
  @file DFRobot_RaspberryPi_A02YYUW.py
  @brief Ranging distance sensor。
  @copyright   Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
  @license     The MIT License (MIT)
  @author      Arya([email protected])
  @version     V1.0
  @date        2021-08-30
  @url https://github.com/DFRobot/DFRobot_RaspberryPi_A02YYUW
'''

import serial

import time

class DFRobot_A02_Distance:

  ## Board status 
  STA_OK = 0x00
  STA_ERR_CHECKSUM = 0x01
  STA_ERR_SERIAL = 0x02
  STA_ERR_CHECK_OUT_LIMIT = 0x03
  STA_ERR_CHECK_LOW_LIMIT = 0x04
  STA_ERR_DATA = 0x05

  ## last operate status, users can use this variable to determine the result of a function call. 
  last_operate_status = STA_OK

  ## variable 
  distance = 0

  ## Maximum range
  distance_max = 4500
  distance_min = 0
  range_max = 4500

  def __init__(self):
    '''
      @brief    Sensor initialization.
    '''
    self._ser = serial.Serial("/dev/ttyUSB0", 9600)
    if self._ser.isOpen() != True:
      self.last_operate_status = self.STA_ERR_SERIAL

  def set_dis_range(self, min, max):
    self.distance_max = max
    self.distance_min = min

  def getDistance(self):
    '''
      @brief    Get measured distance
      @return    measured distance
    '''
    self._ser.write(b'\xFF') 
    self._measure()
    return self.distance
  
  def _check_sum(self, l):
    return (l[0] + l[1] + l[2])&0x00ff

  def _measure(self):
    data = [0]*4
    i = 0
    timenow = time.time()

    while (self._ser.inWaiting() < 4):
      time.sleep(0.01)
      if ((time.time() - timenow) > 1):
        break
    
    rlt = self._ser.read(self._ser.inWaiting())
    #print(rlt)
    
    index = len(rlt)
    if(len(rlt) >= 4):
       index = len(rlt) - 4
       while True:
         try:
            
           data[0] = ord(rlt[index])
         except:
           data[0] = rlt[index]
         if(data[0] == 0xFF):
           break
         elif (index > 0):
           index = index - 1
         else:
           break
       #print(data)
       if (data[0] == 0xFF):
         try:
           data[1] = ord(rlt[index + 1])
           data[2] = ord(rlt[index + 2])
           data[3] = ord(rlt[index + 3])
         except:
           data[1] = rlt[index + 1]
           data[2] = rlt[index + 2]
           data[3] = rlt[index + 3]
         i = 4
    #print(data)
    if i == 4:
      sum = self._check_sum(data)
      if sum != data[3]:
        self.last_operate_status = self.STA_ERR_CHECKSUM
      else:
        self.distance = data[1]*256 + data[2]
        self.last_operate_status = self.STA_OK
      if self.distance > self.distance_max:
        self.last_operate_status = self.STA_ERR_CHECK_OUT_LIMIT
        self.distance = self.distance_max
      elif self.distance < self.distance_min:
        self.last_operate_status = self.STA_ERR_CHECK_LOW_LIMIT
        self.distance = self.distance_min
    else:
      self.last_operate_status = self.STA_ERR_DATA
    return self.distance

Ultrasonic loop

# -*- coding:utf-8 -*-


'''!
  @file demo_get_distance.py
  @brief Get ranging data.
  @n Connect board with raspberryPi with TTL to USB adapter
  @n 
  @n
  @Copyright   Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
  @license     The MIT License (MIT)
  @author [Arya]([email protected])
  @version  V1.0
  @date  2019-8-31
  @url https://github.com/DFRobot/DFRobot_RaspberryPi_A02YYUW
'''

import sys
import os
import time



sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))

from DFRobot_RaspberryPi_A02YYUW import DFRobot_A02_Distance as Board

board = Board()

file='/home/rpi/q.wav'
file_wav=wave.open(file)
frequency=file_wav.getframerate()
mixer.init(frequency=frequency)
mixer.music.load("q.wav")
mixer.music.set_volume(1)



def print_distance(dis):
  
  if board.last_operate_status == board.STA_OK:
    print("Distance %d cm" %dis)
  elif board.last_operate_status == board.STA_ERR_CHECKSUM:
    print("ERROR")
  elif board.last_operate_status == board.STA_ERR_SERIAL:
    print("Serial open failed!")
  elif board.last_operate_status == board.STA_ERR_CHECK_OUT_LIMIT:
    print("Above the upper limit: %d" %dis)
  elif board.last_operate_status == board.STA_ERR_CHECK_LOW_LIMIT:
    print("Below the lower limit: %d" %dis)
  elif board.last_operate_status == board.STA_ERR_DATA:
    print("No data!")

if __name__ == "__main__":
  #Minimum ranging threshold: 0mm
  dis_min = 260 
  #Highest ranging threshold: 4500mm  
  dis_max = 4500 
  board.set_dis_range(dis_min, dis_max)
  while True:
    
    distance = board.getDistance()
    print_distance(distance/10)
    
    
    
  
    
    #Delay time < 0.6s
    time.sleep(0.3) 

#include “esphome.h”

class AJ_SR04M_Sensor : public PollingComponent, public UARTDevice, public Sensor {

public:

AJ_SR04M_Sensor(UARTComponent *parent) : PollingComponent(1000), UARTDevice(parent) {}

void update() override {

  byte frame[5];

  int pos = 0;

  float value = 0.0;

 

  while (available()) {

    frame[pos] = read();

    pos++;

    if (pos == 4) {

  if ((frame[0] == 0xFF) && (((frame[0] + frame[1] + frame[2]) &0x00FF) == frame[3])){

        value = ((frame[1] * 256) + frame[2]);

       

        publish_state(value);

      }

      break;

    }

  }

}

};

Works for me.!

uart:

id: uart_bus

tx_pin: GPIO3

rx_pin: GPIO1

baud_rate: 9600

stop_bits: 1

sensor:

  • platform: custom

    lambda: |-

    auto my_sensor = new AJ_SR04M_Sensor(id(uart_bus));

    App.register_component(my_sensor);

    return {my_sensor};

    sensors:

    unit_of_measurement: inch

    accuracy_decimals: 2

    name: “Distance”

    filters:

    • lambda: return x / 25.4;

Hi,
i´m a beginner in Home assistent.
I´ve tried to conifgure my AJ-SR04M with a esp8266 like you explained in this post.
Do not work. Can you explain for “dummies” =

Can’t get this to work:
sensor yaml

esphome:

name: upper-water-tank-sensor

friendly_name: Upper Water Tank Sensor

includes:

- AJ_SR04M_Sensor.h

esp32:

board: esp32-s3-devkitc-1

framework:

type: arduino

# Enable logging

logger:

# Enable Home Assistant API

api:

encryption:

key: "PMZ7+0x6HDDjbMO5XVZdutk6YIndcF8DIqN4y0E8xdc="

ota:

password: "7a72e32750314e115482f2dfb1b6d6c0"

wifi:

ssid: !secret wifi_ssid

password: !secret wifi_password

manual_ip:

static_ip: 192.168.15.29

gateway: 192.168.15.1

subnet: 255.255.255.0

dns1: 192.168.1.2

# Enable fallback hotspot (captive portal) in case wifi connection fails

ap:

ssid: "Upper-Water-Tank-Sensor"

password: "7rl3vO1CcCfS"

captive_portal:

uart:

id: uart_bus

tx_pin: GPIO17 # Connect to DYP RX

rx_pin: GPIO16 # Connect to DYP TX

baud_rate: 9600

stop_bits: 1

rx_buffer_size: 4

debug:

sensor:

- platform: wifi_signal

name: upper_water_tank_wifi

update_interval: 600s

unit_of_measurement: '%'

filters:

- lambda: |-

if (x <= -100) {

return 0;

} else {

if (x >= -50) {

return 100;

} else {

return 2 * (x + 100);

}

}

- platform: template

name: upper_water_tank_volume

id: upper_water_tank_volume

icon: 'mdi:water'

unit_of_measurement: 'l'

accuracy_decimals: 0

- platform: template

name: upper_water_tank_percent

id: upper_water_tank_percent

icon: 'mdi:water-percent'

unit_of_measurement: '%'

- platform: custom

lambda: |-

auto my_sensor = new AJ_SR04M_Sensor(id(uart_bus));

App.register_component(my_sensor);

return {my_sensor};

sensors:

unit_of_measurement: cm

accuracy_decimals: 2

name: "Distance"

sensor file

#include "esphome.h"

class AJ_SR04M_Sensor : public PollingComponent, public UARTDevice, public Sensor {

public:

AJ_SR04M_Sensor(UARTComponent *parent) : PollingComponent(1000), UARTDevice(parent) {}

void update() override {

byte frame[5];

int pos = 0;

float value = 0.0;

write(0x55);

while (available()) {

frame[pos] = read();

pos++;

if (pos == 4) {

if ((frame[0] == 0xFF) && (frame[4] ==0x00) && (((frame[0] + frame[1] + frame[2]) & 0x00FF) == frame[3])){

value =(frame[1] + frame[2]) / 10.0;

publish_state(value);

}

break;

}

}

}

};

The helper board has a blue flashing light and the output logs show

any ideas?

Hi, we’re you ever able to fix this?

No, given up. About to try a pressure sensor attached to a FIbaro Universal sensor