Geschichten um einen kleinen Weihnachtsbaum
Les histoires commencent généralement par "Il était une fois". Les histoires suivantes se déroulent aujourd'hui et maintenant. Aujourd'hui, nous allons en effet développer quelques projets autour d'un petit sapin de Noël d'un genre particulier.
Il s'agit d'un petit sapin d'environ 11,5 cm de haut avec 36 (37) lumières multicolores. Elles clignotent joyeusement de toutes les couleurs. Mais elles le font toujours plus ou moins de la même manière. Cela m'a donné l'idée de mettre un peu d'ordre dans ce chaos en ajoutant quelques composants. Il en résulte une commande avec un ESP32 et divers capteurs qui mettent l'illumination des LED au service de différentes tâches de mesure, ou tout simplement de l'étonnement. Je vous présente une partie de ce qui est possible dans les chapitres suivants. Toutes les applications sont programmées en MicroPython. Mon collègue Andreas Wolter a également porté les programmes en C++ pour l'IDE Arduino. Bienvenue dans les

Histoires de petit sapin de Noël

Figure 1: Les histoires d'arbres

Figure 1: Les histoires d'arbres

Le contenu:

  1. La liste des composants
  2. Les logiciels
  3. On construit le petit arbre et on le câble
  4. Une illumination ciblée
  5. L'écran OLED pour des informations en texte clair
  6. Un éclairage étagé
  7. Le petit arbre enchanté
  8. Qui fait tant de bruit ?
  9. Sur la piste du vol de petits arbres
  10. Un bon séjour pour l'ESP32 et le DHT22/DHT11
  11. Un tirage au sort de Noël pas comme les autres
  12. L'application du sapin de Noël


1. La liste des composants

1

DIY LED Kit Sapin de Noël

1

Capteur modulaire KY - 009 RGB LED SMD ou

KY-016 FZ0455 3-Color RGB LED

1

Display OLED I2C 128x64 1,3 pouces

1

DHT22 AM2302 Capteur de température et humidité

1

KY-021 Switch Mini Magnet Reed

1

KeyCard RFID 13.56MHZ MF S50 (13.56 MHz) - Carte RFID 10x

1

Kit RFID RC522 avec lecteur, puce et carte pour Raspberry Pi

1

Breadboard Kit - 3x65pcs Jumper Wire Cable M2M and 3 x Mini Breadboard 400 Pins compatible avec Arduino et Raspberry Pi

1

KY-038 Sound Detection Module - Capteur de Son

1

Module ESP32 Nodemcu WLAN WiFi Development Board avec CP2102 (modèle successeur de ESP8266) compatible avec Arduino

1

GY-521 MPU-6050 Gyroscope 3 axes et accéléromètre alternative GY-61 ADXL335

Contact vibrant KY-020 ou KY-002 (*)

1

Câble Jumper 3 x 40 pcs. chacun 20 cm M2M/ F2M / F2F

3

Résistance 1.0KΩ

(*) L'utilisation des contacts vibrants ou du module GY-51 nécessitent une programmation différente.

2. Le logiciel

Pour le flashage et la programmation de l'ESP32 :

Tôt ou

μpycraft

pacetender Pour tester ESP32 / ESP8266 en tant que serveur UDP

Navigateur: Opéra ou Chrome

Firmware usagé:

Micrologiciel microphon

Veuillez choisir une version stable

Les programmes Microptron pour le projet:

Avec texte de licence

Pilote de périphérique:

Gy521rc.py

mfrc522.py

sh1106.py

Old.py

Fichiers de projet Microptron:

alarme

Noisy.py

rfid.py

roomclimate.pypypy

Amélioration.pypy

enchanted.py

webcontrol.py

Fichiers de projet Arduino IDE :

Remarque :
La version de l'IDE Arduino utilisée ici est 1.8.16
Celle de l'ESP32 Arduino Cores est 2.0.2


Pour utiliser l'ESP32 Core, vous devez saisir ce lien dans les préférences comme URL supplémentaire de l'administrateur de la carte :

https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json

Ensuite, via le menu Outils -> Board -> Boardverwalter, chercher ESP32 et l'installer.

Les bibliothèques utilisées, comme LittleFS, n'ont été intégrées que tardivement dans le core. Il est donc important de le mettre à jour si nécessaire.

I2c_scanner.ino

Oledtest.ino


Alarme

bruyant.ino

optionnel noisy_tinker_h.ino (avec ticker.h bibliothek)

rfid.ino

roomclimate.ino

Amélioration.

Enchanted.ino

Webcontrol.ino


MicroPython - Langage - Modules et programmes

Vous trouverez ici des instructions détaillées sur l'installation de Thonny. Vous y trouverez également une description de la manière dont le MicropythonFirmware est gravé sur la puce ESP.

MicroPython est un langage d'interprétation. La principale différence avec l'IDE Arduino, où vous flashez toujours et exclusivement des programmes entiers, est que vous ne devez flasher le firmware MicroPython qu'une seule fois au début sur l'ESP32, avant que le contrôleur ne comprenne les instructions MicroPython. Vous pouvez utiliser Thonny, µPyCraft ou esptool.py pour cela. Pour Thonny, j'ai décrit le processus ici.

Dès que le firmware est flashé, vous pouvez dialoguer avec votre contrôleur, tester des instructions individuelles et voir immédiatement la réponse, sans devoir compiler et transférer un programme entier. C'est précisément ce qui me dérange dans l'IDE Arduino. On gagne tout simplement énormément de temps lorsqu'on peut tester la syntaxe et le matériel, voire essayer et affiner des fonctions et des parties entières de programme via la ligne de commande avant de tricoter un programme. C'est dans ce but que j'aime créer de petits programmes de test. Ils constituent une sorte de macro regroupant des instructions récurrentes. Il arrive que des applications entières se développent à partir de ces fragments de programme.

Autostart

Si vous souhaitez que le programme démarre de manière autonome à la mise sous tension du contrôleur, copiez le texte du programme dans un fichier vierge que vous venez de créer. Enregistrez ce fichier sous boot.py dans l'espace de travail et téléchargez-le vers la puce ESP. Lors de la prochaine réinitialisation ou mise sous tension, le programme démarrera automatiquement.

Programmes de test

Les programmes sont lancés manuellement à partir de la fenêtre d'édition actuelle dans l'IDE Thonny en appuyant sur la touche F5. C'est plus rapide que de cliquer sur le bouton Start de la souris ou de passer par le menu Run. Seuls les modules utilisés dans le programme doivent se trouver dans la mémoire flash de l'ESP32.

Vous voulez utiliser l'IDE Arduino de temps en temps ?

Si vous souhaitez réutiliser le contrôleur avec l'IDE Arduino, il suffit de flasher le programme de la manière habituelle. Cependant, l'ESP32/ESP8266 aura alors oublié qu'il a déjà parlé MicroPython. Inversement, n'importe quelle puce Espressif contenant un programme compilé à partir de l'IDE Arduino, ou le firmware AT, ou LUA, ou ... peut sans problème être dotée du firmware MicroPython. Le processus est toujours le même que celui décrit ici.

3. On construit le petit sapin et on le câble

Il existe une video pour l'assemblage du petit sapin.

Bien sûr, une fois le petit arbre assemblé, on peut se détendre et admirer son œuvre. Pour plus de plaisir, nous pouvons modifier le processus de montage à certains endroits. À trois endroits dans la vidéo, nous devons procéder différemment pour notre projet. Les résistances de 4,7kΩ qui y sont mentionnées sont celles de 10kΩ dans le paquet de pièces. Et ces trois résistances par platine A et B ne sont soudées qu'aux endroits de la platine, comme le montrent les figures 2 et 3, l'autre extrémité de ces résistances reste pour l'instant libre. Nous souderons plus tard à ces extrémités libres des petits câbles fins (par exemple du ruban plat) pour la connexion à l'ESP32. Cela vaut pour les deux platines, A et B. Les condensateurs électrolytiques sont laissés de côté.

Figure 2: Partie A

Figure 2 : Partie A

Figure 3: Partie B

Figure 3: Partie B

Le reste du montage peut se faire exactement selon le modèle vidéo. Une fois la plaque de base en place, nous soudons les câbles aux extrémités libres des résistances 10kΩ. La longueur doit être comprise entre 25 et 30 cm. A l'autre extrémité du câble, nous soudons un morceau de barrette à broches pour que le tout soit enfichable.

Figure 4: La connexion d'arborescence

Figure 4 : The Tree-Connection

L'affectation des connexions sur l'arbre aux GPIO de l'ESP32 est visible dans le tableau 1. L'index se réfère à la liste des objets pin. Cette liste, nommée schicht, sert à l'adressage des couches de DEL par des boucles, comme nous le verrons plus loin. Les connexions sont réparties de manière à ce qu'un index pair soit toujours suivi de la couche de DEL de même rang sur la platine B. Bien entendu, il est toujours possible de procéder à des réaffectations à volonté.

Sapins

A1

B1

A2

B2

A3

B3

GPIO

32

26

33

27

25

12

index

0

1

2

3

4

5

Tableau 1 : Liens entre les arbres et l'ESP32

Figure 5: Câblage de base

Figure 5 : Câblage de base

Figure 6: Résistances de base sur la partie B - Détail, les extrémités libres sont supérieures à

Figure 6 : Résistances de base sur la partie B - détail, extrémités libres en haut

Figure 7: Câblé de la partie A

Figure 7 : Câblé sur la partie A

4. illumination ciblée

Le câblage est terminé ? Alors nous allons déjà allumer les LED sur le petit sapin. Nous alimentons le petit sapin soit par les piles, soit par le câble fourni à partir d'un port USB. Une fois allumé, il reste dans l'obscurité. Bien sûr, car les bornes de base des transistors sont libres, ce qui empêche le courant de base de circuler et, comme il n'y a pas non plus de courant de collecteur, les LED restent sombres.

Figure 8: L'un des 6 niveaux de transistors

Figure 8 : Un des 6 étages de transistors

Cela change lorsque les GPIO sont programmés comme sorties et que le niveau du potentiel GND est augmenté à 3,3V. Nous y parvenons en attribuant un 1 comme valeur. Dans le terminal de Thonny, nous entrons les lignes suivantes.

>>> from machine import Pin
>>> a1=Pin(32,Pin.OUT,value=0)
>>> a1.value(1)

Si le câblage est correct, les LED du niveau A1 commencent alors à s'allumer, pour s'éteindre après l'entrée de 

>>> a1.value(0)

s'éteindre. Contrairement à la version précédente du kit de l'arbre, la nouvelle version est équipée de LED scintillantes. Auparavant, il s'agissait de simples LED multicolores. Cela a un certain inconvénient, car il n'est plus possible de faire varier l'intensité des "flashing LEDs". Néanmoins, il est amusant de faire des expériences avec. Grâce aux six transistors, nous sommes désormais en mesure de démarrer ou d'éteindre les six niveaux exactement comme nous le souhaitons. Cela nous permet également d'influencer la luminosité globale.

La disposition des lumières est illustrée dans la figure 9. Elle est valable aussi bien pour la partie A que pour la partie B.

Figure 9: La distribution des LED est classée

Figure 9 : La répartition des LED se fait à tour de rôle.

La disposition des DEL, le câblage vers l'ESP32 et les connexions à l'ESP32 sont considérés comme acquis pour toutes les autres expériences. Ils n'apparaissent donc plus explicitement dans les descriptions et les schémas des circuits partiels.

5. L'écran OLED

L'écran OLED peut nous fournir des informations en texte clair, mais il peut aussi afficher des graphiques en noir et blanc. La programmation est simple si nous utilisons les modules logiciels MicroPython correspondants. Le pilote matériel SH1106 est directement responsable de l'écran 1,3'' et ne peut être utilisé que pour cela. Le module framebuf intégré dans le noyau MicroPython met à disposition des commandes graphiques et textuelles simples et le module oled.py nous fournit des commandes confortables pour la sortie de texte.

L'écran est commandé uniquement par les deux lignes du bus I2C. On crée un objet I2C et on le transmet au constructeur de la classe OLED. L'adresse de l'appareil matériel de l'écran est fixe et ancrée dans OLED comme une constante. Néanmoins, nous commençons par regarder ce qui est présent sur le bus. Ensuite, nous effaçons l'écran et affichons quelques lignes.

Le graphique de la figure 10 et le programme suivant en démontrent l'utilisation. Nous saisissons le programme dans l'éditeur Thonny, l'enregistrons et le lançons ensuite avec la touche de fonction F5.

Figure 10: L'OLED sur l'ESP32

Figure 10 : L'OLED sur l'ESP32

OLEDTEST.PY

 # OLED-Display-Demo
 #
 from machine import PinI2C
 from time import sleep
 from oled import OLED
 
 # Initialisieren der Schnittstelle **********************
 i2c=I2C(-1,scl=Pin(22),sda=Pin(21))
 print(i2c.scan())
 d=OLED(i2c)
 
 d.clearAll()
 d.writeAt("Der",0,0,False)
 d.writeAt("kleine",0,1,False)
 d.writeAt("Weihnachts-",0,2,False)
 d.writeAt("baum",0,3)
 sleep(4)
 d.clearFT(0,2,15,2,False)
 d.writeAt("Christ-",0,2)

Sortie:

[60]
this is the constructor of OLED class
Size:128x64

L'adresse de l'appareil de l'écran est en décimal 60 ou en hexadécimal 0x3C. Le constructeur de la classe OLED sait également que l'écran possède 128 x 64 pixels. Après l'édition des quatre lignes, 4 secondes plus tard, "Noël-" est remplacé par "Chist-". Auparavant, nous devons bien sûr supprimer cette ligne. N'hésitez pas à essayer les différentes commandes individuellement via REPL, la console de terminal de Thonny.

Pour Arduino IDE:

La bibliothèque U2G8 est utilisée pour l'affichage. Vous pouvez l'installer via la gestion des bibliothèques.

Télécharger oledtest.ino

6. Un éclairage étagé

Figure 11: Niveau1

Figure 11: Niveau1

Figure 12: Niveau2

Figure 12: Niveau2

Comme on peut commander séparément les différents niveaux de LED sur le petit arbre, nous en profitons pour monter et descendre du niveau le plus bas - OFF - jusqu'à la luminosité maximale. Nous n'avons pas besoin de modifier le circuit. L'écran nous informe du niveau actif.

steigerung.py

 # steigerung.py
 #
 import sys
 from machine import PinI2C
 from oled import OLED
 from time import sleep,ticks_msticks_ussleep_ms
 
 # Initialisieren der Schnittstellen **********************
 i2c=I2C(-1,scl=Pin(22),sda=Pin(21))
 d=OLED(i2c)
 
 # LED-Schichten einrichten *******************************
 #schichtPin = [32,33,25,27,26,12] # sortiert
 schichtPin = [32,26,33,27,25,12]   # verteilt
 schicht=[0]*6
 for i in range(6): # Ausgaenge erzeugen und auf 0
     schicht[i]=Pin(schichtPin[i],Pin.OUT)
     schicht[i].value(0)
 
 # Funktionen defnieren ***********************************
 def  switch(n,val):  # Ebene n ein-/ausschalten
     schicht[n].value(val)
 
 def stop():  # alle LED-Ebenen aus
     d.writeAt("GOOD BYE",4,3)
     for i in range(6):
         switch(i,0)
 
 def alle():  # alle LED-Ebenen ein
     for i in range(6):
         sleep_ms(300)
         switch(i,1)
     
 # Hauptprogramm ******************************************
 d.clearAll()
 d.rect(4,16,123,40,1)  # Rechteck in Pixelwerten
 for j in range(3):
     for i in range(6):
         d.writeAt("Ebene: {} ein".format(i),2,3)
         switch(i,1)
         sleep_ms(3000)
     for i in range(5,-1,-1):
         d.writeAt("Ebene: {} aus".format(i),2,3)
         switch(i,0)
         sleep_ms(3000)
 d.clearFT(2,3,14,3,False)
 stop()

On définit l'ordre des couches dans la liste schichtPin. Les objets pin sont créés selon ce modèle dans la boucle for suivante. Les fonctions switch(), stop() et alle() nous aident à rendre le programme plus clair. De plus, nous les utiliserons plusieurs fois dans les chapitres suivants.

Dans le programme principal, nous effaçons l'écran et dessinons un cadre. 4 et 16 sont les coordonnées en pixels du coin supérieur gauche, 123 et 40 la largeur et la hauteur en pixels et 1 la couleur blanche, il n'y a pas plus de couleurs. La boucle for externe compte le nombre total de passages. La première boucle for interne compte i en haut à des intervalles de 3 secondes et active les calques. La deuxième boucle compte à rebours et éteint à nouveau les LED.

La dernière sortie est supprimée et la fonction stop() éteint toutes les LED de manière fiable et prend congé avec un "GOOD BYE" cordial. 

La longueur de l'intervalle et le nombre de passages nous permettent de définir nous-mêmes le comportement des LED.

Pour Arduino IDE

Télécharger steigerung.ino

7. Le petit sapin enchanté

N'importe qui pourrait venir et vouloir allumer notre petit arbre. Mais non, ce n'est possible que grâce à nos mains magiques. Bien sûr, nous ne disons pas que nous avons caché une petite tige magnétique en néodyme dans chaque main. Pourquoi en avons-nous besoin ? Pour "faire de la magie", justement. Car nous avons entre-temps modifié notre circuit. Un contact Reed est désormais relié à la masse sur la broche GPIO 13. Dans le petit tube en verre se trouve un contact de commutation qui se ferme à l'approche d'un aimant.

Figure 13: Les moldages n'ont pas d'aimant, l'arbre reste sombre

Figure 13 : Les Muggles n'ont pas d'aimant, l'arbre reste sombre

Figure 14: Dans le marqueur jaune: contact et aimant

Figure 14 : Dans le repère jaune : contact et aimant.

Attention:

Le verre est très fragile et les fils sont très rigides. Ne les plie pas, sinon le verre se fend et on risque d'enterrer le composant.

Figure 15: Le contact Reed aide la magie

Figure 15 : Le contact Reed aide à faire de la magie

Il ne faut pas oublier autre chose, à savoir que l'OLED et l'arbre restent connectés comme décrit ci-dessus.

verzaubert.py

from os import uname
 import sys
 from machine import  PinI2C
 from oled import OLED
 from time import sleep_ms
 
 # Initialisieren der Schnittstellen **********************
 i2c=I2C(-1,scl=Pin(22),sda=Pin(21))
 d=OLED(i2c)
 
 taste=Pin(0,Pin.IN,Pin.PULL_UP)
 reed=Pin(13,Pin.IN,Pin.PULL_UP)
 
 # LED-Schichten einrichten *******************************
 schichtPin = [32,33,25,26,27,12]
 schicht=[0]*6
 for i in range(6):
     schicht[i]=Pin(schichtPin[i],Pin.OUT)
     schicht[i].value(0)
 
 # Funktionen defnieren ***********************************
 def  switch(n,val):
     schicht[n].value(val)
 
 def stop():
     d.writeAt("   MUGGLE     ",1,3)
     for i in range(6):
         switch(i,0)
 
 def alle():
     d.writeAt(" DUMBLEDOR   ",1,3)
     for i in range(6):
         sleep_ms(300)
         switch(i,1)
     
 # Hauptprogramm ******************************************
 d.clearAll()
 d.writeAt("Kannst du ...",0,0)
 d.writeAt("ZAUBERN???",0,1)
 while 1:
     if reed()==0:
         alle()
     else:
         stop()

Le contact Reed doit être placé de manière à ce que nous puissions facilement l'approcher avec notre aimant lorsque nous posons un petit arbre ou un breadboard sur la paume de notre main. Un gant noir fin nous aide à garder l'aimant invisible. Il est probable que tous ceux qui vous entourent soient des moldus.

Le programme est très simple. Jusqu'au programme principal, on connait déjà tout. La boucle while fonctionne sans fin jusqu'à la coupure du courant. Si le contact au voisinage de l'aimant est fermé, alors GPIO13 est au potentiel GND et toutes les lumières s'allument. Dans le cas contraire, la résistance intégrée au module tire le GPIO13 vers Vcc=3,3V et les lumières s'éteignent.

Pour que la magie opère mieux, le petit sapin avec breadboard doit être alimenté par la pile. La borne positive de la batterie doit être reliée à la broche Vin / 5V de l'ESP32. De plus, le programme doit être téléchargé en tant que boot.py sur l'ESP32 afin que le contrôleur démarre de manière autonome après la mise sous tension. La manière de procéder est décrite en détail dans le chapitre 2 - Autostart.

Pour Arduino IDE

Télécharger enchanté.ino

8. Avent et Noël, la période "staade".

Traduit dans le langage courant allemand, "Staad" signifie "calme", "contemplatif". Mais la vie quotidienne nous apprend que la période précédant Noël peut aussi être agitée. Lorsque les choses deviennent trop turbulentes, le petit arbre nous invite à baisser de quelques décibels. Comment fait-il ? Eh bien, il y a un module sonore qui capte le son et délivre le signal numérisé à l'ESP32.

Figure 16: Volume OK

Figure 16 : Volume OK

Figure 17: Trop fort

Figure 17 : trop fort

Figure 18: Machine sonore sur l'ESP32

Figure 18 : Moteur de son sur ESP32

noisy.py

 # noisy.py
 import esp32
 from os import uname
 from machine import TimerPinI2C
 from oled import OLED
 from time import timesleep,
 
 
 i2c=I2C(-1,scl=Pin(22),sda=Pin(21))
 d=OLED(i2c)
 
 # IRQ-Steuerung durch Soundmodul
 ST=Timer(1)
 sound=Pin(17,Pin.IN)
 #
 # LED-Schichten einrichten *******************************
 schichtPin = [32,33,25,27,26,12]
 L=len(schichtPin)
 schicht=[0]*L
 for i in range(L):
     schicht[i]=Pin(schichtPin[i],Pin.OUT)
     schicht[i].value(0)
 
 # Funktionen defnieren ***********************************
 def  switch(n,val):
     schicht[n].value(val)
 
 def stop():
     d.clearAll()
     d.writeAt("ALLES GUT",4,2)
     for i in range(L):
         switch(i,0)
 
 def alle():
     d.clearAll()
     d.writeAt("ZU LAUT!!",0,0)
     for i in range(L):
         sleep(0.5)
         switch(i,1)
 
 def soundDetected(pin):
     global n
     if pin==Pin(17):
         sound.irq(handler=None)
         if n:
             return
         n=True
         ST.init(period=15000mode=Timer.ONE_SHOTcallback=soundDone)
         print("begin",time())
         alle()
 
 def soundDone(t):
     global n
     n=False
     print("ende",time())
     stop()
     sound.irq(handler=soundDetectedtrigger=Pin.IRQ_FALLING)
 
 n=False
 sound.irq(handler=soundDetectedtrigger=Pin.IRQ_FALLING)

Le son est transmis par des variations rapides de la pression de l'air. Un signal sonore se propage à une vitesse d'environ 340 m/s. Dans une pièce, il ne subit pratiquement aucun retard perceptible. Le microphone du module sonore transforme les variations de pression en un signal électrique. Mais contrairement au contact Reed, il n'est plus possible de détecter ces oscillations en interrogeant le port GPIO, ce procédé est trop lent. C'est pourquoi on utilise ici une autre technique, la programmation par interruption. Une interruption est l'interruption d'un programme par un événement spécifique. Nous allons utiliser deux sources d'interruption différentes. L'une déclenche une interruption lorsque le niveau sur une broche GPIO change, de 0 à 1 ou inversement. L'autre source d'IRQ est une minuterie matérielle de l'ESP32. Elle déclenche l'IRQ lorsque le réveil sonne.

Les deux se passent maintenant la balle à tour de rôle. Le GPIO17 attend un signal du module son. Si un front descendant arrive, la fonction soundDetected() démarre et vérifie d'abord si elle est signifiée en raison du paramètre pin passé. Si n est True, un cycle est déjà en cours et il n'y a rien d'autre à faire. Si au contraire n est False, alors il s'agit d'une commande fraîche. L'IRQ pin-change est désactivée et n est mis à True afin de supprimer les impulsions immédiatement suivantes sur GPIO17. Ensuite, le timer est lancé, qui définit la durée de fonctionnement de l'éclairage de l'arbre. L'éclairage est allumé en appelant alle().

Si le timer est écoulé, l'interruption correspondante est déclenchée, ce qui lance la fonction soundDone(). n est mis à False, les lumières s'éteignent et l'IRQ Pin-Change est réarmé.

Le programme principal ne comporte que deux lignes. n est défini sur False pour que l'IRQ Pin-Change activée ensuite puisse être déclenchée

Ce qui est intéressant, c'est que les IRQ restent actives, même si le programme principal est déjà terminé. Pour désactiver cela, l'ESP32 doit être réinitialisé avec le bouton STOP/RESTART.

Pour Arduino Ide

Télécharger noisy.ino ou optionnellement noisy_ticker_h.ino (avec la librairie Ticker.h)

Deux variantes ont été utilisées ici pour implémenter les interruptions du timer. Dans le noisy.ino, seules des variables binaires sont commutées dans chacune des deux routines de service d'interruption. Le changement est ensuite détecté dans la boucle principale normale. Une alternative consiste à intégrer la bibliothèque Ticker.h. Ses réglages sont un peu plus faciles à effectuer. Elle est incluse dans le ESP32 Core. Il n'est pas nécessaire de l'installer séparément. Si elle n'est pas trouvée lors de la compilation, vous devrez éventuellement mettre à jour le noyau ESP32.

9. Sur la piste du vol de sapins

Il paraît qu'il y a des gens qui volent les sapins de Noël - dans la forêt. Eh bien, chers gardes forestiers, faites comme nous et installez dans vos petits arbres un gardien comme celui que nous allons décrire.

OK, ce sera certainement aussi difficile que pour la surveillance d'autres interdictions, si le personnel manque. Pourquoi interdire quelque chose si on ne peut pas le contrôler ? Quoi qu'il en soit.

Notre petit arbre se voit attribuer un surveillant, à savoir lui-même ! Il est aidé par un capteur qui vient d'un tout autre monde. Le module GY-521 utilisé avec le composant MPU6050 est un accéléromètre avec gyroscope. Il permet de mesurer les accélérations, les forces et les rotations. Oui, et si l'on veut enlever quelque chose, il faut le soulever et le mettre en mouvement. Dans les deux cas, on accélère l'objet, même en le faisant basculer.

Figure 19: L'inclinaison de la lumière est suffisante pour déclencher l'alarme

Figure 19 : Un léger basculement suffit à déclencher l'alarme.

Même de très faibles changements de lieu produisent des forces et donc font réagir notre capteur. Le reste est simple : le déclenchement est suivi de l'éclairage du petit arbre et le voleur potentiel prend la fuite, espérons-le. La durée de l'alarme est à nouveau déterminée par une interruption de la minuterie.

Figure 20: Unité Antiklau

Figure 20 : Unité antivol

alarm.py

 # alarm.py
 # RED-ALLERT by movement
 import esp32
 from os import uname
 from machine import TimerPinI2C
 from oled import OLED
 from time import sleep,ticks_msticks_ussleep_ms
 from gy521rc import GY521
 
 # Initialisieren der Schnittstellen **********************
 i2c=I2C(-1,scl=Pin(22),sda=Pin(21))
 d=OLED(i2c)
 
 AT=Timer(0)
 acc=GY521(i2c)
 limit=36
 dauer=5000
 
 schichtPin = [32,33,25,27,26,12]
 L=len(schichtPin)
 schicht=[0]*L
 for i in range(L):
     schicht[i]=Pin(schichtPin[i],Pin.OUT)
     schicht[i].value(0)
 
 # Funktionen defnieren ***********************************
 def TimeOut(t):
     start=ticks_ms()
     def compare():
         return int(ticks_ms()-start>t
     return compare
 
 def  switch(n,val):
     schicht[n].value(val)
 
 def stop():
     d.clearAll()
     d.writeAt("ALLES GUT",4,2)
     for i in range(L):
         switch(i,0)
 
 def alle():
     d.clearAll()
     d.writeAt("DIEBSTAHL",0,0)
     for i in range(L):
         sleep(0.5)
         switch(i,1)
 
 def hasMoved(delay):
     xs,ys,zs=0,0,0
     for i in range(100):
         x,y,z=acc.getXYZ()
         xs+=x
         ys+=y
         zs+=z
     xs//=100
     ys//=100
     zs//=100
     #print(xs,ys,zs)
     n=0
     while 1:
         x,y,z=acc.getXYZ()
         x=abs(xs-x)
         y=abs(ys-y)
         z=abs(zs-z)
         #print(x,xs//limit)
         if x > abs(xs//limit) :
             print("*******",n)
             n+=1
             alle()
             # Optional Nachricht via UDP
             AT.init(period=delaymode=Timer.ONE_SHOTcallback=alertDone)
         sleep(0.3)
 
 def alertDone(t):
     stop()
 
 print("Diebstahlschutz gestartet")
 hasMoved(dauer)

On retrouve à nouveau de bonnes connaissances dans le programme. Mais la nouveauté réside dans l'initialisation du GY521. Pour le module, nous devons télécharger un autre module vers l'ESP32, gy521rc.py. La classe qu'il contient porte le même nom que le module.

Comme l'écran OLED, le GY521 est commandé par le bus I2C. Nous transmettons au constructeur le même objet I2C, définissons le seuil de déclenchement de l'alarme et sa durée en millisecondes.

Le seuil est la valeur absolue de l'écart de la valeur mesurée par rapport à la valeur moyenne de la mesure d'accélération dans la direction x. Le capteur est orienté de manière à ce que l'axe x positif soit orienté verticalement vers le haut. La valeur mesurée se situe autour de 16000 counts et correspond dans ce cas à l'accélération de la pesanteur g=9,81m/s².

La fonction hasMoved() représente ici la boucle principale. A l'entrée, la valeur moyenne est déterminée par 100 mesures. Il est évident que le capteur ne doit pas bouger.

Ensuite, on passe à la boucle principale. L'accélération actuelle est mesurée et les écarts par rapport aux valeurs moyennes sont calculés. Si la différence dépasse la limite prédéfinie, l'alarme est déclenchée et la minuterie activée. L'alarme signifie que l'arbre passe en pleine luminosité.

La routine de service de l'IRQ de la minuterie éteint les lumières. La solution via l'IRQ fait en sorte que le circuit soit à nouveau armé immédiatement après le déclenchement de l'alarme. Si la durée de l'alarme était fixée par une instruction sleep dans la boucle principale, le circuit serait mort pendant cette durée.

Les contacts vibrants mentionnés dans la liste des pièces seraient raccordés à l'ESP32 de manière similaire au contact Reed, mais ne permettent pas de régler la sensibilité.

Pour Arduino IDE

Télécharger alarm.ino

Pour le capteur gyroscopique, la bibliothèque GY521 est intégrée ici. Celle-ci peut également être installée via la gestion des bibliothèques. De plus, la bibliothèque Ticker.h est également utilisée ici. Contrairement au modèle MicroPython, tous les axes du capteur sont considérés ici.

Si vous n'êtes pas sûr de l'adresse I2C utilisée par le capteur, vous pouvez charger le programme I2C_Scanner.ino sur l'ESP. Dans ce cas, deux adresses devraient être affichées dans le moniteur série (pour l'écran et le capteur). Sur le capteur lui-même, vous avez la possibilité de choisir entre les adresses 0x68 et 0x69. Pour cela, vous devez relier la broche A0 soit à GND, soit à 3.3V de l'ESP.

10. Séjour agréable ESP32 et DHT22

Un climat ambiant agréable fait partie de l'ambiance des fêtes. Dans cette application simple, l'ESP32 ne peut pas modifier le climat ambiant, mais il peut en rendre compte. Les valeurs exactes de la température et de l'humidité de l'air sont affichées sur l'écran OLED, les valeurs approximatives nous sont communiquées par le petit arbre. Il indique les valeurs de la température ambiante par paliers de 2 degrés grâce à différents niveaux de LED activés.

Figure 21: température moyenne - demi-éclairage

Figure 21 : température moyenne - demi-éclairage

Figure 22: Mesure de la température et de l'humidité dans un module

Figure 22 : Mesure de la température et de l'humidité de l'air dans un module.

Il existe deux variantes du DHT22, alias AM2302. Le module de l'illustration de gauche contient déjà la résistance pull-up nécessaire pour le bus One-Wire, qui ne doit d'ailleurs pas être confondu avec le système du module Dallas DS18B20. Le bus Dallas a un timing très différent. Pour la version nue de l'illustration de droite, il faut installer une résistance de 4,7kΩ à 10kΩ contre Vcc.

L'utilisation dans le programme est très simple. Les trois commandes nécessaires sont fournies par le module dht déjà intégré dans MicroPython..

roomclimate.py

 import esp32, dht
 from os import uname
 import sys
 from machine import PinI2C
 from oled import OLED
 from time import sleep
 
 # Initialisieren der Schnittstellen **********************
 i2c=I2C(-1,scl=Pin(22),sda=Pin(21))
 d=OLED(i2c)
 
 taste=Pin(0,Pin.IN,Pin.PULL_UP)
 dhtPin=Pin(13)
 dht22=dht.DHT22(dhtPin)
 
 # LED-Schichten einrichten *******************************
 schichtPin = [32,33,25,26,27,12]
 schicht=[0]*6
 for i in range(6):
     schicht[i]=Pin(schichtPin[i],Pin.OUT)
     schicht[i].value(0)
 
 # Funktionen defnieren ***********************************
 def  switch(n,val):
     schicht[n].value(val)
 
 def stop():
     d.writeAt("TEMP TO LOW",4,2)
     for i in range(6):
         switch(i,0)
 
 def alle():
     for i in range(6):
         sleep_ms(300)
         switch(i,1)
         
 def tree(n):
     for i in range(6):
         if i <=n:
             switch(i,1)
         else:
             switch(i,0)
     
 # Hauptprogramm ******************************************
 d.clearAll()
 d.writeAt("***RAUMKLIMA***",0,0)
 while True:
     sleep(0.3)
     dht22.measure()
     t=dht22.temperature()
     h=dht22.humidity()
     d.rect(0,10,126,38,1)
     d.clearFT(1,2,14,3)
     d.writeAt("TEMP: {:.1f} *C".format(t),1,2)
     d.writeAt("HUM : {:.1f} %".format(h),1,3)
     tree(int(((t-15)//2)%6))
     sleep(2.7)

Outre les suspects habituels, le programme ne propose comme nouveautés que l'importation du module dht, l'instanciation de l'objet dht22 et la boucle principale avec l'ordre de mesure dht22.measure() et la lecture de la valeur de température et d'humidité. Nous connaissons déjà la sortie à l'écran et l'affichage de l'arbre. Ce qui est peut-être intéressant et peu visible, c'est la conversion de la température de °C en indice de niveau d'éclairage. par le terme int(((t-15)//2)%6). A partir de la valeur du quotient de la division entière de l'écart de la température de 15 °C vers le haut et 2, on détermine le reste de la division par 6 et, pour plus de sécurité, on le représente sous forme d'un nombre entier. Encore une fois, très lentement.

Example:
t = 18 °C
18-15 =3
3//2 = 1
1 % 6 =1 also Stufenindex 1

pour 28°C, on obtiendrait : 28-15=13 ; 13//2=6 ; 6%6 = 0 ; La dernière étape est nécessaire parce qu'il n'y a pas de niveau avec le numéro 6.

Pour Arduino IDE

Télécharger roomclimate.ino

Pour ce capteur, veuillez installer la DHT sensor library et la Adafruit Unified Sensor Library via la gestion des bibliothèques. Contrairement à Python, il faut faire très attention aux types de données en C/C++. Les valeurs de mesure du capteur sont retournées de type float. En conséquence, il faut configurer la sortie formatée et le calcul pour la commande des LEDs peut également être erroné si l'on fait abstraction du type de données.

11. Le tirage au sort de Noël (un peu différent)

Je me souviens de cette pratique à l'école. Chacun apportait un paquet pendant la période de l'Avent et la semaine avant les vacances, on lançait la tombola - chaque billet était gagnant.

J'ai choisi des cartes RFID neutres comme lots recyclables. Le tirage au sort est effectué par l'ESP32 en même temps que le kit RFID. Il n'y a que les gains dont il faut s'occuper soi-même. Bien entendu, le petit arbre est également de la partie. Grâce à sa luminosité, il annonce son gain à chaque joueur. Pour qu'il n'y ait aucun doute quant à l'interprétation, l'écran indique sans équivoque le lieu de chaque tirage : Fribourg, Berlin, Hambourg ... Six cartes de tirage et une carte maîtresse sont nécessaires.

Figure 23: La hauteur du numéro de déclenchement détermine la résistance lumineuse

Figure 23 : La hauteur du numéro d'impact détermine l'intensité lumineuse

Figure 24: Loterie

Figure 24 : Loterie

Figure 25: Reader de la carte RFID

Figure 25 : Lecteur de carte RFID

En raison du bus SPI, le câblage est un peu plus complexe que pour le bus I2C avec ses deux lignes. Les appareils du bus SPI n'ont pas d'adresse d'appareil matériel, mais ils ont une connexion Chip-select qui doit être sur LOW si l'appareil doit être adressé. Le transfert de données est également un peu différent, l'envoi et la réception se font toujours simultanément. Il n'est pas nécessaire de préciser la procédure ici, car la classe MFRC522 s'en charge pour nous. Nous indiquons seulement au constructeur l'affectation des ports et la vitesse de transfert. Le transfert fonctionne à une vitesse de 3,2 MHz. À titre de comparaison, l'I2C fonctionne à 400 kHz.

La fonction readUID() lit l'identifiant unique de la carte et le renvoie sous forme de valeur hexadécimale et de nombre décimal. Les cartes sont demandées via l'écran OLED. Pour que la fonction ne bloque pas l'ensemble du processus, un timeout assure un retrait ordonné. Dans ce cas, la valeur None est renvoyée au lieu de l'ID de la carte.

Figure 26: Cartes et puces RFID

Figure 26 : Cartes RFID et puce

Pour que les cartes de tirage au sort entrent en jeu, nous avons besoin d'une carte maîtresse. Pour ce faire, nous prenons une carte ou une puce quelconque dans la pile, lisons son ID et l'utilisons pour attribuer la valeur décimale à la variable dès le début du programme :

MasterID=4217116188.

Lors du premier démarrage, l'ESP32 constate qu'il n'existe pas encore de fichier contenant les données de la carte de lot et demande la carte maître. Une fois celle-ci reconnue, une carte de lot est demandée. Après la lecture de l'ID, celle-ci est écrite dans le fichier et la carte maître est à nouveau demandée. La lecture se poursuit jusqu'à la dernière carte de lot. Si aucune carte de jeu n'est proposée pendant 10 secondes après la demande de la carte maîtresse, le système redémarre de lui-même. Pour cela, il faut que le programme rfid.py ait été envoyé à l'ESP32 en tant que boot.py. Le chapitre 2 - Autostart explique exactement comment procéder. Pour repartir complètement à zéro, nous pouvons supprimer le fichier slavecards.txt contenant les ID des cartes de lot via la console Thonny. Après une réinitialisation, les cartes de lot peuvent alors être lues à nouveau.

rfid.py

 # rfid.py
 # workes with RC522 13,2MHz
 import mfrc522
 import esp32dht
 from os import uname
 from machine import TimerPinI2CADCreset
 from oled import OLED
 from time import sleep,ticks_msticks_ussleep_ms
 from gy521 import GY521
 
 # Initialisieren der Schnittstellen **********************
 if uname()[0] == 'esp32':
     #                     sck, mosi, miso, cs=sda
     rdr = mfrc522.MFRC522(14,  16,   15,   5baudrate=3200000)
 elif uname()[0] == 'esp8266':
     #                     sck, mosi, miso, cs=sda
     #                     D3   D4   D2   D5
     rdr = mfrc522.MFRC522(0,   2,    4,    14baudrate=100000)
 else:
     raise RuntimeError("Unsupported platform")
 MasterID=4217116188  # 0XFB5C161C
 
 i2c=I2C(-1,scl=Pin(22),sda=Pin(21))
 d=OLED(i2c)
 
 schichtPin = [32,33,25,27,26,12]
 schicht=[0]*6
 for i in range(6):
     schicht[i]=Pin(schichtPin[i],Pin.OUT)
     schicht[i].value(0)
 gewinn=[
         "Freiburg",
         "Berlin",
         "Hamburg",
         "Augsburg",
         "Ratzeburg",
         "Erfurt",
         "Essen",
         "Bonn",
        ]
 # Funktionen defnieren ***********************************
 def TimeOut(t):
     start=ticks_ms()
     def compare():
         return int(ticks_ms()-start>t
     return compare
 
 def readUID(display,kartentyp,timeout):
     display.clearFT(0,1,15,show=False)
     display.writeAt("Put on "+kartentyp,0,1)
     readTimeOut=TimeOut(timeout)
     while not readTimeOut():
        (stattag_type) = rdr.request(rdr.REQIDL)
         if stat == rdr.OK:
            (statraw_uid) = rdr.anticoll()
             if stat == rdr.OK:
                 display.clearFT(0,2,15,show=False)
                 display.writeAt("Card OK",0,2)
                 sleep(1)
                 userID=0
                 for i in range(4):
                     userID=(userID<<8| raw_uid[i]
                 userIDS="{:#X}".format(userID)
                 print(userIDS)
                 return userID,userIDS
     return None
 
 def addUID(display):
     display.clearAll()
     m=readUID(display,"Master",3000)
     if m is not None:
         mid,_m
         if mid==MasterID:
             sleep(3)
             u=readUID(display,"Slavecard",3000)
             if u is not None:
                 uid,uids=u
                 if uid is not None and uid !MasterID:
                     with open("slavecards.txt","a"as f:
                         f.write("{}\n".format(uids))
                         display.writeAt("New slave written",0,3)
                         sleep(3)
                         return True
             else:
                 display.writeAt("ERROR!!!",0,3)
                 display.writeAt("Card not added!",0,4)
                 return False
         else:
             display.writeAt("ERROR!!!",0,3)
             display.writeAt("Not mastercard",0,4)
             sleep(3)
     return False
 
 def  switch(n,val):
     schicht[n].value(val)
 
 def stop():
     d.writeAt("GOOD BYE",4,2)
     for i in range(6):
         switch(i,0)
 
 def alle():
     for i in range(6):
         sleep_ms(300)
         switch(i,1)
         
 def tree(n):
     for i in range(6):
         if i <=n:
             switch(i,1)
         else:
             switch(i,0)
     
 # ******************* Hauptprogramm *********************
 d.clearAll()
 d.writeAt("*XMAS LOTTERIE*",0,0)
 d.rect(0,20,127,28,1)
 cards=[]
 try:
     with open("slavecards.txt","r"as f:
         for line in f:
             cards.append(line.strip("\n"))
     closed=TimeOut(60000)
     while not closed():
         u=readUID(d,"LOSKARTE",5000)
         d.clearFT(1,3,14,4,False)
         if u is not None:
             uid,uids=u
             try:
                 n=cards.index(uids)
                 d.writeAt("TREFFER {}".format(n),1,3False)
                 d.writeAt(gewinn[n],1,4)
             except ValueError as e:
                 d.writeAt("TROSTPREIS",1,3)
                 n=-1
             tree(n)
             closed=TimeOut(60000)
             sleep(10)
             stop()
 except OSError as e:
     print("keine Datei, keine Daten!")
     allRead=TimeOut(10000)
     while not allRead():
         if addUID(d):
             allRead=TimeOut(10000)
     print("Alle Karten eingelesen und gespeichert")
     d.clearFT(0,3,15,4,False)
     d.writeAt(" ALL CARDS READ",0,3)
     d.writeAt("**R E B O O T**",0,4)
     reset()
 d.clearFT(0,1,15,3,False)
 d.writeAt("Lotterie neu",0,2)
 d.writeAt("starten",0,3)

Pour un cycle de jeu, 6 gains sont déterminés, les 6 cartes sont mélangées et distribuées et le nouveau cycle est lancé avec la touche PROG de l'ESP32.

Pour Arduino IDE

Télécharger rfid.ino

Remarque : dans ce cas, les connexions aux broches du ESP32 doivent être modifiées. La raison en est que l'interface matérielle SPI est utilisée. Ses broches ne peuvent pas être modifiées.

MOSI  =  23
MISO  =  19
SCK   =  18

SDA  =  5
RST  = 17

Pour le scanner RFID, veuillez installer la bibliothèque MFRC522.

Pour une comparaison directe avec MicroPython, vous pouvez modifier cette ligne afin de ne pas déplacer les broches à chaque fois :

rdr = mfrc522.MFRC522(14,  16,   15,   5baudrate=3200000)

dans

rdr = mfrc522.MFRC522(18,  23,   19,   5baudrate=3200000)

Pour enregistrer le fichier texte avec les identifiants, un système de fichiers est créé dans la mémoire flash de l'ESP à l'aide de la bibliothèque LittleFS (le successeur de SPIFS). Des fichiers peuvent alors y être déposés. La bibliothèque fait désormais partie du cœur de l'ESP32. Il n'est pas non plus nécessaire de l'installer séparément. Le programme est écrit de manière à ce que vous n'ayez pas à traiter le fichier texte sur le PC.

Si vous souhaitez tout de même le faire, vous pouvez installer le plugin ESP32 Upload. L'échange de données ne fonctionne alors que dans un sens (comme son nom l'indique).

La structure du programme a été quelque peu modifiée ici. Le comportement du programme devrait toutefois rester le même. Quelques fonctions ont encore été ajoutées. Il est possible de lire la carte maître au démarrage du programme. De plus, il est possible de supprimer le fichier texte de la flash. Pour cela, vous pouvez relier un câble ou un bouton à la broche indiquée (voir code source) à GND. Maintenez-le enfoncé et redémarrez l'ESP. Le fichier est alors supprimé, s'il existe. Ensuite, coupez la connexion et relisez les cartes gagnantes. Le nombre maximal de cartes gagnantes correspond aux niveaux de LED du sapin de Noël. Si vous souhaitez utiliser moins de cartes, vous pouvez attendre le timeout lors du processus de lecture.

12. Le petit sapin sur le LAN/WLAN

Complétons la douzaine et connectons le petit sapin au réseau. En effet, si un ESP32 est utilisé pour la commande, il faut aussi un accès LAN ou WLAN pour la commande. J'ai opté pour la réalisation d'un serveur web sur l'ESP32, car les niveaux du petit arbre peuvent alors être contrôlés avec presque n'importe quel navigateur. Un serveur UDP sur le contrôleur et une application pour téléphone portable auraient pu être envisagés comme alternatives. Mais cela aurait dépassé le cadre de ce blog et j'y ai donc renoncé. Pour les personnes intéressées, j'ai déjà décrit un tel type de contrôle dans d'autres articles, par exemple ici et ici.

Pour le circuit, nous avons besoin du montage du chapitre 5, auquel nous ajoutons une LED RGB et trois résistances de 1,0 kΩ.

Figure 27: Structure Web

Figure 27: Structure du site web

Après avoir importé les modules nécessaires, nous définissons les broches pour la LED RGB, qui nous indique l'état du réseau de manière visible à grande distance. Vient ensuite le choix du mode de fonctionnement du réseau, WLAN ou point d'accès propre à ESP32. Le WLAN est préréglé par défaut. Pour accéder au routeur WLAN, il faut également indiquer ici les données d'accès. La définition des couches est complétée par trois listes, texte en clair pour on/off, couleur de fond pour le tableau dans la page web et les états de commutation des couches.

Dans les services, les fonctions bloquantes ont été supprimées : seuils(), seuils de sortie(), vague() et arbre(). Les nouvelles fonctions sont hexMac(), blink(), ledsOff() et web_page(). hexMac donne l'adresse MAC de l'ESP32 en mode station, blink() signale l'état du réseau et du serveur. ledsOff() éteint la LED RGB et web_page() décortique les requêtes du navigateur, exécute les ordres et renvoie une réponse sous forme de texte de page web.

La demande du navigateur est envoyée au serveur sous la forme d'une chaîne de requête. La chaîne a la forme ?a, ?p ou ?e=x&v=y. Dans cette chaîne, x représente le numéro de niveau et y l'état de commutation, 0 ou 1.

web_page() convertit la requête en majuscules, vérifie d'abord la présence de "A" et de "P". Si la requête contient plus de 2 caractères, elle essaie de déterminer le niveau et l'état de commutation. Si une erreur se produit, aucune action n'est déclenchée et la page d'accueil nue est appelée. Cela se produit également si aucune chaîne de requête n'est indiquée. Ensuite, la page web est construite sous forme de chaîne de caractères et renvoyée à la boucle principale.

Après les définitions de fonctions, la connexion au réseau est établie, soit comme point d'accès propre, soit comme connexion au routeur WLAN. Cela est contrôlé par les deux variables ownAP et WLANconnect. Dans les deux cas, une adresse IP fixe (10.0.1.181) est attribuée, puisqu'il s'agit d'un serveur. Les adresses dynamiques du routeur WLAN ne sont pas appropriées, car elles peuvent changer d'une fois à l'autre. L'établissement de la connexion avec le routeur est marqué par le clignotement de la LED bleue. L'écran nous informe que la connexion est établie et que le socket de connexion s est également prêt à recevoir des demandes.

Dans la boucle principale, la boucle de réception de la méthode accept() attend une demande. Si rien n'arrive avant le timeout, accept() lance une exception que nous interceptons avec le try précédent.

S'il y a une demande, accept() renvoie un socket de communication c et l'adresse de la machine qui a fait la demande. c sert à gérer l'échange de données entre le client et le serveur, tandis que s est à nouveau libre pour accepter d'autres requêtes entrantes. La méthode c.recv() renvoie le texte de la requête, dont seuls les premiers caractères nous intéressent. Pendant la phase de développement, il est possible d'entrer des requêtes à la main pour tester l'analyseur web_page(). ownAP et WLANconnect doivent alors tous deux être définis sur False.

L'objet octet request du texte reçu est maintenant décodé en une chaîne de caractères r, plus facile à manipuler. Nous recherchons un "GET /" au tout début de la chaîne r et la position à laquelle " HTTP" suit. Si les deux sont trouvés, nous isolons le texte après le "/" de "GET" jusqu'à l'espace de " HTML" et l'envoyons comme chaîne de requête à l'analyseur syntaxique web_page(). Nous recevons sa réponse dans la variable response. Ensuite, nous renvoyons l'en-tête HTML et le texte de la page HTML avec la réponse contenue à l'appelant. Les deux else et except qui suivent servent à intercepter et à traiter les éventuelles erreurs. Le dernier c.close(), qui ferme le socket de communication c, est important.

Après une demande de touche pour l'interruption du programme, la LED verte nous indique par son bref clignotement comme un battement de cœur que le système est encore en vie.

webcontrol.py

 # webcontrol.py
 # Fernsteuerung vom Browser via TCP
 # (C) 2021 Jürgen Grzesina
 # released under MIT-License (MIT)
 # http://www.grzesina.de/az/weihnachtsbaum/MIT-License.txt
 #
 from machine import PinI2C
 from oled import OLED
 
 # ******************** Network stuff ********************
 from time import sleep,ticks_mssleep_ms
 try:
   import usocket as socket
 except:
   import socket
 import ubinascii
 import network
 
 statusLed=Pin(18,Pin.OUT,value=0# blau=2
 onairLed=Pin(19,Pin.OUT,value=0)  # gruen=1
 errorLed=Pin(23,Pin.OUT,value=0)  # rot=0
 led=[errorLed,onairLed,statusLed ]
 red,green,blue=0,1,2
 request = bytearray(50)
 response=""
 taste=Pin(0,Pin.IN,Pin.PULL_UP)
 
 # Auswahl der Betriebsart Netzwerk oder Tastatur:
 # --------------------------------------------------------
 # Netzwerk: Setzen Sie genau !_EINE_! Variable auf True
 WLANconnect=True  # Netzanbindung ueber lokales WLAN
 ownAP=False       # Netzanbindung ueber eigenen Accessppoint
 # beide False ->> Befehlseingabe ueber PC + USB in Testphase
 # Falls WLANconnect=True:
 # Geben Sie hier die Credentials Ihres WLAN-Accesspoints an
 mySid = 'YOUR_SSID'myPass = "YOUR_PASSWORD"
 myIP="10.0.1.181"
 myPort=9002
 
 # Initialisieren der Schnittstellen **********************
 i2c=I2C(-1,scl=Pin(22),sda=Pin(21))
 d=OLED(i2c)
 
 #schichtPin = [32,33,25,27,26,12] # sortiert
 schichtPin = [32,26,33,27,25,12]   # verteilt
 schicht=[0]*6
 for i in range(6):
     schicht[i]=Pin(schichtPin[i],Pin.OUT)
     schicht[i].value(0)
 zustand=["aus","an "]
 color=["red","lightgreen"]
 eState=[0,0,0,0,0,0]
 
 connectStatus = {
     1000"STAT_IDLE",
     1001"STAT_CONNECTING",
     1010"STAT_GOT_IP",
     202:  "STAT_WRONG_PASSWORD",
     201:  "NO AP FOUND",
     5:    "GOT_IP"
    }
 
 # Funktionen defnieren ***********************************
 def TimeOut(t):
     start=ticks_ms()
     def compare():
         return int(ticks_ms()-start>t
     return compare
 
 def  switch(n,val):
     schicht[n].value(val)
 
 def stop():
     d.writeAt("ALL LEDS OFF",2,5)
     for i in range(6):
         switch(i,0)
 
 def alle():
     d.writeAt("ALL LEDS ON ",2,5)
     for i in range(6):
         sleep_ms(300)
         switch(i,1)
         
 def tree(n):
     d.writeAt("TREE PROGR. ",2,5)
     for i in range(6):
         if i <=n:
             switch(i,1)
         else:
             switch(i,0)
 
 def hexMac(byteMac):
   """
  Die Funktion hexMAC nimmt die MAC-Adresse im Bytecode  
  entgegen und bildet daraus einen String fuer die Rueckgabe
  """
   macString =""
   for i in range(0,len(byteMac)):     # Fuer alle Bytewerte
     macString += hex(byteMac[i])[2:]  # ab Position 2 bis Ende
     if i <len(byteMac)-1 :            # Trennzeichen
       macString +="-"
   return macString
 
 def blink(pulse,wait,col,inverted=False):
     if inverted:
         led[col].off()
         sleep(pulse)
         led[col].on()
         sleep(wait)
     else:
         led[col].on()
         sleep(pulse)
         led[col].off()
         sleep(wait)
 
 def ledsOff():
     for i in range(3):
         led[i].value(0)
 
 def web_page(q):
     global eState
     q=q.upper()
     print("Anfrage: ",q)
     if q=="?A":
         alle()
         for i in range(6):
             eState[i]=1
     elif q=="?P":
         stop()
         for i in range(6):
             eState[i]=0
     elif len(q)>2:
         try:
             ebene,state=q[1:].split("&")
             _,ebeneebene.split("=")
             _,statestate.split("=")
             ebene=(int(ebeneif 0<=int(ebene)<=5 else 0)
             state=(int(stateif 0<=int(state)<=1 else 0)
             switch(ebene,state)
             eState[ebene]=state
         except:
             pass
     else:
         pass
     antwort="<tr>"
     for i in range(6):
         h="<td bgcolor={}><H3>E{} {}.</H3></td>".format(color[eState[i]],izustand[eState[i]])
         antwort=antwort+h
     antwort=antwort+"</tr>"
     html1 = """<html>
    <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    <h2>Hallo, <br>ich bin dein Weihnachtsb&auml;umchen</h2>"""
     html2="""<table border=2 cellspacing=2>
    """
     html3="""
    <tr>
    <td>
    <a href='http://10.0.1.181:9002/?e=0&v=1'><H3>E0 An </H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?e=1&v=1'><H3>E1 An </H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?e=2&v=1'><H3>E2 An </H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?e=3&v=1'><H3>E3 An </H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?e=4&v=1'><H3>E4 An </H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?e=5&v=1'><H3>E5 An </H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?a'><H3>ALLE AN </H3> </a>
    </td>
    </tr>
    <tr>
    <td>
    <a href='http://10.0.1.181:9002/?e=0&v=0'><H3>E0 Aus</H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?e=1&v=0'><H3>E1 Aus</H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?e=2&v=0'><H3>E2 Aus</H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?e=3&v=0'><H3>E3 Aus</H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?e=4&v=0'><H3>E4 Aus</H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?e=5&v=0'><H3>E5 Aus</H3> </a>
    </td>
    <td>
    <a href='http://10.0.1.181:9002/?p'><H3>ALLE AUS</H3> </a>
    </td>
    </tr>
    """
     html9 = "</table> </body> </html>"
     html=html1+html2+antwort+html3+html9
     return html
 
 if taste.value()==0:
     print("Mit Flashtaste abgebrochen")
     ledsOff()
     d.writeAt("Abbruch d. User ",0,5)
     sys.exit()    
 
 # ********************************************************
 # Netzwerk einrichten
 # ********************************************************
 # Eigener ACCESSPOINT
 # ********************************************************
 if ownAP and (not WLANconnect):
     #
     nic = network.WLAN(network.AP_IF)
     nic.active(True)
     ssid="christbaum"
     passwd="don't_care"
 
     # Start als Accesspoint
     nic.ifconfig((myIP,"255.255.255.0",myIP,\
                   myIP))
 
     print(nic.ifconfig())
 
     # Authentifizierungsmodi ausser 0 werden nicht unterstuetzt
     nic.config(authmode=0)
 
     MAC=nic.config("mac"# liefert ein Bytes-Objekt
     # umwandeln in zweistellige Hexzahlen
     MAC=ubinascii.hexlify(MAC,"-").decode("utf-8")
     print(MAC)
     nic.config(essid=ssidpassword=passwd)
 
     while not nic.active():
       print(".",end="")
       sleep(0.5)
 
     print("Unit1 listening")
 # *************** Setup accesspoint end *****************
 
 # ********************************************************
 # WLAN-Connection
 # ********************************************************
 if WLANconnect and (not ownAP):
     nic = network.WLAN(network.STA_IF# erzeuge WiFi-Objekt
     nic.active(True)  # Objekt nic einschalten
     #
     MAC = nic.config('mac')  # binaere MAC-Adresse abrufen +
     myMac=hexMac(MAC)        # in Hexziffernfolge umwandeln
     print("STATION MAC: \t"+myMac+"\n"# ausgeben
     # Verbindung mit AP im lokalen Netzwerk aufnehmen,
     # falls noch nicht verbunden, dann
     # connect to LAN-AP
     if not nic.isconnected():
       nic.connect(mySidmyPass)
       # warten bis die Verbindung zum Accesspoint steht
       print("connection status: "nic.isconnected())
       while not nic.isconnected():
         blink(0.8,0.2,0)
         print("{}.".format(nic.status()),end='')
         sleep(1)
     # zeige Verbindungsstatus & Config-Daten
     print("\nconnected: ",nic.isconnected())
     print("\nVerbindungsstatus: ",connectStatus[nic.status()])
     print("Weise neue IP zu:",myIP)
     nic.ifconfig((myIP,"255.255.255.0",myIP\
                   myIP))
     STAconf = nic.ifconfig()
     print("STA-IP:\t\t",STAconf[0],"\nSTA-NETMASK:\t",\
           STAconf[1],"\nSTA-GATEWAY:\t",STAconf[2] ,sep='')
 
 # *********** Setup Router connection end ***************
 
 # ********************************************************
 # TCP-Web--Server
 # ********************************************************
 # ----------------- Server starten --------------------------
 if WLANconnect or ownAP:
     s = socket.socket(socket.AF_INETsocket.SOCK_STREAM)
     s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
     s.bind((''myPort))
     print("Socket established, waiting on port",myPort)
     d.clearAll()
     #         0123456789012345
     d.writeAt("SOCK ESTABLISHED",0,0)
     d.writeAt("LISTENING AT",0,1)
     d.writeAt(myIP+":"+str(myPort),0,2)
     s.settimeout(0.9)
     s.listen(2)
     
 if taste.value()==0:
     print("Mit Flashtaste abgebrochen")
     ledsOff()
     d.writeAt("Abbruch d. User ",0,5)
     sys.exit()
 
 # ------------------- Serverschleife ----------------------
 while True:
     try:  # wegen timeout
         r=""
         if WLANconnect or ownAP:
             caddr = s.accept()
             print('Got a connection from {}:{}\n'.\
                   format(addr[0],addr[1]))
             request=c.recv(1024)
         else:
             request=input("Kommando:")
             addr="999.999.999.999:99999"
         try:  # decodieren und parsen
             r=request.decode("utf8")
             getPos=r.find("GET /")
             if r.find("favicon")==-1:
                 print("***********************************")
                 print("Position:",getPos)
                 print("Request:")
                 print(r)
                 print("***********************************")
                 pos=r.find(" HTTP")
                 if getPos == 0 and pos !-1:
                     query=r[5:pos# nach ? bis HTTP
                     print("*********QUERY:{}*********\n\n".\
                           format(query))
                     response = web_page(query)
                     print("---------------\n",response,\
                           "\n----------------")
                     c.send('HTTP/1.1 200 OK\n'.encode())
                     c.send('Content-Type: text/html\n'.encode())
                     c.send('Connection: close\n\n'.encode())
                     c.sendall(response.encode())
                 else:
                     print("##########\nNOT HTTP\n###########")
                     c.send('HTTP/1.1 400 bad request\n'.encode())
             else:
                 print("favicon request found")
                 c.send('HTTP/1.1 200 OK\n'.encode())
         except:  # decodieren und parsen
             request = rawRequest
             c.send('HTTP/1.1 200 OK\n'.encode())
         c.close()
     except:  # wegen timeout
         pass    
         
     if taste.value()==0:
         print("Mit Flashtaste abgebrochen")
         ledsOff()
         d.writeAt("Abbruch d. User ",0,5)
         sys.exit()
     blink(0.05,0.05,1)

Figure 28: Vivre du navigateur

Figure 28 : En direct depuis le navigateur

Voici à quoi ressemble le site web en réalité sur Google Chrome. Opera offre une image similaire après avoir saisi l'URL 10.0.1.181:9002. Firefox fait des siennes parce que ses créateurs se sont mis en tête de devoir brider les utilisateurs en n'acceptant que les adresses https dans leur navigateur. Mais il existe des alternatives. Si les choses tournent mal, on pourrait même écrire son propre front-end pour le PC avec CPython.

Eh bien, je pense que vous avez suffisamment à faire jusqu'à Noël avec les projets de sapins. Il y en a certainement un ou deux pour chacun. L'important est que vous ayez du plaisir à les réaliser et que j'aie pu éveiller votre intérêt. Je vous souhaite en tout cas une bonne période de l'Avent.

Pour Arduino IDE

Télécharger webcontrol.ino

Die Oberfläche im Browser sieht hier etwas anders aus. Die Funktion ist dabei die gleiche.

Je nachdem, ob Sie einen eigenen Accesspoint, oder das heimische WLAN verwenden wollen, müssen Sie die jeweils nicht verwendete Option im Quellcode zu Beginn in den #defines auskommentieren.

// #define WLANconnect true
#define ownAP true

N'oubliez pas de saisir vos données d'accès si vous choisissez l'option WLAN.

Nous profitons de l'occasion pour vous souhaiter un joyeux 1er Avent.

DisplaysEsp-32Projekte für anfängerSensoren

6 commentaires

Jürgen

Jürgen

@ Niko Germer
Natürlich können Sie die Flacker-LEDs gegen normale Kandidaten austauschen. Aber Sie haben recht, für unterschiedliche Farben brauchen Sie unterschiedliche Widerstände. Das liegt einerseits an den unterschiedlichen Durchlassspannungen, rote LEDs haben eine niedrigere als blaue. Aber entscheidender ist die Helligkeit der LEDs. Geringe Helligkeit bei rot und gelb steht der blendenden Helligkeit der blauen und vor allem grünen LEDs gegenüber. Ich beziehe mich hier auf auf die LED aus dem LED Assortment Kit. Für die roten schlage ich 150 Ohm vor, blau 560 Ohm und grün 1,5kOhm für jeweils sechs parallele LEDs am Bäumchen. Die normalen LEDs lassen sich dann auch dimmen, wenn an die Basiswiderstände ein PWM-Signal gelegt wird. Der ESP32 kann das Signal an allen digitalen Ausgängen zur Verfügung stellen.

Niko Germer

Niko Germer

Hallo,
ich würde das Bäumchen gerne mit normale LEDs bestücken. Kann man dann die LEDs dimmen? Was muss ich beachten, bei die Vorwiderstände? Bleiben die gleich?

Andreas Wolter

Andreas Wolter

Die Aktion für das Bundle ist leider abgelaufen. Danke für den Hinweis. Den Link haben wir entfernt.

Michael Beckmann

Michael Beckmann

Hallo
wenn man auf das Bild zur Teileliste klickt kommt ein HTTP 404 :-(

Miguel Torres

Miguel Torres

Un articulo muy bueno con muchos ejemplos con un elemento común.

bit.picker

bit.picker

Sehr schöner Artikel. Besonders interessant finde ich das Kapitel mit der Web-Steuerung. Dannke!

Laisser un commentaire

Tous les commentaires sont modérés avant d'être publiés