Initial commit

main
Alex 2022-05-24 18:49:52 +02:00
commit ad780bd65d
14 changed files with 1123 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

6
.vscode/arduino.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"port": "COM7",
"configuration": "xtal=80,vt=flash,exception=disabled,stacksmash=disabled,ssl=all,mmu=3232,non32xfer=fast,eesz=4M2M,led=2,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none,baud=115200",
"board": "esp8266:esp8266:nodemcuv2",
"sketch": ".pio\\libdeps\\nodemcuv2\\WiFiManager\\examples\\AutoConnectWithStaticIP\\AutoConnectWithStaticIP.ino"
}

10
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}

39
include/README Normal file
View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

46
lib/README Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

20
platformio.ini Normal file
View File

@ -0,0 +1,20 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
monitor_speed = 115200
upload_port = COM11
framework = arduino
lib_deps =
adafruit/Adafruit NeoPixel@^1.10.4
knolleary/PubSubClient@^2.8
tzapu/WiFiManager@^0.16.0

197
src/main.cpp Normal file
View File

@ -0,0 +1,197 @@
// ---------------------------------------------------------------------------------------
// Programme permettant :
// - d'allumer/éteindre une barre de LEDS (branchée sur la PIN D1 ).
// - de changer la couleur des LEDS.
// - de modifier l'intensité des LEDS.
// - de lancer une animation des LEDS.
//
// Le programme se branche en WIFI sur un broker MQTT et réagi au topics :
// lumiere/<DeviceID> [ON|OFF] : Allumage de la barre de LEDS.
// lumiere/color/<DeviceID> (R,V,B) : Changement de couleur des LEDS.
// lumiere/animation/<DeviceID>[1/2/3/4/5/6] : Animation des LEDS.
// ---------------------------------------------------------------------------------------
#include <Arduino.h>
#define ModeDebug
// Pour une mise en prod, ne pas oublier de mettre writeToEEPROM = false
const String firmwareActualVersion = "1.2.20";
const boolean writeToEEPROM = false; // Si = true, écrit en EEPROM et affiche les traces de debug de wifimanager (default = false)
#ifdef ModeDebug
#define DEBUG(message) \
Serial.print("[DEBUG:"); \
Serial.print(__func__); \
Serial.print("("); \
Serial.print(__LINE__); \
Serial.print(")]-> "); \
Serial.println(message);
#else
#define DEBUG(message);
#endif
#include <ESP8266HTTPClient.h>
#include <WiFiManager.h>
WiFiManager wifiManager;
// Define memory size we want to define (1 bytes / caract) for EPPROM storage
#define EEPROM_LEDS_SIZE 3
#define EEPROM_DEVICEID_SIZE 15
// Customisation du nom du module ESP
#define HOSTNAME "ESP8266-" // Pour la connection Wifi (doit être unique)
char HostName[16];
// Pour répondre au topic MQTT [portal|barreverticale]
char DeviceID[EEPROM_DEVICEID_SIZE] = "portal"; // N'est pris en compte que si writeToEEPROM = true (car sinon on lit la valeur provenant de l'EEPROM)
// EEPROM ( pour stockage du nombre de leds )
// ESP 2C:F4:32:77:3C:3B -> 125 leds (chemin de table)
// ESP 2C:F4:32:77:5F:07 -> 64 leds (barre verticale)
// ESP 2C:F4:32:77:31:8B -> 63 leds
// ESP 30:83:98:82:6A:6F -> 10 leds (portal)
// ESP 192.168.1.71 -> 50 leds (Sapin de noel)
int LED_COUNT = 66; // N'est pris en compte que si writeToEEPROM = true (car sinon on lit la valeur provenant de l'EEPROM)
#include "my_EEPROM.h"
// LEDS
boolean g_BOO_AnimationSeconde = true;
#include "my_leds.h"
// Définition d'une structure pouvant stocker le message provenant de MQTT
#include "my_MQTT.h"
// Over The Air
#include "my_OTA.h"
// ***************************************************************************************
void setup() {
#ifdef ModeDebug
// initialisation de la liaison série (pour le moniteur) .........................
Serial.begin(115200);
delay(5000); // On attend que le port serie soit initialisé
Serial.println();
Serial.flush();
#endif
DEBUG("OK, let's go **********************************************************************");
DEBUG("Version firmware :" + String( firmwareActualVersion ));
// Lecture du nombre de leds dans l'EEPROM ........................................
// si writeToEEPROM = true, on sauve la valeur lue dans la globale LED_COUNT
EEPROM_Start();
// initialisation de la liaison WIFI ..............................................
/* Si la connexion échoue, on lance un Access Point (AP) qui est visible dans les réseaux WIFI
Il faut alors se connecter avec un smarthpone sur l'AP pour configurer le Wifi, le NodeMCU
reboot et se connect avec le SSID et mot de passe saisie.
*/
snprintf(HostName, 16, HOSTNAME"%06X", (uint32_t)ESP.getChipId()); // Concaténation du HOSTNAME avec la fin de l'adresse MAC
wifiManager.setDebugOutput(writeToEEPROM); // false ->Pour ne plus avoir le mot de passe WIFI qui s'affiche.
wifiManager.autoConnect(HostName, "123456789");
DEBUG("IP address: "); Serial.println(WiFi.localIP());
DEBUG("HOSTNAME: "); Serial.println(HostName);
// Connection Wifi pour l'OTA ....................................................
OTA_setup();
// Create a MQTT client ..........................................................
MQTT_setup();
// Initialisation des leds .....................................................
strip.begin(); // INITIALIZE NeoPixel strip object
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(BRIGHTNESS);
LED_Animation(5);
g_BOO_AnimationSeconde = true;
DEBUG("************************** Tout est initialise");
}
// **********************************************************************************************************
// **********************************************************************************************************
unsigned long lastRecu = 0;
int numled = 0;
void loop() {
// On écoute le serveur OTA
// OTA_doUpdate();
// Test si la connection Wifi existe toujours ...................................
if (WiFi.status() != WL_CONNECTED) {
// Si on est déconnecté on tente de se reconnecter automatiquement avec les anciens settings.
wifiManager.autoConnect();
}
// Test si la connection MQTT est toujours valide ..............................
if (!clientMQTT.connected()) {
Serial.println("OUPS, on est plus connecté au server MQTT--------------------------");
//MQTT_connect();
// On reboot
ESP.restart();
}
clientMQTT.loop();
// Animation des LEDS toutes les secondes .........................................
if (millis() - lastRecu > 1000 ) {
lastRecu = millis();
// Allumage d'une led
if ( g_BOO_AnimationSeconde ) {
if ( numled >= LED_COUNT /2 ) {
LED_AllumeLedNum( numled -1, 0,0,0 ); // Noir
LED_AllumeLedNum( LED_COUNT - numled, 0,0,0 ); // Noir
LED_AllumeLedNum( numled -2, 0,0,0 ); // Noir
LED_AllumeLedNum( LED_COUNT - numled +1, 0,0,0 ); // Noir
numled = 0;
}
LED_AllumeLedNum( numled, 255,0,100 ); // Rouge
LED_AllumeLedNum( LED_COUNT - numled -1, 255,0,100 ); // Rouge
LED_AllumeLedNum( numled -1, 50,0,70 ); // Noir
LED_AllumeLedNum( LED_COUNT - numled, 50,0,70 ); // Noir
LED_AllumeLedNum( numled -2, 0,0,0 ); // Noir
LED_AllumeLedNum( LED_COUNT - numled +1, 0,0,0 ); // Noir
numled++;
}
}
// Traitement des Messages MQTT ...................................................
// Tout est fait dans MQTT_callback()
}

61
src/my_EEPROM.h Normal file
View File

@ -0,0 +1,61 @@
/*
Bibliothèque permettant de stocker 2 valeurs en EEPROM
@see : https://projetsdiy.fr/esp8266-comment-lire-ecrire-effacer-eeprom/
*/
#include <EEPROM.h>
/*----------------------------------------------------------------------------
Permet d'écrire une chaine de caractère en EEPROM à une adresse donnée.
*/
void EEPROM_writeString(char add, String data) {
int _size = data.length();
int i;
for (i = 0; i < _size; i++)
{
EEPROM.write(add + i, data[i]);
}
EEPROM.write(add + _size, '\0'); //Add termination null character for String Data
EEPROM.commit();
}
/*----------------------------------------------------------------------------
Permet de lire une chaine de caractère en EEPROM à une adresse donnée.
*/
String EEPROM_read_String(char add){
char data[100]; //Max 100 Bytes
int len=0;
unsigned char k;
k=EEPROM.read(add);
while(k != '\0' && len<500) //Read until null character
{
k=EEPROM.read(add+len);
data[len]=k;
len++;
}
data[len]='\0';
return String(data);
}
/*----------------------------------------------------------------------------
Permet de mettre en place la gestion du nombre de leds en EEPROM.
*/
void EEPROM_Start() {
EEPROM.begin(EEPROM_LEDS_SIZE + EEPROM_DEVICEID_SIZE + 2);
if ( writeToEEPROM == true ) {
Serial.println("* * * * * * * * * * * * * Stockage des valeurs en EEPROM * * * * * * * * * * * * * * ");
// Ecriture des valeurs dans l'EEPROM
EEPROM.put(0, LED_COUNT); // on a un entier, on peut utiliser put
EEPROM_writeString(sizeof(LED_COUNT), DeviceID); // on a une chaine de caractère, on doit utiliser une fonction custom
}
// Relecture de la valeur stockée dans l'EEPROM
EEPROM.get(0, LED_COUNT);
Serial.print("Nb leds (from EEPROM) LED_COUNT: "); Serial.println(LED_COUNT);
String data = EEPROM_read_String(sizeof(LED_COUNT)); // Lecture dans une string
data.toCharArray(DeviceID, EEPROM_DEVICEID_SIZE); // Convertion de String en char
Serial.print("DeviceID (from EEPROM) : "); Serial.println(DeviceID);
}

201
src/my_MQTT.h Normal file
View File

@ -0,0 +1,201 @@
// MQTT client
#include <PubSubClient.h>
#define mqtt_broker "192.168.0.11"
#define topic_temperature "sensor/temperature" //Topic température
#define topic_batterie "sensor/batterie" //Topic batterie
#define MQTT_user ""
#define MQTT_password ""
// DEFINITION DES TOPICS POUR CE MODULE -------------------------------------------
char topic_lumiere[8 + EEPROM_DEVICEID_SIZE];
char topic_lumiere_color[8 + 6 + EEPROM_DEVICEID_SIZE];
char topic_lumiere_bright[8 + 11 + EEPROM_DEVICEID_SIZE];
char topic_lumiere_anim[8 + 10 + EEPROM_DEVICEID_SIZE];
WiFiClient espClient;
PubSubClient clientMQTT(espClient); // Definition du client MQTT
char g_CHAR_messageBuff[100];
// --------------------------------------------------------------------------------
// Envoie un message sur le canal de debug MQTT.
//
void MQTT_publishDebug(String message){
strcpy( g_CHAR_messageBuff, "/hardware/debug/MQTT-leds-color/"); // Initialisation de <g_CHAR_messageBuff> avec lce topic
strcat( g_CHAR_messageBuff, HostName); // Concatenation de l'ID du hostname
// Publicaiton du message
clientMQTT.publish(g_CHAR_messageBuff,message.c_str() );
}
// --------------------------------------------------------------------------------
// Envoi les mesures ("data") passées en paramètre au brocker MQTT.
// L'envoie se fait sous la forme :
// sensor/temperature/<sensorID>/<value>
// avec "sensor/temperature qui est dans le parametre p_CHAR_topic
//
// @param moduleID : L'identifiant du thermometre défini dans FIBARO
// @param data : la valeur de la mesure.
// @p_CHAR_topic : un char pointant sur la chaine contenant le nom du topic dans lequel on veut publier
//
void MQTT_publishDataToMQTT(String moduleID, String value, char *p_CHAR_topic) {
// Creation du topic (on rajoute un / suivi de l'ID du sensor)
String topic = "/" + moduleID;
char buff[20];
topic.toCharArray(buff, 20); // On met le topic dans la variable char buff
// Construction du char contenant le topic pour ce module
strcpy( g_CHAR_messageBuff, p_CHAR_topic); // Initialisation de <g_CHAR_messageBuff> avec ler topic qui est passé en paramètre
strcat( g_CHAR_messageBuff, buff); // Concatenation de temperature_topic + buff
// Publication de la temperature dans le topic
DEBUG("Publication d'un message sur le topic :");
DEBUG(g_CHAR_messageBuff);
clientMQTT.publish(g_CHAR_messageBuff, String(value).c_str() );
}
// --------------------------------------------------------------------------------
// Reconnexion au serveur MQTT
//
void MQTT_connect() {
//Boucle jusqu'à obtenir une reconnexion
while (!clientMQTT.connected()) {
Serial.print("Connexion au serveur MQTT...");
MQTT_publishDebug(" Connexion au serveur MQTT...");
// ON arrive à se conecter au brocker MQTT
if (clientMQTT.connect(HostName, MQTT_user, MQTT_password)) {
DEBUG("OK");
MQTT_publishDebug(" OK");
// Connection au brocker MQTT ratée
} else {
Serial.print("KO, erreur : ");
Serial.println(clientMQTT.state());
DEBUG(" On attend 5 secondes avant de recommencer");
MQTT_publishDebug( "... Connection impossible. On attend 5 secondes avant de recommencer\nErreur connection = " + String(clientMQTT.state()) );
delay(5000);
}
}
// Souscription aux topics
clientMQTT.subscribe("lumiere/#");
MQTT_publishDebug("Abonnement au topic MQTT lumiere/#");
}
// --------------------------------------------------------------------------------
// Déclenche les actions à la réception d'un message MQTT.
// lumiere/portal [ON|OFF] : Allumage de la barre de LEDS.
// lumiere/portal/color [#RRVVBB] : Changement de couleur des LEDS.
// lumiere/portal/animation [1/2/3/4/5] : Animation des LEDS.
//
void MQTT_callback(char* topic, byte* payload, unsigned int length) {
// create character buffer with ending null terminator (string)
char message[100];
unsigned int i;
for ( i = 0; i < length; i++) {
message[i] = payload[i];
}
message[i] = '\0';
// Traitement des topics
// ................................................................................
if ( strcmp( topic, topic_lumiere ) ==0 ) {
DEBUG("Detection du topics :" + String( topic_lumiere ));
if ( String( message ) == "ON") {
DEBUG("Allumage les leds");
MQTT_publishDebug("MQTT_callback> Allumage les leds ");
LED_colorWipe(strip.Color(0, 0, 255), 20);
} else if ( String( message ) == "OFF") {
DEBUG("Extinction des leds");
MQTT_publishDebug("MQTT_callback> Extinction les leds ");
LED_colorWipe(strip.Color(0, 0, 0), 20);
}
g_BOO_AnimationSeconde = false;
// ................................................................................
} else if ( strcmp( topic, topic_lumiere_color) == 0) {
DEBUG("Detection du topics :" + String( topic_lumiere_color ));
// Test si on a une couleur RGB dans le message
if ( LED_isAColor( message ) ) {
// Définition de la couleur
Couleur c;
c = LED_ExtractRVB( message );
DEBUG("Affichage de la couleur : " + String(c.R) + " " + String(c.V) + " " + String(c.B));
MQTT_publishDebug("MQTT_callback> Affichage de la couleur : " + String(c.R) + " " + String(c.V) + " " + String(c.B));
// Changemnt des LEDS avec la couleur
LED_colorWipe(strip.Color(c.R, c.V, c.B), 20);
}
// ................................................................................
} else if ( strcmp( topic, topic_lumiere_bright) == 0 ) {
DEBUG("Detection du topics :" + String( topic_lumiere_bright ));
// Test si on a bien une valeur numérique
if ( LED_isADigit( message ) ) {
DEBUG("Luminosite : " + String( message ));
MQTT_publishDebug("MQTT_callback> Luminosite : " + String( message ));
strip.setBrightness( String( message ).toInt() % 255 );
strip.show();
}
// ................................................................................
} else if ( strcmp( topic, topic_lumiere_anim) ==0 ) {
DEBUG("Detection du topics :" + String( topic_lumiere_anim ));
DEBUG("Lancement de l'Animation avec le parametre :" + String( message ));
MQTT_publishDebug("MQTT_callback> Lancement de l'Animation avec le parametre :" + String( message ));
LED_Animation(String( message ).toInt());
}
}
// --------------------------------------------------------------------------------
// Initialisation du brocker MQTT.
//
void MQTT_setup(){
// Création du client MQTT
clientMQTT.setServer(mqtt_broker, 1883); // Configuration de la connexion au serveur MQTT
clientMQTT.setCallback(MQTT_callback); // La fonction de callback qui est executée à chaque réception de message
// Connection au Brocker MQTT
MQTT_connect();
// Construction des topcs auxquels s'abonner.
sprintf( topic_lumiere, "lumiere/%s", DeviceID);
sprintf( topic_lumiere_color, "lumiere/color/%s", DeviceID);
sprintf( topic_lumiere_bright, "lumiere/brightness/%s", DeviceID);
sprintf( topic_lumiere_anim, "lumiere/animation/%s", DeviceID);
}

273
src/my_OTA.h Normal file
View File

@ -0,0 +1,273 @@
/**
Bibliothèque pour avoir une mise à jour Over The Air d'un code source.
Utilisation :
Dans le setup, rajouter OTA_setup();
Dans la loop, rajouter OTA_doUpdate();
Fonctionnement :
Toutes les OTA_TimerInSecond secondes, le programme va vérifier qu'il y a une mise à jour sur le serveur.
Si une mise à jour existe, elle est téléchargée et installé et l'ESP reboot.
S'il n'y a pas de mise à jour, le serveur le dit et on ne fait rien.
Avant toutes upload de ce script dans un Arduino, il faut executer un script Python de récupération de certificats qui
se trouve sur le repot https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py
*/
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
char outputBuffer[100]; // Pour les messages à afficher
char macAdresse[12];
// Fait un serveur de mise à jour local dans l'ESP
#include <ESP8266HTTPUpdateServer.h>
#include <ESP8266WebServer.h>
ESP8266WebServer OTA_HttpServer(80);
ESP8266HTTPUpdateServer OTA_httpUpdater;
const char* OTA_update_username = "ESPadmin";
const char* OTA_update_password = "admin";
const char* OTA_update_path = "/firmware";
boolean g_BOO_UpdateFirmware = true;
const String firmwareUrlMiseAJour = "https://update.alex-design.fr/MQTT-leds-color/update.php";
const String fileSystemUrlMiseAJour = "https://update.alex-design.fr/MQTT-leds-color/updateFS.php";
//const String firmwareUrlMiseAJour = "http://192.168.0.32:9090/MQTT-leds-color/update.php"; <- Ne peut pas fonctionner car il est en http, et on veut du https
// Define global variable to know if upate is available
long OTA_UpdateTimer;
const int OTA_TimerInSecond = 60 * 10; // every 10 minute
// Utilisation d'un certificat ------------------------------------------
// Pour mettre à jour l'heure (obligatoire avec un certificat https)
#include <time.h>
// A single, global CertStore which can be used by all
// connections. Needs to stay live the entire time any of
// the WiFiClientBearSSLs are present.
#include <CertStoreBearSSL.h>
BearSSL::CertStore certStore;
#include <FS.h>
#include <LittleFS.h>
char versionLitteFS[10] = "0.0.0";
// Define a wifi client
ESP8266WiFiMulti WiFiMulti;
/**
----------------------------------------------------------------------------------------------------------------
Callback lorsque la maj OTA démarre
*/
void _update_started() {
DEBUG("CALLBACK: HTTPS update process started");
}
/**
----------------------------------------------------------------------------------------------------------------
Callback lorsque la maj OTA est terminée
*/
void _update_finished() {
DEBUG("CALLBACK: HTTPS update process finished. Reboot");
}
/**
----------------------------------------------------------------------------------------------------------------
Callback lorsque la maj OTA est en cours
*/
void _update_progress(int cur, int total) {
sprintf(outputBuffer, "CALLBACK: HTTPS update process at %d of %d bytes...", cur, total);
DEBUG( outputBuffer );
}
/**
----------------------------------------------------------------------------------------------------------------
Callback lorsque la maj OTA a plantée
*/
void _update_error(int err) {
sprintf(outputBuffer, "CALLBACK: HTTPS update fatal error code %d\n", err);
DEBUG( outputBuffer );
if (err == -103) {
DEBUG(" Please allow me, I am ");
DEBUG(WiFi.macAddress());
DEBUG(WiFi.localIP());
}
if ( err == 0 ) {
DEBUG("La mise à jour du firmware via OTA n'a pas marché, on stop !!!!!!!!!!!!");
g_BOO_UpdateFirmware = false;
}
}
/**
----------------------------------------------------------------------------------------------------------------
Set time via NTP, as required for x.509 validation
----------------------------------------------------------------------------------------------------------------
*/
void OTA_setClock() {
configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC
DEBUG("OTA Waiting for NTP time sync: ");
time_t now = time(nullptr);
while (now < 8 * 3600 * 2) {
yield();
delay(500);
Serial.print(F("."));
now = time(nullptr);
}
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
DEBUG("Current time: ");
DEBUG(asctime(&timeinfo));
}
/**
----------------------------------------------------------------------------------------------------------------
Initialisation d'une connexion wifi à l'aide des settings de wifimanager en eeprom.
----------------------------------------------------------------------------------------------------------------
*/
void OTA_setup() {
/* Mise en place d'un serveur pour uploader directement un binaire */
OTA_httpUpdater.setup(&OTA_HttpServer, OTA_update_path, OTA_update_username, OTA_update_password);
OTA_HttpServer.begin();
sprintf(outputBuffer, "OTA HTTPUpdateServer ready! Open http://%d.%d.%d.%d%s in your browser and login with username '%s' and password '%s'\n",
WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3], OTA_update_path, OTA_update_username, OTA_update_password);
DEBUG(outputBuffer);
MQTT_publishDebug(String(outputBuffer));
// Test la version du file system littleFS en place
if ( !LittleFS.begin()) {
DEBUG("Il n'y a pas de file system little FS installé, lecture impossible !");
} else {
// Get version of the certificat in LittleFS (frome file version.txt
File file = LittleFS.open("/version.txt", "r");
if ( file ) {
while (file.available()) {
strcpy(versionLitteFS, file.readString().c_str() );
}
file.close();
}
// Récupération des CA stockés dans LittleFS pour les certificats SSL
int numCerts = certStore.initCertStore(LittleFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
DEBUG("Number of CA certs read: ");
DEBUG(numCerts);
if (numCerts == 0) {
DEBUG(F("No certs found. Did you run certs-from-mozill.py and upload the LittleFS directory before running?"));
}
}
DEBUG("Numéro de version de littleFS : " + String(versionLitteFS) );
// Création de la connection Wifi à partir du SSID et PWD sauvé par wifimanager
/*
WiFi.mode(WIFI_STA);
WiFiMulti.addAP(wifiManager.getWiFiSSID(true).c_str(), wifiManager.getWiFiPass(true).c_str());
*/
// We will request a firmware update in OTA_TimerInSecond secondes
OTA_UpdateTimer = (OTA_TimerInSecond * 1000);
}
/**
----------------------------------------------------------------------------------------------------------------
Va voir sur l'URL si une mise à jour du firmware est disponible. Si c'est le cas,
la télécharge et met à jour le firmeware.
----------------------------------------------------------------------------------------------------------------
*/
boolean OTA_doUpdate() {
// Si on a pas besoin de faire de mise à jour (ou ça c'est planté la dernière fois)
if ( !g_BOO_UpdateFirmware ) {
return false;
}
// Fait tourner le serveur http sur l'ESP
// Lorsqu'on se connect à ce serveur, il est possible d'uploader un firmware ou un filesystem
OTA_HttpServer.handleClient();
// Check is this is the time to check a new update
delay( 1 ); // Wait 1 milliseconde
if (OTA_UpdateTimer > 0 ) {
OTA_UpdateTimer--;
return false;
}
if (WiFiMulti.run() == WL_CONNECTED) {
// WiFiClient client; // Client simple (incompatioble en https )
// Mise à jour de l'heure via un serveur NTP
OTA_setClock();
// Récupération du certificat SSL pour la connexion https
BearSSL::WiFiClientSecure client; // Client securise
bool mfln = client.probeMaxFragmentLength(fileSystemUrlMiseAJour, 443, 1024); // server must be the same as in ESPhttpUpdate.update()
if (mfln) {
client.setBufferSizes(1024, 1024);
}
client.setCertStore(&certStore);
// Add optional callback notifiers
ESPhttpUpdate.onStart(_update_started);
ESPhttpUpdate.onEnd(_update_finished);
ESPhttpUpdate.onProgress(_update_progress);
ESPhttpUpdate.onError(_update_error);
ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW);
// Get the mac adresse in a char
snprintf(macAdresse, 12, "%06X", (uint32_t)ESP.getChipId() );
// Try to update the filesystem
DEBUG(fileSystemUrlMiseAJour + "?chipID=" + String(macAdresse) );
t_httpUpdate_return ret = ESPhttpUpdate.updateFS(client, fileSystemUrlMiseAJour + "?chipID=" + String(macAdresse), versionLitteFS);
if (ret == HTTP_UPDATE_OK) {
DEBUG("Update FileSystem Successfully");
}
// Try to update the firmware
DEBUG(firmwareUrlMiseAJour + "?chipID=" + String(macAdresse) );
DEBUG("Version firmware :" + firmwareActualVersion );
ret = ESPhttpUpdate.update(client, firmwareUrlMiseAJour + "?chipID=" + String(macAdresse), firmwareActualVersion);
switch (ret) {
case HTTP_UPDATE_FAILED:
sprintf(outputBuffer, "HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
DEBUG( outputBuffer );
OTA_UpdateTimer = (OTA_TimerInSecond * 1000);
break;
case HTTP_UPDATE_NO_UPDATES:
DEBUG("No new update available");
OTA_UpdateTimer = (OTA_TimerInSecond * 1000);
break;
default:
break;
}
return true;
}
return false;
}

244
src/my_leds.h Normal file
View File

@ -0,0 +1,244 @@
/*
Bibliothèque pour l'annimation de leds sur une bande de leds.
*/
#include <Adafruit_NeoPixel.h>
#define PIN_LED D1
// NeoPixel brightness, 0 (min) to 255 (max)
#define BRIGHTNESS 255 // Set BRIGHTNESS to about 1/5 (max = 255)
// Le nombre de pixels déclaré ici n'est pas important @FIXME
// Le nombre de pixels utilisé est celui qui est stocké en EEPROM, à savoir LED_COUNT
Adafruit_NeoPixel strip = Adafruit_NeoPixel(500, PIN_LED, NEO_GRB + NEO_KHZ800);
struct Couleur {
int R = 0;
int V = 0;
int B = 0;
};
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void LED_colorWipe(uint32_t color, int wait) {
for (int i = 0; i < LED_COUNT; i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
// --------------------------------------------------------------------------------
// Allume la led dont le numéro est passé en paramètre
// et éteind la led précédente.
void LED_AllumeLedNum( int led, int R, int V, int B) {
strip.setPixelColor(led, strip.Color(R, V, B)); // Blanc
strip.show();
}
// --------------------------------------------------------------------------------
// Vérifie que la chaine est est bien un integer entre 0 et 255
// Si c'est le cas on renvoie True, False sinon
//
boolean LED_isADigit(char* s) {
char chaine[] = "rrr";
int i = 0;
for (i = 0; s[i]; i++) {
chaine[i] = s[i]; // On construit la copie de la chaine passée en parametre
}
chaine[i] = s[i]; // Pour ne pas oublier le \0 de la fin
char* couleur = NULL;
couleur = strtok(chaine, ","); // On travail sur la copie
while (couleur != NULL) {
// Convertion de la chaine en integer
// Si l'integer n'est pas compris en 0 et 255 ...
if (atoi( couleur ) < 0 or atoi( couleur ) > 255 ) {
// ... on a pas une couleur, on sort du test
return false;
}
couleur = strtok(NULL, ",");
}
return true;
}
// --------------------------------------------------------------------------------
// Vérifie que la chaine est est bien une couleur du style R,V,B
// Si c'est le cas on renvoie True, False sinon
//
boolean LED_isAColor(char* s) {
char chaine[] = "rrr,bbb,vvv";
// On compte les virgules dans la chaine
int i, count = 0;
for (i = 0; s[i]; i++) {
if (s[i] == ',') {
count++;
}
chaine[i] = s[i]; // On construit la copie de la chaine passée en parametre
}
chaine[i] = s[i]; // Pour na pas oublier le \0 de la fin
// on a bien 2 virgules
if (count == 2 ) {
char* couleur = NULL;
couleur = strtok(chaine, ","); // On travail sur la copie
while (couleur != NULL) {
// Convertion de la chaine en integer
// Si l'integer n'est pas compris en 0 et 255 ...
if (atoi( couleur ) < 0 or atoi( couleur ) > 255 ) {
// ... on a pas une couleur, on sort du test
return false;
}
couleur = strtok(NULL, ",");
}
} else {
return false;
}
return true;
}
// --------------------------------------------------------------------------------
// Convertie la chaine RVB en une couleur.
// @return Color
Couleur LED_ExtractRVB(char* s) {
// Définition d'une couleur
Couleur c;
char* couleur = strtok(s, ",");
int count = 0;
while (couleur != NULL) {
if (count == 0) {
c.R = atoi(couleur);
} else if (count == 1) {
c.V = atoi(couleur);
} else if (count == 2) {
c.B = atoi(couleur);
}
count ++;
couleur = strtok(NULL, ",");
}
return c;
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
void rainbow(uint8_t wait) {
uint16_t i, j;
for (j = 0; j < 256; j++) {
for (i = 0; i < LED_COUNT; i++) {
strip.setPixelColor(i, Wheel((i + j) & 255));
}
strip.show();
delay(wait);
}
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for (j = 0; j < 256 * 1; j++) { // 5 cycles of all colors on wheel
for (i = 0; i < LED_COUNT; i++) {
strip.setPixelColor(i, Wheel(((i * 256 / LED_COUNT) + j) & 255));
}
strip.show();
delay(wait);
}
}
//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
for (int j = 0; j < 10; j++) { //do 10 cycles of chasing
for (int q = 0; q < 3; q++) {
for (uint16_t i = 0; i < LED_COUNT; i = i + 3) {
strip.setPixelColor(i + q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (uint16_t i = 0; i < LED_COUNT; i = i + 3) {
strip.setPixelColor(i + q, 0); //turn every third pixel off
}
}
}
}
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
DEBUG("LED_COUNT:"+String(LED_COUNT));
for (int j = 0; j < 256; j++) { // cycle all 256 colors in the wheel
for (int q = 0; q < 3; q++) {
for (uint16_t i = 0; i < LED_COUNT; i = i + 3) {
strip.setPixelColor(i + q, Wheel( (i + j) % 255)); //turn every third pixel on
}
strip.show();
delay(wait);
for (uint16_t i = 0; i < LED_COUNT; i = i + 3) {
strip.setPixelColor(i + q, 0); //turn every third pixel off
}
}
}
}
// --------------------------------------------------------------------------------
// Fait une animation sur les leds en fonction du numéro passé en paramètre.
// [1..9]
//
void LED_Animation(int num) {
g_BOO_AnimationSeconde = false;
DEBUG(num);
switch ( num ) {
case 0:
LED_colorWipe(strip.Color(255, 255, 255), 20); // Blanc
break;
case 1:
LED_colorWipe(strip.Color(0, 0, 255), 20); // Bleu
break;
case 2:
theaterChase(strip.Color(0, 0, 255), 50);
break;
case 3:
theaterChaseRainbow(50);
break;
case 4:
rainbow(50);
break;
case 5:
rainbowCycle(10);
break;
case 6:
g_BOO_AnimationSeconde = true;
break;
default:
DEBUG("Animation inconnue ->" + String(num) );
break;
}
}

11
test/README Normal file
View File

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html