Automated Curtains using an IR-remote

Hi everyone,

Corona lockdown is undoubtedly a boring time, but I used the time to set about on a new DIY adventure. Since my bedroom curtains run behind my bed's headboard they are often tricky to open. To make life easy I would like to be able to open and close them using a remote.

Hence, I have been playing around with an Arduino setup in order to control the position of a stepper motor that will drive the curtains. I use a Nano, an EasyDriver v4.4, a NEMA 17 stepper motor, an IR sensor + remote and of course a power supply. The setup works; I can program the Nano so I can move the stepper motor using the IR remote. However, in this case the driver and motor are constantly powered on, which is unnecessary. For power consumption purposes and to prevent the motor from heating and making noise I would like to 'sleep' the EasyDriver.

The EasyDriver should 'sleep' by default and should wake up when a button on the IR-remote is pressed. Next it should stay awake and wait for a button to be pressed again, to either open or close the curtains. After it has moved the curtain I would like it to wait a few seconds before going into sleep-mode again.

I have tried to do so by making an If-loop that check if the IR sensor has received a signal. If so, wake the driver and proceed to move the motor accordingly using a switch...case command. Unfortunately it doesn't work this way. It behaves is such a way that I struggle to understand what is going on.

The driver doesn't wake immediately after it receives a signal / a button is pushed. When it does, the motor is held in position. When pressing a button again, say '5', it should run to its corresponding position. However, it doesn't. It stays stationary for 20 seconds (delay time) and then goes to sleep again. The loop seems to be blocking some of the functionalities.

Does anyone have a suggestion for solving this problem? Maybe using a function in order to move the stepper? Maybe a while loop (while the driver is awake...move to position when receiving a signal)?

Thanks in advance!

Ps. I must admit I'm not knowledgable when it comes to using a function command (yet).

Ps.Ps. I left out all other lines of code (define, constants and void setup lines) in order to fit it within the allowed 9000 characters.

void loop() {

  if (irrecv.decode(&results))                    // check if a IR-code is recieved
  {
    digitalWrite(sleepPin,HIGH);                  // Wake the EasyDriver
    
    if (results.value == 0xFFFFFFFF) {            // If key is repeated, the code becomes FFFFFFFF
      results.value = lastKey;                    // IR-code is re-written to the code of the key being held
    }

    switch (results.value) {                      // Follows set of commands if IR-code equals case-code

      case 0xFF6897:                              // When the '*' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later axecution by .run();)
        stepper.moveTo(beginPos);                 // Move to the starting position
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // Indicate position
        break;

      case 0xFF629D:                              // When the '2' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later axecution by .run();) 
        stepper.moveTo(pos1);                     // Move to position
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // indicate position
        break;

      case 0xFF02FD:                              // When the '5' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later axecution by .run();)
        stepper.moveTo(pos2);                     // Move to position 2
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // Indicate position
        break;

      case 0xFFA857:                              // When the '8' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later axecution by .run();)
        stepper.moveTo(pos3);                     // Move to position 3
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // Indicate position
        break;

      case 0xFF9867:                              // When the '0' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later axecution by .run();)
        stepper.moveTo(pos4);                     // Move to position 4
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // Indicate position
        break;

      case 0xFFB04F:                              // When the '#' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later axecution by .run();)
        stepper.moveTo(endPos);                    // Move to end position
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // Indicate position
        break;
    }

    delay(100);                                   // Small delay to prevent misreadings
    irrecv.resume();                              // Resume recieving of codes

  }
   
  stepper.run();                                  // Let the stepper move to the desired position
  delay(20000);                                    // Add delay before turning into sleep-mode
  
  digitalWrite(sleepPin,LOW);                      // Return to sleep mode
  
}

Just some information. How do you determine end of travel open or closed?v What if the curtain gets caught? How big is the stepper motor? what is posX and how is it determined. Posting a schematic, not a frizzy thing would be helpful.

to add to gilshultz's comments ...

how do you know the position of the stepper motor when the device starts up?

a simple approach is to have have sensor/limit-switch (micro-switch) at either end of the curtain's travel. presumably the curtain is stopped at one sensor, so when powered up, move in the direction of the opposite sensor. in no sensor is active, move in either direction until a sensor becomes active.

Thus far I have not given the end design and mechanics much thought. Initially I want to be able to control the stepper motor using the IR remote. Hereafter I will think of the rest in more detail.

For testings sake I entered begin and end values of 0-2000 respectively. And four positions in between on even intervals.

The EasyStepper library includes a functions that take care of positioning. It tracks the steppers position and saves it using the function currentPosition. Furthermore it move to positions relative to that. These all seem to work fine; This script works flawlessly without the sleep function in it. This I've tested!

When using the sleep function the Nano stays awake, so its current position is known. Therefore a 'homing sensor' shouldn't be necessary. However, I did buy two switches for either side as a failsafe. These will be integrated later.

All of this IS in the script, but I didn't upload the completed script due to the maximum length of the post.

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// This code is written for curtain automation. 
// It moves curtains to a desired position, using a NEMA 17 stepper motor (Casun 42SHD0001-24) and the EasyDriver v4.4
// These positions are predetermined. Increments are set to 1/5 of the total length. 
// In addition the code uses the drivers' sleep mode, for power consumtion and minimal heat production. 

//Author: Kasper van Wijk, December 2020


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <IRremote.h>                             // Include IR Remote library
#include <AccelStepper.h>                         // Include AccelStepper library

#define stepPin         2                         // Define Step Pin
#define directionPin    3                         // Define Direction Pin
#define sleepPin        4                         // Define Sleep Pin (pulled LOW = power consumption)
#define ms1Pin          5                         // Define MS1 Pin (Micro Step Resolution 1)
#define ms2Pin          6                         // Define MS2 Pin (Micro Step Resolution 2)
#define enablePin       7                         // Define Enable Pin (LOW = enabling FET functionality)
#define pfdPin          8                         // Define PFD pin (decay mode)
#define resetPin        9                         // Define Reset Pin (igronring FET functionality)
#define IRpin           15                        // Define IR reciever pin


const int beginPos = 0;                           // Starting position (curtains open)
const int endPos = 100000;                        // End position (curtains closed)
const int pos1 = 1 / 5 * endPos;                  // Position 1
const int pos2 = 2 / 5 * endPos;                  // Position 2
const int pos3 = 3 / 5 * endPos;                  // Position 3
const int pos4 = 4 / 5 * endPos;                  // Position 4
const int Max_speed = 3000;                       // Maximum speed
const int Max_accel = 2000;                       // Miximum acceleration

IRrecv irrecv(IRpin);                             // Define IR Receiver
decode_results results;                           // Define results objects returned by library decoder
AccelStepper stepper(1, stepPin, directionPin);   // Define steppermotor (driver,step pin,direction pin)


long lastKey;
long Pos = stepper.currentPosition();             // Value of current position (in steps, relative to starting position 0)

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void setup()
{
//pinMode(stepPin,         OUTPUT);               // Configure Step Pin to output           (configured by Accelstep)
//pinMode(directionPin,    OUTPUT);               // Configure Direction Pin to output      (configured by Accelstep)
//pinMode(sleepPin,        OUTPUT);               // Configure Sleep Pin to output
//pinMode(ms1Pin,          OUTPUT);               // Configure MS1 Pin to output            (configured by Accelstep)
//pinMode(ms2Pin,          OUTPUT);               // Configure MS2 Pin to output            (configured by Accelstep)
//pinMode(enablePin,       OUTPUT);               // Configure Enable Pin to output
//pinMode(pfdPin,          OUTPUT);               // Configure PFD pin to output            (configured by Accelstep)
//pinMode(resetPin,        OUTPUT);               // Configure Rest Pin to output
  pinMode(IRpin,            INPUT);               // Configure IR reciever pin to input
  
//digitalWrite(stepPin,       LOW);               // Set Step Pin to LOW                    (configured by Accelstep)
//digitalWrite(directionPin,  LOW);               // Set Direction Pin to LOW               (configured by Accelstep)
//digitalWrite(sleepPin,      LOW);               // Set Sleep Pin to LOW
//digitalWrite(ms1Pin,        LOW);               // Set MS1 Pin to LOW                     (configured by Accelstep)
//digitalWrite(ms2Pin,        LOW);               // Set MS2 Pin to LOW                     (configured by Accelstep)
//digitalWrite(enablePin,     LOW);               // Set Enable Pin to LOW
//digitalWrite(pfdPin,        LOW);               // Set PFD pin to LOW                     (configured by Accelstep)
//digitalWrite(resetPin,     HIGH);               // Set IR reciever pin to LOW
  
  Serial.begin(9600);                             // Enable the Serial Monitor, for tracking current position
  
  irrecv.enableIRIn();                            // Enable the IR Receiver
  stepper.setMaxSpeed(Max_speed);                 // Set max speed for stepper
  stepper.setAcceleration(Max_accel);             // Set max acceleration for stepper
  stepper.setCurrentPosition(beginPos);           // Set current position to begin position
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void loop() {

  if (irrecv.decode(&results))                    // check if a IR-code is recieved
  {
    //digitalWrite(sleepPin,HIGH);                  // Wake the EasyDriver
    
    if (results.value == 0xFFFFFFFF) {            // If key is repeated, the code becomes FFFFFFFF
       results.value = lastKey;                   // IR-code is re-written to the code of the key being held
    }

    switch (results.value) {                      // Follows set of commands if IR-code equals case-code

      case 0xFF6897:                              // When the '*' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later execution by .run();)
        stepper.moveTo(beginPos);                 // Move to the starting position
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // Indicate position
        break;

      case 0xFF629D:                              // When the '2' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later execution by .run();) 
        stepper.moveTo(pos1);                     // Move to position
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // indicate position
        break;

      case 0xFF02FD:                              // When the '5' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later execution by .run();)
        stepper.moveTo(pos2);                     // Move to position 2
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // Indicate position
        break;

      case 0xFFA857:                              // When the '8' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later execution by .run();)
        stepper.moveTo(pos3);                     // Move to position 3
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // Indicate position
        break;

      case 0xFF9867:                              // When the '0' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later execution by .run();)
        stepper.moveTo(pos4);                     // Move to position 4
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // Indicate position
        break;

      case 0xFFB04F:                              // When the '#' key is pressed
        lastKey = results.value;                  // Store as last key (saved for later execution by .run();)
        stepper.moveTo(endPos);                    // Move to end position
        Pos = stepper.currentPosition();          // Update position
        Serial.println(Pos);                      // Indicate position
        break;
    }

    delay(100);                                   // Small delay to prevent misreadings
    irrecv.resume();                              // Resume recieving of codes

  }
   
  stepper.run();                                  // Let the stepper move to the desired position
  //delay(20000);                                    // Add delay before turning into sleep-mode
  
  // digitalWrite(sleepPin,LOW);                      // Return to sleep mode
  
}

Just for getting the sleep function of the driver sorted I wrote a piece of code. I want it to:

  • wake up when a button on the IR remote is pressed.
  • stay awake for some time, lets say 6 seconds
  • (ultimately during this time seek for another IR signal that translates to a movement of the stepper)
  • go back to sleep, if no button is pressed again in during this 6 second.

So the period of 6 seconds should reset every time a button is pressed. And it shouldn't matter when in time it is pressed; either during awake or sleep.

I tried this with the millis() function. However, I cannot get it to work. It will only work one single time (the driver wakes up), but after it goes to sleep, it never wakes again.

int period = 6000;
boolean Awake = false;
long WakeupTime = 0;

void loop() {


  if (irrecv.decode(&results)) {                   // check if a IR-code is recieved {

    unsigned long currentMillis = millis();
    
      
    WakeDriver();
    Awake = true;
    
  
  
    if (Awake && (currentMillis - WakeupTime > period)) {
      unsigned long WakeupTime = millis();
      SleepDriver();
      Awake = false;
    }
  }
}

What do I overlook?

Grz.

At this point it is getting confusing. Could you post a preliminary schematic complete with all power supplies, sensors, showing all power and ground connections. I have not figured how a microprocessor will monitor a button when the conde is not operating. To do that magic you need to use an interrupt or reset type of function, preferably interrupt.

Both my Nano and the easydriver have their own power supply. 5V and 12V respectively.

The easydriver is connected as prescribed in the documentation. The setup works fine as long as the driver is always awake. This I would like to change; I want it to sleep when not used (majority of the day). And wake when I give it a signal to either open or close the curtains.

When talking about pressing a button, I mean a button on the IR remote, which is connected to pin A1 on my Nano and to the Nano’s 5V power supply.

I already found that sleeping the Nano itself is most favorable in combination with an interrupt. But a IR remote / sensor cannot be used for that. Therefore the Nano will always need to be awake.

why sleep if it's not battery powered?

why not add a 5V regulator to the 12V supply and only use one power supply?

First of all its a waste of energy. Secondly sleeping the driver turns off the power to the stepper. This prevents it from applying holding torque and thus making any sound. This is needed because it will be installed in my bedroom.

The Nano and EasyDriver will eventually be powered by one single power supply. But this is off topic for now. I have asked this question before, and it was kindly answered by others: Powering a Nano with EasyStepper +5V pin - #8 by braskas - Motors, Mechanics, Power and CNC - Arduino Forum

The Nano will stay awake and doesn't need to sleep.

My question remains on the programming side of the story; how to wake up a easy driver using a IR remote...

braskas:
Secondly sleeping the driver turns off the power to the stepper.

that makes sense. never heard of depowering hardware as putting it to "sleep".

looks like the easydriver a pin the nano can drive

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