Code help for a project to raise a door with a motor

I have a project where I want a door to be raised up into the wall to create a entrance for my kid, the idea is that when they press a button (like an elevator) that the door will rise to a pre determined height reading that they Ultrasonic sensor detects and then stop the motor. and when the other button is pressed, the door will go down. I have been able to test the wiring and I have figured out the hardware side of the project but the coding side is still giving me issues.

The end goal is that the door should raise to a certain distance from the US sensor and stop but I also want to put LED's on the side that are red when the door is closed but as the door raises up the LED's turn from red to white.

My current code I have not been able to control the LED's the way I want, I found some code to have the strip one color and then as the distance on the US is reduced, the pixels go out, I like that but I want the strip to be red at max distance and then to change to white as the distance is reduced.

The other issue that I am having is the motor right now will just turn when the US sensor is detecting anything more than 76 cm, also without a button being pressed. The other thing that I believe is an issue is that I would like the motor to execute the action with only a button press and not have to hold the button.

This is the code that I have so far. Any help would be appreciated.

#include <Adafruit_NeoPixel.h>
#include <HCSR04.h>

#define LED_COUNT 50

// Define button pins
int button_1 = 8;
int button_2 = 9;

// Define output pins
int motor_ccw = 2;
int motor_cw = 3;

// Define LED pins
#define LED_1 5
#define LED_2 6


Adafruit_NeoPixel strip1(LED_COUNT, LED_1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2(LED_COUNT, LED_2, NEO_GRB + NEO_KHZ800);

// Define the pin for the ultrasonic sensor
#define TRIGGER_PIN 10
#define ECHO_PIN 11

// Define the maximum distance for the color change
#define MAX_DISTANCE 76.2 // in centimeters


// Initialize variables for distance and color
int distance = 0;
int red = 0;
int green = 0;
int blue = 0;

void setup() {
// Initialize serial connection to display values
Serial.begin(9600);

// Initialize the NeoPixel strip
strip1.begin();


// Initialize the NeoPixel strip
strip2.begin();


// Set button pins as input
pinMode(button_1, INPUT_PULLUP);
pinMode(button_2, INPUT_PULLUP);

// Set the trigger and echo pins for the ultrasonic sensor
pinMode(TRIGGER_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);

// Set output pins as output
pinMode(motor_ccw, OUTPUT);
pinMode(motor_cw, OUTPUT);
pinMode(LED_1, OUTPUT);
pinMode(LED_2, OUTPUT);


}
// Main loop
void loop() {

  // Send a pulse to the ultrasonic sensor
  digitalWrite(TRIGGER_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIGGER_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIGGER_PIN, LOW);

  // Read the duration of the pulse from the echo pin
  long duration = pulseIn(ECHO_PIN, HIGH); 

  // Calculate the distance in centimeters
  distance = duration * 0.034 / 2;

 strip1.clear();

  for(int i=0; i<distance; i++)                      // For each pixel in strip...
  { 
    strip1.setPixelColor(i, 0,0,255); 
  }
  
  strip1.show();                                  // Send the updated pixel colors to the hardware.
  
    // Print the distance and color values for debugging
  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.print(" cm, Color: ");
  Serial.print(red);
  Serial.print(", ");
  Serial.print(green);
  Serial.print(", ");
  Serial.println(blue);

  // Delay for smoother color change
  delay(100);

  // Map the distance to a value between 0 and 255 for the color change
  red = map(distance, 0, MAX_DISTANCE, 0, 255);

  //make sure motor is off
  digitalWrite(motor_ccw, LOW);
  digitalWrite(motor_cw, LOW);

  // Read button states
  int button1State = digitalRead(button_1);
  int button2State = digitalRead(button_2);

  // If button 1 is pressed, activate output 1 for 2 seconds
  if (button1State == LOW) {
    if (distance < 77.2) { //if distance is less than 76.2 cm
    digitalWrite(motor_ccw, HIGH); //turn on motor
  } else { //if distance is less than or equal to 76.2 cm
    digitalWrite(motor_ccw, LOW); //turn off motor
  }
  }

  // If button 2 is pressed, activate output 2 for 2 seconds
  if (button2State == LOW) {
     if (distance > 1) { //if distance is greater than 1 cm
    digitalWrite(motor_cw, HIGH); //turn on motor
  } else { //if distance is greater than or equal to 1 cm
    digitalWrite(motor_cw, LOW); //turn off motor
  }
  }
}

I suggest declaring that as "float distance" since you are using it as a float.

Also, it would be much better to structure your code as a state machine.

Did some research and, I agree, a state machine looks like it would help with that.

Your button is probably wired differently than your code expects.

Where did you find this code?

I ask also. I am at a disadvantage, but through the tiny window the code looks incoherent. The comments do not match the code, and the code doesn't look like it comes close to @littlehawn1's goals.

I don't see anything doing the "activate output 1 for 2 seconds", for example.

@littlehawn1, please start by fixing the comments so they make any sense.

a7

1 Like

In your for-loop, the variable i increases until it is the same as the variable distance. That variable could get as high as 77 or 78 or higher? But your strip has only 50 LEDs. So once distance reaches 50, the strip will be fully lit. Then the code will try to set the colour of LEDs 51, 52 etc. But those LEDs don't exist. With the Adafruit library, it will simply ignore those instructions.

So I think you need to calculate how many of the 50 LEDs you want lit depending on the distance. You are already using the ideal function for this. You use that function to calculate the value of variable red but then don't do anything with it.

edited the main post with updated code.

I rewrote the whole code after watching a tutorial on state machines. Hopefully it makes more sense.

Looking better :slight_smile:

That should be

if (digitalRead(downButton) == LOW && (distanceIN <= 30) ) {

and similar for the other 3 occurrences.

Your switch statement is missing the state STOPRAISEDOOR !

Doing that now makes every comment from #2 to now incoherent. Leave all code as it was, and post updates in new posts.

What @xfpd said… you basically made trash of the thread. For the future… don't do that.

Maybe the code makes sense, maybe not.

Is everything else you said still the case? Or is that what was wrong with the original?

Say right now where you got that code. If you wrote it, you can fix it.

a7

there no need to have separate STOPLOWERDOOR, STOPRAISEDOOR and IDLE states

state machines are written such that each state monitors for events which require some action to be take and a change of state which then monitors for other events

look the following over.

  • i simulated your distance sensor with a simple pot
  • added prints when events occur and the distance
0
0
Door Down

---- after adjusting distance pot and reset  ------
4
3
Door partially open
Raise Door
3
4
3
10
20
31
Door Up
Lower Door
37
37
37
37
37
28
14
4
0
Door Down

modified LED pins and corrected LED logic

const byte PinMotorUp   = 12;
const byte PinMotorDown = 11;

const byte PinLedUp     = 13;
const byte PinLedDown   = 10;

const byte PinBut       = A3;

enum { LedOff   = HIGH, LedOn   = LOW };
enum { MotorOff = HIGH, MotorOn = LOW };

enum { DoorDown, DoorUp };
int doorPos = DoorDown;

enum { S_Idle, S_Raise, S_Lower };
int state = S_Idle;

const int DistanceUp   = 30;
const int DistanceDown =  0;

// -----------------------------------------------------------------------------
int
getDistance ()
{
    int dist = analogRead (A0);
    Serial.println (dist);
    delay  (1000);
    return dist;
}

// -----------------------------------------------------------------------------
void
loop (void)
{
    switch (state)  {
    case S_Idle:
        digitalWrite  (PinMotorDown, MotorOff);
        digitalWrite  (PinMotorUp,   MotorOff);

        if (LOW == digitalRead (PinBut))  {
            if (DoorDown == doorPos)  {
                state = S_Raise;
                digitalWrite   (PinMotorUp, MotorOn);
                digitalWrite   (PinLedDown,  LedOff);
                Serial.println ("Raise Door");
            }
            else {
                state = S_Lower;
                digitalWrite   (PinMotorDown, MotorOn);
                digitalWrite   (PinLedUp,    LedOff);
                Serial.println ("Lower Door");
            }
        }

        break;

    case S_Raise:
        if (DistanceUp <= getDistance ())  {
            state = S_Idle;
            doorPos = DoorUp;
            digitalWrite (PinLedUp, LedOn);
            Serial.println ("Door Up");
        }
        break;

    case S_Lower:
        if (DistanceDown >= getDistance ())  {
            state = S_Idle;
            doorPos = DoorDown;
            digitalWrite (PinLedDown, LedOn);
            Serial.println ("Door Down");
        }
        break;
    }
}

// -----------------------------------------------------------------------------
void
setup (void)
{
    Serial.begin (9600);

    digitalWrite  (PinMotorUp,   MotorOff);
    digitalWrite  (PinMotorDown, MotorOff);
    pinMode       (PinMotorUp,   OUTPUT);
    pinMode       (PinMotorDown, OUTPUT);

    digitalWrite  (PinLedUp,     LedOff);
    digitalWrite  (PinLedDown,   LedOff);
    pinMode (PinLedUp,           OUTPUT);
    pinMode (PinLedDown,         OUTPUT);

    if (DistanceDown >= getDistance ())  {
        digitalWrite  (PinLedDown,   LedOn);
        doorPos = DoorDown;
        Serial.println ("Door Down");
    }
    else if (DistanceUp <= getDistance ())  {
        digitalWrite  (PinLedUp,     LedOn);
        doorPos = DoorUp;
        Serial.println ("Door Up");
    }
    else
        Serial.println ("Door partially open");
}

intelligent people ask questions

Shouldn't it be

        if (getDistance () > DistanceUp)  {
            state = S_Idle;
            doorPos = DoorUp;
            digitalWrite (PinLedUp, LedOn);
            Serial.println ("Door Up");
        }
        break;

    case S_Lower:
        if (getDistance() < DistanceDown)  {

?

a7

don't understand '<=' vs '>?

I wrote the code, the sources I used to write it are a variety of youtube videos and other forums along with libraries. For the state machine part, I used this github example to base my code off of: BB6-Arduino-State-Machine-Part-2/State Machine Sketch/StateMachineDemo.ino at main · RalphBacon/BB6-Arduino-State-Machine-Part-2 · GitHub
I apologize for reworking the first post. At this point I have reworked the original code so much that the first post code would be unrecognizable to what I currently have. If I make future projects and need assistance I will be sure to not do that again.


I have just finished the current iteration of the code I am working on, hopefully the notes make sense here. @gcjr I just read your comment and I looked over the code you posted but I don't quite understand it. I am going to do some more research to understand how to use enum like you are to see if I can make my code better. I can see that your example is a lot cleaner than mine is.

Currently this is what I have for the code after my edits

#include <FastLED.h>
#include <HCSR04.h>

// how many LEDS on the strip there will be on each side of the door and other LED programming 
#define NUM_LEDS_PER_STRIP 50
#define COLOR_ORDER GRB 
#define CHIPSET WS2812B
#define BRIGHTNESS 60
#define VOLTS 5 
#define MAX_AMPS 500 

CRGB leds[NUM_LEDS_PER_STRIP];

// Define motor output pins
#define motor_ccw 2
#define motor_cw 3

// Define LED strip pins
#define LED_1 5
#define LED_2 6

// Define push button pins
#define downButton 8
#define upButton 9

// Define the pins for the ultrasonic sensor
#define TRIGGER_PIN 10
#define ECHO_PIN 11

// Forward declaration of all functions, added from example by Ralph Bacon, but he said its not needed
void ledState();
void doorState();
void sensorState();
void displayState(String currState);

// ultrasonic sensor using float to allow decimals
float duration;
float distanceCM;
float distanceIN;

// setting parameters for fading amount
int fadeUpAmount = 10;
int fadeDownAmount = 3;

void setup() {
  // Initialize serial connection to display values
  Serial.begin(9600);

  // Initialize the NeoPixel strip
  FastLED.addLeds<CHIPSET,LED_1,COLOR_ORDER>(leds, NUM_LEDS_PER_STRIP);
  FastLED.addLeds<CHIPSET,LED_2,COLOR_ORDER>(leds, NUM_LEDS_PER_STRIP);
  FastLED.setMaxPowerInVoltsAndMilliamps(VOLTS,MAX_AMPS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  FastLED.show();

  // Set button pins as input
  pinMode(downButton, INPUT_PULLUP);
  pinMode(upButton, INPUT_PULLUP);

  // Set the trigger and echo pins for the ultrasonic sensor
  pinMode(TRIGGER_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);

  // Set output pins as output
  pinMode(motor_ccw, OUTPUT);
  pinMode(motor_cw, OUTPUT);
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);

  digitalWrite(motor_ccw, LOW);
  digitalWrite(motor_cw, LOW);
  digitalWrite(motor_ccw, LOW);

  // All done
  Serial.println("Setup completed");

}

  void loop() {
  //execute the state machine for the door
  doorState();

  //complete the LED funciton in accordance to where the door is 
  ledState();

  //obtain the ultrasonic sensor readings
  sensorState();
  }

void doorState() {
    enum class doorState : uint8_t {
      IDLE,               // defaults to 0
      LOWERDOOR,          // defaults to 1
      STOPLOWERDOOR,      // defaults to 2
      RAISEDOOR,          // defaults to 3
      STOPRAISEDOOR,      // defaults to 4 
    };

    // keep track of the current state of the door
    static doorState currState = doorState::IDLE;

    switch (currState) {

      // initial state or state returned to
      case doorState::IDLE:
        displayState("IDLE state");

        // has someone pushed the button and the door is less than or equal to 30 inches away from the sensor
        if (digitalRead(downButton) == LOW && (distanceIN) <= 30) { 
          digitalWrite(motor_ccw, HIGH); //turn on the motor to lower the door
          {
            // prints out the current status of the door
            Serial.println("door closing");
          }

         // move to the next state 
          currState = doorState::LOWERDOOR;
        }
        break;
    
        //door is now lowering 
        case doorState::LOWERDOOR:
        displayState("Door lowering");

        // the door is currently lowering and needs to know when to stop
        if (digitalRead(motor_ccw) == HIGH && (distanceIN) == 30) {
          digitalWrite(motor_ccw, LOW); // stops the motor at the closed distance
          { 
            // prints out the current status of the door
            Serial.println("door closed");
          }

          // move to the next state
          currState = doorState::STOPLOWERDOOR;
        }
        break;

        //door is ready to be raised
        case doorState::STOPLOWERDOOR:
        displayState("Up button pressed");

        // the up button is pressed and the door is greater than or equal to 1 inch from the sensor
        if (digitalRead(upButton) == LOW && (distanceIN) >= 1) {
          digitalWrite(motor_cw, HIGH); //turn on the motor to raise the door
          {
            // prints out the current status of the door
            Serial.println("door opening");
          }

          // move to the next state 
          currState = doorState::RAISEDOOR;
        }
        break;          
      
        //door is now opening 
        case doorState::RAISEDOOR:
        displayState("Door opening");

        // the door is currently opening and needs to know when to stop
        if (digitalRead(motor_cw) == HIGH && (distanceIN) == 1) {
          digitalWrite(motor_cw, LOW); // stops the motor at the open distance
          { 
            // prints out the current status of the door
            Serial.println("door opened");
          }

          // move to the next state
          currState = doorState::STOPRAISEDOOR;
        }
        break;

        case doorState::STOPRAISEDOOR:
        displayState("Stopped raise door state");

        // has someone pushed the button and the door is less than or equal to 30 inches away from the sensor
        if (digitalRead(downButton) == LOW && (distanceIN) <= 30) { 
          digitalWrite(motor_ccw, HIGH); //turn on the motor to lower the door
          {
            // prints out the current status of the door
           Serial.println("door closing");
          }

          // move to the next state 
          currState = doorState::LOWERDOOR;
        }
        break;

        default:
        // Nothing to do here
        Serial.println("'Default' Switch Case reached - Error");
    }  
  }

  

  void ledState() {
   if (digitalRead(distanceIN) <= 2) {
      for(int i = 0; i < NUM_LEDS_PER_STRIP; i++) {
        leds[i] = CRGB::White;  
        leds[i].maximizeBrightness(fadeDownAmount);
      }
      FastLED.show();
      fadeDownAmount --;
    }
      // this will detect when the motor is lowering the door and change all the LED's to red 
   if (digitalRead(motor_ccw) == HIGH) {
      for(int i = 0; i < NUM_LEDS_PER_STRIP; i++) {
        leds[i] = CRGB::Red;
        leds[i].maximizeBrightness(fadeUpAmount);
        FastLED.show();
        fadeUpAmount ++;
        delay(100); // use this to control how fast the LED's change
      }
    }
      // this will activate once the door stops at the bottom and fade out the lights, it should fade the lights out
   if (digitalRead(distanceIN) >= 29) {
    for(int i = 0; i < NUM_LEDS_PER_STRIP; i++) { 
        leds[i] = CRGB::Red;
        leds[i].maximizeBrightness(fadeDownAmount);
      }
      FastLED.show();
      fadeDownAmount --;
    }

   if (digitalRead(motor_cw) == HIGH) {
      for(int i = NUM_LEDS_PER_STRIP; i >=0 ; i--) {  // decrements from the end of the strip to the begining 
        leds[i] = CRGB::Red;
        leds[i].maximizeBrightness(fadeUpAmount);
        FastLED.show();
        fadeUpAmount ++;
        delay(100);
      }  
    }  
  }

  void sensorState() {
    // start with clean signal
    digitalWrite(TRIGGER_PIN, LOW);
    delayMicroseconds(2);
    //send trigger signal
    digitalWrite(TRIGGER_PIN, HIGH);
    delayMicroseconds(10);
    digitalWrite(TRIGGER_PIN, LOW);
    // return pulse duration in microseconds
    // if set to HIGH, pulseIN() waits for the pin to go from LOW to HIGH
    // stops timing when pin goes back LOW
    duration = pulseIn(ECHO_PIN, HIGH);
    // convert m/s to in/microsec
    // 343 m/s = .034 cm/microseconds
    distanceCM = (duration * 0.034) / 2;
    // convert to inches, 1in = 2.54cm
    distanceIN = distanceCM / 2.54;
    // print distance
    Serial.print("Distance: ");
    Serial.print(distanceCM);
    Serial.print(" cm | ");
    Serial.print(distanceIN);
    Serial.print(" in");
    delay(100);
  }

  // Helper routine to track state machine progress
  void displayState(String currState) {
    static String prevState = "";

    if (currState != prevState) {
        Serial.println(currState);
        prevState = currState;
    }
}



<= means less than or equal to. > means greater than. There are a few more relational operators in C/C++.

See


So shouldn't it be
if (getDistance () >= DistanceUp)  {
            state = S_Idle;
            doorPos = DoorUp;
            digitalWrite (PinLedUp, LedOn);
            Serial.println ("Door Up");
        }
        break;

    case S_Lower:
        if (getDistance() <= DistanceDown)  {

HTH

a7

Thanks, I can't but look forward to being able to read your code.

Are you getting plausible and useful data from the sensor? I'm not sure what you are measuring, is it the feedback element for knowing where the door is, informing how it should I've to be where it wants to be?

Also, does this version handle the buttons the way you want?

a7

aren't they the same thing? (do need to test for ==)

the idea of the sensor is to be placed above the door in the wall cavity and let me know how far away the door is to the sensor, so when the door raises into the wall the sensor number will get lower and lower, I plan on having the sensor 1 inch away from the top position of the door. so once the motor raises the door up and reaches that top point, the motor turns off leaving the door open. when the door is fully lowered it should be 31 inches away from the sensor (ill fiddle with the numbers once i fully build it) because the door is 30 inches high. All the sensor should be able to read is the distance that the door is from the sensor.

This version hopefully should handle what I need to do, my previous versions have fried my uno's that I had though so I am waiting for some more so I can continue with actually testing it. I feel it wasnt the code but user error that did that though, I was powering the motor with a 12v power source and had also pigtailed it to power the arduino and I believe the power was not playing nice and fried the uno, I plan on using a nano this time and powering it with a usb cable instead of the 12v power supply and then another usb for the leds since they are 5v.

which parts?

in the Idle state, turn off the motors and check for a button press.

If the door i down,

  • change to the Raise state
  • turn the motor on to raise the doowm
  • shoud turn off the LED that indicates the door is down (corrected)
  • print that the door is being raised.

similar if the door is up. there no need to 2 buttons

    case S_Idle:
        digitalWrite  (PinMotorDown, MotorOff);
        digitalWrite  (PinMotorUp,   MotorOff);

        if (LOW == digitalRead (PinBut))  {
            if (DoorDown == doorPos)  {
                state = S_Raise;
                digitalWrite  (PinMotorUp, MotorOn);
                digitalWrite (PinLedDown    LedOff);
                Serial.println ("Raise Door");
            }
            else {
                state = S_Lower;
                digitalWrite  (PinMotorDown, MotorOn);
                digitalWrite (PinLedDown,  LedOff);
                Serial.println ("Lower Door");
            }
        }
        break;

when the door is being raised, check if the distance is equal or above the desired height. If it is:

  • change state to Idle which will turn off the motor
  • update doorPos
  • turn on the LED indicating the door is up
  • print that the door is up
    case S_Raise:
        if (DistanceUp <= getDistance ())  {
            state = S_Idle;
            doorPos = DoorUp;
            digitalWrite (PinLedUp, LedOn);
            Serial.println ("Door Up");
        }
        break;

similar for if the door is being lowered

setup also checks the door position and initializes doorPos and sets the appropriate LED

    if (DistanceDown >= getDistance ())  {
        digitalWrite  (PinLedDown,   LedOn);
        doorPos = DoorDown;
        Serial.println ("Door Down");
    }
    else if (DistanceUp <= getDistance ())  {
        digitalWrite  (PinLedUp,     LedOn);
        doorPos = DoorUp;
        Serial.println ("Door Up");
    }
    else
        Serial.println ("Door partially open");
}

enum are a simple way to define related constants

The C Programming Language is a succinct descritionm of the language co-authored by the inventor