SW-420 vibration and HC-SR501 PIR sensor together

Desired Behavior

Logging detection sensor system where:

  • LED is turned on after:
    • 3 instances of simultaneous motion + vibration activity
    • Sustained vibration activity for 7 seconds or longer
  • Activity counters are reset for each of the two thresholds after 10 minutes without stimuli
  • 1 hr cooldown following LED activation

Issues

  • Doesn't detect motion + vibration activity consistently enough
  • Might be hardware:
    • Vibration sensor spam reading HIGH when not touched at all
    • Even after the activity has ended (during which it has already read HIGH then LOW), vibration sensor reads HIGH again—predictably doesn't when potentiometer is dialed up

Code

// Pin Definitions
const int ledPin = 12;
const int vibPin = 7;
const int pirPin = 2;

// Variable Declarations
int vibVal = 0;
int pirState = 0;
int pirVal = 0;
unsigned long lastOutputTime = 0;
unsigned long lastVibActivityTime = 0;
unsigned long lastDualActivityTime = 0;
int vibActivityCount = 0;
int dualActivityCount = 0;

// Time Constants
const unsigned long activityWindow = 600000;
const unsigned long outputCooldown = 3600000;
bool outputOnCooldown = false;

void setup() {
  // Initialize pin modes
  pinMode(ledPin, OUTPUT);
  pinMode(pirPin, INPUT);
  pinMode(vibPin, INPUT);
  
  // Initialize serial communication
  Serial.begin(9600);
}

void output() {
  // Output sequence
  unsigned long currentTime = millis();
  Serial.println("Output activated. Cooldown started.");
  digitalWrite(ledPin, HIGH);
  delay(5000);
  digitalWrite(ledPin, LOW);
  dualActivityCount = 0;
  vibActivityCount = 0;
  lastOutputTime = currentTime;
  outputOnCooldown = true;
}

void loop() {
  // Read sensor values
  pirVal = digitalRead(pirPin);
  delay(700); // Delay to account for swing motion during logging
  vibVal = digitalRead(vibPin);

  // Print debugging
  Serial.print("pir");
  Serial.println(pirVal);
  Serial.print("vib");
  Serial.println(vibVal);

  // Get current time
  unsigned long currentTime = millis();

  // Check idleness
  if (currentTime - lastDualActivityTime > activityWindow) {
    dualActivityCount = 0;
  }
  if (currentTime - lastVibActivityTime > activityWindow) {
    vibActivityCount = 0;
  }

  // Check if vibration detected
  if (vibVal == 1) {

    if (!outputOnCooldown && currentTime - lastVibActivityTime < activityWindow) {
      Serial.println("Vibration detected!");

      // Update vibration activity count
      vibActivityCount++;
      Serial.print("Vibration activity count within last 10 minutes: ");
      Serial.println(vibActivityCount);
      lastVibActivityTime = currentTime;

      // Activate LED output if sustained vibration activity detected
      if (vibActivityCount >= 7) {
        output();
      }
    }

    // Check if motion (and vibration) activity detected
    if (pirVal == 1) {
      if (!outputOnCooldown && currentTime - lastDualActivityTime < activityWindow) {
        Serial.println("Dual activity detected!");

        // Update activity count
        dualActivityCount++;
        Serial.print("Dual activity count within last 10 minutes: ");
        Serial.println(dualActivityCount);
        lastDualActivityTime = currentTime;

        // Activate LED output if dual activity count threshold reached
        if (dualActivityCount >= 3) {
          output();
        }
      }
    }
  }
  delay(300); // Delay to limit false positives due to narrow detection timeframe

  // Check if cooldown period ended
  if (outputOnCooldown && currentTime - lastOutputTime >= outputCooldown) {
    outputOnCooldown = false;
    Serial.println("Output cooldown ended.");
  }
}

You are probably having "no motion detect" and other issues do to your use of "delay(x)"... this command makes the processor do noting, causing vibration or motion not to be detected.

You need to read about timed events. If you search "YouTube, Programming Electronics Academy, timed events" you will find very useful videos.

Here is a link to a simulation making many timed events, each event with different regularity. I imagine this working with your project by using a timed event to look at your PIR, then look at your vibration sensor. You will choose how often to check your pins for vibration or motion.

Files for WOKWI

sketch.ino
// Blink LEDs and serial print at these specific intervals

// *** SEE welcome() FUNCTION FOR TIMING CHART

byte ledPin[] = {2, 4, 6, 8, 10}; // signal pins for the LEDs
#define EVENTS sizeof(ledPin)/sizeof(ledPin[0]) // find the size of the pin array

unsigned long interval[] = {300, 700, 900, 2100, 3150}; // event intervals
unsigned long oldTime[] = {0, 0, 0, 0, 0}; // clear event timers (start at zero time)
int counter;

void setup() {
  Serial.begin(9600); // for the serial monitor
  welcome(); // call the "welcome" screen with the timing diagram
}

void loop() {
  for (int i = 0; i < EVENTS; i++) { // cycle through all events array
    if (millis() - oldTime[i] >= interval[i]) { // compare intervals to time difference
      oldTime[i] = millis(); // event interval 'i' has occurred, set new event i zero time

      // The interval[] for each event (ledPin[]) has been tested
      // now call the function you for that interval[]

      blinkLED (i); // blink the 'i' LED
      if (counter > 79) {
        counter = 0;
        Serial.println();
        Serial.print("interval[#] ---> ");
      }
      counter++;
      // // Serial.print(ledPin[i]); // print the event pin number
      Serial.print(i); // print the single event array number
    }
  }
}

void blinkLED (int thisLED) {
  digitalWrite(ledPin[thisLED], HIGH);
  delay(10);
  digitalWrite(ledPin[thisLED], LOW);
}

void welcome() {
  Serial.println("Each character on this timing graph represents 100ms.");
  Serial.println("interval[0] RED LED every  300ms |--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*");
  Serial.println("interval[1] ORG LED every  700ms |------*------*------*------*------*------*------*------*------*");
  Serial.println("interval[2] YEL LED every  900ms |--------*--------*--------*--------*--------*--------*--------*");
  Serial.println("interval[3] GRN LED every 2100ms |--------------------*--------------------*--------------------*");
  Serial.println("interval[4] BLU LED every 3150ms |-------------------------------*------------------------------*");
  Serial.print("interval[#] ---> ");
}
diagram.json
{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-nano", "id": "nano", "top": 4.8, "left": -0.5, "attrs": {} },
    { "type": "wokwi-led", "id": "led1", "top": -51.6, "left": 90.2, "attrs": { "color": "red" } },
    {
      "type": "wokwi-led",
      "id": "led2",
      "top": -51.6,
      "left": 71,
      "attrs": { "color": "orange" }
    },
    {
      "type": "wokwi-led",
      "id": "led3",
      "top": -51.6,
      "left": 51.8,
      "attrs": { "color": "yellow" }
    },
    {
      "type": "wokwi-led",
      "id": "led4",
      "top": -51.6,
      "left": 32.6,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-led",
      "id": "led6",
      "top": -51.6,
      "left": 13.4,
      "attrs": { "color": "blue" }
    },
    {
      "type": "wokwi-led",
      "id": "led5",
      "top": 11.2,
      "left": 61.8,
      "rotate": 180,
      "attrs": { "color": "white" }
    }
  ],
  "connections": [
    [ "led1:A", "nano:2", "red", [ "v0" ] ],
    [ "nano:GND.2", "led1:C", "black", [ "v-9.6", "h-19.2" ] ],
    [ "nano:4", "led2:A", "gold", [ "v0" ] ],
    [ "nano:6", "led3:A", "yellow", [ "v0" ] ],
    [ "nano:8", "led4:A", "green", [ "v0" ] ],
    [ "led6:A", "nano:10", "blue", [ "v0" ] ],
    [ "nano:GND.2", "led6:C", "black", [ "v-9.6", "h-96" ] ],
    [ "nano:GND.2", "led2:C", "black", [ "v-9.6", "h-38.4" ] ],
    [ "nano:GND.2", "led3:C", "black", [ "v-9.6", "h-57.6" ] ],
    [ "nano:GND.2", "led4:C", "black", [ "v-9.6", "h-76.8" ] ],
    [ "nano:GND.2", "led5:C", "black", [ "v-9.6", "h-57.6" ] ],
    [ "nano:11", "led5:A", "white", [ "v0" ] ]
  ],
  "dependencies": {}
}

Thanks! I've included the revised program below. It's more responsive and efficient.

Question: How do you think I can set this up so that there is a 700ms interval between reading PIR and vibration input?

I'm trying to avoid delay(700) here, too, per your advice. The timing for both PIR and vibration is still 1000ms, except the vibration read should lag by 700ms.

Like in my original program, I need it to account for the time difference between the start of motion and vibration-causing impact.

/* Pin definitions */
int PIR = 2;
int VIB = 7;
int LED = 12;

/* Sensor state and counter variables */
int pirState = 0;
int vibState = 0;
int bothActivityCount = 0;
int vibActivityCount = 0;

/* Time variables in ms */
const unsigned long READ_TIMING = 1000;
const unsigned long BOTH_EVENT_WINDOW = 600000;
const unsigned long VIB_EVENT_WINDOW = 15000;
const unsigned long OUTPUT_CD = 3000; // 3600000, lowered for debugging
unsigned long previousReadTime = 0;
unsigned long previousBothEventTime = 0;
unsigned long previousVibEventTime = 0;

void setup() {
  pinMode(PIR, INPUT);
  pinMode(VIB, INPUT);
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

/* Output sequence */
void output() {
  digitalWrite(LED, HIGH);
  delay(3000);
  digitalWrite(LED, LOW);

  Serial.println("Cooldown started");
  delay(OUTPUT_CD); // Processor does nothing for OUTPUT_CD ms
  Serial.println("Cooldown ended");
}

void loop() {
  unsigned long currentTime = millis();

  /* Check if within window */
  if ( currentTime - previousBothEventTime >= BOTH_EVENT_WINDOW ) {
    bothActivityCount = 0;
    previousBothEventTime = currentTime;
  }
  if ( currentTime - previousVibEventTime >= VIB_EVENT_WINDOW ) {
    vibActivityCount = 0;
    previousVibEventTime = currentTime;
  }
  
  /* Check for timing */
  if ( currentTime - previousReadTime >= READ_TIMING ) {
    pirState = digitalRead(PIR);
    vibState = digitalRead(VIB);
    previousReadTime = currentTime;

    /* Output condition 1 */
    if ( pirState == 1 && vibState == 1 ) {
      bothActivityCount++;
      Serial.println("Count: Motion + vibration detected");
      previousBothEventTime = currentTime;

      if ( bothActivityCount == 3 ) {
        Serial.println("Output: Frequent motion + vibration detected");
        output();
      }
    }

    /* Output condition 2 */
    if ( pirState != 1 && vibState == 1 ) {
      vibActivityCount++;
      Serial.println("Count: Vibration detected");
      previousVibEventTime = currentTime;

      if ( vibActivityCount == 7 ) {
        Serial.println("Output: Sustained vibration detected");
        output();
      }
    }
  }
}

When the first-event happens, start a new timer, and when this new timer becomes 700, start the second-event timer.

What do you think of this solution? And, does this mean I have to change READ_TIMING from 1000 to 300 to account for the 700ms delay?

    pirState = digitalRead(PIR);
    while ( millis() - currentTime <= 700 ) {
    }
    vibState = digitalRead(VIB);

Do not use "do nothing/blocking" style. Try...

if (millis() - currentTime > 700)
  vibState = digitalRead(VIB);

This works well. Thanks a bunch!

/* Pin definitions */
int PIR = 2;
int VIB = 7;
int LED = 12;

/* Sensor state and counter variables */
int pirState = 0;
int vibState = 0;
int bothActivityCount = 0;
int vibActivityCount = 0;

/* Time variables in ms */
const unsigned long PIR_READ_TIMING = 1000;
const unsigned long VIB_READ_TIMING = 1000;
const unsigned long BOTH_EVENT_WINDOW = 600000;
const unsigned long VIB_EVENT_WINDOW = 40000;
const unsigned long OUTPUT_CD = 3000; // 3600000, lowered for debugging
unsigned long previousPirReadTime = 0;
unsigned long previousVibReadTime = 0;
unsigned long previousBothEventTime = 0;
unsigned long previousVibEventTime = 0;

void setup() {
  pinMode(PIR, INPUT);
  pinMode(VIB, INPUT);
  pinMode(LED, OUTPUT);
  Serial.begin(9600);

  previousVibReadTime = millis() - (PIR_READ_TIMING - 700); // Initialize reading lag
}

/* Output sequence */
void output() {
  bothActivityCount = 0;
  vibActivityCount = 0;
  
  digitalWrite(LED, HIGH);
  delay(3000);
  digitalWrite(LED, LOW);

  Serial.println("Cooldown started");
  delay(OUTPUT_CD); // Processor does nothing for OUTPUT_CD ms
  Serial.println("Cooldown ended");

  /* Re-establish reading lag */
  previousPirReadTime = millis();
  previousVibReadTime = previousPirReadTime - (PIR_READ_TIMING - 700);
}

void loop() {
  unsigned long currentTime = millis();

  /* Check if within window */
  if (currentTime - previousBothEventTime >= BOTH_EVENT_WINDOW) {
    bothActivityCount = 0;
    previousBothEventTime = currentTime;
  }
  if (currentTime - previousVibEventTime >= VIB_EVENT_WINDOW) {
    vibActivityCount = 0;
    previousVibEventTime = currentTime;
  }
  
  /* Check for PIR timing */
  if (currentTime - previousPirReadTime >= PIR_READ_TIMING) {
    pirState = digitalRead(PIR);
    previousPirReadTime = currentTime;
  }

  /* Check for VIB timing */
  if (currentTime - previousVibReadTime >= VIB_READ_TIMING) {
    vibState = digitalRead(VIB);
    previousVibReadTime = currentTime;
    Serial.println(previousVibReadTime - previousPirReadTime);
  }

  /* Output condition 1 */
  if (pirState == 1 && vibState == 1) {
    bothActivityCount++;
    Serial.println("Count: Motion + vibration detected");
    previousBothEventTime = currentTime;
    pirState = 0;

    if (bothActivityCount == 5) {
      Serial.println("Output: Frequent motion + vibration detected");
      output();
    }
  }

  /* Output condition 2 */
  if (vibState == 1) {
    vibActivityCount++;
    Serial.println("Count: Vibration detected");
    previousVibEventTime = currentTime;
    vibState = 0;

    if (vibActivityCount == 12) {
      Serial.println("Output: Sustained vibration detected");
      output();
    }
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.