logo elektroda
logo elektroda
X
logo elektroda

ESP32 and touch display - part 6, RGB lamp control, RGB picker

p.kaczmarek2 
.
Today we are creating another mini project - this time it will be a touchscreen RGB lamp controller. The controller itself will be based on the ESP32 board + ESP32-2432S028R touch display, while it will control any Tasmota/OpenBeken device via Tasmota's HTTP interface. Commands will be sent as GET requests and responses will be parsed from the received JSON format, which the Tasmota documentation discusses in detail:
https://tasmota.github.io/docs/Commands/
https://tasmota.github.io/docs/Commands/#with-web-requests

This topic continues the series on the ESP32-2432S028R board:
ESP32 and touchscreen display - tutorial - part 1 - how to program? Basics .
ESP32 and touch display - part 2 - how to draw pixels, lines, shapes, performance issues
ESP32 and touch display - tutorial part 3 - interactions, games and fun .
ESP32 and touch display - tutorial part 4 - web weather, APIs, JSON .
ESP32 and touch display - part 5 - LVGL in SquareLine Studio

I've taken the liberty of using the trial version of SquareLine Studio in this topic, but it's essentially redundant here, the RGB controller can be placed from within the code (which SquareLine Studio de facto does), I'll post the code for creating it too.

Step 1 - the UI itself .
The whole project is very attractive and beginner-friendly. We have the necessary components ready - even the colour selection widget is already in LVGL, which by the way you can read about in their documentation:
https://docs.lvgl.io/7.11/widgets/cpicker.html
In addition, a slider to control the brightness level and maybe an on/off button of some kind is useful.
We add objects in SquareLine Studio:
.
.
As in the previous section, we also add events:
.
Now it's time to implement the events, i.e. button press, slider move and colour selection separately.
So far without sending data to the lamp.
Button - colour change when pressed (lamp state is either on or off):
Code: C / C++
Log in, to see the code
.

Colour selection - here you need to swap the colour from the packed 16 bit mode to something closer to us, RGB in a more convenient form:
Code: C / C++
Log in, to see the code
.
The result:

[ 7382][I][main.cpp:39] MyColorChange(): Selected color: R=248, G=76, B=0

[ 9884][I][main.cpp:39] MyColorChange(): Selected color: R=128, G=252, B=0

[ 10375][I][main.cpp:39] MyColorChange(): Selected color: R=8, G=252, B=0

.

And then there's the slider, which for now just displays the change in the log:
Code: C / C++
Log in, to see the code
.
Result:

[ 17024][I][main.cpp:19] MySliderChange(): Selected brightness=55

[ 17084][I][main.cpp:19] MySliderChange(): Selected brightness=54

[ 19754][I][main.cpp:19] MySliderChange(): Selected brightness=91
.


Step 2 - putting everything together .
This step is optional, as with the Tasmota we can send the colour value, brightness level and on/off status separately, but for controlling the LEDs directly from the ESP (e.g. the WS2812 strip) this could still be useful.
So, we have three values (colour, brightness and on/off status) and we want to combine them. The easiest way is to multiply them:
Code: C / C++
Log in, to see the code
.
Of course, this function has to be called after each change.

Step 3 - Tasmota/OpenBeken control interface .
However, let us return to the control of the Tasmota device. We will implement communication based on HTTP, this is probably the simplest option, although MQTT could also be considered in the future. Whatever the method, it's worth reading the Tasmota command documentation:
https://tasmota.github.io/docs/Commands/
Regarding HTTP, it is worth reading the related topic, it is about the interface we are using:
OpenBeken as a mini HTTP host - writing pages in Javascript, Tasmota's REST API etc .
That is, we will send commands in the format:

http://192.168.0.201/cm?cmnd=POWER%20ON
.
Yes, the same can easily be tested in a web browser.
Rather, you should start by connecting to WiFi:
Code: C / C++
Log in, to see the code
.
Now you need to send the command somehow. This is accomplished by the following code snippet:
Code: C / C++
Log in, to see the code
.
I implement the sending of the command in the thread. Otherwise, I would block the refresh of the user interface while waiting for a response from the device. This could take an extremely long time, especially if the target device was offline.
The code above creates a thread using xTaskCreate:
Code: C / C++
Log in, to see the code
.
Documentation: https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32/api-reference/system/freertos.html
The URL is passed as an argument to the thread.
Using our function is very simple. We just call (for example):
Code: C / C++
Log in, to see the code
.

Now we need to put it to use. Let's start with the on/off. We plug these directly into the button, as our manual colour counting is no longer needed:
Code: C / C++
Log in, to see the code
.
Similarly, the slider:
Code: C / C++
Log in, to see the code
.
And the colour:
Code: C / C++
Log in, to see the code
.


Step 4 - Bilateral status synchronisation .
So far we have only done the sending of data to the lamp. What we lack is communication in the other direction. That is, if we externally change the state of our lamp, the state of the touch interface will not change. Simply put, the ESP will not know that something has changed.
In an ideal world, the lamp itself would be able to send us information about changes, but here we are relying on Tasmota's capabilities, so we will have to query the lamp itself about its state.
In Tasmota, the Status command is used for this.
We send:

http://192.168.0.212/cm?cmnd=Status
.
An example response is:
Code: JSON
Log in, to see the code
.
This will need to be parsed using ArduinoJSON, but we've already done a similar thing in the section on getting weather information from the internet.
What's worse is that we need to think about what to do with this response afterwards.
We shouldn't edit the UI from another thread, at least not while it's being refreshed.
In that case, we need to pick up the response from the thread somehow, but it's not as easy as it might seem. How about using the thread-safe queue from FreeRTOS?
https://www.freertos.org/a00018.html
Queues from FreeRTOS, like other mechanisms, take care of thread-safety and allow data to be passed safely between threads. We can't just share resources between threads without safeguards, because it can go to errors that are difficult to reproduce and fix.
We define a queue:
Code: C / C++
Log in, to see the code
.
Create a queue (arguments are maximum number and size of element):
Code: C / C++
Log in, to see the code
.
Now the addition to the queue needs to be done, but a moment. First, I also had to change the GET sending code so that it uses HttpClient. This is because the previous code had a problem with a missing Content-Length in the GET response:
Code: C / C++
Log in, to see the code
.
The code above also includes an add to queue - xQueueSend call. In the argument, I specify a long time to wait for the queue to be available, and still release the created buffer if the add fails. Otherwise we would have a memory leak that would quickly run out of memory....

Receive response - called from loop:
Code: C / C++
Log in, to see the code
.
Time to compile and upload - just to check that the code works and that we get a response in the main thread.

Connecting to WiFi...
Connected to WiFi
[ 6344][I][esp32-hal-adc.c:235] __analogReadMilliVolts(): ADC1: Characterized using eFuse Vref: 1072

HTTP GET Status = 200
Received 139 bytes: {"Dimmer":59,"Fade":"OFF","Speed":1,"LedTable":"ON","Color":"77,37,0,0,0","HSBColor":"29,100,97","Channel":[30,14,0],"CT":500,"POWER":"ON"}
[ 7253][I][main.cpp:266] checkForReplies(): [MainThread] Received response: {"Dimmer":59,"Fade":"OFF","Speed":1,"LedTable":"ON","Color":"77,37,0,0,0","HSBColor":"29,100,97","Channel":[30,14,0],"CT":500,"POWER":"ON"}
.
It works! Now it's time for the next step, which is parsing. We need to load this JSON into the appropriate structures. ArduinoJSON will come in handy:
Code: C / C++
Log in, to see the code
.
Similarly, as in the example with the weather. We deserialise the JSON:
Code: C / C++
Log in, to see the code
.
Then we need to apply a trick. We want to support both reading from the full state, where we need to search for the element StatusSTS , and from the truncated state (where the document root is StatusSTS ). To do this, we first look for StatusSTS, and if we do not find it, we assume that the root itself is this object. Whether we get a full or truncated status from Tasmota in response depends on which command we use.
Code: C / C++
Log in, to see the code
.
Once we have extracted the statusSTS, the brightness level (Dimmer), on/off status (POWER) and colour in HSB format (HSBColor) can be selected from it:
Code: C / C++
Log in, to see the code
.
Then we need to save the received information to our internal variables.
Code: C / C++
Log in, to see the code
.
The biggest fun is with the colour, because we need to convert it to RGB. We have a ready-made function for this from the internet. It is worth mentioning here that this colour is not multiplied by the brightness level, etc:
Code: C / C++
Log in, to see the code
.
Now you still need the helper function SetColorFromBytes, which essentially just sets the colourWheel from the given RGB:
Code: C / C++
Log in, to see the code
.
Similarly, now a helper function that sets the enabled state on the GUI:
Code: C / C++
Log in, to see the code
.
For convenience, I also added a bSend argument to specify whether the state change should be sent to the target device or not. If we are receiving the state from that particular device, there is no reason to send it back to it.
We check, everything works:
.
Final test on video:


.
The video shows control of the lamp from its native OBK panel and from the touchscreen display control programme developed here. The communication is two-way, although the states refresh with some small delay. The video shows that, for example, when I move the brightness level bar on the OBK panel, it also moves itself on the touch display.

Summary .
Our program works, the lamp can be controlled, and we got a taste of the basics of linking the user interface to web actions and were again exposed to the JSON format. By the way, we can appreciate LVGL again, because the colour selection widget was for like.
But of course this is not the end of the adventure, now the code could still be significantly improved and optimised. I would consider, for example, abandoning the creation of a new thread separately each time, in favour of a single "worker thread" that would simply perform network operations and collect new "network commands" from some queue. But this was only supposed to be a short demo so this is a secondary issue.
In the next part I will try either to optimise this controller shown here, or I will be tempted to use a slightly different panel, maybe some form of sensor reading? .

About Author
p.kaczmarek2
p.kaczmarek2 wrote 11842 posts with rating 9939 , helped 566 times. Been with us since 2014 year.

Comments