My project to automatize my home

Hello everyone, today i want to present my project made with Blynk at heart.

The goal for the project is to automate my house, or at least some room.
The project is hosted on a raspberry pi 3 that run a Blynk local server, (there is also a second raspberry pi 2 always ready and in sync in case the first one fail).
The “client” devices are mainly esp8266, they are all setted to retrieve updated firmware from the raspberry pi and auto-update.

#Device: Weather station

The weather station is the first device that I’ve realized, it’s an esp-8266 battery powered, solar charged and use a DS18B20 temperature sensor to monitor external temperature,
List of sensor:

  • DS18B20 digital temperature sensor, 16 bit accuracy, onewire protocol.
  • Resistive rain sensor connected to the ADS1115 external adc.
  • DHT11 monitor internal box temperature and humidity, poor accuracy, onewire protocol.
  • LM35 monitor the internal temperature , medium accuracy, connected to the ADS1115.
  • Battery voltage is measured by both the internal adc and the external ADS1115.
  • Solar panel voltage is measured by the external ADS1115.

The battery is a Li-ion 1 cell battery (4400 mAh) recharged by a solar panel trough a classic TP4056

Weather station schematic:

Weather station code:

#define myName "/ext/file.bin"
#define version "0.3.5"

//add the following line to the build option
//build_flags = '-DSSID_PREP="your_wifi_ssid_goes_here"' '-DPASS_PREP="your_wifi_password_goes_here"' '-DAUTH_PREP="your_token_goes_here"'
//pin to connect
//EN 5V
//GPIO0 5V
//GPIO15 0V
//TO DO LIST
//add dht11 code
//rewrite spediscidati

#define numL 200//Number of reading for internal adc

#include <Arduino.h>
//DS18B20
#include <OneWire.h>
#include <DallasTemperature.h>
//TIMER
#include <SimpleTimer.h>

//ADC
#include <Wire.h>
#include <Adafruit_ADS1015.h>
Adafruit_ADS1115 ads;
//WIFI
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>

int ONE_WIRE_BUS = 12;//ds18b20 pin

//int sda = 13;
//int scl = 15;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

SimpleTimer timer;


char ssid[] = SSID_PREP;//wifi ssid
char pass[] = PASS_PREP;//wifi password
char auth[] = AUTH_PREP;//blynk token
const char server_upd[] = "192.168.1.124";//ip of the update server
const char server_blynk[] = "192.168.1.124";//ip of the blynk server
const int port_upd = 8090;//port for searching update
const int port_blynk = 8080;//blynk port
const int port_telegram = 8091;//telegram port

const double Kc = 0.183105487805;//constant for converting adc to real voltage

int sendData(float, String);//function to send float trough http api
int sendData(int, String);//function to send int trough http api
int sendVersion(String);//function to send firmware version trough http api
double readVin();//function to read vin
void sendlowbat();//function to send low bat alert to telegram chat

void spedisciDati();//function to send data to blynk server
void aggiornaDormi();//function to search update and deepSleep


const long deltat = 1000*15;//max time to be awake
const long deltat2 = 1000*2;//delay between send data to server

const long tsleep = 1000*1000*60*5;//time to sleep (5 mins.)

void setup(){

  Serial.begin(115200);
  Serial.setDebugOutput(true);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);

  while (WiFi.status() != WL_CONNECTED)  {
    delay(100);
    Serial.println(WiFi.status());
  }

  sensors.begin();//init ads sensor

  timer.setInterval(deltat2, spedisciDati);
  timer.setInterval(deltat, aggiornaDormi);

  //--**ADC
  ads.begin(D2, D1);//init i2c on D2 and D1 pin(i modified the ads library to make this)
  ads.setGain(GAIN_ONE);//+-4.096V
  //--**ADC

  if(readVin() < 3.5) {
    sendlowbat();
  }

}

int sended = 0;

void loop() {

  timer.run();

}

int sendData(float value, String pin){
  HTTPClient http;
  String payload = String("[\n") + value + String("\n]");
  Serial.println("\nstart:");
  Serial.println(http.begin(server_blynk, port_blynk, String("/") + auth + String("/pin/") + pin));
  http.addHeader("Content-Type", "application/json");//String("/") + auth + String("/pin/") + pin
  int codice_ritorno = http.sendRequest("PUT",(uint8_t *) payload.c_str(), payload.length());
  Serial.println(codice_ritorno);
  Serial.println(http.writeToStream(&Serial));
  http.end();
  Serial.println();
  return codice_ritorno;
}

int sendData(int value, String pin){
  HTTPClient http;
  String payload = String("[\n") + value + String("\n]");
  Serial.println("\nstart:");
  Serial.println(http.begin(server_blynk, port_blynk, String("/") + auth + String("/pin/") + pin));
  http.addHeader("Content-Type", "application/json");//String("/") + auth + String("/pin/") + pin
  int codice_ritorno = http.sendRequest("PUT",(uint8_t *) payload.c_str(), payload.length());
  Serial.println(codice_ritorno);
  Serial.println(http.writeToStream(&Serial));
  http.end();
  return codice_ritorno;
}

int sendVersion(String pin){
  HTTPClient http;
  String payload = String("[\n\"") + String(version) + String("\"\n]");
  Serial.println("\nstart:");
  Serial.println(http.begin(server_blynk, port_blynk, String("/") + auth + String("/pin/") + pin));
  http.addHeader("Content-Type", "application/json");//String("/") + auth + String("/pin/") + pin
  Serial.println(http.sendRequest("PUT",(uint8_t *) payload.c_str(), payload.length()));
  Serial.println(http.writeToStream(&Serial));
  http.end();
}

void sendlowbat(){
  HTTPClient http;
  Serial.println("\nstart:");
  Serial.println(http.begin(server_blynk, port_telegram, String("/lowbat") ));
  int codice_ritorno = http.GET();
  Serial.println(codice_ritorno);
  Serial.println(http.writeToStream(&Serial));
  http.end();
}

double readVin(){
  long sum = 0;
  for(int i = 0; i < numL;i++){
    int val = analogRead(A0);
    sum += val;
  }
  double avg = (double) sum / numL;
  double vin = (double) avg / (Kc * 1024.0f);
  return vin;
}

void aggiornaDormi(){
  if((WiFi.status() == WL_CONNECTED)) {    // wait for WiFi connection
    t_httpUpdate_return ret = ESPhttpUpdate.update(server_upd, port_upd, myName, version);//send to the update server the name of the device and the firmware version

    switch(ret) {
      case HTTP_UPDATE_FAILED:
        Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
        break;

      case HTTP_UPDATE_NO_UPDATES:
        Serial.println("HTTP_UPDATE_NO_UPDATES\n");
        break;

      case HTTP_UPDATE_OK:
        Serial.println("HTTP_UPDATE_OK\n");
        break;
    }
  }
  ESP.deepSleep(tsleep);
}

void spedisciDati(){//retrive sensor data and send to the Blynk server
  //this function is horrible, i will rewrite it one day
  sensors.requestTemperatures();

  double vin = readVin();
  float temp = sensors.getTempCByIndex(0);
  Serial.println(temp);

  sendData(int(millis()/1000), "V1");
  sendData(float(vin), "V0");
  sendVersion("V3");

  int16_t adc0, adc1, adc2, adc3;
  float Voltage0,Voltage1,Voltage2,Voltage3;

  adc0 = ads.readADC_SingleEnded(0);
  Voltage0 = (adc0 * 0.125)/1000;
  delay(20);

  adc1 = ads.readADC_SingleEnded(1);
  Voltage1 = (adc1 * 0.125)/1000;
  delay(20);

  adc2 = ads.readADC_SingleEnded(2);
  Voltage2 = (adc2 * 0.125)/1000;
  delay(20);

  adc3 = ads.readADC_SingleEnded(3);
  Voltage3 = (adc3 * 0.125)/1000;
  delay(20);

  adc0 = ads.readADC_SingleEnded(0);
  Voltage0 = (adc0 * 0.125)/1000;
  delay(20);

  adc1 = ads.readADC_SingleEnded(1);
  Voltage1 = (adc1 * 0.125)/1000;
  delay(20);

  adc2 = ads.readADC_SingleEnded(2);
  Voltage2 = (adc2 * 0.125)/1000;
  delay(20);

  adc3 = ads.readADC_SingleEnded(3);
  Voltage3 = (adc3 * 0.125)/1000;
  delay(20);

  Voltage2 *= 5.31969;

  Serial.print("(V Solare) AIN0: "); Serial.print(adc0);Serial.print("\tVin0: "); Serial.println(Voltage0);
  Serial.print("(V in) AIN0: "); Serial.print(adc2);Serial.print("\tVin0: "); Serial.println(Voltage2);
  Serial.print("(V LM35) AIN0: "); Serial.print(adc3);Serial.print("\tVin0: "); Serial.println(Voltage3);

  sendData(Voltage0, "V5");

  sendData(Voltage1, "V8");

  sendData(Voltage2, "V6");

  sendData(Voltage3, "V7");

  if(temp > -120){//failed read
    int send_code = sendData(temp, "V2");
    if(send_code == 200){
      sended = 1;
      aggiornaDormi();
    }
  }

  Serial.printf("Connection status: %d\n", WiFi.status());
}

Solar panel and rain sensor:

Weather station intern:

#Device: Tv control and living room monitor
(This device is under heavy development, i will upload the code and photo in future)

This device is an Esp8266 wall powered that monitor the room temperature and control Tv, Sky decoder, Xbox, HVAC and Bd-player trough an ir LED.
The wiring is similar to the weather station and i will reuse the pcb with little modification.
Since this device is powered from the wall the code is very different because here I’ve used the Blynk library.

#Device: light relay

This device is very simple, it’s only purpose is to control a relay that trig the light in my bedroom,
because of this I’ve use a Esp01, the OTA-update here isn’t implemented because of the little memory.
Here the code is the basic Blynk example because the only thing to do is trig a digital pin.

#Server

The Rasperry pi’s host the local Blynk server, all my local git repo’s, and some JS script that provide service like OTA-update, telegram bridge and google maps API bridge(the last one is under development).
The emergency raspberry pi is kept in sync with the primary one by a cron job that every 15 minute copy the data, the switch between the couple of raspberry isn’t already automatic so i must do it manually.
Near the server there is an arduino mega connected trough Ethernet that monitor and control:

  • Temperature and humidity(DS18B20, LM35 and DHT11)
  • Voltage from the 12 switching power supply
  • Voltage from every dc-dc (12V -> 5V) that power the raspberry pis
  • Relay that power the 3D printer
  • Light for the 3D printer (RGB strip not NeoPixel)

Photo of the server with double Raspberry Pi and Arduino Mega on top

Here is the script for the OTA-update:

//TO DO LIST
//comment the code
//translate in english
//move the parameter like ip, port and auth to a config file or as parameter

//How to use:
//install al dependencies trough npm:
/*
"compare-version": "^0.1.2",
"connect": "^3.5.0",
"express": "^4.14.0",
"filesize": "^3.3.0",
"httpdispatcher": "^2.0.1",
"serve-static": "^1.11.1",
"yargs": "^6.5.0"
*/
//prepare the folder tree like this: /home/user/folder_of_bin_file/name_of_device/version.bin
//(version.bin is the compiled file from platformio or arduino ide, you must rename it with a
// version number greater than the actual in use, example if an ESP-8266 with code ext is running
//version 1.3.4 you must insert a file like this:/home/user/folder_of_bin_file/ext/1.3.5.bin)
//run with node main.js -d /home/user/folder_of_bin_file -p port_of_the_service (if omitted the port is 8080)

var app = require('express')();
var http = require('http').Server(app);
var dispatcher = require('httpdispatcher');
var compareVersion = require('compare-version');
var fs = require('fs');
var filesize = require('filesize');

var argv = require('yargs')
.usage('Usage: $0 -d [dir] -p [port]')
.demand(['d'])
.default('p', 8080)
.argv;

var overdir = argv.d;
var port = argv.p;

var reqCont = 0;

app.get(/\/(.+)\/file.bin/, function(req, res) {

if(req.headers['user-agent'] == "ESP8266-http-Update"){

  var subdir = req.url.match(/\/(.+)\/file.bin/)[1];

  var maxfile;

  fs.readdir(""+overdir+"/"+subdir, function(err, files){
    console.log(""+overdir+"/"+subdir);
    if (files.length > 0){
      maxfile = files[0];
      files.forEach(function(file){
        if(compareVersion(file.split(".bin",1)[0], maxfile.split(".bin",1)[0])){
          maxfile = file;
        }
      });

      console.log("\nNuova richiesta in arrivo:\t%s\nVersione locale: " + maxfile + "\tPosizione richiesta: " + subdir, Date().toString());
      console.log("Versione remota ('x-esp8266-version'): " + req.headers['x-esp8266-version']);

      if(compareVersion(maxfile.split(".bin",1)[0], req.headers['x-esp8266-version']) == 1){
        var stats = fs.statSync(overdir+"/"+subdir+'/'+maxfile);
        var fileSizeInBytes = stats["size"]
        var dimensione = filesize(fileSizeInBytes);
        reqCont++;
        console.log("Aggiorno, invio il file: %s (%f) a %s, Aggionamento num: %d", overdir+"/"+subdir+'/'+maxfile, dimensione, reqCont, req.ip);
        res.sendFile(''+overdir+"/"+subdir+'/'+maxfile);
      }
      else {
        reqCont++;
        console.log("Già aggiornato, Aggionamento num: %d", reqCont);
        res.writeHead(304);
        res.end();
      }
    }
  });
}
});

http.listen(port, function(){
console.log("Server listening on: http://localhost:%s", port);
});

#Future development

  • Change the weather station dc-dc converter(it waste around 20mA in sleep-mode)
  • Make the fallback between the raspberry pi automatic.
  • Create the pcb for the Esp8266 device.
  • Add the NeoPixel led in my bedroom to report the traffic status from my home to work and gym(the software is ready, I’m waiting for the NeoPixel shipping)
  • Add a tsop4838 receiver to control device with the TV remote.
  • Terminate the openTherm bridge to monitor/control the heating system
  • Add internal and external air quality sensor

I will update the post with new feature, improvements and upload all the code and script to Github in the future.

7 Likes

Wow, thanks for sharing, I had no time to check all details but there’s something I want to bring up:

I’m using the Local Server and never thought the use of a second device just in case… As far as I can see, (as per my experience using my own server), I never have had a fail… For me is a little bit extrange…

Anyway, I’ll wait for your updates to learn a little bit more regarding this feature.

Kind regards!!

@psoro in the early Pi days, before they sold circa 11,000,000, there was a lot of problems with SD card’s failing. It’s a lot better these days but it can still happen.

We have backup copies of our SD cards in case they fail. We also run some of the Pi’s via USB sticks as they are more reliable than SD cards. You still need a bootable SD card but the important filesystem is on the USB stick.

Please please please make a github repo! I would love to fork this!

For sure, a backup copy should be mandatory just in case… I used to do it from time to time.

“early Pi days”…2012? I knew nothing about “Arduino” those days…It seems really far from here…:slight_smile:

Wow! Nice stuff, great post! Thanks for sharing it with others!

This is the code of the Weather station, i will upload the other code in future

I added a second Rapsberry Pi because i like to to experiments with software and hardware and sometimes one raspberry pi fails (one time was power supply, another time was iptable) so a backup raspberry was required :slight_smile:

For sure, I recognize sometimes a second device is a good way to keep everything under control…in fact, for a special project I use two WeMos, but never came to my mind to use of two servers…

Anyway, in order to avoid issues due to power supply, I use a power bank to feed the Pi cause sometimes the power goes down (storms…) Maybe this could work for you…:relaxed:

@psoro my UPS stopped working within 12 months so I need two UPS’ in case the first one goes down :slight_smile:

1 Like

New device to the family: soil moisture.
Is a simple resistive soil moisture sensor that send data to Blynk trough an Esp8266 battery powered.


In the next days I will upload schematic and some screen from blynk app.

2 Likes