Home Domotics

Hi all, after due consideration I’d like to share my home domotics project. I’ve been working at it for several months (on and off of course) and I think it reached a stage in which I can present something to show some features of Blynk which might be of use to other folks.

First of all, here is an image of how the setup looks like:

The idea behind this is to not automate everything, but rather small parts of things. I will post the code too here (too it’s quite long, sorry about that :smiley: )

The Arduino Mega sets two lights according to measurements taken from the LDR’s (averaged values). When it’s daytime everything is supposed to be off, when it’s evening or early morning the LED light attached to the Arduino goes on and when it’s night, the LED from an ESP-01 will turn on so you can find the bathroom.

The other ESP-01 controls two relays. These are manually operated, but act on the same bridge widget as the LED ESP (hence the use of the Virtual Buttons). The whole Mega, LCD and DHT are built into a little cardboard box underneath the TV. The DHT11 displays the temperature too (that was the thing I was least worried about, lol).

For now it operates a bit wonky, but I suspect it is because I take way too much power with the direct attached LED lights. Power MOSFET’s are on the way to fix that issue. It may also be responsible for the wonky way the Bridge widget operates. It seems to loose connection to the ESP-01’s sometimes. After a reset of the Mega everything is fine, but about 10 hours later it doesn’t respond to commands anymore. Ah well, that is the next challenge, but for now, it does more or less what it is supposed to do.

So, without further ado, here’s 600+ lines of code for you to enjoy!

/****************************************************************************************
 * Changelog:
 * v0.1 initial setup
 * v0.2 using simple timer instead of calling functions from main loop
 * v0.3 removed NRF modules to see if it affects stability
 * v0.4 New tiny box in blue!
 * v0.5 Rewriting whole codebase
 * v0.6 Removed IF and switched it for Case with seperate functions
 *      Added manual IP, DHCP is messing up because lease expires and no new lease is given away (I think...)
 * 
 * ToDo:
 * - Plugs for lights/other periphals (DB9)
 * - Separate plugs for LDR?
 * - ...
 * - Optimize code:
 *  + EEPROM usage?
 *  + Clean up widgets/vPins (still missing vPins from IOS bug)
 *  + Document code ...
 *    * VOID doStuff()          Runs LDR readings
 *    * VOID calculateStuff()   Creates the LDR values for doStuff (averaged value of all LDR's normalized), global var
 *    * VOID initLCD()          Init LCD with fancy animation of letters (shiny!)
 *    * VOID doDHT()            Reads DHT sensor readings every 10s and sets LCD info
 *    * INT  giveBackTime()     Get time of day in 0, 1 and 2 (day, evening, night)
 *    * VOID setLEDs()          Set LED's based on time of day
 *    * VOID setLamp1(<0|1>)    Set Lamp1 relay on/off (1 or 0)
 *    * VOID setLamp3(<0|1>)    Set Lamp3 on DB-9 on/off (1 or 0)
 *  - Light measurements (curtains closed, dark-ish)
 *   + Big lights on  + Lizard ~ 100
 *   + Night ~ 134
 *   + Lizard ~ 120
 ****************************************************************************************/

// Local Physical Pin assignments

/****************************************************************************************
 * A0, A1, A2 = LDR's
 * 3, 5, 6    = PWM output for, currently, bottle lamp, needs to be made into a DB9 pin with more options for PMW
 *              and maybe also multiple plugs for more lamps and/or other stuff (relays?)
 * 13 - 10    = DB9-1 output (2, 3, 4, 5 & 8 (GND) )
 * 22 - 28    = Future relays via DB9?
 * 23         = DHT11 temp/humid
 * SDA        = I2C Display SDA (Data)
 * SCK        = I2C Display SCK (Clock)
 * 50         = (MI)SO Ethernet
 * 51         = (MO)SI Ethernet
 * 52         = SCK Ethernet
 * 53         = CS Ethernet
 ****************************************************************************************/

// Local Virtual Pin assignments

/****************************************************************************************
 * 31         = Bridge widget
 * 20 - 29    = LED's for dashboard
 * 10, 11     = Manual control Living room lights
 * 13, 14     = Humidity/temperature from DHT11
 * 15         = Set Lamp3 to manual-mode
 * 16         = Master override switch to prevent light timers from running
 * 17         = Software reset Arduino
 * 18         = Reset Lamp3 to auto-mode
 ****************************************************************************************/

// Remote (Virtual) Pin assignments

/****************************************************************************************
 * (V)24, (V)25     = Boxed ESP-01 relays living room lights
 * (V)23            = Hallway LED strip
 ****************************************************************************************/
 
// Includes and general stuff

#include <UIPEthernet.h>                  // Ethernet lib
#include <BlynkSimpleUIPEthernet.h>       // Blynk Ethernet add-ons
#include <Wire.h>                         // Wire for I2C communications (Matrix display)
#include <LiquidCrystal_I2C.h>            // Matrix display
#include <SimpleTimer.h>                  // Timer lib
#include "DHT.h"

SimpleTimer timer;

/****************************************************************************************
 * Blynk Stuff
 ****************************************************************************************/

// #define BLYNK_PRINT Serial
// #define BLYNK_DEBUG

// Local Raspi, user iphone

char auth[] = "(Auth A)";     // Dashboard 1

byte arduino_mac[] = { 0xBF, 0x4D, 0x32, 0xCB, 0xF3, 0x0D };
IPAddress server_ip  (  192, 168,   0,  25   );
IPAddress arduino_ip (  192, 168,   0,  51   );
IPAddress dns_ip     (    8,   8,   8,  8    );
IPAddress gateway_ip (  192, 168,   0,  254  );
IPAddress subnet_mask(  255, 255, 255,  0    );

WidgetLED autolightsLED(20);         // Auto mode on/off
WidgetLED hallwayLED(21);            // Night light on/off
WidgetLED lamp1LED(22);              // Living room lamps (relay with two lamps)
WidgetLED lamp2LED(23);              // Spare 230v plug
WidgetLED lamp3LED(24);              // Little tree

WidgetBridge lights(31);             // Bridge to (LED) lamps

// Min/max values and light corrections
int lightsNight       = 125;
int lightsEvening     = 90;
int lightsCorrection  = 120;

// Master override, skips all timer stuffs
bool masterOverride = 0;

// Overrides for lights/plugs/whatevers
bool lamp3Button;
bool lamp3Manual = 0;   // Defaults to auto light

/****************************************************************************************
 * LCD Matrix display
 ****************************************************************************************/

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

char text1[] = {'I','n','i','t','i','a','l','i','z','i','n','g'};
char text2[] = {'M','e','g','a','D','o','m','o',' ','v','0','.','6'};
char text3[] = {'C','o','n','n','e','c','t','i','n','g','.','.','.'};

/****************************************************************************************
 * LDR
 ****************************************************************************************/

int lampAverage;    // Averaged value all three LDR's

/* Enable for global vars if we need to use it for direct attached lamps
int lightRed;       // Averaged value LDR 1
int lightGreen;     // Averaged value LDR 2
int lightBlue;      // Averaged value LDR 3
*/

byte timerDoStuffShort  = timer.setInterval(10000L,   doStuff);         // Short interval for checking if lights are off
byte timerDoStuffLong   = timer.setInterval(300000L,  doStuff);         // Long interval to prevent light from flickering
byte timerDHT           = timer.setInterval(10000L,   doDHT);           // DHT readings
byte timerLEDs          = timer.setInterval(1000L,    setLEDs);         // Set LED's every 2 seconds for indicators
byte timerCalculate     = timer.setInterval(10000L,   calculateStuff);  // Calculate LDR outcome continiously

/****************************************************************************************
 * DHT11 sensor
 ****************************************************************************************/

#define DHTPIN 23
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

// First 230v output with Living room lights attached
BLYNK_WRITE(V10)
{
  int lamp1Button = param.asInt();
  if(lamp1Button == 1)  { setLamp1(1); }
  else                  { setLamp1(0); }
  
  doStuff();
}

// Second 230v output underneath the couch for something else, manual only a.t.m.
BLYNK_WRITE(V11)
{
  int lamp2Button = param.asInt();
  if(lamp2Button == 1)  { setLamp2(1); }
  else                  { setLamp2(0); }

  doStuff();
}

// Little tree attached to DB9
BLYNK_WRITE(V15)
{
  lamp3Button = param.asInt();
  lamp3Manual = 1;
  
  if(lamp3Button == 1)  { setLamp3(1); }
  else                  { setLamp3(0); }
  
  doStuff();
}

BLYNK_WRITE(V16)
{
  masterOverride = param.asInt();
  autolightsLED.off();
}

void(* resetFunc) (void) = 0;

BLYNK_WRITE(V17)
{
  resetFunc();
}

BLYNK_WRITE(V18)
{
  lamp3Manual = 0;
  doStuff();
}

void setup() 
{
  // Serial output for debugs
  // Serial.begin(9600);
 
  // DHT 11
  dht.begin();
  
  // Init LCD display (16x2)
  lcd.begin(16,2);
  lcd.backlight();

  initLCD();

  // Start Blynk
  Blynk.begin(auth, server_ip, 8442, arduino_ip, dns_ip, gateway_ip, subnet_mask, arduino_mac);

  // Wait for Blynk to connect
  while (Blynk.connect() == false) { }

  // Auth token(s) for remote projects via WidgetBridge
  lights.setAuthToken("Auth B");

  // Timers for LDR actions
  timer.enable(timerDoStuffShort);
  timer.disable(timerDoStuffLong);

  // LEDs off
  autolightsLED.off();
  hallwayLED.off();
  lamp1LED.off();
  lamp2LED.off();
  lamp3LED.off();
  
  pinMode(A0, INPUT); // LDR input
  pinMode(A1, INPUT); // LDR input
  pinMode(A2, INPUT); // LDR input

  // These four go to the DB9 on the back for a LED light or something
  pinMode(13, OUTPUT); // RGB / LED
  pinMode(12, OUTPUT); // RGB / LED
  pinMode(11, OUTPUT); // RGB / LED
  pinMode(10, OUTPUT); // RGB / LED
}

void loop() 
{
  Blynk.run();  
  timer.run();
}

void doStuff()
{
  // Execute procedure to figure out the time of day
  int statusTime = giveBackTime();
  
  // If master override button is on, skip the rest of the function
  if(masterOverride == 1)
  {
    return;
  }
  
  // Act according to time of day (day, evening or night)
  switch (statusTime)
  {
    // ****** Day ******
    case 0:
      if(timer.isEnabled(timerDoStuffLong))
      {
        timer.enable(timerDoStuffShort);
        timer.disable(timerDoStuffLong);
      }

      setLEDs();
      
      // Hallway off
      lights.virtualWrite(V23, LOW);
      
      // DB-9, tree goes off, if button is pushed turn it on anyway, regardless of manual status
      if(lamp3Manual == 0)
      {
        setLamp3(0);      // Light is set to auto, turn it off
      }
      else if((lamp3Manual == 1) && (lamp3Button == 1))
      {
        lamp3Manual = 1;  // Keep state to manual
        setLamp3(1);      // Turn on lamp
      }
      else
      {
        lamp3Manual = 0;  // Set state back to auto because lamp is turned off anyway
        setLamp3(0);      // Keep it off and keep auto state on
      }
      
      break;
      
    // ****** Evening/Morning ******
    case 1:
      if(timer.isEnabled(timerDoStuffShort))
      {
        timer.disable(timerDoStuffShort);
        timer.enable(timerDoStuffLong);
      }

      setLEDs();
      
      // Hallway off
      lights.virtualWrite(V23, LOW);
      
      // DB-9, tree goes on, set manual flag to off and turn on lamp
      if(lamp3Manual == 0)
      {
        setLamp3(1);      // Light is set to auto, turn it on and keep automatic on
      }
      else if((lamp3Manual == 1) && (lamp3Button == 1))
      {
        lamp3Manual = 0;  // Set state to automatic again
        setLamp3(1);      // Turn on lamp
      }
      else
      {
        setLamp3(0);
      }
      
    break;
      
    // ****** Night ******
    case 2:
      if(timer.isEnabled(timerDoStuffLong))
      {
        timer.enable(timerDoStuffShort);
        timer.disable(timerDoStuffLong);
      }

      setLEDs();
      
      // Hallway on
      lights.virtualWrite(V23, HIGH);

      // DB-, tree goes off, unless manual is on
      if(lamp3Manual == 0)
      {
        setLamp3(0);  // Light is set to auto, turn it off and keep automatic on
      }
      else if((lamp3Manual == 1) && (lamp3Button == 1))
      {
        setLamp3(1);      // Keep on lamp and keep manual state
      }
      else
      {
        lamp3Manual = 0;  // Set state back to automatic
        setLamp3(0);      // Turn lamp off
      }

      break;
  }
}

void calculateStuff()
{
  // Here we delete the highest and lowest value to account for reading mistakes. It makes for a better average later on

  // Number of readings you want to take
  const byte readings = 9;
  
  // Start up arrays for taking measurements
  int valueRed[readings];
  int valueGreen[readings];
  int valueBlue[readings];

  // Read analog ports
  for(int i=0;i<readings;i++)
  {
    valueRed[i]   = analogRead(A0);
    valueGreen[i] = analogRead(A1);
    valueBlue[i]  = analogRead(A2);
    delay(10);
  }

  // Subtract highest and lowest values from readings 
  byte maxIndex = 0;
  byte minIndex = 0;
  
  int max = valueRed[maxIndex];
  int min = valueRed[minIndex];

  for(int i=0;i<readings;i++)
  {
     if (max<valueRed[i])
     {
       max = valueRed[i];
       maxIndex = i;
     }

     if (min>valueRed[i])
     {
       min = valueRed[i];
       minIndex = i;
     }       
  }

  maxIndex = 0;
  minIndex = 0;

  max = valueGreen[maxIndex];
  min = valueGreen[minIndex];

  for(int i=0;i<readings;i++)
  {
    if (max<valueGreen[i])
    {
      max = valueGreen[i];
      maxIndex = i;
    }

    if (min>valueGreen[i])
    {
      min = valueGreen[i];
      minIndex = i;
    } 
  }

  maxIndex = 0;
  minIndex = 0;
  
  max = valueBlue[maxIndex];
  min = valueBlue[minIndex];
  
  for(int i=0;i<readings;i++)
  {
    if (max<valueBlue[i])
    {
      max = valueBlue[i];
      maxIndex = i;
    }

    if (min>valueBlue[i])
    {
      min = valueBlue[i];
      minIndex = i;
    }  
  }

  // Reset total values
  int totaleValueRed    = 0;
  int totaleValueGreen  = 0;
  int totaleValueBlue   = 0;

  // Calculate total values per sensor
  for(int i=0;i<readings;i++)
  {
    totaleValueRed    = totaleValueRed + valueRed[i];
    totaleValueGreen  = totaleValueGreen + valueGreen[i];
    totaleValueBlue   = totaleValueBlue + valueBlue[i];
  }

  // Average out sensors
  int averageValueRed     = (totaleValueRed - (valueRed[minIndex] + valueRed[maxIndex])) / (readings - 2);
  int averageValueGreen   = (totaleValueGreen - (valueGreen[minIndex] + valueGreen[maxIndex])) / (readings - 2);
  int averageValueBlue    = (totaleValueBlue - (valueBlue[minIndex] + valueBlue[maxIndex])) / (readings - 2); 

  // Map values to something which we can use with analogWrite()
  int lightRed    = map(averageValueRed, 0, 1023, 0, 255);
  int lightGreen  = map(averageValueGreen, 0, 1023, 0, 255);
  int lightBlue   = map(averageValueBlue, 0, 1023, 0, 255);

  // Correct light
  int correctionRed     = lightRed - lightsCorrection;
  int correctionGreen   = lightGreen - lightsCorrection;
  int correctionBlue    = lightBlue - lightsCorrection;
  
  // Correct light values with a given variable and constrain to sensible values
  int correctedLightRed     = constrain(correctionRed, 0, 255);
  int correctedLightGreen   = constrain(correctionGreen, 0, 255);
  int correctedLightBlue    = constrain(correctionBlue, 0, 255);

  // Average of all three measurements, Global var!
  lampAverage = (correctedLightRed + correctedLightGreen + correctedLightBlue) / 3;

  // Send info to graph/log in Blynk
  Blynk.virtualWrite(V0, constrain(correctedLightRed, 0, 255));
  Blynk.virtualWrite(V1, constrain(correctedLightGreen, 0, 255));
  Blynk.virtualWrite(V2, constrain(correctedLightBlue, 0, 255));
  Blynk.virtualWrite(V3, lampAverage);
}

void initLCD()
{
  lcd.clear();
  lcd.home();
   
  for(int i = 0; i< 4; i++)
  {
    lcd.noBacklight();
    delay(100);
    lcd.backlight();
    delay(100);
  }

  for(int i=0;i<sizeof(text1);i++)
  {
    lcd.print(text1[i]);
    delay(50);
  }

  for(int i=0;i<sizeof(text1);i++)
  {
    lcd.setCursor(i,0);
    lcd.print(" ");
    delay(50);  
  }

  lcd.home();
  
  for(int i=0;i<sizeof(text2);i++)
  {
    lcd.print(text2[i]);
    delay(50);
  }

  for(int i=0;i<sizeof(text2);i++)
  {
    lcd.setCursor(i,0);
    lcd.print(" ");
    delay(50);  
  }

  for(int i = 0; i< 4; i++)
  {
    lcd.noBacklight();
    delay(100);
    lcd.backlight();
    delay(100);
  }
  
  lcd.clear();

  for(int i=0;i<sizeof(text3);i++)
  {
    lcd.print(text3[i]);
    delay(50);
  }
}

void doDHT()
{
  float h = dht.readHumidity();
  float c = dht.readTemperature();

  lcd.clear();
  
  lcd.home();
  lcd.print("Hum.: ");
  lcd.setCursor(6,0);
  lcd.print((int)h);
  lcd.print("%");
  
  lcd.setCursor(0,1);
  lcd.print("Tmp.: ");
  lcd.setCursor(6,1);
  lcd.print((int)c);
  lcd.print("C");

  lcd.setCursor(10,0);
  lcd.print(" | Avg"); 
  lcd.setCursor(10, 1);
  lcd.print(" | ");
  lcd.print(lampAverage);
  
  int tempHumid = (int)h;
  int tempTemp  = (int)c;
  
  String humid  = (String)tempHumid;
  String temp   = (String)tempTemp;
  
  humid += "%";
  temp += "°C";
  
  Blynk.virtualWrite(V13, humid);
  Blynk.virtualWrite(V14, temp);
}

int giveBackTime()
{
  if(lampAverage < lightsNight)
  {
    // It is evening
    if(lampAverage > lightsEvening) // Light value is between x and y
    {
      return 1;
    }
    else // Now it's daytime (light value is below y)
    {
      return 0;
    }
  }
  else // Now it's night (light value is above x)
  {
    return 2;
  }
}

void setLEDs()
{
  // If master override button is on, skip the rest of the function
  if(masterOverride == 1)
  {
    return;
  }
  
  int statusTime = giveBackTime();

  switch (statusTime)
  {
    case 0:
      autolightsLED.on();
      hallwayLED.off();
      
      break;
    case 1:
      autolightsLED.on();
      hallwayLED.off();
      
      break;
    case 2:
      autolightsLED.on();
      hallwayLED.on();
      
      break;
  }
}

void setLamp1(bool onoff)
{
  if(onoff == 1)
  {
    lights.virtualWrite(V24, HIGH);
    lamp1LED.on();
  }
  else
  {
    lights.virtualWrite(V24, LOW);
    lamp1LED.off();
  }
}

void setLamp2(bool onoff)
{
  if(onoff == 1)
  {
    lights.virtualWrite(V25, HIGH);
    lamp2LED.on();
  }
  else
  {
    lights.virtualWrite(V25, LOW);
    lamp2LED.off();
  }
}

void setLamp3(bool onoff)
{
  if (onoff == 1)
  {
    digitalWrite(10, HIGH);
    digitalWrite(11, HIGH);
    digitalWrite(12, HIGH);
    digitalWrite(13, HIGH);
    
    if(lamp3Manual == 1)
    {
      lamp3LED.off();
    }
    else
    {
      lamp3LED.on();
    }
  }
  else
  {
    digitalWrite(10, LOW);
    digitalWrite(11, LOW);
    digitalWrite(12, LOW);
    digitalWrite(13, LOW);
    lamp3LED.off();
  }
}

If you have any questions, please don’t hesitate to ask!

4 Likes

Nice. Thanks for sharing. Do you have some ideas why ESP stops working? As I understood ESP doesn’t reconnects automatically?

Well, I’m not entirely sure what happens.

Case: Arduino Mega is online, temperature and direct attached light work fine
Sympton: virtualWrite to Bridge device(s) doesn’t work anymore (after couple hours maybe?)
But: ESP dashboard sends command just fine

So that is a bit weird. I’ve just soldered a MOSFET on a print to light the direct attached tree to exclude that it has something to do with the power supply, but I don’t think so. It’s not really reproducible, so I’m trying to figure out what happens.

I’m 95% sure it’s somewhere between the Mega and the “slave” ESP’s working in bridge mode. I’ve reset my Mega last night a couple times (before bed actually) and bridge was working fine, but this morning (roughly 9 hours later) bridge function stopped working, but direct control of the ESP was doing fine.

Both dashboards are active. Could it be something with the hardware timeout in server.properties?

When I reset my Mega it works fine again (just tried). There has to be some timeout somewhere. I don’t have much space on my Pi, so I’ll try to make a trace log and hope it won’t crash.

Don’t think so. Does your hardware lost connection during the night?

Well, that’s the silly bit, it doesn’t. Both devices keep working fine, I just can’t send commands from one to the other.

Created ticket on guthub https://github.com/blynkkk/blynk-library/issues/67

@Lichtsignaal Could you please try next :

BLYNK_CONNECTED() {
bridget.setAuthToken(token);
}

Sure thing, I’ll put that in tonight. I should be able to reproduce it within one evening, tnx so far :smile:

-edit-

Where should I put that @Dmitriy ? lol.

I’ll put it here:

void doStuff()
{
  if(Blynk.connected())
  {
    lights.setAuthToken("1abe473676ae47bea208a8b702ad266e");
  }

That one gets called every 20s or so.

No. it should be put out of any function, just like BLYNK_WRITE(V1).

1 Like

Ok I’ll do that, but I got an error compiling:

MegaDomo_v0.6:206: error: ISO C++ forbids declaration of ‘BLYNK_CONNECTED’ with no type [-fpermissive]
ISO C++ forbids declaration of ‘BLYNK_CONNECTED’ with no type [-fpermissive]

You need the latest blynk library (master branch)

Awesome, that did the trick, I don’t know why I wasn’t on the latest release, lol.

20:30 uploaded code (check before I uploaded code, Bridge command didn’t work), after upload, Bridge command works fine.

I’m gonna keep trying every half hour or so to see what happens.

21.00 Bridge works fine … so far so good
22.15 Still working! Going to bed I think and hoping it will also be good tomorrow morning.

I’ll see if I can reproduce the issue tomorrow night/morning, maybe without the code or other version library.

We had a power outage because the heater lamp of the lizard blew up this morning so I can’t test after a longer period, will try again tonight to see what happens, but I have good hope :slight_smile:

-edit-

After half a day, approximately 6 hours, it sill works. yay!

It’s in good shape now. My relays still respond and my nightlight is turning on and off as it supposed to be, you guys made me a happy man :slight_smile:

3 Likes