Skip to content

Instantly share code, notes, and snippets.

@jackwillis
Created February 5, 2025 14:56
Show Gist options
  • Select an option

  • Save jackwillis/705bb1e0cea4e02d7149cd386abda339 to your computer and use it in GitHub Desktop.

Select an option

Save jackwillis/705bb1e0cea4e02d7149cd386abda339 to your computer and use it in GitHub Desktop.
// ----- Ultrasonic Sensor -----
const int TRIG_PIN = 10;
const int ECHO_PIN = 11;
// ----- Piezoelectric buzzer -----
const int PIEZO_PIN = 8;
// LED pins
const int RED_LED_PIN = 3; // Red LED: No response or max distance
const int YELLOW_LED_PIN = 5; // Yellow LED: Object detected at all
const int GREEN_LED_PIN = 6; // Green LED: Object is within trigger zone
const int BLUE_LED_PIN = 9; // Blue LED: Gate is active
// ----- Distance & Smoothing Parameters -----
const float MIN_DISTANCE = 2.0; // cm (minimum valid reading)
const float MAX_DISTANCE = 400.0; // cm (max or no response)
const float DISTANCE_ALPHA = 0.1; // Smoothing factor for EMA (0 < alpha < 1)
// ----- Gate (State Machine) Settings -----
const float GATE_TRIGGER_DISTANCE = 100.0; // cm (distance to trigger the gate)
const unsigned long GATE_TRIGGER_TIME = 250; // ms (must stay in the trigger zone for this long)
const float GATE_RELEASE_DISTANCE = 200.0; // cm (distance to release the gate)
const unsigned long GATE_RELEASE_TIME = 1000; // ms (must stay beyond release distance for this long)
// Distance measured by the ultrasonic sensor, in centimeters
float distance = MAX_DISTANCE;
float smoothedDistance = MAX_DISTANCE;
// Gate state variables
bool gateTriggered = false; // Indicates whether the gate is active
unsigned long triggerStartTime = 0; // Timestamp when object enters the trigger zone
unsigned long releaseStartTime = 0; // Timestamp when object moves beyond release zone
// ----- Setup -----
void setup() {
Serial.begin(115200); // Start serial communication for debugging
// Initialize sensor pins
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
// Initialize LED pins
pinMode(RED_LED_PIN, OUTPUT);
pinMode(YELLOW_LED_PIN, OUTPUT);
pinMode(GREEN_LED_PIN, OUTPUT);
pinMode(BLUE_LED_PIN, OUTPUT);
// Seed the smoothing filter with an initial ultrasonic reading
float initialDistance = readUltrasonic();
smoothedDistance = constrain(initialDistance, MIN_DISTANCE, MAX_DISTANCE);
}
// ----- Main Loop -----
void loop() {
// Get the raw distance from the ultrasonic sensor
distance = readUltrasonic();
// Apply the Exponential Moving Average (EMA) smoothing to the distance
smoothedDistance = smoothDistance(distance);
// Update the gate state based on the current distance
updateGateState();
// Update the LEDs based on the gate state and current distance
updateLEDs();
// Beep with piezo if gate state changes
beepOnGateStateChange();
// Debugging: Output distance and gate state to the serial monitor
Serial.print("Raw Distance: ");
Serial.print(distance);
Serial.print(" cm, Smoothed Distance: ");
Serial.print(smoothedDistance);
Serial.print(" cm, Gate: ");
Serial.println(gateTriggered ? "TRIGGERED" : "OFF");
}
// ----- Function to Read Ultrasonic Distance -----
/*
This function sends a pulse to the ultrasonic sensor and measures the duration
it takes for the echo to return, converting that duration into distance (in cm).
*/
float readUltrasonic() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // Timeout after 30ms
return (duration == 0) ? MAX_DISTANCE : (duration * 0.0343) / 2.0; // Return the calculated distance in cm
}
// ----- Exponential Moving Average (EMA) for Distance -----
/*
This function applies the Exponential Moving Average (EMA) to the distance.
It smooths out sensor noise and makes the transition between distances more stable.
*/
float smoothDistance(float newDistance) {
return DISTANCE_ALPHA * newDistance + (1 - DISTANCE_ALPHA) * smoothedDistance;
}
// ----- Gate State Machine -----
/*
This function handles the logic of triggering and releasing the gate based on
the smoothed distance. It uses the configured thresholds for triggering and releasing.
*/
void updateGateState() {
unsigned long now = millis(); // Get the current time in milliseconds
// If distance exceeds MAX_DISTANCE, reset the gate and timers
if (smoothedDistance >= MAX_DISTANCE) {
gateTriggered = false;
triggerStartTime = releaseStartTime = 0;
return;
}
// Gate not yet triggered: Check if the object is within the trigger distance
if (!gateTriggered && smoothedDistance < GATE_TRIGGER_DISTANCE) {
if (triggerStartTime == 0) {
triggerStartTime = now; // Start the timer if object enters trigger zone
}
if (now - triggerStartTime >= GATE_TRIGGER_TIME) {
gateTriggered = true; // Gate is triggered after staying in the zone for enough time
releaseStartTime = 0; // Reset release timer when gate is triggered
}
return; // Exit function if gate is triggered
}
// Gate is triggered: Check if object moves beyond the release distance to reset
if (gateTriggered && smoothedDistance > GATE_RELEASE_DISTANCE) {
if (releaseStartTime == 0) {
releaseStartTime = now; // Start the release timer
}
if (now - releaseStartTime >= GATE_RELEASE_TIME) {
gateTriggered = false; // Reset the gate after staying beyond the release zone
triggerStartTime = 0;
}
} else {
// If object moves back into the trigger zone, reset release timer
releaseStartTime = 0;
}
}
// ----- LED Control -----
/*
- Red LED brightness represents smoothedDistance (far = dim, close = bright)
- Yellow LED lights up when the object is detected
- Green LED turns on when close enough to trigger the gate
- Blue LED fades in/out smoothly when the gate transitions
*/
void updateLEDs() {
static int blueBrightness = 0; // Current blue LED brightness
static int targetBrightness = 0; // Target brightness (0 or 255)
// Map smoothedDistance to PWM brightness for the red LED (far = dim, close = bright)
int redBrightness = map(constrain(smoothedDistance, MIN_DISTANCE, MAX_DISTANCE),
MIN_DISTANCE, MAX_DISTANCE, 255, 10);
analogWrite(RED_LED_PIN, redBrightness);
// Yellow LED: Lights up when an object is close enough to maintain the gate
digitalWrite(YELLOW_LED_PIN, smoothedDistance < GATE_RELEASE_DISTANCE);
// Green LED: Lights up when the object is close enough to trigger the gate
digitalWrite(GREEN_LED_PIN, smoothedDistance < GATE_TRIGGER_DISTANCE);
// Update target brightness based on gate state
targetBrightness = gateTriggered ? 255 : 0;
// Smooth transition: Adjust brightness gradually
if (blueBrightness < targetBrightness) {
blueBrightness += 5; // Increase brightness
if (blueBrightness > targetBrightness) blueBrightness = targetBrightness;
} else if (blueBrightness > targetBrightness) {
blueBrightness -= 5; // Decrease brightness
if (blueBrightness < targetBrightness) blueBrightness = targetBrightness;
}
// Apply smoothed brightness
analogWrite(BLUE_LED_PIN, blueBrightness);
}
// ----- Beep Function (Piezo Buzzer) -----
/*
This function plays a beep when the gate's state changes. It plays a higher pitch beep
when the gate is triggered (entering), and a lower pitch when the gate is released (leaving).
*/
void beepOnGateStateChange() {
static bool lastGateState = false; // Track the last gate state
// If the gate state changed from triggered to untriggered or vice versa
if (gateTriggered != lastGateState) {
if (gateTriggered) {
// Beep when the gate is triggered (object enters)
tone(PIEZO_PIN, 1000, 200); // 1000Hz tone for 200ms
} else {
// Beep when the gate is released (object leaves)
tone(PIEZO_PIN, 500, 200); // 500Hz tone for 200ms
}
lastGateState = gateTriggered; // Update the last gate state
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment