Webux Lab

Par Studio Webux

Station IOT

TG
Tommy Gingras Studio Webux S.E.N.C 2020-12-17

IOT avec ESP8266

Introduction

Ce projet collecte la température, l’humidité, la lumière ambiante et détecte les mouvements.

Il est intégré avec le Apple HomeKit pour lire et collecter les évènements.

Puis une écran affiche la température, l’humidité, les LUX* et les évènements.

Le calcul des LUXs a été pris sur un site quelconque, donc ne reflète pas la réalité absolue (Vous pouvez laisser un commentaire avec vos suggestions).

De plus, le buzzer émet des “bips” selon les évènements définis et envoi une push notification.

Matériel

Ce projet possède plusieurs modules:

Les étapes

Pré-requis

J’utilise le Arduino IDE (1.8.13) avec Visual Studio Code.

il faut les dépendences d’installé pour utiliser le ESP8266.

http://arduino.esp8266.com/stable/package_esp8266com_index.json

Quelques librairies ont été requises pour ce projet,

Étape 1 - Le code

Voici la structure du projet:

.
|
|- accessories.c
|- buzzer.h
|- dht22.h
|- ldr.h
|- notification.h
|- pir.h
|- screen.h
|- spec.h
|- main.ino
|- web.h
|- wifi_webux.h

Spécifications

Contient toutes les variables et constantes

spec.h

#ifndef SPEC_H_
#define SPEC_H_

#define HOMEKIT_OCCUPANCY_DETECTED 1
#define HOMEKIT_OCCUPANCY_NOT_DETECTED 0

// Hardware
const int ldr_sensor = 0;     // A0
const int status_LED = 12;    // D6
const int motion_sensor = 13; // D7
const int buzzer = 14;        // D5
const int built_in_led = 16;

// Light Sensor
const float VIN = 3.3;
const int RESISTANCE = 10000;
float sensorVal;
float lux;
String lightState = "Unknown";

// Motion Sensor
int motion_state = LOW;
bool flag = false;

// DHT
#define DHTTYPE DHT22
#define DHTPIN 2 // D4
DHT dht(DHTPIN, DHTTYPE);

// Timer
const long reportInterval = 30;      // 30 Seconds

// // Sensor Details
const String SENSOR_NAME = "ESP8266";
const String LOCATION = "Office";

// // Notifications
const char *host = "api.pushover.net";
const String apiKey = "";
const String userKey = "";
const String url = "/1/messages.json";
String message = "";

// PIR Timer and switch
unsigned long previousMillis = 0;
unsigned long lastCheck = 0;
const long interval = 10000; // 10 seconds
const long checkInterval = 2000; // 2 seconds
const long cooldown = 60000; // 1 minute
bool isCoolingDown = false;

// // Web Server
ESP8266WebServer server(80);

#endif /* SPEC_H_ */

Apple HomeKit

Le fichier accessories.h a une structure très spécifique et quelques standards à respecter.

Je vous recommande une lecture de la documentation de Apple pour bien comprendre les requis, puis d’aller voir le code du projet : Arduino-HomeKit-ESP8266 Pour mieux comprendre se que la librairie s’attend comme valeurs

En résumé ce fichier couvre le DHT22, la photorésistance (LDR) et la détection de mouvement. La manière dont j’ai réussi à le faire fonctionner pour deux modules différents est :

accessories.h

#include <homekit/homekit.h>
#include <homekit/characteristics.h>

// Called to identify this accessory. See HAP section 6.7.6 Identify Routine
// Generally this is called when paired successfully or click the "Identify Accessory" button in Home APP.
void my_accessory_identify(homekit_value_t _value) {
	printf("accessory identify\n");
}

homekit_characteristic_t cha_light = HOMEKIT_CHARACTERISTIC_(CURRENT_AMBIENT_LIGHT_LEVEL, 1);
homekit_characteristic_t cha_motion = HOMEKIT_CHARACTERISTIC_(MOTION_DETECTED, false);
homekit_characteristic_t cha_occupancy = HOMEKIT_CHARACTERISTIC_(OCCUPANCY_DETECTED, 0);

// For TEMPERATURE_SENSOR,
// the required characteristics are: CURRENT_TEMPERATURE
// the optional characteristics are: NAME, STATUS_ACTIVE, STATUS_FAULT, STATUS_TAMPERED, STATUS_LOW_BATTERY
// See HAP section 8.41 and characteristics.h

// (required) format: float; HAP section 9.35; min 0, max 100, step 0.1, unit celsius
homekit_characteristic_t cha_current_temperature = HOMEKIT_CHARACTERISTIC_(CURRENT_TEMPERATURE, 0);
homekit_characteristic_t cha_current_humidity = HOMEKIT_CHARACTERISTIC_(CURRENT_RELATIVE_HUMIDITY, 0);


homekit_accessory_t *accessories[] = {
    HOMEKIT_ACCESSORY(.id=1, .category=homekit_accessory_category_bridge, .services=(homekit_service_t*[]) {
        HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]) {
            HOMEKIT_CHARACTERISTIC(NAME, "Motion and Light Sensor"),
            HOMEKIT_CHARACTERISTIC(MANUFACTURER, "Studio Webux S.E.N.C"),
            HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "00000002"),
            HOMEKIT_CHARACTERISTIC(MODEL, "ESP8266"),
            HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "1.0"),
            HOMEKIT_CHARACTERISTIC(IDENTIFY, my_accessory_identify),
            NULL
        }),
        NULL
    }),

    HOMEKIT_ACCESSORY(.id=2, .category=homekit_accessory_category_sensor, .services=(homekit_service_t*[]) {
        HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]) {
            HOMEKIT_CHARACTERISTIC(NAME, "Light Sensor"),
            HOMEKIT_CHARACTERISTIC(IDENTIFY, my_accessory_identify),
            NULL
        }),
        HOMEKIT_SERVICE(LIGHT_SENSOR, .primary=true, .characteristics=(homekit_characteristic_t*[]) {
            HOMEKIT_CHARACTERISTIC(NAME, "Light"),
            &cha_light,
            NULL
        }),
        NULL
    }),

    HOMEKIT_ACCESSORY(.id=3, .category=homekit_accessory_category_sensor, .services=(homekit_service_t*[]) {
        HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]) {
            HOMEKIT_CHARACTERISTIC(NAME, "Motion Sensor"),
            HOMEKIT_CHARACTERISTIC(IDENTIFY, my_accessory_identify),
            NULL
        }),
        HOMEKIT_SERVICE(MOTION_SENSOR, .primary=true, .characteristics=(homekit_characteristic_t*[]) {
            HOMEKIT_CHARACTERISTIC(NAME, "Motion"),
            &cha_motion,
            NULL
        }),
        NULL
    }),

    HOMEKIT_ACCESSORY(.id=4, .category=homekit_accessory_category_sensor, .services=(homekit_service_t*[]) {
        HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]) {
            HOMEKIT_CHARACTERISTIC(NAME, "Occupancy Sensor"),
            HOMEKIT_CHARACTERISTIC(IDENTIFY, my_accessory_identify),
            NULL
        }),
        HOMEKIT_SERVICE(OCCUPANCY_SENSOR, .primary=true, .characteristics=(homekit_characteristic_t*[]) {
            HOMEKIT_CHARACTERISTIC(NAME, "Occupancy"),
            &cha_occupancy,
            NULL
        }),
        NULL
    }),

    HOMEKIT_ACCESSORY(.id=5, .category=homekit_accessory_category_sensor, .services=(homekit_service_t*[]) {
        HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]) {
            HOMEKIT_CHARACTERISTIC(NAME, "Temperature Sensor"),
            HOMEKIT_CHARACTERISTIC(IDENTIFY, my_accessory_identify),
            NULL
        }),
        HOMEKIT_SERVICE(TEMPERATURE_SENSOR, .primary=true, .characteristics=(homekit_characteristic_t*[]) {
            HOMEKIT_CHARACTERISTIC(NAME, "Temperature"),
            &cha_current_temperature,
            NULL
        }),
        HOMEKIT_SERVICE(HUMIDITY_SENSOR, .characteristics=(homekit_characteristic_t*[]) {
            HOMEKIT_CHARACTERISTIC(NAME, "Humidity"),
            &cha_current_humidity,
            NULL
        }),
        NULL
    }),

    NULL
};

homekit_server_config_t config = {
		.accessories = accessories,
		.password = "123-45-678",
        //.on_event = on_event,
        //.setupId = "ABCD"
};

Le buzzer (Active Buzzer)

Permet d’émettre des “bips”

Voici le code utilisé:

Permet de configurer le nombre de bip et le temps entre chaque.

buzzer.h

#ifndef BUZZER_H_
#define BUZZER_H_

#include "spec.h"

void Buzz(int iterations, int waitFor)
{
    for(int i = 0; i < iterations; i ++){
        digitalWrite(buzzer, HIGH);
        delay (50);
        digitalWrite(buzzer, LOW);
        delay (waitFor);
    }
}

#endif /* BUZZER_H_ */

Température et Humidité

Le DHT22 est un module qui est très lent pour prendre ses lectures. Dans ce projet ce n’est pas très grave.

dht22.h

#ifndef DHT22_H_
#define DHT22_H_

#include "DHT.h"
#include "spec.h"
#include "screen.h"

float GetHumidity()
{
    float h = dht.readHumidity();

    if (isnan(h))
    {
        // Serial.println("Failed to read from DHT sensor!");
        return 1337;
    }

    return h;
}

float GetTemperature()
{
    float t = dht.readTemperature();

    if (isnan(t))
    {
        // Serial.println("Failed to read from DHT sensor!");
        return 1337;
    }

    return t;
}

String reportTemperature()
{
    float t = GetTemperature();

    NotifyHomeKit("TEMPERATURE", 0, t);

    // Serial.print("Temperature: ");
    // Serial.print(t);
    // Serial.println(" *C");

    return "Temperature: " + String(t) + " *C";
}

String reportHumidity()
{
    float h = GetHumidity();

    NotifyHomeKit("HUMIDITY", 0, h);

    // Serial.print("Humidity: ");
    // Serial.print(h);
    // Serial.println(" %");

    return "Humidity: " + String(h) + " %";
}

#endif /* DHT22_H_ */

Notification

Fonction utilisée pour mettre à jour le Apple HomeKit et envoyer les notifications avec le service de push notification.

notification.h

#ifndef NOTIFICATION_H_
#define NOTIFICATION_H_

// #define DEBUG
#include "wifi_webux.h"

extern "C" homekit_characteristic_t cha_light;
extern "C" homekit_characteristic_t cha_motion;
extern "C" homekit_characteristic_t cha_occupancy;
extern "C" homekit_characteristic_t cha_current_temperature;
extern "C" homekit_characteristic_t cha_current_humidity;

void SendNotification(String message)
{
    const int LEN = message.length();

    CheckWiFi();

    WiFiClient client;
    const int httpPort = 80;
    if (!client.connect(host, httpPort))
    {
        return;
    }

    client.print(String("POST ") + url + " HTTP/1.1\r\n" +
                 "Host: " + host + "\r\n" +
                 "Content-Type: application/x-www-form-urlencoded\r\n" +
                 "Content-Length: " + LEN + "\r\n\r\n" +
                 message);

    unsigned long timeout = millis();
    while (client.available() == 0)
    {
        if (millis() - timeout > 5000)
        {
            client.stop();
            return;
        }
    }
}

void NotifyHomeKit(String name, int value, float fValue=0)
{
    if (name == "LDR")
    {
        cha_light.value.float_value = fValue;
        homekit_characteristic_notify(&cha_light, cha_light.value);
    }
    else if (name == "MOTION")
    {
        cha_motion.value.bool_value = value;
        cha_occupancy.value.uint8_value = value;
        homekit_characteristic_notify(&cha_occupancy, cha_occupancy.value);
        homekit_characteristic_notify(&cha_motion, cha_motion.value);
    }
    else if (name == "TEMPERATURE")
    {
        cha_current_temperature.value.float_value = fValue;
        homekit_characteristic_notify(&cha_current_temperature, cha_current_temperature.value);
    }
    else if (name == "HUMIDITY")
    {
        cha_current_humidity.value.float_value = fValue;
        homekit_characteristic_notify(&cha_current_humidity, cha_current_humidity.value);
    }
}

#endif /* NOTIFICATION_H_ */

Le LDR

La photorésistance,

Lecture et convertion du résultat en LUX, (je vais tenter de retrouver mes sources pour ces fonctions.)

La valeure du LUX ne peut pas être égale à 0 alors la fonction automatiquement va retournée 0.0001.

ldr.h

#ifndef LDR_H_
#define LDR_H_

#include "spec.h"
#include "notification.h"

int sensorRawToPhys(int raw)
{
    float Vout = float(raw) * (VIN / float(1023));   // Conversion analog to voltage
    float RLDR = (RESISTANCE * (VIN - Vout)) / Vout; // Conversion voltage to resistance
    int phys = 500 / (RLDR / 1000);                  // Conversion resitance to lumen
    return phys;
}

float ReadLDR()
{
    float photocellReading = analogRead(ldr_sensor);
    //Serial.println(photocellReading);

    if (photocellReading < 10)
    {
        lightState = "Dark";
    }
    else if (photocellReading < 200)
    {
        lightState = "Dim";
    }
    else if (photocellReading < 500)
    {
        lightState = "Light";
    }
    else if (photocellReading < 800)
    {
        lightState = "Bright";
    }
    else
    {
        lightState = "Very bright";
    }

    sensorVal = analogRead(ldr_sensor);
    lux = sensorRawToPhys(sensorVal);

    if (lux <= 0)
    {
        // Workaround to fix the issue that returns : the accessory isn't responding
        lux = 0.0001;
    }

    return lux;
}

String reportLDR()
{
    float l = ReadLDR();

    NotifyHomeKit("LDR", 0, l);

    // Serial.print("Lux: ");
    // Serial.println(l);

    return "Lux: " + String(l);
}

#endif /* LDR_H_ */

La détection de mouvement

Le module HC-SR501 fonctionne sur le 5V (c’est le seul dans ce projet)

Il vérifie si une présence est détectée et possède un cooldown. Le buzzer émet des bips lors d’une détection et une notification est envoyée sur l’écran, sur le service de push notification et le Apple HomeKit.

pir.h

#ifndef PIR_H_
#define PIR_H_

#include "spec.h"
#include "buzzer.h"
#include "notification.h"

void checkPIR()
{
    unsigned long currentMillis = millis();

    if (flag && isCoolingDown && currentMillis - previousMillis >= cooldown)
    {
        digitalWrite(status_LED, LOW);
        NotifyHomeKit("MOTION", HOMEKIT_OCCUPANCY_NOT_DETECTED);
        flag = false;
        isCoolingDown = false;
    }

    if (currentMillis - lastCheck >= checkInterval)
    {
        // Serial.println("Reading Motion Sensor !");
        motion_state = digitalRead(motion_sensor);

        if (motion_state == HIGH)
        {
            digitalWrite(status_LED, HIGH);
            flag = true;

            // Serial.println("Motion detected!");

            if (currentMillis - previousMillis >= interval)
            {
                if (!isCoolingDown)
                {
                    isCoolingDown = true;
                    Buzz(3, 100);
                }

                NotifyHomeKit("MOTION", HOMEKIT_OCCUPANCY_DETECTED);
                UpdateDisplay("Motion Detected !");

                // Serial.println("Send Notification");
                message = "user=" + userKey +
                          "&token=" + apiKey +
                          "&message=" + SENSOR_NAME + ": Motion Detected !\r\n";
                SendNotification(message);

                previousMillis = currentMillis;
            }
        }
        lastCheck = currentMillis;
    }
    else
    {
        return;
    }
}

#endif /* PIR_H_ */

L’écran

Une écran simple pour afficher des messages.

screen.h

#ifndef SCREEN_H_
#define SCREEN_H_

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "spec.h"

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Screen
// const int SCL = 5; // D1
// const int SDA = 4; // D2
// Vin == 3.3v

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void TestScreen()
{
    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))
    { // Address 0x3C for 128x64
        Serial.println(F("SSD1306 allocation failed"));
        for (;;)
            ;
    }
    delay(2000);
    display.clearDisplay();

    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0, 10);
    // Display static text
    display.println("Hello, world!");
    display.println("By Studio Webux");
    display.display();
    delay(1000);
}

void UpdateDisplay(String displayMessage)
{
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(1, 10);

    display.println(displayMessage);
    display.display();
}

#endif /* SCREEN_H_ */

Le WiFi

webux_wifi.h

#ifndef WIFI_WEBUX_H_
#define WIFI_WEBUX_H_

// WiFi
const char *ssid = "";
const char *password = "";

void ConnectWiFi()
{
    WiFi.persistent(false);
    WiFi.mode(WIFI_STA);
    WiFi.setAutoReconnect(true);
    WiFi.begin(ssid, password);

    while (!WiFi.isConnected())
    {
        delay(500);
    }
}

void CheckWiFi()
{
    if (WiFi.status() != WL_CONNECTED)
    {
        ConnectWiFi();
        return;
    }
}

#endif /* WIFI_WEBUX_H_ */

La page web

Cette fonction provient du détecteur d’ouverture de porte et n’a pas été adapté totalement pour ce projet.

web.h

#ifndef WEB_H_
#define WEB_H_

#include "spec.h"

void handle_index()
{
    String page = "<!DOCTYPE html><html>";

    page += "<head>";
    page += "<title>Motion and Light Sensor (" + LOCATION + ")</title>";
    page += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
    page += "<meta charset=\"UTF-8\">";
    page += "<meta name=\"description\" content=\"Motion and Light Status\">";
    page += "<meta name=\"author\" content=\"Studio Webux S.E.N.C\">";
    page += "</head>";

    page += "<body>";
    page += "<h1>Motion and Light Status</h1>";
    page += "<p>Light : <ul><li>LUX: " + String(lux) + "</li><li>Light: " + lightState + "</li></ul></p>";
    page += "<p>Motion : <b>" + String(motion_state) + "</b></p>";
    page += "<p>" + SENSOR_NAME + " | " + LOCATION + "</p>";
    page += "</body>";

    page += "</html>";

    server.send(200, "text/html", page);
}

void handle_api()
{
    String JSON = "{";

    JSON += "\"location\":\"" + LOCATION + "\",";
    JSON += "\"sensor_name\":\"" + SENSOR_NAME + "\",";
    JSON += "\"lux\":\"" + String(lux) + "\",";
    JSON += "\"light\":\"" + String(lightState) + "\",";
    JSON += "\"motion\":\"" + String(motion_state) + "\"";

    JSON += "}";

    server.send(200, "application/json", JSON);
}

void StartHTTPServer()
{
    server.on("/", handle_index);  //Handle Index page
    server.on("/api", handle_api); //Handle API page

    server.begin(); //Start the server
}

#endif /* WEB_H_ */

Le main

le fichier .ino

main.ino

/*
    Studio Webux S.E.N.C 2020-12-05
*/

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

#include <arduino_homekit_server.h>
#include "DHT.h"
#include "spec.h"
#include "wifi_webux.h"
#include "notification.h"
#include "web.h"
#include "screen.h"
#include "buzzer.h"
#include "pir.h"
#include "ldr.h"
#include "dht22.h"

// #define LOG_D(fmt, ...)   printf_P(PSTR(fmt "\n") , ##__VA_ARGS__);

extern "C" homekit_server_config_t config;

unsigned long lastBroadcast = 0;
// static uint32_t next_heap_millis = 0;
static uint32_t next_report_millis = 0;


void my_homekit_setup() {
	arduino_homekit_setup(&config);
}

void my_homekit_loop() {
	arduino_homekit_loop();
	const uint32_t t = millis();
	if (t > next_report_millis) {
		// report sensor values every X seconds
		next_report_millis = t + reportInterval * 1000;
    String txt = "Report\n\n";

    txt += reportTemperature() + "\n";
    txt += reportHumidity() + "\n";
    txt += reportLDR() + "\n";

    UpdateDisplay(txt);

	}
}

void setup()
{
  // Serial.begin(115200);
  pinMode(motion_sensor, INPUT); // declare motion_sensor as input
  pinMode(status_LED, OUTPUT);   // declare LED as output
  pinMode(buzzer, OUTPUT);       // Declare the Active buzzer as output

  ConnectWiFi();

  my_homekit_setup();

  message = "user=" + userKey +
            "&token=" + apiKey +
            "&message=" + SENSOR_NAME + ": Powered ON (" + WiFi.localIP().toString() + ")\r\n";
  SendNotification(message);

  StartHTTPServer();

  dht.begin();
  delayMicroseconds(50);

  TestScreen();
  UpdateDisplay("My IP \n" + WiFi.localIP().toString());

  Buzz(2, 75);

  delay(10000);
}

void loop()
{
  my_homekit_loop();
  delay(10);

  // //Handling of incoming client requests
  server.handleClient();

  checkPIR();
}

Étape 2 - Le schéma

Je n’ai aucune formation en électronique. Ce schéma fonctionne et peut certainement être optimisé et amélioré ! Vous pouvez laisser un commentaire, c’est grandement apprécié

Voici les schémas fait avec Fritzing

Le schéma:

le breadboard:

Le fil Bleu est du 3.3v Le fil Rouge est du 5v Le fil Jaune, Violet & Cyan sont du Digital Le fil Orange est un Analogue

Améliorations

Photos de l’assemblage

Le boitier est un prototype. Je dois refaire plusieurs trucs pour ajuster les trous et l’espacement entre les modules.

Sources


Recherche