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:
- 1x DHT22 (Température et Humidité)
- 1x Photorésistance (Photoresistor)
- 1x Écran 0.96in 128x64 SSD1306
- 1x Détection de mouvement (HC-SR501)
- 1x ESP8266
- 1x Active Buzzer
- 1x LED 5mm (Rouge)
- 1x Potentiomètre 1K Ohm
- 1x Résistance 10K Ohm
- 1x Résistance 100 Ohm
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,
- DHT Sensor Library de Adafruit Version 1.4.0
- Adafruit GFX Library de Adafruit Version 1.10.2
- Adafruit SSD1306 de Adafruit Version 2.4.1
É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 :
- Changer le numéro de série (
HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "00000002"),
) - Changer le nom du capteur (
HOMEKIT_CHARACTERISTIC(NAME, "Sensor NAME 00000002"),
) - Puis pour des raisons de sécurité, changer le mot de passe (
.password = "123-45-678",
)
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.
- Dans le cas où la lecture n’est pas valide, la valeure retournée sera
1337
. - Les deux fonctions de report, sont utilisés pour mettre à jour le Apple HomeKit et l’affichage sur l’écran.
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 fonction report met à jour l’écran et le Apple HomeKit.
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
- Compléter la page web pour ajouter les modules.
- Faire la version adaptée du boitié.
Photos de l’assemblage
Le boitier est un prototype. Je dois refaire plusieurs trucs pour ajuster les trous et l’espacement entre les modules.