Project Overview & Difficulty Rating
To use the ShillehTek HC-SR04 ultrasonic distance sensor with an RGB light for Arduino, you wire the sensor's Trigger pin to digital Pin 9, the Echo pin to digital Pin 10, and the RGB LED's PWM channels to Pins 3, 5, and 6. The microcontroller calculates the time-of-flight for the 40kHz acoustic pulse and shifts the LED color smoothly from green (far) to red (close) based on the measured distance.
Target Board Variant: This code and wiring diagram specifically target the Arduino Uno R3 Rev3 (ATmega328P) running at 5V logic. If you are using a 3.3V board (like an ESP32 or Arduino Due), you must add a voltage divider to the Echo pin to prevent silicon damage.
Difficulty Rating: ⭐⭐☆☆☆ (Beginner/Intermediate) | Time to Build: 30 minutes
Hardware Specs & Parts List
The ShillehTek sensor kits typically bundle standard 5V HC-SR04 clones. While the branding is ShillehTek, the underlying silicon (the EM4325 or equivalent timing IC) behaves identically to the original HC-SR04. The blind zone (minimum reading distance) is physically limited to 2cm by the acoustic ring-down time of the transducers.
| Component | Exact Variant / Spec | Quantity |
|---|---|---|
| Microcontroller | Arduino Uno R3 Rev3 (ATmega328P, 5V logic) | 1 |
| Ultrasonic Sensor | ShillehTek HC-SR04 (5V, 40kHz transducers) | 1 |
| RGB LED | 5mm Common-Cathode Diffused RGB LED | 1 |
| Current Limiting Resistors | 220Ω 1/4W Carbon Film (for R, G, B pins) | 3 |
| Prototyping | Half-size solderless breadboard & male-to-male jumpers | 1 kit |
Wiring the ShillehTek HC-SR04 and RGB LED
Proper current limiting is critical when driving a common-cathode RGB LED directly from the Arduino's ATmega328P GPIO pins. The absolute maximum current per I/O pin is 40mA, but the recommended continuous limit is 20mA. The 220Ω resistors keep the forward current well within safe limits for the red (~2.0Vf), green (~3.2Vf), and blue (~3.2Vf) dies.
Pin Mapping Table
| Component Pin | Arduino Uno R3 Pin | Notes |
|---|---|---|
| HC-SR04 VCC | 5V | Do not use 3.3V; the sensor will fail to trigger. |
| HC-SR04 Trigger | Digital 9 | Output: Sends 10µs HIGH pulse. |
| HC-SR04 Echo | Digital 10 | Input: Reads HIGH duration (5V safe on Uno). |
| HC-SR04 GND | GND | Shared ground with RGB LED. |
| RGB Red Anode | Digital 3 (PWM) | Via 220Ω resistor. |
| RGB Green Anode | Digital 5 (PWM) | Via 220Ω resistor. |
| RGB Blue Anode | Digital 6 (PWM) | Via 220Ω resistor. |
| RGB Cathode | GND | Longest leg on standard 5mm LEDs. |
Step-by-Step Wiring
- Insert the Arduino Uno R3 and breadboard into your workspace. Connect the Arduino 5V and GND pins to the breadboard's power rails.
- Seat the ShillehTek HC-SR04 on the breadboard. Wire VCC to the 5V rail and GND to the ground rail.
- Connect the Trigger pin to Digital 9 and the Echo pin to Digital 10 using jumper wires.
- Insert the common-cathode RGB LED. Identify the longest leg (cathode) and wire it directly to the ground rail.
- Place a 220Ω resistor in series with each of the three remaining anode legs. Wire the other ends of the resistors to Digital Pins 3 (Red), 5 (Green), and 6 (Blue).
Complete Arduino Code with Error Handling
This sketch uses the NewPing library by Tim Eckel. The native Arduino pulseIn() function blocks execution and causes severe RGB LED flickering. NewPing uses timer interrupts, allowing smooth PWM fading while waiting for the acoustic echo. Install 'NewPing' via the Arduino Library Manager before compiling.
#include <NewPing.h>
// --- PIN DEFINITIONS ---
#define TRIGGER_PIN 9
#define ECHO_PIN 10
#define RED_PIN 3
#define GREEN_PIN 5
#define BLUE_PIN 6
// --- SENSOR CONFIGURATION ---
#define MAX_DISTANCE 200 // Maximum distance we want to ping (in cm)
#define BLIND_ZONE 2 // HC-SR04 physical blind zone
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
// --- SMOOTHING ARRAY ---
// HC-SR04 is notoriously noisy. We use a 5-sample moving average.
const int numReadings = 5;
int readings[numReadings];
int readIndex = 0;
int total = 0;
int averageDistance = 0;
void setup() {
Serial.begin(115200);
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
// Initialize smoothing array
for (int i = 0; i < numReadings; i++) {
readings[i] = MAX_DISTANCE;
}
// Boot sequence: flash white
setColor(255, 255, 255);
delay(500);
setColor(0, 0, 0);
}
void loop() {
delay(30); // Wait 30ms between pings (HC-SR04 max rate is ~20Hz)
int rawDistance = sonar.ping_cm();
// --- ERROR HANDLING ---
// NewPing returns 0 if the echo is not received within the timeout.
// Since the blind zone is 2cm, a true 0cm is physically impossible.
if (rawDistance == 0) {
Serial.println("[FAULT] HC-SR04 Echo Timeout - Raw Ping: 0");
setColor(255, 0, 255); // Flash Magenta to indicate hardware fault
delay(100);
setColor(0, 0, 0);
return; // Skip smoothing and color mapping
}
// --- MOVING AVERAGE FILTER ---
total = total - readings[readIndex];
readings[readIndex] = rawDistance;
total = total + readings[readIndex];
readIndex = (readIndex + 1) % numReadings;
averageDistance = total / numReadings;
Serial.print("Distance: ");
Serial.print(averageDistance);
Serial.println(" cm");
// --- COLOR MAPPING ---
// Map distance (2cm to 100cm) to color gradient
if (averageDistance <= BLIND_ZONE) {
setColor(255, 0, 0); // Solid Red (Too close)
} else if (averageDistance <= 30) {
// Transition Red to Yellow
int greenVal = map(averageDistance, BLIND_ZONE, 30, 0, 255);
setColor(255, greenVal, 0);
} else if (averageDistance <= 60) {
// Transition Yellow to Green
int redVal = map(averageDistance, 30, 60, 255, 0);
setColor(redVal, 255, 0);
} else {
// Solid Green (Safe distance)
setColor(0, 255, 0);
}
}
void setColor(int red, int green, int blue) {
// Common cathode: HIGH turns the LED ON
analogWrite(RED_PIN, red);
analogWrite(GREEN_PIN, green);
analogWrite(BLUE_PIN, blue);
}
Debugging: Echo Timeouts and Common Failures
When working with 40kHz acoustic transducers, environmental factors and wiring mistakes frequently cause the serial monitor to output the exact error string: [FAULT] HC-SR04 Echo Timeout - Raw Ping: 0. This means the sensor fired the trigger pulse, but the Echo pin never went HIGH within the maximum timeout window.
The First Three Things to Check When It Fails:
- Verify Trigger/Echo Swap: The most common mistake is wiring Trigger to 10 and Echo to 9. The code will compile, but the sensor will never receive the return pulse. Check your physical breadboard traces against the pin mapping table.
- Check 5V Rail Voltage: Use a multimeter to measure the breadboard's 5V rail relative to GND. If your Arduino is powered via a weak USB hub, the voltage may drop below 4.8V under load. The HC-SR04's internal comparator will fail to register the echo threshold if VCC sags.
- Clear the Blind Zone: Ensure the object you are measuring is at least 3cm away. If an object is pressed directly against the metal mesh of the transducers, the acoustic ring-down masks the echo, resulting in a 0cm timeout.
Ranked Causes for Persistent Timeouts
- Cause 1: Acoustic Dampening. The target surface is soft, angled away, or highly textured (like foam or clothing). The 40kHz wave scatters instead of reflecting back. Fix: Aim at a hard, flat surface like a wall or a book.
- Cause 2: Parasitic Capacitance on Long Wires. If you are using jumper wires longer than 20cm, the capacitance of the wire can slow the rising edge of the 5V Echo pulse, causing the ATmega328P to miss the interrupt. Fix: Keep sensor wires under 15cm or add a 74HC14 Schmitt trigger buffer.
- Cause 3: Defective Transducer. ShillehTek kits are highly affordable, but occasionally a batch has a dead receiver transducer. Fix: Swap the sensor with a known good unit or test the receiver with an oscilloscope to look for the 40kHz decay envelope.
Extending and Simplifying the Build
Once you have the baseline proximity visualizer working, you can adapt the hardware to fit different project constraints.
How to Simplify the Build
If you want to eliminate the three 220Ω resistors and free up two PWM pins, swap the common-cathode RGB LED for a single WS2812B NeoPixel. A NeoPixel requires only one digital GPIO pin (e.g., Pin 4), a 5V connection, and a GND connection. You will need to include the Adafruit_NeoPixel library and replace the setColor() function with pixels.setPixelColor(0, pixels.Color(r, g, b)). This simplifies the breadboard layout significantly and provides much brighter, more accurate color mixing.
How to Extend the Build
To turn this from a visual indicator into a complete parking sensor or desk-proximity alarm, add an I2C 16x2 LCD (PCF8574 backpack) or an I2C 0.96-inch OLED (SSD1306). Because the HC-SR04 and RGB LED do not use the I2C bus (Pins A4/A5 on the Uno), you can wire the display in parallel without modifying the existing sensor code. You can print the exact millimeter distance to the screen while the RGB LED continues to provide peripheral visual feedback. For audio feedback, wire a 5V active buzzer to Pin 8 and trigger a 100ms beep when averageDistance < 15.
Frequently Asked Questions
Can I use the ShillehTek HC-SR04 ultrasonic distance sensor with an ESP32 instead of Arduino?
Yes, but you cannot wire the Echo pin directly to the ESP32. The HC-SR04 outputs a 5V HIGH signal on the Echo pin, which will permanently damage the 3.3V GPIO pins on an ESP32. You must build a voltage divider using a 1kΩ and 2kΩ resistor to step the 5V Echo signal down to a safe ~3.3V before it reaches the ESP32's GPIO pin. The Trigger pin can be wired directly, as the ESP32's 3.3V output is sufficient to trigger the HC-SR04's internal logic.
Why is my RGB light flickering when the HC-SR04 takes a reading?
Flickering happens when you use the native Arduino pulseIn() function to read the sensor. pulseIn() is a blocking function; it halts all microcontroller operations—including PWM signal generation—while it waits for the echo to return. This causes the LED to briefly turn off or dim during the measurement cycle. The code provided in this guide uses the NewPing library, which relies on hardware timer interrupts, allowing the PWM to run smoothly in the background without flickering.
How do I calibrate the ShillehTek HC-SR04 for accurate millimeter readings?
The HC-SR04 is not inherently a millimeter-precision instrument; its acoustic wavelength at 40kHz is roughly 8.5mm, meaning resolution is physically limited. However, you can improve accuracy by compensating for ambient temperature. The speed of sound changes by approximately 0.6 m/s for every 1°C change. Add a DS18B20 or DHT22 temperature sensor to your build, calculate the exact speed of sound for the current room temperature, and replace the hardcoded divisor in the NewPing library's source code (or calculate distance manually using distance = (duration * speed_of_sound) / 2) to achieve sub-centimeter accuracy.






