Unterbrechungsfreie Spannungsversorgung "PIUSV+" direkt ansteuern

Aus FHEMWiki
Wechseln zu: Navigation, Suche
Unterbrechungsfreie Spannungsversorgung "PIUSV+
Foto: RITTER Elektronik GmbH

Idee

Hier wird der direkte FHEM-Zugriff auf die unterbrechungsfreien Spannungsversorgung PIUSV+, die früher von CW2 und aktuell von Ritter Elektronik hergestellt wird, beschrieben. Die PIUSV+ wird über Reichelt Elektronik vertrieben (https://www.reichelt.de/raspberry-pi-usv-rpi-usv-p169883.html).
Der direkte Zugriff bedeutet, dass die Kommunikation vollständig über die I2C-Schnittstelle erfolgt, wie es die Originalsoftware "piupsmon" auch bewerkstelligt.

Der direkte Zugriff hat folgenden Vorteile:

  • Die Originalsoftware "piupsmon", die für den Betrieb der USV vorgesehen ist, wird nicht bzw. nicht mehr benötig (ggf. sollte diese mit sudo apt-get --purge piupsmon deinstalliert werden). Die Originalsoftware, die vom ursprünglichen Hersteller CW2 stammt und nicht mehr supportet wird, arbeitet nicht immer zuverlässig.
  • Es muss kein zusätzliches Skript in die piupsmon-Software zum sauberen Runterfahren des FHEM-Servers eingebunden werden.
  • Die direkte Ansteuerung bietet mehr Möglichkeiten (auch für eigene Erweiterungen). So wird beispielsweise nach einem Spannungsausfall wie hier beschrieben der Shutdown nicht nach einer festen Zeit ausgelöst, sondern beim Unterschreiten einer bestimmten Akkusspannung.
  • Es können diverse Werte wie Versorgungspannung, Pi-Spannung, Pi-Strom, Pi-Leistung und Akku-Status ausgelesen bzw. angezeigt werden.
  • Durch die eigene Kontrolle über die USV wird die Sicherheit erhöht, wodurch ein Crash des Betriebssystems auf dem Systemspeicher (SD-Karte oder externer USB-Speicher) noch effektiv verhindert werden kann.

Umsetzung

Die hier vorgestellte Lösung ist als Beispiel zu verstehen. Je nach Anforderung können/müssen entsprechend Anpassungen durchgefüht werden.

Da die Kommunikation über die I2C-Schnittstelle des Raspberry Pi's erfolgt, muss ein geeignetes I2C Interface Modul (z.B. RPII2C, FRM oder NetzerI2C) zuvor definiert werden (siehe Commandref). Nachfolgend wurde der Zugriff auf die I2C-Schnittstelle über das Modul RPII2C realisiert.

In die Datei "99_myUtils.pm" müssen die folgende Funktionen kopiert werden:

sub USV_Werte_auslesen($$)
{
my ($USV_Netzteilspannung_Error_Counter, $USV_Akkuspannung_Error_Counter) = @_;

my $content;
my $datagramm;
my $u_akku_high;
my $u_akku_low;
my $u_akku;
my $u_netz_high;
my $u_netz_low;
my $u_netz;
my $u_pi_high;
my $u_pi_low;
my $u_pi;
my $i_pi_high;
my $i_pi_low;
my $i_pi;
my $p_pi;
my $status;
my $usv_meldung;

# Werte lesen
fhem("set PIi2c_1 writeByte 18 02");

$content=fhem("get PIi2c_1 readblock 18 10",1);
$content=~ m/received : (\d+).(\d+).(\d+).(\d+).(\d+).(\d+).(\d+).(\d+).(\d+).(\d+) |/is;
$datagramm=$content;
$u_akku_high=$1;
$u_akku_low=$2;
$u_netz_high=$7;
$u_netz_low=$8;
$u_pi_high=$5;
$u_pi_low=$6;
$i_pi_high=$3;
$i_pi_low=$4;

$u_akku=((($u_akku_high & 0x7F)*256+$u_akku_low)/1000);
$u_netz=((($u_netz_high & 0x7F)*256+$u_netz_low)/1000);
$u_pi=((($u_pi_high & 0x3F)*256+$u_pi_low)/1000);
$i_pi=((($i_pi_high & 0x3F)*256+$i_pi_low)/1000);
$p_pi=floor($u_pi*$i_pi*1000+0.5)/1000;

if ($u_akku<5 && $u_akku>2.5 && $u_netz<6 && $u_pi<6 && $i_pi<3 && not($u_pi==0 && $i_pi>0) && not($i_pi==0 && $u_pi>0))
	{
	if ($u_netz<0.1)
		{
		$USV_Netzteilspannung_Error_Counter++;
		if ($USV_Netzteilspannung_Error_Counter==2) { $usv_meldung="FHEM: USV-Netzspannung fehlt!"; }
		fhem("set USV_Netzteilspannung_Error_Counter $USV_Netzteilspannung_Error_Counter");
		}
	else
		{
		$USV_Netzteilspannung_Error_Counter=0;
		fhem("set USV_Netzteilspannung_Error_Counter $USV_Netzteilspannung_Error_Counter");
		}
	
	if ($u_akku<3.0)
		{
		$USV_Akkuspannung_Error_Counter++;
		if ($USV_Akkuspannung_Error_Counter==2) { $usv_meldung="FHEM: USV-Akkuspannung zu niedrig!"; }
		fhem("set USV_Akkuspannung_Error_Counter $USV_Akkuspannung_Error_Counter");
		}
	else
		{
		$USV_Akkuspannung_Error_Counter=0;
		fhem("set USV_Akkuspannung_Error_Counter $USV_Akkuspannung_Error_Counter");
		}
	
	if (($u_netz<0.1) && ($u_akku>3.0) && ($u_akku<3.5)) # shutdown-Bedingung ermitteln
		{
		$usv_meldung="Raspberry Pi und USV wurden wegen Stromausfall abgeschaltet!";
		USV_abschalten();
		rasp_shutdown();
		}
		
	$u_akku=$u_akku." V (3.0 ... 4.2 V)";
	$u_netz=$u_netz." V (5V)";
	$u_pi=$u_pi." V (5V)";
	$i_pi=$i_pi." A";
	$p_pi=$p_pi." W";

	fhem("set PIi2c_1 writeByte 18 00");
	$content=fhem("get PIi2c_1 read 18",1);
	$content=~ m/received : (\d+) |/is;
	$status = $1;

	fhem("set USV_Datagramm $datagramm");
	fhem("set USV_Akkuspannung $u_akku");
	fhem("set USV_Netzteilspannung $u_netz");
	fhem("set USV_Spannung $u_pi");
	fhem("set USV_Strom $i_pi");
	fhem("set USV_Leistung $p_pi");
	fhem("set USV_Meldung $usv_meldung");
	fhem("set USV_Statusregister $status");
	}
}


sub USV_abschalten()
{
# USV-Abschaltung auslösen (Abschaltung erfolgt hier nach 21 Sekunden)
fhem("set PIi2c_1 writeByte 18 10");
fhem("set PIi2c_1 writeByte 18 15");
}

sub rasp_shutdown()
{
my @processes = `sudo shutdown -h now`;
}

Anmerkung: Um den Raspberry wie hier direkt vom FHEM aus über den Befehl "sudo shutdown -h now" auszuschalten, muss in die Datei /etc/sudoers.d/010_pi-nopasswd hinter dem Eintrag

pi ALL=(ALL) NOPASSWD: ALL

folgendes ergänzt werden

pi ALL=(ALL) NOPASSWD: ALL
fhem ALL=(ALL) NOPASSWD: /sbin/reboot, /sbin/shutdown, /sbin/halt

In der Datei "fhem.cfg" kann dann die Anzeige der USV-Parameter und die Abschaltung der USV beispielsweise folgendermaßen realisiert werden:

#i2C-Schnittstelle einrichten
define PIi2c_1 RPII2C 1

# PI USV+ Datagramm
define USV_Datagramm dummy

# PI USV+ Netzteilspannung
define USV_Netzteilspannung dummy
define USV_Netzteilspannung_Error_Counter dummy
define USV_Netzteilspannung_Error_Counter_Init notify global:INITIALIZED set USV_Netzteilspannung_Error_Counter 0

# PI USV+ Akkuspannung
define USV_Akkuspannung dummy
define USV_Akkuspannung_Error_Counter dummy
define USV_Akkuspannung_Error_Counter_Init notify global:INITIALIZED set USV_Akkuspannung_Error_Counter 0

# PI USV+ Spannung
define USV_Spannung dummy

# PI USV+ Strom
define USV_Strom dummy

# PI USV+ Leistung
define USV_Leistung dummy

# PI USV+ Statusregister
define USV_Statusregister dummy

# PI USV+ Meldung
define USV_Meldung dummy

# PI USV+ Werte aktualisieren und ggf. Abschaltung auslösen
define USV_Werte_auslesen at +*00:00:05 { USV_Werte_auslesen(Value("USV_Netzteilspannung_Error_Counter"),Value("USV_Akkuspannung_Error_Counter")) }

Anmerkung:
Der Inhalt in USV_Meldung bleibt hier so lange stehen, bis dieser durch einen selbst zu realisierenden set-Befehl gelöscht wird. Dadurch können auch kurze Spannungsunterbrechungen erfasst werden.
Der Wert des Statusregister wird hier als Dezimalzahl dargestellt und muss zur weiteren Verarbeitung ggf. noch binär ausdekodiert werden.

Registerbeschreibung (für eigene Erweiterungen)

Die PIUSV+ kann per I2C-Schnittstelle über die Adresse "0x18" angesprochen werden.
Achtung: lt. Foren wurde bei älteren PIUSV+ teilweise auch die Adresse "0x30" verwendet.
Das nächste Byte, das hinter der Adresse an die PIUSV+ gesendet werden muss, ist als Befehl zu verstehen.

Folgende Befehle sind möglich:

"Status lesen"              0x00   danach muss 1 Byte (Statusregister) gelesen werden
                                   Die einzelnen Bits des Statusregister haben folgende Bedeutung:
                                     Bit0:  Primäre Spannungsversorgung (5V) aktiv
                                     Bit1:  Sekundäre Spannungsversorgung (5V...25V) aktiv
                                     Bit2:  Akkuspannung zu niedrig
                                     Bit3:  Akku wird geladen
                                     Bit4:  Akku ist voll
                                     Bit5:  Taster S1 an der USV betätigt

"Firmare Version lesen"     0x01   danach müssen 12 Bytes (Zeichen) gelesen werden

"Werte lesen"               0x02   danach müssen 10 Byte gelesen werden
                                   Die zu lesenden Bytes haben folgende Reihenfolge und Bedeutung:
	                             HIGH_BYTE (UINT) Akku-Spannung in mV	
	                             LOW_BYTE  (UINT) Akku-Spannung in mV
                                     HIGH_BYTE (UINT) Strom in mA, der zum Raspberry Pi fließt
                                     LOW_BYTE  (UINT) Strom in mA, der zum Raspberry Pi fließt
                                     HIGH_BYTE (UINT) 5V-Versorgungsspannung in mV (USV-Spannungversorung für Raspberry Pi)
                                     LOW_BYTE  (UINT) 5V-Versorgungsspannung in mV (USV-Spannungversorung für Raspberry Pi)
                                     HIGH_BYTE (UINT) Spannung in mV am Micro USB-Stecker der USV (primäre Spannungsversorgung)
                                     LOW_BYTE  (UINT) Spannung in mV am Micro USB-Stecker der USV (primäre Spannungsversorgung)
                                     HIGH_BYTE (UINT) Spannung in mV externe Spannungsversorgung (sekundäre Spannungsversorgung)
                                     LOW_BYTE  (UINT) Spannung in mV externe Spannungsversorgung (sekundäre Spannungsversorgung)

"USV-Abschaltung auslösen"  0x10   danach muss 1 Byte geschrieben werden
                                   Folgende Werte sind möglich:
                                     0x00 (default Wert): USV wird nach einer Zeit von 30sec. abgeschaltet
                                     0x01...0xFF:	  USV wird nach einer Zeit von 1...255 Sekunden abgeschaltet
                                   Anmerkung: Durch das zeitverzögerte Auschalten der USV kann sichergestellt werden,
                                              dass ein zuvor ausgelöster Shutdown des Raspberry Pi abgeschlossen ist.

Ausblick

Es wäre sinnvoll, das hier Beschriebene in einem eigenen Modul z.B. "52_I2C_PIUPS_PLUS.pm" unterzubringen, um den Zugriff noch eleganter und vollständiger zu realisieren.