In this tutorial we will learn how to make an Arduino range measurer and a digital spirit level. You can watch the following video or read the written tutorial below.
Overview
The device features an ultrasonic sensor for measuring the distance to the nearest object, an accelerometer for measuring the angle with respect to the ground, an LCD display for showing the results and a custom designed PCB on which all components are connected.
We can operate the device with just a single button. Once we power the device we need to select the unit of measurement.
By pressing the button we can toggle through the units, and if we press and hold the button for a while we will get into the first program. Here we can measure the distance and also have the ability to store the last two measurements.
For getting into the second program, again we have to press and hold the button for a while. In this program we can measure a square area by taking two perpendicular distance measurements.
The next program is the digital spirit level and here we can measure the angle with respect to the ground.
Using the button we can toggle between the two axes or we can measure either the Pitch or the Roll.
Arduino Range Measurer Circuit Schematic
Here’s the circuit schematic of this Arduino project.
Note that I already have detailed tutorials on how each of these modules work, and you can check them on the following links: Ultrasonic sensor tutorial, LCD tutorial, MEMS accelerometer tutorial.
You can get the components needed for this project from the links below:
- Ultrasonic Sensor HC-SR04……………………………… Amazon / Banggood / AliExpress
- MPU6050 3 Axis Gyroscope Accelerometer……… Amazon / Banggood / AliExpress
- 16×2 Character LCD………………………………………… Amazon / Banggood / AliExpress
- Arduino Nano………………………………………………… Amazon / Banggood / AliExpress
Disclosure: These are affiliate links. As an Amazon Associate I earn from qualifying purchases.
Custom design PCB
According to the circuit schematic we need to design the custom PCB and I did that using the EasyEDA free online circuit design software.
Once we finished the design here, we can simply export the Gerber file which is used for manufacturing the PCB. You can check the EasyEDA project files of this project here.
Then we can order our PCB from JLCPCB, which is actually the sponsor of this project.
Here we can simply drag and drop the Gerber file. Once uploaded we can review our PCB in the Gerber viewer. If everything is all right then we can go on, select the properties that we want for our PCB, and then we can order our PCB at a reasonable price. Note that if it’s your first order from JLCPCB, you can get up to 10 PCBs for only $2.
Assembling the Device
Nevertheless, after several days the PCBs have arrived. The quality of the PCBs is great and everything is exactly the same as in the design.
Ok, now we can start assembling the electronics for this project. I started by soldering pin headers on the PCB. In this way we can easily connect and disconnect the components when needed.
Then I inserted and soldered the three resistors. Two of them are for the voltage divider for the contrast of LCD. The 1k resistor should be placed at R1 and the 220 ohms at R2. The third one is a pull-up resistor for the button.
Next I moved on with making the case for the device. I decided to use transparent acrylic because I wanted the beauty of all electronics components to be visible. I had some 5mm tick acrylic from an old project, and I used a circular for cutting it to size.
Then I had to make an opening for the LCD on the top side of the case, which is actually 4mm tick because it will fit better with the LCD. So first I made a two hole with a drill and then inserted a hacksaw through them. Using the hacksaw I roughly made the opening and then using a rasp I made fine straight lines so that the LCD can tightly fit.
Then using a forstner bit I made the openings for the power switch, the control button and the ultrasonic sensor.
Once I had all pieces ready I used a 5 minutes epoxy to assembly to case. As for the top, I inserted and glued two bolts through which the top panel can be inserted and secured using some nuts on top of it.
The case is now ready and so I continued with soldering pin headers to the LCD so I can easily attach it to the PCB. I also soldered pin headers or jump wires to the power switch, the button and the battery connector.
Finaly, I had everything ready to assembly the device. I started with inserting the ultrasonic sensor on the PCB, and then through the holes on the side panel. Next was the Arduino board, the accelerometer module as well as the battery connector.
On the top panel, I secured the LCD, the power switch and the button, and then connected them to the PCB. Lastly I inserted the 9V battery to the connector and secured the top panel with the nuts.
And that’s it, the Arduino range measurer project is done but what’s left in this video is to explain how the program works, so let’s take a look at the Arduino code.
Arduino Range Measurer and Digital Spirit Level Source Code
As the code is a bit longer, for better understanding, I will post the source code of the program in sections with description for each section. And at the end of this article I will post the complete source code.
So first we need to include the Wire.h library for the I2C communication of the accelerometer, as well as the LiquidCrystal.h library for controlling the LCD. Then we need to define the LCD, the I2C address of the MPU6050 accelerometer module, the ultrasonic sensor pins, as well as some variables needed for the program below.
#include<Wire.h> // I2C communication library
#include <LiquidCrystal.h> // includes the LiquidCrystal Library
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // Creates an LCD object. Parameters: (rs, enable, d4, d5, d6, d7)
const int MPU = 0x68; // I2C address of the MPU6050 accelerometer
#define trigPin 8
#define echoPin 9
#define selectButton 10
int16_t AcX, AcY, AcZ;
long duration;
float distance;
int program = 0;
float d = 0;
float d1 = 0;
float d2 = 0;
float area = 0;
int axis = 0;
int angle = 0;
int unitSelect = 0;
String unit = "cm";
Code language: Arduino (arduino)
In the setup section we need to initialize both the I2C interface of the accelerometer and the liquid crystal display, as well as define the pin modes for the ultrasonic sensor trigger and echo pins, and the button pin.
void setup() {
// Initialize interface to the MPU6050
Wire.begin();
Wire.beginTransmission(MPU);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
lcd.begin(16, 2); // Initializes the interface to the LCD screen
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(selectButton, INPUT_PULLUP);
}
Code language: Arduino (arduino)
In the main loop section we have a switch statements through which we can toggle between the different programs of our device. In the first, or case number 0, we select the unit of measurement. Using the lcd.print() function we print the text on the LCD and using the if statements we toggle through the four unit of measurements.
switch (program) { // Switch between different programs
case 0: // Select unit of measurement
lcd.setCursor(0, 0); // Sets the location at which subsequent text written to the LCD will be displayed
lcd.print("Select Unit: ");
lcd.setCursor(13, 0);
lcd.print(unit);
lcd.print(" ");
delay(10);
// If button is pressed - change unit
if (digitalRead(selectButton) == 0) {
if (unitSelect == 0) {
unit = "in";
unitSelect = 1;
}
else if (unitSelect == 1) {
unit = "m";
unitSelect = 2;
}
else if (unitSelect == 2) {
unit = "ft";
unitSelect = 3;
}
else if (unitSelect == 3) {
unit = "cm";
unitSelect = 0;
}
// If button is held longer then half a second - change program
delay(500);
if (digitalRead(selectButton) == 0) {
program = 1;
lcd.clear();
delay(500);
}
}
break;
Code language: Arduino (arduino)
We should note here that the unitSelect variable takes action in the getDistance() cunstom function, where it actually tells to which unit we should convert the basic cm unit that we are getting from the ultrasonic sensor.
/ Converting the units
if (unitSelect == 1) {
distance = distance; // cm to cm
unit = "cm";
}
else if (unitSelect == 2) {
distance = distance * 0.393701; // cm to in
unit = "in";
}
else if (unitSelect == 3) {
distance = distance * 0.01; // cm to m
unit = "m";
}
else if (unitSelect == 0) {
distance = distance * 0.0328; // cm to ft
unit = "ft";
}
Code language: Arduino (arduino)
In order to selecting the unit of measurement and also get in the first program of the device we just have to hold the press button longer than half a second.
// If button is held longer then half a second - change program
delay(500);
if (digitalRead(selectButton) == 0) {
program = 1;
lcd.clear();
delay(500);
}
}
break;
Code language: Arduino (arduino)
In case number 1 or in the distance measuring program first we get the distance to the nearest object using the getDistance() custom function.
case 1: // Distance measuring program
distance = getDistance(); // Distance to the nearest object
lcd.setCursor(0, 0);
lcd.print("Dist: ");
lcd.print(distance); // Prints the distance value from the sensor
lcd.print(" ");
lcd.setCursor(14, 0);
lcd.print(unit);
delay(10);
lcd.setCursor(0, 1);
lcd.print("d:");
lcd.setCursor(8, 1);
lcd.print("d:");
delay(200);
// Save distance 1
if (digitalRead(selectButton) == 0) {
if (d == 0) {
lcd.setCursor(0, 1);
lcd.print("d: ");
lcd.setCursor(2, 1);
lcd.print(distance);
d = 1;
delay(100);
}
// Save distance 2
else if (d == 1) {
lcd.setCursor(8, 1);
lcd.print("d: ");
lcd.setCursor(10, 1);
lcd.print(distance);
d = 0;
delay(100);
}
// If button is held longer then half sencond change program
delay(500);
if (digitalRead(selectButton) == 0) {
program = 2;
d = 0;
lcd.clear();
delay(500);
}
}
break;
Code language: Arduino (arduino)
Let’s see how this function works.
//===== getDistance - Custom Function
float getDistance() {
// Clears the trigPin
digitalWrite(trigPin, LOW);
// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Reads the echoPin, returns the sound wave travel time in microseconds
duration = pulseIn(echoPin, HIGH);
// Calculating the distance
distance = duration * 0.034 / 2; // distance in cm
// Converting the units
if (unitSelect == 1) {
distance = distance; // cm to cm
unit = "cm";
}
else if (unitSelect == 2) {
distance = distance * 0.393701; // cm to in
unit = "in";
}
else if (unitSelect == 3) {
distance = distance * 0.01; // cm to m
unit = "m";
}
else if (unitSelect == 0) {
distance = distance * 0.0328; // cm to ft
unit = "ft";
}
return distance;
}
Code language: Arduino (arduino)
Here using the trigger pin we tell sensor to generate an ultrasonic sound wave.
Then using the echo pin and the pulseIn() function we measure the duration the wave traveled from the sensor to the object and back. Considering the speed of sound and the travel time we can easily calculate the distance. So we print the measured distance along with some text on the LCD and using the “if” statements, if we press the button, we print or save last two measurements.
Next is the program for calculating area in which we are using similar methods. We need to take two perpendicular measurements and simply multiply them in order to get the area of the square that they form.
case 2: // Area measuring program
distance = getDistance();
lcd.setCursor(0, 0);
lcd.print("Area: ");
lcd.print(area); // Prints the calculated area from the two measurements
lcd.setCursor(12, 0);
lcd.print(unit); // Prints the selected unit and the square sign below
lcd.print("^2");
delay(200);
if ( d == 0) {
lcd.setCursor(0, 1);
lcd.print("d1: ");
lcd.setCursor(3, 1);
lcd.print(distance);
delay(200);
}
else if (d == 1) {
lcd.setCursor(9, 1);
lcd.print("d2: ");
lcd.setCursor(12, 1);
lcd.print(distance);
delay(200);
}
else if (d == 2) {
lcd.setCursor(6, 0);
lcd.print(area);
delay(200);
}
// Save distance 1
if (digitalRead(selectButton) == 0) {
if (d == 0) {
lcd.setCursor(0, 1);
lcd.print("d1: ");
lcd.setCursor(3, 1);
lcd.print(distance);
d = 1;
d1 = distance;
delay(100);
}
// Save distance 2
else if (d == 1) {
lcd.setCursor(9, 1);
lcd.print("d2: ");
lcd.setCursor(12, 1);
lcd.print(distance);
d = 2;
d2 = distance;
area = d1 * d2; // Calculate the area
delay(100);
}
else if (d == 2) {
lcd.clear();
d = 0;
area = 0;
delay(100);
}
// If button is held longer then half sencond change program
delay(500);
if (digitalRead(selectButton) == 0) {
program = 3;
d = 0;
lcd.clear();
delay(500);
}
}
break;
Code language: Arduino (arduino)
The last case, is the angle measuring program. Here we need to read the accelerometer data which is actually the strength of the Earth’s gravitational field in three different axes, X,Y, and Z. The value of each axis is stored in 2 registers, so we need to read total of 6 registers and combine them in order to get the right value.
case 3: // Angle measuring program
// Read the accelerometer data
Wire.beginTransmission(MPU);
Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
AcX = Wire.read() << 8 | Wire.read(); // X-axis value
AcY = Wire.read() << 8 | Wire.read(); // Y-axis value
AcZ = Wire.read() << 8 | Wire.read(); // Z-axis value
if ( axis == 0) {
// Calculating the Pitch angle (rotation around Y-axis)
angle = atan(-1 * AcX / sqrt(pow(AcY, 2) + pow(AcZ, 2))) * 180 / PI;
lcd.setCursor(0, 0);
lcd.print("Pitch");
}
else if (axis == 1) {
// Calculating the Roll angle (rotation around X-axis)
angle = atan(-1 * AcY / sqrt(pow(AcX, 2) + pow(AcZ, 2))) * 180 / PI;
lcd.setCursor(0, 0);
lcd.print("Roll ");
}
lcd.setCursor(0, 1);
lcd.print("Angle: ");
lcd.print(abs(angle));
lcd.print(" ");
lcd.setCursor(10, 1);
lcd.print("deg");
delay(200);
// Change axis
if (digitalRead(selectButton) == 0) {
if (axis == 0) {
axis = 1;
delay(100);
}
// Save distance 2
else if (axis == 1) {
axis = 0;
delay(100);
}
// If button is held longer then half sencond change program
delay(500);
if (digitalRead(selectButton) == 0) {
program = 0;
lcd.clear();
delay(500);
}
}
break;
Code language: Arduino (arduino)
Once we have the X,Y and Z values of the accelerometer we can calculate the Pitch angle, or rotation around the Y-axis, as well as the Roll angle, or rotation around the X-axis, using the the two equations. You can find more details about these equations on Freescale Semiconductor application note, Tilt Sensing Using a Three-Axis Accelerometer. So after that we print the values along with some text on the LCD, and by pressing the button we change what we show on the LCD, whether the Pitch or the Roll angle.
Here’s the complete source code for this Arduino range measurer and spirit level project :
/*
DIY Digital Range Measurer and Spirit Level
by Dejan Nedelkovski,
www.HowToMechatronics.com
*/
#include<Wire.h> // I2C communication library
#include <LiquidCrystal.h> // includes the LiquidCrystal Library
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // Creates an LCD object. Parameters: (rs, enable, d4, d5, d6, d7)
const int MPU = 0x68; // I2C address of the MPU6050 accelerometer
#define trigPin 8
#define echoPin 9
#define selectButton 10
int16_t AcX, AcY, AcZ;
long duration;
float distance;
int program = 0;
float d = 0;
float d1 = 0;
float d2 = 0;
float area = 0;
int axis = 0;
int angle = 0;
int unitSelect = 0;
String unit = "cm";
void setup() {
// Initialize interface to the MPU6050
Wire.begin();
Wire.beginTransmission(MPU);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
lcd.begin(16, 2); // Initializes the interface to the LCD screen
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(selectButton, INPUT_PULLUP);
}
void loop() {
switch (program) { // Switch between different programs
case 0: // Select unit of measurement
lcd.setCursor(0, 0); // Sets the location at which subsequent text written to the LCD will be displayed
lcd.print("Select Unit: ");
lcd.setCursor(13, 0);
lcd.print(unit);
lcd.print(" ");
delay(10);
// If button is pressed - change unit
if (digitalRead(selectButton) == 0) {
if (unitSelect == 0) {
unit = "in";
unitSelect = 1;
}
else if (unitSelect == 1) {
unit = "m";
unitSelect = 2;
}
else if (unitSelect == 2) {
unit = "ft";
unitSelect = 3;
}
else if (unitSelect == 3) {
unit = "cm";
unitSelect = 0;
}
// If button is held longer then half a second - change program
delay(500);
if (digitalRead(selectButton) == 0) {
program = 1;
lcd.clear();
delay(500);
}
}
break;
case 1: // Distance measuring program
distance = getDistance(); // Distance to the nearest object
lcd.setCursor(0, 0);
lcd.print("Dist: ");
lcd.print(distance); // Prints the distance value from the sensor
lcd.print(" ");
lcd.setCursor(14, 0);
lcd.print(unit);
delay(10);
lcd.setCursor(0, 1);
lcd.print("d:");
lcd.setCursor(8, 1);
lcd.print("d:");
delay(200);
// Save distance 1
if (digitalRead(selectButton) == 0) {
if (d == 0) {
lcd.setCursor(0, 1);
lcd.print("d: ");
lcd.setCursor(2, 1);
lcd.print(distance);
d = 1;
delay(100);
}
// Save distance 2
else if (d == 1) {
lcd.setCursor(8, 1);
lcd.print("d: ");
lcd.setCursor(10, 1);
lcd.print(distance);
d = 0;
delay(100);
}
// If button is held longer then half sencond change program
delay(500);
if (digitalRead(selectButton) == 0) {
program = 2;
d = 0;
lcd.clear();
delay(500);
}
}
break;
case 2: // Area measuring program
distance = getDistance();
lcd.setCursor(0, 0);
lcd.print("Area: ");
lcd.print(area); // Prints the calculated area from the two measurements
lcd.setCursor(12, 0);
lcd.print(unit); // Prints the selected unit and the square sign below
lcd.print("^2");
delay(200);
if ( d == 0) {
lcd.setCursor(0, 1);
lcd.print("d1: ");
lcd.setCursor(3, 1);
lcd.print(distance);
delay(200);
}
else if (d == 1) {
lcd.setCursor(9, 1);
lcd.print("d2: ");
lcd.setCursor(12, 1);
lcd.print(distance);
delay(200);
}
else if (d == 2) {
lcd.setCursor(6, 0);
lcd.print(area);
delay(200);
}
// Save distance 1
if (digitalRead(selectButton) == 0) {
if (d == 0) {
lcd.setCursor(0, 1);
lcd.print("d1: ");
lcd.setCursor(3, 1);
lcd.print(distance);
d = 1;
d1 = distance;
delay(100);
}
// Save distance 2
else if (d == 1) {
lcd.setCursor(9, 1);
lcd.print("d2: ");
lcd.setCursor(12, 1);
lcd.print(distance);
d = 2;
d2 = distance;
area = d1 * d2; // Calculate the area
delay(100);
}
else if (d == 2) {
lcd.clear();
d = 0;
area = 0;
delay(100);
}
// If button is held longer then half sencond change program
delay(500);
if (digitalRead(selectButton) == 0) {
program = 3;
d = 0;
lcd.clear();
delay(500);
}
}
break;
case 3: // Angle measuring program
// Read the accelerometer data
Wire.beginTransmission(MPU);
Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
AcX = Wire.read() << 8 | Wire.read(); // X-axis value
AcY = Wire.read() << 8 | Wire.read(); // Y-axis value
AcZ = Wire.read() << 8 | Wire.read(); // Z-axis value
if ( axis == 0) {
// Calculating the Pitch angle (rotation around Y-axis)
angle = atan(-1 * AcX / sqrt(pow(AcY, 2) + pow(AcZ, 2))) * 180 / PI;
lcd.setCursor(0, 0);
lcd.print("Pitch");
}
else if (axis == 1) {
// Calculating the Roll angle (rotation around X-axis)
angle = atan(-1 * AcY / sqrt(pow(AcX, 2) + pow(AcZ, 2))) * 180 / PI;
lcd.setCursor(0, 0);
lcd.print("Roll ");
}
lcd.setCursor(0, 1);
lcd.print("Angle: ");
lcd.print(abs(angle));
lcd.print(" ");
lcd.setCursor(10, 1);
lcd.print("deg");
delay(200);
// Change axis
if (digitalRead(selectButton) == 0) {
if (axis == 0) {
axis = 1;
delay(100);
}
// Save distance 2
else if (axis == 1) {
axis = 0;
delay(100);
}
// If button is held longer then half sencond change program
delay(500);
if (digitalRead(selectButton) == 0) {
program = 0;
lcd.clear();
delay(500);
}
}
break;
}
}
//===== getDistance - Custom Function
float getDistance() {
// Clears the trigPin
digitalWrite(trigPin, LOW);
// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Reads the echoPin, returns the sound wave travel time in microseconds
duration = pulseIn(echoPin, HIGH);
// Calculating the distance
distance = duration * 0.034 / 2; // distance in cm
// Converting the units
if (unitSelect == 1) {
distance = distance; // cm to cm
unit = "cm";
}
else if (unitSelect == 2) {
distance = distance * 0.393701; // cm to in
unit = "in";
}
else if (unitSelect == 3) {
distance = distance * 0.01; // cm to m
unit = "m";
}
else if (unitSelect == 0) {
distance = distance * 0.0328; // cm to ft
unit = "ft";
}
return distance;
}
Code language: Arduino (arduino)
So that would be all, I hope you enjoyed this Arduino project and learned something new. Feel free to ask any question in the comments section below.
Great project. Can I order the board using the information provided in your link. It looks like everything is included after the download is complete. I plan to build this for a friend and think he will really like this.
Is the angle of the gyroscope important for measering? The Pins of the gyroscope are not exactly 90 degrees after soldering.
Hi, absolutly geart projekts. I want to do this with my pupils in school. What is the ohm of R3 Resistor? Thanks
Thanks! R3 can be anything from 1K to 100K.
Hello, is there any template to make the housing, or 3D design to print?
Thanks in advance
hay dejan
really great project, thanks. i made it and it works..i did not build the box (im lazy) just put the switches on the board and glued the 9v to the back… but the r2 in schematic goes to ground but the pcb dead ends, should i connect the end of r2 to ground? it works now but i dont like to violate your schematic
again , great project ,,,works fine
regards
tom
hello dejan
great project…i made it work. did not build the box, just mounted the switches on the pcb and super glued a 9 v to the back. one question though, the scematic shows r 2 going to ground but it dead ends on the pcb…works fine though…what the purpose of r 2 and should i wire it to ground?
thanks again for a great project
Hey, thanks! Well R2 is used for the voltage divider for setting the contrast of the LCD. Yes, it should be connected to GND.
Hello,
I am attempting to do this project for my class activity and I had some questions regarding the materials.
Do the pins connectors come with the PCB? Or I will have to buy them separately.
Hey, the pins connectors or headers are don’t come with the PCB, you need to get them separate.
hello sir,
can you please suggest if i can use arduino uno instead of arduino nano.
Sure you can use Arduino Uno for this project.
Good morning.
When exporting the file, the DRC error detector detects 2 errors:
1-track to pad from sensor trig to D8.
2-track to pado from echo to D9.
Will it give trouble when mounting it?
Thanks
Hey, you won’t have any problem, but still I updated the project file and fixed those errors.
Hello. thank you for the amazing project that you did. I did the project, but I am having issues and I need your assistance.
after doing all the connection and uploading the code, the project will work, but in a Loop. I can’t control it. hope you can help. Thank You.
sir any suggestions for an additional thing that we can add to the existing project?
Maybe shrink the project down, using smaller display like 0.96″ OLED, add compensation for measuring from the back side of the case, add laser pointer to know the exact direction of what you measure ect.
Awesome project
I made it Was amazing but i have problem in sprit level because sometimes turn off when,I switch to the sprit level option but other thing are working nicely.
Cool!
An interesting project!
I’m gonna make it now!
Thanks, for your hard work putting together this website. It is really one of the best I’ve found for Arduino projects.
Thank you!