#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_ADXL345_U.h>
#include <FastLED.h>
// ---------- LEDs ----------
#define NUM_LEDS 8
#define LED_PIN 5
CRGB leds[NUM_LEDS];
// ---------- Accelerometer ----------
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);
// ---------- Ultrasonic ----------
#define TRIG_PIN 8
#define ECHO_PIN 7
long lastValidDistance = -1;
// ---------- IO ----------
#define BUZZER 9
#define BUTTON 10 // keep wiring consistent with your hardware (INPUT used)
// ---------- Modes ----------
enum Mode { ORIENTATION_MODE, DISTANCE_MODE };
Mode currentMode = ORIENTATION_MODE;
// ---------- Button / hold progress ----------
bool pressActive = false;
unsigned long buttonPressStart = 0;
int lastSecondShown = 0;
bool modeToggledThisPress = false;
// ---------- Alarm (non-blocking) ----------
bool alarmActive = false;
unsigned long alarmLastToggle = 0;
bool alarmState = false;
const unsigned long ALARM_INTERVAL = 200; // ms
// ---------- Misc timing ----------
unsigned long lastDistanceUpdate = 0;
void setup() {
Serial.begin(9600);
pinMode(BUZZER, OUTPUT);
pinMode(BUTTON, INPUT); // keep as INPUT to match your wiring (if you use pull-up change to INPUT_PULLUP and invert logic)
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);
FastLED.clear();
FastLED.show();
if (!accel.begin()) {
Serial.println("ADXL345 not detected");
while (1);
}
accel.setRange(ADXL345_RANGE_16_G);
Serial.println("System Ready");
}
// main loop — button handler runs first so progress always updates promptly
void loop() {
handleButtonProgress();
// If the button is actively being held, the progress LEDs take precedence.
// We skip normal mode behavior while showing the progress.
if (pressActive) return;
if (currentMode == ORIENTATION_MODE) {
orientationMode();
} else {
distanceMode();
}
}
// ---------------- Button + progress logic ----------------
void handleButtonProgress() {
bool pressed = digitalRead(BUTTON);
if (pressed && !pressActive) {
// start a new hold
pressActive = true;
buttonPressStart = millis();
lastSecondShown = 0;
modeToggledThisPress = false;
// immediately show 0-> no leds until first second passes
FastLED.clear();
FastLED.show();
return;
}
if (!pressed && pressActive) {
// button released — clear progress display and reset
pressActive = false;
lastSecondShown = 0;
modeToggledThisPress = false;
FastLED.clear();
FastLED.show();
Serial.print("Button held for (ms): ");
Serial.println(millis() - buttonPressStart);
return;
}
if (pressed && pressActive) {
unsigned long elapsed = (millis() - buttonPressStart) / 1000; // whole seconds
if (elapsed > lastSecondShown && elapsed <= NUM_LEDS) {
// show progress for each newly reached second
lastSecondShown = elapsed;
showProgressLeds(elapsed);
Serial.print("Hold seconds: ");
Serial.println(elapsed);
}
// If we reached 8 seconds (NUM_LEDS) toggle mode once
if (elapsed >= NUM_LEDS && !modeToggledThisPress) {
// give a tiny audible confirmation
tone(BUZZER, 1200);
delay(120);
noTone(BUZZER);
toggleMode();
modeToggledThisPress = true;
}
}
}
void showProgressLeds(int seconds) {
// Lights LEDs 0..seconds-1 green
FastLED.clear();
for (int i = 0; i < seconds && i < NUM_LEDS; i++) {
leds[i] = CRGB::Green;
}
FastLED.show();
}
// ---------------- Mode switching ----------------
void toggleMode() {
// clear outputs and switch
noTone(BUZZER);
FastLED.clear();
FastLED.show();
currentMode = (currentMode == ORIENTATION_MODE) ? DISTANCE_MODE : ORIENTATION_MODE;
Serial.print("Switched mode to: ");
Serial.println(currentMode == ORIENTATION_MODE ? "ORIENTATION" : "DISTANCE");
// small debounce pause so the button release doesn't immediately retrigger things
delay(200);
}
// ---------------- Orientation mode (non-blocking alarm) ----------------
void orientationMode() {
sensors_event_t event;
accel.getEvent(&event);
float X = event.acceleration.x;
float Y = event.acceleration.y;
// tilt thresholds (same as your original)
if (abs(X) > 9 || abs(Y) > 8) {
alarmActive = true;
} else {
alarmActive = false;
// ensure outputs are off when not alarming
noTone(BUZZER);
FastLED.clear();
FastLED.show();
}
// Non-blocking blink/tone while alarmActive
if (alarmActive) {
unsigned long now = millis();
if (now - alarmLastToggle >= ALARM_INTERVAL) {
alarmLastToggle = now;
alarmState = !alarmState;
if (alarmState) {
tone(BUZZER, 900);
fill_solid(leds, NUM_LEDS, CRGB::Red);
FastLED.show();
} else {
noTone(BUZZER);
FastLED.clear();
FastLED.show();
}
}
}
}
// ---------------- Distance mode ----------------
void distanceMode() {
// Rate-limit distance updates
if (millis() - lastDistanceUpdate < 60) return;
lastDistanceUpdate = millis();
long distance = readDistanceCM();
// Ignore invalid readings
if (distance < 0 || distance > 80) return;
// Hysteresis to avoid jitter
if (lastValidDistance != -1 && abs(distance - lastValidDistance) < 2) return;
lastValidDistance = distance;
// ---- 1 LED per 10 cm ----
int ledsOn = distance / 10;
ledsOn = constrain(ledsOn, 0, NUM_LEDS);
FastLED.clear();
for (int i = 0; i < ledsOn; i++) {
leds[i] = CRGB::Red;
}
FastLED.show();
Serial.print("Distance: ");
Serial.print(distance);
Serial.print(" cm | LEDs: ");
Serial.println(ledsOn);
}
// ---------------- Ultrasonic helper ----------------
long readDistanceCM() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(5);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // 30 ms timeout
if (duration == 0) return -1; // NO ECHO
return duration * 0.034 / 2;
}
Comments