Monitoring Sys Files with QFileSystemWatcher

When the driver turns off the ignition in a vehicle, display computers like terminals in harvesters or infotainment systems in cars should be notified. The display computers should save important data and shut down orderly.

When the display computer runs on Linux, it learns the status of the ignition by monitoring the GPIO for clamp 15. Linux writes the value of the GPIO to a special file in the sys filesystem. On one of my display computers, clamp15 is mapped to the special file /sys/class/gpio/gpio72/value. When the ignition is on, this file contains the string "1". When off, this file contains "0". The number of GPIO (here: 72) varies from display computer to display computer.

On the Linux command line, we get the value of clamp 15 with this command:

$ cat /sys/class/gpio/gpio72/value 1

The command returns "0" when the ignition is off and "1" when it is on.

How can a Qt application monitor the status of the GPIO clamp 15 such that it can shut down orderly when the driver turns off the ignition?

Our first idea most likely is to use QFileSystemWatcher on the file /sys/class/gpio/gpio72/value. QFileSystemWatcher does exactly what we want. When the watched file is modified, QFileSystemWatcher emits the signal fileChanged(const QString &path). We connect a slot to this signal and read the value from the sys file.

// In the constructor of SomeClass auto clamp15Monitor = new QFileSystemWatcher{this}; clamp15Monitor->addPath("/sys/class/gpio/gpio72/value"); connect(clamp15Monitor, &QFileSystemWatcher::fileChanged, this, &HarvesterServicesImpl::onClamp15FileChanged);

The slot onClamp15FileChanged does nothing else but the command cat /sys/class/gpio/gpio72/value. It reads the value from the sys file and prints it.

void SomeClass::onClamp15FileChanged(const QString &path) { QFile clamp15File{filePath}; if (!clamp15File.open(QIODevice::ReadOnly)) { qWarning() << "ERROR: Could not open clamp15 file."; return; } auto clamp15 = clamp15File.readAll(); if (!clamp15.isEmpty()) { qDebug() << "INFO: clamp15 = " << clamp15.at(0); } }

When we run this code, we are flooded with INFO messages. The messages show the correct value of clamp 15 - many times per second. However, we want to see an INFO message only when the value of clamp 15 changes.

We could give up at this point and set up a QTimer, which calls onClamp15FileChanged() every second. We poll the status of the clamp-15 file once a second. This works, but it wastes many CPU cycles.

Fortunately, we can do better. The file /sys/class/gpio/gpio72/edge comes to our rescue. Section "GPIO and sysfs" (p. 394ff) of John Madieu's book "Linux Device Drivers Development" points us in the right direction.

edgepollselectnonerisingfallingboth

The system functions poll and select monitor I/O activities on file descriptors. John Madieu explains:

poll (2)/sys/class/gpio/gpio72/valuepoll (2)poll (2)POLLPRIselect (2)exceptfdspoll (2)lseek (2)

In other words: If the Linux kernel (the GPIO device driver, to be exact) sees an edge on a GPIO, it triggers an interrupt. The interrupt writes the changed value into the sysfs file corresponding to the GPIO. poll and select see this activity on the monitored file descriptor and return from blocking. The caller of these system functions can read the contents of the sys file.

As QFileSystemWatcher is implemented with one of these system functions, we should get it working properly. The trick is to configure the GPIO device driver to check for edges on GPIO 72. This isn't the case, as the command

$ cat /sys/class/gpio/gpio72/edge none

shows. This explains why QFileSystemWatcher has been misbehaving so far. We configure GPIO 72 to check for "both" edges with the following command:

$ echo "both" > /sys/class/gpio/gpio72/edge

When we run our application now, we will see INFO messages only when the value of /sys/class/gpio/gpio72/value changes. We never see the same value twice in a row. QFileSystemWatcher emits its signal fileChanged only if the driver turns the ignition from "on" to "off" or from "off" to "on".

Read next

Running Wayland Clients as Non-Root Users

Many embedded Linux systems use a Wayland compositor like Weston for window management. Qt applications act as Wayland clients. Weston composes the windows of the Qt applications into a single window and displays it on a screen. I still have