For the past few years, programming of embedded systems is not the exclusive job of assembler enthusiasts or low-level operations on the registers of the microcontroller. The dynamic development of the Internet of Things caused a significant part of high-level language programmers to transfer their professional interests from the area of high-performance PCs to small microprocessors and microcontrollers. This trend has not gone unnoticed by the hardware and software manufacturers, who provide developers with ready-made solutions that allow fast – and above all – high-level, preparation of a complete hardware and software project.

In this article, we will build a simple control system for Philips Hue series bulbs, using the intelligent Riverdi IoT Display module, the dedicated Zerynth programming environment, and the OKdo IoT Cloud– enabling easy and quick application development using Python. Now, let’s get started.

Table of Contents:

About Riverdi IoT Display displays and the Philips Hue system

IoT software in Python – it’s easier than you think!

Start writing the code

Programming in Python with Zerynth Studio

Graphical interface

OKdo IoT Cloud

About Riverdi IoT Display displays and the Philips Hue system

The aim of the presented project is to build a simple Philips Hue lighting control system using an intelligent LCD display, which in the final solution will be installed in the form of an aesthetic wall module.

All Intelligent IoT displays provided by Riverdi have a pre-loaded Zerynth Studio license, so we can start working with the module right after removing it from the packaging.

Figure 1 - the back of the Riverdi IoT display

The main task for the Riverdi IoT Display module will be to establish communication with the Philips Hue intelligent lighting system. In the described case, the lighting system consists of two light bulbs with simple on/off control and one RGB bulb, controlled by determining the hue, brightness, and colour saturation parameters. The heart of the system is Philips Hue Bridge, connected to the local area network using an Ethernet cable and communicating with light bulbs using the ZigBee protocol. The block diagram of the existing configuration and its planned expansion with an intelligent control panel module is shown in Figure 2.

Figure 2. Block diagram of the projected lighting control system

IoT software in Python – it’s easier than you think!

The chosen hardware solution allows us to quickly move to issues related directly to the software. This process starts with the download of an integrated and cross-platform (made available for Windows, Linux, and macOS) programming environment called Zerynth Studio.

The entire installation process of Zerynth Studio runs in a standard manner for the selected operating system. When the installer is started for the first time, the user will be asked to accept the license agreement and choose the installation method (online or offline installation – if the user has previously downloaded library repositories). The last step in the installation process is the choice of the software version (at the time of creating the article, the last available version is the version r2.3.0):

Figure 3. The software version selection window

Now, our environment is ready for work. But before we start writing the first lines of code, it is worth creating a simple block diagram, showing the way the designed application works – Figure 4.

Figure 4. A block diagram showing the way the application design works

We start the application by configuring the display and displaying a simple logo, which will allow us to ensure that the first configuration process has run correctly. In the next step, we connect to the WiFi network defined directly in the program (saving the SSID of the network and the access password is not the safest solution, but for the needs of the home control system that is fully sufficient).

After a successful connection to the WiFi network, the system user will be asked to enter the IP address of the Philips Hue Bridge device. The correctness of the entered IP address is verified by an attempt to read the device status. The penultimate step – before displaying the main application interface – is the process of creating a new system user and its authorization (in the final solution, this stage will be executed once).

A correctly completed process for creating a new user and its authorization, allow us to go to the last level of the menu, which is the display of the control panel. As shown in Figure 2, the existing lighting system consists only of three light bulbs (including one with the possibility of colour control), so the control panel can be implemented in the form of a “single-screen”, as shown in Figure 5.

Figure 5. The main menu of the lighting control panel

Start writing the code

Having prepared a complete block diagram of the implemented application, we can proceed to write the first lines of the code.

Programming in Python with Zerynth Studio

The process of preparing the application starts with the creation of a new project in Zerynth Studio.

In the main.py file created by default, we will place the main functionality of the program. According to the adopted block diagram from Figure 4, the application starts with the display configuration. In the Riverdi IoT Display module, the role of the graphics engine is played by the Bridgetek BT815 chip. This system acts as an intelligent bridge between the LCD display (connected to the BT815 system using a 24-bit RGB interface) and a microcontroller. A typical BT815 application is shown in Figure 6.

Figure 6. A typical BT81x application

Thanks to the use of the BT815 system, the user’s application is exempt from the obligation to create frame buffers in the RAM area and to implement low-level “drawing” functions of the interface. The BT815 system defines both a series of simple graphical objects (buttons, switches, sliders, etc.) enabling quick creation of applications as well as more complex functions related to graphics compression or media playback.

Complete documentation of the system together with the Programming Guide – presenting the possibilities of the system is available here.

With the support of the Riverdi IoT Display module, Zerynth developers have prepared a simple to use API for BT81x circuits, enabling the creation of an interface using a few simple functions. The complete Zerynth API for BT81x systems is on the documentation.

With the use of the above documentation, let’s proceed with the first changes in the main.py file. The edition starts with the import of library modules for BT81x systems and the 5-inch Riverdi display:

In the next step, we initialize the display and communication via the selected SPI interface, additionally assigning the Chip Select line, Interrupt, Power Down line and the bus speed (default – 3 MHz).

For the integrated Riverdi IoT display, these values are fixed and take the form of the following line of code:

bt81x.init(SPI0, D4, D33, D34)

After the correct initialization of the system, we can proceed to display a simple logo. For this purpose, the selected graphic must be pre-loaded into the BT81x internal GRAM memory. To place the loadImage () function in the newly created gui.py file already at the first stage of the work, put in the new file:

def loadImage(image):
bt81x.load_image(0, 0, image)

The task of the loadImage () function is only to call the bt81x.load_image () a method whose parameters are: the address in the GRAM memory area, additional control flags and the file name with the selected graphics. In the prepared application – in the area of GRAM memory – we will store only one graphic at a time, so the address in memory will always be set to 0. For testing purposes, we will use graphics in PNG format with the Riverdi logo of 642×144 pixels – the remaining area The image (for a 5-inch display is 800×480 pixels) will be completed with a white background. In the main.py file, loading the graphic gui_riverdi_logo.png into the GRAM memory, we will execute the following set of calls:

For full functionality, let’s implement the showLogo () function, which displays the loaded image in the specified coordinates. For this purpose – in the gui.py file – place the following code fragment:

Using the bt81x.dl_start () method, we start creating a new Display List that describes the currently created frame. Calling bt81x.clear_color () sets the colour for the image cleansing operation, which is done by the bt81x.clear () method. Using the Bitmap class, we define the parameters of the displayed image, including image source (in this case it is GRAM memory) and its size. Use the bt81x.prepare_draw () and bt81x.draw () calls to add the indicated graphics to the Display List. Each creation of the Display List by means of the bt81x.dl_start () call must be terminated by calling bt81x.display () – terminating its creation, and calling bt81x.swap_and_empty (), which changes the currently displayed content. The showLogo () function constructed in this way is called directly in the main.py file.

Communication between the control panel and the Philips Hue bridge will be made using the local network. For this purpose, it is necessary to connect the Riverdi IoT Display module to the WiFi network. Thanks to the use of the ESP32 system – integrating WiFi communication modules and Bluetooth Low Energy – the user does not have to connect any additional wireless modems. In order to establish a connection with the selected WiFi network (specified ssid variable and the access password – wifiPWD variable), in the main.py file, we will implement the following code fragment:

Using the for () loop and the wifi.link () method, the application attempts to connect to a defined network five times. If the operation fails completely, the mcu.reset () function will reset the device. Depending on the strength of the WiFi network signal, the linking process may be longer, so that the system user receives feedback from the graphical interface, it is informed about the progress of the process by calling the gui.showSpinner () function, which displays the animated Spinner object specified by the argument message. The showSpinner () function is defined in the gui.py file:

Using the above function, in the newly created Display List we place elements like Spinner and Text – the whole list is closed with calls bt81x.display () and bt81x.swap_and_empty (). The resulting graphic effect is shown in Figure 7.

Figure 7. A screen with a Spinner object informing the user about the progress of processes

In the next step – after successfully connecting to the WiFi network – we can start to create a graphical interface that allows entering the IP address assigned to the Philips Hue Bridge device. For the construction of the interface, we use the bt81x.add_keys () method, which places a single row of buttons in Display List – for an example call:

bt81x.add_keys(450, 70, 280, 60, 30, 0, "123")

The obtained effect will look like this:

Figure 8. A single line of buttons created using bt81x.add_keys ()

Graphical interface

Let’s go back to the operation of preparing the graphical interface. The screen for entering the IP address will consist of a 4-line keyboard (digits from 0-9, a dot symbol and a button to delete incorrectly entered values), a Connect button and a simple graphic. The entire operation was implemented in the form of the showAddrScreen () function in the gui.py file:

By using the bt81x.tag () method in the showAddrScreen () function, the value of the TAG identifier is assigned to the Connect button. The buttons from the keypad generated by a series of calls bt81x.add_keys (), as the TAG field, return ASCII codes corresponding to the individual key labels. Handling of all events is performed in the push function pressed (), as defined in the call bt81x.touch_loop (). A fragment of the pressed () function – associated with keyboard support – is shown below:

The call to the showAddrScreen () function is performed in the while () loop (in the main.py file) until the Connect button is selected (this process is repeated if the entered IP address of the device is incorrect):

After the correct indication of the IP address of the Philips Hue Bridge device, the control panel will go to the authorization stage (issues related directly to establishing a connection, authorization and communication protocol with Philips Hue Bridge, will be discussed later in the article). At this point, the message displayed to the user will be limited only to a simple graphic and a short message asking you to press the button located on the Philips bridge housing (it is a confirmation that the system user has physical access to the device). For displaying the message and graphics, the showAuthScreen () function from the gui.py file will be responsible:

In the main.py file – before displaying the message asking for authorization – add a short intermediate screen (using the previously prepared function showSpinner ()). During this time, to the GRAM memory of the BT81x controller, we upload the graphic file gui_auth_hue.png, which is used in the construction of the interface from the function showAuthScreen (). All operations will be performed in the while () loop until the user is properly authorized (the createUser () function will be discussed later in the article):

Leaving the above while () loop is tantamount to successfully terminating the connection process with Philips Hue Bridge. So the time has come to display the main control screen, consisting of ON / OFF buttons for two bulbs and control elements for one “coloured” light bulb. Before we go on to discuss the code snippets, let’s look at the final effect presented in Figure 10 beforehand.

Figure 10. The interface of the main control panel

The main control panel is definitely more extensive than the previous screens of the application – a single addition of 10 buttons and 8 text elements would complicate the main function very much, and the possible introduction of adjustments of the positions of individual interface elements would be very tedious. For this purpose, all components of the main interface are described using the JSON array, and their placement on the Display List, grouped into two for () loops (separate for Button and Text elements). Fragments of JSON descriptions are presented below:

The cyclic call to the showMainMenu () function has been placed in the main.py file in the main program loop. The main loop updates the values presented in the graphical interface, checks the status of variables representing states of controlled bulbs and in the event of changes (resulting from touch panel operation and event handling in the call function pressed ()) sends messages to the Philips Hue Bridge device:

We have successfully managed to omit all aspects of communication with the Philips Hue bridge, but it’s time to devote a few moments to this device and how it communicates with the world. In the most popular configuration, Philips Hue Bridge acts as an intermediary in communication between the mobile application and terminal devices. The programmers of the Philips Hue system, however, went a step further and instead of creating a closed system on the line mobile application <-> bridge, they decided to prepare an open and well-documented API, thus allowing external programmers to create their own control and system management devices.

A set of information related to the creation of own applications has been made available here.

Another nod to programmers is to create a simple interface that allows testing the API and system operation without creating a dedicated application. This interface – called the CLIP API Debugger – has been made available at:

https://debug/clip.html

The address of the Philips bridge can be determined by using the UPnP protocol, through https://discovery.meethue.com/ or by reading the configuration of the home router)

The appearance of the CLIP API Debugger interface window is shown in Figure 11.

Figure 11. The interface of the CLIP API Debugger panel

Communication with Philips Hue Bridge is carried out using HTTPS and REST API calls, so the interface window has been divided into the URL field, the choice of method type (GET, PUT, POST or DELETE) and the BODY section in which JSON queries are placed.

Before we move on to the question of controlling the work of light bulbs, it is necessary to create a new user and its authorization beforehand. To create a new user, make the following call:

To increase the security of the system, it is necessary to properly authorize the user. In the received communication, the user is asked to press a button placed on the device casing – this procedure is to confirm that the person attempting to gain access to the lighting control also has physical access to the control bridge. Pressing the button and re-sending the POST request above will be confirmed by the following message:

The value of the username field is the new name of the authorized user, which will be used in further communication. However, let’s get back to the code of our application for a moment and consider how to accomplish the above tasks using Python and a set of libraries provided by Zerynth. The request module comes with help (for Python programmers this name is probably well known – this module – for the API is inspired by a quite popular module with the same name). This module enables fast implementation of HTTP support and GET, POST, PUT and DELETE calls in the form of single functions. The full API documentation for the requests module is available at the Zerynth docs.

The createUser () function – based on calls from the requests module – responsible for creating a new user is defined in the hue.py file:

The above function is performed in a loop until the value of success in the response returned by the bridge is read. To the main program loop, this function returns the value read from the username field, which will be used in subsequent calls controlling the status of light bulbs:

OKdo IoT Cloud

Is this the end of the possibilities of this extraordinary mix which is Riverdi IoT Display and the Zerynth environment? Certainly not! To provide remote control over the lighting system, the application can be extended in several lines of code with the possibility of remote synchronization with the OKdo IoT Cloud solution [6]. This solution allows us to remotely monitor and control our lighting system by using a mobile device or web interface.

The OKdo Cloud is free, and you can easily sign up for the account, on the official page linked above. As they say on the OKdo Cloud page:

“Connect your things and interact with them for free on the OKdo Cloud.”

I've learned early on in my career that I love to be at the forefront of innovation. To see new technologies emerging and be a part of their creation. From the first days on the Internet and the Silicon Valley revolution to the IoT and Blockchain - I've seen it all, and I've done it all.
I have over 25 years of experience in business development, driving revenue, and encouraging new ideas. I'm a co-founder of Modis and Fortebit, and business developer for Zerynth, Riverdi, and Chipsee.