Home Automation with self-learning 433 power switches

About This Project

This is my 2nd project with Cayenne. I already had a static 433 running but this Cayenne variant has the oppertunity to run without changing the sketch but do the further scheduling with events and triggers.

The sketch is also prepared for a Wemos D1 R2; the RTC seems to work, 433 looks alright but nothing happens. Any ideas? Feel free to comment.

What’s Connected

The project exits off:
• Arduino UNO
• W5100 LAN module
• 433-module
• RTC-module (DS3231)
• Self-learning power switches
Added functionality:
• Daylight Saving Time for CET
• Sunset/Sunrise calculation

The RTC-module (DS3231) is added for two reasons.
• Unaware if the time can be retrieved from Cheyenne
• Store in the alarm if we are on DST-time or not
Some lines are set to comment, you can use this section to reset your RTC-module and/or to set a new time.
In this section you also “give the “DTS_check the initial correct value; DST in CET is calculated a bit odd).

The Sunset/Sunrise is calculated with “TimeLord tardis” in combination with your location.
It also uses the variable “DST_check” to add 1 hour during summer.

The present time is calculated continuously but only send when it changes to the Cayenne cloud.
All three times are displayed in minutes on the dashboard but this is not really required.
Perhaps it is better to use the UNIX-time (but not sure If this is available).

The switches used are self-learning ones and are available under various names, to name a few:
Flaminco F500, Elro 8-serie and the cheapest from Action 3 for 10 euro.
The Action switches are also easy because they give you more time for programming the switch.
So far the enclosed remote control has pretty unique codes, I got 3 sets, all different.

The library is at the moment limited to 8 switch codes. I did some simplification to this library and made it more Cayenne friendly; It used to have a remote-unit key, I converted it to (virtual) single code key, condensed it a bit and added to the sketch the additional functions for V0 to V7.
Possible in the library something needs to be added to make sure the 433 is not continuous on (this might be the case at this moment).

Triggers & Alerts

The Present time, sunrise and sunset are used to calculate if it is night time or not:
if (time_now >= time_sunset or time_now <= time_sunrise) {
and saved as a digital sensor. This sensor is used in a trigger to switch light on/off at sunset/sunrise.

Scheduling

Sunset and sunrise is calculted daily at 2 AM.
Some lights will switch off at pumkin time (others on sunrise done with a trigger).

Dashboard Screenshots

The dashboard:


Trigger:

Photos of the Project

The hardware:

##The code
The code is in 4 tabs: Main tab:

 /*
Initally based upon a sketch from Nick Gammon
Colected various values for the swtiches and adapted for Cayenne By Wouter-Jan 
http://forum.arduino.cc/index.php?topic=129249.msg972860#msg972860
info Sodaq_DS3231.h https://github.com/SodaqMoja/Sodaq_DS3231
Info DS3231: http://www.dx.com/p/ds3231-at24c32-iic-high-precision-real-time-clock-module-for-arduino-3pcs-408743
There you can find a self-learning 433 libary in wording: https://forum.fhem.de/index.php/topic,36399.60.html
*/

// Libraries
#include <Wire.h>
#include <TimeLord.h> //timelord tardis libary for sunset/sunrise

int check_DST = 0;
int time_now = 0;
int time_sunset = 0;
int time_sunrise = 0;

//#define CAYENNE_DEBUG         // Uncomment to show debug messages
//#define CAYENNE_PRINT Serial  // Comment this out to disable prints and save space

#include <CayenneEthernet.h> // with W5100
/*
// with the ESP8266 module
#include "CayenneDefines.h"
#include "BlynkSimpleEsp8266.h"
#include "CayenneWiFiClient.h"
// Your network name and password.
char ssid[] = "***";
char password[] = "****";
*/
// Cayenne authentication token. This should be obtained from the Cayenne Dashboard.
char token[] = "***";

#include <DS3231.h> // RTC-module plus variables
DS3231 clock;
RTCDateTime dt;
RTCAlarmTime a1;

// Own library (Flaminco statements)
int rfPin = 7;                                     /* with ethernet shield D4 = SS (for SD card) */
#include "flaminco.h"

void setup()   {
  Cayenne.begin(token); //with W5100
  //Cayenne.begin(token, ssid, password); //with ESP8266
  Serial.begin(115200);
  Serial.println("info: sketch_433_Cayenne");
  Serial.print("compiled: ");
  Serial.print(__DATE__);
  Serial.print(" ");
  Serial.println(__TIME__);
  delay(1000);

  // Initialize DS3231
  Serial.println("Initialize DS3231");
  clock.begin();
/*
  // use this to reset your RTC and set the date/time to the compile values 
  // Disarm alarms and clear alarms for this example, because alarms is battery backed.
  // Under normal conditions, the settings should be reset after power and restart microcontroller.
  clock.armAlarm1(false);
  clock.armAlarm2(false);
  clock.clearAlarm1();
  clock.clearAlarm2();
  // Set sketch compiling time
  clock.setDateTime(__DATE__, __TIME__);
  // are in we daylight saving time? if yes set last value 1 else 0
  clock.setAlarm1(0, 0, 0, 0, DS3231_MATCH_H_M_S);
*/
  // retrieve alarm second as DST and store in check_DST
  a1 = clock.getAlarm1();
  check_DST = a1.second;
  Serial.println("---------------");
  dt = clock.getDateTime(); // show present time
  Serial.println(clock.dateFormat("d-m-Y H:i:s - l (w)", dt));
  Serial.println(check_DST); 
}

CAYENNE_IN(V0) // action switch lamp V0
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V0, 0); // ON
  } else {
    Flamingo_Send(V0, 1); // OFF
    }
  delay(3000);
}

CAYENNE_IN(V1) // action switch lamp V1
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V1, 0); // ON
  } else {
    Flamingo_Send(V1, 1); // OFF
    }
  delay(3000);
}

CAYENNE_IN(V2) // action switch lamp V2
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V2, 0); // ON
  } else {
    Flamingo_Send(V2, 1); // OFF
    }
  delay(3000);
}

CAYENNE_IN(V3) // action switch lamp V3
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V3, 0); // ON
  } else {
    Flamingo_Send(V3, 1); // OFF
    }
  delay(3000);
}

CAYENNE_IN(V4) // action switch lamp V4
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V4, 0); // ON
  } else {
    Flamingo_Send(V4, 1); // OFF
    }
  delay(3000);
}

CAYENNE_IN(V5) // action switch lamp V5
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V5, 0); // ON
  } else {
    Flamingo_Send(V5, 1); // OFF
    }
  delay(3000);
}

CAYENNE_IN(V6) // action switch lamp V6
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V6, 0); // ON
  } else {
    Flamingo_Send(V6, 1); // OFF
    }
  delay(3000);
}

CAYENNE_IN(V7) // action switch lamp V7
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V7, 0); // ON
  } else {
    Flamingo_Send(V7, 1); // OFF
    }
  delay(3000);
}

CAYENNE_IN(V8) // action switch lamp V8
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V8, 0); // ON
  } else {
    Flamingo_Send(V8, 1); // OFF
    }
  delay(3000);
}

CAYENNE_IN(V9) // action switch lamp V9
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V9, 0); // ON
  } else {
    Flamingo_Send(V9, 1); // OFF
    }
  delay(3000);
}

CAYENNE_IN(V10) // action switch lamp V10
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V10, 0); // ON
  } else {
    Flamingo_Send(V10, 1); // OFF
    }
  delay(3000);
}

CAYENNE_IN(V11) // action switch lamp V10
{
  if (getValue.asInt() == 1) {
    Flamingo_Send(V11, 0); // ON
  } else {
    Flamingo_Send(V11, 1); // OFF
    }
  delay(3000);
}

CAYENNE_OUT(V20) // calculate present time
{
  // covert present time in minutes (any other ideas?)
  dt = clock.getDateTime(); // show present time
  if (time_now != (dt.hour * 60 + dt.minute)) {
     time_now = dt.hour * 60 + dt.minute;
     Cayenne.virtualWrite(V20, time_now);
     if (time_now >= time_sunset or time_now <= time_sunrise) {
       Cayenne.virtualWrite(V25, 1);
       Serial.println("Night time = 1");
       } else {
       Cayenne.virtualWrite(V25, 0);
       Serial.println("Night time = 0");
       }
  }
}

CAYENNE_IN(V24) // calculate sunrise and sunset (calculate daily with event at 02:00) 
{
  sunsetrise();
  Cayenne.virtualWrite(V21, time_sunrise);
  Cayenne.virtualWrite(V22, time_sunset);
}

void loop(){
  Cayenne.run();
}

Tab: DST

    // inspired by: http://www.instructables.com/id/The-Arduino-and-Daylight-Saving-Time-Europe/
    // RTC: DS3231 
    // DST is a flag we set when DST starts and that we clear at the end of DST 
    // to avoid that the program starts correcting the time for a full hour and even getting in an endless loop 
    // when we set the clock back As October has 31 days, we know that the last Sunday will always fall from the 25th to the 31st
    // So our check at  will be as follows:
    // using the following variables:
    int DST() {
    Serial.println("Inside DST module");

    // the end of daylight saving
    if(dt.dayOfWeek == 0 && dt.month == 10 && dt.day >= 25 && dt.hour == 3 && check_DST != 0){
      dt = clock.getDateTime();
      clock.setDateTime(dt.year,dt.month,dt.day,2,dt.minute,dt.second);
      clock.setAlarm1(0, 0, 0, 0, DS3231_MATCH_H_M_S);
      a1 = clock.getAlarm1();
      check_DST = a1.second;
      Serial.println("Daylight saving time set to OFF");
      }
     
    //To start summertime/daylightsaving time on the last Sunday in March
    if(dt.dayOfWeek == 0 && dt.month == 3 && dt.day >= 25 && dt.hour == 2 && check_DST != 1){
      dt = clock.getDateTime();
      clock.setDateTime(dt.year, dt.month, dt.day, 2, dt.minute, dt.second);
      clock.setAlarm1(0, 0, 0, 1, DS3231_MATCH_H_M_S);
      a1 = clock.getAlarm1();
      check_DST = a1.second;
      Serial.println("Daylight saving time set to ON");
      } 
    }

Tab libary “flaminco.h”
******************************************************************************
** Flamingo_send **
** **
** Flamingo device commands (on/off)hard coded (not deciphered yet) **
** code is 28 byte long. To transfer it in 32 bit code 4 0 **
** is added to the left. For programming reason hex code representation
*
** of device code is used. E.g. Remote 0,C,On: **
** code 28 bits: 0001100110111111110100100110 **
** code 32 bits: 00011001101111111101001001100000 **
** Hex: 19BFD260 **
** **
** “Sync” 1 pulse High, 15 pulse low **
** “1” 1 pulse High, 3 pulse low **
** “0” 3 pulse High, 1 pulse low **
** “end” N/A **
** **
** For internal ref: Unit 0 = white remote , unit 1 = black remote **
** **
*****************************************************************************/
/
Assigned:
*
* Flamingo_Send(y, z) = y->remote 0 - 12 , z-> 0=on, 1=off
/
// Compiler only coding
#define FUNITS 8 /
Amount of units supported per remote /
#define FCMDN 2 /
Amount of CMD 0 = On, 1 = Off */

// define device codes:

uint32_t fdev[FUNITS][FCMDN] = {0xD9762A10, 0xDAA47850, /* Remote 0 */ 
                                0xDBDA22E0, 0xDBA27220, /* Remote 1 */
                                0x19BFD260, 0x195EEAA0, /* Remote 2 */
                                0x984CC650, 0x9A8C1050, /* Remote 3 */
                                0xDBFFFE90, 0xD91CEF10, /* Remote 4 */
                                0xDBC52FA0, 0xD9E35160, /* Remote 5 */
                                0x19B0FE60, 0x19682B20, /* Remote 6 */
                                0x9924E7D0, 0x9BA928D0, /* Remote 7 */                                        
           };                                         


// Define Flamingo_send variables / constants

int fpulse = 300;                              /* Pulse witdh in microseconds */
int fretrans = 5;                              /* Code retransmission         */
uint32_t fsendbuff;
uint32_t fdatabit;
uint32_t fdatamask = 0x80000000;

void Flamingo_Send(int funitc, int fcmd)
{

 // Test if used codes are valid

 if ((funitc < 0) || (funitc >  11)) {                  // check if unit code between 1-2 (Remote 0 of 1)
   Serial.print("Unit error: ");
   Serial.println(funitc);
   return;
 }

 if ((fcmd < 0) || (fcmd > 1)) {                       // check if command = 0 (off) or 1 (on)
   Serial.print("Command error: ");
   Serial.println(fcmd);
   return;
 }
 
 //End test used codes
 //*
 Serial.println("Send Flamingo command ");
 Serial.print("Flamingo Unit = :");
 Serial.println(funitc);
 Serial.print("command = :");
 Serial.println(fcmd);
 Serial.println();
 //*/

 // Send Command

 for (int nRepeat = 0; nRepeat <= fretrans; nRepeat++) {

   fsendbuff = fdev[funitc][fcmd];

   // send SYNC 1P High, 15P low
   Serial.println("Send sync");

   digitalWrite(rfPin, HIGH);
   delayMicroseconds(fpulse * 1);
   digitalWrite(rfPin, LOW);
   delayMicroseconds(fpulse * 15);

   // end send SYNC

   // Send command

   for (int i = 0; i < 28; i++)                                 // Flamingo command is only 28 bits */
   {
     // read data bit
     fdatabit = fsendbuff & fdatamask;                         // Get most left bit
     fsendbuff = (fsendbuff << 1);                             // Shift left

     if (fdatabit != fdatamask)
     { // Write 0
       digitalWrite(rfPin, HIGH);
       delayMicroseconds(fpulse * 3);
       digitalWrite(rfPin, LOW);
       delayMicroseconds(fpulse * 1);
     }
     else
     { // Write 1
       digitalWrite(rfPin, HIGH);
       delayMicroseconds(fpulse * 1);
       digitalWrite(rfPin, LOW);
       delayMicroseconds(fpulse * 3);
     }
   }
 }
}

Tab: setsunrise

    void sunsetrise(){

    float const LONGITUDE = 4.26;
    float const LATITUDE = 52.08;

      TimeLord tardis; 
      tardis.TimeZone(60+check_DST*60); // tell TimeLord what timezone your RTC is synchronized to. You can ignore DST
      // as long as the RTC never changes back and forth between DST and non-DST
      tardis.Position(LATITUDE, LONGITUDE); // tell TimeLord where in the world we are
      //byte today[] = {  0, 0, 12, 21, 1, 2016    }; // store manual date (at noon) in an array for TimeLord to use
      byte today[] = {  0, 0, 12, dt.day, dt.month, dt.year   }; // store RTC date (at noon) in an array for TimeLord to use

    if (tardis.SunRise(today)) // if the sun will rise today (it might not, in the [ant]arctic)
       {
       Serial.print("Sunrise: ");
       print2digits(today[tl_hour]);
       Serial.print(":");
       print2digits(today[tl_minute]);
       time_sunrise = today[tl_hour] * 60 + today[tl_minute];
       Serial.print(" -> time_sunrise: ");
       Serial.println(time_sunrise);
       }
       if (tardis.SunSet(today)) // if the sun will set today (it might not, in the [ant]arctic)
       {
       Serial.print("Sunset:  ");
       print2digits(today[tl_hour]);
       Serial.print(":");   
       print2digits(today[tl_minute]);
       time_sunset = today[tl_hour] * 60 + today[tl_minute];
       Serial.print(" -> time_sunset : ");
       Serial.println(time_sunset);
       }
    }

Last tab is not important:

void print2digits(int number) {
  if (number < 10) {
     Serial.print("0"); // print a 0 before if the number is < than 10
     }
     Serial.print(number);
}

A bit lenghty;)

1 Like

Hi
Interesting project.
Could you please share your code?

Tadeas

here you go

Thank You