Datenlogger mit Fernübertragung: Unterschied zwischen den Versionen

Aus fablab Cottbus
Zur Navigation springen Zur Suche springen
(Diskussion)
(Änderung 1983 von Ron (Diskussion) rückgängig gemacht.)
 
(26 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 56: Zeile 56:
 
= Diskussion =
 
= Diskussion =
  
Ich habe schon einige Punkte erledigt (siehe Liste oben). Momentan verfolge ich das Projekt noch aus eigener Bastelleidenschaft. Bin aber sicher, dass wir Unterstützung von meinem Chef bekommen würden, wenn wir zeigen können, dass es funktioniert und auch failsafe ist.
+
= Übersicht momentane Lösung =
 +
In seinem jetzigen Stand erledigt der Raspberry als Datenlogger drei Aufgaben:
 +
# Kommunikation mit dem Messgerät herstellen und Daten loggen
 +
# Bei Anschluss eines USB-Stick Daten auf Stick kopieren
 +
# Wenn ein Internet-Stick angeschlossen ist Internetverbindung herstellen und Dropbox-upload
  
Ron hatte jemand angesprochen, dass an der Uni jede Menge Messgeräte herumstehen, die niemand nutzt. Vielleicht könnte man das damit verbinden und einige davon wieder in Betrieb nehmen.
+
Hier ein Überblick über die Herausforderungen und die gefundenen Lösungen:
:: Im UP-Büro in der Taubenstr. 4 werden wir uns einige abholen. Martin macht dort gerade Praktikum [[Benutzer:Nanu|Nanu]] ([[Benutzer Diskussion:Nanu|Diskussion]]) 18:29, 9. Jul. 2014 (CEST)
+
* USB-Geräte erkennen und entsprechende Skripte starten → Lösung: [http://wiki.ubuntuusers.de/udev UDEV-Regeln]
:: Auja. Oszilloskope, gute Multimeter, und vielleicht auch das eine oder andere Labornetzteil. Auch die dazugehörigen Strippen. Her damit, braucht die Elektronikecke. [[Benutzer:Stefan B.|Stefan B.]] ([[Benutzer Diskussion:Stefan B.|Diskussion]]) 21:20, 31. Jul. 2014 (CEST)
+
* Serielle Kommunikation zum Messgerät herstellen → <code>[http://unixhelp.ed.ac.uk/CGI/man-cgi?stty stty]</code>, Kommunikationseinstellungen spezifisch für jedes Gerät
::: So viel gab es dann dort doch nicht zu holen. Aber wir haben mehrere Sensoren bekommen, eine Wetterstation mit Datenlogger und RS232-Schnittstelle und eine ganze Kiste mit Arduino-Kram im Wert von 200 Euro! Wenn er seine Rechnungen findet kriegt er dann noch eine Spendenquittung von uns. [[Benutzer:Nanu|Nanu]] 12:02, 3. Aug. 2014 (CEST)
+
* Messgerät konfigurieren (vor allem Uhrzeit stellen) → <code>[http://unixhelp.ed.ac.uk/CGI/man-cgi?echo echo -e]</code> (<code>-e</code> to enable interpretation of backslash escapes), Befehle nach Handbuch
 +
* Eingehende Daten in Datei umleiten → <code>[http://unixhelp.ed.ac.uk/CGI/man-cgi?cat cat] /dev/USB-Serial-Port >> Datei.txt</code>
  
Hat jemand einen UMTS-Stick, mit dem ich ein paar Tests machen könnte? [[User:Nanu|Nanu]] ([[User talk:Nanu|talk]]) 13:21, 25 January 2014 (CET)
+
= Erläuterung der momentanen Lösung =
:: Dominic hat freundlicherweise seinen UMTS-Stick dem Projekt zur Verfügung gestellt [[Benutzer:Nanu|Nanu]] ([[Benutzer Diskussion:Nanu|Diskussion]]) 18:29, 9. Jul. 2014 (CEST)
+
 
 +
Um zu verstehen was der Datenlogger macht und wie man die Skripte anpassen kann, muss man sich vor allem in drei Themenfelder einarbeiten:
 +
# Grundsätzlich: wie schreibt man Shell-Skripte?
 +
# Wie schreibt man UDEV-Regeln?
 +
# Wie kann man aus der Shell über die serielle Schnittstelle (RS-232) mit einem Gerät kommunizieren?
 +
 
 +
Zum Schreiben von UDEV-Regeln gibt es unter [http://wiki.ubuntuusers.de/udev hier] eine schöne Anleitung. Über die Serielle Schnittstelle hat [http://papers.mpastell.com/serial.pdf hier] jemand eine kurze und gut verständliche Anleitung zusammen gestellt.
 +
 
 +
Zum Aufbau eines Shell-Skriptes sollte man folgendes wissen:
 +
 
 +
Die erste Zeile des Skripts muss lauten:
 +
  #!/bin/bash
 +
Diese sogenannte [https://de.wikipedia.org/wiki/Shebang Shebang-Zeile] macht das Shell-Skript überhaupt erst als solches erkennbar.
 +
 
 +
Da die Skripte später nicht mehr vom User direkt, sondern von den UDEV-Regeln oder Cron-Jobs aufgerufen werden sollen, lohnt sich die Umleitung aller Meldungen in eine Log-Datei. Dies wird am besten direkt in den nächten beiden Zeilen veranlasst:
 +
logfile=/home/pi/logs/usb-serial-log.log &&
 +
exec >> $logfile 2>&1
 +
Nun werden Fehlermeldungen in die definierte Datei geschrieben. Ausserdem kann man mit <code>echo</code> Kommentare über den (reibungslosen) Ablauf des Skripts in die Log-Datei schreiben. So zum Beispiel:  
 +
echo -e Connecting to parsivel distrometer $(date +"%F %X")
 +
Alles was normalerweise an den User ausgegeben wird, wird nun in die Log-Datei geschrieben.
 +
 
 +
Kommandozeilen in Shell-Skripten sind grundsätzlich wie folgt aufgebaut: Das erste Wort ist der Befehl, gefolgt von den Optionen, die meist mit -e beginnen. Der Befehl <code>cat</code> kann zum Beispiel den Inhalt einer Datei anzeigen:
 +
cat -n MeinText.txt
 +
Die Option <code>-n</code> sorgt dabei dafür, dass eine Zeilennummerierung dargestellt wird.
 +
 
 +
Das Ergebnis eines Befehls kann mit <code>></code> umgeleitet werden. Zum Beispiel in eine Datei:
 +
cat alte_Datei.txt > neue_Datei.txt
 +
Da die Kommunikation, die von der seriellen Schnittstelle kommt genauso zu handhaben ist wie eine Datei, können wir mit:
 +
cat /dev/USB-Serial-Port >> /home/pi/Documents/Loggerdaten/Loggerdaten.txt
 +
einkommende Daten in eine Datei schreiben. <code>>></code> statt <code>></code> sorgt dabei dafür, dass neue Daten an die Datei angehängt werden und nicht ihren Inhalt ersetzen.
 +
 
 +
Um diesen Befehl im Hintergrund laufen zu lassen fügt man <code>&</code> an den Befehl an:
 +
cat /dev/USB-Serial-Port >> /home/pi/Documents/Loggerdaten/Loggerdaten.txt &
 +
Verwendet man als nächste Zeile
 +
MeineProzessNR=$!
 +
hat man die Prozessnummer (PID) in einer Variablen gespeichert, um später mit
 +
kill $MeineProzessNR
 +
den Prozess wieder beenden zu können. Die PID kann man natürlich auch in eine Datei schreiben, um den Prozess mit einem anderen Skript wieder beenden zu können.
 +
 
 +
Für gewöhnlich bauen die Kommandos eines Skriptes aufeinander auf. Das heißt das die nächste Kommandozeile das erfolgreiche abarbeiten der vorangehenden Kommandozeile voraussetzt.
 +
Um zu vermeiden, dass die nächste Zeile (bzw. der ganze Rest des Skriptes) ausgeführt wird, wenn ein Teil davon nicht funktioniert, muss jedes Kommando von einem <code>&&</code> gefolgt werden. <code>&&</code> sorgt dafür, dass das Skript nur fortgesetzt wird, wenn das davorstehende Kommando <code>0</code> zurück gibt. Jede andere Rückgabe bedeutet einen Fehler. Systemkommandos sind alle so eingestellt, dass sie <code>0</code> zurück geben, wenn sie erfolgreich ausgeführt wurden und ansonsten einen Fehlercode übergeben. In eigenen Skripts kann man das genauso umsetzen. Mit
 +
exit 0
 +
beendet man sein Skript und signalisiert, dass die Ausführung erfolgreich war. Somit würde ein ganz simples Skript also so aussehen:
 +
#!/bin/bash
 +
cat TestDatei.txt &&
 +
echo Hallo Welt &&
 +
exit 0
 +
Wenn Zeile zwei nicht ausgeführt werden kann (z.B. weil die Datei TestDatei.txt nicht vorhanden ist), wird auch der Rest des Skripts nicht ausgeführt. Da auch <code>exit 0</code> nicht ausgeführt wird, ist klar, dass das Skript nicht erfolgreich bis zu Ende ausgeführt wurde.
 +
 
 +
Dabei muss man beachten, dass das Starten von Hintergrundprozessen das nacheinander abarbeiten der Kommandos mit <code>&&</code> unterbricht und zu unvorhergesehenen Ergebnissen führt. Das Ausführen von:
 +
echo eins &&
 +
sleep 5 && echo zwei &
 +
echo drei &&
 +
echo vier
 +
zum Beispiel führt zu der Ausgabe:
 +
drei
 +
vier
 +
eins
 +
zwei
 +
 
 +
Abhilfe schafft hier Klammersetzung:
 +
echo eins &&
 +
(sleep 5 && echo zwei &)
 +
echo drei &&
 +
echo vier
 +
 
 +
Hier kann man die Prozessnummer wie folgt speichern:
 +
echo eins &&
 +
pid=$(sleep 5 && echo zwei & echo $!)
 +
echo drei &&
 +
echo vier
 +
 
 +
Auf diese Weise wurde z.B. folgende Zeile im Skript <code>usb-serial.sh</code> umgesetzt:
 +
pid=$( cat /dev/USB-Serial-Port >> $logfile & echo $! )
  
 
= Verwendete Skripte =
 
= Verwendete Skripte =
Zeile 109: Zeile 187:
  
 
=== cron-dropbox-sync.sh ===
 
=== cron-dropbox-sync.sh ===
Syncronisiert mit der Dropbox
+
Synchronisiert mit der Dropbox
  #!/bin/bash
+
#!/bin/bash
 
+
  logfile=/home/pi/logs/crontab-dropbox-sync_$$.log
+
logfile=/home/pi/logs/crontab-dropbox-sync_$$.log
  exec > $logfile 2>&1
+
exec > $logfile 2>&1  
 
+
  cd /home/pi/Documents/Loggerdaten/ &&
+
cd /home/pi/Documents/Loggerdaten/ &&  
 
+
  timestamp=$(date +%Y_%m_%d-%H_%M_%S) &&
+
timestamp=$(date +%Y_%m_%d-%H_%M_%S) &&
 
+
  timestamp >> $logfile
+
timestamp >> $logfile
 
+
  file=Loggerdaten_$timestamp.txt
+
file=Loggerdaten_$timestamp.txt
 
+
  mv Loggerdaten.txt $file &&
+
cp Loggerdaten.txt $file &&
 
+
  gzip --best $file &&
+
echo Fortsetzung der Messung $(date +"%F %T %Z") > Loggerdaten.txt &&
 
+
  sudo sakis3g SIM_PIN=<vierstelligePIN> APN=internet.eplus.de connect
+
gzip --best $file &&
 
+
  /home/pi/Dropbox-Uploader/dropbox_uploader.sh upload Loggerdaten_*.gz Loggerdaten/ &&
+
sudo sakis3g SIM_PIN=1328 APN=internet.eplus.de connect
 
+
  rm Loggerdaten_*
+
/home/pi/Dropbox-Uploader/dropbox_uploader.sh upload Loggerdaten_*.gz Loggerdaten/ &&
 
+
  /home/pi/Scripts/cron-dropbox-execute.sh
+
rm Loggerdaten_*
 
+
  sudo sakis3g disconnect
+
/home/pi/Scripts/cron-dropbox-execute.sh
 
+
 +
sudo sakis3g disconnect
  
 
=== cron-dropbox-execute.sh ===
 
=== cron-dropbox-execute.sh ===
Zeile 175: Zeile 254:
 
=== UDEV-Regel: ===
 
=== UDEV-Regel: ===
 
Eventuell kann hier die Seriennummer auch weggelassen werden, wenn man mit jedem beliebigen USB-Stick zugreifen möchte
 
Eventuell kann hier die Seriennummer auch weggelassen werden, wenn man mit jedem beliebigen USB-Stick zugreifen möchte
 
+
ATTRS{idVendor}=="0951", ATTRS{idProduct}=="1643", ATTRS{serial}=="<Seriennummer>", ACTION=="add", MODE="0755", GROUP="plugdev", OWNER="pi", SUBSYSTEMS=="usb", SYMLINK+="commandstick", RUN+="/home/pi/Scripts/mountCommandStick.sh"
  ATTRS{idVendor}=="0951", ATTRS{idProduct}=="1643", ATTRS{serial}=="<Seriennummer>", ACTION=="add", MODE="0755", GROUP="plugdev", OWNER="pi", SUBSYSTEMS=="usb", SYMLINK+="commandstick", RUN+="/home/pi/Scripts/mountCommandStick.sh"
+
ENV{DEVLINKS}=="/dev/commandstick", ACTION=="remove", RUN+="/home/pi/Scripts/unmount.sh"
  ENV{DEVLINKS}=="/dev/commandstick", ACTION=="remove", RUN+="/home/pi/Scripts/unmount.sh"
 
 
 
  
 
=== mountCommandStick.sh ===
 
=== mountCommandStick.sh ===
 
Mountet den Commandstick
 
Mountet den Commandstick
  #!/bin/bash
+
#!/bin/bash
  mkdir /media/commandstick
+
mkdir /media/commandstick
  mount -o uid=pi,gid=plugdev /dev/commandstick /media/commandstick
+
mount -o uid=pi,gid=plugdev /dev/commandstick /media/commandstick
 
+
  /media/commandstick/command.sh
+
/media/commandstick/command.sh
 +
 
 +
=== command.sh ===
 +
Kommandodatei auf dem Stick:
 +
#!/bin/bash
 +
 +
logfile=/media/commandstick/logs/rsync.log
 +
exec >> $logfile 2>&1
 +
 +
cd /home/pi/Documents/Loggerdaten/ &&
 +
 +
timestamp=$(date +%Y_%m_%d-%H_%M_%S) &&
 +
 +
echo -e "\nProtokoll Auslesung" $timestamp "\n"
 +
df #to write free diskspace to log file
 +
echo " "
 +
 +
#file=Loggerdaten_$timestamp.txt
 +
#cp Loggerdaten.txt $file &&
 +
#echo Fortsetzung der Messung $(date +"%F %T %Z") > Loggerdaten.txt &&
 +
 +
 +
# Datein auf den Stick kopieren:
 +
rsync -r /home/pi/Documents/Loggerdaten/ /media/commandstick/Loggerdaten/ -v
 +
 +
# logs auf den Stick kopieren:
 +
rsync -r /home/pi/logs/* /media/commandstick/logs/ -v
 +
 +
 +
# diese Zeile auskommentiren um dei Daten auf dem Logger zu löschen:
 +
#rm /home/pi/Documents/Loggerdaten/Loggerdaten.txt
  
 
=== unmount.sh ===
 
=== unmount.sh ===
 
Unmountet den Commandstick
 
Unmountet den Commandstick
  #!/bin/bash
+
#!/bin/bash
  logfile=/home/pi/logs/unmount_$$.log
+
logfile=/home/pi/logs/unmount_$$.log
  exec >> $logfile 2>&1
+
exec >> $logfile 2>&1
  umount /media/commandstick
+
umount /media/commandstick
  rm -r /media/commandstick
+
rm -r /media/commandstick
  
 
== Serielle Kommunikation ==
 
== Serielle Kommunikation ==
 +
 +
Viele Messgeräte besitzen eine serielle Schnittstelle (z.B. [[HeliosMini Wetterstation/Datenlogger]]). In diesem Aufbau wird ein USB zu Seriell Adapter verwendet, um die Kommunikation herzustellen. Wenn dieser angeschlossen wird, wird eine UDEV-Regel aktiviert:
 +
 
=== UDEV-Regel: ===
 
=== UDEV-Regel: ===
  # this contains the rules to execute serial comminication with the Parsivel
+
# this contains the rules to execute serial comminication with the Parsivel
  ATTRS{idVendor}=="067b", SUBSYSTEMS=="usb", ATTRS{manufacturer}=="Prolific Technology Inc.", ATTRS{product}=="USB-Serial Controller", ACTION=="add", SYMLINK+="USB-Serial-Port", RUN+="/home/pi/Scripts/usb-serial.sh"
+
ATTRS{idVendor}=="067b", SUBSYSTEMS=="usb", ATTRS{manufacturer}=="Prolific Technology Inc.", ATTRS{product}=="USB-Serial Controller", ACTION=="add", SYMLINK+="USB-Serial-Port", RUN+="/home/pi/Scripts/usb-serial.sh"
 
    
 
    
  ENV{DEVLINKS}=="/dev/USB-Serial-Port", ACTION=="remove", RUN+="/home/pi/Scripts/usb-serial-remove.sh"
+
ENV{DEVLINKS}=="/dev/USB-Serial-Port", ACTION=="remove", RUN+="/home/pi/Scripts/usb-serial-remove.sh"
 
+
  #ATTRS{serial}=="Prolific_Technology_Inc._USB-Serial_Controller"
+
#ATTRS{serial}=="Prolific_Technology_Inc._USB-Serial_Controller"
  
 
=== usb-serial.sh ===
 
=== usb-serial.sh ===
 
Macht eine Pipe vom Seriellen Anschluss in die Datei "Loggerdaten.txt".
 
Macht eine Pipe vom Seriellen Anschluss in die Datei "Loggerdaten.txt".
Läuft noch nicht sauber.
 
  
  #!/bin/bash
+
#!/bin/bash
 
+
  logfile=/home/pi/logs/usb-serial-log.log
+
logfile=/home/pi/logs/usb-serial-log.log &&
  #exec >> $logfile 2>&1
+
exec >> $logfile 2>&1
 
+
echo -e "\nConnecting to parsivel distrometer" $(date +"%F %X") &&
  sudo -u pi touch /home/pi/Documents/Loggerdaten/Loggerdaten.txt  
+
#sudo -u pi touch /home/pi/Documents/Loggerdaten/Loggerdaten.txt
 
+
  stty -F /dev/USB-Serial-Port speed 19200 inlcr
+
#for the HeliosMini
 
+
#stty -F /dev/USB-Serial-Port speed 9600 &&
  ttylog -d /dev/USB-Serial-Port >> /home/pi/Documents/Loggerdaten/Loggerdaten.txt
+
  #cat /dev/USB-Serial-Port >> /home/pi/Documents/Loggerdaten/Loggerdaten.txt
+
#for Parsivel:
  #cat /dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0 >> /$
+
#while ! `tty -s`; do sleep 3; done
 +
#sleep 120 # necessary to run properly at system start
 +
#stty -F /dev/USB-Serial-Port speed 19200 &&
 +
# repeated because the first time doesnt work on boot (don't know why)
 +
 +
stty -F /dev/USB-Serial-Port speed 19200 raw &&
 +
stty -F /dev/USB-Serial-Port -inlcr -icrnl -imaxbel -opost onlcr -isig -icanon -echo &&
 +
 +
# Return von Parsivel in logdatei schreiben
 +
pid=$( cat /dev/USB-Serial-Port >> $logfile &
 +
echo $! ) &&
 +
sleep 1 &&
 +
#echo $pid &&
 +
 +
# turn sdi-12 mode off:
 +
echo turn sdi-12 mode off &&
 +
echo -e "CS/S/E/0\r" > /dev/USB-Serial-Port &&
 +
sleep 1 &&
 +
 +
# turn poll mode on for settings:
 +
echo "turn poll mode on for settings" &&
 +
echo -e "CS/I/0\r" > /dev/USB-Serial-Port &&
 +
sleep 3 &&
 +
 +
# set time of parsivel:
 +
#sudo hwclock -s &&
 +
#echo $(TZ="UTC-1" date +"%H:%M:%S") > /home/pi/testlog.txt &&
 +
echo "set date to $(date +%d.%m.%Y)" &&
 +
echo -e "CS/D/$(date +%d.%m.%Y)\r" > /dev/USB-Serial-Port &&
 +
sleep 3 &&
 +
echo "set time to $(TZ=UTC-1 date +%H:%M:%S)" &&
 +
echo -e "CS/T/$(TZ=UTC-1 date +%H:%M:%S)\r" > /dev/USB-Serial-Port &&
 +
sleep 1 &&
 +
 +
# Anwendertelegramm anschalten:
 +
echo "switch to user data telegram" &&
 +
echo -e "CS/M/M/1\r" > /dev/USB-Serial-Port &&
 +
sleep 1 &&
 +
 +
# Datentelegramm festlegen:
 +
echo "define data telegram" &&
 +
echo -e "CS/M/S/\r" > /dev/USB-Serial-Port &&
 +
sleep 1 &&
 +
echo -e "%21;%20;%01;%02;%11;%10;%12;%17;%03;%04;%05;%06;%07;%09;%08;%18;%90;%91;%93;/n\r" > /dev/USB-Serial-Port &&
 +
sleep 1 &&
 +
 +
# Messintervall auf 60 Sekunden:
 +
echo "set sample interval" &&
 +
echo -e "CS/I/60\r" > /dev/USB-Serial-Port &&
 +
sleep 1 &&
 +
# Return nicht mehr in logdatei schreiben
 +
kill $pid &&
 +
 +
# Daten in Datei speichern
 +
(
 +
cat /dev/USB-Serial-Port >> /home/pi/Documents/Loggerdaten/Loggerdaten.txt &
 +
logging=$! &&
 +
echo "started logging with PID $logging"
 +
) &&
 +
exit 0
  
 
=== usb-serial-remove.sh ===
 
=== usb-serial-remove.sh ===
 
Wird aufgerufen beim abziehen des Seriellen Adapters. Garantiert, dass beim nächsten Anstecken die Verbindung wieder hergestellt werden kann
 
Wird aufgerufen beim abziehen des Seriellen Adapters. Garantiert, dass beim nächsten Anstecken die Verbindung wieder hergestellt werden kann
  #!/bin/bash
+
#!/bin/bash
  sudo pkill ttylog
+
echo -e "\nserial connector removed" $(date +"%F %X") >> /home/pi/logs/usb-serial-log.log &&
  echo gemacht >> /home/pi/testlog.txt
+
pkill cat &&
 +
exit 0
 +
 
 +
== Real Time Clock ==
 +
Damit der Raspberry auch ohne Internet-Verbindung die richtige Zeit behält, ist eine Real Time Cloc (RTC) notwendig, die man auf einem kleinen Chip kaufen kann, der auf die GPIO-Pins des Raspberrys aufgesteckt wird. Ich hab [http://www.ebay.de/itm/331215053208?_trksid=p2059210.m2749.l2649&ssPageName=STRK%3AMEBIDX%3AIT diese] gekauft. [http://nicegear.co.nz/blog/using-an-i2c-real-time-clock-rtc-with-a-raspberry-pi/ Hier] habe ich eine Anleitung gefunden, um den Raspberry auf die RTC einzustellen. Konkret habe ich verwendet:
 +
 
 +
# Remove the module blacklist entry so it can be loaded on boot
 +
sudo sed -i 's/blacklist i2c-bcm2708/#blacklist i2c-bcm2708/' /etc/modprobe.d/raspi-blacklist.conf
 +
# Load the module now
 +
sudo modprobe i2c-bcm2708
 +
# Notify Linux of the Dallas RTC device
 +
echo ds1307 0x68 | sudo tee /sys/class/i2c-adapter/i2c-1/new_device
 +
# Test whether Linux can see our RTC module.
 +
sudo hwclock
 +
 
 +
Eine neue Real-Time-Clock muss man wahrscheinlich erstmal auf die richtige Uhrzeit einstellen. Dazu am besten den Raspberry einmal mit Internetverbindung booten, damit er sich die Zeit aus dem Internet holt. Dann mit folgendem Befehl die RTC einstellen:
 +
 
 +
sudo hwclock -w
 +
 
 +
Nun muss noch eingestellt werden, dass beim nächsten Booten die Systemzeit von der RTC geholt wird:
 +
 
 +
# Add the RTC device on boot
 +
sudo sed -i 's#^exit 0$#echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device#' /etc/rc.local
 +
echo "sudo hwclock -s" | sudo tee -a /etc/rc.local
 +
echo exit 0 | sudo tee -a /etc/rc.local

Aktuelle Version vom 19. April 2016, 18:24 Uhr

Am Lehrstuhl an dem ich arbeite fahren die Kollegen regelmäßig raus, um Datenlogger auszulesen. Das ist natürlich aufwändig. Ausserdem, wenn man nur einmal im Monat die Daten abholt, oder einmal die Woche, kann man schonmal feststellen, dass der Logger oder das Messgerät irgendwann ausgefallen ist. Im schlimmsten Falle direkt nachdem man das letzte mal da war. Wenn dadurch Messreihen unterbrochen werden ist das immer ärgerlich.

Ich frage mich, warum in Zeiten, wo an den meisten Stellen UMTS verfügbar ist, solche fernübertragenden Datenlogger noch nicht installiert sind. Selbst nicht am Hühnerwasser, wo haufenweise Messgeräte stehen. Bestimmt liegt das daran, dass kommerzielle Datenlogger sehr teuer sind. Und dass die meisten nicht wissen, das es heute billige Minicomputer wie die den Raspberry Pi gibt, die prima für so eine Aufgabe geeignet sind. Und dann muss man sich natürlich ein bisschen reinfuchsen, wie man aus einem Raspberry Pi einen Datenlogger mit Fernübertragung macht.


An den Messstellen, die ich kenne, gibt es folgende Vorraussetzungen:

  • Messsignal über serielle Schnittstelle
  • Stromversorgung entweder 220V oder 12V aus Batterie, die mit einem Solarpanel aufgeladen wird


TODO

  • Raspberry Pi mit serieller Schnittstelle ausstatten (entweder über USB-Adapter oder über die GPIO-Pins) [erledigt]
    • habe ich jetzt mit einem USB-Adapter gemacht
  • Skript zum Ansprechen und auslesen des Messgerätes schreiben, dass automatisch gestartet wird, wenn der Raspberry bootet [50% erledigt, mit Fehlern]
    • Das automatische Starten der Seriellen Verbindung zum Messgerät nach Anschluss des USB-Serial-Adapters funktioniert noch nicht reibungslos. Das Auslesen brach immer wieder ab. Warum genau ist aber noch unklar. Eventuell haben andere USB-Geräte wie der UMTS-Stick die Kommunikation gestört, vor allem weil dieser sich immer wieder an- und abzumelden scheint. Ich bräuchte vor allem mal ein Gerät, mit dem ich das in meinem Büro testen kann. Brauche nämlich die Nähe zum Internet, um nach möglichen Lösungsansätzen zu suchen ;-)
  • UMTS-Stick installieren [erledigt]
    • funktioniert mit sakis3G und läuft besser als die Vodafone-Software auf meinem Mac.
    • um UMTS-Kosten zu sparen sollte man sich überlegen, bei welcher Datenmenge sich der Verbindungsaufbau zur Dropbox lohnt. Es werden nämlich eine ganze Menge Daten verschickt, bevor die Verbindung überhaupt erstmal besteht. Eventuell wäre z.B. FTP auch weniger Datenintensiv.
  • Skript schreiben, dass die IP des Raspberry einem korrespondierenden Server bekannt macht (damit man im Notfall den Raspberry ansprechen per SSH kann, wenn der sich nicht mehr meldet oder Einstellungen vorgenommen werden müssen) [im Grunde erledigt, aber nicht ganz zufriedenstelltend]
    • Eine wirkliche IP-Adresse gibt es nicht im UMTS-Netz. Lösung war jetzt ein reverse ssh tunnel. Dazu braucht man allerdings wieder einen Server oder Computer, der über seine IP frei im Internet erreichbar ist. Also geht eine Verbindung direkt ins Uni-Netz leider nicht.
    • Eine gut funktionierende Lösung war, die Daten in eine Dropbox hoch zu laden. Das habe ich auch so eingerichtet, dass der Pi in der Dropbox nach einem Schell-Script mit einem bestimmten Namen schaut in die man den Befehl zum aufbauen des revers tunnel schreiben kann. Oder irgend ein anderes Wartungs-Script.
  • Skript schreiben, dass alle paar Minuten oder Stunden die Daten verschickt (Cron-Job) [erledigt]
    • Daten werden gezippt und unter dem Datum gespeichert. Dann wird die Zip in die Dropbox hochgeladen und nach erfolgreichem hochladen gelöscht. Wenn die Dropbox-Verbindung nicht aufgebaut werden kann, sammeln die Zip-Dateien sich im Ordner an und können z.B. über USB abgeholt werden. Wenn die Dropbox-Verbindung das nächste mal erfolgreich ist, werden alle Zip-Dateien im Ordner hochgeladen (auch die, die das letzte mal nicht geladen werden konnten).
    • Eventuell lieber Owncloud oder FTP auf dem fablab-Server benutzen. Dann müssen die Daten nicht nach Amerika geschickt werden ;-)
  • Routine für das Auslesen per USB schreiben (automatischer Datensync, wenn USB-Stick angeschlossen wird) [erledigt]
    • udev-Regel reagiert, wenn der USB-Stick angeschlossen wird und startet ein Skript zum Kopieren. Die udev-Regel kann auch nur auf einen bestimmten USB-Stick eingestellt werden (über dessen Seriennummer).
    • Auch auf den Stick kann ein Shell-Script gelegt werden, dass beim Anschluss des USB-Sticks ausgeführt wird.
    • Beim Anschluss des USB-Sticks werden bisher nicht die Daten vom Datenlogger gelöscht, da man nicht weiß, ob der USB-Stick nicht unterwegs verloren gehen könnte. Das Löschen der Dateien könnte man dann in das Shell-Script schreiben, sodass die erfolgreich Übertragenen Daten beim nächsten Abholen gelöscht werden.
  • 12V-Stromversorgung (Eventuell über Autonetzteil) [0% erledigt]

Der Datenlogger sollte eine hohe Redundanz haben, damit ja keine Daten verloren gehen.

  • Mehre Auslesewege (UMTS, USB-Stick, Auslesung mit Laptop über LAN)
  • keine Daten löschen, bevor nicht bestätigt wurde, dass sie ausgelesen wurden und sicher irgendwo angekommen sind

Dann sollte der Datenlogger automatisch Warnungen verschicken, wenn die Batteriespannung zu gering, der Datenspeicher voll ist oder das Messgerät nicht mehr arbeitet.

Man muss sich natürlich auch noch Gedanken machen, wie man den Raspberry wetterfest macht. Manchmal könnte man ihn in bestehende Kästen integrieren, manchmal bräuchte man aber auch ein Gehäuse wie z.B. dieses hier.


Was bringt das dem fablab?

  • Ein tolles Bastelprojekt
  • Ein Vorzeigeprojekt
  • Eine erste Bastelanleitung, die wir in unserem Wiki veröffentlichen können
  • Ein Ergebnis, dass einige Lehrstühle hier an der Uni gut gebrauchen können
  • Deswegen auch wahrscheinlich Unterstützung bzw. Spenden von entsprechenden Lehrstühlen
  • Ein Ergebnis, dass vielleicht auch woanders Interessenten findet
  • Folgeprojekte?

Diskussion

Übersicht momentane Lösung

In seinem jetzigen Stand erledigt der Raspberry als Datenlogger drei Aufgaben:

  1. Kommunikation mit dem Messgerät herstellen und Daten loggen
  2. Bei Anschluss eines USB-Stick Daten auf Stick kopieren
  3. Wenn ein Internet-Stick angeschlossen ist Internetverbindung herstellen und Dropbox-upload

Hier ein Überblick über die Herausforderungen und die gefundenen Lösungen:

  • USB-Geräte erkennen und entsprechende Skripte starten → Lösung: UDEV-Regeln
  • Serielle Kommunikation zum Messgerät herstellen → stty, Kommunikationseinstellungen spezifisch für jedes Gerät
  • Messgerät konfigurieren (vor allem Uhrzeit stellen) → echo -e (-e to enable interpretation of backslash escapes), Befehle nach Handbuch
  • Eingehende Daten in Datei umleiten → cat /dev/USB-Serial-Port >> Datei.txt

Erläuterung der momentanen Lösung

Um zu verstehen was der Datenlogger macht und wie man die Skripte anpassen kann, muss man sich vor allem in drei Themenfelder einarbeiten:

  1. Grundsätzlich: wie schreibt man Shell-Skripte?
  2. Wie schreibt man UDEV-Regeln?
  3. Wie kann man aus der Shell über die serielle Schnittstelle (RS-232) mit einem Gerät kommunizieren?

Zum Schreiben von UDEV-Regeln gibt es unter hier eine schöne Anleitung. Über die Serielle Schnittstelle hat hier jemand eine kurze und gut verständliche Anleitung zusammen gestellt.

Zum Aufbau eines Shell-Skriptes sollte man folgendes wissen:

Die erste Zeile des Skripts muss lauten:

 #!/bin/bash

Diese sogenannte Shebang-Zeile macht das Shell-Skript überhaupt erst als solches erkennbar.

Da die Skripte später nicht mehr vom User direkt, sondern von den UDEV-Regeln oder Cron-Jobs aufgerufen werden sollen, lohnt sich die Umleitung aller Meldungen in eine Log-Datei. Dies wird am besten direkt in den nächten beiden Zeilen veranlasst:

logfile=/home/pi/logs/usb-serial-log.log &&
exec >> $logfile 2>&1

Nun werden Fehlermeldungen in die definierte Datei geschrieben. Ausserdem kann man mit echo Kommentare über den (reibungslosen) Ablauf des Skripts in die Log-Datei schreiben. So zum Beispiel:

echo -e Connecting to parsivel distrometer $(date +"%F %X")

Alles was normalerweise an den User ausgegeben wird, wird nun in die Log-Datei geschrieben.

Kommandozeilen in Shell-Skripten sind grundsätzlich wie folgt aufgebaut: Das erste Wort ist der Befehl, gefolgt von den Optionen, die meist mit -e beginnen. Der Befehl cat kann zum Beispiel den Inhalt einer Datei anzeigen:

cat -n MeinText.txt

Die Option -n sorgt dabei dafür, dass eine Zeilennummerierung dargestellt wird.

Das Ergebnis eines Befehls kann mit > umgeleitet werden. Zum Beispiel in eine Datei:

cat alte_Datei.txt > neue_Datei.txt

Da die Kommunikation, die von der seriellen Schnittstelle kommt genauso zu handhaben ist wie eine Datei, können wir mit:

cat /dev/USB-Serial-Port >> /home/pi/Documents/Loggerdaten/Loggerdaten.txt

einkommende Daten in eine Datei schreiben. >> statt > sorgt dabei dafür, dass neue Daten an die Datei angehängt werden und nicht ihren Inhalt ersetzen.

Um diesen Befehl im Hintergrund laufen zu lassen fügt man & an den Befehl an:

cat /dev/USB-Serial-Port >> /home/pi/Documents/Loggerdaten/Loggerdaten.txt &

Verwendet man als nächste Zeile

MeineProzessNR=$!

hat man die Prozessnummer (PID) in einer Variablen gespeichert, um später mit

kill $MeineProzessNR

den Prozess wieder beenden zu können. Die PID kann man natürlich auch in eine Datei schreiben, um den Prozess mit einem anderen Skript wieder beenden zu können.

Für gewöhnlich bauen die Kommandos eines Skriptes aufeinander auf. Das heißt das die nächste Kommandozeile das erfolgreiche abarbeiten der vorangehenden Kommandozeile voraussetzt. Um zu vermeiden, dass die nächste Zeile (bzw. der ganze Rest des Skriptes) ausgeführt wird, wenn ein Teil davon nicht funktioniert, muss jedes Kommando von einem && gefolgt werden. && sorgt dafür, dass das Skript nur fortgesetzt wird, wenn das davorstehende Kommando 0 zurück gibt. Jede andere Rückgabe bedeutet einen Fehler. Systemkommandos sind alle so eingestellt, dass sie 0 zurück geben, wenn sie erfolgreich ausgeführt wurden und ansonsten einen Fehlercode übergeben. In eigenen Skripts kann man das genauso umsetzen. Mit

exit 0

beendet man sein Skript und signalisiert, dass die Ausführung erfolgreich war. Somit würde ein ganz simples Skript also so aussehen:

#!/bin/bash
cat TestDatei.txt &&
echo Hallo Welt &&
exit 0

Wenn Zeile zwei nicht ausgeführt werden kann (z.B. weil die Datei TestDatei.txt nicht vorhanden ist), wird auch der Rest des Skripts nicht ausgeführt. Da auch exit 0 nicht ausgeführt wird, ist klar, dass das Skript nicht erfolgreich bis zu Ende ausgeführt wurde.

Dabei muss man beachten, dass das Starten von Hintergrundprozessen das nacheinander abarbeiten der Kommandos mit && unterbricht und zu unvorhergesehenen Ergebnissen führt. Das Ausführen von:

echo eins && 
sleep 5 && echo zwei & 
echo drei && 
echo vier

zum Beispiel führt zu der Ausgabe:

drei
vier
eins
zwei

Abhilfe schafft hier Klammersetzung:

echo eins &&
(sleep 5 && echo zwei &)
echo drei &&
echo vier

Hier kann man die Prozessnummer wie folgt speichern:

echo eins &&
pid=$(sleep 5 && echo zwei & echo $!)
echo drei &&
echo vier

Auf diese Weise wurde z.B. folgende Zeile im Skript usb-serial.sh umgesetzt:

pid=$( cat /dev/USB-Serial-Port >> $logfile & echo $! )

Verwendete Skripte

Installation

 # make dropbox available systemwide:
 sudo ln -s /home/pi/Scripts/Dropbox-Uploader/dropbox_uploader.sh /usr/bin/dropbox
 # same for sakis3g
 sudo ln -s /home/pi/Scripts/sakis3g /usr/bin/sakis3g

Die UDEV-Regeln müssen als Datei in /etc/udev/rules.d angelegt werden. Man kann alle Regeln in eine Datei legen. Eventuell macht man auch einen Symlink in den Ordner wo man alle Skripte zentral zu liegen hat.

online upload

UDEV-Regel beim Anschluss des USB-Modems:

Einfach nur, um dem Gerät einen immer gleichen Namen zu geben:

 ATTRS{idProduct}=="1003", ATTRS{idVendor}=="12d1", ATTRS{bInterfaceNumber}=="00", ACTION=="add", SYMLINK+="umts0"

Um die Verbindung aufzubauen, sobald das Modem vollständig erkannt ist:

 #ATTRS{idVendor}=="Huawei Technologies Co., Ltd.", ATTRS{idProduct}=="E220 HSDPA Modem / E230/E270/E870 HSDPA/HSUPA Modem"
 #ATTRS{bInterfaceNumber}=="01",
 #KERNEL=="ttyUSB?", SUBSYSTEMS=="usb", ATTRS{idProduct}=="1003", ATTRS{bInterfaceNumber}=="01", ACTION=="add", RUN+="/home/pi/test.sh"
 
 KERNEL=="ttyUSB*", SUBSYSTEM=="tty", ATTRS{bInterfaceNumber}=="01", ACTION=="add", RUN+="/home/pi/Scripts/3Gconnect.sh"
 
 #, ATTRS{idProduct}=="1003", ATTRS{idVendor}=="12d1",

3Gconnect.sh

Aufbau der 3G-Verbindung

 #!/bin/bash
 
 logfile=/home/pi/logs/3Gconnect_$$.log
 exec > $logfile 2>&1
 #sleep 10s
 sudo sakis3g SIM_PIN=<vierstelligePIN> APN=internet.eplus.de connect

cron-job zum starten des Dropbox Sync

crontab -e zum editieren der cron-jobs. Folgende Zeilen einfügen:

 0 7-20   * * *   /home/pi/Scripts/cron-dropbox-sync.sh
 @reboot  /home/pi/Scripts/cron-dropbox-sync.sh

cron-dropbox-sync.sh

Synchronisiert mit der Dropbox

#!/bin/bash

logfile=/home/pi/logs/crontab-dropbox-sync_$$.log
exec > $logfile 2>&1 

cd /home/pi/Documents/Loggerdaten/ && 

timestamp=$(date +%Y_%m_%d-%H_%M_%S) &&

timestamp >> $logfile

file=Loggerdaten_$timestamp.txt

cp Loggerdaten.txt $file &&

echo Fortsetzung der Messung $(date +"%F %T %Z") > Loggerdaten.txt &&

gzip --best $file &&

sudo sakis3g SIM_PIN=1328 APN=internet.eplus.de connect

/home/pi/Dropbox-Uploader/dropbox_uploader.sh upload Loggerdaten_*.gz Loggerdaten/ &&

rm Loggerdaten_*

/home/pi/Scripts/cron-dropbox-execute.sh

sudo sakis3g disconnect

cron-dropbox-execute.sh

Führt das Shell-Script aus, dass von der dropbox geladen wurde

 #!/bin/bash
 cd /home/pi/executeScript &&
 
 /home/pi/Dropbox-Uploader/dropbox_uploader.sh download executeScript/execute.sh ../ &&
 
 chmod +x execute.sh &&
 
 ./execute.sh &&
 
 /home/pi/Dropbox-Uploader/dropbox_uploader.sh delete executeScript/execute.sh
 
 /home/pi/Dropbox-Uploader/dropbox_uploader.sh upload * executionLog/ &&
 
 rm *

execute.sh

Wenn man execute.sh in den Ornder "executeScript" in der Dorpbox legt, wird das Shell-Skript beim Dropbox-Sync ausgeführt und daraufhin in den Ordner "executionLog" gelegt, zusammen mit dem Log-File der execution.

Die folgende Datei ruft Beispielhaft einen reverse ssh tunnel auf, um den Raspberry live steuern zu können. Der reverse tunnel öffnet den Port 2222 auf dem Server und leitet eine eingehende ssh-Verbindung an den Pi weiter. Die Verbindung mit dem Raspberry stellt man also her mit ssh <benutzer>@<serverIP>:2222. Arbeitet man auf dem Server can "localhost" anstatt der IP verwendet werden.

 #!/bin/bash
 
 exec 1>> log.txt 2>&1
 echo $(date)
 
 #-f would be for background
 ssh -v -N -R 2222:localhost:22 <benutzer>@<ServerIP> &
 sshPID=$!
 # hold the connection open for some time, then close
 sleep 900
 kill $sshPID

USB download

UDEV-Regel:

Eventuell kann hier die Seriennummer auch weggelassen werden, wenn man mit jedem beliebigen USB-Stick zugreifen möchte

ATTRS{idVendor}=="0951", ATTRS{idProduct}=="1643", ATTRS{serial}=="<Seriennummer>", ACTION=="add", MODE="0755", GROUP="plugdev", OWNER="pi", SUBSYSTEMS=="usb", SYMLINK+="commandstick", RUN+="/home/pi/Scripts/mountCommandStick.sh"
ENV{DEVLINKS}=="/dev/commandstick", ACTION=="remove", RUN+="/home/pi/Scripts/unmount.sh"

mountCommandStick.sh

Mountet den Commandstick

#!/bin/bash
mkdir /media/commandstick
mount -o uid=pi,gid=plugdev /dev/commandstick /media/commandstick

/media/commandstick/command.sh

command.sh

Kommandodatei auf dem Stick:

#!/bin/bash

logfile=/media/commandstick/logs/rsync.log
exec >> $logfile 2>&1

cd /home/pi/Documents/Loggerdaten/ &&

timestamp=$(date +%Y_%m_%d-%H_%M_%S) &&

echo -e "\nProtokoll Auslesung" $timestamp "\n"
df #to write free diskspace to log file
echo " "

#file=Loggerdaten_$timestamp.txt
#cp Loggerdaten.txt $file &&
#echo Fortsetzung der Messung $(date +"%F %T %Z") > Loggerdaten.txt && 


# Datein auf den Stick kopieren:
rsync -r /home/pi/Documents/Loggerdaten/ /media/commandstick/Loggerdaten/ -v

# logs auf den Stick kopieren:
rsync -r /home/pi/logs/* /media/commandstick/logs/ -v


# diese Zeile auskommentiren um dei Daten auf dem Logger zu löschen:
#rm /home/pi/Documents/Loggerdaten/Loggerdaten.txt

unmount.sh

Unmountet den Commandstick

#!/bin/bash
logfile=/home/pi/logs/unmount_$$.log
exec >> $logfile 2>&1
umount /media/commandstick
rm -r /media/commandstick

Serielle Kommunikation

Viele Messgeräte besitzen eine serielle Schnittstelle (z.B. HeliosMini Wetterstation/Datenlogger). In diesem Aufbau wird ein USB zu Seriell Adapter verwendet, um die Kommunikation herzustellen. Wenn dieser angeschlossen wird, wird eine UDEV-Regel aktiviert:

UDEV-Regel:

# this contains the rules to execute serial comminication with the Parsivel
ATTRS{idVendor}=="067b", SUBSYSTEMS=="usb", ATTRS{manufacturer}=="Prolific Technology Inc.", ATTRS{product}=="USB-Serial Controller", ACTION=="add", SYMLINK+="USB-Serial-Port", RUN+="/home/pi/Scripts/usb-serial.sh"
 
ENV{DEVLINKS}=="/dev/USB-Serial-Port", ACTION=="remove", RUN+="/home/pi/Scripts/usb-serial-remove.sh"

#ATTRS{serial}=="Prolific_Technology_Inc._USB-Serial_Controller"

usb-serial.sh

Macht eine Pipe vom Seriellen Anschluss in die Datei "Loggerdaten.txt".

#!/bin/bash

logfile=/home/pi/logs/usb-serial-log.log &&
exec >> $logfile 2>&1
echo -e "\nConnecting to parsivel distrometer" $(date +"%F %X") &&
#sudo -u pi touch /home/pi/Documents/Loggerdaten/Loggerdaten.txt

#for the HeliosMini
#stty -F /dev/USB-Serial-Port speed 9600 &&

#for Parsivel:
#while ! `tty -s`; do sleep 3; done
#sleep 120 # necessary to run properly at system start
#stty -F /dev/USB-Serial-Port speed 19200 &&
# repeated because the first time doesnt work on boot (don't know why)

stty -F /dev/USB-Serial-Port speed 19200 raw &&
stty -F /dev/USB-Serial-Port -inlcr -icrnl -imaxbel -opost onlcr -isig -icanon -echo &&

# Return von Parsivel in logdatei schreiben
pid=$( cat /dev/USB-Serial-Port >> $logfile &
echo $! ) &&
sleep 1 &&
#echo $pid &&

# turn sdi-12 mode off:
echo turn sdi-12 mode off &&
echo -e "CS/S/E/0\r" > /dev/USB-Serial-Port &&
sleep 1 &&

# turn poll mode on for settings:
echo "turn poll mode on for settings" &&
echo -e "CS/I/0\r" > /dev/USB-Serial-Port &&
sleep 3 &&

# set time of parsivel:
#sudo hwclock -s &&
#echo $(TZ="UTC-1" date +"%H:%M:%S") > /home/pi/testlog.txt &&
echo "set date to $(date +%d.%m.%Y)" &&
echo -e "CS/D/$(date +%d.%m.%Y)\r" > /dev/USB-Serial-Port &&
sleep 3 &&
echo "set time to $(TZ=UTC-1 date +%H:%M:%S)" &&
echo -e "CS/T/$(TZ=UTC-1 date +%H:%M:%S)\r" > /dev/USB-Serial-Port &&
sleep 1 &&

# Anwendertelegramm anschalten:
echo "switch to user data telegram" &&
echo -e "CS/M/M/1\r" > /dev/USB-Serial-Port &&
sleep 1 &&

# Datentelegramm festlegen:
echo "define data telegram" &&
echo -e "CS/M/S/\r" > /dev/USB-Serial-Port &&
sleep 1 &&
echo -e "%21;%20;%01;%02;%11;%10;%12;%17;%03;%04;%05;%06;%07;%09;%08;%18;%90;%91;%93;/n\r" > /dev/USB-Serial-Port &&
sleep 1 &&

# Messintervall auf 60 Sekunden:
echo "set sample interval" &&
echo -e "CS/I/60\r" > /dev/USB-Serial-Port &&
sleep 1 &&
# Return nicht mehr in logdatei schreiben
kill $pid &&

# Daten in Datei speichern
(
cat /dev/USB-Serial-Port >> /home/pi/Documents/Loggerdaten/Loggerdaten.txt &
logging=$! &&
echo "started logging with PID $logging"
) &&
exit 0

usb-serial-remove.sh

Wird aufgerufen beim abziehen des Seriellen Adapters. Garantiert, dass beim nächsten Anstecken die Verbindung wieder hergestellt werden kann

#!/bin/bash
echo -e "\nserial connector removed" $(date +"%F %X") >> /home/pi/logs/usb-serial-log.log &&
pkill cat &&
exit 0

Real Time Clock

Damit der Raspberry auch ohne Internet-Verbindung die richtige Zeit behält, ist eine Real Time Cloc (RTC) notwendig, die man auf einem kleinen Chip kaufen kann, der auf die GPIO-Pins des Raspberrys aufgesteckt wird. Ich hab diese gekauft. Hier habe ich eine Anleitung gefunden, um den Raspberry auf die RTC einzustellen. Konkret habe ich verwendet:

# Remove the module blacklist entry so it can be loaded on boot
sudo sed -i 's/blacklist i2c-bcm2708/#blacklist i2c-bcm2708/' /etc/modprobe.d/raspi-blacklist.conf
# Load the module now
sudo modprobe i2c-bcm2708
# Notify Linux of the Dallas RTC device
echo ds1307 0x68 | sudo tee /sys/class/i2c-adapter/i2c-1/new_device
# Test whether Linux can see our RTC module.
sudo hwclock

Eine neue Real-Time-Clock muss man wahrscheinlich erstmal auf die richtige Uhrzeit einstellen. Dazu am besten den Raspberry einmal mit Internetverbindung booten, damit er sich die Zeit aus dem Internet holt. Dann mit folgendem Befehl die RTC einstellen:

sudo hwclock -w

Nun muss noch eingestellt werden, dass beim nächsten Booten die Systemzeit von der RTC geholt wird:

# Add the RTC device on boot
sudo sed -i 's#^exit 0$#echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device#' /etc/rc.local
echo "sudo hwclock -s" | sudo tee -a /etc/rc.local
echo exit 0 | sudo tee -a /etc/rc.local