Table of Contents

Synchronizing the Backlight with the X11 Screensaver

This program will turn the backlight ON or OFF according to the ON/OFF state of the display as reported by DPMS (run xset q from a terminal to see the DPMS report).

#include <cstdlib>
#include <cstdio>
#include <X11/Xlib.h>
#include <X11/extensions/dpms.h>
#include <chrono>
#include <thread>
#include <sys/syslog.h>
#include <pigpiod_if2.h>
 
using namespace std;
using namespace std::chrono;
 
#define PIN 34  // CPi-A/B/F/S
// #define PIN 44 // CPi-C
 
void log_info(const char *const message)
{
    printf("%s\n", message);
    syslog(LOG_INFO, "%s", message);
}
 
void log_err(const char *const message)
{
    printf("%s\n", message);
    syslog(LOG_ERR, "%s", message);
}
 
void backlight_on()
{
    auto instance = pigpio_start(NULL, NULL);
    if (instance != 0)
    {
        log_err("pigpio_start did not return success");
        return;
    }
 
    auto result = set_mode(instance, PIN, PI_OUTPUT);
    if (instance != 0)
    {
        log_err("set_mode did not return success");
        return;
    }
 
    result = gpio_write(instance, PIN, 1);
    if (instance != 0)
    {
        log_err("gpio_write did not return success");
        return;
    }
 
    pigpio_stop(instance);
}
 
void backlight_off()
{
    auto instance = pigpio_start(NULL, NULL);
    if (instance != 0)
    {
        log_err("pigpio_start did not return success");
        return;
    }
 
    auto result = set_mode(instance, PIN, PI_OUTPUT);
    if (instance != 0)
    {
        log_err("set_mode did not return success");
        return;
    }
 
    result = gpio_write(instance, PIN, 0);
    if (instance != 0)
    {
        log_err("gpio_write did not return success");
        return;
    }
 
    pigpio_stop(instance);
}
 
void set_backlight(CARD16 state)
{
    switch (state)
    {
        case DPMSModeOn:
            log_info("Turning backlight on");
            backlight_on();
            break;
        case DPMSModeOff:
            log_info("Turning backlight off");
            backlight_off();
            break;
        default:
            log_info("Invalid state");
            break;
    }
}
 
int main(int argc, char *argv[])
{
    openlog("backlight_service", LOG_NDELAY, LOG_USER);
 
    log_info("Starting");
 
    Display *dpy;
    dpy = XOpenDisplay(NULL);
    if (dpy == NULL)
    {
        log_err("Unable to open display");
        exit(EXIT_FAILURE);
    }
 
    BOOL onoff;
    CARD16 last_state;
    CARD16 state;
 
    // Initialize last_state
    if (!DPMSInfo(dpy, &state, &onoff))
    {
        log_err("DPMSInfo returned FALSE");
    }
    set_backlight(state);
    last_state = state;
 
    while(true)
    {
        if (!DPMSInfo(dpy, &state, &onoff))
        {
            log_err("DPMSInfo returned FALSE");
            this_thread::sleep_for(seconds(2));
            continue;
        }
 
        // If DPMS is not enabled, then display a message
        if (!onoff)
        {
            log_err("DPMSInfo is not enabled");
            this_thread::sleep_for(seconds(2));
            continue;
        }
 
        if (last_state != state)
        {
            set_backlight(state);
 
            last_state = state;
        }
        this_thread::sleep_for(milliseconds(200));
    }
 
    XCloseDisplay(dpy);
 
    log_info("Exiting");
 
    return 0;
}

Compile

Add the necessary development packages …

sudo apt install libx11-dev libxext-dev

… then compile.

g++ backlight_service.cpp -lX11 -lXext -lpigpiod_if2 -o backlight_service

Bash Script Implementation

The same can be a achieved using the following Bash script:

#!/bin/bash
 
PWM_PIN=31
PIN=34
 
# Set pins as output
raspi-gpio set $PWM_PIN op
raspi-gpio set $PIN op
 
# Start with both pins HIGH.  If either of the
# pins go LOW, the backlight will turn off.
raspi-gpio set $PWM_PIN dh
raspi-gpio set $PIN dh
 
# Get the current state
CURRENT_STATUS=$(xset q | grep "Monitor is" | awk '{print $3}')
LAST_STATUS=$CURRENT_STATUS
 
logger -p info -t "backlight_service" -s "Monitor is $CURRENT_STATUS"
 
# Loop indefinitely updating backlight in sync with monitor status
while true; do
 
  # Check the current monitor status
  CURRENT_STATUS=$(xset q | grep "Monitor is" | awk '{print $3}')
 
  # Only turn the backlight on/off if the Monitor state has changed
  if [ "$CURRENT_STATUS" != "$LAST_STATUS" ]; then
    logger -p info -t "backlight_service" -s "Monitor is $CURRENT_STATUS"
 
    # Control backlight pin according to the monitor status
    if [ "$CURRENT_STATUS" = "On" ]; then
      logger -p info -t "backlight_service" -s "Turning Backlight On"
      raspi-gpio set $PIN dh
    else
      logger -p info -t "backlight_service" -s "Turning Backlight Off"
      raspi-gpio set $PIN dl
    fi
    LAST_STATUS=$CURRENT_STATUS
  fi
 
  # Sleep before checking again
  sleep 0.2s
done

Automatically Start the Service

Add the line @{/path/to/}backlight_service to the /etc/xdg/lxsession/LXDE-pi/autostart file or to the file .config/lxsession/LXDE-pi/autostart in the user's home directory to automatically start the backlight_service executable when the desktop starts. See Configure an X Program to Auto-Start for more information.

To set the timeout to 60 seconds run xset dpms 0 0 60 from a terminal.

Add the line @xset dpms 0 0 {timeout_in_seconds} to the autostart file to set the timeout each time the desktop loads.

Run journalctl -t backlight_service -r to view the log entries.