ESPHome Ultrasonic sensor SR04T via UART

Hi HA users!

I’m trying to get my ultrasonic sensor to work with ESPHome. The sensor is a JSN-SR04T-AJ-SR04M which is similar to the HC-SR04 but not exactly the same. So using the default ESPHome template ‘ultrasonic’ doesn’t work (wrong distance values are show, only 2 out of 10measurements are somewhat correct).

The SR04 has different modes, I soldered a 47kΩ resistor into the R27 Pin to enable the “contious mode”. In this mode the distance calculation is done on device all the time. To get the distance on can use the UART to retrieve the data.

Now I’m struggling with implementing this in ESPHome. I read that it is possible to add custom UART components, but I’m not sure if this is necessary in my case, or if I’m able to just receive the data using the regular uart integration.
The code to read the data from the sensor every two seconds looks like this in Python:

import serial
import time
import sys
ser = serial.Serial('/dev/ttyUSB0')  # open serial port
byte = b'\xff' # xff is the start byte
while True:
    x = ser.read()
    if x == byte: #starbyte found
        b1 = ser.read(1)
        b2 = ser.read(1)
        x1 = int.from_bytes(b1, sys.byteorder)
        dist = (((x1 << 8) + int.from_bytes(b2, sys.byteorder)) / 10) # some bitshift magic, no idea what's happening here
        print(dist - 5) # for some reason the distance value is 5cm off, remove them before printing the value
        clear = ser.read_all()
        ser.flushOutput()
    time.sleep(2)

There is also a german blogpost with some arduino examples for the different modes of the sensor, which I used to implement my python example above:

I hope that someone could give me a hint in the right direction since I’m not an arduino pro as you can probably see, thanks in advance for helping me out :slight_smile:

I faced the same challenge. Here my solution based on a custom sensor. Hope it will help you out…

esphome:
  name: test-level
  includes:
    - AJ_SR04M_Sensor.h

uart:
  id: uart_bus
  tx_pin: GPIO14
  rx_pin: GPIO12
  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: cm
    accuracy_decimals: 1
    name: "Distance"

AJ_SR04M_Sensor.h:

#include "esphome.h"
 
class AJ_SR04M_Sensor : public PollingComponent, public UARTDevice, public Sensor {
 public:

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

// AJ_SR04M_Sensor format:
// Trigger: 0x00
// Response: Byte1          Byte2  Byte3  Byte4               Byte5
//           Start Byte=FF  MSB    LSB    Checksum (LSB+MSB)  00

  void update() override {
  
    char frame[5];
    int pos = 0;
    float value = 0.0;

    write(0x00);
    while (available()) {
      frame[pos] = read();
      pos++;
      if(pos==5) {
        if ((frame[0] == 0xFF) && (frame[4] == 0x00) && ((frame[1]+frame[2])==frame[3])) {
          value = ((frame[1]<<8) + frame[2]) / 10.0;
          publish_state(value);
        }
        break;
      }
    }
  }
};

The update interval is fixed to 5 seconds but you can change it accordingly.

3 Likes

Hi,
I am trying to use your code but the sensor is not updated.

Distance
Unknown

Any tips?

I managed to make it work:


#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(0x00); // Try this
      write(0x55); // Try this
      //write(0x01); // Try this
      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] << 8) + frame[2]) / 10.0;
            publish_state(value);
          }
          break;


        }
      }
    }
};

1 Like

@bullar @walberjunior
I’m in same hell as you were earlier.
I had ordered JSN-SR04T but received AJ-SR04M with limited resources of information for setting it up.
My problem is explained in other query. Then found this page and your messages.
First realization was the type of the sensor, then the operational mode (soldered 120kΩ resistor for enabling the “Automatic Serial Mode” as per the link).
Following your instructions, I have succeeded to have some activity on the board of the sensor (blue blinking led) but the “Distance” is returning error (unknown).
Im using a ESP32 Development Board ESP32-WROOM-32U and GPIO12 and GPIO14 are in use.
If observing the log in real time, is not returning any error message as other trials (before soldering resistore, etc)
Any clue?

Did you try change the write byte?

      //write(0x00); // Try this
      write(0x55); // Try this
      //write(0x01); // Try this

Did you try change rx with tx?

Post your codes and photo of the wire connection

Thanks for your response,
I did the reverse of Tx and Rx and immediately I got a quick flashing of the blue led. So It was a good start.

I have tried both 0x00 and 0x55, copied exactly as it is on the above posts. The funny thing was that I had a measurement in both cases although with unexplained lag and freezes. After last test with option 0x01, in your posted example, I cant see anything in the debug log or in ESPHome integration. The blue led still is flashing. In every change I did a complete reboot of the Home aasistant pc and the ESP32 board. Other sensors on the same ESP board, 3x dallas for temperature, are working flawlessly.

Here my config files:
/config/esphome/AJ_SR04M_Sensor.h

#include "esphome.h"
 
class AJ_SR04M_Sensor : public PollingComponent, public UARTDevice, public Sensor {
 public:

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

// AJ_SR04M_Sensor format:
// Trigger: 0x00
// Response: Byte1          Byte2  Byte3  Byte4               Byte5
//           Start Byte=FF  MSB    LSB    Checksum (LSB+MSB)  00

  void update() override {
  
    char frame[5];
    int pos = 0;


    float value = 0.0;

    write(0x00);
    while (available()) {


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

and /config/esphome/esp32-sensors.yaml

esphome:
  name: esp32-sensors
  friendly_name: ESP32-sensors
  includes:
    - AJ_SR04M_Sensor.h

uart:
  id: uart_bus
  tx_pin: GPIO14
  rx_pin: GPIO12
  baud_rate: 9600
  stop_bits: 1
  
esp32:
  board: esp32dev
  framework:
    type: arduino
dallas:
  - pin: GPIO22
sensor:
  - platform: dallas
    address: 0xea0722b0dcbabc28
    name: "Boiler 1" 
  - platform: dallas
    address: 0xd30822b0f48ffd28
    name: "Boiler 2"
  - platform: dallas
    address: 0xa90722b0e5ac1328
    name: "Boiler 3"
  
  - 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: 1
     name: "Ullages"
    
# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

ota:
  password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "xxxxxxxxxxxxxxxxxxxxxxx"
    password: "xxxxxxxxxxxxxxxxxxxxxxxxxx"

captive_portal:
    

Please note that I cant reinstate the readings, even those with lag.

From the link you sent, using the 120k resistor activates the automatic mode. You would need to change the code.

If you want to use the above code, you would need to change the resistor to 47k.

Ok,
I’m going to solder a 47KΩ at R19 for activating the “Low Power Serial Mode” and reverting with results.

What code should be changed if remaining into “Automatic Serial Mode”?

Edit1.This link is for the specific model that I have in my hands, labeled AJ-SR04M and resistors value is different than JSN-SR04T. I’m highlighting this for the sake of clarity.

Edit2. After the soldering of the 47KΩ at R19 the blue led is flashing every 5-6 seconds
I have try

      write(0x00); not any indication in logs or in esphome
      write(0x55); not any indication in logs or in esphome
      write(0x01); not any indication in logs or in esphome


History2– Home Assistant

The two screen shots are the debug logs and the integration. the theird one is the history of the sensor. As you can see yesterday after soldering the resistor and trying x55 amd x00 was visible some measurements althouth with lag which situation I can reproduce anymore.

If is the same module from the link you should send 0x01

void loop() {
    jsnSerial.write(0x01);
    delay(50);
  if(jsnSerial.available()){
    getDistance();
  }
}

Try write(0x01)

I’m trying various things,
first finding is that your config is different than tuxflo’s,
with yours I’m receiving distance athought with lag (does not matter if it is 47kΩ or 120)

Now recompilling with 01, reverting

Edit1. with 47kΩ, your posted config, with 0x01 is working, updating fast. Accuracy is a question, sometimes 10cm plus , other time ok.
** Edit2. If I’ll unplug the esp32 board from power supply then I have to reinstall/recompile the settings before having readings!!! Alternatively unlpug the Tx or Rx pin (very weird) **

See screenshot, 1)anything to improve it? 2) how to add a sensor with the quantity and %

Any help on the reset issue?

  tx_pin: GPIO14
  rx_pin: GPIO12

Change the pins, the pins you chose interfere with the boot.

I have changed to

uart:
  id: uart_bus
  tx_pin: GPIO19
  rx_pin: GPIO18
  baud_rate: 9600
  stop_bits: 1

but still having same behavior. I’m going to try more couples of pins from the recommended list.

Other thing tried was separate power supply for sensor, it didn’t work.

Edit2. Same with 16,17!!!

Edit3. As I have a second identical sensor but only a 8266 spare board, Could anybody advise me how to setup that board for further testing?

framework:
    type: arduino

is not working with that board, so I cant perform a comparison test

I use this pins:

uart:
  id: uart_bus
  tx_pin: GPIO17 # echo/RX do SR04M 
  rx_pin: GPIO18 #trigger/TX do SR04M 
  baud_rate: 9600
  stop_bits: 1
  rx_buffer_size: 4

Change the board:

esp8266:
  board: d1_mini

I have spent this afternoon doing trials:

  1. no any chance to test the esp8266 board, the board type or the framework is not passing the validation, therefore no time or knowledge from my side for this anymore.

  2. I went to the basement and removed one ESP32-cam for testing . I used the second sensor with this board and it was working flawlessly ! responding immediately after reboot, used pins

  tx_pin: GPIO3
  rx_pin: GPIO1

This made me suspicious for the pins of the initial board.

  1. I have tried any combination of pins ESP32-WROOM-32U but the results were the same. No responding after a reboot unless remove the pin tx or rx of the sensor.

  2. I have removed the dallas sensors from the ESP32-WROOM-32U but no change, issues after reboot is there.

  3. I have removed everything related to dallas sensors from config. I have recompiled it, its working immediately after it (as usual) but not after reboot. WTF is the matter with this board?

No idea what else to try!

You need to change pins to 8266.

Maybe your esp32 is faulty or the first sensor.
To be sure you would have to use the first sensor on the ESP32-cam and vice versa.

Thanks for your patience!
I have exchange the sensors to the boards. Reboot issue only with ESP32-WROOM-32U.
The issue occuring with the particular board and I have no idea what could be!

The only reason that Im insisting with that board is the external antena that could be used with it due to the poor wifi coverage at the place of installation

Edit1. I got by express courier and on high price a D1 mini, delivered while ago, I did the same setup as ESP32-cam, in selected GPIO, in connections, in config file etc still have same results.

  • D1 min (as ESP32-WROOM-32U) when rebooting is not communicating with sensor unless remove the tx or Rx pin as kick start.

  • ESP32-cam is NOT working fine (anymore) after reboot!!! Not any more!

I’m giving up on this project!

Which pins did you use?
Look at the link below, there is a table with the pins and a note with possible problems.

If you tested with pins 1 and 3, change to whatever has OK on the input and output and nothing on the note.

I did it again, no difference. It should be disconnect/reconect the tx or rx pin before resuming measurements. Its a mystery for me!
Other observation is the need for common source of power/grounding for a responsive sensor (before reboot).

Edit: finally I did it with UART method, see here for my case