Erschließung des HX711 Treibers für Linux-IIO

Kannst Du das Paket denn nicht per apt-get install linux-modules oder irgendwie manuell per dpkg -i installieren?

@weef: Weißt Du, woher das kommt? Unter Index of /raspbian/pool werde ich gerade nicht fündig.

Könnte evtl. hier mit gehen. Ich versuche es mal.
https://wiki.ubuntuusers.de/module-assistant/

Edit: geht leider auch nicht. Er kann keine offenen Quellen für den kernel version: 5.10.17+
finden. Ein manuelles einfügen einer vorher runter geladenen HX711.ko geht leider nicht.

Starting the Dialog UI...

Updated infos about 27 packages
Getting source for kernel version: 5.10.17+
apt-get install linux-headers-5.10.17+
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package linux-headers-5.10.17
E: Couldn't find any package by glob 'linux-headers-5.10.17'
E: Couldn't find any package by regex 'linux-headers-5.10.17'
apt-get install build-essential
Reading package lists... Done
Building dependency tree
Reading state information... Done
build-essential is already the newest version (12.6).
0 upgraded, 0 newly installed, 0 to remove and 36 not upgraded.

Done!

Installiert mal das meta-Paket linux-generic, das zieht dann alle zum kernel-Bau benötigten Dinge hinterher - vor allen die header, aber auch u.a. modules und modules-extra. Die speziellen Versionen sind von der kernel-Version abhängig, daher kann ich hier keinen genaueren filenamen angeben.

2 Likes

Bin jetzt endlich mal ein wenig weiter gekommen.
nachdem ich mehrmals versucht habe den Kernel nach Anleitung inkl. Molulen neu zu bauen, bin ich nun nach dieser Anleitung aus den Beispielen aus der README dieses Git-Branches
https://github.com/RPi-Distro/rpi-source weiter gekommen.
also nicht den kompletten Kernel ( sondern nur die benötigten Module)

pi@raspberrypi:~ $ lsiio -v
Device 001: bme280
   in_temp_input
   in_humidityrelative_input
   in_pressure_input

Device 000: hx711
   in_voltage0_raw
   in_voltage1_raw

pi@raspberrypi:~ $

Aktuell fehlt mir noch eine nau7802.dts bzw .dtb/.dtbo,
bin mir noch nicht sicher, wie IIO-I²C-Devices in den Device-Tree hinzugefügt werden müssen.
Habe bisher nur eine .dts mit einem I²C Multiplexer und Den Nau´s gefunden.

Werde wenn ich alles geprüft und am laufen habe versuchen meinen Weg nochmal zu rekonstruieren, bin auf alle fälle in ein paar Sackgassen und halbgare Lösungsansätze getappt.

Edit der Nau7802 scheint jetzt auch mit dem iio Treiber zu gehen leider ohne den internen Temperatursensor.

Device 001: 1-002a
   in_voltage0_raw
   in_voltage1_raw

Device 002: bme280
   in_temp_input
   in_humidityrelative_input
   in_pressure_input

Device 000: hx711
   in_voltage0_raw
   in_voltage1_raw
pi@raspberrypi:~ $

finde aber den Grund nicht, warum er den Nau7802 nicht richtig benennt.
i2c-sensor-overlay.dts.txt (10,9 KB)

2 Likes

Hi Michael,

Das sieht ja astrein aus, gratuliere! Kommen da schon plausible Werte raus?

Viele Grüße,
Andreas.

P.S.:

Musstest Du irgendein Device Overlay dazu konfigurieren oder anpassen?

Ahja, das wäre exzellent! Vielen Dank schon im Voraus.

Falls ja, und Bezug nehmend auf:

Bei bienen/src/bienen/bienen.c at 110b9a4ad98f12a0c1c72e718ec1d7b891817961 · it-klinger/bienen · GitHub kann man z.B. sehen, wie die Daten ausgelesen werden, nämlich einfach aus dem Dateisystem unterhalb von bw_iio_dir. Das ist in C geschrieben natürlich alles ziemlich viel Aufwand, wenn das noch einigermaßen konfigurierbar sein darf, aber in Python wird das deutlich kompakter, egal ob mit oder ohne Hilfsbibliothek.

Die Frage ist also, bekommst Du bereits plausible Werte von den unter /sys/bus/iio/devices eingeblendeten Geräten ausgelesen?

Der Ansatz für den nächsten Schritt, das in Terkin zu integrieren, ist bereits vorbereitet.

Gerätetreiber

Bei terkin-datalogger/src/lib/terkin/sensor/linux.py at 0.12.0 · hiveeyes/terkin-datalogger · GitHub findest Du bereits einen Gerätetreiber, um Meßwerte aus dem Linux sysfs auszulesen, nämlich den für die DS18B20 Sensoren unter /sys/bus/w1/devices.

Dort müssten dann noch entsprechende Treiber für IIO-basierte Geräte zur Seite gestellt werden, um Meßwerte aus /sys/bus/iio/devices auszulesen.

Sensoradapter

Bei terkin-datalogger/src/lib/terkin/driver/ds18x20_sensor.py at 0.12.0 · hiveeyes/terkin-datalogger · GitHub sieht man, wie der sysfs-Gerätetreiber für den DS18x20 dann im Sensoradapter benutzt wird. Das müsste man dann entweder in terkin-datalogger/src/lib/terkin/driver/hx711_sensor.py at 0.12.0 · hiveeyes/terkin-datalogger · GitHub oder parallel dazu wohl ebenfalls tun, um den alternativen HX711-Auslesepfad im Framework zur Verfügung zu stellen [1]. Das wird noch etwas Arbeit.


  1. Da wir an dieser Stelle vermutlich noch keine Bus-Paradigmen über Linux IIO implementieren wollen, können wir uns die Anpassungen hinsichtlich des “Bus”-Adapters (siehe terkin-datalogger/src/lib/terkin/sensor/core.py at 0.12.0 · hiveeyes/terkin-datalogger · GitHub) vermutlich sparen. ↩︎

Ja, da kommen schon Werte raus die zumindest, vom Verständnis hin kommen. Kann gerade nicht zeigen, da die Wägezelle nicht angeschlossen ist, aber es funktioniert beim HX711 und beim Nau7802

Die device werden nach der Reihenfolge der Aktivierung in der config.txt fortlaufend benannt.
Den richtigen Namen erfährt man unter der Datei name im entsprechendem Unterordner.

pi@raspberrypi:/sys/bus/iio/devices $ ls
iio:device0  iio:device1  iio:device2
pi@raspberrypi:/sys/bus/iio/devices $ cat iio:device0/name
hx711
pi@raspberrypi:/sys/bus/iio/devices $ cat iio:device1/name
1-002a
pi@raspberrypi:/sys/bus/iio/devices $ cat iio:device2/name
bme280

über die Datei uevent lassen sich noch weitere Infos abrufen

pi@raspberrypi:/sys/bus/iio/devices $ cat  iio:device0/uevent
MAJOR=240
MINOR=0
DEVNAME=iio:device0
DEVTYPE=iio_device
OF_NAME=weight
OF_FULLNAME=/weight
OF_COMPATIBLE_0=avia,hx711
OF_COMPATIBLE_N=1
pi@raspberrypi:/sys/bus/iio/devices $ cat  iio:device1/uevent
MAJOR=240
MINOR=1
DEVNAME=iio:device1
DEVTYPE=iio_device
OF_NAME=nau7802
OF_FULLNAME=/soc/i2c@7e804000/nau7802@2a
OF_COMPATIBLE_0=nau7802
OF_COMPATIBLE_N=1
pi@raspberrypi:/sys/bus/iio/devices $ cat  iio:device2/uevent
MAJOR=240
MINOR=2
DEVNAME=iio:device2
DEVTYPE=iio_device
OF_NAME=bme280
OF_FULLNAME=/soc/i2c@7e804000/bme280@76
OF_COMPATIBLE_0=bosch,bme280
OF_COMPATIBLE_N=1

Die Verzeichnisse sehen so aus:

pi@raspberrypi:/sys/bus/iio/devices $ ls iio:device0
buffer                   dev              in_voltage0_scale_available  in_voltage1_scale_available  name     power          subsystem  uevent
current_timestamp_clock  in_voltage0_raw  in_voltage1_raw              in_voltage_scale             of_node  scan_elements  trigger
pi@raspberrypi:/sys/bus/iio/devices $ ls iio:device1
dev  in_voltage0_raw  in_voltage1_raw  in_voltage_sampling_frequency  in_voltage_scale  in_voltage_scale_available  name  of_node  power  sampling_frequency_available  subsystem  uevent
pi@raspberrypi:/sys/bus/iio/devices $ ls iio:device2
dev  in_humidityrelative_input  in_humidityrelative_oversampling_ratio  in_pressure_input  in_pressure_oversampling_ratio  in_temp_input  in_temp_oversampling_ratio  name  of_node  power  subsystem  uevent
pi@raspberrypi:/sys/bus/iio/devices $

Wie man die verfügbaren werte über das Filesystem abfragen kann habe ich noch nicht gefunden.
Könnte also etwas Arbeit werden, da ein system zu finden.

beim Nau7802 können wir noch überlegen, im Treiber den 3. Kanal im Muxer freizuschalten, um auch Temperaturwerte zu erhalten.

Vielleicht so?

# iio:device0
cat /sys/bus/iio/devices/iio:device0/in_voltage0_raw
cat /sys/bus/iio/devices/iio:device0/in_voltage1_raw
cat /sys/bus/iio/devices/iio:device0/in_voltage_scale

# iio:device1
cat /sys/bus/iio/devices/iio:device1/in_voltage0_raw
cat /sys/bus/iio/devices/iio:device1/in_voltage1_raw

ja genau so :rofl:
aber ich habe nicht rausgefunden, ob es eine Auflistung über die verfügbaren Werte wie bei lsiio gibt

pi@raspberrypi:/sys/bus/iio/devices/iio:device0/scan_elements $ lsiio -v
Device 001: 1-002a
   in_voltage0_raw
   in_voltage1_raw

Device 002: bme280
   in_temp_input
   in_humidityrelative_input
   in_pressure_input

Device 000: hx711
   in_voltage0_raw
   in_voltage1_raw

lsiio liest ja vermutlich genau diesen Dateisystembaum aus. z.B. naiverweise ganz einfach so:

ls -alF /sys/bus/iio/devices/iio:device?/in_*

Nicht ganz, da würde dann noch ein wenig mehr aufgezählt.
mom… wir haben ja den Code.
lsiio muss man vor Benutzung aus den Kernel Ressourcen selbst Compilern und kann es nicht per apt-get install installieren

pi@raspberrypi:~/linux/tools/iio $ cat lsiio.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Industrial I/O utilities - lsiio.c
 *
 * Copyright (c) 2010 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
 */

#include <string.h>
#include <dirent.h>
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include "iio_utils.h"

static enum verbosity {
        VERBLEVEL_DEFAULT,      /* 0 gives lspci behaviour */
        VERBLEVEL_SENSORS,      /* 1 lists sensors */
} verblevel = VERBLEVEL_DEFAULT;

const char *type_device = "iio:device";
const char *type_trigger = "trigger";

static inline int check_prefix(const char *str, const char *prefix)
{
        return strlen(str) > strlen(prefix) &&
               strncmp(str, prefix, strlen(prefix)) == 0;
}

static inline int check_postfix(const char *str, const char *postfix)
{
        return strlen(str) > strlen(postfix) &&
               strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;
}

static int dump_channels(const char *dev_dir_name)
{
        DIR *dp;
        const struct dirent *ent;

        dp = opendir(dev_dir_name);
        if (!dp)
                return -errno;

        while (ent = readdir(dp), ent)
                if (check_prefix(ent->d_name, "in_") &&
                   (check_postfix(ent->d_name, "_raw") ||
                    check_postfix(ent->d_name, "_input")))
                        printf("   %-10s\n", ent->d_name);

        return (closedir(dp) == -1) ? -errno : 0;
}

static int dump_one_device(const char *dev_dir_name)
{
        char name[IIO_MAX_NAME_LENGTH];
        int dev_idx;
        int ret;

        ret = sscanf(dev_dir_name + strlen(iio_dir) + strlen(type_device), "%i",
                     &dev_idx);
        if (ret != 1)
                return -EINVAL;

        ret = read_sysfs_string("name", dev_dir_name, name);
        if (ret < 0)
                return ret;

        printf("Device %03d: %s\n", dev_idx, name);

        if (verblevel >= VERBLEVEL_SENSORS)
                return dump_channels(dev_dir_name);

        return 0;
}

static int dump_one_trigger(const char *dev_dir_name)
{
        char name[IIO_MAX_NAME_LENGTH];
        int dev_idx;
        int ret;

        ret = sscanf(dev_dir_name + strlen(iio_dir) + strlen(type_trigger),
                     "%i", &dev_idx);
        if (ret != 1)
                return -EINVAL;

        ret = read_sysfs_string("name", dev_dir_name, name);
        if (ret < 0)
                return ret;

        printf("Trigger %03d: %s\n", dev_idx, name);

        return 0;
}

static int dump_devices(void)
{
        const struct dirent *ent;
        int ret;
        DIR *dp;

        dp = opendir(iio_dir);
        if (!dp) {
                fprintf(stderr, "No industrial I/O devices available\n");
                return -ENODEV;
        }

        while (ent = readdir(dp), ent) {
                if (check_prefix(ent->d_name, type_device)) {
                        char *dev_dir_name;

                        if (asprintf(&dev_dir_name, "%s%s", iio_dir,
                                     ent->d_name) < 0) {
                                ret = -ENOMEM;
                                goto error_close_dir;
                        }

                        ret = dump_one_device(dev_dir_name);
                        if (ret) {
                                free(dev_dir_name);
                                goto error_close_dir;
                        }

                        free(dev_dir_name);
                        if (verblevel >= VERBLEVEL_SENSORS)
                                printf("\n");
                }
        }
        rewinddir(dp);
        while (ent = readdir(dp), ent) {
                if (check_prefix(ent->d_name, type_trigger)) {
                        char *dev_dir_name;

                        if (asprintf(&dev_dir_name, "%s%s", iio_dir,
                                     ent->d_name) < 0) {
                                ret = -ENOMEM;
                                goto error_close_dir;
                        }

                        ret = dump_one_trigger(dev_dir_name);
                        if (ret) {
                                free(dev_dir_name);
                                goto error_close_dir;
                        }

                        free(dev_dir_name);
                }
        }

        return (closedir(dp) == -1) ? -errno : 0;

error_close_dir:
        if (closedir(dp) == -1)
                perror("dump_devices(): Failed to close directory");

        return ret;
}

int main(int argc, char **argv)
{
        int c, err = 0;

        while ((c = getopt(argc, argv, "v")) != EOF) {
                switch (c) {
                case 'v':
                        verblevel++;
                        break;

                case '?':
                default:
                        err++;
                        break;
                }
        }
        if (err || argc > optind) {
                fprintf(stderr, "Usage: lsiio [options]...\n"
                        "List industrial I/O devices\n"
                        "  -v  Increase verbosity (may be given multiple times)\n");
                exit(1);
        }

        return dump_devices();
}

Bekommst Du pylibiio · PyPI folgendermaßen installiert?

apt install python3-libiio

Erste Tests

>>> import iio
>>> iio.version

Ja

pi@raspberrypi:~/linux/tools/iio $ sudo apt install python3-libiio
Paketlisten werden gelesen... Fertig
Abhängigkeitsbaum wird aufgebaut.
Statusinformationen werden eingelesen.... Fertig
Die folgenden zusätzlichen Pakete werden installiert:
  libiio0 libserialport0
Die folgenden NEUEN Pakete werden installiert:
  libiio0 libserialport0 python3-libiio
0 aktualisiert, 3 neu installiert, 0 zu entfernen und 0 nicht aktualisiert.
Es müssen 87,2 kB an Archiven heruntergeladen werden.
Nach dieser Operation werden 251 kB Plattenplatz zusätzlich benutzt.
Möchten Sie fortfahren? [J/n] j
Holen:1 http://ftp.halifax.rwth-aachen.de/raspbian/raspbian buster/main armhf libserialport0 armhf 0.1.1-3 [42,3 kB]
Holen:2 http://mirror.de.leaseweb.net/raspbian/raspbian buster/main armhf libiio0 armhf 0.16-1 [35,4 kB]
Holen:3 http://ftp.halifax.rwth-aachen.de/raspbian/raspbian buster/main armhf python3-libiio all 0.16-1 [9.480 B]
Es wurden 87,2 kB in 1 s geholt (111 kB/s).
Vormals nicht ausgewähltes Paket libserialport0:armhf wird gewählt.
(Lese Datenbank ... 222438 Dateien und Verzeichnisse sind derzeit installiert.)
Vorbereitung zum Entpacken von .../libserialport0_0.1.1-3_armhf.deb ...
Entpacken von libserialport0:armhf (0.1.1-3) ...
Vormals nicht ausgewähltes Paket libiio0:armhf wird gewählt.
Vorbereitung zum Entpacken von .../libiio0_0.16-1_armhf.deb ...
Entpacken von libiio0:armhf (0.16-1) ...
Vormals nicht ausgewähltes Paket python3-libiio wird gewählt.
Vorbereitung zum Entpacken von .../python3-libiio_0.16-1_all.deb ...
Entpacken von python3-libiio (0.16-1) ...
libserialport0:armhf (0.1.1-3) wird eingerichtet ...
libiio0:armhf (0.16-1) wird eingerichtet ...
python3-libiio (0.16-1) wird eingerichtet ...
Trigger für libc-bin (2.28-10+rpi1) werden verarbeitet ...
pi@raspberrypi:~/linux/tools/iio $
pi@raspberrypi:~/linux/tools/iio $ python3
Python 3.7.3 (default, Jan 22 2021, 20:04:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import iio
>>> iio.version
(0, 16, 'v0.16')
>>>

Wunderbar. Dann lass doch mal jenes Beispiel laufen:

1 Like

BINGO!!!

local: - hx711 - timestamp
local: - hx711 - voltage0
local: - hx711 - voltage1
local: - 1-002a - voltage0
local: - 1-002a - voltage1
local: - bme280 - humidityrelative
local: - bme280 - pressure
local: - bme280 - temp
2 Likes

Bei libiio kommen u.U. ein paar Werkzeuge gleich automatisch mit:

Danke. Das ist ja eine hervorragende Grundlage für einen entsprechenden Treiberadapter. Vielleicht kannst Du das Codebeispiel noch erweitern, dass als dritte Spalte der entsprechende Sensorwert ausgegeben wird?

Hinweis: dir(variable_name) ist Dein Freund, z.B. print(dir(dev)) oder print(dir(chan)). Im Zweifel ist der Meßwert einfach direkt chan.value ;]