Skip to content

Instantly share code, notes, and snippets.

@petewill
Created December 6, 2020 13:41
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save petewill/ac31b186291743e046f83497de0ffa87 to your computer and use it in GitHub Desktop.
Save petewill/ac31b186291743e046f83497de0ffa87 to your computer and use it in GitHub Desktop.
MySensors Dooya Blind Control Code
/*
// 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 <henrik.ekblad@gmail.com> 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