Custom component (A02YYUW) ESP Home

I have a water proof ultrasonic sensor (A02YYUW) which i would like to add to a project built in esphome.

It outputs via UART and there is a sample code in the data sheet.

is it possible to integrate this code directly as a custom component in esp home or am I barking up the wrong tree :roll_eyes:

Any help is appreciated.

/*
  *@File  : DFRobot_Distance_A02.ino 
  *@Brief : This example use A02YYUW ultrasonic sensor to measure distance
  *         With initialization completed, We can get distance value 
  *@Copyright [DFRobot](http://www.dfrobot.com),2016         
  *           GUN Lesser General Pulic License
  *@version V1.0           
  *@data  2019-8-28
*/

#include <SoftwareSerial.h>

SoftwareSerial mySerial(11,10); // RX, TX
unsigned char data[4]={};
float distance;

void setup()
{
 Serial.begin(57600);
 mySerial.begin(9600); 
}

void loop()
{
    do{
     for(int i=0;i<4;i++)
     {
       data[i]=mySerial.read();
     }
  }while(mySerial.read()==0xff);

  mySerial.flush();

  if(data[0]==0xff)
    {
      int sum;
      sum=(data[0]+data[1]+data[2])&0x00FF;
      if(sum==data[3])
      {
        distance=(data[1]<<8)+data[2];
        if(distance>30)
          {
           Serial.print("distance=");
           Serial.print(distance/10);
           Serial.println("cm");
          }else 
             {
               Serial.println("Below the lower limit");
             }
      }else Serial.println("ERROR");
     }
     delay(100);
}
1 Like

Have you read this:

Thanks Tom,

I have had a read through the docs and some examples on esphome.io

the below is what i have come up with so far. I have left the names as is from the exaamples to try and eliminate any naming errors on my behalf.

So far this is giving me an output on the esphome logger of:

nothing is attached to the tx and rx

  • [10:01:59][E][uart:229]: Reading from UART timed out at byte 0!

This is as I would expect with nothing connected.

When the Tx of the distance sensor is connected to the Rx pin (D2 in this case) on the 8266 the logger ceases to function immediately after initiating. If i disconnect the pin the logger immediately resumes.

Is this a problem with the logger not being able to function with an external UART connected or is my code off track?

This is the yaml in esphome.

Copy to clipboard

esphome:
  name: a_test_board
  platform: ESP8266
  board: d1_mini
  includes:
    - my_custom_component.h

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

captive_portal:

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:


uart:
  id: uart_bus
  tx_pin: D1
  rx_pin: D2
  stop_bits: 1
  baud_rate: 9600
  
custom_component:
- lambda: |-
    auto my_custom = new MyCustomComponent(id(uart_bus));
    return {my_custom};

This is my_custom_component.h

Copy to clipboard

#include "esphome.h"

class MyCustomComponent : public Component, public UARTDevice {
public:
	MyCustomComponent(UARTComponent *parent) : UARTDevice(parent) {}

	unsigned char data[4] = {};
	float distance;

	void setup() override {
		
	}
	void loop() override {
	
		String line = readString();
		

		if (data[0] == 0xff)
		{
			int sum;
			sum = (data[0] + data[1] + data[2]) & 0x00FF;
			if (sum == data[3])
			{
				distance = (data[1] << 8) + data[2];
				if (distance > 30)
				{
					Serial.print("distance=");
					Serial.print(distance / 10);
					Serial.println("cm");
				}
				else
				{
					Serial.println("Below the lower limit");
				}
			}
			else Serial.println("ERROR");
		}
		delay(100);

		
	}
};

You’re calling readString() but never using the result. It’s probably blocking waiting for an end of line, which doesn’t occur in your protocol, and that’s why it seems like nothing happens. You need to copy the first part of the original code too I think, the part that populates the data array.

2 Likes

Hi @chocolatejoe , i am also facing identical trouble here. What device do you use for ESPHome?
I use wemos D1 mini with only 1 UART available and having same problem when nothing is attached to UART.

  • Reading from UART timed out at byte 0!

But when attach my sensor, ESPHome device will be disconnected. Do you figure something out? Thanks

Hi @hmoffatt, I am facing identical problem like @chocolatejoe. Can i ask your help.

I used Wemos D1 Mini with UART on GPIO1 and 3. I also read the documentation here and made my_custom_component.h same with the documentation as follow

#include "esphome.h"

class MyCustomComponent : public Component, public UARTDevice {
 public:
  MyCustomComponent(UARTComponent *parent) : UARTDevice(parent) {}

  void setup() override {
    // nothing to do here
  }
  void loop() override {
    // Use Arduino API to read data, for example
    String line = readString();
    int i = parseInt();
    while (available()) {
      char c = read();
    }
    // etc
  }
};

Here is the configuration yaml file for my ESPHome device

esphome:
  name: test_serial
  platform: ESP8266
  board: d1_mini
  includes:
    - my_custom_component.h

uart:
  id: uart_bus
  tx_pin: GPIO1
  rx_pin: GPIO3
  baud_rate: 9600

custom_component:
- lambda: |-
    auto my_custom = new MyCustomComponent(id(uart_bus));
    return {my_custom};

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Test Serial Fallback Hotspot"
    password: ..

captive_portal:

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:

When nothing is attached to GPIO1 and 3, it will show error like this

I used Arduino with serial logic level of 3.3V, the same voltage of Wemos D1 logic level, to simply send string from this arduino to Wemos D1 mini. Here is my arduino code. When i attach arduino serial to Wemos D1 mini serial, the connection is cut off. My Wemos D1 mini is not connected to home assistant server anymore. The status become offline.

void setup() {
  Serial.begin(9600);
}

void loop() {
//  Serial.write(45); // send a byte with the value 45

  Serial.write("hello\r\n");  //send the string "hello" and return the length of the string.
}

Can you help me. what is happening here? Thanks.

You need to customise the loop() code. I would start by just keeping the loop that reads characters and printing each one out as it’s received. Then you will know is it timing out on the readString(), or the parseInt() or what.

You definitely don’t want the parseInt, because you’re not sending any ints…

Sorry for the delay in reply :confounded: :confounded:

I ended up abandoning the ultrasonic sensor for liquids due to error from condensation and went with one of these instead.

https://www.dfrobot.com/product-1863.html

this gives me readings with repeatable accuracy to about 40mm in the tank measured using the A01 on esp8266. (a 12bit ADC would probably increase this)

This is the setup that finally worked for me with the A02YYUW sensor for the record.
note i also have a temperature sensor in my tank.

my_custom_component.h

#include "esphome.h"
#include "SoftwareSerial.h"
using namespace esphome;
 
// Receive Pin: 14 (D5)
// Transmit Pin: 4 (D2)
SoftwareSerial mySerial(RX, TX);

 // byte last[4];
 // unsigned char last[4]={};
unsigned char data[4]={};

float value = 0;
float lastPublished = -1;
float distance;
int u = 0;
  
class TableHeightSensor : public PollingComponent, public sensor::Sensor{
 public:

 void setup() override{
    mySerial.begin(9600);
  }
 
 void update() override {
  do{
     for(int i=0;i<4;i++)
     {
       data[i]=mySerial.read();
     }
  }while(mySerial.read()==0xff);

  mySerial.flush();

  if(data[0]==0xff)
    {
      int value;
      value=(data[0]+data[1]+data[2])&0x00FF;
      if(value==data[3])
      {
      distance=(data[1]<<8)+data[2];
     }
    }
  //  if(value != lastPublished) 
   // {
      publish_state(distance);
     // lastPublished = value;
   // }
   delay(100);
   }
};

tank_monitor.yaml

esphome:
  name: tank_monitor
  platform: ESP8266
  board: d1_mini_pro
#  includes:
#  - my_custom_component.h

wifi:

  networks:
  - ssid: !secret wifi_ssid01
    password: !secret wifi_password01
  - ssid: !secret wifi_ssid02
    password: !secret wifi_password02
  
# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:

ota:

dallas:
  - pin: GPIO5

sensor:
  - platform: dallas
    address: 0x170119113CA44528
    name: "Tank Water Temp"
  

  - platform: wifi_signal
    name: "WiFi Signal"
    update_interval: 300s
  - platform: custom
    lambda: |-
      auto height_sensor = new TableHeightSensor();
      App.register_component(height_sensor);
      return {height_sensor};
    sensors:
      name: "Tank Water Level"
      unit_of_measurement: "%"
      accuracy_decimals: 1

Hope this helps someone :slight_smile:

3 Likes

Hello,
what would be the changes to the above for the PWM version of A02YYUW?
Thank you
Paolo

you should be able to use the PWM function in ESP Home and use a calibrate_linear to convert the value into distance.

Hi,
Just saw your old post. I’m trying to get my A02YYUW to work. Having trouble using your pasted code. I see you have commented out:

includes:
       - uart_ultrasonic_sensor.h

In the yaml file. I guess it is not on purpose since the compilation will fail.

With it incommented I get 0.0000 values out. Has it been working with correct values for you?

How do I know what pins/GPIO this is referring to (I’m using a Wemos D1 mini):

SoftwareSerial mySerial(RX, TX);

For the record I know the sensor itself is working. I have a setup with a Pi2ZeroW that is working flawlessly.

Just out of curiousity. Are you still happy about the “Throw-in Type Liquid Level Transmitter” I have an idear about avoiding physical object in my watertank. Over the years i think they will corode no matter what the specs say. The condense problem has not been a problem for me and if it will be then I have a backup plan for leading the condensed water away from the sensor.

Thanks in advance. Really appreciate you managed to post you solution even though you ditched the solution for other reasons.

Hi Michael,

Really interested to hear how you would stop the condensation on the sensor because I have 2 of these sensors sitting unused because of this:)

I assume the uart_ultrasonic_sensor.h you are reffering to is what I have as my_custom_component.h

This is a file which you place in the /config/esphome directory

SoftwareSerial mySerial(RX, TX);

this is defined in the my_custom_component.h file.

#includes:
#       - my_custom_component.h

This may be a mistake in my posted code. I believe this will need to be uncommented.

Let me know how you go. It is a while since I have played with this code so if you are still having troubles with it I will make up a board to test it again.

Regarding the throw in type transmitter, it has been working great. I don’t believe there would be any corrosion issues as it is stainless steel. It has been installed for a year now so I will pull it up and check it however and maybe post some photos of its condition if you would like.

Hi,

Thanks for your reply. I could not get your solution to work so I switched to another and in some way more complex solution, which seems to be working well. I use a Raspberry Pi Zero to sample every 10 seconds calculate the average everty minute and then by using MQTT publishing the values to my HA.

Regarding the condensation, I’m not sure about your problem, but I havent had any showstopping problems so far. Soo far I have only tested it in a cold winter environment (live in Denmark), which does generate condense, but maybee not as much as in a warmer climate. Maybee I get the same problems during summer next year!

If I get problems my plan is to construct a casing that will lead the water away from the sensor somehow. Don’t know if it will work though.

Thanks for the advice!
I corrected your code for myself, now it works like this for me.
I use the JSN-SR04T ultrasonic sensor

It sends out a message every 100ms in the format: 0xFF 0xAA 0xBB 0xSS
Where:

  • 0xFF - start of sending
  • 0xAA - high byte of distance
  • 0xBB - low byte of distance
  • 0xSS - check sum

Maybe someone will come in handy …

Code “bitumen_tanker.yaml”:

esphome:
  name: bitumen_tanker
  platform: ESP32
  board: ttgo-t1
  includes:
    - uz_component.h

wifi:
  ssid: "myssid"
  password: "mypass"
  reboot_timeout: 60s
  power_save_mode: none

uart:
  id: uart_bus
  tx_pin: 4 # No connected
  rx_pin: 5
  baud_rate: 9600

sensor:
  - platform: custom
    lambda: |-
      auto height_sensor = new BitumenSensor(100, id(uart_bus));
      App.register_component(height_sensor);
      return {height_sensor};
    sensors:
      name: "Tank Bitumen Level"
      unit_of_measurement: "m"
      accuracy_decimals: 2
      icon: "mdi:hydraulic-oil-level" #https://iconify.design/icon-sets/mdi/hydraulic-oil-level.html

Code “uz_component.h”:

#include "esphome.h"
using namespace esphome;
 
unsigned char data[3]={};
float distance;
  
class BitumenSensor : public PollingComponent, public sensor::Sensor, public UARTDevice{
 public:
    BitumenSensor(uint32_t update_interval, UARTComponent *parent) : PollingComponent(update_interval), UARTDevice(parent) {}

    void setup() override{};

    void update() override {
      while(read()!=0xff){};//Read while start packet
      for(int i=0;i<3;i++){
        data[i]=read();
      }
      flush();

      int value;
      value=(0xff + data[0] + data[1])&0x00FF;
      if(value==data[2]){
        distance=((data[0]<<8)+data[1]);
      }
      publish_state(distance/1000);  //in meters
    };
};

4 Likes

is there any update about condensation ? or working fine ?

@chocolatejoe
is Throw-in type sensor still going strong ? if possible please pull it up and check condition

@tech_fr3ak sorry for the delay.
had to pull the throw in up the other day because my esp8266 on top of the tank somehow got water in it and fried.
New board in with conformal coating so hopefully this doesn’t happen again.

Throw in sensor has been in use since March 20 and is still functioning fine.

1 Like

I find this version of the code is more stable:

#include "esphome.h"
using namespace esphome;
 
enum statesRX
{
    ST_RX_HEADER=0,
    ST_RX_DATA,
    ST_RX_PROCESSMSG    
};

unsigned char data[4]={};
  
class SewerSensor : public PollingComponent, public sensor::Sensor, public UARTDevice{
 public:
    SewerSensor(uint32_t update_interval, UARTComponent *parent) : PollingComponent(update_interval), UARTDevice(parent) {}

    void setup() override{};

    void update() override {
    static uint8_t
        stateRX = ST_RX_HEADER,
        rxIdx;
        
    if( available() > 0 )
    {
        do
        {
            uint8_t ch = read(); 
            switch( stateRX )
            {
                case    ST_RX_HEADER:
                    if( ch == 0xff )
                    {
                        rxIdx = 0;
                        data[rxIdx++] = ch;
                        stateRX = ST_RX_DATA;
                        
                    }//if
                    
                break; 

                case    ST_RX_DATA:
                    data[rxIdx++] = ch;
                    if( rxIdx == 4 )
                        stateRX = ST_RX_PROCESSMSG;
                break;

                case    ST_RX_PROCESSMSG:
                    uint8_t sum = 0;
                    for( uint8_t i=0; i<3; i++ )
                        sum = sum + data[i];

                    if( sum == data[3] )
                    {
                        uint16_t distance = ((uint16_t)data[1] << 8) + data[2];
                        if( distance > 30 )
                        {
                            publish_state(distance/10);
                        }//if
                        else
                            ESP_LOGD("custom", "Distance below lower limit.");     
                        
                    }//if
                    else
                        ESP_LOGD("custom", "Msg checksum error.");

                    stateRX = ST_RX_HEADER;
                    
                break;
                
            }//switch
            
        }while( available() > 0 );
        
    }//if
    
  }//loop
};


Sorry for the very late response. I was in here on another issue and saw my notification. Better late than never :-).

My solution has been running for roughly a year now. I have had no issues with condensation at all. I did have some spikes in my data in my early version, which probable was due to some condensation piled into a raindrop poluting the measurement for that particular moment, but it was not often. To eliminate it I create a filter in my sampling on my PiZero (which is doing the data collection). I simply filtered out those values every minute that was to far from the variance of the current population (sample rate 10secs). This seemed to do the trick!

It have HOWEVER experienced another problem with my A02YYUW but it was my own fault. During a period of heavy rain my tank got overloaded with water, flooded the sensor for some hours which ruined the sensor. So I had to bey a new one. Conclusion: The sensor is moistproof but nut water proof. Nothing surprising actually, according to the specs, but now I have testet it :slight_smile:

Hi, were you able to get this up and running ?

Hi,

Thanks for your scripts. Somehow when I try to compile in ESPhome, it fails because it can’t find softwareserial.h and other components. How do you make your script work with the .h files in esphome ?

Thanks,

B.