RGB-LED unter Python und unter C#

Um die Möglichkeiten, auf dem Raspberry Pi in C# zu programmieren, auszutesten, habe ich ein einfaches Python-Projekt genommen und versucht, es mit C# umzusetzen.

Das Projekt kommt aus dem Freenove Starter Kit, Kapitel 9: Potentiometer und RGB-LED.

Das sah eigentlich nach einem guten Startprojekt aus. Nicht zu simpel (wie eine LED zum Blinken zu bringen) und auch nicht zu schwer, da nicht allzu viel Hardware und Code zum Einsatz kommt.

Zunächst wurde die Hardware vorbereitet und der Python-Code ausprobiert. Das funktionierte alles auf Anhieb und machte Mut für die C#-Schritte.

Mit der .net-Vorbereitung sollte schon mal ein guter Grundstein gelegt sein. Da die Hardware schon vorbereitet ist, kann es direkt mit der Projekterstellung in Visual Studio losgehen:

Ganz mutig .net9:

Damit die GPIO-Pins und die daran angeschlossene Hardware unter C# zur Verfügung steht, müssen dem Projekt zwei Pakete hinzugefügt werden:

Und dort in der Suchmaske im Reiter „Durchsuchen“ iot.device.bingings

und system.device.GPIO

hinzufügen. Als letzter Vorbereitungsschritte muss jetzt noch im Konfigurationsmanager die ARM64-Umgebung hinzugefügt werden.

Nun kann mit der Programmierung gestartet werden:

Der erste Block ist recht einfach und wenig überraschend:

Mit den using-Zeilen wird der Zugriff auf die benötigten Elemente aus den Paketen erleichtert, die Konstanten helfen bei der Programmierung und das erzeugte I2CDevice wird für den Analog->Digitalkonverter benötigt:

using System.Device.I2c;
using System.Device.Pwm.Drivers;

// Definitionen der Adressen und Pin-Zuordnungen für den PCF8591
const int BUS_ID = 1;
const int PCF8591_ADRESS = 0x48;
const int ANALOG_IN_MASKE = 0x40;

const int KANAL_ROT = 0;
const int KANAL_GRÜN = 1;
const int KANAL_BLAU = 2;

I2cDevice I2CDevice = System.Device.I2c.I2cDevice.Create(new System.Device.I2c.I2cConnectionSettings(BUS_ID, PCF8591_ADRESS));

// Defintionen für den GPIOController und die GPIO-Pins
const int LED_ROT = 22;
const int LED_GRÜN = 27;
const int LED_BLAU = 17;

Etwas überraschender ist der zweite Teil:

In der Freenove-Python-Version wird einfach nur drei GPIO-Pins für die Ausgabe der umgewandelten analogen Werte verwendet. Bei der Entwicklung des Programms unter C# führte das, wenig überraschend, zu keinem richtigen Ergebnis: Ein GPIO-Pin kann nur zwei Zustände annehmen: High/Low, true/false, 1/0, 3,3V/0V. Aber nichts dazwischen. Unter Python scheint das irgenwie zu funktionieren.

Die Magie liegt in der (versteckten) Anwendung der Pulsweitenmodulation. Und die können mittels der SoftwarePwmChannel auf die GPIO-Pins gelegt werden. Also identisch mit der Python-Programmierung, nur unter einem anderen Namen:

// Kanäle für die Ausgabe der Werte
SoftwarePwmChannel ErzeugePWMKanal(int pinNr)
{
SoftwarePwmChannel pwmKanal = new SoftwarePwmChannel(

pinNr, 400, 0.5, true);
pwmKanal.Start();
return pwmKanal;
}

SoftwarePwmChannel PwmRot = ErzeugePWMKanal(LED_ROT);
SoftwarePwmChannel PwmGrün = ErzeugePWMKanal(LED_GRÜN);
SoftwarePwmChannel PwmBlau = ErzeugePWMKanal(LED_BLAU);

Der SoftwarePwmChannel wird mit der Pin-Nr, der Frequenz für die Pulsweitenmodulation (400 Hz), dem dutyCycle-Wert von 0,5 und der Verwendung eines Präzisionstimer (true-Parameter) konfiguriert.

Der dritte Teil hat etwas Datenanalysle bei den Tests erfordert.

Der verwendete Analog-Digitalwandler erfordert ein bestimmtes Verfahren zum Auslesen der Werte:

  1. Vom I2C-Busmaster (Der Raspberry) wird der Baustein und der Kanal zum Lesen aufgerufen. Dazu schreibt der Busmaster den Lesebefehl auf dem Bus.
  2. Der PCF8591 wird nun beim Lesen die Werte des gewünschten Kanals zurückgeben.

Dafür kann die I2CDevice.WriteRead-Funktion genutzt werden. Die Erfahrung beim Testen hat jedoch gezeigt, das der erste gelesene Wert noch vom vorher angesprochenen Kanal kommt. Deshalb liest die Routine zwei Werte aus (Ergebnis = new byte[2]) und verwendet nur den zweiten Wert:

// Lesen und ausgeben der Werte eines Kanals
void KanalLesenUndAusgeben(
int kanal,
string name,
SoftwarePwmChannel pwmKanal)
{
Span Lesebefehl = new byte[1];
Lesebefehl[0] = (byte)(ANALOG_IN_MASKE + kanal);
Span Ergebnis = new byte[2];
I2CDevice.WriteRead(Lesebefehl, Ergebnis);
Console.WriteLine(name + "(" + Lesebefehl[0] + "):" +
Ergebnis[1].ToString("000") + ", ");
pwmKanal.DutyCycle = Ergebnis[1] / 255f;
}

Das war die ganze erforderliche Magie. Mit dieser Umsetzung funktionert das Freenove-Projekt unter C# exakt gleich:

Console.Clear();
Console.WriteLine("Hello, I2C (Strg-C für Abbruch):");
while (true)
{
Console.SetCursorPosition(0, 2);
KanalLesenUndAusgeben(KANAL_ROT, "rot ", PwmRot);
KanalLesenUndAusgeben(KANAL_GRÜN, "gruen", PwmGrün);
KanalLesenUndAusgeben(KANAL_BLAU, "blau ", PwmBlau);
System.Threading.Thread.Sleep(100);
}

Nun kann das Projekt veröffentlicht werden:

Am einfachsten in einem Verzeichnis direkt auf dem Raspberry Pi:

Über Laufwerk R ist in dieser Installation der Raspberry auf dem Windowsrechner zu erreichen.

In diesem Verzeichnis kann nun auf dem Raspberry das Programm gestartet werden:

dotnet RGBLED.dll

Diese erste Umsetzung hat unter C# sehr gut funktioniert. Es war zwar etwas Jugend-forscht-Motivation erforderlich (Einlesen der Werte über den I2C-Bus aus dem PCF8591-Baustein, Ausgabe der Werte über die Pulsweitenmodulatin), aber nichts, wo vor man sich fürchten muss.

Für die verwendete Pulsweitenmodulation wurde unter Einsatz etwas älterer Elektronikausrüstung ein kleines Video erstellt:

Auf zum nächsten Projekt.

Lied des Monats

Diese Band ist in der Liste Lied des Monats schon lange fällig: Dire Straits. Sie werden sicher noch öfter hier auftreten. Ihren Anfang machen sich mit den Sultans of Swings:

Mai 2025

Eine in Europa recht unbekannte Musikerin: Jewel. Musikerin durch und durch. Mit einem, sagen wir mal durchaus anstrengendem Lebenslauf.

Ralfs Raspberry PI

(Dieser Artikel ist noch in Bearbeitung)

Anleitungen und Unterstüzung findet manimNetz zuhauf. Schnell ist etwas installiert und konfiguriert und schwubs: Das Projekt läuft. Dank der Möglichkeit, SD-Karten einzusetzen, kommt man schnell auf die Idee, es noch enmal, vielleicht unter leicht anderer Konfiguration zu probieren. Wie war das noch beim ersten mal? Was habe ich wie konfiguriert? Um das zu erleichtern, schreibt man am besten einfach alles auf.

Mein aktueller Raspberry PI ist ein PI5 mit 8GB Ram und dem Geekworm X1001 PCIe to M.2 NVMe Key-M SSD Shield, der aktuell noch etwas hemdsärmelig auf einem Holzbrett montiert ist (ein Raspberry- Pi-Koffer ist in Arbeit):

Zunächst muss eine SD-Karte für die Konfiguration und den Bootprozess vorbereitet werden.

Jetzt geht es weiter mit ein paar Grundkonfigurationsschritten.

Als C#- und .net-Fan darf natürlich auch die Konfiguration dafür nicht fehlen.

Ein paar wichtige Linux-Befehle werde ich noch an anderer Stelle zusammenfassen (sudo, nano-Editor, …).

Die ersten Experimente liefen, unter Python mit MU, schon mal rechts vielversprechend:

(Bilder folgen) (Wichtig: Unter Einstellungen-> Raspberry Pi-Konfiguration im Reiter Schnittstelle I2C-Schnittstellen aktivieren!

.net auf dem Raspberry Pi 5

(Dieser Artikel ist noch in Bearbeitung)

Da Pete Gallagher von Pete Codes sehr gute Vorarbeit geleistet hat, ist der Raspberry Pi 5 nach wenigen Schritten für .Net bereit:

Auf seiner Seite Install-Dotnet gibt es ein install-Script für .net8:

wget -O - https://raw.githubusercontent.com/pjgpetecodes/dotnet8pi/main/install.sh | sudo bash

und Wunder oh Wunder, es gibt auch ein Script für .net9:

wget -O - https://raw.githubusercontent.com/pjgpetecodes/dotnet9pi/main/install.sh | sudo bash
Anschließend noch das GPIO-Paket hinzufügen

dotnet add package System.Device.Gpio
und schon kann es losgehen.

Ein paar grundsätzliche Konfigurationsschritte

(Dieser Artikel ist noch in Bearbeitung)

Nachdem wir die SD-Karte für den Raspberry PI vorbereitet haben (hier) und der Raspberry Pi erfolgreich gebootet hat, können wir mit ein paar wichtigen Schritten fortfahren.

Testen der SSH-Konfiguration

Ist alles korrekt konfiguriert (der Raspberry PI ind der PC befinden sich im gleichen Netzwerk), wir kennen den Hostnamen und den Benutzernamen samt Passwort, können wir mit dem Programm PUTTY die Konfiguration testen.

So sollte es anschließend aussehen.

Remotedesktop

Um den Remotedesktop unter Windows nutzen zu können und so den Raspberry Pi ohne weitere Tastatur, Mouse und Monitor nutzen zu können, müssen wir xrdp installieren

sudo apt install xrdp

Zum Abschluß starten wir den Dienst:

sudo service xrdp start

Mit

ifconfig

können wir den Erfolg überprüfen.

Jetzt kann es unter Windows weitergehen:

Auf dem zweiten Reiter (Anzeige) können wir auch die Bildschirmauflösung einstellen:

Damit wir nicht zwei Fenster gleichzeitig offen haben, schalten wir am Raspberry Pi den automatischen Login aus:

Bei der Gelegenheit stellen wir den Raspberry Pi noch auf deutsch ein:

Samba installieren

Damit wir den Raspberry unter Windows im Explorer z.B. als Laufwerk einbinden können, installieren und konfigurieren einen Samba-Server:

sudo apt install samba

Dort tragen wir den folgenden Abschnitt ein (für benutzer muss der Benutzername eingetragen werden):

[benutzer]
path=/home/benutzer
browsable=yes
writeable=yes
create mask=0777
directory mask = 0777

Außerdem muss noch das Passwort gesetzt werden:

sudo smbpasswd -a benutzer

In der Eingabekonsole auf dem Windowsrechner können wir für den Raspberry Pi ein Laufwerk hinzufügen (hostname, benutzer und Passwort entsprechend anpassen):

net use R: \\hostname\benutzer /user:benutzer Passwort /persistent:yes

Im Netzwerk einbinden

Mit dem folgenden Befehlen sollte der Raspberry Pi auch in der Netzwerkansicht zu finden sein. Da scheint es allerdings noch ein Problem zu geben. Liegt eventuell an der in unserem Netzwerk eingerichtetn Arbeitsgruppe

sudo apt install wsdd
sudo service wsdd start

SSD

In unserem Raspberry Pi ist ein Geekworm X1001 PCIe to M.2 NVMe Key-M SSD Shield eingebaut.

Auf den ersten Blick scheint alles in Ordnung zu sein (die Platte ist durch den Befehl lsblk samt Partitionen zu sehen), aber beim Zugriff kommt eine Fehlermeldung („Der Pfad ist ungültig“)

Das liegt daran, das in der Standardkonfiguration der PCIe-Anschluß nicht aktiviert ist.

Das ist einfach über den nano-Editor nachzuholen:

sudo nano /boot/firmware/config.txt

#enable PCIe
dtparam=pciex1

Vorbereitung des Betriebssystems

Der Raspberry Pi Imager erleichtert die Vorbereitung des Betriebssystems auf der SD-Karte erheblich.

  1. Imager starten
  2. Modell auswählen (in meinem Fall Raspberry Pi 5)
  3. Betriebssystem aus wählen


  4. SD-Karte auswählen

    Nach dem Klick auf „Weiter“ wird noch nachgefragt, ob man die Einstellungen bearbeiten will. Und ja, das will man:
  5. In den Einstellungen gibt es drei Reiter:
    • Allgemein
      Die Werte Hostname, Benutzername und Wifi (falls vorhanden) kann man nach seinen Wünschen anpassen. Den Hostnamen und auch den Benutzernamen lohnt es sich anzupassen. So kann man seinen Pi im Netzwerk leichter erkennen und vom Standardbenutzernamen abzuweichen ist aus Sicherheitsgründen immer eine gute Idee:
    • Dienste:
      Auf jeden Fall SSH (Secure Shell) aktivieren
    • Optionen:
      Ob man so nett ist, dem Imager zu erlauben die Telemetrie-Daten zu senden, sollte jeder für sich entscheiden
  6. Nach dem Klich auf „Speichern“ und anschießendem Klich auf „Ja“

    wird man gefragt, ob man wirklich die Daten auf der SD-Karten überschreiben will:

    Mit Klick auf „Ja“ geht es los:

Damit haben wir die SD-Karte vorbereitet und wir können den Raspberry jetzt mit ihr starten…

Lied des Monats

Eine in Europa recht unbekannte Musikerin: Jewel. Musikerin durch und durch. Mit einem, sagen wir mal durchaus anstrengendem Lebenslauf.

April 2025

So viele schöne Lieder habe die beiden hinterlassen. Stimmgewaltige Wortakrobatik. Leider werden wir das erst im Himmel wieder live hören hören.

März 2025

When the night has been too lonely
And the road has been too long
And you think that love is only
For the lucky and the strong

Just remember in the winter
Far beneath the bitter snows
Lies the seed that with the sun’s love
In the spring becomes the rose

Bette Midler: The Rose

Bette Midler: The Rose