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

Not to be fly in ointment.
Is there anything that covers objects in way of door travel? Or door weight such as something hanging on door?

should use LEDs to test logic

i use a Multifunction board

you can also use the wokwi simulator

you'll need to change the logic for the door heights

I took your code and adapted it to what I was planning, can you let me know if is going in the right direction? I am also going to try to simulate it using that simulator.

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

const byte PinMotorUp   = 2;
const byte PinMotorDown = 3;

const byte PinLedUp     = 7;
const byte PinLedDown   = 8;

const byte LED_1   = 5;
const byte LED_2   = 6;

const byte PinBut  = 4;

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

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

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

// distance to stop the motor in inches
const int DistanceUp   = 2;
const int DistanceDown = 30;

// Define the pins for the ultrasonic sensor
const byte TRIGGER_PIN = 10;
const byte ECHO_PIN = 11;

// ------------------------LEDS-----------------------------------------------------

// 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];

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

//-------------------------------------ULtrasonic Sensor---------------------------------------------------

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

// --------------------------------------SETUP---------------------------------------
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);

    // ---------- start the US sensor and get base reading ------------

     // 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);
  

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

      // 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(PinBut, INPUT_PULLUP);

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

    // 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() { 
    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 <= digitalRead(distanceIN))  {
            state = S_Idle;
            doorPos = DoorUp;
            digitalWrite (PinLedUp, LedOn);
            Serial.println ("Door Up");
        }
        break;

    case S_Lower:
        if (DistanceDown >= digitalRead(distanceIN))  {
            state = S_Idle;
            doorPos = DoorDown;
            digitalWrite (PinLedDown, LedOn);
            Serial.println ("Door Down");
        }
        break;
    } 
  }
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 (PinMotorDown == 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 (PinMotorUp == 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;
    }
}

shouldn't this just be

    else if (DistanceUp <= distanceIN)  {

which is computed in sensorState()

i would make sure the door operation works reliable before playing with fancy LEDs

you need to verify the polarity, HIGH/LOW, of turning the motors On/Off with some test code that also reports the distance before do this automatically

the code should also check for a button press when the motors are moving to immediately stop them by changing to the Idle state and then change direction

@gcjr

My thought for fixing this problem is coding the instructions to only action button presses if both motor pins also read Low so then the code will not try to do anything if either of the motors are moving.

if (LOW == digitalRead (PinBut) && LOW == (PinMotorUp, PinMotorDown))  {
            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");
            }
        }

For the polarity I am using a 2 ch relay module to control a wiper motor both directions wired up like this

so if the polarity should be low on both to keep both directions off.

I agree with not worrying about the LED's until I have figured out the motor functions are working properly, I will create another copy without the LEDs.

How much of that have you tried in any way? I only ask because ultrasonic sensors can be a bit less than satisfactory in some deployments.

When you are testing in the wokwi, the sensor will be perfect, as to manipulate its reading is simply to move a slider. So you may get a false confidence.

When the time comes, it may be necessary to get the door position in a different way.

a7

Thanks for the advice, I was also thinking about using these type of switches placed on the side of the door

I feel like they would be much more reliable than the sensor (also probably easier to code). What do you think? If I did that than I believe the code should look something like this.

const byte PinMotorUp   = 2;
const byte PinMotorDown = 3;

const byte PinLedUp     = 7;
const byte PinLedDown   = 8;

const byte SwUp     = 5;
const byte SwDown   = 6;

const byte PinBut  = 4;

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

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

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


// --------------------------------------SETUP---------------------------------------
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 (LOW == digitalRead(SwDown))  {
        digitalWrite  (PinLedDown,   LedOn);
        doorPos = DoorDown;
        Serial.println ("Door Down");
    }
    else if (LOW == digitalRead(SwUp))  {
        digitalWrite  (PinLedUp,     LedOn);
        doorPos = DoorUp;
        Serial.println ("Door Up");
    }
    else
        Serial.println ("Door partially open");


    // Set button pins as input
  pinMode(PinBut, INPUT_PULLUP);
  pinMode(SwUp, INPUT_PULLUP);
  pinMode(SwDown, INPUT_PULLUP);

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

// -----------------------------------------------------------------------------
void loop() {  

    switch (state)  {
    case S_Idle:
        digitalWrite  (PinMotorDown, MotorOff);
        digitalWrite  (PinMotorUp,   MotorOff);

        if (LOW == digitalRead (PinBut) && LOW == (PinMotorUp, PinMotorDown))  {
            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 (LOW == digitalRead(SwDown))  {
            state = S_Idle;
            doorPos = DoorUp;
            digitalWrite (PinLedUp, LedOn);
            Serial.println ("Door Up");
        }
        break;

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


that's why you don't need to check for a stop button in the Idle state

again what happens in the Idle State and the values of
MotorOff and MotorOn are specified in the code and their values can be set for the correct polarity ... MotorOff = LOW

yes. notice how the state machine logic isn't really changing, just the input mechanism

the problem with recognizing a button press in all states: in Idle to initiate a move and in others to STOP means that a button press, not simply that the button is LOW needs to be recognized because it ping-pong between states while the button is LOW

Hmm, I think I understand what you are saying. What if I add a delay in the code during raising and lowering, like 5 seconds so it can make it to the up or down position before it reads the next line?

Do you have a better way in mind?

I think limit switches would be much easier to get working.

Do you have a motor on mind, or any ideas from this other part of the project? How heavy is the door?

As for buttons and perhaps limit switches, you will need to do some kind of button handling. You want to be able to know if a switch is open or closed, you do that now with digitalRead(). You also want to be able to know when the switch went from not-pressed to pressed. Maybe the time you let your finger off the button should do something, or something else.

For that you need to debounce the contacts and use state change detection. Both are a hole thing, I do recommend everyone takes a try at it, at least to the point of thoroughly understanding a few different approaches.

Or you can promise to do that one day (!) and just use a library. ezButton (small e small z big Button) is one such library and gets my highest praise "it doesn't suck". It is indeed easy to use, and as long as your loop runs free works well.

In the case code for the states, you can use the button library to see if, for example, some button just got pushed. OR if some limit switch is still closed.

a7

handles button to STOP motors

don't you want to STOP immediately

The motor I am using is a car wiper motor, and the door is about 5 - 10 lbs, I tested the motors ability to raise the door off the ground and it did not seem to struggle with it.

This does look like does what I would need it to. Later this week I will get to work on building this and I will let you know the result.

since completing it i fried my boards so finally got new ones that the IDE recognized, now I am getting this error when uploading. I saw that some people got errors when loading the IDE from the sketch so I made sure to open the sketch through the open option on the IDE then selected the board and clicked upload. I tried using the old bootloader option and that didnt work either. This is what I keep getting:

Sketch uses 2302 bytes (7%) of program storage space. Maximum is 30720 bytes.
Global variables use 261 bytes (12%) of dynamic memory, leaving 1787 bytes for local variables. Maximum is 2048 bytes.
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 1 of 10: not in sync: resp=0xc3
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 2 of 10: not in sync: resp=0xc3
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 3 of 10: not in sync: resp=0xc3
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 4 of 10: not in sync: resp=0xc3
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 5 of 10: not in sync: resp=0xc3
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 6 of 10: not in sync: resp=0xc3
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 7 of 10: not in sync: resp=0xc3
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 8 of 10: not in sync: resp=0xc3
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 9 of 10: not in sync: resp=0xc3
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 10 of 10: not in sync: resp=0xc3
Failed uploading: uploading error: exit status 1

nvmd, now it felt like working after I updated the IDE.

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