Created
December 6, 2020 13:41
-
-
Save petewill/ac31b186291743e046f83497de0ffa87 to your computer and use it in GitHub Desktop.
MySensors Dooya Blind Control Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
// 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. | |
// | |
// DESCRIPTION | |
// This sketch provides a way to control blinds from www.blinds.com using a 433MHz RF | |
// signal. The motors in the blinds are Dooya DV24CE motors. | |
// See https://forum.mysensors.org/topic/7/controlling-blinds-com-rf-dooya-motors-with-arduino-and-vera | |
// for more info. | |
// | |
// The sketch is based on Henrik Ekblad's <[email protected]> MySensors project | |
// (http://www.mysensors.org). Credit also goes to Ray (http://rayshobby.net/?p=3381) | |
// for instruction on how to decode the RF signal from the remote as well as code for | |
// sending the RF signal. | |
// Developed by PeteWill. | |
// | |
// REVISION HISTORY | |
// Version 1.0 - March 19, 2014 - Original Program | |
// Version 1.1 - April 17, 2014 - Added support for multiple remotes that are programmed from blinds.com | |
// Version 1.2 - May 16, 2014 - Added gw.send() to update Vera blinds up/down status | |
// Version 1.3 - Nov 21, 2014 - Upgraded code to work with MySensors v1.4 | |
// Version 1.4 - Oct 2, 2015 - Changed code to work as a repeater node | |
// Version 1.5 - June 20, 2016 - Updated code to work with MySensors v1.5 | |
// Version 1.6 - December 6, 2020 - Update code to work with MySensors v2.x | |
*/ | |
// Enable debug prints to serial monitor | |
//#define MY_DEBUG //MySensors debug messages | |
//#define LOCAL_DEBUG //Code specific debug messages | |
#define SKETCH_NAME "Blind Control" | |
#define SKETCH_VERSION "1.6" | |
//MySensors configuration options | |
#define MY_RADIO_RF24 // Enable and select radio type attached | |
#define MY_RF24_PA_LEVEL RF24_PA_HIGH //Options: RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX | |
//#define MY_RF24_CHANNEL 73 | |
//#define MY_NODE_ID 1 //Manually assign a node ID. Comment out to auto assign. | |
//#define MY_TRANSPORT_WAIT_READY_MS 3000 //This will allow the thermostat to function if it can't find the gateway when it first starts up | |
//#define MY_SLEEP_TRANSPORT_RECONNECT_TIMEOUT_MS 2000 //How long the node will try to establish a connection before going to sleep, default is 10s. | |
//#define MY_PARENT_NODE_ID 0 // AUTO | |
//#define MY_PARENT_NODE_IS_STATIC | |
#define MY_REPEATER_FEATURE //Create repeating node | |
//Include MySensors related libraries | |
#include <MySensors.h> | |
//Define Constants | |
#define SEND_DATA 3 //Data pin for RF Transmitter | |
#define ZERO_HIGH 395 //Delay for the high part of a 0 in microseconds | |
#define ZERO_LOW 687 //Delay for the low part of a 0 in microseconds | |
#define ONE_HIGH 750 //Delay for the high part of a 1 in microseconds | |
#define ONE_LOW 333//Delay for the low part of a 1 in microseconds | |
#define DWELL_TIME 50 //Time to delay but still process repeating messages | |
/* | |
//List all your blinds here. These will have to be added as child nodes in setup() | |
//The numbers will be used to assign the different remotes in the remote() method | |
//So, make a note of which blind uses which remote then add it to the if statement | |
//in remote(). This is referred to as the blindNumber in remote(). | |
*/ | |
#define NUMBER_OF_BLINDS 9 | |
//Child Node Numbers | |
//Family Room = Node 1, Remote 2, Channel 1 | |
//Kitchen = Node 2, Remote 2, Channel 2 | |
//Dining Room = Node 3, Remote 2, Channel 3 | |
//Bedroom 1 = Node 4, Remote 1, Channel 1 | |
//Bedroom 2 = Node 5, Remote 1, Channel 2 | |
//Guest Room = Node 6, Remote 1, Channel 3 | |
//Master Bedroom = Node 7, Remote 1, Channel 4 | |
//Master Closet = Node 8, Remote 1, Channel 5 | |
//Living Room = Node 9, Remote 2, Channel 4 | |
/* | |
//These 28 standard bits appear at the beginning of each transmit sequence: | |
//0111011100000101010111001011. They are then followed by 12 other | |
//bits depending on the command being sent to the blind. These bits | |
//distinguish between the different remotes. | |
//Because I'm not good at Arduino coding I needed to use someone else's | |
//code to send the bits. They only used 8 bits and I couldn't get any | |
//more to send. Because if this I have broken up the 28 bits into 8 bit | |
//sections. Make sure to put 4 zeros at the beginning of the first | |
//sequence. They will be ignored later in the code. | |
//I added support for multiple remotes so you don't have to reprogram | |
//anything when you buy more blinds. Just add the additional remote codes. | |
*/ | |
//Remote One | |
unsigned char remote1Bits1 = 0b00000111; //integer value of the 28 bit standard sequence referenced above. "0b" prefix is for ?? | |
unsigned char remote1Bits2 = 0b01110000; | |
unsigned char remote1Bits3 = 0b01010101; | |
unsigned char remote1Bits4 = 0b11001011; | |
//Remote Two | |
unsigned char remote2Bits1 = 0b00000111; //integer value of the 28 bit standard sequence referenced above. "0b" prefix is for ?? | |
unsigned char remote2Bits2 = 0b01110000; | |
unsigned char remote2Bits3 = 0b01011101; | |
unsigned char remote2Bits4 = 0b11111110; | |
//Remote codes will be put in standardBits with remote() method, depending on which remote is used | |
unsigned char standardBits1 = 0b00000000; | |
unsigned char standardBits2 = 0b00000000; | |
unsigned char standardBits3 = 0b00000000; | |
unsigned char standardBits4 = 0b00000000; | |
MyMessage blindMsg(0, V_PERCENTAGE); | |
#ifdef LOCAL_DEBUG | |
#define dbg(...) Serial.print(__VA_ARGS__) | |
#define dbgln(...) Serial.println(__VA_ARGS__) | |
#else | |
#define dbg(x) | |
#define dbgln(x) | |
#endif | |
void presentation() | |
{ | |
// Send the sketch version information to the gateway | |
sendSketchInfo(SKETCH_NAME, SKETCH_VERSION); | |
wait(DWELL_TIME); | |
// Register sensors to gw (they will be created as child devices) | |
for (uint8_t i = 0; i < NUMBER_OF_BLINDS; i++) { | |
present(i + 1, S_COVER); | |
wait(DWELL_TIME); | |
} | |
} | |
void setup() { | |
} | |
void loop() { | |
} | |
void receive(const MyMessage &message) { | |
dbg("Blind Channel: "); | |
dbgln(message.sensor); | |
dbg("Message Data: "); | |
dbgln(message.data); | |
dbg("Message Type: "); | |
dbgln(message.type); | |
int incomingBlindData = atoi(message.data); | |
if (message.type == V_STOP) { //Stop | |
//unsigned char i; | |
for (uint8_t i = 0; i < 2; i++) { | |
blindAction(message.sensor, 3); //blindAction(channel, action) action: 1=up, 2=down, 3=stop | |
wait(DWELL_TIME); | |
} | |
dbgln("STOP command"); | |
} | |
else if (incomingBlindData == 100 || message.type == V_UP) { //100 = Open/Up | |
//unsigned char i; | |
for (uint8_t i = 0; i < 2; i++) { | |
blindAction(message.sensor, 1); | |
wait(DWELL_TIME); | |
} | |
dbgln("UP command"); | |
send(blindMsg.setSensor(message.sensor).set(100)); // Update controller with status of blinds (up/down) | |
} | |
else if (incomingBlindData == 0 || message.type == V_DOWN) { //0 = Closed/Down | |
//unsigned char i; | |
for (uint8_t i = 0; i < 2; i++) { | |
blindAction(message.sensor, 2); | |
wait(DWELL_TIME); | |
} | |
dbgln("DOWN command"); | |
send(blindMsg.setSensor(message.sensor).set(0)); // Update controller with status of blinds (up/down) | |
} | |
} | |
void remote(int remoteNum) { | |
if (remoteNum == 1) { //Which remote will be used? | |
standardBits1 = remote1Bits1; //Assign remote specific codes to standardBits variable used throughout the code | |
standardBits2 = remote1Bits2; | |
standardBits3 = remote1Bits3; | |
standardBits4 = remote1Bits4; | |
} | |
else { | |
standardBits1 = remote2Bits1; //Assign remote specific codes to standardBits variable used throughout the code | |
standardBits2 = remote2Bits2; | |
standardBits3 = remote2Bits3; | |
standardBits4 = remote2Bits4; | |
} | |
} | |
void fourBits(unsigned char bits) { | |
unsigned char i; | |
int delayTime; | |
for (i = 0; i < 4; i++) { | |
int highTime; | |
int lowTime; | |
delayTime = ((bits >> (3 - i)) & 1 ? 1 : 0); | |
if (delayTime == 1) { | |
highTime = ONE_HIGH; | |
lowTime = ONE_LOW; | |
} | |
else { | |
highTime = ZERO_HIGH; | |
lowTime = ZERO_LOW; | |
} | |
digitalWrite(SEND_DATA, HIGH); | |
delayMicroseconds(highTime); | |
digitalWrite(SEND_DATA, LOW); | |
delayMicroseconds(lowTime); | |
} | |
} | |
void eightBits(unsigned char bits) { | |
unsigned char k; | |
int delayTime; | |
for (k = 0; k < 8; k++) { | |
int highTime; | |
int lowTime; | |
delayTime = ((bits >> (7 - k)) & 1 ? 1 : 0); | |
if (delayTime == 1) { | |
highTime = ONE_HIGH; | |
lowTime = ONE_LOW; | |
} | |
else { | |
highTime = ZERO_HIGH; | |
lowTime = ZERO_LOW; | |
} | |
digitalWrite(SEND_DATA, HIGH); | |
delayMicroseconds(highTime); | |
digitalWrite(SEND_DATA, LOW); | |
delayMicroseconds(lowTime); | |
} | |
} | |
//Separator Delay Method (this is repeated frequently) | |
void separatorDelay(boolean upDown) { | |
if (upDown == true) { | |
digitalWrite(SEND_DATA, LOW); | |
delayMicroseconds(8020); | |
} | |
digitalWrite(SEND_DATA, HIGH); | |
delayMicroseconds(4812); | |
digitalWrite(SEND_DATA, LOW); | |
delayMicroseconds(1479); | |
} | |
void endDelay() { | |
digitalWrite(SEND_DATA, LOW); | |
delayMicroseconds(51895); //Time of delay at the end of each sequence | |
} | |
void blindAction(int child, int a) { | |
//c or channel: Order on the remote from left to right 1-16 available | |
//a or action: 1=up, 2=down, 3=stop | |
char channel; | |
//Assign remote based on Child_ID | |
if (child == 1 || child == 2 || child == 3 || child == 9) { //Check for child IDs that use remote 2 | |
remote(2); | |
} | |
else { | |
remote(1); | |
} | |
//Assign channel based on Child_ID | |
if (child == 1 || child == 4) { | |
channel = 0b00000001; | |
} | |
else if (child == 2 || child == 5) { | |
channel = 0b00000010; | |
} | |
else if (child == 3 || child == 6) { | |
channel = 0b00000011; | |
} | |
else if (child == 7 || child == 9) { | |
channel = 0b00000100; | |
} | |
else { | |
channel = 0b00000101; | |
} | |
unsigned char action; //8 action bits. Only the first 4 bits are used in the up/down end sequence | |
unsigned char action2; //Last 4 bits from the up/down end sequence | |
if (a == 1) { | |
action = 0b00010001; //code for up | |
action2 = 0b00001110; | |
} | |
else if (a == 2) { | |
action = 0b00110011; | |
action2 = 0b00001100; | |
} | |
else { | |
action = 0b01010101; | |
} | |
int i = 0; | |
//first 6 transmissions are the same for each blind action (up, down & stop) | |
while (i < 6) { | |
separatorDelay(false); //false unless in the last part of the up or down commands | |
fourBits(standardBits1); | |
eightBits(standardBits2); | |
eightBits(standardBits3); | |
eightBits(standardBits4); | |
fourBits(channel); | |
eightBits(action); | |
i++; | |
} | |
if (a == 3) { //If a stop command is issued just send the end delay then exit the method | |
endDelay(); | |
} | |
else { //No stop issued so run through the last sequence | |
separatorDelay(false); //send true because we are in the up/down end sequence so there is an additional delay | |
fourBits(standardBits1); | |
eightBits(standardBits2); | |
eightBits(standardBits3); | |
eightBits(standardBits4); | |
fourBits(channel); | |
fourBits(action); | |
fourBits(action2); | |
int j = 0; | |
while (j < 3) { | |
separatorDelay(true); | |
fourBits(standardBits1); | |
eightBits(standardBits2); | |
eightBits(standardBits3); | |
eightBits(standardBits4); | |
fourBits(channel); | |
fourBits(action); | |
fourBits(action2); | |
j++; | |
} | |
endDelay(); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment