16-channel LED driver + ESP8266

Hi all.
I have designed a stand-alone LED driver board with a bare ESP8266-12, PCA9685 PWM chip, 3V3 Pololu buck, and 16 CC LED drivers.
Harware works 100%, but I have some problems with the code (hardware guy, software noob).
Code I used is based on the excellent work of PieterP, which can be found on page 45 of his Beginnner's Guide to ESP...pdf
I have removed parts (rainbow), and added PCA9685/dimming code, but that parts works.

I have problems with this HTML part. Only four channels from the 16 are shown, and some lines removed for clarity.

<td><input class="enabled" id="0" type="range" min="0" max="255" step="1" oninput="sendRGBW();" value="0"></td>
<td><input class="enabled" id="1" type="range" min="0" max="255" step="1" oninput="sendRGBW();" value="0"></td>
<td><input class="enabled" id="2" type="range" min="0" max="255" step="1" oninput="sendRGBW();" value="0"></td>
<td><input class="enabled" id="3" type="range" min="0" max="255" step="1" oninput="sendRGBW();" value="0"></td>

Named it sendRGBW() instead of the sendRGB() in the original code, to see if four channels could work.
That is used/packaged in the websocket.js file like this:

function sendRGBW () {
  var r = document.getElementById('0').value;
  var g = document.getElementById('1').value;
  var b = document.getElementById('2').value;
  var w = document.getElementById('3').value;

  var rgbw = r << 24 | g << 16 | b << 8 | w;
  var rgbwstr = '#' + rgbw.toString(16);
  console.log('RGBW: ' + rgbwstr);

And used in the Arduino sketch here:

case WStype_TEXT:
Serial.printf("[%u] get Text: %s\n", num, payload);
if (payload[0] == '#') {
  uint32_t rgbw = (uint32_t) strtol((const char *) &payload[1], NULL, 16);
  byte r = ((rgbw >> 24) & 0x3FF);
  byte g = ((rgbw >> 16) & 0x3FF);
  byte b = ((rgbw >> 8) & 0x3FF);
  byte w =          rgbw & 0x3FF;
  request[0] = r; // request a dim level
  request[1] = g;
  request[2] = b;
  request[3] = w;

Firefox inspector shows weird negative values when the sliders are moved, but the four LED channels work perfectly.

Problem #1: When you restart the web page (phone or PC), the sliders are all at zero.
If you change one slider, the three other channels/values/LED brightness drop to zero.
Because the four values are all packaged in one value/string.

Problem #2: This has to be expanded to 16 channels, and maybe a second set of 16 for a PCA9585 (I2C) slave board.

Noob thinking:
Stored dim level values need to be used when a new phone/PC connects, to set the range slider to that value.
That should fix problem #1. Not sure how to do that.

Hex values (byte) of 16 (or 32) channels could be transferred in one string, and used in request, without converting (just parsing). Sliders might lag too much (don't know). There might be other ways.
Please enlighten me (slowly).

16-ch_LED.zip (2.86 KB)

Hi Leo,

To make sure that the sliders are in the right position when you reload the page, just make the ESP send the values to the client when it first connects.

I would send only one value per WebSocket message, instead of all 4 (or 16) at once. Such a message would contain the output number and its new value.

I've written a quick sketch that does this (adapted from a previous project that used toggle switches / checkboxes to turn on or off LEDs, so it wasn't a big deal to change 'on' or 'off' to a continuous value).

You can find it here: https://github.com/tttapa/Projects/tree/master/ESP8266/Control%20Panel/Control-Panel-WebSocket-Sliders

The most important files are Control-Panel-WebSocket-Sliders.ino and data/Control-Panel.js.

Here's an overview of what it does:

  • Start a SoftAP
  • Connect to a WiFi network
  • Start an mDNS responder
  • Start the OTA service to upload new sketches over WiFi
  • Start a web server that serves files stored in SPIFFS
  • Start a WebSocket server

When you open the web page in the browser, the following happens:

  • JavaScript connects to the WebSocket server on the ESP
  • The ESP responds with the number of sliders to display (1)
  • JavaScript adds the right number of sliders to the HTML web page
  • After sending the number of sliders, the ESP sends messages with the value of each output to the client (2)
  • JavaScript sets the positions of the sliders accordingly
  • JavaScript starts sending ping messages to the ESP to let it know that it's still online
  • The ESP just echoes the pings

When you move a slider:

  • JavaScript sends a message with the new value to the ESP (3)
  • The ESP changes the output to the new value
  • The ESP broadcasts the message to all connected clients
  • The other clients change the position of the slider as well

If the client sends a ping, and the ESP doesn't reply in a couple of seconds, it will blur the interface and show that the connection is lost. If the ESP replies to one of the next pings, it just un-blurs the interface, and sends a message to the ESP to request the slider values again (should they have changed while the client was offline) (4). If the server doesn't reply for more than 20 seconds, the client will reload the page.

If the server doesn't receive any messages from a client for more than 10 seconds, it will disconnect the client. (The reason is that sending (or broadcasting) data to clients that are not connected will time out after 5 seconds, so the program will just 'hang' for some time).

The format of the messages is as follows:

  • #AA: a number sign to indicate that the message contains the number of outputs / sliders, and AA is the number of outputs, in hexadecimal format
  • FF:VV: a message to send the value of one of the outputs to a client, FF is the number of the output, and VV is the value, both are in hexadecimal
  • Idem to (2), a message to send the new value of one of the sliders from a client to the ESP
  • ?: a message to request the values of all outputs from the ESP. The ESP will respond with messages of type (2).

Steps to get it working:

  • Download/clone it from GitHub
  • Rename WiFi-Credentials.example.h to WiFi-Credentials.h and add your own SSID and password (if you don't want to connect to an access point, comment out "#define STATION" in the .ino file
  • Change the constant nb_outputs on line 21 of the .ino file
  • Initialize I²C in the setup
  • Implement the setOutput function at the bottom of the file to actually send the value to the PCA9585
  • Select the right settings in Tools > Boards, close the Serial monitor, and click Tools > ESP8266 Sketch Data Upload
  • Once that's finished, upload the sketch

I've done my best to document the code, but there's just a lot going on. If you have any questions, or want me to clarify some parts, don't hesitate to ask!


Thank you for taking the time to look at this. Might take some time to get my head around it. Leo..

Basically worked first try after merging with my own PCA9685 code.
Just small things to iron out (once I uderstand all of it).
e.g. slider look (.css) works on android, but not on Firefox.
The “displaySlider(index)” function in the .js file looks like this now.

function displaySlider(index) {
    let slidercontainer = document.createElement("div");
    slidercontainer.innerHTML =
        `<td style="width:14.4px; text-align: right">${index + 1}: </td>
         <input id="${byte_to_str(index)}" type="range" min="0" max="255" step="1" value="0">`;
    let slider = slidercontainer.getElementsByTagName("input")[0];
    slider.onchange = sendSliderValue;

Not sure why you didn’t use value=“0” in line 5 to preload the sliders on startup.
Not that important though. Update goes fast enough.
Thanks again.

Hi all.
Back with some more problems.

The ESP-12 module doesn’t seem to switch smoothly between AP and ST.
For testing I used a wired (Giganet) PC with W7/64 and Firefox, and a Samsung smartphone with Android.

Everything works fine when the WiFi router is available.
I can operate the sliders on PC and smartphone, and they update each other.

Only problem is that the browser(s) won’t easilly let go of the web page.
The browser just reloads the slider page again when you try to go to a different site.
Several tries are needed, or a browser restart.
Even after the 10 sec “disconnectOldClients()” timeout.

When the ESP is connected to the router, I also can use it’s AP (, and operate the sliders there.
Because the ESP has been configurated both as Access point and Station.
The wired PC still follows the smartphone.

/* Not sure why it is “WiFi.mode(WIFI_AP_STA);”
That will just use (polute) a WiFi channel for no reason.
Because AP won’t be used when the WiFi router is available.*/

Problems start when I disable WiFi on the router, to simulate a mains power cut.
Boards are currently used for downlights, and operating the lights must also be possible during a power cut (with phone only).
The boards can also be used e.g. in a (mobile) home with solar etc. so must be able to work in AP mode.

I can switch the smartphone to the AP of the ESP (, and see the sliders in short bursts.
During those flashes, I can operate the sliders/lights when I’m fast enough.
It seems that the ESP is constantly working on finding the lost WiFi router again, and has no time for anything else.

I also tried booting the ESP without WiFi router enabled.
It starts up in AP mode, but the same thing happens.
Web page is available only in short bursts.
The smartphone reports “connection lost” between bursts.

I have no clue where the problem lies. My guess is the WiFiMulti/Websocket combo, but I could be wrong.
A 3-minute timeout before the ESP starts searching for the router again (once every three minutes) would be acceptable.
I don’t even want/need multiple WiFi connections, because this ESP board is permanantly fixed to one spot, with one WiFi router in the house.
It also seems that a fixed IP is easier on a PC and phone without mDNS.
Because you don’t need to update all your shortcuts after the router has decided to give the ESP a different IP address.
This could happen when you are away for a long weekend, and the DNS lease time has expired in the router.
Fixed IP should also connect faster (no DNS lookup).
Tried to config a fixed IP. Worked, but the above problems still persist.

Another problem is OTA updates. Can’t get passed the OTA password.
Not a priority though.

.zip file attached, because of the multiple tabs and a data file.
This one is the bare minimum, without the PCA9685/PWM files embedded.
So should be ok without installing any libraries.
Enter your WiFi credentials in the WiFi-Credentials.h file before uploading.
Data file must be uploaded to SPIFFS separately with “ESP8266 Sketch Data Upload” in the tools tab of the IDE.
Flash was set to “1M(512K SPIFFS)”.

Here are some extracts of that .zip file if you don’t want to download/install the .zip
Thanks in advance.

// this is in the main .ino
#define STATION

// this is the WiFi part of the Networkinit.hpp file
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>

#ifdef STATION
#include <ESP8266WiFiMulti.h>
#include "WiFi-Credentials.h"

ESP8266WiFiMulti wifiMulti;  // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'

void startWiFi() {  // Start a Wi-Fi access point, and try to connect to some given access points. Then wait for either an AP or STA connection
#ifdef STATION
  WiFi.softAP(AP_ssid, AP_password);  // Start the access point
  Serial.print("Access Point \"");
  Serial.println("\" started\r\n");
#ifdef STATION
  Serial.print("Connecting to ");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);  // Connect to a given access point (specified in WiFi-Credentials.h)

Control-Panel-WebSocket-Sliders.zip (12.2 KB)