I'm trying to make a box that revolves around a pivot point using a NEMA17 stepper motor. The idea is straightforward: US1881 Hall effect sensor will change state based on magnets on the base of the box acting as an end stop, when the PIR sensor detects movement the stepper should move until the status of the Hall effect sensor changes, then wait 10 seconds before accepting any new movement.
I tried multiple times but something doesn't work. either the stepper won't move, or the trigger is not working properly, I assume there is an issue in the logic but I can't quite get it, and last attempt basically bricked the Arduino since RX and TX are always active.
This is the latest iteration of my code:
#include <AccelStepper.h>
// Define pins
#define HALL_SENSOR_PIN 2
#define PIR_SENSOR_PIN 3
#define STEP_PIN 4
#define DIR_PIN 5
// Create stepper object
AccelStepper stepper(1, STEP_PIN, DIR_PIN); // 1 is the half-step mode
// Define variables
volatile bool hallTriggered = false; // Interrupt flag
bool pirTriggered = false;
unsigned long pirStartTime = 0;
int motorState = 0; // 0: stopped, 1: moving forward, -1: moving backward
int cycleCount = 0;
bool initialMoveComplete = false; // Flag for initial movement
bool hallPreviousState; // Declare hallPreviousState globally
void setup() {
pinMode(HALL_SENSOR_PIN, INPUT_PULLUP);
pinMode(PIR_SENSOR_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(HALL_SENSOR_PIN), hallInterrupt, CHANGE); // Interrupt on any state change
stepper.setMaxSpeed(1000); // Adjust as needed
stepper.setAcceleration(100); // Adjust as needed
// Initial movement: Move until Hall sensor state changes
hallPreviousState = digitalRead(HALL_SENSOR_PIN); // Initialize previous state
while (digitalRead(HALL_SENSOR_PIN) == hallPreviousState) {
stepper.run();
}
stepper.stop();
motorState = 0;
}
void loop() {
handlePIRTrigger();
if (hallTriggered) {
handleHallTrigger();
hallTriggered = false; // Reset the interrupt flag
}
stepper.run();
}
void hallInterrupt() {
hallTriggered = true;
}
void handlePIRTrigger() {
if (digitalRead(PIR_SENSOR_PIN) == HIGH &&!pirTriggered && millis() - pirStartTime > 10000) {
pirTriggered = true;
if (motorState == 0) { // Only start moving if currently stopped
motorState = 1;
stepper.run(); // Start motor continuously
}
}
}
void handleHallTrigger() {
if (motorState!= 0) { // If moving, stop immediately
stepper.stop();
motorState = 0;
// Check for 4 cycles and reverse
cycleCount++;
if (cycleCount == 4) {
cycleCount = 0;
stepper.move(-stepper.currentPosition()); // Reverse direction using move()
}
pirTriggered = false; // Reset PIR trigger
pirStartTime = millis(); // Start 10-second delay after Hall trigger
}
}
The sensors are working correctly, I tested them in isolation, but they don't seem to play nicely together or I'm missing something.
This is how everything is connected:
-
Test the stepper motor separately. Maybe it is missing adequate power supply.liaifat85– liaifat852025年02月12日 17:46:41 +00:00Commented Feb 12 at 17:46
-
@liaifat85: Thanks for your input. It's powered by an external 12v 5A power supply, I don't think it's the stepper. I tested every part of the circuit independently, in separation they work. It's when you tie them together that they don't work as intended.MDChaara– MDChaara2025年02月12日 18:57:18 +00:00Commented Feb 12 at 18:57
-
I see. Since parts are alright, I think you need to edit your code.liaifat85– liaifat852025年02月13日 14:15:41 +00:00Commented Feb 13 at 14:15
-
@liaifat85: Any suggestions?MDChaara– MDChaara2025年02月13日 19:32:15 +00:00Commented Feb 13 at 19:32
2 Answers 2
Is the circuit diagram you provided an exact one? As far as I can see, it looks like the power line connections are missing. The HC-SR501 and US1881 have their power and GND connected to each other, but they are not connected to the Arduino Nano.
My fundamental advice is to first confirm that each sensor can be read properly and that the motor can be driven individually. After that, you should move on to considering the software logic. If you try to do everything at the same time, it will make troubleshooting much harder.
Edited
By your comment, I understand that the power is correctly wired and each element works individually. Now, let's check the logic.
Are you familiar with the AccelStepper library? stepper.run()
is not for setting step count or position but acts as a scheduler, executing movement set by move()
or moveTo()
.
In setup()
, before calling stepper.run()
, shouldn't you first specify movement with move()
or moveTo()
? Also, since loop()
already calls stepper.run()
every iteration, other functions don’t need to call it. In handlePIRTrigger()
, what you likely need is move()
or moveTo()
, not run()
.
To summarize, stepper.run()
should be called only in loop()
as frequently as possible, while other functions should use move()
, moveTo()
, or stop()
.
Hope this helps!
-
Hi @Atsusui! They are connected to the 5V and GND of the arduino. I did test each individual element separately. They do work. The issue is when they are all placed together from what I see. Thanks for your input!MDChaara– MDChaara2025年02月12日 11:30:37 +00:00Commented Feb 12 at 11:30
-
My answer is getting long, so I’ll edit the original post. Please check it again.Atsushi Yokoyama– Atsushi Yokoyama2025年02月12日 12:26:00 +00:00Commented Feb 12 at 12:26
-
I wish I saw the long version! This definitely helps. I'll try some modifications and get back to you.MDChaara– MDChaara2025年02月12日 12:58:57 +00:00Commented Feb 12 at 12:58
Note: This answer is and update with the code I wrote to get the result I'm seeking
With thanks to Atsushi Yokoyama, I had to rethink the whole logic of the sketch. I've rewritten the code realizing that I don't need to check the step count which meant that using AccelStepper is not necessary to accomplish this task.
The following code does exactly what I need, and I'm posting it in case someone needs it down the line:
// Pin Definitions
const int hallSensorPin = 2; // Hall effect sensor
const int pirSensorPin = 3; // PIR sensor
const int dirPin = 4; // Direction pin for A4988
const int stepPin = 5; // Step pin for A4988
// Variables
bool motorRunning = false; // Tracks if the motor is running
bool coolDownActive = false; // Tracks if the cool-down period is active
unsigned long coolDownStart = 0; // Tracks when the cool-down period started
unsigned long motorStartTime = 0; // Tracks when the motor started
int movementCount = 0; // Tracks the number of movements
bool directionForward = true; // Tracks the motor direction
bool checkForHigh = true; // Alternates between HIGH and LOW detection
void setup() {
// Pin Modes
pinMode(hallSensorPin, INPUT);
pinMode(pirSensorPin, INPUT);
pinMode(dirPin, OUTPUT);
pinMode(stepPin, OUTPUT);
pinMode(enablePin, OUTPUT);
// Enable the motor driver
digitalWrite(enablePin, LOW);
// Initialize Serial for debugging
Serial.begin(9600);
Serial.println("System Initialized. Waiting for PIR trigger...");
}
void loop() {
// Check if the motor is running
if (motorRunning) {
// Move the motor : Change the delay to control speed
digitalWrite(stepPin, HIGH);
delayMicroseconds(2500);
digitalWrite(stepPin, LOW);
delayMicroseconds(2500);
// Check if the Hall effect sensor is triggered (end stop)
// Only check after 1 second of motor movement
if (millis() - motorStartTime >= 1000) {
if ((checkForHigh && digitalRead(hallSensorPin) == HIGH) ||
(!checkForHigh && digitalRead(hallSensorPin) == LOW)) {
motorRunning = false; // Stop the motor
movementCount++; // Increment movement count
coolDownActive = true; // Start cool-down period
coolDownStart = millis();
Serial.println("Hall effect sensor triggered. Motor stopped.");
Serial.print("Movement count: ");
Serial.println(movementCount);
// Reverse direction every 4 movements
if (movementCount % 4 == 0) {
directionForward = !directionForward;
digitalWrite(dirPin, directionForward ? HIGH : LOW);
Serial.println("Direction reversed.");
}
// Alternate between HIGH and LOW detection
checkForHigh = !checkForHigh;
Serial.print("Next Hall effect detection: ");
Serial.println(checkForHigh ? "HIGH" : "LOW");
Serial.println("Starting 10-second cool-down period...");
}
}
} else {
// Check if the cool-down period is active
if (coolDownActive) {
if (millis() - coolDownStart >= 10000) { // 10-second cool-down
coolDownActive = false;
Serial.println("Cool-down period ended. Ready for new PIR trigger.");
}
} else {
// Check if the PIR sensor is triggered
if (digitalRead(pirSensorPin) == HIGH) {
motorRunning = true; // Start the motor
motorStartTime = millis(); // Record the motor start time
Serial.println("PIR sensor triggered. Motor started.");
}
}
}
}
Thanks!
Explore related questions
See similar questions with these tags.