In this Android Things project we will use an Ultrasonic sensor with a Raspberry Pi 3 to measure distance.
We will work with the standard GPIO read and write methods, we will learn how to send ultrasonic waves with the sensor, and we will discover some issues.
The sensor is an HC-SR04 Ultrasonic sensor. (I ordered from Amazon.)
The HC-SR04 ultrasonic sensor uses sonar to determine distance to an object like bats or dolphins do. It offers excellent non-contact range detection with high accuracy and stable readings in an easy-to-use package. From 2cm to 400 cm or 1” to 13 feet. It operation is not affected by sunlight or black material.
From here you can download the datasheet of the sensor.
As in the previous project, I suggest to follow Google’s tutorial to prepare your Raspberry Pi 3 for Android Things development.
Please follow the steps here:
Raspberry Pi 3 Model B is the latest iteration of the world's most popular single board computer. It provides a quad…developer.android.com
Now it is the time to connect the sensor to our Raspberry Pi.
As you see in the picture the Ultrasonic sensor has 4 pins.
The VCC needs to be connected to a 5V power pin, the GND obviously to a Ground pin, and the Trig (Trigger) and the Echo pins connect to a GPIO.
And here is how I connected it to my Raspberry Pi:
- VCC → 5V (Pin 2)
- Trig → GPIO 21 (BCM21, Pin 40)
- Echo → GPIO 20 (BCM20, Pin 38)
- GND → GND (Pin 34)
And this it how it looks together:
(On the left side you can see a pinout diagram which illustrates the locations of the available ports exposed by the breakout connectors of the Raspberry Pi 3 board)
To configure a new project, you can follow the (already mentioned) tutorial from Google, or you can use and modify the source of our previous application.
Before we start implementing the necessary code, I explain a bit how the Ultrasonic sensor works:
- to start a measurement, the Trig (Trigger) pin must receive a pulse of HIGH (5V) for at least 10us (10 microsecond)
- this will initiate the sensor and it will transmit out 8 cycle of ultrasonic burst at 40kHz and wait for the reflected ultrasonic burst
- when the sensor detected and ultrasonic wave, it will set the Echo pin to HIGH (5V) and delay for a period (width) which proportion to distance.
- to obtain the distance, we need to measure the width of the Echo pulse.
This is how it looks on a timing diagram:
Let’s see the code!
First we declare the GPIO pins:
Then we need to setup the Echo pin (BCM20) for read:
And the Trigger (BCM21) pin for write:
And now the fun begins.
We need to send a 10us pulse to the Trigger pin and listen on the Echo for the beginning and the end of the response pulse.
I will show you two ways to do this, because I discovered some performance issues in some cases. (It is possible that my implementation is not correct in the under-performed code)
The first one is the “old school way”, the way how you do it on an Arduino device, or in C or Python e.g. on your Raspberry Pi under a Linux operating system.
This is a universal, synchronous, procedural way, you don’t have callbacks, asynchronous methods.
This code performs well, as good as you would implement it in C or Python.
You can run this code in different ways, with a Handler, or just in a new Thread, etc…
I run it now in a separated Thread :
It works properly.
You can put something in front of the sensor, move it farther and closer, and you will see the distance in cm in the console.
(I will mention the commented lines in the Findings section)
The second way is the Android Things API provided asynchronous GpioCallback() way.
To use the callbacks, we need to register our listener on the ECHO pin:
I defined a callback Handler also, because it has an impact in the measurement.
I spent a lot of time to get correct results with the async implementation, and this helped also.
Usually you just register your callback with the registerGpioCallback (GpioCallback callback) method. That means you listener will be called on the same Thread where you called the register method. Sometimes (in a lot of basic example) this is the UI Thread. And the slowness of your UI has impact on your callbacks.
In our cases the impact is 1,5cm in the accuracy of the distance measurement.
So let’s prepare the Handler:
And here is our method to “read” the distance asynchronously:
Basically it does nothing, except triggering the measurement on the sensor.
The real calculation happens in the callback:
To run the whole calculation we can also use the separated Thread way what we used in the 1. implementation, or we can use a Handler to execute our calculation:
Run your code and check the measurements in the ADB log. It works quite well.
(Oh yeah, and do not forget to close your resources in both implementation, if you don’t need them)
- The accuracy of the measurement results are the same (between a certain range) in both implementation.
- The minimum measurable distance with the 1. implementation is around 8cm. (Same as you write your code in C or Python)
- The minimum measurable distance with the 2. implementation is around 12cm.
- If we use lot of computation in the callbacks, between the catch of the ECHO high/low pulses, it will impact your measurement.
You see a lot of commented log lines. If you put them back, the minimum measurable distance will be around 20 cm, because the device needs some time to process your logging. And if you measure your time in nanoseconds, it matters!
- You can also experience with the duration of the methods.
In the first implementation in the while loops if you put back the time logging methods you will see.
Processing a Log.d() method takes almost 10x more time than assigning a value to a variable.
- Avoid from the UI Thread.
E.g. If you have a combination of heavy computation or logging and callbacks on the UI Thread you will see a really huge impact on your measurements. The minimum measurable distance can easily jump above 30cm.
- The error cases are not handled in the example app.
- The measurement capability of the sensors are not the same. I ordered a pack of HC-SR04 sensors from Amazon (pack of 3 pieces, same sensors), and e.g. one of them can measure properly only for 160cm, and an other one measures properly for 240–300cm.
- If you want to measure properly, you need to first verify the distance with a ruler or with a tape measure, then adapt your code.
- When you measure a distance, I suggest to do 2–3 measurements and calculate an average of those, because the results have 1–2cm dispersion.
- Always check the datasheet of your sensor to implement correct algorithm
I was not able to reproduce the same performance in the 2. implementation as we have it in the first one.
Maybe I missed something, I implemented something on a wrong way or this is an API issue in Android Things.
I am not sure yet.
I am still continuously thinking and testing, hopefully I will figure it out.
If you have any idea, any remarks, please feel to free to comment or send me an e-mail, or modify the source of the project: