Code

Afin de ne pas encombrer mes articles par de longues pages de code, vous trouverez dans cette page le code des tous les programmes que j'ai écrit pour mes articles.



Liste des programmes




test_fonctionnement_serial

Ce programme ne fait rien mais c'est celui que je téléverse en premier dans un ESP lorsque je commence un projet pour tester que mon environnement de développement fonctionne bien. Si j'arrive à téléverser ce programme et lire les traces sur la console série alors c'est que je suis prêt.


/*******************************************************************
* Ce programme ne fait qu'afficher un compteur sur la liaison série.
* Il sert à tester que l'environnement de programmation est prêt: 
*   - compilation et téléversement d'un sketch.
*   - communication avec la liaison série pour débugger.
* Il donne également un cadre clair pour structurer un programme. 
* Evolutions possible :  
*   - Aucune 
* Versions : 
*   v1 : Création
* (c) acampeaux@gmail.com
********************************************************************/

/*====================================================================
  * Inclusion des librairies externes 
 * ==================================================================*/

// Ici aucun #include nécessaire.
/*====================================================================
  * Définition de constantes (#define) et des variables globales
 * ==================================================================*/
String stNomProgramme = "test_fonctionnement_serial";
String stVersion = "v1.0";

//////////////////////////////constantes relatives à la connexion série
// Fixe la vitesse de communication série. 
// Sur ESP8266 je mets 74880 car c'est la vitesse de communication du controleur au boot.
#define SERIAL_BIT_RATE 74880


//////////////////////////////définitions relatives à la boucle loop

//Temps d'attente à la fin de la boucle loop avant de recommencer un cycle
//En millisecondes
#define DELAY_INTER_CYCLES 2000
// Compte le nombre d'itérations
int iNbIteration = 0;

/*====================================================================
  * Fonction setup(): executée une fois au démarrage du microcontroleur.
  * - Ouvre la liaison série.
 * ==================================================================*/
void setup() {

  ////////////////////////////////////////////Variable locales au setup
  String stMessageAccueil = "";

  ///////////////////////////////////Initialisation de la liaison série
  Serial.begin(SERIAL_BIT_RATE);
  delay( 100 );
  stMessageAccueil = stNomProgramme + " - Version: " + stVersion;
  Serial.println( stMessageAccueil );
  Serial.println( "Serial: OK" );
}


/*====================================================================
  * Fonction loop(): executée en boucle.
  * - Incrémente un compteur.
  * - Affiche le nombre d'itération sur la liaison série.
  * - Attends un délai donné.
 * ==================================================================*/
void loop() {

  ///////////////////////////////////Variable locales a la fonction loop
  String stMessageIteration = "Iteration loop number: ";

  ////////////////////////////////////////////Incrément et affichage
  iNbIteration++; 
  stMessageIteration = stMessageIteration + String( iNbIteration );
  Serial.println( stMessageIteration );

  ////////////////////////////////////////////Attente avant nouveau cycle
  delay( DELAY_INTER_CYCLES );
}








Sonde Wifi


Ce programme est celui de la sonde de la cuve de récupération d'eau dont on parle dans l'article sur l'ESP8266 ICI.
Il sert à mesurer le niveau d'eau d'une cuve de récupération d'eau de pluie et communique les données avec un serveur domoticz pour en réaliser le suivi.


/******************************************************
* Ce programme est destiné à ESP8266 - NodeMCU ESP12
* Permet de mesurer le niveau d'ean dans une cuve par deduction par rapport à la profondeur totale
*   et la distance entre la sonde et la surface de l'eau :
*   Hauteur_eau = Hauteur_totale - Hauteur_surface.
* Renvois la profondeur calculee au serveur domotique avec une requete JSON.
*
* Évolutions :
* Modifier les paramètres de temps : nombre de mesure par cycle et temps entre deux cycles.

* Dans l'affichage de l'aide remplace l'adresse IP generic en exemple par la vraie adresse IP
* Utiliser la librairie WiFiManager pour pouvoir se connecter sans SSID (à vérifier si possible?)
* Implémenter une fonction "info" dans l'url d'appel qui affiche les informations de l'eeprom
* Prévoir une fonction qui permet de forcer l'envoi d'une valeur à domoticz (pour remettre à zero par exemple).
*
* Évolutions possibles :
*
* Versions :
* v3 : ajout du mode WiFi.mode(WIFI_STA);
*******************************************************/
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>

//#include <DNSServer.h>
//#include <WiFiManager.h>

///////////////////////////////////////////////////constantes relatives à la sonde HC-SR04
#define TRIG D5
#define ECHO D6

///////////////////////////////////////////////////constantes relatives à la gestion mémoire
// pour controler qu'une valeur t[1] est bonne => t[0] et t[2] doivent correspondre aux valeurs ci-dessous
#define MEMORY_CONTROL_START 127
#define MEMORY_CONTROL_END 255

//La structure represente les valeurs memorisee en EEPROM (mémoire
struct structEepromDomoticz {
  int iMemoryControlStart;
  int iIdxDomoticzCuve;
  int iSecondesEntreDeuxCycles;
  int iNbMesuresParCycle;
  int iProfondeurCuve;
  int iVitesseSon;
  int iMemoryControlEnd;
};
//Variables qui doivent être memorisées pour domoticz
structEepromDomoticz eepromDomoticz; //Variable pour récupérer les données stockées dans epprom.
int index_eepromDomoticz;
int iTaille_eeprom;

////////////////////////////////////////constantes et variable relatives à la gestion des mesures de la cuve
//defini la hauteur d'eau maximum dans la cuve (en cm)
#define PROFONDEUR_CUVE 164
#define VITESSE_SON 340

// Nombre de mesures que l'on prendra par cycle. Chaque cycle correspond à la mise à jour domoticz.
// Le nombre de mesure ne doit pas dépasser le nombre MAX (dimensionnement du tableau des mesures)
// NB_SECONDES_ENTRE_CYCLE indique le temps d'attente entre deux cycles de mesures.
// La duree d'un cycle sera donc : NB_MESURES_PAR_CYCLE * NB_SECONDES_ENTRE_MESURES (exple 5 * 1 = 5 )

#define NB_MESURES_PAR_CYCLE 10
#define NB_MESURES_MAX_PAR_CYCLE 100
#define NB_SECONDES_ENTRE_CYCLE 20
#define NB_SECONDES_ENTRE_MESURES 1

int tiTableauDesMesures[ NB_MESURES_MAX_PAR_CYCLE ];
int iSecondesEntreDeuxMesures = NB_SECONDES_ENTRE_MESURES;

////////////////////////////////////////////////////// Constantes et variable relatives au wifi
#define MAX_TIME_WAIT_WIFI 60

IPAddress oipIpEsp(192, 168, 1, 60);
IPAddress oipGateway(192, 168, 1, 1);
IPAddress oipSubnet(255, 255, 255, 0);
IPAddress oipDns(192, 168, 1, 1);
const char* sSsid = "framboise";
const char* sPassword = "aupotager";
const char* sHostname ="esp1";

// Variables relatives au serveur sur cet ESP
#define ESP_SERVER_PORT 80
ESP8266WebServer oEspServer( ESP_SERVER_PORT );

////////////////////////////////////////////////////// Variables relatives à domoticz
#define INDEX_CUVE_DEFAULT  1026
char *sDomoticzIp = "192.168.1.150";
int iDomoticzPort = 8080;
HTTPClient oDomoticzClient;
String osUrl = "";



/************************************************************************************************************
 * ***********************************************************************************************************
 * SETUP
 * Fonction setup
 * ***********************************************************************************************************
 ************************************************************************************************************/
void setup() {
  byte tbMac[ 6 ] = {0,0,0,0,0,0};


  /////////////////////////////////////////////////////////Initialisation port série
  Serial.begin(74880);
  delay( 100 );
  Serial.println( "\nInitialisation liaison serie : OK" );

  ////////////////////////////////////////////////////////Récupère les valeur de la mémoire EEPROM
  iTaille_eeprom = sizeof( structEepromDomoticz ); // Ajouter la taille des autres structures
  EEPROM.begin( iTaille_eeprom );

  //Recupere les données relatives à domoticz
  index_eepromDomoticz = 0; //Index variable eepromDomoticz dans l'eeprom

  EEPROM.get( index_eepromDomoticz, eepromDomoticz );

  if ( ( eepromDomoticz.iMemoryControlStart != MEMORY_CONTROL_START ) or
       ( eepromDomoticz.iMemoryControlEnd != MEMORY_CONTROL_END ) ){
        //On a pas la bonne valeur de controle. Donc on prend les valeurs par défaut
        eepromDomoticz.iIdxDomoticzCuve = INDEX_CUVE_DEFAULT;
        eepromDomoticz.iSecondesEntreDeuxCycles = NB_SECONDES_ENTRE_CYCLE;
        eepromDomoticz.iNbMesuresParCycle = NB_MESURES_PAR_CYCLE;
        eepromDomoticz.iProfondeurCuve = PROFONDEUR_CUVE;
        eepromDomoticz.iVitesseSon = VITESSE_SON;
        Serial.println( "Valeur par defaut pour eeprom : " );
       } else {
         Serial.println( "Read from EEPROM : " );
       }
     
  Serial.print( "eepromDomoticz.iMemoryControlStart = " ); Serial.println( eepromDomoticz.iMemoryControlStart );
  Serial.print( "eepromDomoticz.iIdxDomoticzCuve = " ); Serial.println( eepromDomoticz.iIdxDomoticzCuve );
  Serial.print( "eepromDomoticz.iSecondesEntreDeuxCycles = " ); Serial.println( eepromDomoticz.iSecondesEntreDeuxCycles );
  Serial.print( "eepromDomoticz.iNbMesuresParCycle = " ); Serial.println( eepromDomoticz.iNbMesuresParCycle );
  Serial.print( "eepromDomoticz.iProfondeurCuve = " ); Serial.println( eepromDomoticz.iProfondeurCuve );
  Serial.print( "eepromDomoticz.iVitesseSon = " ); Serial.println( eepromDomoticz.iVitesseSon );
  Serial.print( "eepromDomoticz.iMemoryControlEnd = " ); Serial.println( eepromDomoticz.iMemoryControlEnd );
 
  ////////////////////////////////////////////////////////// Initialisation Wifi
  //WiFi.hostname( sHostname ); Ne fonctionne pas.
  WiFi.config(oipIpEsp, oipGateway, oipSubnet, oipDns );
  delay(100);

  Serial.print( "Local IP avant connect = " );
  Serial.println( WiFi.localIP().toString() );

  WiFi.disconnect(1);
  WiFi.mode(WIFI_STA);

  Serial.print( "Local IP apres disconnect = " );
  Serial.println( WiFi.localIP().toString() );

  WiFi.begin( sSsid, sPassword );

  delay( 1000 );

  int iCompteur = 0;
  Serial.printf("Connection status: %d\n", WiFi.status());
  Serial.print( "Connecting WiFi ." );
  while (WiFi.status() != WL_CONNECTED ){
     delay( 1000 );
     Serial.print( "." );
     if ( iCompteur++ > MAX_TIME_WAIT_WIFI ) vFonctionReboot();
     //WiFi.begin( sSsid, sPassword );
     //ajouter ici le compte du nombre d'essai de connection et mettre en sommeil si temps trop long
  }

  String stRes = "\nConnected to : " + String( sSsid );
  stRes += "\nAdresse IP : ";
  stRes += WiFi.localIP().toString() ; //Type IPAddress = tableau de 4 entiers.

  WiFi.macAddress( tbMac ); //récupère l'adresse MAC
  stRes += "\nAdresse MAC : ";
  for (int i = 0; i<=5; i++ ) {
    if ( i == 0 ) stRes += String( tbMac[ i ], HEX );
    else stRes += "." + String( tbMac[ i ], HEX );
  }

  Serial.println( stRes );


  ////////////////////////////////////////////////////////////// Initialisation des PIN
  pinMode( D1, OUTPUT );
  digitalWrite( D1, LOW) ; //LED qui indique un cycle de mesure.

  pinMode( TRIG, OUTPUT); //pin TRIG de la sonde HC-SR04
  digitalWrite( TRIG, LOW );

  pinMode( ECHO , INPUT ); //pin  ECHO de la sonde HC-SR04

  /////////////////////////////////////////////////////////////// Initialisation des variables
  iSecondesEntreDeuxMesures = NB_SECONDES_ENTRE_MESURES;


  // Routage des requêtes HTTP
  oEspServer.on("/", vFonctionRoot);
  oEspServer.on("/action",vFonctionAction);
  oEspServer.on("/set",vFonctionSet);
 
  oEspServer.begin();
  Serial.println ( "HTTP server started" );
 
}

 /**********************************************************************************************************************************************
  * **********************************************************************************************************************************************
  * Fonction LOOP :
  * Algorythme :
   *  Faire le nombre de lecture de sonde demandé et pour chaque lecture
   *    Mémoriser dans un tableau la valeur
   *    Attendre le temps demandé entre deux lectures.
   *  Supprimer la valeur la plus haute et la plus petite.
   *  Faire la moyenne
   *  Mettre à jour le capteur domoticz
   *  Attendre le temps demandé entre deux mises à jour.
   *  **********************************************************************************************************************************************
   ***********************************************************************************************************************************************/
void loop() {
  int iNiveauCuve = 0;
  int iNombreDeLectureRealise = 0;
  int iValeurMin = 0;
  int iIndexValeurMin = 0;
  int iValeurMax = 0;
  int iIndexValeurMax = 0;

  //Ecoute les requetes faites par des clients.
  //Appel les fonctions associées à l'url reçu.
  oEspServer.handleClient();
   
  //Allume la LED indiquant qu'un cycle de mesure commence
  Serial.println( "\nOn debute un nouveau cycle de mesures ..................." );
  digitalWrite( D1, HIGH );


  //Controle que le nombre de mesures à prendre ne depasse pas ce qui est possible
  if ( eepromDomoticz.iNbMesuresParCycle >= NB_MESURES_MAX_PAR_CYCLE ){
      Serial.println( "Nombre de mesure demandee trop grand. On retablit au nombre MAX" );
      eepromDomoticz.iNbMesuresParCycle = NB_MESURES_MAX_PAR_CYCLE;
  }

  //On va prendre les mesures et les memoriser dans le tableau.
  Serial.print( "Nombre de mesure a prendre = " );
  Serial.println( eepromDomoticz.iNbMesuresParCycle );

  for ( int i = 1; i <= eepromDomoticz.iNbMesuresParCycle; i++ ){ // on va faire iNbMesuresParCycle mesures.
    //Lecture du niveau de la cuve
    iNiveauCuve = iLectureNiveauCuve();
    Serial.print( "Mesure numero " );
    Serial.print( i );
    Serial.print( " = " );
    Serial.println( iNiveauCuve );

    //Memorise le niveau dans le tableau.
    if ( iNiveauCuve < 0 ) iNiveauCuve = 0;
    tiTableauDesMesures[ i ] = iNiveauCuve;

    //Recherche le min et le max
    if ( i == 1 ){ //On a qu'une seule mesure
      iValeurMin = iNiveauCuve;
      iIndexValeurMin = i;
      iValeurMax = iNiveauCuve;
      iIndexValeurMax = i;
   
      Serial.print( "iValeurMin = " );
      Serial.print( iValeurMin );
      Serial.print( " iIndexValeurMin = " );
      Serial.println( iIndexValeurMin );
      Serial.print( "iValeurMax = " );
      Serial.print( iValeurMax );
      Serial.print( " iIndexValeurMax = " );
      Serial.println( iIndexValeurMax );
    }
    else {
      // On regarde si on est plus grand que la valeur max
      if ( iNiveauCuve > iValeurMax ){
        // Oui : on a un nouveau plus grand
        iValeurMax = iNiveauCuve;
        iIndexValeurMax = i;
      } else { // On regarde si on est plus petit que la valeur min
        if ( iNiveauCuve < iValeurMin ){
          // Oui : on a trouvé un nouveau plus petit
          iValeurMin = iNiveauCuve;
          iIndexValeurMin = i;       
        }
      }
    Serial.print( "iValeurMin = " );
    Serial.print( iValeurMin );
    Serial.print( " iIndexValeurMin = " );
    Serial.println( iIndexValeurMin );
    Serial.print( "iValeurMax = " );
    Serial.print( iValeurMax );
    Serial.print( " iIndexValeurMax = " );
    Serial.println( iIndexValeurMax );
    }
 
   
    // Attend le temps demandé entre deux mesures
    delay( iSecondesEntreDeuxMesures * 1000 );
  }

  //Calcul la moyenne sans prendre en compte les valeur Min et Max
  int iNbMesuresValides = eepromDomoticz.iNbMesuresParCycle - 2;
  //Si iIndexValeurMax = iIndexValeurMin alors la somme se fera sur iNbMesuresParCycle - 1 seulement
  //Donc je rajoute 1 pour corriger
  if ( iIndexValeurMax == iIndexValeurMin ) iNbMesuresValides++;

  int iSommeDesMesuresValides = 0;
  int iValeurMoyenneDuCycle = 0;

  if ( iNbMesuresValides >= 1 ){
    for ( int i = 1; i<= eepromDomoticz.iNbMesuresParCycle; i++ ){
       // Si on a pas la valeur Min, ni la valeur Max
      int iValAAjouter = tiTableauDesMesures[ i ];
      if ( ( i != iIndexValeurMin ) and ( i != iIndexValeurMax ) ){
         Serial.print( "On ajoute la valeur suivante : " ); Serial.println( iValAAjouter );
         iSommeDesMesuresValides += iValAAjouter;
         Serial.print( "La somme est de : " ); Serial.println( iSommeDesMesuresValides );
      } else {
        Serial.print( "On ignore la valeur min ou max suivante : " ); Serial.println( iValAAjouter );
      }
    }
    // On calcul sur des float pour ajouter 0.5 afin d'avoir l'arrondi à l'entier le plus proche.
    iValeurMoyenneDuCycle = int ( ( float( iSommeDesMesuresValides ) / float( iNbMesuresValides ) ) + 0.5 );
   }
  Serial.print( "Nombre de mesures valides du cycle = " ); Serial.println( iNbMesuresValides );
  Serial.print( "Valeur Moyenne du cycle = " ); Serial.println( iValeurMoyenneDuCycle );
 
  //Met à jour domoticz
  osUrl = "/json.htm?type=command&param=udevice&idx=";
  osUrl += String( eepromDomoticz.iIdxDomoticzCuve );
  osUrl += "&nvalue=0&svalue=";
  osUrl += String( iValeurMoyenneDuCycle );

  Serial.print( "Url d'appel domoticz = " );
  Serial.println( osUrl );


  //////////////////// Appel domoticz pour mettre a jour le capteur
  int iResCallDomo = iEnvoiDomoticz( iValeurMoyenneDuCycle );
  switch ( iResCallDomo ){
    case 0 :
      // L'appel de la fonction c'est bien passé
      Serial.println( "iEnvoiDomoticz OK" );
      break;
     default :
      // On a un code <> 0 donc il y a une erreur.
      // Les codes erreurs sont 1,2.
      Serial.println( "iEnvoiDomoticz NOK" );
      break;
  }
 

 
   //Attente entre deux cycles : on fait plusieurs fois délais pour ne pas bloquer le code
  digitalWrite( D1, LOW );
  for ( int i = 1; i<= eepromDomoticz.iSecondesEntreDeuxCycles; i++ ) {
    oEspServer.handleClient();
    delay( 1000 );
  }
}


/**********************************************************************************************************************************************
 **********************************************************************************************************************************************
 * Fonction pour mettre a jour le capteur dans domoticz
 **********************************************************************************************************************************************
 *********************************************************************************************************************************************/
 int iEnvoiDomoticz( int iValeur ){
  Serial.println( "Start fonction iEnvoiDomoticz" );
  int iRes = 0;

  oDomoticzClient.begin( sDomoticzIp, iDomoticzPort, osUrl );
  int iHttpCode = oDomoticzClient.GET();
  if ( iHttpCode ){
    if ( iHttpCode == 200 ){
      String osPayload = oDomoticzClient.getString();
      Serial.println( "Domoticz response" );
      Serial.println( osPayload );
    } else {
      String osPayload = oDomoticzClient.getString();
      Serial.println( "Domoticz response error " );
      Serial.println( osPayload );
      Serial.print( "Adresse IP ESP : " );
      Serial.println( WiFi.localIP().toString() );
      Serial.print( "iHttpCode : " );
      Serial.println( iHttpCode );
      iRes = 1;
    }
  } else {
    Serial.println( "Domoticz not response" );
    iRes = 2;
   }
  oDomoticzClient.end();

  return iRes;
 }

/**********************************************************************************************************************************************
 **********************************************************************************************************************************************
 * Fonction qui lit le niveau de la cuve
 **********************************************************************************************************************************************
 *********************************************************************************************************************************************/
int iLectureNiveauCuve(){
  // definition des constantes
  const float vitesse_son = float( eepromDomoticz.iVitesseSon ); // en m/s
  const unsigned long timeout = 29500UL ; // 29.5 ms = 10m à 340m/s

  //Variables
  int iNiveauLu = 0;
  long mesure = 0;
  float distance = 0;

  Serial.print( "Debut fonction iLectureNiveauCuve. Vitesse Son = " );
  Serial.println( eepromDomoticz.iVitesseSon );

  // Envoi le signal de 10us sur TRIG pour déclancher la mesure
  digitalWrite( TRIG, HIGH );
  delayMicroseconds( 10 );
  digitalWrite( TRIG, LOW );

  // Mesure le temps entre l'envoi de l'impulsion et le retour du son
  mesure = pulseIn( ECHO, HIGH, timeout );

  // Calcul la distance d = t*v/2 (t en us )
  distance = mesure * vitesse_son / 20000 ; // conversion pour avoir des cm
  Serial.print( "distance mesuree = " );
  Serial.println( distance );

  //Converti la distance entre le capteur et la surface en profondeur d'eau.
  if ( distance >= eepromDomoticz.iProfondeurCuve ){
    iNiveauLu = 0;
  } else {
    iNiveauLu = int( eepromDomoticz.iProfondeurCuve - ( distance + 0.5 ) ); //On ajoute 0.5 pour avoir l'arondi à l'entier le plus proche
  }

  Serial.print( "Niveau lu dans fonction iLectureNiveauCuve = " );
  Serial.println( iNiveauLu );

  return iNiveauLu;
}

 /**********************************************************************************************************************************************
 * *********************************************************************************************************************************************
 * vFonctionRoot
 * Fonction qui renvois la page / du serveur
 * Affiche comment utiliser le serveur associé au module
 * *********************************************************************************************************************************************
 **********************************************************************************************************************************************/
 void vFonctionRoot(){

  Serial.println( "Entre dans fonction vFonctionRoot" );

  //construit le code html a retourner
  String osMessage = "<html><body>";
  osMessage += "<h1>Usage : </h1>";
  osMessage += "http://IP_ADRESS:PORT:/commande?arg1=val1&arg2=val2... ( exemple: http://192.168.1.100/action?reboot=1 )<br><br>";
  osMessage += "Where : commande and args are : <br>";

  osMessage += "<ul>";

  //décrit les options possibles pour la commande action
  osMessage += "<li><b>action :</b></li>";
    osMessage += "<ul>";
    //Pour faire un reboot
    osMessage += "<li>reboot=T : reboot ESP after T secondes (reboot now if T=0) ( T between [0-3600] )</li>";

    //Pour effacer les données de l'eeprom pour chaque structure memorisée : cela permet de repartir sur les valeurs par defaut.
    osMessage += "<li>clear_eeprom=OK : clear eeprom. Defaults values will be used to next start. </li>";
    osMessage += "<li>info=OK : Show all values for ESP</li>";
    osMessage += "</ul>";
 
  //Decrit les actions associées à la commande set :
  //Cette commande est utilisée pour donner des valeurs à des variable durant l'execution de la boucle loop
    osMessage += "<li><b>set :</b></li>";
    osMessage += "<ul>";
    osMessage += "<li>idx_cuve=I : set idx of domoticz device for cuve to I</li>";
    osMessage += "<li>s_per_cycle=S : wait S seconds between each cycle of mesures. ( S < 3600 ).</li>";
    osMessage += "<li>mesures_per_cycle=N : take N mesures in each cycle. ( N < 100 ).</li>";
    osMessage += "<li>profondeur=P : set deepth of the water recovery tank (centimeters).</li>";
    osMessage += "<li>vson=V : set sound speed in m/s (defaul = 340 ).</li>";
    osMessage += "</ul>";
 
  osMessage += "</ul>";

  osMessage += "</body></html>";

  oEspServer.send(200, "text/html", osMessage );
 }

 /**********************************************************************************************************************************************
 * *********************************************************************************************************************************************
 * vFonctionReboot
 * Fonction lance le reboot de l'ESP en placant D0 à LOW
 * *********************************************************************************************************************************************
 ********************************************************************************************************************************************/
 void vFonctionReboot(){
    pinMode( D0, OUTPUT );
    digitalWrite( D0, LOW ); //D0 connecté sur RST (obligatoire pour utilisation deepsleep)
 }

 /**********************************************************************************************************************************************
 * *********************************************************************************************************************************************
 * vFonctionAction
 * Fonction qui gère le parametre actions :
 * - reboot=T : reboot l'esp après T secondes.
 * *********************************************************************************************************************************************
 ********************************************************************************************************************************************/
 void vFonctionAction(){
  String osRes = "<html>";
  Serial.println( "Entre dans fonction vFonctionAction" );

   //Analyse les arguments sauf reboot qui sera analysé en dernier
   if ( oEspServer.hasArg("clear_eeprom") ){
    Serial.print( "-- Demande effacement eeprom : " );
    String osClear = oEspServer.arg( "clear_eeprom" );
    if ( osClear == "OK" ){
      Serial.println( osClear );
      vFonctionClearEEPROM();
      osRes += "EEPROM cleared.<br>";
     } else {
      Serial.println( "Mauvais argument" );
      osRes += "Bad value for action clear_eeprom<br>";
     }
     osRes += "action : clear_eeprom OK<br>";
   }

   // Regarde si on demande d'afficher les info
  if ( oEspServer.hasArg( "info" ) ){
    Serial.print( "-- Demande affichage info : " );
    String osArgInfo = oEspServer.arg( "info" );
    if ( osArgInfo == "OK" ){
       Serial.println( "OK" );
      osRes += osFonctionInfo();
    } else {
      Serial.println( "Erreur d'argument pour info" );
      osRes += "Bad value for arg info.<br>";
    }
  }

  // On envois la page de resultat 
   osRes += "</html>";
   oEspServer.send(200, "text/html", osRes );
 
 
 //On a traité tous les arguments donc on peut regarder s'il faut traiter un reboot
  if ( oEspServer.hasArg("reboot") ){
    Serial.print( "-- Demande de reboot after (secondes) : " );
    // récupère l'argument associé
    int iDelay = oEspServer.arg( "reboot" ).toInt();
    if ( ( iDelay < 0 ) or ( iDelay > 3600 ) ) iDelay = 0; //On accepte en 0 et 3600s
    oEspServer.send(200, "text/html", "<html>reboot</html>" );
    Serial.println( iDelay );
    delay( iDelay * 1000 );
    vFonctionReboot();
    }
 
  Serial.println( "Sortie fonction Action" );

  }

/**********************************************************************************************************************************************
 * *********************************************************************************************************************************************
 * vFonctionSetIdxCuve( int idx )
 * Met à jour l'iIdxDomoticzCuve dans l'eeprom
 * *********************************************************************************************************************************************
 ********************************************************************************************************************************************/
 void vFonctionSetIdxCuve( int idx ){
 
    Serial.print( "--Fonction vFonctionSetIdxCuve de mise à jour eeprom pour Idx Cuve. Valeur = " );
    Serial.println( idx );

    if ( idx < 0 ) return;
    if ( ( eepromDomoticz.iMemoryControlStart == MEMORY_CONTROL_START ) and
        ( eepromDomoticz.iMemoryControlEnd == MEMORY_CONTROL_END ) and
        ( eepromDomoticz.iIdxDomoticzCuve == idx ) ){
          // Tout est déjà à jour
           Serial.print( "--Fonction vFonctionSetIdxCuve : pas de mise à jour a faire" );
           return;
        } else {
           eepromDomoticz.iIdxDomoticzCuve = idx ;
           eepromDomoticz.iMemoryControlStart = MEMORY_CONTROL_START;
           eepromDomoticz.iMemoryControlEnd = MEMORY_CONTROL_END;
         }
     
      EEPROM.begin( iTaille_eeprom );
      EEPROM.put( index_eepromDomoticz, eepromDomoticz );
      Serial.println( "Write to EEPROM : " );
      Serial.println( eepromDomoticz.iMemoryControlStart );
      Serial.println( eepromDomoticz.iIdxDomoticzCuve );
      Serial.println( eepromDomoticz.iMemoryControlEnd );
      EEPROM.end();
   
      return;
 }

/**********************************************************************************************************************************************
 * *********************************************************************************************************************************************
 * vFonctionSetVitesseSon( int idx )
 * Met à jour iVitesseSon dans l'eeprom
 * *********************************************************************************************************************************************
 ********************************************************************************************************************************************/
 void vFonctionSetVitesseSon( int idx ){
 
    Serial.print( "--Fonction vFonctionSetVitesseSon de mise à jour eeprom pour vitesse du son. Valeur = " );
    Serial.println( idx );

    if ( idx < 0 ) return;
    if ( ( eepromDomoticz.iMemoryControlStart == MEMORY_CONTROL_START ) and
        ( eepromDomoticz.iMemoryControlEnd == MEMORY_CONTROL_END ) and
        ( eepromDomoticz.iVitesseSon == idx ) ){
          // Tout est déjà à jour
           Serial.print( "--Fonction vFonctionSetVitesseSon : pas de mise à jour a faire" );
           return;
        } else {
           eepromDomoticz.iVitesseSon = idx ;
           eepromDomoticz.iMemoryControlStart = MEMORY_CONTROL_START;
           eepromDomoticz.iMemoryControlEnd = MEMORY_CONTROL_END;
         }
     
      EEPROM.begin( iTaille_eeprom );
      EEPROM.put( index_eepromDomoticz, eepromDomoticz );
      Serial.println( "Write to EEPROM : " );
      Serial.println( eepromDomoticz.iMemoryControlStart );
      Serial.println( eepromDomoticz.iVitesseSon );
      Serial.println( eepromDomoticz.iMemoryControlEnd );
      EEPROM.end();
   
      return;
 }

/**********************************************************************************************************************************************
 * *********************************************************************************************************************************************
 * vFonctionSetProfondeurCuve( int idx )
 * Met à jour la profondeur de la cuve.
 * *********************************************************************************************************************************************
 ********************************************************************************************************************************************/
 void vFonctionSetProfondeurCuve( int idx ){
 
    Serial.print( "--Fonction vFonctionSetProfondeur de mise à jour eeprom pour profondeur Cuve. Valeur = " );
    Serial.println( idx );

    if ( idx < 0 ) return;
    if ( ( eepromDomoticz.iMemoryControlStart == MEMORY_CONTROL_START ) and
        ( eepromDomoticz.iMemoryControlEnd == MEMORY_CONTROL_END ) and
        ( eepromDomoticz.iProfondeurCuve == idx ) ){
          // Tout est déjà à jour
           Serial.print( "--Fonction vFonctionSetProfondeur : pas de mise à jour a faire" );
           return;
        } else {
           eepromDomoticz.iProfondeurCuve = idx ;
           eepromDomoticz.iMemoryControlStart = MEMORY_CONTROL_START;
           eepromDomoticz.iMemoryControlEnd = MEMORY_CONTROL_END;
         }
     
      EEPROM.begin( iTaille_eeprom );
      EEPROM.put( index_eepromDomoticz, eepromDomoticz );
      Serial.println( "Write to EEPROM : " );
      Serial.println( eepromDomoticz.iMemoryControlStart );
      Serial.println( eepromDomoticz.iProfondeurCuve );
      Serial.println( eepromDomoticz.iMemoryControlEnd );
      EEPROM.end();
   
      return;
 }

/**********************************************************************************************************************************************
 * *********************************************************************************************************************************************
 * vFonctionSetMesure_Per_Cycle( int idx )
 * Met à jour l'iNbMesuresParCycle dans l'eeprom
 * *********************************************************************************************************************************************
 ********************************************************************************************************************************************/
 void vFonctionSetMesures_Per_Cycle( int sd ){
 
    Serial.print( "--Fonction vFonctionSetMesures_Per_Cycle de mise à jour eeprom pour iNbMesuresParCycle. Valeur = " );
    Serial.println( sd );

    if ( sd < 0 ) return;
    if ( ( eepromDomoticz.iMemoryControlStart == MEMORY_CONTROL_START ) and
        ( eepromDomoticz.iMemoryControlEnd == MEMORY_CONTROL_END ) and
        ( eepromDomoticz.iNbMesuresParCycle == sd ) ){
          // Tout est déjà à jour
           Serial.println( "--Fonction vFonctionSetMesures_Per_Cycle : pas de mise à jour a faire" );
           return;
        } else {
           eepromDomoticz.iNbMesuresParCycle = sd ;
           eepromDomoticz.iMemoryControlStart = MEMORY_CONTROL_START;
           eepromDomoticz.iMemoryControlEnd = MEMORY_CONTROL_END;
           Serial.println( "--Fonction vFonctionSetMesures_Per_Cycle : Ok pour mise à jour" );
         }
     
      EEPROM.begin( iTaille_eeprom );
      EEPROM.put( index_eepromDomoticz, eepromDomoticz );
      Serial.println( "Write to EEPROM : " );
      Serial.println( eepromDomoticz.iMemoryControlStart );
      Serial.println( eepromDomoticz.iNbMesuresParCycle );
      Serial.println( eepromDomoticz.iMemoryControlEnd );
      EEPROM.end();
   
      return;
 }


/**********************************************************************************************************************************************
 * *********************************************************************************************************************************************
 * vFonctionSetS_Per_Cycle( int idx )
 * Met à jour l'iSecondesEntreDeuxCycles dans l'eeprom
 * *********************************************************************************************************************************************
 ********************************************************************************************************************************************/
 void vFonctionSetS_Per_Cycle( int sd ){
 
    Serial.print( "--Fonction vFonctionSetS_Per_Cycle de mise à jour eeprom pour iSecondesEntreDeuxCycles. Valeur = " );
    Serial.println( sd );

    if ( sd < 0 ) return;
    if ( ( eepromDomoticz.iMemoryControlStart == MEMORY_CONTROL_START ) and
        ( eepromDomoticz.iMemoryControlEnd == MEMORY_CONTROL_END ) and
        ( eepromDomoticz.iSecondesEntreDeuxCycles == sd ) ){
          // Tout est déjà à jour
           Serial.println( "--Fonction vFonctionSetS_Per_Cycle : pas de mise à jour a faire" );
           return;
        } else {
           eepromDomoticz.iSecondesEntreDeuxCycles = sd ;
           eepromDomoticz.iMemoryControlStart = MEMORY_CONTROL_START;
           eepromDomoticz.iMemoryControlEnd = MEMORY_CONTROL_END;
           Serial.println( "--Fonction vFonctionSetS_Per_Cycle : Ok pour mise à jour" );
         }
     
      EEPROM.begin( iTaille_eeprom );
      EEPROM.put( index_eepromDomoticz, eepromDomoticz );
      Serial.println( "Write to EEPROM : " );
      Serial.println( eepromDomoticz.iMemoryControlStart );
      Serial.println( eepromDomoticz.iSecondesEntreDeuxCycles );
      Serial.println( eepromDomoticz.iMemoryControlEnd );
      EEPROM.end();
   
      return;
 }
 /**********************************************************************************************************************************************
 * *********************************************************************************************************************************************
 * vFonctionClearEEPROM( )
 * Efface toutes les données (remise à zero de la structure) EEPROM
 * *********************************************************************************************************************************************
 ********************************************************************************************************************************************/
 void vFonctionClearEEPROM(){
 
    Serial.println( "--Fonction vFonctionClearEEPROM." );
 
    // On efface eeprom
    // Pour cela on met juste à jour les valeurs des controles de la structure.
    // Ainsi le programme pourra continuer à fonctionner mais l'EEPROM sera réinitialisée avec
    // les valeurs par défaut au prochain reboot.
    eepromDomoticz.iMemoryControlStart = 0;
    eepromDomoticz.iMemoryControlEnd = 0;   
     
    EEPROM.begin( iTaille_eeprom );
    EEPROM.put( index_eepromDomoticz, eepromDomoticz );
    Serial.println( "Write to EEPROM." );
    EEPROM.end();
   
    return;
 }
 
/**********************************************************************************************************************************************
 * *********************************************************************************************************************************************
 * vFonctionSet
 * Fonction qui met à jour des données en cours de programme
 * - idx_cuve=I : Fixe l'idx du capteur virtuel de cuve dans domoticz
 * *********************************************************************************************************************************************
 ********************************************************************************************************************************************/
 void vFonctionSet(){
  Serial.println( "Entre dans fonction vFonctionSet" );
  String osRes = "";

  //Analyse les arguments
  if ( oEspServer.hasArg("idx_cuve") ){
    Serial.print( "-- Change idx du capteur de cuve domoticz = " );
    // récupère l'argument associé
    int iIdx = oEspServer.arg( "idx_cuve" ).toInt();
    if ( ( iIdx <= 0 ) or ( eepromDomoticz.iIdxDomoticzCuve == iIdx ) ){
      Serial.println( "Pas de changement( idx identique ou 0)" );
      osRes += "set idx_cuve : Pas de changement\n";
    } else {
      Serial.println( iIdx );
      vFonctionSetIdxCuve( iIdx );
      osRes += "set idx_cuve : set to " + String( iIdx ) + "\n";
    }
  }


  if ( oEspServer.hasArg("s_per_cycle") ){
    Serial.print( "-- Change nombre de secondes par cycle = " );
    // récupère l'argument associé
    int iIdx = oEspServer.arg( "s_per_cycle" ).toInt();
    if ( ( iIdx <= 0 ) or ( iIdx > 36000 ) or ( eepromDomoticz.iSecondesEntreDeuxCycles == iIdx ) ){
      Serial.println( "Pas de changement( nb secondes identique ou <= 0 ou >= 3600)" );
      osRes += "set s_per_cycle : No Changed\n";
    } else {
      Serial.println( iIdx );
      vFonctionSetS_Per_Cycle( iIdx );
      osRes += "set s_per_cycle : set to " + String( iIdx ) + "\n";
    }
  }

  if ( oEspServer.hasArg("mesures_per_cycle") ){
    Serial.print( "-- Change nombre de mesures par cycle = " );
    // récupère l'argument associé
    int iIdx = oEspServer.arg( "mesures_per_cycle" ).toInt();
    if ( ( iIdx <= 0 ) or ( iIdx > NB_MESURES_MAX_PAR_CYCLE ) or ( eepromDomoticz.iNbMesuresParCycle == iIdx ) ){
      Serial.println( "Pas de changement( nb secondes identique ou <= 0 ou >= MAX)" );
      osRes += "set mesures_per_cycle : No Changed\n";
    } else {
      Serial.println( iIdx );
      vFonctionSetMesures_Per_Cycle( iIdx );
      osRes += "set mesures_per_cycle : set to " + String( iIdx ) + "\n";
    }
  }

   if ( oEspServer.hasArg("profondeur") ){
    Serial.print( "-- Change profondeur cuve = " );
    // récupère l'argument associé
    int iIdx = oEspServer.arg( "profondeur" ).toInt();
    if ( ( iIdx <= 0 ) or ( eepromDomoticz.iProfondeurCuve == iIdx ) ){
      Serial.println( "Pas de changement." );
      osRes += "set profondeur : No Changed\n";
    } else {
      Serial.println( iIdx );
      vFonctionSetProfondeurCuve( iIdx );
      osRes += "set profondeur : set to " + String( iIdx ) + "\n";
    }
  }

  if ( oEspServer.hasArg("vson") ){
    Serial.print( "-- Change vitesse son = " );
    // récupère l'argument associé
    int iIdx = oEspServer.arg( "vson" ).toInt();
    if ( ( iIdx <= 0 ) or ( eepromDomoticz.iVitesseSon == iIdx ) ){
      Serial.println( "Pas de changement." );
      osRes += "set vson : No Changed\n";
    } else {
      Serial.println( iIdx );
      vFonctionSetVitesseSon( iIdx );
      osRes += "set vson : set to " + String( iIdx ) + "\n";
    }
  }

  if ( osRes == "" )
    //renvoi sur la page d'aide
    vFonctionRoot();
  else {
    osRes += "\n\n";
    oEspServer.send(200, "text/html", osRes );
 }

 return;
 }

/**********************************************************************************************************************************************
 * *********************************************************************************************************************************************
 * vFonctionInfo
 * Fonction qui renvois la page d'information avec la valeur de toutes les variables
  * *********************************************************************************************************************************************
 **********************************************************************************************************************************************/
 String osFonctionInfo(){

  String osMessage = "";
  Serial.println( "Entre dans fonction vFonctionInfo" );

  //construit le code html a retourner
  byte tbMac[ 6 ] = {0,0,0,0,0,0};
  WiFi.macAddress( tbMac ); //récupère l'adresse MAC
  osMessage += "Adresse MAC = ";
  for (int i = 0; i<=5; i++ ) {
    if ( i == 0 ) osMessage += String( tbMac[ i ], HEX );
    else osMessage += "." + String( tbMac[ i ], HEX );
  }
  osMessage += "<br>";


  //Adresses IP du module
  osMessage += "Adresse Ip = ";
  osMessage += WiFi.localIP().toString();
  osMessage += "<br>";

  osMessage += "Adresse Ip Gateway = ";
  osMessage += oipGateway.toString();
  osMessage += "<br>";

  osMessage += "Subnet = ";
  osMessage += oipSubnet.toString();
  osMessage += "<br>";

  osMessage += "Adresse Ip DNS = ";
  osMessage += oipDns.toString();
  osMessage += "<br>";

// Valeurs de la structure EEPROM
  osMessage += "eepromDomoticz.iMemoryControlStart = ";
  osMessage += String( eepromDomoticz.iMemoryControlStart );
  osMessage += "<br>";

  osMessage += "eepromDomoticz.iIdxDomoticzCuve = ";
  osMessage += String( eepromDomoticz.iIdxDomoticzCuve );
  osMessage += "<br>";

  osMessage += "eepromDomoticz.iSecondesEntreDeuxCycles = ";
  osMessage += String( eepromDomoticz.iSecondesEntreDeuxCycles );
  osMessage += "<br>";

  osMessage += "eepromDomoticz.iNbMesuresParCycle = ";
  osMessage += String( eepromDomoticz.iNbMesuresParCycle );
  osMessage += "<br>";

  osMessage += "eepromDomoticz.iProfondeurCuve = ";
  osMessage += String( eepromDomoticz.iProfondeurCuve );
  osMessage += "<br>";

  osMessage += "eepromDomoticz.iVitesseSon = ";
  osMessage += String( eepromDomoticz.iVitesseSon );
  osMessage += "<br>";

  osMessage += "eepromDomoticz.iMemoryControlEnd = ";
  osMessage += String( eepromDomoticz.iMemoryControlEnd );
  osMessage += "<br>";



  return osMessage;
 }



courbe_decharge_et_temperature_thingspeak

Ce programme correspond à un circuit à base d'ESP01s et de DHT11. Il est destiné à être alimenté par un accu. Il mesure l'évolution de la tension, la température et l'humidité. Les données sont envoyées dans un channel ThnigSpeak.

/*******************************************************************
* Ce programme mesure la tension interne du microcontroleur ainsi que les
* données (temperature et humidité) retournées pas un module DHT11.
* Toutes ces données sont envoyées sur un channel ThingSpeak
* pour en tracer l'évolution dans le temps.
* Il a été écrit pour un ESP01/ESP01s
*
* Evolutions possible :
*   - Voir si un reboot après chaque cycle est mieux ?
*
* Versions :
*   v1 - 28/08/2019: Création
*   v2 - 28/08/2019: Connexion Wifi dans le setup
*
* (c) acampeaux@gmail.com
********************************************************************/

/*====================================================================
 * La section ci-dessous contient les valeurs pour adapter facilement le programme.
 *
 * COEF_GETVCC: Définit le coefficient correcteur de tension qui sera appliqué au résultat
 * de la fonction getVcc. Ce coefficient n'est pas le même sur un ESP01 ou
 * un NodeMCU ESP12e. Il s'agit d'un nombre réél. Si les coefficients que j'utilise
 * ne sont pas corrects dans votre contexte alors il faudra les échantilloner
 * vous meme en suivant les directive de l'article:
 * https://framboiseaupotager.blogspot.com/2019/07/tout-connaitre-du-suivi-des-charges-et.html
 * ==================================================================*/
#define COEF_GETVCC 1.07
#define DEBUG //Il faudra commenter cette ligne pour compiler sans les traces.

//Nombre de secondes séparant deux cycles de mesures + envois
#define NB_SECONDES_ENTRE_CYCLE 60

//Valeurs pour régler la connexion wifi
#define WIFI_ID "framboise"
#define WIFI_PWD "aupotager"

#define CHANNEL_ID "****" //Mettre ici la clé du channel ThingSpeak

/*====================================================================
  * Inclusion des librairies externes
 * ==================================================================*/
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <DHT.h>

/*====================================================================
 * Place le convertisseur ADC dans un mode de lecture de la tension interne.
 * Les broches analogiques doivent être libres et ne pourront être utilisées
 * ==================================================================*/
ADC_MODE(ADC_VCC);


/*====================================================================
  * Définition de constantes (#define) et des variables globales
 * ==================================================================*/
String stNomProgramme = "courbe_decharge_thingspeak";
String stVersion = "v2.0";

//////////////////////////////constantes relatives à la connexion série
/* Fixe la vitesse de communication série.
  Sur ESP8266 je mets 74880 car c'est la vitesse de communication du controleur au boot.*/
#define SERIAL_BIT_RATE 74880


//////////////////////////////définitions relatives à la boucle loop
/* Temps d'attente à la fin de la boucle loop avant de recommencer un cycle
En millisecondes */
#define DELAY_INTER_CYCLES NB_SECONDES_ENTRE_CYCLE*1000
/* Compte le nombre d'itérations */
int iNbIteration = 0;

/* Pour calculer une valeur médianne de la tension parmis des mesures faites à
 *  intervalles donnés. Le nombre de valeurs doit etre supérieur à 3 sinon cela ne fonctionne pas.
 *  Il est conseillé de prendre un nombre impaire.
 */
#define MESURE_DELAIS 50
#define NB_VAL  11
int tiMesures[ NB_VAL ];

////////////////////////////////////////////////////// Constantes et variable relatives au wifi
// Temps d'attente en seconde pour se connecter au wifi
#define MAX_TIME_WAIT_WIFI 60

// SSID et identifiants de connexion au Wifi de votre routeur internet.
const char* sSsid = WIFI_ID;
const char* sPassword = WIFI_PWD;

////////////////////////////////////////////////////// Variables relatives à ThingSpeak
String osThinkSpeakUrl = "api.thingspeak.com";
int iThinkSpeakPort = 80;
String osTSKeyChannel = CHANNEL_ID;

HTTPClient oThinkSpeakClient;

////////////////////////////////////////////////////// Variables relatives aux broches
#define LED_CTRL 0 //broche qui fera clignoter la LED

////////////////////////////////////////////////constantes relatives à la sonde DHT11 (ou DHT22)
#define  DHT_DATA 2

// Remplacez DHT11 par DHT22 ou DHT21 en fonction de votre modèle de capteur
#define DHTTYPE DHT11

DHT dht(DHT_DATA, DHTTYPE);

// Delais d'inertie du capteur en ms=délais minimum entre deux prise de mesure.
// Pour le DHT11 il faut attendre 2s.
// Pour le DHT22 il faut attendre 0,5s (500ms)
#define DHT_INERTIE 2000

// Pour plus de précision, avant d'envoyer à ThingSpeak on va prendre plusieurs mesures et en faire la moyenne
#define NB_MESURE 5


/*====================================================================
  * Fonction setup(): executée une fois au démarrage du microcontroleur.
  * - Ouvre la liaison série.
 * ==================================================================*/
void setup() {

#ifdef DEBUG
  ////////////////////////////////////////////Variable locales au setup
  String stMessageAccueil = "";

  ///////////////////////////////////Initialisation de la liaison série
  Serial.begin(SERIAL_BIT_RATE);
  delay( 100 );

  stMessageAccueil = stNomProgramme + " - Version: " + stVersion;
  Serial.println( "" ); //Saute de ligne.
  Serial.println( stMessageAccueil );
  Serial.println( "Serial: OK" );
#endif

  ////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////Connexion Wifi
  WiFi.begin( sSsid, sPassword );

#ifdef DEBUG
  Serial.printf( "Connecting WiFi: SSID=%s PWD=%s ", sSsid, sPassword  );
#endif

int iNbSec = 0;
  while (WiFi.status() != WL_CONNECTED ){
  delay( 1000 );
  iNbSec++; 
#ifdef DEBUG
  Serial.print( "." );
#endif 
  if ( iNbSec >= MAX_TIME_WAIT_WIFI ) {
#ifdef DEBUG
  Serial.printf( "\nDelais de connexion de %d secondes depasse -> restart\n", MAX_TIME_WAIT_WIFI );
#endif   
  // Si on dépasse le délais de connexion on réalise un restart. Pour que la fonction "restart"
  //  fonctionne l'ESP doit avoir été resété une première fois manuellement (avec un reset ou
  //  avec un arret d'alimentation électrique.
    ESP.restart();
  }
 }

#ifdef DEBUG
  Serial.printf( "\n-> Wifi connected in %d seconds\n", iNbSec );
  vCheckWifiStatus( WiFi.status(), WiFi.getMode() );

  String stRes = "Connected to : " + String( sSsid ) + "\nAdresse IP : ";
  stRes += WiFi.localIP().toString() ; //Type IPAddress = tableau de 4 entiers.
  Serial.println( stRes );
#endif


  ///////////////////////////////////Initialisation des broches
  // Led de controle qui va s'allumer avant et après l'envois de données wifi
  pinMode( LED_CTRL, OUTPUT );
  digitalWrite(LED_CTRL, LOW);

  /////////////////////////////////////////////////////// Initialisation capteur de temperature
  dht.begin();
}


/*====================================================================
  * Fonction loop(): executée en boucle.
  * - Realise des mesures de la tension interne
  * - Calcul la valeur médiane
  * - Connexion wifi et envois vers ThingSpeak.
  * - Déconnexion et attente.
 * ==================================================================*/
void loop() {

  ////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////Variable locales a la fonction loop

  ////////////////////////////////////////////////////////////////////////
  //////////////////////////////On s'assure que le Wifi est bien déconnecté
  int iWifiStatus = WiFi.status();
  int iWifiMode = WiFi.getMode();

#ifdef DEBUG
  Serial.printf( "\n-----------> Debut de boucle loop.\n" );
  vCheckWifiStatus( iWifiStatus, iWifiMode );
#endif

 ////////////////////////////////////////////////////////////////////////
 //////////// realise le nb de mesures demandees avec delais entre chaque
  for ( int iCompteur = 0; iCompteur < NB_VAL; iCompteur++ ){
    /* Lecture de la tension en ms */
    tiMesures[ iCompteur ] = ESP.getVcc();
    delay( MESURE_DELAIS );
  }

  /* Classe le tableau */
  for (int i = 0; i < NB_VAL; i++)
    for (int j = 0; j < NB_VAL; j++){
      if ( tiMesures[ i ] > tiMesures[ j ]){
        int iMem = tiMesures[ i ];
        tiMesures[ i ] = tiMesures[ j ];
        tiMesures[ j ] = iMem;
      }
    }

  /* recherche la médiane en prennant la valeur centrale du tableau*/
  int iMediane = tiMesures[ int( ( NB_VAL / 2.0 ) + 0.5 ) - 1 ];

  /* Applique le coefficient correcteur dont la valeur est fonction du type d'ESP */
  float fResultat = float( iMediane ) * COEF_GETVCC / 1000;
  char szResultat[11];
  dtostrf( fResultat, 0, 2, szResultat);
  iNbIteration++;

#ifdef DEBUG
  Serial.println( String("Iteration no: ") + iNbIteration + " Valeur Mediane: " + \
    iMediane + " Tension resultante: " + szResultat );
#endif


////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////Relevé température et humidité

// Pour plus de fiabilité on va prendre un nombre de mesures et faire la moyenne
  float fTemp = 0.0;
  float fHum = 0.0;
  int i = 0;
  for ( i = 1; i <= NB_MESURE; i++){
    Serial.print( "Prise de la mesure numero " );
    Serial.println( i );
    // Lecture du taux d'humidité
    float h = dht.readHumidity();

    // Lecture de la température en Celcius
    float t = dht.readTemperature();

    // Stop le programme et renvoie un message d'erreur si le capteur ne renvoie aucune mesure
    if (isnan(h) || isnan(t) ) {
      Serial.println("Echec de lecture capteur temperature.");
      delay( DHT_INERTIE );
      return;
    }

    Serial.print("Humidite: ");
    Serial.println(h);
    Serial.print("Temperature: ");
    Serial.println(t);

    // Additionne les mesures
    fTemp += t;
    fHum += h;

    // Attend un peu avant de prendre la mesure suivante
    delay( DHT_INERTIE );
  }

  fTemp = fTemp / NB_MESURE;
  Serial.print("Temperature moyenne: ");
  Serial.println( fTemp );

  fHum = fHum / NB_MESURE;
  Serial.print("Humidite moyenne: ");
  Serial.println( fHum );



  ////////////////////////////////////////////////////////////////////////
  ///////////////////////////////Envois des données à ThingSpeak
  digitalWrite(LED_CTRL, HIGH);   //LED de controle d'envois des données allumée
  delay( 200 );
  int iRes = iEnvoiThingSpeak( fResultat, fTemp, fHum );
#ifdef DEBUG 
  Serial.printf( "Retour iEnvoiThingSpeak = %d\n", iRes );
#endif 

  digitalWrite(LED_CTRL, LOW);   //LED de controle d'envois des données allumée

#ifdef DEBUG 
  Serial.printf( "-----------> Fin de boucle loop.\n" );
#endif 
  delay( DELAY_INTER_CYCLES );
}


/*====================================================================
  * Fonction vCheckWifiStatus(iWifiStatus, iWifiM):
  * - Affiche un message texte comprehensible correspond
  *   au status et au mode du wifi
 * ==================================================================*/
void vCheckWifiStatus( int iWifiStatus, int iWifiMode ){

    //On va afficher un message fonction du status wifi
    Serial.printf("Connection status: %d\n", iWifiStatus );
    switch ( iWifiStatus ) {
      case WL_DISCONNECTED: { //Valeur 6
        Serial.printf("-> Wifi disconnected.\n" );break;}
      case WL_IDLE_STATUS: { //Valeur 0
        Serial.printf("-> Wifi in process of changing.\n" );break;}
      case WL_NO_SSID_AVAIL: { //Valeur 1
        Serial.printf("-> SSID (%s) cannot be reached.\n", sSsid );break;}
      case WL_CONNECTED: { //Valeur 3
        Serial.printf("-> Wifi connection is established.\n" );break;}   
      case WL_CONNECT_FAILED: { //Valeur 4
        Serial.printf("-> password (%s) is incorrect.\n", sPassword );break;}                 
      default: {
          Serial.printf("-> Status Wifi: inconnu (%d)\n", iWifiStatus );
        }
    }

    switch( iWifiMode) {
      case WIFI_OFF: { //Valeur 0
         Serial.println( "-> Mode Wifi OFF" );break;
      }
      case WIFI_STA: { //Valeur 1
        Serial.println( "-> Mode Wifi WIFI_STA" );break;
      }
      case WIFI_AP: { //Valeur 2
        Serial.println( "-> Mode Wifi WIFI_AP" ); break;
      }
      case WIFI_AP_STA: { //Valeur 3
        Serial.println( "->Mode Wifi WIFI_AP_STA" ); break;
      }
      default: { //Valeur inconnue
        Serial.printf( "Mode (%d) inconnu\n", iWifiMode );
      }
    }
  }

/*====================================================================
  * Fonction vCheckWifiDisconnected()
  * - Deconnecte le wifi s'il ne l'est pas.
 * ==================================================================*/
void vCheckWifiDisconnected(){

  //Si le wifi n'est pas déconnecté on déconnecte
  if ( ( WiFi.status() != WL_DISCONNECTED ) or ( WiFi.getMode() != WIFI_OFF ) ){
#ifdef DEBUG
  Serial.print( "Deconnexion wifi" );
#endif
    WiFi.disconnect( true );
    WiFi.mode( WIFI_OFF );
  while ( ( WiFi.status() != WL_DISCONNECTED ) or ( WiFi.getMode()!= WIFI_OFF ) ){
#ifdef DEBUG 
    Serial.print( "." );
#endif 
    delay( 500 );
  }
#ifdef DEBUG
  Serial.println( "." );
#endif
  }
}

/*====================================================================
  * Fonction iEnvoiThingSpeak( float fValue, float fTemp, float fHum )
  * - Envois les donnees a ThingSpeak
  * - fValue = tension sur champ2 1
  * - fTemp = temperature sur champs 2
  * - fHum = Humidité sur champs 3
 * ==================================================================*/
 int iEnvoiThingSpeak( float fValue, float fTemp, float fHum ){

    int iRes = 0;
    String osUrl = "/update?api_key=" + osTSKeyChannel \
                                      + "&field1" + "=" + String( fValue ) \
                                      + "&field2" + "=" + String( fTemp ) \
                                      + "&field3" + "=" + String( fHum );

#ifdef DEBUG 
  Serial.println( "-> Envois a ThingSpeak: " + osUrl );
#endif
    oThinkSpeakClient.begin( osThinkSpeakUrl, iThinkSpeakPort, osUrl );
 
    int iHttpCode = oThinkSpeakClient.GET();
 
    if ( iHttpCode ){
      if ( iHttpCode == 200 ){
#ifdef DEBUG     
        String osPayload = oThinkSpeakClient.getString();
        Serial.print( "oThinkSpeakClient response: " );
        Serial.println( osPayload );
#endif     
        iRes = 0;
      } else {
#ifdef DEBUG         
        String osPayload = oThinkSpeakClient.getString();
        Serial.print( "oThinkSpeakClient response error: " );
        Serial.println( osPayload );
        Serial.print( "Adresse IP ESP : " );
        Serial.println( WiFi.localIP().toString() );
        Serial.print( "iHttpCode : " );
        Serial.println( iHttpCode );
#endif     
        iRes = 1;
      }
    } else {
#ifdef DEBUG       
      Serial.println( "oThinkSpeakClient not response" );
#endif   
      iRes = 2;
     }
    oThinkSpeakClient.end();
    return iRes;
 }


Test deepSleep

Ce programme est utilisé pour tester le fonctionnement du deep sleep.

/*******************************************************************
* Ce programme permet de tester le deepsleep.
* Pour simuler un traitement, une LED sera allumée.
*
* Evolutions possible :
*
* Versions :
*   v1.0 - 11/09/2019: Création
*   v1.1 - 12/09/2019: Ajout de la LED

* (c) acampeaux@gmail.com
********************************************************************/

/*====================================================================
  * Définition de constantes (#define) et des variables globales
 * ==================================================================*/
String stNomProgramme = "test_deepsleep";
String stVersion = "v1.1";

//fixe la durée du sommeil.
#define DUREE_SOMMEIL_SECONDE 20

//Duree de fonctionnement simulé (en secondes)
#define DUREE_TRAITEMENT 20

//Défini la broche sur laquelle est connectée la LED
#define LED 2

//////////////////////////////constantes relatives à la connexion série
#define SERIAL_BIT_RATE 115200

//////////////////////////////variables globales
int iCompteur = 0;

/*====================================================================
 * Fonction setup.
 * Executée une fois a chaque reveil.
 * ==================================================================*/
void setup() {

 ////////////////////////////////////////////Variable locales au setup
 int iCompteur = 0;

  //Initialisation laison série
  Serial.begin(SERIAL_BIT_RATE);
  delay( 100 );

  //Attente initialisation Serial
  while( !Serial ) {
      delay( 100 );
      iCompteur++;
      if ( iCompteur >= 10 ) ESP.restart();
      }

  //Affiche message initial
  Serial.println( String( "\n" ) + stNomProgramme + String( " - Version: " ) + stVersion );

  //Affiche le compteur pour vérifier si on a attendu l'initialisation de Serial.
  Serial.println( String( "iCompteur = " ) + String( iCompteur++ ) );

  //Simule un traitement en allumant la LED
  Serial.println( String( "On allume la LED durant " ) + String( DUREE_TRAITEMENT ) + String( " secondes." ) );
  pinMode( LED, OUTPUT );
  digitalWrite( LED, HIGH);

  //Affiche les secondes qui passent
  for (int i = 0; i <= DUREE_TRAITEMENT; i++ ){
    delay( 1000 );
    Serial.print( String( i ) + String( " " ) );
  }
  Serial.print( String( "\n" ) );
 
  //Passe en mode sommeil
  //La fonction demande une valeur en micro seconde. On ne peux pas dépasser 71 minutes.
  Serial.println( String( "ESP.deepSleep " ) + String( DUREE_SOMMEIL_SECONDE ) + String (" secondes" ) );
  ESP.deepSleep( DUREE_SOMMEIL_SECONDE * 1000000, RF_DEFAULT );

}

/*====================================================================
 * loop est vide car l'ESP entre en sommeil à la fin du setup.
 * A son réveil c'est la fonction setup qui sera appelée.
 * Loop ne sera donc jamais appelée.
 * ==================================================================*/
void loop() {

  //Cette instruction ne devrait jamais être executée
  Serial.println("Passage par fonction loop" );

}


Sonde BMP280 

Ce programme est utilisé pour piloter une sonde BMP280 et envoyer les données lue de température et de pression sur des capteurs virtuels domoticz.


/*******************************************************************
* Ce programme utilise la librairie Adafruit_BMP280.h pour lire la temperature
* et la pression depuis un capteur BMP280 et l'envoyer à domoticz.
* Le niveau de tension de l'alimentation est également suivi.
* Entre deux cycles le programme met l'ESP en deep sleep.
*
* Evolutions possible :
*   - Aucune
*
* Versions :
*   v1 - 06/10/2019 : Création
*   v2 - 09/10/2019 : Ajoute l'envoi des données à Domoticz
*   v2.1 - 19/10/2019 :
*     Prise d'une valeur médiane pour avoir plus de précision sur les mesures.
*     Choix des broches SDA et SCL pour le bus I2C
*
* (c) acampeaux@gmail.com
********************************************************************/

/*====================================================================
  * Constantes à modifier
 * ==================================================================*/
//Adresse Ip/port du serveur domoticz
#define DOMOTICZ_IP "192.168.43.151"
#define DOMOTICZ_PORT (8080)

//idx du device virtuel domoticz qui représente le capteur BMP280
#define DEVICE_IDX  17

//idx du device virtuel domoticz qui représente le suivi de tension
#define DEVICE_TENSION  18

//Coefficient pour lire la tension avec la fonction getVCC
//Il dépend du type d'ESP utilisé
#define COEF_GETVCC (1.42) //1.07 sur ESP01 et 1.42 sur ESP12
#define NB_VAL  11 //Nombre de mesures de la tension pour en faire une mediane
#define MESURE_DELAIS   100 //Delais en ms entre deux prises de mesures de tension

//Identifiants de connexion wifi
#define WIFI_ID "votre id wifi"
#define WIFI_PWD "mot de passe wifi"

//Temps d'attente entre chaque mesures en secondes. Doit être inférieur à 4294s (71 min)
#define DELAY_INTER_CYCLES (2*60)

//Pour compiler sans les traces sur la liaison série commenter cette ligne
#define DEBUG true

//Choix des broches pour le bus I2C. Par défaut nous avons SDA = GPIO4 et SCL = GPIO5
#define I2C_SDA_GPIO  12
#define I2C_SCL_GPIO  5

//I2C addresse: 0x76 ou 0x77 (0x77 est la valeur par défaut de la librairie)
//Si on défini 0x76 il faudra que SD0 soit connecté à GND, sinon VCC pour 0x77.
#define BMP280_I2C_ADDRESS  0x77


/*====================================================================
  * Inclusion des librairies externes
 * ==================================================================*/
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h> //Si BME280 mettre: #include <Adafruit_BME280.h>


/*====================================================================
 * Place le convertisseur ADC dans un mode de lecture de la tension interne.
 * Les broches analogiques doivent être libres et ne pourront être utilisées
 * ==================================================================*/
ADC_MODE(ADC_VCC);

/*====================================================================
  * Définition de constantes (#define) et des variables globales
 * ==================================================================*/
String stNomProgramme = "bmp280";
String stVersion = "v2.1";

//////////////////////////////variable relatives au composant BMx280
Adafruit_BMP280 bmp; //Si BME280 mettre: Adafruit_BME280 bme;


////////////////////////////////////////////////////constantes relatives à la connexion série
// Fixe la vitesse de communication série.
// Sur ESP8266 je mets 74880 car c'est la vitesse de communication du controleur au boot.
#define SERIAL_BIT_RATE 74880


//////////////////////////////////////////////////////Autres déclarations
// Temps d'attente en seconde pour se connecter au wifi
#define MAX_TIME_WAIT_WIFI 30

// SSID et identifiants de connexion au Wifi de votre routeur internet.
const char* sSsid = WIFI_ID;
const char* sPassword = WIFI_PWD;

////////////////////////////////////////////////////// Variables relatives à Domoticz
char *sDomoticzIp = DOMOTICZ_IP;
int iDomoticzPort = DOMOTICZ_PORT;
HTTPClient oDomoticzClient;
String osUrl = ""; //Pour construire l'URL JSON d'envoi des données à domoticz


/*====================================================================
  * Fonction setup(): executée une fois au démarrage du microcontroleur.
  * - Ouvre la liaison série.
 * ==================================================================*/
void setup() {

#ifdef DEBUG
  ///////////////////////////////////Initialisation de la liaison série
  int iCompteur = 0;
  String stMessageAccueil = "";

  Serial.begin(SERIAL_BIT_RATE);
  delay( 100 );

  //Attente initialisation Serial
  while( !Serial ) {
      delay( 100 );
      iCompteur++;
      if ( iCompteur >= 10 ) ESP.restart();
      }

  //Affiche message initial
  stMessageAccueil = stNomProgramme + " - Version: " + stVersion;
  Serial.println( stMessageAccueil );
  Serial.println( "Serial: OK" );
#endif

  ///////////////////////////////////Défini les broche SDA/SCL pour bus I2C
  Wire.begin( I2C_SDA_GPIO, I2C_SCL_GPIO );

  ///////////////////////////////////Initialisation et lecture du module BMP280
  if (!bmp.begin( BMP280_I2C_ADDRESS )){
#ifdef DEBUG   
    Serial.println( "Aucun capteur BMP280 valide: vérifiez le cablage!");
#endif 
    //On a pas réussi initialiser le capteur alors on passe en mode sommeil
    ESP.deepSleep( DELAY_INTER_CYCLES * 1000000, RF_DEFAULT );
  }


  float fTemp= bmp.readTemperature();
  float fPression = bmp.readPressure() / 100.00f;

#ifdef DEBUG
    Serial.print("Temperature = ");
    Serial.print( fTemp );
    Serial.print(" *C");
    Serial.print( " - " );
    Serial.print("Pression = ");
    Serial.print( fPression );
    Serial.println(" hPa");
#endif

  ////////////////////////////////////////////Connexion Wifi
  WiFi.begin( sSsid, sPassword );

#ifdef DEBUG
  Serial.printf( "Connecting WiFi: SSID=%s PWD=%s ", sSsid, sPassword  );
#endif

  int iNbSec = 0;
  while (WiFi.status() != WL_CONNECTED ){
    delay( 1000 );
    iNbSec++; 
#ifdef DEBUG   
    Serial.print( "." );
#endif   
    if ( iNbSec >= MAX_TIME_WAIT_WIFI ){
#ifdef DEBUG       
      Serial.printf( "\nDelais de connexion de %d secondes depasse -> sommeil\n", MAX_TIME_WAIT_WIFI );
#endif   
      // Si on dépasse le délais de connexion on se remet en sommeil
      ESP.deepSleep( DELAY_INTER_CYCLES * 1000000, RF_DEFAULT );         
    }
  }

#ifdef DEBUG
  Serial.printf( "\n-> Wifi connected in %d seconds\n", iNbSec ); 
  vCheckWifiStatus( WiFi.status(), WiFi.getMode() );
  String stRes = "Connected to : " + String( sSsid ) + "\nAdresse IP : ";
  stRes += WiFi.localIP().toString() ; //Type IPAddress = tableau de 4 entiers.
  Serial.println( stRes );
#endif


  ////////////////////////////////////////////Envoi des données à domoticz
  //construit l'URL d'appel
  osUrl = "/json.htm?type=command&param=udevice&idx=" + String( DEVICE_IDX ) +       \
    "&nvalue=0&svalue=" + String( fTemp ) + ";" + String( fPression ) + ";0;0";

#ifdef DEBUG
  Serial.println( osUrl );
#endif

  //////////////////// Appel domoticz pour mettre a jour le capteur
  int iResCallDomo = iEnvoiDomoticz( osUrl );
#ifdef DEBUG
  switch ( iResCallDomo ){
    case 0 :
      // L'appel de la fonction c'est bien passé
      Serial.println( "iEnvoiDomoticz OK" );
      break;
    default :
      // On a un code <> 0 donc il y a une erreur.
      // Les codes erreurs sont 1,2.
      Serial.println( "iEnvoiDomoticz NOK" );
      break;
  }
#endif

  //Construit Url de suivi de la tension
  float fTension = fGetTension();
  String osUrlTension = "/json.htm?type=command&param=udevice&idx=" + String( DEVICE_TENSION ) +  \
    "&nvalue=0&svalue=" + String( fTension );

#ifdef DEBUG
  Serial.println( osUrlTension );
#endif

  iResCallDomo = iEnvoiDomoticz( osUrlTension );
#ifdef DEBUG
  switch ( iResCallDomo ){
    case 0 :
      // L'appel de la fonction c'est bien passé
      Serial.println( "iEnvoiDomoticz OK" );
      break;
    default :
      // On a un code <> 0 donc il y a une erreur.
      // Les codes erreurs sont 1,2.
      Serial.println( "iEnvoiDomoticz NOK" );
      break;
  }
#endif

  ////////////////////////////////////////////Remise en sommeil
  //Sortie du sommeil seulement si GPIO16 connecté à RST.
  ESP.deepSleep( DELAY_INTER_CYCLES * 1000000, RF_DEFAULT );     
}


/*====================================================================
  * Fonction loop(): Nomralement pas executée ici
 * ==================================================================*/
void loop() {
#ifdef DEBUG
    Serial.println( "Loop" );
    delay( 1000 );
#endif
}


/*====================================================================
  * Fonction vCheckWifiStatus(iWifiStatus, iWifiM):
  * - Affiche un message texte comprehensible correspond
  *   au status et au mode du wifi
 * ==================================================================*/
void vCheckWifiStatus( int iWifiStatus, int iWifiMode ){

    //On va afficher un message fonction du status wifi
    Serial.printf("Connection status: %d\n", iWifiStatus );
    switch ( iWifiStatus ) {
      case WL_DISCONNECTED: { //Valeur 6
        Serial.printf("-> Wifi disconnected.\n" );break;}
      case WL_IDLE_STATUS: { //Valeur 0
        Serial.printf("-> Wifi in process of changing.\n" );break;}
      case WL_NO_SSID_AVAIL: { //Valeur 1
        Serial.printf("-> SSID (%s) cannot be reached.\n", sSsid );break;}
      case WL_CONNECTED: { //Valeur 3
        Serial.printf("-> Wifi connection is established.\n" );break;}   
      case WL_CONNECT_FAILED: { //Valeur 4
        Serial.printf("-> password (%s) is incorrect.\n", sPassword );break;}                 
      default: {
          Serial.printf("-> Status Wifi: inconnu (%d)\n", iWifiStatus );
        }
    }

    switch( iWifiMode) {
      case WIFI_OFF: { //Valeur 0
         Serial.println( "-> Mode Wifi OFF" );break;
      }
      case WIFI_STA: { //Valeur 1
        Serial.println( "-> Mode Wifi WIFI_STA" );break;
      }
      case WIFI_AP: { //Valeur 2
        Serial.println( "-> Mode Wifi WIFI_AP" ); break;
      }
      case WIFI_AP_STA: { //Valeur 3
        Serial.println( "->Mode Wifi WIFI_AP_STA" ); break;
      }
      default: { //Valeur inconnue
        Serial.printf( "Mode (%d) inconnu\n", iWifiMode );
      }
    }
  }


/*====================================================================
  * Fonction pour mettre a jour le capteur dans domoticz
 * ==================================================================*/
 int iEnvoiDomoticz( String osDomoUrl ){

  int iRes = 0;

  oDomoticzClient.begin( sDomoticzIp, iDomoticzPort, osDomoUrl );
  int iHttpCode = oDomoticzClient.GET();
  if ( iHttpCode ){
    if ( iHttpCode == 200 ){
#ifdef DEBUG   
      String osPayload = oDomoticzClient.getString();
      Serial.println( "Domoticz response" );
      Serial.println( osPayload );
#endif   
      iRes = 0;
    } else {
#ifdef DEBUG   
      String osPayload = oDomoticzClient.getString();
      Serial.println( "Domoticz response error " );
      Serial.println( osPayload );
      Serial.print( "Adresse IP ESP : " );
      Serial.println( WiFi.localIP().toString() );
      Serial.print( "iHttpCode : " );
      Serial.println( iHttpCode );
#endif   
      iRes = 1;
    }
  } else {
#ifdef DEBUG 
    Serial.println( "Domoticz not response" );
#endif 
    iRes = 2;
   }
  oDomoticzClient.end();

  return iRes;
 }

/*====================================================================
  * Fonction qui retourne une valeur médiane pour la tension
 * ==================================================================*/
 float fGetTension( ){
  int iConteur = 0;
  int tiMesures[ NB_VAL ];

  //////////// realise le nb de mesures demandees avec delais entre chaque
  for ( int iCompteur = 0; iCompteur < NB_VAL; iCompteur++ ){
    /* Lecture de la tension en ms */
    tiMesures[ iCompteur ] = ESP.getVcc();
    delay( MESURE_DELAIS );
  }

  /* Classe le tableau */
  for (int i = 0; i < NB_VAL; i++)
    for (int j = 0; j < NB_VAL; j++){
      if ( tiMesures[ i ] > tiMesures[ j ]){
        int iMem = tiMesures[ i ];
        tiMesures[ i ] = tiMesures[ j ];
        tiMesures[ j ] = iMem;
      }
    }

  /* recherche la médiane en prennant la valeur centrale du tableau*/
  int iMediane = tiMesures[ int( ( NB_VAL / 2.0 ) + 0.5 ) - 1 ];

  return (float( iMediane ) * COEF_GETVCC / 1000);
 }




Réinitialisation des fusibles ATTiny 

Ce programme repris de la page de Wayne Holder, permet à un Arduino de réinitialiser les fusibles d'un Attiny avec la méthode HVDC (High Voltage Serial Programmer).

Il est compatible avec les modèles ATtiny13/24/25/44/45/84/85.


// AVR High-voltage Serial Fuse Reprogrammer
// Adapted from code and design by Paul Willoughby 03/20/2010
//   http://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/
//
// Fuse Calc:
//   http://www.engbedded.com/fusecalc/

#define  RST     13    // Output to level shifter for !RESET from transistor
#define  SCI     12    // Target Clock Input
#define  SDO     11    // Target Data Output
#define  SII     10    // Target Instruction Input
#define  SDI      9    // Target Data Input
#define  VCC      8    // Target VCC

#define  HFUSE  0x747C
#define  LFUSE  0x646C
#define  EFUSE  0x666E

// Define ATTiny series signatures
#define  ATTINY13   0x9007  // L: 0x6A, H: 0xFF             8 pin
#define  ATTINY24   0x910B  // L: 0x62, H: 0xDF, E: 0xFF   14 pin
#define  ATTINY25   0x9108  // L: 0x62, H: 0xDF, E: 0xFF    8 pin
#define  ATTINY44   0x9207  // L: 0x62, H: 0xDF, E: 0xFFF  14 pin
#define  ATTINY45   0x9206  // L: 0x62, H: 0xDF, E: 0xFF    8 pin
#define  ATTINY84   0x930C  // L: 0x62, H: 0xDF, E: 0xFFF  14 pin
#define  ATTINY85   0x930B  // L: 0x62, H: 0xDF, E: 0xFF    8 pin

void setup() {
  pinMode(VCC, OUTPUT);
  pinMode(RST, OUTPUT);
  pinMode(SDI, OUTPUT);
  pinMode(SII, OUTPUT);
  pinMode(SCI, OUTPUT);
  pinMode(SDO, OUTPUT);     // Configured as input when in programming mode
  digitalWrite(RST, HIGH);  // Level shifter is inverting, this shuts off 12V
  Serial.begin(19200);
}

void loop() {
   if (Serial.available() > 0) {
    Serial.read();
    Serial.println("Loop Begin");
    pinMode(SDO, OUTPUT);     // Set SDO to output
    digitalWrite(SDI, LOW);
    digitalWrite(SII, LOW);
    digitalWrite(SDO, LOW);
    digitalWrite(RST, HIGH);  // 12v Off
    digitalWrite(VCC, HIGH);  // Vcc On
    delayMicroseconds(20);
    digitalWrite(RST, LOW);   // 12v On
    delayMicroseconds(10);
    pinMode(SDO, INPUT);      // Set SDO to input
    delayMicroseconds(300);
    unsigned int sig = readSignature();
    Serial.print("Signature is: ");
    Serial.println(sig, HEX);
    readFuses();
    if (sig == ATTINY13) {
      writeFuse(LFUSE, 0x6A);
      writeFuse(HFUSE, 0xFF);
    } else if (sig == ATTINY24 || sig == ATTINY44 || sig == ATTINY84 ||
               sig == ATTINY25 || sig == ATTINY45 || sig == ATTINY85) {
      writeFuse(LFUSE, 0x62);
      writeFuse(HFUSE, 0xDF);
      writeFuse(EFUSE, 0xFF);
    }
    readFuses();
    digitalWrite(SCI, LOW);
    digitalWrite(VCC, LOW);    // Vcc Off
    digitalWrite(RST, HIGH);   // 12v Off
  }
}

byte shiftOut (byte val1, byte val2) {
  int inBits = 0;
  //Wait until SDO goes high
  while (!digitalRead(SDO))
    ;
  unsigned int dout = (unsigned int) val1 << 2;
  unsigned int iout = (unsigned int) val2 << 2;
  for (int ii = 10; ii >= 0; ii--)  {
    digitalWrite(SDI, !!(dout & (1 << ii)));
    digitalWrite(SII, !!(iout & (1 << ii)));
    inBits <<= 1;
    inBits |= digitalRead(SDO);
    digitalWrite(SCI, HIGH);
    digitalWrite(SCI, LOW);
  }
  return inBits >> 2;
}

void writeFuse (unsigned int fuse, byte val) {
  shiftOut(0x40, 0x4C);
  shiftOut( val, 0x2C);
  shiftOut(0x00, (byte) (fuse >> 8));
  shiftOut(0x00, (byte) fuse);
}

void readFuses () {
  byte val;
        shiftOut(0x04, 0x4C);  // LFuse
        shiftOut(0x00, 0x68);
  val = shiftOut(0x00, 0x6C);
  Serial.print("LFuse: ");
  Serial.print(val, HEX);
        shiftOut(0x04, 0x4C);  // HFuse
        shiftOut(0x00, 0x7A);
  val = shiftOut(0x00, 0x7E);
  Serial.print(", HFuse: ");
  Serial.print(val, HEX);
        shiftOut(0x04, 0x4C);  // EFuse
        shiftOut(0x00, 0x6A);
  val = shiftOut(0x00, 0x6E);
  Serial.print(", EFuse: ");
  Serial.println(val, HEX);
}

unsigned int readSignature () {
  unsigned int sig = 0;
  byte val;
  for (int ii = 1; ii < 3; ii++) {
          shiftOut(0x08, 0x4C);
          shiftOut(  ii, 0x0C);
          shiftOut(0x00, 0x68);
    val = shiftOut(0x00, 0x6C);
    sig = (sig << 8) + val;
  }
  return sig;
}





Test SoftwareSerial pour attiny

Ce programme permet d'implémenter une communication série entre un moniteur série sur PC et un attiny. L’utilisateur peut envoyer des caractères à l'attiny. Si le mot "reset" est envoyé, le MCU réalise un reboot en utilisant le Watch Dog Timer (WDT). Ce programme permet une compilation conditionnelle en fonction de la librairie série que l'on souhaite utiliser.

L’intérêt est essentiellement à titre de l'apprentissage. 

/*******************************************************************
* Ce programme permet de tester le fonctionnement de la communication 
* série UART entre microcontroleurs ATTINY et PC. 
* Le programme se base sur une librairie externe pour gérer la liaison série.
* Il reçoit le texte envoyé par l'utilisateur sur la liaison série, le répète 
* pour montrer la bonne réception. Si le message est "reset" il effectue un "soft reset". 
* Versions : 
*   v1 : Création
*   v2 : Ajout affichage message d'attente tant qu'aucun caractère n'est 
*     reçu sur RX. 
*       Ajout pour exemple d'une autre méthode de reboot que par le WDT.
*       Ajout compilation condtionnelle de la librairie serial à utiliser.
*       
* (c) acampeaux@gmail.com
********************************************************************/

/*====================================================================
  * Définitions nécessaires pour compilation conditionnelle afin d'utiliser 
  * la librairie de gestion liaison série souhaitée. 
  * Donnez à SERIAL_LIBRARY la valeur:
  * - SOFTWARE_SERIAL pour compiler avec <SoftwareSerial.h>
  * - TINY_SERIAL pour compiler avec <TinySerial.h>
  * Toute autre valeur générera une erreur à la compilation
 * ==================================================================*/
#define SOFTWARE_SERIAL 0
#define TINY_SERIAL 1

//#define SERIAL_LIBRARY TINY_SERIAL
#define SERIAL_LIBRARY SOFTWARE_SERIAL

/*====================================================================
  * Inclusion des librairies externes 
 * ==================================================================*/
#if( SERIAL_LIBRARY == TINY_SERIAL )
  #include <TinySerial.h>
  #include <TinyTimer1Compare.h>
  #include <TinyPinChangeB.h>
#elif( SERIAL_LIBRARY == SOFTWARE_SERIAL )
  #include <SoftwareSerial.h>
#else
  #error "SERIAL_LIBRARY: valeur incorrecte"
#endif

#include <avr/wdt.h>

/*====================================================================
  * Définition de constantes (#define) et des variables globales
 * ==================================================================*/
#define LED PB4     // Broche pour mettre une LED de test sur l'attiny
#define DELAI 500  // Temporisation 
#define DELAY_RAPPEL 2000 //délais au bout duquel on rappel d'envoyer un message

// Reprend les fonctionnalité de Serial d'Arduino
#define RX  PB2 // Si on ne souhaite pas utiliser RX on peut mettre la valeur -1
#define TX  PB1 // Si on ne souhaite pas utiliser TX on peut mettre la valeur -1
#define BAUDS_RATE 19200

#if( SERIAL_LIBRARY == TINY_SERIAL )
  TinySerial serial(RX, TX, Timer1Compare, PinChangeB);
#elif( SERIAL_LIBRARY == SOFTWARE_SERIAL )
  SoftwareSerial serial(RX,TX);
#else
  #error "Variable serial: mauvaise librairie utilisée."
#endif

#define TAILLE_BUFFER 16  //16 caractères max 
char le_buffer[ TAILLE_BUFFER ] = "\0";

/*====================================================================
  * Fonction soft_reboot(): Permet de faire un reset logiciel
  * - Active le watch dog timer (wdt)
  * - rentre dans une boucle sans fin pour attendre le reset de la part du wdt.
 * ==================================================================*/
void soft_reboot(){
  serial.println( F( "soft_reboot: reset par appel du Watch Dog Timer" ) );
  wdt_enable(WDTO_15MS);
  while(1){}; //attente que le WDT se déclanche. 
}

/*====================================================================
  * Fonction softReset(): Permet de faire un reset logiciel
  * - Quand elle est appelée le programme est repris à partir de l'adresse 0
 * ==================================================================*/
void (*soft_reboot) (void) = 0;

/*====================================================================
  * Fonction setup(): executée une fois au démarrage du microcontroleur.
  * - Ouvre la liaison série.
  * - Fait clignoter le LED pour valider visuellement le bon fonctionnement.
 * ==================================================================*/
void setup() {
  
  // Désactive le watch dog timer au cas ou il avait été activé. 
  cli();                  // empeche les interruption
  MCUSR &= ~(1<<WDRF);    // Remet à zero le "reset flag" 
                          // MCUSR est le registre d'état du MCU. WDRF est le bit qui indique si le reset provient du WD
  wdt_disable();          // desactive wdt
  sei();    // Autorise à nouveau les interruptions. 
  
  // Initialisation de la liaison série
  serial.begin( BAUDS_RATE );
  
  //La macro F() permet d'économiser la mémoire SRAM en plaçant les chaine dans la mémoire flash
  serial.print( F( "\nSetup: serial begin OK: " ) );
  serial.print ( BAUDS_RATE );
  serial.println( F( " bauds" ) );
  serial.println( F( "(Entrez le mot \"reset\" pour reseter le MCU.)\n" ) );
  
  // Initialisation de la broche associée à la LED pour témoignage visuel du bon fonctionnement.
  pinMode(LED, OUTPUT);

  // Fait clignoter la LED pour montrer le bon fonctionnement du setup
  for (int iTeration = 0; iTeration <= 20; iTeration++){
  digitalWrite(LED, HIGH);    
  delay( 50 );               
  digitalWrite(LED, LOW);
  delay( 50 );  
  }
}

/*====================================================================
  * Fonction loop(): executée en boucle.
  * - Lit les caracteres sur la liaison série et rempli le buffer
  * - Affiche le message reçu. 
  * - Detecte si reset demandé: si oui alors reset.
  * - Attends un délai donné (extinction LED).
 * ==================================================================*/
void loop() {
  // Allume la LED 
  digitalWrite(LED, HIGH);  

  // Envoie un message toutes les "DELAY_RAPPEL" secondes s'il n'y rien de reçu
  if ( !serial.available() ){
    unsigned long startMilli = millis();
    while ( !serial.available() ){
      unsigned long currentMilli = millis();
      if ( (currentMilli - startMilli ) >= DELAY_RAPPEL ){
          serial.println( F( "(Entrez des caracteres (le mot \"reset\" va reseter le MCU.)" ) );
          break;
      }
    }
   }
  
  // Lecture et affichage du caractère si présent sur serial.
  if (serial.available()) {
    uint8_t index = 0;
    le_buffer[ 0 ] = '\0';

    while ( ( serial.available()> 0 ) && ( index <= ( TAILLE_BUFFER - 2 ) ) ){
      le_buffer[ index++ ] = serial.read();

#if( SERIAL_LIBRARY == TINY_SERIAL )
      delay( 5 ); // temporisation nécessaire si on utilise TinySerial.
#endif       
    }
    
    if ( ( le_buffer[ index - 1 ] == '\n' ) || ( le_buffer[ index - 1 ] == '\r' ) ) index--;
    le_buffer[ index ] = '\0';
  
    serial.print(F("Received: ") ); 
    serial.print( le_buffer );
    serial.print( F(" ( nombre de caracteres lus = ") );
    serial.print( index );
    serial.println( F(" )"));
    
    // Si on a reçu un "reset" on réalise un reset.
    if ( !strcmp( le_buffer, "reset" ) ) {
      serial.println( F("Reset reçu!") );
      soft_reboot();
    } else {
       serial.println( F("Ce n'est pas un reset!") );
      }
  }
  
  // Attend et éteint la LED
  delay( DELAI / 2 );
  digitalWrite(LED, LOW);
  delay( DELAI / 2 );
}





X10_et_serial_test_switch-v1 pour attiny

Ce programme est une adaptation du précédent. Il permet à l'utilisateur de saisir des commande dans le moniteur série. Si cette commande est "on" ou "off" alors l'ordre correspondant est envoyé à un interrupteur X10 dont les identifiants sont dans le programme.
Le programme s’appuie sur la librairie x10rf et il est développé pour un attiny85 (compatible attiny45 et Arduino avec adaptation des identifiants des broches).
Le montage électronique correspondant est donné dans le paragraphe 3.2.2 de l'article ICI.

/*******************************************************************
* Ce programme permet de tester le fonctionnement de la communication
* série UART entre microcontroleurs ATTINY et PC et envois ON/OFF a un switch en RX433.
* Le protocole utilisé est X10.
* Le programme se base sur une librairie externe pour gérer la liaison série.
* Il reçoit le texte envoyé par l'utilisateur sur la liaison série, le répète
* pour montrer la bonne réception. Si le message est "reset" il effectue un "soft reset".
*
* Versions :
*   v1 : Création
*   v2 : Ajout affichage message d'attente tant qu'aucun caractère n'est
*     reçu sur RX.
*       Ajout pour exemple d'une autre méthode de reboot que par le WDT.
*       Ajout compilation condtionnelle de la librairie serial à utiliser.
*   v3 : Gestion des commandes ON/OFF en lecture de serial.
*     Envois ordres ON ou OFF à un swicth X10 en RF433.
*     
* (c) acampeaux@gmail.com
********************************************************************/
#include <x10rf.h>
#include <avr/wdt.h>

/*====================================================================
  * Définitions nécessaires pour compilation conditionnelle afin d'utiliser
  * la librairie de gestion liaison série souhaitée.
  * Donnez à SERIAL_LIBRARY la valeur:
  * - SOFTWARE_SERIAL pour compiler avec <SoftwareSerial.h>
  * - TINY_SERIAL pour compiler avec <TinySerial.h>
  * Toute autre valeur générera une erreur à la compilation
 * ==================================================================*/
#define SOFTWARE_SERIAL 0
#define TINY_SERIAL 1

//#define SERIAL_LIBRARY TINY_SERIAL
#define RX  PB0 // Si on ne souhaite pas utiliser RX on peut mettre la valeur -1
#define TX  PB1 // Si on ne souhaite pas utiliser TX on peut mettre la valeur -1
#define SERIAL_LIBRARY SOFTWARE_SERIAL

/*====================================================================
  * Inclusion des librairies externes
 * ==================================================================*/
#if( SERIAL_LIBRARY == TINY_SERIAL )
  #include <TinySerial.h>
  #include <TinyTimer1Compare.h>
  #include <TinyPinChangeB.h>
#elif( SERIAL_LIBRARY == SOFTWARE_SERIAL )
  #include <SoftwareSerial.h>
#else
  #error "SERIAL_LIBRARY: valeur incorrecte"
#endif


/*====================================================================
  * Définition et constantes relatives à l'envoi en RF433
 * ==================================================================*/
#define X10_TX PB3          // Pin pour la transmission 433mhz
#define X10_REPETITIONS 3   // Nombre de répétition de l'envoi de la donnée en RF
#define X10_LED PB2         // LED de controle qui s'alume lors de l'envois en RF433.
x10rf myx10 = x10rf( X10_TX, X10_LED, X10_REPETITIONS);


/*====================================================================
  * Définition de constantes et des variables pour le SWITCH domoticz
 * ==================================================================*/
 byte switch_state = ON;
 const char MAISON = 'A';
 const byte UNITE = 7;


/*====================================================================
  * Définition de constantes (#define) et des variables globales
 * ==================================================================*/
#define LED PB4     // Broche pour mettre une LED de test sur l'attiny
#define DELAI 500  // Temporisation
#define DELAY_RAPPEL 5000 //délais au bout duquel on rappel d'envoyer un message

// Vitesse communication serie
#define BAUDS_RATE 19200

#if( SERIAL_LIBRARY == TINY_SERIAL )
  TinySerial serial(RX, TX, Timer1Compare, PinChangeB);
#elif( SERIAL_LIBRARY == SOFTWARE_SERIAL )
  SoftwareSerial serial(RX,TX);
#else
  #error "Variable serial: mauvaise librairie utilisée."
#endif

#define TAILLE_BUFFER 16  //16 caractères max
char le_buffer[ TAILLE_BUFFER ] = "\0";

/*====================================================================
  * Fonction soft_reboot(): Permet de faire un reset logiciel
  * - Active le watch dog timer (wdt)
  * - rentre dans une boucle sans fin pour attendre le reset de la part du wdt.
 * ==================================================================*/
void soft_reboot(){
  serial.println( F( "soft_reboot: reset par appel du Watch Dog Timer" ) );
  wdt_enable(WDTO_15MS);
  while(1){}; //attente que le WDT se déclanche.
}

/*====================================================================
  * Fonction softReset(): Permet de faire un reset logiciel
  * - Quand elle est appelée le programme est repris à partir de l'adresse 0
 * ==================================================================*/
//void (*soft_reboot) (void) = 0;

/*====================================================================
  * Fonction setup(): executée une fois au démarrage du microcontroleur.
  * - Ouvre la liaison série.
  * - Fait clignoter le LED pour valider visuellement le bon fonctionnement.
 * ==================================================================*/
void setup() {

  // Désactive le watch dog timer au cas ou il avait été activé.
  cli();                  // empeche les interruption
  MCUSR &= ~(1<<WDRF);    // Remet à zero le "reset flag"
                          // MCUSR est le registre d'état du MCU. WDRF est le bit qui indique si le reset provient du WD
  wdt_disable();          // desactive wdt
  sei();    // Autorise à nouveau les interruptions.

  // Initialisation de la liaison série
  serial.begin( BAUDS_RATE );

  //La macro F() permet d'économiser la mémoire SRAM en plaçant les chaine dans la mémoire flash
  serial.print( F( "\nSetup: serial begin OK: " ) );
  serial.print ( BAUDS_RATE );
  serial.println( F( " bauds" ) );
  serial.println( F( "Vous pouvez envoyer du texte ou une commande!\n" ) );


  // Initialisation de l'emmetteur RF433
  myx10.begin();

  // Initialisation de la broche associée à la LED pour témoignage visuel du bon fonctionnement.
  pinMode(LED, OUTPUT);

  // Fait clignoter la LED pour montrer le bon fonctionnement du setup
  for (int iTeration = 0; iTeration <= 20; iTeration++){
  digitalWrite(LED, HIGH); 
  delay( 50 );             
  digitalWrite(LED, LOW);
  delay( 50 );
  }
}

/*====================================================================
  * Fonction loop(): executée en boucle.
  * - Lit les caracteres sur la liaison série et rempli le buffer
  * - Affiche le message reçu.
  * - Detecte si reset demandé: si oui alors reset.
  * - Attends un délai donné (extinction LED).
 * ==================================================================*/
void loop() {
  // Allume la LED
  digitalWrite(LED, HIGH);

  // Envoie un message toutes les "DELAY_RAPPEL" secondes s'il n'y rien de reçu
  if ( !serial.available() ){
    unsigned long startMilli = millis();
    while ( !serial.available() ){
      unsigned long currentMilli = millis();
      if ( (currentMilli - startMilli ) >= DELAY_RAPPEL ){
          serial.println( F( "Rappel des commandes disponibles: RESET|ON|OFF" ) );
          serial.print( F( "\tON: Action ON sur le switch ") ); serial.print( MAISON ); serial.println( UNITE );
          serial.print( F( "\tOFF: Action OFF sur le switch ")); serial.print( MAISON ); serial.println( UNITE );
          serial.println( F( "\tRESET: Provoque un reset logiciel du microntroleur") );
          break;
      }
    }
   }

  // Lecture et affichage du caractère si présent sur serial.
  if (serial.available()) {
    uint8_t index = 0;
    le_buffer[ 0 ] = '\0';

    while ( ( serial.available()> 0 ) && ( index <= ( TAILLE_BUFFER - 2 ) ) ){
      le_buffer[ index++ ] = serial.read();

#if( SERIAL_LIBRARY == TINY_SERIAL )
      delay( 5 ); // temporisation nécessaire si on utilise TinySerial.
#endif     
    }
 
    if ( ( le_buffer[ index - 1 ] == '\n' ) || ( le_buffer[ index - 1 ] == '\r' ) ) index--;
    le_buffer[ index ] = '\0';

    serial.print(F("Received: ") );
    serial.print( le_buffer );
    serial.print( F(" ( nombre de caracteres lus = ") );
    serial.print( index );
    serial.println( F(" )"));
 
    // Si on a reçu un "reset" on réalise un reset.
    if ( !strcmp( le_buffer, "reset" ) ) {
      serial.println( F("reset") );
      soft_reboot();
    } else if ( !strcmp( le_buffer, "on" ) ){
       serial.println( F("switch ON") );
       myx10.x10Switch( MAISON , UNITE, ON); // Switch on
    } else if ( !strcmp( le_buffer, "off" ) ){
       serial.println( F("switch OFF") );
       myx10.x10Switch(MAISON , UNITE, OFF); // Switch on
    } else {
       serial.println( F("Instruction inconnue!") );
    }
  }

  // Attend et éteint la LED
  delay( DELAI / 2 );
  digitalWrite(LED, LOW);
  delay( DELAI / 2 );
}



To be continued ...






Soutenez la blogoculture ...

Le plus simplement du monde, si vous avez un achat à faire sur Amazon, accédez au site à partir de ce lien (que vous pouvez ajouter dans vos favoris)https://amzn.to/2nbe4sm



Merci

Commentaires