Why Your Ultrasonic Distance Sensor Gives You Numbers When What You Need Is Experience
The Parking Sensor Problem
You wire up the HC-SR04. You upload the library example. You open the serial monitor.
Distance: 45 cm
Distance: 44 cm
Distance: 43 cm
Distance: 42 cm
Your visitor looks at the numbers. Then looks at you. Then walks away.
Printing distance in centimeters is not an interactive experience. It's a science fair project.
The Wrong Mental Model
Most HC-SR04 tutorials start with: "Distance = (travel time × speed of sound) / 2."
That's the right formula. But it leads you to think about distance as a number. And numbers are not experiences.
The real question is: what should HAPPEN when something gets closer?
An installation that just prints numbers is boring. An installation where LEDs shift from cool blue (far) to warm orange (near) to red (very close) with a gentle sound that increases in frequency — that's an experience.
The difference is proximity design, not distance measurement.
What Actually Creates Proximity Experience
The key is defining proximity zones and mapping each zone to a distinct output behavior.
The Core Principle
Distance → Zone identification → Zone-specific output behavior
A parking sensor gives you numbers. A good proximity experience gives you a feeling.
The Wiring
HC-SR04 VCC → 5V (separate from LED strip if using many LEDs)
HC-SR04 GND → GND
HC-SR04 TRIG → Arduino Pin 9
HC-SR04 ECHO → Arduino Pin 10
WS2812B Data → Arduino Pin 6
Passive Buzzer → Arduino Pin 5 (with 100Ω resistor)
The Code That Actually Creates Proximity Feeling
// 區塊:初始化
#include <Adafruit_NeoPixel.h>
#include <NewPing.h>
#define TRIG_PIN 9
#define ECHO_PIN 10
#define MAX_DISTANCE 200 // 最大測量距離:200cm
#define BUZZER_PIN 5
NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DISTANCE);
Adafruit_NeoPixel strip(16, 6, NEO_GRB);
// 區塊:proximity 區域設定(單位:cm)
#define ZONE_FAR 100 // 100cm 以上:冷色光
#define ZONE_MID 50 // 50-100cm:過渡色
#define ZONE_NEAR 25 // 25-50cm:暖色光
#define ZONE_ALERT 15 // 15cm 以內:紅色+聲音警告
// 區塊:顏色定義
uint32_t colorFar = strip.Color(30, 100, 255); // 冷藍
uint32_t colorMid = strip.Color(100, 200, 150); // 青綠
uint32_t colorNear = strip.Color(255, 150, 50); // 暖橙
uint32_t colorAlert = strip.Color(255, 50, 30); // 紅色
void setup() {
Serial.begin(115200);
strip.begin();
strip.setBrightness(100);
pinMode(BUZZER_PIN, OUTPUT);
colorAll(strip.Color(0, 0, 0)); // 初始熄滅
}
void loop() {
// 區塊:讀取距離
unsigned int distance = sonar.ping_cm();
if (distance == 0) {
distance = MAX_DISTANCE; // 無回應=太遠
}
// 區塊:區域判斷 + 輸出行為
if (distance > ZONE_FAR) {
// 無人:熄滅
colorAll(strip.Color(0, 0, 0));
noTone(BUZZER_PIN);
}
else if (distance > ZONE_MID) {
// 區域 1:進入範圍,亮冷色光
float ratio = (float)(distance - ZONE_MID) / (ZONE_FAR - ZONE_MID);
uint32_t color = blend(colorFar, colorMid, ratio);
colorAll(color);
strip.setBrightness(80);
noTone(BUZZER_PIN);
}
else if (distance > ZONE_NEAR) {
// 區域 2:靠近中,暖色光+開始發出聲音
float ratio = (float)(distance - ZONE_NEAR) / (ZONE_MID - ZONE_NEAR);
uint32_t color = blend(colorMid, colorNear, ratio);
colorAll(color);
strip.setBrightness(150);
// 區塊:聲音提示(頻率隨距離變化)
int freq = map(distance, ZONE_NEAR, ZONE_MID, 200, 800);
tone(BUZZER_PIN, freq);
}
else if (distance > ZONE_ALERT) {
// 區域 3:接近中,紅色+快節奏
colorAll(colorAlert);
strip.setBrightness(255);
int beepFreq = map(distance, ZONE_ALERT, ZONE_NEAR, 1500, 400);
tone(BUZZER_PIN, beepFreq);
}
else {
// 區域 4:太近,警告
colorAll(strip.Color(255, 0, 0));
strip.setBrightness(255);
tone(BUZZER_PIN, 2000); // 持續高音
delay(100);
noTone(BUZZER_PIN);
delay(100);
}
strip.show();
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" cm");
delay(50); // HC-SR04 需要至少 50ms 的測量間隔
}
// 區塊:輔助函式
void colorAll(uint32_t color) {
for (int i = 0; i < 16; i++) {
strip.setPixelColor(i, color);
}
}
uint32_t blend(uint32_t c1, uint32_t c2, float ratio) {
uint8_t r1 = (c1 >> 16) & 0xFF, g1 = (c1 >> 8) & 0xFF, b1 = c1 & 0xFF;
uint8_t r2 = (c2 >> 16) & 0xFF, g2 = (c2 >> 8) & 0xFF, b2 = c2 & 0xFF;
uint8_t r = r1 + (r2 - r1) * ratio;
uint8_t g = g1 + (g2 - g1) * ratio;
uint8_t b = b1 + (b2 - b1) * ratio;
return strip.Color(r, g, b);
}
Why this feels alive instead of mechanical:
- Four distinct proximity zones — each with a different color AND sound behavior
- Color interpolation — smooth transition between zones, not hard jumps
- Audio feedback — the beep frequency changes with distance, creating a spatial feeling
- Idle = off — the installation is only alive when someone approaches
The Adjustment Points
| What you adjust | Effect |
|---|---|
| Zone boundaries | How close someone needs to be before behavior changes |
| Color per zone | Emotional feeling — warm = inviting, red = warning |
| Beep frequency mapping | Spatial audio feeling — higher = closer |
| Brightness per zone | How "present" the installation feels |
delay(50) in loop |
Response speed vs. stability tradeoff |
When It Still Feels Mechanical
The LED changes are hard-switching. If you see sudden color jumps instead of smooth transitions, add a smoothing filter — store the previous color and interpolate toward the target over several frames instead of setting it immediately.
The HC-SR04 is giving erratic readings. This sensor has a ±3mm accuracy spec, but in practice, soft surfaces (clothes, foam) return weak echoes that give wildly incorrect readings. Use a坚硬表面(如牆面)for calibration, and add a simple median filter: read three times, take the middle value.
The buzzer is annoying. Not every proximity installation needs sound. If it's for a gallery or museum, remove the buzzer entirely and let the color behavior tell the story silently.
The Real Principle
Distance is a number. Proximity is an experience.
When you design the proximity zones first — and map each zone to a distinct color, brightness, and audio behavior — the installation creates a feeling of something alive. When you just print distance numbers, you have a parking sensor.
If you're trying to decide which sensors and output modules to use for an interactive installation — and you want a personalized blueprint that maps your specific modules to the interaction experiences they can create — I offer a one-page PDF that turns your module selection into a complete interaction design.
Top comments (0)