Ultrasonic Sensor GPIO -> display value on home assistant

Hello everyone,
So what am trying to do is:

  • I have an Ultrasonic Sensor (HC-SR04) connected to my pi via GPIO and i want to display its values on Home Assistant so i can use it for some automatons.
  • I’ve tried creating a python script and use MQTT to publish its metrics. The script seems to be working just fine but i dont know how to link it with Home Assistant. If please anyone can help. Sorry am new to these kind of things. Thank you.

I personally would have taken the same approach and probably will, I just received one last week that I want to use as a post detector in my mailbox, how will you use yours?
provided you’ve set yup your python script to publish correctly, all you have to do is to setup mqtt on HA:

Then add a sensor that pulls the data out of the topic that you publish to:

Let me know if you still need help

1 Like

Hello lolouk44, thanks for helping. So the reason am using it, is for a project am currently working on for the university and i want to use it in comparison with the PIR motion sensor to trigger an alarm on light system. So its basically for an alternative approach. So now since i have my mqtt setup on HA, and as it seems it doesn’t retrieve any values while the python script is running. So the issue is most probably on my python script?
So the python scripts are in two parts (this is partially my work, i found it as base help from github).
The first part must run on a separate terminal and will display the values of the ultrasonic sensor accordingly.
name of 1st script “distancemeter_mqtt.py”

import socket
import json
import time
from distancemeter import get_distance, cleanup
import paho.mqtt.client as mqtt

MQTT_HOST = "myhostaddress"
MQTT_PORT = "1883"
MQTT_TOPIC = "/sensors/distancemeter"
MQTT_USERNAME = "username"
MQTT_PASSWORD = "password"

def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))

def on_message(client, userdata, msg):
    print(msg.topic + " " + str(msg.payload))

CM_PER_SEC_AIR = 34300

if __name__ == '__main__':
    client = mqtt.Client()

    try:
        client.on_connect = on_connect
        client.connect(MQTT_HOST, MQTT_PORT, 60)

        client.loop_start()

        while True:
            distance = get_distance()
            print ("Received distance = %.1f cm" % distance)
            data = {'message': 'distance %.1f cm' % distance, 'distance': distance, 'hostname': socket.gethostname()}
            client.publish("sensors/distancemeter", json.dumps(data))
            time.sleep(0.2)

    except KeyboardInterrupt:
        print("Programm interrupted")
        client.loop_stop()
        client.disconnect()
        cleanup()

Now on a second terminal i run the 2nd python script which connects with the mqtt and will display the values of ultrasonic sensor in parallel.
name of 2nd script “mqtt_consumer.py”

import paho.mqtt.client as mqtt

MQTT_HOST = "myhostaddress"
MQTT_PORT = "1883"
MQTT_TOPIC = "/sensors/distancemeter"
MQTT_USERNAME = "username"
MQTT_PASSWORD = "password"

def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))

    client.subscribe("sensors/distancemeter")

def on_message(client, userdata, msg):
    print(msg.topic + " " + str(msg.payload))

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message

client.connect(MQTT_HOST, MQTT_PORT, 60)

client.loop_forever()

And lastly on HA config.yaml i have this (replaced with my actual IP and credentials):
mqtt:
broker: hostaddress
port: 1883
keepalive: 60
username: username
password: password
sensor:

  • platform: mqtt
    name: “Ultrasonic_Sensor”
    state_topic: “/sensors/distancemeter”
    #value_template: ‘{{ distance }}’
    unit_of_measurement: “cm”
    qos: 0
    #payload_on: “TRUE”
    #payload_off: “FALSE”

I know this might be complicated, if you have any idea if am doing something wrong or if you have anything else to suggest i would be very grateful. Thank you.

can you try and use markdown to post your code? It’s a bit difficult to read as it is (check the blue bar on top of the page for help)
Can you also post the message that is sent via mqtt? It looks like you send more than just a number representing the distance .
also what does your sensor status show when you click on <> and search for it?

I sorted the code above sorry for that, ok so when i run the scripts.
The 1st one displays
Received distance = 19.4 cm
Received distance = 57.9 cm
Received distance = 52.7 cm
Received distance = 33.8 cm
Received distance = 52.7 cm
Received distance = 52.7 cm
Received distance = 50.4 cm

And the 2nd one:
sensors/distancemeter {“distance”: 19.377195835113525, “message”: “distance 19.4 cm”, “hostname”: “raspberrypi”}
sensors/distancemeter {“distance”: 57.88216590881348, “message”: “distance 57.9 cm”, “hostname”: “raspberrypi”}
sensors/distancemeter {“distance”: 52.71791219711304, “message”: “distance 52.7 cm”, “hostname”: “raspberrypi”}
sensors/distancemeter {“distance”: 33.81911516189575, “message”: “distance 33.8 cm”, “hostname”: “raspberrypi”}
sensors/distancemeter {“distance”: 52.6852011680603, “message”: “distance 52.7 cm”, “hostname”: “raspberrypi”}
sensors/distancemeter {“distance”: 52.72200107574463, “message”: “distance 52.7 cm”, “hostname”: “raspberrypi”}
sensors/distancemeter {“distance”: 50.41996240615845, “message”: “distance 50.4 cm”, “hostname”: “raspberrypi”}

On my HA the sensor status is “unknown”.

ok it’s still difficult to read, try and enclose you code using the </> button above.
I guess the message you send to your MQTT broker is
{“distance”: 19.377195835113525, “message”: “distance 19.4 cm”, “hostname”: “raspberrypi”}
if so you need to adjust your MQTT sensor:
uncomment your value_template and try this:
value_template: '{{ value_json.distance | float(2) }}'

1 Like

Think you may also need to replace
state_topic: "/sensors/distancemeter"
with this:
state_topic: "sensors/distancemeter"

1 Like

OMG, thank you!!! It worked :slight_smile:. I will try some automatons and let you know how it goes. Again thank you i had a hard time trying to debug it.

1 Like

My script

Libraries

import RPi.GPIO as GPIO
import time
import os
import paho.mqtt.client as mqtt

#GPIO Mode (BOARD / BCM)
GPIO.setmode(GPIO.BCM)

#set GPIO Pins
GPIO_TRIGGER = 25
GPIO_ECHO = 7

#set GPIO direction (IN / OUT)
GPIO.setup(GPIO_TRIGGER, GPIO.OUT)
GPIO.setup(GPIO_ECHO, GPIO.IN)

time.sleep(2)


client = mqtt.Client("G424")
client.connect ("192.168.60.5")



def distance():
    # set Trigger to HIGH
    GPIO.output(GPIO_TRIGGER, True)

    # set Trigger after 0.01ms to LOW
    time.sleep(0.00001)
    GPIO.output(GPIO_TRIGGER, False)

    StartTime = time.time()
    StopTime = time.time()

    # save StartTime
    while GPIO.input(GPIO_ECHO) == 0:
        StartTime = time.time()

    # save time of arrival
    while GPIO.input(GPIO_ECHO) == 1:
        StopTime = time.time()

    # time difference between start and arrival
    TimeElapsed = StopTime - StartTime
    # multiply with the sonic speed (34300 cm/s)
    # and divide by 2, because there and back
    distance = (TimeElapsed * 34300) / 2

    return distance


if __name__ == '__main__':
    try:
#	tempo = 0
#        aberto = False
        while True:
            dist = distance()
	    time.sleep(10)
            print ("Distancia Atual Aproximada = %.1f cm" % dist)
	    if dist < 30:
#		if aberto == False:
		    #mandar email
		    #os.system('sudo sh /home/pi/SensorUltraSonico-Raspberry/teste.sh')
		    #os.system('echo Aberto > /home/Qnap/sensoresexternos/estadoportaoaveiro.txt')
		    client.publish("aveiro/garagem/portao/estado","Aberto")
		    print ("Portao Aberto")
#		    aberto = True
#		elif aberto == True and tempo == 36000:
		    #os.system('sudo sh /home/pi/SensorUltraSonico-Raspberry/teste.sh')
		    #print ("Email Enviado2")
#		    tempo = 0
#                tempo += 1
	    else:
#	        aberto = False
#		tempo = 0
		#os.system('echo Fechado > /home/Qnap/sensoresexternos/estadoportaoaveiro.txt')
                client.publish("aveiro/garagem/portao/estado","Fechado")
                print ("Portao Fechado")
        # Reset by pressing CTRL + C
    except KeyboardInterrupt:
        print("stopped by User")
        GPIO.cleanup()

Hi there everyone

I have the ultrasonic sensor showing up in home assistant as centimeter. Is there someone that can help me to change that it shows the percentage water.

Im thinking if the tank is 100cm high and the sensor shows 40cm, that means that there must be 60% full

Anyone able to help with this?

create a template sensor. Try this

- platform: template
  sensors:
    bmw_220d_mileage:
      entity_id: sensor.220d_mileage
      value_template: '{{100-((states("sensor.your_sensor_name_here") | int) / depth_of_your_water_tank_in_cm * 100) | int }}'
      unit_of_measurement: '% Full'
      friendly_name: Tank Level
      icon_template: 'mdi:ruler'

Could someone post their code on the distance meter side? I’m trying to set up a presence detection using a sonar sensor and I can’t seem to get the MQTT publisher to work. Thanks, David.

I’ve recently managed to setup the below:

I’ve got it working in HA too but I’m mainly looking to display the distance in HA and can’t seem to figure that part out, I’ve never worked with scripts before. Any assistance would be great.

This is the full code for the GarageDoor.ino


#include <dummy.h>

#define HOSTNAME "ESP8266-GarageDoor"

#define TELNET_PORT 23

#define WIFI_SSID "MyWifi"
#define WIFI_PSK "12345678"
#define WIFI_RETRY_INTERVAL 100 // ms

#define MQTT_SERVER "192.168.0.100"
#define MQTT_PORT 1883
#define MQTT_TOPIC "hass/garagedoor"
#define MQTT_USERNAME "admin"
#define MQTT_PASSWORD "admin"
#define MQTT_RETRY_INTERVAL 500 // ms
#define MQTT_PUBLISH_INTERVAL 1000 // ms

#define ULTRASONIC_DIST_OPEN 60 // cm
#define ULTRASONIC_TRIG_PIN 5 // D1
#define ULTRASONIC_ECHO_PIN 4 // D2
#define ULTRASONIC_TIMEOUT 40000 // µs

#define VERIFICATION_INTERVAL 3000 // ms that the state must stay the same before it is reported

#define LED_PIN 16 // D0

#include <ArduinoOTA.h>
#include <PubSubClient.h>
#include <Ultrasonic.h>

#if defined(ESP8266)
	#include <ESP8266WiFi.h>
#else
	#include <WiFi.h>
#endif

Ultrasonic ultrasonic(ULTRASONIC_TRIG_PIN, ULTRASONIC_ECHO_PIN, ULTRASONIC_TIMEOUT);
WiFiClient wifi, telnet;
WiFiServer telnetServer(TELNET_PORT);
PubSubClient mqtt(wifi);
int distance;

void setupWifi() {
	if (WiFi.status() == WL_CONNECTED) {
		return;
	}
	
	Serial.print("[WiFi] Connecting to ");
	Serial.print(WIFI_SSID);

	WiFi.hostname(HOSTNAME);
	WiFi.mode(WIFI_STA);
	WiFi.begin(WIFI_SSID, WIFI_PSK);

	while (WiFi.status() != WL_CONNECTED) {
		delay(WIFI_RETRY_INTERVAL);
		Serial.print(".");
	}

	Serial.println();
	Serial.print("[WiFi] Connected as ");
	Serial.println(WiFi.localIP());
}

void setupTelnet() {
	if (!(telnet = telnetServer.available())) {
		return;
	}

	telnet.print("mqtt_connected = ");
	telnet.println(mqtt.connected());
	
	telnet.print("distance_cm = ");
	telnet.println(distance);
	
	telnet.print("door_open = ");
	telnet.println(isDoorOpen());
	
	telnet.flush();
	telnet.stop();
}

void setupMqtt() {
	if (mqtt.connected()) {
		return;
	}
	
	mqtt.setServer(MQTT_SERVER, MQTT_PORT);
	
	Serial.print("[MQTT] Connecting to ");
    Serial.print(MQTT_SERVER);
    Serial.print(":");
    Serial.print(MQTT_PORT);
    Serial.print(" as ");
    Serial.print(MQTT_USERNAME);

    mqtt.connect(HOSTNAME, MQTT_USERNAME, MQTT_PASSWORD);

    if (mqtt.connected()) {
        Serial.println();
        Serial.println("[MQTT] Connected");
    } else {
        Serial.println();
        Serial.print("[MQTT] Error connecting: ");
        Serial.println(mqtt.state());   
    }
}

void setup() {
	Serial.begin(9600);
	telnetServer.begin();
	delay(10);

	ArduinoOTA.begin();
	ArduinoOTA.setHostname(HOSTNAME);

	pinMode(LED_PIN, OUTPUT);
	digitalWrite(LED_PIN, LOW);

	setupWifi();
	setupMqtt();

	// Turn on the LED for 1 second when setup is complete
	digitalWrite(LED_PIN, HIGH);
	delay(1000);
	digitalWrite(LED_PIN, LOW);
}

bool isDoorOpen() {
    distance = ultrasonic.distanceRead();
	return distance <= ULTRASONIC_DIST_OPEN;
}

void loop() {
	setupWifi();
	setupTelnet();
	setupMqtt();
	ArduinoOTA.handle();
	
	bool state = isDoorOpen();
	delay(VERIFICATION_INTERVAL);
	bool stateVerify = isDoorOpen();
	
	if (state == stateVerify) {
		digitalWrite(LED_PIN, !state);
	
        Serial.print("[loop] Distance = ");
        Serial.print(distance);
        Serial.println(" cm");
		Serial.print("[loop] Door is ");
		Serial.println(state ? "Open" : "Closed");
		
		mqtt.publish(MQTT_TOPIC, state ? "1" : "0");
		delay(MQTT_PUBLISH_INTERVAL);
	}
}

At the end of your code you have a mqtt.publish(MQTT_TOPIC, state ? "1" : "0");
Try changing this to mqtt.publish(MQTT_TOPIC, distance);

EDIT: You’ll have to change how home assistant handles the messages also since it will move from binary into JSON format.

I haven’t messed around with ESPs at all so I am completely lost with its code. Is this made with Python? If not, you may be better starting a new thread as we are using python scripts on a Raspberrypi GPIO.

Thanks, I did try that but I got a multitude of errors when compiling.

First being:

GarageDoorSensor2:154:36: error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]

mqtt.publish(MQTT_TOPIC, distance);

So I managed to figure it out.

I ended up flashing ESP Easy to the control board and then I was able to link into Home Assistant on Windows or PI.

I’ve attached the important sections on ESP Easy just in case someone else is needing help in the future.
I will also include my code I used to bring the data into Home Assistant

HA Code:

sensor:

  • platform: mqtt
    name: “Distance”
    entity_namespace: Ultrasonic
    state_topic: “Ultrasonic/distance”
    force_update: true
    unit_of_measurement: “cm”

Hi, I am a new user of Home Assistant. In the past I connected the ultrasonic sensor to the GPIO pins on the RPI3 and read it with Node Red and MQTT, that worked well. With Home Assistant I don’t know how to read the GPIO pins and config Home Assistent.
Can someone help me further?
Greetings Maarten.