Pi IoT In C Using Linux Drivers - Hwmon
Written by Harry Fairhead   
Monday, 21 June 2021
Article Index
Pi IoT In C Using Linux Drivers - Hwmon
HWMON
Working!

Hwmon

Drivers that install into the hwmon system work in the same way as most Linux drivers by pretending to be folders and files. You will find all hwmon devices in:

/sys/class/hwmon/

and each device creates a deviceX folder where X is an integer. Within each folder you will find the files you need to work with the device. In particular you will find a name file which gives the usual name of the device and often an updat
e_interval file which gets and sets the update interval for the device. If you look at the Pi’s /sys/class/hwmon folder you will see that there are already two device folders:

 Hwmon1

The first corresponds to the built-in temperature sensor and the second to the A-to-D converter that monitors CPU voltage. As an example let’s read the CPU temperature. Inside the hwmon0 folder are a number of files and folders:

Hwmon2

The ones that matter most are name, which gives the name of the sensor and temp1_input, the data file. The uevent file is present in all driver folders and it is used by the system to implement dynamic changes to the hardware, see udev in Chapter 16.

Reading the CPU temperature is just a matter of opening the file and reading:

#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char** argv) {
    int fd = open("/sys/class/hwmon/hwmon0/temp1_input", 
O_RDONLY); char buf[100] = {0}; read(fd, buf, 100); printf("%s\n\r", buf); float temp; sscanf(buf, "%f", &temp); temp = temp / 1000; printf("%f\n\r", temp); }

Notice that the string returned from the file is terminated by /n and not by a zero, hence the need to initialize the buffer to all zeros.

You can use the same approach to read the CPU voltage – only the filename changes.

Installing An Hwmon Device - LM75

Installing an hwmon device is very straightforward, with no surprises if you have been following how things work. As an example, let’s install an LM75 temperature device. This is a low-cost I2C temperature sensor with an accuracy of around 2 degrees Celsius and a resolution of 11 bits. You can buy a suitable LM75 module, complete with prototype board, from many sources. It has three pins that can be used to set the low three bits of the address, which means you can support up to eight devices on the same bus:

Hwmon3

You can see the standard I2C pins plus power and ground. The final pin, OS, is a thermal shutdown output which goes high when the temperature is above a set value.

To use the device you generally have to solder either pins or wires to the connections. You also have to connect the address pads on the back of the device. This can be done with a solder bridge – a blob of solder connecting the pads together. You don’t need pull-up resistors as they are in place on the other side of the board. Be careful not to create a solder blob so large that it shorts out the ground and power pads.

Hwmon4

For this example, connect all of the address lines to ground, giving an address of 0x48. By connecting them differently you can use addresses from 0x48 to 0x4f. The chip has registers that can be read to discover the temperature and that can be written to set the critical temperature and the hysteresis. The over-temperature alarm is triggered when the temperature exceeds the critical temperature and it is untriggered when the temperature is lower than that set by the hysteresis.

The LM75 can be used via the raw I2C interface or you can make use of its Linux driver. The LM75 driver is implemented by a general i2c-sensor driver which can be set to work with a range of devices. In this case we need to add:

dtparam=i2c_arm=on
dtoverlay i2c-sensor,lm75,addr=0x48

to the /boot/config.txt file. If you don’t specify an address parameter the default is 0x4F.

You can also load the drivers dynamically:

FILE *doCommand(char *cmd)
{
    FILE *fp = popen(cmd, "r");
    if (fp == NULL)
    {
        printf("Failed to run command %s \n\r", cmd);
        exit(1);
    }
    return fp;
}
int findInd(FILE *fd, char *indicator)
{
    char output[1024];
    int txfound = 0;
    while (fgets(output, sizeof(output), fd) != NULL)
    {
        printf("%s\n\r", output);
        fflush(stdout);
        if (strstr(output, indicator) != NULL)
        {
            txfound = 1;
        }
    }
    return txfound;
}
void checkLM75()
{
    FILE *fd = doCommand("sudo  dtparam -l");
    char indicator1[] = "i2c_arm=on";
    char command1[] = "sudo dtparam i2c_arm=on";
    char indicator2[] = "lm75";
    char command2[] = 
"sudo dtoverlay i2c-sensor lm75 addr=0x48"; int txfound = findInd(fd, indicator1); if (txfound == 0){ pclose(fd); fd = doCommand(command1); sleep(2); } pclose(fd); fd = doCommand("sudo dtparam -l"); txfound = findInd(fd, indicator2); if (txfound == 0){ pclose(fd); fd = doCommand(command2); sleep(2); } pclose(fd); }


Last Updated ( Monday, 21 June 2021 )