@Lucas-van-rossum
Hi,
I had a similar challenge with my rollershutters and I think most standard ones do exactly the same, they have one neutral pin and two for the directions (up, down). I started a few months ago building the first prototype which was controlled via WIFI and worked very well. I did not find much examples and all was build up myself. However WIFI didn't seem to be the best and I did not want to pollute my wireless therefore I searched for alternatives which I found here. Still struggling with the stability of the sensor network but besides that I am very happy with my solution.
I also thought that I need to build an intelligent device which drives the shutters up, down and to intermediate positions. Played a lot with timers and stuff and eventually found the arduino-shutters of marvinroger with which I wasted another few days until I gave up and found out the code does not work for me - at least the latest version. Someone (I guess it was you) pointed out that earlier release seems to be working.
By the time I decided, that I do not want any intelligence in my hardware and instead put the logic into the controller (I am using openhab). There it is quite easy to work with threads and move one down for 20 sec and then stop. It even shows me the position where it is when driving from the physical buttons. Another point which played a role, my shutters need different time for up and down (up mostly 54sec, down 50) and I do have various variations. some of the rollos only take 35 sec and my automated windows even just 25 sec for a full cycle. And instead of making the code more complicated and potentially send an update or setup parameter via the air to the note, I maintain it centrally in the controller.
I have build shutter controllers for 1, 2 and 3 shutters on one board and all have the same logic.
I do need to have full control via the physical buttons all the time. So when the controller decides to send them down and I do not want, I can go to the swich and stop it immediately. So basically, my logic is checking the pysical state of the swiches and what is sent from contoller and whatever is latest wins.
I also thought maybe I should implement a possibility to block the controller from activation and overrule it by e.g. having the physical button pressed in but then I did not want to have them all the time in neutral or blocking the controller activation.
Anyhow, my code is based on the relay example. It needs some cleanup for the physical button evaluations but I was too lazy to do this as most of my shutters are single nodes anyway and I have only 2 with 2 shutters on one board and 1 with 3. So for the moment there is some spaghettiy code.
I am not working with timers, delays etc. in order to find the end spots, the shutters stop anyway at their end if properly installed and the physical buttons do not do anything differently - so why bother? I only have delays of 200ms when going from up to down etc. So basically before I trigger the relay for "down" I disable the one for "up" wait for 200ms and then trigger "down" just making sure there will never be power on both directions to save my motors.
And finally, in order to "save" my relays I trigger from my controller a "stop" every now and then to all shutters in the house. In worst case, it might interfere with one rule but I can live with that.
Here is the code based on mysensors for a node with 6 relays for 3 rollershutters attached to physical buttons:
/**
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 1.0 - Henrik Ekblad
DESCRIPTION
Example sketch showing how to control physical relays.
This example will remember relay state after power failure.
http://www.mysensors.org/build/relay
*/
#include <MySigningNone.h>
#include <MyTransportNRF24.h>
#include <MyTransportRFM69.h>
#include <MyHwATMega328.h>
#include <MySensor.h>
#include <SPI.h>
#define RELAY_1 3 // Arduino Digital I/O pin number for first relay (second on pin+1 etc)
#define NUMBER_OF_RELAYS 6 // Total number of attached relays
#define RELAY_ON 1 // GPIO value to write to turn on attached relay
#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
MyMessage sensorMsg0(0, V_DIMMER);
MyMessage sensorMsg1(1, V_DIMMER);
MyMessage sensorMsg2(2, V_DIMMER);
static int currentLevel0 = 0; // Current dim level...
static int currentLevel1 = 0; // Current dim level...
int upPin0 = 14; // choose the input pin (for a up)
int downPin0 = 15; // choose the input pin (for a down)
int motorUp0 = 0; // variable for reading the pin status
int motorDown0 = 0; // variable for reading the pin status
int upPin1 = 16; // choose the input pin (for a up)
int downPin1 = 17; // choose the input pin (for a down)
int motorUp1 = 0; // variable for reading the pin status
int motorDown1 = 0; // variable for reading the pin status
int upPin2 = 18; // choose the input pin (for a up)
int downPin2 = 19; // choose the input pin (for a down)
int motorUp2 = 0; // variable for reading the pin status
int motorDown2 = 0; // variable for reading the pin status
int switchState0 = 0; //0=off, 1=up, 2=down
int tempSwitchState0 = 0; //0=off, 1=up, 2=down
int rUnitPos0 = 0;
int switchState1 = 0; //0=off, 1=up, 2=down
int tempSwitchState1 = 0; //0=off, 1=up, 2=down
int rUnitPos1 = 0;
int switchState2 = 0; //0=off, 1=up, 2=down
int tempSwitchState2 = 0; //0=off, 1=up, 2=down
int rUnitPos2 = 0;
// NRFRF24L01 radio driver (set low transmit power by default)
MyTransportNRF24 radio(RF24_CE_PIN, RF24_CS_PIN, RF24_PA_LEVEL_GW);
//MyTransportRFM69 radio;
// Message signing driver (none default)
//MySigningNone signer;
// Select AtMega328 hardware profile
MyHwATMega328 hw;
// Construct MySensors library
MySensor gw(radio, hw);
void shuttersUp(int shutter)
{
if (shutter == 0) {
Serial.println("A Shutter 0 going up.");
digitalWrite (RELAY_1 + 1, LOW);
delay(100);
digitalWrite (RELAY_1, HIGH);
gw.send( sensorMsg0.set(0) );
}
else if (shutter == 1){
Serial.println("A Shutter 1 going up.");
digitalWrite (RELAY_1 + 3, LOW);
delay(100);
digitalWrite (RELAY_1 + 2, HIGH);
gw.send( sensorMsg1.set(0) );
}
else if (shutter == 2){
Serial.println("A Shutter 2 going up.");
digitalWrite (RELAY_1 + 5, LOW);
delay(100);
digitalWrite (RELAY_1 + 4, HIGH);
gw.send( sensorMsg2.set(0) );
}
}
void shuttersDown(int shutter)
{
if (shutter == 0) {
Serial.println("A Shutter 0 going down.");
digitalWrite (RELAY_1, LOW);
delay(100);
digitalWrite (RELAY_1 + 1, HIGH);
gw.send( sensorMsg0.set(100) );
}
else if (shutter == 1){
Serial.println("A Shutter 1 going down.");
digitalWrite (RELAY_1 + 2, LOW);
delay(100);
digitalWrite (RELAY_1 + 3, HIGH);
gw.send( sensorMsg1.set(100) );
}
else if (shutter == 2){
Serial.println("A Shutter 2 going down.");
digitalWrite (RELAY_1 + 4, LOW);
delay(100);
digitalWrite (RELAY_1 + 5, HIGH);
gw.send( sensorMsg2.set(100) );
}
}
void shuttersStop(int shutter)
{
if (shutter == 0) {
Serial.println("A Shutter 0 halted.");
digitalWrite (RELAY_1, LOW);
digitalWrite (RELAY_1 + 1, LOW);
}
else if (shutter == 1){
Serial.println("A Shutter 1 halted.");
digitalWrite (RELAY_1 + 2, LOW);
digitalWrite (RELAY_1 + 3, LOW);
}
else if (shutter == 2){
Serial.println("A Shutter 2 halted.");
digitalWrite (RELAY_1 + 4, LOW);
digitalWrite (RELAY_1 + 5, LOW);
}
}
void setup()
{
pinMode(upPin0, INPUT); // declare pushbutton as input
pinMode(downPin0, INPUT); // declare pushbutton as input
pinMode(upPin1, INPUT); // declare pushbutton as input
pinMode(downPin1, INPUT); // declare pushbutton as input
pinMode(upPin2, INPUT); // declare pushbutton as input
pinMode(downPin2, INPUT); // declare pushbutton as input
// Initialize library and add callback for incoming messages
gw.begin(incomingMessage, AUTO, true);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("RollerShutter Atrium", "1.0");
// Fetch relay status
for (int sensor = 1, pin = RELAY_1; sensor <= NUMBER_OF_RELAYS; sensor++, pin++) {
// Register all sensors to gw (they will be created as child devices)
gw.present(sensor, S_COVER);
// Then set relay pins in output mode
pinMode(pin, OUTPUT);
}
}
void loop()
{
// Alway process incoming messages whenever possible
gw.process();
motorUp0 = digitalRead(upPin0); // read input value
motorDown0 = digitalRead(downPin0); // read input value
//relay 2
motorUp1 = digitalRead(upPin1); // read input value
motorDown1 = digitalRead(downPin1); // read input value
motorUp2 = digitalRead(upPin2); // read input value
motorDown2 = digitalRead(downPin2); // read input value
//evaluate switch positions
// GROUP 0
if (motorUp0 == LOW && motorDown0 == HIGH) {
switchState0 = 1;
//Serial.println("SWITCH: " + String(switchState0)); //switch position
}
else if (motorDown0 == LOW && motorUp0 == HIGH) {
switchState0 = 2;
//Serial.println("SWITCH: " + String(switchState0)); //switch position
}
else if (motorDown0 == HIGH && motorUp0 == HIGH)
{
switchState0 = 0;
//Serial.println("SWITCH: " + String(switchState0)); //switch position
}
// GROUP 1
if (motorUp1 == LOW && motorDown1 == HIGH) {
switchState1 = 1;
//Serial.println("SWITCH: " + String(switchState1)); //switch position
}
else if (motorDown1 == LOW && motorUp1 == HIGH) {
switchState1 = 2;
//Serial.println("SWITCH: " + String(switchState1)); //switch position
}
else if (motorDown1 == HIGH && motorUp1 == HIGH)
{
switchState1 = 0;
//Serial.println("SWITCH: " + String(switchState1)); //switch position
}
// GROUP 2
if (motorUp2 == LOW && motorDown2 == HIGH) {
switchState2 = 1;
//Serial.println("SWITCH: " + String(switchState2)); //switch position
}
else if (motorDown2 == LOW && motorUp2 == HIGH) {
switchState2 = 2;
//Serial.println("SWITCH: " + String(switchState2)); //switch position
}
else if (motorDown2 == HIGH && motorUp2 == HIGH)
{
switchState2 = 0;
//Serial.println("SWITCH: " + String(switchState2)); //switch position
}
//GROUP 0
if (tempSwitchState0 != switchState0) //man only needed in case of changed switch state
{
if (motorUp0 == LOW && rUnitPos0 != 1) //man up
{
switchState0 = 1;
tempSwitchState0 = 1;
delay(200);
shuttersUp(0);
rUnitPos0 = 1;
Serial.println("//////////////man 0 up");
gw.send( sensorMsg0.set(0) );
}
if (motorDown0 == LOW && rUnitPos0 != 2) // man down
{
switchState0 = 2;
tempSwitchState0 = 2;
delay(200);
shuttersDown(0);
rUnitPos0 = 2;
Serial.println("//////////////man 0 down");
gw.send( sensorMsg0.set(100) );
}
if (motorUp0 == HIGH && motorDown0 == HIGH && rUnitPos0 != 0) //man stopp
{
switchState0 = 0;
tempSwitchState0 = 0;
delay(200);
shuttersStop(0);
rUnitPos0 = 0;
Serial.println("//////////////man 0 stopp");
}
}
//GROUP 1
if (tempSwitchState1 != switchState1) //man only needed in case of changed switch state
{
if (motorUp1 == LOW && rUnitPos1 != 1) //man up
{
switchState1 = 1;
tempSwitchState1 = 1;
shuttersUp(1);
rUnitPos1 = 1;
Serial.println("//////////////man 1 up");
gw.send( sensorMsg1.set(0) );
}
if (motorDown1 == LOW && rUnitPos1 != 2) // man down
{
switchState1 = 2;
tempSwitchState1 = 2;
shuttersDown(1);
rUnitPos1 = 2;
Serial.println("//////////////man 1 down");
gw.send( sensorMsg1.set(100) );
}
if (motorUp1 == HIGH && motorDown1 == HIGH && rUnitPos1 != 0) //man stopp
{
switchState1 = 0;
tempSwitchState1 = 0;
shuttersStop(1);
rUnitPos1 = 0;
Serial.println("//////////////man 1 stopp");
}
}
//GROUP 2
if (tempSwitchState2 != switchState2) //man only needed in case of changed switch state
{
if (motorUp2 == LOW && rUnitPos2 != 1) //man up
{
switchState2 = 1;
tempSwitchState2 = 1;
shuttersUp(2);
rUnitPos2 = 1;
Serial.println("//////////////man 2 up");
gw.send( sensorMsg2.set(0) );
}
if (motorDown2 == LOW && rUnitPos2 != 2) // man down
{
switchState2 = 2;
tempSwitchState2 = 2;
shuttersDown(2);
rUnitPos2 = 2;
Serial.println("//////////////man 2 down");
gw.send( sensorMsg2.set(100) );
}
if (motorUp2 == HIGH && motorDown2 == HIGH && rUnitPos2 != 0) //man stop
{
switchState2 = 0;
tempSwitchState2 = 0;
shuttersStop(2);
rUnitPos2 = 0;
Serial.println("//////////////man 2 stopp");
}
}
}
void incomingMessage(const MyMessage &message) {
// Let's check for the message type.
if (message.type == V_DIMMER) {
// Retrieve the power or dim level from the incoming request message
int requestedLevel = atoi( message.data );
// Adjust incoming level if this is a V_LIGHT variable update [0 == off, 1 == on]
requestedLevel *= ( message.type == V_LIGHT ? 100 : 1 );
// Clip incoming level to valid range of 0 to 100
requestedLevel = requestedLevel > 100 ? 100 : requestedLevel;
requestedLevel = requestedLevel < 0 ? 0 : requestedLevel;
Serial.print( "Changing level to " );
Serial.print( requestedLevel );
Serial.print( ", from " );
Serial.println("NodeID:" + String(message.sensor) + " " + currentLevel0 );
if (requestedLevel == 0) {
// move up
shuttersUp(message.sensor);
}
else if (requestedLevel == 100) {
// move down
shuttersDown(message.sensor);
}
else {
// move percentage
// Serial.println("currentLevel" + String(shutters.currentLevel()));
Serial.println("move to desired postion");
//NOT IMPLEMENTED HERE
}
currentLevel0 = requestedLevel;
}
else if (message.type == V_UP) {
shuttersUp(message.sensor);
Serial.println("going up:" + String(message.type));
}
else if (message.type == V_DOWN) {
shuttersDown(message.sensor);
Serial.println("going down:" + String(message.type));
}
else if (message.type == V_STOP) {
shuttersStop(message.sensor);
Serial.println("stopping:" + String(message.type));
}
else {
Serial.println("wrong data received:" + String(message.type));
}
}
Cheers
SJ