Curtain Control Node.
-
@barduino I tried downloading the code, it works. Anyways I am pasting the code below.
For end stops I have not yet decided. I had some Ideas of using Hall effect sensor on both ends and a small piece of magnet in the string to detect ends or Maybe use stop switches. Not sure but will have to try both and see which works the best.
I am yet to clean the code I have some debugging serial prints in code which will remove once finalized.
#include <SPI.h> #include <MySensor.h> int motor_forward = 5; int motor_reverse = 6; int LDRinput = 1; //analog pin to which LDR is connected, here we set it to 0 so it means A0 int LDRValue = 0; //that�s a variable to store LDR values int light_sensitivity = 500; //This is the approx value of light surrounding your LDR int stopSwLeft = 4; int stopSwRight = 3; int stopSwNow = 2; int CHILD_CURTAIN_ID =1; int lastState; MySensor gw; MyMessage msg_S_COVER_U(CHILD_CURTAIN_ID, V_UP); MyMessage msg_S_COVER_D(CHILD_CURTAIN_ID, V_DOWN); MyMessage msg_S_COVER_S(CHILD_CURTAIN_ID, V_STOP); //MyMessage msg(CHILD_CURTAIN_ID,V_UP); // the setup routine runs once when you press reset: void setup() { Serial.begin(115200); // initialize the digital pin as an output for L239D. pinMode(motor_forward, OUTPUT); pinMode(motor_reverse, OUTPUT); // initialize the digital pin as an output for Stop Switches. pinMode(stopSwLeft, INPUT_PULLUP); pinMode(stopSwRight, INPUT_PULLUP); pinMode(stopSwNow, INPUT_PULLUP); gw.begin(incomingMessage, AUTO, true); // Send the sketch version information to the gateway and Controller gw.sendSketchInfo("Window_Curtain", "1.0"); gw.present(CHILD_CURTAIN_ID, S_COVER); } void loop(){ gw.process(); } void cover(int coverVal){ //int coverVal = gw.loadState(CHILD_CURTAIN_ID); Serial.print("Cover is : "); lastState = coverVal; switch (coverVal) { case 0: Serial.println("Opening"); while (lastState == coverVal) { m_left(); gw.process(); checkHWInnputs(); coverVal = gw.loadState(CHILD_CURTAIN_ID); } gw.send(msg_S_COVER_U.set(V_UP)); break; case 1: Serial.println("Closing"); while (lastState == coverVal) { m_right(); gw.process(); checkHWInnputs(); coverVal = gw.loadState(CHILD_CURTAIN_ID); } gw.send(msg_S_COVER_D.set(V_DOWN)); break; case 2: Serial.println("Idle"); while (lastState == coverVal) { m_stop(); gw.process(); checkHWInnputs(); coverVal = gw.loadState(CHILD_CURTAIN_ID); } gw.send(msg_S_COVER_S.set(V_STOP)); break; } return; } void incomingMessage(const MyMessage &message) { // We only expect one type of message from controller. But we better check anyway. Serial.println("recieved incomming message"); switch (message.type) { case V_UP: gw.saveState(CHILD_CURTAIN_ID, 0); Serial.print("Incoming change for ID_S_COVER:"); Serial.print(message.sensor); Serial.print(", New status: "); Serial.println("V_UP"); cover(gw.loadState(CHILD_CURTAIN_ID)); Serial.print("Done cover procedure"); //m_right(); break; case V_DOWN: gw.saveState(CHILD_CURTAIN_ID, 1); Serial.print("Incoming change for ID_S_COVER:"); Serial.print(message.sensor); Serial.print(", New status: "); Serial.println("V_DOWN"); cover(gw.loadState(CHILD_CURTAIN_ID)); Serial.print("Done cover procedure"); //m_left(); break; case V_STOP: gw.saveState(CHILD_CURTAIN_ID, 2); Serial.print("Incoming change for ID_S_COVER:"); Serial.print(message.sensor); Serial.print(", New status: "); Serial.println("V_STOP"); cover(gw.loadState(CHILD_CURTAIN_ID)); Serial.print("Done cover procedure"); //m_stop(); break; } Serial.print("exiting incoming message"); return; } // the loop routine runs over and over again forever: void m_right() { digitalWrite(motor_forward, HIGH); //terminal D1 will be HIGH digitalWrite(motor_reverse, LOW); //terminal D2 will be LOW Serial.print("Set For:High and Rev: Low"); exit; } void m_left() { digitalWrite(motor_forward, LOW); //terminal D1 will be LOW digitalWrite(motor_reverse, HIGH); //terminal D2 will be HIGH Serial.print("Set For:Low and Rev: High"); exit; } void m_stop() { digitalWrite(motor_forward, LOW); //terminal D1 will be LOW digitalWrite(motor_reverse, LOW); //terminal D2 will be HIGH Serial.print("Set For:Low and Rev: Low"); exit; } void checkHWInnputs() { if (digitalRead(stopSwNow) == LOW) { Serial.println("Detected stop button push. Stopping"); cover(2); Serial.println("Saved stop state to EEPROM"); } if (digitalRead(stopSwLeft) == LOW) { Serial.println("Detected stop button push. Stopping"); cover(1); Serial.println("Saved stop state to EEPROM"); } if (digitalRead(stopSwRight) == LOW) { Serial.println("Detected stop button push. Stopping"); cover(0); Serial.println("Saved stop state to EEPROM"); } }I'm just curious, maybe its the arduino IDE version or something.
I'm using 1.6.4 and I get this:
However opening with a text editor is fine:
Cheers
-
I'm just curious, maybe its the arduino IDE version or something.
I'm using 1.6.4 and I get this:
However opening with a text editor is fine:
Cheers
@barduino Seems like the IDE. Open any example MySensors Sketch. Delete the contents from sketch and copy paste from my sketch. I guess that should work.
Are you able to compile any other sketch in Arduino?
-
@AWI Are you able to share the sketch you used with the UL2003 driver board? I have tried to combine the MySensors servo sketch with other arduino only sketches utilizing the UL2003, but without luck. Thanks.
-
@AWI Are you able to share the sketch you used with the UL2003 driver board? I have tried to combine the MySensors servo sketch with other arduino only sketches utilizing the UL2003, but without luck. Thanks.
@patchmaster I used the relay sketch and modified it to my needs. Sorry but I dont have any sketch for ULN2003. :disappointed:
-
@AWI Are you able to share the sketch you used with the UL2003 driver board? I have tried to combine the MySensors servo sketch with other arduino only sketches utilizing the UL2003, but without luck. Thanks.
@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 ; } } -
@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 ; } }@AWI Thanks. I have been using the Accelstepper library, but this has already given me several other ideas.
I appreciate the help. -
@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 ; } }@AWI
I have been searching for something like this for a while, thank you for the code!But i have some problems compiling it :\
C:\Users\ToreR\Documents\Arduino\stepper_mysensor\stepper_mysensor.ino: In function 'void saveStateL(int, long int)': stepper_mysensor:74: error: 'saveState' was not declared in this scope saveState(EEposition + y , convLongInt.LongByte[y]) ; ^ C:\Users\ToreR\Documents\Arduino\stepper_mysensor\stepper_mysensor.ino: In function 'long int loadStateL(int)': stepper_mysensor:81: error: 'loadState' was not declared in this scope convLongInt.LongByte[y] = loadState(EEposition + y) ; ^ C:\Users\ToreR\Documents\Arduino\stepper_mysensor\stepper_mysensor.ino: In function 'void presentation()': stepper_mysensor:129: error: 'present' was not declared in this scope present(CHILD_ID, S_COVER, "Curtain"); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP ^ stepper_mysensor:132: error: 'sendSketchInfo' was not declared in this scope sendSketchInfo(SN, SV); ^ C:\Users\ToreR\Documents\Arduino\stepper_mysensor\stepper_mysensor.ino: In function 'void loop()': stepper_mysensor:195: error: 'send' was not declared in this scope send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ; ^ exit status 1 'saveState' was not declared in this scope``` -
@AWI
I have been searching for something like this for a while, thank you for the code!But i have some problems compiling it :\
C:\Users\ToreR\Documents\Arduino\stepper_mysensor\stepper_mysensor.ino: In function 'void saveStateL(int, long int)': stepper_mysensor:74: error: 'saveState' was not declared in this scope saveState(EEposition + y , convLongInt.LongByte[y]) ; ^ C:\Users\ToreR\Documents\Arduino\stepper_mysensor\stepper_mysensor.ino: In function 'long int loadStateL(int)': stepper_mysensor:81: error: 'loadState' was not declared in this scope convLongInt.LongByte[y] = loadState(EEposition + y) ; ^ C:\Users\ToreR\Documents\Arduino\stepper_mysensor\stepper_mysensor.ino: In function 'void presentation()': stepper_mysensor:129: error: 'present' was not declared in this scope present(CHILD_ID, S_COVER, "Curtain"); // Window Cover sub-type, commands: V_UP, V_DOWN, V_STOP ^ stepper_mysensor:132: error: 'sendSketchInfo' was not declared in this scope sendSketchInfo(SN, SV); ^ C:\Users\ToreR\Documents\Arduino\stepper_mysensor\stepper_mysensor.ino: In function 'void loop()': stepper_mysensor:195: error: 'send' was not declared in this scope send( percentageMsg.set((100 * setPosition)/(closedPosition - openPosition))) ; ^ exit status 1 'saveState' was not declared in this scope```@Tore-André-Rosander are you using the development branch? The errors seem to indicate that is the case.
-
@Tore-André-Rosander are you using the development branch? The errors seem to indicate that is the case.
@AWI Thank you, that helped :)
I now have it up and running.But how do i connect and use this with a controller (like Domoticz)?
I use an ESP gateway (from the dev branch) and the curtain node is connected and showing up in domoticz.
But how do i make domoticz send commands to the curtain node?All i get when adding the curtain node both as on/off button and blinds in domoticz is "unreconized" in the curtain serial monoitor.
-
@AWI Thank you, that helped :)
I now have it up and running.But how do i connect and use this with a controller (like Domoticz)?
I use an ESP gateway (from the dev branch) and the curtain node is connected and showing up in domoticz.
But how do i make domoticz send commands to the curtain node?All i get when adding the curtain node both as on/off button and blinds in domoticz is "unreconized" in the curtain serial monoitor.
@Tore-André-Rosander I have defined it as "Blinds Percentage" in Domoticz. This send the V_STATUS and V_PERCENTAGE messages where the node should respond to...
-
@Tore-André-Rosander I have defined it as "Blinds Percentage" in Domoticz. This send the V_STATUS and V_PERCENTAGE messages where the node should respond to...
@AWI THanks again for your fast replys!
This is what i get in serial monitor when i use the "up" "down" buttons for blinds percentage in domoticz.
This is my very first MySensor setup so it might be some error with my setup, i will have a deeper look at it later tonight.
Starting sensor (RNNNA-, 2.0.0-beta) Radio init successful. State read: -1 State read: -1 send: 13-13-0-0 s=255,c=3,t=15,pt=0,l=2,sg=0,st=ok: send: 13-13-0-0 s=255,c=0,t=17,pt=0,l=10,sg=0,st=ok:2.0.0-beta send: 13-13-0-0 s=255,c=3,t=6,pt=1,l=1,sg=0,st=ok:0 read: 0-0-13 s=255,c=3,t=15,pt=0,l=2,sg=0: read: 0-0-13 s=255,c=3,t=6,pt=0,l=1,sg=0:M send: 13-13-0-0 s=1,c=0,t=5,pt=0,l=7,sg=0,st=ok:Curtain send: 13-13-0-0 s=255,c=3,t=11,pt=0,l=18,sg=0,st=ok:Curtain control 13 send: 13-13-0-0 s=255,c=3,t=12,pt=0,l=3,sg=0,st=ok:1.0 Init complete, id=13, parent=0, distance=1 read: 0-0-13 s=1,c=1,t=29,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=30,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=29,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=30,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=29,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=30,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=29,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=30,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=29,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=30,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized``` -
@AWI THanks again for your fast replys!
This is what i get in serial monitor when i use the "up" "down" buttons for blinds percentage in domoticz.
This is my very first MySensor setup so it might be some error with my setup, i will have a deeper look at it later tonight.
Starting sensor (RNNNA-, 2.0.0-beta) Radio init successful. State read: -1 State read: -1 send: 13-13-0-0 s=255,c=3,t=15,pt=0,l=2,sg=0,st=ok: send: 13-13-0-0 s=255,c=0,t=17,pt=0,l=10,sg=0,st=ok:2.0.0-beta send: 13-13-0-0 s=255,c=3,t=6,pt=1,l=1,sg=0,st=ok:0 read: 0-0-13 s=255,c=3,t=15,pt=0,l=2,sg=0: read: 0-0-13 s=255,c=3,t=6,pt=0,l=1,sg=0:M send: 13-13-0-0 s=1,c=0,t=5,pt=0,l=7,sg=0,st=ok:Curtain send: 13-13-0-0 s=255,c=3,t=11,pt=0,l=18,sg=0,st=ok:Curtain control 13 send: 13-13-0-0 s=255,c=3,t=12,pt=0,l=3,sg=0,st=ok:1.0 Init complete, id=13, parent=0, distance=1 read: 0-0-13 s=1,c=1,t=29,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=30,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=29,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=30,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=29,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=30,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=29,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=30,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=29,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized read: 0-0-13 s=1,c=1,t=30,pt=0,l=0,sg=0: Message - valid: 1, Unrecognized```@Tore-André-Rosander Nothing wrong with you interpretation. I have not written this sketch as a complete S_COVER implementation. Only the V_PERCENTAGE part (not V_UP/V_DOWN). If you define it in Domoticz as "Blinds percentage" (see my previous post) it will work. Sorry for the confusion.
-
@Tore-André-Rosander Nothing wrong with you interpretation. I have not written this sketch as a complete S_COVER implementation. Only the V_PERCENTAGE part (not V_UP/V_DOWN). If you define it in Domoticz as "Blinds percentage" (see my previous post) it will work. Sorry for the confusion.
@AWI Seems like my problem is with Domoticz itselfe.
I have the same problem as described in this post https://www.domoticz.com/forum/viewtopic.php?f=6&t=7748 -
@AWI Seems like my problem is with Domoticz itselfe.
I have the same problem as described in this post https://www.domoticz.com/forum/viewtopic.php?f=6&t=7748@Tore-André-Rosander You can probably solve it by deleting the sensor in Domoticz and let the sketch present itself as S_DIMMER (and use another sensor id)
-
Maybe its because i have not calibrated it? I dont have any pushbuttons laying around, so im gonna make one with some wires. But i see that there is two pins defined as buttons. Should i use the buttonPin1 as VCC?
So it would look something like buttonPin1 -> buttonPin2 -> button -> gnd?
-
Maybe its because i have not calibrated it? I dont have any pushbuttons laying around, so im gonna make one with some wires. But i see that there is two pins defined as buttons. Should i use the buttonPin1 as VCC?
So it would look something like buttonPin1 -> buttonPin2 -> button -> gnd?
@Tore-André-Rosander You can ignore buttonpin2. Just connect your "button" to buttonpin1 and ground.
-
Looks good. I am curious to see the details... jealous for the speed :runner: your curtain is moving.
I have been working on a similar project last week. The mechanics and ´looks´ are the most difficult part for me.
For now I have used a ´tuned´ cheap & small stepper which makes it slooow but effective ;-) The rail and mounting is standard IKEA. The pro-mini has the jModule attached. Works like a charm (/snail)!
-
@AWI I'm curious where you got the rails for the curtain. I wasn't been able to find them, but stumbled on this topic.
-
I'm currently working on a similar project, only I got started with 24V DC motors that I got from my work (left overs from a test project) These motors are very strong (way way over rated) and have a big gearbox what unfortunately makes the movement a bit slow.
I started off with 1 motor to move 2 curtains at the same time, but will change this to 2 motors for either side since the better half of me likes to control the curtains separately in case she only wants to close 1.
Next to the automation there will also be a manual control switch be hidden behind the curtains in case the node is down and I'm not at home to fix the issue...
all running on an ESP8266 (ESP-12e) unit to have it over WiFi and have enough ioCurrently the test setup is working fine and I'm ordering parts to start implementing this a bit bigger in the first room. the test setup I made with some old wood 1 meter of rail and a few old rugs as curtains...

I use a GT2 timing belt that is commently used for 3D printers and pully's are widely available as well as the belts in different lengths (I order them on rolls of 10 meters) and attached the timing belt to a carriage that is normally to pull the curtains with a rod.

the motor is mounted on a bracket with currently 2 end stop switches for open and close (but will be 1 switch for open and 1 for close in the final solution)

resulting in driving the curtain from 1 side


electronics are working fine now and need to be ported to a test print and later to a real PCB design

