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 19.9k 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.
  • 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


                      7

                      Online

                      11.7k

                      Users

                      11.2k

                      Topics

                      113.0k

                      Posts


                      Copyright 2019 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