Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. My Project
  3. Curtain Control Node.

Curtain Control Node.

Scheduled Pinned Locked Moved My Project
domoticzcurtain controlmysensors node
51 Posts 15 Posters 20.0k Views 22 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • R Offline
    R Offline
    romeok01
    wrote on last edited by
    #35

    I write program with AccelStepper library

    / Enable debug prints to serial monitor
    #define MY_DEBUG
    
    // Enable and select radio type attached
    #define MY_RADIO_NRF24
    //#define MY_RADIO_RFM69
    
    // Enable repeater functionality for this node
    #define MY_REPEATER_FEATURE
    
    #include <SPI.h>
    #include <MySensors.h>
    #include <AccelStepper.h>      //import biblioteki AccelStepper 
    
    #define HALFSTEP 8
    #define CURTAIN_CLOSED 2000  // wartosc gdy kurtyna zamknieta
    #define CURTAIN_OPEN 0       // wartosc gdy kurtyna otwarta
    #define CHILD_ID 1
    
    // definicje MySensors
    
    MyMessage message(CHILD_ID, S_COVER);
    
    // Definicja pinow silnika
    #define IN1  3     // IN1 - zielony
    #define IN2  4     // IN2 - czarny
    #define IN3  5     // IN3 - niebieski
    #define IN4  6     // IN4 - czerwony
     
    AccelStepper stepper1(HALFSTEP, IN1, IN2, IN3, IN4);
    
    void setup()
    {
      stepper1.setMaxSpeed(200.0);
      stepper1.setAcceleration(1000.0);
      stepper1.setSpeed(200);
      stepper1.moveTo(CURTAIN_OPEN);
    }
    
    void presentation() 
    {
      // Wyslanie informacji o wersji programu
      sendSketchInfo("Program kurtyna", "1.0");
      // Register all sensors to gw (they will be created as child devices)
      present(CHILD_ID, S_COVER); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP
    }
    
    void loop()
    {
      stepper1.run();  //Start
    }
    
    void receive(const MyMessage &message)
    {
      // if message = V_UP start moving until closed
      if (message.type==V_UP) {
         if (stepper1.distanceToGo() == 0){
             if (stepper1.currentPosition() == CURTAIN_OPEN){
                 stepper1.moveTo(CURTAIN_CLOSED);
         // Store state in eeprom
         saveState(message.sensor, message.getBool());
         request(CHILD_ID, V_UP, 0); // request new values from controller
             }
          }
       }
       if (message.type==V_DOWN) {
           stepper1.moveTo(CURTAIN_OPEN);
           // Store state in eeprom
           saveState(message.sensor, message.getBool());
           request(CHILD_ID, V_DOWN, 0); // request new values from controller   
        }
        if (message.type==V_STOP) {
            stepper1.setCurrentPosition(0);
            // Store state in eeprom
            saveState(message.sensor, message.getBool());
            request(CHILD_ID, V_STOP, 0); // request new values from controller
        }
    }
    
    

    I use MySensors library 2.0

    Now i working.

    1 Reply Last reply
    0
    • J Offline
      J Offline
      jacikaas
      wrote on last edited by
      #36

      Hello,

      I trying to adopt the code for 28BYJ-48 stepper motor with ULN2003 driver board for roller blinds control. Code is working, but when the stepper motor is always on - it gets hot. Because of it also its consumes more energy. So thats why I want to turn on ULN2003 board only when new action is started, and after that it should be shuted down again. I could do it, because ULN2003 has On/Off jumper:
      alt text

      Question #1: I trying to use relay for that On/Off. Could I do it without relay and control On/Off jumper directly from arduino? Does relay is the best solution?

      Question #2: I suck at programming, but I trying to add theese lines to my code to control the relay SIL05-1A72-71D, which controls the ULN2003 driver board:
      int powerPin = 7; //before SETUP
      pinMode(powerPin, OUTPUT); // In SETUP
      digitalWrite(powerPin, LOW); // In SETUP
      digitalWrite(powerPin, HIGH); // In void receive function
      delay(CURTAIN_CLOSED); // In void receive function
      digitalWrite(powerPin, LOW); // In void receive function

      digitalWrite(powerPin, HIGH); - I think is in right position, but I don't know how to turn off relay when action is done. Please advise for coding, because I not good in it. Thank You! Below is the code:

      // Enable debug prints to serial monitor
      #define MY_DEBUG
      
      // Enable and select radio type attached
      #define MY_NODE_ID 10
      #define MY_RADIO_NRF24
      //#define MY_RADIO_RFM69
      
      // Enable repeater functionality for this node
      //#define MY_REPEATER_FEATURE
      
      #include <SPI.h>
      #include <MySensors.h>
      #include <AccelStepper.h>      //import biblioteki AccelStepper 
      
      #define HALFSTEP 8
      #define CURTAIN_CLOSED 10000  // wartosc gdy kurtyna zamknieta
      #define CURTAIN_OPEN 0       // wartosc gdy kurtyna otwarta
      #define CHILD_ID 1
      
      int powerPin = 7;
      
      // definicje MySensors
      
      MyMessage message(CHILD_ID, S_COVER);
      
      // Definicja pinow silnika
      #define IN1  3     // IN1 - zielony
      #define IN2  4     // IN2 - czarny
      #define IN3  5     // IN3 - niebieski
      #define IN4  6     // IN4 - czerwony
       
      AccelStepper stepper1(HALFSTEP, IN1, IN3, IN2, IN4);
      
      void setup()
      {
        stepper1.setMaxSpeed(1000.0);
        stepper1.setAcceleration(100.0);
        stepper1.setSpeed(200);
        stepper1.moveTo(CURTAIN_OPEN);
        
        pinMode(powerPin, OUTPUT);
        digitalWrite(powerPin, LOW);
      }
      
      void presentation() 
      {
        // Wyslanie informacji o wersji programu
        sendSketchInfo("Roller blinds", "1.0");
        // Register all sensors to gw (they will be created as child devices)
        present(CHILD_ID, S_COVER); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP
      }
      
      void loop()
      {
        stepper1.run();  //Start
      }
      
      void receive(const MyMessage &message)
      {
        // if message = V_UP start moving until closed
        if (message.type==V_UP) {
           digitalWrite(powerPin, HIGH);
           if (stepper1.distanceToGo() == 0){
               if (stepper1.currentPosition() == CURTAIN_OPEN){
                   stepper1.moveTo(CURTAIN_CLOSED);
           // Store state in eeprom
           saveState(message.sensor, message.getBool());
           request(CHILD_ID, V_UP, 0); // request new values from controller
               }
            }
         }
         if (message.type==V_DOWN) {
             digitalWrite(powerPin, HIGH);
             stepper1.moveTo(CURTAIN_OPEN);
             // Store state in eeprom
             saveState(message.sensor, message.getBool());
             request(CHILD_ID, V_DOWN, 0); // request new values from controller   
          }
          if (message.type==V_STOP) {
              digitalWrite(powerPin, HIGH);
              stepper1.setCurrentPosition(0);
              // Store state in eeprom
              saveState(message.sensor, message.getBool());
              request(CHILD_ID, V_STOP, 0); // request new values from controller
          }
       //   delay(CURTAIN_CLOSED);
       //   digitalWrite(powerPin, LOW);
      }
      
      
      Suresh MaliS 1 Reply Last reply
      0
      • J jacikaas

        Hello,

        I trying to adopt the code for 28BYJ-48 stepper motor with ULN2003 driver board for roller blinds control. Code is working, but when the stepper motor is always on - it gets hot. Because of it also its consumes more energy. So thats why I want to turn on ULN2003 board only when new action is started, and after that it should be shuted down again. I could do it, because ULN2003 has On/Off jumper:
        alt text

        Question #1: I trying to use relay for that On/Off. Could I do it without relay and control On/Off jumper directly from arduino? Does relay is the best solution?

        Question #2: I suck at programming, but I trying to add theese lines to my code to control the relay SIL05-1A72-71D, which controls the ULN2003 driver board:
        int powerPin = 7; //before SETUP
        pinMode(powerPin, OUTPUT); // In SETUP
        digitalWrite(powerPin, LOW); // In SETUP
        digitalWrite(powerPin, HIGH); // In void receive function
        delay(CURTAIN_CLOSED); // In void receive function
        digitalWrite(powerPin, LOW); // In void receive function

        digitalWrite(powerPin, HIGH); - I think is in right position, but I don't know how to turn off relay when action is done. Please advise for coding, because I not good in it. Thank You! Below is the code:

        // Enable debug prints to serial monitor
        #define MY_DEBUG
        
        // Enable and select radio type attached
        #define MY_NODE_ID 10
        #define MY_RADIO_NRF24
        //#define MY_RADIO_RFM69
        
        // Enable repeater functionality for this node
        //#define MY_REPEATER_FEATURE
        
        #include <SPI.h>
        #include <MySensors.h>
        #include <AccelStepper.h>      //import biblioteki AccelStepper 
        
        #define HALFSTEP 8
        #define CURTAIN_CLOSED 10000  // wartosc gdy kurtyna zamknieta
        #define CURTAIN_OPEN 0       // wartosc gdy kurtyna otwarta
        #define CHILD_ID 1
        
        int powerPin = 7;
        
        // definicje MySensors
        
        MyMessage message(CHILD_ID, S_COVER);
        
        // Definicja pinow silnika
        #define IN1  3     // IN1 - zielony
        #define IN2  4     // IN2 - czarny
        #define IN3  5     // IN3 - niebieski
        #define IN4  6     // IN4 - czerwony
         
        AccelStepper stepper1(HALFSTEP, IN1, IN3, IN2, IN4);
        
        void setup()
        {
          stepper1.setMaxSpeed(1000.0);
          stepper1.setAcceleration(100.0);
          stepper1.setSpeed(200);
          stepper1.moveTo(CURTAIN_OPEN);
          
          pinMode(powerPin, OUTPUT);
          digitalWrite(powerPin, LOW);
        }
        
        void presentation() 
        {
          // Wyslanie informacji o wersji programu
          sendSketchInfo("Roller blinds", "1.0");
          // Register all sensors to gw (they will be created as child devices)
          present(CHILD_ID, S_COVER); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP
        }
        
        void loop()
        {
          stepper1.run();  //Start
        }
        
        void receive(const MyMessage &message)
        {
          // if message = V_UP start moving until closed
          if (message.type==V_UP) {
             digitalWrite(powerPin, HIGH);
             if (stepper1.distanceToGo() == 0){
                 if (stepper1.currentPosition() == CURTAIN_OPEN){
                     stepper1.moveTo(CURTAIN_CLOSED);
             // Store state in eeprom
             saveState(message.sensor, message.getBool());
             request(CHILD_ID, V_UP, 0); // request new values from controller
                 }
              }
           }
           if (message.type==V_DOWN) {
               digitalWrite(powerPin, HIGH);
               stepper1.moveTo(CURTAIN_OPEN);
               // Store state in eeprom
               saveState(message.sensor, message.getBool());
               request(CHILD_ID, V_DOWN, 0); // request new values from controller   
            }
            if (message.type==V_STOP) {
                digitalWrite(powerPin, HIGH);
                stepper1.setCurrentPosition(0);
                // Store state in eeprom
                saveState(message.sensor, message.getBool());
                request(CHILD_ID, V_STOP, 0); // request new values from controller
            }
         //   delay(CURTAIN_CLOSED);
         //   digitalWrite(powerPin, LOW);
        }
        
        
        Suresh MaliS Offline
        Suresh MaliS Offline
        Suresh Mali
        wrote on last edited by
        #37

        @jacikaas said in Curtain Control Node.:

        void receive(const MyMessage &message)
        {
        // if message = V_UP start moving until closed
        if (message.type==V_UP) {
        digitalWrite(powerPin, HIGH);
        if (stepper1.distanceToGo() == 0){
        if (stepper1.currentPosition() == CURTAIN_OPEN){
        stepper1.moveTo(CURTAIN_CLOSED);
        // Store state in eeprom
        saveState(message.sensor, message.getBool());
        request(CHILD_ID, V_UP, 0); // request new values from controller
        }
        }
        }
        if (message.type==V_DOWN) {
        digitalWrite(powerPin, HIGH);
        stepper1.moveTo(CURTAIN_OPEN);
        // Store state in eeprom
        saveState(message.sensor, message.getBool());
        request(CHILD_ID, V_DOWN, 0); // request new values from controller
        }
        if (message.type==V_STOP) {
        digitalWrite(powerPin, HIGH);
        stepper1.setCurrentPosition(0);
        // Store state in eeprom
        saveState(message.sensor, message.getBool());
        request(CHILD_ID, V_STOP, 0); // request new values from controller
        }
        // delay(CURTAIN_CLOSED);
        // digitalWrite(powerPin, LOW);
        }

        You can enable and disable outputs and try. In the following function:

        void receive(const MyMessage &message)
        
        //add the following at the begining of the function
        stepper1.enableOutputs ()
        //and add the following at the end in the same function.
        stepper1.disableOutputs ()
        

        I hope this helps haven't tried it myself.

        J 1 Reply Last reply
        0
        • Suresh MaliS Suresh Mali

          @jacikaas said in Curtain Control Node.:

          void receive(const MyMessage &message)
          {
          // if message = V_UP start moving until closed
          if (message.type==V_UP) {
          digitalWrite(powerPin, HIGH);
          if (stepper1.distanceToGo() == 0){
          if (stepper1.currentPosition() == CURTAIN_OPEN){
          stepper1.moveTo(CURTAIN_CLOSED);
          // Store state in eeprom
          saveState(message.sensor, message.getBool());
          request(CHILD_ID, V_UP, 0); // request new values from controller
          }
          }
          }
          if (message.type==V_DOWN) {
          digitalWrite(powerPin, HIGH);
          stepper1.moveTo(CURTAIN_OPEN);
          // Store state in eeprom
          saveState(message.sensor, message.getBool());
          request(CHILD_ID, V_DOWN, 0); // request new values from controller
          }
          if (message.type==V_STOP) {
          digitalWrite(powerPin, HIGH);
          stepper1.setCurrentPosition(0);
          // Store state in eeprom
          saveState(message.sensor, message.getBool());
          request(CHILD_ID, V_STOP, 0); // request new values from controller
          }
          // delay(CURTAIN_CLOSED);
          // digitalWrite(powerPin, LOW);
          }

          You can enable and disable outputs and try. In the following function:

          void receive(const MyMessage &message)
          
          //add the following at the begining of the function
          stepper1.enableOutputs ()
          //and add the following at the end in the same function.
          stepper1.disableOutputs ()
          

          I hope this helps haven't tried it myself.

          J Offline
          J Offline
          jacikaas
          wrote on last edited by
          #38

          @suresh-mali Thank You Suresh for Your answer!
          I disconnect relay and delete it lines from the code.

          I add lines to code like You said:

          void receive(const MyMessage &message)
          {
          stepper1.enableOutputs ();
           // rest of code
          stepper1.disableOutputs ();
          }
          

          It looks like it have to work because it is simple solution, but it doesn't... On driver board ULN2003, when motor is not rotating, A Led is always on. I think if command with disableOutputs would be activated, then the LED should not be on?

          Suresh MaliS 1 Reply Last reply
          0
          • J jacikaas

            @suresh-mali Thank You Suresh for Your answer!
            I disconnect relay and delete it lines from the code.

            I add lines to code like You said:

            void receive(const MyMessage &message)
            {
            stepper1.enableOutputs ();
             // rest of code
            stepper1.disableOutputs ();
            }
            

            It looks like it have to work because it is simple solution, but it doesn't... On driver board ULN2003, when motor is not rotating, A Led is always on. I think if command with disableOutputs would be activated, then the LED should not be on?

            Suresh MaliS Offline
            Suresh MaliS Offline
            Suresh Mali
            wrote on last edited by Suresh Mali
            #39

            @jacikaas Ok.

            //Remove this line from loop function
            stepper1.run(); 
            
            //Change this line
            stepper1.moveTo(CURTAIN_CLOSED);
            //to
            stepper1.runToPosition(CURTAIN_CLOSED);
            

            I hope this works. Also make similar changes in block which closes curtains.
            Refernce article: https://www.pjrc.com/teensy/td_libs_AccelStepper.html

            1 Reply Last reply
            0
            • J Offline
              J Offline
              jacikaas
              wrote on last edited by
              #40

              @suresh-mali
              I now reading about that funktions in http://www.airspayce.com/mikem/arduino/AccelStepper/classAccelStepper.html#a344f58fef8cc34ac5aa75ba4b665d21c

              Did what You said, but getting error: no matching function for call to 'AccelStepper::runToPosition(int)'
              I paste here all code, but I think I did everything without mistakes:

              // Enable debug prints to serial monitor
              #define MY_DEBUG
              
              // Enable and select radio type attached
              #define MY_NODE_ID 10
              #define MY_RADIO_NRF24
              //#define MY_RADIO_RFM69
              
              // Enable repeater functionality for this node
              //#define MY_REPEATER_FEATURE
              
              #include <SPI.h>
              #include <MySensors.h>
              #include <AccelStepper.h>
              
              #define HALFSTEP 8
              #define CURTAIN_CLOSED 10000
              #define CURTAIN_OPEN 0
              #define CHILD_ID 1
              
              // int powerPin = 7;
              
              // definicje MySensors
              
              MyMessage message(CHILD_ID, S_COVER);
              
              // Definicja pinow silnika
              #define IN1  3     // IN1
              #define IN2  4     // IN2
              #define IN3  5     // IN3
              #define IN4  6     // IN4
               
              AccelStepper stepper1(HALFSTEP, IN1, IN3, IN2, IN4);
              
              void setup()
              {
                stepper1.setMaxSpeed(1000.0);
                stepper1.setAcceleration(100.0);
                stepper1.setSpeed(200);
                stepper1.runToPosition(CURTAIN_CLOSED);
              }
              
              void presentation() 
              {
                sendSketchInfo("Roller blinds", "1.0");
                present(CHILD_ID, S_COVER); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP
              }
              
              void loop()
              {
                //stepper1.run();  //Start
              }
              
              void receive(const MyMessage &message)
              {
              stepper1.enableOutputs ();
                // if message = V_UP start moving until closed
                if (message.type==V_UP) {
                   if (stepper1.distanceToGo() == 0){
                       if (stepper1.currentPosition() == CURTAIN_OPEN){
                           stepper1.runToPosition(CURTAIN_CLOSED);
                   // Store state in eeprom
                   saveState(message.sensor, message.getBool());
                   request(CHILD_ID, V_UP, 0); // request new values from controller
                       }
                    }
                 }
                 if (message.type==V_DOWN) {
                     stepper1.moveTo(CURTAIN_OPEN);
                     // Store state in eeprom
                     saveState(message.sensor, message.getBool());
                     request(CHILD_ID, V_DOWN, 0); // request new values from controller   
                  }
                  if (message.type==V_STOP) {
                      stepper1.setCurrentPosition(0);
                      // Store state in eeprom
                      saveState(message.sensor, message.getBool());
                      request(CHILD_ID, V_STOP, 0); // request new values from controller
                  }
              stepper1.disableOutputs ();
              }
              
              
              skywatchS 1 Reply Last reply
              0
              • J jacikaas

                @suresh-mali
                I now reading about that funktions in http://www.airspayce.com/mikem/arduino/AccelStepper/classAccelStepper.html#a344f58fef8cc34ac5aa75ba4b665d21c

                Did what You said, but getting error: no matching function for call to 'AccelStepper::runToPosition(int)'
                I paste here all code, but I think I did everything without mistakes:

                // Enable debug prints to serial monitor
                #define MY_DEBUG
                
                // Enable and select radio type attached
                #define MY_NODE_ID 10
                #define MY_RADIO_NRF24
                //#define MY_RADIO_RFM69
                
                // Enable repeater functionality for this node
                //#define MY_REPEATER_FEATURE
                
                #include <SPI.h>
                #include <MySensors.h>
                #include <AccelStepper.h>
                
                #define HALFSTEP 8
                #define CURTAIN_CLOSED 10000
                #define CURTAIN_OPEN 0
                #define CHILD_ID 1
                
                // int powerPin = 7;
                
                // definicje MySensors
                
                MyMessage message(CHILD_ID, S_COVER);
                
                // Definicja pinow silnika
                #define IN1  3     // IN1
                #define IN2  4     // IN2
                #define IN3  5     // IN3
                #define IN4  6     // IN4
                 
                AccelStepper stepper1(HALFSTEP, IN1, IN3, IN2, IN4);
                
                void setup()
                {
                  stepper1.setMaxSpeed(1000.0);
                  stepper1.setAcceleration(100.0);
                  stepper1.setSpeed(200);
                  stepper1.runToPosition(CURTAIN_CLOSED);
                }
                
                void presentation() 
                {
                  sendSketchInfo("Roller blinds", "1.0");
                  present(CHILD_ID, S_COVER); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP
                }
                
                void loop()
                {
                  //stepper1.run();  //Start
                }
                
                void receive(const MyMessage &message)
                {
                stepper1.enableOutputs ();
                  // if message = V_UP start moving until closed
                  if (message.type==V_UP) {
                     if (stepper1.distanceToGo() == 0){
                         if (stepper1.currentPosition() == CURTAIN_OPEN){
                             stepper1.runToPosition(CURTAIN_CLOSED);
                     // Store state in eeprom
                     saveState(message.sensor, message.getBool());
                     request(CHILD_ID, V_UP, 0); // request new values from controller
                         }
                      }
                   }
                   if (message.type==V_DOWN) {
                       stepper1.moveTo(CURTAIN_OPEN);
                       // Store state in eeprom
                       saveState(message.sensor, message.getBool());
                       request(CHILD_ID, V_DOWN, 0); // request new values from controller   
                    }
                    if (message.type==V_STOP) {
                        stepper1.setCurrentPosition(0);
                        // Store state in eeprom
                        saveState(message.sensor, message.getBool());
                        request(CHILD_ID, V_STOP, 0); // request new values from controller
                    }
                stepper1.disableOutputs ();
                }
                
                
                skywatchS Offline
                skywatchS Offline
                skywatch
                wrote on last edited by
                #41

                @jacikaas Sorry to revive an old thread, but did you get this resolved and if so how?

                I just 3D printed my first motorised roller blind mounts and have tested with arduino stepper only.

                Next is to go mysensors with it, but which way is best? Standard arduino stepper or accelstepper?

                1 Reply Last reply
                0
                • xydixX Offline
                  xydixX Offline
                  xydix
                  wrote on last edited by
                  #42

                  Okey. This is an old thread but I hope it's ok I keep it alive.
                  I just printed som parts for my blinds.
                  I use a stepper motor (28BYJ-48 with ULN2003 Driver board) and this sketch found in this thread.
                  The sketch is awesome.
                  To my quiestion.
                  I want to run 2 blinds at the same time from ONE node. Synchronized or maybe individual.
                  Is this possible?
                  There isn't any digital pins over for this when using button(s).
                  I think i saw someone using analog pin connected to the ULN2003 someware.
                  Is it possible to run 2 motors on one board?
                  Really hope somone can answer som of my question.
                  What modifications is needed in the sketch if this is possible.
                  This is the sketch I use.

                  /*
                   PROJECT: MY Sensors curtain controller
                   PROGRAMMER: AWI
                   DATE: march 11, 2016
                   FILE: AWI stepper1.ino
                   LICENSE: Public domain
                  
                   Hardware: ATMega328p board w/ NRF24l01
                  	and MySensors 2.0 (Development)
                  		
                  Special:
                  	uses AccelStepper library
                  	
                  Summary:
                  	Curtain control with stepper motor. 
                  	Manual operation with 1 push button 
                  	Calibration with manual button
                  
                  Remarks:
                  	Fixed node-id
                  	
                  Change log:
                  20160312 - Cleanup
                  */
                  
                  // Enable debug prints to serial monitor
                  #define MY_DEBUG 
                  
                  #define MY_NODE_ID 13											// fixed node number
                  // Enable and select radio type attached
                  #define MY_RADIO_RF24
                  //#define MY_RADIO_RFM69
                  
                  #include <SPI.h>
                  #include <MySensors.h> 
                  
                  // stepper library
                  #include <AccelStepper.h>										// http://www.airspayce.com/mikem/arduino/AccelStepper/
                  #define HALFSTEP 8  											// Stepper uses "Halfstep" mode
                  
                  // button library
                  // used for:
                  // - manual open close - single click: close/ stop/ open/ stop
                  // - calibration  - after long press: open calibarion- until single press - closed calibration - until single press (stop)
                  #include <Button.h>												// https://github.com/JChristensen/Button
                  
                  #define CHILD_ID 1   											// Id of the sensor child
                  
                  #define SN "Curtain control 13"
                  #define SV "1.0"
                  
                  #define buttonPin1 7  											// Arduino pin connected to buttonPin1
                  #define buttonPin2 A0  											// Arduino pin connected to buttonPin2 (fixed to ground)
                  
                  // Motor pin definitions
                  #define motorPin1  3     										// IN1 on the ULN2003 driver 1
                  #define motorPin2  4     										// IN2 on the ULN2003 driver 1
                  #define motorPin3  5     										// IN3 on the ULN2003 driver 1
                  #define motorPin4  6     										// IN4 on the ULN2003 driver 1
                  
                  const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ;	// heartbeatinterval
                  unsigned long heartbeatCounter = 0 ;
                  
                  //
                  // helper routines to store and retrieve long in mysensors EEPROM
                  union {															// used to convert long to bytes for EEPROM storage
                  	long longInt;
                  	uint8_t LongByte[4];
                  	} convLongInt ;
                  
                  void saveStateL(int EEposition, long StateL){
                  	convLongInt.longInt = StateL ;
                  	for (int y = 0; y < 4 ; y++){								// convert to bytes
                  		saveState(EEposition + y , convLongInt.LongByte[y]) ;
                  		}
                  	Serial.print("State saved: "); Serial.println(StateL);
                  	}
                  		
                  long loadStateL(int EEposition){
                  	for (int y = 0; y < 4 ; y++){								// convert from bytes
                  		convLongInt.LongByte[y] = loadState(EEposition + y) ;
                  		}
                  	Serial.print("State read: "); Serial.println(convLongInt.longInt);
                  	return convLongInt.longInt ;
                  	}
                  	
                  
                  // Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48
                  AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4);
                  // Initialize button active low, debounce and internal pull-up
                  Button myBtn(buttonPin1, true, true, 40);						// Initiate the button (pin, pull_up, invert, debounce_ms)
                  
                  MyMessage percentageMsg(CHILD_ID, V_PERCENTAGE);				// used to send updates to controller
                  
                  const long maxRun = 4000000L ;									// maximum runway
                  long setPosition = 0 ; 											// remembers set position, need to be saved in EEPROM
                  const int setPositionEE = 4 ;									// eeprom location
                  long openPosition = 0 ; 										// Position at open, need to be saved in EEPROM?
                  const int openPositionEE = setPositionEE + 4 ;					// eeprom location
                  long closedPosition = 120000UL ; 								// Position at full close, need to be saved in EEPROM
                  const int closedPositionEE = openPositionEE + 4 ;				// eeprom location
                  
                  unsigned long idleTimer = millis() ;							// return to idle timer
                  unsigned long idleTime = 100000UL ;								// return to idle after 100 secs
                  
                  unsigned long printTimer = millis() ;							// print timer
                  unsigned long printTime = 1000UL ;								// print after 1 secs
                  
                  enum position_t {Open, Close, Idle, Running} ;
                  position_t lastDirection = Open ; 								// lastDirection only for buttonpress
                  position_t runStatus = Idle ;									// indicates current status for running motor. used for status reporting to controller
                  
                  enum State_t {sIdle, sCalibrateOpen, sCalibrateClose} ;
                  State_t State = sIdle ;
                  
                  
                  void setup() {
                  	// setup buttons
                  	pinMode(buttonPin1, OUTPUT);
                  	stepper1.setMaxSpeed(2000.0);
                  	stepper1.setAcceleration(1000.0);
                  	//saveStateL(closedPositionEE, closedPosition) ; 			// INIT: save closed position in EEPROM
                  	closedPosition = loadStateL(closedPositionEE) ;				// need to get last values from EEPROM and assume the current position is correct
                  	setPosition = loadStateL(setPositionEE) ;
                  	stepper1.setCurrentPosition(setPosition );
                  }//--(end setup )---
                  
                  void presentation() {
                    present(CHILD_ID, S_COVER, "Curtain");						// Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP
                  
                    // Register the LED Dimmable Light with the gateway
                    sendSketchInfo(SN, SV);
                  }
                  
                  
                  void loop() {
                  unsigned int now = millis() ;								// current time for loop
                  // simple state machine for button press
                  	myBtn.read();  
                  	switch (State) {
                          // Idle state, waiting for some action
                  		// - button press
                  		// - idleTimer
                          case sIdle:                
                              if (myBtn.wasReleased()){						// idle
                  				Serial.println("Button release") ;
                  				// if running stop
                  				if (stepper1.isRunning()){
                  					setPosition = stepper1.currentPosition();
                  					stepper1.moveTo(setPosition) ;			// move to current position (was already there..)
                  				} else if (lastDirection == Open) {
                  					stepper1.moveTo(closedPosition) ;
                  					lastDirection = Close ;
                  				} else {									// lastDirection == Close
                  					stepper1.moveTo(openPosition) ;
                  					lastDirection = Open ;
                  				}	
                  			} else if (myBtn.pressedFor(3000)){				// move to calibratete state with long press
                  				Serial.println("Button press long") ;
                  				idleTimer = now ;							// return to idle after ...
                  				State = sCalibrateOpen ;
                  				stepper1.move(-maxRun) ;					// let the stepper open with maximum
                  				}
                  			break ;
                  		// if not running and last action was open close ;  else open
                  		// if longpress Calibrate open
                          case sCalibrateOpen:           						// calibration going on     
                  			if (myBtn.wasPressed()){
                  				stepper1.setCurrentPosition(0 );		// set new 0 position ??
                  				openPosition = setPosition = 0 ;
                  				State = sCalibrateClose ;					// next is close calibarion
                  				stepper1.move(maxRun) ;						// let the stepper close with maximum
                  			} else if (now > idleTimer + idleTime) {		// timer expired -> abort calibration
                  				State = sIdle ;
                  			}
                  			break ;
                          case sCalibrateClose:								// calibrate closed position, end with keypress
                  			if (myBtn.wasPressed())	{
                  				closedPosition = setPosition = stepper1.currentPosition() ;
                  				saveStateL(closedPositionEE, closedPosition) ; // save closed position in EEPROM
                  				State = sIdle ;
                  				stepper1.moveTo(openPosition) ;				// return to open after calibration
                  			} else if (now > idleTimer + idleTime) {		// timer expired -> abort calibration
                  				State = sIdle ;
                  			}
                  			break ;
                  		default :
                  			break ;
                  		}
                  	// power off stepper if not running (no need to reenable))
                  	if (!stepper1.isRunning()){
                  		if (runStatus != Idle){								// there was a change in runningstatus, so report to controller
                  			setPosition = stepper1.currentPosition() ;		// store in EEPROM and report ready to controller
                  			saveStateL(setPositionEE, setPosition) ;
                  			send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ;
                  			runStatus = Idle ;
                  		}
                  		stepper1.disableOutputs();
                  	} else {
                  		runStatus = Running ;
                  	}
                  	stepper1.run();
                  	/*
                  	if (printTimer++ > now + printTime){
                  		printTimer = now ;
                  		Serial.println(stepper1.currentPosition());
                  		}
                  	*/
                  }
                  
                  // This is called when a message is received 
                  void receive(const MyMessage &message) {
                  // We only expect few types of messages from controller, check which
                    switch (message.type) {
                  	case V_PERCENTAGE:
                  	// Curtain should be opened
                  	stepper1.moveTo(message.getInt() * (closedPosition - openPosition)/100);
                  	Serial.print("Message: "); Serial.print(message.sensor); Serial.print(" , value: % "); Serial.println( message.getInt());
                  	Serial.print("Moving to: "); Serial.println(message.getInt() * (closedPosition - openPosition)/100);
                  	break ;
                  	case V_STATUS:
                  	// Curtain should be opened or closed full
                  	stepper1.moveTo((message.getInt() == HIGH)?openPosition:closedPosition);
                  	Serial.print("Message - valid: ");
                  	Serial.print(message.sensor);
                  	Serial.print(" , value: % ");
                  	break ;
                  	default : 
                  	// not recognizable message
                  	Serial.print("Message - valid: ");
                  	Serial.print(message.sensor);
                  	Serial.print(", Unrecognized ");
                  	break ;
                  	}
                    }
                  
                  TON RIJNAARDT xydixX 2 Replies Last reply
                  0
                  • TheoLT Offline
                    TheoLT Offline
                    TheoL
                    Contest Winner
                    wrote on last edited by
                    #43

                    You can use an I/O expander like an MCP23017 which is a 16 port expander or a 8 port expander. They're cheap. Keep in mind they consume 1 mAmp. I'm currently working on a product to overcome that power issue for a battery powered Node. Will try to post about it in a while

                    1 Reply Last reply
                    0
                    • xydixX xydix

                      Okey. This is an old thread but I hope it's ok I keep it alive.
                      I just printed som parts for my blinds.
                      I use a stepper motor (28BYJ-48 with ULN2003 Driver board) and this sketch found in this thread.
                      The sketch is awesome.
                      To my quiestion.
                      I want to run 2 blinds at the same time from ONE node. Synchronized or maybe individual.
                      Is this possible?
                      There isn't any digital pins over for this when using button(s).
                      I think i saw someone using analog pin connected to the ULN2003 someware.
                      Is it possible to run 2 motors on one board?
                      Really hope somone can answer som of my question.
                      What modifications is needed in the sketch if this is possible.
                      This is the sketch I use.

                      /*
                       PROJECT: MY Sensors curtain controller
                       PROGRAMMER: AWI
                       DATE: march 11, 2016
                       FILE: AWI stepper1.ino
                       LICENSE: Public domain
                      
                       Hardware: ATMega328p board w/ NRF24l01
                      	and MySensors 2.0 (Development)
                      		
                      Special:
                      	uses AccelStepper library
                      	
                      Summary:
                      	Curtain control with stepper motor. 
                      	Manual operation with 1 push button 
                      	Calibration with manual button
                      
                      Remarks:
                      	Fixed node-id
                      	
                      Change log:
                      20160312 - Cleanup
                      */
                      
                      // Enable debug prints to serial monitor
                      #define MY_DEBUG 
                      
                      #define MY_NODE_ID 13											// fixed node number
                      // Enable and select radio type attached
                      #define MY_RADIO_RF24
                      //#define MY_RADIO_RFM69
                      
                      #include <SPI.h>
                      #include <MySensors.h> 
                      
                      // stepper library
                      #include <AccelStepper.h>										// http://www.airspayce.com/mikem/arduino/AccelStepper/
                      #define HALFSTEP 8  											// Stepper uses "Halfstep" mode
                      
                      // button library
                      // used for:
                      // - manual open close - single click: close/ stop/ open/ stop
                      // - calibration  - after long press: open calibarion- until single press - closed calibration - until single press (stop)
                      #include <Button.h>												// https://github.com/JChristensen/Button
                      
                      #define CHILD_ID 1   											// Id of the sensor child
                      
                      #define SN "Curtain control 13"
                      #define SV "1.0"
                      
                      #define buttonPin1 7  											// Arduino pin connected to buttonPin1
                      #define buttonPin2 A0  											// Arduino pin connected to buttonPin2 (fixed to ground)
                      
                      // Motor pin definitions
                      #define motorPin1  3     										// IN1 on the ULN2003 driver 1
                      #define motorPin2  4     										// IN2 on the ULN2003 driver 1
                      #define motorPin3  5     										// IN3 on the ULN2003 driver 1
                      #define motorPin4  6     										// IN4 on the ULN2003 driver 1
                      
                      const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ;	// heartbeatinterval
                      unsigned long heartbeatCounter = 0 ;
                      
                      //
                      // helper routines to store and retrieve long in mysensors EEPROM
                      union {															// used to convert long to bytes for EEPROM storage
                      	long longInt;
                      	uint8_t LongByte[4];
                      	} convLongInt ;
                      
                      void saveStateL(int EEposition, long StateL){
                      	convLongInt.longInt = StateL ;
                      	for (int y = 0; y < 4 ; y++){								// convert to bytes
                      		saveState(EEposition + y , convLongInt.LongByte[y]) ;
                      		}
                      	Serial.print("State saved: "); Serial.println(StateL);
                      	}
                      		
                      long loadStateL(int EEposition){
                      	for (int y = 0; y < 4 ; y++){								// convert from bytes
                      		convLongInt.LongByte[y] = loadState(EEposition + y) ;
                      		}
                      	Serial.print("State read: "); Serial.println(convLongInt.longInt);
                      	return convLongInt.longInt ;
                      	}
                      	
                      
                      // Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48
                      AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4);
                      // Initialize button active low, debounce and internal pull-up
                      Button myBtn(buttonPin1, true, true, 40);						// Initiate the button (pin, pull_up, invert, debounce_ms)
                      
                      MyMessage percentageMsg(CHILD_ID, V_PERCENTAGE);				// used to send updates to controller
                      
                      const long maxRun = 4000000L ;									// maximum runway
                      long setPosition = 0 ; 											// remembers set position, need to be saved in EEPROM
                      const int setPositionEE = 4 ;									// eeprom location
                      long openPosition = 0 ; 										// Position at open, need to be saved in EEPROM?
                      const int openPositionEE = setPositionEE + 4 ;					// eeprom location
                      long closedPosition = 120000UL ; 								// Position at full close, need to be saved in EEPROM
                      const int closedPositionEE = openPositionEE + 4 ;				// eeprom location
                      
                      unsigned long idleTimer = millis() ;							// return to idle timer
                      unsigned long idleTime = 100000UL ;								// return to idle after 100 secs
                      
                      unsigned long printTimer = millis() ;							// print timer
                      unsigned long printTime = 1000UL ;								// print after 1 secs
                      
                      enum position_t {Open, Close, Idle, Running} ;
                      position_t lastDirection = Open ; 								// lastDirection only for buttonpress
                      position_t runStatus = Idle ;									// indicates current status for running motor. used for status reporting to controller
                      
                      enum State_t {sIdle, sCalibrateOpen, sCalibrateClose} ;
                      State_t State = sIdle ;
                      
                      
                      void setup() {
                      	// setup buttons
                      	pinMode(buttonPin1, OUTPUT);
                      	stepper1.setMaxSpeed(2000.0);
                      	stepper1.setAcceleration(1000.0);
                      	//saveStateL(closedPositionEE, closedPosition) ; 			// INIT: save closed position in EEPROM
                      	closedPosition = loadStateL(closedPositionEE) ;				// need to get last values from EEPROM and assume the current position is correct
                      	setPosition = loadStateL(setPositionEE) ;
                      	stepper1.setCurrentPosition(setPosition );
                      }//--(end setup )---
                      
                      void presentation() {
                        present(CHILD_ID, S_COVER, "Curtain");						// Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP
                      
                        // Register the LED Dimmable Light with the gateway
                        sendSketchInfo(SN, SV);
                      }
                      
                      
                      void loop() {
                      unsigned int now = millis() ;								// current time for loop
                      // simple state machine for button press
                      	myBtn.read();  
                      	switch (State) {
                              // Idle state, waiting for some action
                      		// - button press
                      		// - idleTimer
                              case sIdle:                
                                  if (myBtn.wasReleased()){						// idle
                      				Serial.println("Button release") ;
                      				// if running stop
                      				if (stepper1.isRunning()){
                      					setPosition = stepper1.currentPosition();
                      					stepper1.moveTo(setPosition) ;			// move to current position (was already there..)
                      				} else if (lastDirection == Open) {
                      					stepper1.moveTo(closedPosition) ;
                      					lastDirection = Close ;
                      				} else {									// lastDirection == Close
                      					stepper1.moveTo(openPosition) ;
                      					lastDirection = Open ;
                      				}	
                      			} else if (myBtn.pressedFor(3000)){				// move to calibratete state with long press
                      				Serial.println("Button press long") ;
                      				idleTimer = now ;							// return to idle after ...
                      				State = sCalibrateOpen ;
                      				stepper1.move(-maxRun) ;					// let the stepper open with maximum
                      				}
                      			break ;
                      		// if not running and last action was open close ;  else open
                      		// if longpress Calibrate open
                              case sCalibrateOpen:           						// calibration going on     
                      			if (myBtn.wasPressed()){
                      				stepper1.setCurrentPosition(0 );		// set new 0 position ??
                      				openPosition = setPosition = 0 ;
                      				State = sCalibrateClose ;					// next is close calibarion
                      				stepper1.move(maxRun) ;						// let the stepper close with maximum
                      			} else if (now > idleTimer + idleTime) {		// timer expired -> abort calibration
                      				State = sIdle ;
                      			}
                      			break ;
                              case sCalibrateClose:								// calibrate closed position, end with keypress
                      			if (myBtn.wasPressed())	{
                      				closedPosition = setPosition = stepper1.currentPosition() ;
                      				saveStateL(closedPositionEE, closedPosition) ; // save closed position in EEPROM
                      				State = sIdle ;
                      				stepper1.moveTo(openPosition) ;				// return to open after calibration
                      			} else if (now > idleTimer + idleTime) {		// timer expired -> abort calibration
                      				State = sIdle ;
                      			}
                      			break ;
                      		default :
                      			break ;
                      		}
                      	// power off stepper if not running (no need to reenable))
                      	if (!stepper1.isRunning()){
                      		if (runStatus != Idle){								// there was a change in runningstatus, so report to controller
                      			setPosition = stepper1.currentPosition() ;		// store in EEPROM and report ready to controller
                      			saveStateL(setPositionEE, setPosition) ;
                      			send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ;
                      			runStatus = Idle ;
                      		}
                      		stepper1.disableOutputs();
                      	} else {
                      		runStatus = Running ;
                      	}
                      	stepper1.run();
                      	/*
                      	if (printTimer++ > now + printTime){
                      		printTimer = now ;
                      		Serial.println(stepper1.currentPosition());
                      		}
                      	*/
                      }
                      
                      // This is called when a message is received 
                      void receive(const MyMessage &message) {
                      // We only expect few types of messages from controller, check which
                        switch (message.type) {
                      	case V_PERCENTAGE:
                      	// Curtain should be opened
                      	stepper1.moveTo(message.getInt() * (closedPosition - openPosition)/100);
                      	Serial.print("Message: "); Serial.print(message.sensor); Serial.print(" , value: % "); Serial.println( message.getInt());
                      	Serial.print("Moving to: "); Serial.println(message.getInt() * (closedPosition - openPosition)/100);
                      	break ;
                      	case V_STATUS:
                      	// Curtain should be opened or closed full
                      	stepper1.moveTo((message.getInt() == HIGH)?openPosition:closedPosition);
                      	Serial.print("Message - valid: ");
                      	Serial.print(message.sensor);
                      	Serial.print(" , value: % ");
                      	break ;
                      	default : 
                      	// not recognizable message
                      	Serial.print("Message - valid: ");
                      	Serial.print(message.sensor);
                      	Serial.print(", Unrecognized ");
                      	break ;
                      	}
                        }
                      
                      TON RIJNAARDT Offline
                      TON RIJNAARDT Offline
                      TON RIJNAARD
                      wrote on last edited by
                      #44

                      @xydix
                      Hello @xydix do you have the curtain working with the sketch above?
                      I don't get the button working on pin 7.
                      Witch button library do you use?
                      I have the library witch is in the sketch, i only must change Bbutton.h in LC_Button.h.
                      Is that maby my problem.
                      I hope u can help me.
                      Greatz Ton

                      xydixX 1 Reply Last reply
                      0
                      • barduinoB barduino

                        @Suresh-Mali

                        DC Motor, very interesting!

                        Do you have end stops? The sketch doesn't open for some reason...

                        Cheers

                        S Offline
                        S Offline
                        SENIORA
                        wrote on last edited by
                        #45

                        @barduino said in Curtain Control Node.:

                        @Suresh-Mali

                        DC Motor, very interesting!

                        Do you have end stops? The sketch doesn't open for some reason...

                        Cheers

                        I have been working on a similar project last week. The mechanics and ´looks´ are the most difficult part for me.

                        1 Reply Last reply
                        0
                        • TON RIJNAARDT TON RIJNAARD

                          @xydix
                          Hello @xydix do you have the curtain working with the sketch above?
                          I don't get the button working on pin 7.
                          Witch button library do you use?
                          I have the library witch is in the sketch, i only must change Bbutton.h in LC_Button.h.
                          Is that maby my problem.
                          I hope u can help me.
                          Greatz Ton

                          xydixX Offline
                          xydixX Offline
                          xydix
                          wrote on last edited by
                          #46

                          @ton-rijnaard
                          Hi. This was a problem for me to. The reason is the library changed name and updates was made.
                          I use the library version 0.9.
                          I have like zero skills in arduino. If you use the old library the button will work with the sketch i posted a moth ago.
                          Looke here: https://github.com/JChristensen/JC_Button/releases
                          Maybe some of the other older releases will work. Don't remember why o choose V0.9
                          Look at 2.0.0, "This is a major release that is not backwards-compatible with previous releases."

                          1 Reply Last reply
                          0
                          • xydixX xydix

                            Okey. This is an old thread but I hope it's ok I keep it alive.
                            I just printed som parts for my blinds.
                            I use a stepper motor (28BYJ-48 with ULN2003 Driver board) and this sketch found in this thread.
                            The sketch is awesome.
                            To my quiestion.
                            I want to run 2 blinds at the same time from ONE node. Synchronized or maybe individual.
                            Is this possible?
                            There isn't any digital pins over for this when using button(s).
                            I think i saw someone using analog pin connected to the ULN2003 someware.
                            Is it possible to run 2 motors on one board?
                            Really hope somone can answer som of my question.
                            What modifications is needed in the sketch if this is possible.
                            This is the sketch I use.

                            /*
                             PROJECT: MY Sensors curtain controller
                             PROGRAMMER: AWI
                             DATE: march 11, 2016
                             FILE: AWI stepper1.ino
                             LICENSE: Public domain
                            
                             Hardware: ATMega328p board w/ NRF24l01
                            	and MySensors 2.0 (Development)
                            		
                            Special:
                            	uses AccelStepper library
                            	
                            Summary:
                            	Curtain control with stepper motor. 
                            	Manual operation with 1 push button 
                            	Calibration with manual button
                            
                            Remarks:
                            	Fixed node-id
                            	
                            Change log:
                            20160312 - Cleanup
                            */
                            
                            // Enable debug prints to serial monitor
                            #define MY_DEBUG 
                            
                            #define MY_NODE_ID 13											// fixed node number
                            // Enable and select radio type attached
                            #define MY_RADIO_RF24
                            //#define MY_RADIO_RFM69
                            
                            #include <SPI.h>
                            #include <MySensors.h> 
                            
                            // stepper library
                            #include <AccelStepper.h>										// http://www.airspayce.com/mikem/arduino/AccelStepper/
                            #define HALFSTEP 8  											// Stepper uses "Halfstep" mode
                            
                            // button library
                            // used for:
                            // - manual open close - single click: close/ stop/ open/ stop
                            // - calibration  - after long press: open calibarion- until single press - closed calibration - until single press (stop)
                            #include <Button.h>												// https://github.com/JChristensen/Button
                            
                            #define CHILD_ID 1   											// Id of the sensor child
                            
                            #define SN "Curtain control 13"
                            #define SV "1.0"
                            
                            #define buttonPin1 7  											// Arduino pin connected to buttonPin1
                            #define buttonPin2 A0  											// Arduino pin connected to buttonPin2 (fixed to ground)
                            
                            // Motor pin definitions
                            #define motorPin1  3     										// IN1 on the ULN2003 driver 1
                            #define motorPin2  4     										// IN2 on the ULN2003 driver 1
                            #define motorPin3  5     										// IN3 on the ULN2003 driver 1
                            #define motorPin4  6     										// IN4 on the ULN2003 driver 1
                            
                            const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ;	// heartbeatinterval
                            unsigned long heartbeatCounter = 0 ;
                            
                            //
                            // helper routines to store and retrieve long in mysensors EEPROM
                            union {															// used to convert long to bytes for EEPROM storage
                            	long longInt;
                            	uint8_t LongByte[4];
                            	} convLongInt ;
                            
                            void saveStateL(int EEposition, long StateL){
                            	convLongInt.longInt = StateL ;
                            	for (int y = 0; y < 4 ; y++){								// convert to bytes
                            		saveState(EEposition + y , convLongInt.LongByte[y]) ;
                            		}
                            	Serial.print("State saved: "); Serial.println(StateL);
                            	}
                            		
                            long loadStateL(int EEposition){
                            	for (int y = 0; y < 4 ; y++){								// convert from bytes
                            		convLongInt.LongByte[y] = loadState(EEposition + y) ;
                            		}
                            	Serial.print("State read: "); Serial.println(convLongInt.longInt);
                            	return convLongInt.longInt ;
                            	}
                            	
                            
                            // Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48
                            AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4);
                            // Initialize button active low, debounce and internal pull-up
                            Button myBtn(buttonPin1, true, true, 40);						// Initiate the button (pin, pull_up, invert, debounce_ms)
                            
                            MyMessage percentageMsg(CHILD_ID, V_PERCENTAGE);				// used to send updates to controller
                            
                            const long maxRun = 4000000L ;									// maximum runway
                            long setPosition = 0 ; 											// remembers set position, need to be saved in EEPROM
                            const int setPositionEE = 4 ;									// eeprom location
                            long openPosition = 0 ; 										// Position at open, need to be saved in EEPROM?
                            const int openPositionEE = setPositionEE + 4 ;					// eeprom location
                            long closedPosition = 120000UL ; 								// Position at full close, need to be saved in EEPROM
                            const int closedPositionEE = openPositionEE + 4 ;				// eeprom location
                            
                            unsigned long idleTimer = millis() ;							// return to idle timer
                            unsigned long idleTime = 100000UL ;								// return to idle after 100 secs
                            
                            unsigned long printTimer = millis() ;							// print timer
                            unsigned long printTime = 1000UL ;								// print after 1 secs
                            
                            enum position_t {Open, Close, Idle, Running} ;
                            position_t lastDirection = Open ; 								// lastDirection only for buttonpress
                            position_t runStatus = Idle ;									// indicates current status for running motor. used for status reporting to controller
                            
                            enum State_t {sIdle, sCalibrateOpen, sCalibrateClose} ;
                            State_t State = sIdle ;
                            
                            
                            void setup() {
                            	// setup buttons
                            	pinMode(buttonPin1, OUTPUT);
                            	stepper1.setMaxSpeed(2000.0);
                            	stepper1.setAcceleration(1000.0);
                            	//saveStateL(closedPositionEE, closedPosition) ; 			// INIT: save closed position in EEPROM
                            	closedPosition = loadStateL(closedPositionEE) ;				// need to get last values from EEPROM and assume the current position is correct
                            	setPosition = loadStateL(setPositionEE) ;
                            	stepper1.setCurrentPosition(setPosition );
                            }//--(end setup )---
                            
                            void presentation() {
                              present(CHILD_ID, S_COVER, "Curtain");						// Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP
                            
                              // Register the LED Dimmable Light with the gateway
                              sendSketchInfo(SN, SV);
                            }
                            
                            
                            void loop() {
                            unsigned int now = millis() ;								// current time for loop
                            // simple state machine for button press
                            	myBtn.read();  
                            	switch (State) {
                                    // Idle state, waiting for some action
                            		// - button press
                            		// - idleTimer
                                    case sIdle:                
                                        if (myBtn.wasReleased()){						// idle
                            				Serial.println("Button release") ;
                            				// if running stop
                            				if (stepper1.isRunning()){
                            					setPosition = stepper1.currentPosition();
                            					stepper1.moveTo(setPosition) ;			// move to current position (was already there..)
                            				} else if (lastDirection == Open) {
                            					stepper1.moveTo(closedPosition) ;
                            					lastDirection = Close ;
                            				} else {									// lastDirection == Close
                            					stepper1.moveTo(openPosition) ;
                            					lastDirection = Open ;
                            				}	
                            			} else if (myBtn.pressedFor(3000)){				// move to calibratete state with long press
                            				Serial.println("Button press long") ;
                            				idleTimer = now ;							// return to idle after ...
                            				State = sCalibrateOpen ;
                            				stepper1.move(-maxRun) ;					// let the stepper open with maximum
                            				}
                            			break ;
                            		// if not running and last action was open close ;  else open
                            		// if longpress Calibrate open
                                    case sCalibrateOpen:           						// calibration going on     
                            			if (myBtn.wasPressed()){
                            				stepper1.setCurrentPosition(0 );		// set new 0 position ??
                            				openPosition = setPosition = 0 ;
                            				State = sCalibrateClose ;					// next is close calibarion
                            				stepper1.move(maxRun) ;						// let the stepper close with maximum
                            			} else if (now > idleTimer + idleTime) {		// timer expired -> abort calibration
                            				State = sIdle ;
                            			}
                            			break ;
                                    case sCalibrateClose:								// calibrate closed position, end with keypress
                            			if (myBtn.wasPressed())	{
                            				closedPosition = setPosition = stepper1.currentPosition() ;
                            				saveStateL(closedPositionEE, closedPosition) ; // save closed position in EEPROM
                            				State = sIdle ;
                            				stepper1.moveTo(openPosition) ;				// return to open after calibration
                            			} else if (now > idleTimer + idleTime) {		// timer expired -> abort calibration
                            				State = sIdle ;
                            			}
                            			break ;
                            		default :
                            			break ;
                            		}
                            	// power off stepper if not running (no need to reenable))
                            	if (!stepper1.isRunning()){
                            		if (runStatus != Idle){								// there was a change in runningstatus, so report to controller
                            			setPosition = stepper1.currentPosition() ;		// store in EEPROM and report ready to controller
                            			saveStateL(setPositionEE, setPosition) ;
                            			send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ;
                            			runStatus = Idle ;
                            		}
                            		stepper1.disableOutputs();
                            	} else {
                            		runStatus = Running ;
                            	}
                            	stepper1.run();
                            	/*
                            	if (printTimer++ > now + printTime){
                            		printTimer = now ;
                            		Serial.println(stepper1.currentPosition());
                            		}
                            	*/
                            }
                            
                            // This is called when a message is received 
                            void receive(const MyMessage &message) {
                            // We only expect few types of messages from controller, check which
                              switch (message.type) {
                            	case V_PERCENTAGE:
                            	// Curtain should be opened
                            	stepper1.moveTo(message.getInt() * (closedPosition - openPosition)/100);
                            	Serial.print("Message: "); Serial.print(message.sensor); Serial.print(" , value: % "); Serial.println( message.getInt());
                            	Serial.print("Moving to: "); Serial.println(message.getInt() * (closedPosition - openPosition)/100);
                            	break ;
                            	case V_STATUS:
                            	// Curtain should be opened or closed full
                            	stepper1.moveTo((message.getInt() == HIGH)?openPosition:closedPosition);
                            	Serial.print("Message - valid: ");
                            	Serial.print(message.sensor);
                            	Serial.print(" , value: % ");
                            	break ;
                            	default : 
                            	// not recognizable message
                            	Serial.print("Message - valid: ");
                            	Serial.print(message.sensor);
                            	Serial.print(", Unrecognized ");
                            	break ;
                            	}
                              }
                            
                            xydixX Offline
                            xydixX Offline
                            xydix
                            wrote on last edited by xydix
                            #47

                            @xydix
                            To answer my own question the stepper runs fine from the analog pins.
                            Next step is to find some intersted in the same thing and see if we could get the sketch adjusted to run two steppers.
                            I will try when i get some time but I guess I will have problem to clone the calibration and store-to-eerprom-part.

                            1 Reply Last reply
                            0
                            • AWIA AWI

                              @patchmaster This is my current curtain control sketch. I work with the development branch, so the syntax can be a little different.

                              The Accelstepper library gives you all the abilities to slow start/stop with numerous kinds of steppers.
                              My sketch includes a calibration function (stored in EEPROM) so the you don't need end stop detection (a stepper knows where it is)

                              /*
                               PROJECT: MY Sensors curtain controller
                               PROGRAMMER: AWI
                               DATE: march 11, 2016
                               FILE: AWI stepper1.ino
                               LICENSE: Public domain
                              
                               Hardware: ATMega328p board w/ NRF24l01
                              	and MySensors 2.0 (Development)
                              		
                              Special:
                              	uses AccelStepper library
                              	
                              Summary:
                              	Curtain control with stepper motor. 
                              	Manual operation with 1 push button 
                              	Calibration with manual button
                              
                              Remarks:
                              	Fixed node-id
                              	
                              Change log:
                              20160312 - Cleanup
                              */
                              
                              // Enable debug prints to serial monitor
                              #define MY_DEBUG 
                              
                              #define MY_NODE_ID 13											// fixed node number
                              // Enable and select radio type attached
                              #define MY_RADIO_NRF24
                              //#define MY_RADIO_RFM69
                              
                              #include <SPI.h>
                              #include <MySensor.h> 
                              
                              // stepper library
                              #include <AccelStepper.h>										// http://www.airspayce.com/mikem/arduino/AccelStepper/
                              #define HALFSTEP 8  											// Stepper uses "Halfstep" mode
                              
                              // button library
                              // used for:
                              // - manual open close - single click: close/ stop/ open/ stop
                              // - calibration  - after long press: open calibarion- until single press - closed calibration - until single press (stop)
                              #include <Button.h>												// https://github.com/JChristensen/Button
                              
                              #define CHILD_ID 1   											// Id of the sensor child
                              
                              #define SN "Curtain control 13"
                              #define SV "1.0"
                              
                              #define buttonPin1 7  											// Arduino pin connected to buttonPin1
                              #define buttonPin2 A0  											// Arduino pin connected to buttonPin2 (fixed to ground)
                              
                              // Motor pin definitions
                              #define motorPin1  3     										// IN1 on the ULN2003 driver 1
                              #define motorPin2  4     										// IN2 on the ULN2003 driver 1
                              #define motorPin3  5     										// IN3 on the ULN2003 driver 1
                              #define motorPin4  6     										// IN4 on the ULN2003 driver 1
                              
                              const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ;	// heartbeatinterval
                              unsigned long heartbeatCounter = 0 ;
                              
                              //
                              // helper routines to store and retrieve long in mysensors EEPROM
                              union {															// used to convert long to bytes for EEPROM storage
                              	long longInt;
                              	uint8_t LongByte[4];
                              	} convLongInt ;
                              
                              void saveStateL(int EEposition, long StateL){
                              	convLongInt.longInt = StateL ;
                              	for (int y = 0; y < 4 ; y++){								// convert to bytes
                              		saveState(EEposition + y , convLongInt.LongByte[y]) ;
                              		}
                              	Serial.print("State saved: "); Serial.println(StateL);
                              	}
                              		
                              long loadStateL(int EEposition){
                              	for (int y = 0; y < 4 ; y++){								// convert from bytes
                              		convLongInt.LongByte[y] = loadState(EEposition + y) ;
                              		}
                              	Serial.print("State read: "); Serial.println(convLongInt.longInt);
                              	return convLongInt.longInt ;
                              	}
                              	
                              
                              // Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48
                              AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4);
                              // Initialize button active low, debounce and internal pull-up
                              Button myBtn(buttonPin1, true, true, 40);						// Initiate the button (pin, pull_up, invert, debounce_ms)
                              
                              MyMessage percentageMsg(CHILD_ID, V_PERCENTAGE);				// used to send updates to controller
                              
                              const long maxRun = 4000000L ;									// maximum runway
                              long setPosition = 0 ; 											// remembers set position, need to be saved in EEPROM
                              const int setPositionEE = 4 ;									// eeprom location
                              long openPosition = 0 ; 										// Position at open, need to be saved in EEPROM?
                              const int openPositionEE = setPositionEE + 4 ;					// eeprom location
                              long closedPosition = 120000UL ; 								// Position at full close, need to be saved in EEPROM
                              const int closedPositionEE = openPositionEE + 4 ;				// eeprom location
                              
                              unsigned long idleTimer = millis() ;							// return to idle timer
                              unsigned long idleTime = 100000UL ;								// return to idle after 100 secs
                              
                              unsigned long printTimer = millis() ;							// print timer
                              unsigned long printTime = 1000UL ;								// print after 1 secs
                              
                              enum position_t {Open, Close, Idle, Running} ;
                              position_t lastDirection = Open ; 								// lastDirection only for buttonpress
                              position_t runStatus = Idle ;									// indicates current status for running motor. used for status reporting to controller
                              
                              enum State_t {sIdle, sCalibrateOpen, sCalibrateClose} ;
                              State_t State = sIdle ;
                              
                              
                              void setup() {
                              	// setup buttons
                              	pinMode(buttonPin1, OUTPUT);
                              	stepper1.setMaxSpeed(2000.0);
                              	stepper1.setAcceleration(1000.0);
                              	//saveStateL(closedPositionEE, closedPosition) ; 			// INIT: save closed position in EEPROM
                              	closedPosition = loadStateL(closedPositionEE) ;				// need to get last values from EEPROM and assume the current position is correct
                              	setPosition = loadStateL(setPositionEE) ;
                              	stepper1.setCurrentPosition(setPosition );
                              }//--(end setup )---
                              
                              void presentation() {
                                present(CHILD_ID, S_COVER, "Curtain");						// Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP
                              
                                // Register the LED Dimmable Light with the gateway
                                sendSketchInfo(SN, SV);
                              }
                              
                              
                              void loop() {
                              unsigned int now = millis() ;								// current time for loop
                              // simple state machine for button press
                              	myBtn.read();  
                              	switch (State) {
                                      // Idle state, waiting for some action
                              		// - button press
                              		// - idleTimer
                                      case sIdle:                
                                          if (myBtn.wasReleased()){						// idle
                              				Serial.println("Button release") ;
                              				// if running stop
                              				if (stepper1.isRunning()){
                              					setPosition = stepper1.currentPosition();
                              					stepper1.moveTo(setPosition) ;			// move to current position (was already there..)
                              				} else if (lastDirection == Open) {
                              					stepper1.moveTo(closedPosition) ;
                              					lastDirection = Close ;
                              				} else {									// lastDirection == Close
                              					stepper1.moveTo(openPosition) ;
                              					lastDirection = Open ;
                              				}	
                              			} else if (myBtn.pressedFor(3000)){				// move to calibratete state with long press
                              				Serial.println("Button press long") ;
                              				idleTimer = now ;							// return to idle after ...
                              				State = sCalibrateOpen ;
                              				stepper1.move(-maxRun) ;					// let the stepper open with maximum
                              				}
                              			break ;
                              		// if not running and last action was open close ;  else open
                              		// if longpress Calibrate open
                                      case sCalibrateOpen:           						// calibration going on     
                              			if (myBtn.wasPressed()){
                              				stepper1.setCurrentPosition(0 );		// set new 0 position ??
                              				openPosition = setPosition = 0 ;
                              				State = sCalibrateClose ;					// next is close calibarion
                              				stepper1.move(maxRun) ;						// let the stepper close with maximum
                              			} else if (now > idleTimer + idleTime) {		// timer expired -> abort calibration
                              				State = sIdle ;
                              			}
                              			break ;
                                      case sCalibrateClose:								// calibrate closed position, end with keypress
                              			if (myBtn.wasPressed())	{
                              				closedPosition = setPosition = stepper1.currentPosition() ;
                              				saveStateL(closedPositionEE, closedPosition) ; // save closed position in EEPROM
                              				State = sIdle ;
                              				stepper1.moveTo(openPosition) ;				// return to open after calibration
                              			} else if (now > idleTimer + idleTime) {		// timer expired -> abort calibration
                              				State = sIdle ;
                              			}
                              			break ;
                              		default :
                              			break ;
                              		}
                              	// power off stepper if not running (no need to reenable))
                              	if (!stepper1.isRunning()){
                              		if (runStatus != Idle){								// there was a change in runningstatus, so report to controller
                              			setPosition = stepper1.currentPosition() ;		// store in EEPROM and report ready to controller
                              			saveStateL(setPositionEE, setPosition) ;
                              			send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ;
                              			runStatus = Idle ;
                              		}
                              		stepper1.disableOutputs();
                              	} else {
                              		runStatus = Running ;
                              	}
                              	stepper1.run();
                              	/*
                              	if (printTimer++ > now + printTime){
                              		printTimer = now ;
                              		Serial.println(stepper1.currentPosition());
                              		}
                              	*/
                              }
                              
                              // This is called when a message is received 
                              void receive(const MyMessage &message) {
                              // We only expect few types of messages from controller, check which
                                switch (message.type) {
                              	case V_PERCENTAGE:
                              	// Curtain should be opened
                              	stepper1.moveTo(message.getInt() * (closedPosition - openPosition)/100);
                              	Serial.print("Message: "); Serial.print(message.sensor); Serial.print(" , value: % "); Serial.println( message.getInt());
                              	Serial.print("Moving to: "); Serial.println(message.getInt() * (closedPosition - openPosition)/100);
                              	break ;
                              	case V_STATUS:
                              	// Curtain should be opened or closed full
                              	stepper1.moveTo((message.getInt() == HIGH)?openPosition:closedPosition);
                              	Serial.print("Message - valid: ");
                              	Serial.print(message.sensor);
                              	Serial.print(" , value: % ");
                              	break ;
                              	default : 
                              	// not recognizable message
                              	Serial.print("Message - valid: ");
                              	Serial.print(message.sensor);
                              	Serial.print(", Unrecognized ");
                              	break ;
                              	}
                                }
                                
                              
                              
                              
                              
                              A Offline
                              A Offline
                              adds666
                              wrote on last edited by
                              #48

                              @AWI Sorry for butting in to an old thread, but automating my blinds is top priority!

                              I've copied your sketch and have everything setup - however, I have eratic behaviour and a few questions:

                              void setup() {
                                // setup buttons
                                pinMode(buttonPin1, OUTPUT);
                                stepper1.setMaxSpeed(2000.0);
                                stepper1.setAcceleration(1000.0);
                                //saveStateL(closedPositionEE, closedPosition) ;      // INIT: save closed position in EEPROM
                                closedPosition = loadStateL(closedPositionEE) ;       // need to get last values from EEPROM and assume the current position is correct
                                setPosition = loadStateL(setPositionEE) ;
                                stepper1.setCurrentPosition(setPosition );
                              }//--(end setup )---
                              
                              • Should the pinMode on buttonPin1 actually be an INPUT?

                              • Can you please describe how to go about calibrating the system?

                              • Does the //saveStateL(closedPositionEE, closedPosition);
                                need uncommenting for one boot of the arduino and the blind closed - then the sketch re-uploading with it commented again?

                              Your help is greatly appreciated as I have a twitching motor and feel I'm almost there!

                              xydixX 1 Reply Last reply
                              0
                              • A adds666

                                @AWI Sorry for butting in to an old thread, but automating my blinds is top priority!

                                I've copied your sketch and have everything setup - however, I have eratic behaviour and a few questions:

                                void setup() {
                                  // setup buttons
                                  pinMode(buttonPin1, OUTPUT);
                                  stepper1.setMaxSpeed(2000.0);
                                  stepper1.setAcceleration(1000.0);
                                  //saveStateL(closedPositionEE, closedPosition) ;      // INIT: save closed position in EEPROM
                                  closedPosition = loadStateL(closedPositionEE) ;       // need to get last values from EEPROM and assume the current position is correct
                                  setPosition = loadStateL(setPositionEE) ;
                                  stepper1.setCurrentPosition(setPosition );
                                }//--(end setup )---
                                
                                • Should the pinMode on buttonPin1 actually be an INPUT?

                                • Can you please describe how to go about calibrating the system?

                                • Does the //saveStateL(closedPositionEE, closedPosition);
                                  need uncommenting for one boot of the arduino and the blind closed - then the sketch re-uploading with it commented again?

                                Your help is greatly appreciated as I have a twitching motor and feel I'm almost there!

                                xydixX Offline
                                xydixX Offline
                                xydix
                                wrote on last edited by xydix
                                #49

                                @adds666 What i can tell it was a long time since @AWI was active here.
                                I use this sketch with my first bild since a couple of days.
                                For me it works like a charm. But in my case I control the motor from analog pins just to test and it seems OK.
                                No problem with the button.
                                The only thing I don't like, is the calibration.
                                I my case, using this on a blind, when calibrating, first it goes down, then i press the button, the blind stops at once and change it's direction and goes up. But when i reach the top level and want to stop it and save the state, it stops slow and pass the point i pressed the button and then reverse to the right spot. Problem is, the blind can't go further because it is in the top position.
                                If it would be the other way around it wouldn't be a problem.
                                Maybe i have the motor one the "wrong" side of the blind but when it is down the "downkey" in my HA is greyed out.
                                When it i up the "upkey" it greyed out so that seems right.

                                What is not working in your case?
                                What motor do you use? I use 28BYJ-48 with ULN2003 and had to set stepper1.setMaxSpeed(2000.0); to stepper1.setMaxSpeed(1000.0); because the motor "slipped" at a speed of 2000.
                                Like i wrote earlier in the thread I tried to figure out how to "dublicate" the sketch and ute it for two motors.
                                I really don't know where to start to get this done. It would be awesome to use one arduino with two blinds.

                                1 Reply Last reply
                                0
                                • A Offline
                                  A Offline
                                  adds666
                                  wrote on last edited by
                                  #50

                                  Hi @xydix , glad to hear you have the sketch working properly. Do you have the pinmode as OUTPUT or did you change to input?

                                  And do you still have the saveStateL(closedPositionEE, closedPosition); line commented out?

                                  If I change the pinMode to input, I get the system powered up and connected to the mysensors gateway fine - serial print repeats 'Button Release'. If I short press the button, the system does nothing. If I hold the button for 3000ms serial print reads 'Button Long Press' and the motor turns - however If I let go of the button, the motor stop and system returns to its 'button release' state.

                                  I'm not able to get the system in to calibration mode.

                                  xydixX 1 Reply Last reply
                                  0
                                  • A adds666

                                    Hi @xydix , glad to hear you have the sketch working properly. Do you have the pinmode as OUTPUT or did you change to input?

                                    And do you still have the saveStateL(closedPositionEE, closedPosition); line commented out?

                                    If I change the pinMode to input, I get the system powered up and connected to the mysensors gateway fine - serial print repeats 'Button Release'. If I short press the button, the system does nothing. If I hold the button for 3000ms serial print reads 'Button Long Press' and the motor turns - however If I let go of the button, the motor stop and system returns to its 'button release' state.

                                    I'm not able to get the system in to calibration mode.

                                    xydixX Offline
                                    xydixX Offline
                                    xydix
                                    wrote on last edited by xydix
                                    #51

                                    @adds666
                                    I am still in "testing mode".
                                    I had some problems with the node, it stoped receiving messages but did always work with the button.
                                    Yesterday i built a new node and uploaded the sketch and it seems to work better.
                                    Here is the sketch i use, I changed the pin for the button due to missing digital pins on my pcb.
                                    And I am back on using digital pins for the stepper driver for the same reason.
                                    I greyed out buttonpin 2. I don't think it is needed.
                                    I also greyed out the heartbeat-part as I had problems. Just for testing. Don't know if it is suitable when using Home Assistant.

                                    /*
                                     PROJECT: MY Sensors curtain controller
                                     PROGRAMMER: AWI
                                     DATE: march 11, 2016
                                     FILE: AWI stepper1.ino
                                     LICENSE: Public domain
                                    
                                     Hardware: ATMega328p board w/ NRF24l01
                                      and MySensors 2.0 (Development)
                                        
                                    Special:
                                      uses AccelStepper library
                                      
                                    Summary:
                                      Curtain control with stepper motor. 
                                      Manual operation with 1 push button 
                                      Calibration with manual button
                                    
                                    Remarks:
                                      Fixed node-id
                                      
                                    Change log:
                                    20160312 - Cleanup
                                    */
                                    
                                    // Enable debug prints to serial monitor
                                    #define MY_DEBUG 
                                    
                                    #define MY_NODE_ID 8                     // fixed node number
                                    // Enable and select radio type attached
                                    #define MY_RADIO_RF24
                                    #define MY_RF24_PA_LEVEL RF24_PA_HIGH
                                    //#define MY_RADIO_RFM69
                                    
                                    #include <SPI.h>
                                    #include <MySensors.h> 
                                    
                                    // stepper library
                                    #include <AccelStepper.h>                   // http://www.airspayce.com/mikem/arduino/AccelStepper/
                                    #define HALFSTEP 8                        // Stepper uses "Halfstep" mode
                                    
                                    // button library
                                    // used for:
                                    // - manual open close - single click: close/ stop/ open/ stop
                                    // - calibration  - after long press: open calibarion- until single press - closed calibration - until single press (stop)
                                    #include <Button.h>                       // https://github.com/JChristensen/Button - Använd gammal version. testat med V0.9 och det funkar
                                    
                                    #define CHILD_ID 1                        // Id of the sensor child
                                    
                                    #define SN "Curtain control"
                                    #define SV "1.0"
                                    
                                    #define buttonPin1 A0                        // Arduino pin connected to buttonPin1
                                    //#define buttonPin2 A0                       // Arduino pin connected to buttonPin2 (fixed to ground)
                                    
                                    // Motor pin definitions
                                    #define motorPin1  2                        // IN1 on the ULN2003 driver 1
                                    #define motorPin2  3                        // IN2 on the ULN2003 driver 1
                                    #define motorPin3  5                        // IN3 on the ULN2003 driver 1
                                    #define motorPin4  6                        // IN4 on the ULN2003 driver 1
                                    
                                    //const unsigned long heartbeatInterval = 1 * 3600UL * 1000UL ; // heartbeatinterval
                                    //unsigned long heartbeatCounter = 0 ;
                                    
                                    //
                                    // helper routines to store and retrieve long in mysensors EEPROM
                                    union {                             // used to convert long to bytes for EEPROM storage
                                      long longInt;
                                      uint8_t LongByte[4];
                                      } convLongInt ;
                                    
                                    void saveStateL(int EEposition, long StateL){
                                      convLongInt.longInt = StateL ;
                                      for (int y = 0; y < 4 ; y++){               // convert to bytes
                                        saveState(EEposition + y , convLongInt.LongByte[y]) ;
                                        }
                                      Serial.print("State saved: "); Serial.println(StateL);
                                      }
                                        
                                    long loadStateL(int EEposition){
                                      for (int y = 0; y < 4 ; y++){               // convert from bytes
                                        convLongInt.LongByte[y] = loadState(EEposition + y) ;
                                        }
                                      Serial.print("State read: "); Serial.println(convLongInt.longInt);
                                      return convLongInt.longInt ;
                                      }
                                      
                                    
                                    // Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper with 28BYJ-48
                                    AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4);
                                    // Initialize button active low, debounce and internal pull-up
                                    Button myBtn(buttonPin1, true, true, 40);           // Initiate the button (pin, pull_up, invert, debounce_ms)
                                    
                                    MyMessage percentageMsg(CHILD_ID, V_PERCENTAGE);        // used to send updates to controller
                                    
                                    const long maxRun = 4000000L ;                  // maximum runway
                                    long setPosition = 0 ;                      // remembers set position, need to be saved in EEPROM
                                    const int setPositionEE = 4 ;                 // eeprom location
                                    long openPosition = 0 ;                     // Position at open, need to be saved in EEPROM?
                                    const int openPositionEE = setPositionEE + 4 ;          // eeprom location
                                    long closedPosition = 120000UL ;                // Position at full close, need to be saved in EEPROM
                                    const int closedPositionEE = openPositionEE + 4 ;       // eeprom location
                                    
                                    unsigned long idleTimer = millis() ;              // return to idle timer
                                    unsigned long idleTime = 100000UL ;               // return to idle after 100 secs
                                    
                                    unsigned long printTimer = millis() ;             // print timer
                                    unsigned long printTime = 1000UL ;                // print after 1 secs
                                    
                                    enum position_t {Open, Close, Idle, Running} ;
                                    position_t lastDirection = Open ;                 // lastDirection only for buttonpress
                                    position_t runStatus = Idle ;                 // indicates current status for running motor. used for status reporting to controller
                                    
                                    enum State_t {sIdle, sCalibrateOpen, sCalibrateClose} ;
                                    State_t State = sIdle ;
                                    
                                    
                                    void setup() {
                                      // setup buttons
                                      pinMode(buttonPin1, OUTPUT);
                                      stepper1.setMaxSpeed(1000.0);
                                      stepper1.setAcceleration(1000.0);
                                      //saveStateL(closedPositionEE, closedPosition) ;      // INIT: save closed position in EEPROM
                                      closedPosition = loadStateL(closedPositionEE) ;       // need to get last values from EEPROM and assume the current position is correct
                                      setPosition = loadStateL(setPositionEE) ;
                                      stepper1.setCurrentPosition(setPosition );
                                    }//--(end setup )---
                                    
                                    void presentation() {
                                      present(CHILD_ID, S_COVER, "Curtain");            // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP
                                    
                                      // Register the LED Dimmable Light with the gateway
                                      sendSketchInfo(SN, SV);
                                    }
                                    
                                    
                                    void loop() {
                                    unsigned int now = millis() ;               // current time for loop
                                    // simple state machine for button press
                                      myBtn.read();  
                                      switch (State) {
                                            // Idle state, waiting for some action
                                        // - button press
                                        // - idleTimer
                                            case sIdle:                
                                                if (myBtn.wasReleased()){           // idle
                                            Serial.println("Button release") ;
                                            // if running stop
                                            if (stepper1.isRunning()){
                                              setPosition = stepper1.currentPosition();
                                              stepper1.moveTo(setPosition) ;      // move to current position (was already there..)
                                            } else if (lastDirection == Open) {
                                              stepper1.moveTo(closedPosition) ;
                                              lastDirection = Close ;
                                            } else {                  // lastDirection == Close
                                              stepper1.moveTo(openPosition) ;
                                              lastDirection = Open ;
                                            } 
                                          } else if (myBtn.pressedFor(3000)){       // move to calibratete state with long press
                                            Serial.println("Button press long") ;
                                            idleTimer = now ;             // return to idle after ...
                                            State = sCalibrateOpen ;
                                            stepper1.move(-maxRun) ;          // let the stepper open with maximum
                                            }
                                          break ;
                                        // if not running and last action was open close ;  else open
                                        // if longpress Calibrate open
                                            case sCalibrateOpen:                      // calibration going on     
                                          if (myBtn.wasPressed()){
                                            stepper1.setCurrentPosition(0 );    // set new 0 position ??
                                            openPosition = setPosition = 0 ;
                                            State = sCalibrateClose ;         // next is close calibarion
                                            stepper1.move(maxRun) ;           // let the stepper close with maximum
                                          } else if (now > idleTimer + idleTime) {    // timer expired -> abort calibration
                                            State = sIdle ;
                                          }
                                          break ;
                                            case sCalibrateClose:               // calibrate closed position, end with keypress
                                          if (myBtn.wasPressed()) {
                                            closedPosition = setPosition = stepper1.currentPosition() ;
                                            saveStateL(closedPositionEE, closedPosition) ; // save closed position in EEPROM
                                            State = sIdle ;
                                            stepper1.moveTo(openPosition) ;       // return to open after calibration
                                          } else if (now > idleTimer + idleTime) {    // timer expired -> abort calibration
                                            State = sIdle ;
                                          }
                                          break ;
                                        default :
                                          break ;
                                        }
                                      // power off stepper if not running (no need to reenable))
                                      if (!stepper1.isRunning()){
                                        if (runStatus != Idle){               // there was a change in runningstatus, so report to controller
                                          setPosition = stepper1.currentPosition() ;    // store in EEPROM and report ready to controller
                                          saveStateL(setPositionEE, setPosition) ;
                                          send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ;
                                          runStatus = Idle ;
                                        }
                                        stepper1.disableOutputs();
                                      } else {
                                        runStatus = Running ;
                                      }
                                      stepper1.run();
                                      /*
                                      if (printTimer++ > now + printTime){
                                        printTimer = now ;
                                        Serial.println(stepper1.currentPosition());
                                        }
                                      */
                                    }
                                    
                                    // This is called when a message is received 
                                    void receive(const MyMessage &message) {
                                    // We only expect few types of messages from controller, check which
                                      switch (message.type) {
                                      case V_PERCENTAGE:
                                      // Curtain should be opened
                                      stepper1.moveTo(message.getInt() * (closedPosition - openPosition)/100);
                                      Serial.print("Message: "); Serial.print(message.sensor); Serial.print(" , value: % "); Serial.println( message.getInt());
                                      Serial.print("Moving to: "); Serial.println(message.getInt() * (closedPosition - openPosition)/100);
                                      break ;
                                      case V_STATUS:
                                      // Curtain should be opened or closed full
                                      stepper1.moveTo((message.getInt() == HIGH)?openPosition:closedPosition);
                                      Serial.print("Message - valid: ");
                                      Serial.print(message.sensor);
                                      Serial.print(" , value: % ");
                                      break ;
                                      default : 
                                      // not recognizable message
                                      Serial.print("Message - valid: ");
                                      Serial.print(message.sensor);
                                      Serial.print(", Unrecognized ");
                                      break ;
                                      }
                                      }
                                    
                                    1 Reply Last reply
                                    0
                                    Reply
                                    • Reply as topic
                                    Log in to reply
                                    • Oldest to Newest
                                    • Newest to Oldest
                                    • Most Votes


                                    21

                                    Online

                                    11.7k

                                    Users

                                    11.2k

                                    Topics

                                    113.1k

                                    Posts


                                    Copyright 2025 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
                                    • Login

                                    • Don't have an account? Register

                                    • Login or register to search.
                                    • First post
                                      Last post
                                    0
                                    • MySensors
                                    • OpenHardware.io
                                    • Categories
                                    • Recent
                                    • Tags
                                    • Popular