Part 2 - Asynchronious Web Page Updates
Part 3 - Better User Experience
Part 4 - Commands - version 0.0.8
Turning a LED on and off was the leitmotif of my first post about the XIAO ESP32C3. A connected LED was controlled with a physical push button connected to the development board, then with a button on a Web page served by the XIAO running MicroPython and finally with Bluetooth. The GitHub repository associated with that post also contained two further examples where the LED was controlled from a Web page using two different C++ libraries to run the web server. I now want to expand on this theme. In essence, the XIAO ESP32C3 will be used as the basis of a Wi-Fi switch integrated into my home automation system.
The source code for the PlatformIO projects / Arduino sketches presented in this post can be found in a GitHub Repository: sigmdel/xiao_esp32c3_wifi_switch.
Table of Content
- The Goal
- Hardware
- Hardware Abstraction
- Tiny Sketch, Big Footprint
- Magic Ingredients
- Compiling, Uploading, and Running the Firmware
- Integration with Domoticz using HTML
- Useful Domoticz Additions
- Timed Light
- Auxiliary Light (updated 2023-06-24)
- Night Light
- Perceived Humidity
- Thoughts on the XIAO Starter Kit
- Alternate Hardware
- Critique and Future Developments
The Goal
The XIAO ESP32C3 Wi-Fi switch should resemble the venerable Sonoff Basic except that it has built-in sensors: one that measures temperature and humidity and another that measures the ambient brightness or light level. The Wi-Fi switch will run a Web server, specifically ESPAsyncWebServer
in order to handle more than one connection, and it will be integrated in the Domoticz home automation system. The diagram below gives an overview of those components.
Any resemblance between the
Kitchen Light
web interface and the (edited) Tasmota web interface next to it is not accidental. The objective is to try to reproduce the basic functionality of Theo Arends' powerful software which is used in many devices on our home automation system. That is an ambitious goal and if it's reached at all it will be with the culmination of many more posts. The immediate goal in this first step is getting together something that works acceptably.
It's not just a matter of toggling a LED on and off with a button on a Web page; numerous examples can be found on the Web. Whenever the state of the light is changed locally with the button, the light's status has to be updated on the Web page displayed by all clients connected to the Web server and in the home automation system. Similarly, if the toggle button on a client's Web page is clicked, then the hardware controlling the light must be activated accordingly and the light's status must be updated in the home automation system and on all connected clients' Web page simultaneously. Likewise, if the virtual light switch in the home automation system is toggled on or off, the actual relay on the Wi-Fi switch must be updated and the new status of the light must be shown on all connected clients' Web pages.
Hardware
Here is a description of the hardware used in this project.
- Obviously, to run
ESPAsyncWebServer
, an Espressif ESP microcontroller based development board must be used. I chose the XIAO ESP32C3 which uses the RISC-V single-core ESP32-C3 for the simple reason that I have been investigating it lately. - An LED with a current limiting resistor. Most dev boards have a built-in LED, the XIAO ESP32C does not. The LED represents the relay that in turns controls a light bulb.
- A normally open push-button switch. Again many dev boards have a built-in user push-button switch. The XIAO ESP32C3 does have one, (the tiny tactile button labelled B) but I did not use it.
- A temperature and humidity sensor. This could be a DHT11, DHT22, or DHT20. Actually just about any sensor could be used as long as there is an ESP32 library for it. In a pinch, the sensor can be simulated, meteorological measurements is not the point of this post.
- A light level sensor. Since there is no requirement for precise measurement of the ambient light level, a light dependant resistor (LDR) could be used. Again, it is possible to simulate this sensor during the development of the software.
In my first build of the project, I used discrete components which happened to be on hand. Then I remembered that Seeed Studio had kindly sent me a XIAO Starter Kit which contains an XIAO Expansion Base which is a carrier board for XIAO form factor developments boards. The Kit contained everything needed for this project and more. Given how easy it was to connect everything together, I decided to use the Kit build in this initial description of the hardware used. A description of the initial build with discrete components is relegated to the penultimate section of this post.
The base has an on-board user push button switch connected to the
D1
pin of the XIAO dev board. Among many other things, the Starter kit included other devices that made it simple to build the hardware for the project.
- A Grove - Temperature&Humidity Sensor(DHT20). Because it uses an I²C interface, it can be plugged into either of the I²C Grove connectors on the base.
- A Grove - Light Sensor v1.2 (LS06-S Photodiode). This is an analog sensor which emits an output voltage that increases from 0 to Vcc as the brightness of the ambient light increases. It can be connected to the A0-D0 Grove connector.
- A Grove - LED Pack on-board potentiometer with red, green, blue, and white LEDs also known as the LED Socket kit. There's a small two-pin female header on board into which a LED can be plugged in (anode + or long lead into the female header labelled +). A potentiometer sets the LED brightness. Start with the pot turned fully counterclockwise for the brightest light. A slight clockwise rotation will quickly diminish the visibility so the LED is not visible over most of the range of the potentiometer. As the name suggests, the signal at the Grove connector on the small board should be wired to the D0 pin of the XIAO ESP32C3 through the A0-D0 Grove connector on the XIAO base. However that connector is used for the light sensor, so the LED was connected to I/O pin D10 of the XIAO ESP32C3.
- Numerous (7) Grove cables to connect these accessories to the base. Three were used here. Two cables were used with connectors A0-D0 and I²C on the base. The third Grove cable was sacrificed replacing the connector at one end with four male Dupont connectors to make connections with the headers parallel to the XIAO on the expansion base.
- A USB cable with a Type C connector at one end to connect to the XIAO and the appropriate connector at the other end to connect to the desktop or portable computer. The kit comes with a very short 20 cm (8") Type A to Type C cable which is just fine for my situation. I encountered a problem that was related to the USB connection. At first it seemed as if I had somehow destroyed the Wi-Fi capabilities of the XIAO ESP32C3 when soldering the header pins in order to connect it to the base. It was possible to download sketches to the XIAO, the latter could read the sensor, button, and flash the LED, but it would refuse to connect to the Wi-Fi network. It turns out that the Type C connector was very tight and I had not pushed it in far enough. This was probably an isolated case related to the connector on the XIAO itself, because I was able to use the USB cable from the kit with other XIAO's without any problem and the XIAO with soldered headers would not connect to the Wi-Fi network with a cable from another manufacturer. Again I was probably not pushing its Type C connector hard enough into the XIAO USB-C connector.
Hardware Abstraction
Whenever the Web interface is updated, the data displayed is obtained from four String
s in main.cpp
.
Of course, the values of these strings depend on the sensor values. I decided to create a hardware abstraction layer to take care of the details of reading the sensor values and updating the corresponding strings. This is the pertinent part of the hardware header file (hardware.h
).
That is pretty sparse and hopefully clear. The initHardware()
function, called once in the setup()
routine, does what one expects given its name. It initializes the two sensors and puts the LED in an initial off state. The checkHardware()
function should be called in each iteration of the main loop()
routine of the sketch. Every time called, it checks for a button press, and if one has occurred, it calls on toggleLed()
to swith the state of the LED from on to off or vice versa. The toggleLed()
function also updates the ledStatus String
in main.cpp
. The toggleLed()
function is also used by the web server in an HTTP request that signals that the button has been clicked.
The details of the handling the hardware are hidden in the hardware.cpp
implementation file. Here is how the LED (representing a relay) is handled.
The function setLed()
does most of the work, and there's not much to it! It sets the digital I/O pin connected to the LED to 1 (HIGH
) or 0 (LOW
) according to its value
parameter and then it updates the ledStatus
string in main.cpp
. Toggling the LED, is just a matter of reading the state of the I/O pin connected to the LED and then inverting its value. This is done in response to a user action, so there is no timing consideration involved. Initializing the LED, is just setting the mode of the I/O pin connected to the LED to OUTPUT
and ensuring that the LED is off.
The push button is simpler to handle because the mdSimpleButton
library is used. Initialization is implicit; it is done when the button
instance of the mdSimpleButton
class is created.
The state of the button is checked at every iteration of the loop()
function, in order to have a best response rate as close to a wall light switch as possible.
An appropriate library takes care of the details of reading the temperature and humidity sensor. There is no need to read the sensor continuously, or at least I can't conceive of a reason to do that. Consequently, the reading is only done at specified intervals. The minimum time in milliseconds between readings of the sensor is defined in the SENSOR_DELAY
macro defined in hardware.h
.
That initSensor()
function is a terrible bit of programming. What happens if the DHT20 is defective? The program could be cought in an endless while
loop with no warning at all. The actual code does have logging functions and an error message will be printed to the serial monitor. That's not ideal, but it is better than nothing. Note that the DFRobot_DHT20::getTempAndHumidity
method is not part of the original library.
Tiny Sketch, Big Footprint
While the main program is a mere 47 lines of actual code, yet compiled it does take a considerable portion of the Flash memory.
The result is slightly different if compiled with the Arduino IDE.
Lest one thinks something is wrong, the XIAO ESP32C3 does make use of its 4 MB of Flash memory. The latter is partitioned into three areas.
Region | Size (MB) |
---|---|
Program storage area | 1.2 |
Over-the-air buffer | 1.2 |
File system (spiffs) | 1.5 |
total: | 3.9 |
So there is still plenty of room for a bigger program while ensuring easy over the air updates, but the program is nevertheless very big compared with what Tasmota achieves with the typical 1 MB of Flash memory as found on the Sonoff Basic switch.
Let's look at the source, which as typical starts with the list of libraries that are explicitly included.
Of course, some of these libraries include many more libraries. The next block of importance in the code has already been seen. It contains the String
declarations for the data that will be displayed in the Web interface.
Default values are specified just to ensure that something reasonable is displayed until the actual status of the sensors has been updated. Then the string substitution function is defined. Its role is central to the functioning of this program.
The HTML code served to the client contains placeholders which will be replaced by this function just as the HTML page is sent out to a client. Placeholders are "%" quoted strings. For example, the temperature read from the sensor is displayed in a cell of a table in the HTML code send to the client. The placeholder %TEMPERATURE% indicates where the numerical value is to be inserted.
When the Web server is sending out the HTML file which contains that segment shown below, it recognizes the placeholder (because of the "%" quotes) and consequently it calls on the processor
function with var
= TEMPERATURE. The function will then return the Temperature String
to the Web server that will substitute the content of Temperature
, as last updated by the hardware, into the file being served to a client. This is called template processing.
In the next block of code, an instance of the web server is created, and then the setup()
function initializes the Serial
peripheral and the hardware. It then goes on to initialize the Wi-Fi radio and connects to the Wi-Fi network, because no Wi-Fi connection means no web server.
The end of the setup()
function is all about configuring the web server and that amounts to defining three type of HTTP request handlers.
URI | Response |
---|---|
http://<IP>/ or http://<IP> | Sends to the client the HTML page html_index defined in html.h |
http://<IP>/led | Toggles the state of the LED (using toggleLED which updates ledStatus ) Sends to the client the HTML page html_index defined in html.h |
Anything else | Sends to the client the error HTML page html_404 defined in html.h |
The rest of the program is the repeating loop()
which does nothing but check on the hardware to update the sensor data and the state of the LED.
The async web server is running in the background, handling client requests as they come in and does not need to be "pumped" in the loop()
function.
Magic Ingredients
This is the char
constant that holds the HTML that will be sent by the web server in response to a known request.
Don't forget that placeholders will be replaced as the HTML code is served. So this is the HTML code received by a web client.
There is not much to it. A few CSS styles to make the display pretty, a header, a table with three rows showing the current sensor values, a large ON
or OFF
showing the status of the LED and a form button labelled . Finally, there is an information line that will help distinguish various versions of this firmware. In the <head>
section there are three meta
tags that are of some importance.
- <meta name="viewport" content="width=device-width, initial-scale=1">
Boilerplate code which improve layout on smart phones and tablets.
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
Displays non US-ASCII characters. There is one in the text: "°".
- <meta http-equiv="refresh" content="5;url=/">
This is the magic sauce. It instructs the web client to request a new copy of the HTML page every five seconds.
Given the AsyncWebServer template substitution mechanism discussed above, this ensures that the page displayed by a web client is never more than 5 seconds out of date. It does not matter how many clients are connected to the server, each will request the page every five seconds on its own.
Compiling, Uploading, and Running the Firmware
Once the source code for this project has been downloaded and installed on a desktop machine or portable computer, the following directory structure will be in place. It is designed to make it possible to use either the Arduino IDE or the PlatformIO IDE.
DFRobot_DHT20
│ │ └── README
│ ├── platformio.ini
│ └── simple_wifi_switch
│ ├── hardware.cpp
│ ├── hardware.h
│ ├── html.h
│ ├── main.cpp
│ ├── secrets.h.template
│ └── simple_wifi_switch.ino
├── 02_basic_wifi_switch
│
...
│
└── libraries
├── AsyncTCP
├── DFRobot_DHT20
├── ESPAsyncWebServer
├── mdSimpleButton
└── SimpleDHT
The project as described above is in the first subdirectory 01_simplified_hdw_version
, but to make it as self-contained as possible third-party libraries are also included in the libraries
directory. Actually, a couple of libraries found there are modified versions of the currently available repository.
-
DFRobot_DHT20
: I added a type and a function,TempAndHumidty_t DFRobot_DHT20::getTempAndHumidity()
, to the library to access the DHT20 only once when getting the temperature and humidity readings instead of twice if one were to use the separate functions found in the library. -
ESPAsyncWebServer
: I could not find a version of this library compatible with the ESP32-C3 variant of the Arduino-ESP32 core. The PlatformIO library manager adds the version 1.2.3 of theESP Async WebSever
library by Hristo Gochckov found here https://github.com/me-no-dev/ESPAsyncWebServer which is already 4 years old. It is incompatible with the latest version ofmd5.h
in the Arduino-ESP32 core. The development branch of the library does contain a solution but there remains a version issue which prevents the PlatformIO library manager from getting the newer version. The Arduino library manager installs a fork by dam74 (dvarrel) found here https://github.com/dvarrel/ESPAsyncWebSrv which is currently at version 1.2.6 which does get around the problem with the upstream repository. Unfortunately, the sketch will not compile with either of these newest versions. The issue and the solution have been identified ( Compile error for ESP32 C3- based boards #1164), but the change has not been committed in the repositories. So the solution adopted here is to include a corrected version of the latestme-no-dev
code in thelibraries
directory alongside other libraries that were either modified or not readily available using the library manager of either the Arduino IDE or PlatformIO.
Looking at the source code in simple_wifi_switch
, three have already been covered main.cpp
, hardware.h
and html.h
. The WiFi
library handles connection with a Wi-Fi network but it needs the network's credentials which it expects to find in a file named secrets.h
file not included in the source code. Instead there is a file named secrets.h.template
.
As instructed edit the file to provide the Wi-Fi network credentials and save it as secrets.h
.
The sketch, simple_wifi_switch.ino
, is almost nothing but comments. It is there because the Arduino IDE expects a sketch file with the same name as the directory in which it is situated. The Arduino IDE treats .ino
files differently from other source files, in particular, by generating a temporary header file. PlatformIO can work with .ino
files so presumably, the content of main.cpp
could be in simple_wifi_switch.ino
and both IDE would be able to compile the source. I prefer avoiding this non standard behaviour which I find confusing given my limited experience with C/C++.
Instead of using Serial.print()
statements throughout the code, I decided to use the logging facility built into Arduino-ESP32. Not only does that look very professional, but it has the advantage of removing all the associated code from the binary when the log level is set to 0 or none at a later date, without having to change the source code at all. You might want to look at the debug code in DFRobot_DHT20
which achieves the same thing in a universal fashion meaning it does not depend on a particular platform.
In the PlatformIO IDE
To open the project in PlatformIO, go to the PIO home page, click on the Open Project button and then browse to the 01_simplified_hdw_version
directory that contains the platformio.ini
configuration file for the project and then click on the button to load the project.
There are a few things that are unusual about the content of the platformio.ini
configuration shown above.
- src_dir = simple_wifi_switch
Normally, the
main.cpp
file of a PlatformIO project is in thesrc
dirctory alongside theplatformio.ini
configuration file for the project. But that clashes with the Arduino contraint that the sketch must be in a directory with the same name as itself except for the.ino
extension. The way around this is to explictly change the source directory in PlatformIO as done here with thesrc_dir
directive. - lib_dir = ../libraries
Similarly, PlatformIO expects to find private libaries is a specific location, but it is incompatible with Arduino. The solution is to explictly change the local libraries directory in PlatformIO as done here with the
lib_dir
directive. - monitor_speed = 460800
The baud of the serial port does not matter, because a USB CDC interface is used. I just set a very high speed here to no hobble the terminal emulator used by PlatformIO.
Debug Levels Value Meaning 0 None 1 Error 2 Warn 3 Info 4 Debug 5 Verbose
That build flag and an#include #include "esp32-hal-log.h"
in a module is all that is needed to enable logging in that module. Here the log level has been set toVerbose
. Not only will the log messages from the sketch be displayed, but log messages from other libraries will also be displayed.
Make sure that a recent version of the Espressif 32 platform is used. It should be version 6.1.0 or newer, otherwise PlatformIO may have problems finding the XIAO ESP32C3 when uploading the firmware.
The project is compiled with the PlatformIO: Build
command and then uploaded to the microcontroller with the PlatformIO: Upload
command. Once the upload is completed successfully, the ESP32-C3 will start executing the new firmware. The serial output can be seen in a terminal. PlatformIO: Serial Monitor
will start a terminal session and connect to the XIAO. Most times when the upload fails, it's because I have a serial monitor opened and connected with the XIAO. I just need to close all the open sessions and try the upload again.
There are many ways to execute PIO commands, the easiest is to click on the appropriate button in the button bar found at the bottom of the window.
In the Arduino IDE
As far as I know, the Arduino IDE does not support configuration files for individual sketches. The setup is a manual thing. Installing the Espressinf Arduino-ESP32 platform is a two-step procedure.
- Add the URL of the JSON index file of the
arduino-esp32
package,https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json
in theAdditional boards manager URLs:
list in theSettings
tab of the applicationPreferences
found in theFile
menu. It is simpler than described as the image below attests.
Details can be found under Software setup in the Getting Started Seeed Wiki. Make sure that
esp32
by Espressif is installed. This is done with the BOARDS MANAGER which is found under theTools
/Board:...
/Boards manager...
menu. There is an icon that can be clicked instead of going through the menu, it is the one highlighted in the column of icons on the left of the IDE window on the screen capture to the right.
There should be no need to repeat these steps, it is a one-time operation. The IDE needs to be told about the location of the sketch otherwise it will not find the libraries
directory. This is done in the Preferences
. Click on the button and navigate to the code_switch
directory so as to fill in the Sketchbook location:
field as shown in the image below.
It would be good to note the previous value of the field in order to restore it when no longer working with this xiao_esp32c3_project2.
There are two configuration steps that are required for each sketch as it is loaded into the IDE.
- Select the board in the
Tools
/Board
menu. Be patient, there are many, many ESP32 based boards and they do not seem to be arranged in any meaningful way. - Select the serial port of the board in the
Tools
/Port:
menu. Of course the actual selected port can differ from what is shown on the right. Just pick the correct port from the list of all found ports displayed when the menu item is clicked.
Finally, the debug level for the logging done by the sketch is set in the simple_wifi_switch.ino
file.
Debug Levels | |
---|---|
Value | Meaning |
0 | None |
1 | Error |
2 | Warn |
3 | Info |
4 | Debug |
5 | Verbose |
Just set the CORE_DEBUG_VALUE
to the desired level. Once all the configuration is completed, compiling the code can be done by clicking on the check mark icon on the top tool bar. Uploading to the XIAO is done by clicking on the right arrow icon in the tool bar. Note that uploading will compile the code if need be. Starting the serial monitor once the code has been uploaded is done by clicking on the looking-glass icon at the right edge of the tool bar. There are keyboard short cuts and menu items to do these operations.
Execution of the Firmware
It is easier to capture the start of the program when the XIAO is reset in PlatformIO because it's serial monitor reconnects automatically. Each time the XIAO is reset, the serial connection is broken and the Arduino IDE disconnects the serial monitor and clears the Port
setting. Before restarting the serial monitor the port has to be reset manually in the Tools
manu and I am not nimble enough to do this in time to see the logging message unless a long delay were to be introduced after the Serial.begin()
in the setup()
function.
Here is a the logging output in from the firmware in the PlatformIO serial terminal along with comments in italic. First let's look at the start-up events.
Now let's look at the use of the physical push-button and the Web interface to control the LED/relay.
At this point we are two thirds of the way through the first version of this project; adding the XIAO ESP32C3 Wi-Fi switch into the home automation system remains to be done.
Integration with Domoticz using HTML
It seems as if everyone is talking about Home Assistant. In just February and March there have been two articles on the Seeed Studio Wiki about using the XIAO ESP32C3 with HA: XIAO ESP32C3 accesses Home Assistant via ESPHome service and Connect Grove Modules to Home Assistant using ESPHome. However, there are other open source home automation systems, among them Domoticz which has been our home automation server for a number of years. I am not trying to convert anyone, but I do think Domoticz has some very good qualities. Among them, there is the simple installation and the very low system requirements. Initially, the system was built around Domoticz running on a single core Raspberry Pi Model B+ v1.2 and the performance was more than adequate. I even prepared a small system on a Raspberry Pi model B (Rev 2.0 211.12) with only 512 MB of memory for my sister. The Web interface might have been slow, but Domoticz itself had no problem controlling IoT devices quickly enough to be transparent.
Below I am assuming that Domoticz is already installed on a computer connected to the local area network. The Domoticz Wiki Main Page has links to installation instructions on the "big three" operating systems and specifif instructions for Raspberry Pis, Docker and a couple of NAS appliances. In Linux and any Raspberry Pi model the procedure is very simple. Enter a one line command at the prompt,
The only thing that is a prerequisite for this post is that the "Dummy" hardware interface be installed.

- Open the Domoticz Web interface in a Web browser. The address is
http://
where IP is the network address of the computer hosting Domoticz and 8080 is the default TCP port used by Domoticz. If you selected something else during the installation process, adjust accordingly. If you prefer using a secure protocol use the:8080 https://
address if the 443 default port was accepted, otherwise add the port after the IP address separated with a colon. A self-signed certificate is included in the Domoticz installation so it will probably be necessary to confirm that an exception can be made to access the site. - Starting with the newly released 2023.1 stable version of Domoticz, a username (admin) and password (domoticz) will have to be given.
- Click on the Setup button and then the Hardware button. In the drop-down list select
Dummy (Does Nothing...
type and above give the hardware a name. I choseVirtual
. Click on the button.

Let's try to add the XIAO ESP32C3 Wi-Fi switch with its sensors into the Domoticz environment with the least number of changes to the code presented above. Frankly, there is not much to do. The first step is to create three virtual devices in Domoticz: a virtual switch, a virtual lux meter and a virtual temperature + humidity sensor. To add these virtual devices go back to the HARDWARE
page.
Click on the
button. This brings up the following dialog box. Select the sensor type, in the first case, Switch
and enter a name for the switch. Do the same for the other two devices choosing the correct device type. These three devices will have consecutive identity numbers (idx) if constructed one after the other. In my case they were assigned id numbers 204, 205 and 206.
We are not truly measuring lux and there is a percent % virtual sensor, but I prefer the lux meter icon. Strictly speaking it does not really matter and in daily use the lux meter will be hidden (just by starting the name with a "$") or even replaced with a user variable. In the beginning, though, it will be useful to see the ersatz lux values in order to calibrate the sensor and determine which value represents dusk.
The values displayed on the virtual devices in the Web interface of the home automation system will be done with HTML requests. These must be JSON formatted HTTP requests as explained in the Wiki on Domoticz API/JSON URL's. It is not a bad idea to test with curl
to verify that the HTTP request sent to the Domoticz web server does what is desired.
The first two requests will respectively set the virtual light switch state to on and off only. Domoticz will not take any action except for changing the displayed state of the virtual switch on the Web interface. While POST request can be used as seen in the second example above, GET requests will be used as they are simpler to make. The third request shows the typical respons when something is wrong with the query, such as an invalid index number.
As in the case of the hardware, I created a simple domoticz
library to take care of updating the sensors in the home automation web interface. Here is the header file with the three functions to update each type of sensor.
The state
parameter will be discussed later under the heading Perceived Humidity.. As it is, the default 0 value will display "normal" in the Web interface no matter the relative humidity. The implementation found in domoticz.cpp
is straight forward. The logic is simple, the function handling each type of sensor builds up a URL with the appropriate JSON formatted query. The function sendHttpRequest()
takes care of sending that URL and logging the result obtained from the Domoticz web server.
A little bit of care is needed when handling the response from the Domoticz web server. As seen with the curl
commands, even when Domoticz receives an incorrectly formatted query, it will respond with a JSON formatted response with a return code of 200 (OK). Consequently the JSON response must be scanned to ensure that "status" : "OK"
entry is present. The identity numbers of the virtual sensors and the IP address of the Domoticz web server are defined in the header file domoticz_data.h
which is not included in the source code of this project. As with the Wi-Fi credential, there is a template file, domoticz_data.h.template
instead.
Edit this file entering the correct values and save as domoticz_data.h
in the same directory as the template and main.cpp
.
It is rather obvious when the virtual sensors should be updated by the XIAO ESP32C3 firmware. It has to be done in the hardware handlers. So there are four extra lines in harware.cpp
.
#include "domoticz.h"
is added at the top of the file.updateDomoticzSwitch(SWITCH_IDX, value);
is added at the end of thesetLed(int value)
function.updateDomoticzTemperatureHumiditySensor(TEMP_HUMI_IDX, tah.temperature, 100*tah.humidity);
is added at the end of thereadTemp()
function.updateDomoticzLightSensor(LUX_IDX, value);
is added at the end of thereadLight()
function.
So now the Domoticz web interface will be updated each time the LED state is changed or a sensor is read. Indeed, the update on the Domoticz Web interface could occur more quickly than on a client Web browser connected to the XIAO ESP32C3 given that may require up to 5 seconds before that update is displayed. At this point clicking on the virtual switch in the Domoticz Web interface will cause its value to toggle between on and off, but nothing will happen on the XIAO side. Adding actions to be performed when the state of the virtual switch is changed within Domoticz will help solve this problem.
Click on the button of the virtual switch in teh
Switches
tab. Then add two URLs in the On Action
and Off Action
fields as shown below. Do not forget to click on the button otherwise the changes to fields will not be kept.
To complete this step, the XIAO Web server must include an additional two handlers for the requests that Domoticz may send to it. This is done in the setup()
function in main.cpp
.
Domoticz does not expect much of a response when it executes the On Action
or Off Action
. However to avoid polluting the Domoticz log, it is necessary to send a non-empty response with a 200 OK
HTTP code. The mime type of the response probably does not matter much, but the text cannot be empty, so I chose to send back to the Domoticz server the same message as is logged to the serial monitor. A 202 Accepted
code also satisfies Domoticz as long as the response is not empty.
Useful Domoticz Additions
Home automation devices become "intelligent" when decisions are made by the system independent of human intervention. Let's look at a few examples of things that can be done with our simple Wi-Fi device.
Timed Light
With help from Domoticz, the Wi-Fi light can become a timed light. Click on the button of the XIAO ESP32C3 (virtual) Switch. Then put the desired number of seconds during which time the switch will remain on in the Off Delay
field. Do not forget to click on the button before pressing the button.
I have done this for a light in a staircase and something similar for a light in a wardrobe. In both cases the delay was longer than what is shown above but, unless one is really patient, 30 seconds seems like an eternity when testing.
Auxiliary Light (updated 2023-06-24)
Let's say that the XIAO ESP32C3 is controlling the light in a hallway that goes to a wardrobe which is also illuminated with a Wi-Fi controlled light. If the hallway has a window, then during the day it would be better to be able to turn the wardrobe light on without lighting up the hallway. However when the hallway light needs to be on, so will the wardrobe light. Instead of putting a timer on the latter, it makes more sense to let the hallway light control it. After two previous attempts, here is the actual dzVents script that I use.
This method works no matter how the state of the Domoticz virtual switch is updated and no matter the value of the Prevent loop
setting in the MQTT Hardware definition in Domoticz. Save the script in the domoticz/scripts/dzVents/script
directory and ensure that its file name end with the .lua
extension.
This can be done with a Domoticz group.
On the left we see the two virtual sensors which will be linked with a group. Start by creating a group. To do that click on the Domoticz web interface and then name the group. Here it is named AuxSwitch
. Set the type to Group
in the drop-down type list and then click on the Add Scene button.
Once the group shows up in the Activation Devices
list. Add the XIAO ESP32C3 Switch
to the list by selecting it in the Device:
drop-down list and then clicking on the Add Device button.
Add the Wardrobe virtual sensor to the list of Devices
in the group. Do this by selecting the sensor in the Device:
drop-down list and then clicking on the button. All that remains to be done is to save the group. I would suggest adding a "$" in front of the name of the group so that it becomes hidden in the Domoticz interface (it is still displayed in the list of devices) and adding a description of the group's purpose. It is easy to forget in a few months time why a hidden group or device is there. Don't forget to click on the button.
WARNING: This only works if HTTP request sent to Domoticz with changes to the status of the relay is
http://<DTZ_IP>:<DTZ_PORT>/json?type=command¶m=switchlight&idx=<SWITCH_IDX>&switchcmd=<ACTION>
where
<DTZ_IP>
= IP address of the Domoticz server<DTZ_PORT>
= TCP port use by teh Domoticz server<SWITCH_IDX>
= Domoticz index (idx) number of the activation device, theXIAO ESP32C3 Switch
<ACTION>
= On or Off
Prevent Loop:
to False
in the Domoticz MQTT Hardware.
More details here.
slave device
facility would accomplish the desired task. I was wrong. Test of what I had proposed seemed to work, but I had forgotten to remove a Lua script which had taken care of one-way synchronization of the switches. I wanted to offer a solution that did not require a script and managed to fall into a trap, not for the first time I might add! Let me quote a short 2017 Domoticz forum reply by emontnemery on this topic.
[The p]urpose of [a] slave switch is to update [the] state of [a] main switch without sending a command to the [main] switch. This is useful for simple RF switches (meaning they don't report [their] state) with several remotes: If you press one of the remotes, [the state of the Domoticz virtual sensor will be] updated to try to be in sync with the real switch.Then emontnemery goes on to provide the solution.
To do what you want to do you can instead create a group with only the [wardrobe], then you add [XIAO ESP32C3 Switch] as [the] "activation device" for the group.With thanks, that is so much more succinct than my long-winded explanation.
This is not a theoretical discussion. Our house has outside lights controlled with a wall switch beside the front entrance and the attached garage has outside lights controlled by a switch in the garage. When it was time to illuminate the way for guests returning to their car in the driveway in the dark, I had to run to the garage to turn its outside lights and then come back to the front door to turn on the house outside lights. When no longer needed I had to retrace my steps to turn off both lights. Since the auxiliary light script has been in place, the switch by the front entrance turns all outside lights on or off. The switch in the garage controls the outside garage lights only.
While the attempt at using a Domoticz group instead of script was in place, my spouse asked me why the garage lights were broken. It had to be my fault of course. It wasn't hard to come to that conclusion, there are actually three outside lights along the garage with minuscule odds that all three would burn out at the same time. The script is back doing its job, and my status as chief geek is reestablished.
Night Light
The XIAO ESP32C3 could be the basis of a night light, because Domoticz has built-in functions to do that. Click on the button of the virtual switch.
There are many ways to set the time at which a IoT switch will be turned on or off. In this example, the light will be turned on at dusk every day, 40 minutes before sunset. Note that the action is enabled
and that the button must be pressed to add the action to the list at the top.
The screen capture above shows the result after adding an Off command to be performed 10 minutes after sunrise. Domoticz calculates the sunrise and sunset times for every day and shows them for the current date under its top banner in every tab sheet except for the settings page. Using the sunset and sunrise times to turn on and off a night light is certainly preferable to using fixed hours which would have to be adjusted over the course of a year. However, with the light sensor on the Wi-Fi switch it is possible to turn the light when needed implicitly taking into consideration the cloud cover and other possible determinants of the actual light level.
Here is a very simple dzVents Lua script to do that.
I should point out that dzVents is just one of the many scripting methods available in Domoticz. There are two problems with that dzVents script. First the presence of the magic number 60. Remember that the output of the analog to digital converter connected to the light sensor was mapped to a value in the 0 to 100 range. I chose 60 as the threshold measure between light and dark, but that number is totally arbitrary. The threshold will crucially depend on the exact placement of the sensor. There needs to be some calibration. The other problem is that with such a hard decision rule between turning the light on and off, one has to expect that it will be flickering as the light level fluctuates around the threshold value. Here is a better version of the script.
Instead of a magic value, the script depends on two thresholds that are set as user variables and therefore easily changed while calibrating the light sensor without any need to edit the script.
User variables are managed in the Setup
/More Options
/User variables
pop-up window. The above shows five user variables still present on my system after deleting the very first one ever created and adding the threshold variables. To add a user variable just enter a new name in the Variable name:
field, chose the variable type in the drop-down list, assign a value to the variable and then press on the button. To change the value of a user variable, click on its name in the list of entries, enter the new value in the Variable value:
field and then click on the button under the list of variables.
The high and low thresholds establish a band of lux values in which the LED/relay is neither turned on nor turned off. If the lux level goes above the high threshold then the LED/relay is turned off, and if the light level is inferior to the low threshold then the LED/relay is turned on.
Perceived Humidity
The temperature and humidity sensors in the Domoticz calculate the dew point when the temperature and humidity are set. One of four strings, 'Normal', 'Comfortable', 'Dry', or 'Wet' called the humidity status is also displayed. I presume it is meant to indicate how we perceive the relative humidity. It is important to note that Domoticz does not set this string, it is specified in the bool updateDomoticzTemperatureHumiditySensor(int idx, float value1, float value2, int state=0);
helper function. Since version 3.0.15 the dzVents scripting system does provide a means of updating the humidity status. Here is a dzVents script that exploits this added functionality. One needs to add the idx
of all Temp + Humidity
and Temp + Humidity + Baro
meter sensors i the trigger devices list.
By default this script works correctly if the temperature units are displayed in the Celsius/Centigrade scale (°C) in the Domoticz interface. If temperatures are displayed in the ° Fahrenheit, the a conversion is necessary. As indicated in the top comment of the script, it must be edited and the line units = 'C'
must be changed to units = 'F'
. This "hardwired" definition entails problems when the temperature units are changed Setup
/Settings
/Meters/Counters
, until the units = '?'
line is updated. I suspect that changing units will not be done often so it is not too onerous. The table summarizes how the dzVents will chose the humidity status.
Relative Humidity % | Temperature | Humidity status | |
---|---|---|---|
°C | °F | ||
0-30 | any | Dry | |
0-30 | any | Normal | |
35-65 | <22 | <72 | Normal |
22-26 | 72-79 | Comfortable | |
>26 | >79 | Normal | |
65-70 | any | Normal | |
70-100 | any | Wet |
I am not convinced that this is the appropriate way to go. Back in July 2017, I had chosen to use the dew point instead. I may come back to that approach in a future installement.
Thoughts on the XIAO Starter Kit
It was surprising that Seeed Studio had sent a XIAO Starter Kit, I had never expressed an interest in it nor even mentioned it in the few e-mails we have exchanged. Do not misunderstand me, I am not against starter kits as such, I just thought I had outgrown them.
When dipping my toes in the Arduino universe, I did purchase a starter kit from Sparkfun built around their RedBoard, an "official" Arduino clone. At the time, it was comforting to get a development board and a few components that I could trust would work together. However, when working through a few of the examples in the included project book, I was underwhelmed by the experience. Again, I do not want this last statement to be misunderstood. I had experience in programming with Pascal, Modula, Delphi and some Java and I already had a decent collection of parts such as I²C and SPI displays, sensors and so on.
After first building the project with discrete components, I remembered the XIAO Starter Kit and build a second project with it as explained above. This experience has changed my opinion. I can now see the value of the kit for those that have a bit more experience. First of all, the XIAO Expansion Base by itself does transform the XIAO boards into true development boards. There is a built-in I²C display, a buzzer, a "user" push button, a reset switch, a micro SD card reader and a lithium coin cell battery holder. Having all these components so readily available often makes it possible to start developing software without delay. The ability to easily add two other I²C devices and a serial port using Grove compatible peripherals only enhances this. And there's more. As shown above, there is a Grove connector for the D0 I/O pin and female headers for all the pins of the XIAO so that it is easy to connect the base to a breadboard.
There is a downside to using the XIAO Expansion Base. It is necessary to solder male headers on the XIAO board. Sometimes one would not want to do this in case the XIAO is to be soldered to a carrier board using the castellated pads or if connections will be made with wires. Of course, I did solder the header pin on one of my XIAO ESP32C3 and decided to leave it in the XIAO Expansion Base declaring that to be my development test bed for all members of the XIAO family. Given that the XIAO costs a tenth as much as the Starter Kit and a third of the Expansion Base, it is not much of an investment. To use accounting terminology, there is no real sunk cost as the XIAO with pin headers could still be used elsewhere.
In conclusion, I can see two use cases for the XIAO Starter Kit. Beginners will enjoy using it. They will be able to concentrate on learning how to use the Arduino IDE (or better yet the PlatformIO IDE) which is daunting enough at the start without having to worry much about the hardware. For those that are already at ease working with microcontrollers, sensors and so on, then there is a gain in productivity to be had with the kit. The kit is not cheap compared to the XIAO boards, so if that is a problem, perhaps the XIAO Expansion Base would make more sense. In that case, individual Grove sensors and other peripherals could be purchased as needed, although one has to be careful because shipping charges can be proportionally high when making little purchases. No matter, I no longer think that I have outgrown useful devices such as the XIAO Expansion Base or Starter kits.
Alternate Hardware
As made amply clear that the XIAO Expansion Base and the Grove components are not needed. At first, I used discrete components on a small breadboard and connections to the XIAO with flying leads. The light sensor was a voltage divider made with a light dependant resistor and a fixed resistor and the temperature and humidity sensor was a DHT11 which are notoriously inaccurate. I must admit that the humidity measures of the DHT11 were grossly inaccurate but that was expected because the sensor is one of two rejects from another project. So in actual use, I would recommend the DHT22 or the DHT20 which is more accurate than the DHT11. At the very least get more than one DHT11 and test their accuracy. Here is a schematic of the four components as connected to the XIAO ESP32C3 board.
I used a DHT11 mounted on a small printed circuit board which takes care of the pull up resistor. Be careful the pinout is different on different boards. The value of the current limiting resistor connected to the LED is not critical, 200 to 500 ohms would be acceptable depending on the LED's colour. I know that sounds odd, but the power requirement of LEDs typically depends on their colour; look it up on the Web. I am not sure which LDR I used; the package says "type 5516 5-10K" but I remember that I wrote that months after buying the photoresistor and having a hard time identifying it. A 4.3 kΩ seems well matched to the LDR.
Of course the sensor drivers used previously will not work with this hardware. I rewrote the header and implementation files to accommodate the two sets of peripherals presented above and even other types of sensors. I have even made it possible to substitute emulated sensors instead of physical devices. Here is the modified header file. I also renamed the files hardware2.h
and hardware2.cpp
to avoid overwriting the original versions.
As can be seen all this is handled with macros, making the code messier, especially in the implementation part so I will not go into details. I will point out that I added a First-In-First-Out (FIFO) queue to calculate a rolling average for the light level measurements. It seemed appropriate to average the data from the light sensor because a cloud could cover the sun for a short interval, a person could cast a shadow over the sensor for a few seconds, or a temporary light might push up the reading artificially. The light level is read at a faster frequency, specified with the LS_READ
millisecond time interval between readings. On each reading, the latest value is added to the queue while the oldest reading is removed and the average value is recalculated and saved. When the SENSOR_DELAY
has expired, it is the latest calculated average value which is used to update String Light;
displayed in Web clients and in the Domoticz Web interface. The size of the queue is 10 by default but that can be changed with the LS_FIFO
. Obviously if LS_FIFO < 2
, there will not be much averaging going on!
Critique and Future Developments
While the Wi-Fi switch does work, at this stage, this is just a demonstration project. It is much too brittle. Case in point, when I rebuilt the project with the XIAO starter kit, it did not work properly. The DHCP assigned IP address of the second ESP32-C3 was different because it has a different MAC address. Using fixed IP addresses was not a good idea, akin to using unnamed numerical constants or so-called magic numbers. There are ways around this. The ESP32 does support multicast DNS (mDNS) so that the switch could be assigned a host name which it can advertise. This is not foolproof but it does work, some of the time at least. I find that using an MQTT broker much more resilient and dependable. Like most other home automation systems, Domoticz does support MQTT.
There is no provision for things going wrong in the firmware. One problem has already been identified: the firmware will hang if the DHT20 cannot be initialized for some reason. Getting temperature readings is not the main function of the Wi-Fi switch so it is inexcusable to disable the device if an auxiliary function cannot be done for whatever reason. What happens if the Wi-Fi connection is lost? What happens if the Wi-Fi connection is never established in the first place because the network credentials are incorrect or had to be changed at the router? There is no provision for updating the firmware, where really, over-the-air firmware should be supported.
Using the template substitution engine of ESPAsyncWebServer along with the content refresh meta tag does work, but it is not perfect by any means. For one thing, the web interface flashes each time the page is reloaded. In order to mitigate that problem, a longer delay between reloads might be chosen, with the consequence that the web interface is less responsive. Even a relatively short five second delay before the Web interface is updated seems long when the LED is toggled with the physical switch.
The list of things to do to approach what Tasmota does is long, but I think the next bit to do will be to find a better way to update the data displayed on client Web browsers.