Electronics
& MicroPython©
with ESP32©:
80 Complete Projects
© 2024 Gregorio Chenlo (@arquiteutis)
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Index (v2)
Dedicated 6
Introduction 7
Other titles by the author 14
Copyright 16
1.-HARDWARE 18
• Box of the ESP32© 21
• Heat sinks 22
• Power supply 22
• Connection cables 22
• The ESP32© module 22
1. SOC 24
2. USB controller 24
3. USB 24
4. BOOT 24
5. Regulator 24
6. Antenna 24
7. GPIO 24
2.-SOFTWARE 26
• Installation of Python© & ESPTool© 27
• Thonny© MicroPython© Editor 29
3.-MICROPYTHON 31
• The interpreter Thonny© 33
• The art of programming 38
• Structure of a program 39
• Importing libraries 45
• Comments 47
• Parameters & variables 48
• Type of data 49
1. Numerical data 49
2. Logical data 50
3. Characters 50
4. Time 51
5. Lists 52
6. Dictionaries 53
• Input & output 55
• Operators 57
1. Mathematical operators 57
2
Electronics & MicroPython© with ESP32©: 80 complete projects
2. Logical operators 59
3. Comparator operators 60
4. Bit operators 61
5. Character operators 62
6. Assignment and inclusion operators 64
• Flow control 66
1. If conditional 66
2. While loops 68
3. For loops 72
• User functions 74
• Predefined functions 78
1. Functions with lists and strings 78
2. Functions with numbers and strings 79
3. Functions with dictionaries 80
• Files 82
1. Using MicroPython© 82
2. Using Thonny© 83
3. Using the file manager 83
• Error control 87
• Command summary 90
4.-EXERCISES 95
• Basic Electricity 96
• ESP32© GPIO usage 100
• E1: Turn on/off an LED 103
• E2: SOS signal with LED 112
• E3: DC10EGWA© level indicator 116
• E4: JQC3F© on/off relay 120
• E5: KSK-1A66© reed switch 126
• E6: OS25B10© photo switch 133
• E7: LED management with a button 137
• E8: ESP32© internal data 148
• E9: ESP32© internal watchdog 150
• E10: Connect the ESP32© to WiFi 156
• E11: Scan WiFi with ESP32© 160
• E12: Analog to Digital Converter 163
• E13: Digital to Analog Converter 169
• E14: Wav generator with DAC 174
• E15: KY-006© passive buzz & DAC 183
• E16: DAC and *.wav files 188
• E17: DHT11/22© double sensor 194
• E18: DS18B20© temperature sensor 200
• E19: KY-106© RGB LED on/off 208
3
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• E20: RGB LED and PWM 215
• E21: Fan and ESP32© temperature 227
• E22: L293D© DC motor control 234
• E23: SG90© servo control 242
• E24: ESP32© internal clock 248
• E25: DS1302© external clock 253
• E26: Web server 259
• E27: BLE Bluetooth 272
• E28: ESP32© internal timer 277
• E29: KY-040© rotary encoder 287
• E30: Frequency meter with 7555© 296
• E31: HC-SR501© motion sensor 302
• E32: HLK-LD2410C© human presence sensor 306
• E33: ESP32© and TTP223© touch sensors 312
• E34: 49E© Hall sensor 319
• E35: 44E© Hall switch 325
• E36: MF52AT© NTC thermistor sensor 329
• E37: Switch with thermistor 335
• E38: KY-033© line detector 339
• E39: ERD29© laser diode 345
• E40: TCS3200© colors sensor 349
• E41: KY-032© obstacle sensor 354
• E42: 1838B© infrared receiver 359
• E43: Remote control with infrared 363
• E44: SW-200D© tilt sensor 368
• E45: SW-18010P© vibration sensor 372
• E46: MAX-4466© sound sensor 375
• E47: HC-SR04© ultrasound sensor 380
• E48: GL5539© photoresistor sensor 386
• E49: TIL-78© fire sensor 390
• E50: MQ-2 gas and smoke sensor 394
• E51: SKU-500© rain sensor 396
• E52: Scan I2C bus 400
• E53: BMP280© barometric sensor 404
• E54: YB-GS07381© water pressure sensor 408
• E55: GY-271© magnetic sensor 413
• E56: OV7670© camera sensor 418
• E57: COM90133P© joystick 422
• E58: HW-209© logic level converter 427
• E59: MQ135© air quality sensor 429
• E60: Alternating current sensor 433
• E61: Send email with ESP32© 436
4
Electronics & MicroPython© with ESP32©: 80 complete projects
• E62: Home Automation with IFTTT© 441
• E63: NO-IP© remote management 451
• E64: ADXL345© accelerometer 456
• E65: MPU6050© gyroscope 462
• E66: PCF8574© GPIO extender 469
• E67: SH1106© OLED screen 475
• E68: PCM5102© I2S player 482
• E69: Simultaneous ESP32© execution 489
• E70: 74HC595© 8 LED manager 494
• E71: SMA42056© 6 segments display 500
• E72: 4x7 segment cascade display 505
• E73: LCD1602© I2C display 511
• E74: LCD1602© display with PCF8574© 516
• E75: ILI9341© TFT display 523
• E76: RC522© RFID reader 530
• E77: Angeek© SPI uSD memory 537
• E78: Samiore© mp3 player 554
• E79: 4x4 matrix keyboard 559
• E80: MAX7219© LED matrix display 563
5.-ADDITIONAL SOFTWARE 570
• IPscanner Home© 571
• iCircuit© 572
• Wokwi© 573
• KingstVIS© 574
• OpenHantek© 575
• chatPGT© 576
6.-MORE INFORMATION 577
• Python2© vs Python3© 578
• Interruptions managed by the ESP32© 582
• IFTTT© 583
• Home Assistant© and ESPHome© 585
◦ Home Assistant© installation 587
◦ ESPHome© installation 588
• MB-102© power supply 591
• Eagle© 592
• Bibliography 593
• Glossary of terms 595
• Thanks 600
⊝⊝⊝
5
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Dedicated to my Son
For decades I was dreaming,
wishing to know how you'd be,
waiting for you, how long your hair would be.
And when you were finally born,
you looked me in the eyes, held my finger tight,
and we became friends forever, from this first light.
I was with you, helping you grow older,
listening and reading your mind,
getting stronger, shoulder to shoulder.
You're unique, having you is worth it,
you always surprise me, nothing compares to you,
you're what I love most, a part of me true.
I no longer have you, many will say that I fell no sorrow,
obviously they're wrong, I'll always be with you,
in any world, dear and admired Son, through every morrow.
The more you grow, the higher up I lose you,
wait for me and forgive me if I'm so slow,
remember, I'll always carry you inside, wherever I go.
We have our talks, the good and the bad,
of course it unites us, making us whole,
it helps and encourages us to continue, both glad and sad.
I'm very proud of you, I tell you little,
I'll always be what you need, my friend,
believe me, it's what I feel, a love that will never end.
⊝⊝⊝
6
Electronics & MicroPython© with ESP32©: 80 complete projects
INTRODUCTION
W
hen I published my first book “Home
Automation with Raspberry©, Google© and
Python©: an useful and fun Home Automation
project”, www.amazon.com, both in Spanish
and English, I received several comments from readers
encouraging me to write another book for beginners in
the use of micro controllers such as the Raspberry©,
Arduino© or ESP32© as a basis for the creation of
small projects development exercises, which will be
explained step by step, more slowly, in more detail,
more easily, etc. how to use an ESP32©, starting from
scratch and adding elements from less to more
complexity: LED, buttons, sensors, sounds, switches,
controllers, Home Automation devices, access from the
Web, voice control, IFTTT©, server Web, etc.
Th e fi r s t b o o k d e s cr i b ed h ow t o us e t he
Raspberry© to create a specific and complete Home
Automation project with multiple sensors (temperature,
humidity, thermostat with geolocation, power supply
failures, garage door status sensor, air toxicity in
the garage, ringing the main door bell, presence
sensors, gas leaks, fire and smoke detector, flood,
Internet connection status, buttons, etc.), multiple
actuators (lighting, raising/lowering of blinds,
system status LED, acoustic and voice signals,
watchdog barking simulation, relays, gas and water
valves, electronic control by “watchdog”, etc.).
The book also included intelligent control
algorithms (repetitive processes, routines, scenes,
etc.), use of application integrators and devices from
various manufacturers, such as IFTTT©, or even
bidirectional management of the entire system using
voice, broadcasting, with a natural language, using
Artificial Intelligence (AI) included in an assistant
such as Google Home© or Alexa©.
7
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Therefore, in response to this proposal from the
readers of my first books, I have decided to write
this one and it aims to help the beginner reader to
acquire general basic knowledge about Electronics and
particulars in the use of an ESP32© to be able to use
their management potential of various electronic
devices, Home Automation, etc.
There are already several publications on the
market that pursue similar objectives to this one,
especially in English, but they tend to be very
theoretical, boring, too concise, without sufficient
depth, without explaining the reasons of each item,
oriented to very complex projects that on many
occasions they only apply to very specific solutions
and do not help a solid learning process which allows
the development of one's own project.
On the contrary, no specific closed project is
developed in this book, but small projects or
exercises that consolidate knowledge are explained in
depth so that a beginner and enthusiast of self-
learning and self-development is able to implement
their own ideas that will surely be new and creative,
unpublished and excellent.
In this sense, this book contains a first part
where the basic concepts necessary to have the
hardware (choice of the ESP32©, GPIO details, modules,
components, etc.) and the software (firmware loading,
instructions, configuration, update, IP assignment,
remote access, system startup, devices startup,
MicroPython© editing and management software, etc.)
necessary to start a recreational training activity
around Electronics managed with an ESP32©.
The second part of the book includes some
practical exercises, 80 with detailed solutions and
almost 400 proposed for the reader to design, develop
and experiment on their own, to strengthen the
formation of basic theoretical concepts and with
increasing complexity exercise by exercise.
Each exercise describes the objective pursued,
the necessary components and the theory, hardware and
8
Electronics & MicroPython© with ESP32©: 80 complete projects
software, essential to understand the concepts and
achieve the objective, in addition all of this written
in a language that pursues entertainment and fun for
the reader, with graphics, diagrams, formulas, photos,
etc. that facilitate a greater understanding of all
the concepts used.
The third part includes a list of additional
software (applications for the ESP32©, computer,
mobile phone and tools), which help improve the
project development process, pre-testing,
documentation, ESP32© advance features, search for
technical resources, etc., making the entire process
much simpler, easier, more affordable and friendly.
Today, fans of these technologies are very
fortunate that in recent decades the development of
consumer electronic systems has been drastically
promoted, the ease of access to new technologies by
anyone interested, the proliferation of electronic
devices of all types (controllers, sensors, actuators,
applications, integrators, voice assistants, etc.),
the lowering of these systems that make the price no
longer a problem for their use.
To all of the above, we must add the
“democratization” in the use of Artificial
Intelligence (both software and specific devices,
personal assistants, etc.), the great ease of access
to information provided by the Internet (search
engines, training content, free dissemination of
information, explanatory videos, manuals, discussion
fo r um s , g r o up s , n e tw o r ks , AI , d ow n lo a di n g of
information by manufacturers and users, tutorials,
etc.), application and device integration software
(type “If this then that” or IFTTT©) etc. what they do
with all this is that there is no excuse not to face
and dedicate a few hours to training in this
technology, considering the process as a formal and
structured training but also, and very important, as
part of a hobby or time dedicated to entertainment.
With all these factors, the emergence of
Electronic and computer systems has been promoted, at
a very low cost or even free, associated with the
9
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
needs of digitalization, learning, personal
de ve l op m en t , l e is u re , ho b bi es , en t er t ai n me n t,
training, self-development, etc., that have completely
eliminated the barriers to entry so that anyone, of
any age and with any type of knowledge, wants to get
started in this world.
In addition to everything mentioned in relation
to electronic devices in particular and hardware in
general, the Python© programming language has burst
onto the market for years and with increasing force,
with application in multiple facets of the industry
and even in our daily lives. This language, in its
version adapted to micro controllers called
MicroPython©, will allow us to extract, in a pleasant
and simple way, the most out of the ESP32© to be able
to carry out multiple interesting, useful and fun
projects.
We will go into all the details of the
MicroPython© programming environment later, but we
could anticipate that this programming language is a
new and special version of the already very famous
Python3©, designed specifically for micro controllers
and embedded systems, that is, systems that include
integrated processors in other larger systems.
With MicroPython© we will learn Python© and we
will be able to run scripts written in this language
on cheap, easily available devices with limited
resources, such as development boards such as ESP32©,
STM32©, etc. We can quickly and easily program micro
controllers using a high-level language, which
facilitates the development of projects for hobbyists,
teachers, students and professionals in Electronics,
Computer Science, Home Automation, entertainment,
training, R&D, etc.
We will be able to use MicroPython© to automate
the home, control electronic devices, robotics, data
acquisition and prototyping IoT (Internet of Things)
products. MicroPython© has an active community that
contributes libraries and resources to expand its
functionality and compatibility with different devices
and hardware types.
10
Electronics & MicroPython© with ESP32©: 80 complete projects
Basic questions about Python©, MicroPython© and
ESP32© often arise, such as the following, which this
book solves:
• Did they give you an ESP32© and you don't know
what to do with it?: This book explains, in
detail, what basic hardware and software you
need so that you can use your ESP32© with
MicroPython© as a training language or for the
development of various projects.
You can easily learn to write programs in this
great language, learn the basic commands one by
one, load libraries already tested by other
users, enjoy multiple examples so that you can
finally design and control your own projects.
• Would you like to know how to use the
MicroPython© programming language to build a
simple project and you have no idea where to
start?: this book teaches you basic MicroPython©
from scratch, explains in detail the structure
of a program, the various instructions, the
import of external libraries, the design of
functions, error control, etc. and how to create
simple programs, without complications, easy to
follow and all this in a pleasant and fun
environment.
• Do you already have an ESP32©, already know
something about Python© and would like to get
more out of it?: this book guides you how you
can enhance your knowledge and develop more
complete and complex projects with professional
tools, WAV and MP3 sounds, WiFi connection or
Bluetooth, Home Automation integration with
IFTTT©, Web servers, network file sharing, etc.
• Do you like Electronics and its integration with
Home Automation but you don't know how to do
it?: in this book you have the opportunity to
learn to program in MicroPython©, use an ESP32©
to control that Electronics and enhance the
project with multiple exercises.
11
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Exercise in MicroPython©, whether basic or
advanced, can be done by reading a command book,
browsing a website, an online course, etc., but all of
this is pure theory and difficult to assimilate.
It is much more effective for your training to
perform the exercises as described in this book, that
is, to exercise the learning on a real and not a
virtual hardware platform, for example an ESP32©. With
the method proposed here we can check how the code we
have written really “runs”, how well or badly the
small programs we have designed behave and therefore
review instantly and live, the topics that you and
only you need.
Finally, this book pursues the training,
entertainment and development of the reader, therefore
it does not include any “turnkey” project or “plug &
play” service, on the contrary, it seeks to make the
path followed be the driving force of the training
objective and personal development and not the
destination of the trip.
To do this, both the 80 already solved exercises
or projects are included, as well as another 400
proposed and unsolved exercises, therefore "have a
good trip" and I hope that after reading the book, or
part of it, you have acquired, for on the one hand,
some basic knowledge that will serve as an impetus to
fly in this world, where MicroPython© and ESP32© are
the springboards and on the other hand you have spent
a time of entertainment in which the process of trial
and error is the foundation of a solid learning.
Important: t o e d i t t h e e x e r c i s e s i t i s
preferable that yourself use the Thonny© editor (or
another one) to create or modify a MicroPython©
script, you will gain practice in writing,
indentation, error correction, maintenance, etc. and
you will fix the theoretical knowledge acquired much
better.
For your convenience, in the Python© section of
my blog indicated below, you will find the complete
scripts for the exercises on:
12
Electronics & MicroPython© with ESP32©: 80 complete projects
https://gregochenlo.blogspot.com/
In this blog you also find
more information about my books.
In the Python© section of the
blog you will also find two folders
(Spanish and Italian) that include
mo re t ha n 50 c om p le t e P y th o n©
exercises described in detail that deal with the use
of multiple sensors and actuators based on basic
Electronics controlled with a Raspberry© and you will
also find most of the exercises in this book.
These exercises are fully explained in my
already mentioned books and whether you have them or
not, you can use them to practice Python© and
MicroPython©, learn functions, guide projects, etc. I
recommend viewing and putting into practice the ones
that interest you the most.
⊝⊝⊝
13
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
OTHER TITLES BY THE AUTHOR
“Domótica con Raspberry©, Google© y Python©” (Ed-1)
“Domótica con Raspberry©, Google© y Python©” (Ed-2)
“Domótica con Raspberry©, Google© y Python©” (Ed-3)
“Home Automation with Raspberry©, Google© & Python©”
“Electrónica divertida con Raspberry©”
“Elettronica divertente con Raspberry©”
“Electrónica y Domótica con Raspberry©”
“Python© con Raspberry© y Google©”
“MicroPython© con ESP32©”
“Electrónica y MicroPython© con ESP32©”
“400 Ejercicios Resueltos de Física Universitaria”
“400 Solved Exercises of University Physics”
“400 Esercizi Risolti di Fisica Universitaria”
“400 Problemas Resolvidos de Física Universitaria”
“Ejercicios de Física 1: Cálculo Vectorial”
“Ejercicios de Física 2: Mecánica Clásica”
“Ejercicios de Física 3: Mecánica de Fluidos”
“Ejercicios de Física 4: Calorimetría y Termodinámica”
“Ejercicios de Física 5: Campo Eléctrico y Magnético”
“Ejercicios de Física 6: Corriente Continua y Alterna”
“Algebra y Análisis en Carreras Universitarias”
“50 Poesías sin Título”
“Pescando Tiburones”
“Pescando Squali”
“Snooker fácil”
“Superar los Problemas de Próstata”
“Porra de Próstata”
“Prostata Infiammata”
⊝⊝⊝
14
Electronics & MicroPython© with ESP32©: 80 complete projects
15
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
COPYRIGHT©
T
he author of this book is Gregorio Chenlo,
who reserves the rights that the Law grants
him in each region or country where this
book is published.
This book, in its 1st edition, was published in
October 2024 and the copyright that Spanish & USA Law
grants it, applies from the moment of its publication.
All rights reserved. Total or partial reproduction of
this work is not permitted.
With the © symbol next to each product, logo,
idea, etc., we want to indicate respect for the
possible brands, owners and suppliers of hardware,
software, etc., described here, so it could be
recognized that all of them are possibly registered
trademarks and potentially have the rights that the
Law may grant them.
The author is not an expert on this subject and
does not have the information or does not know if any
of them are subject to any type of copyright or other
rights that would prevent him from using them as a
reference in this book. All of them are extracted from
the Google© public browser, so it is understood that
their use is completely public to use them, at least
as a reference, in works similar to this one.
On the other hand, it is stated that the system
detailed here is used and is intended only for use in
the particular home environment, training and/or
recreational use, as it is written, without any
commercial pretension, without any guarantee and
declining all the responsibility that readers, other
people, third parties, companies, etc., may carry out
16
Electronics & MicroPython© with ESP32©: 80 complete projects
on their own and for the use of all or part of the
information described here.
Although everything described in this book has
been sufficiently implemented and tested, any
responsibility is also declined for incorrect or not
exactly identical operation of the various components,
both hardware and software.
Finally, indicate that the various public
sources used, Web, etc., are attached, reaffirming the
rights that may correspond to them and declining any
type of responsibility, guarantee, etc., as a
consequence of the inaccuracy, variation or
disappearance of these sources of information.
⊝⊝⊝
17
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
1.-HARDWARE
A
s we have already mentioned, we are going
to carry out Electronics exercises using an
ESP32© that we will program with
MicroPython©, therefore our scripts will
run in real life with that micro controller so we need
to know its hardware in some depth and that is what we
will do in this first chapter of this book.
There are several options to
perform these code tests. We
could use a PC©, MAC©, hardware
simulator to practice, but I
have believed that it is much
more interesting to use a
physical ESP32© that, due to
its low price and availability
of technical information, we
can dedicate “full time” to
this task.
There are various types and models of ESP32© and
above all multiple modules (ESP32© with voltage
regulator, WiFi, Bluetooth, USB interface, pins, etc.)
on the market that can be used for this project. Until
now, the legendary ESP8266© dominated the market, but
I recommend using the ESP32©, which is much more
powerful, with more and better hardware, a very low
price and also very easy to find on the market.
There are also various modules and it will be up
to the reader to choose the one they want to use based
on their current but, above all, future needs. The
price of all ESP32© models and all modules with ESP32©
is very low, therefore my recommendation is to use a
module, with all the components and with 38 pins since
it incorporates more GPIO.
18
Electronics & MicroPython© with ESP32©: 80 complete projects
Below we can see a brief comparison of the basic
parameters of the most interesting ESP32© modules
currently available.
Element ESP32© ESP32-S2© ESP32-C3© ESP32-S3©
CPU Xtensa© Xtensa© RISC-V© Xtensa©
Cores 1-32bits 1-32bits 1-32bits 2-32bits
Hz 240MHz 240MHz 160MHz 240MHz
RAM 520kB 320kB 400kB 512kB
Ext RAM 16MB 1GB 16MB 1GB
WiFi 2.4GHz 2.4GHz 2.4GHz 2.4GHz
Bluetooth V4.2 BLE - V5.0 V5.0
ADC 2x12bit 2x12bit 2x12bit 2x12bit
DAC 2x8bit 2x8bit - -
Timers 4x64bit 4x64bit 2x54bit 4x54bit
Sensor ºF - 1 1 1
Touch 10 14 - 14
Hall 1 - - -
GPIO 34 43 22 45
SPI 4 4 3 4
UART 3 2 2 3
I2C 2 2 1 2
I2S 2 1 2 2
PWM 16 8 6 8
The manufacturer of the ESP32©, Espressif©, has
all the information on its website and also a very
useful comparison tool, but the price of all versions
is very similar, therefore my recommendation is to
choose one of the most complete unless the dimensions
are a limitation of use.
As can be seen, we have multiple options for the
ESP32© controller to which we must additionally add
multiple options if we use a module and therefore the
19
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
options are very wide. In this book, an ESP32© has
been used in a WROOM C3 module that has the following
characteristics and that each reader can decide to use
according to their needs.
• A module from the manufacturer XTVTX© that
includes a SOC (System on Chip) with: ESP32-S2©,
320KB RAM, 4MB Flash, WiFi and Bluetooth chip
and a small printed circuit board with
everything we need.
• Two buttons: RESET which restarts the ESP32©
micro controller and BOOT which is generally
necessary to load new firmware, this last button
can be used as a general purpose button when
connected to GPIO0.
• Micro USB port to power the ESP32© module at +5V
and internally the board regulator transforms it
into +3.3V. Let us remember that all GPIO pins
and other connection protocols can only be
connected to signals of +3.3V maximum. If this
voltage is exceeded, the ESP32© or any of the
elements of the SOC or module will burn out.
There are ESP32© modules, which use USB-C ports
for power and others have other USB ports (micro
USB or USB-C) for connecting other devices.
• The module has 38 pins with various functions
described below, listing them from pin 1 to 38.
• +3.3V: output pin of this voltage when the
module is powered by micro USB or VIN or module
power input pin at +3.3V. This pin does not
provide more than 200mA-250mA.
• RESET: connection input pin to reset the micro
controller, performing the same function as the
RESET button.
• GPIO (GPIOXX): pins configurable as general
purpose inputs/outputs (some are input only and
some are output only).
20
Electronics & MicroPython© with ESP32©: 80 complete projects
• GPIO (ADCXX): configurable as analog to digital
converters or ADC. Although there are multiple
ADC inputs, internally there is only one
operational 12bit ADC, whose input can be
selected by software.
• GPIO (DACXX): configurable as digital to analog
converters or DAC. The ESP32© has two
independent 8bit DAC.
• GPIO (TOUCHX): configurable as touch contact
sensors that can be used as inputs.
• GND: ground pins required for proper power.
• GPIO (RXX-TXX): intended for asynchronous serial
communication or UART, in this case 3
independent UART are available.
• GPIO (FLASHXX): for updating, programming and
maintaining the internal firmware of the ESP32©.
• VIN: +5V input to power the ESP32© module as an
alternative to the micro USB port.
• GPI O ( VS PI ): us ed in sy nc hr ono us se ri al
communication of the SPI port intended for
communications, for example, with screens,
memories, etc.
• GPIO (I2CXXX): for high-speed serial
communication between devices and integrated
circuits over an I2C bus.
Depending on the ESP32© module model chosen, we
will need some additional components, which we
will see in detail below:
1. Box for the ESP32©: Depending on the use we give
to our project, we will need a box or not. If we
are only going to use the ESP32© to learn
Electronics, MicroPython© and with a few
peripherals (sensors or actuators) we will not
need any box, it is enough to place the ESP32©
21
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
module on a breadboard or even with some cables
inserted or soldered on the pins that we are
going to use is more than enough.
2. Heat sinks: The ESP32© is a low-performance,
very low-consumption micro controller and
therefore will not need generally active or
passive coolers.
There is an exception and that is when we power
the module through the VIN pin with a high
voltage (always lower than +7V) and in that case
the internal +3.3V voltage regulator of the
module can heat up moderately.
3. Power supply: This is a very important element
because we need it to be as stable as possible
so that there is no loss of information in our
scripts or deterioration of the hardware. It is
best to power the module through the micro USB
or USB-C port with a stabilized +5V charger and
let the module make the conversion to the
working +3.3V of the ESP32©.
In this book the module has been connected to
the USB port of a MAC© from where all the
programming has been carried out without any
problem. In any case, let's consider that each
GPIO can only safely supply a maximum of 40mA
with a limit of 250mA for the maximum total
current supplied by the ESP32©. See annex with
more information.
4. Connection cables: We will need the micro USB or
USB-C connection cable for power and data from
the PC or MAC, as well as Dupont© type
connection cables, for example, if we connect
the ESP32© to a breadboard. In principle, to
learn only MicroPython©, we will only need the
corresponding USB cable.
5. The ESP32© module: The ESP32© printed circuit
board includes multiple components, as we see in
the following figure, but we are interested in
those discussed below.
22
Electronics & MicroPython© with ESP32©: 80 complete projects
In this book, an ESP32© WROOM3-32 module has
been used and described because it has, at the time of
writing, very acceptable performance, 38 pins
configurable with all types of communication ports,
very easy to obtain, at a reasonable very small price
and with infinite information on the Internet.
This ESP32© has a dual-core ESP32-D0WDQ6©
processor, type MCU LX6©, these cores can be activated
separately, an UART type CP2102© and a DEV KIT C
configuration that allows compatibility with multiple
already designed projects.
The board has WiFi and Bluetooth low consumption
or BLE, its own MAC address and an LED connected to
the UART I/O port and manageable in the GPIO01.
The WiFi IP address is set by software, the CPU
clock speed is configurable between 80-240MHz. The
GPIO provide up to 40mA output, the DAC only 14mA and
has the SPI, UART, I2C, I2S, 1-Wire©, Touch, etc.
protocols. The firmware loading is automatic and in
general does not require pressing the BOOT button on
the board that we had mentioned.
23
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• SOC: The SOC (System On Chip) resides in this
metal package, that is, the ESP32© CPU, the RAM
and a WiFi and Bluetooth controller.
• USB Controller: This chip allows the ESP32© to
communicate with the outside world through a
micro USB or USB-C port. In general there are
two types of this controller: the CP2102© which
is square and the CH340C© which is rectangular,
personally I prefer the CP2102© for being more
reliable and with drivers that are easier to
obtain.
• USB: Here we will connect the micro USB or USB-C
charger if the module is already configured, the
firmware loaded and the programs installed and
therefore we do not need data or a USB, charging
and data cable, to connect it to the computer
from where we are going to work with
MicroPython© and the ESP32©.
• BOOT: T h i s b u t t o n, i n c o rp o r a t e d i n t o th e
module's printed circuit board, is usually used
to load the initial firmware of the ESP32© or
also as a general purpose button.
In the latter case, in most modules, this button
is configured as GPIO0 and we can use it by
controlling it with our software.
• Regulator: As we have already mentioned, the
ESP32© uses +3.3V for its internal operation and
for the control of all GPIO. When powering the
module with a +5V USB cable, the system requires
a +3.3V voltage regulator.
• Antenna: This component allows 2.4GHz WiFi and
Bluetooth BLE (low consumption) communication
between the ESP32© and the rest of the devices.
• GPIO: Finally, the module has 38 pins where the
GPIO are accessible, that is, input and output
ports that are configurable by software and that
allow us to directly connect multiple devices:
sensors, components, actuators, etc. but also
24
Electronics & MicroPython© with ESP32©: 80 complete projects
us e i ts pi n s, in di vi du a ll y o r i n gr ou ps
(communications protocols SPI, I2C, I2S, 1-
Wire©, UART, parallel, etc.), to be able to
carry out exercises and small projects. In this
book we will see how to use this connector from
MicroPython©. In any case, handle the module
with care, avoiding touching the GPIO pins with
metal parts.
In short, we need hardware elements whose use
will depend on the objective of the current project
and the future that we want to give to our system,
however, to learn basic Electronics and MicroPython©,
it is enough with a basic ESP32© module, the USB cable
power supply, a breadboard where the module and
components, sensors, actuators, etc. that are
necessary, can be located.
Finally, since the maximum output current of a
GPIO cannot exceed 40mA, this current must be limited
with the corresponding resistors if necessary. The DAC
converter provides a maximum output current of 14mA,
so i ts ou tp ut mus t b e l im it ed w it h a 220 Ω
resistance or equivalent and which we will see in
detail in each exercise.
⊝⊝⊝
25
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
2.-SOFTWARE
Logically, in addition to the hardware that we
have already detailed, we need a software environment
(the ESP32© does not have an operating system or any
factory firmware pre loaded) to interact with the
hardware comfortably and some basic applications to
access the various functions that we want our ESP32©
to perform in each of the Electronics with
MicroPython© exercises that we develop.
Therefore, in this section we will see the
topics discussed below and that will help us to
comfortably access all the software and hardware
functions of the ESP32© so that later, programming in
basic MicroPython© is as efficient as possible.
Software required to run MicroPython©:
• Installation of Python© and ESPTool©.
• MicroPython© Thonny© editor.
At the end of the book we also include other
additional software that helps us a lot to build
electronic projects using an ESP32©.
⊝⊝⊝
26
Electronics & MicroPython© with ESP32©: 80 complete projects
*Installation of
Python© & ESPTool©
For the MicroPython©
environment works properly
on our ESP32©, we must
install a version of Python©
on the computer, higher than
3.0. We do the following in
Windows© or MAC©:
PYTHON© INSTALLATION:
Go to www.python.org and do:
[download] [macOS] or [windows, use option 64]
Latest Python 3 Release - Python 3.12.1
Follow the instructions on the screen, locate
the APP in the application bar and check the installed
version from Terminal or CMD with: python –version.
INSTALL ESPTool©:
Enter to Terminal and execute the commands:
sudo pip3 install --upgrade pip
sudo pip install esptool
Test the installation of ESPTool© with: esptool.py
INSTALL DRIVER OF THE CP2102© CHIP:
Enter the CP2102© driver manufacturer's website,
that is, www.silabs.com, and access to: [download]
[mac] or [windows], if necessary give permissions to
the driver installation in [security].
For MAC© we can see the installed driver from
t h e t e r m i n a l w i t h t h e c o m m a n d : ls /dev/tty.*,
displaying the installed device:
/dev/cu.SLAB_USBtoUART. For Windows©, the driver can
be located in the traditional device manager window.
27
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
DELETE THE ESP32©:
E n t e r Terminal o r CMD and use the following
command, it is quite long, check carefully that it is
written correctly:
esptool.py --chip esp32 --port /dev/tty.
SLAB_USBtoUART erase_flash
If everything is ok, the following text or
similar will appear:
Chip is ESP32-D0WD-V3 (review v3.0)
Features: WiFi, BT, Dual Core, 240MHz, VRef
calibration in efuse, Coding Scheme None Crystal is
40MHz MAC: [the MAC of your ESP32©]
INSTALL NEW FIRMWARE:
We now have everything almost ready, we just
need to install the firmware on the ESP32© so that we
can program it in MicroPython© and the good news: all
the steps carried out so far do not have to be
repeated (unless there are errors...).
Access the Web www.micropython.org/download and:
[esp32/wroom] and download, for example:
v1.22.1(2024-01-05).bin (ESP32_GENERIC-
20240105-v1.22.1.bin)
Copy it to the command path and from Terminal:
esptool.py --chip esp32 --port /dev/tty.SLAB_USBtoUART
--baud 460800 write_flash -z
0x1000 ESP32_GENERIC-20240105-v1.22.1
Review the command carefully and wait for the
new firmware to be loaded into the ESP32©... and
that's it!!!!.
If there is an error in the previous step,
review the commands, repeat the operation and if
necessary, search for help on the Web, there is plenty
available.
⊝⊝⊝
28
Electronics & MicroPython© with ESP32©: 80 complete projects
*Thonny© MicroPython© Editor
To write, test, load into the ESP32©
and execute MicroPython© scripts we
need a programming editor. There are
multiple programming environment
editors: simple, complex, light, heavy,
with or without aids, adapted or not to
the hardware, etc.
After seeing several programming editors: IDLE©,
PyCharm©, Visual Studio Code©, Thonny©, etc. my
proposal for this basic level of Electronics with
MicroPython© using the ESP32© hardware is the Thonny©
editor because it is very easy to obtain, install,
understand, run, debug, etc.
To install Thonny© we access the Web:
www.thonny.org, we can download it following the
instructions on the screen, place it in a suitable
place and run it directly. We make:
1. We enter on [preferences] and use the Micro-
Python© interpreter (ESP32©) and select in [Port
or WebREPL] the driver installed in the previous
section, for example, CP2102 USB...
2. We open [file] [new program] and we can write a
MicroPython© script in *.py format, which we can
save both on the computer and in the internal
memory of the ESP32©.
3. To run a script we press the [run] button and
stop it with [stop]. At first times we will make
many mistakes but the MicroPython© editor or
interpreter, Thonny©, will give us enough clues
to detect and correct code errors, logically it
will not help us with the algorithm's own
errors. Be patient.
29
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
4. We can stop most of the scripts by pressing
<CTRL>+<C>, previously placing the cursor in the
console or lower window. From the console we can
also restart the ESP32©, by software, by
pressing <CTRL>+<D>.
5. If we want to access the ESP32© via WiFi instead
of a USB cable connection, we must power the
module with a charger or battery and from the
Thonny© console enable WebRPL with the import
webrepl_setup command, activating it with [E]
a n d d e a c t i v a t i n g i t w i t h [D]. I f i t i s
activated, we must assign a password of 8
characters. In [preferences] [interpreter], we
must also activate WebREPL with ws://[IP]:8266/
where [IP] is the IP address of the ESP32© and
the password is the one created previously.
Finally we will have to restart the ESP32© and
activate the WiFi, but to do this we need to run
a specific MicroPython© script that we will see
later in some exercise.
To access to the ESP32© we will do so from a
browser with http://[IP]:8266/ and the previous
password, being able to view the execution of
any script located in the module or executing it
from the console by importing it with import
[script].
6. If we want a script to be executed when the
ESP32© starts, for example the script that
connects the module to the WiFi, we must save
this script in the ESP32© memory within the
boot.py script or within main.py.
To get started in this book I recommend using
the physical connection to the computer via USB cable,
this way we will have everything we need closer, being
able to control it much more easily. Once we master
the language and the hardware we will be able to use
the WiFi connection without any problem.
⊝⊝⊝
30
Electronics & MicroPython© with ESP32©: 80 complete projects
3.-MICROPYTHON©
W
e have seen the ESP32©, the
necessary hardware and the
Thonny© programming
environment to start using
MicroPython©, but before starting
to program we have to know what
is Python© in general and
MicroPython© in particular, how
they works and how we can get the
most out of them doing hundreds
of Electronics exercises.
Python© in general and MicroPython© in
particular are a modern, powerful, widely used, easy
to implement programming languages, which have
multiple libraries or additional programs already
tested and debugged, with manuals of all kinds,
hundreds of tutorials, forums, online courses, books,
millions of users, very easy integration with other
systems and projects, which integrates and manages
perfectly with the ESP32© hardware, etc.
In this book we will use the most current
version of MicroPython© and therefore the changes in
the writing are included: way of importing libraries,
need for parentheses in print() statements, exceptions
management, etc. and also some functions already
integrated as well as a multitude of libraries already
tested by thousands of users that will allow us to
manage our Electronics projects more comfortably.
Comment before continuing that MicroPython© is
an interpreted programming language, that is, once the
code is written, a program must be used to interpret
it, line by line, and translate it, at run time, into
the machine language, that is, to the CPU language of
the ESP32© module.
31
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
This condition of interpreted language has the
advantage that the script code is much easier for us
Humans to read, but its execution is slower because
the code must be interpreted, that is, it must be
translated every time the program is executed, line by
line, to the internal language of the machine, that
is, of the ESP32© CPU.
On the other hand, a compiled language such as
C++© or Java©, are much more difficult for Humans to
understand, but they are translated once into the
machine language and then the machine executes them
much faster than an interpreted language.
In our case, both the program editor, the
interpreter, and the MicroPython© debugger are carried
out through the Thonny© interpreter software, which we
will see below.
⊝⊝⊝
32
Electronics & MicroPython© with ESP32©: 80 complete projects
*The interpreter Thonny©
As we have already mentioned, to start using
MicroPython© in our exercises, we need an interpreter
that will translate and execute our code, transferring
it to the language of the machine we are using, in our
case an ESP32©.
For greater convenience on our part, we can add
the Thonny© icon to the computer's application bar
(MAC© or Windows©) so that, just by pressing it, it
opens directly.
To do this we do the following and logically it
also helps us add icons from other applications or
utilities of the operating system or even personal
applications that execute a Python© script that we
have designed.
Once version 4.1.4 of the Thonny© interpreter
for Python3.10© has been started, we access the main
screen, where we can basically enter commands directly
into the console, open already created MicroPython©
files [*].py, edit, etc.
33
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
At the bottom is another window that is the
console, that is, where the inputs or outputs of the
script are shown and where we can also interact
directly with the ESP32© in MicroPython© by writing at
the prompt [>>>]. Try the following:
>>>a = 5
>>>print(a)
>>>5
This is a very simple example where we have
created a variable called a, we have assigned it the
integer value 5 and we have displayed its value on the
screen. Many times we will need to test complex
functions without having to write and debug a specific
program, for example, try this:
>>>a,b = 'Happy 2024','Birthday'
>>>print(a[:6] + b + a[5:])
>>>Happy Birthday 2024
In general, in this book we are almost never
going to use MicroPython© in this way because we will
lose what we have written, we will only go to the
Thonny© console in very specific cases to see how a
specific command behaves.
We will use programs or scripts that we are
going to save in files with the extension [*].py and
that we will invoke later to modify, execute and save
them again to use when we need.
Like all languages in the real or virtual world,
writing well implies following rules and here it is no
different, we will use a writing protocol for our
scripts whenever we can. This protocol is defined
under the PEP8© standard that contemplates the
following basic rules but, in this book, I have
decided to add an “aesthetic” component that helps to
arrange the information on the screen in a more visual
and readable way.
1. Indentation: Use 4 spaces for each level of
indentation. In general, do not use tabs as they
can be misinterpreted by MicroPython©.
34
Electronics & MicroPython© with ESP32©: 80 complete projects
Adequate indentation is not an aesthetic issue,
it is absolutely crucial in MicroPython© as it
structures the commands in blocks, establishing
a specific order of execution.
2. Line length: Try to limit lines of code to 80
characters to make it easy to read in most
editors and screens. Thonny© allows us to divide
a long line into several lines automatically.
3. Whitespace: Use whitespace consistently to
improve the readability of mathematical
operations, logic, variable assignment, etc. Do
not place spaces at the end of lines of code and
use blank lines appropriately to separate
functions and logical blocks.
4. Names: Use self-descriptive names for the
variables and follow the general convention of
all variables in lowercase with underscores
between words and all constants in uppercase
letters, in any case do not use excessively long
or complex names.
5. Comments: Add comments when necessary to explain
complicated code fragments or to provide
additional information about the purpose of the
code. The comments may seem obvious but they are
not obvious to all readers or for us at all
times. Comments start with #.
6. D o c s t r i n g s :
Include docstrings or self-
explanatory text to describe the purpose and
functionality of functions, classes, modules and
the script itself. This helps generate
documentation automatically. Use # to comment
out lines and text delimited with '''example'''
for long texts.
7. Imports: Group import statements into three
sections: standard MicroPython© imports, third-
party imports, and local imports. Separate these
sections with a blank line.
Also briefly comment on the purpose of each
35
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
imported library using #.
8. Script structure: Organize your code into
functions and classes for easy maintenance and
reuse. Use the block if __name__ == "__main__":
to execute the main code when the script is
executed directly and especially when the script
is very long or complex.
Let's see a summary of the many most useful
Thonny© commands, they are very intuitive, try them:
Command Description
[File] operations with script [*.py]
[Open] open a existing script [*.py]
[Recent files] open recent scripts
[Save] save a script
[Save as] save current script with other name
[Close] close without saving the opened script
[Exit] exit Thonny©
[Edit] edit [*.py] script
[Indent] format specific areas of the script
[Preferences] Thonny© interpreter configuration
[Help] help on using Thonny©
[View] events, plotter, variables, etc.
[Change] change text in open script
[Go to line] goes to a specific line in the script
[Packages] load MicroPython© libraries
As we can see, only some of the most used
Thonny© options for a basic start have been explained,
but their use is very simple and intuitive.
In Thonny©'s own help [Help] or on the Internet
there is enough information to work with all the
exercises in this book.
In addition to the specific examples for each
36
Electronics & MicroPython© with ESP32©: 80 complete projects
concept, in each of the explanations, we will see a
multitude of specific examples for each phase of
programming and thus be able to better establish all
the theoretical concepts.
An attempt has also been made, as far as
possible, to have a common thread between all the
exercises proposed and with a degree of incremental
complexity between all of them. In this way we do not
lose the training thread between exercise and exercise
and we consolidate progress with a solid base and
increasingly complex and powerful concepts.
The necessary concepts are gradually introduced
with fair and essential theoretical explanations, with
more than 40 examples, more than 90 proposed exercises
and in the end 80 complete and solved projects and 400
proposed ones.
The topics to be discussed in the following
chapters have been classified in the following order:
• The art of programming
• Structure of a program
• Importing libraries
• Comments
• Parameters and variables
• Type of data
• Input and output
• Operators
• Flow control
• User functions
• Predefined functions
• Files
• Error control
• Command summary
• Complete exercises
⊝⊝⊝
37
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*The art of programming
Purist professional programmers may not agree
with what I am going to say in this chapter, but I
believe that it is the basis for ensuring that any
training started on MicroPython© is continued to the
end and is not abandoned because it is complicated,
tedious and bored.
The trick is to think that programming in
general and programming in MicroPython© in particular
is like an Art, that is, each one has their own method
and although we have to respect the rules of the
language so that the system understands us, so that
the ESP32© execute the necessary commands, so that the
script achieves the intended objective, etc., it is
essential that we feel comfortable with our own “art
of programming”, that we use the instructions that we
best understand, with which we feel comfortable and
proud even if they are not as efficient as possible.
There was a time in computing when super
efficient and minimalist codes prevailed in order to
take advantage of the limited memory of the devices.
At this time, the speed of execution of the code
itself also took precedence since the speed of the
hardware was very poor.
Nowadays we have an ESP32© internal memory with
sufficient capacity and hardware with more than enough
power for what we are going to practice at our level,
therefore we must orient our art of programming
towards other objectives, especially towards ease of
reading by ourselves and by others, both now and in
the future.
⊝⊝⊝
38
Electronics & MicroPython© with ESP32©: 80 complete projects
*Structure of a program
Regardless of our creativity and “our art to
program”, what is evident is that we must respect the
MicroPython© programming rules, the basic structure of
a program and the PEP8© rules already mentioned.
In general, the basic structure of a basic
MicroPython© program, at our initial level, can
consist of the following sections and in the order
discussed.
We could make multiple changes on our scripts:
change the order, delete some sections and add others
but, at our level, it is very important that we
maintain a fixed structure so that we do not forget
any important section and have a regular structure,
easy to interpret and read at all times.
1. Title, description and parameters
2. Import of libraries
3. Data and variables
4. Functions
5. Main body
6. Error management
We will look at each of the previous points in
detail in the next chapters, however we are going to
detail them a little more to understand what we are
talking about and have an initial global vision.
1.-TITLE, DESCRIPTION AND PARAMETERS
The title and description of our MicroPython©
program is very important to be clear at a glance what
its main objective is, what its inputs are and what
its most important outputs are.
39
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Let's look at our first example of a
MicroPython© program or script, for this we will use
the following instructions in Thonny©:
• [File] [New Program] or by pressing:
• [File] [Save as] [This computer]
p_exercises.py
In this way we will have created the
MicroPython© program p_exercises.py in the directory
of the selected computer. Now the program is empty but
we will add interesting things, which we can modify,
expand, delete, etc. subsequently. Let's add an
important instruction:
# -*- coding: utf-8 - # Special characters
This instruction will guarantee that our program
includes, in the comments, content of variables,
strings, etc., special characters such as accents,
symbols, etc. In many versions of MicroPython© this
instruction is not necessary as it is included in the
editor configuration, test it to ensure its necessity.
Now we will add the following:
###########################
# MODULE: p_exercises.py
# Our first MicroPython© example
# INPUTS: none
# OUTPUTS: none
##########################
[File] [Save] or by pressing:
The program does not do anything yet but it will
do so as we add data, commands, operators, functions,
etc., be patient.
We can run the program with the [Run] option or
the Thonny© button, but it won't do anything at all,
logically, we haven't incorporated commands yet.
Except for the special instruction indicated at
the beginning, everything that begins with #, the
program will interpret as a comment and will not do
40
Electronics & MicroPython© with ESP32©: 80 complete projects
anything with it. If we wanted to make a comment with
many lines, we can delimit them with ''' at the
beginning and at the end.
Let's see that we have added some ### that the
only thing they do is delimit parts of the program. We
will use them every time we want to highlight a part:
the library import body, the data, the functions, the
main body of the program, etc. We can use these
characters or others, but always the first one on the
left must be # to indicate that it is a comment and
not an instruction.
We have also included the name of the program
with a brief description of what it does, the inputs
it uses and the outputs, at the moment nothing at all.
2.-IMPORT OF LIBRARIES
In our program we are going to use other
MicroPython© programs or libraries already written by
third parties and that have been sufficiently tested
so that we can use them with confidence and for free.
For example, the time library will allow us to
calculate the time elapsed between two moments in our
program, the current hour, minutes, seconds, etc.
Let's add the library import body and the time import
to our program with:
#---------------------------
#IMPORT OF LIBRARIES BODY
#---------------------------
import time # Time control
3.-DATA AND VARIABLES
In MicroPython© we can use fixed data that we
can conveniently store with a name and also variables,
that is, data that will change during the course of
the program. Let's use easy-to-understand names, for
example, name instead of x_name%12 or age instead of
f_age_if%18, etc.
41
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Let's add the following to our script:
#---------------------------
#DATA & VARIABLES BODY
#---------------------------
name = 'John' # Variable assignment
age = 20 # We can add spaces
We have created the name data and we have
assigned it the value John. It is important to see
that John is a data, not a variable and therefore it
has to be enclosed in single or double quotes. Try not
putting them on, run the program with and see that
the error appears:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
NameError: name 'John' isn't defined
In the error message we see interesting things:
• The line where the error is, 4
• The error: John is not defined.
4.-FUNCTIONS
To make a program easier to understand, easier
to maintain, simpler to debug and above all so that it
does not have repeated and inefficient code, we will
often use functions created by ourselves and we will
invoke them every time it is necessary.
Let's add the following code to the exercise:
#---------------------------
#FUNCTIONS BODY
#---------------------------
def see(x): # Function see()
global age # Global variable age
edad += 1 # age increment
print(x + ' is ' + str(edad) + ' old')
print(time.localtime()[3:5])
We have created a function called see() that
includes the parameter x, that is, the function see()
operates on the variable that we pass to it in x.
42
Electronics & MicroPython© with ESP32©: 80 complete projects
When, later, see(x) is invoked, it will be
executed: the age data is a data defined outside the
function see(x), 1 is added to it, the result is
stored again in age, it is displayed on the screen
that the variable x has such years and the hour and
minutes at the time of program execution.
Let's try to run the program with we will see
that it does nothing, why?...it does nothing because,
although the function see() is defined, we are not
invoking it, that is, we are not saying anywhere that
this function should be executed. We are going to do
it in the next part of our program.
5.-MAIN BODY
Let's add the next component to our program, the
main body with:
#---------------------------
#MAIN BODY
#---------------------------
if __name__=='__main__': # Script start
see(name) # Call function see()
T h e if __name__=='__main__': s t a t e m e n t i s
reserved and indicates that this is where the
instructions in the main body of the program begin.
We also see that the instructions see(name) and
those of def see(x): are shifted to the right. This
displacement is called indentation or bleeding and is
very very important in MicroPython© as it indicates
which instructions are included in each action.
In our case, the execution of the see(name)
function is included in the main body of the program
and both global, age += 1 and print() are included in
the see(x) function.
Try to eliminate this indentation or bleeding,
we will see that the program behaves in an unexpected
way. Many errors made when writing a MicroPython©
script occur for this reason.
43
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
6.-ERRORS MANAGEMENT
Finally we will add to the main body a group of
instructions to manage possible errors in the
execution of the program.
Error handling is often used when the script
invokes hardware functions that may not be available.
For example, sending an email may be unavailable if we
do not have an Internet connection at the time of
running our script.
Let's add the following instructions to our code
within the main body:
#---------------------------
#MAIN BODY
#---------------------------
if __name__=='__main__': # Script start
try: # Try action
see(name) # see() function
except Exception: # Is there an error?
print('ERROR') # Indicate it
raise # Display error
The function see(name) will be executed, but if
an error occurs, the program flow is resumed by the
except system function and the message 'ERROR' will be
printed.
Try changing the age += 1 instruction to age =
1/0, an error will be generated for dividing 1 by 0.
Also try commenting with # or uncommenting the raise
instruction, see what happens.
We already have the first MicroPython© script,
it doesn't do much, we will expand it as we learn more
commands, operators, functions, etc.
⊝⊝⊝
44
Electronics & MicroPython© with ESP32©: 80 complete projects
*Importing libraries
To complete some of the MicroPython© modules
discussed in this book, we need your own MicroPython©
or third-party libraries already configured and
sufficiently tested without us having to develop them
again from the beginning.
These libraries make our scripts more powerful,
complete and even efficient. The most used in the
exercises in this book are indicated below and are
related to system functions, GPIO, communication
buses, Wi-Fi network, Bluetooth, sensors, actuators,
displays, external RTC, memory cards, mathematical
functions, Web servers , multitasking, etc.
Library Description
utime Time data, hour, minutes, etc.
utime Precise time control
ntptime RTC synchronization server
Pin GPIO control
esp32 ESP32© internal data
DAC Digital to analog management
ADC Analog to digital management
PWM Pulse width modulation signals manager
TouchPad Touch GPIO control
Timer Hardware timer management
network WiFi connection management
usocket Web server connection manager
urequests Web request manager
ubinascii Binary to ACII converter
45
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Library Description
uasyncio Multitasking process manager
ramdom Handling random numbers
array Data buffer management
ir_rx.nec Infrared remote control
SoftI2C I2C data bus manager
I2S I2S music bus manager
SPI SPI data bus manager
dht DHT11© temperature sensor control
onewire 1-Wire© bus management
lcd1602 Display LCD1602© control
lcd_api PCF8574© bus extender manager
i2c_lcd Display LCD1602© with I2C control
ili9341 TFT Display ILI9341© control
ds18x20 DS18B20© temperature sensor manager
sh1106 OLED display SH1106© control
ds1302 External RTC DS1302© control
ds1307 External RTC DS1307© control
BLE Bluetooth BLE connection manager
BME280 BMP280© barometer controller
hmc5883l GY-271© magnetic sensor controller
mfrc522 RFID RC522© card reader management
max7219 Matrix LED controller
uos System functions
sdcard SD and micro SD memory manager
dfplayermini Control of mp3 player modules
⊝⊝⊝
46
Electronics & MicroPython© with ESP32©: 80 complete projects
*Comments
As we have already seen, we can add comments to
each line of code to note what objective it pursues,
but it is very important to add comments in the
following sections, especially to be able to read the
code more easily both at the time of its construction
and after a certain time has passed. If the code is
also going to be read by other people, all the more
reason we should make all the necessary comments.
• Title of the program: pursued objective, its
main inputs and outputs.
• Import body: brief description of each library.
• Data and variables: what we are going to store
in each of them and what they are for.
• Functions: what they pursue, main inputs and
outputs.
• Main body: what objective it pursues and what
main functions it performs.
• Error control: What happens if an expected error
or an unforeseen error occurs.
• Etc.: whenever necessary.
# for one line comments
''' comments ''' for multiple lines or
“”” comments “”” for multiple lines.
EXERCISES:
• Add comments to p_exercises.py
• Try with # and '''
• See what happens using: ''' comment “””
⊝⊝⊝
47
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Parameters & variables
To perform calculations, enter and display
results on the screen, make decisions, transfer data
to functions, etc. we are going to need to create
parameters and variables. We will call parameters data
that remains fixed throughout the script, for example
our name, our age, the size of a window, etc. We will
call variables data that vary depending on
calculations, algorithms, etc.
Both parameters and variables are assigned with
t h e = symbol and must have a name that clearly
identifies what objective it pursues but without being
too extensive so that it is easy to handle and cannot
have accents, etc. For example:
MY_NAME = 'John' # Name assignment
YOUR_NAME = 'Peter'
john_age = 21 # Ages assignment
peter_age = 25
name = MY_NAME # Variable assignment
Note that spaces can be added between the
parameter or variable name and the = sign and that
constants are usually written in capital letters.
Note that name=MY_NAME does not assign the text
'MY_NAME' to the variable name, it assigns the content
o f t h e c o n s t a n t MY_NAME t o t h e v a r i a b l e name,
therefore it is as if we assigned name = 'John'.
EXERCISES:
• Assign to YOUR_NAME the text Julia
• Assign the name variable to YOUR_NAME
• Assign Pedro's age to john_age
⊝⊝⊝
48
Electronics & MicroPython© with ESP32©: 80 complete projects
*Type of data
MicroPython© handles many types of data that are
already established in the system and that we are
going to see in this section, but it also handles
other types of data, which we could create and define,
called objects, for example, we could define what
cities are, what are cars, etc. what characteristics
they have and what functions they perform, but we are
not going to see them in this book because they go
beyond the concepts of a basic MicroPython© course. We
can highlight the following types of basic data, later
we will see how we can operate with each of them.
1.-NUMERICAL DATA
Numerical data are the easiest to understand and
refer to the numbers that we usually use and that we
could classify into:
• integers: both negative and positive, for
example: 2, 3, -2, 1000, etc.
• real: or also called floating point with all
types of decimals, for example: 2.5, 3.0, -2.01,
1e3, etc.
• complex: with integer or real components, for
ex a m p le : 4+5j, 4.0-5.01j, etc. Due to its
complexity, we will not see this last type of
numerical data in this book.
EXAMPLES:
john_age = 21 # Integer
john_weight = 68.1 # Real
margin = .001 # Real
total = 1e2 # Real 1e2=1x10^2=100
negative = -.100 # Real
49
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
EXERCISES:
• Assign a negative integer value to temperature.
• Assign a floating point value to the integer
name variable, is it possible?, is it
appropriate?.
• Assign 13 to x which is a real parameter.
2.-LOGICAL DATA
Logical data, also called Boolean, are those
that admit only the values true or false or in
MicroPython© the values True or False and are widely
used to perform logical operations or make simple
decisions.
EXAMPLES:
repeat = True # Logical data True
not_repeat = False # Logical data False
print(repeat, not_repeat) # Display data
EXERCISES:
• Assign the variable called value the logical
value 'False' including the quotes, analyzes the
impact of the quotes.
• Now assign the value True to the value variable,
without including the quotes, and describe the
difference with the previous exercise.
• How are both cases different?. Look closely at
how Thonny© interprets them when writing them.
3.-CHARACTERS
Characters are text strings of various sizes,
which may include letters (upper or lower case or
both), numbers, special characters, etc., which are
grouped and delimited between single (') or double (“)
quotes and which can be used to manage names, text
sequences, etc.
50
Electronics & MicroPython© with ESP32©: 80 complete projects
EXAMPLES:
city_name = 'Los Angeles'
book_name = 'MicroPython© Exercises'
number = ”123456”
quotes = 'between ”quotes”'
EXERCISES:
• Assign the value “2” to the variable númbèr.
what happens?...the variable names cannot have
accents.
• Assigns the character value '3' to the variable
text, i n c l u d i n g t h e s i n g l e q u o t e s , w h a t
happens?... we can make text = ”'3'”
• Test with text = '”3”'.
4.-TIME
In reality, the time data type is a numeric data
type of the real or floating point type, which
includes all chronological information: year, month,
day of the week, day, hour, minute, second, etc. and
that each of its components can be translated into
integers with the corresponding conversion function.
There are many ways to manage time variables in
MicroPython©, only some examples are discussed here.
In any case, to manage the time variables we must
import the corresponding libraries since they are not
included in Thonny©.
The most used libraries are:
import time # Time library
import RTC # Internal ESP32© clock library
EXAMPLES:
Add the following instructions to our program
p_exercises.py, run it with and observe the results
of each added instruction.
51
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
hour_1 = time.localtime() # Assign time data
hour_2 = time.localtime()[0] # Assign the year
print(hour_1) # Display data
print(hour_2)
We can see that various information about the
time variables can be displayed with different
functions.
Let's see more cases, add the following code and
we can visualize the time elapsed between two
instructions:
now_1 = time.time() # Assign time 1
now_2 = time.time() # Assign time 2
print(now_2-now_1) # Display calculations
Add the following code and see how we can
separately manage the current year, month, day, hour,
minute and second:
now_x = time.localtime()
print(now_x[:3]) # View date, test [0:3]
print(now_x[3:6]) # View time
EXERCISES:
• Display with print() the text type variable
hour='ss:mm:hh' and just below it the value of
the current seconds, minutes and hour.
• Assign the numeric variable now_x the value of
seconds. Change [x:y] and view it.
• Try the previous exercise with both
time.localtime() a n d time.time(). What is the
fundamental difference?.
5.-LISTS
Lists and also dictionaries, which we will see
later, are very interesting types of data, very useful
and that can help us save and manage data in a
grouped, related and very simple, very interesting
way. It's very important to know this type of data
well, because they are widely used.
52
Electronics & MicroPython© with ESP32©: 80 complete projects
A list is assigned in the following way: list =
[e_1,...,e_n], where list[] is the name that we assign
to this variable list and e_1,...,e_n are all the
elements that make up this list, from the first
element, 1, to the last element, n, grouped with []
and separated by commas.
EXAMPLES:
Let's add to our program p_exercises.py the
definition or assignment of the following lists[] with
the following contents:
names = ['John', 'Julia', 'Peter']
ages = [39, 50, 10]
surnames = [['J1', 'J2'],['A1', 'A2'],['P1', 'P2']]
print(names, ages)
print(surnames)
In the previous examples we can see that the
elements of a list[] can be of all types: characters,
numbers, even other lists[] or the empty list: list =
[], etc.
EXERCISES:
• Try viewing the list example = ['a', b, 'c']
what happens?, does an error appear?, why?, how
is this error resolved?.
• Display the list example = ['a, 'b', 'c'] what
happens?, how is the error resolved?.
• Generates, for example, the list today = [] but
containing the current year, day and month as
elements.
6.-DICTIONARIES
Dictionaries are similar to lists but each
element of the dictionary is made up of a pair of
data: a key and a value. The components of each pair
are separated by : and the elements are separated by
commas and grouped with {}.
53
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
A dictionary is assigned as follows: dictionary
= {key_1:value_1,...,key_n:value_n}, where dictionary
is its name, key_1 is the first key, value_1 is its
first value until key_n is the last key and value_n is
its last value.
T h e m a i n d i f f e r e n c e b e t w e e n list[] a n d
dictionary{} is that in the latter we can directly
access each data through the use of keys.
EXAMPLES:
Try adding to p_exercises.py and see how the
definitions and displays of the following dictionaries
behave:
addresses = {'Greg':'USA', 'Julia':'Italy'}
weights = {'Greg':60, 'Julia':50, 'Peter':80}
print(addresses['Greg'], weights['Greg'])
weights['Greg'] = 90
print(addresses['Greg'], weights['Greg'])
We can see how a dictionary data is accessed or
changed through its key. In the operators chapter we
will see many more functions.
EXERCISES:
• C r e a t e t h e variables{} dictionary with the
variables one, two, three and the numerical
values 1, 2, 3. Previously assign each variable
its value and display the variables{}
dictionary.
• In the previous point, modify the values of each
numerical data by its double, accessing to
variables{} key by key.
• Changes data of variables{} to a logical data,
the 2-element string[] and the hour.
⊝⊝⊝
54
Electronics & MicroPython© with ESP32©: 80 complete projects
*Input & output
In order to advance our knowledge of basic
MicroPython©, we will need to provide our programs
with additional data in a comfortable way and without
having to change the code every time some data
changes. That is, we need to be able to have input
data or variables and also be able to display data and
variables as output elements.
We will see the most basic data input and output
formats included in Thonny©: direct display in the
console through print() statements of our code or with
the Thonny© plotter tool.
Keyboard data entry is performed, in its most
basic version of MicroPython©, with the instruction
t y p e variable = input('name'), where variable is a
variable where the input will be loaded and 'name' is
a text that summarizes the content of the input loaded
into the variable variable.
If we want to give a certain format to the input
data, for example, if we want the input data to be an
integer, we have to use functions that we will see
later, for example the int() function.
EXAMPLES:
See what happens with an integer like 3, a real
number like 3.8, a variable like x or a text like
'text' in the following code:
while True: # Infinite loop
try: # Try the following
x = int(input('x=')) # Capture integer data
print(x) # It display them
except: # If there's an error
print('Incorrect data') # It indicates
55
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Later we will see more cases of flow control
such as the while True: statement, which, in this
case, performs an infinite loop since the True
variable is always true and the try:...except: couple
controls possible errors.
For data output we have used the print() command
which supports some variants such as:
name = 'Ana' # Assign name
age = 20 # Assign age
print('%s is %d years old.'%(name,age)) # Substitution
Specifically, with the print output we can use
data format substitutions such as:
Type Description Examples
%s Text strings 'My list: %s'%list[x]
%d Integer data “I'm %d years old”%age
%f Real data 'I weight %f kg'%weight
Real data w i t h n 'You weight %.1f'%70
%.nf
decimals
EXERCISES:
• Eliminate the try:...except: pair and explain
the errors that occur if we do not enter an
integer value in the input() statement.
• Repeat the previous exercise, but with the
function x=float(input('x=')). What happens if
we now enter the value 3.8?, and 3,789?.
• Include this part in the see() function and
define the variable x to present its value if
requested in the input() statement.
⊝⊝⊝
56
Electronics & MicroPython© with ESP32©: 80 complete projects
*Operators
Finally something interesting is going to happen
in our example p_exercises.py, why?..., well now we
are going to use operators that make controlled
changes in all types of variables and therefore we are
going to be able to create algorithms, that is, codes
that use inputs and these operators or operations to
create new outputs that could again be inputs for
other algorithms, etc. Let's look at some operators.
1.-MATHEMATICAL OPERATORS
They are the best known operators, for example:
Operator Description Examples
a+b Add the variables a and b 1+2=3
a-b Subtract a and b 1-2=-1
4/2=2.0
a/b Divides a by b 4/3=1.33333
4./3=1.3333
a%b Calculate remainder a by b 5%2=1
a//b Gets the integer a by b 5//2=2
a**b Raise a to b 3**2=9
These are the basic mathematical operators
commonly used in all areas but, if we want to use more
advanced ones, we will have to use predefined
functions in MicroPython© or invoke additional
libraries, for example, with the following library:
import math.
With these libraries we can calculate the square
root, the factorial, trigonometric functions, random
numbers, exponentials, logarithms, etc.
57
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Add the following example to our p_exercises.py
program:
import math # Import library
print(math.cos(90)) # Display the cosine of 90
print(math.sqrt(2)) # Display square root of 2
Important: l e t ' s n o t e t h a t m a t h e m a t i c a l
operations between integers always obtain a real
decimal number, which is why the result of 2/2=1.0.
Let us be very attentive to these types of
issues because unexpected results often occur, if we
are used to other versions of Python©, in our
mathematical calculations that are integrated into the
MicroPython© code.
If we want to get the exact result as a real
number or floating point number, it is not necessary
for any of them to be real or floating, for example:
4/3 = 4.0/3 = 4/3. = 4.0/3.0 = 1.333333.
EXERCISES:
• D e f i n e t h e v a r i a b l e s : Divisor, dividend,
quotient, remainder and verify, with an example
and using the operators // and %, that it is
true that:
Divisor = dividend * quotient + remainder.
Try integers and real numbers, what happens?.
• Use the trigonometric mathematical functions
included in the math library: sin(x) and cos(x)
and check that cos 2 (x )+sin2 (x )=1.
• Apply the Pythagorean Theorem and calculate the
hypotenuse, H of an equilateral triangle knowing
that C_1=3, C_2=4 and that the formula of such
theorem is the famous: H =√ C_1 2+C_22 .
• Defines the function hypotenuse(x,y) t h a t
calculates the hypotenuse of a right triangle
with sides the variables x and y and that have
been previously entered by keyboard.
Present the result and the sides. Why is the
hypotenuse always a floating point number?.
58
Electronics & MicroPython© with ESP32©: 80 complete projects
2.-LOGICAL OPERATORS
Logical operators are used with the True and
False variables that we have seen and are basically
used to make quick decisions based on previous results
of conditions, functions, variable states, etc. It is
important to know how they work as they affect the
flow of the program, etc.
Operator Description Examples
Returns True if a and b are True and
a and b
both True False=False
Returns True when either a or True or
a or b
b is True False=True
not
not a Returns the opposite of a True=False
EXAMPLES:
a,b = True,False # Assign variables
c = not a # Deny variables
d = not b
print(a and b and c) # Display operations
False
print(a or b and c) # Display others
True
print(a or b or d) # Display others
True
We can use & instead of and, & | instead of or.
The previous result may have surprised us be-
cause in the second case we would expect False and the
program returns True. This is because the operators
not, and, or have that priority in that order and
therefore in a or b and c, b and c is executed first
as if we had typed or (b and c). Try (a or b) and c
and we will see that instead of True coming out the
initially expected False comes out. This same priority
happens with *, /, +, -, etc.
Therefore, on this occasion and in the rest of
the operators and functions, if we have doubts about
the priorities, we use parentheses (...) in the
operations that we need to be executed first.
59
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
EXERCISES:
• With the previous data calculate:
a or not b and d
not (a or d) and b
not a & not d | c&d
• Display all the possible options with a and b
and a or b. Use the not operator if necessary.
• Check with an example if the famous Morgan's
Laws are fulfilled, that is:
not(a & b) = not a or not b
not(a | b) = not a and not b
3.-COMPARATOR OPERATORS
These operators are very well known, they are
used in many areas of programming and everyday life.
They are simple symbols that compare two arguments,
one to the left and other to the right, and return a
True or False value after the comparison.
These operators are usually used to control the
flow of a program depending on whether or not a
condition is met, so that certain instructions,
functions, etc. are executed. Let's look at several
examples:
Operator Description Examples
a > b True if a is greater than b 2>1 is True
a < b True if a is less than b 3<4 is True
a == b True if a is equal than b a==a is True
a != b True if a is different than b 1!=2 is True
T h e > and < operators can be combined with =
like >= <= which are True when a is greater than or
equal to b o r b is greater than or equal to a
respectively.
The comparison options with: <>, ><, =>, =<, =!
are not allowed in MicroPython©. The expression a!=b
is True when a & b are not equal. It is very important
to understand that == and = are not the same.
60
Electronics & MicroPython© with ESP32©: 80 complete projects
EXERCISES:
• View the results of the following comparisons:
-5 < 4 # Less than
a == b # Equal to
'a' != 'b' # Not equal to
'Germany' > 'France' # Greater than
Could the comparison operator > be used to
establish an alphabetical order with character
strings?.
• Try comparisons between lists, for example:
[1,3] > [1,2] and dictionaries, for example:
{1:2} > {1:3}.
MicroPython© does not support the direct
comparison of dictionaries but it does support
the comparison of lists.
• Use the time.localtime() function and also the
time.time() function and check with a comparator
if the time increases or decreases between in-
structions of our program. Try it on console and
see how time evolves.
4.-BIT OPERATORS
On some occasions, especially if we want to
manage electronic hardware from MicroPython© (for
example using the GPIO port of our ESP32©), or we want
to filter some type of information encoded in a bit of
a variable or perform binary calculations, we will
need operate in binary and manage bits directly.
We are not going to explain binary notation or
translations from binary to decimal or hexadecimal and
vice versa in this book (see it on the Internet,
there's a lot of information), but we are going to
summarize some operations of this type.
In the following examples we consider the
following binary numbers: a=100 (4 in decimal) and
b=111 (7 in decimal). We can assign binary numbers in
MicroPython© in the following ways:
a=int('100',2) # a is the binary 100 in base 2 or
a=0b100 # a is the binary 100
61
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Operator Description (bit per bit) Examples
a & b a and b operation a&b=0b100
a | b a or b operation a|b=0b111
a>>n Shift n bits from a to right a>>2=0b1
a<<n Shift n bits from a to left a<<1=0b1000
EXERCISES:
• Add the following code to see how MicroPython©
handles binary numbers. Note that to convert a
result to binary the specific function bin() is
normally used.
a = 0b100
b = 0b111
print(bin(a & b), bin(a | b))
print(bin(a >> 2), bin(a << 1))
• Define a 5-bit binary number as a=0bxxxxx and
apply the operation a&0b00100 to it and verify
that the result is the middle bit of binary a.
This function acts as a bit “filter”.
• The operation a|0b11011 also applies, what type
of filter does this function make?.
5.-CHARACTER OPERATORS
We have already seen that character data are
sequences or strings of letters, numbers, etc. grouped
within a variable delimited with quotes ' or “.
Now let's see how these strings can be managed
with different operators and functions.
Let's start with the chains: a='Hello' and
b=”123”.
Operator Description Examples
a+b Concatenate the a+b='Hello123'
strings a and b b+a='123Hello'
Concatenate a as a*2='HelloHello'
a*2
2 strings a
62
Electronics & MicroPython© with ESP32©: 80 complete projects
Operator Description Examples
a+=b Assign a=a+b a='Hello123'
len(a) Length of a len(a)=5
Return the posi-
a.find('H')=0
a.find('x') tion of 'x' in a a.find('o')=4
(from 0)
a.lower()=
a.lower() a in lowercase 'hello'
a.upper()=
a.upper() a in uppercase 'HELLO'
a.replace('x','y) C h a n g e d a t a 'x' ('H','')='ello'
a.replace
by 'y' into a
a[x:y] Cut a from x to a[1:2]='e'
y-1
Extract position
a[x] a[0]='H'
x of a (from 0)
EXAMPLES:
a='Hello' # Characters with '' or “”
b='123'
print(b[0]) # Display first character of b
'1'
print(a+b) # Concatenate a with b
'Hello123'
a+=b # Change a with a+b
b+=a # Change b with b+a
print(a,b) # Display a and b
'Hello123','123Hello123'
print(a+b) # Concatenate new a and b
'Hello123123Hello123'
print(len(a+b)) # Display length of a+b
19
print((a+b).find('3H')) # Find '3H' in a+b
10 # Starts in 0
print(a.lower()) # Display a in lowercase
'hello123'
print(a.upper()) # Display a in uppercase
'HELLO123'
print(a.replace('H',''))# Change 'H' by '' in a
ello123
print(a[1:2]) # Cut a from 1 to 2-1=1
'e' # x=1 y-1=1, starts in 0
print(a[:6]) # a from start to 4
'Hello1' # Starts in 0
63
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
print(a[2:]) # a from 2 to the end
'llo123]' # Starts in 0
print(a[:-1]) # Delete the last character
'Hello12'
print(a[-1]) # Display the last character
'3'
EXERCISES:
• Extracts from the string a = 'Good morning' the
letter 'd' of the first word and the final 'ing'
of the second and converts a to a = 'ding'.
• Find the character 'o' in the string a = 'Good
morning', why does it only give one value?, how
would we search for the others 'o'?.
• Try to visualize the following parts of a a[:-1]
a n d a[-1:], what purpose can we use negative
integers?.
• Try visualizing a[x:y] where the variables x and
y can be assigned integers, for example,
visualize the following a[1:3], a[-1:3], a[-3:1]
and a[-3:-1]. Interpret the results.
• Display a[x:y] with x and y keyboard-entered
positive integers in a while True: loop with
try:...except: error handling.
• Try viewing a+'\”'. As we can see, it is a
simple way to print double or single quotes.
Likewise a+'\t' includes a tab and a+'\n' a line
break.
6.-ASSIGNMENT AND INCLUSION OPERATORS
We have already seen some assignment operator,
such as = (not to be confused with the == operator
which is a comparison operator) and which allows us to
assign a value to a variable, or also += which allows
us to increment and assign the value of a variable in
a given quantity.
In this section we will see other assignment
operators that allow several consecutive assignment
and variation operations of the variable to be carried
out in a single statement. Let's look at some examples
with a=5 and y=[1,2,3,4].
64
Electronics & MicroPython© with ESP32©: 80 complete projects
Operator Description Examples
a=x Assign x value to a a=5
a+=x Assign a=a+x a+=3 a=a+3=8
a-=x Assign a=a-x a-=3 a=a-3=2
a*=x Assign a=a*x a*=2 a=a*2=10
a/=x Assign a=a/x a/=2 a=a/2=2
a**=x Assign a=a**x a**=2 a=a**2=25
R e t u r n True if x is 3 in y=True
x in y
included in y 'a' in y=False
x not in y Return True if x is not 'a'
3 not in y=False
included in y not in y=True
EXERCISES:
• F o r a = 5 calculate the final value of a by
executing each following statement:
a+=a+2 a-=a-2 a*a=/2 a/a=*2
Let's keep in mind that a is an integer.
• Could we do y+=1?, could we do it with each
component of the list y[], that is, y[i]+=1
where i is a pointer that goes from 0 to len(y)-
1?, why not even len(y)?, try it. Check if: y[i]
+=1 == y[i]=+1, why?.
• Look at the result of a in b in the cases:
If a='12' and b=[a,'13']
If a='12' and b=['1','123']
If a='12' and b='123'.
⊝⊝⊝
65
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Flow control
In the vast majority of our programs, whether or
not they are written in MicroPython©, we will need
their flow, that is, the sequence of execution of
their commands or instructions, to change in some way,
depending on a decision, a certain algorithm, the
value of a certain variable, an input data, a GPIO
state, a data included in a file, etc.
With the flow control included in our program,
we are going to provide it with a certain
“intelligence” because it alone will make decisions
and execute or not execute certain instructions.
For this control we have several methods, here
we will see the most basic ones but they are
sufficient (maybe they are not the most efficient) to
write basic MicroPython© programs.
1.-IF CONDITIONAL
IF conditionals in MicroPython© consist of up to
3 parts: if, elif, else, with elif and else being
optional. The syntax is as follows:
if condition_1:
run this_1
elif condition_2: # elif are not required
run this_2
… # There can be multiple elif
elif condition_n:
run this_n
else: # Otherwise run end
run this_end
These types of conditionals are well known for
being common in many languages, let's look at the
following examples:
66
Electronics & MicroPython© with ESP32©: 80 complete projects
EXAMPLES:
Request the variable a and make some decision
based on its value.
a=int(input('a=')) # Request a
if a<0: # Is it negative?
print(a,' is negative')
elif a==0: # Is it zero?
print(a,' is zero')
else: # Neither negative
print(a,' is positive') # nor zero
Create the list number_list=[1,2,3], request the
integer variable x and indicate if x is in the list.
number_list=[1,2,3] # Without accents
a=int(input('a=')) # Request integer a
if a in number_list: # Is it in the list?
print(a, ”It's in the list...”)
else: # Not in the list
print(a,'Not in the list...')
EXERCISES:
• Add the following code to p_exercises.py.
In this code we load the library that generates
random numbers, generates one between 1 a n d 3
and we play to guess it.
import random # Import randoms
a=random.randint(1,3) # Random between 1 and 3
while True: # Infinite loop
b=int(input('x=')) # Request variable b
if b==a: # I got it right
print('Right...')
print('New number...')
a=random.randint(1,3)
else: # I failed
print('Failed...')
W h a t if...else: conditionals do in this
example?.
• In the previous exercise, change the random
number limits from (1,3) to (1,10) and add
additional if...elif...elif..else statements to
67
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
give some hint (such as is greater than... or is
less than...) and be able to easily guess the
random number generated by MicroPython©.
• Generates an infinite loop while True: that
displays all the even numbers between 0 and 10
using the x%2 operation to find out if each
number is even or not. Add more instructions to
this example to include those even numbers in
the list even_list[].
2.-WHILE LOOPS
While loops, which we have already used before,
allow us to control the flow of our program by
repeating several instructions as long as the
condition defined in that loop is met. The structure
is as follows:
while [condition] # Start of loop condition
instructions # Instructions to repeat
These loops are very easy to interpret: as long
as the [condition] defined in while is met, that is,
as long as the result of evaluating [condition] is
True, the loop will repeat, therefore if the
[condition] occurs every time, the loop will be
infinite.
It is highly recommended that we review the code
to verify that there is an instruction that alters the
result of [condition] so that it is False and the loop
ends and the rest of the code of our program can
continue executing.
The type of [condition] may vary, for example we
could have the following cases as [condition]:
• A counter, that is, a variable that when
reaching a certain value changes the result of
evaluating [condition], for example:
while a<10: # While a less than 10
a+=1 # Increment a by 1
68
Electronics & MicroPython© with ESP32©: 80 complete projects
• A logical variable, that is, a variable that is
True until it changes to False, for example:
while a: # Repeats while a=True
a = False # Change a to False
It is important that in all loops we check that
the condition stops being met at the appropriate time
to be able to exit the while loop.
If we want to force the exit of a while loop we
can use the break statement. We can use this case, for
example, when we are not sure when the loop is going
to end. Let's look at several examples:
EXAMPLES:
Let's add the following MicroPython© code:
import random # Import library
x = 0 # Start x
y = int(input('end=')) # Request variable y
while True: # Infinite loop
if x==y: # If x=y
print('End') # Display End
break # Abort the loop
else: # Otherwise
x=random.randint(1,200) # Generate another
print('.'), # Display progress
As we see, this loop requests a number and
generates another random number between 1 and 200. If
the requested number y does not match the generated
number x, the loop repeats and since we do not know
when both will match, we use an infinite loop while
True: which we can finish it with a break statement.
Try deleting the break and we will go to... “infinity
and beyond”.
The following example is a complete program that
we are going to call p_guess.py and it is a simple
game where we have to guess a number from 0 to 10. To
do this we have 5 attempts and the program gives the
clues to raise or lower the proposed number.
69
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# -*- coding: utf-8 -
##############################
#MODULE: P_GUESS.PY
# Game to guess a number between 0 and 10
#INPUTS: Random number to guess, attempts
#OUTPUTS: Major or minor clues, remaining attempts
##############################
#LIBRARIES IMPORT BODY
#-----------------------------
import random # Random numbers library
#DATA AND VARIABLES BODY
#-----------------------------
exit = False # Exit the program or not
maximum = 10 # Largest random number
count = 0 # Attempt counter
to_try = 5 # Maximum number of attempts
text ='Guess: ' # Instructions
#FUNCTIONS
#-----------------------------
# GUESS(): Controls the number guessed
# INPUTS: Counter, guesses, number to guess
# OUTPUTS: Track up, track down, success, etc.
#-----------------------------
def guess():
global count # Attempts counter
while count!=to_try: # Repeat all attempts
x=int(input(text)) # Number bet
if x<to_guess: # Hint if low
print('You have to go up...', end=' ')
elif x>to_guess: # Hint if high
print('You have to go down...', end=' ')
else:
print('YOU ARE RIGHT¡¡¡¡')# You're right
count=0 # Attempts counter
break # Loop ends
count+=1 # Increment the counter
if count!=to_try: # Did I exhaust attempts?
print('You are left: '+str(to_try-count)
+' attempts')
else: # Attempts exhausted
print('YOU LOST¡¡¡, you exhausted
the attempts')
#MAIN BODY
#---------------------------
if __name__=='__main__': # The program starts here
print ('\n'*40) # Clear de screen
print ('-'*40) # Display separator
print ('PLAY GUESS A NUMBER...')
print ('-'*40)
70
Electronics & MicroPython© with ESP32©: 80 complete projects
while not exit: # Repeat until exit=True
try: # Instructions try
to_guess=random.randint(0,maximum)# Guess
print('Guess number between 0 and
'+str(maximum)+'...')
guess() # Call to function
j=input('Exit y/n: ') # Exit yes/no?
if j in 'Yy' and j!='': # I've to exit
exit=True # Active exit
print('\nGame over...')
else: # Don't have to leave
exit=False # Disable exist
print('\n'*40) # Clear screen
count=0 # Attempts counter
except: # If there's an error
print('Wrong number')# I indicate
In this example we see several interesting
things, let's try to identify and understand them.
• Complete structure of a MicroPython© program.
• Concatenation of strings and integers passed to
characters with the str(x) function.
• While: loops conditioned by counters, logical
expressions and break.
• Using mathematical functions, for example:
random.randint(x,y) with x and y integers.
• Conditional groupings of the type if elif else
• Error handling with try:...except:.
• End=' ' option in print() statement, to avoid
unwanted line breaks.
EXERCISES:
• In the previous program, it requests by keyboard
the maximum number of the range of integers to
guess.
• If the maximum is large, for example 100, add
some other clue, for example it is an even or
odd number.
• Add a timer at the beginning and end of each
game and show how much time in minutes and
seconds it took to guess the secret number.
• Add a number indicating the record for the
shortest time to get it right. This number will
be updated and displayed in each game.
71
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
3.-FOR LOOPS
For loops in MicroPython© are quite different
from the for statements used in other programming
languages and aren't instructions that are repeated a
predefined number of times.
Basically, for loops allow us to traverse and
even generate, in a simple way, the elements that make
up a list[] or a dictionary{}.
For loops can also be of the type for...else:
similar to while...else:, where the else statement
will be executed when the condition included in the
for statement is no longer met.
EXAMPLES:
Let's go through the following list:
name_list=['me','you','she','we','they']
for x in name_list: # Loop list
print(x) # Display x element
Let's generate a list of the squares of the
integers from 1 to 10 with:
squares = [i**2 for i in range(1,11)] # Define
print(squares) # Display
Notice that the range defined by range(x,y) goes
from the integers x to y-1.
We are going to go through a dictionary showing,
separately, the data and its keys:
dic={1:'me',2:'your',3:'her',4:'our',5:'them'}
for x in dic: # Scan dictionary
if x==3: # The key x==3
pass # It's not displayed
else:
print(x,dic[x]) # See the rest of keys & data
72
Electronics & MicroPython© with ESP32©: 80 complete projects
Note that if the key of dic{} is 3, its data is
not displayed and it is skipped to the next key using
pass. The pass statement does not perform any action,
it is only used to give body to a structure, in this
case the if x==3: statement. See that the output is
not ordered by any criteria.
EXERCISES:
• Create a MicroPython© code to loop through a
keyboard-requested text string containing vowels
and consonants and remove all the vowels. Repeat
the same but only remove the vowels at odd
position in the string, remember that 0 is a
even number.
• Create a dictionary that includes the 12 months
of the year as keys and data and with the
structure months = {month_number : month_name}.
Design a for loop that loops through the entire
dictionary and displays all odd months.
• Create a list[] with the months of the year and
present it in reverse order, that is, starting
with the last data to the first.
• Use a for type statement and control data ranges
such as range(start, end, step) where start, end
ar e t he be gi nn in g a nd en d of th e r an ge
respectively and step is the jump (positive or
negative) of travel in this range.
HINT: use len(list[])-1 as start and -1 as step,
try multiple values for end.
⊝⊝⊝
73
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*User functions
As we program in MicroPython© and our code grows
in number of lines, we realize that there are parts of
it that are repeated.
Instead of repeating that code and having to
rewrite it, it is more useful and elegant, more
reliable, easier to read, maintain, etc. encompass the
code that is repeated in a small program that we will
call a function and to which we will pass various
parameters, so that this function returns some result
of having operated with those parameters and some
internal algorithm.
We will call these functions, in this book, user
functions because we ourselves will assign them a
name, input parameters, requirements, algorithms and
output results.
Likewise, MicroPython© has some predefined
functions that perform a series of operations that we
will see in another chapter.
We have already seen superficially how functions
are created and invoked, but in this section we are
going to go deeper and better, learning to use
parameters in functions and to use global and local
variables outside and inside of functions.
EXAMPLES:
For example, if we have to reverse the order of
a string of data, we will create the function
invert(string) that receives a list[] as a parameter
and returns it to us with its elements in reverse
order.
74
Electronics & MicroPython© with ESP32©: 80 complete projects
Every time we need to invert a string we won't
have to rewrite new code, test it and maintain it, we
will just have to call our invert(string) function and
that's it. Let's see it:
#---------------------------
# INVERT(x): Invert the x[] list
# INPUTS: x[] list to invert
# OUTPUTS: inverse[] x[] list inverted
#---------------------------
def invert(x): # Definition
global inverse # Inverse is global
inverse=[] # Inverse void
for i in range(len(x)-1,-1,-1): # Scan list x[]
inverse.append(x[i]) # Add data to inverse[]
return # End of function
As seen in the structure of the function
invert(x), it is built as if it were a small program,
indicating what it does, its inputs, its outputs and
the necessary comments.
The previous point is not trivial, let's think
that a program can have dozens or even hundreds of
user-defined functions, which are called dozens of
times, it has to be very clear what they do, what they
are for, how they affect global variables and how they
operate with inputs and outputs.
The empty list inverse[] is defined and made
global, that is, the interpreter is told that this
variable (list type) will also be used outside the
function invert(x).
The function is terminated with the return
statement which simply indicates, for readability,
where invert(x) ends. On other occasions we can use
return(p1,...,pn) where p1,...,pn are various locally
defined output parameters of the function that we have
defined.
The function invert(x) that we have created is
invoked by its own name and replacing x or the
variable that we have indicated, with the work
parameter of the function, for example it can be
list_1=[1,2,3,4] or list_2 ='Hello, how are you?'.
75
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Try invert(list_1) and invert(list_2).
Important: the variables that we define within a
user-defined function() only apply outside of this
function() if they are declared global with the global
statement.
Try the following cases:
a)
a=1 # Assign variable a
def function(): # Define the function
a=2 # which assigns a
function() # Call the function
print('a='+str(a)) # Display a
The result is a=1, because although a is modi-
f i e d w i t h i n function(), t h e v a r i a b l e a in this
function is a local variable and not a global one. Now
try with:
b)
a=1 # Assign a
def function(): # Define the function
global a # Make a as global
a=2 # Which assigns a
function() # Call the function
print('a='+str(a)) # Display a
The result is a=2, since the user function
funcion() modifies the variable a, which is now global
and its modifications apply both inside funcion() and
outside of it. It is very, very important to
understand this well.
An easy way to avoid these types of problems
with global variables or local variables is to use
different names for the local variables of each
function and that they will only be used within this
function.
Here we have created a user function that
reverses the order of a list but MicroPython© also has
this function implemented using the command:
inverse = list[::-1]
Which creates the list inverse[] containing the
elements of list[] in reverse order.
76
Electronics & MicroPython© with ESP32©: 80 complete projects
EXERCISES:
• Define the function calculate(x,y,z) t h a t
returns the result of r=ln( x)∗e y +x z . HINT: use
the expression r=math.log(x)*math.e**y+x**z.
• Defines the function max_min(list) that returns
the maximum and minimum of the values included
in list[]. Use for loops for this.
Try using the predefined functions in MicroPy-
thon© called max(list[]) and also min(list[])
and compare the results.
• Defines the function create_list(x,y) t h a t
returns the random[] list containing x random
numbers generated in the interval range(0,y).
Pass the result of create_list(x,y) to the
function max_min(list) and present the maximum
and minimum of the list random[].
• Create the function view() that uses local
variables with the same name as others used in
the main program that calls it. Use the return
statement inside view() to return these local
variables.
Checks that the variable assignments made within
the view() function do not affect the variables
defined, with the same name, outside of view().
Set the affected variables in view() to global
and see how it affects our program.
⊝⊝⊝
77
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Predefined functions
As we already mentioned, MicroPython© has a
series of functions or methods that are already
predefined and that can help us perform simple
operations without us having to create new an untested
additional code.
These predefined functions have the incremental
advantage that they have already been tested, both by
the language developers and by hundreds of users and
we could affirm that they work perfectly, therefore
being 100% reliable.
Below is a list of some of the most useful
functions and methods with a brief description of how
they work, although I recommend going to the Internet
to have detailed information on each of them.
Let's consider the following data:
c = 'c' # A character
k = 'a b' # A string of characters
i = 1 # An integer variable
v = 2.0 # A real variable
x = [1, 2] # A list of two integers
d = {1: 'a', 2: 'b'} # A dictionary
1.-FUNCTIONS WITH LISTS AND STRINGS
Function Description Examples
max(x) Maximum of the list x[] max(x)=2
min(x) Minimum of the list x[] min(x)=1
sum(x) Sum elements of x[] sum(x)=3
list(k) Converts string to list(k)=
list[] ['a','','b']
78
Electronics & MicroPython© with ESP32©: 80 complete projects
Function Description Examples
x.append(e) Add to the list x[] the x.append(3)
element e x=[1,2,3]
x.sort() Sort the list x[] x.sort() x=[1,2]
x.insert(p,b)
Inserts b at position p x.insert(0,c)
of list x[] x=['c',1,2]
len(x) Length of list x[] len(x)=2
'c'.join(x) Join list of characters '-'.join('1','2',
x[] with character 'c' '3')='1-2-3'
Converts string k to k.split()=
k.split()
list without spaces ['a','b']
k.replace Change in the string k k.replace('a','c')
(a,b) the character a by b k='cbc'
k.upper() Change k to uppercase k.upper()='A B C'
k.lower() Change k to lowercase k.lower()='a b c'
list(range C r e a t e l i s t w i t h range(5)=
(n)) integers 0 to n-1 step [0,1,2,3,4]
1
list(range C r e a t e l i s t w i t h Range(4,7)=
(x,y)) integers x to y-1 step [4,5,6]
1
list(range C r e a t e l i s t w i t h Range(4,8,2)=
(x,y,z)) integers x t o y-z step [4,6]
z
2.-FUNCTIONS WITH NUMBERS AND STRINGS
Function Description Examples
hex(i) Change i to hexadecimal hex(i)=0x1
abs(x) Absolute value of x abs(-3)=3
int(c) Character c to integer int('1')=1
Change character c in
int(c,b) int('111',2)=7
base b to integer
float(c) Change character c to float('1')=1.0
real
79
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Function Description Examples
round(v,n) Rounded real v w i t h n round(v,3)=2.0
decimals not zeroes
Change number v to
str(v) str(v)='2.0'
character
ord(c) Change character c to ord(c)=99
Unicode
chr(i) Inverse of ord(c) chr(99)='c'
Return position of k.find('b')=2
k.find(x)
character x in string k -1 if isn't
3.-FUNCTIONS WITH DICTIONARIES
Function Description Examples
d.clear() Delete dictionary d{} d.clear() d={}
Return data of key x in
d.get(x) d.get(1)='a'
dictionary d{}
d.keys() Return the list with d.keys()=[1,2]
the keys of d{}
d.values() Return the list with d.values()=
the values of d{} ['a','b']
Here we have detailed some of the predefined
functions in MicroPython© but to them (which are not
all of them) we should add all those (some we have
already seen) that are included in specific function
libraries such as math for mathematical functions,
time for time variables, machine for hardware, etc.
EXAMPLES:
t='ba12' # Assign string
l=['March','January'] # Assign list
a=list(t) # Convert string to list
print(a) # Display new string
a.sort() # We sort it
print(a) # Display it again
for x in l: # Scan list
print(x, end=' ') # Display elements
a=list(range(5,0,-1)) # Create list
80
Electronics & MicroPython© with ESP32©: 80 complete projects
print(a) # Display it
a.sort() # We sort it
print(a) # Display it again
d={1:'January',2:'March'} # Assign dictionary
print(d.get(2)) # See data with key 2
print(d[1]) # See data with key 1
print(d.keys()) # See all the keys
print(d.values()) # See all the values
EXERCISES:
• Create the list months[] with the names of the
months of the year and x='0987654321ab'. With
both elements, automatically generates a
dictionary that has the elements of x as keys
and the elements of months[] as data.
• Generate an alphabetically ordered list with the
months[] list and add the elements of x also
ordered.
• Create a list with the data from the dictionary
created in the first point.
• Generate a dictionary that has the Unicode
values of x as keys and its characters as data.
• Generate the dictionary friends{} with keys the
names of 5 people and their ages. View this
dictionary, first sorted by name and then sorted
by age. HINT: MicroPython© does not allow us to
sort dictionaries directly but we can pass
friends{} to a list sorted by keys and display
it using key = friends.keys() and key_order =
sorted(key). To sort friends{} by their data, we
can create the ages{} dictionary, exchanging the
key vs. friends{} data and repeating the same
operation as in the first sorting.
• Create the function order(dic, mode) to which we
can pass as parameters: dic{} as the dictionary
to be sorted and mode = 'key' or mode = 'data'
so that the function order(dic, mode) display us
the information with the selected criteria.
⊝⊝⊝
81
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Files
On many occasions we will need to save permanent
information in a file so that it can be recovered,
processed and saved again later.
Let's realize that every time a value is
assigned to a variable in our MicroPython© program,
this information is lost if the program is restarted
or the ESP32© is turned off and on. This programming
is often used to save configuration parameters.
In this section we are going to see how we can
create, save and recover all types of information in a
file using three methods:
1.-USING MICROPYTHON©
In MicroPython© we have the following functions
to manipulate a file:
Function Description
open() Open a file
write() Write data to a file
read() Read data from a file
readline() Read a line from a file
close() Close the open file
EXAMPLES:
place='/folder' # Location
# of the file
f=open(place+'/test.txt','w') # Open the file
# for writing
f.close() # Close the file
82
Electronics & MicroPython© with ESP32©: 80 complete projects
Here we have defined, for convenience, the place
(folder) where the test.txt file will be located and
that we will have created before, in the ESP32©
memory, from the “MicroPython Device” window in
Thonny©.
We created test.txt for writing with the 'w'
option, we could also open it to add data with the 'a'
option. Attention: the 'w' option deletes all data
previously recorded in the test.txt file.
The data that can be written to a file can be
text, numbers, lists, dictionaries or binary data
formatted in text mode, this way it is also easier to
read from outside of MicroPython©. With the ESP32© the
files will be saved in the / folder by default or in
the folder that we have created with Thonny©, for
example: /folder/.
Let's see how to record various types of data in
the test.txt file with the following instructions:
list=['January','February','March'] # A list
number = 150.14 # A real number
text = ”Hi, I'm fine” # A string
f=open('/test.txt','w') # Open test.txt
f.write(str(list)+'\n') # Write list
f.write(str(number)+'\n') # Write number
f.write(text+'\n') # Write text
f.close() # Close the file
Here we have defined several data types and
recorded them in test.txt each on one line using the
line break separator '\n' and converting each data to
text.
If from Thonny© we double click on test.txt, we
can see all its content. Now to add more information
we will do the following:
f=open('/test.txt','a') # Open test.txt
f.write('Other information\n') # Add text
f.close() # Close file
We have reopened the test.txt file but this time
to add information to that saved in the previous
example using the 'a' option (from append).
83
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Now that we know how to create a file with
open() and 'w', save information with write() or add
additional information with the 'a' option, we are
going to use the read() commands to read this
information.
f=open('/test.txt','r') # Open for reading
print(f.read()) # Read test.txt
f.close() # Close file
We have seen how the file is opened with the 'r'
option for reading and how all its contents are
displayed with read(), but if we want to recover the
test.txt information and assign it to a previous known
variable structure, we would do the following:
f=open('/test.txt','r') # Open to read
data=[] # Empty list
while True: # Reading loop
x=f.readline()[:-1] # Read one line
if x=='': # Is it the end?
break # End of reading
else: # Otherwise
data.append(x) # Open to data[]
f.close() # Close file
for x in data: # Scan data[]
print(x) # Display elements
An interesting option, both for writing and
reading files, is to use the with open() option, this
way we do not have to wait to close the file created
so that it is updated since this work is carried out
by the script itself. In our example we would do:
with open('/test.txt', 'a') as my_file: # Append 'a'
my_file.write('More data\n')
my_file.write('End...\n')
with open('/test.txt', 'r') as my_file: # Read 'r'
x=my_file.read()
print(x)
Here we have opened test.txt for reading with
the 'r' option, we create the data[] list, where we
are going to save the parameters stored in test.txt
and we generate a while True: infinite loop that will
end when we finish reading the file.
84
Electronics & MicroPython© with ESP32©: 80 complete projects
We read line by line (deleting the last
character '\n') and add it to the data[] list. Finally
we go through the data[] list and view its content.
Also try with a single print(data[]) type statement.
It is important that we know how to manage the
files created, both from MicroPython© and from the
“MicroPython Device” menu of Thonny© and logically
from the operating system that we are using on the
Windows© or MACOSX© computer.
2.-USING THONNY©
From the Thonny©
menu, specifically from
the “Micropython De-
vice” window, we can
create the folder or
directory where we can
locate our files creat-
ed with MicroPython©,
download it to the com-
puter directory, delete
them, see the proper-
tie s, t he ava ila ble
space, etc. It is im-
portant to use
[refresh] t o e n s u r e
that all information is
correct.
3.-USING THE FILE MANAGER
The ESP32© does not have its own file manager,
therefore, we can open the file using the Thonny©
editor as if it were a script, or download it to the
computer and use the usual Windows© or MAC© file
manager there.
In any case, regardless of the method used, a
good practice is to save all the information in the
same folder where our MicroPython© script is located
and remember to make the corresponding backup copies.
85
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
EXERCISES:
• Create, using the Thonny© menu, the file
/exercises/data.txt, also create the dictionary
months{} that contains the month number as key
and the full name of this month as data.
Record each month name on a line in data.txt,
using:
months = ("January","February","March","April",
"May","June","July","August","September",
"October","November","December").
• After the previous exercise, read the data.txt
file with all the months and create the list
months[] that contains the names of these months
but ordered alphabetically.
HINT: use the predefined function in
MicroPython© months.sort() to alphabetically
sort the list months[].
• Create the domo.txt file with the on/off states,
for example, five light bulbs. Each state con-
tains [name, state].
Save these states in the domo.txt file located
in our usual directory, read and present them in
the console in a formatted and orderly manner.
⊝⊝⊝
86
Electronics & MicroPython© with ESP32©: 80 complete projects
*Error control
Previously we have already seen and used the
try:...except: system to properly control the errors
th a t oc c u r d u r i ng t h e e x e cu t i o n t i m e o f o ur
MicroPython© script, but to manage them well we are
going to need more detail about such errors, how to
control them and now, what we know how to read and
write to files, we are also interested in knowing how
to record all of this in a file, etc.
As the complexity of our MicroPython© programs
grows and especially when accessing external hardware
devices, which can provide a large number of errors,
the use of the error manager becomes essential.
With this control, when an error occurs, the
program will not be paralyzed; on the contrary, its
flow is redirected to a specific module where it is
treated and, if possible, redirected to the point
where the execution of the main program can continue.
The general basic structure of error handling in
MicroPython© is as follows:
try: # Attempt to execute:
block_1 # first block to test
except: # There's an error
block_2 # Error control block
else: # No error
block_3 # Block without error
finally: # Whether on not there's error
block_4 # always executed
EXAMPLES:
To see examples of use of try:...except:, we are
going to write a script that contains an error, for
example the use of a variable that does not exist. See
the following example:
87
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
try: # Try to execute de code
print(x) # x isn't defined
except: # If there's an error
print(”There's an error”) # indicates
else: # If there's not error
print('All right') # is also indicated
finally:
print('End of test') # End of this test
An error is indicated but we do not know what
error it is, to find out add the raise instruction and
try again we will see: NameError: name 'x' is not
defined.
We can create several except: statements to deal
with specific errors, for example, we could add:
try: # Try execution
f=open('/control.txt','r') # Open control.txt
print(x) # Try display x
except NameError: # x does not exist
print('Variable not found')
except OSError: # file doesn't exist
print('File does not exist')
As we can see, when executing this test, the
error OSError: [Errno 2] ENOENT appears, since we have
not created the control.txt file. In this case,
although x is not defined, this error is not presented
because MicroPython© only presents the first error
that occurs at the time of execution of the script.
Other types of errors interesting to control are
the following:
Error Description
ZeroDivisionError Division by zero
TypeError Incompatible data
ImportError Library not found
KeyError Dictionary key not exists
KeyboardInterrupt <CTRL>+<C> interrupt
IndexError Error in index
Exception Unclassified exception
88
Electronics & MicroPython© with ESP32©: 80 complete projects
EXERCISES:
• Add error handling to the p_guess.py script:
KeyboardInterrupt: (try it when data is request-
ed via keyboard), TypeError: to handle input()
statements, and Exception: if an unhandled error
occurs.
• Create the errors.log file to record any errors
that occur. The file must include the date and
time when the error occurred.
• Include in each except[error] b l o c k t h e
error_name variable with a summary of such error
and which must also be recorded in errors.log.
HINT: record error_name in the finally block:
• In the error handling section of the script, add
a variable that contains the line number of each
except block and write the value of that
variable to the file when an error occurs. Look
on the Internet, if it is possible to
automatically capture the running line number
from a predefined MicroPython© function or use
an integer counter to simulate this line number.
⊝⊝⊝
89
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Command summary
The most common MicroPython© commands used in
this book are described below and can be used as a
quick reference.
This list is not intended to be very exhaustive
at all, it is just a list of commands with a very
brief description that can serve as a reminder when
we are writing a very simple program.
For more detailed information,
it is recommended to go to the
official guide:
https://www.micropython.org/
We can also use the
information available on the
Internet for online learning with
courses, tutorials, forums,
explanatory videos, etc.
Command Description
# Comment 1 line
'''…''' Comment several lines
print(text) Display text on the screen
print('%s'%var) Display var in text format
print(f'txt{var}') Display text txt and variable var
var=input() Input text and load in var
= Assign integer, real, logic, etc.
variables
+,-,*,/ Sum, subtract, multiply, divide
90
Electronics & MicroPython© with ESP32©: 80 complete projects
Command Description
** Exponent
% Module, rest
// Integer division
\ Scape character, example:
'\n' line break
Get position x on variable var
var[x]
count from 0
len(x) Length of string x
x.lower() Convert string x to lowercase
x.upper() Convert string x to uppercase
str(x) Convert number x to string
x+y Concatenate strings x and y
Comparison in sentences if
==
Not to be confused with =
!= Not equal in sentences if
-=,+=,*= Operate and assign in the same
function
and, or, not Basic logic operations
a>>b Shift 1 bit to right
a<<b Shift 1 bit to left
a&b Operation and between bytes
a|b Operation or between bytes
0bx Transform x to binary
bin(x) Transform string x to binary
int(x) Transform string x to integer
Transform string x to integer on
int(x,s)
base s
If expression [exp] is True the
if [exp]:
following instruction is executed
elif [exp]: Otherwise this is executed
else: Or the following are executed
91
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Command Description
s[:-j] Extract from s, j right characters
Extract from string s characters
s[i:j]
from i to j-1. Count from 0
s[i:] String s from i to the end
s[:j] String s from the beginning to j-1
Define fn() function with a and b
def fn(a,b):
arguments
return(x) Return local variable x of fn()
fn(a,b,c) Call fn() function with a, b and c
parameters
import lib Import library lib
from lib import m Import module m of library lib
math.sqrt(x) Invoke square root of x
type(x) Return data type of x
list=[x,...,z] Define list[] as data list x,...,z
list.append(e) Add element e to list[]
list.remove(e) Delete element e from list[]
len(list) Number of elements in list[]
Truncate list[] from position a to
list[a:b]
position b-1. Count from 0
list.index(x) Get the position of x in list[]
list.insert(x,s) Insert string s at position x in
list[]
list.sort() Sort the elements o f list[]
modifying it
sum(list) Sum the elements of list[]
for x in list[]: Scan x on list[]
Run following instructions while
while condition:
condition is True
break Go out loop while
Create dictionary dic{} with keys
dic={x:a,...,z:b}
x,...,z and values a,...,b
92
Electronics & MicroPython© with ESP32©: 80 complete projects
Command Description
dic[x] Return key x in dictionary dic{}
Delete key a and its value in
del dic[a]
dictionary dic{}
dic={} Create an empty dictionary
Add key x and value a to
dic[x]=a
dictionary dic{}
dic.items() Return list[key, value] of dic{}
(in a disorderly manner)
Return list[keys] of dic{} (in a
dic.keys()
disorderly manner)
dic.values() Return list[values] of dic{} (in a
disorderly manner)
Display the key of value x in
print(dic[x])
dictionary dic{}
range(x) Create list[] with x elements
Create a range from x variable to
range(x, y) y-1 variable with increment 1 by
default
range(x, y, z) Range from x to y-z with increment
z
random.randint(x,y)
Random number between integers x
and y
round(x, y) Round x to y decimals
Transform string s i n list[]
s.split()
according spaces in s
pass Does nothing fill structure
open([file],'w') Open [file] for writing
open([file],'r') Open [file] for read
open([file],'a') Open [file] for append
[file].read() Read the entire [file]
Read one line in [file] including
[file].readline()
final character '\'
[file].close() Close [file]
93
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Command Description
with open(ar,'a') Open file ar for append with the
as name name name. Automatic closed
global x Declare x as global variable
Define the function fn() with the
def fn(p1,...,pn)
parameters p1,...,pn
Invoke to function fn() passing
fn(p1,...,pn)
the parameters p1,...,pn
End function returning the local
return(v1,...,vn)
variables v1,...,vn
try:...except: Error control structure
finally: Run always in try:...except:
__name__=='__main
__' Program starts here
# -*- coding:
utf-8 - Special characters included
time.sleep(s) Wait s seconds
utime.sleep_ms(ms) Wait ms mili seconds
Since the book is about MicroPython© on ESP32©,
we also need specific commands to control the hardware
of this micro controller (internal temperature data,
identifiers, MAC device address, GPIO control, touch
pins, control signals of the PWM type, I2C, I2S, SPI,
UART, etc. buses) and which we will see in detail in
the following chapters.
⊝⊝⊝
94
Electronics & MicroPython© with ESP32©: 80 complete projects
4.-EXERCISES
O
nce we have seen the basic concepts of the
ESP32© hardware and its associated
components, also seen the Thonny©
environment and finally after having
learned the MicroPython© basic commands and script
creation, we are ready to carry out complete and
complex Electronics exercises, where we can see in
detail how we control the hardware through a
MicroPython© script using an ESP32©.
The fundamental objective of this book is
twofold: on the one hand to satisfy the information
needs of beginners in Electronics and MicroPython©
using an ESP32© as a hardware platform and on the
other hand to be a self-learning, self-training and
study document. In this last sense, the exercises
contain enough explanations so that we can follow step
by step the development of the described activity,
both theoretical and practical.
There are 80 complete resolved projects and
almost 400 proposed of all types: sensors, actuators,
servers, protocols, devices, memories, etc.
In all of them, Electronics and Programming
concepts are introduced with increasing complexity and
describing in more than sufficient detail both the
hardware and the software necessary for the proper
functioning of the topic presented.
⊝⊝⊝
95
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Basic Electricity
Before continuing and how we are going to talk
about voltages, tensions, currents, power, resistance,
capacity, frequency, etc. it is very important to know
what each one is and how to relate them.
There are many similes to explain what electric
current is and how it works, and their understanding
depends on each person's basic knowledge. At the risk
of not being exact and remaining very far from reality
(some Physicist would kill me, I am also a Physicist),
I like to compare the electric current with a current
of liquid water (current of electrons that circulate
through a conductor vs current of molecules of water
flowing through a tube) just to understand the
following basic concepts:
• Voltage or tension: in the simile, it is
equivalent to the necessary unevenness at the
ends of the tube for the water to circulate. For
there to be an electric current, there must be
an electric potential difference between the
ends of the conductor. We will call this
potential difference Electrical V o l t a g e or
Electrical Tension with the letter V and we will
measure it in Volts (symbol V). We will also use
the milli volt 1V=1,000mV.
• Current: equivalent to the flow of water that
circulates through the tube. Electric current is
the “volume” of electrons that circulate through
a conductor. We will call it Intensity, with the
letter I and it is measured in Amperes (symbol
A). W e w i ll a l s o us e t h e m il l i a m p e re :
1A=1,000mA.
• Resistance: each type of tube has specific
characteristics: it may have more or less
96
Electronics & MicroPython© with ESP32©: 80 complete projects
section, it may have more or less internal
roughness, elbows, joints, etc. and therefore
allow greater or lesser flow to pass through its
section.
This characteristic in an electrical conductor
is called Resistance, with the letter R and is
measured in Ohms ( Ω symbol).
We will also use the kilo ohm: 1k Ω=1,000Ω and
the mega ohm: 1M Ω=1,000k Ω=1e6 Ω .
The value of a commercial resistance is marked
with several colored rings arranged in an
internationally established code, these are read
from left to right: 2 or 3 digits, a multiplier
factor and the tolerance (% error in
measurement). We can find conversion tables and
mobile APP, similar to the following, on
specialized Internet websites.
• Power: is equivalent to the product of the
difference in level of the tube (voltage or
tension) and the flow of water that circulates
through that tube (current).
97
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
A current of water has more or less power
depending on the height from which it falls
(voltage or tension) and the amount of water it
carries (current).
We will assign it the letter W and it is
measured in Watts (symbol w). We will also use
the milliwatt: 1w=1,000mw. Do not confuse this
concept with kilo watt hours, which measures the
energy consumed over time.
• Capacity: is the amount of water stored in a
container. Electrical capacity would be the
amount of electrons stored in a device, for
example a capacitor, we will assign the letter C
and it is measured in Farads (symbol F). The
Farad is a very large unit so we will use the
μF or micro Farad: 1F=1,000,000 μ F and also
1pF=0.001 μ F.
• Frequency: if we change the slope of the pipe,
the water current can circulate in one direction
or the other. Likewise, if we invert the voltage
at the ends of the conductor, the current will
change direction. The frequency is the number of
times this direction changes per second.
We will assign the letter f to the electrical
frequency and it is measured in Hertz (symbol
Hz) . W e w i l l a l s o u s e t h e k i l o H e r t z :
1kHz=1,000Hz and the mega Hertz:
1MHz=1,000kHz=1e6Hz.
• Others: other concepts will emerge that, in
order not to tire the reader, we will explain
throughout the book.
• Basic formulas: with the previous concepts, some
basic formulas are deduced that are very
important to know, as they will allow us to make
some simple calculations to adjust the value of
the components so that everything works well and
we do not have problems damaging important
components such as the ESP32©.
98
Electronics & MicroPython© with ESP32©: 80 complete projects
In Electricity and Electronics there are many
formulas but here we are going to use only two:
V
I= called Ohm's Law, it is one of the most
R
important laws of Electricity and the most used
in this book and what it indicates is that the
current I that circulates through a conductor is
proportional to the potential difference V
(voltage or tension) existing between its ends
and inversely proportional to the resistance R
that the conductor offers to the passage of this
current. Logically we can deduce both that: and
V =I∗R so that: R=V / I.
P=V*I is the definition of power, that is, the
product of the voltage V and the current I.
Combining this formula with Ohm's Law we have
2
P=I ∗R, I leave it to the reader to deduce it
from the previous two, it is very easy.
Suggested exercises (they are very easy):
1. Connect a resistor R in series with an LED to
limit the intensity that circulates through it
to 20mA. There is +5V voltage between both
components. What minimum value should resistance
R have?. If the LED requires a minimum current
of 5mA to light, what maximum resistance could
we put in series with this LED?.
2. Look on the Internet for the colors of the
commercial resistance R or the one closest to
its value.
3. In the previous exercise, what power does
resistance R consume?.
4. If we put two resistors in series: R1 1k Ω
and R2 10k Ω and at the ends of the set we
apply +5V, what current will flow through R1?,
and through R2?, what voltage will there be at
the ends of R1?, and about R2?. And with +3.3V?.
⊝⊝⊝
99
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*ESP32© GPIO usage
Before facing some basic Electronics and Home
Automation exercises with ESP32© we have to know what
basic MicroPython© instructions we need to manage the
GPIO, that is, to manage this general purpose input
and output port that our micro controller has. Below
are some of the basic commands to control both the
inputs and outputs, that is, the GPIO, touch pins, as
well as the internal data of the ESP32©, the PWM
(P u ls e Wi d th M od u la t io n ) s i gn a l s, I 2C ( In t er
-Integrated Circuit), I2S (Inter-IC Sound), SPI
(Serial Peripheral Interface), etc.
For more detailed, more advanced information,
diagrams, configuration, tips, multiple exercises,
etc., I recommend my books: “Home Automation with
Raspberry©, Google© & Python©” or “Electrónica y
Domótica con Raspberry©” (Spanish version) already
mentioned above, and which are available on Amazon©.
First of all, in our script we will have to
import some of the libraries that manage the GPIO, the
PWM, the DAC, the I2C, etc. We will see it in detail
in each of the 80 exercises. For example:
from machine import Pin, PWM, DAC, I2C
Command Description
x=Pin(n, Pin.IN/OUT) Star GPIOn as input IN, or output
OUT
Activate the GPIO pre-defined x to
x.on()/off()
on or off
Activate the GPIO pre-defined x to
x.value(0/1)
LOW or HIGH
x=Pin(n, Pin.IN, Pin. St ar GPIOn as input with pull_up
PULL_UP/PULL_DOWN) or pull_down
100
Electronics & MicroPython© with ESP32©: 80 complete projects
Command Description
x.irq(trigger=Pin GPIOn o n x with input IRQ by
.IRQ_FALLING/RISI
NG, handler=f) falling or rising invoking to f()
esp32.raw
_temperature() Gets the temperature of ESP32©
machine.unique_id() Gets the MAC of ESP32©
machine.WDT(
timeout=T) Activate Watchdog at T useconds
wdt.feed() Feed the Watchdog
w=network.WLAN(
network.STA_IF) Configure WiFi connection
sta_if.ifconfig( Configure static IP with mask m,
ip,m,p,dns) port p and DNS dns
w=wf.scan() Scan Wifi network
adc=ADC(Pin(n)) Activate ADC on GPIOn as adc
x=adc.read() Read ADC to variable x
dac=DAC(Pin(n)) Activate DAC in GPIOn as dac
dac.write(x) Write to DAC the value of x
x=PWM(Pin(n),freq A c t i v a t e P W M o n GPIOn w i t h
=f,duty=d) frequency f and duty cycle d
x.deinit() Stop signal PWM in x
ntptime.settime() Update RTC from Internet
data=rtc.datetime() Update data from RTC
t.init(period=T, Activate hardware timer t each T
mode=Timer.PERIOD mili seconds, periodically
IC, callback=f) invoking to function f()
t.deinit() Stop hardware timer t
p=TouchPad(Pin(n)) Activate GPIOn as touch
p.read() Read touch GPIO assigned to p
i2c=I2C(sda,scl,f)
Star I2C bus in pins sda, scl and
frequency f (optional)
i2c.writeto(d,f) Write on I2C data with format
i2c.scan() Scan devices on I2C bus
101
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Command Description
Activate I2S bus with pins p, mode
a=I2S(p,m,b,f,v,b) m, bits b, format f, speed v and
buffer b
a.write(d) Write data on I2S bus
SIMPLE EXERCISES:
• Create a script with an infinite loop that turns
GPIO26 on HIGH for 1 second and turns it off
again at LOW for 1 second.
HINT: use the time.sleep(time) system function.
• Add to the previous exercise a parameter entered
by keyboard to change the activation and
deactivation time. This input, controlled
through a user function, must be requested every
20 HIGH vs LOW cycles.
• Since we are working with the hardware of our
ESP32© and it may not be available, or we want
to stop an infinite loop, add the corresponding
error control of type try:... except: to
specifically control hardware errors such as
OSError and keyboard execution interrupt with
KeyboardInterrupt.
In both cases of interruptions, define separate
control functions with the corresponding error
messages via console.
⊝⊝⊝
102
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 01:
Turn on/off an LED
This is the simplest exercise, turning an LED on
and off from the ESP32©, it seems simple and it is,
but it is very important to handle this case well as
it is the basis for many others.
We only turn an LED on and off, but
this action could be extended to anything
else that needs to be turned on or off: a
light bulb, a blind, an appliance, the
garage door, the heater, anything, we will
only have to change the hardware that it
acts as an interface between the ESP32© and
whatever we want to turn off or on.
In this first exercise,
a lot of important informa-
tion is detailed: the
concep ts, th e nece ssary
calculations, the formulas,
laws, etc. and the same in
software: libraries, func-
tions, commands, operation,
etc.
As the book progresses
we will rely on these
concepts from the first
exercises.
We have to take into
account several issues to
avoid problems with these
exercises:
103
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
1. The maximum current, i, that a GPIO pin of the
ESP32© can switch, for safety, is 40mA and an LED only
needs about 15mA, therefore to limit this current we
will use a resistor calculated following Ohm's Law:
V 3,3 Volts
R= = =220 ohms=220 Ω
i 0,015 Amperes
this is:
3,3 v
R= =220 Ω and commercially we have
0,015 A
220 Ω resistors available
that, using the color code,
are resistors with colored
lines:
red-red-brown.
If we want to know what power or size the resistor
should be, we can do it with:
2
2 15
P=i ∗R=( ) ∗220=0,05 watts
1000
Therefore a usual resistance of 1/4w is more than
enough.
2. We can previously simulate the circuit with a
digital and analog circuit simulator, for example with
the iCircuit© software
This case is very
simple but it serves as
an example of using
iCir cuit ©, w hic h is
very useful for testing
complex analog and
digital circuits before
using them physically
and therefore avoiding
errors, avoiding
damaging any component,
adjusting values, redoing the design of the initial
physical circuit, etc.
104
Electronics & MicroPython© with ESP32©: 80 complete projects
iCircuit© is very simple to use: we open the
application, we add components that are already pre-
configured: resistor, LED and the ESP32© we simulate
it with a generator of a square wave or a pulse, we
a d j u s t v a l u e s of the components, we join the
components with virtual cables, we add the lands, etc.
and.... ready.
3. We can use two hardware solutions:
• Discrete components: that is, we can create the
circuits with separate individual components, in
this case 1 red LED, 1 green LED, two 220 Ω
resistors and the corresponding connections
between all of them.
This solution is usually cheap and more
educational since it involves the process of
calculating, purchasing and assembling all the
components, one by one.
• Already pre-configured modules: that is, small
printed circuit boards that include the LED, the
resistors (already calculated) and the
connectors necessary to make the connection with
the micro controller (Arduino©, Raspberry©,
ESP32©, etc.). This solution is faster and more
convenient to implement and is the one we use
and recommend in this book and that we can find
o n w e b s i t e s s u c h a s www.amazon.com or
www.aliexpress.com with manufacturers such as
Sunfounder©, AZDelivery©, Elegoo©, etc.
In any case, remember that we cannot connect any
ESP32© GPIO to more than +3.3V because otherwise we
run the risk of “burning” some ESP32© circuit in a
totally irreversible way.
As we already mentioned, in this exercise we
will use a module that already includes all the
elements: a dual LED with two internal LED, one red
and one green, protected by two resistors, a connector
and all of this soldered and screen printed on a
printed circuit board of reduced dimensions.
105
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Additionally, the kit that
includes this board has various
connectors that adapt perfectly,
on the one hand to the board with
the LED and on the other hand, it
has some pins that we can click on
a breadboard together with the
ESP32©.
The LED has a certain polarity
that must be respected and is
indicated by a flat edge or
chamfer on their lower
circumference, which indicates the
– terminal or cathode, or by a
longer pin or + terminal or anode.
If we use voltages no higher
than +3.3V, we can check the
polarity of the LED by simply
exchanging the terminals and see
which one it lights.
4. We connect the LED to the
ESP32© as follows:
106
Electronics & MicroPython© with ESP32©: 80 complete projects
5. As we see in the previous figures, in simple
circuits with as little consumption as this one,
the components can be connected directly to the
ESP32©, but it is much more comfortable, clear
and, above all, safer to avoid damage to the
ESP32©, to use a breadboard similar to the
following:
NOTE: We can also plug the ESP32© into a test
board or breadboard but since the ESP32© has a
very wide circuit, we need to attach two
breadboards together and if possible, remove the
power line from one of them.
6. Instead of individual LED we can also use the
dual LED module that we have already mentioned
and which already includes the 2 LED, the
resistors and the connectors. The connections
would be like:
107
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In future exercises we will use modules
interchangeably (when available), individual
components with breadboard, with the intention of
visualizing and practicing with both systems.
7. Additionally, we can use a circuit simulator
such as the already mentioned iCircuit©, but if
we are going to work with a micro controller
with MicroPython©, it is more useful to have a
circuit editor and simulator that includes the
ESP32© and in which we can choose between
multiple sensors and actuators. There are
several on the market, in this book we will use
Wokwi© which is free, with several languages,
with ESP32© programming, both with Arduino© and
MicroPython©, easy to use, allows us to save and
share our projects, etc. See annex if necessary.
8. How does it work?:
The operation of this exercise is very simple:
when the ESP32© puts the physical pin GPIO26 (green
LED) or GPIO27 (red LED) to OFF (LOW level to GND) the
LED turns off and on the contrary, when we put it on
ON (HIGH level at +3.3V) the LED lights up. As we have
already seen, whether we use individual components or
a module, we must include the corresponding resistors
that limit the current that circulates through both
GPIO and the LED so that none of them burn out.
108
Electronics & MicroPython© with ESP32©: 80 complete projects
8. Finally we are going to use
a M i c r o P y t h o n © script,
which runs on our ESP32©,
as a programming language
for all our projects and
which in this case
implements an algorithm
that flashes both LED in
various sequences that we
can easy configure by
software.
Once installed, we can make multiple algorithms
by changing only the software and without having to
modify any of the hardware, this is the great
advantage of managing hardware with software.
This and the following exercises describe an
example MicroPython© program that is quite simple and
includes sufficient explanations, however in this book
we have also incorporated a complete section that
summarizes the most common MicroPython© commands.
Now we write or load the MicroPython© code to
manage this circuit, to do this we enter Thonny©,
wr i t e t h e fo l l o wi n g pr o g ra m , s av e i t w i t h a
descriptive name and execute it with the button .
The most used scripts and libraries in
MicroPython© from this book are accessible from my
blog at the address:
https://gregochenlo.blogspot.com/
109
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The script basically turns on and off 2 LED (one
green and one red) in an infinite loop with a
configurable activation time. The time library are
used to control the power-on and the power-off time
and machine one to manage the GPIO of the ESP32©.
GPIO26 is used for the green LED and GPIO27 for
the red LED but any other ESP32© GPIO that has the
option to be configured as an output could have been
used without any problem.
An error or exception control of the
try:...except: type has also been included, is very
simple and allows us to exit the infinite loop from
the keyboard when press <CTR>+<C>, in the latter case
this situation is indicated via console and both LED
turn off in an orderly manner.
As a good practice, when writing the scripts in
this book, the header has always included a brief
description of what the script does, as well as a
summary of the inputs and outputs used.
When the circuit is complex, a small summary of
the connections is also included that allows us to
easily identify how to perform the exercise.
############################
# E001_2LED.PY: Activate 2 LED
# INPUTS: Activation time
# OUTPUTS: GPIO26 Green and GPIO27 Red
############################
import time # Time manager
from machine import Pin # GPIO manager
led1=Pin(26, Pin.OUT) # Green LED
led2=Pin(27, Pin.OUT) # Red LED
led1.off() # Green off
led2.off() # Red off
t=1 # Activation time
try: # Errors control
while True:
print('Green')
110
Electronics & MicroPython© with ESP32©: 80 complete projects
led1.on() # Green on
time.sleep(t) # Wait a while
led1.off() # Green off
print('Red')
led2.on() # Red on
time.sleep(t) # Wait a while
led2.off() # Red off
except KeyboardInterrupt:
print('Program completed')
led1.off()
led2.off()
A variant of the
previous exercise is to use
the LED module described that
contains a Dual LED of two
colors and that is an LED that
includes the two LED inside
and that can be used
independently in on/off mode
or achieve a mixture of both
colors using a PWM signal in
each of them (we will see this
in other exercises).
The two LED share the same package, available in
both 3mm and 5mm and can be configured with common
cathode (connected to GND) or common anode (connected
to +3.3V). This configuration involves the use of
forward or reverse logic respectively as we will see
later in other exercises.
This configuration is widely used in electronic
equipment: televisions, recorders, cameras,
appliances, etc. to know the on/off status of the
equipment.
⊝⊝⊝
111
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 02:
SOS signal with LED
In this exercise we are going to go a little
deeper into the use of LED control, the use of
MicroPython© and demonstrate that with the same
hardware, without changing anything, we can do
multiple projects just by making some small
modifications to the software.
In this exercise we will make an infinite loop
that will read the lists, of the on/off type and that
will control the green LED and the red one to
alternately compose the sequence of the international
SOS distress signal.
The script imports the machine a n d time li-
braries to control the GPIO and time respectively. The
LED pins are configured on GPIO17 and GPIO18 (we have
changed pins to become familiar with the GPIO of the
ESP32©). The flashing times are defined for the letter
S and for the letter O. The letters{} dictionary is
defined with the parameters of each action: [name]:
{LED, color, time, repeat} a s dictionaries. The func-
t i o n blink_letter() is created that executes the
parameters of the letters{} dictionary component and
the infinite loop that runs through a list with the
sequence of actions to be performed.
############################
# E002_SOS_LED.PY: Blink 2 LED with SOS
# INPUTS: SOS sequence
# OUTPUTS: GPIO18 green and GPIO17 red LED
############################
import machine
import time
# Configure the pins for the green and red LED
112
Electronics & MicroPython© with ESP32©: 80 complete projects
pin_green = machine.Pin(18, machine.Pin.OUT)
pin_red = machine.Pin(17, machine.Pin.OUT)
# Flashing times
tpS = 0.5 # Flashing time letter S
tpO = 0.2 # Flashing time letter O
# Letter settings
letters= {"Sr": {"led": pin_red, "color": "Red",
"time": tpS, "repeat": 3},
"Or": {"led": pin_red, "color": "Red",
"time": tpO, "repeat": 3},
"Sv": {"led": pin_green, "color": "Green",
"time": tpS, "repeat": 3},
"Ov": {"led": pin_green, "color": "Green",
"time": tpO, "repeat": 3},}
# Function to flash a letter
def blink_letter(letter):
config = letters[letter]
for _ in range(config["repeat"]):
config["led"].on()
print(f"{config['color']} LED on")
time.sleep(config["time"])
config["led"].off()
print(f"{config['color']} LED off")
time.sleep(config["time"])
# Infinite loop for SOS sequence
while True:
for letter in ["Sr", "Or", "Sr", "Sv", "Ov","Sv"]:
blink_letter(letter)
time.sleep(tpS * 2) # Pause of tpS*2 seconds
#between letters or sequences
An interesting variant to the LED that we have
already seen, which we could call “normal” LED, is to
use an “auto-flash” type LED, for
example the KY-034©, which includes
inside an integrated circuit with
an oscillator that it changes the
color of the LED.
This device can be connected
directly to the power supply (+3.3V
or +5V) for example to display when
a device is on or we can also
connect it to a GPIO of the ESP32©
to display that a certain program
is running properly.
113
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Later we will see another type of LED, called
RGB LED, where we can change the luminosity of its
components independently.
Finally we can see the connections of the LED
with the ESP32© in GPIO18 (green) and GPIO17 (red).
As we can see, we can use various ESP32© GPIO to
perform the same functions, in this case the control
of the red and green LED.
⊝⊝⊝
114
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
Now it's time to work a little to reinforce the
knowledge acquired and to do this it is best to do
some exercise related to what we have already seen. In
all sections we will incorporate several simple
exercise statements with incremental complexity, to
practice and learn.
•E001: LED with timer.
Create a simple program in MicroPython© for
ESP32© that toggles the lighting of two LED, one red
and one green, connected to two different GPIO pins
using a predefined timer to switch between the LED at
regular intervals.
•E002: Synchronization with background music.
Develop a program that synchronizes the turning
on and off of the red and green LED with the
background music. Use a microphone, real or software
simulated, to detect changes in sound and adjusts the
LED lighting accordingly. We can rely on the use of an
analog digital converter or ADC that we will see in
future exercises.
•E003: Random flashing.
Implement a program that randomly flashes the
red and green LED. Alternate the state of the LED at
unpredictable times, creating a dynamic visual effect
without predefined patterns using the random library.
•E004: LED sequence.
Use the SOS exercise to define the dictionary
ESP32{} that includes the definition, in Morse code,
of the letters E, S, P and the numbers 3 and 2 and in
the infinite loop a traversal of the list ['Er',
'Sr','Pr','3r','2r,'Er','Sv','Pv','3v','2v], that is,
ESP32 letters in red and green.
⊝⊝⊝
115
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 03:
DC10EGWA© Level Indicator
In this exercise we will see
how to use a module that incorporates
a set or array of 10 individual LED
(selectable red, yellow, green). For
example, the DC10EGWA© from
KingBright© or similar, allows us to
create a light level to indicate
various situations: temperature,
humidity, sound, voltage, time, etc.
We are going to use this array
of LED to present the state of an
internal variable of a script, for
example a counter. In future
exercises we will see various sensors and this array
could present the status of their levels. We will use
the 10 LED connected to 10 GPIO, in other exercises we
will see that a single GPIO can be used to perform a
similar exercise with an array of special LED.
Since the array has 10 LED of 3 colors, we will
use, for example, 5 in green for high values, 3 yellow
for medium levels and 2 in red for low levels.
We use the array with a common cathode (we join
all the cathodes to GND) and since we do not have all
the LED always on, we can insert a 160 Ω resistor
between the cathode and GND. We will connect the
common cathode (negative) to GND and each anode
(positive) to the GPIO. This solution is not perfect
since the luminosity of the LED will depend on the
number of LED on. If we want all the LED to look good,
we will have to insert a 220 Ω resistor between each
LED and its corresponding GPIO.
116
Electronics & MicroPython© with ESP32©: 80 complete projects
In the script we import the machine, time and
random libraries for GPIO control, time management and
the creation of random numbers respectively. We define
the pins[] list that includes the GPIO of each LED in
the array and order them from the lowest to the
highest (change this option if necessary). We start
the list with a loop as OUT and in another loop we
turn them off.
We define the function level_on() that turns on
all LED that are below a level and turns off all those
that are above that level. We define two simulation
functions: linear(), which lights up the LED from
bottom to top, simulating an increasing level, and
audio(), which generates a random level, simulating
the VU meter (level audio meter) of an audio signal.
The corresponding error control and stopping by
pressing <CTR>+<C> on the keyboard is included in the
main loop of the script.
117
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E003_ARRAY_LED.PY: Activate a 10 LED array
# INPUTS: GPIO and activation time
# OUTPUTS: GPIO levels
############################
import machine # GPIO control
import time # Time activation management
import random # Creation of random numbers
# Define GPIO pins connected to the LED in order
# 15,2 red, 0,4,16 yellow and rest green (hardware)
pins = [15, 2, 0, 4, 16, 17, 5, 18, 19, 21] # List
LED_time = .1 # Activation time per each LED
# Configure, in a loop, the GPIO pins as outputs
leds = [machine.Pin(pin, machine.Pin.OUT)
for pin in pins]
# Off all LED
for led in leds:
led.off()
# Turns the LED on up to a given level
def level_on(level):
for i, _ in enumerate(pins): # Scan tuple pins[]
if i < level: # It's below, put it on
leds[i].on()
else: # It's above it, put it off
leds[i].off()
# Simulates the linear growth of a level
def linear():
for level in range(10): # LED array number
level_on(level+1)
time.sleep(LED_time) # Adjust waiting time
# Simulates an audio level
def audio():
level = random.randint(0, 10) # Random level
level_on(level)
time.sleep(LED_time) # Wait a while
# Call function to simulate level growth
print('10 LED ARRAY LEVEL SIMULATION')
try:
linear()
while True: # Infinite loop
audio()
except KeyboardInterrupt:
print('Finished...')
for led in leds: # LED off
led.off()
⊝⊝⊝
118
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E005: On and off sequence.
Write a MicroPython© program for the ESP32© that
makes the LED in an array, 10 LED for example, turn on
one by one in sequence, starting from the first LED
and moving towards the last, and then turn off at the
same order as previous one.
•E006: Random flashing.
Create a program in MicroPython© that makes the
array LED flash randomly. In each iteration, it
randomly chooses one or more LED to turn on for a
short period of time and then turn off.
The chosen LED will be defined in the dictionary
led{led1:t1,...,ledn:tn} w h e r e e a c h L E D a n d i t s
activation time are specified.
•E007: LED and internal variable.
Create a script that requests a number between 0
and 10 through the console, check that the number is
correct and, if so, turn on the corresponding LED
number sequentially.
The LED will flash 3 times and move towards one
end until it disappears completely. The program will
repeat in a loop until interrupted by the keyboard.
Add the necessary information messages and error
checks.
⊝⊝⊝
119
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 04:
JQC3F© On/Off Relay
An exercise similar to the previous one in
philosophy, but very different in application, is to
be able to switch from
the ESP32© and with a
relay of a certain power,
the power supply of a
device with medium
consumption, for example
250V and 10A of
alternating current, that
is about 2,500w.
With this simple solution we can turn on/off a
small stove, lighting, heating, air conditioning
machine, Christmas Tree lights, a motor, etc.
To do this, we will use
a relay, for example the
BESTEP JQC3F©, which consists
of a device that, using a
small current (+3.3V control
circuit), can interrupt a
current of high power or
intensity (10A and 2,500w
power circuit).
All relays have 2 input
pins, where the signal is
applied to the low intensity
and/or low voltage control circuit (in our case the
one generated by the GPIO26 of the ESP32©) and 3
output pins: common C, normally open NO and normally
closed NC where the signal of the power and/or high
intensity and/or voltage circuit is switched.
120
Electronics & MicroPython© with ESP32©: 80 complete projects
With the relay we
use we must take into
account the voltage
of the low intensity
circuit (in our case
+3.3V) and the
maximum power
supported by the
power circuit (in our
case 250V*10A=2,500w)
Relays are usually
accompanied by a
simple electronic
control circuit that
performs several
operations: isolation
with optocouplers,
polarity control and
amplification of the
low intensity signal,
status display LED,
connectors, etc.
All of these char-
acteristics depend on
the brand, model of
the relay and its associated circuit, so we will re-
view the manufacturer's schematic and take into
account its specific parameters.
In our case, the GPIO26 activates the input of
the module, this is the transistor Q1 and this is the
relay, so the relay contacts go from C+NC to C+NO,
closing the power circuit.
Resistors R0 and R2 limit the current in the LED
and resistors R1 and R3 adjust the current through
them and the voltage needed at the base of this
transistor Q1.
LED D0 and D1 indicate the normal operation of
the module and diode D protects it from currents
induced by the relay coil when it switches with high
current or voltage.
121
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
When activating and deactivating the relay we
will hear the sound produced by its moving parts when
contacting terminal C with NC or NO, but to better
test the circuit we can connect some LED to its output
simulating the load (as always, add the corresponding
resistors) as seen in the following figure:
The MicroPython© script imports the machine
library for managing the GPIO and time one for
controlling the closing and opening times of the relay
contacts. Specifies the variables where the GPIO26 and
the control time are specified.
The script also defines the setup() function
where it specifies that GPIO26 is an output of the
ESP32© and starts it off. Defines the loop() function
that contains the closing and opening algorithm of the
relay in a loop and with the corresponding console
messages. Specify the stop() function to close the
script and leave the relay open.
Finally, in the main body of the script, which
as we know, is identified with __name__ == '__main__',
the three previous functions are called in the order
necessary for the script to achieve the desired
effect.
122
Electronics & MicroPython© with ESP32©: 80 complete projects
############################
# E004_RELAY.PY: Relay management (direct logic)
# INPUTS: Activation time
# OUTPUTS: Relay activation at GPIO26
############################
from machine import Pin # GPIO manager
import time # Time management
pin = 26 # GPIO26 of ESP32©
tmp = 1 # Relay closing and opening time
# Start GPIO26 for relay open
def setup():
global relay_pin
# Configures pin as output
relay_pin = Pin(pin, Pin.OUT)
relay_pin.off() # Relay start off
# Relay closing and opening loop
def loop():
print('Relay ON...')
relay_pin.on() # Activate relay, relay closed
time.sleep(tmp) # and keeps it tmp seconds
print('Relay OFF...')
relay_pin.off() # Deactivate relay, relay opened
time.sleep(tmp) # and keeps it tmp seconds
# Stopping the script that leaves the relay open
def stop():
relay_pin.off() # Deactivate the relay
# Main body of the script
if __name__ == '__main__':
print('Relay management')
setup() # Pin GPIO start
try:
while True:
loop()
except KeyboardInterrupt:
# <CTRL>+<C> function for stop the script
stop()
An example of a wired circuit that uses the
previous script would be something similar to the
following and we can simulate its operation with
Wokwi©.
With the previous script we can activate and
deactivate the relay and see the “real” state with two
LED (red and green) connected, with the corresponding
resistors at +3.3V, NC, C and NO.
123
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Important: Each relay contains a different
control circuit, it is possible that the relay is
activated in HIGH (direct logic) or in LOW (inverse
logic), in each case we must adjust the GPIO
management of the script as appropriate but when using
a script MicroPython©, the changes are very simple.
Finally, we must choose the relay carefully
based on the voltage of each country and the
consumption of the primary circuit (connection to the
GPIO) and the secondary circuit (device connected to
the relay).
⊝⊝⊝
124
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E008: Push-button activated relay.
Add a button connected to a GPIO so that
pressing it activates or deactivates the relay. This
activation or deactivation of the relay will be
carried out for a time specified from the console. We
will see push buttons management later.
•E009: Conditional relay activation.
Add an additional condition to the previous
script, for example a True or False variable entered
by keyboard, or when a variable in the loop exceeds a
certain amount, for example 100 cycles of x seconds,
or that the internal temperature of the ESP32© exceeds
a certain level. We will see the internal temperature
data of the ESP32© in other exercises.
•E010: Relay and other LED.
Add an LED to another ESP32© GPIO to turn on or
off to indicate relay status. All information must
appear on the screen or console and with detailed
error management.
•E011: Relay and vibration sensor.
Include a vibration sensor (we will also see how
it works in other exercises) so that the relay closes
or opens its secondary circuit when a vibration of a
certain level is detected.
•E012: Status of a relay.
Connect, with the appropriate resistors to avoid
short circuits, the output of the relay to +3.3V
and/or to GND and to a GPIO configured as an input of
the ESP32© so that the “real” state of the contacts of
the relay can be known at all times.
⊝⊝⊝
125
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 05:
KSK-1A66© Reed Switch
In this exercise we will
see how a reed switch works,
such as the KSK-1A66©, which
is basically a magnetic
field sensor that
opens/closes a contact when
that field is detected. It
consists of simple
electrical contacts
(ferromagnetic) inside an
airtight seal filled with an
inert gas.
When this element is in-
tegrated into a package that
also includes a coil that
generates a magnetic field,
the assembly constitutes a
reed relay where the reed
switch acts as a connection
element for the output con-
t a c t s . It s f u n d a m e n t a l
characteristics could be
summarized as follows:
• Contactless operation: There is no physical
contact between the reed blades and the element
that produces the magnetic field; when the
magnetic field is brought closer, the blades
close the circuit.
• Long Life: Due to their simple design and lack
of moving parts, reed switches are known for
their high reliability and long life.
126
Electronics & MicroPython© with ESP32©: 80 complete projects
• Fast response and low hysteresis: They have a
fast response to changes in the magnetic field
and have low hysteresis, that is, the switching
point is the same both on activation and
deactivation.
• Environmental resistance: Its encapsulation is
hermetic, a glass or ceramic tube, which
provides protection against humidity, dust, etc.
making them suitable for applications in harsh
environments.
• Versatility: They are used in a wide variety of
applications: security, door and window opening
alarm, water and electricity meters, proximity
switches in industrial control systems, position
detectors, voltage changers, etc.
• Simplicity: They are very simple, small in size,
easy to obtain, very cheap, easy to install,
easy to maintain, etc.
Like other sensors, this
element requires a simple
control circuit.
When the r eed s witch
conducts, it sets GPIO25
to LOW (reverse logic) and
makes D1 light. DO lights
up when powering the
circuit and as always R0
and R1 limit the maximum
current that circulates
through the LED and that
also enters the GPIO25.
In the following
Mic roP yt hon © s cr ipt we
will do that when we bring
a magnetic element close to the reed switch, it drives
and activates the GPIO25 input to LOW and this will be
the signal that activates a Dual LED, connected to
GPIO26 and GPIO27, turning it from green to red.
127
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
When we move the magnetic element away from the
reed, it is disconnected by turning GPIO25 to HIGH and
therefore the Dual LED will turn green again. We use:
• Polling control: The main body of the script
checks the status of the reed in each cycle and
acts accordingly.
• Interrupt control: The main body of the script
is executed normally WITHOUT checking the state
of the reed sensor periodically. The reed
control is done by a special function (interrupt
or IRQ) that is only executed when the ESP32©
detects changes in the reed sensor.
Two scripts are attached with both options and
the polling option is described. In later exercises we
will see in detail how the IRQ option is used.
In the script with POLLING control, we import
the machine and time libraries to manage the GPIO and
the times respectively. The reed and LED management
pins and the pins() tuple are defined for better
control.
T h e setup() function is defined in which the
GPIO of the LED are started as output and turned off.
The LED() function is responsible for changing their
state depending on the state of the reed. This
function also presents the corresponding messages.
The look() function checks the reed status in a
loop and applies timing to avoid sensor bounces. The
stop() function stops the program when <CTR>+<C> is
pressed at the console. In the main body of the
script, the console is cleared, the startup messages
are presented, setup() a n d look() are invoked to
execute the reed scanning or polling function, and
error controls are added.
############################
# E005_REED_POL.PY: Reed management by polling
# INPUTS: GPIO25 Reed input
# OUTPUTS: GPIO26 green LED and GPIO27 red LED
############################
128
Electronics & MicroPython© with ESP32©: 80 complete projects
from machine import Pin # GPIO manager
import time # Times management
reed_pin = 25 # GPIO25 reed sensor
r_pin = 27 # GPIO27 red LED
g_pin = 26 # GPIO26 green LED
pins = (r_pin, g_pin) # List of pins
# Flip-flop status (flag) on/off
state = False
# Pins configuration
def setup():
for pin in pins: # LED output and off
pin_obj = Pin(pin, Pin.OUT)
pin_obj.off() # LED off
# Changes LED status
def LED(x):
if x:
Pin(r_pin).on() # Red LED on
Pin(g_pin).off() # Green LED off
print('Red LED on...')
else:
Pin(r_pin).off() # Red LED off
Pin(g_pin).on() # Green LED on
print('Green LED on...')
# Polling to detect reed activation
def look():
global state
while True:
# If the reed is activated (low value)
if Pin(reed_pin).value() == 0:
# Change state to the opposite state
state = not state
LED(state) # LED changes
time.sleep(0.2) # Secure reading
else:
#Short wait if reed is not activated
time.sleep(0.05)
# Keyboard pressed, script ends
def stop():
for pin in pins: # Off LED list
pin_obj = Pin(pin, Pin.OUT)
pin_obj.off()
print('Program completed...')
# Main body of the script
if __name__ == '__main__':
print('\n' * 50) # Clear screen
129
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
print('REED SENSOR ACTIVATION')
print('Bring magnet closer to reed')
setup() # GPIO start
try:
#HERE IS THE POLLING
look() # Run the polling function
# HERE THE REST OF THE SCRIPT
except KeyboardInterrupt:
stop() # Script ends
I n t h e s c r i p t w i t h IRQ control, the same
operations of loading libraries, defining and
activating pins are carried out, but in the setup()
function the interrupt control is defined indicating
that the reed GPIO has a pull-up and therefore is will
activate LOW and when this GPIO detects a “falling”
control will be passed to the look() function.
The rest of the script is very similar to the
polling case with the logical difference that in the
main body of the script no polling is done and only
the instructions of the main program are executed,
which in this case, we have simulated with a while
True: loop and a time.sleep() to not load the ESP32©
CPU (adjust the time if necessary).
############################
# E005_REED_IRQ.PY: Reed management by IRQ
# INPUTS: GPIO25 Reed input
# OUTPUTS: GPIO26 green LED and GPIO27 red LED
############################
from machine import Pin # GPIO manager
import time # Times management
reed_pin = 25 # GPIO25 reed sensor
r_pin = 27 # GPIO27 red LED
g_pin = 26 # GPIO26 green LED
pins = (r_pin, g_pin) # Pins list
# Flip-flop status (flag) on/off
state = False
# Pins configuration
def setup():
for pin in pins: # LED as out and off
pin_obj = Pin(pin, Pin.OUT)
pin_obj.off() # LED off
# THIS IS WHERE IRQ IS TREATED
130
Electronics & MicroPython© with ESP32©: 80 complete projects
# Reed sensor whit pull-up
reed = Pin(reed_pin, Pin.IN, Pin.PULL_UP)
# Falling edge detection interrupt
reed.irq(trigger=Pin.IRQ_FALLING, handler=look)
# Change LED status
def LED(x):
if x:
Pin(r_pin).on() # Red LED on
Pin(g_pin).off() # Green LED off
print('Red LED on...')
else:
Pin(r_pin).off() # Red LED off
Pin(g_pin).on() # Green LED on
print('Green LED on...')
# Reed activated by IRQ
def look():
global state
# Change state to the opposite one
state = not state
LED(state)
time.sleep(0.2) # Secure reading
# Keyboard pressed, ends script
def stop():
for pin in pins: # LED list off
pin_obj = Pin(pin, Pin.OUT)
pin_obj.off()
print('Program completed...')
# Main body of the script
if __name__ == '__main__':
print('\n' * 50) # Clear screen
print('REED SENSOR ACTIVATION')
print('Bring magnet closer to reed')
setup() # GPIO start
try:
while True:
# HERE ISN'T POLLING
# HERE THE REST OF THE SCRIPT
time.sleep(0.05) # Rest script simulation
except KeyboardInterrupt:
stop() # Script ends
⊝⊝⊝
131
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E013: Relay control via reed.
Develop a MicroPython© script that uses a reed
switch to control the state of a contact relay. The
script should periodically check the status of the
reed switch by polling. When the reed is activated,
the relay will be activated and vice versa. Use an
LED, connected to the GPIO, to indicate the status of
the relay and another LED connected to the relay
contacts (add resistors) to see its output.
•E014: Safety with reed switch.
Create a script that uses a reed switch to
detect the opening of a door or window. Use polling to
verify the status of the reed switch. When the reed
switch detects that the door or window has been
opened, turn on a flashing red LED as an alert
indicator. The LED will turn off when the alert stops.
•E015: Lighting control with reed.
Design a script that uses a reed switch to
control lighting using an RGB LED (see following
exercises). Use polling to detect changes in the state
of the reed switch. When the reed switch is on, the
RGB LED changes to a sequence of specific colors and
when it is off, the RGB LED turns off.
•E016: Reed and IRQ.
Use the same circuit as the reed exercise and
change the previous scripts to use the switch control
detecting changes by IRQ, see later exercises for more
explanations and adjust the timing in the IRQ control
function to avoid possible bounces.
⊝⊝⊝
132
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 06:
OS25B10© Photo Switch
A photo switch, for
example the OS25B10© or
similar, is a sensor that
consists of two parts located
very close together: a light
emitter (infrared LED or
laser) and a light receiver,
generally a photo transistor.
When an object passes
between both parts, it
interrupts the light link
between emitter and receiver, causing the
corresponding activation of this sensor. Since the
photo switch does not have moving parts, it is widely
used in rotation measurements and calculation of
rotation speeds of
various elements.
As in other cases,
this sensor needs
addi tion al e leme nts
that control both the
light emitter and the
receiver.
In the emitting
part, an LED is always
emitting light as it
is powered with +3.3V
through R2, which
limits the current
that passes through it
to GND.
133
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In the receiving part, a photo transistor is
activated with the light from the emitter, turning
GPIO34 to LOW (reverse logic). Resistor R3 controls
the current that crosses LED D1 and resistor R1 acts
as a pull-up, keeping GPIO34 HIGH when the photo
transistor is not conducting. LED D0 illuminates with
the circuit power. In the MicroPython© script we will
use a Dual LED to indicate that the emitter-receiver
circuit of the photo switch is free or busy.
We can use a paper piece and insert it between
the transmitter and receiver to simulate an object
through the photo switch. If we wanted, for example,
to calculate the rotation speed of a wheel, we would
only have to calculate the time, per revolution, that
it takes one of its parts to interrupt the emitter-
receiver circuit or multiply the time by the number of
parts that interrupt the circuit in each lap.
The photo switch can contain an analog output
that gives a voltage depending on the amount of light
captured by the sensor. In this case, we should
convert this analog output to digital using an ADC
that we will see in future exercises.
The MicroPython© script begins by importing the
u s u a l machine a n d time libraries and defining the
photo switch pins in GPIO34 and the outputs for the
LED in GPIO26/27, the pin list and a boolean variable
to store the state of the photo switch. The setup()
function is defined to start the LED as output and the
photo switch pin as input with a pull-up (resistor
R1). The function look() checks, by polling, the state
of the photo switch and LED() changes the state of the
LED according to the state of the photo switch. The
stop() function stops the script by pressing <CTRL> +
<C> on the keyboard and turns off the LED and finally
the main body of the script is defined with a loop
that simulates the main program.
############################
# E006_PHOTO_INTE.PY: Photo switch and LED manager
# INPUTS: Activates photo switch on GPIO34
# OUTPUTS: Red LED on GPIO26 and green LED on GPIO27
############################
134
Electronics & MicroPython© with ESP32©: 80 complete projects
from machine import Pin # GPIO manager
import time # Times management
photo_pin = 34 # Photo switch
r_pin = 26 # Red LED
g_pin = 27 # Green LED
pins = (r_pin, g_pin) # Pins list
state = False # Flip-flop status
# LED as outs and photo switch as input
def setup():
for pin in pins: # LED out and off
pin_obj = Pin(pin, Pin.OUT)
pin_obj.off() # LED off
# Photo switch with pull-up
photo = Pin(photo_pin, Pin.IN, Pin.PULL_UP)
return photo
# Change LED status with photo switch one
def look(ph):
global state
state = ph.value() # Photo switch value
LED(state)
# LED status management
def LED(x):
if x:
Pin(r_pin).on() # Red LED on
Pin(g_pin).off() # Green LED off
print('Red...')
else:
Pin(r_pin).off() # Red LED off
Pin(g_pin).on() # Green LED on
print('Green...')
# Stop script
def stop():
for pin in pins: # LED list off
pin_obj = Pin(pin, Pin.OUT)
pin_obj.off()
print('Program completed...')
# Main body of the script
if __name__ == '__main__':
print('\n' * 50) # Clear screen
print('LED ACTIVACION BY PHOTO SWITCH')
print('Emitter-Receiver light interruption')
photo_s= setup() # Start GPIO
try:
while True:
look(photo_s) # Photo switch status
time.sleep(0.05) # Main program simulation
except KeyboardInterrupt:
stop() # End script
⊝⊝⊝
135
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E017: Format photo switch output.
With the previous exercise, change the script so
that it only displays the “Green” or “Red” states only
if there is a change in the state of the photo switch.
•E018: Photo switch analog output.
Use the analog output of the photo switch with
an Analog to Digital converter, ADC, to display the
amount of light captured by the sensor (see later
exercises) and depending on a previously defined
threshold, change the state of a reed relay or a relay
with traditional contacts.
•E019: Step speed.
Add a pulse and time counter to the polling loop
and based on both data estimate a speed of passage,
measured in interruptions per second, of an object
that circulates between the emitter and receiver of
the photo switch.
•E020: Photo switch and IRQ.
Modify the previous exercise and count the
pulses generated in the photo switch using a more
precise method such as the use of interruptions or
IRQ, which we will see in subsequent exercises.
•E021: Photo switch and LED array.
Connect to the ESP32© a photo switch controlled
by IRQ or polling and an array of LED so that the
interruption “speed” is displayed on the LED bar and
the numerical quantification of that speed is
displayed on the console.
⊝⊝⊝
136
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 07:
LED management with a Button
In this exercise we are going to manage several
actions of an LED with a simple button.
It seems like a strange exercise, why are we
going to need to turn on and off an LED or a device
using an ESP32©, why don't we just use the button and
the device to turn on or off and that's it?.
It seems much simpler to directly connect the
button to the LED and when we press it, the LED will
turn on or off as if it were a normal light bulb,
but... what if we want that when the button is
activated, the LED will turn on for a while and turn
off for another?, or vary its lighting depending on
whether the button is held down for more than x
seconds, or if it flashes several times... then we
would have to complicate the hardware much more.
Using the ESP32© and some very
simple circuits with the LED
(using the hardware from the
previous exercises) and adding a
button as indicated, the
management options are infinite
without changing anything in the
hardware, it will only be
necessary to modify the
software.
There are many alternatives
when connecting a push button to
the GPIO, here one is used that
eliminates fictitious pushes,
for this there is a resistor R
137
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
10k Ω (brown-black-orange), which connects the
GPIO25 to GND acting as a “pull-down”.
In this exercise, we will use 2 LED (red and
green) and a push button so that when we press the
button each LED will light up alternately. Each
element has its corresponding resistance associated
with it to properly manage the incoming and outgoing
currents in each affected GPIO.
When the P button is open, a minimum current i
flows through the resistor R supplied by the GPIO25
and in this way the logic level of the GPIO25 is LOW.
By pressing P, the GPIO25 is connected to +3.3V and
therefore its logic level goes to HIGH.
When we install the P button in the circuit and
depending on the manufacturer and model used, we take
into account that if it has 4 pins, internally these
can be linked 2 by 2 (1-2 and 3-4) as indicated below.
138
Electronics & MicroPython© with ESP32©: 80 complete projects
It is important to look with a multimeter to see
how these pins are connected before installation to
avoid unwanted short circuits.
We can also use more
complex buttons that
al re ad y in cl u de LE D to
display their status.
Here, LED D0 lights up
when the circuit is
connected, to indicate that
there is power, and D1 when
the S1 button is pressed. Resistors R1 and R0 control
the maximum intensity that flows through the LED.
As seen in the diagrams, for each button, adjust
the script so that when we press it, it goes from LOW
to HIGH or from HIGH to LOW.
139
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
There are two ways to capture the state of a
button: the easy way and the difficult one, and as in
almost everything in this world, the easy way is less
effective than the difficult way, although both ways
can be used interchangeably depending on the needs of
each project.
We will call the easy solution scan capture,
polling in English, and the difficult capture by
interruption, IRQ in English.
We will see both options in all detail, although
in this book we will talk more about interrupt
capture, especially so as not to burden the main body
of the script with the repetitive task of querying the
status of the button.
For more details and practical examples of using
pushbutton capture and other events through an
interruption, for example, in the management of Home
Automation alarms, I recommend my book (Spanish
version) “Electrónica y Domótica con Raspberry©” also
available on the Amazon© website.
Let's see what each case is about:
1. Capture by polling: in this mode the main
program polls the situation of the button in each
execution cycle, in our case the state of GPIO25 of
the ESP32© and acts accordingly as indicated in the
software.
In this case, the main software itself has to
control the bounces, dummy presses, for example,
waiting a while, polling several times, etc.
140
Electronics & MicroPython© with ESP32©: 80 complete projects
While the software is controlling the button, it is
not performing any other operations and this
greatly slows down the operation of our project,
especially when it has to probe the status of
several systems: keyboards, sensors, variables in
files, etc.
2. Capture by interruption: in this mode the main
program does NOT poll the state of the button in
each execution cycle since, on the other hand and
by software, the ESP32© is previously indicated
that when it receives a change of state
(configurable change) in the GPIO25, this is when
it receives an interruption, the execution of the
main cycle is momentarily stopped, the software
defined in this interruption will be executed and
when finished, the main cycle continues executing
from where the interruption occurred.
141
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
On the other hand, bounces and fictitious
keystrokes are controlled by the function defined
specifically for the management and control of
interruptions, freeing the main script from this
task, making it much cleaner and easier to read.
As we can see, this last option is more complex but
it is also much more effective, since the main
program does not stop in each cycle to check how
the button is and only when it is pressed, that is,
when the interruption occurs, is when the software
defined for the push button action is addressed,
ensuring that it is always managed properly.
We will do a first test, so that when we press
the button (short or long press) the green LED turns
on and on the next press the green LED turns off and
the red turns on and so on.
It is important that we configure GPIO25 of the
button as INPUT and GPIO27 (green LED) and GPIO14 (red
LED) as OUTPUT.
In this first option we manage the pressing of
the button BY POLLING.
The script scans a button by polling, that is,
checking the status of the button's pin in an infinite
loop. The button is located on the GPIO25 configured
with a pull-down to GND, this pull-down prevents the
entry of electrical parasites that activate the GPIO25
in an uncontrolled manner.
The script uses the machine libraries to control
the LED and pushbutton pins and time to control the
transition times in the events.
T h e toggle_led() function is defined so that
pressing the button alternates the activation of the
green and red LED and presents the corresponding
messages on the screen. The polling, that is, the
scanning of the state of the button, is carried out
within an infinite while True: loop checking the value
of the variable pin_boton.value() adding a small stop
to avoid “bounces” of the button, that is, readings
142
Electronics & MicroPython© with ESP32©: 80 complete projects
random actions of the same, also through another while
loop, the button is expected to be released. Finally
the script can be stopped by keyboard.
############################
# E007_2LED_BUTTON_POLLING.PY: Activates 2 LED
# according to pushbutton, button scan by polling
# INPUTS: Pushbutton status on GPIO25
# OUTPUTS: Green GPIO27 and red GPIO14
############################
from machine import Pin
import time
# Pins configuration
pin_ledR = Pin(14, Pin.OUT) # Red LED pin
pin_ledV = Pin(27, Pin.OUT) # Green LED pin
# Button pin with pull-down resistance
pin_button = Pin(25, Pin.IN, Pin.PULL_DOWN)
flag = True # Change LED status
# LED management
def toggle_led():
global flag # Status flag changes
if flag:
print('Red on, Green off')
pin_ledR.value(1) # Red LED on
pin_ledV.value(0) # Green LED off
else:
print('Green on, Red off')
pin_ledR.value(0) # Red LED off
pin_ledV.value(1) # Green LED on
flag = not flag
# Main loop
print('2 LED control with button by polling')
print('Push de button...')
print('End with <CTRL>+<C>')
try:
while True: # Here is the polling
if pin_button.value():# GPIO25 HIGH
print('Pushed')
time.sleep(0.2) # Avoid bounces
# Wait until the button is released
while pin_button.value():
pass
toggle_led() # Change LED status
except KeyboardInterrupt: # Ends loop
print('Completed')
pin_ledR.value(0) # All LED off
pin_ledV.value(0)
143
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In this second option we manage the pressing of
the button BY INTERRUPTION. On this occasion the main
loop presents the state of the LED only if this state
changes. These changes are managed in a function that
is only executed when a change of the button by IRQ to
RISING is captured, that is, from LOW to HIGH state.
With this script the load is released in the
main loop, avoiding the permanent review of the state
of the button and therefore this main loop does not
suffer delays or jumps in execution. The main loop
waits 100ms to simulate the main body of the script.
In the definition of the button it is indicated
that the boton_irq() function will be activated only
when a step in the button from low to high level is
detected. If there are button bounces, they must be
controlled by hardware (adding a pull-up or an
additional capacitor) or by software by adding a delay
such as time.sleep(.001), since the ESP32© does not
support automatic bounce control or “bounce time”
within the definition of the IRQ (the Raspberry© and
other micro controllers do).
############################
# E007_2LED_BUTTON_IRQ.PY: Activates 2 LED
# according to interrupt button scan (IRQ)
# INPUTS: Pushbutton status on GPIO25
# OUTPUTS: Green GPIO27 and red GPIO14 on/off
############################
import time
from machine import Pin
# Pins configuration
# Pushbutton with 10k pull-down
pin_button = Pin(25, Pin.IN, Pin.PULL_DOWN)
led_green = Pin(27, Pin.OUT)
led_red = Pin(14, Pin.OUT)
# Initial status: LED off
led_green.off()
led_red.off()
# Starts with green LED when the button is pressed
flag = True
# Print 50 blank lines to clear console
print ('\n' * 50)
print ('Bounce-free pushbutton capture')
print ('Push button')
144
Electronics & MicroPython© with ESP32©: 80 complete projects
# Interrupt control for button
def button_irq(pin):
global flag
# If the button is still pressed
if pin.value() == 1:
time.sleep(.001) # Avoid bounces, adjust
# Check if button is pressed again
if pin.value() == 1:
# Display 50 blank lines
print('\n' * 50)
if flag:
led_green.off() # Green LED off
led_red.on() # Red LED on
print("Red")
flag = False
else:
led_green.on() # Green LED on
led_red.off() # Red LED off
print("Green")
flag = True
print ('Push button')
# Interrupt settings for the button,
# active in HIGH (rising) and when the IRQ is
# activated it call the irq_button() function
pin_button.irq(trigger=Pin.IRQ_RISING,
handler=button_irq)
try:
while True:
time.sleep(.1) # Main loop of the script
except KeyboardInterrupt:
print('\n' * 50) # Display 50 blank lines
print("Program completed...")
finally:
led_green.off() # LED of when exit
led_red.off()
Note that this example has used error control
with three bodies: try:, except: and finally:, this
last option is always executed, whether or not there
is an error in the main while True: loop.
Also note that in the main loop of the script,
there is no polling of the button state and there is
only a while True: loop with a single statement that
simulates the main loop of the script that would
perform most of the script's tasks.
⊝⊝⊝
145
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E022: LED lighting with a button.
Develop a program in MicroPython© for ESP32©
that turns on a green LED when a button is pressed and
turns it off when it is released, uses the polling
method to know the state of the press.
•E023: LED toggle with one button.
Create a program that, when we press a button,
alternates between turning a green LED on and off each
time the button is pressed. Try toggling off/on with
two different colored LED.
•E024: Pulsation counter.
Write a program that counts and displays on the
console the number of times a button is pressed while
turning a green LED on and off. When a certain amount
is reached, another LED should light up.
•E025: Button with temporary lock.
Develop a MicroPython© simple program that
ignores button presses for a predetermined amount of
time, entered by console, after each press to prevent
multiple reads.
•E026: Message on screen when pressing the button:
Implement a program that, when we press the
button, clears the screen, presents a message and
alternately lights the green and red LED. Use a
try:...except:...finally: block to handle interrupts
appropriately.
•E027: Control an LED.
Create a program MicroPython© that blinks an LED
connected to a GPIO. The LED should flash on for 1
second and off for another second until an IRQ
controlled button is pressed. The LED status should be
indicated, if applicable, in the function that
controls the IRQ.
146
Electronics & MicroPython© with ESP32©: 80 complete projects
• E028: Read the status of a button.
Design a program that reads the state of a
button connected to a GPIO and controlled by IRQ.
Prints to the console "Button pressed" when the button
is pressed and "Button not pressed" when it is
released. Controls that messages are only presented
when there is a state change, lasting more than 1
second, in the function that controls the IRQ.
•E029: Temperature measurement with a sensor.
Use a temperature sensor (such as the DS18B20©)
properly connected to a GPIO of the ESP32© to measure
the ambient temperature periodically. Prints on the
console the value of the last temperature read when
the button is pressed, presenting the maximum and
minimum value since the last press.
If a long press is performed, it resets the
maximum and minimum historical values and indicates it
with the appropriate messages on the console.
•E030: Tone generation with a buzzer.
Write a program that causes a buzzer connected
to a GPIO to emit tones of different predetermined
frequencies and volumes included in the dictionary
start_data{}, which alternate sequentially or randomly
when a button is pressed.
•E031: Control of a servo motor.
Connect a servo motor to the ESP32© with the
corresponding circuit (it will depend on the power of
the motor, etc.) and create a program that moves the
servo in different directions (see other exercises).
⊝⊝⊝
147
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 08:
ESP32© Internal Data
In this exercise we leave the hardware external
to the ESP32© “parked” for the moment, which logically
we will return to later and we focus on some variables
that we can extract and manage from its own structure.
To carry out this exercise we are going to use the
esp32 library to obtain the internal temperature of
the ESP32© CPU, with data formatted in degrees Celsius
and the machine library to see the MAC address of that
CPU in the format of 6 hexadecimal numbers that will
allow us always have our ESP32© identified, both via
WiFi and Bluetooth. We import the aforementioned
libraries, obtain the temperature, convert it from
Fahrenheit to Celsius, obtain the MAC address and
present both data in a formatted manner.
############################
# E008_DATA_ESP32.PY: Display ESP32© chip data
# INPUTS: Access to ESP32©
# OUTPUTS: ESP32© chip formatted data
############################
import esp32 # To get the temperature
import machine # To get the MAC address
print ('Data of my ESP32©')
temperature = esp32.raw_temperature() # ºF
temperature = (temperature-32)*5/9 # ºC
print("Temperature: {:.1f} ºC".format(temperature))
# Gets the unique identifier of the ESP32© chip
chip_id_bytes = machine.unique_id()
# Convert identifier to hexadecimal and format it
formatted_chip_id = ':'.join('{:02X}'.format(byte)
for byte in chip_id_bytes)
# Display formatted identifier
print("My ESP32© chip MAC:", formatted_chip_id)
⊝⊝⊝
148
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E032: CPU temperature with polling.
Create a program that uses the esp32 library to
present data, using polling, of the CPU temperature.
Displays temperature in Celsius, Fahrenheit and
Kelvin, updates the reading every second using the rtc
library.
•E033: CPU temperature with IRQ.
Design a program that uses the esp32 library and
IRQ interrupts to control the CPU temperature reading.
Each time a button connected to the corresponding pin
is pressed, display the updated temperature in
Celsius degrees.
•E034: MAC address with polling
Write a program that polls the MAC address of
the ESP32© and prints it in 6 hexadecimal number
format. Updates the reading every 2 seconds
controlling it from rtc.
•E035: MAC address with IRQ.
Create a program that uses the machine library
and controlled interrupts in a specific function, to
obtain the MAC address of the ESP32©. When we press a
button connected to the necessary pin, display the MAC
address in hexadecimal format.
•E036: Temperature and MAC with buttons.
Design a program that, using two buttons
connected to the appropriate pins (see ESP32© pins
depending on model), alternates between displaying the
CPU temperature and the MAC address of the ESP32©.
Each time a button is pressed, the information
displayed on the device changes, for example, one
button displays the temperature and MAC alternately
and another clears the screen.
⊝⊝⊝
149
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 9:
ESP32© Internal WatchDog
In this exercise we will see a very important
component to complete an electronic or Home Automation
project with hardware managed by software. As we all
know, when we build a project with hardware components
controlled by software, there is the possibility that,
under certain conditions, the software "hangs", stays
in an infinite loop, stops, etc., causing a stop in
the normal execution of our algorithm.
To avoid this situation, there is an electronic
circuit, called “watchdog” or supervisor, which is
responsible for resetting the ESP32© if the script
does not “feed” that watchdog circuit before a
previously established maximum time.
Luckily, the ESP32© implements a hardware
watchdog inside and which we will see at the end of
this exercise, however we will see also how a watchdog
works with an external circuit and that it can help us
with other micro controllers.
The watchdog circuit consists of a pair of 7555©
ty p e t i m er s ( ve r s io n o f t h e N E 5 55 © i n C M O S©
technology) configured in stable mono mode and whose
input is powered by a clock signal, CK, generated by
our script through a GPIO.
I f t h e CK clock signal, generated by the
MicroPython© script, disappears or is altered because
the script is not executed correctly, hang, fall into
infinite loop, stops, etc., this watchdog circuit
automatically resets the micro controller hardware and
al s o t h e s c ri p ,t r es t or i ng i ni t ia l o pe r at i ng
conditions.
150
Electronics & MicroPython© with ESP32©: 80 complete projects
In the electrical diagram of the “watchdog” we
can see the following elements:
1 . T h e CK clock signal of the “watchdog” comes
directly from the GPIO of the micro controller and is
a square signal with
+3.3V amplitude and
be tw e en 1 Hz a nd 5 Hz
frequency.
2. T h i s CK s i g n a l
attacks an integrated
ci rc u it I C1 o f t y pe
7555©, in stable mono
configuration, adjusted
by the pair R3+C2 with
a value of 39kΩ+220uF
to a pulse activation,
T1, o f 1 0 s e c o n d s ,
calculated with the
formula following:
T1=1.1*39k*220u=9.5seconds, see (1) in the figure.
3. The 10-second pulse signal T1 is continuously
regenerated (monostable, retriggerable) by the
transistor T1 S8050© NPN type and the 10kΩ resistor
R4, as long as the CK pulse exists at intervals of
less than 10 seconds.
151
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
4. If the 10-second pulse signal T1 disappears or
is more distant than 10 seconds, a pulse, T2, of
+3.3V is generated by the second IC2 type 7555©,
set by the pair R1+C3=8k2Ω + 220uF at 2 seconds,
calculated as: T2=1.1*8k2*220u=2seconds, see (2) in
the figure.
5. The second signal, T2, of 2 seconds of duration
and +3.3V of voltage, activates the reed relay K1
type G-35770©.
6. The K1 reed relay type G-3570© temporarily
links, during those 2 seconds, the reset pins of
the micro controller, producing the corresponding
total hardware reset of the system and the restart
of the script.
7. The K1 reed relay, type G-35770©, also serves to
electrically isolate the micro controller from the
watchdog electronics, avoiding electrical problems
between both systems.
8. We must include in our script the feed function
feed() that emits a down pulse in the GPIO to
generate the CK signal that attacks the circuit.
T h i s p o w e r o r feed() function must be called
periodically within the script, for example using a
hardware timer activated in less than 10 seconds,
otherwise the system will reset.
As we mentioned at the beginning of the
exercise, the ESP32© incorporates an internal watchdog
that consists of a timer that is responsible for
monitoring the execution of the program and restarting
the device if the program does not respond within a
certain period of time. It works as follows:
1. Configuration: The watchdog can be configured to
have a specific timeout, which is the time the
system waits between feeds from the watchdog
before automatically restarting the device.
2. Feed: To prevent the watchdog from rebooting the
device, the program must power or feed the
watchdog periodically.
152
Electronics & MicroPython© with ESP32©: 80 complete projects
This is done by calling the watchdog power or
feed function before the configured timeout
expires.
If the watchdog is not powered within the
waiting time, the ESP32© will automatically
restart without generating any type of
exception.
3. Monitoring: The watchdog continuously monitors
whether it is feeding within the configured
timeout. If it does not receive a power supply
within this time, it assumes that the program is
not responding correctly and triggers a reboot
of the device.
4. Reset: When a reset occurs due to watchdog
ac t iv a ti o n, t h e E S P3 2 © r e se t s a n d bo o ts
normally. If we place our script inside boot.py
or main.py it will restart again.
In short, the ESP32©'s internal watchdog is a
safety measure that helps ensure system stability and
reliability by restarting the device in case the
program does not respond correctly within a certain
time. It is especially useful for avoiding system
crashes or bugs caused by programming errors or
unexpected conditions.
The watchdog, whether external or internal, is
not infallible since it may happen that the script
hangs in a loop where the feed() power function of the
ESP32© internal watchdog is implemented and logically,
the watchdog can not correct the source that caused
the error or hang of the script.
Despite these issues the watchdog is very useful
in many projects and for all types of micro
controllers.
To control the internal watchdog of ESP32© we
can use a MicroPython© script like the following and
it is important to understand that once activated it
cannot be deactivated or modified at not time.
153
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E009_WATCHDOG.PY: Activate ESP32© internal WatchDog
# INPUTS: Feeding time
# OUTPUTS: Messages and ESP32© reset
############################
# Test WatchDog: TIME = 2/6 no/yes activates
import machine # WatchDog manager
import time # Times management
TIME = 6 # Change for WatchDog simulation
# Set WatchDog with a 5s timeout
wdt = machine.WDT(timeout=5000) # Micro seconds
# Simulates task that could crash
def task():
print("Task start...")
time.sleep(TIME) # Change for error simulation
print("Task end")
wdt.feed() # Feed WatchDog
# Main program loop
print('Example of WatchDog use of the ESP32©')
print(f'WatchDog to 5s and feed each {TIME}s')
try:
while True:
task() # Function that could crash
except KeyboardInterrupt:
print('Completed...')
except Exception as e:
print("Error:", e)
⊝⊝⊝
154
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E037: Temperature control and Watchdog.
Write a MicroPython© script for ESP32© that
monitors its internal temperature. If the temperature
exceeds a specific threshold, entered through the
console, activate an LED (simulated fan) as a
“cooling” measure. Use the watchdog to ensure that the
ESP32© does not overheat and reboot in case the script
cannot handle the situation correctly. We will assume
that the restart of the script occurs with the Reset
of the ESP32©.
•E038: Alarm with reed switch and Watchdog.
Create a simple alarm system using a reed switch
as a door or window opening sensor (reed sensor, not
reed relay). When an opening is detected, it activates
an alarm (for example, a flashing LED or a buzzer).
Use the watchdog to ensure that the alarm system
remains operational and responds correctly even in the
event of system failures.
•E039: Lock simulation, LED and Pushbutton.
Implement a system that turns an LED on and off
using a button. However, introduce (for example by
pressing 3 times in a row) a deliberate infinite loop
in the code to simulate a crash or malfunction. During
this loop, the watchdog will not be fed, which should
trigger the ESP32© to reset.
Use the watchdog to detect this state and
automatically restart the system in case of a crash,
thus ensuring system recovery even in extreme failure
situations.
⊝⊝⊝
155
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 10:
Connect the ESP32© to WiFi
The ESP32© is a very complete micro controller
and has a 2.4GHz 802.11 b/g/n WiFi connection that al-
lows it to connect to
its environment, to the
Internet, access it re-
motely, and have access
to cloud services, sen-
sor data collection,
u s e o f W e b a p p l i c a-
tions, Web server, etc.
and all this in a fair-
ly simple way.
To activate the WiFi and the corresponding
connection, we will make an example script like the
one below, where we use the network library to connect
the ESP32© to a WiFi previously identified with its
name, that is, with its SSID (name of the network) and
with the corresponding WiFi PASSWORD.
Basically the script loads the network and time
libraries, assigns the SSID and PASSWORD constants
according to the name and password of the WiFi to
which we are going to connect, configures and
activates the connection (in case it is not already
connected) and performs a waiting loop until the
connection is made successfully.
############################
# E010_WIFI.PY: Activates ESP32© Wifi
# INPUTS: SSI/PASSWORD
# OUTPUTS: Connection made and IP used
############################
import network
import time
156
Electronics & MicroPython© with ESP32©: 80 complete projects
# Name of your WiFi network, SSID and password
SSID = "[WiFi network name]"
PASSWORD = "[WiFi password]"
# WiFi connection configuration
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
if not sta_if.isconnected():
sta_if.connect(SSID, PASSWORD)
# Wait for the connection to be established
while not sta_if.isconnected():
time.sleep(1)
# Configure the static [IP], gateway and DNS
sta_if.ifconfig(('192.168.1.[IP]', '255.255.255.0',
'192.168.1.1', '8.8.8.8'))
# Display the IP address assigned by the router
print("Connection established. IP direction:",
sta_if.ifconfig()[0])
In the script we will have to change the SSID,
PASSWORD and IP with the data of the WiFi network to
which we want to connect. If necessary, we must also
change the IP of the gateway (in the example
192.168.1.[IP]) and the DNS (in the example, the
Google© DNS, that is, 8.8.8.8 or 8.8.4.4).
⊝⊝⊝
157
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E040: WiFi connection with status LED.
Design a script that connects the ESP32© to a
specific WiFi network using the network library. Use
an LED connected to the corresponding GPIO that can be
used as a status indicator. The LED should flash
rapidly while the ESP32© attempts to connect to the
network and stay on once the connection is successful.
Use two LED, one red and the other green, so
that the red flashes while the connection is attempted
and the green lights up when the connection is made.
U s e try:...except: blocks to handle possible errors
and display appropriate messages.
•E041: Automatic disconnection and reconnection.
Write a script that automatically handles
re c o nn e c ti o n t o t he W i Fi n et w o rk i n ca s e of
disconnection. Force the disconnection, for example by
turning off the router.
Use an LED connected to a GPIO to indicate the
status of the connection. When the connection is lost,
the LED should flash, and the ESP32© should attempt to
reconnect automatically. Define a disconnection
timeout and handle errors with the corresponding
try:...except: blocks.
•E042: LED control with WiFi connection.
Create a script that, after successfully
connecting the ESP32© to the WiFi network, turns on an
LED connected to a GPIO and that we will use as a
visual indicator of an active connection.
Use a polling loop to periodically check the
connection status and the corresponding check with
try:...except:.
Add a watchdog to control possible hangs when
Wi-Fi is not properly detected.
158
Electronics & MicroPython© with ESP32©: 80 complete projects
•E043: Disconnection by push button with IRQ.
Design a script that allows us to manually
disconnect the ESP32© from the WiFi network by
pressing a button connected to the GPIO. Use
interrupts (IRQ) to detect the push button event and
performs the corresponding disconnection. Add all
necessary messages, as well as error controls with
try:...except: bodies.
Expand this exercise with another button that
allows the connection. Presents the status with
corresponding LED and appropriate messages.
•E044: Conditional connection, button and polling.
Write a script that, at startup, waits for a
button connected to the GPIO to be pressed before
attempting to connect to the WiFi network. Use polling
to periodically check the status of the button and,
when pressed, proceed to connect to the WiFi network.
Add appropriate error messages and checks.
⊝⊝⊝
159
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 11:
Scan WiFi with ESP32©
Logically, in addition to connecting to a WiFi
for which we previously know its SSID, sometimes we
will need to scan the existing WiFi in the environment
of our ESP32© and for this we will also use the
network library, activate WiFi, and load the WiFi list
detected in a list[]. If desired, we can connect to
one of the detected WiFi, if we can indicate its SSID
and logically its password and present the IP and MAC.
############################
# E011_SCAN.PY: Performs WiFi network scanning
# INPUTS: Nothing
# OUTPUTS: Available Wifi networks
############################
import network
import utime
# Define and activate WiFi connection
wf = network.WLAN(network.STA_IF)
wf.active(True)
# Load to list[] available WiFi
list = wf.scan()
# Display list[] elements
for net in list:
print (net[0].decode())
# Change WiFi name and password
# to which we want to connect
wf.connect('ssid','password')
while not wf.isconnected():
print ('.')
utime.sleep(1)
print ('IP:'+wf.ifconfig()[0]) # Display IP
print ('MAC:'+str(wf.config('mac'))) # Display MAC
print (wf.ifconfig())
⊝⊝⊝
160
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises
•E045: WiFi scanning with LED indicator.
Design a script that scans the available WiFi
networks using the network library. Use an LED
connected to the GPIO to visually indicate the
scanning process.
The LED should flash while the ESP32© scans for
available networks and stay off when the scan is
complete at all. Use the appropriate controls and
messages.
•E046: WiFi network selection with buttons.
Create a program that, after scanning for
available WiFi networks, allows the user to select a
network using two pushbuttons connected to the
appropriate GPIO pins.
One button advances in the list of networks and
another button goes back and the selection is
confirmed by holding down a button for a certain
amount of time.
An LED connected to the GPIO should visually
indicate the selection. Upon confirming the selection,
the ESP32© will connect to the chosen network and
present the corresponding messages.
•E047: Manual disconnection with IRQ.
Design a MicroPython© script that allows us to
manually disconnect or not the ESP32© from a selected
WiFi network by pressing a button connected to the
GPIO pin.
Use interrupts (IRQ) to detect the push button
event and performs the corresponding disconnection. An
LED connected to the GPIO and messages indicate the
status of the connection.
161
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
•E048: Automatic connection with polling and LED.
Write a program that, when starting the ESP32©,
automatically tries to connect it to a WiFi network
predefined by the console.
Use polling to periodically check whether the
connection is successful and use an LED connected to
the GPIO to visually indicate the connection status.
•E049: IP and MAC display with LED.
Design a script that, after successfully
connecting to the determined WiFi network and entering
(SSID and password) by keyboard, displays the IP
address and MAC address of the ESP32© on the console.
Use an LED connected to the GPIO to visually
indicate that the connection and data display have
been successful. Use the appropriate controls to
verify the entry of WiFi parameters.
⊝⊝⊝
162
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 12:
Analog to Digital Converter
In this exercise we are going to see a very
interesting feature of the ESP32©, which also includes
the Arduino© but not a Raspberry©, it is the hardware
conversion of analog signals external to the ESP32©
into digital signals that can be processed numerically
with a MicroPython© algorithm. We will therefore see
how to work with the ADC (Analog to Digital Converter)
and later we will see the reverse process, this is,
the DAC (Digital to Analog Converter).
Before getting fully into this exercise we have
to understand very well what analog signals are and
what digital signals are, which are them differences
and what this conversion is for.
• ANALOG SIGNAL: It is a continuous representation
of information, where the values vary
continuously over time and therefore can have
infinite values between two points. Examples:
the voltage variation provided by a temperature
sensor depending on the ambient temperature, the
voltage variation provided by a water pressure
sensor in a pipe, the sound we hear, the amount
of light we perceive with our eyes, etc.
• DIGITAL SIGNAL: I t i s a d i s c r e t e ( n o n -
continuous) representation of information, where
values are quantized in discrete steps, meaning
that the signal can only take a finite set of
values. Examples: a binary signal that can only
be 0 or 1, the state of a relay that can be open
or closed, sound recorded in MP3 format which is
a set of specific numbers, the push button of a
light, etc.
163
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Having this clear, we must also know what
advantages it has to use digital signals instead of
using their analog equivalents and what we could
summarize:
• Noise resistance: Digital signals are much less
susceptible to interference, distortion and
loss. They can be easily filtered and recovered
to their original form using mathematical error
detection and correction algorithms.
• Ease of storage and processing: They can be
processed, manipulated, encoded, compressed,
received, transmitted and stored very easily and
without significant quality losses.
• Precision and stability: Being discrete, they
offer greater precision and stability in data
transmission and processing. Errors can be more
easily detected and even corrected by using
redundancy in digital information.
• Flexibility and versatility: They allow greater
flexibility and versatility by being easily
adapted and reconfigured through simple or
complex mathematical algorithms that allow their
use to be integrated into multiple systems.
• Compatibility with computer systems: They are
fully compatible with computer systems, which
facilitates their integration with electronic
devices, allowing integration with all types of
practical applications.
The ESP32© has a single
internal ADC, that is, a single
device that converts an external
analog signal into an internal
digital one, that is, we are talking
about using the GPIO as an input for
that analog signal.
Depending on the ESP32©
model, we can have up to 20 GPIO
(ADC0...ADC19), which can be
configured as inputs for the ADC and
used alternatively but not
simultaneously.
164
Electronics & MicroPython© with ESP32©: 80 complete projects
The ADC of the ESP32© is 12bit and therefore we
can convert an analog signal, with a voltage between 0
12
and +3.3V, into a digital 2 =4,096 levels signal.
IMPORTANT: Since we are going to apply an external
voltage to a GPIO of the ESP32©, let us remember that
this voltage must be between 0 and +3.3V, otherwise we
will damage the ESP32© irreversibly. If we need to
convert analog signals of voltages higher than +3.3V,
we must use some type of voltage divider. For example,
if we want to convert a voltage of +5V we can use a
voltage divider built with two resistors, for example
one 10k Ω , connected between the GPIO of the ADC and
+5V and another 20k Ω , connected between that GPIO
and GND.
In this exercise we will use
the GPIO34 as the input of the ADC
and to simulate an analog signal
with a voltage between 0 and +3.3V
we will use a 10k Ω potentiome-
ter, connecting its ends to +3.3V
and GND and the intermediate point
to the GPIO34.
As we have already mentioned, we can use either
a module that includes the potentiometer, some
additional element such as an LED with its control
resistor, or we can use an isolated potentiometer.
On the other hand, and to
display the voltage level detected
by the ADC, we are going to use two
LED, a green one connected to
GPIO27 and a red one on GPIO26,
which will flash depending on
whether or not the average
measurement level is exceeded, that
is, the 1.65V.
Let's remember to use a 2-LED
module with built-in resistors or
add some 220 Ω resistors to each
LED to limit the current. In the
module circuit that includes the
165
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
potentiometer, we can see that a 10k Ω variable
resistor is incorporated where its ends are connected
to +3.3V and GND and its moving point to the ADC of
the ESP32©. This module also optionally incorporates
the pair composed of the LED D0 and the current
limiting resistor R0, to indicate that it is connected
to the power supply.
The MicroPython© script uses the machine library
to activate the ADC converter in the GPIO34 and be
able to manage the red and green LED with the
objective of being able to display a signal that
varies with the potentiometer between 0 and +3.3V and
making the green LED blink if the voltage does not
exceed 1.65V and the LED turns red otherwise. The time
library is also used to control the blinking time.
By default, the ESP32© ADC captures levels from
0 to +1V, to extend it to +3.3V it is necessary to add
the adc.atten(machine.ADC.ATTN_11DB) instruction to
our code. The script assigns the LED to each GPIO and
initializes them as outputs. The function adc_read()
is created, where the ADC value is converted into
voltage data and an infinite loop is defined where the
166
Electronics & MicroPython© with ESP32©: 80 complete projects
ADC is read periodically and, based on this reading,
the flashing of the LED is managed. Finally, the
script includes the corresponding status messages of
the LED, the ADC voltage values, the error controls
and the completion of its execution if <CTRL>+<C> is
pressed. Note that if the script is finished, both LED
turn off.
############################
# E012_ADC.PY: Read analog to digital converter ADC
# INPUTS: ADC in GPIO34 between 0 and +3.3V
# OUTPUTS: ADC in volts GPIO27/26 LED green/red
############################
import machine
import time
pin_adc = 34 # Pin ADC configuration
adc = machine.ADC(machine.Pin(pin_adc))
adc.atten(machine.ADC.ATTN_11DB)#0-3.3V (default 0-1V)
pin_led_green = 27 # Configure pins for LED
pin_led_red = 26
led_green=machine.Pin(pin_led_green,machine.Pin.OUT)
led_red =machine.Pin(pin_led_red ,machine.Pin.OUT)
# ADC is 12bits therefore 2^12-1=4095 levels
def adc_read():
adc_value = adc.read()
voltage = (adc_value / 4096) * 3.3
return voltage
t=.5 # LED blink time
try: # Script main body
while True:
value = adc_read()
if value > 1.65: # If tension raise range
led_green.off() # Green LED off
led_red.on() # Red LED on
time.sleep(t) # Red LED blinks
led_red.off()
else:
led_red.off() # Red LED off
led_green.on() # Green LED on
time.sleep(t) # Green LED blinks
led_green.off()
print("ADC value:", value, 'v')
time.sleep(t) # Space between readings
except KeyboardInterrupt:
print("\nADC stop reading.")
led_green.off() # LED off
led_red.off()
⊝⊝⊝
167
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises
•E050: ADC display with threshold LED.
Design a script that activates an ADC converter
and displays the signal levels coming from a
potentiometer between 0 and +3.3V. Implement an LED on
a GPIO that flashes when the signal level exceeds a
predetermined threshold entered by keyboard and
checking its numerical validity.
•E051: LED, potentiometer and polling control.
Create a program that uses a potentiometer
connected to the ADC on a GPIO pin and that allows
controlling the intensity of an LED connected to
another GPIO. Use polling to read the values.
•E052: LED flashing according to ADC levels.
Design a script that drives an ADC on two pins
and uses two potentiometers. Incorporate two LED that
blink if the signal, added or subtracted from the two
ADC inputs, is close to the average level. Use polling
to capture the value of each ADC.
•E053: Switches and ADC.
Write a program that uses a connected switch to
activate the ADC. If ADC is enabled, display the
potentiometer levels and light an LED on the
corresponding GPIO if a certain threshold is exceeded
and display a red LED and with appropriate messages
when ADC is not enabled.
•E054: Connection to WiFi with ADC threshold.
Design a script that, when connecting to a WiFi
network, activates the ADC and uses a potentiometer.
Display ADC levels and light an LED if a threshold is
exceeded. Disconnect the WiFi and see how the
programmed LED flashes, checking for errors.
⊝⊝⊝
168
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 13:
Digital to Analog Converter
In this exercise we will see the reverse process
of the previous exercise, that is, we will see how a
digital process is converted into an analog signal
created with the Digital to Analog Converter (DAC)
that our ESP32© contains.
The uses of a DAC converter are multiple, for
example, digital audio playback (*.wav, *.mp3, etc.),
motor control (printers, robots, etc.), video signals
(graphics cards, HDMI, etc.), instrumentation
(polymeters, oscilloscopes, etc.), communications
(modem, fiber optics, etc.), etc.
The ESP32© has two DAC (DAC1 and DAC2), that is,
two digital-to-analog converters that allow the
generation of an output signal between 0 and +3.3V and
with a limited frequency, to several tens of kHz, by
the hardware of the ESP32© and the software algorithm
used in our script.
The ESP32© DAC are 8bit, therefore they can
8
generate up to 2 =256 voltage levels at the GPIO
output, for example GPIO25 or GPIO26 depending on the
ESP32© model. With this configuration we can modify
the voltage of one of those output GPIO between 0 and
+3.3V in steps of approximately 13mV, that is:
3.3 /256=0.013V=13mV.
To see applications of a DAC we are going to
start with a simple example, specifically we are going
to vary the luminosity of an LED, connected to the
output of the DAC in the GPIO25, periodically changing
the voltage applied to the LED between +1.5V (minimum
voltage trigger) and +3.3V (ESP32© maximum voltage).
169
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
We will use a step of 180mV to be able to
observe the generated signal on an oscilloscope but it
could be any step between 0 and +3.3V. It is important
to remember that we must limit the output current of
the DAC to a maximum of 14mA and to do this we will
add a 220 Ω resistor in series with the LED.
Below we see the circuit mounted on a breadboard
where we can see the LED connected through the
resistor to the GPIO25 and through the other end to
the GND pin of the ESP32©.
In the script, the time library is imported for
time control and machine for managing the DAC and the
GPIO that will give the variable voltage output.
170
Electronics & MicroPython© with ESP32©: 80 complete projects
############################
# E013_DAC.PY: Change LED brightness using a DAC
# INPUTS: Maximum, minimum voltage and inc/dec step
# OUTPUTS: Various illumination of an LED on GPIO25
############################
import time
from machine import Pin, DAC
# Configuring the GPIO25 DAC on the ESP32©
dac_pin = Pin(25)
dac = DAC(dac_pin)
print('Generates an analog signal on GPIO25')
start = 1500 # Minimum voltage (mV) triggers the LED
end = 3300 # Maximum voltage (mV) +3.3V
step = 10 # Step between voltage, change
p_time = .0001 # Pause between values, change
# Generates sequence of voltage values in GPIO25
while True:
# For example an upload sequence
for volt in range(start, end, step): #min,max,inc
# Desired voltage level in mV
voltage_mV = volt
# Converts voltage to DAC range (0-255)
dac_value = int(voltage_mV / 3300 * 255)
# Set the voltage level
dac.write(dac_value)
# Wait [time] seconds
time.sleep(p_time)
# For example a descent sequence
for volt in range(end, start, -step): #max,min,dec
# Desired voltage level in mV
voltage_mV = volt
# Converts voltage to DAC range (0-255)
dac_value = int(voltage_mV / 3300 * 255)
# Set the voltage level
dac.write(dac_value)
# Wait [time] seconds
time.sleep(p_time)
If we view the output of GPIO25 on an
oscilloscope we will see something similar to:
171
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In the diagram we can see the triangular signal
generated by the DAC of the ESP32©, which is
approximately 36Hz and 166mV and which we compare with
a square reference signal of 50Hz and 2.1V.
⊝⊝⊝
172
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E055: DAC level display with LED.
Create a program that uses the DAC converter to
generate a variable voltage between +1.5V and +3.3V in
steps of 100mV. Connect an LED to the corresponding
GPIO and use this generated voltage to control the
brightness of the LED. Be sure to limit the output
current of the DAC to the LED and consider the
resistors needed to avoid damage to the ESP32©.
•E056: Brightness control with push button.
Additionally, implement a logic that, when
pressing the button, increases the DAC voltage and
when releasing the button, it decreases, thus
controlling the luminosity of the LED proportionally
to the time the button is held down.
•E057: Remote brightness adjustment via WiFi.
Modify the program to enable the WiFi connection
and activate it or not depending on the DAC value.
Finally, add the controls in loops of the
try:...except: type, to avoid possible errors.
•E058: Response to temperature changes.
Integrate a temperature sensor using an ADC, or
use the internal sensor and adjust the program so that
the LED changes according to temperature measured
using DAC. Defines thresholds to increase or decrease
LED brightness. Experiment with different thresholds
and response times to achieve a desired behavior.
•E059: Alarm configuration with DAC.
Add items to generate alarm using DAC and LED.
Defines a threshold on the keyboard that activates the
alarm, increasing the LED frequency. The threshold
will be configured through the WiFi connection.
⊝⊝⊝
173
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 14:
Wave generator with DAC
In this exercise, using the same hardware and
configuration as the previous exercise, we are going
to use the potential of the ESP32© DAC to generate a
signal between 0 and +3.3V to use it as a generator of
different types of waves.
Before continuing, it must be taken into account
that the speed of the ESP32© hardware, the precision
of its DAC (only 256 levels), the delays introduced by
MicroPython© scripts, etc. make this wave generator
very basic (up to 200Hz and 70 samples) but for
training tasks it is more than enough.
Let's take into account basic concepts such as:
• Frequency (f): is the number of times an event
repeats, in this case the number of times the
complete cycle of a wave repeats in one second.
This number is called Hertz and is represented
by the acronym Hz.
• Period (p): it is the time necessary for
something periodic to repeat itself, in our case
it is the time that a cycle of a wave lasts and
we will measure it in seconds. The period is
mathematically the inverse of the frequency and
vice versa, that is: p=1/f or f =1/ p.
• Samples (S): is th e n umb er of p oi nts or
measures, that we take to represent a wave well
enough. In our case and since the DAC of the
ESP32© is 8 bits, we can use samples only
8
2 =256 and they are sufficient for this and
another exercises of this book.
174
Electronics & MicroPython© with ESP32©: 80 complete projects
The more samples we use in generating the wave,
the more reliable the generated signal is, but
to guarantee a certain precision, in this
exercise, we do not exceed 70 samples.
•Sampling time (t): it is the expected time between
sample and sample and therefore it is the period
of the wave divided by the number of samples,
that is: t=p/ S.
The inverse of the sampling time is the sampling
frequency, that is: f '=1/t and to avoid loss
of information, it is recommended (Shannon's
Theorem) that at least the sampling frequency be
twice the frequency of the wave that we are
going to use, this is: f '≥2f.
Finally we can use an analog oscilloscope (not a
logic analyzer) traditional model or USB model to
visualize the generated waveform. This book presents
and describes results displayed on a Hantek 6022BE©
with the OpenHantek© application. See annex.
The script imports the machine, utime (more
precise than time), math and sys libraries to control
the DAC, time and sine wave generation respectively.
The DAC is activated in GPIO25 of the ESP32© and
the maximum values of frequency and number of samples
to be used are specified. The see_data() function is
defined that limits the execution of the script to
acceptable values of these frequencies and samples to
avoid unacceptable distortions in the results.
Finally, functions are defined that generate various
types of waves such as the following:
•Saw: the validity of the parameters is checked,
the step of the DAC is defined according to the
samples, the time between sample and sample
depending on the frequency and the total number
of samples and an infinite loop is created that
builds a wave in the shape of saw going through
the range (0, 255) and the previous step
previously calculated and which must be an
175
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
integer. Here we can see the wave generated from
90Hz and 10 samples compared to a reference one.
And here we see the generated wave of 98Hz and
50S which, as can be seen, is much more similar to the
reference wave.
176
Electronics & MicroPython© with ESP32©: 80 complete projects
•Triangular: it is similar to the previous saw but
defining an upward line in the range (0, 255)
and positive step and a downward line in the
range (255, 0) and negative step.
We can see the 100Hz and 50S triangular wave
compared with a reference wave generated by a hardware
wave generator from Internet, see
https://onlinetonegenerator.com/ and extracted by the
sound jack (it is not perfect but it works for us).
•Square: generates a square wave, therefore and in
principle it does not need to use samples since
the values are either 0 or 255, unless we want
to include an amplitude control.
In this case, in addition to controlling the
frequency, we can also control the Duty Cycle,
that is, the % of time (within its period p)
that the square wave is at HIGH (value 255).
With this type of control, a square wave with a
Duty Cycle of 50% would be perfectly square and
therefore have the same time at LOW as at HIGH,
p/ 2 exactly. Here the script controls the
time at 255 according to the period of the wave,
this is p=1/f multiplied by the % Duty Cycle,
177
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
with the time at 0 being the rest of the period
p of the wave. We see a square wave of 100Hz and
50% Duty Cycle, therefore it has a symmetrical
period p.
Here f=100Hz and the Duty Cycle is 10%.
And here f=100Hz and the Duty is 90%.
178
Electronics & MicroPython© with ESP32©: 80 complete projects
•Sine wave: it is similar to a perfectly square
wave but using the sin() function from the math
library. A central value of 127.5 is used so
that the wave is displayed centered, so that the
first half cycle of period p comes out positive
and the second half cycle comes out negative.
The wave generator
that we have created is
a software wave genera-
tor and therefore has
the limitations of an
algorithm: total depen-
dence on the hardware,
on multiple other pro-
cesses, on interruptions
generated by the hard-
ware, on timer updates,
on management of de-
vices, etc. and there-
fore it will not be fast
or precise but it can be
useful for basic
projects and above all
to acquire more knowl-
edge about using a DAC.
179
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In the previous figure we can see the 70Hz and
100S sine wave generated by the ESP32© with our script
and below it its frequency spectrum. If the sine wave
were “perfect” in the spectrum, only a vertical line
centered at 70Hz would be shown, but as can be seen
there is a dispersion of frequencies between
approximately 10Hz and 300Hz.
############################
# E014_DAC_WAVE.PY: Wave generator using DAC
# INPUTS: Form, frequency (Hz), samples (S)
# OUTPUTS: Generated wave on DAC by GPIO25
# IMPORTANT: Hardware imitated at 200Hz with 100S
############################
import machine
import utime # Use utime better than time
import math, sys
# Use DAC by GPIO25
dac = machine.DAC(machine.Pin(25))
# Maximum values
fre_max = 200 # Frequency
sam_max = 100 # Number of samples
# Data control until f=200Hz and m=100S
def see_data(frequency, samples):
# Frequency control
if frequency <=0 or frequency > fre_max:
print('Frequency error...')
sys.exit()
# Samples control
if samples > sam_max:
print('Samples error')
sys.exit()
# Generates saw wave of [frequency] and [samples]
def saw(frequency, samples):
see_data(frequency, samples) # Data control
print(f'Generates SAW signal of {frequency}Hz')
step = int(256 / samples)
# Wait micro seconds
w_time = int(1 / frequency / samples * 1e6)
while True:
for value in range(0, 255, step):
dac.write(value)
utime.sleep_us(w_time)
# Triangular wave of [frequency] and [samples]
def triangular(frequency, samples):
see_data(frequency, samples) # Data control
print(f'Generates TRIANGULAR of {frequency}Hz')
step = int(256/samples)
# Divide by 2 for rise and fall
w_time = int(1 / frequency / samples * 1e6 / 2)
# Wait in micro seconds
180
Electronics & MicroPython© with ESP32©: 80 complete projects
w_time = int(w_time*.85) # Calibration
while True:
# Ascent line
# minimum, maximum, increase
for value in range(0, 255, step):
dac.write(value)
utime.sleep_us(w_time)
# Descent line
# maximum, minimum, decrement
for value in range(255, 0, -step):
dac.write(value)
utime.sleep_us(w_time)
# Generates square wave of [frequency] and [dutycycle]
def square(frequency, dutycycle):
samples = 50 # Only for control
see_data(frequency, samples) # Data control
print(f'Generate SQUARE signal of {frequency}Hz
with duty cycle {dutycycle}%')
# Period in micro seconds
period_us = int(1 / frequency * 1e6)
time_high_us = int(period_us * (dutycycle /
100.0)) # High level micro seconds
while True:
dac.write(255) # High level
utime.sleep_us(time_high_us)
dac.write(0) # Low level
utime.sleep_us(period_us - time_high_us)
# Generate sinusoidal of [frequency] and [samples]
def sine(frequency, samples):
see_data(frequency, samples) # Data control
print(f'Generate SINUSOIDAL of {frequency}Hz')
step = int(256/samples)
# Divide by 2 for rise and fall
# Wait in micro seconds
w_time = int(1 / frequency / samples * 1e6 / 2)
while True:
for i in range(samples):
value_dac = int((math.sin(2 * math.pi *
i / samples) + 1) * 127.5) # Sinusoidal value
dac.write(value_dac)
utime.sleep_us(w_time)
# Examples of use of functions
# Generates a saw wave of 100Hz and 50S
saw(100,50)
# Generates a 50Hz 20S triangle wave
triangular(50,20)
# Generates a 150Hz 40S sine wave
sine(150,40)
⊝⊝⊝
181
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E060: Simple calculations.
We want to generate a 50Hz sine wave, what
period does this wave have?, if we use 20S (samples),
what is the sampling period?, what is the sampling
frequency?, is it enough to use that sampling
frequency according to Shannon's Theorem?
•E061: Wave amplitude.
Include an amplitude control for the previous
script so that by console or as a parameter of each
function the maximum amplitude of each of them is
added within the maximum range of the DAC. Add range
and error controls as necessary.
•E062: Phase and Offset.
Add phase or time shift control and a positive
or negative offset on the zero value. Keep in mind
that when adding an offset we will have to readjust
the range of the DAC so that the added values are
valid for that DAC.
•E063: Logarithmic wave.
With the square wave base, eliminate the duty
cycle control and add a logarithmic rise and fall.
Take care of the DAC margins and add an error check
with the corresponding messages to clarify them.
•E064: Ramp wave.
Based on the triangular() function, design the
ramp() function to create a wave where the upper and
lower vertices can be configured independently. Check
that if the upper and lower vertices are located at
50% or 100%, triangular or sawtooth waves are
generated respectively.
⊝⊝⊝
182
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 15:
KY-006© Passive Buzz & DAC
In this exercise we will see how we can generate
basic sounds with a DAC (Digital to Analog Converter)
and a simple and very cheap buzzer like a KY-006©.
There are multiple types and models of buzzers
on the market, but we could basically classify them
into two families:
• Passive buzzers: they are devices that reproduce
a sound coming from an external signal, therefore
they need an external square
signal (a fixed voltage
level does not work) of a
frequency between 2kHz and
5kHz. They have the
advantage that the sound
produced is variable, both
in amplitude and frequency
and dependent on the volume
and frequency of the
received signal.
• Active buzzers: they are devices that generate
their own sound only when receiving power, they
have an internal oscillator
that is activated with that
power. They have the
advantage that they are
autonomous in generating the
sound and only need to be
powered, but the acoustic
signal is always of the same
frequency, the same volume
and cannot be changed.
183
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In general, both devices use an additional
circuit to activate or deactivate them properly, which
we will see below. We'll see a module incorporating
all the control elements of a passive buzz.
When the GPIO25 goes to
LOW (reverse logic) the LED
D2 lights up and the
transistor Q1 (PNP 8550©)
conducts the current,
feeding the internal
oscillator of the buzzer
(case of active buzzer).
For the passive buzzer,
the signal to be reproduced
has to come from the
outside and reach the buzz
through the GPIO25 itself.
Transistor Q1 (PNP 8550©)
amplifies this signal (provides
sufficient energy that the GPIO25
signal does not have) and
activates the buzzer. R2 limits
the base current of transistor Q1.
R1 and R3 limit the currents that
flow through the LED and finally
D1 lights up when the circuit is
connected to the power supply.
To test the active
buzzer we could write a
MicroPython© script that
activates/deactivates the
buzzer cyclically for a
period of time to produce
a few beeps.
For the passive, we can
adapt the script that
generates a wave with the
ESP32© DAC and by applying
this signal to the passive
buzzer generate sounds or
even music.
184
Electronics & MicroPython© with ESP32©: 80 complete projects
Logically, with this hardware we will not be
able to generate quality or high-volume music, for
this we will need a *.mp3 file player and a quality
audio amplifier. We will create a MicroPython© script
in which we define a dictionary, notes{}, that con-
tains the basic musical notes (A, B, C,...), their as-
sociated frequencies and their durations and sound the
buzz with the melody list[(note_1, duration_1),...,
(note_n, duration_n)].
In the example we use the song “Happy Birthday”
but any song could be used. To visually liven up the
melody, an LED is added, connected to the GPIO26 that
accompanies each note. We import the machine libraries
to control the DAC and the LED and time to manage the
timing of the notes. We configure the DAC pins to
GPIO25 and the LED to GPIO26.
We define the dictionary notes{}, which includes
most of the notes on the staff with the frequency in
Hz of each one and the function generate() that
specifies the period of each note (inverse of the
frequency), the number of cycles (function of the
duration of each note and its period) and a for loop
that generates the square wave (HIGH=255, LOW=0) with
a duty cycle of 50%. The melody[] list is specified
with the (notes, durations) of each note to be emitted
and finally a for loop that reads each element of this
li s t an d g en e r a te s t he s q ua r e wa v e wi t h t he
corresponding frequency and duration.
############################
# E015_DAC_BUZZ.PY: Generate melody with DAC and buzz
# INPUTS: Song notes "Happy Birthday"
# OUTPUTS: Sounds on passive buzz connected to the DAC
############################
import machine
import time, utime
# Configure GPIO25 as a DAC and GPIO26 for an LED
dac = machine.DAC(machine.Pin(25))
led1 = machine.Pin(26, machine.Pin.OUT) # Green LED
# Dictionary of notes and their frequencies
notes = {
'C' : 261.63, 'D' : 293.66, 'E' : 329.63,
'F' : 349.23, 'G' : 392.00, 'A' : 440.00,
'B' : 493.88, 'C#': 277.18, 'D#': 311.13,
'E#': 369.99, 'F#': 415.30, 'G#': 466.16,
185
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
'Db': 277.18, 'Eb': 311.13, 'Fb': 349.23,
'Gb': 369.99, 'Ab': 415.30, 'Bb': 466.16,
'C5': 523.26}
# Generates square wave with each note
def generate(frequency, t_duration):
# Calculates the period in micro seconds
period = int(1e6 / frequency)
# Calculate cycles for specified duration
num_cycles = int(t_duration * 1e6 / period)
# Generates the wave for the specified duration
for _ in range(num_cycles): # Without name, use _
# Set the DAC output to a high value
dac.write(255)
# Wait half the period (Duty Cycle=50%)
utime.sleep_us(period // 2)
# Set the DAC output to a low value
dac.write(0)
# Wait for the other half of the period
utime.sleep_us(period // 2)
# "Happy Birthday" melody with note(s) duration
melody = [
('C' ,0.5),('C',0.5),('D' ,1) ,('C' ,1),
('F' ,1) ,('E',2) ,('C' ,0.5),('C' ,0.5),
('D' ,1) ,('C',1) ,('G' ,1) ,('F' ,2),
('C' ,0.5),('C',0.5),('C5',1) ,('A' ,1),
('F' ,1) ,('E',1) ,('D' ,2) ,('Bb',0.5),
('Bb',0.5),('A',1) ,('F' ,1) ,('G' ,1),
('F' ,2)]
# Adjust the playback speed (original 1.0)
speed = 2
# Generate the melody
print ('\n'*50) # Clear screen
print ('Happy Birthday...!!!')
for note, duration in melody: # Scan tuples
if note in notes: # If nota is ok
led1.on()
print (note, end=' ')
generate(notes[note], duration / speed)
led1.off()
else:
# Note is not in the dictionary, I indicate it
print(”Note doesn't exist”)
# Notes separation
time.sleep(0.1 / speed) # Adjust
⊝⊝⊝
186
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E065: Buzz songs.
Repeat the previous exercise but with another
song, for example “We are the Champions” or the
national anthem of your country or your county.
•E066: Square vs sinusoidal wave.
Change the script so that it generates the
sounds with a sinusoidal wave instead of a square wave
and compare the sound quality between both options.
•E067: Passive buzz and PWM.
Modify the script that generates square wave
music with a PWM wave generated in a GPIO by the
variable frequency ESP32©.
Try changing the duty cycle of this wave and see
ho w i t a f f ec t s th e s ou n d q ua l i ty ( s ee l a t er
exercises).
•E068: Door alarm, buzzer and LED.
The ESP32© monitors the status of the reed
switch to detect the opening of a door. If the door is
opened, the buzzer sounds to sound an alarm and the
LED lights up.
•E069: WiFi signal indicator with buzz.
The ESP32© loop scans available WiFi networks
and determines the signal quality of each network. The
buzzer sounds with a “beep” at the beginning and the
end of the scan and the LED lights up to indicate when
the WiFi network scan is being performed.
⊝⊝⊝
187
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 16:
DAC and *.WAV Files
A very interesting variant of the previous
exercise is to sound the buzzer with a *.wav (Waveform
Audio File Format) file, which is the typical
uncompressed audio file format used by CD.
Playing a *.wav file using the ESP32© DAC and a
buzzer is not a high fidelity solution nor a high
volume one since the ESP32© only has a few milli watts
of power, therefore the quality the audio obtained
with this proposal is quite poor but it helps us to
delve deeper into the operation of a DAC as well as
the development of a script that manages files hosted
on the ESP32©.
The first thing is to get a *.wav file that
meets the necessary requirements:
1. I t must be encoded in mono and linear
uncompressed PCM (for example U8 format).
2. It must have a precision of 8 bits (the ESP32©
DAC only has 8 bits).
3. It will be created with a sampling rate of
22,050Hz or less.
4. We will have to shorten the *.wav file as much
as possible (2 or 3 seconds, no more), since the
memory capacity and loading speed of the ESP32©
are quite limited.
To ob t ai n a * . wa v th a t m e et s al l th e se
requirements, we can download *.wav files for free
from the Web address:
https://sound-effects.bbcrewind.co.uk/
188
Electronics & MicroPython© with ESP32©: 80 complete projects
Here we can select the category of the *.wav
file, filter it by theme and duration and download it
for free.
To convert the *.wav file downloaded previously,
also for free and online, we can use tools such as:
https://audio.online-convert.com/es/convertir-a-wav
On this website we upload the *.wav file and use
the options: resolution: [8bit], audio frequency:
[22050Hz], audio channels: [Mono], trim audio for
example to: [00:00:03] seconds , normalize audio:
[yes/none] and change PCM format to: [U8] (unsigned 8-
bit, from 0 to 254).
Once the *.wav is ob-
tained, we have to store
it in the ESP32© using,
for example, the [Upload
to/] option from the Thon-
ny© [files] menu (see sub-
sequent figure).
The MicroPython© script
includes a summary of the
above specifications and
uses the machine and utime
libraries.
The GPIO25 is defined as
the output port of the
analog signal of the DAC
and that will attack the
buzz (it could attack an
amplifier or speakers).
The *.wav file to be
played is specified and
must be in the ESP32©
memory and the waiting
time, in micro seconds,
between recordings in the
DAC depending on the
sampling frequency.
189
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The s cri pt o pens the
*. w a v f i l e f o r b i n a r y
re a d i n g w i t h a b u f f e r
large enough to be able to
load sufficient informa-
tion in a single read and
w i t h t h e with statement
that guarantees that the
resources are managed ap-
propriately, closing the
file after finishing the
script even if some excep-
tions occur.
S e q u e n t i a l l y , t h e for
loop writes the previous
reading to the output of
the DAC, waiting the
previously defined time
between each writing.
Remember to upload the *.wav file to the ESP32©
memory, using the Thonny© [Upload to/] option.
############################
# E016_DAC_WAV: Play *.wav files
# INPUTS: *.wav PCM, mono, 8bit, 22.050Hz
# OUTPUTS: Audio DAC in GPIO25
# IMPORTANT: Wav in PCM, 8bit, U8 (without sign)
############################
''' Example of instructions:
Get *.wav from:
https://sound-effects.bbcrewind.co.uk/
Change parameters with:
https://audio.online-convert.com/es/convertir-a-wav
Recommended parameters:
Resolution: 8bits (ESP32© DAC only 8bits)
Frequency: 22050Hz (enough precision)
Channels: Mono
Trim audio to: 00:00:0x-00:00:0y (ver needs)
Normalize audio: Yes or None
PCM format: U8 (0 to 254 data)
190
Electronics & MicroPython© with ESP32©: 80 complete projects
Upload new *.wav to ESP32© from Thonny©
'''
from machine import DAC, Pin # DAC control
import utime # Time control
# Configuration
pin = 25 # ESP32© DAC
dac = DAC(Pin(pin))
wav = 'campana.wav' # Wav file in ESP32©
# Wait between readings in micro seconds
tmp = int(1e6/22050 – 7)
# Open file for binary reading
with open(wav, 'rb') as file: # Wav file for read
data = file.read(60000) # Buffer bytes
for x in data:
dac.write(x) # DAC write
utime.sleep_us(tmp)
file.close()
Additionally you can add the necessary error
controls to the script to avoid file not found errors,
playback error, DAC error, library import error, index
out of range, etc. Record the errors in a file and at
the end of the script read that file and displays its
contents on the screen.
⊝⊝⊝
191
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E070: LED with push button and passive buzz.
Develop a script in MicroPython© that uses a
button connected to the ESP32©. Implement polling
logic to detect the state of the button and control
the on and off of an LED connected to the
corresponding GPIO.
Add a passive buzz to emit a short sound each
time the button is pressed, using the DAC to generate
the necessary signal.
•E071: Button with IRQ, ESP32© data and RGB LED.
Extend the previous script to include an
additional button and use an interrupt control by
changing the pin associated with the button.
When one of the buttons is pressed, display the
current temperature of the ESP32© and its MAC address
on the console. In addition, incorporate a RGB LED or
another LED that changes color each time the button is
activated, which must emit a clicking sound.
•E072: WiFi scanning with RGB LED and passive buzz.
Modify the script to incorporate a WiFi network
scan. By pressing a button, the ESP32© should scan for
available WiFi networks and display the results on the
console.
Use a RGB LED or another LED to indicate the
scan status and the passive buzz to emit a short sound
at the end of each scan.
•E073: ADC and DAC with button and passive buzz.
Integrate a button into the ESP32© and use the
ADC to read its status. When the push button is
pressed, adjust the output of the DAC to vary the
intensity of an LED connected to the corresponding
GPIO with the necessary resistance.
192
Electronics & MicroPython© with ESP32©: 80 complete projects
Finally, incorporate a passive buzz to emit a
short sound every time the button is pressed.
•E074: WiFi and push button with passive buzz.
Modify the script to allow activating and
deactivating the WiFi connection using a button.
Pressing the button activates the WiFi connection, and
releasing it deactivates it.
Use a passive buzz to emit a short sound when
activating or deactivating the WiFi connection. In
addition, use LED as indicators to show the status of
the WiFi connection and the button.
•E075: Wav and DAC.
Request through the console the name of a *.wav
file (which will be previously loaded in the ESP32©
memory), check its existence and play it cyclically
every time a button is pressed.
The button management must be carried out by IRQ
and the corresponding messages must appear on the
console. The script will also contain the
corresponding error control. Use the uos library and
the uos.listdir() function to check the existence of
the file.
⊝⊝⊝
193
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 17:
DHT11/22© Double Sensor:
Humidity and Temperature
In this example we will see a
widely used sensor, a double
sensor that is capable of
measuring humidity and ambient
temperature and giving us a good
and stable reading in digital
format.
This is the DHT11© sensor that
has a measurement range of 20-80%
relative humidity, 0-50ºC
temperature, a precision of
±5 % humidity measurement and
±2ºC temperature measurement
with a maximum reading frequency
of 1 scan per second. If we need
greater precision, there is the DHT22© but its
sampling frequency is one reading every 2 seconds.
The DHT11© only uses 3 pins: VCC, GND and DATA
or SIG to communicate information between it and the
ESP32© and only requires a 10k Ω pull-up between
DATA and VCC (+3.3V). As in other exercises, we can
use the DHT11© and the necessary components separately
or a module that already
incorporates them all.
The communication process
between the DHT11© or DHT22© and
the ESP32© is a proprietary and
specific protocol that consists of
the following:
194
Electronics & MicroPython© with ESP32©: 80 complete projects
1. The ESP32© sends a start signal to the DATA/SIG
pin, through GPIO32 in our case.
2. The DHT11© responds to the start signal.
3. The DHT11© sends 40bits (humidity: 8bits integer
part, 8bits decimal part, temperature: 8bits
integer part, 8bits decimal part, 8control
bits).
4. The MicroPython© script with the dht library and
the DHT11 or DHT22 class, decodes the 40 bits.
The script presents temperature data, in degrees
Celsius (or Fahrenheit), and ambient humidity measured
in relative % using the DHT11© or DHT22© sensor and
the dht library. The machine libraries are also used
to control the alarm LED and time to control time. In
Wokwi© we can select the environment data to read.
GPIO32 is defined for communication with the
DHT11© through the DATA pin and GPIO26/27 for LED.
195
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The DHT11 class is used and two maximum limits
of temperature and humidity are defined so that, if
any of these maximum limits are exceeded, an alarm is
initiated by turning on a red LED and if everything is
normal, the green LED turns on.
############################
# E017_DHT11.PY: Read temperature humidity DHT11/22
# INPUTS: Maximums and data of DHT11/22
# OUTPUTS: Temperature (ºC), humidity (%), LED alarm
############################
import dht # Sensor DHT11/22 manager
from machine import Pin # Access to GPIO
import time # Time management
# DATA connection pin of DHT11/22©
pin_dht = Pin(32, Pin.IN) # Pin adjust
# Configure the alarm LED pins
led1=Pin(26, Pin.OUT) # Green LED
led2=Pin(27, Pin.OUT) # Red LED
led1.off() # Green off
led2.off() # Red off
# Create a DHT11 or DHT22 object
sensor = dht.DHT11(pin_dht)# DHT22 if is necessary
t_max = 22 # Maximum Temperature
h_max = 65 # Maximum Humidity
print('TEMPERATURE AND HUMIDITY DHT11/22 SENSOR')
while True: # Data read loop
try:
# Read sensor data
sensor.measure()
# Read temperature
temperature = sensor.temperature()
# Read humidity
humidity = sensor.humidity()
# there's alarm?
if temperature >= t_max or humidity >= h_max:
led1.off() # Red LED on
196
Electronics & MicroPython© with ESP32©: 80 complete projects
led2.on()
else:
led1.on() # Green LED on
led2.off()
# Print the results in format
print("Temperature: {}C, Humidity: {}
%".format(temperature, humidity))
except Exception as e:
print("Error reading sensor:", e)
# Wait next reading
# DHT11/22© need 1s/2s between reading
time.sleep(2)
Below we see, in the LA1010© logic analyzer, the
signal from the DATA pin (or SIG depending on the
module) of the DHT11© and an 8kHz square reference
signal generated by PWM.
As we can see, a list of bits is generated with
the following order: ACK, 0x4800 (humidity at 72%),
0x1803 (temperature 24.3ºC) and CHEKSUM 0x63.
⊝⊝⊝
197
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E076: Temperature control with DAC and button.
Create a script in MicroPython© that uses a push
button connected to the ESP32©. By pressing the
aforementioned button, the ESP32© must read the
current temperature of the DHT11© or DHT22© sensors
and adjust the output of the DAC converter to control
a heating component, such as a heating resistance,
simulated with an LED.
If th e te m pe ra t ur e ex ce e ds a Fa h re nh e it
temperature predefined limit, introduced by console,
the DAC will increase the heating power and if it is
within the limits, the power will decrease. Use
another LED, for example red, as an alarm indicator
when limits are exceeded. Change the red LED for a
PWM-controlled passive buzzer (see later exercises).
•E077: Remote monitoring with ADC.
Extend your script to include the reading of a
light sensor connected to the ADC of the ESP32©, it
can be simulated with a potentiometer.
Use the ADC to measure the light intensity and
display it along with the temperature and humidity of
the DHT11© or DHT22©. Add a visual indicator with a
flashing green LED to indicate successful transmission
of all data.
•E078: Limit update with push button.
Integrate a push button to the ESP32© and use
this push button to allow the temperature and humidity
limits to be updated.
By pressing the button, the ESP32© must read and
display new predefined limits and adjust the
corresponding alarm logic. Use the red LED to indicate
if the new limits have been exceeded and the green LED
to indicate normal operation.
198
Electronics & MicroPython© with ESP32©: 80 complete projects
•E079: Response to sudden changes with IRQ and ADC.
Implement an interruption due to a state change
on a pin of your ESP32©, activated by a motion sensor,
for example, or simulated by a button.
When the interrupt is triggered, the ESP32©
should immediately read the temperature and humidity
of the DHT11©, as well as the reading from the
simulated or non-simulated light sensor. Properly
format all data.
Use the ADC to detect sudden changes in ambient
light and respond by adjusting limits and visual alarm
with red and green LED. Please note that the ESP32©
has two independent DAC that could be used for two
sensors.
•E080: Alarm configuration with IRQ and polling.
Extend the previous scripts to add an audible
alarm using an active buzzer connected to a GPIO, as
if it were an LED, or an active buzzer connected to a
DAC output or a PWM output (see following exercises).
Implement a state change interrupt on a pin
activated by an external switch or pushbutton. If the
switch is activated, the ESP32© should sound the alarm
and display the current temperature and humidity
captured from the DHT11©.
The temperature will be displayed in degrees
Fahrenheit, Celsius and Kelvin, see the conversion
method of these units on the Internet.
Use a polling system to reset the alarm and turn
off the buzzer after a predefined time without
activation of the switch. How can there be limits
exceeded, etc., check the corresponding errors and add
all the corresponding messages.
⊝⊝⊝
199
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 18:
DS18B20© Precision
Temperature Sensor
In this exercise we will
see a complete, integrated,
precision and very
interesting temperature
sensor such as the DS18B20©
or equivalent.
In this case we can use
the chip loose or also
integrated within a module,
which is more convenient by
integrating LED that indicate
power and data reading on the
chip.
With the DS18B20© we are not at all just dealing
with another thermistor in which its resistance varies
with temperature, its variation is read with an ADC
converter and this approximate value is transformed
into a temperature reading.
On the contrary, the DS18B20© device contains a
complete device that includes all the necessary
circuits: the temperature sensor
itself, a 12-bit ADC converter, a
calibration and reading
compensation system, a 1-Wire© type
communications system single-pin
bidirectional (DQ pin) supported by
the ESP32© natively, a system with
anti-interference control, signal
stabilization, etc.
200
Electronics & MicroPython© with ESP32©: 80 complete projects
The measurement range of the DS18B20© goes from
-55ºC to +125ºC with a precision of ±0.5ºC and all
this in a very small package, low cost, easy to obtain
and with a very simple control circuit.
The 1-Wire© system only
requires the use of a
single pin, in our case the
GPIO25, which is one of the
several pins of the ESP32©
that natively supports the
1-Wire© hardware protocol.
In the module that
contains the DS18B20©,
diode D1 indicates when
there is power and D2
lights up every time there
is data reading in DQ.
R1 and R3 limit the
current of LED D1 and D2 respectively and R2 acts as a
pull-up to stabilize the data in DQ and in the GPIO25.
201
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The fundamental characteristics of the DS18B20©
precision temperature sensor are:
• 1-Wire© Interface: This sensor uses a single
cable for data transmission with the possibility
of power through that cable.
• Communication: Data is transmitted using the
low-speed 1-Wire© protocol.
• Precision: H i g h p r e c i s i o n i n t e m p e r a t u r e
measurement with a programmable resolution of 9
to 12 bits (default), that is, +/-0.06ºC.
• Temperature range: It has a wide measurement
range, from -55°C to +125°C.
• Unique Identification: Each sensor has a unique
fixed 64-bit address defined by the
manufacturer, allowing easy identification on a
1-Wire© bus. For this sensor the first byte is
usually hexadecimal 0x28.
• Asynchronous Conversion: Initiates temperature
conversion serially asynchronously and allows
the ESP32© to perform other tasks while the data
conversion is completed.
• Temperature alarm: Allows us to configure upper
and lower temperature limits to activate alarms,
although this function can also be programmed
more conveniently in the MicroPython© script on
the ESP32©.
• Low power consumption: Extremely low consumption
during normal operations and idle.
• Very common use: It is a sensor widely used in
applications that require precise temperature
measurement. It is also very cheap and easy to
get in the market.
• Easy integration: Thanks to being available in
TO-92© type encapsulations and inside metal
202
Electronics & MicroPython© with ESP32©: 80 complete projects
probes, it is very easy to integrate into
various projects.
• Platform Compatibility: There are various
libraries for various micro controllers such as
Arduino©, Raspberry©, ESP32©, etc.
Key features of the 1-Wire© protocol include the
following:
•Cabling: All 1-Wire© devices share a single data
cable, which acts as a bus and can be used as a
power source. Due to consumption limitations, it
is possible to connect up to 8 devices of this
type to the same 1-Wire© line, which allows
controlling large areas in buildings, machinery,
process monitoring, etc.
•Power: 1-Wire© devices can be powered through the
same data cable but must have a common ground
connection.
•Communication: It is done through voltage pulses
in the data cable. Data bits are transmitted by
pulse duration. A short pulse represents a '0'
bit and a long pulse represents a '1' bit. The
transfer speed is low, in the kb/s range.
Thi s c om mu ni ca ti on pr oto co l is l ik e t he
following:
1. Start: Before starting communication, the 1-
Wire© bus is placed in a standby state (HIGH)
for a certain time, the DS1820© indicates its
presence by generating a short pulse (LOW) on
the bus and then leaving it in standby, the
ESP32© detects this pulse and knows that at
least one 1-Wire© device is present.
2. Read and Write: Each data transmission begins
with a start transmission command. The ESP32©
issues read or write commands and receives
responses from the DS18B20©. While reading, 1-
Wire© devices can send data over the same cable.
203
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Communication is completed with a reset or
termination pulse. The transmitted data is
controlled by a cyclic redundancy code or CRC.
3. Unique Addresses: Each 1-Wire© device has a
unique 64-bit (8-byte) address, configured by
the manufacturer and used to specifically
address that device.
The script imports the onewire libraries for the
management of the 1-Wire© bus, the ds18x20 for the
identification of the DS18B20© sensor and the
conversion of data to temperatures, making the script
much simpler. The machine libraries are also used to
c o n t r o l t h e G P I O a n d time to control reading
intervals. Finally, the GPIO25 is specified as
input/output from the ESP32© and the bus and sensor
are configured appropriately.
T h e search() function is defined, which scans
devices connected to the 1-Wire© bus, presenting those
found and their 8-byte hexadecimal address, all of
which is properly formatted.
T h e read_temperature() function reads the
temperature of the found sensor and presents it in
degrees Celsius and degrees Fahrenheit.
Likewise, the reading time interval is defined
and the necessary controls for errors and script
completion are carried out.
############################
# E018_DS18B20.PY: Temperature read with DS18B20©
# INPUTS: Data pin in GPIO25
# OUTPUTS: Hex address and temperature (ºC & ºF)
############################
import onewire # 1-Wire© bus manager
import ds18x20 # DS18B20© sensor manager
from machine import Pin # GPIO control
import time # Time management
# Sensor pin configuration
sensor_pin = Pin(25) # Change if is necessary
204
Electronics & MicroPython© with ESP32©: 80 complete projects
# Bus an device configuration
bus = onewire.OneWire(sensor_pin)
dis = ds18x20.DS18X20(bus)
dire= [] # DS18B20© address variable
# 1-Wire© bus scan
def search():
global dire # DS18B20© address
print('Sensor scanning...')
dire = dis.scan() # Get hex address
if dire: # See if it detects a DS18B20©
# Format the address in hexadecimal
dire_hex = ':'.join(['{:02X}'.format(b)
for b in dire[0]])
print('DS18B20© sensor found in...')
print("Hex address:", dire_hex)
else:
print("No DS18B20© sensor detected")
# Read the device found in 1-Wire© bus
def read_temperature():
dis.convert_temp() # Temperature conversion
# Wait until the conversion is done
while dis.read_temp(dire[0]) == 85.0:
pass
# Read temperature and convert it to
# Celsius and Fahrenheit degrees
temp_celsius = dis.read_temp(dire[0])
temp_fahrenheit = (temp_celsius * 9.0/5.0)+32.0
# Show the temperature
print("Temperature: {:.2f}°C or {:.2f}°F"
.format(temp_celsius, temp_fahrenheit))
# Reading cycle every x seconds
interval = 2 # Change if is necessary
print('DS18B20© TEMPERATURE SENSOR')
# Infinite loop stops with <CTR>+<C>
search() # 1-Wire© search function
205
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
try:
while True: # Read loop
read_temperature()
time.sleep(interval)
except KeyboardInterrupt:
print('DS18B20© read completed')
except:
print('Sensor error')
If we connect a logic analyzer, for example the
LA1010©, to the 1-Wire© bus we can see the serial
signal of the bus with the different communication
blocks with the DS18B20© sensor:
• System reset.
• Presence of 1-Wire© device.
• Address search.
• Family code (0x28).
• Device code.
• CRC and data.
The diagram also shows a 13kHz PWM signal used
as a reference.
⊝⊝⊝
206
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E081: LED control according to temperature.
Connect an LED and a DS18B20© sensor to measure
the ambient temperature, program the system o turn the
LED on/off based on thresholds. Use a temperature
range to avoid LED fluctuations.
•E082: DS18B20© vs DHT11©.
Connect the DS18B20© and DHT11© temperature
sensors. Add an LED that will light up when the
temperature difference exceeds a threshold entered by
the console. Include all the controls for reading
errors and absence of sensors provided.
•E083: DAC control with temperature.
Connect a digital-to-analog converter (DAC) to
an output pin of the ESP32©, use a DS18B20© sensor to
measure the ambient temperature, and program the
ESP32© to vary the analog output of the DAC based on
the temperature measured by the DS18B20© inside of
pre-established margins. Visualize the variations of
the DAC with an LED.
•E084: Display temperature with push button.
Connect a button and a DS18B20©, configure an
IRQ for the button. When the button is pressed, read
the temperature with the DS18B20© and display it on
the console. If the temperature exceeds a threshold,
the red LED lights up and if the button is pressed it
turns off until a new alarm situation occurs.
•E085: LED, DAC and temperature.
Connect a DS18B20© sensor and, on the other
hand, a red LED to the DAC output of the ESP32©.
Program the ESP32© to adjust the intensity of the LED
proportionally to the temperature measured by the
DS18B20© within certain limits.
⊝⊝⊝
207
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 19:
KY-106© RGB LED on/off
In this exercise we will
see a very interesting
component that is widely used
in real life, especially on
modern TV. It is an RGB type
LED, which includes in a
single package three very
close LED, with the basic
colors: red, green and blue,
so that with a combination of
these colors, the human eye
will interpret it as a unique
and different color.
To achieve this effect
we must light the 3 colors but
at a different intensity, so
that the sum of the
luminosities of these three
basic colors generates,
theoretically, any of the rest
3
of the colors (up to 100 =1
million colors).
There are two types of RGB LED:
• T h e common cathode ones (negative terminal),
where the 3 cathodes of the 3 LED are connected
to GND therefore each individual LED is
activated with HIGH.
• Those with a common anode (positive terminal),
where the 3 anodes of the 3 LED are connected to
the power supply and activated with LOW.
208
Electronics & MicroPython© with ESP32©: 80 complete projects
In this exercise we have used
a common anode RGB LED mounted on an
AZDelivery© or similar brand module,
but we could use a common cathode
one, the KingBright© or similar.
When using the common anode RGB LED
we will have to connect that common
terminal to +3.3V and each LED will
be activated to LOW from a GPIO.
If we use an individual RGB LED, see that the
longest pin of the RGB LED is that of the common
element (common cathode or common anode) depending on
the RGB LED model we are using.
In this type of RGB, the red LED (R pin) usually
requires less voltage than the other two to achieve
the same luminosity, which is why it is advisable to
modify the resistor that powers it from the GPIO of
the ESP32©. In the following exercise we will see how
we can compensate for this situation in software
without resorting to resistors of different values,
which makes this exercise more comfortable to
implement.
209
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
We will do the calculations on how to adjust the
luminosity of each LED if we want to have a balanced
system to generate the desired colors in a more
reliable way, but it is not essential.
To adjust the luminosity of the LED in “fine”
mode, we must take several factors into account:
• Luminosity of each LED: although the power
consumed by an RGB LED was the same, its
luminous power is not. Typical values, for a
current of 20mA are: R=1,200mcd (mini candelas),
G=1,700mcd and B=800mcd.
• Voltage drop in each LED: although the current
flowing through an RGB LED is the same, its
voltage drop at its ends is not. Typical value
are: R=1.8V, G=2.0V and B=3.0V, therefore the
necessary resistors are:
V −V c
R= where V is the voltage that powers
i
them, that is +3.3V, V c the drop in the LED
a n d i the current that must circulate through
the LED diode and that we will adjust to 14mA to
protect the GPIO of the ESP32©.
So, summing up:
Luminosity
LED i(mA) V c (V ) V c (V ) Rc (Ω)
(mcd)
R 1,200 14 2.0 93 100
G 1,700 14 2.2 79 82
B 800 14 3.0 21 22
In the table we have added the equivalence of
the calculated theoretical resistance to the available
commercial resistance or Rc (Ω).
In the MicroPython© script we will activate a
common anode RGB LED connected to the GPIO R=GPIO32,
G=GPIO12 and B=GPIO14, (red, green and blue
respectively).
210
Electronics & MicroPython© with ESP32©: 80 complete projects
We will do on/off on each element to create
different colors that we can also simulate, adapting
the values, online on an RGB calculation website such
as follows:
www.w3schools.com/colors/colors_rgb.asp
In the example of this exercise, the LED are
activated by LOW because the RGB is a common anode
connected to +3.3V, therefore we must adapt the values
provided by the Web to this situation.
We use the machine and time libraries to control
the GPIO of the LED and the on and off times. We
define the pins connected to each LED as outputs and
create the combinations[] list with some basic color
examples. We also define the transition time between
combinations.
Finally we will show each possible combination
in an infinite loop that can be ended with <CTRL>+<C>
that stops it and turns off the LED.
############################
# E019_LEDRGB_ONOFF.PY: Activate RGB LED on/off
# INPUTS: Activation sequence and time
# OUTPUTS: GPIO32 red, GPIO12 green and GPIO14 blue
# IMPORTANT: RGB used is common anode and
# therefor the LED turn to LOW
############################
'''
Color RGB calculus Web
https://www.w3schools.com/colors/colors_rgb.asp
Adapt values to reverse logic (activation to LOW)
'''
import machine # Pin control
import time # Time management
# Set GPIO pins on output to 0 (white color)
pin_R = machine.Pin(32, machine.Pin.OUT, value=0)
pin_G = machine.Pin(12, machine.Pin.OUT, value=0)
pin_B = machine.Pin(14, machine.Pin.OUT, value=0)
211
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# List of combinations with their names
combinations = [
((1, 1, 1), "Off"), # HIGH is off
((0, 1, 1), "Red"),
((1, 0, 1), "Green"),
((1, 1, 0), "Blue"),
((0, 0, 1), "Yellow"),
((0, 1, 0), "Purple"),
((1, 0, 0), "Turquoise"),
((0, 0, 0), "White"),] # LOW is on
# Activation time in seconds
t = 2
# Set the status of the LED and
# show color name
def set_leds(state):
pin_R.value(state[0])
pin_G.value(state[1])
pin_B.value(state[2])
return state[0], state[1], state[2]
# Main loop of the script
print ('\n'*50)
print ('LED RGB management')
time.sleep(t) # Wait to see initial white color
try:
while True:
for combine, color_name in combinations:
current_state = set_leds(combine)
# Fit fields in two columns
print(f"Color: {color_name:<8}
State: {current_state}")
time.sleep(t)
except KeyboardInterrupt:
print ('Program finished, RGB LED off')
set_leds((1,1,1))
⊝⊝⊝
212
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E086: RGB LED control with button and polling.
Develop a script in MicroPython© that manages
the on and off of an RGB LED connected to the ESP32©.
Use a button and a polling system to detect the state
of the button and control the RGB LED.
When we press the button, the RGB LED should
turn on, and when we release it, each of its
components should turn off. As always include messages
and error control.
•E087: Button with IRQ, RGB LED and ESP32© data.
Extend the previous script to include an
additional second pushbutton and use a state change
interrupt on the pin associated with the pushbutton.
When one of the buttons is pressed, display the
current internal temperature of the ESP32© and its MAC
address on the console. Additionally, use this
interrupt to toggle some component of the RGB LED on
and off.
•E088: WiFi scanning, RGB LED and push button.
Modify the previous script to incorporate a WiFi
network scan. By pressing a button, the ESP32© should
scan for available WiFi networks and display the
results on the console in alphabetical order. Use an
RGB LED to indicate scanning status, changing the RGB
color while scanning and flashing sequentially when
scanning stops.
•E089: ADC and DAC with push button for RGB LED.
Integrate a button into the ESP32© and use the
ADC (Analog to Digital Converter) to read its status.
When the button is pressed, adjust the output of the
DAC (Digital to Analog Converter) to vary the
intensity of the red and green components of the RGB
LED connected to the corresponding GPIO (remember that
the ESP32© has 2 DAC).
213
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Experiment with different DAC output levels to
control the brightness of the RGB LED colors. It is
not necessary that we fine-tune the value of the
resistors.
If we dare, we can create the calibrated()
function that is executed at the beginning of the
script to calibrate, by software, the luminosity of
each component of the RGB LED used and using the PWM
process as shown in the following project.
•E090: WiFi, push button, RGB LED and passive buzz.
Modify the script to allow activating and
deactivating the WiFi connection using a button.
Pressing the button activates the WiFi connection, and
releasing it deactivates it.
Use a passive buzz to emit a brief sound, of
pre-established frequency and duration, when
activating or deactivating the WiFi connection.
Finally, control the on and off of the RGB LED
according to the status of the WiFi connection and add
all the error messages and controls that are necessary
to describe most situations.
⊝⊝⊝
214
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 20:
RGB LED and PWM
In the previous exercise, the LED either turned
on at 100% or turned off at 0%, but in this project
a n d with the same hardware, but controlled by
software, we can change the brightness of an LED.
The output of the GPIO is a digital signal, that
is: it is either at 1 (HIGH=+3.3V) or 0 (LOW=GND=0V),
th e r ef o r e it s e em s
completely impossible
to achieve intermedi-
ate values between 0
and 1.
There is a
technique, called
Pulse Width Modulation
or PWM, that allows us
to solve it in a
simple way.
How is it possible
to carry out this
modulation?, by
generating a chain of
pulses, with amplitude
fixed at +3.3V,
frequency f, period P
but with variable
width in the HIGH
state and therefore
also variable width in
the LOW state (HIGH
and LOW complete the P
period).
215
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
If there are many +3.3V pulses per second, the
LED will light brighter than if there are few +3.3V
pulses. The area under this pulse at HIGH is
equivalent to the energy we transmit to the LED and
therefore its luminosity.
In the attached figure we see a square signal of
period P. When, within this period P, the pulse is ON
for longer (the width T of the pulse is modulated),
the LED will be on for longer, for example 90% of the
time.
As the pulse stays ON for less time, for
example, going from 90% to 70%, 50%, 30% or 10%, the
luminosity of the LED is reduced. This % is called
Duty Cycle or useful work cycle and is expressed as:
T
D= ∗100 % or also: D=T∗f∗100 %
P
Where P, P=1/ f , is the total duration of the
pulse and T is the time in which the pulse is ON.
In this way we can assimilate that for a signal
of period P and for example a voltage V=+3.3V, if we
apply a Duty Cycle of 90%, we would have a voltage
equivalent to: V 90% =2.97V or with one of 10% we
would have a voltage equivalent to: V 10% =0.33V.
PWM signals have many uses in the real world and
therefore knowing how they are configured, how they
are generated and how they are managed is very
important.
We could highlight the main uses:
• Speed control: The PWM signal is used to control
the speed of electric motors, such as DC motors
and servomotors (they include a special feedback
circuit with angle and position control by PWM).
By varying the pulse width, both the motor speed
and the rotation angle can be adjusted, all
precisely, simply and efficiently.
216
Electronics & MicroPython© with ESP32©: 80 complete projects
• Brightness control: This is what we are applying
in this exercise, where the PWM signal is used
to control the light intensity.
By modulating the pulse width, the brightness of
the LED lights can be varied smoothly and
without noticeable flickering.
• Power Control: The PWM signal is used in power
control circuits to regulate the amount of power
delivered to a device.
This applies to switching power supplies (those
in a computer or mobile phone charger), voltage
inverters (including UPS) and voltage
regulators, where the PWM signal controls the
switching of semiconductor devices to adjust the
output power.
• Energy efficiency: Compared with analog signal
control methods, pulse width modulation is more
energy efficient. This is because power is
transferred in short bursts (pulses), which
reduces heat dissipation losses in switching
devices by eliminating control resistors.
• Ease of implementation: The generation and
control of PWM signals is relatively simple and
can be implemented with micro controllers such
as the ESP32© and specialized chips.
This makes pulse width modulation a versatile
and accessible technique for a wide variety of
control applications.
We can, for example, simulate with the iCircuit©
application (it is detailed a little more in the
annexes of this book) a circuit generating a pulse
with modulation or PWM signal with variable Duty Cycle
(by modifying the potentiometer of 100k Ω ), with an
integrated circuit type NE555© timer, powered at +5V
and some additional components as shown later.
With de ESP32© micro controller we can use
better the TLC555© or 7555© (with CMOS technology),
equivalent to the NE555© and which can be powered at
+3.3V and thus not damage our device.
217
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Duty Cycle to 10% Duty Cycle to 90%
In this exercise we will see that the ESP32©
itself has the possibility of generating PWM signals
through hardware. Depending on the ESP32© model we
will have up to 16 channels or GPIO with the
possibility of generating PWM signals of up to several
hundred kHz more precise than with the DAC.
In the software that controls this option we
have to define the frequency of the square signal,
that is, the duration of the pulse P (in seconds) and
the Duty Cycle (in %) or the time that the signal is
ON (in seconds).
With this same software and this same hardware
we could control a small direct current motor
(operating at +3.3V and 15mA maximum current), which
will rotate at more or less revolutions depending on
the Duty Cycle applied, or a servo motor, that will
rotate more or less angle (for example to operate a
rudder of a boat, car, airplane or toy robot) and
since we can control the frequency of the pulses, we
can play to make a musical note generator, a generator
of waves, etc.
218
Electronics & MicroPython© with ESP32©: 80 complete projects
To practice with this device, we use the circuit
from the previous exercise of the 3 LED RGB on/off as
is, fine-tuning or not with precise resistors the
luminosity of each LED.
It is important to take into account the type of
logic used in our circuit (direct logic or reverse
logic) to use Duty Cycle from 0% to 100% or from 100%
to 0% and this will be a function of the use of a
common cathode RGB LED (direct logic) or common anode
one (reverse logic).
By default, the PWM of the ESP32© is 10bit reso-
lution, which means that the duty cycle can be varied
10
i n 2 =1,024 steps, that is, steps of
1 /1,024∗100 ≈ 0.1 % . The frequency f of the PWM sig-
nal can be adjusted within a wide range in the range
from Hz to MHz and if not nothing is specified, the
frequency used by the PWM of the ESP32© is 1kHz.
For this exercise we will do 2 MicroPython©
scripts: the first, simple and that only manages the
red LED and the second, more complex with the 3 RGB
LED to be able to generate multiple colors.
In the first script we import the machine
libraries for LED and PWM control and time for the
time management. We define the GPIO32 to connect the R
pin of the RGB and configure it as an output.
We create the PWM signal in the GPIO32 and
assign it an initial duty cycle of 1,023, that is,
100% at HIGH and since the RGB LED is a common anode,
this causes it to start completely off.
We define the limits and the step of decrease
and increase of the duty cycle and indicate in which
position the first point of the loop begins. We create
an infinite loop where the duty is decreased or
increased between the established limits.
Finally we use the try:... except:...
finally:... control to stop the keyboard program and
in any case turn off the red LED.
219
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Next, to visualize how a variable duty cycle PWM
signal works, we can see the image of the LA1010©
logic analyzer where we can observe 4 PWM signals of
10kHz each and that are generated by the ESP32© with
duty cycle of 20% and 40 % and by the LA1010© itself
with duty cycle of 60% and 80%, as we see the time
that the signal is at HIGH varies although the signal
frequency always remains fixed at a value of 10kHz.
############################
# E020_LEDRGB_PWM.PY: Modulate RGB LED by PWM
# INPUTS: PWM start, end, dutycycle step values
# OUTPUTS: Intensity modulation in GPIO32 red LED
# IMPORTANT: ESP32© PWM is 10bit and the LED
# RGB is common anode so 100% duty is LED off
############################
import machine # PWM control
import time # Time management
# Configure the GPIO pins for the red RGB LED
pin_R = machine.Pin(32, machine.Pin.OUT) # Red only
220
Electronics & MicroPython© with ESP32©: 80 complete projects
# Set the PWM to red LED
pwm_R = machine.PWM(pin_R, duty=1023) # 1,023 off
# Initial cycle value for red LED (0-1,023)
start = 1023 # Higher duty lower illumination
end = 100 # Not the entire range of 0-1,023
step = 10 # Change if is necessary
duty_cycle_R = start # Start of variable
# Direction of change in duty cycle
# (step to increase, -step to decrease)
direction_R = -step # Start by increasing luminosity
# Main loop of the script
try:
while True:
# PWM duty cycle for red LED
pwm_R.duty(duty_cycle_R)
# Print the current value of the cycle
print(f"Duty cycle (Red): {duty_cycle_R}")
# Increase/decrease duty cycle
duty_cycle_R += direction_R
# Change address at the ends (0 or 1,023)
if duty_cycle_R <= end
or duty_cycle_R >= start:
direction_R = -direction_R
# Wait a short period of time
time.sleep(0.01)
except KeyboardInterrupt:
# Stops looping when <CTRL>+<C> is pressed
print("\nProgram completed.")
finally:
# Stops the PWM and turns off the LED when finished
pwm_R.deinit()
pin_R.init(mode=machine.Pin.OUT)
pin_R.value(1) # Red LED off
In this second script we use the 3 RGB LED
managed by pulse with modulation, PWM signals, that
allow, for a resolution by 10 bit PWM signal, to
10 3
generate (2 ) >1 Billion of colors, therefore we can
see that the use of PWM is much more powerful than the
on process on/off seen above.
In this script we define the red pin in GPIO32,
green in GPIO12 and blue in GPIO14 and their
corresponding PWM signals where we do not assign
frequency and therefore, by default, it is 1kHz in
each component. We assign the initial duty of 1,023 to
each PWM signal so that it turns off.
221
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
We specify the dictionary common_colors{} that
contains a series of predefined colors (dictionary
keys) through their name and components R, G, B
(dictionary data). Observe that the white, all the LED
on, is (0,0,0) and the “black”, all the LED off, is
(1023,1023,1023), why?, I leave the answer to the
reader.
T h e show_colors() function is defined that
clears the screen, presents a header for some columns
where the name of the color and its RGB components are
seen. This function, assigning the corresponding duty
cycle, generates the corresponding colors.
F i n a l l y a try:...except:...finally:... l o o p
invokes the previous function, controls the keyboard
interrupt, stops the PWM signals and turns off the RGB
LED.
############################
# E020_LEDSRGB_PWM.PY: Modulates RGB luminosity by PWM
# INPUTS: Values of each color in a dictionary
# OUTPUTS: Colors generated in the RGB LED by PWM
# IMPORTANT: LED light up at 0, turn off at 1,023
############################
import machine # PWM control
import time # Time management
# Configure the GPIO pins for the RGB LED as output
pin_R = machine.Pin(32, machine.Pin.OUT) # Red LED
pin_G = machine.Pin(12, machine.Pin.OUT) # Green LED
pin_B = machine.Pin(14, machine.Pin.OUT) # Blue LED
# Set the PWM on the corresponding pins
# By not indicating frequency, 1kHz is assigned
# Duty to 1,023 to turn off LED
# The duty cycle goes from 1,023 to 0
pwm_R = machine.PWM(pin_R, duty=1023)
pwm_G = machine.PWM(pin_G, duty=1023)
pwm_B = machine.PWM(pin_B, duty=1023)
# Color dictionary with inverted values
common_colors = {
"Red": (0, 1023, 1023),
"Yellow": (0, 0, 1023),
"Green": (1023, 0, 1023),
"Cyan": (1023, 0, 0),
"Blue": (1023, 1023, 0),
"Magenta": (0, 1023, 0),
# All on
"White": (0, 0, 0),
# All off
222
Electronics & MicroPython© with ESP32©: 80 complete projects
"Black": (1023, 1023, 1023),
"Orange": (0, 511, 1023),
"Violet": (511, 1023, 0),
"Pink": (0, 511, 511),
"Turquoise": (1023, 511, 511),
"Sky blue": (1023, 511, 0),
"Purple red": (0, 1023, 511),
"Light blue": (1023, 511, 0),
"Purple": (511, 1023, 511),
"Pinkish": (0, 511, 255),
"Warm yellow": (0, 255, 1023)}
# Function to present colors in columns
# Name with 20 characters and RGB with 10 characters
def show_colors(dictionary):
print("-" * 50) # Display a separator
print("{:<20} {:<10} {:<10} {:<10}".format
("Name", "(R)", "(G)", "(B)"))
print("-" * 50)
# Change duty in each LED
for name, values in dictionary.items():
cycle_R, cycle_G, cycle_B = values
pwm_R.duty(cycle_R)
pwm_G.duty(cycle_G)
pwm_B.duty(cycle_B)
print("{:<20} {:<10} {:<10} {:<10}".format
(name, cycle_R, cycle_G, cycle_B))
time.sleep(1) # Wait between colors
# Scripts starts here
print ('\n'*50) # Clear screen
print ('Presents basic colors in RGB LED with PWM')
try:
while True:
show_colors(common_colors)
except KeyboardInterrupt:
# Stops when <CTRL>+<C> is pressed
print("\nProgram completed.")
finally:
# Stop the PWM and turn off the LED when finished
print('PWM and RGB LED off...')
pwm_R.deinit()
pwm_G.deinit()
pwm_B.deinit()
pin_R.init(mode=machine.Pin.OUT)
pin_G.init(mode=machine.Pin.OUT)
pin_B.init(mode=machine.Pin.OUT)
pin_R.value(1)
pin_G.value(1)
pin_B.value(1)
⊝⊝⊝
223
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E91: RGB PWM LED, pushbutton, polling and DHT11©.
Develop a script in MicroPython© that manages
the luminosity of the red component of the RGB LED
using pulse width modulation (PWM).
Use a button and a polling system to detect its
status and adjust the intensity of the corresponding
RGB LED. Pressing the button gradually increases the
intensity of the RGB LED, and releasing it gradually
decreases the intensity.
Also integrate the reading of the DHT11© sensor
to display the temperature and humidity on the console
and using PWM, modify the intensity of another RGB LED
component based on certain temperature and/or humidity
thresholds.
•E92: Push button, RGB PWM, ESP32© and DHT11© data.
Extend the script to include an additional
button and use an interrupt for changing the pin
associated with the button. When one of the buttons is
pressed, display on the console the current
temperature and humidity of the DHT11©, as well as the
internal temperature of the ESP32© and its MAC
address. Additionally, use this interrupt to toggle
between increasing and decreasing the intensity of the
RGB LED using PWM.
•E93: WiFi, RGB LED, push button and DHT11©.
Modify the script again to incorporate a WiFi
network scan. By pressing a button, the ESP32© should
scan for available WiFi networks and display the
results on the console. Use the PWM RGB LED to
indicate the scan status, gradually changing the
intensity as the scan is performed. In addition, show
the current temperature and humidity of the DHT11© by
polling in the main loop, announcing the exceeding of
thresholds previously entered by keyboard.
224
Electronics & MicroPython© with ESP32©: 80 complete projects
•E94: ADC, DAC, button, PWM RGB LED and DHT11©.
Integrate a button into the ESP32© and use the
ADC to read its status. When the button is pressed,
adjust the DAC output to vary the intensity of some
component (Red, Green, Blue). Also display the
temperature and humidity of the DHT11© on the console
when the button is pressed and use that data to
illuminate some component of the RGB LED using PWM.
•E95: WiFi, push button, RGB PWM, buzz and DHT11©.
Modify the script to allow activating and
deactivating the WiFi connection using a button.
Pressing the button activates the WiFi connection, and
releasing it deactivates it.
Use a passive buzz to emit a short sound when
activating or deactivating the WiFi connection using
PWM. Also control the intensity of the RGB LED using
PWM depending on the status of the WiFi connection and
display the temperature and humidity of the DHT11© on
the console, duly formatted.
•E96: RGB LED transitions.
Experiment with smooth color transitions or
gradients and gradually change the RGB components,
red, green and blue, to create rainbow effects or
smooth color transitions.
•E97: Breathing RGB.
Creates a gentle breathing effect by varying the
brightness of each LED in a slow rising and falling
pattern.
•E98: RGB with Web commands.
Implement an algorithm with the ability to
control the RGB LED through commands sent via WiFi or
Bluetooth. I recommend seeing following exercises or
consulting on the Internet.
•E99: RGB and sensors.
Relate the operation of the RGB with sensors
such as a button, light, temperature or motion sensor
so that the LED responds to different environmental
conditions. Propose various examples.
225
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
•E100: RGB LED and ambient music.
Implement a system that synchronizes the colors
of the RGB LED with the ambient music. You can use a
microphone to detect changes in the sound and adjust
the LED accordingly (for example use an ADC).
Synchronizes the RGB LED based on the music notes from
previous exercises.
•E101: Timed RGB LED.
Create a timer that changes the LED color at
specific times of the day or set a visual alarm. Use
time control libraries. See the use of timers below.
•E102: RGB and games.
Develop simple light games, such as “Simon
Says”, where the LED displays successive, incremental
random patterns and the user must replicate them. Use
PWM modulation and keyboard sequence entry.
•E103: RGB, PWM and data.
Connect the RGB LED to a system that monitors
some type of data (CPU temperature, server status,
status of some internal variable, etc.) and visualize
this information through changes in color or
predefined light patterns on the RGB LED.
•E104: Fine brightness adjustment.
Since the luminosity of the RGB components are
not equal, adjust the values of the dictionary used
previously so that the difference in mini candelas
(mcd) produced is compensated by software, for
example: R 600mcd, G 1,300mcd and B 300mcd. Start by
adjusting the pure colors.
•E105: DAC and PWM.
Create a script that generates a square wave
with a DAC and also with PWM and compare the precision
of both solutions with an oscilloscope: stability,
frequency, amplitude. Conclusions?
⊝⊝⊝
226
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 21:
Fan and ESP32© temperature
We have already seen how we can control the
power supplied to an LED by
managing the Duty Cycle of a
PWM signal.
Taking advantage of this
concept we can build a
controller for the rotation
speed of a small fan that
helps cool our electronic
project: power supply,
additional circuits of the
ESP32© (it does not heat up),
audio amplifiers, etc.
Since a fan, even if it is small, consumes
between 150-200mA and is powered at +5V, it is not
possible to power it directly from an ESP32© GPIO nor
do I recommend using its +5V output, therefore we need
an external power supply, for example the MB-102© and
an adapter circuit between the GPIO and the fan.
Here we have several options:
1. A simple transistor, such as the NPN S8050©
connected to the GPIO25 that powers the fan to
an external +5V source and to the GND of the
ESP32© (there must always be a common GND
between all devices).
2. Add an optocoupler type isolator between the
GPIO25 and the base of the transistor to act as
an electrical separator and also as a protector
between both circuits.
227
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
3. Replace the transistor with a normal relay or a
low-consumption relay module such as a reed
relay (see other exercises), connecting its
input to the output of the optocoupler and its
output contacts: common and normally open (C-NO)
to the fan and the source. This case is simple
but does not allow PWM control of the fan speed,
it would only be an on/off solution.
In this exercise, the first option has been used
where the ESP32© acts as a temperature sensor and
generator of the PWM signal to control the fan
revolutions. The basic circuit would be like the
following:
The base of the transis-
tor T (NPN type), for
example an S8050©, which
supports up to 700mA of
current collector, is con-
nected to the GPIO25
t h r o u g h a ( y e l l o w - v i o-
l e t - b r o w n ) 470 Ω
resistor R, which protects
the base b of the transis-
tor and which sends the
signal from this GPIO to
this base, making T act as
a switch and therefore
passing the supply current
through the fan V, through
the collector c and to the
e m i t t e r e connec ted to
GND.
This solution allows
b a s e b to receive a PWM
signal, coming from the
GPIO25 and which will reg-
ulate, according to our
MicroPython© algorithm,
the revolutions of the fan
depending on the tempera-
ture of the circuit that
we want to cool.
228
Electronics & MicroPython© with ESP32©: 80 complete projects
Therefore, transistor T feeds the fan V with the
+5V or +3.3V from the MB-102© supply, which can supply
up to 500mA, depending on the charger that we connect
to its input.
IMPORTANT: respect the polarity of the fans because,
in general, they internally include a brushless DC
motor and have an electronic controller that requires
correct polarity.
Finally, and to divert
possible parasitic currents
caused by the rotation of the
f a n , a d i o d e D is placed in
parallel with it, for example a
1N4007© that supports up to 1A of
current (it is important to
respect its polarity).
On the other hand, we need a temperature sensor,
but here we are in luck because the ESP32© has an
internal one accessible by software as we have already
seen or we can use a DS18B20©.
229
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Finally we will add an array of LED, like the
one already seen, with 3 colors that will give us
visual information on the temperature of the element
to be refrigerated. We can implement a 3-level
“traffic light” algorithm, like the following, which
we must adapt to our needs.
Temperature ºC %Duty Cycle Array LED on
<40 50 green
>=40<50 75 yellow
>=50 95 red
We can see how the PWN
wave is generated in a
“square” shape with a
frequency of 1kHz and
75% Duty Cycle for an
EP32 © t emp era tur e of
47.8ºC, with the green
and yellow LED lighting
up.
In the script, the
machine, time and esp32
libraries are imported
to control the GPIO,
PWM, waiting times and internal temperature of the
ESP32© respectively.
All the GPIO necessary to control the LED array,
the PWM signal, as well as the control variables of
the management algorithm of this LED array and the fan
revolutions are defined.
The PWM signal is started with a frequency of
1,000Hz (1kHz) and duty cycle of 50% (default value),
finally all the GPIO are started as output and all the
array LED are turned off.
The level_on() function controls the lighting of
the LED based on their stacking and the adjust_pwm()
function controls the array LED and the revolutions
based on the temperature of the ESP32©.
230
Electronics & MicroPython© with ESP32©: 80 complete projects
It is important to note that the duty cycle must
be converted from % to levels between 0-1,023, which
is the resolution of the internal wave generator of
10
the ESP32© PWM signal, that is, 10bit , 2 =1,024
levels. A frequency of 1kHz has been used as it is
recommended in most PWM controls in many DC motors,
but we must adjust this parameter depending on the
characteristics of the motor used in our project.
In the main body of the script, a while True:
loop is performed in which the internal temperature of
the ESP32© is read from time to time and the
temperature vs LED vs PWM algorithm is applied.
Finally, if the keyboard interrupt is pressed, all the
LED turn off and the PWM signal stops.
############################
# E021_FAN_PWM.PY: Ventilation, Temperature ESP32©
# INPUTS: ESP32© parameters and internal temperature
# OUTPUTS: PWM and LED according to algorithm
############################
from machine import Pin, PWM # GPIO and PWM control
import time # Activation times management
import esp32 # Get ESP32© temperature
# GPIO pins connected to LED in low vs high order
# Change based on LED array model
# 15,2 red, 0,4,16 yellow, green other (by hardware)
pins = [15, 2, 0, 4, 16, 17, 5, 18, 19, 21] # LED
pin_PWM = 25 # GPIO25 activates fan by PWM
r_time = 2 # ESP32© temperature reading time
tempG = 40 # Green temperature (this or lower)
tempR = 50 # Yellow, red top temperature
# Duty Cycle in %, must be changed to scale 0-1,023
PWM_g = 50 # PWM level in green
PWM_y = 75 # PWM level in yellow
PWM_r = 95 # PWM level in red
# Configure GPIO25 output for PWM with frequency 1kHz
pwm_pin = PWM(Pin(pin_PWM), freq = 1000)
# Configure, in a loop, the GPIO pins as outputs
leds = [Pin(pin, Pin.OUT) for pin in pins]
for led in leds: # All LED off
led.off()
# Turns the LED on up to a certain level
231
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
def level_on(level):
for i, pin in enumerate(pins): # Scan list
if i < level: # Below he turns it on
leds[i].on()
else: # Above it turns it off
leds[i].off()
# Gets the internal temperature of the ESP32©
def see_temperature():
temperature = esp32.raw_temperature() # Fahrenheit
temperature = (temperature-32)*5/9 # Celsius
temperature = "{:.1f}".format(temperature) # Text
print(f"Temperature: {temperature} ºC")
return float(temperature) # Number
# Adjust PWM fan according to temperature
def adjust_pwm(temp):
#Temperature = xx.x # Scrip test
print('Adjust fan PWM:',end=' ')
if temp < tempG: # Green on
print('Green level to: ',end=' ')
level_on(5) # Lights up 5 green LED
pwm_pin.duty(int(1023*PWM_g/100)) # PWM
print(str(PWM_g)+'%')
if temp >= tempG and temp < tempR: # Yellow is on
print('Yellow level to: ',end=' ')
level_on(8) # 5 green and 3 yellow
pwm_pin.duty(int(1023*PWM_y/100)) # PWM
print(str(PWM_y)+'%')
if temp > tempR: # Red on
print('Red level to: ',end=' ')
# 5 green on, 3 yellow and 2 red
level_on(10)
pwm_pin.duty(int(1023*PWM_r/100)) # PWM
print(str(PWM_r)+'%')
# PWM fan rotates, ESP32© temperature algorithm
print('FAN ROTATE ACCORDING TO TEMPERATURE')
try:
while True:
temperature = see_temperature() # Of ESP32©
adjust_pwm(temperature) # LED and PWM
time.sleep(r_time) # Wait
except KeyboardInterrupt:
print('Completed...')
for led in leds: # LED off
led.off()
pwm_pin.deinit() # PWM stop
⊝⊝⊝
232
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E106: Fan with PWM and DS18B20© sensor.
Design a system that uses a PWM-controlled fan
to regulate a room temperature. Incorporate a DS18B20©
sensor to measure the temperature and adjust the fan
speed according to a predefined threshold containing 5
steps entered by console.
•E107: Fan with PWM and ignition relay.
Create a system that uses a PWM-controlled fan
to maintain the temperature of an environment within a
specific range. Incorporate a relay that allows an
additional ventilation system to be turned on and off
as necessary.
•E108: Fan, PWM and power button.
Develop a system that uses a fan with PWM and a
power button to regulate the temperature. Pressing the
button will activate the fan, and its speed will be
dynamically adjusted according to the temperature
measured by a DHT22© sensor.
•E109: Fan with PWM and photo switch.
Create a ventilation system that uses a PWM-
controlled fan and a photo switch to activate
automatically when a certain amount of ambient light
is detected. As the light decreases, the fan will
increase its speed to improve air circulation.
•E110: Fan control with PWM and ADC.
Design a system that uses a fan controlled by a
PWM signal and ads an analog-to-digital converter
(ADC) to regulate the fan speed based on the position
of a potentiometer. Dynamically adjust fan speed based
on values measured by the ADC.
⊝⊝⊝
233
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 22:
L293D© DC Motor Control
This exercise is an
example of how, with a
single chip, simple, cheap,
easy to install and manage,
we can control the direction
of rotation and the speed,
or rpm, of two small direct
current (DC) motors of 12V
and up 600mA using the GPIO
and PWM of the ESP32© to
control rotation and speed.
The chip used is the L293D© which includes two
controllers for two independent motors (each side of
the chip for each motor). The chip has two special
pins: Vcc1 that powers the chip (+5V) and Vcc2 that
powers the motor (for example +12V). We can connect
the GPIO of the ESP32© to the L293D© since they will
all be output GPIO and the L293D© admits inputs at
+3.3V also.
In this exercise a low power DC motor is used
and therefore Vcc1=Vcc2=+5V but if a higher power DC
motor is used, it is necessary to connect Vcc2 to an
external power source. For +5V we can use the external
MB-102© source already mentioned in this book.
The rotation control is carried out only with a
pair of signals for each motor, 1A and 2A on pins 2
and 7 respectively for motor 1 and 3A and 4A for motor
2. The motor rotation control is as follows:
If 1A is High and 2A is Low, the motor rotates
turn right and if 1A is Low and 2A is High, turn left.
234
Electronics & MicroPython© with ESP32©: 80 complete projects
We will connect the
rotation control signals,
for example, like this:
signal 1A (pin 2 chip) to
GPIO21 and signal 2A (pin 7
chip) to GPIO22.
Speed control and motor
start and stop are achieved
by applying a PWM signal
with variable Duty Cycle generated by the ESP32©
through the GPIO19 and which we will connect to the
1.2EN signal (pin 1 chip).
In this exercise a simple configuration with a
single motor has been used, the +5V power supply for
the L293D© is taken from
the +5V output of the
external power supply MB-
102©, without using
optocouplers and without
include any type of
suppressor of possible
electrical parasites
generated by the motor.
If the motor is of a
certain power and is going
to be used repeatedly and
close to other sensitive
electronic circuits
(such as the ESP32©
itself), the following
elements are necessary
or at least highly
recommended:
• Optocoupler in each
connection of the
ESP32© with the L293D©
controller, that is,
in the 3 GPIO that
control the direction
of rotation and the
speed of rotation.
235
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The necessary circuit is very simple.
We can use a 4N35© type optocoupler that
includes one opto for each GPIO, or much more
convenient, the TLP620-4© that includes 4 optocouplers
and allows us to control the 3 GPIO used.
When using this circuit, the GPIO input and the
Enable output (1,2EN) have inverted logic with respect
to each other. We have to take this into account in
the software to control the PWM signal of the Enable
pin, that is, for the motor to rotate slowly we need a
high duty cycle and vice versa.
• Suppressor circuit that suppresses possible
electrical parasites
(generated by the rotation of
the moving electrical parts of
the motor or by induced
currents) and that we must use
with motors that interfere
with our electronic circuits.
This suppressor circuit
consists of 4 diodes, for
example 1N4142©, forming a
bridge that diverts these
unwanted currents to ground.
236
Electronics & MicroPython© with ESP32©: 80 complete projects
VERY IMPORTANT:
When using the MB-102© power supply connected to
a breadboard we will take into account:
1. Place the MB-102© power supply on the breadboard
so that its +|- pins coincide in the similar and
corresponding spaces already defined on this board.
2. Place the jumpers on the MB-102© so that in the
upper row of the breadboard we have +5V and in the
lower row +3.3V. Maintaining this order will
prevent dangerous mistakes for the circuits and
especially for the ESP32©
3. Do not share either the +3.3V signal or the +5V
signal of the MB-102© supply with the equivalent
signals of the ESP32©.
4. We must always share the GND signal of all
devices (even measuring devices), therefore, we
must make a bridge between the GND of the MB-102©
and the GND of the ESP32©. Also make a jumper
between the GND of the upper line and the GND of
the lower line of the breadboard.
On some breadboards we also have to bridge the
right side with the left side (check it with a
tester).
5. Power the MB-102© power supply with a transformer
with a 6.5-12V output (it is recommended that this
signal comes from a rectified and stabilized
source, not just a transformer), never exceed 12V
as we will burn out the internal regulator of +5V.
6. The USB port containing in the MB-102© power
supply is only an output and not a power input. It
is important not to use this USB as input to avoid
damaging the MB-102© power supply.
As a summary of the main characteristics of the
L293D© chip controller, we can summarize the
following:
237
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• Double H-bridge: The L293D© features two
complete H-bridges (direction control circuits),
allowing the direction and speed of two direct
current or DC motors to be controlled completely
independently.
• Maximum current: It can handle a continuous
current per channel of up to 600mA and peak
currents of up to 1.2A.
• Power: It supports a wide range of supply
voltages, typically +4.5V to +36V, making it
suitable for a variety of applications.
• Over-current Protection: Includes over-voltage
protection diodes and internal thermal
protection to prevent damage from over-current
and overheating.
Therefore it is highly recommended that the pins
connected to GND (4, 5, 12, 13) be connected to
a heat sink.
• Speed control: Allows us to control the speed of
the two motors using the pulse width modulation
(PWM) technique, providing precise regulation of
motor speed and torque.
• Simple control interface: It can be controlled
with simple control signals, making it easy to
integrate into electronics and robotics
projects.
• Micro controller compatibility: It is compatible
with a wide variety of micro controllers:
ESP32©, Arduino©, Raspberry©, etc.
In the example script the motor starts rotating
to the right (clockwise) and increasing the speed and
changes the rotation to the left (counterclockwise)
while lowering the speed. To do this we do:
We've import the machine an d time libraries to
manage the direction control pins, the speed control
by PWM and timing management.
238
Electronics & MicroPython© with ESP32©: 80 complete projects
The constants are defined with the affected GPIO
and the PWM control is started with a frequency of
1kHz and a duty cycle of zero. Check the start of duty
cycle (direct or reverse) to avoid starting the motor
in an uncontrolled manner.
The motor operating parameters are specified:
start speed, end speed, the increase or decrease step
and the time between steps. The setup() functions are
defined, which starts with the motor stopped, setting
pins 1A and 2A to LOW, and the loop() function, which
includes the operating algorithm already described:
right turn, increasing the speed, stopping for 3
seconds to do not damage the engine, turn left, lower
the speed and stop for 3 seconds. Let's be careful
with the engine rotation.
T h e stop() function stops the script and
importantly, it also stops the engine. Finally, the
main body of the script is defined where the previous
functions are called or the script is ended.
############################
# E022_MOTOR_L293D.PY: Motor control with L293D©
# INPUTS: GPIO and management times
# OUTPUTS: Direction and speed of rotation
############################
from machine import Pin, PWM # GPIO control
import time # Time management
motor_EN_pin = 19 # On/off and PWM control
motor_1A_pin = 21 # 1A direction control
motor_2A_pin = 22 # 2A direction control
'''
PIN LOGIC
motor_EN control by PWM (inverse using optos)
motor_1A H motor_2A L right direction
motor_1A L motor_2A H left direction
'''
# Configure PWM 1kHz frequency and motor stopped
pwm_EN = PWM(Pin(motor_EN_pin), freq=1000, duty=0)
# Configure xA pins as output
motor_1A = Pin(motor_1A_pin, Pin.OUT)
motor_2A = Pin(motor_2A_pin, Pin.OUT)
# Duty speed parameters (0 to 1,023)
pwm_start = 0 # Initial speed
pwm_end = 1000 # Final speed
step = 100 # Speed increase
i_time = .5 # Pause between increments
239
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Start the engine stopped
def setup():
print("Pins configuration...")
motor_1A.off() # Both control pins off
motor_2A.off()
# Engine motion loop
def loop():
''' DC MOTOR MOVING ALGORITHM WITH L293D©
*Turns right from faster to slower speed
*Stops
*Turn left from slower to faster speed
*Stops '''
print('DC MOTOR CONTROL WITH L293D©')
print('<Ctrl>+<C> to stop')
while True:
print('RIGHT turn increasing speed...')
motor_1A.on() # Rotate clockwise
motor_2A.off()
for x in range(pwm_start, pwm_end, step):
print(f'Duty: {int(x/1023*100)}%')
pwm_EN.duty(x)
time.sleep(i_time) # Avoid pulling
pwm_EN.duty(0) # Motor less speed
time.sleep(2) # Avoid damage
print('LEFT turn decreasing speed...')
motor_1A.off() # Rotate clockwise
motor_2A.on()
for x in range(pwm_end, pwm_start, -step):
print(f'Duty: {int(x/1023*100)}%')
pwm_EN.duty(x)
time.sleep(i_time) # Avoid pulling
pwm_EN.duty(0) # Motor less speed
time.sleep(2) # Avoid damage
# Program ends
def stop():
pwm_EN.duty(0) # Motor off
print('Program completed..')
# Main body of the script
if __name__ == "__main__":
setup() # Pins configuration
try:
loop() # Main loop
except KeyboardInterrupt:
stop() # Stops program with <CTRL>+<C>
⊝⊝⊝
240
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E111: LED indicator of direction of rotation.
In the previous exercise, add a button and 2 LED
to change and indicate the direction of rotation of
the motor with each press. Be careful to stop the
engine at each change of direction.
•E112: Rotary motor and decoder.
Change the push button for a rotary decoder (see
other exercises) and use the central button of the
decoder to start and stop the rotation of the motor
and change the direction of rotation of the motor with
the change of direction of rotation of the decoder.
•E113: Motor and opto couplers.
Add optocouplers to the enable signals, 1A and
2A to avoid electrical parasites generated by the
motor that affect the operation of the ESP32©. Check
the script because when introducing the optocouplers,
the PWM logic must be changed from positive logic to
negative logic.
•E114: Motor and DS18B20©.
Include in the circuit a precision temperature
sensor such as the DS18B20© that measures the ambient
temperature and makes the rotation speed, within
certain thresholds, according to that temperature.
•E115: Motor and PWM.
Add an RGB LED also controlled by PWM whose
colors change depending on the state of the motor.
Define a table with two inputs: rotation speed vs RGB
color and builds the corresponding function that
incorporates a change algorithm.
⊝⊝⊝
241
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 23:
SG90© Servo Control
Finally, in this section on electronic motor
rotation control, we will see how we can control a
simple servo motor from the GPIO of our ESP32©. First
we will start by seeing what it is, what a servo motor
is like and what it is for.
A servo motor, such as
the SG90© (Tower Pro©), is a
motor with a special
electronic circuit and is used
to control movement precisely
such as in electro-mechanical
devices, robots, remote-
controlled vehicles, barriers,
3D printers, cameras
surveillance, medicine,
automation, drones, etc.
The basic operation of a
servo motor focuses on dynamic feedback of the
position, that is, the device makes a movement and
periodically checks whether this movement has been
obtained correctly, making the necessary micro changes
to obtain the desired movement. The basic components
of a servo motor could be summarized as:
1. Motor: The servo motor contains a direct current
(DC) electric motor that provides mechanical
movement as seen in previous exercises.
2. Reducer: Often the motor is connected to a
gearbox (reducer) that reduces the rotational
speed and increases the torque in order to move
more weight.
242
Electronics & MicroPython© with ESP32©: 80 complete projects
3. Controller: Inside the servo motor there is a
control electronic circuit that receives control
signals (generally in the form of pulse width
modulated, PWM) from a micro controller such as
our ESP32©. These pulses indicate the desired
position of the servo.
4. Potentiometer: Along with the motor, there is a
potentiometer (or similar position sensor) that
provides feedback of the current position of the
servo shaft to the control circuit. This
element, which provides feedback, drastically
differentiates it from a traditional DC motor.
5. Comparator: The control circuit compares the
desired position (based on the received control
pulses) with the current position detected by
the potentiometer.
6. PWM signal control: Based on the difference
between the desired position and the current
position, the controller adjusts the final PWM
signal sent to the motor to move the servo axis
in the direction and amount necessary to reach
the desired position.
7. Continuous feedback mechanism: The servo motor
makes constant adjustments to its position based
on feedback from the potentiometer, allowing it
to remain in the desired position even if an
external load is exerted on the shaft.
The basic characteristics of the SG90© servo are
the following:
• Connection to 3 pins: VCC, GND and PWM signal,
this is the typical connection for most servos,
it is a very simple connection.
• +5V power supply: This is the typical power
supply for small servos, therefore we must power
it with an external source, NOT with the ESP32©,
because the servo needs more power than the
micro controller can provide.
243
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• Even if we find servos that are powered at
+3.3V, I always recommend powering them with an
external source with a minimum current of 200mA.
• Rotation of up to 180º depending on the duty
cycle of the PWM signal sent from the ESP32©.
• PWM frequency 50Hz (P=20ms) and the relationship
between pulse width
(in ms) and rotation
angle is similar that
of the attached figure
and is dependent on
the servo model.
So to vary the
rotation between 0º
and 180º, we have to
vary the HIGH pulse
between 1ms and 2ms
within a period P of
20ms (1/50Hz*1000).
As we had already
seen, the PWM of the ESP32© is 10 bits and therefore
10
we have 2 =1,024 levels and thus we can calculate:
Angle Pulse 1/f (ms)
Duty% Max Duty Duty
(º) (ms) Total
0 1 20 5 1,024 51
180 2 20 10 1,024 102
Where, according to manufacturer data:
• Angle: servo angle of rotation in degrees.
• Pulse: pulse width in ms for each angle.
• 1/f: total pulse width 1 /50Hz∗1,000=20ms=P
• Duty Max: maximum number of PWM levels.
• Duty: value to apply %Duty∗Duty Max
To make this exercise a little more complete, we
are going to add a potentiometer to the ADC input of
the ESP32© so that moving it moves the servo.
244
Electronics & MicroPython© with ESP32©: 80 complete projects
As the ADC of the ESP32© is 12 bits and
therefore the movement of the potentiometer will
12
generate a total of up to 2 =4,096 levels, we have
to convert a range of 4,096 to another range that
varies the duty cycle between 51 and 102 as we will
see in the script MicroPython©. We will have to adjust
these values according to the servo manufacturer's
parameters.
############################
# E023_SERVO.PY: Control a servo with a potentiometer
# INPUTS: GPIO potentiometer and servo
# OUTPUTS: Servo angle vs potentiometer position
############################
from machine import Pin, ADC, PWM # GPIO, ADC and PWM
import time # Timing control
# ADC configuration reading potentiometer value
pot_pin = ADC(Pin(4)) # Use pin that has ADC
pot_pin.atten(ADC.ATTN_11DB) # Voltage 0 to +3.3V
245
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# PWM pin configuration to control the servo
# Adjust frequency according to servo model
servo_pin = PWM(Pin(15), freq=50) # GPIO15 with 50Hz
# Conversion ADC value (0-4,095) to PWM duty (51-102)
def convert(value,in_min, in_max, out_min, out_max):
# Ensures value is in input range
value = max(min(value, in_max), in_min)
# Converts value to output range
return int((value - in_min) * (out_max -out_min) /
(in_max - in_min) + out_min)
# Main program loop
print('SERVO CONTROL WITH POTENTIOMETER')
print('Rotate potentiometer...')
try:
while True:
# Potentiometer value reading
pot_value = pot_pin.read()
# Converts the potentiometer value to
# range from 0º to 180º for the servo
# Important: adjust theses values according
# to the servo manufacturer's parameters.
# value, ADC, PWM
angle = convert(pot_value, 0, 4095, 51, 102)
print(f'Angle: {angle}')
# Set the servo position
servo_pin.duty(angle)
# Wait between potentiometer readings
time.sleep(0.5)
except KeyboardInterrupt:
# Stops the script if <CTRL>+<C> is pressed
servo_pin.deinit()
• print('\nStopped script...')
⊝⊝⊝
246
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E116: Temperature control with servo.
Use a DHT11© or DS18B20© to measure the ambient
temperature. Depending on the temperature, control a
servo to simulate the opening/closing of a window to
regulate it. Use a higher power servo than the SG90©.
Add 2 reed sensors used as “limit switches” of the
window to paralyze the movement of the servo.
•E117: Irrigation system, servo and humidity.
Create a system that uses a humidity sensor,
such as the DHT11© (see later exercises), to measure
soil moisture in a pot. Use a servo motor to simulate
the opening of an irrigation valve based on the
detected humidity level.
•E118: RGB control and servo with potentiometer.
Design a system that controls the color of an
RGB LED using PWM signals. Modify the position of a
servo, managed by PWM, using a potentiometer and
reflects the angle of the servo in the RGB as if it
were a traffic light.
•E119: Access control with servo and relay.
Implement an access control system that uses a
push button as input that must be controlled by IRQ.
When the button is activated, a relay unlocks the door
and the servo motor moves an simulated arm to open it.
Add limit switches.
•E120: Brightness control with servo and ADC.
Design a system that adjusts the brightness of
an LED and uses a servo motor to simulate the position
of a filter. Use an ADC to tune the servo based on an
LDR. Simulate the LDR with a potentiometer or consult
later exercises.
⊝⊝⊝
247
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercises 24:
ESP32© Internal Clock
In this exercise we will see how we can use the
internal real-time clock (RTC) of the ESP32© itself to
integrate it into our scripts. It is important to know
that it is necessary to update the RTC of the ESP32©
every time it is restarted or reset because, as it
does not have autonomous power, the time and date are
lost, but we can update it without problems.
It is feasible to power the ESP32© with a small
battery since it only consumes 7uA in “deepsleep”
mode, however if we need to always keep the time
updated, I recommend using an external RTC like the
one we will describe in the following exercise.
Updating the internal RTC of the ESP32© is very
simple and can be done by first updating it from the
script and using the ntptime library for which, and
previously, our ESP32© must be connected to the
Internet. A convenient way to connect our ESP32© to
the Internet is to use the WiFi connection seen above
and load the connection script within the boot.py or
main.py files.
In the script we first invoke the internal RTC
clock using the machine library and the ntptime
library to access an NTP (Network Time Protocol)
server accessible from the Internet and which allows
us to update the RTC of the ESP32©.
In the example we update the data from the
Internet, paralyzing the script if there is an error,
for example, if the ESP32© is not connected to the
Internet. The time thus captured is adjusted based on
the local time zone and the ESP32© is updated.
248
Electronics & MicroPython© with ESP32©: 80 complete projects
The list week_days[] is used to convert the day
number of the week to its equivalent name and the main
loop of the program is defined where the RTC is
invoked, the data is obtained, formatted appropriately
and displayed, waiting for a few seconds for the next
reading.
As always, the program includes the necessary
console messages, as well as a check for possible
errors and the option to end the reading loop by
pressing <CTRL>+<C> on the keyboard.
############################
# E024_RTC.PY: Synchronizes the ESP32© internal clock
# INPUTS: Day and hour of a NTP server
# OUTPUTS: ESP32© RTC update
# IMPORTANT: ESP32© must be connected to the Internet
############################
import machine # RTC management
import time # Timing control
import ntptime # Time server
# RTC configuration
rtc = machine.RTC()
# Try to sync with an NTP server
try:
print ('Getting date and time from Internet...')
ntptime.settime() # Get Internet data
except OSError as e:
print('Error syncing with NTP:', e)
machine.reset() # Exit the script
# Set the time zone (e.g. UTC+2)
time_zone = 2 # According to local time
# Gets the current date and time of the RTC
year, month, day, weekday, hour, minute, second, _ =
rtc.datetime()
# Set the time according to the time zone
hour += time_zone
# Ensures that the time is in the range of 0 to 23
hour = hour % 24
249
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Updates the RTC with the set time
print('Update Date and Time of ESP32©')
rtc.datetime((year, month, day, weekday, hour, minute,
second, 0))
# Translate the day of the week from number to text
week_days = ["Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", "Sunday"]
print('RTC DATA OF MY ESP32©')
# Main program loop
try:
while True: # Get day and hour each 2 seconds
# Get current RTC date and hour
year, month, day, weekday, hour, minute,
second, _ = rtc.datetime()
# Format and show date and hour
formatted_datetime = '{}, {:02d}-{:02d}-{:04d}
{:02d}:{:02d}:{:02d}'.format(
week_days[weekday], day, month, year, hour,
minute, second)
print('Current data and hour: {}'
.format(formatted_datetime))
# Wait 2 seconds between reading RTC
time.sleep(2)
except KeyboardInterrupt:
print('Program completed...')
⊝⊝⊝
250
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E121: RGB control, push button, DHT11© and RTC.
Develop a script in MicroPython© that manages
the luminosity of a RGB LED using pulse width
modulation (PWM). Use a push button and polling logic
to detect its status and adjust the RGB intensity.
Pressing the button gradually increases the
intensity of the LED, and releasing it gradually
decreases it. Also integrate the reading of the DHT11©
sensor to display the temperature and humidity on the
console, and use the RTC of the ESP32© to record the
time of important LED intensity changes.
•E122: Button, RGB, ESP32©, DHT11© and RTC data.
Extend the previous script to include an
additional button and use an IRQ per change in its
state. When one of the buttons is pressed, display on
the console the current temperature and humidity of
the DHT11©, as well as the internal temperature of the
ESP32© and its MAC address.
Finally, use this interrupt to toggle between
increasing and decreasing the intensity of the RGB LED
using PWM. Also record the time of each major change
using the RTC of the ESP32©.
•E123: WiFi scanning, RGB, button, DHT11© and RTC.
Modify the script to be able to incorporate a
WiFi network scan. By pressing a button, the ESP32©
should scan for available WiFi networks and display
the results on the console.
Use the RGB LED with PWM technology to indicate
the WiFi scanning status, gradually changing the
intensity as the scan is performed. Additionally,
display the temperature and humidity of the DHT11© and
record the time using the RTC of the ESP32© during
scanning.
251
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
•E124: ADC, DAC, button, RGB PWM, DHT11© and RTC.
Integrate a button into the ESP32© and use the
ADC (Analog-Digital Converter) to read its status.
When the button is pressed, adjust the intensity of
each component (red, green and blue) of the RGB LED
using PWM and using a set pattern.
Finally, also display the temperature and
humidity of the DHT11© and record the time using the
RTC of the ESP32© previously updated.
•E125: WiFi, button, RGB, buzz, DHT11© and RTC.
Modify the previous script to allow activating
and deactivating the WiFi connection using a button.
Before deactivating WiFi, update the RTC of the ESP32©
Pressing the button activate the WiFi
connection, and releasing it deactivates it. Use a
passive buzz, managed by PWM, to emit a brief sound
when activating or deactivating the WiFi connection.
Control the intensity of the RGB LED using PWM
signals depending on the status of the WiFi connection
and display the temperature and humidity of the
DHT11©. Additionally, record the time of each major
change using the RTC of the ESP32©.
⊝⊝⊝
252
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 25:
DS1302© External Clock
In this exercise, as we had promised, we are
going to see the time “sensor”, that is, the real-time
clock or RTC, which can help us keep our project
always updated and not depend on reading this variable
from Internet, the computer or the own ESP32©.
To do this, we are going to use the DS1302© chip
or equivalent, which contains
the RTC that counts seconds,
minutes, hours, days, day of
the week, months, years (valid
un t il 2 10 0 ), w hi c h h a s a
backup battery of +3.0V to
always stay on time even if it
is not being used or is not
powered by the ESP32© and that
it is easy to read and update
with the software that we are
going to use.
Communications with this chip are serial through
3 pins: I/O, SCL and RST, and like other sensors, it
requires additional components within a very simple
module that we will discuss below.
The 32.768kHz XTAL quartz crystal along with C1
and C2 create the main oscillator to generate the RTC
clock signal.
The +3.0V battery (a button cell) supplies the
necessary voltage through vcc1 pin chip to keep the
data always updated, even after resetting the ESP32©.
R1, R2 and R3 are pull-ups from data pins GPIO26
(SCL), GPIO27 (I/O) and GPIO14 (RST) respectively.
253
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
C3 acts as a voltage stabilizer and R0 limits
the current of LED D0 that lights up when the circuit
is powered.
For the MicroPython© script, we need to download
the ds1302.py library from the Internet and to be able
to import it into our script, once downloaded, we open
it with Thonny© as if it were another script and save
it in the ESP32© or use the Upload to/ function as we
have already described in other exercises.
The script uses the machine, ds1302, ntptime and
utime libraries to control the GPIO, the RTC, the
update from the Internet and the timing respectively
and configures the GPIO for the 3 signals: clock
(SCL), data (I/O or SDA) and reset (RST).
The RTC of the DS1302© is started and a request
is made whether or not to update the RTC from NTP. If
so, the external RTC is stopped, the internal RTC of
the ESP32© is updated from the Internet, the external
RTC is updated with this data and it is started.
The week[] list is defined to translate weekday
numbers to day name and in the main loop of the script
the data is fetched from the external RTC, formatted,
displayed and refreshed every 2 seconds.
254
Electronics & MicroPython© with ESP32©: 80 complete projects
In the image of the LA1010© analyzer we see the
3 signals of the RTC 3-wire bus: the clock in CLK
(scl_pin), the input and output data in DIO (dio_pin)
and the select chip in CS (rst_pin), in addition a
50kHz square reference signal.
As can be seen, the clock synchronizes the data
communication between the ESP32© and the DS1302©, the
select chip indicates when the data is valid and this
is transmitted serially through the dio_pin.
############################
# E025_RTC_EXT.PY: Synchronize ESP32© RTC with DS1302©
# INPUTS: Day and time of a NTP server
# OUTPUTS: Update of the two RTC
# IMPORTANT: The ESP32© will be connected to Internet
# ds13202.py will be loaded on the ESP32©
############################
from machine import Pin # GPIO management
255
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
try:
import ds1302 # RTC manager
except:
print('Upload ds1302.py to ESP32©')
import ntptime # Update the RTC online
import utime # Timing, date and hour
# Configure the GPIO, they must be input and output
scl_pin = Pin(26, Pin.OUT) # Clock
dio_pin = Pin(27, Pin.OUT) # I/O
rst_pin = Pin(14, Pin.OUT) # Chip select
# RTC DS1302© initialization
rtc = ds1302.DS1302(scl_pin, dio_pin, rst_pin)
# Start program
print('EXTERNAL RTC DS1302© CONTROL')
# Ask if it wants to update the RTC DS1302©
print('Update RTC DS1302© from NTP? (y/n): ', end='')
update_rtc = input() # Get options
if update_rtc.lower() == 'y': # Lowercase
# Stop RTC DS1302© before update
print('Stopping RTC DS1302©')
rtc.stop()
# Update the internal RTC of the ESP32© from NTP
print('Get NTP data')
ntptime.settime()
# Date, current time from ESP32© internal RTC
print('Internal RTC ESP32© update')
# year, month, day, hour, minute, second, week
data = utime.localtime()
h = 2 # Adjust hour to UTC+h
# Update RTC DS1302© with the new date and time
# year month day week
rtc.date_time([data[0], data[1], data[2], data[6],
# hour minute second
data[3] + h, data[4], data[5]])
print('Update external RTC in DS1302©')
256
Electronics & MicroPython© with ESP32©: 80 complete projects
# Restart the RTC DS1302© after the update
print('Restart external RTC DS1302©')
rtc.start()
# Names of days of the week
week=['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
# Main loop
try:
while True:
# Obtains date and time from DS1302© RTC
data = rtc.date_time()
# Gets the name of the day of the week
week_day = week[data[3:4][0]]
# Format the date and time
data = "{} {:04d}-{:02d}-{:02d} {:02d}:{:02d}:
{:02d}".format(week_day,*data[:3],*data[4:7])
# Shows the formatted date and time
print("Date and hour:", data)
utime.sleep(2) # Wait 2 seconds
except KeyboardInterrupt:
print('Program completed...')
There is the external RTC
version, the DS1307©, which uses an
I2C bus with only two signals: SDA
(data) and SCL (clock), which can
be easily used with the ds1307.py
library and which can be used after
seeing how the I2C bus works in
following exercises.
⊝⊝⊝
257
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
• E126: LED and external RTC.
Design a script to turn on and off an LED
connected to a certain GPIO depending on whether it is
before or after a certain time captured from the
DS1302© RTC. Capture the time by keyboard and present
the corresponding messages. Create a function to read
the external RTC when necessary.
• E127: Push button with polling, LED and RTC.
Connect a button to the ESP32© and use polling
to detect any change in the button's state. When the
button is detected, the status of the button is
displayed by turning on an LED and the corresponding
current time is displayed.
• E128: Temperature, MAC address and time.
Creates a loop to read the internal temperature
of the ESP32© and its MAC ID and present the data
together with the date and time of the external RTC.
• E129: WiFi and RTC DS1302© network scanning.
Scan all available WiFi networks and display the
information on the console showing network names and
scan time.
• E130: IRQ button, LED brightness and RTC.
Connect a push button to the ESP32© and use the
interruptions generated by its press to change the
state of an LED, adjusting the brightness via PWM and
displaying the start and end time of the button press.
As always, create the necessary titles and
messages for important operations as well as error
control by try:...except: when accessing the RTC,
activating the PWM, etc.
⊝⊝⊝
258
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 26:
Web Server
We have already seen how various scripts are
built to control both sensors and actuators of all
types and the logic and intelligence that we can
implement with a MicroPython© script so that these
inputs and outputs can be related following some type
of algorithm.
Sometimes we will need to start/stop a script
remotely, from our local network, WiFi network or from
the Internet. In this exercise we will see in detail
and with several examples, how to manage remotely,
several tasks related to MicroPython© scripts already
covered in the previous exercises.
We will start by starting a script remotely
using a Web address on our local WiFi network and
configuring our ESP32© as a Web server, that is,
installing on the ESP32© an application that can
receive HTTP (Hypertext Transfer Protocol) requests
from devices acting as clients, such as Web browsers,
and respond to those requests by sending Web content,
such as HTML pages, images, files, etc. and executing
concrete actions.
In the following example, using the ESP32© as a
Web server, we can execute a script, one of those
already seen, invoking it remotely. To do this we
carry out the following tasks:
1. We must copy the script to be executed on the
ESP32© internal memory, to do this just open the
script with Thonny© editor and using [file]
[save as] menu, we can place it without problem
on our ESP32©.
259
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In the example we have used the script that
makes 2 LED blink, which we have already seen
and which we have called e001_2led.py script.
2. We will connect the ESP32© to our WiFi network
using some method or script already seen in
previous exercises. We can place this WiFi
connection script within the boot.py or main.py
files and thus guarantee that when starting or
resetting the ESP32©, the automatic connection
to the WiFi occurs and we can even assign a
specific (static) IP to ESP32©.
3. We will create a script that configures a simple
Web server, using the following example, where:
The usocket library is used and the address that
the server is going to use, we start the socket (as a
gateway through the WiFi network) and link it with the
address obtained, the script to start remotely is
indicated (change if necessary) and that we have
already loaded into the ESP32© and finally the
listen() function is activated to “listen” to the
requests from the server's client devices.
The function run_script() is defined to execute
the script e001_2led.py and a loop is created where
the connection with the browser and the corresponding
controls on the path of the selected script are
expected.
On the server we will see the message “Server
started”, indicating that the ESP32© is in server mode
waiting for HTTP type requests.
On the client, for example on a mobile phone, we
open the url: [IP]:8080/execute, where [IP] is the IP
address (it better be static) that we have assigned to
o u r E S P 3 2 © a n d /execute i s a c o m m a n d t o r u n
instructions. By going to this address we will see how
the message “Connection from ([ip], port)” appears in
the Thonny© console, where [ip] and port identify the
mobile from which we have executed the e001_2led.py
script and we will see how runs on the ESP32©.
260
Electronics & MicroPython© with ESP32©: 80 complete projects
############################
# E026_WEB_ON.PY: Web Server starts a script
# INPUTS: Script name to execute
# OUTPUTS: Run the script from a Web
# The Web is: [IP]:8080/execute
# IMPORTANT: ESP32© connected to WiFi and static [IP]
# The script must be saved on the ESP32©
############################
import usocket as socket # Web Server configuration
# Get server address (change 8080 if is necessary)
addr = socket.getaddrinfo('0.0.0.0', 8080)[0][-1]
# Creates a socket and binds it to obtained address
try:
s = socket.socket()
s.bind(addr)
except: # If socket was already open yet, close it
s.close()
s = socket.socket()
s.bind(addr)
# Listen incoming connections (only one in this case)
s.listen(1)
sc = 'e001_2led.py' # Name of the script to execute
# Executes script
def run_script(name):
try:
# Run the script using the exec() function
exec(open(name).read(), globals())
return f"Script {name} executed correctly"
except Exception as e:
return f"Error running script
{name}: {str(e)}"
# Main loop of the Server
print('Server started')
try:
while True:
# Accept the connection from the browser
conn, addr = s.accept()
print('Connection from: ', addr)
# Receives the client request (HTTP method)
request = conn.recv(1024).decode('utf-8')
# Verify the request and send a response
if '/execute' in request: # Command received
response = 'Correct route'
print(response)
conn.sendall('HTTP/1.1 200 OK\nContent-
Type: text/html\n\n' + response +
'<br> Running: ' + sc)
conn.close()
run_script(sc) # Executes script
else:
response = 'Wrong route'
261
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
print(response)
conn.sendall('HTTP/1.1 200 OK\nContent-
Type: text/html\n\n' + response +
'<br> Review URL')
conn.close()
except KeyboardInterrupt:
print('Server stopped by keyboard') # End
finally:
s.close() # Stop Server
As we have seen, we can access our ESP32© and
execute a script remotely. To perform this operation
more securely, we can add a username and password when
launching the server script to perform the control.
In this exercise, the usocket library is used to
configure the Web server, preparing the Web address
and listening for client devices. The script that we
want to launch from the Web is specified, the
credentials [user] and [password] expected to allow
access are defined.
T h e run_script() function is defined that will
execute the script to be launched, the connection from
the client to the server is reviewed and presented,
the credentials embedded in the access URL used are
extracted, these credentials are displayed and
verified.
Finally, the corresponding warning messages are
presented, both in the console and on the Web itself,
and the script is executed if the data is correct.
Finally, the control to stop the script due to
keyboard interruption is used.
On the client, for example a mobile phone, we
must write in a browser:
[IP]:8080/execute?user=esp32&password=123
Where [IP] is the IP address of the ESP32© and
[username] and [password] are those defined in the
script that starts the Web server and that we can
customize. This script checks that [username] and
[password] are correct but we should add that the
/execute command is correct, do we dare?.
262
Electronics & MicroPython© with ESP32©: 80 complete projects
############################
# E026_WEB_PASS.PY: Web Server with credentials
# INPUTS: Script to run with credentials
# OUTPUTS: Run the script from the Web:
# [IP]:8080/execute?[user]=esp32&[password]=123
# IMPORTANT: ESP32© with WiFi and static [IP]
# IMPORTANT: Script to execute saved on the ESP32
############################
import usocket as socket # Configure Web server
# Prepare the Web address and listen to clients
addr = socket.getaddrinfo('0.0.0.0', 8080)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
# Script to execute (must be on the ESP32©)
sc = 'e001_2led.py' # Change to the desired one
# Expected credentials, change if necessary
expected_user = 'esp32' # [user] example
expected_pass = '123' # [password] example
# Function to run the script
def run_script(name):
try:
exec(open(name).read(), globals())
return f"Script {name} executed successfully"
except Exception as e:
return f"{name}:Error running script {str(e)}"
# Main server loop
print('Server started')
print('Use: [IP]:8080/execute?
user=esp32&password=123')
try:
while True:
# Accepts connection from client and indicates
conn, addr = s.accept()
print('Connection from', addr)
request = conn.recv(1024).decode('utf-8')
# Extract credentials from URL
parts = request.split(' ')
# Dictionary {'password': '123', 'user': 'esp32'}
params = {}
if len(parts) > 1:
url_parts = parts[1].split('?') # Find ?
if len(url_parts) == 2:
query_string = url_parts[1]
for param in query_string.split('&'):
263
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
key, value = param.split('=')
params[key] = value
user = params.get('user', '')
password = params.get('password', '')
print(user, password)
print(params)
# Verify credentials and run the script
if user == expected_user and password ==
expected_pass:
response = 'Correct credentials.'
print(response)
conn.sendall('HTTP/1.1 200 OK\nContent-
Type: text/html\n\n' + response +
'<br> Running: ' + sc)
conn.close()
run_script(sc)
else:
response = 'Wrong credentials.'
print(response)
conn.sendall('HTTP/1.1 200 OK\nContent-
Type: text/html\n\n' + response +
'<br> Review entered URL')
conn.close()
except KeyboardInterrupt:
# Capture <CTRL>+<C> to stop the server
print('Server stopped by keyboard')
finally:
s.close()
To practice a little more using a Web Server
with ESP32©, we will do an exercise similar to the
previous one in which the internal temperature of the
ESP32© is read, the time of this reading, a flashing
of a green and red LED is activated to indicate the
reading of data correctly and of course, all this from
a Web.
To do this, we create the following script where
we import the necessary libraries, update the time of
the internal RTC of the ESP3©2 from the Internet,
activate the GPIO to control the LED and obtain the
address for the Web server.
We define a function to read the internal
temperature using the esp32 library, we convert the
degrees Fharenheit into Celsius and finally we use an
infinite loop where we read the request that comes
f r o m [IP]:8080/temperature and if it is correct we
264
Electronics & MicroPython© with ESP32©: 80 complete projects
present the temperature, time and we activate a
flashing in the predefined LED.
############################
# E026_WEB_TEMP.PY: Web server displays temperature
# INPUTS: ESP32© temperature
# OUTPUTS: Temperature, time on Web and LED on/off
# The Web is: [IP]:8080/temperature
# IMPORTANT: ESP32© with WiFi and static [IP]
############################
import usocket as socket # Communications
from machine import Pin # GPIO LED
import esp32 # Temperature ESP32©
import utime # Timing
import ntptime # Update hour
ntptime.settime() # ESP32© RTC update
# Definition of green and red LED pins
led1=Pin(26, Pin.OUT) # Green LED
led2=Pin(27, Pin.OUT) # Red LED
led1.on() # Green on
led2.off() # Red off
t=.1 # On/off LED timming
# Gets the address information for the server
addr = socket.getaddrinfo('0.0.0.0', 8080)[0][-1]
# Creates a socket and binds it to the address
s = socket.socket()
s.bind(addr)
# Incoming connections (only one in this case)
s.listen(1)
# Function obtains internal temperature of the ESP32©
def get_temperature():
temperature = esp32.raw_temperature()
# Convert to degrees Celsius
temperature = (temperature-32)*5/9
h_time = utime.localtime() # Gets hour
# String hour with format: 'hour:minute:second'
current_hour = "{:02}:{:02}:{:02}"
.format(h_time[3]+1, h_time[4], h_time[5])
temperature = "{:.2f}".format(temperature) # 2 dec
# Returns temperature and hour
return temperature+' C a: '+current_hour
# Main server loop
print('Server started')
try:
while True:
# Accept the connection from the browser
conn, addr = s.accept()
265
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
print('Connected to: ', addr)
# Receive request from browser (HTTP method)
request = conn.recv(1024).decode('utf-8')
# Verify request, send temperature
if '/temperature' in request:
temperature = get_temperature()
response = f"Internal temperature:
{temperature}"
led1.off() # Change LED vs temperature
led2.on()
utime.sleep(t) # Wait a while
led1.on()
led2.off()
utime.sleep(t) # Wait a while
else:
response = "Wrong route"
# Send the response to the browser
conn.sendall('HTTP/1.1 200 OK\nContent-Type:
text/html\n\n' + response)
# Close the connection with the browser
conn.close()
except KeyboardInterrupt:
# Capture <CTRL>+<C> to stop the server
print('Server stopped...')
finally:
s.close() # Web server stopped
led1.off() # LED off
led2.off()
Another interesting exercise is to be able to
execute various actions from a Web, such as turning
on/off an LED, connected to the GPIO27, doing from the
Web: [IP]:8080/on or [IP]:8080/off.
###########################
# E026_WEB_ONOFF.PY: Web server for LED management
# INPUTS [IP]:8080/on or [IP]:8080/off
# OUTPUTS: LED on or off in GPIO27
# IMPORTANT: ESP32© WiFi connected and static [IP]
############################
import socket # Server control
import machine # LED management
# Server port configuration
port = 8080 # Change if is necessary
# LED pin configuration
led_pin = machine.Pin(27, machine.Pin.OUT)
# LED management function
def control_led(state):
266
Electronics & MicroPython© with ESP32©: 80 complete projects
led_pin.value(state)
# Server socket creation
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', port))
s.listen(5) # For connect 5 devices
print("Web Server on http://[IP]:{}".format(port))
# Main loop
while True: # Web command reading loop
conn, addr = s.accept()
print("Connection from: {}".format(addr))
request = conn.recv(1024)
request = str(request)
# Analyzes request
if 'GET /on' in request: # ON command
control_led(1)
elif 'GET /off' in request: # OFF command
control_led(0)
# Text for the Web
response = "HTTP/1.1 200 OK\n\nLED State:
{}".format(led_pin.value())
conn.send(response)
conn.close()
And finally we will make a more complete and
refined script, which will be able to manage 2 LED
(green and red) from a Web but also using a friendlier
environment, that is, having 2 buttons on the screen
that when pressed will turn each of these LED on or
off. We will also see, although it is not the
objective of this book, how HTML code can be
integrated into a MicroPython© script.
The script basically performs the following ac-
tivities:
I m p o r t t h e l i-
braries, define the
connection port (8080
or other), configure
GPIO26 for the green
LED and GPIO27 for the
red one, define the
control_led() f u n c-
tion, which performs
on/off on a certain
LED, define the gen-
erate_html() function
267
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
to generate the html page with the 4 buttons and the
corresponding texts, creates a connection with the
server at [IP]:8080, defines the main loop where the
button press is read, controls the errors and finally
the stop of the loop of the script.
If we do not have sufficient knowledge to write
the necessary part of the script in HTML, we can count
on the help of chatGPT©.
###########################
# E026_WEB_HTML.PY: Web server, HTML LED buttons
# INPUTS: [IP]:8080 and on/off buttons on HTML
# OUTPUTS: LED on or off in GPIO26/27
# IMPORTANT: ESP32© with WiFi and static [IP]
############################
import socket # Server activation
import machine # LED control
# Server configuration
port = 8080 # Change if is necessary
# LED pins configuration
led_green_pin = machine.Pin(26, machine.Pin.OUT) # G
led_red_pin = machine.Pin(27, machine.Pin.OUT) # R
# LED control function
def control_led(led_pin, state):
led_pin.value(state)
if state: # Change state to on
e = 'ON'
else:
e = 'OFF'
if led_pin == led_green_pin:
c = 'Green' # Change color to text
else:
c = 'Red'
print(f'LED {c} {e}')
# Generate HTML response with circular buttons
def generate_html(state_led_green, state_led_red):
html = """<!DOCTYPE html>
<html>
<head>
<title>LED Control</title>
</head>
<body>
268
Electronics & MicroPython© with ESP32©: 80 complete projects
<h1>LED Control</h1>
<p>Current state:</p>
<!-- Green LED Controls -->
<p>Green LED: {}</p>
<form action="/green_on" method="get"
style="display:inline;">
<input type="submit" value="Green on"
style="font-size:20px; background-color:green;
border-radius:50%; padding:10px;">
</form>
<form action="/green_off" method="get"
style="display:inline;">
<input type="submit" value="Green off"
style="font-size:20px; background-color:green;
border-radius:50%; padding:10px;">
</form>
<!-- Red LED Controls -->
<p>Red LED: {}</p>
<form action="/red_on" method="get"
style="display:inline;">
<input type="submit" value="Red on"
style="font-size:20px; background-color:red; border-
radius:50%; padding:10px;">
</form>
<form action="/red_off" method="get"
style="display:inline;">
<input type="submit" value="Red off"
style="font-size:20px; background-color:red; border-
radius:50%; padding:10px;">
</form>
</body>
</html>""".format("On" if state_led_green
else "Off",
"On" if state_led_red
else "Off")
return html
# Create the server socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', port))
s.listen(5)
print("Web Server on http://[IP]:{}".format(port))
# Main loop
try:
269
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
while True:
conn, addr = s.accept()
print("Connection from {}".format(addr))
request = conn.recv(1024)
request = str(request)
# Request analyzer
if 'GET /green_on' in request:
control_led(led_green_pin, 1)
control_led(led_red_pin, 0)
elif 'GET /green_off' in request:
control_led(led_green_pin, 0)
elif 'GET /red_on' in request:
control_led(led_red_pin, 1)
control_led(led_green_pin, 0)
elif 'GET /red_off' in request:
control_led(led_red_pin, 0)
# Get current state of LED
state_led_green = led_green_pin.value()
state_led_red = led_red_pin.value()
# Generate HTML response with buttons
response = "HTTP/1.1 200 OK\n\n{}".format
(generate_html(state_led_green, state_led_red))
conn.send(response)
conn.close()
except KeyboardInterrupt:
print("Program completed")
finally:
# Turn off both LED when exiting the loop
led_green_pin.off()
led_red_pin.off()
s.close()
⊝⊝⊝
270
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E131: Remote execution of button with polling.
Design a system in MicroPython© for ESP32© that
allows remote execution, via Web, to use polling to
verify if a physical button has been pressed on the
device and reflect the state in the Web interface.
•E132: RGB LED remote control with IRQ.
Implement a script that allows remote control of
an RGB LED using interrupts (IRQ) in response to
events in a Web interface. Users should be able to
turn the LED on and off and adjust the intensity of
each color, with the PWM system, through the
interface.
•E133: Data monitoring and WiFi activation.
Develop a system that offers remote monitoring
of data such as internal temperature and MAC address
through a Web. It must also allow remote activation of
the WiFi connection using commands from that
interface.
•E134: DAC remote control and ADC readout.
Implement a solution that allows remote control
of a Digital-Analog Converter (DAC) and remote reading
of an Analog-Digital Converter (ADC) through a Web.
•E135: Remote script execution.
Modify the previous exercises so that scripts
located on the ESP32© can be remotely executed using
[IP]:8080/[script] on the Web. Always remember that we
must first connect to the WiFi with the specific
static [IP] and start the Web server.
⊝⊝⊝
271
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 27:
BLE Bluetooth
In this exercise we are going to see how we can manage
a script, remotely, using the Bluetooth capability
(classic v4.2 or low energy or BLE type) of the
ESP32©. To carry out this exercise there are multiple
solutions since the Bluetooth service is quite complex
and complete, but in
this exercise we will
only use the basic
fu n ct i o na l i ti e s to
activate the 2 LED,
from some exercise
already seen, through
a script that runs on
the ESP32© and
controlled from
Bluetooth.
For remote Bluetooth ac-
cess to the ESP32© we will use
the Android Serial Bluetooth©
APP. The script uses the BLE.py
library that we must download
from the Internet (GitHub, Mi-
croPython© repositories, etc.),
t h e bluetooth.BLE class could
also be used if the necessary
information is available. We
must save these libraries in the
ESP32© memory. In the script,
t h e bluetooth and bleuart libraries are imported to
manage the Bluetooth BLE and machine to control the
two LED. The green and red LED GPIO are configured,
the Bluetooth connection is started with the usual
name 'ESP32' and various functions are defined.
272
Electronics & MicroPython© with ESP32©: 80 complete projects
T h e receive() function is the destination of
uart.irq(), that is, when information is received via
Bluetooth. The send() function sends messages over
Bluetooth. Additionally, the receive() function acts
with the LED depending on the message received:
Ron/Roff, turns on/off the red LED and Von/Voff the
same but with the green LED.
Since most mobile terminals do not display
Bluetooth low energy or BLE signals, we use the Serial
Bluetooth© APP. In this APP, in the [ ] [devices] tab,
we can select classic
Bluetooth devices or
Bluetooth low energy or BLE
devices. In this last option,
the ESP32© should appear with
the Bluetooth name that we
have assigned in the script
and its associated MAC.
If we click on ESP32©
and on the upper connection
symbol, the connection to the
ESP32© will be made and with
the keyboard we can send the
defined commands (Ron, Roff,
Von, Voff) . T o m a k e t h e
management of the LED easier,
we can create macros in the
APP, holding down the buttons
located above the keyboard
input. The ESP32© can be
connected via WiFi and
Bluetooth simultaneously
without problems. Finally we
can see that the data
received by Bluetooth is
managed by the IRQ, which
redirects the control of the
script to the receive()
function.
############################
# E027_BT_LED.PY: Activate 2 LED by Bluetooth BLE
# INPUTS: Press R, G or other in APP Serial BT©
273
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# OUTPUTS: GPIO26 green LED and GPIO27 red LED
# Use Android Serial Bluetooth© APP
# Use BLE.py library and load it on the ESP32©
# Get BLE.py at /docs.micropython.org
############################
import bluetooth # BT connection management
from BLE import BLEUART # Load it in ESP32© memory
from machine import Pin # GPIO manager
# Init LED as output
led1=Pin(26, Pin.OUT) # Green LED
led2=Pin(27, Pin.OUT) # Red LED
led1.off() # Green off
led2.off() # Red off
# Start BT connection
name = 'ESP32: ' # My ESP32© BT name
ble = bluetooth.BLE() # BLE class
uart = BLEUART(ble, name) # UART class
print('BT started as' + name + '...')
# Receive BT
def receive():
# We receive, read, decode without spaces
rx = uart.read().decode().strip()
# We act with the LED according received command
if rx == 'Ron':
print('Red on...')
led2.on()
send(name + 'Red on' + '\n')
elif rx == 'Roff':
print('Red off...')
led2.off()
send(name + 'Red off'+ '\n')
elif rx == 'Von':
print('Green on...')
led1.on()
send(name + 'Green on' + '\n')
elif rx == 'Voff':
print('Green off...')
led1.off()
send(name + 'Green off'+ '\n')
else:
print('LED off')
led1.off()
led2.off()
# Send BT
def send(men):
uart.write(men)
# Records BT events for received data
uart.irq(handler=receive)
⊝⊝⊝
274
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E136: Read temperature with DHT11© and Bluetooth.
Develop a program in MicroPython© for ESP32©
that uses the DHT11© sensor to measure ambient
temperature and humidity.
Via a Bluetooth connection, send the temperature
reading to an external device for viewing. It must
also have an LED to indicate that the read request has
been received.
•E137: RGB control with PWM via Bluetooth.
Create a MicroPython© code that uses a pulse
with modulation or PWM signal to control an RGB LED
color and luminosity.
Use the BLE Bluetooth of the ESP32© connection
to receive several commands from an external device as
mobile or tablet, to change the LED color (red, green,
blue) and adjust the intensity of each color
individually according to a table of percentages
included in a dictionary.
•E138: WiFi, Bluetooth internal temperature.
Configure a script on the ESP32© to connect to a
WiFi network and, using Bluetooth, send the internal
temperature of the ESP32© to an external device.
It also allows, through simple Bluetooth
commands, to activate or deactivate the WiFi
connection. The script must contain the necessary
console and Bluetooth messages as well as possible
error controls.
•E139: Touch and LED controlled by Bluetooth.
Create a script that uses a touch switch to turn
an LED on and off. Use the Bluetooth connection to
activate or deactivate the touch button, as well as
communicate the current status of the LED on or off.
275
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
•E140: ADC input and DAC generation with Bluetooth.
Implement a MicroPython© code that reads the
analog input of a potentiometer using the Analog-
Digital Converter (ADC) of the ESP32©, view other
exercises.
Use the BLE Bluetooth of ESP32© connection to
send the potentiometer value reading to an external
device.
On the other hand, it must allow controlling an
analog output electronic signal, generated by the
Digital-Analog Converter (DAC), using BLE Bluetooth
several commands and to generate an audio signal with
volume and frequency controlled by these commands.
⊝⊝⊝
276
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 28:
ESP32© Internal Timer
On some occasions we will need a script to
ex e cu t e a s p ec i fi c fu n c ti o n p e r io d ic a ll y a nd
independently of the normal
execution of the script that
contains this function. To perform
this task, the ESP32© has either a
hardware timer (the ESP32©
electronics controls the
interruption) or a software timer (a
special software function controls
the interruption).
The hardware timers, up to a maximum of 4, are
controlled by the ESP32© hardware itself in the
background and independently of the software that is
running and therefore are more precise and consume
less energy as they are executed by specific and
expressly dedicated hardware to this process, on the
other hand software timers can be more configurable
but less precise and with greater consumption.
1. HARDWARE TIMER
In this exercise we will start by looking at the
timers implemented by hardware and for this we will
use, as the main loop, the example of flashing 2 LED
and as the timer function the presentation of a
Boolean variable that will change its True/False state
each time the interruption is executed by timer.
T h e s c r i p t i m p o r t s t h e time a n d machine
libraries to manage the timer and the GPIO, defines
the GPIO to be used for the LED as output and the
277
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
event control times (LED activation and IRQ of the
timer), specifies the function funcion(), which
indicates that the timer has been activated,
configures the timer timer with the IRQ time, the mode
of operation (in this case periodic) and the function
it points to.
Finally, the script includes the main loop where
the activated LED are seen, the corresponding
exception controls and the keyboard stop.
The ESP32© supports up to 4 simultaneous
hardware timers that can be configured as
Timer.PERIODIC so that the timer interrupt is executed
periodically or Timer.ONE_SHOT so that it is executed
only once.
############################
# E028_TIMER1_HW.PY: Hardware timer management
# INPUTS: Interruption time, LED activation time
# OUTPUTS: Timer message and LED flashing in polling
############################
import time # Timing management
from machine import Timer, Pin # Timer, GPIO manager
# GPIO and variables definition
led1 = Pin(26, Pin.OUT) # Green
led2 = Pin(27, Pin.OUT) # Red LED
led1.off() # Green off
led2.off() # Red off
T_ACT = 1 # LED activation seconds
T_IRQ = 4000 # IRQ by timer in ms
# Function to execute when the timer is triggered
def function(_): # Must have an argument
print('-->Timer activated...')
# Configure hardware timer (Timer(0) here)
timer = Timer(0) # Number (#) can't be negative
# Timer 0 calls function() every T_IRQ useconds
timer.init(period=T_IRQ, mode=Timer.PERIODIC,
callback=function)
# Main program
try: # Errors control
while True:
print('Green-', end='') # Without line break
led1.on() # Green on
time.sleep(T_ACT)
led1.off() # Green off
print('Red-', end='') # Without line break
led2.on() # Red on
278
Electronics & MicroPython© with ESP32©: 80 complete projects
time.sleep(T_ACT)
led2.off() # Red off
except:
print('\nProgram completed') # Line break
led1.off() # Both LED off
led2.off()
timer.deinit() # Stops timer
2. TIMER SOFTWARE
On this occasion we will implement two software
timers similar to the exercise proposed above and that
will perform similar tasks. Note the difference in the
construction of the timer made with negative values.
The number of timers created is not specifically
limited and will depend on the amount of ESP32© memory
available, etc.
The script is similar to the previous one but we
define two timers: timer_0, which calls funcion_0()
that presents a counter and timer_1, which calls
funcion_1() that presents the RTC time of the ESP32©
adjusted to the local time and formatted
appropriately.
############################
# E028_TIMER2_SW.PY: Two software timers manager
# INPUTS: IRQ timer time and LED activation time
# OUTPUTS: Timer1, timer2, LED polling message
############################
import time # Timing manager
# Timer, GPIO, RTC management
from machine import Timer, Pin, RTC
import ntptime # RTC update
led1 = Pin(26, Pin.OUT) # Green LED
led2 = Pin(27, Pin.OUT) # Red LED
led1.off() # Green off
led2.off() # Red off
T_ACTI = 1 # Activation LED time in seconds
T_IRQ0 = 4000 # Tiempo IRQ timer_0 en m seconds
T_IRQ1 = 10000 # Tiempo IRQ timer_1 en m seconds
local = 2 # Local hour
counter = 1 # Timer_0 counter
# I update the RTC of the ESP32© from the Internet
ntptime.settime()
# Function to be executed when timer_0 is triggered
279
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
def function_0(_): # Must have an argument
global counter
print(f'-->[{counter}] Timer_0 activated...')
counter+=1
# Function to execute when timer_1 is triggered
def function_1(_):
rtc = RTC() # Get RTC data
year, month, day, weekday, hour, minute, second,
subsecond = rtc.datetime()
hour = hour + local # Adjusts local hour
# Display hour, minutes and seconds
print("Hour: {:02}:{:02}:{:02}".format(hour,
minute, second))
# Configure two software timers (negative values)
timer_0 = Timer(-1) # Display Timer_0 activated
timer_1 = Timer(-2) # Display RTC data
# Timer 0 calls function_0() every T_IRQ0 m seconds
timer_0.init(period=T_IRQ0, mode=Timer.PERIODIC,
callback=function_0)
# Timer 1 calls function_1() every T_IRQ1 m seconds
timer_1.init(period=T_IRQ1, mode=Timer.PERIODIC,
callback=function_1)
# Main program
print('2 TIMER MANAGER')
try: # Errors control
while True:
print('Green-', end='') # Without line break
led1.on() # Green on
time.sleep(T_ACTI)
led1.off() # Green off
print('Red-', end='') # Without line break
led2.on() # Red on
time.sleep(T_ACTI)
led2.off() # Red off
except KeyboardInterrupt:
print('\nProgram completed') # Line break
led1.off() # Both LED off
led2.off()
timer_0.deinit() # Stops both timer
timer_1.deinit()
3. HARDWARE TIMER WITH LA1010©
To consolidate the timer exercises with ESP32©
and see that we can generate pulses of any type
(within the limitations of the ESP32©) and see them in
the LA1010© analyzer, or another, we created the
following script that includes the management of 3
hardware timer.
280
Electronics & MicroPython© with ESP32©: 80 complete projects
We import the machine libraries to control the
GPIO and the timer (0, 1 and 2), we assign the 3 GPIO
(25, 26 and 27) as output and specify the pulse times
in milli seconds (adjust consistently with the LA1010©
sampling).
We define the function pulse_x() where the pulse
is generated for each GPIO, we configure the 3 timers
with the activation period, the operating mode and the
function that each one calls and finally, in the main
loop, if there is an error we present it, being able
to finish the script and stop the 3 timers.
In the analyzer we observe the 3 pulses
generated in the probes that we have called Timer_0,
Timer_1 and Timer_2 and we compare them with a
reference 20Hz PWM signal.
281
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
We can adjust various pulse timings in the
script, both the total time, the HIGH time and the LOW
time and observe with the LA1010© how they behave.
With this system we can generate some types of pulses
but they do not have the precision of a PWM signal nor
the waveform that can be created with a digital to
analog converter.
Keep in mind that times created with time or
utime libraries can interfere with timers, especially
if the timers are software type.
Practice with the LA1010© the trigger option
with rising edge, HIGH, falling edge and LOW.
Likewise, test how defining a sample volume and
sampling frequency in the LA1010© affects the
exercise.
############################
# E028_TIMER3_HW_LA1010.PY: Three hardware timers
# INPUTS: Interruption timing
# OUTPUTS: 3 pulses to display on LA1010©
############################
from machine import Pin, Timer # GPIO and Timer
import time
sig0 = Pin(25, Pin.OUT) # Signal 0
sig1 = Pin(26, Pin.OUT) # Signal 1
sig2 = Pin(27, Pin.OUT) # Signal 2
T_I0 = 1 # Timer_0 interruption in ms
T_I1 = 2 # Timer_1 interruption in ms
T_I2 = 3 # Timer_2 interruption in ms
# Function to be executed when timer_0 is triggered
def pulse_0(_): # Must take 1 argument
sig0.on() # Generate pulse in GPIO25
sig0.off()
# Function to be executed when timer_1 is triggered
def pulse_1(_):
sig1.on() # Generate pulse in GPIO26
sig1.off()
# Function to be executed when timer_2 is triggered
def pulse_2(_):
sig2.on() # Generate pulse in GPIO27
sig2.off()
282
Electronics & MicroPython© with ESP32©: 80 complete projects
# Timer 0 calls pulse_0() every T_I0 ms
# Hardware timer: positive numbers or 0
Timer(0).init(period=T_I0, mode=Timer.PERIODIC,
callback=pulse_0)
# Timer 1 calls pulse_1() every T_I1 ms
Timer(1).init(period=T_I1, mode=Timer.PERIODIC,
callback=pulse_1)
# Timer 2 calls pulse_2() every T_I2 ms
Timer(2).init(period=T_I2, mode=Timer.PERIODIC,
callback=pulse_2)
# Main program starts here
print(f'Timer initiated in: {sig0}-{sig1}-{sig2}')
try: # Error control
print('INITS 3 TIMER IN 3 GPIO OF ESP32...')
while True: # Main loop simulation
time.sleep(.1)
except Exception as e: # If there's an error
print(f'Error: {e}') # Display it
except KeyboardInterrupt: # If <CTRL>+<C> is pressed
print('Timers stopped...')
Timer(0).deinit() # Stop 3 timers
Timer(1).deinit()
Timer(2).deinit()
⊝⊝⊝
283
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E141: Two timers.
Add to the first script with a timer, a second
timer, called timer_1, which is activated every 10
seconds, which displays the internal temperature and
time of the ESP32©, which must be previously updated
from the Internet.
•E142: Timer and LED.
Create a script that flashes a couple of LED at
every hour on the hour. Keep in mind that the hardware
or software timers of the ESP32© do not have this
option directly, therefore we will have to create a
timer that checks the RTC of the ESP32© for polling
and acts accordingly.
•E143: Timer with ONE_SHOT.
Design a MicroPython© script that uses a timer
with the ONE_SHOT option and that is activated when
there is an alarm, for example, the internal
temperature of the ESP32© rises above a pre-
established limit.
When the alarm goes on, 2 options will be
presented: ignore the alarm, which will stop the
script, or activate the alarm again, which will
request a new reference level and activate the timer's
ONE_SHOT option again.
•E144: 4 timers, 2 hardware and 2 software.
Create a program in MicroPython© for ESP32© that
uses 4 timers (2 hardware and 2 software), with four
different functions: on/off an LED, read the state of
a button as if it were by IRQ and present its state,
send a email with the temperature of the ESP32© and
send an SMS indicating that there is an alarm (see
subsequent exercises if necessary). Decide why and
what type of timer (hardware or software) is best for
each function.
284
Electronics & MicroPython© with ESP32©: 80 complete projects
•E145: Timer vs IRQ.
Use a periodic timer to read the state of a
button and use an IRQ to read the state of another
button, see how both situations work and determine
which is the most convenient (response, consumption,
CPU usage, simplicity, bounces, etc.) to manage
keystrokes and why.
•E146: Timer and DHT11© double sensor.
Implement a MicroPython© script that uses a
hardware timer on the ESP32© to read data from a
DHT11© double sensor every 10 seconds and display it
on the console while the main loop changes colors on
an RGB LED.
•E147: Timer and ADC.
Create a script that uses a software timer to
perform analog readings with an Analog-to-Digital
Converter (ADC) every 5 seconds. Display the results
in the console and make them accessible via the Web.
Simulates analog data with an ESP32© GPIO touch sensor
or a traditional button.
•E148: NO-IP© and remote sensor.
Design a system that uses a hardware timer to
collect data from a remote sensor (for example, a
potentiometer) and send this data to a IP using the
NO-IP© dynamic DNS service, (see later exercises) or
via from the local network, without using NO-IP©.
•E149: Timer and remote display.
Develop a program that uses a hardware timer to
take readings from a specific sensor. These readings
and the time at which they are taken must be visible
remotely on a Web with local or global access. The
script will be started or stopped by voice commands on
a Google Home© assistant (see later exercises).
•E150: Hardware vs software timer.
Create a script where a hardware and a software
timers are used to blink an LED using a button.
Analyze the advantages and disadvantages of both cases
and propose specific practical cases where it is more
convenient to use each timer mode.
285
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
•E151: Timer with RGB LED.
Create timers that manage basic colors of an RGB
LED, visualize with the LA1010© the pulses that reach
the LED, practice modifying the parameters of the
timers and those of the LA1010©.
•E152: RGB LED with push button and IRQ.
Connect a push button to the ESP32© and
configure an interrupt (IRQ) to detect its pushes.
Each time the button is pressed, a timer is activated
that generates flashes on a certain LED. Visualize the
blinks with the LA1010©.
•E153: Timer and scanning of WiFi networks.
Use a timer to scan the available WiFi networks
periodically. When detecting a network, it will
activate an LED whose GPIO will be displayed with the
LA1010©.
•E154: WiFi scanning and Web server.
Add to the previous exercise that the ESP32© is
activated as a Web server so that when we access it we
can view the list of detected WiFi networks.
•E155: Timer and passive buzz.
Connect the passive buzz to a GPIO of the ESP32©
and generate an audible signal using a hardware timer
that varies the tone of the signal when using a
button. Use the LA1010© to view the generated pulses,
change the trigger if necessary.
•E156: LED control with DAC.
Use the ESP32©'s Digital-Analog Converter (DAC)
to generate a signal that changes the brightness of an
LED. Connect the GPIO to the LA1010© and see how an
analog signal affects a digital analyzer, modifying
the time base. Try a timer to create the LED
brightness pulse and also change the trigger value on
the LA1010©. Explain what happens when we visualize
these pulses.
⊝⊝⊝
286
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 29:
KY-040© Rotary Encoder
In this exercise we will see what a rotary
encoder is, such as the KY-040© or similar, how to
program it and how to integrate it, in a simple way,
into any of the previous exercises.
A rotary encoder is
nothing more than a sensor
that encodes the angular
position of a rotary knob on
a device, for example: the
volume control or station
search of a car radio, the
air conditioning control,
the microwave selector or a
modern washing machine, the
Thermomix© control, the
mouse scroll, etc.
In this way, these
sensors measure angle,
angular velocity, direction
of rotation, length,
position, acceleration, etc.
When used, an integrated
circuit inside sends a
series of electronic pulses,
which allows a script to
increase or decrease a
variable, change pages,
raise/lower a window with
the computer mouse, regulate
sounds, change temperatures,
change parameters, etc.
287
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The KY-040© rotary encoder also incorporates a
push button, so that we will have a compact device
with multiple functions.
Therefore, a rotary encoder, or signal decoder
is a very useful and very interesting sensor device to
integrate into one of our exercises or our projects.
There are basically two types of rotary
encoders:
• Absolute: In them the encoder indicates the
current position of the selector, therefore they
behave like angle transducers.
• Relative or incremental: In them the encoder
indicates the movement of the selector.
Most rotary encoders have 5 pins and physically
perform 3 basic functions: left turn, right turn, and
on/off by pressing the push button.
There are many models on the market, here we
have used the Keyes© KY-040© model or similar, with
the following pins:
Pin Signal Function
1 GND Ground
2 +3.3V Power supply
Normally open (NO) terminal of push button
3 SW
switch, connect to GND
4 DT 0 (LOW) means that the knob turns
5 CLK 0 clockwise, 1 counterclockwise
Here we will test both the switch function, SW
pin connected to GPIO25 of the ESP32©, and the “right,
left” function, DT and CLK pins connected to GPIO32
and GPIO35 respectively.
We will also connect 2 LED to display the
direction of rotation of the rotary encoder.
288
Electronics & MicroPython© with ESP32©: 80 complete projects
In idle state the
decoder is with DT=H.
If any rotation occurs,
DT goes to state L and
if CLK=L the rotation
is cl o ck w is e an d if
CLK=H, the rotation is
counterclockwise.
On the other hand, if SW=L indicates that the
button has been pressed and if SW=H, the button is at
rest.
289
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
If at any time the rotary encoder button is
pressed again, both LED turn off, the counter is reset
and the system is positioned in its initial state.
Both the button pin (pin_SW) and the data pin
(pin_DT) are controlled by IRQ, in this way the
response of the script is immediate and “bouncing” of
the buttons is avoided.
In the script we import the machine libraries to
control the decoder logic and time to control the
operation times and we define the pins to use with the
following diagram:
ROTARY DECODER CONNECTIONS
KY04© ESP32© Observations
1 GND GND Ground
2 +3.3V +3.3V Power supply
3 SW GPIO25 Pushbutton
4 DT GPIO32 Spin detection
5 CLK GPIO35 Direction of rotation
In rest state pin_DT is in HIGH state and when
going down to LOW state, if pin_CLK is in LOW, the
rotation is to the right (clockwise direction) and if
it is in HIGH state, the rotation is to the left
(counterclockwise direction).
When the SW_pin, connected to GPIO25, goes from
HIGH to LOW, it indicates to the ESP32© that the
button has been pressed.
A red LED is also added to GIPO27 and a green
LED to GPIO26. Global variables are defined to control
the counter, decoder states, increment/decrement step.
Th e setup() function is defined where the pins
for the LED are initialized as output and the decoder
pins as input for the ESP32©.
290
Electronics & MicroPython© with ESP32©: 80 complete projects
The managements for the button and data are
created as inputs treated by interruption when going
from HIGH to LOW level and that point to the clear()
and spinning() functions respectively.
T h e spinning() function that controls the
decoder logic, the direction of rotation, the counter
and the activation of the LED is detailed with the
algorithm described above.
The clear() function is defined that turns off
the LED and indicates the use of the decoder, the
loop() function that represents a main loop of the
script and in which any other function could be
integrated.
Finally we have the stop() function that stops
the keyboard program and turns off the LED.
In the LA1010© analyzer, the DAT (pin_DT) and CK
(pin_CLK) signals are defined in addition to a PWM
signal that, as always, serves as a reference. In this
case, the vertical marks A1-A2 and also B1-B2 have
been added to more easily compare the states between
DAT and CK.
•Right turn: In this case, when the DT_pin goes LOW it
indicates that a turn has been initiated and since the
CLK pin is at LOW, the turn has been made to the
right. When the turn is made to the right, the script
increments the counter by the previously defined step,
turns on the green LED and turns off the red LED and
displays it on the console.
•Left turn: Now, when the DT_pin goes from HIGH (rest)
to LOW it indicates that a turn has been initiated and
since the CLK_pin is at HIGH, the turn has been made
to the left. When the turn is made to the left, the
script decrements the counter by the previously
defined step, turns off the red LED and turns on the
green LED and displays it on the console.
We can see the logical levels discussed in both
cases in the LA1010© analyzer.
291
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Right turn (CLOCKWISE)
When DAT=LOW CK goes from HIGH to LOW
Left turn (COUNTERCLOCKWISE)
When DAT=LOW CK goes from LOW to HIGH
292
Electronics & MicroPython© with ESP32©: 80 complete projects
############################
# E029_ROTARY_DECODER.PY: Rotary decoder
# INPUTS: Decoder rotation and push button status
# OUTPUTS: Turn counter and LED
############################
'''KY-040© ESP32© Observations
-----------------------------------------
1 GND GND Ground
2 +3.3V +3.3V Power supply
3 SW GPIO25 Pushbutton
4 DT GPIO32 Spin detection
5 CLK GPIO35 Spin direction'''
from machine import Pin # GPIO management
import time # Timing control
# Definition of GPIO in the ESP32©
pin_DT = 32 # GPIO32 DT o data
pin_CLK = 35 # GPIO35 CLK o clock
pin_SW = 25 # GPIO25 SW o pushbutton
led_r = 27 # GPIO27 Red LED
led_v = 26 # GPIO26 Green LED
# Global Variables
counter = 0 # Spin counter
last_state_CLK = 0 # Las state of CLK
step = 1 # +/- counter
change = False # Change spin direction
# Initial configuration function
def setup():
# LED out and off
for pin in [led_r, led_v]:
Pin(pin, Pin.OUT).value(0)
# Input pins configuration
Pin(pin_DT, Pin.IN)
Pin(pin_CLK, Pin.IN)
# Pin_SW/DT inputs, pull-up and IRQ falling edge
Pin(pin_SW, Pin.IN, Pin.PULL_UP).irq(trigger=Pin.
IRQ_FALLING, handler=clear)
Pin(pin_DT, Pin.IN, Pin.PULL_UP).irq(trigger=Pin.
IRQ_FALLING, handler=spinning)
# Function handles the rotation of the rotary encoder
def spinning(_): # An argument must be added
global counter, last_state_CLK, change
# Read the current status of CLK
current_state_CLK = Pin(pin_CLK).value() # CLK
if last_state_CLK==0 and current_state_CLK==1:
counter=counter-step # Decrement
print('COUNTERCLOCKWISE spin')
Pin(led_r).value(1) # Red LED on
Pin(led_v).value(0) # Green LED off
293
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
if last_state_CLK==1 and current_state_CLK==1:
Pin(led_r).value(1)
if change: # Change spin
print('COUNTERCLOCKWISE spin')
change = not change # Avoid repeat text
counter=counter-step
if last_state_CLK==1 and current_state_CLK==0:
counter=counter+step # Increment
print('CLOCKWISE spin')
Pin(led_r).value(0) # Red LED off
Pin(led_v).value(1) # Green LED on
if last_state_CLK==0 and current_state_CLK==0:
Pin(led_v).value(1)
if not change: # Change spin
print('CLOKWISE spin')
change = not change # Avoid repeat text
counter=counter+step
print('counter = %d' % counter)
last_state_CLK=current_state_CLK # Update
#Function handles reset push button interrupt
def clear(_): # An argument must be added
global counter, change
change = not change # Reset flag
print('\n' * 20) # Blank lines for cleaning
Pin(led_r).value(0) # Red LED off
Pin(led_v).value(0) # Green LED off
print('ROTARY DECO CONTROL WITH PUSH BUTTON')
print('Turn knob or press button...')
counter = 0 # Reset counter
print('Counter = %d' % counter)
# Main loop
def loop():
#print('\n' * 20) # Blank lines for cleaning
print('Turn knob or press button...')
while True:
time.sleep(0.01) # Main loop simulation
# For stop the program
def stop():
Pin(led_r).value(0) # Red LED off
Pin(led_v).value(0) # Green LED off
# Main input point to the program
if __name__ == '__main__':
print('\n' * 20) # Blank lines for cleaning
print('ROTARY DECO CONTROL WITH PUSH BUTTON')
setup() # Initial configuration
try:
loop() # Call to main loop
except KeyboardInterrupt:
stop() # Stop the program
⊝⊝⊝
294
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E157: RGB LED control with rotating deco.
Develop a script that uses a rotary decoder to
control an RGB. Turning the decoder clockwise
increases the value of a color, and counterclockwise
decreases the value. The push button will turn the LED
on/off. Try an RGB dictionary that scrolls, in both
directions, when we turn the rotary decoder.
•E158: Temperature with ADC and rotating deco.
Write a script for ESP32© that uses the rotary
decoder to adjust the settings (measurement scale) of
a temperature sensor connected to the ESP32© ADC
converter.
•E159: Timer, PWM and rotary decoder.
Develop an adjustable timer using the decoder.
Use PWM to control an LED, and turning the decoder
sets the timer time that should be displayed on the
console. Pressing the button starts the timer.
•E160: Analog signal with DAC and decoder.
Create a script that uses the rotary decoder to
adjust the frequency of an analog signal generated by
the ESP32© Digital-Analog Converter (DAC) and make it
audible on a passive buzz. Pressing the button
switches between different waveforms: square, sine and
triangular.
•E161: RTC control with rotary decoder.
Develop an adjustable clock using a decoder.
Turns and presses will set the RTC time and date.
There will also be a hardware timer that will flash a
red LED every 5 seconds.
⊝⊝⊝
295
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 30:
Frequency meter with 7555©
In this exercise we are going
to build a frequency meter with
a 7555© circuit, which is a
timer with the same
functionalities as the famous
NE555©, but since it uses CMOS
technology, it can be powered at
+3.3V and therefore we can
connect it directly to our
ESP32© without any problem.
It will be a test frequency
meter and not very precise (it
measures frequencies between 30Hz and 20kHz), since to
improve it would be necessary to have very well
calibrated components, with very little tolerance and
temperature compensation, connected to a precision
power source, etc. but in any case the exercise is
valid to practice with these elements.
We are going to use the 7555© integrated
circuit, which is a timer or pulse generator (from 0
to +3.3V), very low cost, easy to obtain, which can be
used in multiple exercises and which can basically be
configured in three very interesting modes:
Monostable: if the input of the circuit is
activated, its output changes for a while and returns
to the initial state, that is, the system has 1 state.
Bistable: when the input of the circuit is
activated, its output changes its state and does not
return to the initial state until the input is
activated again, therefore the circuit has 2 states.
296
Electronics & MicroPython© with ESP32©: 80 complete projects
Astable: When the input of the circuit is
activated, its output periodically changes between the
initial and final state, therefore generating a square
wave that varies between the 2 states.
This last configuration is the option that we
are going to use in this exercise to generate a square
wave, of a certain frequency and that we will later
measure with the ESP32© using a GPIO controlled by IRQ
and a simple script.
We need the following components (others with
other values can be used to obtain another output
frequency):
1 7555 © timer
2 100nF capacitors
2 10k Ω resistors
1 1k Ω resistor
The pins of the 7555©, totally equivalent to the
NE555© (+5V), are as follows:
[1] GND or 0V.
[2] trigger or lower
comparator.
[3] output: 0 or +3.3V.
[4] reset: active at +3.3V.
[5] control: change the
trigger levels or also
comparison levels.
[6] threshold: upper comparator.
[7] discharge: capacitor discharge.
[8] power: between +2V and +18V.
Other interesting features of the 7555©:
• Very low consumption of 80 μ A.
• High frequency, up to 1MHz.
• Timing range from seconds to hours.
• Wide temperature range from -25ºC to 85ºC.
And the circuit diagram in astable mode that we
will use is the following:
297
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
If we test it with the iCircuit© application, we
observe how a wave similar to a “square” wave of the
frequency calculated with:
1
f= where:
ln(2)∗C2∗( R1+2∗R2)
ln(2)=0.69315 (Neperian Logarithm)
−7
C2=100nF= 10 F.
R1=R2= 10K Ω, therefore:
1
f= ≡481Hz
0,693∗10 ∗(104 +2∗104 )
−7
The frequency measured with the script is 488Hz
and we can observe it on an oscilloscope.
298
Electronics & MicroPython© with ESP32©: 80 complete projects
To carry out the
measurement with the
ESP32© we count,
through the GPIO12,
the pulses generated
by the 7555© for a
certain time, for
example 1 second, and
using the IRQ method.
The frequency f
will be the result of
dividing the number
of pulses by the spe-
cific time selected.
We can try with
various values of R1,
R2 and C2, see the
generated frequencies
and the measurement
provided by our
frequency meter.
In t h e MicroPy-
thon© script we
i m p o r t t h e machine
and time libraries to control the GPIO and the times
respectively. We configure the GPIO12 as input and de-
fine global variables that we are going to use for the
pulse counter, counting time, separation between read-
ings and previous frequency.
We define the function pulse_counter() which is
called by the IRQ on the rising edge generated by the
square wave of the 7555© when entering through the
GPIO12. We define the function frequency_meter(),
which calculates the frequency f by dividing the
number of pulses counted by IRQ by the time [wait].
Finally we write the main loop of the script
that presents the frequency generated by the 7555©
whenever there is a value change. It also incorporates
stopping the script when pressing <CTRL>+<C> from the
console.
299
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E030_FRECU_7555.PY: Frequency meter with 7555 timer
# INPUTS: GPIO12 and wait times
# OUTPUTS: Measured frequency
############################
from machine import Pin # GPIO manager
import time # Timing management
# Pins configuration
pin_input = 12 # GPIO frequency meter input
pin = Pin(pin_input, Pin.IN)
# Global variables
pulses = 0 # Pulses counter
wait = 1 # Calculus time
reading = 2 # Time between readings
frec_ant = 0 # Previous frequency
# Pulses counter function
def pulse_counter(_): # Must have an argument
global pulses
pulses += 1 # Increments counter
# Configure pulse counter by IRQ
pin.irq(trigger=Pin.IRQ_RISING, handler=pulse_counter)
# Function to measure frequency
def frequency_meter(wait):
global pulses, frequency
pulses = 0 # Reset counter
time.sleep(wait) # Wait for a while
num_pulses = pulses # Counted pulses
frequency = num_pulses / wait # Frequency f
print(f'Calculate frequency in GPIO {pin_input}')
try:
while True:
frequency_meter(wait)
if frequency != frec_ant:
print(f'Measured frequency:
{frequency}Hz')
frec_ant = frequency
time.sleep(reading)
except KeyboardInterrupt:
print('Frequency meter stopped...')
⊝⊝⊝
300
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E162: Variable frequency.
Add to the previous circuit, in series with
resistor R1, a 50k Ω potentiometer P (variable
resistance) and measure the frequency generated with
various positions of P. See that we could vary it
between 180Hz and 480Hz, perform the corresponding
calculations with the previous formula.
•E163: Control start/stop.
Perform a start and stop control of the pulse
generation of the 7555© using its pin ̄
R , setting it
to LOW with a GPIO of the ESP32©, protecting this GPIO
with a resistor if necessary.
•E164: Monostable mode.
Search the web how to configure the 7555© in
monostable mode and initiate a pulse from an ESP32©
GPIO. Add an LED to the output and observe how its
state changes. Calculate the pulse time with the
components, resistors and capacitors already seen.
•E165: Bistable mode.
Use the same circuit but configuring the 7555©
as bistable mode. It also calculates theoretically and
with the frequency meter created with the ESP32© all
the timings generated.
•E166: Generate PWM with 7555© and with ESP32©.
Using only the previous circuit and without
using the ESP32©, manage the brightness of an LED by
modifying the Duty Cycle of the 7555© between
predefined margins. Check if the PWM logic is positive
or negative. Generate a PWM wave in a GPIO of the
ESP32© and compare both, for example, with the
oscilloscope.
⊝⊝⊝
301
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 31:
HC-SR501© Motion Sensor
In this exercise we will see how to use a simple
but very powerful motion sensor such as the well-known
HC-SR501© or equivalent. A
motion sensor is a passive
infrared radiation (PIR) sensor
that detects the movement of
objects that emit heat and is
used in Home Automation projects
and electronic devices that
detect motion.
The basic features of the HC-
SR501© or equivalent are:
• Power supply at +5V.
• +3.3V output compatible with the ESP32©.
• It has two potentiometers to configure the
sensitivity adjustment (from 3 to 7 meters) and
the switching time (0.5-200s).
• Trigger adjustment bridge: single or
retriggerable.
The sensor connections are very simple:
• GND GND of the ESP32©.
• OUT Digital output 0 to +3.3V.
• VDC +5V.
• Switching time setting.
• Sensitivity adjustment.
• Jumper L (single trigger), H (retriggerable).
The switching time indicates how long the OUT
output is at H after motion is detected, the
sensitivity setting allows us to indicate the minimum
distance at which motion is detected, and the jumper
302
Electronics & MicroPython© with ESP32©: 80 complete projects
can be used so that the OUT output is at H for a
period of time. Single switching time or retrigger
every time motion is detected.
In this exercise we
have connected the
sensor to GPIO14 and an
LED connected to GPIO25
to indicate when motion
is detected.
In the script we
import the machine and
time l i b r a r i e s t o
control the GPIO and
control times.
The OUT pin of the
PIR is configured as an input for the ESP32© and the
LE D a s a n o ut p u t, i n it i a l ly t u rn i n g i t o f f.
Additionally, the management variables are defined.
The functions turn led_off() are added, which turns
off the LED after a certain time has elapsed,
c o n t r o l l e d b y t h e Timer(0), a n d t h e f u n c t i o n
detect_motion(), which when detecting movement,
checking the status of OUT, increases a counter,
presents the message detection, turns on the LED and
starts a one-shot timer that invokes the led_off().
303
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Finally, in the main body of the script, the
d e t e c t i o n f u n c t i o n detect_motion() is called
periodically. If we press <CTRL>+<C> the program
stops, turning off the LED and stopping the Timer(0).
############################
# E031_MOTION_SENSOR.PY: HC-SR501© motion sensor
# INPUTS: Motion detected by sensor
# OUTPUTS: LED and messages on screen
############################
from machine import Pin, Timer
import time
# Pines configuration
pin_sensor = 14 # Motion sensor pin
pin_led = 25 # Red LED
t_time = 2000 # Timer time in m seconds
counter = 0 # Activation counter
# Configure sensor in input and output LED
motion_sensor = Pin(pin_sensor, Pin.IN)
red_led = Pin(pin_led, Pin.OUT)
red_led.off() # LED off
# LED off function
def led_off(_): # Must include an argument
global counter
red_led.off() # Red LED off
print('LED off...')
counter = 0 # Start activation counter
# Motion detection function
def detect_motion():
global counter
if motion_sensor.value() == 1: # Detected
counter+=1 # Activation counter goes up
print(f'[{counter}], Motion detected!')
red_led.on() # LED on
# Timer turns off LED [t_time] m seconds
Timer(0).init(period=t_time, mode=
Timer.ONE_SHOT, callback=led_off)
print('HC-SR501© MOTION SENSOR')
print('Approach the sensor...')
# Main loop to scan sensor status
try:
while True:
detect_motion()
time.sleep(.5) # Wait .5s between scans
except KeyboardInterrupt:
print('Program completed...')
red_led.off() # LED off
Timer(0).deinit() # Timer stop
⊝⊝⊝
304
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E167: Sensor controlled by IRQ.
Use the previous script but modify what is
necessary so that the motion detection is controlled
by IRQ instead of by polling with the detect_motion()
function.
•E168: Motion detection and relay.
Create a script that controls a motion sensor by
IRQ and two control LED. If motion is detected,
activate a relay to turn on an electrical load, such
as a lamp simulated by another LED.
•E169: Servo, push button and motion sensor.
Configure a servo motor connected to the ESP32©
and monitor, by IRQ, the state of a button to control
the movement of the servo manually. If movement is
detected with the HC-SR501 sensor, move the servo to a
predetermined position that will be reset with the
push button.
•E170: DS18B20© sensor, detector and relay.
Use a motion detector so that when activated,
the room temperature is read using the DS18B20©
sensor. If the temperature exceeds a predefined
threshold, activate a relay simulating being connected
to a fan.
•E171: RGB LED, ADC and motion sensor.
Read the value of a potentiometer with ADC and
change the color of an RGB LED by PWM when necessary.
If motion is detected with the sensor, the color of
the RGB LED changes automatically depending on the
time the sensor is triggered, for example in traffic
light mode: green, yellow and red.
⊝⊝⊝
305
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 32:
HLK-LD2410C© Human
Presence Sensor
In this exercise we will see a much improved
option for a motion sensor, it is a human presence
sensor, that is, a sensor that detects movement but
also detects presence and therefore, although people
who are within the area of the sensor are completely
“still”, the sensor also detects them, making their
use much more comfortable and reliable.
We will use the HLK-
LD2410C© presence sensor,
it is a simple and cheap
human presence sensor that
uses 24GHz microwave radar
technology to detect the
presence of people in a
certain area.
Presence sensors are
capable of detecting
subtle movements and can
penetrate through
materials such as plastic,
wood, etc., making them
suitable for home security
and automation appli-
cations.
These human presence
sensors are widely used in
automatic lighting
systems, security systems, access control systems and
multiple other devices that require precision and
reliable motion and presence detection.
306
Electronics & MicroPython© with ESP32©: 80 complete projects
The HLK-LD2410C© allows multiple adjustments to
be made to adapt to the needs of each project,
highlighting the following fundamental
characteristics:
• Plug and play, easy assembly, only needs power,
very easy to set up.
• The detection distance between 0.75 and 6 meters
configurable. Wide detection angle, coverage up
to ±60 degrees.
• Accurate identification within its range,
supporting detection range division, and
eliminating out-of-range interference. It has an
APP that allows Android©, with version LD2410C©,
to connect via Bluetooth. There is also an
application for Windows©. It has two TX and RX
pins that allow connection, via UART, to the
ESP32©. With these three options we can
configure multiple operating parameters of this
wonderful sensor.
• Its si ze is mi ni ma l, su pp or tin g v ar io us
installation methods such as ceiling hanging or
wall hanging. Avoid obstacles close to the
sensor.
• Power supply: +5V and 200mA, therefore we should
NOT connect it to the ESP32©. It has an OUT
output that is activated at HIGH when detection
occurs. This output is +3.3V and therefore we
can connect it to a GPIO of the ESP32©. Remember
that the GND of the sensor and the GND of the
micro controller must be connected.
We can use the ESP32©, through the UART
connection, to configure the parameters of this sensor
but I recommend using the HLKRadarTool© APP available
on Android©.
Once installed, we activate Bluetooth on our
mobile. We start the APP and the device HLK-
LD2410_C90B© or similar will appear. If we click on
the name, we will have two screens where, in the first
one we see a traffic light that tells us if there is
movement, static presence (with an indication of the
distance in cm) or neither of the two.
307
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The default Blue-
tooth connection
password is HiLink,
which we can easy modi-
fy in the application.
In the first window
of the APP we can see
if the detection is mo-
tion, static or without
detection, as well as
four graphs where we
can view the evolution
of the movement, the
static detection, the
detection range and the
luminosity level.
In the first two
graphs we can see the
target sensitivity lev-
els that we can
configure in the second
window.
In the second
window we can see the
firmware version and
with the option to
update it, the minimum
shooting distance, the
password change, the
duration of the output
pulse, the sensitivity
of the light sensor,
the detection level
HIGH or LOW (e.g.
interesting if we want
to connect a relay to
the OUT pin), the
connection speed, the
target sensitivity
levels depending on the
distance, etc.
308
Electronics & MicroPython© with ESP32©: 80 complete projects
If we wanted to configure the sensor from the
ESP32©, we could connect the TX and RX pins to two
GPIO of the micro controller and use the ld2410.py
library. Remember to load it into the ESP32© memory
and pass the UART parameters.
In this exercise we are going to configure the
sensor via Bluetooth and connect it to the ESP32© in
on/off mode as the same as we did with the infrared
motion sensor and using the same script, therefore we
will connect the OUT pin of the sensor to GPIO14 and
an LED to identify the detection on the GPIO26.
When the sensor detects movement or presence,
the OUT output goes to HIGH (also configurable to LOW
in the APP) and with it the GPIO14 of the ESP32©,
which will indicate the detected movement and turn on
the LED in the GPIO26.
⊝⊝⊝
309
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E172: Presence sensor and relay.
Add to the previous circuit a relay connected to
a GPIO of the ESP32© and change the script so that
when movement is detected the relay is activated. Try
doing the same by directly connecting the relay to the
OUT pin of the sensor without using the ESP32©. Please
note that the relay may have reverse logic. Take
advantage of the +5V power supply to power the sensor
and the +3.3V output to power the relay. Do not power
the presence sensor from the ESP32©.
•E173: Presence sensor and PWM.
Design a system that uses the LD2410© presence
sensor and an ambient light sensor or LDR connected to
the ESP32© ADC or software simulation, to control the
light intensity of a lamp.
When motion is detected with the presence sensor
and the ambient light is low, gradually increase the
light intensity using the PWM signal. When no motion
is detected for a set period of time, gradually reduce
the light intensity until it turns off completely.
•E174: Alarm with presence sensor and DAC.
Create an alarm system using the presence sensor
and a buzzer. When motion is detected with the LD2410©
human sensor, activate an audible alarm on the buzzer
using the analog signal generated by a DAC converter.
The intensity of the alarm sound may vary
depending on the distance detected by the presence
sensor.
•E175: Position control with servo and RTC.
Design an automatic door opening mechanism that
uses a servo motor to open and close the door. Use the
human presence sensor to detect when someone
approaches the door.
310
Electronics & MicroPython© with ESP32©: 80 complete projects
Additionally, program the system to
automatically close the door at certain times of the
day using information from the external RTC, such as
at dusk.
•E176: Activity with presence sensor and RTC.
Develop an activity recording system that uses
the LD2410© human presence sensor to detect the
presence of people in a room. Add a DS18B20© high
precision temperature sensor and record the ambient
temperature at the time of detection.
Store all information in a circular data file on
the ESP32© and use the external RTC to also record the
date and time of each motion detection. Implement an
algorithm to send, at a certain time or using a
hardware time, a report for each day by email.
Finally, create an acoustic alarm using PWM or
DAC with a passive buzzer and sends a message via SMS
when a certain presence and/or temperature control
algorithm indicates it.
⊝⊝⊝
311
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 33:
ESP32© and TTP223©
Touch Sensors
In this exercise we will see a very interesting
sensor, easy to use, widely used in everyday life and
which is a touch sensor that detects, without any
mechanical part and using the conductivity of the
human body, when we touch it with a finger.
These sensors have multiple advantages over
traditional buttons that we could summarize as:
• They are intuitive: They offer a very easy-to-
use user interface, as they allow completely
direct interaction with your fingers.
• Greater precision: They allow precise, detailed
and reliable entry.
• Adaptability: They adapt to various shapes and
sizes, making them ideal for a variety of
devices and applications.
• Durability: Touch sensors, since they do not
have moving mechanical components susceptible to
wear, are much more durable than traditional
input devices.
• Ease of cleaning: In several clean environments,
such as medical or industrial applications,
touch sensors are easy to clean and disinfect,
being airtight and with completely smooth
surfaces.
For all these reasons and others, tactile
sensors are widely used in human activities that
require finger presses: elevator buttons, control
panels, traffic lights, automotive, ATM, household
appliances, etc.
312
Electronics & MicroPython© with ESP32©: 80 complete projects
Luckily, the ESP32© has special GPIO that can be
configured as touch input pins, that is, pins that
activate HIGH when touched with a finger. This allows
the use of buttons to be implemented in a very simple
way since hardly any additional hardware is needed.
The ESP32© WROOM C3 has 10 GPIO with this
function, therefore we can implement several buttons
to perform multiple functions. In this exercise we
will use an ESP32© touch sensor, this is a cable
connected to GPIO32 and two LED, in GPIO26/27 that
will alternate as the touch sensor is touched.
It is important that we adjust the sensor
threshold and for this it is convenient to read the
values of GPIO32, which will be defined as touch with
tactile_pin= TouchPad(Pin(32)) and will be read with
tactile_pin.read(). In our case, the values exceed 670
when the pin is at rest, dropping to 80 when held with
two fingers, having to experiment until finding the
appropriate value, in this case 300.
313
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
If we do not have GPIO available with the touch
function, we can use an external touch sensor such as
the TTP223© or similar.
This external touch
sensor has an activation zone
that attacks a special circuit
necessary to amplify and
stabilize the signal that the
human body captures and that
generates an on/off type
digital signal.
The amplifier circuit
used, whi ch requir es high
sensitivity and gain, is the
TTP223-BA6© or equivalent.
Basically it is a set of
cascade transistors that
amplify, stabilize and self-
regulate the very low voltage
provided by the human body by
acting as an antenna.
In the schematic of the
amplifier circuit we can see
how the TTP223© touch sensor
attacks pin 1 of the TTP223-
BA6© amplifier circuit with
the capacitor C1 acting as a
high-pass filter and after
amplifying the signal, the Q
output is activated that
attacks the GPIO32 of the
ESP32© and that, in this example, we must configure it
as an input instead of configuring it as a touch
sensor. Resistor R0 powers the LED that indicates that
there is current in the circuit, R1 does the same with
D1, R2 acts as a pull-up of Q and C2 stabilizes the
+3.3V line.
By default, the TOG signal activates the
“Toggle” function, that is, the Q states (HIGH and
LOW) alternate with each press.
314
Electronics & MicroPython© with ESP32©: 80 complete projects
In the MicroPython© script to control the
internal touch sensor of the ESP32©, the utime
libraries are imported for the management of control
times and machine for the control of the GPIO and
touch sensor.
The touch GPIO are started as touch input and
the LED as output, turning on the green LED, turning
off the red LED, deleting the console, presenting the
script start message and defining the minimum
sensitivity already mentioned for the sensor to
trigger.
The touch sensor is read with the call to
tactile_pin.read() by polling and to avoid flashing of
the LED, caused by multiple readings, it is checked
that the pin value is within a range and at various
times.
Finally, a waiting time is used between readings
that we must adjust, the corresponding exceptions that
may occur are controlled, and the script can be
stopped by pressing <CTRL>+<C> in the console.
315
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E033_TACTILE_SENSOR_POL.PY: Touch sensor management
# Scanning touch sensor in GPIO32 by polling
# INPUTS: Touch sensor status
# OUTPUTS: GPIO26 green and GPIO27 red alternation
#########################
import utime # Time management
from machine import Pin, TouchPad # GPIO and touch
# Pins configuration
tactile_pin = TouchPad(Pin(32)) # Touch
green_led = Pin(26, Pin.OUT) # Green LED
red_led = Pin(27, Pin.OUT) # Red LED
green_led_state = True # Green LED current status
green_led.on()
red_led.off()
print('\n' * 50) # 50 lines to clean screen
print('ESP32© INTERNAL TOUCH SENSOR CONTROL')
print('Touch the sensor...')
sen = 300 # Touch sensitivity, change if desired
# Read sensor status and update LED
def led_update():
global green_led_state # Used outside
tactile_value = tactile_pin.read() # First reading
utime.sleep_ms(100) # Wait to avoid errors
new_tactile_value = tactile_pin.read() # Second
# See if it is touched
if tactile_value< sen and new_tactile_value< sen:
print('\n' * 50) # Clean console
if green_led_state:
green_led.off() # Green LED of
red_led.on() # Red LOD on
print('Red')
green_led_state = False
else:
green_led.on() # Green LED on
red_led.off() # Red LED off
print('Green')
green_led_state = True
print('Touch the sensor...')
try:
while True: # Stop with <CTRL>+<C>
led_update()
utime.sleep_ms(200) # Time between reading
except KeyboardInterrupt:
print('\n' * 50) # Clean console
print('Program completed.')
finally:
# Turns off both LED when exiting the loop
green_led.off()
red_led.off()
⊝⊝⊝
316
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested Exercises:
•E177: Touch interruption reading.
The touch pins of the ESP32© do not support
direct control by interruptions generally but we can
simulate touch interruptions with normal interruptions
generated periodically by a timer or by polling.
Design a MicroPython© script similar to the
previous one but that reads the GPIO configured as
touch when a timer interrupt is generated. Use the
following function:
# Timer that calls function every second.
timer = Timer(0)
timer.init(period=1000, mode=Timer.PERIODIC,
callback=upload_led)
•E178: Reading DHT11© and touch pins.
Develop a MicroPython© script that uses a DHT11©
dual sensor to measure temperature and humidity, and
also include tactile reading, using several pins to
control the display of separate results and a third to
convert the read temperature into various measurement
scales.
•E179: Brightness control with touch and PWM.
Create a program that uses touch pins to adjust
the brightness of an LED in increasing vs decreasing
order, using PWM signals, by pressing touch pins.
•E180: Signal generation with DAC and touch.
Build a MicroPython© script for ESP32© that uses
several special touch pins to control the generation
of analog signals through a digital-to-analog
converter (DAC).
Use different frequencies and/or waveforms
(square, sine, etc.) depending on the touch input.
Also use a passive buzz to listen to the audible
frequencies generated.
317
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
•E181: Monitoring inputs with touch pins.
Develop a program that reads multiple analog
inputs (use polling since internally we only have 1
ADC) using several inputs configured as ADC
(potentiometer, push button, etc.). Use touch pins to
select the corresponding analog input and an LED to
indicate which of the ADC is active. Finally, we can
see several types of Touch sensors that can be used
with the ESP32©.
Important: If we use the ESP32© with a battery,
we must touch GND with another finger for the GPIO
touch to work.
⊝⊝⊝
318
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 34:
49E© Hall Sensor
Continuing with the
sensor exercises, in this case
we will see a not very well-
known but quite interesting
sensor. This is the sensor of
magnetic materials based on
the Hall Effect, that is, the
generation of an electric
current when charges are
separated inside a conductor,
through which a current
circulates in the presence of
a magnetic field, that is, a
normal magnet.
This sensor is widely
used in electric current detectors, speed meters,
proximity sensors, positioning, limit switches, etc.
The sensor used in this exercise is similar to
the 49E© (linear response)
and we can use it within a
module that incorporates
the Hall sensor and also
an additional circuit that
allows two signals to be
obtained: an analog signal
AO (Analog Output) that
proceeds directly from the
49E© sensor and a digital
signal DO (Digital Output)
of the on/off type that we
can use, for example, as
activation for an IRQ.
319
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
We will manage the analog output AO with an
analog-digital converter (ADC) to know the value of
the magnetic signal and the digital output DO of the
Hall sensor, which is treated by a comparator and
linear amplifier type LM393©, is connected to a GPIO
of the ESP32© read by polling or by IRQ.
The module seen
incorporates an
LM393© type opera-
tional amplifier
that acts as a
comparator, pro-
viding a fully
digital DO signal,
depending on the
analog output AO
of the 49E© and a
reference value
controlled by a
potentiometer.
W i t h t h e p o t e n-
tiometer 10k Ω P, we can adjust the comparison
level between the comparator input 1IN- (Hall sensor)
and the input 1IN+ (reference P) and therefore select
whether the LED D1 and the digital output DO are acti-
vated with (N)orth or (S)outh poles.
Resistors R0 and R1 limit the current of the LED
and R2 is a pull-up to stabilize the 1OUT output of
the LM393© amplifier and the GPIO32 input.
Some previous versions of the ESP32©, not all,
have an internal Hall sensor included in the main chip
and accessible through the esp32 library but it is not
very reliable, which is why it has been removed in
more ESP32© recent versions.
To control the 49E© sensor with its module, we
will make a MicroPython© script where we will connect
the digital output DO of the module to GPIO32, the
analog output AO to the ADC converter in GPIO34 and we
will add two LED: green in GPIO26 and red one in
GPIO27 that will act of magnet polarity indicators.
320
Electronics & MicroPython© with ESP32©: 80 complete projects
When DO is activated, controlled by IRQ, a
message will be displayed on the console, when the
North pole (N) approaches, the green LED will be
activated and with South or (S) the red LED and the
value captured by the analog-digital converter (ADC)
will also be presented.
When the sensor rests, without detecting
anything, the theoretical output value of the ADC will
be the average value (M) of the scale (0-4,095), that
is, a value close to 2,048 but here it's 1,900.
When approaching the North pole (N), this ADC
value will rise above the average value (M), depending
on the value of the magnetic field of the approximate
material and if we approach the South pole (S), this
value will fall below the average value (M).
We will add some configurable thresholds for the
North values, for example greater than 1,900 and South
less than 1,800. These thresholds depend on multiple
factors: sensor type, power supply calibration,
temperature, etc.
321
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In the script, the machine and time libraries
are imported to control the GPIO, the ADC and the
timing. The GPIO of the AO and DO signals coming from
the Hall sensor and the GPIO for controlling the LED
are defined, as well as the ADC thresholds that define
the North pole and South pole of the magnet that
activates the sensor (configure as needed).
The ADC is configured by specifying the GPIO34,
12
the 12-bit resolution and therefore 2 =4,096
levels and the extension of the default measurement
range from 0-1V to 0-3.3V.
T h e sensor() function is established that
controls the IRQ call and where the ADC is read and
depending on its value and the predefined thresholds,
the messages of the detected poles are presented and
the corresponding LED are activated.
Finally, the control IRQ is defined for the
input to the ESP32© through the GPIO32 and by the
transition from HIGH to LOW (falling) configurable by
hardware with the potentiometer P. The start messages
and a while True: loop are added which simulates the
main body of the script which is controlled, as
always, by the error handler and the console shutdown.
############################
# E034_HALL_SENSOR.PY: 44E© Sensor Hall with AO and DO
# Hall sensor scanning on GPIO34 by IRQ
# INPUTS: Hall sensor status AO and DO
# OUTPUTS: North GPIO26 green and South GPIO27 red
#########################
from machine import Pin, ADC # GPIO & ADC control
import time # Timing management
# Pines configuration
pin_ao = Pin(34) # Analog AO
pin_do = Pin(32, Pin.IN, Pin.PULL_UP)
# Digital DO with 10k pull-up
pin_led_green = Pin(26, Pin.OUT) # North pole
pin_led_red = Pin(27, Pin.OUT) # South pole
322
Electronics & MicroPython© with ESP32©: 80 complete projects
# Detection thresholds
north = 1900 # Top: North pole
south = 1800 # Bottom: South pole
hall_ant = 10 # Previous state of the Hall (not 0)
# ADC configuration
adc = ADC(pin_ao) # ADC reading
adc.width(ADC.WIDTH_12BIT) # Resolution of 12 bits
adc.atten(ADC.ATTN_11DB) # 0-3.3V (default 0-1V)
# Change sensor status by IRQ
def sensor(_): # Include an argument
global hall_ant
analog = adc.read()
# Management of % variation greater than 2%
if abs(analog-hall_ant)/hall_ant*100 > 2:
if analog > north:
print(f'NORTH Pole detected! {analog}')
pin_led_green.off() # Green LED of
pin_led_red.on() # Red LED on
if analog < south:
print(f'SOUTH Pole detected! {analog}')
pin_led_green.on() # Green LED on
pin_led_red.off() # Red LED off
hall_ant=analog
# IRQ for DO, sets to LOW and calls sensor()
pin_do.irq(trigger=Pin.IRQ_FALLING, handler=sensor)
print('SENSOR HALL 49E©')
print('Approach a magnet...')
# Main program loop
try:
while True:
time.sleep(.1) # Simulates main body
except KeyboardInterrupt:
pin_led_green.off() # Green LED of and
pin_led_red.off() # stop IRQ
pin_do.irq(handler=None)
print('Program completed...')
⊝⊝⊝
323
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E182: LED control with Hall switch and PWM.
Design a program for ESP32© that turns on an LED
using a Hall sensor as an on/off switch, using DO. Use
a PWM signal to control the intensity of the LED
brightness within pre-established ranges.
•E183: Timer with Hall sensor.
Create a hardware timer on the ESP32© that is
activated and deactivated using a Hall sensor. When
activated, the timer must count for a specified period
and then trigger an action, such as lighting an LED or
sounding a buzzer.
•E184: Event registration with RTC and Hall.
Implement an event registration system that
saves the date, time and levels at which a magnetic
field is detected by the Hall sensor. Use the internal
RTC to obtain the accurate timestamp and will be pre-
updated from the Internet and adjusting the necessary
local variables.
•E185: Servo control with Hall sensor.
Design a system that controls a servo motor.
When a magnetic field is detected with the Hall
sensor. The servo must move to a specific position
pre-configured by the console and when no magnetic
field is detected, the servo must return to its
initial rest position.
•E186: Analog generator with DAC and Hall.
Create an analog wave generator that produces
simple waveforms using a DAC. Use the Hall sensor to
start, stop or change the type of wave generated by
the DAC.
⊝⊝⊝
324
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 35:
44E© Hall Switch
In this exercise we will
see the use of a Hall
sensor that has a digital
switch on/off output (SIG
or DAT), which is
activated at LOW and
therefore acts as a normal
switch.
As on other occasions,
we will use a simple
module, which includes the
44E© sensor and other
components for its correct
operation, as well as a 3
pin connector: SIG (on/off
output), VCC (+3.3V) and
GND.
In the Hall switch
circuit, LED D1 lights
up when the circuit is
supplied with +3.3V
power, LED D2 lights
up when the sensor
conducts current when
detecting a magnetic
material, resistor R3
acts as a pull-up at
+3.3V and R1, R2, as
usual, limit the
current that flows
through both LED to
protect them.
325
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The output of the Hall switch (DAT or SIG,
on/off type only) is connected through GPIO32 of the
ESP32© and GPIO26/27 manage the status of two green
and red LED respectively.
The DAT or SIG output of
the Hall switch is of the
“open collector” type, that
is , i t in te rn a ll y h a s a
transistor that can be
connected to +3.3V with a
pull-up and allow the DAT
output to be connected
directly to the GPIO32 of the
ESP32 © without voltage level
adapters.
The differences between the 49E© and 44E© Hall
sensors are fundamental: the 49E© Hall sensor provides
a n analog signal through its pin 3 that must be
treated with an ADC or a comparator, while the 44E©
Hall sensor provides a digital signal, of the on off
type, which can be connected to a GPIO of the ESP32©
to determine its HIGH or LOW value.
The MicroPython© script begins with the import
of the utime and machine libraries for timing control,
switch GPIO and LED. The definition of control
variables is also added and the green LED starts to
turn on, with messages inviting us to bring a magnet
close to the Hall switch.
The led_update() function controls the state of
the LED and messages based on the state of the Hall
switch in pin_hall.value() which is activated at LOW
as we have already mentioned (reverse logic).
As we can see, this management is done by
polling within a while...True: loop and in the main
loop of the script the previous function is called, a
waiting time between readings is controlled, which we
must adjust according to our needs and finally, the
corresponding error control and script interruption
are included, which, if applicable, will turn off the
LED as usual.
326
Electronics & MicroPython© with ESP32©: 80 complete projects
############################
# E035_HALL_SWITCH.PY: Hall Switch 44E©
# Hall switch scanning on GPIO32 by polling
# INPUTS: Hall switch status
# OUTPUTS: GPIO26 green and GPIO27 red alternation
#########################
import utime # Time management
from machine import Pin # GPIO control
# Pins configuration
hall_pin = Pin(32, Pin.IN) # Hall switch
green_led = Pin(26, Pin.OUT) # Green LED
red_led = Pin(27, Pin.OUT) # Red LED
# Current LED status (starts green)
green_led_state = True
green_led.on()
red_led.off()
print('\n' * 50) # Clear screen
print('44E© HALL SWITCH CONTROL')
print('Approach a magnet to the Hall switch')
# Read switch status and update LED
def led_update():
global green_led_state # Used out of function
hall_value = hall_pin.value() # Read switch
if hall_value == 0: # Detects a magnetic field
print('\n' * 50) # Clear display
print('Magnet detected...')
if green_led_state: # Acts as a flag
green_led.off() # Green LED off
red_led.on() # Red LED on
print('Red')
green_led_state = False # Avoid bounces
else:
green_led.on() # Green LED on
red_led.off() # Red LED off
print('Green')
green_led_state = True # Avoid bounces
print('Approach a magnet to the Hall switch')
try:
while True: # Can be stopped with <CTRL>+<C>
led_update()
utime.sleep_ms(300) # Wait between reading
except KeyboardInterrupt:
print('\n' * 50) # Clear display
print('Program completed.')
finally:
# Turns off both LED when exiting the loop
green_led.off()
red_led.off()
⊝⊝⊝
327
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E187: PWM wave generation with Hall switch.
Create a system that generates a PWM wave on a
specific pin of the ESP32©. Use a Hall switch to
control the frequency of the PWM wave, which should
increase or decrease depending on the detection of a
magnetic field.
•E188: Hall switch and push button.
Implement a program that uses a Hall switch,
managed by IRQ, to detect a magnetic field. When a
magnetic field is detected, an LED will light and an
interrupt for a push button will be activated. By
pressing this button, the status of the switch will be
displayed on the console.
•E189: Servo control with Hall switch and RTC.
Design a system that controls a servo motor by
bringing a magnet close to a Hall switch. Use the RTC
to record the date, time, Hall switch status, and
angle applied to the servo motor to a file. Refreshes
the file every 20 seconds.
•E190: Watchdog, Hall switch and timer.
Create a program that uses a Hall switch to
detect a magnetic field. If no field is detected for a
specific period of time, the ESP32© Watchdog will stop
powering based on a timer that will restart the
program from scratch.
•E191: Hall switch and rotary decoder.
Create a system that moves a servo arm based on
the state detected by a Hall switch and the value
entered through a rotary decoder. All data will be
recorded to a file along with the RTC time.
⊝⊝⊝
328
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 36:
MF52AT© NTC Thermistor Sensor
A thermistor is basically a resistance whose
value varies with temperature, therefore we can use it
as a thermometer to measure temperatures within
predefined ranges.
There are two types of thermistors:
• Those that have a negative temperature coefficient,
called NTC, that
is, as the tem-
perature detected
in its environ-
ment rises, the
resistance of the
NTC decreases in
a logarithmic
(non-linear) man-
ner.
• Those with a positive temperature coefficient,
called PTC, in which the opposite happens and the
resistance increases exponentially (also non-linear)
with temperature.
In our case we will use an NTC
type thermistor, similar to the
MF52AT©, which has a resistance of
10k Ω at 25ºC and a variation
between the detected temperature T
and its resistance R with
logarithmic variation, so for
example, at 0ºC its resistance is
R=98k Ω and at 50ºC is from
R=3,6 k Ω.
329
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In this exercise, like others
already seen with other sensors,
we w i ll u s e a m o d ul e t ha t
includes the thermistor and also
some control resistors and some
status LED. We will process the
output of the module, SIG, with
the ADC converter of the ESP32©.
When the tempe-
rature near the NTC
thermistor rises,
its resistance drops
and we can detect
this variation
through the GPIO32
of our ESP32©.
As always, on the
NTC sensor module,
LED D0 indicates
power, resistors R0
and R1 limit the LED
currents, and R2 and
R3 are pull-up to
stabilize all sig-
nals.
In the script we import the time and machine
libraries to control timing, pins and ADC. We define
the minimum and maximum values for alarm control and
configure the ADC in the GPIO32, adjusting the
converter to support a greater dynamic range, allowing
inputs from 0 to +3.3V (by default it only supports 0
to 1V as we already know).
We create the function read_temperature() that
reads the analog value of the thermistor, we create
the function convert_temperature(), where we use a
linear function (it should be logarithmic), which
converts volts into ºC adjusted and limited to the
maximums and minimums established by the ADC and
finally we define the main loop where alarms, the
presentation of the measured temperature and the time
between readings are managed.
330
Electronics & MicroPython© with ESP32©: 80 complete projects
############################
# E036_THERMISTOR_SENSOR.PY: ADC with Thermistor
# INPUTS: ADC in GPIO32 between 0 and +3.3V
# OUTPUTS: ADC in ºC and GPIO26/27 LED green_red
############################
import time # Timing manager
from machine import Pin, ADC # GPIO management
# Configure temperature control LED
led1=Pin(26, Pin.OUT) # Green LED
led2=Pin(27, Pin.OUT) # Red LED
led1.off() # Green LED off
led2.off() # Red LED off
# Temperature alarm thresholds (adjust)
temp_min = 24.0 # Green temperature
temp_max = 26.0 # Red temperature
# Pin ADC configuration
pin_thermistor = 32
adc = ADC(Pin(pin_thermistor))
# Set the ADC range to 0-3.3V (default 0-1V)
adc.atten(ADC.ATTN_11DB)
# Read thermistor values
def read_temperature():
adc_value = adc.read() # Read ADC value
# Convert ADC value to temperature
temperature = convert_temperature(adc_value)
if temp_min < temperature < temp_max:
led1.on() # Correct temperature
led2.off() # Green on
else:
led1.off() # Wrong temperature
led2.on() # Red on
print('Temperature Alarm: {:.2f}
°C'.format(temperature))
return adc_value, temperature
# Convert temperature between limits
def convert_temperature(adc_value):
# Converting thermistor value to temperature
# ADC and temperature inverse
temperature_min = 20 # Minimum ADC temperature
331
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
temperature_max = 40 # Maximum ADC temperature
adc_value_min = 0 # Minimum expected ADC
adc_value_max = 4095 # Maximum expected ADC
adjust = .8 # Calibration value
# Inverted and adjusted linear conversion
temperature = (temperature_max – (temperature_max
- temperature_min) * (adc_value - adc_value_min) /
(adc_value_max - adc_value_min)) * adjust
return temperature
# Main loop of the script
def main():
try:
while True:
value, temperature = read_temperature()
print("Temperature: {:.2f}
°C".format(temperature))
time.sleep(1) # Wait between reading
except KeyboardInterrupt:
led1.off()
led2.off()
print("Program completed...")
if __name__ == "__main__":
print('\n'*20)
print('TEMPERATURE READING BY THERMISTOR')
main()
⊝⊝⊝
332
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E192: Nonlinear thermistor.
In the previous exercise, a linear conversion of
the ADC reading to temperature was carried out, but
the thermistors do not present a linear behavior (both
PTC and NTC).
For this example use a curve for a typical NTC
where the resistance varies with temperature with the
formula:
2
1 /T =a+b∗ln(R)+c∗ln (R), where T is the
temperature in Kelvin, R is the resistance in Ohm and
a, b, c are manufacturer-dependent coefficients.
−3 −4
Use, for example, a=1.4∗10 , b=2.37∗10
−8
and c=9.9∗10 .
Remember that T is in Kelvin, search the Web to
find out how to convert it to Fahrenheit or Celsius
degrees.
•E193: Thermistor with analog comparator.
To complete this exercise, use a sensor module
that contains an NTC thermistor and an analog
comparator, so that when a certain reference
temperature is reached (selected with a small
potentiometer) the comparator is triggered, activating
a digital output that we can capture with a GPIO, see
next exercise.
Implement this function, with the corresponding
control messages, in a MicroPython© script similar to
the previous one.
•E194: Periodic temperature reading.
Set a hardware timer on the ESP32© to take
temperature sensor readings every 10 seconds using an
NTC thermistor.
333
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Display the temperature and uses an LED to
indicate that the temperature is within or outside a
range.
•E195: Temperature monitoring on the Web.
Display the temperature detected with an NTC
thermistor sensor module, read from the analog-digital
converter (ADC), on a Web page hosted on the ESP32©
that acts as a server.
Configure the NO-IP© application (see other
exercises), to access the Web page from anywhere
outside the home.
•E196: Voice temperature control.
Create a routine with IFTTT© (see subsequent
exercises) that allows us to implement voice control
to request the ESP32© to perform a temperature
measurement using an NTC thermistor sensor with ADC
expressing the result on several scales.
The values obtained must be saved in a file,
together with the date and time captured from an
external RTC, previously Internet updated, and these
values will also be presented on a specific website
hosted on the ESP32©.
To ensure ADC readings, they must be accompanied
by flashing LED and corresponding error checks.
⊝⊝⊝
334
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 37:
Switch with Thermistor
Likewise as seen with
the Hall switch, in this
exercise we also have the
option of using a switch
module with thermistor and
that has two outputs: the
digital output, DO, which is
activated based on the
comparison, carried out by
the LM393© with a reference
level and the AO output,
coming from the sensor and
that we can treat with the
ADC procedure.
The control circuit is similar to that of the
Hall sensor switch, that is, as the ambient tempera-
ture rises, the NTC decreases its resistance and
therefore the 1IN-
input activates the
LM393© comparator
in relation to the
reference signal
1IN+ set by the P
potentiometer.
Once this situation
is established, the
digital output, DO,
is activated to LOW
by circulating cur-
rent through D1 and
activating the GPI-
O32 of the ESP32©.
335
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In parallel, through the AO output we can see
the value of the temperature provided by the
thermistor, using an analog-digital converter (ADC).
The connection to the ESP32© is similar to that
of the Hall switch, we use GPIO34 to measure the
analog signal AO with the ADC and we connect the
digital output of the switch, DO, to the GPIO32 to
treat it as a detection signal by polling or by IRQ.
In this case, we will make a very simple script
to poll only the output of the thermistor switch, that
is, the digital output DO that is connected to GPIO32.
We can vary the reference or trigger level of the
LM393© comparator by adjusting the potentiometer P
and/or changing the temperature and therefore the
resistance of the NTC thermistor of the module.
At the beginning of the script, we load the
usual utime and machine libraries for timing control
and GPIO for the DO switch input and the output for
the LED. We start with the LED green, clean the
console and present informative messages.
The update_led() function flashes the LED in the
event that the digital signal DO is activated at LOW
(inverse logic), presenting the corresponding
messages. The main loop of the script calls this
function by polling and can be interrupted by console
by pressing <CTRL>+<C>, in which case the LED turn
off.
############################
# E037_THERMISTOR_SWITCH.PY: DO thermistor management
# Switch scanning on GPIO32 by polling
# INPUTS: Thermistor status
# OUTPUTS: Blink GPIO26 green and GPIO27 red
#########################
import utime # Time management
from machine import Pin # GPIO control
# Pins configuration
pin_DO = Pin(32, Pin.IN) # Thermistor switch
green_led = Pin(26, Pin.OUT) # Green LED
336
Electronics & MicroPython© with ESP32©: 80 complete projects
red_led = Pin(27, Pin.OUT) # Red LED
# Current LED status (starts green)
green_led_state = True
green_led.on()
red_led.off()
print('\n' * 50) # Clear display
print('SWITCH CONTROL WITH THERMISTOR')
print('Put your hand close to the thermistor...')
print('...or turn the potentiometer...')
# Read DO switch and update LED
def update_led():
global green_led_state # Out of function
ntc_value = pin_DO.value() # Switch reading
if ntc_value == 0: # Look if DO is on
print('\n' * 50) # Clear display
print('Switch with activated thermistor...')
if green_led_state: # Acts like a flag
green_led.off() # Green LED off
red_led.on() # Red LED on
print('Red')
green_led_state = False # Avoid bounces
else:
green_led.on() # Green LED on
red_led.off() # Red LED off
print('Green')
green_led_state = True # Avoid bounces
print('Put your hand close to the thermistor')
print('... or turn the potentiometer...')
try:
while True: # Can be stopped with <CTRL>+<C>
update_led()
utime.sleep_ms(300) # Wait between reading
except KeyboardInterrupt:
print('\n' * 50) # Clear display
print('Program completed.')
finally:
green_led.off() # LED off
red_led.off()
⊝⊝⊝
337
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E197: NTC switch with IRQ.
Use the same circuit as the previous exercise
but change the management of the DO digital signal
from polling to IRQ.
•E198: NTC and ADC switch.
Add the thermistor temperature value reading
using the ADC connected to GPIO34 similar to the Hall
sensor exercise. The DO signal will be treated by IRQ
and when it is activated the temperature value, in
degrees Celsius, will be read linearly.
•E199: NTC logarithmic.
Change the NTC reading with the ADC from linear
to logarithmic as discussed in the thermistor sensor
exercise. Save the NTC values each time the DO switch
is tripped, varying the position of the P
potentiometer and/or modifying the ambient temperature
near the thermistor. These values will be saved in a
file, along with the time updated with the ESP32©
internal RTC.
•E200: NTC and servo.
Add to the previous exercise a servo and an
algorithm that moves the servo a number of degrees
depending on the temperature within pre-established
thresholds.
•E201: Multiple temperatures.
Create a periodically updated file containing
the updated RTC timestamp and temperatures from
multiple sources: logarithmically adjusted NTC,
internal to the ESP32©, and external from a DS18B20©
sensor. Activates LED based on a temperature table.
⊝⊝⊝
338
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 38:
KY-033© Line Detector
A line detector or follower is a sensor that
distinguishes the reflectance of a line, for example a
line drawn on paper, a line
painted on a road, etc. In
this sense, this sensor or
similar is interesting to
apply in robotics,
automation, automotive,
counters, etc. Because with
this detector we can follow
a certain line, not cross
it, count crossed lines,
etc.
The line follower discussed here, for example
the KY-033©, consists of two elements: an emitter and
an infrared light receiver included in a package that
detect when all, one or none of the two elements is on
the line to be detected.
These elements, transmitter and receiver,
require an additional circuit that amplifies the
signal and provides an on/off type output, which is
set to LOW when it is detected that the sensor is on a
line. Let's remember that the sensor's emitting diode
is emitting infrared light continuously as long as it
receives power.
When the sensor is outside a line (light zone)
the photo transistor receives infrared light from the
emitter reflected in that light zone and begins to
conduct and therefore the 1IN+ input of the comparator
goes to LOW and the 1OUT output and the GPIO32 input
go to LOW so D2 lights up.
339
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
When the sensor is on a line (dark area) the
photo transistor does not receive light, as it is
absorbed by the line, so the 1IN+ input of the LM393©
comparator amplifier is at HIGH, the 1OUT output and
therefore the GPIO32 input of the ESP32© is at HIGH
and so D2 does not light up.
340
Electronics & MicroPython© with ESP32©: 80 complete projects
In the module, resistor R1 limits the current of
LED D1 that lights up when power is applied to the
circuit, R2 limits the current that crosses the
reference adjustment potentiometer R3, R4 acts as a
pull-up for 1IN+, R5 adjusts the current that
circulates for the infrared emitter, R6 limits the
current of LED D2, R7 is a pull-up for the GIPO32 and
C1 and C2 act as a filter.
In the script we import time and machine for
time and GPIO control, we define the functions:
setup() with the assignment of GPIO and variables,
see() for LED and message state changes, loop() for
polling the detector on the GPIO32 and stop() to stop
the script, turning off the LED and displaying the
corresponding messages.
############################
# E038_LINE_DETECTOR.PY: Line detector by polling
# INPUTS: Line crossing in GPIO32
# OUTPUTS: Line crossing and LED indicator
#########################
import time
from machine import Pin
# Pins configuration
def setup():
global state_a, line_pin, green_led, red_led
line_pin = Pin(32, Pin.IN) # Detector
green_led = Pin(26, Pin.OUT) # Green LED
red_led = Pin(27, Pin.OUT) # Red LED
state_a = 1 # Previous state
# Update detected status
def see(x):
if x == 1:
red_led.on() # Red LED on
green_led.off() # Green LED off
print('To the left...')
else:
red_led.off() # Red LED off
green_led.on() # Green LED on
print('To the right...')
341
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Read the detector by polling
def loop():
global state_a
state_n = line_pin.value() # Read the status
if state_n != state_a: # Change state
see(state_n) # Show new one
state_a = state_n # Update state
time.sleep(0.1) # Wait a while
# Stop keyboard script
def stop():
green_led.off()
red_led.off()
print('Program completed')
# Main loop of the program
if __name__ == '__main__':
setup() # Star GPIO and variables
print('\n'*50) # Clear display
print('LINE DETECTOR KY-033©')
print('Testing line detector...')
try:
while True:
loop() # Run main loop
except KeyboardInterrupt:
stop() # Stop program with <CTRL>+<C>
⊝⊝⊝
342
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E202: Line follower, LED and push button.
Design a program in MicroPython© for ESP32© that
uses a line follower of the type KY-033© or similar,
controlled by IRQ, so that, when the line follower
detects a line, it turns on an array of LED up to a
certain level according to a pre-established algorithm
with a 3-level table.
Finally, integrate a push button already seen in
other exercises and managed by interruption (IRQ), to
stop the tracking of lines when the push button is
pressed.
•E203: Servo control with line follower.
Create a program that uses a KY-033© line
follower to follow a predefined route created by
drawing curves on a piece of paper.
Integrates a servo motor that rotates in reverse
depending on the signal from the line follower. Use a
timer to periodically poll the line follower instead
of using IRQ.
•E204: Wave generation and line follower.
Develop a script in MicroPython© that uses a
line follower to control the generation of analog
waves using the DAC (digital-to-analog converter) of
the ESP32©.
Experiment with different waveforms: sinusoidal,
square, triangle, saw, etc. and different frequencies
and duty cycle, according to the signals detected by
the line follower.
•E205: RGB LED, line follower and RTC.
Create a MicroPython© program for ESP32© that
uses a KY-033© line follower or similar to detect a
given path.
343
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Integrate an RGB LED to change the color of the
LED depending on the state of the line follower and
record in a file the date and time of the internal RTC
of the ESP32©, updated over the Internet, as well as
the state of the line follower and the applied values
to the RGB LED. The recording in the file must be
circular, that is, when recording 20 lines, it must
start recording the first one again.
•E206: Follower and speed control.
Design a MicroPython© program for ESP32© that
uses a KY-033© line follower or similar to follow a
line drawn on a piece of paper with a black marker.
Integrate a DC motor with PWM speed control and a
servo with rotation control.
Use the line follower to adjust the speed and
direction of the motors according to the detected
trajectory. Design an algorithm so that the motor
decreases the speed if nothing is detected and changes
the servo angle if crossing to the right or left of
the line is detected.
⊝⊝⊝
344
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 39:
ERD29© Laser Diode
IMPORTANT: DO NOT LOOK AT THE LASER DIRECTLY
In this exercise we will use a low power laser
diode (5mW), that is, a diode that emits coherent
light, of a specific frequency therefore of a specific
color, created by the emission of electronically
stimulated radiation.
It is a low-power laser but it should NOT be
focused on the eyes as it can cause irreversible
damage. The author of this book declines any
responsibility derived from possible damages caused by
this or any other optical or electronic element
described in this book.
The coherence of the laser light allows us to
focus on a small point and conveniently direct it as
if it were a pointer.
Other higher power lasers are used for other
industrial purposes, here we will use it only for
training reasons to emit pulses based on some
algorithm implemented in a program.
The circuit is very simple, since we only need
to connect it to the GPIO32 that powers it directly,
creating the necessary pulses.
The program example gen-
erates approximately,
respecting the times, the
Morse code of the interna-
tional SOS distress signal,
that is: ...---...
345
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
To make it as realistic as possible, the script
takes into account all the necessary relative times,
that is, of a dot, a dash, the separation between
signs, the separation between letters, the separation
between words, etc.
The script is simple, it imports the usual
machine and time libraries to control the GPIO and the
times, it defines the timing of the elements: point,
line, space between pulses, space between letters and
space between words, it assigns each element a number
and thereby generates the list text[] with all the
necessary sequence.
T h e setup() function is defined with the
association of the GPIO, the loop() function where the
text[] list is traversed and acts with each component,
the stop() function that stops the script by turning
off the laser and the LED that act visual signal so we
don't have to look directly at the laser.
############################
# E039_LASER.PY: Laser SOS emitter
# INPUTS: SOS elements list
# OUTPUTS: Laser and LED visualization
#########################
from machine import Pin # GPIO management
import time # Timing control
# SOS elements
dot = .3 # Morse dot (0)
line = dot*3 # Morse line (1)
space_pulse = dot # Space pulse (2)
letter_pulse = dot*3 # Letter pulse (3)
word_space = line*4 # Word space (4)
text = [0,2,0,2,0,3,1,2,1,2,1,3,0,2,0,2,0,4] # SOS
# GPIO start
def setup():
global laser, green_led, red_led
laser = Pin(32, Pin.OUT) # Laser
green_led = Pin(26, Pin.OUT) # Green LED
red_led = Pin(27, Pin.OUT) # Red LED
346
Electronics & MicroPython© with ESP32©: 80 complete projects
# Laser on/off activation (inverse logic)
def loop():
for x in text: # Scan text[]
if x==0: # Is a dot?
laser.off()
red_led.on()
time.sleep(dot)
laser.on()
red_led.off()
if x==1: # Is a line?
laser.off()
red_led.on()
time.sleep(line)
laser.on()
red_led.off()
if x==2: # Space pulse?
time.sleep(space_pulse)
if x==3: # Letters pulse?
time.sleep(letter_pulse)
if x==4: # Words space?
time.sleep(word_space)
# Stop script
def stop():
print ('Program finished, Laser off')
laser.off() # Laser off
red_led.off() # LED off
green_led.off()
# Main body of the script
if __name__ == '__main__': # Program start here
setup() # Start GPIO
green_led.on()
print('SOS WITH LASER')
print('Transmitting code...')
try:
while True:
loop() # SOS loop
except KeyboardInterrupt: # Stop script
stop()
⊝⊝⊝
347
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E207: Laser and servo with ESP32©.
Simulate the physical connection between the
laser and a servo arm, so that based on the seconds of
the ESP32©'s internal RTC, activate the laser and
moves the servo arm a certain turn making the laser
point at various objects.
The position of the objects can be defined in
the dictionary positions{object:position}.
•E208: Laser and DAC with ESP32©.
Connect the laser to a GPIO with digital-to-
analog conversion (DAC) capability and check whether
changing the output level activates the laser or not.
Modify the SOS signal generation script so that the
DAC does it.
•E209: Laser and PWM signal.
Connect the laser to an ESP32© GPIO that can
generate a square wave, using variable duty cycle PWM
technology and see how it affects the luminosity of
the laser.
Add a Hall sensor whose output, treated by the
ESP32©, acts proportionally on the duty cycle of the
PWM signal and therefore on the possible luminosity of
the laser (it is not a linear device).
⊝⊝⊝
348
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 40:
TCS3200© colors sensor
In this exercise we will
see a very curious and
interesting sensor, it is
a colors sensor, that is,
a sensor that is capable
of detecting and even
measuring the basic colors
of an object.
The TCS3200© sensor or
similar, has 4 white LED
that directly illuminate a
nearby object and an 8x8
pixel sensor that detects the light reflected by the
basic color components of this object and outputs a
sq u a re w a ve o f 50 % d ut y c y cl e a nd f r eq u e n cy
proportional to the intensity of a certain color.
For a better measurement
of the signal associated
with a basic color with the
ESP32© GPIO, the output
frequency on the OUT pin can
be scaled by several factors
with the S0 and S1 inputs
according to the attached
table.
This sensor, which
actually behaves as an intensity converter of a
certain color of light at a frequency, has 8x8
photodiodes: 16 have a red filter (R), 16 green (G),
16 blue (B) and another 16 without filter that can be
selected according to the table:
349
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Therefore, we can select
the applied filter by
connecting S2 and S3, for
example, to GPIO26 and
GPIO32 respectively and
measure with 3 test objects
(red, green and blue) how
the frequency decreases on
the OUT pin of the TCS3200©
sensor. Other colors will be
combinations of the RGB
values.
We also could connect S0
and S1 to the GPIO of the
ESP32© but it is not
recommended because the
frequencies provided by
the sensor in the 20% and
100% configurations are
too high for the ESP32©
(more than 25kHz), so we
can set S0=L and S1=H and
work with 2%. See pin
table.
350
Electronics & MicroPython© with ESP32©: 80 complete projects
In the MicroPython© script we import machine and
time as always, we define the number of CYCLES for
calculating the frequency, the setup() function that
defines the filter pins S2 and S3 and the OUT
frequency output.
W e d e f i n e t h e f u n c t i o n measures() t h a t
calculates the frequency according to the time it
takes to perform CYCLES counted by the MicroPython©
object out.irq() that defines an interruption on the
OUT pin (GPIO34) by passing from HIGH to LOW (falling)
and where lambda p:None is a callback argument
activated by this IRQ and that allows us to count the
number of cycles. Finally, the duration of the CYCLES
is counted and converted into frequency. In this
measures() function we also see how s2 and s3 are
changed depending on the color filter to be used (red,
green, blue, none).
Finally, the setup() function is activated to
configure the GPIO and a while True: loop is performed
where the frequencies for each filter are measured and
presented and a condition is proposed, which must be
adjusted based on the conditions of each case, to the
combinations of frequencies for each color and in this
way the approximate basic color is obtained. For other
colors we will obtain a certain combination of these
frequencies.
############################
# E040_COLOR_SENSOR.PY: Color sensor TCS3200©
# INPUTS: Basic colors RGB
# OUTPUTS: Frequency and color detected
#############################
from machine import Pin # GPIO management
import time # Timing control
''' # Pins definition
out frequency # Frequency output
s2 s3 filter # Filter select
--------------
L L Red
L H Blue
H L NO filter
H H Green '''
CYCLES = 50 # Frequency cycles counter
# GPIO configuration
351
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
def setup():
global s2, s3, out # Global variables
s2 = Pin(26, Pin.OUT) # S2
s3 = Pin(32, Pin.OUT) # S3
out = Pin(34, Pin.IN, Pin.PULL_UP) # OUT
# Measures frequency (S2, S3, color)
def measures(v2, v3, color):
print('Measuring: ' + color)
s2.value(v2) # Activate the filter
s3.value(v3)
time.sleep(0.3) # Wait for the chip
start = time.ticks_ms() # Get star time
for _ in range(CYCLES): # Cycles counter
out.irq(lambda p: None, Pin.IRQ_FALLING)
while out.value() == 1: # Wait on LOW
pass
while out.value() == 0: # Wait on HIGH
pass
duration = time.ticks_diff(time.ticks_ms(),
start) / 1000 # Duration
frequency = int(CYCLES / duration) # Hz
return frequency
# Main body of the script
setup() # Pins configuration
print('TCS3200© COLOR SENSOR')
print('Approach an objet to the sensor...')
try:
while True: # Colors read cycle
red = measures(False,False,'Red') # Red
green = measures(True, True, 'Green') # Green
blue = measures(False,True, 'Blue') # Blue
white = measures(True, False,'White') # White
print(f'RGB: {red}:{green}:{blue}') # Hz
# Adjust conditions as ambient light, etc.
# Proposal of 2% frequency (S0=L y S1=H))
if red > 1000 and green < 800 and blue<800:
print('RED OBJECT')
elif red < 1000 and green > 400 and blue<800:
print('GREEN OBJETC')
elif red < 1000 and green < 800 and blue>200:
print('BLUE OBJECT')
elif red > 1000 and green > 800 and blue>800:
print('WHITE object or none')
else:
print('Unknown color')
except KeyboardInterrupt:
print('Finished...')
⊝⊝⊝
352
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E210: Color sensor and relay.
Design a system, based on ESP32©, that
automatically controls the lighting of a room using
the TCS3200© color sensor and a relay to turn a lamp
on and off based on the intensity of the detected
light of a given color. The script will present
appropriate messages.
•E211: Temperature and color sensor.
Create a device that monitors ambient
temperature and humidity using a DHT11© as well as the
internal temperature of the ESP32©. If these three
variables exceed a certain limit, measure the color
detected by the TCS3200© sensor and if it is red send
an email with all the necessary data.
•E212: TCS3200© sensor and WiFi connection.
Take data from the frequencies detected by the
color sensor, record them in a file along with the
date and time of the updated RTC and using WiFi, send
them by email every 10 readings.
•E213: Color sensor and wave generator.
Implement an audio signal generator using the
ESP32© DAC and the TCS3200© color sensor to modulate
the frequency and amplitude of the generated audio
signals, creating interesting sound effects or
programmed melodies based on the color detected and
set into colors{color:freq}.
•E214: Color, RGB and PWM sensor.
Build a device that reads a basic color with the
color sensor and reproduce it on an RGB LED using PWM
signals. Extend the exercise to non-basic colors from
a predefined color table.
⊝⊝⊝
353
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 41:
KY-032© Obstacle Sensor
This time we will see an
obstacle sensor of the
KY032© type or similar,
which actually consists
of three parts:
• An electrical pulse
generator.
• An infrared pulse
emitter.
• An infrared signal
receiver.
The emitter constantly sends pulses of infrared
light generated by a 7555© type oscillator configured
in astable mode (frequency generator).
When these pulses bounce off an obstacle (not
very far) and are captured by the receiver, an alarm
is activated.
The detection distance of the infrared receiver
depends on the power of the emitter, the reflectance
of the obstacle, the characteristics of the medium
that intervenes between the emitter and receiver, the
sensitivity of the receiver, etc.
The pulse generator signal (7555© timer or
similar) is amplified by a PNP type transistor similar
to the S8550© whose gain factor can be regulated with
a potentiometer, regulating the base current, through
R7, or regulating the supply current through the
collector with the variable resistor R4, this allows
regulating the obstacle detection distance.
354
Electronics & MicroPython© with ESP32©: 80 complete projects
This sensor therefore requires a control circuit
that is a little more complicated than the ones we
have seen so far but easy to understand.
Let's see each part:
Oscillator: This is the timer already seen 7555© (a
NE555© CMOS) in astable oscillator configuration where
the oscillation frequency is determined by:
1 1
f= = ±46KHz
log (2)∗C3∗( R5 +2∗R6 ) 0.693∗10−9∗(103 +2∗15∗10 3 )
C1 and C2 act as stabilizers and the signal
generated by the 7555© comes out through the OUT pin.
Emitter: T h e si g n a l f r o m O U T a t t ac k s t h e PN P
transistor S8550© which acts as a signal amplifier
used by the emitter D3 (an infrared diode) and which
can be regulated with the 1k Ω R4 potentiometer.
R3 limits the current that crosses the
transistor when the potentiometer is at minimum
resistance and R7 limits the base current of the
transistor.
355
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Receiver: This is an infrared photodiode (BPV10NF© or
similar) that captures the infrared beam sent by the
emitter and that has bounced off the obstacle. When
this sensor detects the bounced signal, it sets GPIO34
to GND (reverse logic) and activates LED D1. R1 and R2
limit the current to the LED and D2 lights to indicate
that the circuit is powered.
We will also use a Dual LED that will flash red
to indicate the presence of the obstacle and will
remain green otherwise. This LED is managed by
software with GPIO26 (green) and GPIO27 (red) as we
usually do.
The script is similar to that of the line
detector and others that present the status of a
sensor with an on/off type digital output. The time
control and GPIO libraries are imported, the sensor
and LED pins and global variables are defined.
T h e setup() function initializes the GPIO, the
look() function analyzes the sensor output and acts on
the LED, and the loop() function simulates the main
body of a script that includes the control of the
obstacle sensor by polling. Finally, the stop()
function is presented that stops the script and the
main body that calls setup() a n d loop() within an
error and stop control.
############################
# E041_OBSTACLE_SENSOR.PY: KY-032© obstacles sensor
# INPUTS: Obstacle detected in GPIO32
# OUTPUTS: Message and LED in GPIO26/27
#############################
import time # Timing management
from machine import Pin # GPIO control
pin_v = 26 # Green LED
pin_r = 27 # Red LED
pin_s = 32 # Sensor input
pins = (pin_r, pin_v) # Pins list
state = True # Control flag
counter = 0 # Obstacles trace
356
Electronics & MicroPython© with ESP32©: 80 complete projects
# Start GPIO
def setup():
global pin_s # Sensor output
pin_s = Pin(pin_s, Pin.IN)
for p in pins: # LED output
led = Pin(p, Pin.OUT)
led.value(0) # LED off
# Check sensor status due to polling
def look():
global state, counter
counter += 1 # Obstacles trace
print(f'[{counter}]: Obstacle detected...')
if state: # Change LED states
Pin(pin_r, Pin.OUT).on() # Red LED on
Pin(pin_v, Pin.OUT).off() # Green LED off
else:
Pin(pin_v, Pin.OUT).on() # Green LED on
Pin(pin_r, Pin.OUT).off() # Red LED off
state = not state
# Main loop of the script
def loop():
while True:
if not pin_s.value(): # Inverse logic
look() # Sensor status
time.sleep_ms(100) # Simulate script
# Stop script
def stop():
for p in pins:
Pin(p, Pin.OUT).off() # LED off
print('\nProgram completed.')
# Script starts here
if __name__ == '__main__':
print ('OBSTACLES SENSOR CONTROL')
setup()
print('Approach an obstacle to the sensor...')
try:
loop() # Polling loop
except KeyboardInterrupt:
stop() # Stops script
⊝⊝⊝
357
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E215: Servo control and obstacle sensor.
Design a MicroPython© script to control a servo
motor based on the detection of an obstacle. When an
obstacle is detected, the servo rotates a certain
angle and when the detection is completed, the servo
returns to its initial position.
•E216: Activate a relay with obstacle sensor.
Use an obstacle sensor to detect the presence of
a nearby object. When detected, a relay is activated
that can control any electrical device connected to
it, such as a lamp or a motor (for example, simulate
it with an LED).
•E217: Line and obstacle sensor.
Use a line sensor and an obstacle sensor to
follow a line parallel to a wall. The obstacle sensor
must detect the wall permanently and if not, it will
indicate it with an LED.
•E218: Temperature, thermistor obstacles.
Use an obstacle sensor along with a thermistor
to measure ambient temperature. When an obstacle is
de t e ct e d , t h e s c r ip t a dj u s ts t he s p ee d o f a
thermistor-controlled fan to prevent overheating of
the environment.
•E219: RGB LED and proximity of an obstacle.
Use an obstacle sensor and a color sensor to
detect the proximity of an object of a certain basic
color. Change the RGB color depending on the color
and/or obstacles detected.
⊝⊝⊝
358
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 42:
1838B© Infrared Receiver
In this exercise we will
see how to use an infrared
receiver as a detector, that
is, NOT as a code reader (we
will see this later), only as
a sensor that activates a
signal when detecting an
infrared signal coming from
any infrared emitter (remote
television, decoder, etc.)
For this we will use the
1838B© infrared receiver or equivalent, with an
additional circuit very similar to that used in the
rest of the sensors already seen.
When the
1838B© detects an
infrared signal, it passes its pin 1 to GND and
therefore D2 conducts and the GPIO32 detects the LOW
level (reverse logic) where R2 is a pull-up to +3.3V.
359
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
LED D1 lights up when power is detected,
resistors R1 and R3 control the maximum current that
circulates through LED D1, D2 and through the sensor,
and C1 activates the +3.3V signal filter.
In our example, every time the infrared sensor
detects a signal of this type, coming from a TV remote
control, air conditioning, etc., it will activate and
act on a Dual LED, causing it to alternate between the
red and green LED with each infrared signal received.
The script is very simple and similar to other
sensor exercises seen previously in which the sensor
situation is reviewed by polling.
############################
# E042_INFRARED_RECEIVER.PY: 1838B© sensor
# INPUTS: Signal detected in GPIO32
# OUTPUTS: Message and LED in GPIO26/27
#############################
import time # Timing control
from machine import Pin # GPIO management
pin_v = 26 # Green LED
pin_r = 27 # Red LED
pin_s = 32 # Sensor input
pins = (pin_r, pin_v) # Pins list
state = True # Control flag
counter = 0 # Obstacle trace
wait = .1 # Wait between presses
# GPIO start
def setup():
global pin_s # Sensor output
pin_s = Pin(pin_s, Pin.IN)
for p in pins: # Output LED
led = Pin(p, Pin.OUT)
led.value(0) # LED off
# Check sensor status due to polling
def look():
global state, counter
counter += 1 # Signals trace
print(f'[{counter}]: IR signal detected...')
360
Electronics & MicroPython© with ESP32©: 80 complete projects
if state: # Change LED status
Pin(pin_r, Pin.OUT).on() # Red LED on
Pin(pin_v, Pin.OUT).off() # Green LED off
else:
Pin(pin_v, Pin.OUT).on() # Green LED on
Pin(pin_r, Pin.OUT).off() # Red LED off
state = not state
# Main loop of the script
def loop():
while True:
if not pin_s.value(): # Inverse logic
look() # Look sensor status
time.sleep(wait) # Wait presses
# Script stop
def stop():
for p in pins:
Pin(p, Pin.OUT).off() # LED off
print('\nProgram completed.')
# Script starts here
if __name__ == '__main__':
print ('INFRARED SENSOR CONTROL')
setup()
print('Send a signal to sensor...')
try:
loop() # Polling loop
except KeyboardInterrupt:
stop() # Stop script
⊝⊝⊝
361
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E220: Relay with IR remote control.
Use the infrared or IR receiver to receive
signals from a TV remote control. Set the ESP32© to
act as a relay switch, turning an electrical device
(such as a lamp) on or off each time a button on the
remote control is pressed. Add the corresponding
delays between switches.
•E221: Servo control with remote control.
Use the IR receiver to detect signals from a
remote control. Program the ESP32© to control the
position of a servo motor according to the signal
received from the remote control.
•E222: Change RGB color with an IR control.
Use the IR receiver to capture on/off signals
from an IR remote control. Program the ESP32© to
change the color of an RGB LED in a predefined
sequence in the sequences{step:color} dictionary.
•E223: Hall sensor reading via IR.
Connect a Hall sensor to the ESP32© and use the
IR receiver to receive signals from a remote control.
Modify the script so that it reads the Hall sensor
values every time a button is pressed on the remote
control, and displays these values on the console.
•E224: Motor control via IR and PWM.
Connect a motor to PWM and use the IR receiver
to receive signals from a remote control. Program the
ESP32© to adjust the motor speed according to the
received signals in an increment and decrement
sequence established in a dictionary.
⊝⊝⊝
362
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 43:
Remote Control with Infrared
As promised, in this exercise we will see how we
can receive and manage infrared signals, sent by an
infrared remote control or “command”, and received and
decode them with the ESP32© with a MicroPython©
script.
There are multiple controls and protocols and
for simplicity in this exercise we will see the
Sunfounder© infrared control or similar.
The Sunfounder© remote uses, among others, the
NEC© infrared transmission protocol, which is
basically summarized in the following steps:
• Sequence: The data frame in the NEC© protocol
consists of a header, an 8-bit command, the
complement of the 8-bit command, and a
termination bit.
• Header: The data header is composed of a 9ms
start gap (start mark to LOW) followed by a
4.5ms pulse (start pulse to HIGH) with reverse
logic.
363
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• Data bit: Each data bit is represented by a
sequence of two pulses: a 560µs mark followed by
a 560µs gap to represent a 0 bit, or a 560µs
mark followed by a 1.68ms gap to represent a 1
bit. This notation is with reverse logic.
• Command: Commands are usually 8 bits long,
transmitted from the most significant bit to the
least significant bit.
• Command complement: The command complement is
simply the inverse of the command bits and is
used to verify data integrity.
• Termination: After transmission of the command
and its complement, a long pulse (mark) followed
by a long blank (space) is sent to indicate the
end of the frame.
We will maintain the same connection of the
infrared receiver module already seen in the previous
exercise, that is, +3.3V, GNG and SIG connected to the
GPIO32. We will also use the green and red LED that
will alternate when pressing the “Mode” key.
For the script we need the ir_rx library that we
will download from the Sunfounder© website with which
we will have to upload the ir_rx folder to the ESP32©
from which we will use the nec.py and __init__.py
files only, we will also add the following script:
############################
# E043_INFRARRED_REMOTE.PY: 1838B© infrared sensor
# INPUTS: Signal detected in GPIO32
# OUTPUTS: Key pressed
# IMPORTANT: Needs Sunfounder© ir_rx.nec and remote
#############################
import time # Timing management
from machine import Pin # GPIO control
from ir_rx.nec import NEC_8 # Remote control library
pin_ir = Pin(32, Pin.IN) # Infrared receiver
pin_v = Pin(26, Pin.OUT) # Green LED
pin_r = Pin(27, Pin.OUT) # Red LED
364
Electronics & MicroPython© with ESP32©: 80 complete projects
# Received key codes
codes = {
0x16: "0", 0x0C: "1", 0x18: "2",0x5E: "3",
0x08: "4", 0x1C: "5", 0x5A: "6",0x42: "7",
0x52: "8", 0x4A: "9", 0x09: "+",0x15: "-",
0x07: "EQ", 0x0D: "U/SD", 0x19: "Repeat",
0x44: "Play/Pause",0x43: "FF", 0x40: "RW",
0x45: "On/Off", 0x47: "Mute", 0x46: "Mode"}
# ir_rx.nec library error codes
errors = {"BADSTART" : 'Invalid initial pulse',
"BADBLOCK" : 'Wrong block',
"BADREP" : 'Repeat',
"OVERRUN" : 'Excess data',
"BADDATA" : 'Wrong data',
"BADADDR" : 'Wrong address'}
# Flow control data
flag = True # LED control flag
tmp = .1 # Polling time
# Errors control
def see_error(data):
if data in errors: # There's error
print(errors[data])
else: # There's no error
print('Unknown error')
# Processes received data
def process(data, address, control):
global flag # LED control
if data < 0: # Data waiting
pass
else: # Received data
print(f'Received key: {codes[data]}')
if codes[data] == 'Mode':
if flag:
pin_r.on() # Red LED on
pin_v.off() # Green LED off
else:
pin_r.off() # Red LED off
pin_v.on() # Green LED on
flag = not flag # Change led status
365
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
ir = NEC_8(pin_ir, process) # NEC© protocol instance
# Shows possible errors
ir.error_function(see_error)
# Polling reading loop
print('REMOTE CONTROL IR NEC© PROTOCOL')
print('Press a key on the remote')
try:
while True:
time.sleep(tmp) # Simulate script
except KeyboardInterrupt:
print('Program completed...')
ir.close() # Receiver off
As an example of the NEC© IR transmission
protocol, when transmitting a “1” the following
sequence can be seen in the logic analyzer:
We can see the start at LOW of 9ms (space
between marks A1 and A2) with the header 0x00, the
device address 0xFF, the command 0x0C corresponding to
key 1 and the complement of the command 0xF3 to verify
the integrity of the transmission.
⊝⊝⊝
366
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E225: Control of an LED array with IR control.
Connect an infrared receiver to the ESP32© and
configure it to receive codes from an infrared remote
control. Use the IR command numbers to control the
number of array LED activated.
•E226: Control of a servo with an IR command.
Connect a servo and program the infrared
receiver to receive signals from an IR controller. Use
the received codes to control the position of the
servo according to states{code:rotation}.
•E227: Activation of a relay with an IR command.
Connect a relay to the ESP32© and configure the
infrared receiver to receive codes from a controller.
Use the correct codes received to activate or
deactivate the relay.
•E228: LED modulation with PWM and IR.
Connect an LED to the ESP32© and configure the
infrared receiver to receive codes from a controller.
Use the received codes to modulate the light intensity
of the LED using PWM. Each button on the controller
can be associated with a different LED brightness
level predefined with data{button:brightness}.
•E229: Infrared emitter.
Set the GPIO32 as an output to use the IR module
as an infrared emitter and send the code '2' by
sending the sequence: [start, 0x00, 0xFF, 0x18, 0xE7].
Respect the timing for start and the corresponding 0
and 1 of each command. Could we control a simultaneous
transmitter and receiver with the ESP32©?, why?.
⊝⊝⊝
367
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 44:
SW-200D© Tilt Sensor
Another way to control
an LED or any type of output
is by using some type of
additional sensor. In this
exercise we will see how to
use a tilt sensor or “tilt-
switch”, for example the SW-
200D©. It is a mechanical
sensor that is not very
precise or very fast but easy
to use, cheap and that
detects a certain angle of
inclination.
It consists of a small cylinder with a small
sphere inside that, when tilted, makes the sphere roll
and this activates a mechanical contact, being able,
for example, to detect the rotation or overturning of
an object, movement, impact, anti-theft alarms,
industrial processes, etc.
The inclination sensor
or tilt-switch sensor, in
most cases, is usually
accompanied by a very
simple electronic control
circuit like the one
attached.
In this circuit, R0 and
R1 control the current
through LED D0 (power
indicator) and LED D2
(activation indicator).
368
Electronics & MicroPython© with ESP32©: 80 complete projects
When we tilt the tilt-switch sensor its built-in
switch sets GPIO32 to GND and therefore D2 lights up.
There are more precise tilt-switch sensor
circuits by incorporating integrated circuits that act
as comparators and assign more reliable states to the
switch. The activation of the tilt-switch is what we
will capture with the ESP32© and by software, we will
activate a red or green LED based on an algorithm.
Logically it could be actuated with a relay, an
amplifier or any other type of actuator. As we said,
this sensor is not very precise, later we will see a
very precise three-axis accelerometer, although more
difficult to manage.
To control this simple sensor we will use one of
the scripts already seen previously that also
controlled other on/off type sensors and that include
green and red LED for visual control of the activation
of the sensor. This script will also be useful for
subsequent exercises that include on/off type sensors
like this one.
############################
# E044_TILT_SENSOR.PY: SW-200D© sensor
# INPUTS: Detected signal in GPIO32
# OUTPUTS: Message and LED in GPIO26/27
#############################
import time # Timing management
from machine import Pin # GPIO control
pin_v = 26 # Green LED
pin_r = 27 # Red LED
pin_s = 32 # Sensor input
pins = (pin_r, pin_v) # Pins list
state = True # Control flag
counter = 0 # Motion trace
wait = .1 # Wait between events
# GPIO start
def setup():
global pin_s # Sensor output
pin_s = Pin(pin_s, Pin.IN)
369
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
for p in pins: # Output LED
led = Pin(p, Pin.OUT)
led.value(0) # LED off
# Check sensor status due to polling
def look():
global state, counter
counter += 1 # Motion trace
print(f'[{counter}]: Tilt detected...')
if state: # Change LED status
Pin(pin_r, Pin.OUT).on() # Red LED on
Pin(pin_v, Pin.OUT).off() # Green LED off
else:
Pin(pin_v, Pin.OUT).on() # Green LED on
Pin(pin_r, Pin.OUT).off() # Red LED off
state = not state
# Script main loop
def loop():
while True:
if not pin_s.value(): # Inverse logic
look() # Check sensor status
time.sleep(wait) # Wait for motion
# Stops script
def stop():
for p in pins:
Pin(p, Pin.OUT).off() # LED off
print('\nProgram completed.')
# Script starts here
if __name__ == '__main__':
print ('TILT SENSOR CONTROL')
setup()
print('Move the sensor...')
try:
loop() # Polling loop
except KeyboardInterrupt:
stop() # Stops the script
⊝⊝⊝
370
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E230: Relay control with tilt sensor.
Use a tilt sensor to detect the movement of an
object. Connect a relay to the ESP32© and program the
micro controller to activate the relay when the tilt
sensor detects a movement and deactivate it when it
detects the next. Manage the sensor by IRQ instead of
polling.
•E231: Buzzer and tilt sensor.
Connect a tilt sensor to the ESP32© and an
active buzzer. Program the ESP32© with MicroPython© to
activate the buzzer when the tilt sensor detects an
inclination and deactivate it when it detects the next
one. Repeat the exercise with a passive buzzer managed
by the ESP32© DAC.
•E232: PWM generation and tilt sensor.
Connect a tilt sensor to the ESP32© and an RGB
LED. Program the micro controller, with a MicroPython©
script, so that the movement of the tilt sensor
modifies the color of the LED using PWM.
•E233: Servo control with tilt sensor.
Connect a tilt sensor to the ESP32© and a servo
motor. Program the micro controller to move the servo
in one direction or another depending on the number of
inclinations detected.
As the tilt detection is performed, the state of
an LED array changes and the sound of a buzz is
modified using PWM.
⊝⊝⊝
371
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 45:
SW-18010P© Vibration Sensor
A variant of the tilt
switch is the vibration,
spring or impact switch. When
a vibration or impact of a
certain energy occurs, the
spring makes contact with an
internal element of the
switch, activating its output.
In this exercise we will see a vibration sensor,
of the on/off type, similar to the SW-18010P© or
equivalent, which also requires a simple additional
control circuit like the one in the attached figure
and very similar to other sensors.
In this circuit, the LED diode D0 lights up when
power is supplied to the circuit and the LED D1 lights
up when the vibration
switch detects a movement
or an impact, as it sets
the GPIO32 to GND (LOW).
Resistors R0 and R1
limit the current that
circulates through LED D0
and D1 respectively,
resistor R2 acts as a
pull-up of the GPIO32
input to stabilize its
level when the sensor is
not activated and
capacitor C1 filters the
high frequency signals
normally generated by the
372
Electronics & MicroPython© with ESP32©: 80 complete projects
vibration switch, avoiding false activations (which we
can also reinforce its suppression by software). When
GPIO32 is LOW it will detect vibration (reverse
logic).
To complete the exercise, we
add a Dual LED so that when the
vibration or impact is
detected, the red LED turns on,
in the next vibration the green
LED turns on and the red turns
off and so on as if it were a
“flip-flop” circuit type or
bistable (two-state circuit).
For the MicroPython© script, we will use the
same or similar one seen in the previous tilt sensor
exercise.
⊝⊝⊝
373
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E234: Vibration event log.
Connect a vibration sensor to the ESP32© and
design a MicroPython© script to detect vibration
events and record the date and time of each event in a
file that is updated by a hardware timer.
•E235: Temperature and vibration sensors.
Connect a vibration sensor and a DHT11© sensor
to the ESP32©. Program the micro controller to
activate the DHT11© sensor and read and display
temperature and humidity when a vibration is detected.
•E236: Vibration and color sensors.
Connect a vibration sensor and a color sensor to
the ESP32©. Program the micro controller so that when
it detects a certain color it checks, by polling, the
status of a vibration sensor.
•E237: Infrared control and vibration sensor.
Connect a vibration sensor and an infrared
receiver to the ESP32©. Add an action algorithm based
on both sensors. Perform programming so that the date,
time, state of the vibration sensor, key pressed on
the infrared remote control, and output of the
algorithm are stored in a file.
•E238: Timed events with vibration.
Connect a vibration sensor to the ESP32© and
program it to initiate timed events when a vibration
is detected and using the time and date of the
internal RTC of the ESP32© previously updated from the
Internet. Save all data to a file.
⊝⊝⊝
374
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 46:
MAX-4466© Sound Sensor
The sensor that we are
going to see in this exercise
is very common in homes, very
well known and widely used in
multiple devices.
It is a microphone that
translates the pressure of
sound waves into analog
electrical signals that we
will capture and convert with
the ADC converter.
In our case we will use
the MAX-4466© or similar, which is a sound sensor,
that is, a microphone based on an electrostatic
capacitor that generates a small current when
activated by the acoustic pressure exerted by sound.
This sensor requires a high-
gain, low-noise amplification
circui t, su ch as the L M358©
operational amplifier, and an
additional control circuit like
the fo llowi ng, an d sin ce it
provides an analog signal, we
will connect it to the ESP32© ADC
analog-digital converter.
We will read the ADC quickly to capture all the
information from the sensor, but we must know that to
capture a frequency f we must read the ADC at least
2*f (Shannon Theorem) and with high f values the
ESP32© is limited. For sounds of a certain level and
medium frequencies we should not have any problems.
375
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The microphone, upon receiving sound pressure,
generates through C1, R4 and together with R3, a
variable analog voltage that is amplified by the
LM358©, which comes out through 1OUT and attacks the
GPIO32 configured as ADC.
R1 and R2 generate an intermediate point between
+3.3V and GND that serves as a reference to the 1IN-
input for the LM358© comparator (an adjustment
potentiometer could have been used).
C4 acts as a filter, R5 controls the gain of the
circuit and finally R0 limits the current of the LED
that lights up if the set is powered.
When there is no ambient sound, the SIG signal
(GPIO32) is close to 2V and when the volume increases
it drops to 0V (inverse logic), we must also take into
account that the ADC of the ESP32© is 12 bits,
12
therefore it has 2 =4,096 levels that we will
convert to voltage levels between 0 and +3.3V.
The script converts the analog value generated
by the LM358© comparator (0 to +3.3V) into digital
values between 0 and 4,095 depending on the sound
pressure applied to the sound sensor or microphone.
376
Electronics & MicroPython© with ESP32©: 80 complete projects
The digital value is displayed on the screen and
on the Thonny© plotter. A minimum sound threshold is
defined and when it is exceeded, the red LED is
briefly activated. When the excess sound disappears,
the green LED is activated. The green and red LED are
managed by GPIO26 and GPIO27 respectively.
In the script we import the usual machine and
time libraries, configure the GPIO, adjust the ADC
threshold to 0-3.3V and define the parameters.
The adc_read() function reads the range 0-4,095
and converts it into volts in the range 0-3.3V and
finally the main body of the script adjusts values to
see them well on the Thonny© plotter and if the value
exceeds a threshold it turns on the red LED.
############################
# E046_SOUND_SENSOR.PY: MAX-4466© microphone
# INPUTS: ADC in GPIO32 between 0 to +3.3V
# OUTPUTS: ADC read in volts GPIO27/26 LED
############################
from machine import Pin, ADC # GPIO and ADC manager
import time # Timing management
# ADC pin configuration
pin_adc = 32
adc = ADC(Pin(pin_adc))
# Set the ADC range to 0-3.3V (default 0-1V)
adc.atten(ADC.ATTN_11DB)
# Configure the pins for the LED
green_led = Pin(26, Pin.OUT)
red_led = Pin(27, Pin.OUT)
# Script parameters
t_read = .0001 # Time between reading
alarm = 2.2 # Alarm level
# Performs analog reading from 0 to +3.3V
# The ADC is 12 bits therefore 2^12=4,096 levels
def adc_read():
adc_value = adc.read() # Level to volts conversion
voltage = (adc_value / 4096) * 3.3
return voltage
377
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Script main body
green_led.on()
print('SOUND SENSOR MAX-4466©')
print('Speak close to the microphone...')
try:
while True:
value = adc_read() * 2 # Plotter view ok
print('Sound: ', value) # Plotter trace
if value < alarm: # Alarm level
red_led.on() # Red LED on
time.sleep(.01)
red_led.off()
time.sleep(t_read) # Between reading
except KeyboardInterrupt:
print('Script completed...')
green_led.off() # LED off
red_led.off()
adc = None # ADC release
⊝⊝⊝
378
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E239: Sound intensity and timing.
Design a system that uses an analog sound sensor
to measure the intensity of ambient sound every 5
minutes using an ESP32© hardware timer. Save the data
of each measurement in a file for later analysis.
•E240: LED, rotary decoder and sound.
Connect a rotary decoder and LED to the ESP32©.
Program the micro controller to change the LED
intensity based on the sound intensity measured by the
sound sensor and use the rotary decoder to adjust the
system sensitivity.
•E241: Audible presence alarm.
Create an alarm system that activates when a
sound above a predefined threshold is detected using
the analog sound sensor. Use an additional presence
sensor to deactivate the alarm when motion is no
longer detected in the room.
•E242: Ambient temperature and sound.
Implement a system that measures room
temperature using a thermistor and captures sound
intensity. Records both sets of data and the internal
RTC date and time.
•E243: Laser activation based on sound.
Create a system that turns on a laser when sound
above a certain level is detected using the analog
sound sensor. Use a hardware timer to turn off the
laser 2 seconds later.
⊝⊝⊝
379
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 47:
HC-SR04© Ultrasound Sensor
In this exercise we will see one of the funniest
and most interesting devices (sensor and receiver) in
this book since it is widely used to measure distances
(robots, toys, parking,
access, etc.) with a very
acceptable precision and
using ultrasound as physical
measurement element.
The device used is the
HC-SR04© module, or
equivalent, which has an
emitter and a 40kHz
ultrasonic sensor.
Ultrasounds travel in
the air at a speed of
343.2meters/second, there-
fore, by sending that sound
towards an object and calcu-
lating the time it takes to
go and return, we can easily
calculate the distance r be-
tween the ultrasonic emitter
and object as follows:
343,2
distance(r)=speed∗time= ∗100∗time
2
The distance r is in centimeters, the time is
the total in seconds, from the emission to the
reception of the sound and is divided by 2 as it is
the round trip time from the sender to the receiver.
380
Electronics & MicroPython© with ESP32©: 80 complete projects
The connection of this device to the GPIO of the
ESP32© is very simple since the ultrasonic sensor
already includes all the necessary electronic control
elements and also the adaptation of logic levels.
Our example only needs, in this case, GPIO33 for
the emitter [trig], GPIO32 for the receiver [echo],
GND and a +3.3V power supply.
The MicroPython© script will have a loop that
will periodically send a certain ultrasound pulse,
receive the echo of that pulse, calculate the elapsed
time, do the calculations and present the distance in
cm (change if is necessary) on the console, doing:
1. Set the [trig] or GPIO33 signal to LOW for at
least 0.2s to stabilize the ultrasound emitter.
2. Raise the [trig] signal to HIGH for 10 μ s
(microseconds) and then lower it to LOW. When
performing this operation, the HC-SR04©
automatically sends 8 ultrasound pulses with a
non-audible frequency of 40kHz and places the
[echo] signal at HIGH, this is when we must
start the response time countdown.
381
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
3. The output [echo] remains at HIGH until the echo
signal of the ultrasound sent and bounced off
the object is received. At this moment it goes
to LOW, which is when we must stop the elapsed
time counting timer.
4. Knowing the elapsed time (round trip between
sensor and object to be measured) and the speed
of sound in the environment of use of the HC-
SR04© sensor, we can calculate the distance r
between the sensor and the object with
sufficient precision.
In the script, the machine and time libraries
are imported, the pins for [trig] (emitter) in GPIO33
and [echo] (receiver) in GPIO32 are defined, the speed
of sound in air is considered, which, to be precise,
will depend of temperature, ambient humidity and
atmospheric pressure.
It is specified that the [trig] pin is an output
and that the [echo] pin is an input, the function
distance_measure() is d e f i n ed , w h i c h b a s i c a ll y
activates the HC-SR04© to emit the ultrasonic pulses
and receive them, the time elapsed between these two
events is recorded by the ESP32© which, with the
corresponding calculations, transforms them into cm
(centimeters) and presents them on the console.
In the LA1010© logic analyzer, we observe the
10 μs pulse from LOW to HIGH on the [trig] pin, the
transition to HIGH of the [echo] signal, which is when
the HC-SR04© sensor sends the 8x40kHz pulses and the
time it takes for these pulses to reach the obstacle,
bounce back and be captured again by the HC-SR04©
receiver and which has been marked on the analyzer
between points A1 and A2.
Taking into account the total time elapsed
between the sending of the pulses and their reception
and dividing it by two (round and return trip), in the
example about 26.6ms and assuming a speed of sound in
ambient air of 344.6m/s (depends on temperature,
humidity, pressure, etc.), we will have a distance r
between the HC-SR04© sensor and the obstacle of 4.58m.
382
Electronics & MicroPython© with ESP32©: 80 complete projects
############################
# E047_ULTRASOUND.PY: Ultrasonic measurement HC-SR04©
# INPUTS: Speed of sound in air
# OUTPUTS: Distance r, in centimeters
############################
from machine import Pin # GPIO management
import time # Timing control
# Output pins (trigger) and input (echo)
echo = Pin(32, Pin.IN) # Arrival pulse
trig = Pin(33, Pin.OUT) # Output pulse
# Sound speed in air in m/s
speed = 344.6 # Depending on temperature, humidity...
# Measure the distance r with the HC-SR04© sensor
def distance_measure():
trig.value(0) # Start the HC-SR04©
time.sleep(.2) # Do not go below .2 seconds
trig.value(1) # Sensor gives 8x40kHz pulses
time.sleep_us(10)
trig.value(0)
383
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
while echo.value() == 0: # Wait for echo = 1
pass
start_pulse=time.ticks_us() # Start counter
while echo.value() == 1: # Wait for echo = 0
pass
end_pulse=time.ticks_us() # End counter
# Calculate the delay in microseconds
delay = time.ticks_diff(end_pulse, start_pulse)
# Calculate distance in centimeters (all trip)
distance_centimeters = delay * speed / 2 /
10000 # Divide 1e6/100=1e4 to cm
print (delay)
print("Distance: {} centimeters".format
(distance_centimeters))
# Main loop
print('DISTANCE METER WITH HC-SR04©')
print('Point the sensor at the object to be measured')
try:
while True:
distance_measure()
time.sleep(2)
except KeyboardInterrupt:
print('Completed...')
⊝⊝⊝
384
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E244: Distance measurement with LED indicator.
Use an HC-SR04© to measure distances to an
object, turning on a green LED if the distance is less
than a predetermined threshold and a red LED if it is
greater. The distances will be previously entered and
validated by the console and the measurements will be
carried out periodically using an ESP32© hardware
timer.
•E245: LED control proportional to distance.
Measures the distance to an object using an HC-
SR04© sensor and adjusts the intensity of an LED
proportionally to the measured distance using PWM.
•E246: Distances and rotary encoder.
Use an HC-SR04© to measure distances and use a
rotary decoder to change the measurement scale
presenting it in meters, inches, feet and yards.
•E247: Generation of tones according to distance.
Use an HC-SR04© sensor to measure the distance
to an object and simultaneously generate an audible
tone whose frequency is inversely proportional to the
measured distance similar to a car parking distance
meter. Use the DAC of the ESP32© to generate the tone.
•E248: Distance, temperature and humidity.
Use the distance meter and the DHT11© double
sensor to measure feet, degrees and % humidity.
Previously define a dictionary with various tuples
containing various environments. If all the values
contained in a tuple are exceeded, a red LED will
light up following a certain algorithm.
⊝⊝⊝
385
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 48:
GL5539© Photoresistor Sensor
Just as the microphone detects
sounds, the photoresistor, such
as the GL5539© or similar,
modifies its internal resistance
inversely depending on the amount
of visible light it receives,
therefore we can build a light
sensor and transform its analog
output to digital and process it
with the ADC of ESP32©.
As with all other sensors, we
need a control circuit to be
able to take full advantage of
the photoresistor and adapt its
signals to the ADC converter.
When visible light hits the
photoresistor, that is, the
sensor, its resistance decreases
and when the light stops
hitting, that resistance
increases (through a non-linear
law and dependent on the brand
and model of the sensor).
Therefore, in the GPIO32 we
have a variable voltage between +3.3V and GND
(approximately) that will generate a value between
4,095 and 0 in the ADC converter respectively.
Resistor R1 limits the current that crosses the sensor
and acts as a pull-up for the GPIO32 and R0 limits
that of diode D0, which is activated by the circuit
power supply.
386
Electronics & MicroPython© with ESP32©: 80 complete projects
Finally, with the GPIO26 and
GPIO27 we will make the red and
green LED blink (by software)
respectively when the light
detected by the photoresistor
exceeds a threshold, that is,
when the GPIO32 drops below a
predefined level and they will
remain fixed otherwise.
The script is similar to that of the sound
sensor, the same libraries and the same structure are
used, only changing the timing between readings and
the alarm values, which we will adjust based on the
voltage levels provided by the photoresistor.
Likewise, to visualize data, we will use the plotter
function available in the main menu of the Thonny©
programming environment.
############################
# E048_PHOTORESISTOR.PY: GL5539© photoresistor and ADC
# INPUTS ADC in GPIO32 between 0 to +3.3V
# OUTPUTS: ADC in volts GPIO27/26 LED green/red
############################
from machine import Pin, ADC # GPIO and ADC manager
import time # Timing management
387
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# ADC pin configuration
pin_adc = 32
adc = ADC(Pin(pin_adc))
# Set the ADC range to 0-3.3V (default 0-1V)
adc.atten(ADC.ATTN_11DB)
# Configure the pins for the LED
green_led = Pin(26, Pin.OUT)
red_led = Pin(27, Pin.OUT)
# Script parameters
t_read = .5 # Time between reading
alarm = .6 # Alarm level
# Performs analog reading from 0 to +3.3V
# The ADC is 12 bits therefore 2^12=4,096 levels
def adc_read():
adc_value = adc.read() # Level in volts
voltage = (adc_value / 4096) * 3.3
return voltage
# Script main body
green_led.on()
print('GL5539© PHOTORESISTOR')
print('Lights up the photoresistor...')
try:
while True:
value = adc_read() * 2 # Plotter display
print('Light: ', value) # Plotter trace
if value < alarm: # Alarm level
red_led.on() # Red LED on
time.sleep(.1)
red_led.off() # Red LED off
time.sleep(t_read) # Between reading
except KeyboardInterrupt:
print('Script completed...')
green_led.off() # LED off
red_led.off()
adc = None # Release ADC
⊝⊝⊝
388
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E249: Light intensity control with PWM.
Connect a photoresistor to the ESP32© and an
LED. Use the analog value of the photoresistor to
control the brightness intensity of the LED using PWM.
As the ambient light changes, the LED brightness will
automatically adjust.
•E250: Alarm, vibration and photoresistor.
Design a security system using a vibration
sensor and a photoresistor. When vibration is
detected, the ESP32© should check the ambient light
with the photoresistor. If it is dark (low visible
light level), it will trigger an alarm using a buzzer
or flashing a Dual LED.
•E251: Infrared lights and ambient light.
Set up a system that automatically adjusts the
brightness of a series of infrared lights using a
photoresistor. When the ambient light is low, the
ESP32© will increase the intensity of the infrared
lights, and vice versa. Use PWM for expansion.
•E252: Servo, line detection and photoresistor.
Use a servo motor and a line sensor to simulate
a small follower robot. The photoresistor will detect
ambient light and the ESP32© will adjust the speed or
direction of the servo motor based on the light to
follow a line on the ground.
•E253: Analog signals, DAC and ambient light.
Use the photoresistor to control a DAC. It
generates an analog signal proportional to the ambient
light intensity and generates an audible sound in a
passive buzz. Record the data to a file along with the
ESP32© RTC date and time.
⊝⊝⊝
389
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 49:
TIL-78© Fire Sensor
A fire sensor or flame sensor
basically consists of a device that
detects the infrared radiation
produced by combustion in the
spectrum of 700-1,000nm. There are
other similar sensors that also
detect certain chemical substances.
The one we are going to use in
this exercise is a TIL-78© phototransistor or
equivalent, which detects the infrared radiation
produced by flames.
Like all other sensors, the phototransistor also
needs a control circuit based on the LM393© comparator
amplifier that we have already seen in other
exercises.
390
Electronics & MicroPython© with ESP32©: 80 complete projects
When infrared light, coming from a flame or a
hot light, hits the base of the phototransistor Q1, it
causes it to conduct a certain amount of current,
depending on the energy of that infrared light
received.
This current is captured, on the one hand, by
the GPIO32 input of the ADC converter, which will
translate it to digital levels from 0 to 4,095, and on
the other hand, by the 1IN+ input
of the LM393©, which compares it
with the 1IN- reference level
( a d j u s t a b l e w i t h t h e 10k Ω
potentiometer), making the 1OUT
(DO) output activate or not the
GPIO35, which we will treat as an
interruption in our program.
Finally, GPIO26 and GPIO27 make
the usual Dual LED blink.
Resistors R2 and R3 act as a pull-up at +3.3V,
R0 and R1 limit the current of diodes D0 and D1
respectively and C0, C1 and C2 filter any possible
interference that may occur.
In our script we will capture, on the one hand,
the analog value (AO) of the phototransistor,
translate it to digital in the ADC converter and
display it on the console and on the plotter and on
the other hand, when DO generates an interrupt in the
ESP32© by the GPIO32, the program executes a function
that flashes the LED several times.
############################
# E049_FIRE_SENSOR.PY: TIL-78© fire sensor with ADC
# INPUTS: ADC In GPIO32 and DO (on/off) in GPIO35
# OUTPUTS: ADC read in volts and GPIO27/26 LED
############################
from machine import Pin, ADC # GPIO and ADC control
import time # Timing management
# ADC pin configuration
pin_adc = 32
adc = ADC(Pin(pin_adc))
391
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Set the ADC range to 0-3.3V (default 0-1V)
adc.atten(ADC.ATTN_11DB)
# Configure the pins for the LED and DO
green_led = Pin(26, Pin.OUT)
red_led = Pin(27, Pin.OUT) # DO with 10k pull-up
pin_do = Pin(35, Pin.IN, Pin.PULL_UP)
# Script parameters
t_read = .5 # Time between reading
t_blink = .1 # Blink time
# Performs analog reading from 0 to +3.3V
# The ADC is 12 bits therefore 2^12=4,096 levels
def adc_read():
adc_value = adc.read() # Voltage to level
voltage = (adc_value / 4096) * 3.3
return voltage
# Blink red LED
def blink(_): # Add an argument
for _ in range(2): # 2 repetitions
red_led.on()
time.sleep(t_blink)
red_led.off()
time.sleep(t_blink)
# Fire interruption, activated at LOW
# that calls the blink() function
pin_do.irq(trigger=Pin.IRQ_FALLING, handler=blink)
# Script main body
print('TIL-78© FIRE SENSOR')
print('Approach a flame to the sensor...')
try:
while True:
value = adc_read() * 2 # Plotter display
print('Flame: ', value) # Plotter trace
time.sleep(t_read) # Between reading
except KeyboardInterrupt:
print('Script completed...')
green_led.off() # LED off
red_led.off()
adc = None # Release ADC
⊝⊝⊝
392
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E254: Fire alarm, sensor and timer.
Create a fire detection system that uses a flame
sensor. When a flame is detected, it activates an
audible or visual alarm for a period of time specified
by a timer.
•E255: Lighting control, distance sensor.
Design an intelligent outdoor lighting system
that is activated by a relay when detecting flames and
adjusts its intensity using a photoresistor. In
addition, use an ultrasonic distance sensor to modify
the intensity of the light according to the proximity
of nearby people and controlled by certain algorithm.
•E256: System shutdown with Hall and flames.
Implement a security system to automatically
turn off electrical devices, connected to a relay, in
case of flame detection. Use a Hall switch to detect
if a door is open and disables the devices if flames
are detected while the door is open.
•E257: Internal temperature and flame sensor.
Create a security system that measures the
temperature of the ESP32© and the presence of flames.
If the internal temperature exceeds a threshold or
flames are detected, send a notification via email.
•E258: Fire extinguishing and flame sensor.
Design a simulated fire suppression system that
uses an ultrasonic meter to determine the proximity of
a possible burning moving object. When the object is
at a dangerous distance and the flame sensor indicates
fire, automatically activate a sprinkler system or a
simulated fire extinguisher.
⊝⊝⊝
393
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 50:
MQ-2© Gas and Smoke Sensor
A gas and smoke detector is a sensor that
consists of semiconductors sensitive to a certain
amount and type of gas that varies its resistance, so
that if we transform this analog effect into digital
we will observe how its values rise as a greater
amount of gas is detected.
In this exercise we will use the MQ-2© sensor or
a similar one that detects: liquefied petroleum gas,
smoke, alcohol vapor, propane, hydrogen, methane and
carbon monoxide.
This sensor has an
approximate consump-
tion of 1w, there-
fore, it must be
taken into account
when powering it that
we must do it with an
external power source
and never directly
with the ESP32©, in
addition this gas and smoke sensor needs to reach a
certain operating temperature and reaches its optimal
state after about 24 hours, however a few minutes are
enough to test it in this exercise.
The MQ-2© sensor requires an activation and
amplification circuit similar to those already seen
with the LM393© famous comparator.
When the MQ-2© sensor detects any type of gas
already mentioned, it varies the analog signal AO that
attacks the ADC of the ESP32© through the GPIO32.
394
Electronics & MicroPython© with ESP32©: 80 complete projects
The LM393© amplifier compares this AO signal
with a threshold set by the variable resistor RS
(which serves us as a calibration element), so that
the 1OUT or DO output is activated at LOW when the gas
is detected and the set threshold is exceeded.
The DO output acts with GPIO35, which is treated
in the script by interruption, initiating the alarm
process for detected gas. D0 lights up when the
circuit is powered and D2 when gas is detected.
Resistors R0 and R2 limit the current through the LED,
R3 is a pull-down to GND and C1 filters interference
and micro variations in DO output.
The script is the same as that of the fire
sensor, it presents the level of gas or smoke detected
on the Thonny© console and plotter and when GPIO35
detects the interruption alarm, it launches a function
that makes the Dual LED blink, presenting the alarm in
console and activates an intermittent acoustic signal.
⊝⊝⊝
395
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 51:
SKU-500© Rain Sensor
A rain sensor, for example the SKU-500© or
similar, is an electronic device that consists of two
elements, the sensor element
itself that detects the
presence of humidity in water
droplets, by electrical
conductivity, light reflection,
volume meter, etc. and a
control element that adapts the
signal generated by the sensor
to the appropriate electrical
levels and that generates an
analog signal proportional to
the detected humidity and a
digital, on/off type, that
indicates the presence of rain.
In our case we will use
a sensor that has a plate with
very close tracks that when wet
conduct a small current and an
el ec tr on ic c on tr ol e le me nt
based on the LM393© chip that
includes two operational
amplifiers comparators and we
will only use one.
The LM393© operational
amplifier compares the signal
from the sensor with a
reference value adjustable by a
potentiometer, amplifies the
comparison and extracts the two
analog AO and digital DO
outputs already mentioned.
396
Electronics & MicroPython© with ESP32©: 80 complete projects
The AO analog output coming directly from the
sensor and which is not very precise, we will connect
it to the GPIO32 and process it with the ADC converter
and with the help of the ESP32©, we will be able to
see on the console and on the plotter a measurement of
the volume of rain or humidity that affects the
sensor. We can use the DO digital output as an on/off
alarm by connecting it directly to the GPIO35 and
treating it by IRQ.
For the proper functioning of the LM393©
comparator we also need a series of basic components
so that the complete circuit would be like the
previous one and with the following operation:
The rain sensor sends the analog signal AO to
one of the inputs (1IN+) of the LM393© comparator, the
other input (1IN-) comes from the midpoint of the
potentiometer R3 which acts as an manually adjustable
reference value.
The output (1OUT) of the comparator acts as a
rain detection on/off signal DO and is captured by the
GPIO35 and processed by the MicroPython© script.
Additionally, the analog signal AO is connected to
GPIO32, which will be processed by the ADC of the
ESP32©.
397
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
R5+D1 indicate when the circuit is powered, C1
and C2 act as filters for possible interference. R1
and R2 are pull-up and R4+D2 are activated when rain
is detected and the DO output goes to GND.
With the rain sensor dry, we adjust the
potentiometer R3 until LED D2 turns off, this
indicates that the digital output DO is at HIGH,
without detecting rain, since 1IN- is less than 1IN+.
When the sensor gets wet, 1IN+ is less than 1IN- and
therefore the digital output DO is activated,
illuminating LED D2 and activating GPIO35 on the
Raspberry© (reverse logic).
We can use the same script as the one used in
the smoke sensor and the fire sensor with some small
adjustments to the texts and variables.
⊝⊝⊝
398
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E259: Fire alarm with sensor and relay.
Use a fire sensor with digital output to detect
the presence of fire. When fire is detected, activate
a relay to activate an audible alarm with active buzz
and a visual alarm with an array of LED.
•E260: Smoke, temperature with DHT11 and RGB LED.
Create a system that uses a smoke sensor and a
DHT11© to monitor both the presence of smoke and the
ambient humidity and temperature. Use an RGB LED with
PWM to visually indicate the current situation: green
for normal conditions, yellow for warning and red for
smoke alarm.
•E261: Irrigation, water sensor and relay.
Build an automatic plant watering system that
uses a water sensor with digital output to detect low
levels of moisture in the soil. When potting soil
dryness is detected, activate a relay to turn on a
water pump and water the plants. Simulate what is
necessary and save events to a file also using the RTC
of the ESP32©
•E262: Emergency, rain, fire and LED array lights.
Create an emergency lighting system for fire and
rain situations. Use a fire and a rain sensor with
digital output to detect fire and/or rain to activate
an array of LED and a servo according to an algorithm.
•E263: Remote alarm control with IR command.
Design an alarm system that can be activated and
deactivated remotely using an IR infrared remote
control. The system should include fire, smoke and
rain/water sensors to detect different types of
emergencies and activate the alarm as necessary.
⊝⊝⊝
399
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 52:
Scan I2C Bus
In future exercises we are going to use very
interesting devices that need a fast, efficient and
easy to manage communication protocol. One of these
protocols that is implemented in the ESP32© is the I2C
(Inter-Integrated Circuit) bus and is characterized by
the following:
• Master/slave: The ESP32© functions as both
master and slave on this bus. As a master, it
can control multiple slave devices, this mode is
what we will use in this book to get the ESP32©
to manage multiple elements. As a slave, it can
respond to commands sent by another master
device like another ESP32© (not very efficient).
• Topology: The I2C bus is a bidirectional serial
communication bus that uses only two signal
lines: SDA (Serial Data Line) and SCL (Serial
Clock Line). SDA is used for data transfer
between master and slave.
Data is transmitted sequentially in 8-bit
packets. Both, master and slave devices, can
send and receive data through this line. SCL
(Serial Clock Line) is used always to
synchronize data transfer on the bus.
The master generates clock pulses on this line
to indicate when the data on the SDA line is
ready to be read or written.
• Adjustable baud rates: The ESP32© supports baud
rates up to 400kHz in Standard mode and up to
1MHz in Fast mode.
400
Electronics & MicroPython© with ESP32©: 80 complete projects
• Multi Master Support: The I2C bus on the ESP32©
can support multiple master devices connected
simultaneously, allowing for greater flexibility
in network configuration. We will not use this
configuration here.
• Configurable Pins: Several but not all GPIO pins
of the ESP32© can be easy configured as SCL
(clock) and SDA (data) pins to connect I2C
devices.
• Support for standard I2C devices: The ESP32© I2C
bus is compatible with a wide variety of
standard I2C devices available on the market.
See the following table whit several examples of
I2C interesting devices.
• Device detection capability: The ESP32© has the
ability to scan the I2C bus to detect connected
devices and their addresses.
We will start with this last point, that is, we
will make a script that will help us detect the
devices connected to the I2C bus by analyzing all the
possibilities of 7-bit addresses (0 to 128 or 0x08 to
0x77), which, in the in most cases, they are either
defined by the device manufacturer or are selectable,
within a range, using a jumper or switch.
The MicroPython© script imports the Pin, SoftI2C
and time libraries for managing the GPIO, I2C bus and
time management. Assigns GPIO21 for the SDA pin and
GPIO22 for the SCL pin of the I2C bus and activates
the i2c instance where these GPIO are specified.
The function scan_i2c() is created that tries to
write to an empty buffer of a certain address of the
I2C bus. If this writing does not give an error, it
means that this address exists, otherwise it jumps to
the next one, going through all the possible 7-bit
addresses.
Finally, the script stops if <CTRL>+<C> is
pressed or all addresses have been traversed.
401
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E052_SCAN_I2C.PY: I2C bus scan
# INPUTS: SDA GPIO21 and SCL GPIO22
# OUTPUTS: I2C devices found
############################
from machine import Pin, SoftI2C # Pin, I2C management
import time # Timing control
#GPIO SDA and SCL of ESP32©
sda_pin = 21 # Adjust according to hardware
scl_pin = 22 # Adjust according to hardware
zero = True # Control flag
# SoftI2C objet configuration
i2c = SoftI2C(sda=Pin(sda_pin), scl=Pin(scl_pin))
# Searches all possible I2C addresses (7bits)
def scan_i2c():
global zero
for i in range(128):
time.sleep(0.05)# Pause between samples
try: # Try current address
i2c.writeto(i, b'')
print("Device found on:
0x{:02X}".format(i))
zero = False
except OSError:
print('.', end='')
pass
print('\nScan completed....')
# Script main body
print('I2C BUS SCANNING...')
try:
scan_i2c()
except KeyboardInterrupt:
if zero:
print('No I2C device found...')
print('Scan completed...')
Also try: print(i2c.scan()), what happened?.
There are I2C devices of all types on the
market, here I discuss some interesting examples:
402
Electronics & MicroPython© with ESP32©: 80 complete projects
Device Description
LCD1602© Display LCD 16x2
PCF8574© GPIO extender
ADS1115© External ADC with 4x16bit modules
PCA9685PW© Controller with 16 PWM outputs
MCP23017© Expander with 16 inputs/outputs
BME280© Temperature, humidity and pressure sensor
SI5351A© External RTC
INA219© Power consumption monitor
VL530X© Laser distance meter
CCS811© CO gas and organic compounds sensor
TCA9548A© 8-port I2C bus multiplexer
GY-271© Digital compass
DHT20© Humidity and temperature sensor
ADXL345© Accelerometer
MPU6050© Gyroscope
SH1106© OLED 128x64 display
⊝⊝⊝
403
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 53:
BMP280© Barometric Sensor
In this exercise we will use a barometric
pressure sensor widely used in smartphones and
smartwatches such as the BMP280© or equivalent.
It is a high-precision, low-
cost, low-consumption barometric
pressure and temperature sensor
and since barometric pressure
varies with altitude, it can also
be used as an altimeter (from
-500m to +9,000m).
The BMP280© sensor requires,
if is possible, a high precision
+3.3V voltage regulator and
10k Ω pull-ups to stabilize I2C
communication.
To facilitate the exercise we
will use the HW-611© module that
includes the BMP280© chip, the
pull-up resistors, some
capacitors that act as filters
and the connection pins: VCC
(+3.3V), GND, I2C (SCL and SDA)
and two SPI bus pins (CSB and
SDO) that we will not use in this
exercise since we are only going
to use the I2C bus.
Among the basic characteris-
tics of the BMP280© precision
barometer we can highlight the
following:
404
Electronics & MicroPython© with ESP32©: 80 complete projects
1. Pressure and Temperature: The BMP280© is a high-
precision digital sensor that measures both
atmospheric pressure and ambient temperature.
The BME280© also measures humidity.
2. Communication: It can communicate with the micro
controller through a standard I2C interface or
an SPI interface as we have seen. The I2C
address of the BMP280© is usually 0x76 at the
factory.
3. Pressure Range: It has a wide atmospheric
pressure measurement range, from 0.2966 to
1.0851 atmospheres.
4. Precision and Resolution: It offers high
precision in measuring atmospheric pressure,
with an accuracy of up to ±1 hPa.
5. Temperature Range: It can measure temperature in
the range of -40°C to +85°C.
6. Power Consumption: It has low power consumption,
making it suitable for applications that operate
with limited power sources or batteries.
7. Compact Size: The BMP280© is very compact and
easy to integrate into electronic projects and
embedded systems.
8. Altitude Variations: Offers compensation for
altitude variations, making it useful in
applications such as drone navigation and
digital altimeters.
For our MicroPython© script we will connect the
BME280© sensor to the I2C bus, that is, to the SCL and
SDA pins and we will use the BME280.py library that we
will download from the Web and upload to the ESP32©
through the Thonny© main menu.
To use the I2C bus we will connect the SCL pin
to GPIO22 and the SDA pin to GPIO21 and thus include
them in the definition of the i2c instance. We refer
to the BME280 class included in the BME280.py library
that must be loaded into the ESP32© memory.
Finally, in a loop we will read the temperature
that it will give us in ºC and the pressure that it
will provide in hPa (hecto Pascals) and we will change
it to mBar (milli Bars). The equivalence between hecto
Pascals and milli Bars is 1hPa=1mBar=0.0145psi.
405
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E053_BAROMETER.PY: BMP280© barometer
# INPUTS: Sensor data
# OUTPUTS: Temperature in ºC and Pressure in mBar
############################
from machine import Pin, SoftI2C # GPIO & I2C manager
import time # Timing control
try:
import BME280 # BMP280© library
except:
print('Load the BME280© library into the ESP32©')
# GPIO mapping for i2c
i2c = SoftI2C(scl=Pin(22), sda=Pin(21), freq=10000)
# Main loop of the script
print('BMP280© SENSOR CONTROL')
print('Approach object to measure temperature...')
try:
bme = BME280.BME280(i2c=i2c)
while True:
temp = bme.temperature
#hum = bme.humidity # For BME280©
pres = bme.pressure[:-3]+'mBar' # hPa to mBar
print(f'Temperature: {temp[:-1]}ºC ', end='')
#print(f'Humidity: {hum}') # For BME280©
print(f'Pressure: {pres}')
time.sleep(2)
except KeyboardInterrupt:
print('Script Completed')
except:
print('I2C bus error')
406
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E264: Barometer sensor and LED.
Add a dual LED to the previous exercise. The
green LED should turn on when the script starts and
the barometer is detected on the I2C bus, scanning the
range 0x70 to 0x77. Use, for example, the i2c.scan()
MicroPython© internal function. The red LED will turn
on when a previously defined temperature threshold is
exceeded. This alarm can be canceled with a button IRQ
control.
•E265: Water level control.
Use an ultrasonic sensor to measure the water
level in a tank. Use the BMP280© to offset ultrasonic
sensor readings based on current atmospheric pressure
by adjusting a sound speed table. Use a relay to
simulate filling water if the tank is empty.
•E266: Climate control system.
Use the BMP280© to measure temperature and
atmospheric pressure. Design an algorithm that adjusts
the temperature in a room using a simulated heating
system with an LED connected to the DAC.
•E267: Infrared remote control.
Implement an infrared remote control using an
infrared remote device and an IR receiver. Use the
BMP280© to monitor ambient temperature and barometric
pressure. Program several buttons on the IR remote to
display the temperature in ºC and ºF, the pressure in
mBar, mmHg and psi, the updated time of the RTC, the
internal temperature of the ESP32©, the IP of the WiFi
connection and the value of a connected ADC to a
reference potentiometer.
⊝⊝⊝
407
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 54:
YB-GS07381© Water
Pressure Sensor
This sensor, YB-GS07381©, consists
of a liquid pressure detector (admits
water, oil, diesel and natural gas) that is
embedded in a G1/4” size stainless steel
metal thread that we can connect, with some
adapter, to our sanitary installation.
With this sensor we will be able to
see if the pressure of the sanitary water
in our home exceeds minimum and maximum
limits and the system will notify us if
they are exceeded, being able to cut off
the supply with an electro valve.
There are several models of this sensor
depending on the maximum pressure that we want to
control, so for example, for a normal home we can use
the 0-100psi (pounds per square inch) version which is
equivalent to about 0-7.14bar and which adapts to the
average pressure of a home, which is usually between 3
and 5bars or 43 to 73psi.
The sensor has an output cable with 3 wires: +5V
(red), GND (black) and data (green or blue). This data
signal is analog and varies between 0.5V-4.5V, which
we will have to convert into digital (levels from 0 to
4,095) using the ADC converter of the ESP32©,
therefore the sensor and converter assembly cannot be
very far from the micro controller. As we see, the
output of this sensor exceeds +3.3V, therefore, we
must use a voltage adapter such as a voltage divider
similar to the following.
408
Electronics & MicroPython© with ESP32©: 80 complete projects
To convert the output of the water
pressure sensor (0.5V-4.5V) to the ADC of
the ESP32© (0V-3.3V) we can use a voltage
divider built with two resistors, for
example one 10k Ω connected between the
GPIO of the ADC and the pressure sensor
output (VCC) and another 20k Ω connected
between the GPIO of the ADC and GND. Do the
corresponding calculations to confirm it.
If the distance between the pressure
sensor and the ESP32© is insurmountable, we
can use a second ESP32© close to the main
one (the cost is low) and share information
through file sharing via WiFi.
In the script we import the Pin and ADC
libraries to control the LED and the ADC that we
connect to the GPIO34 and that we configure to accept
0-3.3V input values. We define GPIO26 and GPIO27 for
the green and red LED respectively. We will use green
for normal operation and red for when there is excess
pressure that we set, for example, at 5bars.
We define the function adc_read() that reads the
value of the analog-digital converter or ADC
(measurement range: 0-4,095) and converts it into
pressure (measurement range: 0-7.14bars) which we
format to 2 decimal places.
In the main loop of the program we read the ADC
in a loop and present either the normal value or the
alarm value if a certain maximum is exceeded.
In parallel, we use the LED as a visual trace to
follow the state of the sensor. Finally we can stop
the script with <CTRL>+<C>.
############################
# E054_WATER_PRESSURE.PY: Water pressure sensor & ADC
# INPUTS: ADC in GPIO34 between 0 to +3.3V
# OUTPUTS: ADC in bars GPIO27/26 LED green/red
############################
from machine import Pin, ADC # LED & ADC control
import time # Timing management
409
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# ADC pin configuration
adc = ADC(Pin(34))
# Set the ADC range to 0-3.3V (default 0-1V)
adc.atten(ADC.ATTN_11DB)
# Configure the pins for the LED
green_led = Pin(26, Pin.OUT)
red_led = Pin(27, Pin.OUT)
# Parameters
wait = .5 # Wait between reading
p_alarm = 5 # Alarm pressure
# Analog reading from 0 to +3.3V and controls the LED
# Voltage divider converts (0 to +5V) to (0 to +3.3V)
# The ADC is 12 bits therefore 2^12=4,096 levels
# Water pressure range: 0-7.14bars
def adc_read():
adc_value = adc.read()
pressure = round((adc_value / 4096) * 7.14, 2)
return pressure
# Script main body
print('YB-GS07381© WATER PRESSURE SENSOR')
print('Try turning on and off a water faucet...')
try:
while True:
value = adc_read()
# Control LED according to input threshold
if value > p_alarm: # Alarm pressure
print('ATTENTION: excessive pressure')
green_led.off() # Green LED off
red_led.on() # Red LED on
else:
red_led.off() # Red LED off
green_led.on() # Green LED on
print(f'Water pressure: {value}bars')
time.sleep(wait) # Space between readings
except KeyboardInterrupt:
print('\nClose reading.')
green_led.off() # LED off
red_led.off()
410
Electronics & MicroPython© with ESP32©: 80 complete projects
The complete circuit with the water pressure
sensor, voltage divider, start and alarm display LED
and the ESP32© is similar to the following:
Calculate how many watts
the voltage divider consumes
assuming that the input
current to the ESP32© ADC is
neg li gi bl e. W e c an ad d a
relay, connected to a GPIO
that activates an electro
val ve to cl os e t he wat er
supply if necessary.
⊝⊝⊝
411
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E268: Water pressure monitoring.
Uses the pressure sensor to measure domestic
water pressure and displays the real-time values on
the ESP32© Web server along with the appropriately
updated RTC date and time.
•E269: RGB lighting according to water pressure.
Connect an RGB LED to the ESP32© and use the
water pressure sensor to adjust the LED color using
PWM and a predefined color table.
•E270: Servo based on water pressure.
Connect a servo motor to the ESP32© and use the
pressure sensor to control the position of the servo.
Define pressure ranges for different servo positions
and have the servo move based on the pressure,
simulating the opening or closing of the valve.
•E271: Archived and plotted pressure data.
Configure the ESP32© to record domestic water
pressure data to a file at regular intervals. Activate
the Thonny© plotter to display a graph based on the
pressure values.
•E272: DHT11© double sensor and BMP280© barometer.
Combine the domestic water pressure sensor with
a temperature and humidity sensor (such as the DHT11©)
and create a complete environmental monitoring system.
The data from both sensors must be read, together with
the RTC date and time, and recorded cyclically in a
file on the ESP32©.
⊝⊝⊝
412
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 55:
GY-271© Magnetic Sensor
In this exercise we will use the GY-271©
magnetic sensor incorporated into the HW-246© module,
which is a device that allows
detecting magnetic fields in
three axes (X: up vs down, Y:
left vs right and Z: back vs
forward) in the space.
This sensor is also
known as three-axis electronic
compass or three-axis
magnetometer, which uses the
HMC5883L© chip or equivalent
which is a high-precision, low-
power consumption three-axis
magnetometer sensor.
The GY-271© sensor can
be used for various applica-
tions, such as navigation,
positioning, orientation detec-
tion, metal detection,
measurement of magnetic fields
in industrial environments, etc.
The GY-271© sensor provides magnetic field data
in the form of digital values, which can be read and
processed by a micro controller, such as the ESP32©,
through a I2C communication protocol. These digital
values represent the strength of the magnetic field in
each of the three axes (X, Y, Z), allowing the
relative orientation of the sensor to be determined
with respect to the Earth's magnetic field or other
surrounding magnetic fields. Connect it as follows:
413
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
We will use the
scan_i2c.py script al-
ready seen to know the
I2C address that the
manufacturer has as-
signed to the GY-271©
sensor and we will be
able to verify that it
is the hexadecimal ad-
dress 0x1E.
To know the address of the sensor on the I2C bus
we can also use the i2c.scan() MicroPython©
instruction, which returns the decimal address of the
I2C devices connected to the bus.
Once the address of the sensor on the I2C bus is
known, the script uses the hmc5883l library that we
must download from the Web and install it in the
ESP32© memory. We will invoke the HMC5883L class of
that library, specifying the GPIO of the SDA pins in
GPIO21 and SCL in GPIO22. We could also use the DRDY
interrupt signal which is set to HIGH when there is
new data in the sensor. With this class, we can obtain
the following parameters:
• X, Y, Z: These values represent the magnetic
field strength in the X, Y and Z axes which are
arranged horizontally in the plane of the
surface and vertically up or down. This data
makes it possible to detect nearby magnetic
objects or for navigation.
• Heading: The "heading" or direction calculated
by the sensor represents the orientation of the
magnetic field in relation to the north-south
axis, in other words, it indicates where the X
axis of the sensor is pointing in terms of the
North direction. This value is usually expressed
in degrees or radians, where 0 degrees or 0
radians generally represents the magnetic north.
Heading can be used to implement an electronic
compass that indicates the direction in which
the device is pointing.
414
Electronics & MicroPython© with ESP32©: 80 complete projects
If we connect, by example, the LA1010© analyzer
to the I2C bus we will see the exchange of information
with the ESP32© through the SDA pin and the clock
signals in SCL.
############################
# E055_MAGNETIC_SENSOR.PY: GY-271© magnetic sensor
# INPUTS: SDA GPIO21 and SCL GPIO22
# OUTPUTS: Relative orientation X, Y, Z
############################
from machine import Pin, SoftI2C # GPIO and I2C
from hmc5883l import HMC5883L # Sensor control
from time import sleep # Timing management
# Configure I2C and sensor class
i2c = SoftI2C( sda=Pin(21), scl=Pin(22))
sensor = HMC5883L(sda=Pin(21), scl=Pin(22))
# Scan devices on I2C bus
def scan():
print('Scanning I2C bus...')
devices = i2c.scan() # Devices list
if len(devices) == 0:
print('No devices found')
else:
print('Sensor found on: ', end='')
for dis in devices:
print(hex(dis)) # Hexadecimal address
415
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Script main body
print('GY-271© MAGNETIC SENSOR')
print('Rotate the sensor in all 3 axes...')
scan() # Sensor address
try:
while True:
x, y, z = sensor.read() # Field intensity
# Values formatted with 1 decimal
print(f'X: {x:.1f}, Y: {y:.1f}, Z:
{z:.1f}', end=' ')
guide = sensor.heading(x,y)
# Values formatted with 2 decimals
print(f"Guide: {guide[0]:.2f}º
{guide[1]:.2f}'")
sleep(1)
except KeyboardInterrupt:
print('Script completed...')
except:
print('Sensor error...')
⊝⊝⊝
416
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises
•E273: Magnetic orientation measurement.
Use the GY-271© sensor to measure magnetic
orientation and view the data on a Web server. Users
could access the server from their devices to view
real-time guidance. Use HTML code to view data.
•E274: Control of a relay with compass.
Use the guidance provided by the GY-271© sensor
to control a relay. Activate the relay when the
orientation points approximately South and deactivate
it when it points approximately North. Connect a servo
whose rotation simulates a compass.
•E275: Compass, rain sensor and barometer.
Combine the GY-271© sensor with a rain sensor
and the BMP280© barometer to create a weather
monitoring system. Use the rain sensor to detect
humidity and the barometer to measure atmospheric
pressure. Save all data to a file.
•E276: Sound and compass sensor.
Use the GY-271© sensor together with a sound
sensor to detect loud sounds. Set the system to issue
an alert when the sound level exceeds a threshold and
the magnetic orientation is within a predefined
threshold.
•E277: Orientation control for a vehicle.
Implement an orientation control system for a
possible vehicle using the GY-271© sensor and a line
follower. For example, we could simulate a vehicle
that can move in a specific direction based on the
magnetic orientation detected by the sensor and/or
following a line.
⊝⊝⊝
417
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercises 56:
OV7670© Camera Sensor
In this exercise we will
connect the OV7670© camera
to the ESP32©, which, even
though it is a camera with
complex connections
(proprietary I2C, parallel
data bus and control
signals), has the great
advantage of being an ultra-
cheap camera that can be
used to capture still images
and low-resolution videos.
The basic characteristics of
this camera can be
summarized as:
• OV7670© Sensor: Small size OmmiVisión© sensor,
+3.3V power supply, VGA format.
• Connection: SCCB control bus compatible with I2C
and 8-bit parallel bus to obtain the frame,
sampling, format and resolution.
• Image: VGA 640x480 up to 30 frames/second,
configurable image quality and transmission.
• Processing: Gamma curves, white balance,
saturation, chroma, image stabilization, noise
removal, automatic black level calibration,
color control, saturation, hue, gamma,
sharpness, etc.
• Formats: RawRGB to RGB (GRB4: 2:2, RGB565/
555/444), YUV (4:2:2) and YCbCr (4:2:2).
Resolution matrix 640X480 pixels.
• Power: +3.3V and consumption 60mW at 15fps and
only 20uA in idle state.
• Optics: 1/6”, 25º angle, 30fps, 46dB.
418
Electronics & MicroPython© with ESP32©: 80 complete projects
The camera requires the SCCB bus, a proprietary
version of the I2C bus but not 100% compatible, for
configuration and control, the 8-bit parallel bus
(D0...D7) for data transfer between the camera and
ESP32© and other pins. (VS, HS, PCLK, MCLK, RST and
PWNN) for camera hardware control.
The SDA and SCL pins must have +3.3V 10k Ω
pull-up resistors to stabilize the signals and make
them reliable. These resistors are not included (*).
Important: Due to the complexity of the
necessary MicroPython© script and the absence of
libraries for this specific device (there are for
Arduino©), this exercise only describes the camera and
its possible connection to the ESP32©. We will use the
connections:
VSYNC (Vertical Sync): Start
and end of a frame in the
video signal.
HSYNC (Horizontal Sync):
Start and end of each line
of pixels in the video
signal.
PCLK (Pixel Clock): Each
pulse of this clock
indicates a new pixel
available to be read.
MCLK (Master Clock): Clock
for the internal operation
of the camera, video signal
generation and image
processing.
RST (Reset): Useful to
initialize the camera before
starting image capture.
PWDN (Power Down): Puts the
camera into a low power
state or to turn it off
completely.
419
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Important: For the correct operation of the
camera, the external master clock MCLK must have a
typical frequency of 20MHz that we could generate with
an ESP32© GPIO configured for PWM but it is too high a
frequency for this micro controller. With a 4.7MHz
signal we can obtain acceptable but unreliable data
over time.
As we can see, this exercise is a bit
“cumbersome” because of having to connect so many
cables and because of the difficulty of finding a
simple MicroPython© library developed and tested for
this camera.
A small script is attached where communication
with the camera is achieved to read and write to its
registers using I2C, but synchronization with the
horizontal and vertical signals does not seem feasible
with a MicroPython© script running on an ESP32©.
############################
# E056_CAMERA.PY: OV7670© camera (test only)
# INPUTS: SCCB (I2C) proprietary bus
# OUTPUTS: Addresses, registers
############################
from machine import Pin,SoftI2C,PWM # GPIO, I2C, PWM
from time import sleep # Timing control
# GPIO of the camera
sda = 21 # SCCB SDA I2C data
scl = 22 # SCCB SCL I2C clock
mclk = 32 # MCLK Master clock
dire = 0x21 # Camera address
# PWM on MCLK, essential for its operation
pwm_pin = PWM(Pin(mclk), freq=4700000) # Not down
# I2C communication (*)
i2c = SoftI2C(sda=Pin(sda), scl=Pin(scl))
# Scan available I2C addresses
def detect():
try:
i2c.writeto(dire, b'')
420
Electronics & MicroPython© with ESP32©: 80 complete projects
print(f'Camera connected on: {hex(dire)}')
except:
print('Camera not detected...')
# Read a register
def read_register(register):
i2c.writeto(dire, bytearray([register]))
response = i2c.readfrom(dire, 1) # Read one byte
print(f'Command: {hex(register)} ->
{hex(response[0])}')
# Writes the data to the register and reads it
def write_register(register, data):
i2c.writeto_mem(dire, register, bytearray([data]))
read_register(register)
# Main program
try:
detect()
sleep(.1)
read_register(0x0A) # Product ID MSB
read_register(0x0B) # Product ID LSB
read_register(0x1C) # Brand ID MSB
read_register(0x1D) # Brand ID LSB
write_register(0x55, 0x00) # Bright to 0
write_register(0x56, 0x40) # Contrast to 40
except:
print('Error in camera...')
finally:
pwm_pin.deinit() # Stop MCLK
⊝⊝⊝
421
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 57:
COM90133P© Joystick
I think it is not necessary to explain what a
Joystick is because we have all used it at some point
(game consoles, remote-controlled toys, drones, etc.)
but they are also used in industry (excavators,
elevators, airplanes, medicine, etc.)
In its basic and totally analog version, like
the COM90133P©, it is made up of a package that
c o n t a i n s t w o 10k Ω potentiometers (variable
resistors) located at 90º and that measure the
position of a control on two axes: X (RVX) and Y
(VRY ) and one or more pushbuttons (SW on the Z axis)
that are activated by pressure. This Joystick must
also be connected to +3.3V and GND.
A specific position of the Joystick is a
specific combination of the X, Y and Z values,
therefore, by translating the ohm or voltage values
into position values with an ADC, we will know at all
times how that Joystick is located.
422
Electronics & MicroPython© with ESP32©: 80 complete projects
In this exercise we will connect the Joystick to
the ESP32© ADC analog-digital converter that will
convert the analog variables X, Y to digital variables
(between 0 and 4,095) that we can use to act with any
other type of device. We will connect the output of
the SW button to the GPIO14 of the ESP32©, which we
will manage by polling.
For its correct operation, the Joystick requires
a very simple and easy to understand control circuit.
The signal from the SW
button has a pull-up to
+3.3V to stabilize it, so
that when we press the SW
button it is set to LOW
and D1 lights up. We will
detect this change by
polling. D0 lights up
when powering the
Joystick circuit. R0 and
R1 l i m i t t h e c ur r e n t
flowing through LED D0
and D1. The X, Y signals
go from the Joystick to
the ADC analog digital
converter directly into
GPIO32 and GPIO35
respectively.
In the script we will
use the Thonny© “plotter”
function to display the variables limits, we have to
use the adc.atten(ADC.ATTN_11DB) instruction to enable
these limits since, by default, the ADC measurement is
limited to the 0-1V range.
On the other hand, let us remember that the ADC
of the ESP32© is 12bit and therefore it will give us
12
2 =4,096 values, from 0 to 4,095, with the rest
position of the joystick being at (2048,2048)
theoretically, in practice this is not the case
because the qualities of the Joystick, ADC, power
supply, etc.
423
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The script imports the Pin, ADC and utime
libraries, defines the configuration of the X axis in
GPIO32, Y axis in GPIO35 and the selection switch, SW,
in GPIO14. Specify the function read_joystick(pin),
which will read the corresponding pin, adjust the
reading value to +3.3V maximum and return the reading.
The main loop reads X and Y in the DAC and the
switch directly in the GPIO14 by polling and presents
these values in a formatted way.
To view the joystick values graphically, we can
use the plotter built into Thonny© by accessing it
from: [menu] [view plotter].
############################
# E057_JOYSTICK.PY: Read values X, Y, SW of Joystick
# INPUTS: Joystick position
# OUTPUTS: Plotter X, Y values
############################
424
Electronics & MicroPython© with ESP32©: 80 complete projects
from machine import Pin, ADC # GPIO and ADC control
import utime # Timing management
# Pins configuration
pin_x = Pin(32, Pin.IN) # X position
pin_y = Pin(35, Pin.IN) # Y position
pin_s = Pin(14, Pin.IN) # Pushbutton on/off
# Joystick initial position
x_init = y_init = 2048 # Medium point
s_init = 0 # SW not pressed
# Read analog value from Joystick
def read_joystick(pin):
adc = ADC(pin) # Activate ADC in pin
adc.atten(ADC.ATTN_11DB) # Range 0 to 3.3V
return adc.read() # ADC reading
# Script main body
print('JOYSTICK CONTROL')
print('Move and/or press the controller...')
try:
while True:
value_x = read_joystick(pin_x) # X position
value_y = read_joystick(pin_y) # Y position
value_s = pin_s.value() # SW pressed
# View X, Y, S values if they change
if value_x != x_init or value_y != y_init
or value_s != s_init:
print("X:", value_x, "Y:", value_y,
"Switch:", value_s)
x_init = value_x
y_init = value_y
s_init = value_s
utime.sleep_ms(50) # Wait between reading
except KeyboardInterrupt:
print('Program completed...')
⊝⊝⊝
425
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E278: Joystick and RGB LED.
Combines the reading of a joystick and the color
variation of an RGB using the X and Y variables for
the color change by PWM and the joystick switch to
turn off the RGB completely. Also use the plotter.
•E279: Joystick and ultrasonic meter.
Implement a script in MicroPython© that reads
the Joystick values (X and Y axis) and displays the
position on the ESP32© console. Combine this with the
reading from an ultrasonic meter to display the
distance measured when the joystick switch is pressed.
Defines a calculation algorithm with the X and Y
positions and the distance to simulate a right or left
turn with a two-color LED.
•E280: Analog signal generation with DAC.
Create a script that uses the ESP32© digital-to-
analog converter, DAC, to generate an analog signal.
Control the amplitude and frequency of the signal
using the Joystick connected to the ESP32© to
dynamically change its output. We can view the signal
generated on the plotter.
•E281: Timer and Joystick with LED.
Design a script that uses a hardware timer to
control an LED. Use the joystick to adjust the LED
flashing frequency. Experiment with different timing
intervals to achieve various visual effects.
•E282: Joystick data recording with RTC.
Create a script that logs the joystick values
along with RTC data updated from the Internet to a
trace file.
⊝⊝⊝
426
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 58:
HW-209© Logic Level Converter
Sometimes we will need to change the electronic
logic levels between digital devices in a simple and
uncomplicated way, that is, we will need to adapt the
voltages offered by one device, for example the +5V to
the +3.3V offered by another and vice versa.
Typical examples are the different digital logic
levels provided by the usual Arduino©, Raspberry©,
ESP32©, etc. controllers or those provided by some
devices such as sensors, actuators, interfaces,
controllers, displays, integrated circuits, etc.
This conversion of
voltage levels and digi-
tal logic levels is
essential to avoid dam-
age to the circuits. To
make this change in dig-
ital logic levels we
will use the level con-
verter or level shifter,
which basically consists
of a c i r cu i t w i t h a
transistor and appropri-
ate control resistors,
allowing bidirectional
conversion, at high
speed and in a simple
way.
In the circuit LV1 is the low signal level (for
example +3.3V), HV1 is the high signal level (for
example +5V), LV is the low level power source (+3.3V)
and HV is the high level power source (+5V).
427
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
An example of a
m o d u l e t h a t i n t e-
grates up to 8
digital logic level
converters is the HW-
209© module where we
must connect VCCA to
+5V source, VCCB to
+3.3V source, GND,
the AX pins to the
high level signal and
the BX pins to the
low level signal. The
conversion is bidi-
rectional, high speed
and digital levels.
Remember that these converters are digital, they
are not suitable for converting analog levels as they
are not linear devices and introduce multiple
distortions. For analog level converters we have
already seen simple voltage dividers.
Finally, comment that with this type of
converter we could adapt the voltage levels between
UA R T , G P IO , I 2C , S PI p or t s of v a ri o u s m i c ro
controllers that use different voltages in their GPIO
and logically with sensors and actuators from
different manufacturers with different voltage
protocols in their communications.
In the following exercises we will see examples
of use of this type of digital logic level converters
used to adapt voltage levels provided by sensors to
those used by the GPIO of the ESP32©.
⊝⊝⊝
428
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 59:
MQ135© Air Quality Sensor
The MQ135© is an
electro-chemical sensor, in a
metal encapsulation, that
measures air quality by
varying its internal
resistance depending on the
concentration of the gas to
which it is exposed.
In this case it is also
a small module that includes
all the elements already con-
nected: sensor, sensitivity
potentiometer, status LED,
connectors, etc.
This sensor, for example, could be placed in the
garage of the home and detect excessive concentrations
of harmful gases ( CO 2 , NH 3 , NO, etc.), produced
by the accumulation of vehicle exhaust gases in a
usually closed environment like a garage. We could
also install it in other rooms of the home such as the
kitchen, fireplace area of the living room, etc. We
must consider that:
1. Important: This module requires a +5V power
supply with a minimum consumption of about 140mA
to preheat the sensor and its digital and analog
outputs are +5V.
Take this “high” consumption into account to
connect it to a suitable power source. It is not
possible to power it from the internal source or
through the ESP32© directly.
429
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
2. AOUT: Analog output that we could connect to a
GPIO of the ESP32© with ADC analog-digital
conversion capacity. This output varies between
0 and +5V, therefore we need a voltage divider
to avoid damaging the ESP32©.
3. DOUT: On/off type digital output at +5V that we
must connect to a GPIO of the ESP32© using a
logic level converter like the one already seen.
4. Reverse logic operation: DOUT=LOW indicates
toxic gas alarm, DOUT=HIGH indicates no toxic
gas alarm.
5. Mini green LED ON: the sensor is powered.
6. Mini red LED ON: there is an alarm due to poor
air quality.
7. I t has an adjustable trimmer (mini
potentiometer) to regulate the sensitivity of
the sensor and avoid false alarms. To adjust,
turn it and see how the red LED is.
8. Due to the consumption of the sensor (140mA), we
will connect it to the external power supply MB-
102© already mentioned in other exercises.
9. We will use the HW-209© digital logic level
converter or equivalent, which uses several
BSS138© Mosfet transistors and 10kΩ resistors to
adapt the +5V output to the necessary +3.3V.
We will build a script that uses the GPIO27 to
capture the DOUT logic level coming from the MQ135©
sensor and that we have previously converted with the
logic level converter from 0-5V to 0-3.3V. In order to
use the AOUT output we must use a voltage divider to
adapt the analog level.
I will not tire of commenting that +5V signals
should not be connected to the GPIO of the ESP32©. Any
signal input to the GPIO that exceeds +3.3V will burn
out the circuitry on the board.
430
Electronics & MicroPython© with ESP32©: 80 complete projects
A good way to avoid this situation is to use
this logic level converter and isolator or an special
isolator circuit with an optocoupler that acts as a
total electrical separation between the ESP32© and the
outside.
The MicroPython© script that we can use is the
same as that of the infrared sensor, tilt sensor,
vibration sensor, etc. in which the on/off signal of
the corresponding sensor is detected.
We will have to select the appropriate GPIO, the
type of logic (direct or reverse), the timing, the
messages, the algorithm, etc.
⊝⊝⊝
431
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E283: Air quality sensor, ADC and IRQ.
Modify the commented script so that it controls
the digital output DOUT of the sensor by IRQ instead
of polling and also adds the management of the analog
output AOUT using a voltage divider and a GPIO with
ADC of the ESP32©, shows the activation of a alarm and
its value. Why can't we connect AOUT to a logic level
converter?.
•E284: Environmental quality sensor.
Include fire, smoke and humidity sensors (rain
sensor) in the previous exercise and use the ADC
converter of the ESP32©, with the appropriate level
converters, to read the analog values of all of them,
record them in a file along with data of the RTC and
launch the corresponding alarms.
•E285: Air quality and alarms.
Configure the ESP32© as a Web server where we
can view the air quality level and the air quality
alarm status. In addition to recording all the
relevant information in a file, send these alarms to a
predefined email.
•E286: Air quality, LED array and relay.
Add to the previous exercise an LED array where
the level of air quality is displayed and in case of
alarm activate a relay that sounds a passive buzz,
controlled by PWM, and simulate with a servo the
opening of a door to force ventilation.
•E287: MQ135© and BLE sensor.
Add to the previous exercise some Bluetooth
buttons that allow, from a mobile phone, to know the
status of each ambient sensor mentioned.
⊝⊝⊝
432
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 60:
Alternating Current Sensor
On this occasion we will build a sensor that
will act as an interface with circuits where there is
an AC voltage of up to 250V and we want to know if
they are active or not. To do
this we can use an already
integrated AC sensor module
such as the ZMPT101B© that
gives us a sinusoidal voltage
from 0 to +3.3V proportional
to the 250V alternating signal
for which this sensor has a
small transformer, a voltage
divider and a comparator of
the LM358© type that shifts the alternating signal to
a positive sinusoidal signal from 0 to +3.3V.
An alternative option to this is to build
ourselves an alternating current sensor that will give
us an on/off type signal from 0 to +3.3V when there is
or is not AC voltage in a certain circuit. To do this
we will use the following circuit or equivalent that
we have simulated with iCircuit© application:
433
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Basically the circuit consists of a voltage
reducer composed of a resistor and a capacitor in
series, which attack a diode bridge that converts the
alternating voltage of 220V into DC and which attacks
an optocoupler, represented by a relay and a
transistor, all of this simulated with iCircuit©.
At the output of the optocoupler we will have an
on/off signal, with inverse logic, that is, when 220V
is detected the GPIO goes from HIGH to LOW and vice
versa and that can directly attack a GPIO of the
ESP32© simulated with an LED.
The voltage reducer is composed of a resistor vs
capacitor pair of the 220Ω+470nF type, with a total
Z=220+6k8j Ω impedance (complex notation), to
reduce the input current of the optocoupler to less
than 30mA.
To calculate these values we will use:
2
P=i R=1/ 4w (power of an ordinary resistor)
and with i=30mA maximum, we have: R=220Ω .
On the other hand, for the capacitor, use:
−3
C=I /(2 π fV )=(30∗10 )/(2 π∗50∗220)=470nF where:
• I: maximum current that passes through it, 30mA.
• f: frequency of the electrical network, 50Hz.
• V: mains voltage, 220V (change to 235V or other,
if applicable).
• C: capacitor capacity, 470nF (nano farads).
Choose a capacitor for more than 500V and change
from 50Hz to 60Hz for America or other areas.
Finally, the total impedance Z is the resistance
R plus the impedance of the capacitor C which is
1
calculated with: Z C= and therefore:
2πf C
1 1
Z =R+Z C =R+ =220+ −9
Ω
2 π fC 2 π∗50∗470∗10
434
Electronics & MicroPython© with ESP32©: 80 complete projects
With this we will have to: Z =220+6k8j Ω.
The B250C/1000V© bridge rectifier or equivalent
is composed of 4 diodes in a “Wheatstone Bridge”
configuration that converts the 220V alternating
current into a direct current voltage suitable for the
optocoupler.
The 1.5uF/35V capacitors are made of tantalum
and prevent random jumps of the 4N35© type opto
coupler, which is activated with each alarm by turning
its output to LOW. The 10MΩ resistance at the base of
the phototransistor, at the input of the optocoupler,
prevents noise and fixes its sensitivity. Finally we
add pairs of 1uF, 100nF capacitors and a 10kΩ attack
pull-up to each GPIO of the ESP32©.
With all this, the circuit diagram would be the
following and we could replicate it for each 220V
voltage sensor that we need to control.
The MicroPython© script that we can use is very
similar to that used with the infrared sensor, tilt
sensor, vibration sensor, etc. in which the on/off
signal of the corresponding sensor is detected. We
must select the appropriate GPIO, the type of logic,
direct or inverse depending on whether we want to
detect the alternating current or its absence, the
timing, the messages, the algorithm, etc.
⊝⊝⊝
435
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 61:
Send email with ESP32©
On many occasions we will need
to send data from the ESP32© to
an email address, for example,
at the end of the day the ESP32©
will send a file with the data
captured from various sensors
and/or actuators.
This function is very useful for tracking
variables, errors, turning devices on and off, sending
alarms, the status of sensors and actuators, etc.
In order to use this function, we must have a
series of parameters such as the following examples
and which we must adjust with our data.
• Password: mypassword
• Destine:
[email protected] • Subject: ESP32©
• Body: Temperature and pressure
• Annex: file.txt
In this script, the file.txt located on the
ESP32© is sent from the address
[email protected],
with the access password mypassword, to the address
[email protected], with the subject ESP32© and
the body of the e-mail Temperature and pressure.
The script uses an SMTP type email server, if we
need an IMAP type server we would have to make changes
that are detailed on the Internet. We can use chatGPT©
to help us with the configuration and with the
MicroPython© script.
436
Electronics & MicroPython© with ESP32©: 80 complete projects
I n t h e s c r i p t , t h e usocket libraries are
imported for communication on the server and ubinascii
to perform binary vs ASCII conversions. The main
parameters of the source SMTP server that we are going
to use are configured, the send_email() function is
defined that uses the source, subject, body and
attachment data. This function connects to the server,
performs authentication, creates the email, attaches
the attachment and sends it to the indicated
recipient, controlling the necessary messages and
errors.
############################
# E061_EMAIL.PY: Send ESP32© file by e-mail
# INPUTS: File, origin, destine of e-mail
# OUTPUTS: Send [file.txt]
# IMPORTANT: Server must be of the SMTP type
############################
import usocket as socket # Server communications
import ubinascii, time # Convert Binary-ACII & time
# E-mail configuration
USERNAME = '[email protected]' # Origin
PASSWORD = 'mypassword' # Change
destine = '[email protected]' # Destination
subject = 'ESP32©' # Subject
body = 'Temperature and pressure' # Body
annex = 'file.txt' # Annex
# SMTP server
SMTP_SERVER = "smtp.myserver.com"
SMTP_PORT = 25 # Typical for no encrypted SMTP
# Send email with attached attachment
def send_email(to, e_subject, e_body, file_path):
response = "" # Save error messages
try:
sock = socket.socket()
sock.connect(socket.getaddrinfo(SMTP_SERVER,
SMTP_PORT)[0][-1])
print('Server connected')
# Authenticate to the SMTP server
sock.send(b"EHLO esp32\r\n")
time.sleep(0.1) # Pause
response += sock.recv(256).decode('utf-8')
sock.send(b"AUTH LOGIN\r\n")
time.sleep(0.1) # Pause
response += sock.recv(256).decode('utf-8')
sock.send(ubinascii.b2a_base64(USERNAME.
encode()).strip() + b'\r\n')
time.sleep(0.1) # Pause
response += sock.recv(256).decode('utf-8')
437
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
sock.send(ubinascii.b2a_base64(PASSWORD
.encode()).strip() + b'\r\n')
time.sleep(0.1) # Pause
auth_response = sock.recv(256).decode('utf
-8').strip()
response += auth_response
if not auth_response.startswith("235"):
print('Error, check e-mail credentials')
return
print('Wrong credentials')
# Send the email with the attached attachment
sock.send("MAIL FROM:<{}>\r\n".format
(USERNAME).encode())
sock.send("RCPT TO:<{}>\r\n".
format(to).encode())
sock.send(b"DATA\r\n")
# Send message headers and body
sock.send("From: {}\r\n".format(USERNAME)
.encode())
sock.send("To: {}\r\n".format(to).encode())
sock.send("Subject: {}\r\n".format(e_subject)
.encode())
sock.send("Content-Type: multipart/mixed;
boundary=\"boundary\"\r\n\r\n".encode())
sock.send("--boundary\r\n".encode())
sock.send("Content-Type: text/plain\r\n\r\n{}"
.format(e_body).encode())
sock.send("\r\n--boundary\r\n".encode())
sock.send("Content-Type: application/
octet-stream; name=\"{}\"\r\n".format(file_path)
.encode())
sock.send("Content-Disposition: attachment;
filename=\"{}\"\r\n\r\n".format(file_path).encode())
# Read the content of the annex to attach it
with open(file_path, "rb") as file:
sock.send(file.read())
sock.send("\r\n—boundary--\r\n.\r\n"
.encode())
time.sleep(1) # Minimum send time
print('Message sent')
except OSError as e:
print('Sent error:', e)
finally:
# Close the connections
sock.send(b"QUIT\r\n")
sock.close()
send_email(destine, object, body, annex)
⊝⊝⊝
438
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E288: Joystick and e-mail.
Extend the Joystick exercise to send by e-mail a
file with the X, Y coordinates and status of the push
button, to an address once an hour.
Integrate in the same script the creation of
joy.txt file from the Joystick movement data, a
hardware timer that controls the time change and the
sending of the e-mail with the attached file.
•E289: Environmental monitoring.
Use the DHT11© dual temperature and humidity
sensor to measure both ambient temperature and
humidity and set a timer to take measurements every
certain time interval, store them in a file and send
them by email.
The file will include time, ambient temperature,
humidity, internal temperature of the ESP32© and
status of a touch button.
•E290: Water level with ultrasonic sensor.
Si mu la t e w it h a M i cr oP yt h on © s c ri pt t he
measurement of the water level in a tank using an
ultrasonic sensor connected to the ESP32©. Configure a
hardware timer to perform periodic measurements every
hour and save the results in a file that will be sent
by email twice a day. In tests, use tighter and/or
more comfortable times.
•E291: Light control with DAC.
Use a MicroPython© script and the ESP32© digital
analog converter (DAC) to control the intensity of an
LED light based on the internal temperature of the
ESP32©, also implement a timer to periodically read
that temperature and send an email report with the
changes in the same by request from an HTML or
Bluetooth button.
439
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
•E292: Voltage monitoring with ADC.
Connect a voltage divider, created by 2
resistors to an analog input pin (ADC) to measure the
voltage of a battery. Keep in mind that for the
maximum battery voltage we must have a maximum of
+3.3V at the ADC input.
Add a timer to perform periodic measurements of
that voltage, store the results in a file, and email
them as a periodic battery status report.
•E293: Temperature alarm system.
Use the temperature sensor (e.g. thermistor) to
measure the ambient temperature, set a maximum and
minimum temperature threshold and a timer to
periodically check if the temperature exceeds the
predefined ranges, in which case an alert email will
be sent attaching a log file with measurements.
⊝⊝⊝
440
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 62:
Home Automation with IFTTT©
In this script we have a very interesting
example of integrating the ESP32© as a basic Home
Automation controller that
starts with a Web action, that
is, with a Webhook and the
IFTTT© integrator interprets
and executes an action with a
specific Home Automation
device.
To carry out this task, that is, to be able to
interact with Home Automation devices, we have two
methods, the “difficult” method and the “easy” method.
The “difficult” method would try to get each
API, that is, each MicroPython© application that runs
on the ESP32© and that can interact, via WiFi or
Bluetooth, with each of the Home Automation devices.
This option is quite difficult because for many
devices there are no libraries designed for
MicroPython©, or they are complex or difficult to
integrate into the ESP32©.
The “easy” method consists of relying on
software that integrates applications and devices of
various kinds, and what is more difficult, even from
various manufacturers that use different protocols and
operating specifications.
There are various integrators on the market,
this book uses IFTTT©, “If This then That”, which is
simple, cheap, with free options, and above all with
th e po s si b i li t y o f in t e gr a ti n g mu l ti p le H o me
Automation devices from various companies.
441
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Comment that, in general, today each Home
Automation device has an APP that allows its control,
schedule, creation of scenes, dependence on events,
etc. but what is interesting is being able to
implement algorithms that relate these devices and
this is where IFTTT© comes in.
In the following example, a script has been
created that can execute an IFTTT© function to
interact with various devices: Tao© infrared lights
with Broadlink©, Samsung© TV, Yamaha© audio amplifier,
HP© printer, touch screens with Sonoff© , Raspberry©,
electronic markers, Apple© devices, Wiz© aroma
diffusers, Sonoff© Christmas lights, coffee maker,
Tadoº thermostat, sending SMS, file backup, etc. As we
can see, we can interact in a simple way with multiple
devices of multiple models and brands.
The script describes a summary of operation as
an example list of Webhooks or actions to use with
IFTTT©, imports the urequests library to send Web
requests, includes the IFTTT© personal key that we
will later see how to achieve.
The ifttt() function is defined, which executes
the Webhook in which the Web request is constructed
and the server's response is controlled by controlling
the corresponding errors.
As we can see, this script is very simple and
brief and does not require knowing the hardware and
operation of each of the Home Automation components or
devices that make up our home. IFTTT© includes many
brands of devices and these examples include:
Broadlink© devices that are controlled by infrared,
Sonoff© WiFi switches, Wiz© WiFi lights and switches,
Tadoº thermostat, SMS with any telecom operator,
Dropbox storage ©, etc.
In order to use IFTTT© we have to follow the
following steps in detail but on the Web there is a
lot of documentation and examples of how IFTTT© is
used. At the end of the book there is an annex with
more information.
442
Electronics & MicroPython© with ESP32©: 80 complete projects
• Sign up at www.ifttt.com (free or Pro).
• Create a Webhook, give it a [webhook] name and
act on a device with: [ifttt] [create] [webhook]
[receive a web request] [event name] [create
trigger] [then that...] [device] [options]
[create action] [continue] [finish].
• Obtain the key [key] from IFTTT© with: [profile]
[my services] [webhooks] [documentation] [your
key is:].
• Run the Webhook with:
https://maker.ifttt.com/trigger/
[webhook]/with/key/[key] or:
curl -X POST https://maker.ifttt.com/trigger
/[webhook]/with/key/[key]
Examples for MicroPython© on ESP32©:
urequests.post('https://maker.ifttt.com/trigger/'
+printer_on+'/with/key/'+123)
Webhook Action Device Description
colors_on/off On/off Broadlink© Colored lamp
tv_lounge_toggle On/off Broadlink© Lounge TV
audio_toggle On/off Broadlink© Lounge audio
spotlight_on On Broadlink© RF spotlight on
spotlight_off Off Broadlink© RF spotlight off
printer_on/off On/off Sonoff© Printer switch
screen_on/off On/off Sonoff© HDMI screen switch
home_auto_on/off On/off Sonoff© Raspberry© switch
wizlounge_on/off On/off Wiz© Light and sockets
chameleon_red On Wiz© Chameleon in red
apple_on/off On/off Apple© Apple TV©
sunrise_on/off On/off Smartlife© Sunrise light
diffuser_on/off On/off Smartlife© Diffuser color spray
send_sms Send SMS Alarm to mobile
log_dropbox Add Dropbox© Add file.log
tado_on/off/auto Mode Tadoº Thermostat mode
443
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E062_IFTTT.PY: Activate a device using IFTTT©
# INPUTS: Webhook name and [my IFTTT© key]
# OUTPUTS: Server activation and response
############################
'''
WEBHOOK EXAMPLES (ESP32© STARTS A WEBHOOK)
---------------------------------------------------
NAME ACTION DEVICE DESCRIPTION
---------------------------------------------------
colors_on/off on/off Broadlink© Lamp
tv_lounge_toggle on/off Broadlink© Lounge TV
audio_lounge_toggle on/off Broadlink© Amplifier
spotlight_on on Broadlink© RF spotlight
printer_on/off on/off Sonoff© Switch
screen_on/off on/off Sonoff Raspberry©
wizlounge_on/off on/off Wiz© Light and plug
chameleon_red on Wiz© Red light on
apple_on/off on/off Apple© Apple TV©
sunrise_on/off on/off Smartlife© Sunrise light
diffuser_on/off on/off Smartlife© Diffuser spray
tado_on/off/auto on/off Tado© Thermostat
send_sms send SMS Send alarm
log_dropbox add Dropbox© Add file
'''
import urequests # Web request management
key_ifttt ='[my IFTTT© key]' # Webhooks IFTTT© key
# Invoke the IFTTT© Webhook
def ifttt(webhook):
try:
response =
urequests.post('https://maker.ifttt.com/
trigger/'+webhook+'/with/key/'+key_ifttt)
if response.status_code == 200:
print(response.text)
print('Webhook activated: '+webhook)
else:
print('Server error: '+response.
status_code)
except:
print('Webhook error: '+webhook)
# Examples:
ifttt('colors_off') # Turn off colored lamp
ifttt('tado_auto') # Active Tadoº thermostat mode
444
Electronics & MicroPython© with ESP32©: 80 complete projects
To complete this exercise we will do the reverse
management of the previous exercise, that is, the ESP32©
in server mode, will receive an IFTTT© Webhook request
from various events and will be able to act accordingly:
indicating it on the console, acting with devices or even
generating other Webhooks to act, via IFTTT©, with other
Home Automation devices.
For all of the above, we can add to our project a
Home Automation algorithm for interaction between sensors
and actuators and with this we can build a simple Home
Automation system that is very easy to maintain.
In that script we define 4 devices and 8 actions,
for example, it is best to define a table like the
following:
Name Origin Device Event Action
score_on/off Board eWelink© On/off LED, color
color_on/off Lamp Wiz© On/off LED, color
print_on/off Printer Ewelink© On/off LED, color
tado_away LED, color,
Tadoº Tadoº Modo
tado_home printer on/off
In the script we include a general description
of operation indicating the name of the event
generated in IFTTT©, the origin, the hardware device
that generates it, the type of event and the action
that this script will perform with that event. This is
where the options are endless and we must specify
which Home Automation algorithm we want to be carried
out in our ESP32© with this information, the internal
information (time, temperature, variables, etc.) and
the algorithm that we have designed.
The script imports the socket, machine and
urequests libraries, defines the internal port to
which the external port input defined in the router is
diverted, includes the key defined in IFTTT©,
configures the status LED, the message() function
which displays both messages in the console and on the
W e b , t h e control() function, which includes the
actions to be performed with each of the events in the
previous table and the actions that we have defined.
445
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Finally, the ifttt() function is defined to send
the Webhooks to control Home Automation devices, the
main loop of the server where the received messages
are checked and the corresponding controls for IFTTT©
sending errors, such as server creation, etc.
###########################
# E062_IFTTT_SERVER.PY: Activate Web server & IFTTT©
# INPUTS: [IP]:[int_port]/[origin] [IFTTT© key]
# OUTPUTS: LED simulation with [origin] and algorithm
# IMPORTANT: In IFTTT© use NO-IP© as:
# http://[name].hopto.org:[ext_port]/[action]
# On the router, derive the port [ext_port] to
# [int_port] and the [IP] of the ESP32©
############################
'''
EXAMPLES OF WEBHOOK (ESP32© RECEIVE A WEBHOOK)
NAME ORIGIN DEVICE EVENT ACTION
score_on/off board eWelink© on/off LED
color_on/off lamp Wiz© on/off LED
print_on/off printer eWelink© on/off LED
tado_away/home tado Tado© away/home Several
'''
import socket # Web server management
import machine # GPIO control
import urequests # HTTP client
# Server port configuration
port = '[int_port]' # For example the xx80
key_ifttt = '[my IFTTT© key]' # Webhook IFTTT© key
# LED configuration to display actions
red = machine.Pin(27, machine.Pin.OUT) # Red LED
green = machine.Pin(26, machine.Pin.OUT) # Green LED
red.value(0) # LED off
green.value(0)
# Send messages to console and Web
def message(men):
try:
print(men)
# Text that appears on the Web
response = "HTTP/1.1 200 OK\n\nLED Estate:
{}".format(men)
conn.send(response)
except:
446
Electronics & MicroPython© with ESP32©: 80 complete projects
print('Error sending message')
# IFTTT© action with the Home Automation algorithm
def control(accion):
if 'score_on' in accion: # Snooker© score On
message('Scoreboard on')
red.value(1)
green.value(0)
ifttt('colors_on') # Colored lamp on
if 'score_off' in accion: # Snooker© score Off
message('Scoreboard off')
red.value(0)
green.value(1)
ifttt('colors_off') # Colored lamp off
if 'color_on' in accion: # Colored lamp on
message('Colored lamp on')
red.value(1)
green.value(0)
ifttt('colors_on') # Colored lamp on
if 'color_off' in accion: # Colored lamp off
message('Colored lamp off')
red.value(0)
green.value(1)
ifttt('colors_off') # Colored lamp off
if 'print_on' in accion: # Printer on
message('Printer on')
red.value(1)
green.value(0)
ifttt('colors_on') # Colored lamp on
if 'print_off' in accion: # Printer off
message('Printer off')
red.value(0)
green.value(1)
ifttt('colors_off') # Colored lamp off
if 'tado_away' in accion: # Tado© away from home
message('Away from home')
red.value(1)
green.value(0)
ifttt('colors_off') # Colored lamp off
ifttt('printer_off') # Printer off
message('Printer off')
if 'tado_home' in accion: # Tado inside home
message('Inside home')
red.value(0)
green.value(1)
447
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
ifttt('colors_on') # Colored lamp on
ifttt('printer_on') # Printer on
message('Printer on')
# Call to the IFTTT© Webhook
def ifttt(webhook):
try:
response = urequests.post('https://maker.
ifttt.com/trigger/'+webhook+'/with/key/'+key_ifttt)
if response.status_code == 200:
print(response.text)
print('Webhook activated: '+webhook)
else:
print('Server error: '+response.
status_code)
except:
print('Webhook error: '+webhook)
# Create the server socket
try:
s = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
s.bind(('', port))
s.listen(5) # To connect up to 5 devices
print("Web server on http://[IP]:{}".format(port))
except:
print('Error creating server')
# Script main loop
try:
while True: # Web command reading loop
conn, addr = s.accept()
print("Connection from {}".format(addr))
request = conn.recv(1024)
request = str(request)
control(request)
except:
print('Error receiving Webhook')
conn.close()
⊝⊝⊝
448
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E294: Rotary decoder with Webhook.
Develop a MicroPython© script for ESP32© that
uses a rotary decoder with a callback function that is
triggered when the decoder rotates in a specific
direction by a specific amount.
When that particular type of spin is detected,
the script should send an event to IFTTT© via a
Webhook in which a light turns on for a while and then
turns off. Specify the external device, the IFTTT©
Webhook and the script.
•E295: ADC and internal temperature reading.
Create a script that reads analog values through
the ESP32© ADC converter generated by a joystick. On
the other hand, read the internal temperature of the
ESP32©.
When the ADC reading and ESP32© temperature
reach a specific threshold, the script must send an
event to IFTTT© using a Webhook to trigger a switch.
•E296: Using thermistor with Webhook.
Develop a script that uses a thermistor to
measure room temperature. Convert thermistor readings
into values using an ADC and, if the temperature
exceeds a certain limit for a certain preset time,
triggers an IFTTT© Webhook that turns off an infrared
device.
•E297: Temperature with DHT11© and Webhook.
Write a MicroPython© script that integrates the
DHT11© sensor to measure humidity and temperature.
If the humidity or temperature reaches certain
levels, entered with a rotary decoder, the script must
send an event to IFTTT© that sends an SMS to a mobile
phone.
449
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
•E298: Light control through Webhooks.
Develop a script that configures the ESP32© as a
Web server. When the ESP32© detects an open/close door
sensor Webhook, it activates or deactivates a relay
that controls a light.
•E299: Blinds automation with Webhooks.
Create a script in the ESP32© that, upon
receiving an IFTTT© event, controls a blinds motor
using a relay connected to the ESP32© or another
Webhook that performs this function. The script must
interpret the event and determine, based on the state
and time of day, whether the blinds should open,
close, or stop.
•E300: Thermostat adjustment using Webhooks.
Write a script that receives IFTTT© events via
Webhooks to adjust the temperature of a simulated
thermostat on the ESP32©. We can use a DAC with an LED
to simulate temperature control accordingly.
•E301: Sound playback with Webhooks.
Develop a script that, upon receiving an event
from IFTTT©, plays a specific sound file previously
stored in the ESP32©.
•E302: Geolocation and Home Automation devices.
Create a script that uses the geolocation
received through an IFTTT© Webhook to activate or
deactivate specific Home Automation devices based on
location, time of day, and the state of temperature
and touch sensors.
Include turning on lights when we approach home
or turning off devices when we leave. Add
corresponding error controls with Webhooks and Home
Automation devices.
⊝⊝⊝
450
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 63:
NO-IP© Remote Management
In this exercise we will use
one of the previous exercises to
access a specific script on our
ESP32© but, instead of accessing it
directly from the home local WiFi,
we will do it from the Internet to
be able to access the ESP32© from
outside the home remotely.
To access remotely we use
dynamic DNS services that allow us
to maintain the public IP associated with our router
with a domain provided by a certain provider. In order
to access our ESP32© from outside the home, we need to
know the public IP of our router, but this information
can change every time the router is disconnected from
the power line, every time it is updated, every time
it is restarted, etc.
The most convenient thing would be to have a
domain (if it's free, much better) and for it to
permanently point to the public IP of the router or
for the public IP of our router to be a fixed IP (this
usually has a cost).
There are several solutions on the market:
DynDNS©, DuckDNS©, etc. and in this exercise the NO-
IP© application is used, which is free (we only need
to confirm the domain name monthly) and easy to use.
To configure NO-IP© we will register on their
website at www.noip.com, which will assign us a free
domain in the format [name].hopto.org, where [name]
can be, e.g our NO-IP© user.
451
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
On the other hand, we need to always keep our
router's public [IP] updated to NO-IP©, we can do this
manually or better yet configure it within the router.
We will see later in an example, how it is done.
Finally we will have to indicate in our network
that Web requests, coming from abroad and entering
through our domain and therefore through our router,
reach the ESP32©.
To perform the diversion or “routing” from the
router to the ESP32© we will do the following:
• Open a xx000 port on the main router.
• Access our [IP], for example 192.168.1.[IP].
• Enter the access password.
• Access the advanced configuration menu similar
to: [Advanced Setup] [NAT] [Virtual Servers].
• Add a new line with the following information
and example ports xx000 and xx80:
Server Name (Custom Service): ESP32
Example External Port Start: xx000
Example External Port End: xx000
Protocol: TCP/UDP
Example Internal Port Start: xx80
Example Internal Port End: xx80
Server IP Address: 192.168.1.[IP]
Where [IP] is the IP of the ESP32©.
With this configuration, access from outside the
Wifi to the Web server that runs on the ESP32© is:
[name].hopto.org:xx000, w h e r e [name] is the domain
provided by www.noip.com and xx000 the port opened.
Now if in a Web browser we write the address:
[name].hopto.org:xx000/[command] a n d the specific
[command] contemplated in the MicroPython© script that
is being executed will be executed.
452
Electronics & MicroPython© with ESP32©: 80 complete projects
For example, if we use the script example of use
of Web, HTML and LED and [name].hopto.org:xx000/red_on
the red LED will light up, also for red_off, green_on,
green_off, etc.
To expand this exercise, a very interesting and
fun option is to use a voice assistant, for example a
Google Home© to execute the previous options (turn
on/off LED, relays, request data, etc.) without having
to type anything.
Currently, at the time of writing, IFTTT© is
available for Google Home©, but it is not available
for the Alexa© or Apple© voice assistants.
To implement this option we need a “bridge” that
links the Google Home© with the ESP32© and the
“bridge” described here is IFTTT©, that is: “If This
Then That”.
To use IFTTT© we will:
• Open the application (also available in APP) and
identify ourselves.
• Use the [create] option which is very intuitive.
• In the [If This] section we can look for [google
assistant], select it and give it a name, for
example “turns on green”.
• I n t h e [Then That] section we search for
[webhook], select [make a web request] and add:
URL http://[name].hopto.org:[port]/green_on
Method: GET
Rest: blank
Where:
[name] is the domain that the NO-IP© service has
assigned us and [port] is the port that we have
managed on our previously described router e.g.
xx000.
453
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• Pre ss [create action] [continue] [finish] and
the [connected] button will appear.
We will have to wait a couple of minutes for
IFTTT© to integrate the actions and we can now try on
the Google Home© speaker or the Google© assistant on
the mobile, saying “ok Google, activate green” and the
green LED will light up connected to ESP32©.
As we can see, it is very useful and practical,
but we can make it even more comfortable by
eliminating the word “activate” from the voice command
in Google©.
To do this, we can enter the Google© Home© APP,
go to [automatic] and create a routine with the same
name but without the word “activate”, for example,
“turn on green”.
We can add as a trigger whit: [when we talk to
Google Assistant] [turn on green] [add trigger] [add
action] [Try adding your own commands] [activate turn
green] [done] [save].
Now if we say: “ok Google, turn on green” the
green LED will light up. To complete it, we can add
more activation phrases such as [turn on green LED],
etc., in this way and with various combinations of
phrases, the desired action will always be carried out
comfortably.
Finally, and since the routine is defined in the
Home© APP, we can execute it, via voice, from any
device that contains Google Assistant©, for example,
Home Mini©, Google Nest©, Lenovo© alarm clock, Sony©
headphones, Xiaomi© TV, etc. With this functionality
we will have a very useful, comfortable and also fun
Home Automation tool.
⊝⊝⊝
454
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E303: Execution of scripts with NO-IP©.
Use NO-IP© to run other scripts from the
Internet, that is, from outside the local WiFi
network, for example to display the internal
temperature of the ESP32© and the DHT11© together with
the % humidity.
•E304: LED brightness from NO-IP©.
Similar to the previous exercise but allows to
change, from outside the home, the brightness of an
LED using the DAC converter. The LED will simulate the
lighting of a room.
•E305: HTML and NO-IP© code.
Change the HTML body of previous exercises to
define enough buttons and options so that the previous
options can be executed directly on the Web, by
pressing buttons, without having to write the
corresponding Web addresses for each of the commands.
•E306: Voice commands.
Implement voice commands for the previous
exercises and check their correct operation. Add the
corresponding routines in the Home© APP to make voice
commands as comfortable as possible.
•E307: Access control with username and password.
Add remote access control to the ESP32©, via
user and NO-IP©. Tip: Use the web access format with
the following parameters:
[name].hopto.org:[port]/[script.py]?
user=[user]&password=[pass]
⊝⊝⊝
455
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 64:
ADXL345© Accelerometer
In this exercise we will see
how another electronic device is
connected using the I2C bus
communication, specifically we
will work with the ADXL345©
accelerometer, which is a 3-axis
motion sensor.
The connections of the
ADXL345© module that we are going
to use are the following:
ADXL345© ESP32© Observations
+5V Not used Do not use with ESP32©
VCC +3.3V Power
GND GND Ground
SDA GPIO21 I2C data
SCL GPIO22 I2C clock
CS Not used Chip select
SDO Not used Serial data output
INT1 Not used IRQ 1
INT2 Not used IRQ 2
We must take into account the following:
• VCC, GND: +3.3V power and ground pins that must
be as stable as possible to guarantee the
accuracy of the sensor.
456
Electronics & MicroPython© with ESP32©: 80 complete projects
• SDA, SCL: Pins of the main I2C bus that will
allow us to transmit data between the sensor and
the ESP32© and vice versa and in a simple and
fast way.
• CS: Chip Select to select several hardware
sensors from the ESP32©, in this exercise the
selection is made by the I2C bus itself.
• SDO: Serial Data Output that allows reading
sensor data without using the I2C bus.
• INT1, INT2: IRQ interrupt outputs that can allow
the ESP32© to know that there is some new data
using an interrupt without having to
continuously read the I2C bus by polling.
Let us remember that I2C (Inter-Integrated
Circuit) communication allows the connection of
electronic devices using two main lines: SDA (Serial
Data) and SCL (Serial Clock) and we can highlight:
• Hardware Connection:
SDA (Serial Data): It is the bidirectional data
line through which data is transmitted between the
devices, in the ESP32© located in the GPIO21.
SCL (Serial Clock): It is the clock line that
synchronizes the data transfer, in the ESP32© located
in GPIO22.
• I2C Bus Configuration:
On the ESP32©, we can configure the I2C bus
using the machine l i b r a r y a n d t h e i n s t a n c e
SoftI2C(scl=Pin(22),sda=Pin(21)) to specify the GPIO
GPIO21 and GPIO22.
The usual transmission speed of the I2C bus is
100kb/s, which is what this instance assigns if we do
not specify otherwise. On those occasions (long
cables, specific devices, many devices connected to
the bus, etc.) we can lower this frequency by
indicating it as:
SoftI2C(scl=Pin(22),sda=Pin(21),freq=50000).
457
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• Device Addresses:
Each device on the I2C bus has a unique 7-bit
address (can vary between 8 and 127) assigned by the
device manufacturer. In this case it is 0x53 and we
can scan it.
• Start and Stop of Communication:
I2C communication begins with a start condition
where SDA changes from high to low while SCL is high
and ends with a stop condition where SDA changes from
low to high while SCL is high. These operations are
handled by the MicroPython© SoftI2C instance.
• Data transfer:
Data is transferred in 1byte=8bit packets and
each byte is followed by an ACK/NACK bit that
indicates whether or not the receiver accepts the
received byte.
The script summarizes the connections between
the ADXL345© and the ESP32©, remember that all signals
connected directly to the ESP32© must be a maximum of
+3.3V. We import the machine and time libraries, we
define where the I2C bus is connected, in our case SDA
in GPIO21 and SCL in GPIO22.
Although we know that the manufacturer of the
ADXL345© has assigned address 0x53 for the I2C bus, we
c a n c h e c k i t w i t h t h e scan_i2c() MicroPython©
function, which tells us that it has been found,
therefore we assign this address, we activate the
sensor in measurement mode with parameters 0x2D and
b'\x08 (energy control register and activation value).
We make an infinite sensor reading loop where we
read the reading register 0x32 with 6 bytes (2 bytes
per axis) and process the data obtained in the
acce_data[] list for each axis, shifting the first
byte 8 bits to the left and adding to it (function OR)
the 8btis of the second byte and repeating this action
for the x, y, z axes.
Finally we present the acceleration values on
each axis and wait a while.
458
Electronics & MicroPython© with ESP32©: 80 complete projects
If we connect the LA1010© logic analyzer to the
I2C bus we can see the data in the SDA signal and the
clock in SCL. We can see how I2C communication starts
when SDA is lowered with SCL high and how byte 0x32 is
sent to activate the reading of the next 6 bytes (2
for each axis). The analyzer also displays a PWM
signal that serves as a reference.
############################
# E064_ACCE_ADXL345.PY: ADXL345© accelerometer
# INPUTS: I2C (0x53) & GPIO21 (SDA) & GPIO22 (SCL)
# OUTPUTS: Acceleration values in 3 axes
############################
'''
ADXL345© CONNECTIONS
------------------------------------------
ADXL345© Function ESP32©
------------------------------------------
5V +5V Do not use
VCC, GND +3.3V and ground 3V, GND
SDA, SCL I2C bus GPIO21, GPIO22
CS Chip Select Not used
SDO Serial Data Output Not used
459
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
INT1 Interruption 1 Not used
INT2 Interruption 2 Not used
'''
from machine import SoftI2C, Pin # I2C and GPIO
import time # Timing control
i2c = SoftI2C(scl=Pin(22), sda=Pin(21)) # GPIO & I2C
# Scan the address of the ADXL345© on the I2C bus
def scan_i2c():
print('Scanning I2C bus...')
devices = i2c.scan()
if devices: # Device found
for device in devices:
print('I2C device found on: ',hex(device))
else:
print('No I2C devices found')
# I2C address scanning
scan_i2c()
# ADXL345 I2C address (0x53 by default)
dir_adxl345 = 0x53 # Change as scan_i2c()
# Configure the ADXL345 (activate measurement mode)
i2c.writeto_mem(dir_adxl345, 0x2D, b'\x08')
# Reading loop
print('ADXL345© ACCELEROMETER CONTROL')
print('Move the sensor...')
try:
while True: # ADXL345 (read with 2bytes/axis)
acce_data = i2c.readfrom_mem(dir_adxl345,
0x32, 6)
# Processes data in ADXL345 format
# Byte [x] shifts left 8bits and adds [x-1]
x = (acce_data[1] << 8) | acce_data[0]
y = (acce_data[3] << 8) | acce_data[2]
z = (acce_data[5] << 8) | acce_data[4]
print("Acceleration: X:", x, "Y:", y, "Z:", z)
time.sleep(1) # Wait between reading
except KeyboardInterrupt:
print('Script completed...')# Completed
⊝⊝⊝
460
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E308: Interruption by push button.
Connect a push button to a pin of the ESP32© and
use the interrupt (IRQ) to detect when the button is
pressed. When we press the button, present the
acceleration data detected by the ADXL345© in the next
3 seconds.
•E309: Alarm with timer.
Use the ESP32© hardware timer to generate an
alarm every certain time interval. When the alarm is
activated, display the acceleration data recorded by
the ADXL345© in the next 5 seconds.
•E310: ADXL345© data record.
Create a data file on the ESP32© and save the
ADXL345© time and acceleration data to a circular log
file where the last 100 readings are stored. Use a
button to start recording and send the file by email.
The data must be recorded in the file in well-
identified and formatted columns.
•E311: Distance measurement.
Connect an ultrasonic sensor to the ESP32© and
measure the distance. When the distance exceeds a
limit entered through the console, we must measure the
acceleration in the ADXL345© and present it in the
console.
•E312: Temperature and humidity measurement.
Measure time, internal temperature of the
ESP32©, external temperature and humidity with DHT11©
and acceleration with the ADXL345©. Create a file with
50 events and send it by email. Likewise, if the
combined data exceeds a certain limit, send an alarm
SMS to an specific mobile number.
⊝⊝⊝
461
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 65:
MPU6050© Gyroscope
In this exercise we continue
to delve into the use of
devices connected with an I2C
bus, specifically we will see
the MPU6050© which is a 6-axis
motion sensor that combines a
3-a xis gy ros co pe, a 3 -a xis
accelerometer (similar to the
ADXL345©) and an internal chip
temperature sensor.
It is used to measure the
orientation, acceleration and
angular velocity or rotation of
a certain device.
We will use the same I2C bus connection
configuration with the ESP32©, that is: SDA in GPIO21,
SCL in GPIO22 and in this case the address of the
MPU6050© is the hexadecimal 0x68. The rest of the
configurations are very similar to the previous
exercise, although we will see that the communication
with the MPU6050© is somewhat different.
The connections that we are going to use are:
MPU6050© ESP32© Function
VCC, GND +3.3V, GND Power, GND
SDA, SCL GPIO21, GPIO22 I2C bus
XDA, XCL Not used I2C secondary bus
AD0 Not used I2C 0x68 or 0x69
INT Not used Interruption IRQ
462
Electronics & MicroPython© with ESP32©: 80 complete projects
• VCC,GND: Power pins at +3.3V and ground, which
must be as stable as possible to guarantee the
accuracy of the MPU6050.
• SCL,SDA: Pins of the main I2C bus that will
allow us to transmit data between the MPU6050©
and the ESP32© in a simple and fast way.
• XDA,XCL: Secondary I2C bus pins used to
establish a second I2C interface. This can be
useful where multiple I2C devices are needed and
we want to avoid address conflicts.
• AD0: This pin is used to configure the I2C
address. If we connect the AD0 pin to GND, the
I2C address will be 0x68 and if we connect it to
+3.3V, the address will be 0x69. This allows us
to have up to two MPU6050© on the same I2C bus
with different addresses. In our exercise we do
not use it and the I2C address is 0x68.
• INT: This pin is used to generate an interrupt
in the ESP32© when there is data ready to be
read, this way, we can control the MPU6050© by
interrupt instead of constant reading (polling).
463
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In the LA1010© analyzer or similar, we can see
the I2C bus connections both in SDA and SCL
connections and a reference PWM signal. We can see how
communication on the I2C bus starts when the SDA
signal goes down, with SCL at HIGH, and how it ends
when SDA goes up, also with SCL at HIGH.
On the other hand, we can see how byte 0x00 is
written in register 0x6B to configure the sensor to
read data and byte 0x3B to read acceleration data. We
could also see byte 0x43 to read the rotation data and
byte 0x41 for the temperature data.
The script with the MPU6050© and the ESP32© that
we have created is used to obtain acceleration and
angular speed data in several axes and internal
temperature of the MPU6050©, that is, it captures the
variables of a six-axis inertial sensor.
Acceleration and angular speed information is
essential in many projects and applications such as:
• Motion tracking: We can know the acceleration
and rotation of a moving object such as robots,
drones, etc.
• Spatial orientation: We can determine the
spatial orientation of an object in space for
projects that require knowing the relative
position of a device, etc.
• Stabilization: Helps keep objects level such as
in certain applications, cameras, flight
stabilizers, etc.
• Motion Detection: Use acceleration data to
detect, for example, sudden movements or changes
in speed such as in security applications,
motion alarms, etc.
• Virtual reality and augmented reality: Control
of movement data that is essential to provide an
immersive experience and track the user's
movements.
• Interactive games: We can use sensor data to
create games that respond to your device's
movements, such as tilt-based or motion-based
games.
464
Electronics & MicroPython© with ESP32©: 80 complete projects
In the script we import the machine and time
libraries with the SoftI2C instance to manage the I2C
bus. We define the function scan_i2c() that scans the
bus to know the hexadecimal address of the MPU6050©,
in our case 0x68. We define the calibrate() function
that calculates the average of 100 samples to
determine offsets that will be subtracted from the
subsequent values so that the sensor values are as
close to reality as possible.
The function read_data() is defined that first
configures the MPU6050© to read data by writing a byte
to 0 in register 0x6B of the sensor, 6 bytes of
acceleration are read in register 0x3B, 6 bytes of
rotation in register 0x43 and 2 internal temperature
bytes in register 0x41. Every 2 bytes are processed in
the same way as in the ADXL345©, that is, shifting the
8 bits of the first byte to the left and adding the 8
bits of the second byte.
Finally, after activating the I2C bus and
invoking the calibration function, an infinite reading
loop is performed where the formatted data of angular
speed, acceleration and/or temperature can be
displayed, and the comment on the necessary printing
lines can be eliminated.
############################
# E065_GIRO_MPU6050.PY: Accelerate, spin, temperature
# INPUTS: I2C bus and GPIO21 (SDA) and GPIO22 (SCL)
# OUTPUTS: MPU6050© sensor values
############################
'''
ACCELEROMTER AND GIRÓSCOPO MPU6050© CONNECTIONS
--------------------------------------------
MPU6050 Function ESP32©
--------------------------------------------
VCC,GND +3.3V and ground +3V, GND
SDA,SCL I2C bus GPIO21, GPIO22
XDA,XCL I2C secondary bus Not used
AD0 0x68 or 0x69 address Not used
INT interruption Not used
'''
from machine import SoftI2C, Pin # GPIO and I2C
import time # Timing control
465
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Scan I2C bus
def scan_i2c():
print('Scanning I2C bus...')
i2c = SoftI2C(scl=Pin(22), sda=Pin(21)) # GPIO
try:
devices = i2c.scan() # Bus scan
except KeyboardInterrupt:
devices = [0x68]
if devices:
for device in devices:
print('I2C device found on
address:', hex(device))
else:
print('No I2C devices found.')
# Average readings at rest to adjust measurements
def calibrate(i2c, address=0x68, num_samples=100):
print('Calibrating gyroscope...')
sum_gyro_x = 0 # Star calibration
sum_gyro_y = 0
sum_gyro_z = 0
for _ in range(num_samples):
_, _, _, gyro_x, gyro_y, gyro_z, _ =
read_data(i2c, address)
sum_gyro_x += gyro_x
sum_gyro_y += gyro_y
sum_gyro_z += gyro_z
time.sleep(0.01)
avg_gyro_x = sum_gyro_x / num_samples # Average
avg_gyro_y = sum_gyro_y / num_samples
avg_gyro_z = sum_gyro_z / num_samples
print('Calibration completed.')
return avg_gyro_x, avg_gyro_y, avg_gyro_z
# Reading sensor loop
def read_data(i2c, address=0x68):
# Configure MPU6050©:measure acceleration and spin
i2c.writeto_mem(address, 0x6B, bytearray([0]))
# Read acceleration data
accel_data = i2c.readfrom_mem(address, 0x3B, 6)
accel_x=(accel_data[0]<<8|accel_data[1])/16384.0
accel_y=(accel_data[2]<<8|accel_data[3])/16384.0
accel_z=(accel_data[4]<<8|accel_data[5])/16384.0
466
Electronics & MicroPython© with ESP32©: 80 complete projects
# Angular speed or spin read data
gyro_data = i2c.readfrom_mem(address, 0x43, 6)
gyro_x = (gyro_data[0]<< 8 | gyro_data[1]) / 131.0
gyro_y = (gyro_data[2]<< 8 | gyro_data[3]) / 131.0
gyro_z = (gyro_data[4]<< 8 | gyro_data[5]) / 131.0
# Internal temperature read data
temp_data = i2c.readfrom_mem(address, 0x41, 2)
temp_raw = (temp_data[0] << 8 | temp_data[1])
temperature = (temp_raw / 340.0) + 36.53
return accel_x, accel_y, accel_z, gyro_x,
gyro_y, gyro_z, temperature
# I2C address scanning
scan_i2c()
# MPU6050© I2C and Address Configuration
i2c = SoftI2C(scl=Pin(22), sda=Pin(21))
mpu6050_address = 0x68
# Calibrates gyroscope based on average at rest
gyro_offset_x, gyro_offset_y, gyro_offset_z =
calibrate(i2c, mpu6050_address)
# Reading data: accelerometer, rotation, temperature
print('MPU6050© GYROSCOPE READ')
print('Move and/or rotate the sensor')
try:
while True:
accel_x, accel_y, accel_z, gyro_x, gyro_y,
gyro_z, temperature = read_data
(i2c, mpu6050_address)
# Subtract calibration values
gyro_x -= gyro_offset_x
gyro_y -= gyro_offset_y
gyro_z -= gyro_offset_z
# Uncomment the desired lines
print(f"Gyroscope: X={gyro_x:.1f},
Y={gyro_y:.1f}, Z={gyro_z:.1f}")
#print(f"Acceleration: X={accel_x:.1f},
Y={accel_y:.1f}, Z={accel_z:.1f}")
#print(f"Temperature: {temperature:.1f} °C")
time.sleep(1)
except KeyboardInterrupt:
print('Completed...')
⊝⊝⊝
467
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E313: Interruption by push button:.
Connect a push button to a pin on the ESP32© and
use the interrupt (IRQ) to detect when the button is
pressed. When we press the button, present the
rotation data detected by the MPU6050© in the next 3
seconds.
•E314: Alarm with timer:
Use the ESP32© hardware timer to generate an
alarm every certain time interval. When the alarm is
activated, display the rotation and acceleration data
recorded by the MPU6050© in the next 5 seconds.
•E315: MPU6050© data register.
Create a data file on the ESP32© and save the
acceleration, spin and temperature data from the
MPU6050© to a circular log file where the last 100
readings are stored. Use a button to start recording
and send the file by email. The data must be recorded
in the file in well-identified columns and with the
time of the event.
•E316: Distance measurement.
Connect an ultrasonic sensor to the ESP32© and
measure the distance. When the distance exceeds a
limit entered through the console, we must measure the
spin.
•E317: Temperature and humidity measurement.
Measure the internal temperature of the ESP32©,
external temperature and humidity measured with
DHT11©, rotation, acceleration and internal
temperature of the MPU6050©, create a file and send it
by email. Likewise, if the data exceeds a certain
limit, send an alarm SMS.
⊝⊝⊝
468
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 66:
PCF8574© GPIO Extender
Although the ESP32©
has more than 30 GPIO
(depending on the board
model & configuration)
configurable for in-
puts, outputs, ADC,
DAC, PWM, I2C, SPI,
UART, touch, etc., it
is possible that we
need more inputs and/or
outputs for a more com-
plex project.
For example, if we want to connect an LCD
display type LCD1602© (serial data bus model) that
requires 8 data pins or a camera or an MP3 player, it
is likely that we will need to have more GPIO. In this
exercise we will see how this issue is easily solved
using a GPIO extender such as the PCF8574© that has 8
extra GPIO (P0...P7) and that uses the I2C bus as
communication with the ESP32©.
If we needed even more GPIO, we could connect up
to 8 PCF8754© to the I2C bus since this device allows,
using 3 jumpers, to configure its I2C address from
0x20 to 0x27, therefore we could have up to almost 100
GPIO in total very useful for complex projects.
469
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In this exercise we will see how we use the
extra GPIO P0 and P1 of the PCF8574© to manage a red
LED and a green LED respectively.
IMPORTANT: the P0...P7 outputs operate with
negative logic, therefore we must connect the LED,
using resistors to limit the current to 15mA, between
+3.3V and these outputs, internally the PCF8574©, by
setting the pin to LOW, it will turn it on and when we
set it to HIGH it will turn it off.
In the script we use the machine and utime
libraries, we define the address of the PCF8574© which
by default is 0x20, the I2C bus pins, SDA connected to
GPIO25 and SCL to GPIO26 respectively.
The outputs P0 and P1 of the GPIO extender are
defined, the function all_off() which turns off both
LED by writing the 0xFF byte (0b11111111) to I2C, the
function led_on(pin) which turns on the corresponding
pin, idem the function led_off(pin) and also the
led_toggle() function that alternates between turning
the red and green LED on and off. Finally, if
<CTRL>+<C> is pressed, the program will stop and both
LED will turn off.
470
Electronics & MicroPython© with ESP32©: 80 complete projects
In th e LA 1 01 0© a na l yz er w e c a n s ee t he
communication pulses of the I2C bus, both the data in
SDA and the clock in SCL and the output signals of the
PCF8574© GPIO extender that in this case are
connected, the P0 output to the red LED and output P1
to the green LED only.
We can see that when the P0 pin is at HIGH the
red LED turns off (reverse logic) and when it is at
LOW it turns on, the same happens with the green LED.
It is difficult to see simultaneously with the
same scale, the signals of the I2C bus and the outputs
of the extender since the frequency of the I2C signal
can reach 300kHz while the output of the extender,
limited by the console message presentations of the
script itself, they can reach 1kHz.
We can try using lower frequencies on the I2C
bus by adding the parameter freq=5000 to the I2C bus
instance where freq is the I2C clock frequency in Hz,
but there is a lower limit for the ESP32© above 5kHz.
471
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E066_PCF8574.PY: PCF8574© GPIO EXTENDER MANAGEMENT
# INPUTS: SDA GPIO25 and SCL GPIO26
# OUTPUTS: P0 Red, P1 Green
# IMPORTANT: PCF8574© outputs with inverse logic
############################
from machine import Pin, I2C # GPIO and I2C manager
import utime # Timing management
pcf8574_address = 0x20 # I2C of PCF8574©
i2c = I2C(0, sda=Pin(25), scl=Pin(26))
T_INT = .2 # Blinking LED timing
led1_pin = 0 # P0 Red LED on a 0 and off a 1
led2_pin = 1 # P1 Green LED on a 0 and off a 1
# All LED off
def all_off(): #0xFF is 0b11111111 all off
i2c.writeto(pcf8574_address, bytearray([0xFF]))
print('Program aborted. LED off.')
raise SystemExit
# One LED on
def led_on(led_pin):
# Sets the LED to the specified position at 0
i2c.writeto(pcf8574_address, bytearray([0xFE if
led_pin == 0 else 0xFD]))
# 0xFE is 0b11111110 and 0xFD is 0b11111101
# One LED off
def led_off(led_pin):
# Sets the LED to the specified position at 1
i2c.writeto(pcf8574_address, bytearray([0xFD if
led_pin == 0 else 0xFE]))
# 0xFF is 0b11111111
# Toggle red and green LED
def led_toggle():
try:
while True:
print('Red') # Red On, Green Off
led_on(led1_pin)
utime.sleep(T_INT)
472
Electronics & MicroPython© with ESP32©: 80 complete projects
led_off(led2_pin)
utime.sleep(T_INT)
print('Green') # Red Off, Green On
led_on(led2_pin)
utime.sleep(T_INT)
led_off(led1_pin)
utime.sleep(T_INT)
except KeyboardInterrupt:
print('Program completed...')
all_off()
# Run the function to toggle the LED
print('PCF8574© GPIO EXTENDER TEST')
print('Toggles red vs green LED')
led_toggle()
As we have seen, using the GPIO extender can be
a bit complex. We can make it more user-friendly by
using the pcf8574 library available on GitHub© and
using something similar to:
############################
# E066_PCF8574_library.PY: PCF8574© library manager
# INPUTS: SDA GPIO25 and SCL GPIO26
# OUTPUTS: P0 Red, P1 Green, P2 other
# IMPORTANT: pcf8574.py library in ESP32© memory
############################
from machine import I2C, Pin
from pcf8574 import PCF8574
# I2C bus configuration
i2c = I2C(0, sda=Pin(25), scl=Pin(26))
# PCF8574© I2C address and pcf8574.py library
pcf = PCF8574(i2c, 0x20)
# PCF8574© extra GPIO e.g. control
pcf.pin(0, 1) # P0 HIGH (LED off)
pcf.pin(1, 0) # P1 LOW (LED on)
# Read extra GPIO status
status_pin_2 = pcf.pin(2)
print('Pin 2 status:', status_pin_2)
⊝⊝⊝
473
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E318: LED control with buttons.
Like the previous exercise, connect the PCF8574©
to the ESP32© and configure two pins as output to
control two LED. Add two buttons to two other pins of
the PCF8574© and write a script that turns the LED
on/off using the buttons. Initially use the polling
method. Study the option of using the extender's INT
pin to read, via IRQ, if any input on the extender has
changed and act accordingly with the LED.
•E319: OLED data visualization.
Add an OLED module to the previous exercise and
see which button was used. The OLED must show: time,
button and activated LED. Look other exercises.
•E320: DHT11©, OLED and email.
Add to the previous exercise a DHT11© connected
to the ESP32© (do not connect to the extender),
display the temperature and humidity on the OLED and
send the temperature data by email when the green LED
button is pressed and humidity when the red LED
button, connected to the extender, is pressed.
•E321: Extender, LED and WiFi.
Use the LED control over WiFi exercise to manage
the LED that we have connected to the ports of the
extender. Also add remote reading of the buttons. Use
the pcf8574.py library to control the extender.
•E322: Extender addresses.
Change the GPIO extender's address configuration
pins and write a MicroPython© script for the ESP32©
that scans the I2C bus and displays the new address.
⊝⊝⊝
474
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 67:
SH1106© OLED Screen
We have already seen how the I2C bus connection
can be used to communicate in a fairly simple way an
electronic device such as a gyroscope with the ESP32©.
In this exercise we will see
another case of I2C connection
such as an OLED screen
(similar to an LED screen but
does not need backlighting)
and that will allow us to
complement the ESP32© very
well to be able to display
messages, variables, states,
etc. of the scripts that we
have already seen and of
course new ones.
The display that we are going to use is the very
famous SH1106© or similar and whose basic
characteristics are:
• Type: OLED screen (Organic Light Emitting Diode)
managed by the SH1106© hardware controller that
supports power at +3.3V, has a very low cost and
is very easy to locate in the online market.
• Resolution: May vary depending on the specific
module model, but is commonly found at 128x64
pixels or 128x32 pixels. Here we will use the
128x64 pixel one.
• Connection: It can be used both in I2C (Inter-
Integrated Circuit) mode and in SPI (Serial
Peripheral Interface) mode. In this exercise we
will use the I2C already mentioned.
475
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• Color Depth: Monochromatic, here we will use a
black and white one but there are several
monochromatic colors: blue, green, etc.
• Size: SH1106© modules are available in various
sizes, the one used in this project is 1.3
inches.
• Library: SH1106.py is recommended, available at:
https://github.com/robert-hh/SH1106 ( w h i c h w e
must store in the memory of our ESP32©) and
which allows us to perform the following basic
functions:
init_display() Start and clean screen
poweron() OLED on
poweroff() OLED off
sleep(True/False) Activate low consumption
contrast(0..255) Contrast minimum to maximum
invert(True/False) Inverts display
flip(True, True) Rotates 90 degrees
show() Refresh screen data
line(xi,yi,xf,yf,1) Draw line on xi,yi to xf,yf
pixel(x,y,1) Draw a pixel in x,y position
text([text],x,y,1) Text (of 8x8 pixels) in x,y
hline(x,y,L,1) Horizontal line x,y length L
vline(x,y,L,1) Vertical line x,y length L
fill(c) Fill with 0 black color
scroll(x,y) Scroll in x,y
rect(x,y,L,A,1) Rectangle in x,y and LxA size
The script imports the Pin and SoftI2C libraries
for the management of the I2C bus, the sh1106 for the
management of the screen, time and random for the
management of time and random data of presentation on
the screen and ntptime to update the RTC of the ESP32©
as already we have seen in the internal RTC exercises.
The script activates the RTC and updates it from
the Internet, gets the current time, adjusts it
476
Electronics & MicroPython© with ESP32©: 80 complete projects
according to the time zone, and updates it back to the
RTC. Also activate the I2C bus in GPIO21 (SDA) and
GPIO22 (SCL) and use the address 0x3C (sometimes the
SH1106© module indicates another address but it is
usually not valid).
The SH1106© is started with the resolution data,
I2C address, display rotation, etc., and for this
exercise, it shows the time in the first line and the
text “ESP32” that moves randomly within the available
part of the display controlling the necessary limits.
Note that the show() function is used to force the
data to be displayed and fill(0) to refresh the OLED
screen.
In the LA1010© logic analyzer we can see the two
signals of the I2C bus: the signal with the data or
SDA and the clock signal or SCL, with the logic
analyzer we can see what data is sent (in binary,
hexadecimal, decimal, etc. format) and the frequency
of the clock that synchronizes them.
477
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E067_OLED.PY: OLED SH1106© OLED display manager
# INPUTS: Texts, SDA in GPIO21 & SCL in GPIO22
# OUTPUTS: Hour and text to visualize
# IMPORTANT: I2C in 0x3C and SH1106.PY library must
# be stored in the ESP32© internal memory.
############################
'''
DISPLAY OLED SH1106 I2C 0X3C 128x64 +3.3V
Functions of sh1106, invoke with: oled.[function]
-------------------------------------
FUNCTION DESCRIPTION
-------------------------------------
init_display() Start and clear display
poweron() Display on
poweroff() Display off
sleep(True/False) Low consumption
contrast(0..255) Contrast minimum to maximum
invert(True/False) Inverts display
flip(True, True) Rotates display
show() Refresh screen data
line(xi,yi,xf,yf,1) Draw a line in xi,yi to xf,yf
pixel(x,y,1) Draw a pixel in x,y
text([text],x,y,1) Put [text] (8x8 pixels) in x,y
hline(x,y,L,1) Horizontal line x,y length L
vline(x,y,L,1) Vertical line x,y and length L
fill(c) Fill with black color 0
scroll(x,y) Scroll in x,y
rect(x,y,L,A,1) Rectangle in x,y and LxA size
'''
from machine import Pin, SoftI2C # I2C control
import sh1106 # SH1106© management
import time, random # Hour, random number
import ntptime, machine # ESP32© RTC
# ESP32© RTC ADJUST-----------------------------
# Internal ESP32© RTC adjust
rtc = machine.RTC()
478
Electronics & MicroPython© with ESP32©: 80 complete projects
# Synchronize hour with NTP server
try:
print ('Getting date-time from the Internet...')
ntptime.settime() # Captures internet data
except OSError as e:
print("Error syncing with NTP:", e)
machine.reset()
# Set local time (e.g. UTC+1)
local_time = 1
# Gets the current date and time of the RTC
year, month, day, weekday, hour, minute, second, _ =
rtc.datetime()
hour += local_time
# Adjusts to range 0 to 23
hour = hour % 24
# Updates the RTC with the set time
print('Update the time on the ESP32©')
rtc.datetime((year, month, day, '', hour, minute,
second, 0))
# DISPLAY SH1106© UPDATE WITH I2C --------------------
# Configure I2C and the SH1106© screen address
i2c = SoftI2C(sda=Pin(21), scl=Pin(22), freq=400000)
screen_address = 0x3C # Adjust I2C (see Internet)
# Start SH1106© screen
oled = sh1106.SH1106_I2C(128, 64, i2c, None, 0x3C,
rotate=180, delay=0)
# Shows RTC time and message on screen
# Text size is 8x8 pixels, not configurable
text = 'ESP32'
print('Visualize hh:mm:ss and text')
try:
while True: # Infinite presentation cycle
year, month, day, weekday, hour, minute,
second, _ = rtc.datetime()
hora = "{:02d}:{:02d}:{:02d}".format(hour,
minute, second) # hh:mm:ss format
oled.text(hora, 64-4*len(hour), 0) # Centered
479
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Random positions of x and y
x = random.randint(0, 128-8*len(text))
y = random.randint(9, 64-8)
oled.text(text, x, y)
oled.show() # Display update
time.sleep(1)
oled.fill(0) # Clear display
except KeyboardInterrupt: # Display off
oled.poweroff()
Below we see the connection diagram of the OLED
SH1106© to the ESP32© through the I2C bus in the
GPIO21/22 where we have also connected a green LED, in
the GPIO27, to indicate power to the circuit and a red
LED, in the GPIO26, to indicate alarms and a
potentiometer with an intermediate point in the GPIO34
configured as ADC and whose analog value could be
displayed on the OLED screen.
⊝⊝⊝
480
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E323: Visualization of ESP32© internal data.
Display the following internal ESP32©
information on the OLED: unique identification number
(ID), IP address, updated temperature and time.
•E324: RTC clock on OLED.
Implement a digital clock that displays the
updated current time, in hh:mm:ss format, at the top
of the OLED screen. In the central part draw an analog
clock, with 3 hands (hour, minute, second) and the
corresponding time.
•E325: DHT11© sensor data monitoring.
Connect a dual DHT11© sensor to the ESP32© and
periodically display temperature and humidity data on
the SH1106© OLED display. Additionally, at the
beginning of the program, a text message should appear
that scrolls from right to left on the OLED.
•E326: Gyroscope status display.
Read the data from the gyroscope and display a
point on the screen that will move in the X and Y axes
so that the gyroscope must be moved to locate that
point within a circle centered on the screen. Limits
the point's movements within the screen and modifies
the movement sensitivity if applicable.
•E327 Ping-pong on OLED screen.
Create a script that displays a character, for
example a period or a *, on the screen that bounces
around its limits. Likewise, change the character to a
circle of adjustable size. The movement should start
randomly at each start of the script, simulating a
ping-pong ball.
⊝⊝⊝
481
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 68:
PCM5102© I2S Player
We had seen how to
play a sound file (for
example *.wav) with a
passive buzz and using
the 8-bit DAC of the
ESP32©, its sound
quality was very poor,
limited to making a few
“beeps”. If we want to
improve this situation,
we can use the ESP32©
capabilities to manage
I2S© (Inter IC Sound)
type buses.
If we use the I2S bus, we need an interface
between this bus and a speaker and in this exercise we
will see how we use a simple device that uses the I2S
bus and that allows an analog output through a jack
and that can be connected to a small speaker or to an
general purpose amplifier.
The interface used is a module that contains the
PCM5102©, whose characteristics we can summarize:
• I2S interface: The module uses the I2S interface
for the transmission of digital audio, which
facilitates connection with micro controllers
(Arduino©, Raspberry©, ESP32©, etc.) and other
devices compatible with this interface.
• High-quality DAC: Incorporates a high-quality
digital-to-analog converter (DAC), providing
accurate, high-fidelity audio reproduction.
482
Electronics & MicroPython© with ESP32©: 80 complete projects
• Bit Resolution: Offers up to 32-bit
32
2 >4billion levels of resolution, allowing
detailed representation of digital audio
signals.
• Adjustable sample rate: Allows us to adjust the
sample rate according to the needs of the
system, with support for frequencies from 8kHz
to 384kHz.
• Signal-to-noise ratio (SNR): Provides a good
signal-to-noise ratio to ensure clean, high-
quality audio playback.
• Integrated Amplifier: Some PCM5102© modules
include an low power integrated amplifier to
facilitate direct connection to speakers or
headphones.
• Power: It is typically powered with a voltage of
+3.3V or +5V, making it compatible with a
variety of systems.
The I2S© bus uses a serial communication
protocol that allows the transfer of digital data
between audio devices. Its basic operation can be
summarized as:
• Pins: In the ESP32© we will use the GPIO for the
bit clock signal (BCK to GPIO25), the word clock
signal (WS to GPIO23) and the serial data signal
(SD to GPIO26).
• Parameters: Before communication, we must
configure the sampling rate, the number of bits
(16 or 32), the channel configuration (mono or
stereo), etc.
• Data Sending: Audio data is sent over the I2S
bus in packets of bits. Each packet includes a
specific number of bits per channel, and these
are transmitted serially using the BCK and SD
signals. The WS signal indicates the beginning
of each audio word.
483
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• Bit Clock (BCK): The bit clock (BCK) signal
indicates the rate at which audio bits are sent.
• Word Clock (WS): The word clock signal (WS)
indicates the beginning of each audio word and
the spacing between channels.
• Serial Data (SD): The serial data (SD) signal
carries the actual audio information.
Below we see with the help of the LA1010© logic
analyzer the 3 signals that make up the I2S bus: the
clock connected in BCK, here we can measure its
frequency, the frame or WS word that indicates when
data is transmitted and the serial data in SD where we
can see that channel 1 is transmitted with the data
0xE886 and channel 2 with zero since we have selected
a mono signal only.
484
Electronics & MicroPython© with ESP32©: 80 complete projects
If in the I2S instance we select the stereo
format (format=I2S.STEREO), we can see how the stereo
data is transmitted: 0x6D66 on channel 1 and 0x2074 on
channel 2.
• Playback and capture: We can use the ESP32© both
to play and capture audio data. In playback
mode, data is sent to a DAC (Digital-Analog
Converter), such as the PCM5102©. In capture
mode, data is read from a microphone or ADC
(Analog-Digital Converter) connected to the
ESP32©.
Since the ESP32© has very limited hardware
resources, we can use *.wav files of a few seconds and
for this it is best to encode it in 16bit and a
sampling of 11,025Hz, in addition to not distorting
the amplifier or speaker that we connect to the jack
of the PCM5102©, we will lower the playback volume as
much as necessary.
485
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
T h e s c r i p t i m p o r t s t h e machine a n d array
libraries and begins by discussing how a *.wav can be
modified to reduce its volume. In the playback part of
the *.wav, we begin by defining the sck (clock), ws
(word or frame) and sd (data) pins and the
equivalences to the PCM5102© BCK (bit clock), LCK
(left/right clock) and DIN (data input) respectively.
T h e I2S instance is configured with the parameters
already indicated and the *.wav file we want to play
is specified, the data from the *.wav is read, loaded
into an array and played directly on the PCM5102©. To
allow the reading of large *.wav files, the reading is
done in the example script in blocks of 4,096bits and
which we can adjust according to our particular needs.
To download *.wav files we can use, for example,
the BBC© website:
https://sound-effects.bbcrewind.co.uk/
To change parameters (bits, sample rate, PCM
encoding, etc.) we can use:
https://www.online-convert.com/
We have used a 177Kb *.wav file encoded with
16bits, a sampling frequency of 22,050Hz, a PCM type
S16LE encoding and a block size of 4,096bits and the
result is very acceptable.
############################
# E068_I2S.PY: Play *.wav with PCM5102© with I2S
# INPUTS: *.wav file and PCM5102© pins
# OUTPUTS: Jack audio in PCM5102©
# IMPORTANT: Adjust volume and *.wav size
############################
from machine import Pin, I2S # GPIO and I2S bus
import array # Data buffer
'''# TESTED AUDIO PARAMETERS:
# Source *.wav: https://sound-effects.bbcrewind.co.uk/
# Conversion: https://www.online-convert.com/
# Bits=16, Freq=22050, PCM=S16LE, BLOCK=4096, 177Kb
# CHANGE VOLUME OF A *.WAV IF NECESSARY
# Files: [original.wav], [destine.wav] in ESP32©
# File WAV original: original = '[original.wav]'
# New file WAV: destine = '[destine.wav]'
# Scale factor to adjust the volume (from 0.0 to 1.0)
volume = 0.2
486
Electronics & MicroPython© with ESP32©: 80 complete projects
# Block to process WAV file in blocks
block = 1024
# Open the original WAV file for binary reading
with open(original, 'rb') as input:
# Open new WAV file for binary writing
with open(destine, 'wb') as output:
while True:
# Read a block from the original file
audio = input.read(block)
# Exit loop at end of file
if not audio:
break
# Multiply each sample by the volume
adjusted_audio = bytearray([int(sample *
volume) for sample in audio])
# Block adjusted in new WAV file
output.write(adjusted_audio)'''
# CONFIGURATION OF THE PCM5102© BY I2S BUS WITH JACK
# ESP32© PCM5102©
sck_pin = Pin(25) # BCK (Bit clock)
ws_pin = Pin(23) # LCK (Left/right clock)
sd_pin = Pin(26) # DIN (Data input)
# Do not connect SCK (Serial clock) a GND
# I2S interface configuration
audio = I2S(0, # I2S bus identification
sck=sck_pin, ws=ws_pin, sd=sd_pin, # GPIO pins
mode=I2S.TX, # Transmit or reception
bits=16, # Data bits 16 or 32
format=I2S.STEREO, # Stereo or mono format
rate=22050, # Sampling frequency
ibuf=8000) # Internal buffer (bytes)
# WAV file name
wav = 'water.wav' # Example
# Block size to process large *.wav
block = 4096 # Adjust according to ESP32© memory
print('TURN ON THE EXTERNAL SPEAKER...')
print('Playing '+wav+' with PCM5102©')
# Open WAV file for binary reading
with open(wav, 'rb') as file:
while True: # Scan entire *.wav file in blocks
data = array.array('h', file.read(block))
if not data: # Look to end of file
break
# Play the data array of the WAV file
audio.write(data)
⊝⊝⊝
487
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E328: *.wav file and PCM5102©.
Create a MicroPython© script for ESP32© that
plays a *.wav file with the volume adjusted according
to data entered by the console and displayed on the
OLED screen.
•E329: LED, IRQ and sound.
Build a script that manages a button by IRQ
that, when pressed, changes the brightness of an LED
with PWM, while a sound is played through the
PCM5102©. Display the I2S© signal on the LA1010©.
•E330: Counter and sound.
Implement a pulse counter, using an
interruption-controlled button and show the result on
the OLED display. When the counter reaches the set
number of keystrokes, an audio signal will be played.
•E331: Temperature with DS18B20© and alarm.
Create a script to manage the room temperature
with a DS18B20©, which will be displayed with the OLED
screen. Use the PCM5102© to play an alert sound when
some type of threshold is exceeded. Add a hardware
timer that resets the alarm after 10 seconds. Indicate
this situation on the OLED screen.
•E332: Web Server on ESP32© and temperature.
Configure the ESP32© as a Web server to display
the internal and ambient temperatures with a DS18B20©,
which must also be displayed on an OLED screen and
generate brief ok signals every 10 seconds, controlled
by a timer and alarm as they correspond to parameters
established by console.
⊝⊝⊝
488
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 69:
Simultaneous ESP32© Execution
Sometimes we will need to run two Micropython©
scripts on the ESP32©
simultaneously, for example
script_1.py displays some data on
the OLED screen and script_2.py
initiates some pulses using
hardware timers. To carry out
these two tasks we can create a
third script that includes and
combines the code of scripts 1 and
2, but another more interesting
o p t i o n i s t o u s e t h e uasyncio
library that manages multitasking
in our ESP32©.
To do this, we create the following script
(e069_2scripts.py) that imports the uasyncio library,
scripts 1 and 2 (which must be located in the ESP32©
memory), defines the async def main() function that
starts the tasks task1 and task2 and with waiting
loops between both tasks, finally the main() function
is called to alternate the execution of both tasks.
On the other hand, script_1.py and script_2.py
must have a structure similar to the one shown below,
that is: the uasyncio library is imported, tasks 1 or
2 are defined, a waiting time is included to be able
to switch between tasks, script 1 or 2 is included and
task 1 or 2 is executed. For example:
We use the on/off LED exercise as script_1.py
and the 3 timer hardware startup exercise as
script_2.py. Both scripts adapt to the structure
already mentioned and are saved in the ESP32©.
489
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
T h e e069_2scripts script described is executed
and we can check its operation both in the console and
in the LA1010© analyzer as in the following figure:
In the diagram we can see the 3 signals
generated by the timers, the signal that comes out
through the GPIO connected to one of the LED and as a
reference a PWM signal created by the LA1010© itself
has been added.
490
Electronics & MicroPython© with ESP32©: 80 complete projects
The script_1.py could be like this:
############################
# SCRIPT_1.PY: Example script for multitasking
# INPUTS: Script_1 inputs
# OUTPUTS: Script_1 outputs
# IMPORTANT: Store script_1 in ESP32© memory
############################
import uasyncio as asyncio # Multitasking management
async def task1(): # Task 1
while True:
await asyncio.sleep_ms(1000)
# This loop simulates script_1, change it
while True:
print('1')
await asyncio.sleep_ms(100)
# Execute task 1
asyncio.create_task(task1())
Similarly, script_2.py would be:
############################
# SCRIPT_2.PY: Example script for multitasking
# INPUTS: Script_2 inputs
# OUTPUTS: Script_2 outputs
# IMPORTANT: Store script_2 in ESP32© memory
############################
import uasyncio as asyncio # Multitasking management
async def task2(): # Task 2
while True:
await asyncio.sleep_ms(1000)
# This loop simulates script_2, change it
while True:
print('2')
await asyncio.sleep_ms(100)
# Execute task 2
asyncio.create_task(task2())
And the script that invokes the simultaneous
management of script_1.py and script_2.py would be:
491
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E069_2SCRIPTS.PY: Run 2 scripts simultaneously
# INPUTS: Names of [script_1.py] and [script_2.py]
# OUTPUTS: Execution of 2 scripts in ESP32©
# IMPORTANT: Both scripts must store in ESP32© memory
############################
import uasyncio as asyncio # Multitasking manager
import script_1 # Change name of script_1
import script_2 # Change name of script_2
async def main(): # Both task management
# Start both tasks simultaneously
task1 = asyncio.create_task(script_1.task1())
task2 = asyncio.create_task(script_2.task2())
# Wait for both tasks to execute
while True:
await asyncio.sleep_ms(1000)
# Run the main event loop
try:
asyncio.run(main())
finally:
# Tasks stop when finished
task1.cancel()
task2.cancel()
⊝⊝⊝
492
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises
•E333: Distance meter and OLED.
Create a script that simultaneously executes the
distance meter and OLED scripts already seen. The
distance will be displayed on the console and the time
on the OLED. Modify the meter script to display the
distance on the OLED along with the time.
•E334: Gyroscope and OLED.
Create a script to simultaneously run the
gyroscope script and the time display on the OLED
screen. As in the previous exercise, modify the
gyroscope script to display the rotation and internal
temperature on the OLED screen along with the
measurement time.
•E335: LED, BT and thermistor.
Design a program to run the LED script with
Bluetooth and the temperature measured by a
thermistor. Modify the thermistor script so that the
temperature and time are displayed on the OLED screen
in addition to being displayed on the console.
•E336: LED, touch and sound.
Implement a script that allows the execution of
RGB LED scripts with PWM, touch pulsation and sound
generation. Modifies the OLED screen script for
displaying touch keys and RGB LED parameters.
•E337: Web Server with variables.
Create a script that allows the execution of the
server, temperature with DS18B20©, humidity with
DHT11© and OLED. Modify the Web server script to
display the time, the internal temperature of the
ESP32©, the temperature of the DS18B20©, the humidity
of the DHT11©
⊝⊝⊝
493
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 70:
74HC595© 8 LED Manager
We ha d me n ti o ne d th a t t h e E S P3 2 © h a s a
limitation on the maximum current that a GPIO can
manage, up to 40mA peak and 20mA for sure, but the
total output current of the ESP32© is limited to
250mA, so we have several limitations if we need more
consumption, for example to
connect 8 LED simultaneously
to the ESP32©. To improve
this situation there are many
options and an interesting
one is to use a shift
register, that is, a
register manager as a driver
to generate more current by
connecting it to +3.3V from
an external source.
The 74hc595© is a shift
register that stores up to 8
bits in parallel and with
three-state outputs (high
impedance outputs).
With this element we can
convert a ESP32© GPIO single
pin output into an 8-bit
output and keep this output
fixed while new data is
loaded into the serial
register input. We can also
place several 74hc595© in
cascade to manage more
outputs as we will see in
other exercises later.
494
Electronics & MicroPython© with ESP32©: 80 complete projects
For everything we have already seen before, the
74hc595© helps us easy manage 8 LED or a 7-segment
display (we will see this later) or even other low-
power devices. Its pins are the following:
• Q0 −Q 7 : 8 output bits to control 8 LED or a 7-
segment display (8 with decimal point).
• Q7 ' : serial output to connect to the DS input
of another 74hc595© in series or cascade, we'll
see.
• MR : (master reset) reset pin that is activated
to GND, in our exercise it could be controlled
by a very simple Reset circuit that we will also
see.
• SH_CP: (shift clock pulse) data movement pulse
within the shift-register. On the rise of this
signal, the data is moved by 1 bit, for example
it is moved to and so on. When this signal
drops, the data remains unchanged.
• ST_CP: (storage clock pulse) data storage pulse.
When this signal rises, the data is memorized in
the register.
• OE: (output enable) allows the display of
register data at the output, activated with the
GND level.
• DS: (data serial) serial data input.
• VCC: power supply at +3.3V from external source.
• GND: ESP32© ground and external source connected.
We will use an array of 10 LED of which we will
connect only 8 to the shift-register and with a single
resistor for all of them (solely for clarity of the
circuit), but remember that if the array does not
incorporate a resistor per LED, we will have to add an
220 Ω external resistor for each of the LED in the
array that we use.
495
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The automatic reset circuit (optional) is made
up of the set:
• 10k Ω resistance R.
• 100nF capacitor C.
• A diode D of type
1N4148©.
When the circuit is
connected to the power supply,
capacitor C charges through R
and therefore MR spends a
brief time at GND resetting to
74hc595©.
When capacitor C i s
fully charged, current stops
flowing through it and
therefore it is as MR were
at +3.3V and thus the Reset
ends. Diode D a c t s a s a
discharger for C so that the
process can function properly
on the next power connection.
IMPORTANT: pay attention to the polarity of diode D.
The Reset circuit prevents the 74hc595© from
starting in an “unknown” state when powered and
therefore the LED illuminate randomly.
On the other hand, we connect the inputs DS to
GPIO32, ST_CP to GPIO35 and SH_CP to GPIO34, in this
way GPIO32 (DS) indicates the bit to be stored, GPIO34
(SH_CP) sends level increases to move this bit within
the register (shift function) and the GPIO35 (ST_CP)
stores them.
T h e OE and MR pins remain fixed so that
the output of the 74hc595© is always active and the
circuit is not reset.
With this circuit, depending on the bits that we
send to the shift-register, through the DS pin, we can
carry out the sequence we want in the LED.
496
Electronics & MicroPython© with ESP32©: 80 complete projects
In the following example script, a lit LED, in a
8 array LED, is moved from end to end in an infinite
cycle, but we could do any type of sequence with the 8
LED, loading 1byte=8bits through DS, moving each bit
with a pulse in SH_CP and storing it in the output
with a pulse at ST_CP.
############################
# E070_8led_74hc595.PY: 8LED with shift-register
# INPUTS: GPIO, timing and byte to show
# OUTPUTS: 8LED according to byte to display
############################
from machine import Pin # GPIO management
import time # Timing control
497
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Define pins DS, ST_CP and SH_CP
ds_pin = Pin(25, Pin.OUT) # Data
sh_cp_pin = Pin(32, Pin.OUT) # Shift
st_cp_pin = Pin(33, Pin.OUT) # Storage
# Transition timing
tmp = .5
# Send one byte to 74hc595© shift-register
def look(data):
for n in range(7, -1, -1): # Send 8bit: MSB to LSB
bit = (data >> n) & 0x01 # Umpteenth bit
ds_pin.value(bit) # DS=umpteenth bit
sh_cp_pin.value(1) # UP SH_CP (shift)
time.sleep_us(1) # Wait a while
sh_cp_pin.value(0) # Down SH_CP
st_cp_pin.value(1) # Up ST_CP (store)
time.sleep_us(1) # Wait a while
st_cp_pin.value(0) # Down ST_CP
# Moves a lit LED from D0 to D7 and vice versa
def shift_led():
for n in range(8): # 0 to 7
look(1 << n) # LED on to n position
time.sleep(tmp) # Wait transition
for n in range(6, 0, -1): # 7 a 0
look(1 << n) # LED on to n position
time.sleep(tmp) # Wait transition
# Main loop
if __name__ == "__main__":
print('8LED MANAGEMENT WITH 74HC595©')
print('Display scroll...')
try:
while True: # Infinite loop
shift_led()
except KeyboardInterrupt:
print('Program completed')
look(0) # LED off
⊝⊝⊝
498
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E338: LED array and current sensor.
Design a circuit that uses the shift register
and an array of LED, along with a current sensor.
Program the ESP32© micro controller to simulate a
level with the LED depending on whether current is
detected or not.
•E339: MPU6050© visualization on an OLED display.
Connect the shift register, the LED array, a
MPU6050© and an OLED display. Use the MPU6050© to
detect motion and display the data on the OLED.
Represents the tilt in the LED array.
•E340: Data recording and magnetic sensor.
Combine the shift register, the LED array and a
magnetic sensor. Program the ESP32© to read the data
from the magnetic sensor and write it to a file and
view the recording process on the array.
•E341: Alarm clock with RTC and LED control.
Use the shift register, the LED array and the
updated external RTC and program the micro controller
to function as an alarm clock, where the LED
progressively light up as the programmed wake-up time
approaches, sounding a buzzer.
•E342: LED control with remote control.
Connect the shift register, the LED array and
the infrared receiver to the ESP32©. Program so that
when we press any key from 1 to 8 on the remote
control, the corresponding array LED controlled by the
74hc595© will light up.
⊝⊝⊝
499
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 71:
SMA42056© 7 Segments Display
An advanced variant of
the previous exercise
consists of replacing the
individual or array LED with
a display of 7 grouped
segments (8 if we count the
decimal point, dp) to display
characters. To do this, we
will use two main components:
The 74hc595© “shift-
register” chip that will act
as an interface between the
ESP32© and the 7-segment
display similar to the one we
have seen with the 8
individual or array LED.
A 7-segment module or
display, for example the
SMA42056© to easily display
the characters sent by the
micro controller and
appropriately encoded by the
shift-register.
The 7-segment display is
a package that contains 8
small LED to configure the
numbers from 0 to 9, some
letters (those used in
hexadecimal numbers: A, B, C,
D, E, F) and the decimal point
dp also.
500
Electronics & MicroPython© with ESP32©: 80 complete projects
Each LED is a segment of the character to be
represented and is numbered with letters a through g
and the decimal point.
There are two 7-segment display models: the
common cathode one (used in this exercise) where all
the LED have their cathode (negative) attached and
which must be connected to GND through a 220 Ω
resistor, and the common anode one, where all LED have
their common anode (positive) attached and must also
be connected to +VCC (external source) with 220 Ω
resistors. In this exercise and for simplicity, only
o n e 160 Ω resistor has been added from the common
cathode to GND.
To activate a segment on a 7-segment display
with a common cathode, we must apply a HIGH signal to
it, that is, a logic 1 (limiting the current with the
indicated resistance) and to turn it off we will apply
a LOW or logic 0.
501
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
We connect the GPIO in a similar way as in the 8
LED exercise, that is, DS to GPIO25, SH_CP to GPIO32
and ST_CP to GPIO33. We also use an external +3.3V
source and join the GND of that source and the ESP32©.
We compose the characters with a 1 in the LED
that must be turned on and with a 0 in which we want
to turn it off and for greater clarity in the
management of these segments (a-g) and the output bits
( Q0−Q 7 ) we associate them in the following way:
a b c d e f g dot
Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7
Thus, to construct the bytes (8bit) of each
character (the hexadecimal codes are also represented
here) we would have the following table:
dot g f e d c b a
CHR Q7 Q6 Q5 Q4 Q3 Q2 Q1 Q0 HEX
0 0 0 1 1 1 1 1 1 3F
1 0 0 0 0 0 1 1 0 06
2 0 1 0 1 1 0 1 1 5B
3 0 1 0 0 1 1 1 1 4F
4 0 1 1 0 0 1 1 0 66
5 0 1 1 0 1 1 0 1 6D
6 0 1 1 1 1 1 0 1 7D
7 0 0 0 0 0 1 1 1 07
8 0 1 1 1 1 1 1 1 7F
9 0 1 1 0 1 1 1 1 6F
dot 1 0 0 0 0 0 0 0 80
A 0 1 1 1 0 1 1 1 77
b 0 1 1 1 1 1 0 0 7C
C 0 0 1 1 1 0 0 1 39
d 0 1 0 1 1 1 1 0 5E
502
Electronics & MicroPython© with ESP32©: 80 complete projects
dot g f e d c b a
CHR Q7 Q6 Q5 Q4 Q3 Q2 Q1 Q0 HEX
E 0 1 1 1 1 0 0 1 79
F 0 1 1 1 0 0 0 1 71
null 0 0 0 0 0 0 0 0 00
Where we have in Q0−Q 7 the binary
representation, on the left the representation of the
character (CHR) to be displayed and on the right its
corresponding in hexadecimal (HEX), being able to use
both in the script in which we will display all these
symbols in an infinite loop.
############################
# E071_7seg_74hc595.PY: 7 segments LED with 74hc595
# INPUTS: GPIO, timing and data to show
# OUTPUTS: 7 segments with shown data
############################
from machine import Pin # GPIO management
import time # Timing control
# Define DS, ST_CP and SH_CP pins
ds_pin = Pin(25, Pin.OUT) # Data
sh_cp_pin = Pin(32, Pin.OUT) # Shift
st_cp_pin = Pin(33, Pin.OUT) # Store
# Transition timing
tmp = .4
# 7 segments codes
codes = [
0b00111111, # 0
0b00000110, # 1
0b01011011, # 2
0b01001111, # 3
0b01100110, # 4
0b01101101, # 5
0b01111101, # 6
0b00000111, # 7
0b01111111, # 8
0b01101111, # 9
503
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
0b10000000, # dot
0b01110111, # A
0b01111100, # B
0b00111001, # C
0b01011110, # D
0b01111001, # E
0b01110001, # F
0b00000000] # null]
# Send a byte to 74hc595 shift-register
def look(data):
for n in range(7, -1, -1): # Send 8 bits: MSB-LSB
bit = (data >> n) & 0x01 # Umpteenth bit
ds_pin.value(bit) # DS=umpteenth bit
sh_cp_pin.value(1) # Up SH_CP
time.sleep_us(1) # Wait a while
sh_cp_pin.value(0) # Down SH_CP
st_cp_pin.value(1) # Up ST_CP
time.sleep_us(1) # Wait a while
st_cp_pin.value(0) # Baja ST_CP
# Main loop
if __name__ == "__main__":
print('MANAGEMENT MODULE 7 SEGMENTS WITH 74HC595')
print('Display scroll...')
try:
while True:
for x in codes:
look(x)
time.sleep(tmp)
except KeyboardInterrupt:
print('Program completed')
look(0) # LED off
As we have mentioned, due to consumption, we
should not use the ESP32© supply, neither the +3.3V
nor the +5V one. We must use an external source, such
as the MB-102© and join its GND with the GND of the
ESP32©.
⊝⊝⊝
504
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercises 72:
4x7 Segment Cascade Display
In this exercise we will use, for example, four
7-segment displays, connected in cascade to display a
4-digit numerical sequence, that is, the output of one
circuit is the input of another one like a cascade
configuration.
We could add (work for the reader) a rotary
decoder so that, by rotating the decoder, the sequence
will go forward or backward and pressing the button
will restart the process and we could add 2 additional
individual LED, so that when the sequence advances
sequence the green LED lights up and if it goes back
the red LED lights up.
Finally, to avoid possible flickering on the
displays, we can connect the output enable signal
OE of the four 74hc595© shift-registers with a GPIO
of the ESP32© controlled by software so that we keep
it at HIGH and alone, after performing the scrolling
and storage, we can activate the OE to LOW to
activate the display.
To decode the 7-segment display we will use a
74hc595© for each of them, joining the Q7' output of
the first with the DS data input of the second and so
on. This process can be repeated with more than 2
displays, hence the name “cascade” connection.
The GPIO that we need to manage the four
74hc595© with the four 7-segment modules, are the same
ones that we use for a single 74hc595© and this is the
most important thing since, as we see, with only 3
GPIO we can display multi-digit hexadecimal sequences
without occupying GPIO of the ESP32©.
505
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
GPIO/Pin Device Function
GPIO25 ESP32© DS data
GPIO32 ESP32© SH_CP shift pulse
GPIO33 ESP32© ST_CP storage pulse
1...7 74hc595© Q1...Q7 bits
8 74hc595© GND joined to source GND
9 74hc595© Q7S cascade output
10 74hc595© MR master reset
11 74hc595© SHCP connected to GPIO32
12 74hc595© STCP connected to GPIO33
13 74hc595© OE connected to GND
14 74hc595© DS data
15 74hc595© Bit Q0
16 74hc595© VCC=3.3V-5V power supply
############################
# E072_4x7seg_74hc595.PY: 4x7 segments with 74hc595©
# INPUTS: GPIO, timing and numbers
# OUTPUTS: Numbers in 4x7 segments
############################
506
Electronics & MicroPython© with ESP32©: 80 complete projects
from machine import Pin # GPIO control
import time # Timing management
# Define DS, ST_CP and SH_CP pins
ds_pin = Pin(25, Pin.OUT) # Data
sh_cp_pin = Pin(32, Pin.OUT) # Shift
st_cp_pin = Pin(33, Pin.OUT) # Storage
# Parameters
tmp = .3 # Transition time
n_mod = 4 # Number of modules
# 7 segments codes
codes = [
0b00111111, # 0
0b00000110, # 1
0b01011011, # 2
0b01001111, # 3
0b01100110, # 4
0b01101101, # 5
0b01111101, # 6
0b00000111, # 7
0b01111111, # 8
0b01101111, # 9
0b10000000, # dot
0b01110111, # A
0b01111100, # B
0b00111001, # C
0b01011110, # D
0b01111001, # E
0b01110001, # F
0b00000000] # null]
# Send one byte to 74hc595 shift-register
def look(data):
for n in range(8*n_mod, -1, -1): # MSB to LSB
bit = (data >> n) & 0x01 # Umpteenth bit
ds_pin.value(bit) # DS=umpteenth
sh_cp_pin.value(1) # Up SH_CP
time.sleep_us(1) # Wait a while
sh_cp_pin.value(0) # Down SH_CP
st_cp_pin.value(1) # Up ST_CP
time.sleep_us(1) # Wait a while
st_cp_pin.value(0) # Down ST_CP
# Main loop
if __name__ == "__main__":
print('4x7 SEGMENTS WITH 74HC595© MANAGER')
print('Visualize numbers...')
try:
while True: # Main loop
for number in range(1962, 2025): # Range
print(number, end=' ')
507
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Thousands, hundreds, tens and units
m, c, d, u = map(int, str(number))
# Move each module 7 segments
without = codes[m]<<24|codes[c]<<16|
codes[d]<<8|codes[u]
within = without | codes[10] # Dot
look(within) # Look with dot
time.sleep(tmp) # Wait
look(without) # Look without dot
time.sleep(tmp) # Wait
except KeyboardInterrupt:
print('Program completed')
look(0) # LED off
If we use the logic analyzer LA1010© or similar
and connect it to our circuit, we can see the DS
signal of data transferred in series from the ESP32©
to the shift-register 74hc595© by the GPIO25, the
shift bit shift-register SH_CP pulses in GPIO32 and
after completing the movement we see how the ST_CP
storage pulse is activated in GPIO33.
508
Electronics & MicroPython© with ESP32©: 80 complete projects
There are 7-segment display modules on the
market that group multiple digits and integrate the
necessary shift-register as well as all the data
connections (DIN, CS and CLK) and power (VCC and GND)
so that they can be connected seamlessly to ESP32©.
4x7 segments module:
8x7 segments module:
⊝⊝⊝
509
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E343: Temperature and time display.
Use a 4x7 segment module and a DHT11© sensor to
show the current temperature and time on the display.
The temperature will be updated every second while the
time will be updated every minute using an RTC module.
•E344: Distance measurement and display.
Connect an ultrasonic sensor and a 4x7 segment
module and develop a script that measures the distance
to an object and shows it on the display. Adds the
ability to change the unit of measurement between
centimeters and inches using an infrared remote
control.
•E345: Water pressure control.
Use a water pressure sensor with a 4x7 segment
module and create a system that monitors the water
pressure in a pipe and displays it on the display.
When the pressure drops below a predefined threshold,
send a SMS.
•E346: Weather forecast with barometer.
Connect a barometer to the ESP32© and use a 4x7
segment module to display changes in atmospheric
pressure. Design an algorithm that simulates a weather
prediction: sunny, cloudy, rainy, etc. and show it on
the display. Use an infrared remote control to
activate the display.
•E347: 4x7 module and rotary decoder.
Add to the previous exercise a rotary decoder to
change the pressure measurement units between bars,
hPa, mmHg, psi, etc.
⊝⊝⊝
510
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 73:
LCD1602© I2C Display
In this exercise we are
going to connect an LCD
display to the ESP32© and
that will allow us to view
much more information, move
it easily and apply
commands: clear screen, go
to the beginning, position a
cursor at a point, turn off
the display, turn off the
cursor, activate the
backlight, etc.
There are many displays
of this type on the market,
one that is simple to use,
cheap and easy to obtain is
the LCD1602©, which has 2 lines of 16 characters and
has a version that connects to the ESP32© via the I2C
bus with the typical address 0x27. This LCD is powered
at +5V which we must provide from an external power
source. It also has an adjustable backlight and
contrast with a 10k Ω potentiometer built into the
display board.
There is also another version of this display
that we will see in the following exercise, which does
not have an I2C bus and is connected to a 4 or 8 bit
data bus and some control lines for their flow. In
this exercise we will see the I2C version in which we
will use GPIO21 for SDA and GPIO22 for SCL.
This display is also available in other formats:
4x20, 8x2, etc. and various colors.
511
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
With this option we can display a lo of
information, for example, time, internal temperature
of the ESP32©, ambient temperature provided by a
sensor, other sensors, status of some variable, enter
sensor threshold data, alarm status, counters,
position of a decoder, etc.
To manage the LCD1602© we need an external
MicroPython© library. In this exercise we use the
lcd1602.py library available on the Internet, in which
we must ensure that it uses the appropriate GPIO for
the I2C bus that we are configuring.
The version of the LCD1602© with I2C bus has an
integrated circuit converter from I2C bus to parallel
data bus (D4_D7, RS, RW, E) and that also allows us to
control the backlight by software and the contrast by
hardware (the blue potentiometer).
As we had already seen, all I2C devices are
assigned a hexadecimal address by their manufacturer
and in this case it is 0x27 and which we can detect
with the i2c.scan() function also mentioned.
Let's keep in mind that the lcd1602.py library
itself performs this scan, displaying the necessary
errors if the LCD1602© is not detected on the bus.
512
Electronics & MicroPython© with ESP32©: 80 complete projects
In the analyzer we see the two I2C bus signals:
SDA and SCL that allow sending the texts to be
displayed from the ESP32© to the LCD1602©.
In the script to manage the LCD1602© we import
t h e lcd1692.py library, which must be stored in the
ESP32© memory and utime to control the times but we do
not import i2c since the lcd1692.py library itself
does it.
We call the LCD() instance of the library, which
is actually the one that is in charge of the entire
task, being able to do: erase the screen, write text
in a certain column and row and activate the
backlight, etc.
As an example, we clear the screen and activate
the backlight, write some text centered on row 0, fill
the entire 16x2 matrix with '.' and finally we move
the text 'Bye' to the first line.
513
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E073_LCD1602.PY: I2C LCD1602© management
# INPUTS: Texts and I2C (SDA: GPIO21 & SCL: GPIO22)
# OUTPUTS: Visualization in LCD
# IMPORTANT: lcd1602 library must be in ESP32© memory
# Change in lcd1602.py I2C GPIO if is necessary
############################
from lcd1602 import LCD # LCD1602© control
import utime as time # Timing manager
lcd=LCD() # LCD class control
# Commands available inlcd1602.py
'''
clear() Clear LCD
write(c,f,text) Text in (0,0) to (15,1)
openlight() Activate the backlight
'''
# Start LCD
lcd.clear() # Clear screen
lcd.openlight() # Activate the backlight
# Show title
lcd.write(4,0,'ESP32') # ESP32 in column 4 row 0
time.sleep(1)
lcd.clear()
# Scroll one point
for f in range(2): # For 2 rows
for c in range(16): # For 16 columns
lcd.write(c,f,'.')
time.sleep(.05)
time.sleep(2)
lcd.clear()
# Shift one text
text = 'Bye' # Change text
for col in range(16): # In each column
lcd.write(col,0,text) # Write in column
time.sleep(.05)
lcd.write(0,0,' '*(col+1)) # Crear backwards
⊝⊝⊝
514
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E348: LCD1602©, RTC and temperature.
Add a temperature sensor to the LCD and display
the updated date and time of the RTC every 5 seconds
on the first line alternately and the temperature in
degrees Celsius and Fahrenheit on the second line.
•E349: LCD1602© and servo.
Connect an I2C managed LCD and a servo to the
ESP32© and move the servo and display its angle on the
LCD using Bluetooth managed commands. Add to the
script all the error checks that will detect and
report if any of the elements are not present.
•E350: Gyroscope and LCD.
Create a script for ESP32© that controls an
MPU6050© type gyroscope and display its data on an
LCD. Define critical position ranges and display the
corresponding alarms on the LCD and acoustically with
a passive buzz managed with PWM.
•E351: Home automation control with IFTTT© and LCD.
Recovers the exercise of Home Automation control
with IFTTT© and adds an LCD to display the activated
or deactivated state of some actuators. Test with 4
triggers and RTC displayed cyclically.
•E352: Distance meter and LCD.
Connect an ultrasonic distance meter to the
ESP32© and display the distance along with the time on
the LCD display. If the distance exceeds certain
predefined thresholds, send an alarm signal via SMS
using IFTTT©. Record the alarms in a file on the
ESP32© and periodically, for example every minute,
send an alarm report by email. If there are alarms,
also indicate this with a dual LED that changes color
and a special icon on the display.
⊝⊝⊝
515
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 74:
LCD1602© Display with PCF8574©
In this exercise we will
see how to use the PCF8574©
GPIO expander to control a
display like the LCD1602©
that does not have the I2C
module.
We have already seen the
LCD1602© with a built-in I2C
module, but here the GPIO
extender is used as an
exercise in using the I2C
bus and the PCF8574©. With
this expander we can control
the control signals (RS, E)
and also the data signals
(D4...D7). To facilitate
the management of these
ports we will use third-
part y li brar ies alre ady
tested and configured.
The connections between the PCF8574© GPIO
expander and the LCD1602© are as follows:
LCD1602© PCF8574© Description
VSS GND Ground
VDD +5V +5V external power supply
V0 Contrast 10k Ω potentiometer control
RS P0 Register Select (data/command)
RW P1 Read/Write, only Write is used
516
Electronics & MicroPython© with ESP32©: 80 complete projects
LCD1602© PCF8574© Description
E P2 Enable
D4...D7 P4...P7 Data
A, K Backlight Lightning with +5V and GND
The script imports the machine and ntptime
libraries to control the RTC, SoftI2C to control the
I2C bus (PCF8574©, not LCD1602©), lcd_api to control
the output of the PCF8574© expander and i2c_lcd for
the control commands of the LCD1602©.
The address of the PCF8574© on the I2C bus is
set, the number of rows and columns available, the i2c
instance starts the I2C bus in GPIO21 for the SDA and
GPIO22 for the SCL, the lcd instance to control the
LCD and they are related a series of commands
available with that instance to manage the LCD1602©:
delete, show/hide/blink cursor, refresh/hide text,
backlight on/off, move cursor to column vs row, write
a character/string in cursor position, write a
predefined character to a memory address, etc.
517
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Examples of creating special characters:
https://maxpromer.github.io/LCD-Character-Creator/
The RTC parameters that we have already seen are
controlled and a loop, with error control, where the
RTC data from the ESP32© is obtained, it is formatted,
presenting the date in row 1 and the time in row 2 in
a centered manner of the LCD1602©. IMPORTANT: the
lcd_api.py and i2c_lcd.py libraries must be downloaded
from the Internet and installed in the ESP32© memory.
518
Electronics & MicroPython© with ESP32©: 80 complete projects
We can observe the I2C bus, RS, where HIGH are
data and LOW are commands, RW which is always in LOW,
E which in HIGH is written D4..D7, first the 4 highest
bits of the transmitted character and then the lowest,
this operation is repeated for each byte.
############################
# E074_LCD1602_PCF8574.PY: LCD1602© with PCF8574©
# INPUTS: Text, I2C (SDA: GPIO21, SCL: GPIO22)
# OUTPUTS: Visualization in LCD
# IMPORTANT: lcd_api & i2c_lcd must be in ESP32©
############################
'''
-----------------------------------
LCD1602© PCF8574© Description
-----------------------------------
VSS GND GND
VDD +5V Power supply
V0 Contrast Potentiometer control
RS P0 Data/Commands
RW P1 Read/Write (Write only)
E P2 Enable
D4...D7 P4...P7 Data
A, K Backlight Lighting of the panel
'''
import machine, time # RTC & time control
import ntptime # ESP32© RTC adjusts
from machine import Pin, SoftI2C # Bus I2C
from lcd_api import LcdApi # Bus PCF8574©
from i2c_lcd import I2cLcd # LCD1602© commands
ADDRESS = 0x20 # PCF8574© I2C address
ROWS = 2 # LED rows
COLUMNS = 16 # LED columns
i2c = SoftI2C(scl=Pin(22), sda=Pin(21)) # I2C bus
lcd = I2cLcd(i2c, ADDRESS, ROWS, COLUMNS) # LCD
'''
----------------------------
CONTROL COMMANDS OF LCD1602©
----------------------------
lcd.clear() # clear lcd and set in 0,0
lcd.show_cursor() # show cursor
lcd.hide_cursor() # hide cursor
lcd.blink_cursor_on() # blink cursor
lcd.blink_cursor_off() # not blink cursor
519
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
lcd.display_on() # show text
lcd.display_off() # hide text
lcd.backlight_on() # backlight on
lcd.backlight_off() # backlight off
lcd.move_to(7,0) # move to column(0,15), row(0,1)
lcd.putchar(x) # write character to x in cursor
lcd.putstr(s) # write string s in cursor
lcd.custom_char(a,b) # bytearray b in address a
'''
# Examples of special characters
hart = bytearray([0x00,0x00,0x1B,0x1F,0x1F,0x0E,
0x04,0x00]) # hart
face = bytearray([0x00,0x00,0x0A,0x00,0x11,0x0E,
0x00,0x00]) # face
enne = bytearray([0x0E,0x00,0x16,0x19,0x11,0x11,
0x11,0x00]) # ñ
a_with = bytearray([0x02,0x04,0x0E,0x01,0x0F,0x11,
0x0F,0x00]) # á
lcd.custom_char(0, hart) # Invoke with chr(0)
lcd.custom_char(1, face)
lcd.custom_char(2, enne)
lcd.custom_char(3, a_with)
# RTC configuration
rtc = machine.RTC()
# Try to sync with an NTP server
try:
print ('Getting date and time from Internet...')
ntptime.settime() # Get Internet data
except OSError as e:
print("Error syncing with NTP:", e)
machine.reset()
# Setting the time zone (for example, UTC+1)
adjust_hour = 1 # According local hour
# Gets the current date and time of the RTC
year, month, day, weekday, hour, minute,
second, _ = rtc.datetime()
# Set the time according to the time zone
hour += adjust_hour
# Ensures that the time is in the range of 0 to 23
hour = hour % 24
520
Electronics & MicroPython© with ESP32©: 80 complete projects
# Update the RTC with the set time
print('ESP32© update')
rtc.datetime((year, month, day, weekday, hour,
minute, second, 0))
print('Present the time on LCD1602©')
try:
while True:
# Gets the current date and time of the RTC
year, month, day, weekday, hour, minute,
second, _ = rtc.datetime()
# Format and display date and time
t_day = "{:02d}-{:02d}-{:04d}".format(day,
month, year)
t_hour = "{:02d}:{:02d}:{:02d}".format(hour,
minute, second)
lcd.move_to(2,0) # Center day online 1
lcd.putstr(chr(0)+t_day+chr(0))
lcd.move_to(4,1) # Center time online 2
lcd.putstr(t_hour)
time.sleep(1) # Wait 1 second
except KeyboardInterrupt:
print('Program completed...')
except Exception as err:
print ('Error: '+str(err))
The LCD1602© is available in several colors, we
have seen the blue and here the green:
⊝⊝⊝
521
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E353: Scroll on LCD1602©.
Create a MicroPython© script that includes a
function to perform a horizontal or vertical scroll of
x positions for a predetermined text.
•E354: Temperature and LCD1602©.
Build a script that presents the time and
internal temperatures and external temperatures
measured by a DS18B20© sensor on the LCD display.
•E355: Various data on LCD1602©.
Design a script for that displays the time,
temperature and humidity measured by a DHT11© and a
DS18B20©, each data must be displayed, cyclically,
when a button controlled by IRQ is pressed.
•E356: Rotary decoder and LCD1602©.
Use the previous script and change the button
for a rotary decoder that allows us to scroll through
a data menu and when we press the decoder button, the
value of that data is presented: temperature,
humidity, time, date, etc.
•E357: ADC, PWM and LCD1602©.
Connect a PWM signal, generated by the LA1010©,
to a GPIO of the ESP32© and measure the frequency
using IRQ, add a potentiometer to a GPIO with analog
to digital converter (ADC) of the ESP32© and measure
its value between 0 and + 3.3V, presents all the data,
together with the time, on the LCD1602© display.
•E358: Web and LCD1602©.
Connect two LED to the ESP32©, configure a Web
server and create an HTML script embedded in a script
that presents buttons that act with these LED and
presents the status on the LCD1602©.
⊝⊝⊝
522
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 75:
ILI9341© TFT Display
In this exercise we will
advance a little further in
the use of a display with more
possibilities than a simple
16-character by 2-row LCD,
whether with an I2C bus or
parallel bus. Specifically, we
will use a 320x240 pixel TFT
(Thin-Film Transistor) display
such as the ILI9341©, which
consists of a thin film
transistor LCD panel that
greatly improves the image
quality and its response.
The most important characteristics of the TFT
ILI9341© could be summarized as:
• Touch version or not: There are several versions
of this TFT, for simplicity in this exercise we
will only use this device as output and
therefore without the touch module (XPT2046©
controller) and without a built-in uSD card.
• SPI Bus: Serial communication through the SPI
bus using the SCK (GPIO25), MOSI (GPIO26), MISO
(GOIO32), DC (GPIO27), CS (GPIO12), RST (GPIO14)
and LED (GPIO33) pins.
• 2.8-inch TFT display with 320x240 pixels and
262k low-consumption colors and the possibility
of vertical or landscape orientation.
• Power: from +5V to +3.3V but I recommend
powering it with an external source since
consumption can reach 300mA when adding the
backlight with 4 bright white LED.
523
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The SPI (Serial Peripheral Interface) bus
supported by the ESP32© is a synchronous serial
protocol used for data transfer between the micro
controller and other peripherals, such as sensors,
displays, memory cards, etc.
This bus fundamentally consists of the following
signals:
• MISO (Master In Slave Out): This line is used by
the slave (TFT) to send data to the master
(ESP32©), therefore in this exercise we are not
going to use it directly.
• MOSI (Master Out Slave In): This line is used by
the master to send data to the slave, that is,
from the ESP32© to the TFT.
• SCK (Serial Clock): This line is used to
synchronize the data transfer between the master
and the slave. The master generates clock pulses
to synchronize the communication.
• CS (Chip Select): This line is used by the
master to select the slave with which it wishes
to communicate. Allows communication with
multiple SPI devices.
In addition, this TFT uses the DC (Data/Command)
pins to indicate to the TFT whether the data sent are
control commands (DC to LOW) or pixels (DC to HIGH),
RST (Reset) to start and LED to control the backlight.
524
Electronics & MicroPython© with ESP32©: 80 complete projects
To manage this TFT we need several libraries and
data, on the Internet there are multiple and for
different purposes, in this basic exercise we use the
W o k w i © ili9341.py library together with the
xglcd_font.py to generate the character fonts and a
font example, the Dejavu24x43. These 3 files must be
downloaded and stored in the ESP32© memory to be
invoked from our script.
This TFT has multiple control functions for
pixels, texts, figures, etc. and therefore requires
multiple functions included in the previous libraries.
In this exercise we use some of them, such as:
• backlight.on() Activate the backlight.
• display_on() TFT on.
• cleanup() Clean, TFT off, stops SPI.
• clear() Clear TFT.
• clear(color565(R,G,B)) Fills TFT with RGB color
16bits, R, G, B from 0 to 255.
• draw_hline() Draw horizontal line.
• draw_vline() Draw vertical line.
• draw_rectangle() Draw a rectangle.
• draw_text() Write a text.
• fill_circle() Draw and fill a circle.
Other available functions are:
• clear() Clear screen.
• block() Write a data block
• draw_circle() Draw a empty circle.
• draw_ellipse() Draw a empty ellipse.
• draw_image() Draw the file image.bmp.
• draw_letter() Write a character.
• draw_line() Draw a line.
• draw_lines() Draw multiple lines.
• draw_pixel() Visualize one pixel.
• draw_polygon() Draw an n sided polygon.
• draw_sprite() Visualize an icon.
• load_sprite() Load a sprite.
• fill_ellipse() Visualize & fill an ellipse.
• fill_rectangle() Draw & fill a rectangle.
• is_off_grid() Coordinates out of limits.
525
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• reset_mpy() TFT reset.
• scroll() Vertical scroll.
• Etc. Many others.
In the logic analyzer we can see the MOSI signal
where the data goes from the ESP32© to the TFT, the
MISO signal which is always at HIGH since no data is
transmitted from the TFT to the ESP32©, the SCLK
synchronization clock signal, the selection of the
ILI9341© with the CS signal, the use of data or
commands with the DC signal and the reset signal that
is always at HIGH.
526
Electronics & MicroPython© with ESP32©: 80 complete projects
The script needs the ili9341.py library to
control the TFT, the xglcd_font.py to control text
fonts and a font file such as Dejavu24x43.c and all of
them must be stored in the ESP32© memory.
The program begins with the import of the
aforementioned libraries plus time for time management
and machine for controlling the GPIO and SPI.
The spi instance is defined to specify the bus
speed, the control signals SCK, MOSI, MISO, DC, CS,
RST and backlight, as well as the width of the TFT of
320 pixels, the height of 240 pixels and a rotation of
90º to use the TFT in landscape mode.
In the body of the script we use various
functions to control the TFT: turn on backlight, turn
on TFT, erase screen with a color, draw horizontal
line, vertical line, rectangle, filled circle, text
and finally erase screen, turn off TFT and release the
bus SPI.
############################
# E075_TFT_ILI9341.PY: ILI9341© TFT management
# INPUTS: Texts and SPI bus
# OUTPUTS: Visualization in TFT
# IMPORTANT: ili9341.py library must be in ESP32©
############################
from time import sleep # Timing control
from ili9341 import Display, color565 # TFT manager
from machine import Pin, SPI # GPIO and SPI
from xglcd_font import XglcdFont # Fonts
# GPIO and SPI
spi = SPI(2, baudrate=32000000, sck=Pin(25),
mosi=Pin(26), miso=Pin(32))
tft = Display(spi, dc=Pin(27), cs=Pin(12),
rst=Pin(14), width=320, height=240, rotation=90)
backlight = Pin(33, Pin.OUT)
# Parameters
tmp = .5 # Transition time
font = XglcdFont('Dejavu24x43.c', 24, 43) # Example
print('FUNCTIONS WITH ILI9341© TFT')
527
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
print('Backlight ON') # GPIO33
backlight.on()
sleep(tmp)
print('TFT ON') # GPIO14
tft.display_on()
sleep(tmp)
print('Fill TFT with RGB color')
tft.clear(color565(255,0,0))
sleep(tmp)
# x(0,319) , y(0,219), w(0,319), color
print('Draw a horizontal line')
tft.draw_hline(0, 120, 319, color565(0, 0, 255))
sleep(tmp)
# x(0,319) , y(0,219), w(0,319), color
print('Draw a vertical line')
tft.draw_vline(160, 0, 239, color565(0, 0, 255))
sleep(tmp)
print('Draw a rectangle') # x1, y1 w, h, color
tft.draw_rectangle(10,10,300,220, color565(0, 0, 255))
sleep(tmp)
print('Draw and fill a circle') # x0, y0, r, color
tft.fill_circle(280, 200, 20, color565(0, 255, 0))
sleep(tmp)
# x(0,319), y(0,219), text, fuente, color, bg
print('Visualize text')
tft.draw_text(30, 100, 'Hello ESP32', font,
color565(0, 255, 0), background=color565(255, 0, 0))
sleep(4*tmp)
print('Clear, TFT OFF and release SPI', end=': ')
tft.cleanup()
sleep(tmp)
print('Script completed...')
⊝⊝⊝
528
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E359: Visualization of sensors and RTC.
Connect a DHT11© sensor to measure ambient
temperature and humidity. Use a TFT to display this
data in real time and include the current date and
time using an RTC.
•E360: Water valve control.
Use an ultrasonic meter to detect the water
level in a tank. Connect a servo motor that simulates
a control valve and uses the TFT to display the
current water level and valve status with a visual
representation.
•E361: Clock with alarm and TFT display.
Create a digital alarm clock using an RTC to
keep time. Use the TFT to display the current time as
well as the set alarm time. Include a rotary decoder
to set, activate and deactivate the alarm.
•E362: Servo control with graphical interface.
Create a graphical interface on the TFT to
display the position of a servo motor. Use a rotary
decoder to move the servo to predefined positions.
Include a drawing on the screen that simulates the
movement of the servo.
•E363: Touch screen.
Add touch control of the screen using the
xpt2046.py library. Configure an SPI1 bus for display
control and SPI2 for touch control and display the x
a n d y position of the position captured by touch on
the screen.
⊝⊝⊝
529
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 76:
RC522© RFID Reader
In this exercise we will
see a device that will allow us
to comfortably identify
ourselves with a card or a
wireless key to be able to
perform various actions: run a
script, control access, choose
options, access a website, etc.
To do this we will use
the RFID-RC522© module, which
is basically a device that uses
radio frequency identification
(RFID) technology to read and
write data on RFID cards and
tags, for example on S50© or
S50 Key Ring© cards (1kb cards
organized in 16 sectors with 4 blocks and 16bytes per
block). Its basic operation could be summarized in the
following stages:
Antenna: The RC522© RFID reader vs writer module
or equivalent has an antenna incorporated into the
printed circuit board and is used to emit and receive
radio frequency signals to or from the RFID cards.
Contactless communication: The RFID system
allows wireless communication, that is, contactless,
between the reader (RFID-RC522©) and the RFID card.
This means that the card does not need to be in
physical contact with the reader for data exchange.
This advantage allows both the cards and the RFID
readers to be long-lasting as there is no friction
between both elements.
530
Electronics & MicroPython© with ESP32©: 80 complete projects
Frequency: The RC522© module operates at a
frequency of 13.56 MHz, which is common in RFID
systems and can be configured by software if
necessary.
The reading process consists of 3 steps:
• Communication initiation: When an RFID card is
brought close to the reader, the module's
antenna generates a magnetic field that powers
the card. This aspect allows RFID cards to not
require any batteries and can therefore be very
small.
• Card response: The RFID card, powered by the
magnetic field, responds by transmitting its
data to the reader. This response contains
unique information stored on the card, such as a
unique identifier (UID) and/or other data.
• Processing in the RC522© module: The RC522©
module receives the response from the card and
processes it to extract the relevant information
in a formatted manner. This information can be
used to identify the card or perform other
actions.
The module can also be used to write data to
RFID cards, this allows updating of information on the
card (unique identifier or UID, personal data, access
parameters, dates, serial numbers, medical data, etc.)
Interface with micro controllers: The RC522©
module is used in conjunction with micro controllers
such as Arduino©, Raspberry©, or ESP32© as in this
case.
For communication, the SPI (Serial Peripheral
Interface) interface is used, which facilitates both
high speed communication and control and which we have
already seen in previous exercises.
The connections between the RC522© module and
the ESP32© SPI bus are as follows:
531
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In the logic
analyzer we can see the
SPI bus signals where
initially a pulse is
made at RST, the card
is selected by making a
pulse at LOW on the
Chip Select (SDA/CS)
pin, the outgoing data
from the ESP32© is
syn ch ro ni ze d b y t he
MOSI pin and the data from the reader is received
through the MISO pin. A reference PWM signal has also
been added.
532
Electronics & MicroPython© with ESP32©: 80 complete projects
We can use multiple types of RFID cards:
keychains, credit cards, stickers, etc. All of them
are very easy to get and very cheap.
In the MicroPython© script we will use the
machine libraries to manage the SPI bus of the ESP32©,
the mfrc522 (download it from the Internet) to control
the hardware of the RC522© module and time to manage
the timing between readings.
The connections between the RC522© module and
the ESP32© micro controller, the connection pins and
t h e rdr() instance in which the previous pins are
specified are described. The read() function is
specified to read in a loop the UID of the cards that
approach the RFID sensor, it is invited to make such
an approach and the errors and termination of the loop
are controlled.
In the script the UID is read but we could write
the RFID card records. For this, there are also APP,
such as NFC Tools©, that facilitate this work directly
and wirelessly with a smartphone, being able, for
example, to write: mobile contacts, an URL, activation
of WiFi or BT, sending an email or a SMS, launch a
mobile APP, write a text, etc.
The card we have used in this exercise is the
S50© type that has 1Kb of memory, but there are cards
or even stickers on the market with less memory and
very cheap, for example the NTAG215© type with
500bytes or NTAG213© with only 144bytes that allow us,
in a simple and above all cheap way, to use our
smartphone to perform, with NFC, various functions
such as those already mentioned.
533
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
############################
# E076_RC522_READER: RFID card reader with RC522©
# INPUTS: SPI pins and approaching card
# OUTPUTS: UID card identifier
############################
from machine import Pin # SPI bus control
import mfrc522 # RC522© manager
import time # Timing management
'''
---------------------------------
CONNECTION RC522© BUS SPI ESP32©
---------------------------------
Pin RC522© ESP32©
---------------------------------
SDA/CS SS GPIO5
SCK SCK GPIO18
MOSI MOSI GPIO23
MISO MISO GPIO19
RST - GPIO21
'''
# ESP32© SPI bus pins
cs_pin = 5 # Chip Select GPIO5
sck_pin = 18 # Clock GPIO18
mosi_pin = 23 # MOSI GPIO23
miso_pin = 19 # MISO GPIO19
rst_pin = 21 # Reset GPIO21
# MFRC522© instance definition
rdr = mfrc522.MFRC522(sck_pin, mosi_pin, miso_pin,
rst_pin, cs_pin)
# RFID card reading function
def read():
card = rdr.get_card() # Read card UID
if card: # If there's card
# Convert hexadecimal UID to decimal
uid_decimal = str(int(card, 16))
print('UID: '+card+' '+uid_decimal) # UID
print('RC522© RFID card reader')
print('Approach the card...')
534
Electronics & MicroPython© with ESP32©: 80 complete projects
try:
while True:
read() # Read card
time.sleep(.5) # Wait between reading
except KeyboardInterrupt:
print('Read completed')
As we mentioned before, in mobile application
stores, both on Android© and iOS©, we can find
examples of very simple and intuitive APP (NFC Tools©,
NFC Tag Reader©, etc.), which can help us both read
such as recording all types of information fields:
contacts, URL, social networks, Wi-Fi connection,
Bluetooth, email, geolocations, application launch,
text, SMS, etc. and on all types of RFID cards using
NFC technology.
⊝⊝⊝
535
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises
•E364: RC522© and RGB LED.
Connect an RC522© type RFID reader and an RGB
LED to the SPI bus of the ESP32©, use 3 cards so that
each one of them turns on the LED of its color. If one
of the cards is approached 2 times in less than 1
second, the corresponding LED turns off.
•E365: OLED and RC522©.
Connect an RC522© and an OLED to I2C, carry out
the programming so that when we approach a card close
to the RFID reader, the updated time and the UID code
of the card are displayed on the OLED.
•E366: RC522© and email.
Use an RC522© to read RFID cards, when a card
with a pre-established UID code is read, an LED or a
relay will light up simulating the opening of a door.
All card openings or rejections will be stored in a
file that will be sent by email every hour to a pre-
established destination.
•E367: RC522© and IFTTT©.
Write a MicroPython© script for ESP32© so that
when reading an RFID card with a predefined UID code,
an IFTTT© applet is executed that turns on a lamp or
activates a WiFi switch.
•E368: RC522©, timer and access control.
Design a script to read, using a hardware timer,
an RC522© reader and send an SMS. On the mobile there
will be a URL with HTML buttons that will activate an
LED or a relay simulating the opening of a door. The
system will have an LCD1602© screen connected to the
PCF8574©, which will indicate the time at rest and
when reading the RFID card it will show “allowed” or
“denied” as appropriate.
⊝⊝⊝
536
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercises 77:
Angeek© SPI uSD memory
In this exercise we will
deal with a very interesting
module that allows us to
expand the memory of our
ESP32© to save libraries,
files, *.wav, *.mp3,
projects, etc. Specifically,
it is a microSD (uSD) card
reader module for ESP32©,
similar to the HW-125© that
connects via the SPI bus
and with the following
characteristics:
SPI Interface: Uses the
SPI to ESP32© communication. Typical SPI pins include
MOSI (Master Out Slave In), MISO (Master In Slave
Out), SCK (Serial Clock), and CS (Chip Select).
The connection scheme is as follows, which
includes the general SPI type connection and an
alternative connection depending on the availability
of free GPIO:
HW-125© ESP32© (general) ESP32© (alternative)
GND GND GND
+5V External source External source
MISO GPIO19 GPIO13
MOSI GPIO23 GPIO12
SCK GPIO18 GPIO14
CS GPIO05 GPIO27
537
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Note that this module is powered externally at
+5V as it is prepared for an Arduino© environment but
internally it has a +3.3V regulator which is the
voltage necessary for the circuitry.
There are other modules that are powered
directly at +3.3V and even at both voltages, in any
case we must take care NOT to power any GPIO of the
ESP32© with +5V signals to avoid irreversible
deterioration. In any case, the logic levels of the
SPI pins are compatible with those of the ESP32©.
Card Support: both microSD (up to 2Gb capacity)
and microSDHC (up to 32Gb capacity), which can be more
than enough for simple projects such as those
discussed in this book.
microSD Card Slot: Includes a physical slot to
insert and remove microSD cards for which we must push
the card until a slight “click” sounds. To extract the
memory we must click on it until it is released,
hearing a slight “click” again.
Adjustable Transfer Rate: Allows us to adjust
the data transfer rate using SPI settings to suit
specific application needs. The maximum speed depends
on multiple factors: uSD memory type, connection to
the SPI bus, etc. In this exercise we will use a
standard SPI bus speed of 1MHz.
Support: for the FAT32 file system, which is the
most common format for uSD cards and the existence of
multiple MicroPython© libraries compatible with the
ESP32©, in this exercise we will use the sdcard.py
library, which must be located in the memory of the
ESP32©.
In the script we specify the GPIO that are going
to be used to control the SPI bus and the Pin
libraries are imported for the control of the GPIO
connected to the Select Chip, SoftSPI for the control
of the SPI bus, uos for the control of the system
f u n c t i o n s a n d t h e sdcard.py library for card
management.
538
Electronics & MicroPython© with ESP32©: 80 complete projects
We start the variables that point to SPI bus
pins and define the name of the main directory of the
card, which in our case will be /sd.
We design several functions that we can call
according to our needs and that will be useful for
this example as well as for future ones:
• detect(), which checks if the uSD card is
inserted in the module.
• mount(), which constructs the mount point of the
logical drive and with the commented FAT32
format.
• read_dir(), which lists the (non-hidden) files
that are located in the /sd folder already
created.
• create_file(), which will create the file [file]
for writing, located in the main /sd directory.
As an example, in this case the function creates
20 lines of text separated by a line break.
• include_file(), which adds an extra line to the
end of file.
• read_file(), which reads all lines from the
specified file.
• delete(), which deletes a given file.
Finally, this example script performs several
tasks, invoking the functions described above and in a
certain logical order: it checks that the uSD card is
inserted, the corresponding drive is mounted in the
/sd directory, and all existing files in this
directory are listed, a new file called “hello.txt” is
created.
The directory is read again to verify that this
last file has been created correctly, an additional
last line is added to the end of the created file, all
the lines of this file are read to verify that it has
the 20 lines of text created by the create_file
function() plus a last line created by the
include_file() function, the file created is deleted
and the directory is read again.
All detailed functions have corresponding error
control and information text display.
539
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
In the logic analyzer we can check how the uSD
is selected when the CS signal drops from HIGH to LOW,
the read/write synchronization clock is activated in
the GPIO SCK, data and commands are written through
the GPIO MOSI (ESP32© output to the uSD) and data is
read from the uSD through the MISO GPIO (ESP32©
input).
############################
# E077_SD_MEMORY.PY: Fat32 uSD card management
# INPUTS: SPI pins and volume to be assembled
# OUTPUTS: uSD card status and directory reading
# IMPORTANT: Format the card in Fat32
############################
'''
SD READER CONNECTIONS TO ESP32© BY SPI
MODULE ESP32©(General) ESP32©(Alternative)
--------------------------------------------------
GND GND GND
+5V External supply External supply
MISO GPIO19 GPIO13
MOSI GPIO23 GPIO12
540
Electronics & MicroPython© with ESP32©: 80 complete projects
SCK GPIO18 GPIO14
CS GPIO05 GPIO27
'''
from machine import Pin, SoftSPI # SPI pins support
import uos # System functions
from sdcard import SDCard # Card management
# SPI spi initialization
spi = SoftSPI(1,sck=Pin(18),mosi=Pin(23),miso=Pin(19))
cs = Pin(5)
# Alternative: sck=14, mosi=12, miso=13, cs=27
# Main directory
dir_prin = '/sd' # Change if is necessary
# Detects presence of card inserted in the reader
def detect():
try:
sd = SDCard(spi, cs) # No error, indicate it
print('uSD card detected')
except OSError as e: # There's no card
if 'no SD card' in str(e):
print('No uSD card inserted')
else: # or another error
print('SD card mounting error: ', e)
# Mount the file system in dir_prin
def mount():
try:
print(f'Mounting uSD on: {dir_prin}...')
sd = SDCard(spi, cs) # No error, mount volume
try:
# Unmount any previous file system
uos.umount(dir_prin)
except:
pass # If it's not mounted I do nothing
# Create VfsFat object for SD card (Fat32)
vfs = uos.VfsFat(sd)
# Mount SD card file system
uos.mount(vfs, dir_prin)
print(f'Volume {dir_prin} ok mounted')
except OSError as e:
print('Error mounting uSD card:', e)
541
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Read dir_prin directory
def read_dir():
try:
# List main directory
list = uos.listdir(dir_prin)
print('Files in directory '+dir_prin+':')
for x in list:
if x[0] != '.': # Don't see hidden files
print(x.rstrip())
except OSError as e:
print('Error reading directory:', e)
# Create one file in uSD card
def create_file(f_file):
file = open(dir_prin+'/'+f_file,"w") #Rewrite
# Write 20 lines "Text: x" type
for i in range(20): # Change if is necessary
file.write('Text: %s\r\n' % i)
file.close() # Close file
# Add a line to the end of the file on the uSD
def include_file(f_file):
f_file=dir_prin + '/' + f_file
file = open(f_file, "a")
file.write('Add a line to the end \n')
file.close()
# Read content from a file
def read_file(f_file):
f_file = dir_prin + '/' + f_file
try:
with open(f_file, "r") as file:
print(f'Reading {file} in uSD card')
for line in file:
# Delete line break at the end
line = line.rstrip()
print(line)
except OSError as e:
if e.args[0] == 2: # 2=FileNotFoundError
print(f'The file {f_file} do not exists')
else:
print(f':An error has occurred {e}')
542
Electronics & MicroPython© with ESP32©: 80 complete projects
# Delete the file [dir_prin]/[file]
def delete(f_file):
try: # Volume dir_prin must be mounted
f_file = dir_prin + '/' + f_file
print(f'Deleting file {f_file}')
uos.remove(f_file)
except:
print(f”Doesn't exist {f_file}”)
# Script main body
test = 'hello.txt' # Test file
print(f'Operations in uSD with the file {test}')
detect() # Detects uSD inserted
mount() # uSD in main directory
read_dir() # Read main directory
create_file(test) # Create test file
read_dir() # Read main directory
read_file(test) # Read test file
include_file(test) # Includes a line in test
read_file(test) # And read its new content
delete(test) # Delete test
read_dir() # Read main directory
print('Unmount card')
uos.umount(dir_prin) # Unmount card
Now that we know how to manage files on a uSD
card connected by SPI to our ESP32©, we are going to
carry out a more practical exercise related to
previous exercises and that will allow us to
demonstrate that the use of a uSD connected to the
ESP32© is something very interesting.
In the exercise we record the date, current
time, internal temperature and MAC address data of the
ESP32© in a uSD file. To avoid filling the uSD card
with excess information, we will record the data every
2 seconds and with a maximum of 20 readings or until
the process is stopped by keyboard.
In the script we summarize the connections
between the memory module and the ESP32© using the SPI
bus and import the libraries already described in the
previous exercise, the ntptime library to update the
RTC and the esp32 library to obtain all the internal
data of the ESP32©.
543
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
We divide the body of the script into 3 parts:
• RTC control:
We define the RTC control variable and obtain
updated data from an Internet server, adjust the time
according to the local time and update the internal
RTC of the ESP32©. Finally, we define hour() which
returns the date and time formatted as: weekday, DD-
MM-YYYY HH:MM:SS
• uSD control by SPI:
We start the spi instance specifying the SPI bus
pins and the Chip Select pin, we define a variable for
the name of the main directory, in our case /sd.
We adapt from the previous script the functions
detect() to know if there is a uSD inserted, mount()
to mount the volume in the /sd folder (or other),
read_dir() t o v i e w t h e e x i s t i n g f i l e s i n /sd,
create_file() to create a new file, include_file() to
add lines to an existing file, and read_file() to read
all lines from an existing file. In all cases,
descriptive texts for the console and the
corresponding error controls are added.
• ESP32© internal data control:
As we saw in previous exercises, here we capture
the formatted values of the temperature converted to
degrees Celsius and the MAC of the ESP32©.
Finally, in the main loop of the script we
create a body try:...except:...finally: where there is
a loop limited to 20 interactions or by pressing
<CTRL>+<C>, in the latter case a last line is included
indicating 'End of data' and the content of the data
file is displayed on the screen.
############################
# E077_MEMORY_DATA_SD.PY: Write hour,etc. in uSD
# INPUTS: Pins, volume, hour, temperature
# OUTPUTS: Write to /sd/data.txt
# IMPORTANT: Format previously in Fat32 system
# and load sdcard.py library in ESP32© memory
############################
544
Electronics & MicroPython© with ESP32©: 80 complete projects
from machine import Pin, SoftSPI # SPI pines control
import uos # System functions
from sdcard import SDCard # uSD library controls
import machine # RTC management
import utime, time # Timing manager
import ntptime # RTC synchronization
import esp32 # ESP32© internal data
#-----------------------------
# RTC CONTROL (look to RTC script manager)
#-----------------------------
# RTC configuration
rtc = machine.RTC()
# Try to sync with an NTP server
try:
print ('Getting date/time from Internet...')
ntptime.settime() # Get data from Internet
except OSError as e:
print("Error syncing with NTP:", e)
machine.reset()
# Set the time zone (e.g. UTC+1)
adjust_zone = 1 # According to local time
# Gets the current date and time of the RTC
year, month, day, weekday, hour, minute, second,
_ = rtc.datetime()
# Set the time according to the time zone
hour += adjust_zone
# Ensures that the time is in the range of 0 to 23
hour = hour % 24
# Updates the RTC with the set time
print('Updating ESP32© time')
rtc.datetime((year, month, day, weekday, hour, minute,
second, 0))
# Translate the day of the week from number to text
week_day = ["Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", "Sunday"]
#Gets data and formats date and time
def f_hour():
# Gets current data and time from RTC
year, month, day, weekday, hour, minute, second,
_ = rtc.datetime()
545
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# Format and display date and time
formatted_datetime = "{}, {:02d}-{:02d}-{:04d}
{:02d}:{:02d}:{:02d}".format(
week_day[weekday], day, month, year, hour,
minute, second)
return formatted_datetime
#-----------------------------
# uSD CONTROL WITH SPI (look uSD with SPI script)
#-----------------------------
# SPI pins initialization
spi = SoftSPI(1,sck=Pin(18),mosi=Pin(23),miso=Pin(19))
cs = Pin(5)
# Alternative pins: sck=14, mosi=12, miso=13, cs=27
# Home directory
dir_prin = '/sd' # Change if is necessary
# Detects presence of the uSD inserted in the reader
def detect():
try:
sd = SDCard(spi, cs) # If no error, show it
print('uSD card detected')
except OSError as e: # There's no card
if 'no SD card' in str(e):
print('No uSD card inserted')
else: # Or other error
print('Error mounting SD: ', e)
# Mount the file system in dir_prin
def mount():
try:
print(f'Mounting uSD in: {dir_prin}...')
sd = SDCard(spi, cs) # If no error, mount
try:
# Unmount previous file system
uos.umount(dir_prin)
except:
pass # If it's not mounted it do nothing
# Create VfsFat object on SD card (Fat32)
vfs = uos.VfsFat(sd)
# Mount SD card file system
uos.mount(vfs, dir_prin)
print(f'Volume {dir_prin} ok mounted')
except OSError as e:
print('Error mounting uSD card:', e)
546
Electronics & MicroPython© with ESP32©: 80 complete projects
# Read dir_prin directory
def read_dir():
try:
# Main directory list
list = uos.listdir(dir_prin)
print('Files in directory '+dir_prin+':')
for x in list:
if x[0] != '.': # Don't show hide files
print(x.rstrip()) # Clear spaces
except OSError as e:
print('Error reading directory:', e)
# Create a file on the uSD for writing
def create_file(f_file):
try:
file = open(dir_prin+'/'+f_file,"w") # New
file.close() # Close file
print(f'File {f_file} created')
except:
print(f'Error creating {f_file}')
# Add a line with end mark file in the uSD
def include_file(f_file, mark):
f_file=dir_prin+'/' + f_file
file = open(f_file, "a")
file.write(mark + '\n')
file.close()
# Read the content of a file
def read_file(f_file):
f_file = dir_prin+'/'+f_file # /sd/data.txt
try:
with open(f_file, "r") as file: # Read
print(f'Reading {f_file} in the uSD')
for line in file:
line = line.rstrip() # Delete jump
print(line)
except OSError as e:
if e.args[0] == 2: # 2=FileNotFoundError
print(f'File {f_file} not exists')
else:
print(f'An error occurs: {e}')
547
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
#-----------------------------
# ESP32© INTERNAL DATA CONTROL
#-----------------------------
def data_esp32():
temperature = esp32.raw_temperature() # Fahrenheit
temperature = '{:.2f}'.format((temperature-
32)*5/9) # Temperature Celsius
# Obtains unique identifier of the ESP32© chip
chip_id_bytes = machine.unique_id()
# Convert identifier to hexadecimal string
formatted_chip_id = ':'.join('{:02X}'.format(byte)
for byte in chip_id_bytes)
return temperature, formatted_chip_id # Tuple
#-----------------------------
# SCRIPT MAIN BODY
#-----------------------------
# Record data every 2 seconds until <CTRL>+<C>
# Recording is limited to 20 lines
print('Write Day, MM-DD-AAA, TT.TTºC,
mac:xx:xx:xx:xx:xx:xx')
output = 'data.txt' # Data file
detect() # Look if uSD is inserted
mount() # Mount the volume
create_file(output) # Create /sd/data.txt
read_dir() # Files list in /sd
try:
file = open('/sd/'+output,"a") # Open for append
except:
print(f'Error opening {output}')
# Main data recording loop
print(f'Writing data in {output}')
try:
for x in range(0,20): # 20 line limited
data = data_esp32() # ESP32© data
data =f_hour()+' '+data[0]+'ºC mac:'+data[1]
print(data) # Day, hour, temperature, mac
file.write(data+'\n') # Write data and jump
time.sleep(2) # Wait 2 seconds
file.close()
except KeyboardInterrupt: # Stop with keyboard
print('Recording finished...')
548
Electronics & MicroPython© with ESP32©: 80 complete projects
file.close() # Close file
include_file(output,'End of data') # Add
read_file(output) # Read /sd/data.txt
except:
print('Card access error')
finally: # Unmount card
try:
print('Unmount card')
uos.umount(dir_prin)
except:
pass
⊝⊝⊝
549
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E369: Reading the directory on the uSD card.
Configure the SPI connection with the uSD module
in an ESP32© and based on the previous exercise,
perform the following tasks: mount the uSD card with
the volume /[my_volume], whose name must be entered by
keyboard and perform the corresponding controls, for
example:
Display the contents of the directory on the uSD
card and save it to the dir.txt file on the uSD card.
Include the option to delete a file. To do this, the
console will request whether this option should be
executed and if so, enter the name of the file to be
deleted.
Finally, unmount the uSD card and add all types
of controls that are necessary, both errors and
messages to clarify the process.
•E370: Detail of the uSD card directory.
Configure the SPI connection bus communication
with the uSD module on an ESP32©. Boot and mount the
uSD card, scan and list all files and directories in
the root directory.
Display the detailed information: size and
modification date, of each element using the
uos.ilistdir() function. Finally unmount the uSD card
when finished.
•E371: uSD card volume mounting control.
Configure the SPI connection with the uSD module
on an ESP32© and try to mount the card volume.
Create the corresponding controls that handle
possible errors, such as missing card, empty card,
wrong card, volume already mounted, incorrect volume
name, etc.
550
Electronics & MicroPython© with ESP32©: 80 complete projects
Implement an automatic retry mechanism in case
of failure and provides detailed information about the
entire process.
•E372: Creating a file on the uSD card.
Configure the SPI connection with the uSD module
on an ESP32©, mount the uSD card and verify the
existence of a specific file whose name has been
entered through the console. If the file does not
exist, create a new file with a unique name.
Add content to the file by obtaining data from
another file whose name is also selected by the
console. Include the corresponding error checks and
finally unmount the uSD card.
•E373: Add content to a file on the uSD.
Configure the SPI connection with the uSD module
on an ESP32© and enter the name of three files through
the console, two of them will be the source and the
third the destination. Mount the uSD card and verify
the existence of the source files; if the files do not
exist, repeat the data entry process.
If the source files exist, create a third file
that adds the data from the source files according to
a console-defined criteria (source1+source2,
source2+source1, source1 and source2 interleaved,
etc.).
Indicate in detail and through the console the
sizes of all the files, both source and destination
and both before and after the operations described.
Unmount the uSD when finished.
•E374: Environmental monitoring.
Implement an environmental variable monitoring
sy s t em t h at u s e s a D HT 1 1 © se n s or t o me a s u re
temperature and humidity, a DS18B20© to measure
temperature elsewhere, and an RTC to record the
updated date and time.
The data will be stored in a file on the uSD
card connected by SPI.
551
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
•E375: RFID access control.
Create an access control device that uses an
RFID card reader with a RC522© module, a rotary
decoder to capture input data, and an OLED display to
display lists of accesses or rejections made. A card
access/rejection algorithm will be created based on
your UID. The system will also have an RTC to record
possible entries.
The access information will be stored in a file
on the uSD card which will be sent to an established
email when the corresponding control is pressed.
•E376: Distance and temperature.
Develop a proximity sensor system by measuring
distance with an ultrasonic device connected to the
ESP32© and another temperature control system using
two DS18B20© sensors, connected to the same 1-Wire
line and the same GPIO, that will measure the external
and internal temperatures of the home.
The results from both systems will be saved to a
file on the uSD card along with the date and time
obtained from an RTC.
Periodically, controlled by a predefined
hardware timer, an email will be sent with the data
file and, if necessary, an SMS with an alarm situation
defined in an algorithm.
•E377: Security system.
Develop a security device that combines an
ultrasonic sensor to detect movement in a certain
area, an IRQ-controlled button to activate/deactivate
the alarms and the system, and an ADC module to
monitor ambient light levels (can be simulated, for
example, with a potentiometer).
The system will include also an algorithm that
activates several IFTTT© Webhooks to interact with
Home Automation devices and systems. The events will
be recorded in a file on the uSD card and in case of
intrusion or alarm, SMS notifications will be sent
using IFTTT© as well.
552
Electronics & MicroPython© with ESP32©: 80 complete projects
•E378: Home Automation system.
Create a Home Automation system that contains
sensors (temperature, humidity, proximity, buttons,
date, time, ADC, WiFi scanning, etc.) actuators (LED,
buzz, IFTTT©, OLED, DAC, servos, etc.).
Create a table of 2 sensor vs actuator inputs
and at the intersections describe specific Home
Automation actions to be performed. With all of the
above, create a Home Automation algorithm, this is a
list of actions (automatic or not) that will be
carried out when certain circumstances occur and
records in a file on the uSD all the events detected
and all the actions that are applied.
The file must contain a maximum of 20 lines and
the next line will be recorded over the first one
(circular process), finally it will be sent by email
by pressing a button controlled by IRQ.
⊝⊝⊝
553
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Exercise 78:
Samiore© MP3 Player
This time we will see a
module that will help us
generate audio with the ESP32©
more efficiently than with the
beeps of an active or passive
buzz or using *.wav files. We
will see the Samiore© mp3
module or equivalent, which
will allow us to play *.mp3
files from a uSD memory
inserted in the module itself.
The fundamental characteristics of this type of
modules are the following:
•Configurable supported sampling rates, between 8kHz
and 48kHz.
•24-bit DAC converter output with a signal vs noise
ratio of more than 80dB.
•uSD reader included, with full support for FAT16
and FAT32 file
systems up to 32Gb
maximum capacity.
•Various modes avail-
able for communica-
tion inputs and
outputs.
•Integrated support
for up to 100 folders
with 255 songs, 30
adjustable volume
levels and 6-level
audio equalizer, de-
pending on capacity.
554
Electronics & MicroPython© with ESP32©: 80 complete projects
The module has 16 pins for multiple functions:
power, communication, DAC outputs, speaker (maximum
3W), key control, USB connection and control signals,
but in this exercise we will only use the following:
(*) If we do not exceed
1W of power we could
use the +5V supply of
ESP32©, otherwise use
an external supply. Re-
member that the maximum
power of the mp3 module
is 3W, which at +5V
represents a maximum
output of 600mA.
In many mp3 modules
the pins are not printed, if this is the case, look at
the side notch of the module or a point near a corner
at the bottom of the module and which indicates pin 1
of the module and which is equivalent to VCC.
555
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
To control this module we need a library that
manages the hardware such as dfplayermini.py, which
has multiple functions such as the following:
play(x) Play track x.
play('next') Play the next track.
play('prev') Play the previous track.
pause() Pause playback.
resume() Resume playback after pause.
stop() Stops playback.
fadeout(x) Reduce the volume x (500ms-3,000ms).
loop_track(x) Loop track x.
loop() Loop all tracks.
loop_disable() Stops looping.
volume_up() Raise the volume up to 14 maximum.
volume_down() Lower the volume to 0 minimum.
volume(x) Apply volume x (0 to 14) to mp3.
module_sleep() Put the module into hibernation.
module_wake() Wake up from module_sleep().
module_reset() Reset the module.
The Samiore© mp3 module has an LED to indicate
that mp3 playback is in progress and the communication
protocol with the ESP32© is UART type, that is,
asynchronous serial communication with two wires TX
(transmission) and RX (reception) and that in the
dfplayermini.py library a speed of 9,600 baud is used,
which is more than enough to send the control commands
between the ESP32© and the mp3 module since the mp3
playback is controlled by the module itself with the
micro SD card.
In the script we import the dfplayermini.py and
sleep libraries to control the hardware of the mp3
module and manage the playback or waiting times. We
use the Player instance, where we define the TX and RX
pins of the UART connected to GPIO17 and GPIO16
respectively.
Finally we use some of the available functions
such as: restart module, adjust volume, play track 1,
fade out for 2 seconds, play the next track for 5
seconds, pause for 3 seconds, play everything in loop
for 20 seconds and finally stop the music.
556
Electronics & MicroPython© with ESP32©: 80 complete projects
############################
# E078_MP3.PY: Dfplayermini© mp3 player manager
# INPUTS: *.mp3 file, volume and others
# OUTPUTS: Audio from speaker
############################
from dfplayermini import Player # mp3 manager
from time import sleep # Timing control
# mp3 instance and GPIO
music = Player(pin_TX=17, pin_RX=16) # UART control
# Examples of functions
print('Module restart...')
music.module_reset()
sleep(2)
print('Adjust volume to 10') # Volume (0-14)
music.volume(10)
sleep(1)
print('Music start 1')
music.play(1)
print('Play 1 during 5 seconds...')
sleep(5)
print('Down volume with 2 seconds fading...')
music.fadeout(2000)
print('Jump to next song')
music.play('next')
print('Play 2 during 5 seconds...')
sleep(5)
print('Pause the music...')
music.pause()
print('Pause 3 seconds')
sleep(3)
print('Play all as loop...')
music.play(1)
music.loop()
print('Wait 20 seconds...')
sleep(20)
print('Music off...')
music.stop()
⊝⊝⊝
557
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E379: Mp3 with keyboard.
Add to the ESP32© an mp3 module and a keyboard
(see next exercise) so that we can play a specific
audio track, selecting its position by keyboard,
pause, resume playback, advance to the next track, go
back to the previous track, stop, etc.
•E380: Mp3 with keyboard and LCD1602©.
Add to the previous exercise an LCD1602© display
to be able to display the date and time in the first
line and captured from the RTC of the ESP32©, duly
updated and in the second line to display the title of
the track being played and an icon indicating if it is
running or stopped.
•E381: DHT11© alarm system with mp3 and keyboard.
Create a temperature and/or humidity alarm
system with a DHT11© that emits an alarm recorded in
mp3 format and stored in the player module. With the
keyboard we can view the DHT11© data, the cause of the
alarm and decide whether to cancel or maintain the
alarm situation.
•E382: Alarm system with servo and TFT.
Add a servo and a TFT display to the previous
exercise. All the details of the alarm and RTC states
will appear on the TFT display and the servo will
simulate the opening of a water sprinkler valve to
lower the temperature. A graph with the valve status
should appear on the TFT.
•E383: Alarm recording system.
Expand the previous exercise by recording all
im p o rt a n t e v en t s in a fi l e an d s en d i ng t h em
periodically by email and alarm notifications by SMS.
⊝⊝⊝
558
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 79:
4x4 Matrix Keyboard
In this exercise we will
see a simple keyboard
made up of keys distrib-
uted in 4 rows and 4
columns that we can use
as a data entry element
for our project.
There are multiple types
of k eybo ard s: m atr ix,
with serial data trans-
mission with USB, with
Bluetooth connection, me-
chanical, multimedia,
numerical, alphanumeric, mem-
brane, adhesive, of various
materials, dimensions, all
type of colors, prices, etc.
In this exercise we will
see a very simple one,
composed of a matrix of
physical buttons in a 4x4
arrangement, whose clicks we
can capture with the GPIO of
the ESP32©, checking by
polling, if there is any
intersection between the GPIO connected to the rows
and the GPIO connected to the columns.
The layout of a 4x4 keyboard is 4 rows by 4
columns and therefore a total of 16 keys which,
sometimes, can even be customized by both hardware and
software.
559
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
We will use the following connections:
We will connect on the
one hand the 4 rows to 4
GPIO and then the 4 columns
to another 4 GPIO, in this
way with 8 GPIO we will
easily manage up to 16 keys.
These keyboards, in general,
are completely passive and
do not require any power or
connections to GND, we will
only take into account
whether they are activated
at LOW or HIGH depending on
how we use it.
In the script we import Pin and sleep to control
the GPIO and the timing necessary for polling and to
avoid bounces. We define an array of rows with the
corresponding GPIO and another array of columns and
use the rows as inputs connected to an internal pull-
up of the ESP32© and also the columns but as outputs.
We create an array with the 16 keys and their
symbols that we can adjust according to our needs and
a function that scans the keyboard by polling, going
through each defined column with an output GPIO that
we will set to LOW and we will check in each row if it
is at LOW, if is the case, it indicates that the
intersection of those column and row is connected,
that is, the corresponding key has been pressed.
To avoid bounces we will adjust certain timings
that will depend greatly on the keyboard hardware,
therefore we will have to use several values until we
achieve the optimal result. Finally we present values
and we could stop the script if we press <CTRL>+<C> as
usual.
############################
# E079_KEYBOARD.PY: 4x4 matrix keyboard
# INPUTS: Key pressed
# OUTPUTS: Key displayed
############################
560
Electronics & MicroPython© with ESP32©: 80 complete projects
from machine import Pin # GPIO management
from time import sleep # Timing control
# GPIO rows, columns and parameters
rows = [Pin(pin) for pin in (22, 21, 19, 18)]
columns = [Pin(pin) for pin in (5, 17, 16, 4)]
bounce = .01 # Bounces control
# Configure rows as input with internal pull-up
for row in rows:
row.init(mode=Pin.IN, pull=Pin.PULL_UP)
# Configure rows as outputs with internal pull-up
for column in columns:
column.init(mode=Pin.OUT, pull=Pin.PULL_UP)
# Define the matrix keyboard keys
keys = [
['1', '2', '3', 'A'],
['4', '5', '6', 'B'],
['7', '8', '9', 'C'],
['*', '0', '#', 'D']]
# Scan keyboard by polling
def keyboard_scan():
for i, column in enumerate(columns):
column.value(0) # Current column LOW
for j, row in enumerate(rows):
if not row.value(): # Activated?
sleep(bounce) # Avoid bounces
if not row.value(): # Check
return keys[j][i]
column.value(1) # Current column HIGH
return None
# Main loop
print('4X4 MATRIX KEYBOARD')
print('Press a key...')
while True:
key_pressed = keyboard_scan()
if key_pressed:
print("Key pressed:", key_pressed)
sleep(.2) # Wait between presses
⊝⊝⊝
561
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Suggested exercises:
•E384: Unit converter keyboard.
Connect a 4x4 keyboard to the ESP32© and use it
as a Celsius to Fahrenheit converter. The system will
allow us to enter a whole number, accept it with the
[A] key, delete it with [B] and correct it with [C].
The data will be displayed on the first line of an
LCD1602© display and pressing [D] will calculate the
scale conversion in both directions.
•E385: Keyboard, RTC and OLED.
Use an OLED display and a matrix keyboard
connected to the ESP32© and design a small user
interface to display various information on the OLED:
date, time, internal temperature of the ESP32©, IP
address, MAC, etc.
•E386: Keyboard, TFT and Home Automation.
Expand the features of the previous exercise by
changing the OLED for a TFT so that various options
can be entered via keyboard and various variables can
be displayed on the TFT, both internal to the ESP32©
an d ex t er n al t o v a ri o us s en s or s : t h e rm i st o r,
barometer, water pressure, temperature outside
(captured from the Internet), etc. as well as the
status of several Home Automation devices managed by
IFTTT©.
•E387: Keyboard and uSD memory.
To the previous exercise, add a uSD memory that
records all the information in a file periodically and
cyclically. With the keyboard we can access various
rows and/or columns of the information recorded in the
uSD as well as be able to send it by email and
permanently delete it.
⊝⊝⊝
562
Electronics & MicroPython© with ESP32©: 80 complete projects
*Exercise 80:
MAX7219© LED Matrix Display
In this last exercise we will use a module with
4 LED matrix of 8x8 pixels, specifically the MAX7219©,
which can be easily expanded by adding more modules
and which already include all the necessary circuits.
The power supply is +3.3V or +5V but we must take into
account that the consumption of each panel of 4 8x8
matrix can reach up to 160mA and therefore we should
not power it directly with the ESP32©, we can use an
external source like the MB-102©.
The connection is made with the SPI bus taking
into account that we are only going to use the MOSI
(Master OUT) signal since it is not necessary to
receive data from the matrix through MISO. The
connections are the following:
In the script
we use the max7219
common library,
which we must also
place into the
internal memory of
the ESP32© to be
able to invoke it
from our program.
563
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The script describes the connections to be made,
imports the time and machine libraries for timing and
GPIO control and specifies the spi instance with all
the necessary parameters.
Also activate the GPIO26 as Chip Select, create
the display instance from the Matrix8x8 class, where
we can specify how many 8x8 modules we are going to
use, in our case 4, that is, a total of 4x8x8=256 LED
or pixels.
Example useful functions are also defined that
can be used to easily manage the LED matrix: write a
text, completely erase the screen, turn it on
completely, flash a text, draw a horizontal line, draw
a vertical line, draw a line anywhere address with a
specific start and end, create an empty or full
rectangle, scroll text within the 4 8x8 modules or
scroll text longer than 4 characters, view the status
of a pixel, etc.
Finally in this script we also see a series of
examples of the previous functions: blinking,
rectangle, long text scrolling, right and left
scrolling, etc.
In the logic analyzer we can see how the Chip
Select signal is activated and during the clock
pulses, in the SCK signal, the data is sent through
the DIN pin to the matrix.
564
Electronics & MicroPython© with ESP32©: 80 complete projects
############################
# E080_MATRIX.PY: Matrix 4x8x8 LED display
# INPUTS: SPI pins, text to display
# OUTPUTS: Data and display algorithms
# IMPORTANT: max7219.py library show() or mi_show()
# to reverse text orientation
# 160mA for each block of 4 8x8 LED arrays
############################
'''8X8 MATRIX CONNECTIONS 4 MODULES WITH ESP32©
------------------------------------------
MODULE ESP32© Observations
VCC NO CONECTED Use +3V MB-102© supply
GND GND and MB-102© GND
DIN GPIO25 SPI MOSI
CS GPIO26 GPIO output
CLK GPIO27 SPI CLK'''
import time # Timing control
import max7219 # 8x8xmodule matrix control
from machine import Pin, SPI # SPI and GPIO pins
565
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
# SPI bus connection definition with valid pins
spi = SPI(1, baudrate=10000000, polarity=0, phase=0,
sck=Pin(27), mosi=Pin(25))
# Additional GPIO for Chip Select control
ss = Pin(26, Pin.OUT)
# SPI bus instance, Chip Select and number of modules
display = max7219.Matrix8x8(spi, ss, 4) # 4x8x8 LED
# [text],[column 0:31],[row 0:7],[bright 0:15]
def write(text, column, row, bright):
display.brightness(bright)# Bright from 0 to 15
display.text(text,column,row,1) # 1 color ON
display.mi_show() # Use show() o mi_show()
# Clear matrix by turning off all LED
def clear():
display.fill(0)
display.show()
# Fill the matrix with all LED on
def fill():
display.fill(1)
display.show()
# Flashes [text] in [column], [row] so many [times]
def blink(text, col, row, times):
print(f'Blink {text} {times} times')
for _ in range(times):
write(text, col, row, 2)
time.sleep(.5)
clear()
time.sleep(.5)
# Draw Horizontal line from (x,y) and width
def line_H(x, y, width):
# x y w 1 x(0,31) y(0,7) w(1,32)
display.hline(x, y, width, 1)
display.show()
# Draw Vertical line from (x,y) and height
def line_V(x, y, height):
# x y h 1 x(0,31) y(0,7) h(1,8)
display.vline(x, y, height, 1)
display.show()
566
Electronics & MicroPython© with ESP32©: 80 complete projects
# Draw a line from (x1,y1) to (x2,y2)
def line(x1, y1, x2, y2):
display.line(x1,y1,x2,y2,1)
display.show()
# Draw an empty rectangle from (x,y), width and height
# x=(0,31) y=(0,7) w=(1,32) h=(1,8)
def rectangle_0(x, y, w, h):
display.rect(x,y,w,h,1)
display.show()
# Draw a filled rectangle from (x,y), width and height
# x=(0,31) y=(0,7) w=(1,32) h=(1,8)
def rectangle_1(x, y, w, h):
display.fill_rect(x,y,w,h,1)
display.show()
# Scroll (dx,dy) within the display
def move(dx,dy):
display.scroll(dx,dy)
display.show()
# Perform a long scroll [text] at a certain [speed]
def move_text(text, speed):
clear()
# Add spaces to text for scrolling
text = text + ' '*5
# Gets the length of the text
length = len(text)
# Iterates over the text
for i in range(length - 4):
# Shows an 4 character window
window = text[i:i+4]
display.text(window,0,0,1)
display.mi_show()
time.sleep(speed)
display.fill(0)
# View the state of the pixel (x,y)
def state(x,y):
es = display.pixel(x,y)
print(f'The state of ({x},{y}) is {es}')
# Main body of the script
# Perform various operations with the 8x8 matrix
567
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
print('LED 8x8 4 Module Matrix manager')
tmp = .01 # Transitions time
try:
blink('Here',0,0,3) # Blink Here 3 times
# Shift 1 pixel for all rows and columns
print('Shift 1 pixel for all rows and columns...')
for x in range(32): # Columns
if x % 2:
for y in range(7,-1,-1): # Even rows
line_H(x,y,1) # 1 pixel
time.sleep(tmp)
clear()
else: # Odd rows
for y in range(8):
line_H(x,y,1) # 1 pixel
time.sleep(tmp)
clear()
except:
print('An error occurs')
print('Visualize a rectangle...')
rectangle_0(17,0,15,8) # Draw a rectangle
write('ok',0,0,1) # Write ok
time.sleep(1)
# Scrolls long text on the screen
text = "...I'm your ESP32©, have a great day..."
for _ in range(2): # Repeat 2 times
move_text(text, .2) # Text and speed
# Move “ok” from left to right until <CTRL>+<C>
print('Move ok from left to right...')
print('Press <CTRL>+<C> to finish')
write('ok',0,0,1)
try:
while True:
for _ in range(16): # Move to right
move(1,0) # 1 pixel
time.sleep(.1)
for _ in range(16,0,-1): # Move to left
move(-1,0) # 1 pixel
time.sleep(.1)
except KeyboardInterrupt:
print('Completed...')
clear() # LED off
⊝⊝⊝
568
Electronics & MicroPython© with ESP32©: 80 complete projects
Suggested exercises:
•E388: Time in 8x8 matrix.
Update the month, day and time of the ESP32© and
present it alternately, first date in DDMM format and
then time in HHMM format in 8x8 matrix.
•E389: Temperature in 8x8 matrix.
To the previous exercise, add a DTH11© sensor
and present in a loop: HHMM, T:ttºC, H:hh% that is:
time, temperature and humidity. Flash each data 3
times before moving to the next.
•E390: LED matrix title.
Add to the previous exercise a title that
scrolls from right to left until a button managed by
IRQ is pressed. When we press it, the title disappears
and the data appears. Once presented, the title
appears again and if we do not press the button again
within the next minute, the screen turns off until we
press it again. Use a hardware timer to control the
timing.
•E391: Relay, push button and 8x8 matrix.
To the previous exercise, add a relay that is
activated or deactivated when the data and/or
temperature reach combined values predefined by the
console. Relay changes should be indicated on the 8x8
matrix for 2 seconds.
•E392: Distance in 8x8 matrix.
Connect a distance meter which will be displayed
on the LED matrix. If the distance is always the same
for 5 seconds, the array should be turned off. If a
button is pressed, the screen will display the last
distance measured for 3 seconds. Add the necessary
messages via console.
⊝⊝⊝
569
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
5.-ADDITIONAL
SOFTWARE
T
o optimize our Electronics exercises and
ensure that the MicroPython© scripts for
ESP32© work perfectly with the selected
hardware, we can rely on various additional
software and hardware applications that we will see in
this section very briefly, either because they are the
main object of this book, either because they are
intuitive enough for the reader to use them freely and
on their own without any type of obstacle. On the Web
there is also a lot of bibliography, examples and
experiences of other users that can help us with its
use without any problem.
1. IPscanner Home©: APP that can help us locate the
devices connected to our LAN/WiFi network and
particularly the ESP32©.
2. iCircuit©: APP that allows us to simulate analog
circuits and test their theoretical operation
before their physical implementation.
3. Wokwi©: Web simulator of digital devices with
micro controllers and in MicroPython©
4. KingstVIS©: APP that displays the LA1010© USB
logic analyzer to control, manage and analyze
digital signals and protocols.
5. OpenHantek©: APP that allows us to view the
Hantek6022BE© USB analog oscilloscope.
6. chatGPT©: AI that helps resolve doubts of all
kinds although its information must be verified.
⊝⊝⊝
570
Electronics & MicroPython© with ESP32©: 80 complete projects
*IPscanner Home©
This software allows scanning
a certain range of IP addresses on
the entire set of devices
connected to the network, via WiFi
or LAN, specifying both such IP,
the MAC addresses that identify
each device, as well as open
ports, services, etc.
It is very useful to quickly
detect and view which devices are
active on any of our interior networks in our home and
specifically what IP the ESP32© has.
Although the free version of IPscanner Home©
only scans a specific section of the network, we can
define several sections, so that in two or three scans
we will have a global view of our entire internal
network, both LAN and WiFi.
We can assign friendly names to each device
associated with an IP or an internal MAC address and
assign it an easy-to-view icon, so that we will have
all connected devices perfectly identified.
It also allows us to make a direct ping
connection to a specific device, scan all its open
ports, activate the device with the “Wake on LAN”
service if available, etc.
⊝⊝⊝
571
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*iCircuit©
This software allows the
simu lat ion of e lec tri cal and
electronic operation in circuits,
both digital and analog. Some of
the circuits and schematics in
this book have been previously
tested using this application.
It has an extensive database
with all types of analog and
digital devices: switches, relays,
transformers, power supplies, signal generators,
speakers, microphones, buzzers, resistors, capacitors,
coils, diodes, transistors (TTL© types, Mosfet©,
CMOS©, etc.), logic gates, counters, stable bi or
"flip-flop", digital encoders, ADC converters, etc.
Electrical parameters of all types can be
displayed: in voltmeters, ammeters, frequency meters,
or in a virtual oscilloscope (voltage, intensity,
frequency, etc.), in real time and make the necessary
changes to simulate any situation before its real
implementation.
This software is interesting for projects like
this one, where we have to define small designs and
test them, so we save a lot of time in the
implementation phase because we already know that the
circuit can work safely as expected, thus avoiding
design and operation errors and even defects in the
ESP32©.
⊝⊝⊝
572
Electronics & MicroPython© with ESP32©: 80 complete projects
*Wokwi©
Wokwi© is an extraordinary tool to simulate
projects with multiple devices: sensors, actuators,
inputs, outputs, etc. with
various micro controllers and
using MicroPython© as a
programming language among
others. The tool is completely
free and allows us to create,
save, publish, etc. both
projects created by users and using other projects
created by dozens of other users.
Wokwi© allows users to design, test and debug
their own circuits and their own code in an elegant
and intuitive graphical virtual environment and, most
importantly, before implementing them on real
hardware, increasing productivity and efficiency.
Some features of Wokwi© include:
1. Provides accurate simulation of electronic
components such as LED, sensors, servos,
displays, etc. and its connection to the ESP32©.
2. Facilitates circuit design through a drag, drop
and plug interface.
3. Compatible with the ESP32© and programming in
MicroPython© 100% compatible with Thonny©.
4. Includes libraries and tools to debug the code
and analyze the behavior of the circuit.
5. Being an online platform, it allows users to
access their projects and others from anywhere
with an internet connection.
6. It is currently completely free.
⊝⊝⊝
573
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*KingstVIS©
This application, available
for MAC© and Windows©, allows us to
control logic analyzers such as the
LA10 10©. Th is a naly zer is v ery
complete and its documentation is
very extensive, but basically it can
help us visualize digital signals
with standard protocols, being able
to easily understand its operation as
well as detect problems. Its most
important features are:
1. C h a n n e l s :
16 input channels to monitor
simultaneous digital signals and 2 fully
configurable PWM signal generators.
2. Sampling: A sampling rate of up to 100MHz,
allowing us to capture fast transitions in
signals.
3. Voltage: Wide range of input levels, from +1.2V
to +5.5V, making it compatible with our ESP32©
and all our projects.
4. USB: Connects to a computer via USB, making it
easy to use and configure.
5. Software: With various tools: capture,
visualization, protocol analysis, etc.
6. Portability: Compact, lightweight design, easy
to carry and use in different environments.
7. Price: Economical and easy to obtain and start.
The LA1010© is a very useful tool for our
projects and for users who work with digital systems
and need to analyze and debug electronic signals in a
simple and cheap way.
⊝⊝⊝
574
Electronics & MicroPython© with ESP32©: 80 complete projects
*OpenHantek©
This application is open
source software designed to work
with Hantek© analog oscilloscopes
such as the 6022BE©. The 6022BE©
can be used in both MAC© and
Windows© as a very useful and
flexible tool for the analysis and
diagnosis of analog electronic
signals. Highlights:
1. C o m p a t i b i l i t y :
Supports Hantek 6022BE©
oscilloscope and other compatible models.
2. Interface: Intuitive graphics that make it easy
to view and analyze signals.
3. Advanced: With analysis functions, FFT
transform, protocol decoding, automatic
parameter measurement, etc.
4. Customization: It is open source, it allows us
to contribute with improvements, updates and
customizations of the software.
5. Free: OpenHantek© is free and the 6022BE© is
affordably priced. The performance vs price
ratio is very good, making OpenHantek© a
superior alternative to the official Hantek©
application itself.
6. Robust: Both the software and hardware are very
robust, having been tested by multiple users
around the world.
7. Very useful: Interesting for technicians,
engineers and hobbyists who seek to maximize the
effectiveness of their projects by analyzing
their designs and solving all types of startup
problems.
⊝⊝⊝
575
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*chatGPT©
It is an Artificial Intelli-
gence environment from the company
OpenAI© that is free, modern, fun,
accessible from the Web and that
is at the time of writing this
book also integrated into the
Bing© search engine from
Microsoft©, in the Edge© browser
of the same brand and in full
technological evolution.
This tool is aimed at being a virtual “co-pilot”
that helps us in all types of tasks and with a super
simple natural language environment, although not
necessarily 100% reliable.
To use it, we only need to ask it to help us
write such a MicroPython© script, using or not using
such a library to achieve such an objective, and in
seconds it will give us a fairly efficient solution
with the corresponding explanations of its operation.
Likewise if we have a portion of MicroPython© code or
a function, etc. if we want to improve or review its
operation, we just have to copy it to chatGPT© and ask
it to review it. When doing the review, chatGPT© will
inform us if the code can run properly and will also
provide us with additional advice on how to improve
it, for example by debugging the inputs and outputs of
the function, etc.
In this sense, chatGPT© is an excellent tool to
obtain help with the programming of any type of
Electronic or Home Automation project, but the
supervision of the response is always ours.
⊝⊝⊝
576
Electronics & MicroPython© with ESP32©: 80 complete projects
6.-MORE INFORMATION
I
n my following books, also available some
of them in English, Italian and Portuguese
on the Amazon© website, a lot of detailed
information is included to develop Python©
scripts aimed at Home Automation, Electronics and
intelligent control with Google©.
“Home Automation with Raspberry©, Google© & Python©”
“Electrónica Divertida con Raspberry©” (Spanish)
“Electrónica y Domótica con Raspberry©” (Spanish)
•Voice management with gTTS©.
•Colorama© and Tkinter© for advanced graphics.
•Remote supervision with VNC©, NO-IP© and Apache©.
•Integration with IFTTT©.
•Personal messaging with Telegram© BOT©.
•Multimedia management with PLEX©.
•Creation of schemes with Eagle©.
⊝⊝⊝
577
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Python2© vs Python3©
P
Python2.x© and Python3.x© are two different
versions of the Python© programming language
and each of them has its own features and
advantages. This book is oriented to MicroPython© for
ESP32© which, as we have mentioned, is based on
Python3.x©, therefore if we are familiar with
Python2.x©, in this section I explain what fundamental
differences exist between both versions to help us
understand more and better MicroPython©.
At the time of writing this book, the most
current version of Python© is 3.9 and it introduces a
series of important differences and also other subtle
ones over the Python2.7© version, so if we want to use
3.x version, we will have to rewrite the corresponding
parts of the Python2.7© scripts.
To rewrite the code, it can be done manually or
if it is not very complex, using the 2to3 tool from
Python3.x© itself. To do this we do in a MAC/PC:
• Install 2to3 from Terminal or CMD with:
pip3 install 2to3
• Use 2to3 to convert a script.py with:
2to3 -w script.py
T h i s w a y t h e script.py will be written in
Python3.x© and once reviewed in detail, it will work
perfectly.
The most notable differences between both
versions of Python© are summarized below, which also
apply to the version of MicroPython© used with the
ESP32© and these most important differences are
additionally described in detail and with some
explanatory examples.
578
Electronics & MicroPython© with ESP32©: 80 complete projects
1. Ongoing Support: One of the key reasons to use
Python3.x© in our projects is that Python2.x©
has stopped receiving official support after
January 2020. This means that bug fixes and
updates are no longer released security updates
for Python2.x©.
2. Improvements and New Features: Python3.x©
introduced improvements and new features over
Python2.x©, making it a more modern and wise
choice.
In this sense and as described in the Copyright
of this book, the author declines any
responsibility for the availability of third-
party libraries written in Python2.x©,
Python3.x©, MicroPython© and new versions
necessary for correct operation of examples,
exercises, projects, etc. described here.
Likewise, all negative effects caused by bugs,
instabilities, etc. are declined. existing in
the different versions of Python© and
MicroPython© that are implemented at any time.
3. Future Compatibility: The Python© ecosystem has
currently adopted Python3.x© as the standard
version, the basis of MicroPython© and which is
under constant development.
By using Python3.x© in our projects, we can
ensure that they are compatible with the latest
libraries and tools.
4. Syntax: Python3.x© introduced syntax changes
that make the code clearer and more consistent.
For example, print() is now a function instead
of a statement, which means it is used always
with parentheses.
5. Handling Unicode (character encoding standard):
Python3.x© handles Unicode more efficiently and
accurately, which is essential for working with
international characters in applications.
6. Integer Division: I n P y t h o n 3 . x © , i n t e g e r
division always produces a floating point
number, which avoids common errors related to
this type of division that we have already
discussed.
579
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
7. Libraries Features: Some libraries and modules
have been updated to support Python3.x©, so code
may need to be modified to work with newer
versions.
The most important differences between the
Python2.x© version (we will call it v2) and Python3.x©
(we will call it v3) are described in detail below so
that the reader can make the necessary changes to the
Python© code if necessary and also use it in
MicroPython©.
Likewise, third-party libraries written in the
new version of Python© must be obtained or those
already written in later versions must be converted.
1.-Print syntax:
v2: Use the print syntax without parentheses.
print "Hello, World" # Declare the impression
v3: Use the print() function with parentheses, as if
it were a function and not a declaration.
print ("Hello, World") # Assign the function
2.-Division of integers:
v2: Integer division returns only the integer part
without decimals and without rounding.
A=3/2 # a=1
v3: Integer division returns a decimal number in
floating point and with the corresponding decimals.
A=3/2 # a=1.5
3.-xrange function:
v2: Use xrange() to create a range object for loops
and always occupying the same memory.
for i in xrange(5): # Gives 0,1,2,3,4
print i
v3: xrange() is removed; use range() instead.
for i in range(5): # Gives 0,1,2,3,4
print(i)
4.-raw_input() and input() functions:
v 2 : raw_input() reads a string of characters, while
input() evaluates the input as an expression.
name = raw_input("What is your name?: ")
v 3 : input() reads a string (what raw_input() did
before) and raw_input() is removed.
name = input("What is your name?: ")
580
Electronics & MicroPython© with ESP32©: 80 complete projects
5.-Management of Unicode strings:
v2: Default strings are bytes. To work with Unicode
strings, the u prefix was used
text = u"Hello, World!"
v3: Strings are Unicode type characters by default.
The u prefix is no longer necessary.
text = "Hello, World!"
6.-Exception handling:
v2: Use the except Exception, e: syntax to catch
exceptions.
try:
... # Script code
except Exception, e:
print "An exception has happens”
v3: Use the except Exception as e: syntax also to
catch exceptions.
try:
... # Script code
except Exception as e:
print("An exception has happens:", e)
7.-next() function:
v2: Use next() to get the next element in an iterator
or string.
string = iter([1, 2, 3])
data = next(string)
v3: next() is still used, but the next() function
takes an additional argument called default to provide
a default value if the iterator or string has ended.
string = iter([1, 2, 3])
data = next(string,None) # Default value None
8.-File Management:
v2: Use open() to open files in text ('r') or binary
('rb') mode.
with open('file.txt', 'r') as f:
content = f.read()
v3: The open() function supports an additional and
optional encoding parameter to specify the character
encoding when opening files in text mode.
with open('file.txt', 'r', encoding='utf-8') as f:
content = f.read()
⊝⊝⊝
581
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Interruptions Managed by the
ESP32©
We have already seen in a previous exercise how
we can manage non-periodic events in an efficient way
using the diversion of the main flow of a script when
some type of interruption or IRQ occurs with
predefined action parameters.
The ESP32© allows the management of various
types of IRQ interruptions, both generated by software
and hardware and both generated internally by the
ESP32© itself and by events external to it. We can
summarize them in the following sections:
1. Timers: 4 by hardware and several by software,
they generate periodic interruptions. They can
be configured to generate interrupts at specific
intervals or with a single event.
2. GPIO: When specific changes are detected,
rising, falling, both or logical level edges as
in pushbuttons.
3. UART: Interruptions in data reception through
the serial interface, useful for handling serial
communication asynchronously.
4. I2C: Interruptions can be generated in data
reception, detection of stop conditions in I2C
communication, etc.
5. SPI: Like I2C, we can configure interrupts for
specific events.
6. ADC: Conversion interrupts to handle events when
a read is completed.
7. Touchpad: Capacitive touch switches.
⊝⊝⊝
582
Electronics & MicroPython© with ESP32©: 80 complete projects
*IFTTT©
As we have already seen, with the appearance of
multiple Home Automation hardware manufacturers, with
diverse and incompatible parameters and protocols,
various Home Automation supplier integrators have also
appeared with the necessary
actions for various devices
of different brands and
protocols to interact.
These integrators allow us to create, in a
simple and targeted way, applets (HTML programs) that
relate certain situations in sensors, variables,
commands, etc., with specific actions, actuators,
files, software, APP, etc.
The IFTTT© (“If This,
Then That”) integrator is
described here for its
ease of use, free of
charge (in its basic
version) and endless
possibilities of action
with devices from multiple
Home Automation hardware
and software manufacturers
in a simple way easy to
create and maintain new
applets or use many of the
existing ones tested by
other users.
This integrator is
available in a Web version
and an APP version and
basically has two
versions: the free version
and the Pro version.
583
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
The free version of IFTTT© is very limited: we
can only create and save 3 own applets, we can use
others already created but it is very possible that
they do not adapt to our needs, very simple “If Then”
conditions, single level, etc. and therefore it is not
advisable for a complete or complex Home Automation
project, but it can be advisable for a simple project
or for a training process.
IFTTT© Pro allows us to freely and without
limitations create applets based on our needs and
above all because it allows us to group applets or
re l a te t he m t o m u l ti p l e d e vi c e s t o m ak e t he
intelligence of our project much more powerful.
Another advantage of IFTTT© Pro is that, in
theory, the execution of the applets is faster and on
some occasions this may be critical, for example
turning on a garage light if our presence is detected
has to be almost immediate, etc.
At the time of writing this book, the cost of
IFTTT© is acceptable for the benefits it provides. The
choice of IFTTT© free or Pro is personal, I recommend
starting with the free version, trying it, creating an
applet, etc. and move to the Pro when we see that the
free one is not enough for us.
If we do not want to use IFTTT©, we can try
other solutions such as Zapier© (which also has the
free and professional version), Microsot Flow© (it is
paid), Tasker© (a one-time payment after the trial
period), Integromat© (a free level and several plans
with different prices), Home Assistant© (free,
complete, complex but we will see a summary), etc.
Important: before choosing an integrator, ensure that
it is compatible with our Home Automation devices.
⊝⊝⊝
584
Electronics & MicroPython© with ESP32©: 80 complete projects
*Home Assistant© and ESPHome©
In this section we will see
very briefly how we can get even
more out of our ESP32© with two
very interesting applications:
Home Assistant© and ESPHome©,
where any of them would require
an entire book to describe them
in depth.
I have included these
applications here only so that
the reader has an initial vision
of the great potential that the
ESP32© has when used with
several powerful applications
like these and that they can be
used individually or together
with our micro controller
without any programming language
of any kind.
Home Assistant© is an integrator of WiFi devices
and Home Automation applications similar to IFTTT© but
open source, aimed at Home Automation full integration
and being a centralized solution that allows us to
control and monitor smart devices from a single
environment and independent of the brand and/or model
of the devices used, highlighting:
• It allows us to create complex routines that are
executed based on various factors: time of day,
location, weather conditions, etc.
• It supports a wide range of devices and
services: lights, blinds, multimedia, switches,
thermostats, cameras, voice assistants,
appliances, IFTTT© itself, etc.
585
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• It offers a single friendly Web interface where
users can configure and control all their
devices, automations, statuses, etc.
• It is a solution hosted on a local server
(computer, Raspberry©, etc.) managed by the user
in their own home, providing security and
efficiency in its management.
• The platform has a Lovelace© type user interface
that is easily configured by editing several
*.yaml files.
• It has a huge community of users and developers
who contribute, for free, with new features,
integrations and technical support through both
forums and social networks.
For its part, ESPHome© allows the integration of
our ESP32© within the Home Assistant© community as if
it were just another smart device and therefore we can
control it, from a single interface (the Home
Assistant© itself), both to it (WiFi, Bluetooth,
protocols, communications, ports, internal data, etc.)
as well as to a wide variety of devices, many of which
have already been seen in detail in the exercises.
The ESP32© is the intermediary, being able to
control sensors, devices, meters, consumption
controls, environmental meters, lighting, movement,
binary devices, touch devices, keyboards, NFC,
displays, screens, software components, actuator
sensors, both passive and intelligent, other micro
controllers, routines, etc.
We will save the configuration and we will
automatically be able to control it from the Home
Assistant© general menu. For example, we go to the
ESPHome© home page, www.esphome.io and see how to
configure an LED, a relay, a display, a servo, a DAC,
etc. (name, GPIO or connection bus), we easily and
conveniently add the code indicated in the *.yaml
file, install it on the ESP32© and we can manage it
from Home Assistant© from home or remotely.
586
Electronics & MicroPython© with ESP32©: 80 complete projects
HOME ASSISTANT© INSTALLATION
Home Assistant© is an operating system in itself
and we must install it on a server, for example on a
computer. If we want to have this Home Automation
server 100% available without having to mortgage a
computer, it is best to install it on a Raspberry©,
for example a model 4 with 2Gb is enough. For this
type of installation, let us take into account the
following aspects:
• Home Assistant© is an operating system in itself
that will reside on the Raspberry© and will be
in charge of its control, therefore we'll
dedicate it 100% to this task without other OS.
• Enter the Raspberry Pi Imager© APP and record a
uSD card from the [other specific OS] [Home
assistants] [Home Assistant] [12.3 (Rpi 4/400)]
section.
• Connect the Raspberry© via LAN to the network
and restart it with the uSD inserted. Wait about
20 minutes for the system to load.
• Go to plugins and install SSH if desired.
• Configure the WiFi and/or Wlan0 IP, preferably
assign static IP to both.
• For remote access, enter the router and do:
[nat] [add] [web server (http)] [external/
internal] [35000] [tcp/udp] [8123] [8123 [Static
IP of the Raspberry©].
With the initial installation of Home Assistant©
it will detect most of the devices and we will only
have to sort them in the [summary] tab, but we will
have to integrate some others manually. Below are some
examples without going into details, since each Home
Automation installation in each home is very
different, however on the Internet we will find all
the information necessary to integrate any type of
device from any brand into Home Assistant©.
587
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• SonOff©: enter to [plugins] [store] [3 points]
[repositories]
[http://github.com/CoolKit-Technologies
/ha-addon].
• Tad o©: [settings] [devices] [add integration]
[search Tado©].
• Plex©, eWelink©: [plugins] [plugins store] [open
web interface].
In other cases we can install the necessary
plugins through Terminal, SSH, editing the *.yaml file
with Studio Code Server©, using the HACS module,
searching for plugins in the store, adding
integrations, etc. As there are multiple options, I
recommend following tutorials available on the
Internet for each device that Home Assistant© does not
automatically integrate.
ESPHOME© INSTALLATION
We will see how ESPHome© is installed on Home
Assistant©, warning that since we will install a new
firmware on the ESP32©, we will not be able to use it
to manage our MicroPython© scripts but, as we have
seen, we will be able to manage multiple smart and
Home Automation devices without the need for any
additional programming.
We will follow the steps:
1.-Install ESPHome© in Home Assistant©:
• If the ESPHome© plugin was not already installed
in Home Assistant©, we can reinstall it with:
[tweaks] [plugins] [plugins store] [3 points]
[repositories] [install]:
https://github.com/esphome/hassio.
• Select the stable option and continue with:
[install] [enable updates] [show icon in
sidebar] [start].
588
Electronics & MicroPython© with ESP32©: 80 complete projects
2.-Access:
It can be accessed from the [open web interface]
option or from the sidebar with [ESPHome].
3.-Installation on the ESP32©:
• Installing the ESPHome© firmware will delete the
MicroPython© interpreter from the ESP32© and it
will only work with Home Assistant© and will not
be accessible with Thonny©, although on your
console we can continue to view the installation
process and the status of the ESP32©.
Before deleting the MicroPython© interpreter we
can make a backup copy of the ESP32© content
similar to the initial loading of MicroPython©
but using the [read] command instead of [write],
for example, for a MAC we could make the copy
with esptool like:
esptool.py --port /dev/tty.SLAB_USBtoUART
--baud 115200 read_flash 0x00000 0x400000
firmware_micro.bin
We will have created a copy of the ESP32©
previous content on our MAC in the file:
firmware_micro.bin.
• The first time we install the firmware, we must
connect the ESP32© to the computer via USB cable
and once the new firmware is added, we can
access it via WiFi and update it with the OTA©
(Over The Air) method.
• To install the firmware, we must activate BOOT,
to do this we disconnect the ESP32© from the USB
port, press the BOOT button on the module and,
keeping it pressed, reconnect the USB cable,
wait a few seconds and stop pressing the BOOT
button.
• Once installed, it takes a long time, we have to
be patient, enter the ESPHome© module detected
by Home Assistant© and press [adopt] and follow
the installation instructions
589
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
• Also go to Home Assistant© notifications and
install the ESPHome© Web module detected.
4.-Integration in Home Assistant©:
To integrate into Home Assistant©, both the
ESP32© itself and the devices connected to it
and already configured in the *.yaml file, go to
[settings] [devices and services] [ESPHome©] and
add the configured device: LED, sensor, button,
display, service, connection, etc.
5.-Routines and automations:
In the [settings] [automations and scenes] menu
of Home Assistant©, we can create routines that
relate sensors and/or actuators connected to our
network and/or also connected to the ESP32©, so
that we can create a Home Automation algorithm
that controls our devices automatically.
These routines work very well and are very
useful, so I recommend learning how they work in
detail.
⊝⊝⊝
590
Electronics & MicroPython© with ESP32©: 80 complete projects
* MB-102© Power Supply
To perform some of the exercises in this book,
we need to manage elements that consume more than the
intensities sup-ported
by the internal source
of the ESP32© (motors,
speakers, servos, LED
arrays, etc.) and in
these cases we must use
an external power supply
that provides sufficient
current and power.
Here an external
source has been used, of the type compatible with the
well-known MB-102© like the one attached, which can be
attached directly to the breadboard (ensure that the
+/- pins of both voltages are well inserted), which It
can be powered at 6-12V (for example, with a router
charger or similar but not a mobile charger) and has 2
outputs that can be configured, using individual
jumpers for the upper +5V and lower +3.3V rails and a
maximum current of 700mA which is sufficient for the
exercises described. It's important to know that:
• Do not power the MB-102© at more than +12V.
• Do not use a +5V mobile charger.
• The charger connector must be suitable.
• Do not share the +5V or +3.3V outputs from the
MB-102© with those of the ESP32©.
• Connect GND of the MB-102© to GND of the ESP32©.
• Co nn ec t t he up pe r a nd lo w er GN D o f t he
breadboard.
• The USB-A port on the MB-102© is output only.
⊝⊝⊝
591
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Eagle©
This application allows the design and graphic
registration of electronic
circuits and the generation of
files to manufactu re the PCB
boards of the printed circuits and
the PDF for both tasks, performing
the functions (always very tedious
and with multiple errors) of an
auto router of tracks fully
automatically, both on single and
double-sided PCB, with all types
of digital and analog devices.
The free version is limited to a certain small
volume of circuits and a certain printed board area,
but for the prototypes and exercises described here it
is very useful and enough.
It has a large community of users who contribute
to help forums, tutorials or already designed circuit
projects, which allows for a quick learning curve.
It includes a large database of all types of
components, both active and passive, analog and
digital, connectors of all types, etc.
It is very easy to use, both in virtual wiring
and in assigning labels to devices and tracks.
⊝⊝⊝
592
Electronics & MicroPython© with ESP32©: 80 complete projects
*Bibliography
Below is a summary of a series of Web pages that
have helped or served as a basis and inspiration to
build this book on “Electronics & MicroPython© with
ESP32©: 80 complete projects”.
In these pages there is not the solution to 100%
of the problems sought, perhaps the learning process
is based precisely on the path of searching for
information and on the process of trial and error
rather than on the information or solution itself.
In them there is a lot of useful information and
a lot of work by many Electronics enthusiasts,
programming in Python© in general and in MicroPython©
in particular for various micro controllers, including
the ESP32©, to whom I thank them for their gesture of
sharing with all of us their experiences, their ideas
and their efforts.
Finally, indicate that the author of this book
refuses any responsibility and guarantee derived from
the information collected on these websites, as a
consequence of the variation, errors, or total or
partial disappearance of these sources of information.
*ESP32©
https://www.espressif.com › files › documentation
https://www.amazon.es › esp32 › k=esp32
*MicroPython©
http://www.python.org
https://docs.micropython.org/en/latest/esp32
593
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Wokwi©
https://wokwi.com/projects/new/micropython-esp32
https://www.youtube.com › watch
*Thonny©
https://thonny.org
https://github.com › thonny › rele...
*KingstVIS© & LA1010©
https://www.qdkingst.com/en/download
https://www.amazon.es › innomaker-Logic-Analyzer
*Hantek©
https://ports.macports.org/port/openhantek/
https://github.com/OpenHantek/OpenHantek6022
*Home Assistant©
https://www.home-assistant.io/
https://www.home-assistant.io/integrations/tado/
*ESPHome©
https://esphome.io/
https://esphome.io/guides/physical_device_connection
*Others
https//onlinetonegenerator.com/
https://www.w3schools.com/colors/colors_rgb.asp
https://docs.sunfounder.com/projects/
https://icircuitapp.com
⊝⊝⊝
594
Electronics & MicroPython© with ESP32©: 80 complete projects
*Glossary of Terms
Term Description
1-Wire One wire communication protocol
6022BE© Hantek© USB analog oscilloscope
Actuator Machine or other device
ADC Analog to digital converter
AI Artificial intelligent
Ampere Current intensity unit measurement
Analog Continuously varying signal
Analyzer Device to display electric signals
Android© Google© mobile operative system
APP Application abbreviation
Apple© TV Apple© multimedia receiver
Development platform with open source and
Arduino©
programmable hardware in C++
Synchronization process between sender
Asynchronous
and receiver carried out in each byte
Binary Two state system
Bit Minimum unit of information
BLE Bluetooth of low energy
Industrial specification for wireless
Bluetooth©
personal networks
BMP280© Barometer with low energy chip
Boot Starting system
Bounce Random jump between HIGH and LOW
Breadboard Component testing board
Broadlink© Infrared interface brand
Digital system that transfers data
Bus
between its components
Buzz Audio generator
Byte 8 bits set
595
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Term Description
C++ Programming language derived from C
Binary signal to coordinate actions
Clock
between several circuits
CMD Windows© command execution environment
CMOS Low power chip technology
Electronic device that transforms an
Converter
analog signal vs a digital signal
CP2102© ESP32© USB interface of Silicon Labs©
DAC Digital to analog converter
Decode Translate an encrypted signal
Decoder TV signal conversor and receiver device
DEV KIT C© ESP32© with WiFi, BT, GPIO, ADC, DAC, etc
DHCP© Dynamic server configuration protocol
DHT11© Humidity and temperature sensor
Dictionary Python© structure {key:data} type
Digital Signal composed of discrete data
DNS Domain name system
Duty Cycle Useful cycle of work
Eagle© Electronic automation design software
Embedded Electronic system into bigger one
ESP32© 32bits low power micro controller
ESPHome© ESP32© into Home Assistant© integrator
Espressif© ESP32© manufacturer
ESPTool© Computer vs ESP32© command environment
Wired local area network communication
Ethernet©
standard between computers
Fast Fourier transform from time to
FFT
frequency
Firmware Micro controller internal software
Floating Real number with fractional part
Frequency Repeats per time unit
Device for connection between other
Gateway
devices or computers
Geolocate Locate a device in a map
Gigabit Ethernet© standard with 1,000Mbs
596
Electronics & MicroPython© with ESP32©: 80 complete projects
Term Description
Google Home© Google© speaker assistant
Hantek© 6022BE© oscilloscope manufacturer
Hertz Frequency unit measurement
Hexadecimal Basis 16 number
HIGH VCC or 1 or True signal
Open source Home Automation open source
Home Assistant©
integrator
HTML Web pager design language
HTTP Internet communication protocol
I2C Inter integrated circuit
I2S Inter integrated sound
iCircuit© Circuits simulation application
IFTTT© Home Automation and devices integrator
Apparent resistance of a circuit equipped
Impedance
with capacitance and/or self-induction
Indentation Tab nesting indicator
Input Entrance to a system
Integer Number with fractional part
Software that manages interactions
Integrator
between applications and devices
Interface Systems or devices connection
Interpreter Program vs machine translator
IP Internet protocol address
Software to know the IP of devices
IPScanner©
connected to a network
IRQ Script flow interruption
Java© Object-oriented programming language
KingstVIS© LA1010© logic analyzer manufacturer
LA1010© 16 channels digital logic analyzer
LAN Local area network
Library Main script additional program
List Python© structure composed of elements
LOW GND or 0 or False state
MAC Media access control address
Modulation Signal modification with another signal
597
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
Term Description
Metal oxide semiconductor field effect
MOSFET©
transistor
NFC© Near field communication
Offset Shift of a signal in time
Ohm Law Law relating I=V/R
Ohm Electric resistance unit measurement
OLED Organic LED display
OpenHantek© 6022BE© free application
Operator Symbol representing an operation
Optocoupler Light activated coupler switch
Oscilloscope Analog signals display
Output Signal produced from a system
PEP8© Python© scripts write protocol
Period Inverse of frequency
Ping Network diagnostic program
Plotter Thonny© variables graphic display
Polimeter Instrument for electrical measurements
Polling Periodic control of a process
Port Access point to an informatics service
PWM Pulse width modulation
Python© Interpreted programing language
Real Number with fractional part
Relay Electrically operated switch
RGB Red, green, blue signal or LED
RISC© Reduced instructions set code controller
Program with independent instructions,
Routine
activities, or tasks
RTC Real time clock
Sample Piece of an electrical signal
Sampling Sample capture processing
Script Programing language program
Servo Motor with feedback control
SG90© Low torque, low power servo
Shannon Theorem of the least number of samples
SOC System on chip
598
Electronics & MicroPython© with ESP32©: 80 complete projects
Term Description
Socket Communication point between two machines
Sodial© Manufacturer of the ADUM1201©
SPI Serial peripheral interface
SSID WiFi server identifier
String Characters sequence
Device that allows an electrical current
Switch
to be derived or interrupted
Transmission control protocol
TCP/UDP
/User data protocol
TCPI Information transport control information
Terminal OSX© application for command input
Thonny© MicroPython© editor and interpreter
Pre-established maximum time to execute a
Timeout
process
Timer Hardware or software time controller
Touch GPIO with tactile control
Tuple Data structure to store elements
Universal asynchronous receiver and
UART
transmitter
URL Network resources locator
Volt Electric voltage unit measurement
Watchdog Programming flow supervisor
Watt Electrical power unit measurement
Webhook Method to extend a Web service
WebREPL Interaction with MicroPython© via Web
Wokwi© Digital controllers simulator
WROOM© Espressif© ESP32© module series
XTVTX© ESP32© models manufacturer
⊝⊝⊝
599
Gregorio Chenlo Romero (gregochenlo.blogspot.com)
*Thanks
Thank you very much for purchasing and especially
for reading this book. My intention has always been to
help and share experiences with other people like you.
I hope you liked it or
that it helped you start,
modify, or complete an
Electronics, Home Automation,
ESP32©, MicroPython©, etc.
project, but above all I hope
it has helped you have a
pleasant time.
I appreciate any
suggestions you want to
comment, for this you can
indicate it on my blog:
gregochenlo.blogspot.com
If you liked this book, I appreciate the five
stars on www.amazon.com that will help me continue im-
proving my books and also help other readers find it
more easily and learn about it in more detail.
Thank you very much again.
(v2)
⊝⊝⊝
600