Help with this code- Movement or Motion sensor

Hi, I'm new Arduino and was trying to design this interesting alarm for my bike in storage. The design is relatively simple; the alarm is self contained, battery operated and uses a motion (vibration) sensor from Signal Quest (SQ-SEN-200). The sensor is pulled high (or low) with high value resistor and basically sends fast pulses to interrupt input of Arduino in deep sleep. After waking up, the Arduino carries out a noise rejection algorithm ( see the app note attached) and activates the siren if the algorithm satisfied that there is an actual movement. If not, goes back into the deep sleep.

There is initial exit delay of 60 seconds, accompanied by a blinking LED, so it woundn't sound the siren during this time.

I tested this code, but it seems the device is stuck on the initial exit delay and the LED never stops blinking, and never goes into the deep sleep. I asked Chat GPT, which supposed to be good with this stuff, it changed the code but the issue still exists. The issue might be in Void Set UP. I'd appreciate the help reviewing this code.

EDIT: This code has been corrected and working properly now.



#include "LowPower.h"

const int sensorPin = 2;        // Sensor connected to interrupt pin
const int alarmPin = 3;         // Alarm or output to trigger when motion is detected
const int ledPin = 4;           // External LED pin to indicate arming

volatile int pulseCount = 0;    // Variable to store pulse count
volatile bool sensorTriggered = false;  // Flag for sensor interrupt
unsigned long lastInterruptTime = 0; // Debounce control for interrupts
unsigned long wakeTime = 0;     // Time when device woke up

// Constants for counting and logic
const int incVal = 5;           // Value to increment Pulse_Count by
const int decVal = 1;           // Value to decrement Pulse_Count by
const int maxPulseCount = 50;   // Max value for pulse count to prevent overflow
const int threshold = 25;       // Threshold for detecting motion (pulse count)
const int decayInterval = 500;  // Time interval for pulse count decay (in ms)
const int debounceInterval = 200; // Time interval for the software one-shot (in ms)
const long startupDelay = 10000; // Startup delay in ms (1 minute)
const int alarmDuration = 5000; // How long alarm stays on (5 seconds)
const int stayAwakeTime = 2000; // Time to stay awake after waking up (2 seconds)

// Variables for startup delay and timing
unsigned long startupTime = 0;
unsigned long lastDecayTime = 0;

void setup() {
  pinMode(sensorPin, INPUT);    // Set sensor pin as input
  pinMode(alarmPin, OUTPUT);    // Set alarm pin as output
  pinMode(ledPin, OUTPUT);      // Set LED pin as output
  
  Serial.begin(9600);           // Start serial communication for debugging
  startupTime = millis();       // Capture the start time for the 1-minute delay
  
  digitalWrite(alarmPin, LOW);  // Ensure alarmPin is firmly LOW during startup delay
  digitalWrite(ledPin, LOW);    // Ensure LED is OFF initially
  
  Serial.println("Starting 60-second initialization delay...");
  
  // Startup delay: Skip alarm triggering for the first 1 minute
  while (millis() - startupTime < startupDelay) {
    // Blink the LED every second during startup delay
    digitalWrite(ledPin, HIGH);  // Turn on the LED
    delay(500);                  // Wait for 500ms
    digitalWrite(ledPin, LOW);   // Turn off the LED
    delay(500);                  // Wait for 500ms
    Serial.println("Startup delay active, alarm disabled.");
  }
  
  // After startup delay, attach interrupt and prepare for sleep
  digitalWrite(ledPin, LOW);    // Ensure LED is off
  digitalWrite(alarmPin, LOW);  // Ensure alarm is off
  
  attachInterrupt(digitalPinToInterrupt(sensorPin), sensorInterrupt, RISING);
  
  Serial.println("Startup complete, entering deep sleep mode.");
  Serial.flush();
  delay(100);
}

void loop() {
  // Check if sensor was triggered (woke from sleep)
  if (sensorTriggered) {
    sensorTriggered = false; // Reset flag
    wakeTime = millis();     // Record wake time
    lastDecayTime = millis(); // Reset decay timer
    
    Serial.print("Woke from sleep. Pulse count: ");
    Serial.println(pulseCount);
    Serial.println("Staying awake for decay monitoring...");
  }
  
  // Stay awake for the specified time to allow pulse decay
  while (millis() - wakeTime < stayAwakeTime) {
    // Apply pulse decay every decay interval
    if (millis() - lastDecayTime >= decayInterval) {
      if (pulseCount > 0) {
        pulseCount -= decVal;  // Decay pulse count
        if (pulseCount < 0) pulseCount = 0;  // Prevent negative values
        Serial.print("Pulse count decayed to: ");
        Serial.println(pulseCount);
      }
      lastDecayTime = millis();
    }
    
    // Check if alarm threshold is reached during wake period
    if (pulseCount >= threshold) {
      triggerAlarm();
      wakeTime = millis(); // Reset wake time after alarm
      break; // Exit the while loop after alarm
    }
    
        delay(50); // Small delay to prevent excessive loop cycling
  }
  
  // Reset pulse count to zero before going back to sleep
  pulseCount = 0;
  Serial.println("Pulse count reset to zero before sleep.");
  
  Serial.print("Final pulse count before sleep: ");
  Serial.println(pulseCount);
  Serial.println("Going back to deep sleep...");
  Serial.flush();
  delay(100);
  
  // Enter deep sleep mode
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
}

void sensorInterrupt() {
  // Debounce: Only allow one interrupt every 500ms (software one-shot)
  unsigned long currentTime = millis();
  if (currentTime - lastInterruptTime > debounceInterval) {
    lastInterruptTime = currentTime;
    
    // Only increment pulse count if it won't cause an overflow
    if (pulseCount < (maxPulseCount - incVal)) {
      pulseCount += incVal;  // Increment pulse count by the defined increment value
    }
    
    // Set flag to indicate sensor was triggered (device woke up)
    sensorTriggered = true;
  }
}

void triggerAlarm() {
  Serial.println("*** ALARM TRIGGERED! ***");
  
  // Turn on alarm and LED
  digitalWrite(alarmPin, HIGH);
  digitalWrite(ledPin, HIGH);
  
  // Keep alarm active for specified duration
  delay(alarmDuration);
  
  // Turn off alarm and LED
  digitalWrite(alarmPin, LOW);
  digitalWrite(ledPin, LOW);
  
  // Reset pulse count after alarm
  pulseCount = 0;
  
  Serial.println("Alarm completed. Pulse count reset.");

Digital Filter and Motion Estimation Algorithm (SQ-SEN-200, SQ-MIN-200).pdf (244.3 KB)

Schematic (not Fritzing)?

It is not, and if you aren't very familiar with C/C++, you will have a difficult time finding all the mistakes it makes.

CoPilot and Claude are somewhat better with Arduino, but none of them are very good with microcontroller coding.

Please edit your post to enclose the code in code tags (<code> editor button).

Schematic uploaded in the first post, V-Out would go to interrupt pin of Arduino. Thanks

Pin 2 is floating, the pull-up resistor needs to be on the Arduino side of the vibration sensor, its currents location essentially does nothing but drain the battery.

Not sure about the rest of the code yet, I don't have that sensor or library, but change const int startupDelay = 60000 to const long startupDelay = 60000. Or unsigned int startupDelay = 60000. Using just int is probably causing rollover problems with 60000.

This sensor is normally closed. I couldn't find the right symbol.

What is the exact model of Arduino you are using? Please post a link to the product page.

I ask because on Arduinos like the Uno R3 or classic Nano, where "int" is 16 bits, the line below is typical of the sort of mistakes that chatGPT makes. It will certainly not do what you want, because the maximum value of an "int" is 32767.

const int startupDelay = 60000;

True true, I totally forgot about that as I was using an old Nano and Uno that I had lying around. I took @scottcalv advice and change it to const long, now the LED has stopped after a 60 seconds. Is there any way to tell that it's gone into the deep sleep now? all on board leds seem to be on now, which doesn't make sense if it's gone into a deep sleep.

This sensor is a very simple device, a ball resting on 2 contacts which is closed normally. When you move it in any direction, it chatters i.e. opens and closes very fast, which sends the pulsed DC to the input of Arduino. Your solution worked by the way for stopping after 60 seconds, so many thanks.

Which LowPower library are you using? Are you sure you have set up the interrupt properly for that? Even if it is set up properly, after waking from sleep all you do is immediately go back into sleep mode.

"LowPower.h" is the one. I think it was in Mange Library section. Let me if there is better one please. You mentioned, it goes back into sleep right after, is it because it's in Void loop?

You need to understand, when the sensor is open(whether that is "activated" or "not activated"), the Arduino pin is floating, as nothing "pulls" it up or down; the resistor to VCC needs to be on the pin of the sensor that leads to the Arduino, so that the pin is either pulled up, or grounded. If you doubt this, you can test it by simply changing "INPUT" to "INPUT PULLUP" in your code, and it should then work.

The above diagram has been simplified, the actual implementation of sensor is like this, where V out goes to Arduino,

Yes, after waking from sleep, the code continues with the next line after the call to LowPower.powerDown(), so loop() just repeats and goes back into power down mode. I can't see that you are doing anything useful in the interrupt handler either.

OK thanks, so if I move the lowpower command to void set up as the last line, would it fix it? I'd like to know your solutions for this when you get a chance please.

@DDT12 you are welcome. FYI, you could use this while (millis() < startupDelay) instead of while (millis() - startupTime < startupDelay).

I am not familiar with your library.

Maybe those more experienced will know, but if it needs to go into deep sleep immediately after bootup, why not put it to sleep at the end of the setup() instead of constantly looping it? What happens if the interrupt wakes it? The next loop cycle puts it to sleep again. This might happen so fast that you would never even see a result from waking.

The problem at the moment is that you do not have the code doing anything after it wakes from sleep. The sensorInterrupt() interrupt handler increments pulseCount, but you never do anything with that.

Did you see any of the example codes in the library?

// **** INCLUDES *****
#include "LowPower.h"

// Use pin 2 as wake up pin
const int wakeUpPin = 2;

void wakeUp()
{
// Just a handler for the pin interrupt.
}

void setup()
{
// Configure wake up pin as input.
// This will consumes few uA of current.
pinMode(wakeUpPin, INPUT);
}

void loop()
{
// Allow wake up pin to trigger interrupt on low.
attachInterrupt(0, wakeUp, LOW);

// Enter power down state with ADC and BOD module disabled.
// Wake up when wake up pin is low.
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); 

// Disable external pin interrupt on wake up pin.
detachInterrupt(0); 

// Do something here
// Example: Read sensor, data logging, data transmission.

}

I see what you mean, it's like no command after pulse count is done to activated the siren. It was there before, but got deleted by ChatGPT it seems.