Geschichten um einen kleinen Weihnachtsbaum
Stories usually begin with "Once upon a time". The following stories take place today and now. Today we will develop a few projects around a special kind of Christmas tree.

This is a little tree of about 11,5 cm height with 36 (37) colored lights on it. They blink in all colors in a funny way. But they do it more or less always in the same way. This gave me the idea to bring some order into the chaos by adding some additional parts. The result is a controller with an ESP32 and various sensors, which put the illumination of the LEDs in the service of various measurement tasks, or simply provide for amazement. A part of what would be possible altogether, I present you individually in the following chapters. All applications are programmed in MicroPython. My colleague Andreas Wolter has additionally ported the programs for the Arduino IDE to C++ for you. So welcome to the

Stories around a small Christmas tree

Figure 1: The tree stories

Image 1: The tree stories

The content:

  1. The components list
  2. The software
  3. We build the trees and wire it
  4. Targeted illumination
  5. The OLED display for plain text information
  6. Steamed lights
  7. The enchanted trees
  8. Who makes such a noise?
  9. On the trail of tree theft
  10. A pleasant stay wish ESP32 and DHT22 / DHT11
  11. The somewhat different Christmas raffle
  12. The Christmas tree app


1. The parts list

1

DIY LED Christmas tree kit

1

KY-009 RGB LED SMD module sensor or

KY-016 FZ0455 3-color RGB LED Module 3 Color

1

1.3 inch OLED I2C 128 x 64 pixel display compatible with Arduino and Raspberry Pi

1

DHT22 AM2302 Temperature sensor and humidity sensor

1

KY-021 Magnet Switch Mini Magnet Reed Module Sensor

1

RFID Keycard Card 13.56MHz Key Card Map MF S50 (13.56 MHz) - 10x RFID card

1

RFID Kit RC522 with Reader, Chip and Card for Raspberry Pi and Co. (13.56MHz)

1

Breadboard Kit - 3 x 65STK. Jumper Wire Cable M2M and 3 x Mini Breadboard 400 Pins Compatible with Arduino and Raspberry Pi

1

KY-038 sound detection module microphone voice sound sensor

1

ESP32 NODEMCU Module WLAN WIFI Development Board with CP2102

1

GY-521 MPU-6050 3-axis gyroscope and acceleration sensor alternative GY-61 ADXL335 Acceleration Sensor

Virtual contact KY-020 or KY-002 (*)

1

Jumper wire cable 3 x 40 pcs. per 20 cm M2m / F2M / F2F

3

Resistance 1.0kΩ

(*) The use of the vibrating contacts or GY-51 module requires a different programming required.

2. The software

For flashing and programming the ESP32:

Thonso or

μpycraft

pacetender To test ESP32 / ESP8266 as the UDP server

Browser: Opera or Chrome

Used firmware:

Micropython firmware

Please choose a stable version

The micropython programs for the project:

With license text

Device driver:

GY521RC.py

mfrc522.py

sh1106.py

OLED.PY

Project files Micropython:

alarm.py

noisy.py

rfid.py

roomclimate.py

enhancement.py

enchanted.py

webcontrol.py

Project files Arduino IDE:

Note:
The version of the Arduino IDE used here is 1.8.16
The ESP32 Arduino Cores is 2.0.2


To use the ESP32 Core you have to enter this link in the preferences as additional board manager URL:

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

Then search for ESP32 via the menu Tools -> Board -> Board manager and install it.

The used libraries like LittleFS were integrated late into the core. Therefore it is important to update it if necessary.

I2c_scanner.ino

OLEDTEST.INO


Alarm.ino

noisy.ino

optional noisy_ticker_h.ino (with Ticker.h Bibliothek)

rfid.ino

roomclimate.ino

enhancement.ino

enchanted.ino

webcontrol.ino


Micropython - language - modules and programs

To install Thonny you will find one Detailed instructions. There is also a description as the Micropython firmware On the ESP chip banned will.

MicroPython is an interpreter language. The main difference to the Arduino IDE, where you always and only flash whole programs, is that you only have to flash the MicroPython firmware once at the beginning to the ESP32 before the controller understands MicroPython instructions. You can use Thonny, µPyCraft or esptool.py to do this. For Thonny, I have described the process here.

Once the firmware is flashed, you can casually talk to your controller one-on-one, test individual commands and see the response immediately, without having to compile and transfer an entire program first. In fact, that's what bothers me about the Arduino IDE. You simply save an enormous amount of time if you can do simple tests of the syntax and the hardware up to trying out and refining functions and whole program parts via the command line in advance before you knit a program out of it. For this purpose I also like to create small test programs from time to time. As a kind of macro they summarize recurring commands. From such program fragments sometimes whole applications are developed.

Autostart

If the program is to start autonomously when the controller is switched on, copy the program text into a newly created blank file. Save this file as boot.py in the workspace and upload it to the ESP chip. The program will start automatically at the next reset or power-on.

Testing programs

Manually, programs are started from the current editor window in the Thonny IDE via the F5 key. This is faster than clicking on the Start button or via the Run menu. Only the modules used in the program must be located in the Flash of the ESP32.

In between again Arduino-IDE?

If you want to use the controller together with the Arduino IDE again later, just flash the program in the usual way. However, the ESP32/ESP8266 has then forgotten that it ever spoke MicroPython. Conversely, any Espressif chip that contains a compiled program from the Arduino IDE or the AT firmware or LUA or ... can easily be flashed with the MicroPython firmware. The process is always as described here.

3. We build the trees and wire it

There is one for the assembly of the tree Video.

Of course, after assembling the little tree, you can sit back and relax, admiring his work. Additional fun can be had if we modify the assembly process in a few places. At three points in the video we have to proceed differently for our project. The 4.7kΩ resistors mentioned there are those with 10kΩ in the parts package. And these three resistors per board A and B are soldered only at the places on the board, as it is shown in the figures Fig.2 and Fig.3, the other end of these resistors remains free for the time being. Later we solder thin wires (for example flat ribbon) to these free ends for the connection to the ESP32. This applies to both boards, A and B. The electrolytic capacitors remain completely away.

Figure 2: Part A

Figure 2: Part A

Figure 3: Part B

Image 3: Part B

The rest of the assembly can be done exactly according to the video template. When the base plate is also attached, we solder the little cables to the free ends of the 10kΩ resistors. The length should be between 25 and 30 cm. At the other end of the cable we solder a piece of pin header to make it pluggable.

Figure 4: The Tree Connection

Image 4: The Tree Connection

The assignment of the connectors on the tree to the GPIOs of the ESP32 can be seen in Table 1. The index refers to the list of pin objects. This list with the name layer is used to address the LED layers by loops, as we will see. The pins are distributed in such a way that an even-numbered index is always followed by the LED layer on board B with the same layer. Of course, any rearrangements are possible at any time.

Trees

A1

B1

A2

B2

A3

B3

Gpio

32

26

33

27

25

12

Index

0

1

2

3

4

5

Table 1: Connections between trees and ESP32

Figure 5: Base wiring

Image 5: Base wiring

Figure 6: Basic resistors on Part B - Detail, free ends are above

Image 6: Basic resistors on Part B - Detail, free ends are above

Figure 7: Wired of Part A

Image 7: Wired of Part A

4. Targeted illumination

The wiring is finished? Then we want to ignite the LEDs on the little tree already. We supply the little tree either via the batteries or via the supplied cable from a USB port. After switching it on, it remains dark. Sure, because the base connections of the transistors are exposed, so no base current can flow and because then also no collector current flows, the LEDs stay dark.

Figure 8: one of 6 transistor levels

Image 8: one of 6 transistor levels

This changes when the GPIOs are programmed as outputs and the level of GND potential is raised to 3.3V. We achieve this by assigning a 1 as value. In Thonny's terminal, we enter the following lines.

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

If wired correctly, the LEDs of level A1 now start to light up and go out after the input of.

 >>> A1.value(0)
In contrast to the previous version of the little tree kit, the new version is equipped with flicker LEDs. Previously they were simple colored LEDs. This has a certain disadvantage, because dimming the "Flashing LEDs" is no longer possible. Nevertheless it is fun to experiment with it. Via the six transistors we are now able to start or turn off all 6 levels exactly according to our wishes. With this we also influence the overall brightness.

The arrangement of the lights is shown in Fig. 9. It applies to both part A and part B.

Figure 9: Distribution of the LEDs is rank

Image 9: Distribution of the LEDs is rank

The arrangement of the LEDs, the wiring for and the connections on the ESP32, are provided for all further attempts than given. They therefore no longer immerse explicitly in the descriptions and circuit breakers of the partial circuits.

5. The OLED display

The OLED display can provide us with plain text information, but it can also display graphics in black and white. Programming is easy if we use the associated MicroPython software modules. The SH1106 hardware driver is directly responsible for the 1.3'' display and can only be used for it. The module framebuf integrated in the MicroPython core provides simple graphics and text commands and the module oled.py gives us comfortable commands for text output.

The display is controlled only via the two lines of the I2C bus. We create an I2C object and pass it to the constructor of the class OLED. The hardware device address of the display is fixed and anchored as a constant in OLED. Nevertheless, we first check what is available on the bus. Then we delete the display and output a few lines.

The graphic in Fig. 10 and the following program demonstrate the handling. The program we enter in the Thonny Editor, save it and then start it with the function key F5.

Figure 10: The OLED on the ESP32

Image10: The OLED on the ESP32

OLEDTEST.PY

 # OLED-Display-Demo
 #
 from machine import Pin, I2C
 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)


Output:

[60]
This is the constructor of OLED Class
Size: 128x64

The device address of the display is decimal 60 or 0x3c hexadecimal. The constructor of the OLED class also knows that the display has 128 x 64 pixels. After the output of the four lines, "Christmas" is replaced by "chist" later. Previously, we must delete this line of course. Quietly try the individual commands individually via REPL, the Terminal Console from Thonny.

For Arduino IDE:

For the display, the library becomes U2G8 Used that you can install via library management.

Download OLEDTEST.INO

6. Steamed lights

Figure 11: Level1

Image 11: Level1

Figure 12: Level2

Image 12: Level2

Since we can control the individual levels of the LEDs on the tree separately, let's take advantage of this to go from the lowest level - OFF - up to the maximum brightness and back down again. We don't need to change anything on the circuit. The display informs us about the currently active level.

enhancement.py

 # steigerung.py
 #
 import sys
 from machine import Pin, I2C
 from oled import OLED
 from time import sleep,ticks_ms, ticks_us, sleep_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()

We define the order of the layers in the layerPin list. According to this pattern the pin objects are created in the following for loop. The functions switch(), stop() and all() help us to make the program clearer. Furthermore we will use them several times in the following chapters.

In the main program we clear the screen and draw a frame. 4 and 16 are the pixel coordinates of the upper left corner, 123 and 40 are the width and height in pixels and 1 is the color white, there are no more colors. The outer for-loop counts the total passes. The first inner for loop counts up i in intervals of 3 seconds and turns on the layers. The second loop counts down and clears the LEDs again.

The last edition is removed, and the STOP () function reliably deletes all LEDs and says goodbye with a friendly "good bye".

About the interval length and the number of runs we can specify the behavior of the LEDs itself.

For Arduino IDE

Download enhancement.ino

7. The enchanted trees

Anyone could come along and want to switch on our little tree. But no way, that's only possible with our magic hands. Of course, we don't say that we have a small neodymium magnet rod hidden in each hand. What do we need it for? For "magic". Because we have modified our circuit in the meantime. A reed contact is now connected to ground at GPIO pin 13. Inside the glass tube there is a switching contact which closes when a magnet approaches.

Figure 13: Muggles have no magnet, the tree remains dark

Image 13: Muggles have no magnet, the tree remains dark

Figure 14: In the yellow marker: Contact and Magnet

Image 14: In the yellow marker: Contact and Magnet

Attention:

The glass is very brittle and the wires are very stiff. Do not bend them, otherwise the glass will shatter and you can bury the component.

Figure 15: Reed contact helps magic

Image 15: Reed contact helps magic

There is something else we must not forget, namely that OLED and Trees remain connected as described above.

enchanted.py

 from os import uname
 import sys
 from machine import  Pin, I2C
 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()
The reed contact must be placed in such a way that we can easily get close to it with our magnet when we place Bäumchen or Breadboard on the palm of our hand. A thin black glove helps us to keep the magnet invisible. Probably everyone around you are muggles.

The program is very simple. We already know everything up to the main program. The while loop runs endlessly until the power is turned off. If the contact in the neighborhood of the solenoid is closed, then GPIO13 is at GND potential and all lights go on. Otherwise, the resistor built into the module pulls GPIO13 to Vcc=3.3V and the lights go off.

To make the magic work better, the little tree with breadboard should be powered by the battery. The positive terminal of the battery must be connected to the pin Vin / 5V of the ESP32. Furthermore the program must be uploaded as boot.py on the ESP32, so that the controller starts autonomously after power on. How to do this is described in detail in chapter 2 - Autostart.

For Arduino IDE

Download enchanted.ino

8. Advent and Christmas, the "staade" time

Translated into the common German language, "Staad" means something like "calm", "contemplative". But everyday life teaches us that even in the run-up to Christmas, things can get pretty heated. When things get too turbulent, the little tree reminds us to turn down the volume a few decibels. How does it do that? Well, there is a sound module that picks up sound and delivers the digitized signal to the ESP32.

Figure 16: Volume OK

Image 16: Volume OK

Figure 17: Too loud

Image 17: too loud

Figure 18: Sound machine on the ESP32

Image18: Sound Machine on ESP32

noisy.py

 # noisy.py
 import esp32
 from os import uname
 from machine import Timer, Pin, I2C
 from oled import OLED
 from time import time, sleep,
 
 
 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=15000, mode=Timer.ONE_SHOT, callback=soundDone)
         print("begin",time())
         alle()
 
 def soundDone(t):
     global n
     n=False
     print("ende",time())
     stop()
     sound.irq(handler=soundDetected, trigger=Pin.IRQ_FALLING)
 
 n=False
 sound.irq(handler=soundDetected, trigger=Pin.IRQ_FALLING)

Sound is transmitted by rapid pressure fluctuations in the air. A sound signal propagates at approx. 340 m/s. In a room, practically without any perceptible delay. The microphone in the sound module converts the pressure fluctuations into an electrical signal. Unlike the reed contact, however, these oscillations can no longer be detected by polling the GPIO port; this method is too slow. Therefore we use another technique here, the interrupt programming. An interrupt is the interruption of a program by a specific event. We will use two different interrupt sources. One triggers an interrupt when the level on a GPIO pin changes, from 0 to 1 or vice versa. The other IRQ source is a hardware timer of the ESP32. It triggers the IRQ when the alarm clock rings.

Both now play the ball to each other alternately. The GPIO17 waits for a signal from the sound module. If a falling edge arrives, the function soundDetected() starts and checks first if it is meant because of the passed parameter pin. If n is True, then a cycle is already running and there is nothing further to do. On the other hand, if n is False, then it is a fresh job. The pin change IRQ is turned off, and n is set to True to suppress immediately following pulses at GPIO17. Then the timer is started, which specifies the runtime of the tree lighting. The lighting is switched on by calling all().

If the timer has expired, the associated interrupt is triggered, which the function Sounddone() starts. n will be on False Set, the lights go out and the PIN-CHANGE-IRQ is turned on again.

When the timer expires, the corresponding interrupt is triggered, which starts the soundDone() function. n is set to False, the lights go out, and the pin change IRQ is armed again.

The main program consists of just two lines. When the timer expires, the corresponding interrupt is triggered, which starts the soundDone() function. n is set to False, the lights go out, and the pin change IRQ is armed again.

The main program consists of just two lines. n is set to False so that the pin change IRQ that is activated afterwards can be triggered.

The interesting thing is that the IRQs are still active even if the main program is already finished. To switch this off the ESP32 must be reset with the STOP/RESTART button.is set to False so that the pin change IRQ that is activated afterwards can be triggered.

The interesting thing is that the IRQs are still active even if the main program is already finished. To switch this off the ESP32 must be reset with the STOP/RESTART button.

For Arduino IDE

Download noisy.ino or optional noisy_ticker_h.ino (with ticker.h Bibliothek)

Two variants were used here to implement the timer interrupts. In the noisy.ino only binary variables are switched in the two interrupt service routines. The change is then detected in the normal main loop. As an alternative you can include the Ticker.h library. Its settings are a bit easier to make. It is included in the ESP32 core. You don't have to install it. If it is not found during compilation, you may have to update the ESP32 core.

9. On the trail of tree theft

There are supposed to be people who steal their Christmas tree - from the forest. Well, dear foresters, why don't you do like we do and install a guard in your little trees like the one we are about to describe.

OK, admittedly, this will certainly be as difficult as monitoring other bans if the staff is lacking. What's the point of banning something if you can't control it? Be that as it may.

Our little tree gets a supervisor - namely itself! A sensor, which comes from a completely different corner, helps it to do this. The used GY-521 module with the MPU6050 is an accelerometer with gyroscope. It can be used to measure accelerations, forces and rotations. Yes, and if you want to take something away, you have to lift it and set it in motion. In both cases you accelerate the object, even when tilting.

Figure 19: Light tilting is enough to trigger the alarm

Image19: Light tilting is enough to trigger the alarm

Even very slight changes in location produce forces and thus our sensor to respond. The rest is easy, the triggering is followed by the illumination of the tree and hopefully the potential thief will run away. By the way, a timer interrupt is again responsible for the duration of the alarm.

Figure 20: Antiklau unit

Image 20: Antiklau unit

alarm.py

 # alarm.py
 # RED-ALLERT by movement
 import esp32
 from os import uname
 from machine import Timer, Pin, I2C
 from oled import OLED
 from time import sleep,ticks_ms, ticks_us, sleep_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=delay, mode=Timer.ONE_SHOT, callback=alertDone)
         sleep(0.3)
 
 def alertDone(t):
     stop()
 
 print("Diebstahlschutz gestartet")
 hasMoved(dauer)

Again there are good acquaintances in the program. But new is the initialization of the GY521. For the block we have to upload another module to the ESP32, gy521rc.py. The class contained in it is called the same as the module.

Like the OLED display, the GY521 is also operated via the I2C bus. We pass the same I2C object to the constructor, set the threshold for triggering the alarm and its duration in milliseconds.

The threshold is the absolute amount of deviation of the measured value from the average value of the acceleration measurement in x-direction. The sensor is aligned so that the positive x-axis points vertically upwards. The measured value is around 16000 counts and corresponds in this case to the acceleration due to gravity g=9.81m/s².

The function hasMoved() represents the main loop here. At the entry the average value is determined by 100 measurements. It is clear that the sensor must not move.

Then it goes into the main loop. The current acceleration is measured and the deviations to the average values are calculated. If the difference exceeds the predefined limit, an alarm is triggered and the timer is activated. Alarm means, the tree goes to full brightness.

The service routine of the timer IRQs extinguished the lights. The solution on the IRQ ensures that immediately after alarm is triggered, the circuit is sharp again. If the alarm duration set by a sleep command in the main loop, then the circuit for that period would be dead.

The Rüttelkontakte mentioned in the parts list would be connected like the reed contact at ESP32, but do not allow adjustment of sensitivity.

For Arduino IDE

Download Alarm.ino

The GY521 library is included here for the gyroscope sensor. You can also install this via the library management. In addition, the Ticker.h library is also used here. In contrast to the MicroPython template, all axes of the sensor are considered here.

If you are not sure which I2C address the sensor uses, you can load the program I2C_Scanner.ino on the ESP. There should then be two addresses shown in the serial monitor in this case (for the display and the sensor). At the sensor itself you have the possibility to choose between the addresses 0x68 and 0x69. You have to connect pin A0 either to GND or to 3.3V of the ESP.

10. A pleasant stay wish ESP32 and DHT22 / DHT11

A pleasant room climate is part of the festive mood. Now, the ESP32 cannot change the room climate in this simple application, but it can report on it. The exact values for temperature and humidity are shown on the OLED display, the rough values are told by the little tree. In 2-degree increments, it reports the values of the room temperature through different numbers of switched-on LED levels.

Figure 21: Medium temperature - half lighting

Image 21: Medium temperature - half lighting

Figure 22: Temperature and humidity measurement in a module

Image 22: Temperature and humidity measurement in a module

There are 2 variants of DHT22, alias AM2302. The module in the left picture already contains the necessary pullup resistor for the One-Wire-Bus, which by the way must not be confused with the system of the Dallas- device DS18B20. The Dallas bus has a completely different timing. For the naked version in the right figure a 4.7kΩ to 10kΩ resistor against Vcc must be installed.

The operation in the program is easy. The three necessary orders is already integrated in Micro Python module dht to disposal.

roomclimate.py

import esp32, dht
 from os import uname
 import sys
 from machine import Pin, I2C
 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)

Besides the usual suspects, the program offers only the import of the module dht, the instantiation of the object dht22 and the main loop with the measurement job dht22.measure() and the reading of temperature and humidity value. We already know the output on the display and the tree display. Interesting and inconspicuous is perhaps the conversion of the temperature from °C to the index of the illumination level. by the term int(((t-15)//2)%6). From the quotient value of the integer division of the deviation of the temperature from 15 °C upwards and 2, the 6-division remainder is determined and represented as an integer to be on the safe side. Once again very slowly.

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

for 28°C the result would be: 28-15=13; 13//2=6; 6%6 = 0; The last step is necessary because there is no step with the number 6.

For Arduino IDE

Download roomclimate.ino

For this sensor please install the DHT sensor library and the Adafruit Unified Sensor Library via the library management. In contrast to Python you have to pay close attention to data types in C/C++. The measured values of the sensor are returned of type float. Accordingly you have to set up the formatted output and also the calculation for the control of the LEDs can be faulty if you leave out the data type.

11. The (somewhat different) Christmas raffle

I still know this from my school days. Everyone brought a package during Advent and the week before the vacations the raffle was started - every ticket wins.

I chose neutral RFID cards as recyclable raffle tickets. The draw is done by the ESP32 together with the RFID kit. Only the prizes have to be taken care of by yourself. Of course the little tree is also included. By its luminosity it announces its profit to the respective player. So that no doubts arise in the interpretation, the display unambiguously states the location of each draw: Freiburg, Berlin, Hamburg ... Six lot cards and one master card are needed.

Image 23: The height of the hit number determines the light intensity

Figure 23: The height of the tripping number determines the luminous strength

Image 23: The height of the hit number determines the luminosity

Figure 24: Lottery

Image 24: Lottery

Figure 25: RFID card reader

Image 25: RFID cards reader

Due to the SPI bus the wiring is a bit more complex than with the I2C bus with its two lines. SPI bus devices do not have a hardware device address, but they have a chip select connector that must be set to LOW if the device is to be addressed. Also the data transfer runs a little bit different, it is always send and receive at the same time. It is not necessary to clarify the procedure here, because the class MFRC522 does it for us. We only tell the constructor the pin assignments and the transfer speed. The transfer works with fast 3.2MHz. For comparison, I2C works on 400kHz.

The function Readuid() Reads the unique identifier of the card and returns them as a hexadecimal value and as a decimal number. The cards are requested via the OLED display. So that the function does not block the entire process, a timeout ensures an ordered retreat. In this case, the value None is returned instead of the card ID.

Figure 26: RFID cards and chip

Figure 26: RFID cards and chip

So that the lot cards come into the game, we need a master card. To do this, we take any card or a chip from the stack, read the ID and thus prove the variable with the decimal value at the beginning of the program:

Masterid = 4217116188.

The first start determines the ESP32 that there is no file with the lot card data and requires the master card. After it has been detected, a lot card is requested. After reading the ID, this is written to the file and again requires the master card. Reading will continue until the last lot card. If no loose card is offered after the master card requirement for 10 seconds, the system restarts itself. Prerequisite is that the program rfid.py as boat.py was sent to the ESP32. Chapter 2 - Autostart explains exactly how that works. To start completely from the front, we can get the file slavecards.txt Delete with the lot card IDs via the Thonny Console. After a reset, the lot cards can be read again.

rfid.py

 # RFID.PY
 # Works with RC522 13.2MHz
 import MFRC522
 import ESP32, dht
 from OS import uname
 from machine import timer, Pin code, I2c, ADC, reset
 from OLED import OLED
 from time import sleep,Ticks_ms, Ticks_us, Sleep_ms
 from GY521 import GY521
 
 # Initialize the interfaces **********************
 IF uname()[0] == 'ESP32':
     # Sck, Mosi, Miso, CS = SDA
     RDR = MFRC522.MFRC522(14,  16,   15,   5, baud rate=3200000)
 Elif uname()[0] == 'ESP8266':
     # Sck, Mosi, Miso, CS = SDA
     # D3 D4 D2 D5
     RDR = MFRC522.MFRC522(0,   2,    4,    14, baud rate=100000)
 else:
     Raise RuntimeError("Unsupported platform")
 Masterid=4217116188  # 0xfb5c161c
 
 i2c=I2c(-1,SCL=Pin code(22),SDA=Pin code(21))
 D=OLED(i2c)
 
 layer pin = [32,33,25,27,26,12]
 layer=[0]*6
 for I in ratchet(6):
     layer[I]=Pin code(layer pin[I],Pin code.OUT)
     layer[I].value(0)
 profit=[
         "Freiburg",
         "Berlin",
         "Hamburg",
         "Augsburg",
         "Ratzeburg",
         "Erfurt",
         "Eat",
         "Bonn",
        ]
 # Functions define ***********************************
 def Time-out(t):
     begin=Ticks_ms()
     def Compare():
         return int(Ticks_ms()-begin) >= t
     return Compare
 
 def Readuid(display,card type,time-out):
     display.Clearft(0,1,15,show=False)
     display.writing("Put on"+card type,0,1)
     READTIMEOUT=Time-out(time-out)
     whiler emergency READTIMEOUT():
        (stat, tag_type) = RDR.requester(RDR.REQIDL)
         IF stat == RDR.OK:
            (stat, raw_uid) = RDR.anticoll()
             IF stat == RDR.OK:
                 display.Clearft(0,2,15,show=False)
                 display.writing("Card OK",0,2)
                 sleep(1)
                 Userid=0
                 for I in ratchet(4):
                     Userid=(Userid<<8) | raw_uid[I]
                 userid="{: #X}".format(Userid)
                 print(userid)
                 return Userid,userid
     return None
 
 def adduid(display):
     display.Clearall()
     m=Readuid(display,"Master",3000)
     IF m is emergency None:
         Mid,_= m
         IF Mid==Masterid:
             sleep(3)
             u=Readuid(display,"Slavecard",3000)
             IF u is emergency None:
                 uid,UIDs=u
                 IF uid is emergency None and uid != Masterid:
                     with open("slavecards.txt","a") ace f:
                         f.writing("{} \ 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,3, False)
                 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)

For a game cycle, 6 wins are determined, the 6 cards are shuffled and dealt, and the new round is started by pressing the PROG button on the ESP32.

For Arduino IDE

Download rfid.ino

Note: in this case the connections to the pins of the ESP32 must be changed. The reason for this is that the hardware SPI interface is used. Its pins are not changeable.

MOSI  =  23
MISO  =  19
SCK   =  18

SDA  =  5
RST  = 17

For the RFID scanner, please install the MFRC522 library.

For a direct comparison with MicroPython, you can change this line so that you don't have to change the pins every time:

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

in

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

To store the text file with the IDs, a file system is set up in the flash memory of the ESP using the LittleFS library (the successor to SPIFS). Files can then be stored there. The library is now part of the ESP32 core. You do not have to install it separately either. The program is written so that you do not have to edit the text file on the PC.

If you still want to do this, you can install the ESP32 Upload Plugin. However, the data exchange then also works (as the name suggests) only in one direction.

The program structure has been changed a little here. But the behavior of the program should be the same. Some more functions have been added. You can read the master card at program start. It is also possible to delete the text file from the flash. For this you can connect a cable or a push button to GND at the given pin (see source code). Keep it pressed and restart the ESP. Then the file will be deleted if it exists. Then disconnect it and re-read the winning cards. The maximum number of winning cards corresponds to the LED levels of the Christmas tree. If you want to use fewer cards, you can wait for the timeout during the reading process.

12. The little tree in the LAN/WLAN

Let's do the dozen full, and we bring the trees to the net. Because, if an ESP32 is already used for controlling, then LAN or WLAN access must also be controlled. I decided to realize a web server on the ESP32 because the levels of the trees can then be controlled with almost any browser. Alternatively, an UDP server would have been in question on the controller and a mobile app. But that would have blasted the framework of this blog and therefore I have spaced from it. For those interested, such a kind of control, I have already described in other posts, for example here and here.

For the circuit, the construction of Chapter 5 is used, which we extend around an RGB LED and three 1.0 kΩ resistors.

Figure 27: Web structure

Image 27: Web structure

After importing the necessary modules, we define the pins for the RGB LED, which reveals us the network status visible to a long distance. This is followed by the selection of the network mode, WLAN or ESP32 own accessspoint. Wi-Fi is preset by default. To access the WLAN router, the access data must then be specified here. The layer definition is extended by three lists, plain text for on / off, background color for the table in the web page and the switching states of the levels.

In the functions, the blocking ends were removed, swell(), swell(), wave() and tree(). New hexmac(), blink(), LEDsOFF() and web_page(). hexmac Specifies the MAC address of the ESP32 in station mode, blink() Signals the network and server states. With LEDsOFF() When the RGB LED is turned off and web_page() Captured requests from the browser, executes the orders and returns a response as a web guide text.

The request of the browser goes to the server as a query string. The string has the shape ?, ? P or ? E = X & V = Y. Therein X for the level number and Y for switching state, 0 or 1.

web_page() Converts the request to capital letters, first checks "A" and "P". If the request contains more than 2 characters, attempts to determine the level and switching state. If there is a mistake, no action is triggered and the naked home page is called. This also happens when no QueryString has been specified. Thereafter, the structure of the website takes place as a string and its return to the main loop.

Following the function definitions, the construction of the network connection follows, either as its own access point or as a connection to the WLAN router. This is controlled by the two variables Ownap and Wirisconnect. In both cases, a fixed IP address (10.0.1.181) will be assigned because it is a server. Dynamic addresses from the WLAN router are unsuitable because they can switch from time to time. The connection to the router is marked by the blinking of the blue LED. The display informs us that the connection is and the connection socket S is ready to answer inquiries.

In the main loop, the reception loop is waiting for the method Accept() On a request. Does not matter to the timeout, throws Accept() An exception that we intercept with the foregoing try.

If a request is available, then supplies Accept() a communications seat C and the address of the requesting machine back. C Used processing the data exchange between the client and server, while S is released again to accept additional incoming requests. The method C.Recv() Returns the text of the request from which only the first few characters are interested. In the development phase you can test the parser web_page() Enter requests from hand. Ownap and Wirisconnect then both have to False be set.

The byte object requester of the received text is now in a string r decoded, which are easier to handle leaves. We are looking for a "GET /" far right at the beginning of the string and follows the position where the "http". If both found, then we isolate the text after the "/" of "GET" until the space of "HTML" and send it as a query string to the parser web_page(). Its response we received in the variable response. Then we send the HTML header and the text of the HTML page with the included reply to the caller. The following two else and the except are used to catch and handle of possible errors. Important is the final c.close(), Which the communication socket C Closes.

After a key interrogation program termination shows us the green LED on by their brief flash as a heartbeat that the system is still alive.

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 Pin, I2C
 from oled import OLED
 
 # ******************** Network stuff ********************
 from time import sleep,ticks_ms, sleep_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("&")
             _,ebene= ebene.split("=")
             _,state= state.split("=")
             ebene=(int(ebene) if 0<=int(ebene)<=5 else 0)
             state=(int(state) if 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]],i, zustand[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=ssid, password=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(mySid, myPass)
       # 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_INET, socket.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:
             c, addr = 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: Live from the browser

Image 28: Live from the browser

This is how the website looks in reality on Google Chrome. Opera offers a similar picture after entering the URL 10.0.1.181:9002. Firefox makes a fuss, because the creators got it into their heads that they had to bully the users by having their browser only accept https addresses. But there are alternatives. If things get really bad, you could even write your own frontend for the PC using CPython.

Well, I think now you have enough to do with the tree projects until Christmas. Surely there is one or the other for everyone. The important thing is that you enjoy the implementation and that I could arouse your interest. In any case, I wish you a wonderful Advent season.


For Arduino IDE

Download webcontrol.ino

The interface in the browser looks slightly different here. The function is the same.

Depending on whether you want to use your own access point or the local WLAN, you must comment out the respective unused option in the source code at the beginning in the #defines.

// #define WLANconnect true
#define ownAP true

Do not forget to enter your access data when you select the WLAN option.

The IP address of the access point is here:

    192.168.4.1

You can find the password in the source code.

At this point we wish you a happy 1st Advent! 🕯

DisplaysEsp-32Projects for beginnersSensors

6 comments

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!

Leave a comment

All comments are moderated before being published

Recommended blog posts

  1. Install ESP32 now from the board manager
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - ESP programming via WLAN