ESP32lights

Today’s project, ESP32lights, is a smart device based on the esp32 chip.

esp32lights-004 esp32lights-005

Thanks to ESP32lights you can turn a load on and off (I used it for my christmas lights)

  • manually
  • based on daily schedules
  • based on the light intensity

ESP32lights connects to your wifi network, can be configured and operated via a web browser and it’s optimized for mobile devices (responsive web interface based on jQuery Mobile).

Components

The heart of ESP32lights is the Lolin32 Lite devboard by Wemos. One of its digital pins is connected to a relay module, which controls the load. Two digital pins are assigned to the first i2c controller of the esp32 chip and are connected to a BH1750 light intensity sensor. All the elements are powered by an HLK-PM01 module by Hi-Link, which directly converts the mains’ 220V AC to 5V DC without the need of any external components:

esp32l-001

All the components are placed in a waterproof enclosure, to be able to use the device outdoor:

esp32lights-006 esp32lights-007

Programming

The firmware for the esp32 devboard is available in my Github repository.

In a following paragraph I’ll explain how it works. If you just want to build the device, you can program the firmware as it follows:

1) clone my repository in a local folder of your PC (you also have to install the development env esp-idf):

2) configure the correct settings for your wifi network and your timezone via menuconfig:

esp32lights-003

3) compile and flash the firmware:

make flash

4) store the image of the SPIFFS partition into the flash memory (replace the COM port of your devboard and the path where you saved the img file):

python $IDF_PATH/components/esptool_py/esptool/esptool.py --chip esp32 --port COM15
 --baud 115200 write_flash --flash_size detect 0x180000 /home/esp32lights.img

If everything is ok, when you connect to the serial console of the devboard (make monitor) you should see the following output:

esp32lights-025

Use

ESP32light publishes an HTTP interface you can use to set the schedules or the light intensity threshold or to manually turn the load on and off.

You can open the web interface connecting – through a PC or a smartphone -to the address http://<esp_ip> (the IP address of the board is displayed in the serial output as shown in the previous paragraph).

The interface has 3 tabs, one for each working mode:

esp32lights-001

The page footer displays the actual working mode and the relay status:

esp32lights-002

In this short video, you can see how the device works:

Software

I developed the firmware for ESP32lights leveraging what I explained in my previous tutorials about the esp32 chip. If you follow my blog, you probably understood that I really like the divide et impera method, that is divide a complex project into small, simpler tasks.

All the configuration settings of ESP32lights (actual working mode, start and stop time…) are stored in the NVS partition of the flash memory, as I explained in this tutorial.  In this way, it’s possible to keep them even if the chip is restarted:

nvs_handle my_handle;
int working_mode;
[...]
esp_err_t err = nvs_flash_init();
err = nvs_open("storage", NVS_READWRITE, &my_handle);
err = nvs_get_i32(my_handle, "mode", &working_mode);

The different elements of the web interface (html page, css style sheets…) are stored in an SPIFFS partition. In a previous tutorial you learned how to prepare the image and, in your program, get its content:

esp32l-002

In other tutorials I’ve also already explained you how to connect to a wifi network and how to use digital pins.

The setup phase is completed after having configured the BH1750 light intensity sensor. This sensor offers an i2c interface and therefore can be connected to one of the two i2c controllers of the esp32 chip as shown in this tutorial. In my program I included a driver developed by pcbreflux.

The main program runs two different tasks:

xTaskCreate(&http_server, "http_server", 20000, NULL, 5, NULL);
xTaskCreate(&monitoring_task, "monitoring_task", 2048, NULL, 5, NULL);

esp32l-003

The first one publishes the web interface, while the second one verifies – every second – if conditions exist (time or light intensity) to turn the load on or off:

if(working_mode == MODE_LIGHT && lux_valid) {
  int actual_light_value = get_light_value();
  if(actual_light_value < lux) {
    if(relay_status == false) {
      gpio_set_level(CONFIG_RELAY_PIN, 1);
      relay_status = true;
    }

Here’s in details how the http server fetches a static resource, stored in the SPIFFS partition.

First it adds to the resource path the root prefix for the SPIFFS partition (/spiffs):

sprintf(full_path, "/spiffs%s", resource);

then it checks if the resource exists in the partition:

if (stat(full_path, &st) == 0) {

if so, it opens the file in read mode:

FILE* f = fopen(full_path, "r");

and sends the content of the file to the client, reading blocks of 500 bytes:

char buffer[500];
while(fgets(buffer, 500, f)) {
  netconn_write(conn, buffer, strlen(buffer), NETCONN_NOCOPY);
}

Finally, this is how the web interface works. The interface is made by an html page (index.html) which uses jQuery to perform AJAX requests to the server and update the different page elements. You don’t need to enter the page name in the browser because of the http server automatically performs a redirect to it if the default page is requested:

if(strstr(request_line, "GET / "))
  spiffs_serve("/index.html", conn);

endpoints are published by the server and accessed using AJAX calls::

  • setConfig, to send a new configuration
  • getConfig, to read the actual configuration
  • getLight, to get the actual light intensity

When the page is loaded, it calls the getConfig endpoint to display the actual configuration; moreover it schedules every 5 seconds a call to the getLight endpoint to keep the light value updated:

refreshConfig();
setInterval("refreshLightLevel()", 5000);

When you click on the SET button, the page calls setConfig to send to the server the new configuration:

esp32l-004

All the information are sent using the JSON format. The esp-idf framework includes the cJSON library which makes it easy to create or parse a json message:

cJSON *root = cJSON_Parse(body);
cJSON *mode_item = cJSON_GetObjectItemCaseSensitive(root, "mode");
[...]
cJSON *root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "lux", light_value);
char *rendered = cJSON_Print(root);

Making of

I started the build of the device cutting a perfboard to the size of the enclosure:

esp32lights-008 esp32lights-009

The perfboard is screwed to the enclosure using two spacers:

esp32lights-010 esp32lights-011

I made two holes in one side of the enclosure for the main switch and for a status led:

esp32lights-012 esp32lights-013

I soldered all the different components on the perfboard and made the electric connections using wires:

esp32lights-014 esp32lights-015

To simplify the installation, all the external components (led, relay module…) are connected using jumpers:

esp32lights-016 esp32lights-017

First test:

esp32lights-018

I attached the light sensor to the top of the enclosure, after having made a hole to allow it to “see” the external light:

esp32lights-019 esp32lights-020

Finally I made the external connections, installing the main switch:

esp32lights-021 esp32lights-022

and connecting the output of the relay module to a wire with an universal plug at its end:

esp32lights-023 esp32lights-024