POCL Parapluie connecté
inspiré du travail de julien Levesque et d'Aurélien Fache voici un petit objet rigolo.
Connecté à la météo d'une ville choisie sur le site openweathermap, le parapluie et ouvdert s'il pleut et il est fermé s'il ne pleut pas.
Comme la météo ne change pas souvent, il a aussi une interfaceweb pour le tester !
Matériel
- Un D1 mini
- Un servomoteur
- Un parapluie Cocktail
pour bricoler :
- du fil de fer,
- un bouchon de liège,
- un pistolet à colle,
- du carton
A améliorer
- Modifier le code pour :
- choisir son réseau wifi
- choisir la ville
- meilleur design web
- créer une boitier à imprimer en 3D (à la place du carton)
code
///////////////
// PARAPLUIE //
///////////////
/*
* A FAIRE :
* installer wifiManager pour simplifier la connexion au wifi.
* inclure dans une page web un formulaire pour saisir le code la ville - https://randomnerdtutorials.com/esp32-esp8266-input-data-html-form/ et
* https://circuits4you.com/2019/03/20/esp8266-receive-post-get-request-data-from-website/
* enregistrer le code de la ville dans l'EEPROM - https://circuits4you.com/2016/12/16/esp8266-internal-eeprom-arduino/
*/
/* Un programme pour récupérer les données météo du site http://openweathermap.org/
* puis les afficher sur le moniteur série (partie de programme inspiré de http://educ8s.tv/esp8266-weather-display/)
* et ouvre ou ferme un parapluie chinois suivant les précipitations d'un lieu choisis (inspiré de http://julienlevesque.net/little-umbrella/ )
* Cette version intègre une page web de test du parapluie en hackant des bout de codes trouvés ici : https://www.ulasdikme.com/projects/esp8266/esp8266_ajax_websocket.php
* Antony Le Goïc-Auffret
* sous licence CC-By-SA - dimanche 1er août 2020 - Brest- Bretagne - France - Europe - Terre - Système solaire - Voie Lactée.
_
_| |_
_/ / \ \_
_/ / \ \_
_/ / \ \_
_/ _._ / _._ \ _._ \_
/ \_/ \_/ |_|\_/ \_/ \
/|#|
| | |
| | |
| | |
| | |
| | |
| | |
____|_\_/_
| | |
BROCHAGE | | |
_________________ ______|____|_____|__________
/ D1 mini \ | | |
Non Attribué - |[ ]RST TX[ ]| - Non Attribué | ___ | |
Non Attribué - |[ ]A0 -GPIO RX[ ]| - Non Attribué | |_°_| | |
Non Attribué - |[ ]D0-16 5-D1[ ]| - Non Attribué | | | | |
Non Attribué - |[ ]D5-14 4-D2[ ]| - Non Attribué | | | | |
Non Attribué - |[ ]D6-12 0-D3[X]| - gestion servo < _ | | __|___| |
Non Attribué - |[ ]D7-13 2-D4[X]| - LED_BUILTIN \ | | (o)_____/ |
Non Attribué - |[ ]D8-15 GND[X]| - alim servo <- || |Servo| |
Non Attribué - |[ ]3V3 . 5V[X]| - alim servo <_|_ || |_____| |
| +---+ | | \ \ |_°_| |
|_______|USB|_______| \ \ |\__/|| |
Le wemos est connecté en USB à votre ordinateur, \ \|___/ | |
le moniteur série ouvert \_|____/ |
|___________________________|
*/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <Servo.h>
#include <ESP8266WebServer.h>
ESP8266WebServer server(80); //declaration du serveur web sur le port 80
const char* nomDuReseau = "xxxxxxxxx"; // Nom du réseau wifi local (Ce qu'on appelle le SSID).
const char* motDePasse = "yyyyyyyyy"; // mot de passe du réseau wifi local
String cledAPI = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"; // clé de l'API du site http://openweathermap.org/
// il faudra vous créer un compte et récupérer votre clé d'API, une formalité !
// Sur le site, vous trouvez les identifiants de toutes les villes du monde.
String identifiantVille = "6427109"; //indiquez l'identifiant d'une ville (CityID), ici Brest en France
/*Quelques identifiants d'autres villes françaises :
* 3030300, Brest
* 6431033, Quimper
* 6430976, Morlaix
* 6453798, Lannion
* 6453805, Saint-Brieuc
* 6432801, Rennes
* 6437298, Lorient
* 2970777, Vannes
* 6434483, Nantes
* 6456407, Le Mans
* 6427109, Caen
* 6452361, Angers
* 6456577, La Roche sur Yon
* 3021411, Dieppe
*/
WiFiClient client;
char nomDuServeur[] = "api.openweathermap.org"; // Serveur distant auquel on va se connecter
String resultat;
int webClic = 0;
unsigned long dateDernierChangement = 0;
unsigned long dateCourante;
unsigned long intervalle;
Servo monservo; // créer un objet "monservo" pour le contrôler
String descriptionMeteo = "";
String lieuMeteo = "";
String Pays;
float Temperature;
float Humidite;
float Pression;
int para = 100;
int parastock;
int ferme = 90; // angle pour fermer le parapluie
int ouvre = 170; // angle pour ouvrir le parapluie
bool pluie = 1; // enregistre si il pleut ou pas.
String webSite,javaScript,XML; //déclaration de variables
int start=0; // variable start
void buildWebsite(){ // Fonction qui écrit le code html du site web
buildJavascript(); // appel de la fonction qui contruit le code javascript
webSite="<!DOCTYPE HTML>\n";
webSite+=javaScript; // insertion du javascript dans la page
webSite+="<HTML>\n";
webSite+="<style>\n";
webSite+="#button {\n";
webSite+="background-color: #E6E6FA;\n";
webSite+="border: none;\n";
webSite+="color: white;\n";
webSite+="padding: 32px;\n";
webSite+=" text-align: center;\n";
webSite+=" text-decoration: none;\n";
webSite+="display: inline-block;\n";
webSite+="font-size: 168px;\n";
webSite+="display:block;\n";
webSite+="margin:0 auto;\n";
webSite+="margin-top:130px;\n";
webSite+="cursor: pointer;\n";
webSite+="width:524px;\n";
webSite+="height:400px;\n";
webSite+="}\n";
webSite+="p.thicker{font-weight:900;}\n";
webSite+="#runtime{font-weight:900; font-size: 147%; color:RED;}\n";
webSite+="</style>\n";
webSite+="<BODY bgcolor='#E6E6FA' onload='process()'>\n";
webSite+="<button onClick='RunButtonWasClicked()' id='button'></button> ";
webSite+="</BODY>\n";
webSite+="</HTML>\n";
}
void buildJavascript(){ // Fonction qui contruit le code javascript
javaScript="<SCRIPT>\n";
javaScript+="var xmlHttp=createXmlHttpObject();\n";
javaScript+="function createXmlHttpObject(){\n";
javaScript+=" if(window.XMLHttpRequest){\n";
javaScript+=" xmlHttp=new XMLHttpRequest();\n";
javaScript+=" }else{\n";
javaScript+=" xmlHttp=new ActiveXObject('Microsoft.XMLHTTP');\n";
javaScript+=" }\n";
javaScript+=" return xmlHttp;\n";
javaScript+="}\n";
javaScript+="var click;\n";
javaScript+="function handleServerResponse(){\n";
javaScript+=" xmlResponse=xmlHttp.responseXML;\n";
javaScript+=" xmldoc = xmlResponse.getElementsByTagName('response');\n";
javaScript+=" message = xmldoc[0].firstChild.nodeValue;\n";
javaScript+="if(message == 1){click = 1; message = 'ouvert'; document.getElementById('button').style.background='#FFA200';}else{click=0; message='fermé'; document.getElementById('button').style.background='#111111';}\n";
javaScript+=" document.getElementById('button').innerHTML=message;\n";
javaScript+="}\n";
javaScript+="function process(){\n";
javaScript+=" xmlHttp.open('PUT','xml',true);\n";
javaScript+=" xmlHttp.onreadystatechange=handleServerResponse;\n"; // no brackets?????
javaScript+=" xmlHttp.send(null);\n";
javaScript+=" setTimeout('process()',200);\n";
javaScript+="}\n";
javaScript+="function process2(){\n";
javaScript+=" xmlHttp.open('SET','set1ESPval?Start='+click,true);\n";
javaScript+=" xmlHttp.send(null);\n";
javaScript+=" setTimeout('process2()',400);\n";
javaScript+="}\n";
javaScript+="function RunButtonWasClicked(){\n";
javaScript+="click = (click==1)?0:1;\n";
javaScript+=" xmlHttp.open('SET','set1ESPval?Start='+click,true);\n";
javaScript+=" xmlHttp.send(null);\n";
javaScript+="}\n";
javaScript+="</SCRIPT>\n";
}
uint16_t x;
String data; // variable data qui sert à ?
void buildXML(){
XML="<?xml version='1.0'?>";
XML+="<response>";
XML+=data;
XML+="</response>";
}
void handleWebsite(){ // génère le site web
buildWebsite(); // écriture du html
server.send(200,"text/html",webSite); // mise en ligne du site
}
void handleXML(){ // gère le xml (description de l'état du boutton)
buildXML();
server.send(200,"text/xml",XML);
}
void handle1ESPval(){
start = server.arg("Start").toFloat();
}
int start2=0;
int inc=0;
void setup() {
Serial.begin(9600);
Serial.println();
connexion();
delay(100);
server.on("/",handleWebsite);
server.on("/xml",handleXML);
server.on("/set1ESPval",handle1ESPval);
server.begin();
prendDonneesMeteo();
parapluie ();
}
void loop() {
dateCourante = millis();
intervalle = dateCourante - dateDernierChangement; // interval de temps depuis la dernière mise à jour du parapluie
if (intervalle >= 600000) //Récupère de nouvelles données toutes les 10 minutes
{
dateDernierChangement = millis();
prendDonneesMeteo(); // récupère les données météo
parapluie(); // met à jour le parapluie
//monservo.detach(); // débrancher le servomoteur de la broche D3 (GPIO 0)
}
if (!start == webClic) // si l'état du bouton web à changé
{
parastock = para; // stock la valeur "para" dans "parastock"
if (start)
{
para = 100; // triche sur la valeur "para" pour un test pluie
parapluie(); // met à jour le parapluie
Serial.println("parapluie fermé ");
}
if (!start)
{
para = 900; // triche sur la valeur "para" pour un test pluie
parapluie(); // met à jour le parapluie
Serial.println("parapluie ouvert ");
}
webClic = start; //met à jour webClic
para = parastock; // redonne à para sa valeur initiale
}
data =(String)start;
server.handleClient();
if ((intervalle%6000) == 0){ // toutes les 6 secondes, j'écris ces infos sur le moniteur série
ecritMeteoGeneral(lieuMeteo, descriptionMeteo);
Serial.print("interval modulo 6000 : "); Serial.println((intervalle%60));
Serial.print("interval : "); Serial.println(intervalle);
Serial.print("date Courante : ");Serial.println(dateCourante);
Serial.print("date du Dernier Changement : ");Serial.println(dateDernierChangement);
}
}
void prendDonneesMeteo() //Fonction qui utilise le client web du Wemos pour envoyer/recevoir des données de requêtes GET.
{
if (client.connect(nomDuServeur, 80)) { // Démarre la connexion du client, recherche les connexions
client.println("GET /data/2.5/weather?id=" + identifiantVille + "&units=metric&lang=fr&APPID=" + cledAPI);
client.println("Host: api.openweathermap.org");
client.println("User-Agent: ArduinoWiFi/1.1");
client.println("Connection: close");
client.println();
}
else {
Serial.println("Echec de la connexion"); //message d'erreur si la connexion échoue
Serial.println();
}
while (client.connected() && !client.available()) delay(1); // attend les données
while (client.connected() || client.available()) { // soit le client est connecté, soit il a des données disponibles
char c = client.read(); // récupère les données
resultat = resultat + c; // les agrège dans la chaine de caractère "resultat"
}
client.stop(); // stoppe le client
resultat.replace('[', ' ');
resultat.replace(']', ' ');
Serial.println(resultat); // écrit la chaine de caractère en entier sur le moniteur série
char tableauJson [resultat.length() + 1];
resultat.toCharArray(tableauJson, sizeof(tableauJson));
tableauJson[resultat.length() + 1] = '\0';
StaticJsonDocument<1024> doc;
DeserializationError error = deserializeJson(doc, tableauJson);
if (error) {
Serial.print(F("deserializeJson() failed with code "));
Serial.println(error.c_str());
return;
}
String lieu = doc["name"];
String pays = doc["sys"]["country"];
float temperature = doc["main"]["temp"];
float humidite = doc["main"]["humidity"];
String meteo = doc["weather"]["main"];
String description = doc["weather"]["description"];
String id = doc["weather"]["id"]; //récupère le chiffre identifiant "id" de l'état météo sous forme de texte.
float pression = doc["main"]["pressure"];
descriptionMeteo = description;
lieuMeteo = lieu;
Pays = pays;
Temperature = temperature;
Humidite = humidite;
Pression = pression;
para =id.toInt(); //transforme le texte "id" en entier.
}
void ecritMeteoGeneral(String lieu, String description)
{
Serial.println("------------------");
Serial.print(lieu);
Serial.print(", ");
Serial.print(Pays);
Serial.print(", ");
Serial.print(description);
Serial.print(", ");
Serial.println(para);
}
void parapluie ()
{
if (para<600) { // Si la valeur de l'indicateur météo est inférieur à 600 c'est qu'il pleut.
if (pluie == 0) { // Si avant ça il ne pleuvait pas
monservo.attach(0); // brancher le servomoteur sur la broche D3 (GPIO 0)
monservo.write(ouvre); // ouvre le parapluie
Serial.print("ouvre à : ");Serial.println(ouvre);
pluie = 1; // note qu'il pleut
Serial.print("pluie à : ");Serial.println(pluie);
}
}
else { // si il ne pleut pas
if (pluie == 1) { // et que juste avant il pleuvait (le parapluie était donc ouvert).
monservo.attach(0); // brancher le servomoteur sur la broche D3 (GPIO 0)
monservo.write(ferme); // ferme le parapluie
Serial.print("ferme à : ");Serial.println(ferme);
pluie = 0; // note bien qu'il ne pleut pas
Serial.print("pluie à : ");Serial.println(pluie);
}
}
delay (200);
monservo.detach(); // débrancher le servomoteur de la broche D3 (GPIO 0)
}
void connexion() {
WiFi.mode(WIFI_STA); // Le Wemos est en mode station wifi.
Serial.println("Connexion");
WiFi.begin(nomDuReseau, motDePasse);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("Connecté");
Serial.print("Station IP address: ");
Serial.println(WiFi.localIP());
pinMode(LED_BUILTIN, OUTPUT); // Configure la broche D4 (GPIO 2) sur la quelle est branchée la LED_BUILTIN en sortie
digitalWrite(LED_BUILTIN, LOW); // Allume la LED_BUILTIN
}


