POCL : The Big Red One
Sommaire
résumé du projet
Nous sommes parties de l'idée d'une donnée peu utile qui peut paraitre absurde, mais don le concept est rigolo, il s'agit d'un gros bouton rouge sur un dispositif qui compte le nombre de fous ou les personnes, on appuie dessus. Ce dispositif pourrait donc être installé dans plein d'endroits différant et permet de voir ou les gens son le plus curieux et le plus nombreux. À partir de cette idée amusante, nous nous sommes intéressé au potentiel que pourrais avoir ce dispositif pour passer un message plus complexe, car en attirant les personnes avec un gros bouton rouge au milieu d'une boite de manière ludique, on peut les emmener vers des questionnements plus profonds.
membres du projet
Matériel utilisé
-Matériel-
-Carte arduino mini
-Écran max7219
-Bois
-Colle a bois
-Découpeuse laser
-Imprimante 3D
-Logiciel-
-Inkscape (image vectoriel)
- Wix (création de site)
-Blender (Modélisation 3D)
-Cura (préparation d'impression 3D)
prototype du projet
Nous avons commencé par construire la boite de ce projet avec MakerCase https://fr.makercase.com/#/ Nous avons ensuite rajouté les décors de la boite sur Inkscape. Puis nous l'avons découpé sur la découpeuse Laser.
Diaporama
/* ========================================================================================================= * * POCL Trends - Hackathon * * --------------------------------------------------------------------------------------------------------- * Les petits Débrouillards - décembre 2022 - CC-By-Sa http://creativecommons.org/licenses/by-nc-sa/3.0/ * ========================================================================================================= */ // Bibliothèques requises // ATTENTION AUX MAJUSCULES & MINUSCULES ! Sinon d'autres bibliothèques, plus ou moins valides, seraient utilisées. #include <WiFiManager.h> // Gestion de la connexion Wi-Fi (recherche de points d'accès) #include <ESP8266HTTPClient.h> // Gestion des requêtes HTTP vers le serveur #include <WiFiClient.h> // Gestion de la connexion (HTTP) à un serveur de données #include <ArduinoJson.h> // Fonctions de décodage JSON des réponses du serveur. #include "LiquidCrystal_I2C.h" // Gestion de l'afficheur LCD 1602 avec module i2c #include <MD_Parola.h> #include <MD_MAX72xx.h> // Gestion afficheur MAX7219 #include <SPI.h> // Variables globales WiFiManager myWiFiManager; // Création de mon instance de WiFiManager. const char* mySSID = "AP_PetitDeb" ; // Nom de la carte en mode Point d'Accès. const char* mySecKey = "PSWD1234" ; // Mot de passe associé, 8 caractères au minimum. /* char* Data_HOST = "data.rennesmetropole.fr"; // Serveur web hébergeant les données qui nous intéressent int Data_PORT = 443; // Port sur lequel envoyer la requête char* Data_REQUEST = // Requête (sur cet exemple : demande de l'état du trafic au point // 31553, correspondant à la porte de Saint-Malo de la rocade de Rennes "/api/records/1.0/search/?dataset=etat-du-trafic-en-temps-reel&q=31553"; */ char* GL_HTTP_REQUEST = "http://nodered.lenuage.io/trends" ; // Requête (appel à un serveur intermédiaire compliant les infos de googles trends) const int MAX_RESPONSE_SIZE = 8000 ; // Taille max de la réponse attendue d'un serveur. A modifier en fonction du besoin. char GL_httpResponse[MAX_RESPONSE_SIZE] ; // Buffer qui contiendra la réponse du serveur. // LiquidCrystal_I2C LCD(0x27,16,2); // Définition de notre écran LCD. //------------------------------ // Define hardware type, size, and output pins: #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4 #define CS_PIN D3 #define DATA_PIN D2 #define CLK_PIN D1 // Create a new instance of the MD_Parola class with hardware SPI connection: // Setup for software SPI: MD_Parola myDisplay = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); //------------------------------- #define TEN_SECONDS 10000 // On appelera le serveur de données toutes les 10000 ms = 10 secondes. unsigned long myWakeUp ; // Timer mis en place pour limiter le nombre d'appels au serveur de données. /* -------------------------------------------------------------------------------------------------------------- * GetServerResponse() : Envoi requête HTTP au serveur et récupération de la réponse * valeur de retour : 1 = ok, 0 = pas de connexion serveur, -1 = erreur HTTP GET, -2 = réponse anormale (trop de caractères) * ------------------------------------------------------------------------------------------------------------- */ int getServerResponse() { WiFiClient myWiFiClient; HTTPClient myHTTPClient; /* * Connexion au serveur ... */ if (!myHTTPClient.begin(myWiFiClient, GL_HTTP_REQUEST)) { Serial.printf("[HTTP} Unable to connect\n"); return(0) ; } /* * Envoi de l'entête HTTP ... */ Serial.print("[HTTP] GET...\n"); int httpCode = myHTTPClient.GET(); if (httpCode <= 0) { Serial.printf("[HTTP] GET... failed, error: %s\n", myHTTPClient.errorToString(httpCode).c_str()); myHTTPClient.end(); return(-1) ; } /* * Requête HTTP OK ! --> On fait à nouveau clignoter les leds en bleu (3 flashs longs). */ /* * Lecture des données qui nous intéressent. On récupère caractère par caractère, (semble plus sûr que la fonction payload * qui plante de temps en temps, d'après les forums. De plus, cela permet de faire défiler les leds pour montrer que * ça suit son cours ... */ int myIndex = 0 ; while(myWiFiClient.available() || myWiFiClient.connected()){ int myResp = myWiFiClient.read(); if (myResp == -1) { break ; } GL_httpResponse[myIndex] = myResp; // Serial.print(GL_httpResponse[myIndex]) ; if (myIndex++ > MAX_RESPONSE_SIZE) { Serial.println("Réponse trop longue : " + String(MAX_RESPONSE_SIZE) + "caractères, et ne peut pas être traitée") ; myWiFiClient.stop(); return(-2); } } /* * Tout s'est bien passé --> on passe les leds en vert, et on se déconnecte du serveur. */ myHTTPClient.end(); return(1) ; } /* -------------------------------------------------------------------------------------------------------- * showJSONAnswer : Décodage de la structure de données JSON * Paramètres : * - pResponse : endroit se trouve la réponse (au format JSON) du serveur * - pRespMax : nombre max de caractères autorisés pour la réponse * -------------------------------------------------------------------------------------------------------- */ void showJSONAnswer(char *pResponse, int pRespMax) { // Création de notre structure JSON // Le besoin en mémoire (capacity) doit être vérifié sur l'assistant https://arduinojson.org/v6/assistant/ // 1) dans la première page de l'assistant, sélectionnez le processeur (par exemple "ESP8266"), le mode // "Deserialize", et le type d'entrée "char*", puis cliquez sur le bouton "Netx:JSON" // 2) Lancez votre requête depuis un navigateur. Dans notre exemple, tapez dans la barre d'adresse : // "https://data.rennesmetropole.fr/api/records/1.0/search/?dataset=etat-du-trafic-en-temps-reel&q=31553" // 3) Recopiez la réponse obtenue - sous sa forme "Données Brutes" du navigateur vers l'assistant // 4) L'assistant va alors préconiser le bon objet à créer (StaticJsonDocument ou DynamicJsonDocument), // ainsi que la taille à réserver. L'assistant va même proposer un exemple de programme exploitant toutes // les informations de la structure JSON. // Pour notre exemple, l'assistant a proposé la définition qui suit. StaticJsonDocument<300> doc; // Décodage de la réponse JSON. // La fonction deserializeJson va transformer la réponse "texte" du serveur, en une structure de données recopiée // dans la variable 'doc', où il sera ensuite facile d'aller chercher les informations souhaitées. DeserializationError error = deserializeJson(doc, pResponse, pRespMax); if (error) { Serial.println("--- Décodage réponse JSON KO, code " + String(error.f_str())) ; return; } Serial.println("--- Décodage réponse JSON OK !") ; // Nous pouvons maintenant extraire facilement les informations qui nous intéressent, // en n'oubliant pas le niveau de profondeur de la donnée au sein de la structure JSON. // Ce niveau de profondeur est incrémenté par le nombre de '{' ou '[' rencontrés, et // décrémenté lors de la rencontre des ']' et {}'. Sur notre exemple 'rocade de Rennes', // cela donne ceci : // +-----------------------------------------------------------------+ // | { | ... Entrée niveau 1 // | "nhits": 1, | // | "parameters": { | ... Entrée niveau 2 // | "dataset": "etat-du-trafic-en-temps-reel", | // | (...) | // | }, | ... Retour niveau 1 // | "records": [ | ... Début d'un tableau : niveau 2 // | { | ... Entrée niveau 3 // | (...) | | // | "fields": { | ... Entrée niveau 4 // | (...) | // | "averagevehiclespeed": 88, | // | (...) | // | "datetime": "2022-11-30T11:57:00+01:00", | // +-----------------------------------------------------------------+ // ... et donc : // - (1er niveau) --------- doc["nhits"] donnera la valeur 1, // - (2ème niveau) -------- doc["parameters"]["dataset"] donnera la valeur "etat-du-trafic-en-temps-reel" // - (4ème niveau) -------- doc["records"][0]["fields"]["averagevehiclespeed"] donnera la valeur 87 // Extraction et affichage sur le port série de trois valeurs char myMessage[80]; for (int i = 0; i < 5; i++) { const char* Title = doc[i] ; Serial.println("Trend n° " + String(i) + " : " + String(Title)) ; // LCD.setCursor(1, 0); sprintf(myMessage, "Trends n° %d - %s", i, Title) ; // LCD.print(myMessage); myDisplay.print(myMessage); delay(500) ; // LCD.clear() ; } } /* -------------------------------------------------------------------------------------------------------- * SETUP : Initialisation * -------------------------------------------------------------------------------------------------------- */ void setup() { // Initialisation de la liaison série, affichage 1er message Serial.begin(115200); delay(100) ; Serial.println(); Serial.println("-----------------------") ; Serial.println("Exemple extraction JSON") ; Serial.println("-----------------------") ; // Initialisation de l'écran LCD. // LCD.init(); // initialisation de l'afficheur // LCD.backlight(); myDisplay.begin(); myDisplay.displayClear(); // Tentative de connexion au Wi-Fi. Si la carte n'a pas réussi se connecter au dernier Point d'Accès connu, // alors elle va se positionner en mode Point d'Accès, demandera sur l'adresse 192.168.4.1 quel nouveau // Point d'Accès choisir. Par défaut, on restera bloqué tant que l'utilisateur n'aura pas fait de choix. Serial.println("Connexion au Wi-Fi ..."); // LCD.print("Connexion au Wi-Fi ...") ; myDisplay.print("Connexion au Wi-Fi ..."); if (myWiFiManager.autoConnect(mySSID, mySecKey)) { Serial.println(); Serial.print("Connecté ! Adresse IP : "); // LCD.print("Connecté ! Adresse IP : ") ; LCD.print(WiFi.localIP()) ; Serial.println(WiFi.localIP()); } else { Serial.println("Connexion Wi-Fi KO :-("); } // Initialisation du timer qui sera testé dans loop() - pour faire appel au serveur seulement toutes les 10 secondes // millis() est une fonction système donnant le nombre de ms depuis le lancement ou la réinitialisation de la carte. unsigned long myWakeUp = millis() + TEN_SECONDS ; } /* -------------------------------------------------------------------------------------------------------------- * LOOP : fonction appelée régulièrement par le système * ------------------------------------------------------------------------------------------------------------- */ void loop() { unsigned long myNow = millis() ; if (myNow >= myWakeUp) { Serial.println("Wake Up ! Nouvelle demande au serveur ...") ; if (getServerResponse() == 1) { Serial.println("Réponse reçue du serveur, lancement analyse JSON ...") ; showJSONAnswer(GL_httpResponse, MAX_RESPONSE_SIZE) ; } myWakeUp = myNow + TEN_SECONDS ; } }