Les Carnets de Byfeel domotique , Objets connectés , DIY , Programmation, Nouvelles Technologies ….

DIY Horloge et Notification ( V2 ) – interaction avec Jeedom

Suite à mon précédent article ( que vous pouvez retrouver ici ) , et aux remontées d’infos de DCjona et Thyer voici une nouvelle version de cette horloge / Notification connectée.

  1.  Correction du bug OTA
  2. Ajout du support des caractères étendus
  3. Ajout d’un bouton et gestion des clics.
  4. Quelques nouveautés

Mise en jour en 2.34 ( gestion luminosité et divers correctifs ) – 04/06/2018

Cet article  fait suite à une série , sur la programmation et la mise en place d’une horloge connectée à l’aide d’un WEMOS .

Je vous conseille néanmoins de lire les articles suivants, avant de commencer par celui-ci  :

Correction Bug OTA

DCjona et Thyer , m’ont indiqué tous deux , une problématique avec la partie OTA , dans mon script.

ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH)
      type = "sketch";
    else // U_SPIFFS
      type = "filesystem";
    digitalWrite(led, HIGH); // allume led au debut du transfert
      P.print("Update ..."); 
    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    //Serial.println("Start updating " + type);
  });

Cette partie a été mise en place dans le cas où je serais amené à flasher l’espace file system ( SPIFFS ) au lieu du sketch , cela indique simplement si la mise à jour est un sketch ou si elle se situe dans le File System.

Pour info , ci dessous une illustration de la répartition de la mémoire

|--------------|-------|---------------|--|--|--|--|--|
^               ^       ^                ^       ^
Sketch    OTA update   File system   EEPROM  WiFi config (SDK)

La taille dépend des différentes cartes existantes  dans le commerce.

Carte ESP8266Flash chip size, bytesFile system size, bytes
Generic module512k64k
Generic module1M64k, 128k, 256k, 512k
Generic module2M1M
Generic module4M3M
Adafruit HUZZAH4M1M, 3M
NodeMCU 0.94M1M, 3M
NodeMCU 1.04M1M, 3M
Olimex MOD-WIFI-ESP8266(-DEV)2M1M
SparkFun Thing512k64k
SweetPea ESP-2104M1M, 3M
WeMos D1 & D1 mini4M1M, 3M

Il faut voir l’espace FileSystem ( SPIFFS ) comme une sorte de carte SD interne , dans laquelle il est possible de venir écrire ou lire des données qui resteraient en mémoire même après l’extinction du module.

Finalement  , pour ce module , seul le sketch est mis à jour . J’ai donc simplifié cette partie ( qui bug , chez certains de mes lecteurs ) par cette version :

 ArduinoOTA.onStart([]() {
    digitalWrite(led, HIGH); // allume led au debut du transfert
      P.print("Update ..."); 
  });

Pour que la partie OTA fonctionne , il est impératif que le module soit démarré , avant de lancer l’interface IDE Arduino. Après le premier flashage ( en USB ) , redémarrer votre interface IDE , afin que le port OTA apparaisse dans la liste des ports disponibles.

[adsense]

Caractères accentués et caractères spéciaux

Afin de prendre en compte , les caractéres accentués ainsi que les caractéres spéciaux , il faut ajouter le fichier suivant « Parola_Fonts_data.h » , dans le dossier de votre sketch. Ce fichier se trouve dans le dossier libraries de votre logiciel ide arduino.

arduino/libraries/MD_Parola/examples/Parola_UTF8_display

Ci dessous , le contenu du fichier :

// Data file for UTF-8 example user defined fonts
#ifndef FONTS_DATA_H
#define FONTS_DATA_H

MD_MAX72XX::fontType_t ExtASCII[] PROGMEM =
{
  0,		// 0 - 'Unused'
  0,		// 1 - 'Unused'
  0,		// 2 - 'Unused'
  0,		// 3 - 'Unused'
  0,		// 4 - 'Unused'
  0,		// 5 - 'Unused'
  0,		// 6 - 'Unused'
  0,		// 7 - 'Unused'
  0,		// 8 - 'Unused'
  0,		// 9 - 'Unused'
  0,		// 10 - 'Unused'
  0,		// 11 - 'Unused'
  0,		// 12 - 'Unused'
  0,		// 13 - 'Unused'
  0,		// 14 - 'Unused'
  0,		// 15 - 'Unused'
  0,		// 16 - 'Unused'
  0,		// 17 - 'Unused'
  0,		// 18 - 'Unused'
  0,		// 19 - 'Unused'
  0,		// 20 - 'Unused'
  0,		// 21 - 'Unused'
  0,		// 22 - 'Unused'
  0,		// 23 - 'Unused'
  0,		// 24 - 'Unused'
  0,		// 25 - 'Unused'
  0,		// 26 - 'Unused'
  0,		// 27 - 'Unused'
  0,		// 28 - 'Unused'
  0,		// 29 - 'Unused'
  0,		// 30 - 'Unused'
  0,		// 31 - 'Unused'
  2, 0, 0,		// 32 - 'Space'
  1, 95,		// 33 - '!'
  3, 7, 0, 7,		// 34 - '"'
  5, 20, 127, 20, 127, 20,		// 35 - '#'
  5, 36, 42, 127, 42, 18,		// 36 - '$'
  5, 35, 19, 8, 100, 98,		// 37 - '%'
  5, 54, 73, 86, 32, 80,		// 38 - '&'
  2, 4, 3,		// 39
  3, 28, 34, 65,		// 40 - '('
  3, 65, 34, 28,		// 41 - ')'
  5, 42, 28, 127, 28, 42,		// 42 - '*'
  5, 8, 8, 62, 8, 8,		// 43 - '+'
  2, 128, 96,		// 44 - ','
  5, 8, 8, 8, 8, 8,		// 45 - '-'
  2, 96, 96,		// 46 - '.'
  5, 32, 16, 8, 4, 2,		// 47 - '/'
  5, 62, 81, 73, 69, 62,		// 48 - '0'
  3, 66, 127, 64,		// 49 - '1'
  5, 114, 73, 73, 73, 70,		// 50 - '2'
  5, 33, 65, 73, 77, 51,		// 51 - '3'
  5, 24, 20, 18, 127, 16,		// 52 - '4'
  5, 39, 69, 69, 69, 57,		// 53 - '5'
  5, 60, 74, 73, 73, 49,		// 54 - '6'
  5, 65, 33, 17, 9, 7,		// 55 - '7'
  5, 54, 73, 73, 73, 54,		// 56 - '8'
  5, 70, 73, 73, 41, 30,		// 57 - '9'
  1, 20,		// 58 - ':'
  2, 128, 104,		// 59 - ';'
  4, 8, 20, 34, 65,		// 60 - '<'
  5, 20, 20, 20, 20, 20,		// 61 - '='
  4, 65, 34, 20, 8,		// 62 - '>'
  5, 2, 1, 89, 9, 6,		// 63 - '?'
  5, 62, 65, 93, 89, 78,		// 64 - '@'
  5, 124, 18, 17, 18, 124,		// 65 - 'A'
  5, 127, 73, 73, 73, 54,		// 66 - 'B'
  5, 62, 65, 65, 65, 34,		// 67 - 'C'
  5, 127, 65, 65, 65, 62,		// 68 - 'D'
  5, 127, 73, 73, 73, 65,		// 69 - 'E'
  5, 127, 9, 9, 9, 1,		// 70 - 'F'
  5, 62, 65, 65, 81, 115,		// 71 - 'G'
  5, 127, 8, 8, 8, 127,		// 72 - 'H'
  3, 65, 127, 65,		// 73 - 'I'
  5, 32, 64, 65, 63, 1,		// 74 - 'J'
  5, 127, 8, 20, 34, 65,		// 75 - 'K'
  5, 127, 64, 64, 64, 64,		// 76 - 'L'
  5, 127, 2, 28, 2, 127,		// 77 - 'M'
  5, 127, 4, 8, 16, 127,		// 78 - 'N'
  5, 62, 65, 65, 65, 62,		// 79 - 'O'
  5, 127, 9, 9, 9, 6,		// 80 - 'P'
  5, 62, 65, 81, 33, 94,		// 81 - 'Q'
  5, 127, 9, 25, 41, 70,		// 82 - 'R'
  5, 38, 73, 73, 73, 50,		// 83 - 'S'
  5, 3, 1, 127, 1, 3,		// 84 - 'T'
  5, 63, 64, 64, 64, 63,		// 85 - 'U'
  5, 31, 32, 64, 32, 31,		// 86 - 'V'
  5, 63, 64, 56, 64, 63,		// 87 - 'W'
  5, 99, 20, 8, 20, 99,		// 88 - 'X'
  5, 3, 4, 120, 4, 3,		// 89 - 'Y'
  5, 97, 89, 73, 77, 67,		// 90 - 'Z'
  3, 127, 65, 65,		// 91 - '['
  5, 2, 4, 8, 16, 32,		// 92 - '\'
  3, 65, 65, 127,		// 93 - ']'
  5, 4, 2, 1, 2, 4,		// 94 - '^'
  5, 64, 64, 64, 64, 64,		// 95 - '_'
  2, 3, 4,		// 96 - '`'
  5, 32, 84, 84, 120, 64,		// 97 - 'a'
  5, 127, 40, 68, 68, 56,		// 98 - 'b'
  5, 56, 68, 68, 68, 40,		// 99 - 'c'
  5, 56, 68, 68, 40, 127,		// 100 - 'd'
  5, 56, 84, 84, 84, 24,		// 101 - 'e'
  4, 8, 126, 9, 2,		// 102 - 'f'
  5, 24, 164, 164, 156, 120,		// 103 - 'g'
  5, 127, 8, 4, 4, 120,		// 104 - 'h'
  3, 68, 125, 64,		// 105 - 'i'
  4, 64, 128, 128, 122,		// 106 - 'j'
  4, 127, 16, 40, 68,		// 107 - 'k'
  3, 65, 127, 64,		// 108 - 'l'
  5, 124, 4, 120, 4, 120,		// 109 - 'm'
  5, 124, 8, 4, 4, 120,		// 110 - 'n'
  5, 56, 68, 68, 68, 56,		// 111 - 'o'
  5, 252, 24, 36, 36, 24,		// 112 - 'p'
  5, 24, 36, 36, 24, 252,		// 113 - 'q'
  5, 124, 8, 4, 4, 8,		// 114 - 'r'
  5, 72, 84, 84, 84, 36,		// 115 - 's'
  4, 4, 63, 68, 36,		// 116 - 't'
  5, 60, 64, 64, 32, 124,		// 117 - 'u'
  5, 28, 32, 64, 32, 28,		// 118 - 'v'
  5, 60, 64, 48, 64, 60,		// 119 - 'w'
  5, 68, 40, 16, 40, 68,		// 120 - 'x'
  5, 76, 144, 144, 144, 124,		// 121 - 'y'
  5, 68, 100, 84, 76, 68,		// 122 - 'z'
  3, 8, 54, 65,		// 123 - '{'
  1, 119,		// 124 - '|'
  3, 65, 54, 8,		// 125 - '}'
  5, 2, 1, 2, 4, 2,		// 126 - '~'
  0,		// 127 - 'Unused'
  6, 20, 62, 85, 85, 65, 34,		// 128 - 'Euro sign'
  0,		// 129 - 'Not used'
  2, 128, 96,		// 130 - 'Single low 9 quotation mark'
  5, 192, 136, 126, 9, 3,		// 131 - 'f with hook'
  4, 128, 96, 128, 96,		// 132 - 'Single low 9 quotation mark'
  8, 96, 96, 0, 96, 96, 0, 96, 96,		// 133 - 'Horizontal ellipsis'
  3, 4, 126, 4,		// 134 - 'Dagger'
  3, 20, 126, 20,		// 135 - 'Double dagger'
  4, 2, 1, 1, 2,		// 136 - 'Modifier circumflex'
  7, 35, 19, 104, 100, 2, 97, 96,		// 137 - 'Per mille sign'
  5, 72, 85, 86, 85, 36,		// 138 - 'S with caron'
  3, 8, 20, 34,		// 139 - '< quotation'
  6, 62, 65, 65, 127, 73, 73,		// 140 - 'OE'
  0,		// 141 - 'Not used'
  5, 68, 101, 86, 77, 68,		// 142 - 'z with caron'
  0,		// 143 - 'Not used'
  0,		// 144 - 'Not used'
  2, 3, 4,		// 145 - 'Left single quote mark'
  2, 4, 3,		// 146 - 'Right single quote mark'
  4, 3, 4, 3, 4,		// 147 - 'Left double quote marks'
  4, 4, 3, 4, 3,		// 148 - 'Right double quote marks'
  4, 0, 24, 60, 24,		// 149 - 'Bullet Point'
  3, 8, 8, 8,		// 150 - 'En dash'
  5, 8, 8, 8, 8, 8,		// 151 - 'Em dash'
  4, 2, 1, 2, 1,		// 152 - 'Small ~'
  7, 1, 15, 1, 0, 15, 2, 15,		// 153 - 'TM'
  5, 72, 85, 86, 85, 36,		// 154 - 's with caron'
  3, 34, 20, 8,		// 155 - '> quotation'
  7, 56, 68, 68, 124, 84, 84, 8,		// 156 - 'oe'
  0,		// 157 - 'Not used'
  5, 68, 101, 86, 77, 68,		// 158 - 'z with caron'
  5, 12, 17, 96, 17, 12,		// 159 - 'Y diaresis'
  2, 0, 0,		// 160 - 'Non-breaking space'
  1, 125,		// 161 - 'Inverted !'
  5, 60, 36, 126, 36, 36,		// 162 - 'Cent sign'
  5, 72, 126, 73, 65, 102,		// 163 - 'Pound sign'
  5, 34, 28, 20, 28, 34,		// 164 - 'Currency sign'
  5, 43, 47, 252, 47, 43,		// 165 - 'Yen'
  1, 119,		// 166 - '|'
  4, 102, 137, 149, 106,		// 167 - 'Section sign'
  3, 1, 0, 1,		// 168 - 'Spacing diaresis'
  7, 62, 65, 93, 85, 85, 65, 62,		// 169 - 'Copyright'
  3, 13, 13, 15,		// 170 - 'Feminine Ordinal Ind.'
  5, 8, 20, 42, 20, 34,		// 171 - '<<'
  5, 8, 8, 8, 8, 56,		// 172 - 'Not sign'
  0,		// 173 - 'Soft Hyphen'
  7, 62, 65, 127, 75, 117, 65, 62,		// 174 - 'Registered Trademark'
  5, 1, 1, 1, 1, 1,		// 175 - 'Spacing Macron Overline'
  3, 2, 5, 2,		// 176 - 'Degree'
  5, 68, 68, 95, 68, 68,		// 177 - '+/-'
  3, 25, 21, 19,		// 178 - 'Superscript 2'
  3, 17, 21, 31,		// 179 - 'Superscript 3'
  2, 2, 1,		// 180 - 'Acute accent'
  4, 252, 64, 64, 60,		// 181 - 'micro (mu)'
  5, 6, 9, 127, 1, 127,		// 182 - 'Paragraph Mark'
  2, 24, 24,		// 183 - 'Middle Dot'
  3, 128, 128, 96,		// 184 - 'Spacing sedilla'
  2, 2, 31,		// 185 - 'Superscript 1'
  4, 6, 9, 9, 6,		// 186 - 'Masculine Ordinal Ind.'
  5, 34, 20, 42, 20, 8,		// 187 - '>>'
  6, 64, 47, 16, 40, 52, 250,		// 188 - '1/4'
  6, 64, 47, 16, 200, 172, 186,		// 189 - '1/2'
  6, 85, 53, 31, 40, 52, 250,		// 190 - '3/4'
  5, 48, 72, 77, 64, 32,		// 191 - 'Inverted ?'
  5, 120, 20, 21, 22, 120,		// 192 - 'A grave'
  5, 120, 22, 21, 20, 120,		// 193 - 'A acute'
  5, 122, 21, 21, 21, 122,		// 194 - 'A circumflex'
  5, 120, 22, 21, 22, 121,		// 195 - 'A tilde'
  5, 120, 21, 20, 21, 120,		// 196 - 'A diaresis'
  5, 120, 20, 21, 20, 120,		// 197 - 'A ring above'
  6, 124, 10, 9, 127, 73, 73,		// 198 - 'AE'
  5, 30, 161, 161, 97, 18,		// 199 - 'C sedilla'
  4, 124, 85, 86, 68,		// 200 - 'E grave'
  4, 124, 86, 85, 68,		// 201 - 'E acute'
  4, 126, 85, 85, 70,		// 202 - 'E circumflex'
  4, 124, 85, 84, 69,		// 203 - 'E diaresis'
  3, 68, 125, 70,		// 204 - 'I grave'
  3, 68, 126, 69,		// 205 - 'I acute'
  3, 70, 125, 70,		// 206 - 'I circumplex'
  3, 69, 124, 69,		// 207 - 'I diaresis'
  6, 4, 127, 69, 65, 65, 62,		// 208 - 'Capital Eth'
  5, 124, 10, 17, 34, 125,		// 209 - 'N tilde'
  5, 56, 68, 69, 70, 56,		// 210 - 'O grave'
  5, 56, 70, 69, 68, 56,		// 211 - 'O acute'
  5, 58, 69, 69, 69, 58,		// 212 - 'O circumflex'
  5, 56, 70, 69, 70, 57,		// 213 - 'O tilde'
  5, 56, 69, 68, 69, 56,		// 214 - 'O diaresis'
  5, 34, 20, 8, 20, 34,		// 215 - 'Multiplication sign'
  7, 64, 62, 81, 73, 69, 62, 1,		// 216 - 'O slashed'
  5, 60, 65, 66, 64, 60,		// 217 - 'U grave'
  5, 60, 64, 66, 65, 60,		// 218 - 'U acute'
  5, 58, 65, 65, 65, 58,		// 219 - 'U circumflex'
  5, 60, 65, 64, 65, 60,		// 220 - 'U diaresis'
  5, 12, 16, 98, 17, 12,		// 221 - 'Y acute'
  4, 127, 18, 18, 12,		// 222 - 'Capital thorn'
  4, 254, 37, 37, 26,		// 223 - 'Small letter sharp S'
  5, 32, 84, 85, 122, 64,		// 224 - 'a grave'
  5, 32, 84, 86, 121, 64,		// 225 - 'a acute'
  5, 34, 85, 85, 121, 66,		// 226 - 'a circumflex'
  5, 32, 86, 85, 122, 65,		// 227 - 'a tilde'
  5, 32, 85, 84, 121, 64,		// 228 - 'a diaresis'
  5, 32, 84, 85, 120, 64,		// 229 - 'a ring above'
  7, 32, 84, 84, 124, 84, 84, 8,		// 230 - 'ae'
  5, 24, 36, 164, 228, 40,		// 231 - 'c sedilla'
  5, 56, 84, 85, 86, 88,		// 232 - 'e grave'
  5, 56, 84, 86, 85, 88,		// 233 - 'e acute'
  5, 58, 85, 85, 85, 90,		// 234 - 'e circumflex'
  5, 56, 85, 84, 85, 88,		// 235 - 'e diaresis'
  3, 68, 125, 66,		// 236 - 'i grave'
  3, 68, 126, 65,		// 237 - 'i acute'
  3, 70, 125, 66,		// 238 - 'i circumflex'
  3, 69, 124, 65,		// 239 - 'i diaresis'
  4, 48, 75, 74, 61,		// 240 - 'Small eth'
  4, 122, 9, 10, 113,		// 241 - 'n tilde'
  5, 56, 68, 69, 70, 56,		// 242 - 'o grave'
  5, 56, 70, 69, 68, 56,		// 243 - 'o acute'
  5, 58, 69, 69, 69, 58,		// 244 - 'o circumflex'
  5, 56, 70, 69, 70, 57,		// 245 - 'o tilde'
  5, 56, 69, 68, 69, 56,		// 246 - 'o diaresis'
  5, 8, 8, 42, 8, 8,		// 247 - 'Division sign'
  6, 64, 56, 84, 76, 68, 58,		// 248 - 'o slashed'
  5, 60, 65, 66, 32, 124,		// 249 - 'u grave'
  5, 60, 64, 66, 33, 124,		// 250 - 'u acute'
  5, 58, 65, 65, 33, 122,		// 251 - 'u circumflex'
  5, 60, 65, 64, 33, 124,		// 252 - 'u diaresis'
  4, 156, 162, 161, 124,		// 253 - 'y acute'
  4, 252, 72, 72, 48,		// 254 - 'small thorn'
  4, 157, 160, 160, 125,		// 255 - 'y diaresis'
};

#endif

Ce fichier définit , tous les caractères Ascii Etendu.

Une fois ce fichier copié , il suffira de l’inclure dans notre sketch :

#include « Parola_Fonts_data.h »

Puis, on ajoute ces deux fonctions avant la fonction setup()

....
...

//*****************************************
// UTF8 - Ascii etendu
//*****************************************
uint8_t utf8Ascii(uint8_t ascii)
// Convertit un caractere UTF8 en caractére etendu ASCII selon la norme // ISO 8859-1 , aussi appellé ISO Latin-1
// Codes 128-159 contient les codes Microsoft Windows Latin-1
// 
// - codes 0..127 sont identiques entre ASCII et UTF-8
// - codes 160..191 in ISO-8859-1 and Windows-1252 are two-byte characters in UTF-8
//  + 0xC2 then second byte identical to the extended ASCII code.
// - codes 192..255 in ISO-8859-1 and Windows-1252 are two-byte characters in UTF-8
// + 0xC3 then second byte differs only in the first two bits to extended ASCII code.
// - codes 128..159 in Windows-1252 are different, but usually only the €-symbol will be needed from this range.
//                 + The euro symbol is 0x80 in Windows-1252, 0xa4 in ISO-8859-15, and 0xe2 0x82 0xac in UTF-8.
// Extended ASCII encoding should match the characters at http://www.ascii-code.com/
//
// Return "0" if a byte has to be ignored.
{
  static uint8_t cPrev;
  uint8_t c = '\0';

  if (ascii < 0x7f)   // Standard ASCII-set 0..0x7F, no conversion
  {
    cPrev = '\0';
    c = ascii;
  }
  else
  {
    switch (cPrev)  // Conversion depending on preceding UTF8-character
    {
    case 0xC2: c = ascii;  break;
    case 0xC3: c = ascii | 0xC0;  break;
    case 0x82: if (ascii==0xAC) c = 0x80; // Euro symbol special case
    }
    cPrev = ascii;   // save last char
  }
  return(c);
}

void utf8Ascii(char* s)
// In place conversion UTF-8 string to Extended ASCII
// The extended ASCII string is always shorter.
{
  uint8_t c, k = 0;
  char *cp = s;

  while (*s != '\0')
  {
    c = utf8Ascii(*s++);
    if (c != '\0')
      *cp++ = c;
  }
  *cp = '\0';   // terminate the new string
}

//*****************************************
....

Plus de détails , sur les codes ASCCI , à cette adresse ( table ASCII ).

Enfin dans la partie Setup() , j’ enregistrer en mémoire la nouvelle table  ASCII  étendue .

.....
..
void setup() {
   //demarrage Display
   P.begin();
   P.setFont(ExtASCII);  // Chargement table ASCII Etendu

.....
....

J’ ajoute aussi ,  dans la définition de la page WEB du module ( en fin de script ) , que je travaille sur une table étendue de type Latin.

String page = « <html lang=fr-FR><head><meta http-equiv=’content-type’ content=’text/html; charset=iso-8859-15′ /><meta http-equiv=’refresh’ content=’120’/> »;

Par l’ajout de la balise meta charset=iso-8859-15.

En testant depuis  l’interface WEB du module , avec la phrase suivante :

« ça fonctionne avec les é et même les degrés : 23°C »

test

Je  remarque que dans la page suivante l’url du site WEB a été modifié avec les bons codes :

msg=%E7a+fonctionne+avec+les+%E9+et+m%EAme+les+degr%E9s+%3A+23%B0C

Et surtout que l’affichage led indique bien : ça fonctionne avec les é et même les degrés : 23°C

Super, mais je viens d’essayer sous jeedom et cela ne fonctionne pas ? Tous mes caractères accentués ne sont pas affichés.

Le problème , c’est que lors de l’envoi de la requête http via le module Script de jeedom , cette dernière est codée en UTF8.

Il va falloir passer par un script , qui sera en charge de convertir l’UTF8 en ISO 8859.

Pour cela , dans le module Script de jeedom , nous allons créer une commande de type Script .

Dans le champs requête ,  cliquer sur nouveau,  choisir un nom de fichier par exemple : notifhorloge.php

Puis, dans la fenêtre qui s’ouvre copier le script suivant :

Modification du SCRIPT : Ajout gestion luminosité et options

<?php
  	$IP=$argv[1];
	$notif=iconv("UTF-8", "CP1252", $argv[2]);  // conversion utf8 en ISO 8859-15 - gestion de €
	$notif=urlencode($notif);

// Decoupage des options transmis dans titre sous la forme type = Animation ; lum = intensite de la lumiére
	//$Options = explode(";",str_replace(' ', '', $argv[3]));
	$Options=preg_split("/[\,\;\.\:\-]/", str_replace(' ', '', $argv[3]));
	foreach ( $Options as $Value ){
    list($k, $v) = explode("=",$Value);
    $k = strtolower($k);
    $v = strtoupper($v);
    $result[ $k ] = $v;
    }
// Affectation des differents index
	$type=$result["type"];
	$lum=$result["lum"];

/* Lit un fichier distant sur le serveur www.example.com avec le protocole HTTP */
$url="http://".$IP."/Notification?msg=".$notif."&type=".$type."&intnotif=".$lum;
$httpfile  = file_get_contents($url);
?>

Ce script , permet de récupérer trois arguments passés en ligne de commande :

Modification : gestion  luminosité ( 04/06/18 )

L’adresse IP du module , le Message et  l’info Titre , qui réunit les différentes options : une info type ( qui permet des petits effets sur les notifications envoyées ) et une info lum , pour la valeur de la luminosité.

Le champ message est dans un premier temps decodés de l’utf8 ( utf8_decode ) , puis encodés dans un format url afin d’encapsuler les caractères qui peuvent poser soucis ( avec urlencode ).

Le champ titre est découpé en fonction des séparateurs ( ; , : -.) , dans un tableau Option , qui est ensuite traitée pour récuperer les bonnes valeurs à transmettre à votre horloge.

Le champ titre peut être de la forme suivante :

type=PAC,lum=15  ( animation PacMan avec intensité au max )

lum = 9 ; type=OPENING ( animation Opening avec intensité à 9 )

L’ordre des options dans le champ titre n’a pas d’importance . Les options

peuvent être ignorées ( le champ titre vide = notification avec luminosité par défaut )

Afin de passer les paramètres aux script PHP , dans le champs requête à la suite de notre fichier script , nous indiquons  les champs suivants :

adresse_ip « #message# » « #title# » , ou adresse_ip est l’adresse de votre module.

Une fois votre commande créée , il ne vous reste plus qu’à sauvegarder le tout et à tester votre nouvel équipement.

[adsense]

Pour aller plus loin , avec Jeedom , je vais expliquer comment envoyer une requête de type POST sous jeedom , afin de pouvoir activer depuis l’interface de notre box domotique , toutes les configurations que permet l’interface WEB du module.

Voici , à quoi peut ressembler l’équipement au complet sous JEEDOM.

Exemple Widget Horloge

Pour créer les commandes , permettant d’afficher ou non l’horloge , les secondes ou encore régler manuellement l’intensité, il suffit de créer une nouvelle commande qui sera basée sur un nouveau script php.

  • Commencer par ajouter une nouvelle commande
  • Donner par exemple un nom : Horloge OFF
  • Type de script : script
  • Nouveau , donner un nom à son script , par exemple SetupHorloge.php
  • Puis coller ce script
<?php
   	$IP=$argv[1];
	$commande=urlencode(utf8_decode($argv[2]));
  	$etat=urlencode(utf8_decode($argv[3]));

$url = 'http://'.$IP;
if ($commande =="INT") $data = array("LUM" => "manu" , $commande => $etat);
else $data = array($commande => $etat);
// use key 'http' even if you send the request to https://...
$options = array(
  'http' => array(
    'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
    'method'  => 'POST',
    'content' => http_build_query($data),
  ),
);
$context  = stream_context_create($options);
$result = file_get_contents($url, false, $context);
//var_dump($result);
?>

Comme sur le script précédent , il nous servira à appeler les différentes commandes passées en argument.  IP_du_Module Commande Etat

exemple : /var/www/html/core/php/../../plugins/script/core/ressources/SetupHorloge.php 192.168.80.156 HOR off

Ce qui aura pour effet d’éteindre l’affichage de l’horloge.

Ici notre module a l’adresse IP : 192.168.80.156 , la commande est HOR ( pour Horloge ) et les deux états sont on ou off .

Les différentes commandes implantées sont :

HOR : ( on ou off ) pour afficher ou non l’horloge

SEC : ( on ou off ) pour afficher ou non les secondes

LUM : ( auto ou manu ) pour activer ou désactiver le mode auto pour la luminosité

INT : ( une valeur de 0 à 15 ) pour le réglage de la luminosité ( dans la commande j’ai utilisé le slider  et indiqué #slider# pour état.

Exemples des commandes script horloge

Un bouton pour tout commander

Il y a plusieurs façons d’installer un bouton sur un module WEMOS . Plutôt que de reprendre , ce qui existe déjà sur de nombreux autres sites , qui explique les différences entre un montage pull up , pull down , je vous invite à aller sur le site d’open classroom où est rédigé un article qui explique très bien tout cela .

Le bouton poussoir

Sur les modules Arduino , comme les Wemos , il existe des entrées Logique qui intègrent des résistances de type pull up .

Pour les activer nous disposons de la variable INPUT_PULLUP.

J’ai donc installé mon poussoir , entre le GND et la broche D8 de mon WEMOS D1 R1. Pourquoi la D8 , parce que celle-ci intègre une résistance de 10 K pullup ( voir article sur WEMOS ) . Si vous utilisez la version R2 , prenez la sortie D3.

Ces deux sorties correspondent à la GPIO0 .

On ajoute la constante bouton au programme :

const int bouton = 0; // GPIO0 ( pullup integré – D8 pour R1 ou D3 pour R2 )

Plutôt que de déclarer la fonction : pinMode(bouton,INPUT_PULLUP);  je préfère utiliser la bibliothèque clickbutton de marco Brianza, qui permet de gérer très facilement les clicks , double click , triple click et même les appuis longs.

Vous pouvez récupérer cette bibliothèque sur le Github de marco Brianza .

Cette bibliothèque , permet de paramètrer les problèmes dus au rebond ( bounce ) et de renvoyer une valeur en fonction des différentes actions sur le bouton poussoir .

1 clic rapide = 1 , 2 clic rapide = 2 , 3 clic rapide = 3 , etc ……

1 clic long = -1 , 1 clic rapide suivi d’un clic long = -2 , 2 clic rapide suivi d’un clic long = -3 et ainsi de suite.

Une fois téléchargé , il suffit de copier le dossier décompressé « clickbutton-master » dans le dossier libraries de l’interface Arduino.

Il faudra donc inclure cette librairie , puis initier une instance Clickbutton .

Ce qui donne dans le script :

.......
.....
.....
//librairies click bouton
#include <ClickButton.h>

........
.......
const int bouton = 0; // GPIO0 ( pullup integré - D8 pour R1 ou D3 pour R2 )
ClickButton boutonClick(bouton, LOW, CLICKBTN_PULLUP); // GPIO , mode pressé du bouton ( haut ou bas ) ici bas quand on appuie sur le bouton ( la broche est mise a la masse ) , mode pullup activé )

...........
int clic=0;
.....
.....
void setup() {
....
 // Setting click button : Setup button timers (all in milliseconds / ms)
  // (These are default if not set, but changeable for convenience)
 boutonClick.debounceTime   = 20;   // Debounce timer in ms
  boutonClick.multiclickTime = 250;  // Time limit for multi clicks
 boutonClick.longClickTime  = 1000; // time until "held-down clicks" register
}

void loop() {
  // etat bouton
    boutonClick.Update();
  // Mise a jour du bouton , et récupère la nouvelle valeur
  if (boutonClick.clicks != 0) clic = boutonClick.clicks;
  // stockage de la valeur dans la variable clic

.............

// traitement du clic en fonction de ce que l'on va definir

 switch (clic) {
    case 1:  // Simple clic
.......

......

Dans mon script , voici les actions que j’ai choisis :

1 clic rapide ( valeur 1 ) : Une boucle de 3 états est mis en place qui permet de passer à tour de rôle de : Auto / mode manuel avec Intensité mini / Mode manuel avec Intensité Max , puis on reboucle.

2 clic rapide ( valeur 2 ) : ON / OFF Ecran horloge

3 clic rapide ( valeur 3 ) : ON / OFF Affichage des secondes

……

1 clic long ( valeur -1 ) : diminue luminosité d’un cran

1 clic rapide + 1 clic long ( valeur -2 ) : Augmentation luminosité d’un cran

j’ai essayé avec 4 clics et 5 clics , mais cela devient vite compliqué. Il vaut mieux rester raisonnable et ne pas dépasser 3 clics.

Maintenant , la bibliothèque est capable de gérer autant de clic que vous voulez , il faudra sûrement ajuster les valeurs par défaut afin que le comptage s’adapte à votre rapidité ( par exemple le timelimit pour les multiples click qui est par défaut de 0,25s entre chaque clic)

Dans ce cas là , dans la partie setup() , vous pouvez modifier ces trois valeurs :

  // Setup button timers (all in milliseconds / ms)
  // (These are default if not set, but changeable for convenience)
  button1.debounceTime   = 20;   // Debounce timer in ms
  button1.multiclickTime = 250;  // Time limit for multi clicks
  button1.longClickTime  = 1000; // time until "held-down clicks" register

Ce qui donne pour le paramètrage de la fonction switch :

............

#if (ACTIVBOUTON)
  // etat bouton
    boutonClick.Update();
  // Sauvegarde de la valeur dans la variable click
  if (boutonClick.clicks != 0) clic = boutonClick.clicks;

  switch (clic) {
    case 1:  // Simple clic - boucle : Manuel intensite à 0 / Manu Intensite MAX / Auto
        if (clicstate==1) {
          AutoIn=false;
          Intensite = 0; 
          P.print("Min");
          }
          if (clicstate==2) {
            Intensite = 15;
            P.print("Max");
          }
          if (clicstate==3) { 
            AutoIn=true;
            P.print("Auto");
            clicstate=0;
          }
          ++clicstate;
      delay(400);
      clic=0;
      break;
    case 2: // double clic - ON / OFF horloge
      TimeOn =!TimeOn;
      if (TimeOn) P.print("Time On"); else P.print("Time Off");
      delay(400);
      clic=0;
      break;
         case 3: // double clic - Affiche ou non les secondes
      DisSec =!DisSec;
      clic=0;
      break;
       case -1: // simple clic mais long sur dernier  - diminue
      --Intensite;
      if (Intensite<0) Intensite=0;
      P.print(Intensite);
      delay(400);
      clic=0;
      break;
       case -2: // double clic mais long sur dernier  - Augmente
       ++Intensite;
      if (Intensite>15) Intensite=MAX_INTENSITY;
      P.print(Intensite);
      delay(400);
      clic=0;
      break;
    default:
      // default is optional
      break;  
  }
#endif

Par exemple , dans le cas du double click ( valeur : 2 ) , je réalise une bascule sur la variable TimeOn , qui passe de true à false , lors de chaque double clic grâce à la ligne : TimeOn =!TimeOn;

Puis, j’affiche une info à l’écran pour informer que la commande a bien été prise en compte. Avec un petit delay , afin d’avoir le temps de lire l’info.

Puis on remet la valeur de clic à zero.

Il est donc très facile , de pouvoir attribuer une action , en fonction de vos envies , il suffira pour cela de modifier chaque cas.

Si vous avez des idées de configuration plus pratique , je suis preneur. Pour l’instant , je ne me sers pas du bouton dans mon quotidien , donc je ne suis pas en mesure de dire quel est le mode le plus utilisé.

Quelques Nouveautés :

Script plus structuré :

Avant de vous livrer le script final , j’ai aéré le code , et mis un peu plus de commentaires , afin qu’il soit facilement modifiable , si vous désirez y apporter des modifs.

En entête du script , j’ai placé 5 variables à modifier si besoin , avant la compilation du sketch.

#define NOMMODULE « ESP-Horloge_testV2 » // nom module
#define NTPSERVER « pool.ntp.org » // Serveur NTP
#define ACTIVBOUTON true // Si bouton installé
#define ACTIVCAPTLUM true // Si capteur luminosité installé
#define BUF_SIZE 60 // Taille max des notification ( nb de caractéres max )

Option reboot :

J’ai ajouté une fonction depuis l’interface WEB , permettant de redémarrer le module à distance.

Options :

J’ai ajouté aussi , l’option « type » , lors de l’envoi d’une notification qui permet de choisir un effet avant de diffuser votre notification.

L’idée m’est venue de l’horloge installé e, dans la salle de jeux , qui me permet de notifier les enfants lors des heures de repas par exemple.

J’ai pu constater qu’il ne faisait pas toujours attention à la notification , j’ai donc décidé de mettre en place une petite animation , afin que leur attention soit fixée sur l’horloge.

J’ai donc intégré une animation avec PacMan qui passe , puis repasse poursuive par un fantôme. Pour la déclencher , il suffit d’indiquer l’option « PAC » dans le champ titre.

animation PacMan
Animation PacMan & Ghost

Ci-dessous le code pour l’animation PacMan

.....
// *****************************
// ANIMATION
// SPRITE DEFINITION (PAC MAn )
// *****************************

const uint8_t F_PMAN1 = 6;
 const uint8_t W_PMAN1 = 8;
static uint8_t  pacman1[F_PMAN1 * W_PMAN1] =  // gobbling pacman animation
{
  0x00, 0x81, 0xc3, 0xe7, 0xff, 0x7e, 0x7e, 0x3c,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c,
};

const uint8_t F_PMAN2 = 6;
const uint8_t W_PMAN2 = 18;
static uint8_t pacman2[F_PMAN2 * W_PMAN2] =  // ghost pursued by a pacman
{
  0x00, 0x81, 0xc3, 0xe7, 0xff, 0x7e, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
};

void setup {

.........
 P.setSpriteData(pacman1, W_PMAN1, F_PMAN1,pacman2, W_PMAN2, F_PMAN2);   // chargement animation en memoire
..........


}

void loop {
..........
// Gestion Affichage
  if (P.displayAnimate())
  {
    // Si une demande de notification est arrivée
  if (Alert) {
    // Type de demande 
    if (type=="PAC" ) {    // animation PAC MAn
      P.displayText("Notif", PA_LEFT, 40, 1, PA_SPRITE, PA_SPRITE); // affiche l'animation PacMan
      type="";
    }
    else if (type=="BLINDS" ) {  // animation de type volet
       P.displayText("", PA_CENTER, 40, 1, PA_BLINDS, PA_BLINDS);
      type="";
    }
     else if (type=="OPENING" ) {  // animation Ouverture
       P.displayText("Notif", PA_CENTER, 60, 100, PA_OPENING_CURSOR, PA_OPENING_CURSOR);
      type="";
    }
    else {
         P.displayText(Notif, PA_LEFT, 40, 1000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
        Alert=false;
    }
  }
  // Sinon affichage de l'heure
}
.........
.....

J’en ai profité , pour ajouter  deux types d’animation inclus dans les animations de la bibliothèque Parola ( BLINDS et OPENING ), très facilement modifiable , si vous désirez mettre d’autres animations.

Bref , si le champs titre a pour valeur type= (PAC , BLINDS ou OPENING) , une animation sera jouée avant la notification , pour tous les autres cas il y aura juste l’animation.

Notification avec choix effet depuis interface WEB

Modification : 04/06/18

J’ai ajouté l’option luminosité ( Merci à Thyer )  , qui permet de jouer sur la luminosité de l’affichage ( valeur de 1 à 15 ).

Dans le champ titre , il suffit d’indiquer l’option : lum=6

Bien sur , il est tout a fait possible de combiner les deux en indiquant dans le champ titre : type=PAC,lum=4 par exemple.

Voila , je vous laisse vous amuser avec cette nouvelle mouture , il y a sûrement plein de modifications à apporter .

Je compte sur vos retours pour me donner des idées.

Voici le script complet :

//**************************
// Hotloge / notification
// OTA + Interface Web + Connecté
//  Mai 2018
// Byfeel
// *************************

#define Ver 2.34
#define NOMMODULE "ESP-Horloge_Nom"   // nom module
#define NTPSERVER "fr.pool.ntp.org"         // Serveur NTP
#define ACTIVBOUTON true              // Si bouton installé
#define ACTIVCAPTLUM true              // Si capteur luminosité installé
#define  BUF_SIZE  60                    // Taille max des notification ( nb de caractéres max )

// Parametrage matrice  ( Pin Arduino ou est branché la matrice )
#define MAX_DEVICES 8 // ( nombre de matr ice )
#define CLK_PIN   D5
#define DATA_PIN  D7
#define CS_PIN    D6

// ******************************//
//******** Bibliotheque *********//
//******************************//
//***** Gestion reseau
#include <EthernetClient.h>
#include <Ethernet.h>
#include <Dhcp.h>
#include <EthernetServer.h>
#include <Dns.h>
#include <EthernetUdp.h>
//****** Gestion WIFI
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
// WIFI Manager , afin de gerer la connexion au WIFI de façon plus intuitive
#include <WiFiManager.h>         //https://github.com/tzapu/WiFiManager
// ***** OTA
///includes necessaires au fonctionnement de l'OTA :
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
// **** Matrice
// gestion de l'affichage matricielle
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include "Parola_Fonts_data.h"
// **** Temps
// librairie temps
#include <TimeLib.h>
#include <NtpClientLib.h>
#if (ACTIVBOUTON)
//librairies click bouton
#include <ClickButton.h>
#endif

//**************************
//**** Varaible & service ***
// *************************

ESP8266WebServer server(80);         // serveur WEB sur port 80

// definition du numero de LED interne 
#define led 2 // led built IN

// Bouton
#if (ACTIVBOUTON)
#define bouton  0 // GPIO0 ( pullup integré - D8 pour R1 ou D3 pour R2 )
ClickButton boutonClick(bouton, LOW, CLICKBTN_PULLUP);
#endif

// variable pour stocker la valeur du photoresistor
#if (ACTIVCAPTLUM) 
int sensorValue;
#endif


// initialisation de la matrice
MD_Parola P = MD_Parola(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); 

//variable systemes
char Notif[BUF_SIZE];
boolean DisSec = true;     // on-off secondes
boolean Alert=false;       
boolean AutoIn=true;      // Auto / manu intensite
boolean TimeOn=true;      // ON - off Affichage horloge
int Intensite=5;
int BkIntensite;
const long interval = 10000;  // interval pour mesure luminosite réglé sur 5 s
unsigned long previousMillis=0 ;
int clic=0;
int clicstate=1;
String message="";
String type="";




//*****************************************
// UTF8 - Ascii etendu
//*****************************************
uint8_t utf8Ascii(uint8_t ascii)
// Convert a single Character from UTF8 to Extended ASCII according to ISO 8859-1,
// also called ISO Latin-1. Codes 128-159 contain the Microsoft Windows Latin-1
// extended characters:
// - codes 0..127 are identical in ASCII and UTF-8
// - codes 160..191 in ISO-8859-1 and Windows-1252 are two-byte characters in UTF-8
//                 + 0xC2 then second byte identical to the extended ASCII code.
// - codes 192..255 in ISO-8859-1 and Windows-1252 are two-byte characters in UTF-8
//                 + 0xC3 then second byte differs only in the first two bits to extended ASCII code.
// - codes 128..159 in Windows-1252 are different, but usually only the €-symbol will be needed from this range.
//                 + The euro symbol is 0x80 in Windows-1252, 0xa4 in ISO-8859-15, and 0xe2 0x82 0xac in UTF-8.
//
// Modified from original code at http://playground.arduino.cc/Main/Utf8ascii
// Extended ASCII encoding should match the characters at http://www.ascii-code.com/
//
// Return "0" if a byte has to be ignored.
{
  static uint8_t cPrev;
  uint8_t c = '\0';

  if (ascii < 0x7f)   // Standard ASCII-set 0..0x7F, no conversion
  {
    cPrev = '\0';
    c = ascii;
  }
  else
  {
    switch (cPrev)  // Conversion depending on preceding UTF8-character
    {
    case 0xC2: c = ascii;  break;
    case 0xC3: c = ascii | 0xC0;  break;
    case 0x82: if (ascii==0xAC) c = 0x80; // Euro symbol special case
    }
    cPrev = ascii;   // save last char
  }
  return(c);
}

void utf8Ascii(char* s)
// In place conversion UTF-8 string to Extended ASCII
// The extended ASCII string is always shorter.
{
  uint8_t c, k = 0;
  char *cp = s;

  while (*s != '\0')
  {
    c = utf8Ascii(*s++);
    if (c != '\0')
      *cp++ = c;
  }
  *cp = '\0';   // terminate the new string
}

//*****************************************

// **************************
// Parametre NTP
// Serveur NTP
// **************************

// TimeZone
int8_t timeZone = 1;
// Le fuseau utilise les horaires été / hiver
bool DLS = true;
boolean syncEventTriggered = false; // True if a time even has been triggered
NTPSyncEvent_t ntpEvent; // Last triggered event


void configModeCallback (WiFiManager *myWiFiManager) {
  P.print("Choisir AP..");
  delay(3000);
  P.print(WiFi.softAPIP().toString());
    delay(3000);
    P.print(myWiFiManager->getConfigPortalSSID());
  //Serial.println(WiFi.softAPIP());
  //if you used auto generated SSID, print it
  //Serial.println(myWiFiManager->getConfigPortalSSID());
}


//void processNtpEvent (NTPSyncEvent_t ntpEvent) {
 void processSyncEvent (NTPSyncEvent_t ntpEvent) {
  if (ntpEvent) {
    if (ntpEvent == noResponse)
      P.print ("Serveur NTP injoignable");
    else if (ntpEvent == invalidAddress)
      P.print ("Adresse du serveur NTP invalide");
  } else {
     if ( year() != 1970 or year() != 2036 ) {
             NTP.setInterval (1200);
            }
    
  }
  
}
// **************************************


#if (ACTIVCAPTLUM)
// fonction reglage auto luminosite
void luminosite() {
  sensorValue = analogRead(A0); // read analog input pin 0
  Intensite =round((sensorValue*1.5)/100);
  if (Intensite < 1 ) Intensite = 0 ;
  if (Intensite > 15 ) Intensite = MAX_INTENSITY;
}
#endif

// ******************
// Gestion page WEB
//******************

void handleRoot(){ 
  if ( server.hasArg("SEC") || server.hasArg("LUM") || server.hasArg("HOR")) {
    handleSubmit();
  } 
  else {
    server.send ( 200, "text/html", getPage() );
  }  
}
 
void handleSubmit() {
  String SEC = server.arg("SEC");
  String LUM = server.arg("LUM");
  String HOR = server.arg("HOR");
  if ( LUM =="manu" ) {
            AutoIn=false;
            Intensite=server.arg("INT").toInt();
    } else if ( LUM == "auto") { AutoIn=true;}
    
  if ( HOR =="off" ) {
          TimeOn=false;
      } else if ( HOR =="on")  TimeOn=true;
  
  if ( SEC == "on" ) {
    DisSec=true;
    server.send ( 200, "text/html", getPage() );
  } else if ( SEC == "off" ) {
    DisSec=false;
    server.send ( 200, "text/html", getPage() );
  } else {
   server.send ( 200, "text/html", getPage() );
  }
  
  if ( server.arg("RAZ") =="1" ) {
     P.print("reboot");
     delay(3000);
     ESP.reset();
     
  }
}

// *****************************
// ANIMATION
// SPRITE DEFINITION (PAC MAn )
// *****************************

const uint8_t F_PMAN1 = 6;
 const uint8_t W_PMAN1 = 8;
static uint8_t  pacman1[F_PMAN1 * W_PMAN1] =  // gobbling pacman animation
{
  0x00, 0x81, 0xc3, 0xe7, 0xff, 0x7e, 0x7e, 0x3c,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c,
};

const uint8_t F_PMAN2 = 6;
const uint8_t W_PMAN2 = 18;
static uint8_t pacman2[F_PMAN2 * W_PMAN2] =  // ghost pursued by a pacman
{
  0x00, 0x81, 0xc3, 0xe7, 0xff, 0x7e, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
};

//***********************

//***********************
// Boucle SETUP
// *********************

void setup() {
   //demarrage Display
   P.begin();
   P.setFont(ExtASCII);   // chargement caractere etendue
   P.setSpriteData(pacman1, W_PMAN1, F_PMAN1,pacman2, W_PMAN2, F_PMAN2);   // chargement animation en memoire
   
    //******** WiFiManager ************
    WiFiManager wifiManager;

     //Si besoin de fixer une adresse IP
    //wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0));
    
    //Forcer à effacer les donnees WIFI dans l'eprom , permet de changer d'AP à chaque demmarrage ou effacer les infos d'une AP dans la memoire ( a valider , lors du premier lancement  )
    //wifiManager.resetSettings();
    //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
     wifiManager.setAPCallback(configModeCallback);
    
    //Recupere les identifiants   ssid et Mot de passe dans l'eprom  et essayes de se connecter
    //Si pas de connexion possible , il demarre un nouveau point d'accés avec comme nom , celui definit dans la commande autoconnect ( ici : AutoconnectAP )
    // wifiManager.autoConnect("AutoConnectAP");
    //Si rien indiqué le nom par defaut est  ESP + ChipID
    //wifiManager.autoConnect();
      if(!wifiManager.autoConnect("AP_ESP")) {
              P.print("erreur AP");
              delay(3000);
              //reset and try again, or maybe put it to deep sleep
              ESP.reset();
              delay(5000);
        } 
    
// ****** Fin config WIFI Manager ************



 //******* OTA ***************
// Port defaults to 8266
  // ArduinoOTA.setPort(8266);

  // Hostname defaults to esp8266-[ChipID]
  ArduinoOTA.setHostname(NOMMODULE);

  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

  ArduinoOTA.onStart([]() {
    digitalWrite(led, HIGH); // allume led au debut du transfert
      P.print("Update ..."); 
  });
  ArduinoOTA.onEnd([]() {
     P.print("Reboot ..."); 
    digitalWrite(led, LOW); // eteint a la fin de la mise a jour
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    if (MAX_DEVICES<5) sprintf(Notif,"Up %u%%", (progress / (total / 100)));
    else sprintf(Notif,"Upload  %u%%", (progress / (total / 100)));
     P.print(Notif); 
    //Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    //Serial.printf("Error[%u]: ", error);
  });
  ArduinoOTA.begin();
  //********* Fin OTA ***************
   
 

  // on attend d'etre connecte au WiFi avant de continuer
  P.print("Wait ..."); 
  while (WiFi.status() != WL_CONNECTED) {
  }
  // on affiche l'adresse IP attribuee pour le serveur DSN et on affecte le nom reseau
   WiFi.hostname(NOMMODULE);
  P.print("Ready ...");
  delay(400);
  P.print("");
  //*************************
  
  // on definit les points d'entree (les URL a saisir dans le navigateur web) : Racine du site est géré par la fonction handleRoot
  server.on("/", handleRoot);

 server.on("/Notification", [](){
    // on recupere les parametre dans l'url dans la partie /Notification?msg="notification a affiocher"&type="PAC"
     if ( server.hasArg("msg")) {
      BkIntensite=Intensite;
        message=server.arg("msg");
        if (message ) {
        message.toCharArray(Notif,BUF_SIZE);
        if (server.hasArg("intnotif") && server.arg("intnotif").length() > 0 ) {
          Intensite = server.arg("intnotif").toInt();
          if (Intensite < 1 ) Intensite = 0 ;
          if (Intensite > 14 ) Intensite = MAX_INTENSITY;
          P.setIntensity(Intensite); // intensité pour les notifs si pas de valeur Intensité par defaut
        } 
        if (server.arg("type")) {
              type=server.arg("type");
               }
     Alert=true;
     P.print("");
    // on repond au client
    server.send(200, "text/plain", "message :" + message + " & Animation : "+server.arg("type") + " & Intensite : "+server.arg("intnotif"));
        }
     }
    });
  

  // on demarre le serveur web 
  server.begin();


    //******* Service NTP ********
    NTP.onNTPSyncEvent ([](NTPSyncEvent_t event) {
        ntpEvent = event;
        syncEventTriggered = true;
    });

    
// Démarrage du processus NTP  
NTP.begin(NTPSERVER, timeZone, DLS);
// Interval de synchronisation en seconde , 10 s au depart pour forcer une mise a jour rapide
NTP.setInterval (10);

  
  // Parametrage led interne pour indiquer fin de demmarrage
  pinMode(led, OUTPUT);     // Initialise la broche "led" comme une sortie 
  digitalWrite(led, LOW);

#if (ACTIVBOUTON)
  // Setting click button : Setup button timers (all in milliseconds / ms)
  // (These are default if not set, but changeable for convenience)
 boutonClick.debounceTime   = 20;   // Debounce timer in ms
  boutonClick.multiclickTime = 250;  // Time limit for multi clicks
 boutonClick.longClickTime  = 1000; // time until "held-down clicks" register
#endif

  message = " Systeme OK - Adresse ip de votre Module : ";
  message += WiFi.localIP().toString();
  message.toCharArray(Notif,BUF_SIZE);
 Alert=true;
}
// ********* fin SETUP


 // ********** Boucle loop *******

void loop() {
  // ***********  Gestion bouton
 #if (ACTIVBOUTON)
  // etat bouton
    boutonClick.Update();
  // Sauvegarde de la valeur dans la variable click
  if (boutonClick.clicks != 0) clic = boutonClick.clicks;

  switch (clic) {
    case 1:  // Simple clic - boucle : Manuel intensite à 0 / Manu Intensite MAX / Auto
        if (clicstate==1) {
          AutoIn=false;
          Intensite = 0; 
          P.print("Min");
          }
          if (clicstate==2) {
            Intensite = 15;
            P.print("Max");
          }
          if (clicstate==3) { 
            AutoIn=true;
            P.print("Auto");
            clicstate=0;
          }
          ++clicstate;
      delay(400);
      clic=0;
      break;
    case 2: // double clic - ON / OFF horloge
      TimeOn =!TimeOn;
      if (TimeOn) P.print("On"); else P.print("Off");
      delay(400);
      clic=0;
      break;
         case 3: // double clic - Affiche ou non les secondes
      DisSec =!DisSec;
      clic=0;
      break;
       case -1: // simple clic mais long sur dernier  - diminue
      --Intensite;
      if (Intensite<0) Intensite=0;
      P.print(Intensite);
      delay(400);
      clic=0;
      break;
       case -2: // double clic mais long sur dernier  - Augmente
       ++Intensite;
      if (Intensite>15) Intensite=MAX_INTENSITY;
      P.print(Intensite);
      delay(400);
      clic=0;
      break;
    default:
      // default is optional
      break;  
  }
#endif
// ******** Fin gestion bouton

// ******** Gestion luminosite
 #if (ACTIVCAPTLUM)
  // Si gestion Auto luminositée activé
  if (AutoIn) {
  if( millis() - previousMillis >= interval) {
    previousMillis = millis();
  luminosite();
  }
  }
  #endif
// ********** Fin gestion luminosite
  
// ********* Gestion Reseau : Pages WEB , NTP et OTA
// ********* Service NTP
   if (syncEventTriggered) {
        processSyncEvent (ntpEvent);
        syncEventTriggered = false;
    }
    
//  ****** Page WEb : a chaque iteration, la fonction handleClient traite les requetes 
  server.handleClient();

// ******* OTA
// Surveillance des demandes de mise a jour en OTA
  ArduinoOTA.handle();
  
// ******** Fin gestion reseau

// Gestion Affichage
  if (P.displayAnimate())
  {
    // Si une demande de notification est arrivée
  if (Alert) {
    // Type de demande 
    if (type=="PAC" ) {    // animation PAC MAn
      P.displayText("Notif", PA_LEFT, 40, 1, PA_SPRITE, PA_SPRITE);
      type="";
    }
    else if (type=="BLINDS" ) {  // animation de type volet
       P.displayText("", PA_CENTER, 40, 1, PA_BLINDS, PA_BLINDS);
      type="";
    }
     else if (type=="OPENING" ) {  // animation Ouverture
       P.displayText("Notif", PA_CENTER, 60, 100, PA_OPENING_CURSOR, PA_OPENING_CURSOR);
      type="";
    }
    else {
         P.displayText(Notif, PA_LEFT, 40, 1000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
        Alert=false;
         Intensite=BkIntensite;
    }
  }
  // Sinon affichage de l'heure
  else {
      // Affichage heure
      if (TimeOn) {
       P.setIntensity(Intensite);
        digitalClockDisplay().toCharArray(Notif,BUF_SIZE);
       // P.print(clic); //Debug pour afficher valeur 
      P.displayText(Notif, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);
        } else  
        {
        P.setIntensity(0); 
        P.print(".");  // Affichage d'un point si horloge etaind , peut etre remplaçé par "". Si on ne veut pas d'affichage
        }  
  }
}
}
// ********* Fin boucle



// Fonction clockformat ( ajoute les zeros devant les unités )
String digitalClockDisplay()
    {
          message=printDigits(hour());
          message+=":";
          message+=printDigits(minute());
          if ( DisSec ) { 
            message+=":";
            message+=printDigits(second());
            }
  return message;
  }

String printDigits(int digits)
{
  String Digital;
  // Digital=":";
  if (digits < 10) Digital+="0";
  Digital+=digits;
  return Digital;
}


//************************************
// Page WEB Accueil
// ***********************************
String getPage(){
  String page = "<html lang=fr-FR><head><meta http-equiv='content-type' content='text/html; charset=iso-8859-15' /><meta http-equiv='refresh' content='120'/>";
  page += "<title>Horloge / Notification - Byfeel.info</title>";
  page += "<style> table.steelBlueCols { border: 3px solid #555555;background-color: #555555;width: 95%;text-align: left;border-collapse: collapse;}";
  page +="table.steelBlueCols td, table.steelBlueCols th { border: 1px solid #555555;padding: 4px 9px;}";
  page +="table.steelBlueCols tbody td {font-size: 1em;font-weight: bold;color: #FFFFFF;} table.steelBlueCols td:nth-child(even) {background: #398AA4;}";
  page +="fieldset { padding:0 20px 20px 20px; margin-bottom:10px;border:1px solid #398AA4;width:95%;} div.bloc {float:left;width:450px}";
  //page +="table.steelBlueCols tfoot .links a{display: inline-block;background: #FFFFFF;color: #398AA4;padding: 2px 8px;border-radius: 5px;"
  page +="}</style>";
  page += "</head><body><h1><span style='background-color: #398AA4; color: #ffffff; padding: 0 5px;'>Horloge - Notification</span></h1>";
  page += "<h3><span style='background-color: #398AA4; color: #ffffff; padding: 0 5px;'> Heure : ";
  page += printDigits(hour())+":"+printDigits(minute())+":"+printDigits(second());
  page += " - Date : "+printDigits(day())+"/"+printDigits(month())+"/"+year()+"</span></h3>";
   page += "<div class='bloc'><form action='/Notification' method='GET'>";
  page +="<fieldset><legend>Envoyer une Notification :</legend>";
  page +="<label for='msg'>Message </label><br />";
  page += "<INPUT type='text' name='msg' id='msg'maxlength='59' style='width:400px;' placeholder='Votre Message - 60 caracteres max -'/>";
   page +="<label for='type'>type : </label>";
   page +="<select name='type' id='type'>";
   page +="<option value=''>Defaut</option>";
  page +="<option value='PAC'>PAC MAN</option>";
  page +="<option value='BLINDS'>BLINDS</option>";
  page +="<option value='OPENING'>Opening</option>";
   page +="</select><br />";
  page +="<label for='intnotif'>Intensite : </label><br />";
  page += "<p><input id='sliderINT' type='range' min='0' max='15' step='1' name='intnotif' list='tickmarks' />";
  page +="<datalist id='tickmarks'>";
  page +="<option value='0' label='0'>";
  page +="<option value='1'>";
  page +="<option value='2'>";
  page +="<option value='3'>";
  page +="<option value='4'>";
  page +="<option value='5' label='5'>";
  page +="<option value='6'>";
  page +="<option value='7'>";
  page +="<option value='8'>";
  page +="<option value='9'>";
  page +="<option value='10' label='10'>";
  page +="<option value='11'>";
  page +="<option value='12'>";
  page +="<option value='13'>";
  page +="<option value='14'>";
  page +="<option value='15' label='15'>";
  page +="</datalist></p>";
  page +="</fieldset>";
  page += "<INPUT type='submit' value='Envoyer Message' /></div>";
  page += "</form></div>";
  page +="<div style='clear:both;'></div>";
  page += "<div class='bloc'><h3>Systeme</h3>";
  page += "<table class='steelBlueCols'><tbody>";
  page += "<tr><td>Version </td><td>";
  page += Ver;
  page +="</td></tr>";
  page += "<tr><td>Mise en Marche </td><td>"+ NTP.getUptimeString() + "</td></tr>";
  page += "<tr><td>Serveur NTP </td><td>";
  page += NTPSERVER;
  page +="</td></tr>";
  page += "<tr><td>Premiere synchro NTP </td><td>"+NTP.getTimeDateString(NTP.getFirstSync())+"</td></tr>";
  page += "<tr><td>Derniere synchro NTP </td><td>"+NTP.getTimeDateString(NTP.getLastNTPSync())+"</td></tr>";
  page +="</tbody></table></div>";
  page += "<div class='bloc'><h3>Resau</h3>";
  page += "<table class='steelBlueCols'><tbody>";
  page += "<tr><td>mac adress : </td><td>";
  page += WiFi.macAddress().c_str();
  page +="</td></tr>";
  page += "<tr><td>IP</td><td>"+ WiFi.localIP().toString() + "</td></tr>";
  page += "<tr><td>Masque </td><td>"+ WiFi.subnetMask().toString() + "</td></tr>";
  page += "<tr><td>Passerelle</td><td>"+ WiFi.gatewayIP().toString() + "</td></tr>";
  page += "<tr><td>DNS primaire</td><td>"+ WiFi.dnsIP().toString() + "</td></tr>";
  page += "<tr><td>DNS secondaire</td><td>"+ WiFi.dnsIP(1).toString() + "</td></tr>";
  page +="</tbody></table></div>";
  page +="<div class='bloc'><h3>Wifi</h3>";
  page += "<table class='steelBlueCols'><tbody>";
  page += "<tr><td>Hostname</td><td>"+ WiFi.hostname() + "</td></tr>";
  page += "<tr><td>SSID</td><td>"+ WiFi.SSID() + "</td></tr>";
  page += "<tr><td>Signal WIFI</td><td>";
  page += WiFi.RSSI();
  page +=" dBm</td></tr>";
  page += "<tr><td>BSSID </td><td>";
  page += WiFi.BSSIDstr().c_str();
  page +="</td></tr>";
  page +="</tbody></table></div>";
  page +="<div style='clear:both;'></div>";
  page += "<div class='bloc'><h3>Parametres :</h3>";
  page += "<form action='/' method='POST'>";
  page +="<fieldset><legend>Affichage des Secondes :</legend>";
  page += "<INPUT type='radio' name='SEC' value='on' id='on'";
  if (DisSec==true) page +=" checked ";
  page += "><label for='on'>ON</label>";
  page +="<INPUT type='radio' name='SEC' value='off' id='off'";
  if (DisSec==false) page +=" checked ";
  page += "><label for='off'>OFF</label></fieldset>";
  page +="<fieldset><legend>Affichage Horloge :</legend>";
  page += "<INPUT type='radio' name='HOR' value='on' id='horon'";
  if (TimeOn==true) page +=" checked ";
  page += "><label for='horon'>ON</label>";
  page +="<INPUT type='radio' name='HOR' value='off' id='horoff'";
  if (TimeOn==false) page +=" checked ";
  page += "><label for='horoff'>OFF</label></fieldset>";
  page +="<fieldset><legend>Gestion de la luminosit&eacute;e ( "; 
  page +=Intensite;
  page +=" ) : </legend>";
  page += "<INPUT type='radio' name='LUM' value='auto' id='auto'";
  if (AutoIn==true) page +=" checked ";
  page += "><label for='auto'>AUTO</label>";
  page +="<INPUT type='radio' name='LUM' value='manu' id='manu'";
  if (AutoIn==false) page +=" checked ";
  page += "><label for='manu'>Manuel</label></P>";
  if (AutoIn==false) {
  page +="<p>Valeur luminosit&eacute;e : ";
  page +=Intensite;
  page +="</p>";
  page += "<p><input id='slider1' type='range' min='0' max='15' step='1' name='INT' list='tickmarks' value='";
  page +=Intensite;
  page +="'/>";
  page +="<datalist id='tickmarks'>";
  page +="<option value='0' label='0'>";
  page +="<option value='1'>";
  page +="<option value='2'>";
  page +="<option value='3'>";
  page +="<option value='4'>";
  page +="<option value='5' label='5'>";
  page +="<option value='6'>";
  page +="<option value='7'>";
  page +="<option value='8'>";
  page +="<option value='9'>";
  page +="<option value='10' label='10'>";
  page +="<option value='11'>";
  page +="<option value='12'>";
  page +="<option value='13'>";
  page +="<option value='14'>";
  page +="<option value='15' label='15'>";
  page +="</datalist></p>";
  };
  page +="</fieldset>";
  page +="<fieldset><legend>Redemarrage Module :</legend>";
  page += "<INPUT type='radio' name='RAZ' value='1' id='reset'";
  page += "><label for='reset'>Redemmarage</label>";
   page += "</fieldset>";
   page +="</fieldset>";
  page += "<INPUT type='submit' value='Actualiser'name='parametres'/></div>";
  page += "</form>";
  page += "<br><br>";
  page += "</body></html>";
  return page;
}
[adsense]

PS : Pour ceux qui ne disposent que d’un seul module de 4 led , le script est aussi fonctionnel , il suffira de modifier les lignes suivantes :

// Parametrage matrice ( Pin Arduino où est branchée la matrice )
#define MAX_DEVICES 4 // ( nombre de matrice ) au lieu de 8
#define CLK_PIN D5
#define DATA_PIN D7
#define CS_PIN D6

Il est conseillé de ne pas activer les secondes car l’afficheur ne dispose pas d’assez de place.

Les sources sont disponibles  sur   mon Github  tout fraichement créé.

 

 

 

 

 

58 commentaires sur “DIY Horloge et Notification ( V2 ) – interaction avec Jeedom”

Les commentaires sont fermés.