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 )
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!