WIndow Roller Shutter - Position
-
Hello, I'm putting a automatic Window Roller Shutter at my home. I already have the system to get them up and down, and now is time to incorporate a MySensor Node. It's easy to get them up and down, just open and close a relay, but the hard part is to get the 10% open, or 50% closed (or open, depending on the point of view ). I was thinking on timing the open and close speed and make the math from there... but that seems like it will get it wrong eventually. Another solution would be to have some kind of sensors that would trigger checkpoints.
Any ideas? Any one implemented something like this?
Thank you all
-
hi,
what i did was to to check and recalculate/calibrate each time endstop was reached. I think most commercial devices work like this. I also had a current sensor (optional) to check motor current on endstops.
you can take a look at- my code here: https://github.com/scalz/MySensors-HW/tree/development/RollerShutterNode/FW/MyRollershutter
- or marvinroger lib is nice too: https://github.com/marvinroger/arduino-shutters
-
@soloam you could place a little magnet on the shaft of your roller shutter and read revolutions with a hall sensor. Then you will always know pretty accurate position of your sutter.
-
@scalz thank you for your replay, one qestion, what you use is somting like this?? :
https://www.ebay.com/itm/181026550454?rmvSB=true
https://www.ebay.com/itm/251558433854?rmvSB=trueI like the idea of the auto calibration, It would make this a lot accurate!
@monte thank you... that would be my second approach... probably an hybrid solution, use the magnets instead of the power sensor to determine the up and down positions.
Thank You
-
Ok I found your project in openhardware.io nice work
Thank you
-
what type of motor are you using? If it was a stepper, just count steps, but I doubt it would be that. Maybe you could make some kind of closed loop system with a rotary encoder which should give you pretty accurate position every time.
-
Visit the following https://github.com/marvinroger/arduino-shutters
I made one based on initial version of library, has been working for two years without any problems.
-
I have a similar situation: my house had manually operated roller shutters, where you had to keep the button pressed to open or close them.
Importantly when the end stop is reached, the motor is not powered in the moving direction anymore (probably some integrated reed contact or something). If this is the case for you, my solution will work well:
Just make a normal arduino relay (1 for every direction) and calibrate them with a timer. Then just make sure that you add some time for the opening direction (a few seconds), it will make sure that for every cycle any drift is compensated. This even worked for me when I had to reset the system with the rollers at a certain percentage => just move up and down a few times and it's ok again.
In my system I have implemented the manual switches (which used to switch the 220V by connecting them with the 5V node source and switching dedicated input pins on the arduino. I have two nodes, one with 3 roller shutters and one with 1 shutter and 1 awning.
(I think on the 3 up/down variant, there were not enough input pins for manual input, so I had all bound to one up-down switch).Here's my code (it has become somewhat messy, and if I remember correctly I changed from 100% open to 100% closed mode when changing from home assistant to openhab2):
/** The MySensors Arduino library handles the wireless radio link and protocol between your home built sensors/actuators and HA controller of choice. The sensors forms a self healing radio network with optional repeaters. Each repeater and gateway builds a routing tables in EEPROM which keeps track of the network topology allowing messages to be routed to nodes. Created by Henrik Ekblad <henrik.ekblad@mysensors.org> Copyright (C) 2013-2015 Sensnology AB Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors Documentation: http://www.mysensors.org Support Forum: http://forum.mysensors.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. ******************************* REVISION HISTORY Version 0.4 - October 13, 2017 - Powerpenguin Added dedicated inputs for all manual switches (This is possible for 2 up/down actuators: D2-D5 relay, D9-D13 nRF24, A0-A3=D14-D17: switches) Version 0.3 - October 9, 2017 - PowerPenguin DESCRIPTION Based on Dimmable LED Light using PWM from Henrik Ekblad Simple control of window shades which are already motorised and don't provide any more feedback, i.e. we want to send a target percentage from the controller to the shade which then moves as tgt_percentage * time_to_fully_close In the first implementation we assume that the starting state is fully open, so some "drift" is to be expected? Or "overshoot" opening by a margin to make sure 100% corresponds to fully open? (yes: home assistant takes % open, not % closed) The sketch supports multiple blinds, i.e. multiple relays connected to the arduino. There are two relays per blind, for up/down respectively Also, additional pins are read for a push-button, to manually give the up or down signal (will start/stop moving one or covers simultaneously, based on btcidmap[]) To avoid the relays coils from being powered in normal operation, and most relays are active low, high/low has been reversed (so high unless activated) (better buy active high relays!) The code has become somewhat messy, better to use interrupt on manual switch? Event based? */ // Enable debug prints to serial monitor #define MY_DEBUG // Enable and select radio type attached #define MY_RADIO_NRF24 //#define MY_RADIO_RFM69 #include <MySensors.h> #define SN "WindowShades" #define SV "0.4" #define PIN_BASE 2 // first of the Arduino pins attached to relay #define LOOP_DELAY 950 // loop delay in ms #define LOOP_DELAY_SMALL 30 // delay between transmissions for different sensors #define PIN_BASEA 14 // base of the analog pins to be used as digital input #define NCOV 2 // number of covers, each have an up and down relay; maximum is half the number of available digital i/o pins #define NCSW 2 // number of pairs (up/down) of manual override switches enum coverstates_enum {UP, DOWN, STOPPED}; static bool btmanup_pressed[NCSW]; static bool btmandn_pressed[NCSW]; bool initialValueSent[NCOV]; static int8_t currentLevel[NCOV]; static int8_t targetLevel[NCOV]; static int8_t time_open[NCOV]; static int8_t time_close[NCOV]; static unsigned long tstart[NCOV]; static int8_t lstart[NCOV]; // the manual button vs. CID mapping has to be specified manually, since sometimes we want one manual button to control more channels static int8_t btcidmap[NCOV] = {0, 1}; static coverstates_enum state[NCOV]; // number of values must be NCOV MyMessage msgPct[NCOV]; void setup() { // initialise all child actuators and related vars for (int8_t cid = 0; cid < NCOV; cid++) { msgPct[cid] = MyMessage(cid, V_PERCENTAGE); pinMode(PIN_BASE + 2 * cid, OUTPUT); pinMode(PIN_BASE + 2 * cid + 1, OUTPUT); state[cid] = STOPPED; initialValueSent[cid] = false; currentLevel[cid] = 100; targetLevel[cid] = 100; time_open[cid] = 29; // seconds to open, if not the same for all covers specify independently time_close[cid] = 27; // seconds to close } // specify times independently if not the same for all covers below time_close[0] = 40; time_open[0] = time_close[0] + 3; // overshoot to make sure we open completely, motor stops anyway time_close[1] = 26; time_open[1] = time_close[1] + 3; // overshoot to make sure we open completely, motor stops anyway // time_close[2] = 22; // time_open[2] = time_close[2] + 3; // overshoot to make sure we open completely, motor stops anyway for (int8_t i = 0; i < NCSW; i++) { pinMode(PIN_BASEA + 2 * i, OUTPUT); pinMode(PIN_BASE + 2 * i + 1, OUTPUT); btmanup_pressed[i] = false; btmandn_pressed[i] = false; } } void presentation() { // Register the Cover with the controller sendSketchInfo(SN, SV); for (int cid = 0; cid < NCOV; cid++) { present(cid, S_COVER); } } void loop() { int8_t cid; int8_t delta; for (cid = 0; cid < NCOV; cid++) { if (!initialValueSent[cid]) { Serial.println("Sending initial value"); send(msgPct[cid].set(currentLevel[cid])); Serial.println("Requesting initial value from controller"); request(cid, V_PERCENTAGE); wait(LOOP_DELAY); // extra delay to give more time for initial handshake } else { // toggle moving if manual button pressed (since we arrive heare about once a second, // we don't need debounce, but we need to press the button up to 1 s to have a reaction btmanup_pressed[btcidmap[cid]] = (bool)digitalRead(PIN_BASEA + 2 * btcidmap[cid]); btmandn_pressed[btcidmap[cid]] = (bool)digitalRead(PIN_BASEA + 2 * btcidmap[cid] + 1); // if both pressed, reset (no reaction) if (btmanup_pressed[btcidmap[cid]] && btmandn_pressed[btcidmap[cid]]) btmanup_pressed[btcidmap[cid]] = btmandn_pressed[btcidmap[cid]] = false; if (btmanup_pressed[btcidmap[cid]]) { btmanup_pressed[btcidmap[cid]] = false; if (state[cid] == STOPPED) { targetLevel[cid] = 100; } else { // if already moving, then stop by setting target to current currentLevel[cid]; } } if (btmandn_pressed[btcidmap[cid]]) { btmandn_pressed[btcidmap[cid]] = false; if (state[cid] == STOPPED) { // if (reference) not moving, then make go up targetLevel[cid] = 0; } else { // if already moving, then stop by setting target to current currentLevel[cid]; } } if (currentLevel[cid] > targetLevel[cid]) { // move down (close) => means decreasing pct! if (state[cid] != DOWN) { // we just started going down state[cid] = DOWN; tstart[cid] = millis(); lstart[cid] = currentLevel[cid]; // mstotgt[cid] = (currentLevel[cid] - targetLevel[cid]) * 10 * time_close[cid]; // remaining time to target } digitalWrite(PIN_BASE + 2 * cid, 0); // down pin digitalWrite(PIN_BASE + 2 * cid + 1, 1); // up pin // level change over elpased time, don't exceed targetLevel (will lead to control settle behaviour) currentLevel[cid] = max(lstart[cid] - (millis() - tstart[cid]) / (10 * time_close[cid]), targetLevel[cid]); Serial.print("Sending percentage feedback..."); Serial.println(currentLevel[cid]); send(msgPct[cid].set(currentLevel[cid])); } else if (currentLevel[cid] < targetLevel[cid]) { // move up (open) if (state[cid] != UP) { // we just started going up state[cid] = UP; tstart[cid] = millis(); lstart[cid] = currentLevel[cid]; // mstotgt[cid] = (currentLevel[cid] - targetLevel[cid]) * 10 * time_close[cid]; // remaining time to target } digitalWrite(PIN_BASE + 2 * cid, 1); // down pin digitalWrite(PIN_BASE + 2 * cid + 1, 0); // up pin // level change over elpased time, don't exceed targetLevel (will lead to control settle behaviour) currentLevel[cid] = min(lstart[cid] + (millis() - tstart[cid]) / (10 * time_open[cid]), targetLevel[cid]); Serial.print("Sending percentage feedback..."); Serial.println(currentLevel[cid]); send(msgPct[cid].set(currentLevel[cid])); } else if (currentLevel[cid] == targetLevel[cid]) { // we always stop when the level is reached // we don't change the state here, because we want to send a final level value to the controller first digitalWrite(PIN_BASE + 2 * cid, 1); // down pin digitalWrite(PIN_BASE + 2 * cid + 1, 1); // up pin if (state[cid] != STOPPED) send(msgPct[cid].set(currentLevel[cid])); state[cid] = STOPPED; } // Serial.println("Sending current level to controller"); // Serial.println(currentLevel[cid]); // Serial.println("targetLevel = "); // Serial.println(targetLevel[cid]); // if (state[cid] != STOPPED) send(msgPct[cid].set(currentLevel[cid])); // send current estimated percentage back to gateway, until stopped state reached // if (currentLevel[cid] != targetLevel[cid]) state[cid] = STOPPED; } wait(LOOP_DELAY_SMALL); } wait(LOOP_DELAY); } void receive(const MyMessage &message) { // the message sets the target value for each sensor. // as long as the target is not reached, the approriate relay pin is kept high // and the controller is informed every LOOP_DELAY ms about the progress // Serial.println("Message received from gateway (cid):"); // Serial.println(message.sensor); if (message.isAck()) { Serial.println("This is an ack from gateway"); } switch (message.type) { case V_PERCENTAGE: if (!initialValueSent[message.sensor]) { Serial.println("Receiving initial value from controller"); initialValueSent[message.sensor] = true; } targetLevel[message.sensor] = atoi(message.data); targetLevel[message.sensor] = targetLevel[message.sensor] > 100 ? 100 : targetLevel[message.sensor]; targetLevel[message.sensor] = targetLevel[message.sensor] < 0 ? 0 : targetLevel[message.sensor]; Serial.println("Received V_PERCENTAGE from controller"); break; case V_UP: targetLevel[message.sensor] = 100; Serial.println("Received V_UP from controller"); break; case V_DOWN: targetLevel[message.sensor] = 0; Serial.println("Received V_DOWN from controller"); break; case V_STOP: Serial.println("Received V_STOP from controller"); targetLevel[message.sensor] = currentLevel[message.sensor]; break; } }
Good luck!