POCL Parapluie connecté : Différence entre versions

De Les Fabriques du Ponant
Aller à : navigation, rechercher
(Code)
(Code)
Ligne 454 : Ligne 454 :
  
 
// ssid AP : concaténation POCL_NAME + "-" + POCL_ID
 
// ssid AP : concaténation POCL_NAME + "-" + POCL_ID
const char *POCL_ID = "1"; // id unique du POCL à paramétrer
+
 
 +
const char *POCL_ID = "1";               // id unique du POCL à paramétrer
 
const char *POCL_NAME = "POCL-Parapluie"; // nom du POCL
 
const char *POCL_NAME = "POCL-Parapluie"; // nom du POCL
 +
 
// mot de pas AP: minimun 8 caractères
 
// mot de pas AP: minimun 8 caractères
 
const char *AP_PW = "lpdgo2021";
 
const char *AP_PW = "lpdgo2021";
  
#define CLEF_API  "remplacemoi"// Clef de l'API par default à remplacer ici ou via le site
+
#define CLEF_API  "remplacemoi"           // Clef de l'API par default à remplacer ici ou via le site
#define ID_VILLE "3030300" // ID ville par default à remplacer ici ou via le site
+
#define ID_VILLE "3030300"               // ID ville par default à remplacer ici ou via le site
 
/*Quelques identifiants d'autres villes françaises :
 
/*Quelques identifiants d'autres villes françaises :
 
  * 3030300, Brest
 
  * 3030300, Brest

Version du 16 juin 2021 à 14:52

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 ouvert s'il pleut et il est fermé s'il ne pleut pas.

Comme la météo ne change pas souvent, il possède aussi une interface web permettant de l'activer à volonté !

Photographies du prototype n°1 :

Parapluieferme.jpg ParapluieOuvert.jpg

Parapluiewebtaille.gif

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

ParapluieMontage.jpg

ParapluieFritzing.png

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 D1 mini 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  
}

Parapluieweb.gif


Prototype n°2 du POCL-Parapluie

Prototype n°2

Matériel supplémentaire :

- 1 ombrelle à cocktail

- 1 cure-dents

- 3 câbles Dupont

- De la colle

!!! Le porte-carte maintient le support de la crémaillère droit, afin que les rouages fonctionnent, respecter l'ordre des éléments !!!

Les fichiers à imprimer :

Toutes les pièces : Fichier:1-Parapluie-pieces-finales.stl

Le porte-servo : Fichier:1-Porte-servo.stl

Le porte-carte : Fichier:1-Porte-carte.stl

Les rouages : Fichier:1-Rouages.stl

Code

  • Téléchargement du code :

Télécharger le fichier zip de la branche principale (main) du dépôt Github : https://github.com/t-vernet/petitParapluieConnecte/tree/v1.0.0 Télécharger le fichier zip de WiFiManager : https://github.com/tzapu/WiFiManager Télécharger le fichier zip de WebSocketsServer : https://github.com/Links2004/arduinoWebSockets

  • Paramètres à modifier au besoin

// ssid AP : concaténation POCL_NAME + "-" + POCL_ID

const char *POCL_ID = "1"; // id unique du POCL à paramétrer const char *POCL_NAME = "POCL-Parapluie"; // nom du POCL

// mot de pas AP: minimun 8 caractères const char *AP_PW = "lpdgo2021";

  1. define CLEF_API "remplacemoi" // Clef de l'API par default à remplacer ici ou via le site
  2. define ID_VILLE "3030300" // ID ville par default à remplacer ici ou via le site

/*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
*/
  1. define ANGLE_FERME 0 // angle pour fermer le parapluie
  2. define ANGLE_OUVERT 170 // angle pour ouvrir le parapluie

Boîte pour le Pocl Parapluie :

Murs, haut et bas à découper sur du medium 5mm d'épaisseur, face avant à découper en plexiglas de 5mm :

Erreur lors de la création de la miniature :
Boîte à Pocl n°1
Face en plexiglas