Introduction
I have wanted to build an ultrasonic object detector ever since I saw a post on one. I have seen several projects on ultrasonic object detectors. However, all of the projects tethered the sensor to a PC. I wanted a more portable solution. So I designed an ultrasonic object detector that the user could place anywhere that had Wi-Fi connectivity. The ultrasonic object detector sends the sonar data to the PC via Wi-Fi. The PC then displays the data using a simulated sonar screen.
Figure 1 shows the hardware design. I mounted an HC-SR04 ultrasonic sensor on a tilt-pan servo and attached the tilt-pan servo and ultrasonic sensor to the Arduino. The Arduino pans the environment while taking readings for the HC-SR04 senor for every degree the servo moves. The ESP8266 wifi module requests the sonar data from the Arduino. Finally, the Processing software module on the PC reads the sonar data sent by the wifi module and displays the data on the PC screen.
Figure1. Hardware Design
I mounted the Arduino, ESP8266, HC-SR04, and tilt-pan servo on a wooden block. A 1A, 5-volt USB battery supplies power to all of the components. It gives me the ability to set the Ultrasonic sensor anywhere within wifi range and monitor the environment from my PC on the same network.
Hardware
Hardware List
Arduino Uno R3 or Adafruit Trinket Pro
USB A to USB B cable (if needed)
Micro USB to USB cable (if needed)
ESP8266 NodeMCU Wi-Fi Module (ESP-12E Module)
Adafruit Mini Pan-Tilt Kit - Assembled with Micro Servos
Power Supply or USB Power Bank
Block of Wood
HC-SR04
The HC-SR04 is an inexpensive ultrasonic distance sensor. Developers mainly use it to range the distance from an object. It is popular with DIY roboticists for collision avoidance in mobile robots. Other uses for ultrasonic distance sensors include speed and direction measurement, wireless charging, humidifiers, medical ultrasonography, and burglar alarms.
The HC-SR04 consists of a transmitter, receiver, and a crystal oscillator (Figure 2). The transmitter converts an electrical signal to ultrasonic waves, while the receiver converts the ultrasonic waves back to electrical signals. The purpose of the crystal oscillator is for timing operations.
The HC-SR04 works between 2cm to 400cm away with a resolution of 3mm. However, for the best results, range between 10cm and 250cm. Its measuring angle is 30o (15 degrees on either side of the sensor.) It uses approximately 15mA, and its operating voltage is 5 volts.
Figure 2. HC-SR04 Ultrasonic Sensor
How the HC-SR04 Works
The HC-SR04 is an ultrasonic transducer. It converts a digital signal into an ultrasonic wave. Ultrasound is sound waves with frequencies higher than the upper audible limit of human hearing, which is approximately 20 kHz (20,000 hertz). The HC-SR04 operates at 40 kHz.
When you set the trigger pin high for 10uS, the HC-SR04 generates eight 40kHz sound waves (Figures 3 & 4) and sets the Echo pin high. If the sound waves hit an object, the waves bounce off the object back to the HC-SR04. When the waves reach the HC-SR04, the HC-SR04 sets the Echo pin to low. The waves will take between 150 uS to 25 mS to return to the HC-SR04. The HC-SR04 will set the Echo pin to low if the waves do not return within 38 mS (The waves did not reflect off an object.) Therefore, anything that takes longer than 25 mS is considered an out of range condition.
Figure 3. HC-SR04 Ultrasonic Sensor Timing
Figure 4. Ultrasonic Waves
You can use the following formula to calculate the distance of the object: d = (v x t)/2
Where:
d - equals the distance to the object
v - equals the speed of sound
t - equals the time sound waves take to come back after reflecting off an object (called the duration).
Note that we need to divide the value by two because the time recorded by the Echo pin is twice the distance of the object to the HC-SR04. The duration is the time the waves travel to and from the detected object. Dividing it by two will give the actual distance of the target object.
Also, note that the speed sound travels through the air is affected by temperature, humidity, and air pressure. The temperature has the most effect on the speed of sound. Sound travels at 343 meters per second (1,125 feet per second) at sea level when the temperature is 20 degrees Celsius (68 degrees Fahrenheit). The formula for calculating the speed of sound when factoring in temperature is: v = 331.3 +( .606 * t) m/s
Where:
v - equals the speed of sound
t - equals the temperature in degrees Celsius (°C)
For this project, I used the standard 343 meters per second, ignoring any variances caused by temperature, humidity, and air pressure, since the object detector is for indoor use.
Mini Pan-Tilt Kit
I used the Mini Pan-Tilt Kit (assembled), from Adafruit, to pan the environment (Figure 5). I mounted the HR-SR04 to the Pan-Tilt Kit. I only connected the top servo to the Arduino since I only want to pan the area 180 degrees, side-to-side. I mounted the Pan-Tilt Kit to a block of wood with two screws.
Figure 5. Mini Pan-Tilt Kit
Arduino Uno R3
The Arduino Uno R3 (Figure 6) and its variants are the most popular microcontroller used by robotic DIYers. I used the Arduino to drive the servo motor on the Pan-Tilt Kit and to process the data from the HC-SR04 sensor during development. I also use it to provide sensor data to the ESP8266. You can learn more about the Arduino Uno R3 on the DIY-Robotics website.
Figure 6. Arduino Uno R3
ESP8266 NodeMCU Wi-Fi Module
To communicate with the Processing module on the PC, I used the ESP8266 NodeMCU Wi-Fi Module (ESP-12E Module). The ESP8266 contains a full TCP/IP stack and comes with microprocessor capabilities (Figure 7). The ESP8266 NodeMCU Wi-Fi Module comes with the Arduino firmware pre-loaded on the chip. The specifications are:
- 80 KB of Ram
- 4MB of Flash Memory
- 80MHz Processor
- 11 GPIOs
- 3.3V Operating Voltage
- 1 10-bit ADC
- 1 Serial Line
- 1 I2C Bus
- 1 SPI
- PWM on all I/O Pins with 10-Bit Resolution
- 2MBPS Wi-Fi
- 802.11b/g/n Wi-Fi Transceiver
- Voltage Regulator
- USB Interface
Note that the pins are not 5V tolerant, applying more than 3.6 volts on any pin will destroy the chip. A GPIO pin can only draw a maximum of 12mA.
A sketch running on the ESP8266 must share resources (CPU time and memory) with the Wi-Fi and TCP-stacks (the software that runs in the background and handles all Wi-Fi and IP connections). If your sketch takes too long to execute, it might affect the background process (TCP stack) and may crash the system or lose data. It is best to keep your sketch's loop execution time short as possible, preferably under a couple of hundreds of milliseconds. Note that every time the main loop is repeated, your sketch yields to the Wi-Fi and TCP to handle all Wi-Fi and TCP requests. You can explicitly give CPU time to the Wi-Fi/TCP stacks by using including a delay(0) or yield(). Also, note that if your loop takes longer than 3 seconds, the soft WDT (Watch Dog Timer) will reset the ESP8266 chip.
Figure 7. ESP8266 NodeMCU Wi-Fi Module
PC
I used a laptop with Windows 10 to execute the Processing sketch. I used the Processing sketch to display the data from the HC-SR04 in a format similar to a Sonar Screen.
Software
There are two IDEs that I loaded onto my PC, the Arduino IDE and Processing 3. I used the Arduino IDE to develop the code for the Arduino Uno and the ESP8266. I used Processing 3 to develop the sketch for displaying the sonar data on the laptop screen. You can download the Arduino IDE at:
https://www.arduino.cc/en/Main/Software
You can download Processing 3 at:
https://processing.org/download/
I added four libraries to the Arduino IDE to support the hardware:
NewPing - HC-SR04
Servo - Mini Pan-Tilt Servos
ESP8266 core for Arduino - ESP8266 NodeMCU Wi-Fi Module
ESP8266 Community - ESP8266 NodeMCU Wi-Fi Module
See below for instructions to install the libraries.
Install the NewPing and Servo libraries
From the Arduino IDE, click on Tools and then Manage Libraries (Figure 8).
Figure 8. Arduino IDE - Manage Libraries
Enter NewPing in the search box in the Library Manager window and press the Enter key (Figure 9).
When the description for the NewPing library appears, click on the Install button.
Figure 9. Arduino IDE - NewPing Library
Repeat the above process for the Servo library (replace NewPing with Servo).
Install the ESP8266 Libraries
The ESP8266 NodeMCU Wi-Fi Module comes with the Arduino firmware pre-loaded on the chip. Therefore, all you have to do to program the chip is to add the required libraries. First, you need to add a line in the Additional Boards Manager box from the Preferences window. To display the Preferences window, click on File, then click on Preferences (Figure 10).
Figure 10. Arduino IDE - Preferences
Add the following line in the Additional Boards Manager box (Figure 11):
http://arduino.esp8266.com/stable/package_esp8266com_index.json
Note that I already have a URL entered in the box. So, I separated the current URL and the new URL with a comma.
Then, click on the OK button.
Figure 11. Arduino IDE - Preferences: Additional Boards Manager
Now add the ESP8266 Community library to the Boards Manager.
Click on Tools, Board, Boards Manager (Figure 12).
Figure 12. Arduino IDE - Boards Manager
Enter ESP8266 in the search box in the Boards Manager window and press the Enter key (Figure 13).
When the description for the ESP8266 Community library appears, click on the Install button.
Figure 13. Arduino IDE - Boards Manager - ESP8266 Community
High-Level Design
I developed three sketches to handle receiving the data from the HC-SR04, transferring the data to the PC, and displaying the data on the PC's screen (Figure 14). The first sketch is an Arduino sketch running on the Arduino Uno, called SonarPing. SonarPing pans the HC-SR04 180 degrees, one degree at a time. Every time it moves the sonar sensor one degree, it calculates the distance to an object, stores the distance, and stores the current position of the sensor (in degrees). SonarPing also joins an I2C bus as a slave. The I2C bus connects to the ESP8266 NodeMCU Wi-Fi Module.
The second sketch, SonarTransfer, is loaded on the ESP8266 NodeMCU Wi-Fi Module. It connects to my Wi-Fi network and then to the Processing sketch on my laptop. SonarTransfer joins the I2C bus as a master. It requests data from SonarPing via the I2C interface (distance from an object and position of sonar sensor in degrees). It then sends the data to the Processing sketch via the Wi-Fi interface.
The Processing sketch, DisplaySonarData, receives the data from SonarTransfer, paints the laptop display with a sonar screen, and then displays the sonar data over the sonar screen.
Figure 14. High-Level Design
Sketches
SonarPing
As I stated above, SonarPing is an Arduino sketch running on the Arduino Uno. SonarPing pans the HC-SR04 180 degrees, one degree at a time. Every time it moves the sonar sensor one degree, it calculates the distance to an object, stores the distance, and stores the current position of the sensor (in degrees). SonarPing also joins an I2C bus as a slave. The I2C bus connects to the ESP8266 NodeMCU Wi-Fi Module. The ESP8266 NodeMCU Wi-Fi Module requests data via the I2C bus, and SonarPing passes the requested data back to the ESP8266 NodeMCU Wi-Fi Module via the same bus. Below is a summary of what the SonarPing code does.
- Lines 1-3 contain the includes statements.
- Lines 5-11 contain the define constants.
- Line 13 creates the sonar object, which defines the pins used for the trigger pin and the echo pin. It also defines the maximum range for the sonar sensor. Change Line 7 if you want to use a different maximum range. I used a MAX_DISTANCE of 250 cm, about 8 feet.
- Line 14 creates the servo object.
- Lines 16-19 defines the variables used in the sketch.
- Lines 21-28 contain the setup code. In the setup function, I initialize the serial line, the servo object, and the I2C interface. I also register the function (onRequest), which handles the data requests from the ESP8266 NodeMCU Wi-Fi Module.
- Lines 30-34 contains the code for the loop function. The loop function just calls the scanArea function and delay processing for 50 milliseconds.
- Lines 36-46 contains the code for the scanArea function. This function moves the servo motor from 0 to 180 degrees, in one-degree increments. After each one-degree move, the scanArea function calls the GetUltrasonicDistance function. It generates a string containing the servo angle and object distance, provided by the GetUltrasonicDistance function. The created string (sonarData), includes the data that the ESP8266 NodeMCU Wi-Fi Module requests.
- Lines 48-52 contains the code for the GetUltrasonicDistance function. The GetUltrasonicDistance function calls the sonar.ping_median function to get the duration for the sonar sensor's ping. It calculates the distance from the object, in inches (I am from the United States, so my mind thinks in inches and feet.) If you would prefer to use centimeters, just take out the division by 2.54, in line 51. The object distance is stored in a rounded int variable, called distance.
- Lines 54-61 contains the code for the requestEvent function. Whenever the ESP8266 NodeMCU Wi-Fi Module requests data, the SonarPing sketch calls the requestEvent function. The function passes the information in the sonarData string, one byte at a time, to the ESP8266 NodeMCU Wi-Fi Module.
Note that the loop function runs independently from the requestEvent function. The requestEvent function just sends the ESP8266 NodeMCU Wi-Fi Module, whatever information that the sonarData string contained when the ESP8266 made the request. If you have timing problems (the ESP8266 NodeMCU Wi-Fi Module does not receive all of the data or receives duplicate data), you can adjust the delay in line 33.
SonarPing
#include <Servo.h> #include <NewPing.h> #include <Wire.h> #define TRIGGER_PIN 10 #define ECHO_PIN 13 #define MAX_DISTANCE 250 #define ITERATIONS 5 #define SERVO_PIN 5 #define SLAVE_ADDR 9 #define ANSWERSIZE 8 NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); Servo myservo; int servoAngle = 0; float duration; float distance; String sonarData; void setup() { Serial.begin(9600); myservo.attach(SERVO_PIN); Wire.begin(SLAVE_ADDR); Wire.onRequest(requestEvent); Serial.print("Sonar Ping Started"); } // End setup() void loop() { scanArea(); delay(50); } // End loop() void scanArea() { if (servoAngle == 181) { servoAngle = 0; } // End if (servoAngle == 181) myservo.write(servoAngle); // tell servo to go to position in variable 'pos' GetUltrasonicDistance(); sonarData = String(servoAngle) + "," + String(int(round(distance))) + ";"; ++servoAngle; } // End scanArea() void GetUltrasonicDistance() { duration = sonar.ping_median(ITERATIONS); distance = round(((duration / 2) * 0.0343)/2.54); } // End GetUltrasonicDistance() void requestEvent() { byte response[ANSWERSIZE]; for (byte i=0;i<ANSWERSIZE;i++) { response[i] = (byte)sonarData.charAt(i); } // End for (byte i=0;i<ANSWERSIZE;i++) Wire.write(response,sizeof(response)); } // End requestEvent()
SonarTransfer
As I stated above, SonarTransfer is the sketch loaded on the ESP8266 NodeMCU Wi-Fi Module. It connects to my Wi-Fi network and also to the processing sketch on my laptop. SonarTransfer joins the I2C bus as a master. It requests data from SonarPing via the I2C interface (distance from an object and position of sonar sensor in degrees). It then sends the data to the Processing sketch via the Wi-Fi link. Below is a summary of what the SonarTransfer code does.
- Lines 1-4 contain the includes statements.
- Lines 6-11 contain the define constants. Make sure you insert your SSID, your router password, your host IP address. You can also change the port number and slave address if you desire.
- Line 13 creates the web server object.
- Line 14 creates a Wi-Fi object.
- Lines 16-18 defines the variables used in the sketch.
- Lines 20-40 contain the setup code. In the setup function, I initialize the serial line, Wi-Fi client, and the I2C interface. I connect to my router. Then I connect to the DisplaySonarData Processing sketch.
- Lines 42-57 contain the code for the loop function. The loop function requests data from the SonarPing sketch. It then waits for a response on the I2C interface. When the I2C interface passes data to the loop function, it reads and adds the data, one byte at a time, to the sonarData string. Once the I2C interface reads all of the data, the loop function calls the SendSonarData function to send the data to the DisplaySonarData Processing sketch.
- Lines 59-62 contain the code for the SendSonarData function. The function contains just one line of code that sends the sonarData string to the DisplaySonarData Processing sketch via the Wi-Fi client interface.
Note that if you are having timing problems, you can also try changing the delay in line 44.
SonarTransfer
#include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <Wire.h> #define RT_SSID "YourSSID" #define PASSWORD "YourPassord" // Replace with your password #define HOST "192.168.1.118" // Replace with your IP Address #define PORT 8000 // Replace with another port number if desired #define ANSWERSIZE 8 #define SLAVE_ADDR 9 ESP8266WebServer server(PORT); //Server on port 80 WiFiClient client; String sonarData; String previousSonarData; char aChar; void setup() { Serial.begin(9600); delay(1000); WiFi.begin(RT_SSID, PASSWORD); //Connect to your WiFi router while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } // End while (WiFi.status() != WL_CONNECTED) Serial.println(WiFi.localIP()); if (!client.connect(HOST, PORT)) { Serial.println("Connection to host failed"); delay(1000); return; } // End if (!client.connect(HOST, PORT)) Serial.println("Connected to server successful!"); Serial.println(SDA); Serial.println(SCL); Wire.begin(); } // End setup void loop() { delay(170); Wire.requestFrom(SLAVE_ADDR,ANSWERSIZE); sonarData = ""; while (!Wire.available()) {} while (Wire.available()) { aChar = Wire.read(); if(isAlphaNumeric(aChar) || isPunct(aChar)) { sonarData += aChar; } // End if(isAlphaNumeric(aChar) || isPunct(aChar)) } // End while (Wire.available()) SendSonarData(); } // End loop() void SendSonarData() { client.println(sonarData); } // End SendSonarData()
DisplaySonarData
The Processing sketch, DisplaySonarData, receives the data from SonarTransfer, paints the laptop display with a radar screen, and then displays the sonar data over the radar screen. Note that Processing uses Java instead of C/C++ to develop sketches.
Below is a summary of what the DisplaySonarData code does.
- Lines 1-7 contain the import statements.
- Lines 9-19 contain the variables used by the sketch.
- Lines 22-38 contain the code for the setup function. It contains the code to set up and initialize the display screen, such as screen size, background color, and text font. The setup function creates a socket to receive data from the ESP8266 NodeMCU Wi-Fi Module. It also creates a bufferedReader object to handle the input from the socket.
- Lines 40-51 contain the code for the draw function. The draw function is like the loop function in the Arduino IDE. Code in the draw function executes, then loops to the top of the draw function and executes the code again. The draw function begins by repainting the screen. Lines 42-43 creates the fading effect as the object lines pan across the screen. To change the speed of the fade, change the number of the second parameter on line 42. A larger number creates a faster fade. It then calls the ReadSonarData function to get the data sent from the SonarTransfer sketch. It calls the DrawObject, DrawRadar, and DrawText functions to finish building the radar screen and displaying the data received from the SonarTransfer sketch.
- Lines 53-66 contain the code for the ReadSonarData function. The ReadSonarData function reads a line of data from the bufferedReader and stores it in the sonarInput string. It then parses the angle and distance data into a string array called sonarData. Finally, it parses the data in the string array into the integer variables iAngle and iDistance.
- Lines 68-98 contain the code for the DrawRadar function. This function draws the semi-circles that indicate the distance from the center point of the radar screen. The DrawRadar function calls the DrawRadarAngledLine function to draw the semi-circles. It also calls the DrawRadarAngledLine function that draws the lines that represent the angles on the radar screen.
- Lines 100-103 contains the code for the DrawRadarArcLine function. It draws the semi-circles specified by the DrawRadar function.
- Lines 105-108 contains the code for the DrawRadarAngledLine function, which draws the lines that represent the angles on the radar screen.
- Lines 110-132 contains the code for the DrawObject function. The function draws a red line that represents the distance and the angle an object is from the sonar sensor. It uses the iAngle and the iDistance variables to determine the starting and ending points for the line. Since the maximum distance for the radar screen is about 10 feet, the ending point will always be at 10 feet.
- Lines 148-198 contains the code for the DrawText function. The DrawText function adds the text to the radar screen. It includes the text that specifies the distance for the sonar sensor (2ft, 4ft, 8ft) and the angle of the sonar sensor in degrees (30, 60, 90, 120, 150). It also displays the current distance of a detected object and the object's angle from the sonar sensor at the bottom of the radar screen.
DisplaySonarData
import java.io.*; import java.net.*; import java.io.InputStreamReader; import processing.net.*; import processing.serial.*; import java.awt.event.KeyEvent; import java.io.IOException; Client c; int x = 00; int y = 00; PFont processingSansPro; int iAngle = 00; int iDistance = 00; ServerSocket serverSocket; Socket socket; BufferedReader bufferedReader; String sonarInput; String[] sonarData; void setup() { size(1350, 760); background(50); processingSansPro = loadFont("ProcessingSansPro-Regular-48.vlw"); fill(98, 245, 31); textFont(processingSansPro); noStroke(); fill(0, 4); rect(0, 0, width, 0.935 * height); try{ serverSocket=new ServerSocket(8000); socket=serverSocket.accept(); //establishes connection bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); }catch(Exception e){System.out.println(e);} System.out.print("Connected "); } // End setup void draw() { fill(0, 12); rect(0, 0, width, 0.935 * height); ReadSonarData(); DrawObject(); DrawRadar(); DrawLine(); DrawText(); } // End draw() void ReadSonarData() { try { sonarInput=(String)bufferedReader.readLine(); }catch(Exception e) { System.out.println(e); } sonarData = sonarInput.split("[,;]"); System.out.println(sonarInput); iAngle = Integer.parseInt(sonarData[0]); iDistance = Integer.parseInt(sonarData[1]); } // End ReadSonarData() void DrawRadar() { pushMatrix(); translate(width/2, 0.926 * height); noFill(); strokeWeight(2); stroke(0,255,255); // draws the arc lines DrawRadarArcLine(1.0100); DrawRadarArcLine(0.9100); DrawRadarArcLine(0.8100); DrawRadarArcLine(0.7100); DrawRadarArcLine(0.6100); DrawRadarArcLine(0.5100); DrawRadarArcLine(0.4100); DrawRadarArcLine(0.3100); DrawRadarArcLine(0.2100); DrawRadarArcLine(0.1100); // draws the angle lines final int halfWidth = width/2; line(-halfWidth, 0, halfWidth, 0); for(int angle = 30; angle <= 150; angle+=30) { DrawRadarAngledLine(angle); } line(-halfWidth * cos(radians(30)), 0, halfWidth, 0); popMatrix(); } // End DrawRadar() void DrawRadarArcLine(final float coefficient) { arc(0, 0, coefficient * width, coefficient * width, PI, TWO_PI); } // End DrawRadarArcLine(final float coefficient) void DrawRadarAngledLine(final int angle) { line(0, 0, (-width/2) * cos(radians(angle)), (-width/2) * sin(radians(angle))); } // End DrawRadarAngledLine(final int angle) void DrawObject() { pushMatrix(); translate(width/2, 0.926 * height); strokeWeight(12); stroke(255,10,10); int pixsDistance = int((iDistance * 0.008800 * height)-(iDistance - 10)); if(iDistance < 99 && iDistance != 0) { float cos = cos(radians(iAngle)); float sin = sin(radians(iAngle)); int x1 = +int(pixsDistance * cos); int y1 = -int(pixsDistance * sin); int x2 = +int(0.500 * width * cos); int y2 = -int(0.500 * width * sin); line(x1, y1, x2, y2); } // End if(iDistance < 40 && iDistance != 0) popMatrix(); } // End DrawObject() void DrawLine() { pushMatrix(); strokeWeight(.5); stroke(128, 43, 0); translate(width/2, 0.926 * height); float angle = radians(iAngle); int x = int(+0.88 * height * cos(angle)); int y = int(-0.88 * height * sin(angle)); line(0,0, x, y); popMatrix(); } // End DrawLine() void DrawText() { pushMatrix(); fill(0, 0, 0); noStroke(); rect(0, 0.9352 * height, width, height); fill(98, 245, 31); textSize(25); text("2ft", 0.5790 * width, 0.9167 * height); text("4ft", 0.6800 * width, 0.9167 * height); text("6ft", 0.7810 * width, 0.9167 * height); text("8ft", 0.8810 * width, 0.9167 * height); text("2ft", 0.4000 * width, 0.9167 * height); text("4ft", 0.3000 * width, 0.9167 * height); text("6ft", 0.2000 * width, 0.9167 * height); text("8ft", 0.1000 * width, 0.9167 * height); textSize(44); fill(255, 255, 255); text("Angle: " + iAngle + " °", 0.15 * width, 0.9723 * height); text("Distance: " + iDistance, 0.60 * width, 0.9723 * height); textSize(25); fill(98, 245, 60); translate(0.5006 * width + width/2 * cos(radians(30)), 0.9093 * height - width/2 * sin(radians(30))); rotate(-radians(-60)); text("30°",0,0); resetMatrix(); translate(0.497 * width + width/2 * cos(radians(60)), 0.9112 * height - width/2 * sin(radians(60))); rotate(-radians(-30)); text("60°",0,0); resetMatrix(); translate(0.493 * width + width/2 * cos(radians(90)), 0.9167 * height - width/2 * sin(radians(90))); rotate(radians(0)); text("90°",0,0); resetMatrix(); translate(0.487 * width + width/2 * cos(radians(120)), 0.92871 * height - width/2 * sin(radians(120))); rotate(radians(-30)); text("120°",0,0); resetMatrix(); translate(0.4896 * width + width/2 * cos(radians(150)), 0.9426 * height - width/2 * sin(radians(150))); rotate(radians(-60)); text("150°",0,0); popMatrix(); } // End DrawText()
Assembly
I tested the sketches with the Arduino Uno and the ESP8266 NodeMCU tethered to my laptop. Attaching both boards to the computer allowed me to test the sketches on both boards simultaneously. To test both boards at the same time, I had to run the sketches in its own instance of the Arduino IDE (I had to start two instances of the Arduino IDE). It gave me the ability to start a serial monitor for each board and add Serial prints to the sketches to debug the code. Once I finished testing, I deleted the debug code and used a USB power pack to provide power to the boards. The ESP8266 NodeMCU board received its power from the Arduino Uno.
I attached the HC-SR04 to the upper part of the pan-tilt kit with velcro. To get the sonar sensor to fit, I cut off the two grips.
I planned to mount the components (half-size breadboard, Arduino Uno, and the ESP8266 NodeMCU) onto the block of wood. The block of wood I selected was too small for the Arduino Uno. So, I mounted an Adafruit Trinket Pro to the breadboard. The wiring is the same as it was for the Arduino Uno. To mount the pan-tilt kit to the board, I attached the bottom of the pan-tilt base to the block of wood with two screws. I took off one of the power rails on the half-size breadboard (to make more room on the block of wood) before mounting it to the board, using the self-adhesive backing on the breadboard.
Note that I used the Trinket Pro instead of a different small board because I had one lying around. Also, note I did my development on the Arduino Uno and moved the wires to the Trinket Pro after I was satisfied with the way the code worked. I could have added an FDDI interface to the Trinket Pro and did my development on the Trinket Pro. Another option I could have used was purchasing the newer Adafruit Metro Mini 328, which has a built-in USB-to-Serial interface.
I planned to use a hot glue gun to attach the Arduino Uno to the block of wood. As I noted above, I instead mounted an Adafruit Trinket Pro to the breadboard.
Once I assembled the hardware, I wired all of the components as follows (Figure 15):
- 5-volt pin on the Trinket Pro to the positive rail on the breadboard
- Ground pin on the Trinket Pro to the ground rail on the breadboard
- Ground pin on the ESP8266 to the ground rail on the breadboard
- 5-volt pin on the Trinket Pro to the VIN pin on the ESP8266
- VCC pin on the HC-SR04 to the positive rail on the breadboard
- Ground pin on the HC-SR04 to the ground rail on the breadboard
- The brown wire on the servo to the ground rail on the breadboard
- The red wire on the servo to the positive rail on the breadboard
- The yellow wire on the servo to pin 5 on the Trinket Pro
- Trigger pin on the HC-SR04 to pin 10 on the Trinket Pro
- Echo pin on the HC-SR04 to pin 13 on the Trinket Pro
- A4 pin on the Arduino to the D2 pin on the ESP8266
- A5 pin on the Arduino to the D1 pin on the ESP8266
Figure 15. Wiring Diagram
Figure 16. Portable Ultrasonic Object Detector
Figure 17. ESP8266 NodeMCU Wi-Fi Module
Figure 18. ESP8266 NodeMCU Wi-Fi Module
Figure 19. Adafruit Pro - 5V 16MHz
Operation
Start the Processing 3 IDE on your PC. Load the DisplaySonarData sketch and run it. You must start the DisplaySonarData sketch before running the SonarTransfer sketch. Otherwise, the SonarTransfer sketch will not attach to the socket. If the sonar display does not appear on the PC try pressing the reset button on the ESP8266 NodeMCU Wi-Fi Module.
Place the block of wood (your scanner) in the area you want to scan. The sonar sensor should face the center of the scan area when it is in the 90-degree position (center position). Plug your scanner in an outlet or use a 1 ampere / 5-volt USB power pack to power the components.
The sonar data should begin displaying in the DisplaySonarData screen. Note that ESP8266 and your PC must be in the range of your router.
In the video below, I have the PC and the scanner near each other. It enables me to have both devices in the video. However, you can place the scanner anywhere within the range of your router.
Summary
I created the Portable Ultrasonic Object Detector as an entry in the Acoustics contest. As such, I designed the project around the HC-SR04 ultrasonic sonar distance sensor, although I found that most of my effort was learning how to use the ESP8266 and the Processing IDE.
The HC-SR04 a cheap sensor, which makes it a good fit for a variety of DIY robotics projects. As I stated earlier, other sonar projects use the HC-SR04. What sets this project apart from the other sonar projects is the fact that it is portable. You can place the sonar components in one location and view the sonar sweep on your PC in another area. It does require you to put the sonar components and the PC on the same network. You can get around this restriction by opening up the ESP8266 to the Internet. However, if you decide to do so, make sure you secure the connection, so you do not make your network vulnerable to attacks from hackers.
I used the ESP8266 NodeMCU Wi-Fi Module for communications between the Arduino and the PC. What I liked about the NodeMCU module is that it is pre-loaded with the Arduino firmware. Therefore, I just plugged it in and start programming (OK, I did have to load the appropriate libraries to make use of the Wi-Fi on the chip). Like the HC-SR04, ESP8266 is a popular chip, so it is easy to find information on the chip. My original plan was to attach the HC-SR04 and the Pan-Tilt kit to the NodeMCU module. However, I could not get valid readings from the HC-SR04. The problem could be that the ESP8266 only contains one CPU. Therefore, any Arduino processing must share the CPU with the Wi-Fi processes. There are techniques you can use to yield the Arudino processing to the Wi-Fi processes. So I might revisit using just the NodeMCU module. Another possibility I could explore is using the newer ESP32 chip, which uses two CPUs.
The real challenge was Processing 3. Any information I have read on Processing stated that the developers based Processing on Java. What it looks like is that it is a Java IDE that contains a variety of functions that allow you to perform 2D and 3D graphics easily. I am a Java programmer, so using a Java IDE was a bonus. There was no learning curve for me. However, the 2D and 3D graphics were a different story. I am not a graphics artist, so I had a difficult time learning how to display graphics on the screen. There was defiantly a learning curve. And still, more for me to learn. However, since it is a Java IDE, it opens up a lot of possibilities for interfacing the Arduino to the PC. I can't wait to exploit the features of the Processing 3 IDE.
It was a much more challenging project than I thought. However, I learned a lot while working on the project. So I am glad I entered the Acoustics contest.
Top Comments