Bus Automation: Part 3 - Dimming the Lights and Graceful Degradation

Bus Automation: Part 3 - Dimming the Lights and Graceful Degradation

WARNING: SERIOUSLY nerdy stuff lies ahead. Proceed with caution ;)

This week we have decided to give an update on our "bus automation" project. In our Part 1 we introduced some of the technologies and the some of the hardware pieces we had been playing with. In the Part 2 we actually configured and started running "Home Assistant", MQTT, and Node-Red on a Raspberry Pi 3 B+. We also wrote some Arduino code to have an ESP-32 board talk to the lights and turn them off and on via a relay board with 8 relays.

This was a great start but we really wanted the lights to be able to dim - not just turn on and off. We figured if we overdid it with too many lights, we could always dim them down to a desirable level. It seemed like a much nicer problem to have than the problem of not having enough light. So we did some research into how we could do this. The two keys to accomplishing this are PWM and MOSFET.

MOSFET Semi-Technical Section

A MOSFET is a field effect transistor whose voltage determines how much current flows through it. It is basically a very fast switch that can handle a lot of electrical current. Initially, I just used a few MOSFET chips, but later moved to some optoisolated boards.

PWM Technical Section

When I was looking at Arduino type processors, I gravitated towards the ESP32 because of the built-in WiFi, Bluetooth connectivity, and 16 independent channels for PWM signaling. So what is PWM? PWM stands for Pulse Width Modulation and it is a fancy term for describing the switching of a source on and off by varying the amount of time it is on vs off. The signal, or "power", is chopped up to keep the voltage high enough to power the load but the on/off action controls how much power is transferred to the device. Imagine playing with a motor where it is connected to a light switch. That motor needs full voltage to power up and run and often needs a bit of time to reach full speed (especially true for larger motors with significant mass to move). However, if you toggle the switch back and forth, you can make the motor run slower. This turning on/off of the switch is what PWM does, only much, much faster (5000 times per second in our ESP32 chips). For our LED lights this will control their brightness. Even "non-dimmable" LED lights can effectively be dimmed with this technology.

PWM with the ESP32

As previously mentioned, the ESP32 allows for up to 16 independent channels of PWM switching.
To set up the ESP32 on the Arduino platform for PWM switching, we simply declare which of the 16 channels (0 through 15) we are going to use and then attach that channel to the desired GPIO pin. In code, this looks like:


// use the first channel of 16 channels (started from zero)
#define FD 0

// use 13-bit precision for LEDC timer
#define LEDC_TIMER_13_BIT 13

// use 5000 Hz as a LEDC base frequency
#define LEDC_BASE_FREQ 5000

void setup() {  
    // Setup timer and attach timer to a led pin
    ledcAttachPin(FRONT_DRIVER_LED_PIN, FD);

The ESP32 does PWM switching with 13-bit precision #define LEDC_TIMER_13_BIT 13 (meaning there are 213 or 8192 discrete values). For dimming lights, this is a bit too much (yes that was a nerd pun). Home Assistant supports dimmers with 8-bit precision (meaning there are 28 or 256 discrete values). So we had to write a small translator the handle this.

void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {  
    // calculate duty, 8191 from 2 ^ 13 - 1
    uint32_t duty = (8191 / valueMax) * _min(value, valueMax);
    // write duty to LEDC
    ledcWrite(channel, duty);

The final piece is setting the PWM value. Home Assistant will give a value between 0 and 255. 0 is basically off and 255 is full bright. In our code, when we get an event that gives us a brightness setting we simply set the proper channel by calling our ledcAnalogWrite command:

// function called to turn on/off the light
void setLightState() {  
    if (m_front_driver_state) {
        //First time the light turns on bightness is 0 (kind of annoying)
        if (m_frontDriverBrightness == 0) {
            m_frontDriverBrightness = 255;
        ledcAnalogWrite(FD, m_frontDriverBrightness);
    } else {
        ledcAnalogWrite(FD, 0);

Home Automation Configuration

Since we are no longer dealing with simple on/off switches, Home Assistant needs to be configured to know that we have dimmable lights. These types of lights actually have 4 MQTT that they deal with:

  1. is the light on or off
  2. a command topic for the light to turn on or off
  3. what is the brightness of the light (0-255)
  4. a command topic for adjusting the brightness of the light (0-255)

The configuration file ends up looking like this:

# definitions for lights 
light front_driver:  
    - platform: mqtt
      name: "Front Driver"
      state_topic: "front_driver/light/status"
      command_topic: "front_driver/light/switch"
      brightness_state_topic: "front_driver/light/brightness"
      brightness_command_topic: "front_driver/light/brightness/set"
      qos: 0
      payload_on: "ON"
      payload_off: "OFF"
      optimistic: false
light front_passenger:  
    - platform: mqtt
      name: "Front Passenger"
      state_topic: "front_passenger/light/status"
      command_topic: "front_passenger/light/switch"
      brightness_state_topic: "front_passenger/light/brightness"
      brightness_command_topic: "front_passenger/light/brightness/set"
      qos: 0
      payload_on: "ON"
      payload_off: "OFF"
      optimistic: false

Graceful Degradation

In the video, I go into a little bit about the concept of Graceful Degradation. The concept is fairly popular in software engineering. In a nutshell, it means that even when things break, some core functionality will continue to work. In our case, the perfect scenario requires: A local area WiFi network to be functional, an MQTT server to broadcast and listen for events, Home Assistant to be running, The ESP32 processors to be running. But what if something happens and MQTT is no longer able to accept messages? Or what if the WiFi router goes down and the network is not available? If we wired our switches in a more "traditional" home automation way (sending MQTT messages over the network), our light switches would fail to work. Since the lights are being controlled by the ESP32, it made sense to "wire" some buttons into the chip to function as on/off switches. This gets us past the WiFi, MQTT, Home Assistant dependency. Another benefit of this configuration is we guarantee that the switches will be very responsive. Since we are on the processor, we have control of everything that controls latency. We are not dependent on network or database speed. But what if something catastrophic happens to the chip? Ideally, at about $6 each, I would have a spare ready to go within a few minutes (just download the code). However, just in case, we plan on making one last set of "manual override" switches which will be wired directly into the Mosfet boards to power the lights on "manually". A Mosfet is really just a high-speed switch and if we give it a steady 5V it will turn the LEDs on full bright.

Custom Printed Circuit Board vs Off the Shelf Components

As we start to plan and work our way inside the bus, we are faced with a decision. Should we design a custom board and add components or try to piece something together from readily available assembled boards? For now, we think going with readily available assembled boards would be much easier to deal with if we have to fix something. And let's face it, I have not read a single RV blog or seen a vlog that didn't involve constantly fixing things.


We are really excited about the progress we have made with the bus automation project. It really feels like it is going to be a very nice working system that provides "just enough" automation to make things more convenient but not so much that it fundamentally changes how we think about the things we use.

Watch the video:

Click here If you cannot see the video.

Parts We Used

You May Like Also


Comments powered by Disqus