AdSense

Sunday 30 August 2020

Home Automation with a Raspberry PI and ESP8266, part 2: A simple sensor (ESP8266)

(Deutsche Version) As the first project, I want to show my temperature and humidity sensors. I use an ESP8266 who reads a Si7021 sensor. The Si7021 is connected with a 4 pin cable to the ESP on pins D1-D4, as you can see in the image:

Additionally, you have to connect the pin D0 to RST, so deep sleep works, which we will use later. Using quite cheap phone chargers, I used a capacitor parallel to G and 3V.

This is placed in a 3d printed housing that you can find on thingiverse: https://www.thingiverse.com/thing:4583759. The sensor is fixed with the small hook in the housing. afterwards the ESP is placed on top and the lid fixes everything. The open side is suited to connect a phone charger.



The code is comparable to the one on my last post about this topic, https://physudo-e.blogspot.com/2019/07/wifi-thermometer-with-dht22-and-eps8266.html. In the setup part, the wifi is connected. If this does not work, the ESP is resetted after 100 seconds. Additionally, the Si7021 sensor is initialized.

In loop, if the WiFI is connected (otherwise the ESP is resetted), the temperature and humidity is measured. If anything goes wrong here, the ESP is resetted, too. Afterwards, everythin is sent via http request to the Raspberry PI as described in the previous post (https://physudo-e.blogspot.com/2020/08/home-automation-with-raspberry-pi-and.html).

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
const char* ssid     = "WIFI SSID";
const char* password = "WIFI PASSWORD";

#include "Adafruit_Si7021.h"

Adafruit_Si7021 sensor = Adafruit_Si7021();

void setup()
{
  Serial.begin(9600);

  Serial.print("Your are connecting to;");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  int resetCtr = 0;
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    resetCtr ++;
    Serial.print(".");
    if (resetCtr > 200)
    {
      ESP.restart();
    }
  }

  pinMode(D0, WAKEUP_PULLUP);
  pinMode(D3, OUTPUT);
  pinMode(D4, OUTPUT);
  digitalWrite(D3, HIGH);
  digitalWrite(D4, LOW);
  delay(500);

  while (!sensor.begin())
  {
    Serial.println("Did not find Si7021 sensor!");
  }

  digitalWrite(D3, LOW);
}



void loop() {
  if (WiFi.status() != WL_CONNECTED)
  {
    ESP.restart();
  }

  if (WiFi.status() == WL_CONNECTED)
  {
    Serial.println("");
    Serial.println("Your ESP is connected!");
    Serial.println("Your IP address is: ");
    Serial.println(WiFi.localIP());

    digitalWrite(D3, HIGH);
    delay(500);
    float t = sensor.readTemperature();
    Serial.print("Temperature: ");
    Serial.println(t);
    float h = sensor.readHumidity();
    Serial.print("Humidity: ");
    Serial.println(h);
    if (isnan(t))
    {
      t = sensor.readTemperature();
    }
    if (isnan(h))
    {
      h = sensor.readHumidity();
    }
    digitalWrite(D3, LOW);

    if (isnan(t))
    {
      ESP.restart();
    }
    if (isnan(h))
    {
      ESP.restart();
    }
    

        
    HTTPClient http;
    char requestString[255] = "";
    sprintf(requestString, "http://raspberrypi/SendValues.php?name=Temperatursensor_1&split=1&data=%f,%f", t, h);    
    http.begin(requestString);
    int httpCode = http.GET();
    Serial.println(httpCode);

    if (httpCode > 0)
    {
      String payload = http.getString();
      Serial.println(payload);
    }

    http.end();

    ESP.deepSleep(300e6); 
    delay(300000);
    ESP.reset();
  }
  else
  {
    Serial.println("");
    Serial.println("WiFi not connected");
    delay(1000);
  }
}

Home Automation with a Raspberry PI and ESP8266, part 1: Basics (Raspberry PI)

(Deutsche Version) Today, I want to start presenting my smart home system. I started half a year ago and by now, the system has grown quite big. The current system consists of:

  • 6 temperature- and humidity sensors inside
  • 3 temperature- and humidity sensors outside
  • 2 temperature- and humidity sensors in our terrarium
  • 1 CO2 sensor
  • 5 switchable plug sockets
  • 3 switchable lamps
  • 1 switch for the input of the sound system
  • Sensors at the washing mashine and tumble dryer
  • Microphone and two cameras at the 3d printer
  • Dashboard as website on an old tablet resp. on the smartphone
  • Telegram bot with automatic alerts and commandos

The basic for everything is a Raspberry PI 4, who acts as a server in the WiFi. All data is stored here, the website is placed here and all sensors communicate with the Raspberry. You don't have to setup much on the raspberry, I installed the apache2 webserver, you can find a lot of tutorials on this topic on the internet. Basically it can be condensed to this one command:

sudo apt install apache2

Additionally, I installed apache2 for php with this command:

sudo apt-get install php libapache2-mod-php

These are all the requirements on the Raspberry. I recommend to setup the folder /var/www/html/ as a network share, then you can access all files and scripts from other computers. Additionally, you should create the folder /var/www/html/data/, all data files are located here.

Now we can start with the scripts. I will only describe the basic script to send data, all other modifications and scripts will be presented with the relevant posts. To send data, I use the file "SendValues.php", which requests the following GET parameters from the sensor:

  • "name", the name of the sensor, e.g. "TemperatureSensor_1"
  • "split", how the data should be split. The problem would be files accumulating the data over years, making them very big and hard to handle. Therefore, the data is split by year, month or day. Split 0 means no splitting, 1 equals one file per year, 2 one file per month and 3 one file per day
  • "data", the relevant data, separated by comma, e.g. for a temperature and humidity sensor: 24.1,54.6

The script consists of the following parts. At first the header:

<html>
 <head>
  <title>Send Sensor Values</title>
 </head>
 <body>
  <?php 

Afterwards the relevant code. Here the data is read and the current date is determined:

$name = htmlspecialchars($_GET["name"]);
$split = htmlspecialchars($_GET["split"]);
$data = htmlspecialchars($_GET["data"]);

$date = gmdate("Y-m-d") . "T" . gmdate("H:i:s.u") . "Z";

Now, a [name]_last.csv file is created, which holds the newest value:

$file = "data/".$name."_last.csv";
$Saved_File = fopen($file, 'w');
fwrite($Saved_File, $date . "," . $data . "\r\n");
fclose($Saved_File);

Next, the splitting is performed. Therefore the filename is created, [name]_[year]-[month]-[day].csv. To have an overlap between two files, the next file is also created, for the next year, next month, next day and already holds the values. If e.g. the last year is requested this guarantees that every file contains at least one year of data, not only the values starting from January 1st. $file1 is the current file, $file2 the one for the next time period.

$file = "data/".$name.".csv";

if ($split == "1")
{
  $file = "data/".$name."_" . gmdate("Y") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y", strtotime("+1 year")) . ".csv";
}
if ($split == "2")
{
  $file = "data/".$name."_" . gmdate("Y-m") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y-m", strtotime("+1 month")) . ".csv";
}
if ($split == "3")
{
  $file = "data/".$name."_" . gmdate("Y-m-d") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y-m-d", strtotime("+1 day")) . ".csv";
}

Now the data is saved. If "split" is 0, the saving into the second file is skipped as there only exists one single file for the whole time.

$Saved_File = fopen($file, 'a');
fwrite($Saved_File, $date . "," . $data . "\r\n");
fclose($Saved_File);
if ($split == "0")
{
}
else
{
  $Saved_File = fopen($file2, 'a');
  fwrite($Saved_File, $date . "," . $data . "\r\n");
  fclose($Saved_File);
}

This already was the whole code, we now have to close all the tags and then SendValues.php is completed:

  ?>
 </body>
</html>

This is all you need on the server site, now the sensors can send their data. To test this, you can put the following text into your browser within your WiFI, this should create the files TemperatureSensor_1_last.csv, and two additional files with the current and the next year at the end:

http://raspberrypi/SendValues.php?name=TemperatureSensor_1&split=1&data=25.3,64.59

For everyone not wanting to copy the code parts, here is the complete code:

<html>
 <head>
  <title>Send Sensor Values</title>
 </head>
 <body>
  <?php
 
$name = htmlspecialchars($_GET["name"]);
$split = htmlspecialchars($_GET["split"]);
$data = htmlspecialchars($_GET["data"]);

$date = gmdate("Y-m-d") . "T" . gmdate("H:i:s.u") . "Z";

$file = "data/".$name."_last.csv";
$Saved_File = fopen($file, 'w');
fwrite($Saved_File, $date . "," . $data . "\r\n");
fclose($Saved_File);

$file = "data/".$name.".csv";

if ($split == "1")
{
  $file = "data/".$name."_" . gmdate("Y") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y", strtotime("+1 year")) . ".csv";
}
if ($split == "2")
{
  $file = "data/".$name."_" . gmdate("Y-m") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y-m", strtotime("+1 month")) . ".csv";
}
if ($split == "3")
{
  $file = "data/".$name."_" . gmdate("Y-m-d") . ".csv";
  $file2 = "data/".$name."_" . gmdate("Y-m-d", strtotime("+1 day")) . ".csv";
}
$Saved_File = fopen($file, 'a');
fwrite($Saved_File, $date . "," . $data . "\r\n");
fclose($Saved_File);
if ($split == "0")
{
}
else
{
  $Saved_File = fopen($file2, 'a');
  fwrite($Saved_File, $date . "," . $data . "\r\n");
  fclose($Saved_File);
}

  ?>
 </body>
</html>

Tuesday 11 August 2020

Machine Learning for a camera surveilled 3d printer

(Deutsche Version) It's been now over a year since the last post and I did lots of projects, especially with the multi-colour upgrade for my Ultimaker 2+. Today, I want to share a project that is part of this, but also part of my IoT Home Automation system: A (two) camera based surveillance for my 3d printer with automated recognition of states.

First of all: the cameras. I have two ESP32-Cam modules where you can find endless tutorials on the internet on how to program these. I then added them to my 3d printer. The first camera is on top, with a 3D-printed fixture you can find here: https://www.thingiverse.com/thing:3899159

The second camera is placed in the 3D printer with tape, and I slid a 45° block below the camera module to get the right angle.

These cameras produce a new image roughly every 3 minutes, so I have a lot of training data by now. The images look like this:



The images can be classified into different categories:

  1. Idle: The printhead is in its parking position and the printbed is empty
  2. Preparing: The printhead is in the front left position and the printer is heating up
  3. Printing: I don't really have to explain this I guess
  4. Finished: The printhead is in its parking position and the printbed is not empty
  5. Problem: A distance between the printbed or the current object and the nozzle is visible
  6. Maintenance: My hands are in the printer, the printhead is disassembled or not in its parking position
  7. Off: All lights are switched off and the image is only dark

Camera 1 can't really decide whether it's printing or problem, so this camera cannot detect "problem", camera 2 can't distinguish between idle and finished, so this camera cannot detect "finished".

I sorted the first images I had into the 7 categories and then trained a neural net on them. My architecture of the net (in Tensorflow, Python) looks like this:

model = models.Sequential()
model.add(layers.Conv2D(32, (5, 5), activation='relu', input_shape=(128, 128, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(32, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(32, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(32, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(32, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(7))

It is designed for the input of 128x128 pixel images with 3 colours and consists of 5 convolutional layers, each with a max pooling layer afterwards and then one fully connected layer with 64 neurons which leads to the output layer with 7 neurons.

To further optimise the detection, I added a second net that uses images of the input 64x64 pixels and thus has only 4 convolutional layers. The result percentages for each category of the two nets are added (and divided by 2), which increases the accuracy. In the end, this result is obtained for both cameras and again added, furthermore there is a microphone inside the printed which measures the noise level and can distinguish whether the printhead fan is on or off and thereby delivers another input. The total results then creates a forecast for the status of the printer. As described in the previous post, this status is then sent to my IoT system which alerts me if

  • The printer status changes to "Problem"
  • The printer status changes to "Finished"