 
					
						
					
				
				
					@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 ;
  }
  }