vcontrold mit Raspberry Pi - openv/openv GitHub Wiki
Der Raspberry Pi eignet sich nicht nur als stromsparendes Embedded System zum Erfassen der Heizungsdaten, sondern bietet auch die Möglichkeit der grafischen Darstellung und Auswertung.
In der Heizung ist bei mir immer noch ein alter Windows XP PC im Einsatz. Nicht nur dass dieser unnötig viel Energie verbraucht: im Frühling 2014 ist auch das Aus von Windows XP gegeben. Weil es die Synology USB Station 2 nicht bis in die Heizung geschafft hat und ich schon seit Sommer 2012 zwei Raspi’s habe (Rev. 1 mit 256MB RAM), soll jetzt ein Raspi für vcontrold eingerichtet werden. Ich hatte das schon vor langem einmal durchgespielt, doch nun soll alles von Anbeginn dokumentiert werden und auch bisherige Diskussionen aus dem Forum berücksichtigt werden.
Voraussetzungen Hardware:
- Raspberry Pi (Zero, v1, v2 oder v3)
- ggf. Tastatur und Bildschirm
- Netzwerkverbindung oder WLAN Dongle
- Netzteil (wichtig: stabile Stromversorgung, min 1A)
- 4GB SD Karte (oder grösser)
- Optolink Adapter mit USB (Optolink über GPIO wäre auch möglich, wird hier aber nicht weiter verfolgt)
- Vitotronic-Steuerung, welche von vcontrold unterstützt wird (hier mit Vitotronic 200 KW2 verifiziert) Software:
- Raspbian (Für Raspberry Pi angepasstes Debian 7 aka Wheezy)
- SW zum Bilden von vcontrold
- Weitere SW nach Bedarf (rrd, munin, …)
Solange es kein fertiges vcontrold image zur Installation gibt, ist es am einfachsten, vcontrold auf dem Raspberry Pi selber zu bilden. vcontrold ist so schlank, dass sich ein Cross-Compiling auf einem PC nicht lohnt.
Ich verwende hier Raspbian (Debian Wheezy for Raspberry) und zwar direkt das Raspbian Image. Bei Installation von Raspbian via NOOBS reicht eine 4GB Speicherkarte nicht mehr (Dez. 2013). Aktuelles Raspbian Image herunterladen und mit Win32DiskImager (Windows) oder dd (Linux) auf die SD Karte übertragen Vor dem Entfernen der Speicherkarte evtl. /boot/config.txt anpassen z.B.
disable_overscan=1 (vermeidet schwarzen Rand) hdmi_force_hotplug=1 (HDMI auch dann aktiv, wenn beim booten noch nicht angeschlossen)
Ich mache auch immer einen Kommentar in die config.txt Datei und setzte ein Disk-Label, damit ich die SD-Karten (es liegen einige solche herum) mit jedem PC identifizieren kann.
Nach dem ersten Start mit der neu aufgesetzten SD Karte die übliche Einstellungen vornehmen mit „sudo raspi-config“:
- Passwort für user pi ändern
- Tastaturlayout und Zeitzone anpassen
- Filesystem expandieren (Neustart nötig)
Beim Neustart wird das Filesystem erweitert, so dass die gesamte Größe der SD Karte genutzt wird. Ein Neustart kann auf der Kommandozeile jederzeit mit "sudo shutdown –r now" gemacht werden. Vor dem Ausschalten muss mit "sudo shutdown –h now" das System sauber heruntergefahren werden. Sonst kann es passieren, dass das System nicht mehr hochfährt und auch die Daten nicht wiederhergestellt werden können (Einmaliges Backup des neuen Image und regelmässiges Backup der Daten sind wichtig).
Nach dem Neustart System aktualisieren mit:
sudo apt-get update
sudo apt-get upgrade
Falls Pakete als “not upgraded” aufgezählt werden, können diese mit sudo apt-get dist-upgrade aktualisiert werden.
Weitere Tipps:
- Um den Raspi mit SSH (PuTTY) zu administrieren oder generell um ihn im Netz zu finden empfiehlt sich eine fixe IP Adresse zu vergeben. Am besten wird im DHCP Server (Router) eine feste Adresse eingestellt (die MAC Adresse, wenn nicht im Router ersichtlich) kann im Raspi mit ifconfig auf der Kommandozeile ermittelt werden (HWaddr).
- Für den Datei-Zugriff vom normalem Rechner (z.B. log-Dateien, xml-Dateien usw.) empfiehlt sich die Installation des Samba-Servers. Dann kann man den Raspi z.B. als Netzwerklaufwerk am Windows-Rechner anmelden.
- Unter Windows kann mit dem Win32DiskImager auch ein Image von einer SD Karte erstellt werden.
Bevor folgende Pakete installiert werden, kann auch mit dpkg –l | grep <packet>
in den bereits installierten Paketen nach gesucht werden.
- git
- automake
- autoconf
- build-essential (schon installiert, Dez. 2013)
- cmake
- libxml2-dev
- telnet
sudo apt-get install git automake autoconf build-essential cmake telnet libxml2-dev
- docutils-common (optional zur Generierung der Manpages)
sudo apt-get install docutils-common
Als user pi einloggen
Ein Unterverzeichnis erstellen z.B. /home/pi/openv und in dieses Verzeichnis wechseln:
mkdir openv
cd openv
Source mit 'git clone' holen:
git clone https://github.com/openv/vcontrold.git
und ins neu erstellte Verzeichnis wechseln:
cd vcontrold
Hier nun den Build-Instruktionen in doc/INSTALL.md folgen. Möchte man eine Installation ohne Manpages, mit vsim Programm und in das Default-Zielverzeichnis /usr/, sollten die folgenden Befehle zum Ziel führen.
mkdir build
cd build
cmake -DVSIM=ON -DMANPAGES=OFF ..
make
sudo make install
Hinweis: für die Erstellung von Manpages (oben ausgeschaltet) müssten weitere Bibliotheken installiert werden (s.o.)
Einfacher als das manuelle Compilieren (oben) ist die Installation mit einem (zu Distribution und Maschine) passenden Paket:
Etwa für Raspberry Pi mit ARM CPU - unter Debian:
wget https://github.com/openv/vcontrold/releases/download/v0.98.11/vcontrold_0.98.11-7_armhf.deb
sudo apt install ./vcontrold_0.98.11-7_armhf.deb
Wenn alles erfolgreich durchgeführt wurde, dann sind die ausführbaren Dateien vcontrold, vclient und vsim erstellt worden. vcontrold und vclient sind in /usr/bin kopiert worden und deshalb von überall her ausführbar
Den Optolink Adapter anschließen und mit lsusb prüfen, ob er erkannt worden ist, z.B.:
cd ~/openv/vcontrold-code/vcontrold
lsusb
Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 007: ID 046d:c31d Logitech, Inc.
Bus 001 Device 008: ID 045e:00cb Microsoft Corp. Basic Optical Mouse v2.0
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 004: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB
Bus 001 Device 005: ID 0403:6001 Future Technology Devices International, Ltd FT232 USB-Serial (UART) IC
Bus 001 Device 006: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB
Mit dmesg die Schnittstelle ermitteln z.B:
pi@raspberrypi ~/openv/vcontrold-code/vcontrold $ dmesg | grep "now attached"
usb 1-1.3.1: FTDI USB Serial Device converter now attached to ttyUSB0
Anmerkung zur Schnittstellenbenennung: Seit Debain Whezzy betreibe ich nun meinen Raspberry. Jedes Upgrade führte dazu, dass diese Rules nicht mehr funktionierten und überarbeitet werden mussten. Seit Buster habe ich keine Lösung mehr gefunden und entnervt aufgegeben. Die Schnittstellenbenennung mag funktional erscheinen ist aber aus der jahrelangen Upgrade-Erfahrung nur nervig. Entgegen der weiteren Darstellung habe ich aber nie erlebt, dass die Schnittstellen nicht gleich vergeben wurden
Sollten mehrere Adapter am PI hängen (also beispielsweise auch eine oder zwei zum Auslesen des Stromzählers), werden diese beim Reboot nicht zwangsläufig in der gleichen Reihenfolge durchnummeriert. Hierfür ist es wichtig dem Adapter einen eindeutigen Namen zuzuweisen:
Ausfindig machen der ID_SERIAL_SHORT
/sbin/udevadm info --query=all --name=/dev/ttyUSB0
[...]
P: /devices/platform/bcm2708_usb/usb1/1-1/1-1.3/1-1.3.3/1-1.3.3:1.0/ttyUSB0/tty/ttyUSB0
E: ID_SERIAL_SHORT=A701YM8E
E: ID_VENDOR_FROM_DATABASE=Future Technology Devices International, Ltd
[...]
Nun erweitert man (oder legt die Datei neu an) /etc/udev/rules.d/70-lesekopf.rules
SUBSYSTEM=="tty", ATTRS{product}=="FT232R USB UART", ATTRS{serial}=="A701YM8E", NAME="vitoir0"
Annahme: Der Adapter wurde in Schritt4 als ttyUSB0 erkannt.
sudo udevadm info --name=/dev/ttyUSB0 --attribute-walk
Es werden verschiedene "parent devices aufgelistst". Den Filter der neuen udev-rule passt man nun so an, dass er in einem Block "parent device" zieht. Am Besten dort, wo ATTRS{product}=="FT232R USB UART" und ATTRS{serial}=="AIXYZXYZ" gemeinsam vorkommen. Achtung auch auf den Unterschied SUBSYSTEM vs SUBSYSTEMS.
Nun erweitert man (oder legt die Datei neu an) /etc/udev/rules.d/70-lesekopf.rules
SUBSYSTEMS=="usb", ATTRS{serial}=="AIXYZXYZ", SYMLINK+="vitoir0"
Sauberer ist es auch, mit dem Attribut SYMLINK zu arbeiten, anstatt NAME.
Nach einem Restart des udev
sudo service udev restart
bzw spätestens nach einem
sudo udevadm trigger
ergibt ein ls auf den Adapter ergibt dann:
ls -l /dev/serial/{by-path,by-id}/*
lrwxrwxrwx 1 root root 15 Jan 1 1970 /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A701YM8E-if00-port0 -> ../../vitoir0
lrwxrwxrwx 1 root root 15 Jan 1 1970 /dev/serial/by-path/platform-bcm2708_usb-usb-0:1.3.3:1.0-port0 -> ../../vitoir0
bzw. wenn das Attribut NAME in der Rule verwendet wurde:
ls -l /dev/vitoir0
lrwxrwxrwx 1 root root 15 Jan 1 1970 /dev/vitoir0 -> ttyUSB0
Nun kann der Adapter in der vcontrold.xml immer über /dev/vitoir0 angesprochen werden, und genau der Adapter mit der gefilterten Serien-Nummer wird in Zukunft immer als /dev/vitoir0 erreichbar sein, egal ob er initial als ttyUSB0 oder ttyUSB1 eingeordnet wurde.
Konfiguration der Schnittstelle in ~/openv/vcontrold-code/xml-32/xml/vcontrold.xml unter eintragen (hier für /dev/ttyUSB0 oder /dev/vitoir0, falls du Schritt 4.1 ausgeführt hast):
<config>
<serial>
<tty>/dev/ttyUSB0</tty>
</serial>
<net>
...
Die beiden XML-Konfigurationsdateien von ~/openv/vcontrold-code/xml-32/xml ins Verzeichnis /etc/vcontrold kopieren:
sudo mkdir /etc/vcontrold
sudo cp ~/openv/vcontrold-code/xml-32/xml/vito.xml /etc/vcontrold/
sudo cp ~/openv/vcontrold-code/xml-32/xml/vcontrold.xml /etc/vcontrold/
Wenn diese Dateien auf einem anderen Rechner aktualisiert werden, dann müssen diese auf den Raspberry Pi kopiert werden. Dazu gibt es unterschiedliche Verfahren:
- Zugriff auf den Raspi mittels ftp, scp (WinSCP) oder ähnliches
- vom PC auf die FAT-Partition (boot) der SD Karte kopieren (dazu muss aber jedesmal der Raspi heruntergefahren werden)
Vollständige Konfiguration vcontrold.xml unter :
<config>
<serial>
<tty>/dev/ttyUSB0</tty>
</serial>
<net>
<port>3002</port>
<allow ip='127.0.0.1'/>
<allow ip='192.168.0.0/24'/>
</net>
<device ID="2098"/>
</config>
Wichtig ist vor allem, dass die richtige Steuerung eingetragen ist. (hier ID="2098" für die V200 KW2). Die zweite „allow ip“ Einstellung muss dem eigenen Netz angepasst werden. Das ist nur nötig, wenn das telnet-Interface von anderen Rechnern aus genutzt werden soll. Es kann auch ein einziger externer PC konfiguriert werden (mit /24 wird im Beispiel allen Rechnern im 192.168.0.* Netz Zugang gewährt).
Zuerst sollte der vcontrold mit dem Parameter –n (no fork) aufgerufen werden, um zu verifizieren, dass er korrekt starten kann. vcontrold kann danach mit Ctrl-C abgebrochen werden und bleibt nicht im Speicher. Startup script "/etc/init.d/vcontrol" erstellen Bsp:
#! /bin/sh
### BEGIN INIT INFO
# Provides: vcontrold
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Initscript to start vcontrold deamon
# Description: This file should be used to construct scripts to be
# placed in /etc/init.d.
### END INIT INFO
# Author: Michael Pucher <[email protected]>
# Homepage: openv.wikispaces.org
#
# Please remove the "Author" lines above and replace them
# with your own name if you copy and modify this script.
# Do NOT "set -e"
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="vcontrold deamon"
NAME=vcontrold
DAEMON=/usr/sbin/$NAME
#DAEMON_ARGS="--options args"
DAEMON_ARGS=""
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON $DAEMON_ARGS \ || return 2
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time.
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
#reload|force-reload)
#
# If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'.
#
#log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload
#log_end_msg $?
#;;
restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:
Startup script registrieren:
cd /etc/init.d/
sudo chmod u+x vcontrol
sudo update-rc.d vcontrol start 99 2 3 4 5 . stop 99 0 1 6 .
Mögliche Probleme mit dem Startup script
Falls das Startup script (ohne Fehlermeldung) nicht läuft, hilft ein Blick auf Zeile 25:
DAEMON=/usr/local/sbin/$NAME
Das Verzeichnis /usr/local scheint in früheren Versionen von Raspi-OS vorhanden gewesen zu sein. In der Version "Bullseye" ist es nicht mehr vorhanden und daher wird in Zeile 32
[ -x "$DAEMON" ] || exit 0
fälschlicherweise erkannt, dass das Package nicht installiert sei und das Skript bricht ab. Wenn Zeile 25 geändert wird auf
DAEMON=/usr/sbin/$NAME
läuft das Skript durch.
Ein anders Problem kann sich ergeben, wenn das Startup script von dieser Seite in einen Editor auf einem Windows-PC kopiert und dann (z.B. mit WinSCP) auf den Raspberry Pi übertragen wird. Das Skript bricht dann mit Fehlermeldungen ab wie:
vcontrol: line 14: $'\r': command not found
Die Ursache dafür ist ein "DOS-artiges" Zeilenende. Die Lösung lag darin, die Datei nach dem Kopieren auf dem Raspberry Pi mit "dos2unix vcontrol" zu konvertieren.
Mit Debian Bullseye funktionier der in Schritt 6 angegebene Weg nicht mehr. Im Verzeichis /etc/systemd/system ein neues File anlegen mit nano vcontrol.service Folgenden Inhalt einfügen
[Unit]
Description=Viessmann heating systems interface
Documentation=https://github.com/openv/vcontrold
After=networking.service
[Service]
Type=simple
ExecStart=/home/pi/openv/startup.sh
Restart=always
[Install]
WantedBy=multi-user.target
Das file mittels sudo chmod +x ausführbar machen
System demon neu laden: sudo systemctl daemon-reload
Service registrieren: sudo systemctl enable vcontrol
Service starten: sudo systemctl start vcontrol
ab dann startet der daemon das Strartup script jeden mal beim einschalten
Wenn die obigen Schritte erfolgreich durchgeführt wurden, dann ist nach Aufruf von "sudo /etc/init.d/vcontrol start" der vcontrold-Server bereit.
Am besten wird das System einmal neu gestartet, damit man sicher ist, dass alles funktioniert. Es empfiehlt sich, alle Dateien, welche man für das System angepasst hat auf dem Arbeits-PC zu sichern. Die Einstellungen werden verloren gehen, wenn einmal die SD Karte nicht mehr lesbar ist, was z.B. durch ein unkontrolliertes Ausschalten des Raspi passieren kann.
Am einfachsten ist es, die Telnet Schnittstelle mit PuTTY zu testen.
PuTTY Telnet-Session Einstellungen:
- Connection type = Telnet
- Host = ip Adresse des Raspberry Pi
- Port = 3002
- Bei den Terminal Einstellungen sollte noch die Option „Implicit CR in every LF“ gesetzt werden, damit die Zeilenenden richtig interpretiert werden
- Bei den Einstellungen unter „Connection – Telnet“ den Negitiation Mode = Passiv setzen
(andernfalls werden negotiation commands an vcontrold geschickt, welche von diesem nicht verarbeitet werden. Das führt bei der ersten Eingabe immer zu einem Command not found Fehler).
Man kann auch den SSH Zugang verwenden und in der Shell des Raspis das Command „telnet localhost 3002“ aufrufen.
Wenn der Kommando-Prompt vctrld> erscheint, dann hat alles geklappt!
Durch Eingabe von help werden die gültigen Befehle aufgelistet.
version zeigt die vcontrold Version an. device den in vcontrold.xml konfigurierten device.
commands zeigt alle Befehle für die entsprechende Steuerung an.
Wenn nun getTempA oder getDevType funktioniert, dann ist auch die serielle Kommunikation in Ordnung.
Gratulation, du hast es geschafft!
user:vitoopen
Weiter zur Auswertung mittels RRDB
Weiter zur Auswertung mittels InfluxDB