czwartek, 22 listopada 2012

Raspberry Pi: system backup with dd

First I find the device with SDHC card mounted:


pi@raspberrypi ~ $ sudo fdisk -l

Disk /dev/mmcblk0: 7969 MB, 7969177600 bytes
4 heads, 16 sectors/track, 243200 cylinders, total 15564800 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000108cb

Device Boot Start End Blocks Id System
/dev/mmcblk0p1 8192 122879 57344 c W95 FAT32 (LBA)
/dev/mmcblk0p2 122880 15564799 7720960 83 Linux

Now I mount a USB stick for storing a copy of the system:


pi@raspberrypi ~ $ mkdir /media/sda1
## Assume USB stick is /dev/sda1
pi@raspberrypi ~ $ mount /dev/sda1 /media/sda1

Raw copy with dd


pi@raspberrypi ~ $ sudo dd if=/dev/mmcblk0 | gzip -1 > /media/sda1/sd_backup.img.gz
## w/o compression:
pi@raspberrypi ~ $ sudo dd if=/dev/mmcblk0 of=/media/sda1/sd_backup.img
## over ssh in case there is no USB stick at hand:
user@othercomputer ~ $ sudo ssh root@raspberrystar dd if=/dev/mmcblk0 | gzip -1 | dd of=sd_backup.img.gz

Note: raspberrystar is the name of my system---change it to appropriate IP address or name.

Restoring system image:


pi@raspberrypi ~ $ zcat sd_backup.img.gz > /dev/sdX

Where /dev/sdX denotes the device with blank SDHC card mounted.

More details can be found here.

Copying over sshfs:


pi@raspberrypi ~ $ sudo apt-get install sshfs fuse-utils

pi@raspberrypi ~ $ mkdir -p ~/Dist/jupiter

## mounting (as user tomek) remote directory /public/raspberry at jupiter;
## mounting point is: ~/Dist/jupiter
pi@raspberrypi ~ $ sshfs tomek@jupiter:/public/raspberry/ ~/Dist/jupiter
failed to open /dev/fuse: Permission denied

On first try failure (as usual). Only fuse group members can read/write from/to /dev/fuse. User pi should be added to group "fuse":


pi@raspberrypi ~ $ sudo usermod -a -G fuse pi

To activate the modifications made in /etc/group one should log out/log in now.


pi@raspberrypi ~ $ sshfs tomek@jupiter:/public/raspberry/ ~/Dist/jupiter
pi@raspberrypi ~ $ ls -l /home/pi/Dist/jupiter
total 0

## Raw copy with dd (with compression):
sudo dd if=/dev/mmcblk0 | gzip -1 > /home/pi/Dist/jupiter/raspberrystar.iso

Added 3 Oct 2012:


15564800+0 przeczytanych recordów
15564800+0 zapisanych recordów
skopiowane 7969177600 bajtów (8,0 GB), 3504,04 s, 2,3 MB/s

Raw copying of 8Gb SDHC card with compression over sshfs took about 1 hr. The resulting image size is about 1,6 Gb.

Overclocking raspberry pi

It is said to overclock Raspberry Pi one has to upgrade the system:


sudo apt-get update && sudo apt-get install raspberrypi* raspi-config

Then configure it with raspi-config utility:


sudo raspi-config

It is good anyhow to check the system version first:


uname -a
Linux raspberrystar.pinkaccordions.org 3.2.27+ #174 PREEMPT Wed Sep 26 14:09:47 BST 2012 armv6l GNU/Linux

The kernel is up to date. Inspecting raspi-config I have discovered it is not updated, but I prefer to configure the system via CLI (command line or console) interface rather than GUI one (no need to connect the RPi to a TV set which is in another room:-). So I decided not to upgrade the system but rather manually configure it. To achieve overclocking one has to add the following lines to /boot/config.txt file:


pi@raspberrystar ~ $ sudo vim /boot/config.txt
## add the following:
temp_limit=80
arm_freq=900
sdram_freq=500

Now reboot:


pi@raspberrystar ~ $ sudo reboot

Check the dmesg:


pi@raspberrystar ~ $ dmesg | grep 7000
[ 1.956412] bcm2835-cpufreq: min=700000 max=900000 cur=700000

CPU frequency is still 700Mhz. To increase it one has to edit scaling_governor file:


pi@raspberrystar ~ $ sudo bash
root@raspberrystar:/home/pi#
echo "performance" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
## check if the above works:-)
pi@raspberrystar ~ $ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
performance
# display available options:
pi@raspberrystar ~ $ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
conservative ondemand userspace powersave performance

The ondemand option allows for adjusting CPU frequency depending on CPU utilization.

Without any further reboot the new settings work:


# check current CPU frequency
pi@raspberrystar ~ $ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
900000

There is even a temperature sensor available so one can check if the CPU is not overheated:


# check processor temperature:
pi@raspberrystar ~ $ /opt/vc/bin/vcgencmd measure_temp
temp=46.5'C

To boot the system with scaling_governor set to appropriate value one has to edit /etc/rc.local:


pi@raspberrystar ~ $ sudo vim /etc/rc.local
# Add the following line
# echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

I have performed a simple test:


#!/bin/bash
#
N=5
START=$(date +%s)

for ((i=1;i<=$N;i++ )) ; do

echo "**** Iteration $i ****"

STARTI=$(date +%s)
perl -e 'for ($i=0;$i<=10000000;$i++) { $s .= "xx"; }'
ENDI=$(date +%s) ; TOTALI=$(( $ENDI - $STARTI ))
echo "*** $TOTALI s."

done

END=$(date +%s)
TOTAL=$(( $END - $START ))
MEAN=`awk -v m=$TOTAL -v n=$N 'BEGIN { print m/n }'`

echo "total: " $TOTAL "mean: " $MEAN
##

The test uses the following perl program:


perl -e 'for ($i=0;$i<=10000000;$i++) { $s .= "xx"; }'

Because computing time can vary, the program has to be run N times and the mean time is reported.

My Rpi runs 28--29s at 700 Mhz, 25,8s at 800 Mhz and 21s at 900 Mhz.

So running at 900 Mhz results in almost 30% reduction of computing time.

środa, 14 listopada 2012

Raspberry Pi/czujnik DHT-22: rejestrowanie temperatury i wilgotności

Czujnik DHT-22
Rys. #1: DHT-22

Rys. #2: Testowanie DHT-22
Instalacja 2x czujnik DHT-22
Rys. #3: Okablowanie

Do wykonania instalacji potrzebne są:

Czujnik DHT-22 temperatury/wilgotności (ok. 30 zł za sztukę). Droga sprawa...

REZYSTOR 0,25W 10K OHM węglowy (1,00 zł za 100 sztuk na Allegro).

Do tego: przewody połączeniowe żeńskie, przewód telefoniczny czterożyłowy lub inny podobny, kostka elektryczna oraz rurka termokurczliwa do izolacji i wzmocnienia połączeń. (Por. Raspberry Pi: magistrala 1-Wire i rejestracja temperatury.)

UWAGA: tańszą wersja DHT-22 jest DHT-11 (aka SHT-11). Połasiłem się nawet na takowy, bo taniej ale nie polecam. Przede wszystkim mierzy temperaturę w przedziale od 0C do 50C (z kiepską dokładnością +/- 2C) więc nie nadaje się do pomiaru temperatury zewnętrznej. Do tego odczyt wilgotności jest zaniżony i nie działa podłączony do niektórych pinów GPIO (być może jest to problem oprogramowania, którego używam). Dla porównania DHT-22 mierzy temperaturę w przedziale od -40C do +80C z dokładnością +/- 0,5C.

Na stronie learn.adafruit.com znajduje się tutorial, z którego korzystałem...

Hardware

Sensor DHT-22 ma cztery piny (por. rys #1). Podłączyłem linie danych (DQ) do pinów P22, P24 i P25 (każdy sensor musi mieć oddzielną linię danych). Vdd każdego sensora do pina P1 (zasilanie 3,3V). GND (masa) każdego czujnika do pina P6. Ponadto każde DQ należało połączyć za pomocą rezystora z linią zasilania Vdd.

Pin Null nie jest wykorzystywany.

Lutowanie i łączenie wszystkiego do kupy wykonałem w sposób identyczny (kostka elektryczna, rurka termokurczliwa itp.) z opisanym bardziej szczegółowo we wpisie Raspberry Pi: magistrala 1-Wire i rejestracja temperatury.

Software

Pobieram, kompiluję i instaluję niezbędną bibliotekę:


pi@raspberrystar $ wget http://www.open.com.au/mikem/bcm2835/bcm2835-1.8.tar.gz
pi@raspberrystar $ tar -zxvf bcm2835-1.8.tar.gz
pi@raspberrystar $ cd bcm2835-1.8
pi@raspberrystar $ ./configure && make && sudo make install

Pobieram program do obsługi czujników:


pi@raspberrystar $ git clone https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git
pi@raspberrystar $ cd Adafruit-Raspberry-Pi-Python-Code
pi@raspberrystar $ cd Adafruit_DHT_Driver

W pliku Makefile należy dopisać -l rt na końcu wiersza zaczynającego się od CFLAGS:


CFLAGS = -std=c99 -I. -lbcm2835 -l rt

teraz:


## w katalogu Adafruit_DHT_Driver
pi@raspberrystar $ make

Jeżeli wszystko działa, to:


# Uruchomienie ./Adafruit_DHT typ-czujnika nr-pina-DQ
pi@raspberrystar $ sudo ./Adafruit_DHT 22 25
Using pin #25
Data (40): 0x3 0xe7 0x0 0x17 0x1
Temp = 2.3 *C, Hum = 99.9 %

W katalogu Adafruit_DHT_Driver znajduje się też skrypt Pythona pn. Adafruit_DHT_googledocs.ex.py służący do wysyłania odczytanych danych do arkusza google.docs. Skrypt Adafruit_DHT_googledocs.ex.py do działania potrzebuje modułu gspread:


pi@raspberrystar $ wget http://pypi.python.org/packages/source/g/gspread/gspread-0.0.13.tar.gz
pi@raspberrystar $ tar -zxvf gspread-0.0.13.tar.gz
pi@raspberrystar $ cd gspread
pi@raspberrystar $ sudo python setup.py install

Skrypt Pythona Adafruit_DHT_googledocs.ex.py: 1) w nieskończonej pętli uruchamia co 30 sekund program Adafruit_DHT, 2) wyłuskuje z wydruku wartości temperatury/wilgotności, 3) wysyła co trzeba na google.docs. Fragment skryptu wygląda następująco:


while(True):
output = subprocess.check_output(["./Adafruit_DHT", "2302", "4"]);
print output
# search for humidity printout
matches = re.search("Hum =\s+([0-9.]+)", output)
if (not matches):
time.sleep(3)
continue
humidity = float(matches.group(1))
## pominięty kod ...

time.sleep(30)

Ponieważ ja chcę oprócz wysłania na google.docs coś tam jeszcze zrobić z danymi, to miałem do wyboru albo rozbudować Adafruit_DHT_googledocs.ex.py o dodatkową funkcjonalność albo go uprościć. Wybrałem to drugie: moja wersja Adafruit_DHT_googledocs.ex.py ogranicza się wyłącznie do wysłania na google.docs wartości przekazanych jako argumenty wywołania:


temp = float(sys.argv[1])
humidity = float(sys.argv[2])
## pominięty kod ...

Resztą zajmie się poniższy skrypt basha:


#!/bin/bash
#
LOG_DIR=/home/pi/Logs/DHT
BIN_DIR=/home/pi/bin
SENSTYPE=22
SLEEP_TIME=5

function ReadSensor() {
local sensorType="$1"
local sensorId="$2"
local WYNIK=""
local SUCCESS=""

## zwiększyłem powtórzenia do 5 (sleep zmniejszony do 5s/było 10)
for i in 1 2 3 4 5; do
WYNIK=`sudo $BIN_DIR/Adafruit_DHT $sensorType $sensorId | tr '\n' ' '`
SUCCESS=`echo $WYNIK | awk ' { if (NF > 10) {print "YES"} else { print "NO"}}'`

if [ "$SUCCESS" = "YES" ] ; then
echo "$sensorId=$i $WYNIK" >> $LOG_DIR/DHT22.log
DHT_CURR_TEMP=`echo $WYNIK | awk '{print $13}'`
DHT_CURR_HUM=`echo $WYNIK | awk '{print $17}'`
break
fi
sleep $SLEEP_TIME;
done

## Wszystkie próby okazały się nieudane
if [ $SUCCESS = "NO" ] ; then
echo "$sensorId=? $WYNIK" >> $LOG_DIR/DHT22.log
DHT_CURR_TEMP="999.9"
DHT_CURR_HUM="999.9"
fi
}
echo "@`date "+%Y%m%d%H%M%S"`" >> $LOG_DIR/DHT22.log

## Czujnik w pokoju:
ReadSensor $SENSTYPE "24"
READINGS="$DHT_CURR_TEMP $DHT_CURR_HUM"
sleep 12

## Czujnik na zewnątrz:
ReadSensor $SENSTYPE "25"
READINGS="$READINGS $DHT_CURR_TEMP $DHT_CURR_HUM"
sleep 12

## Czujnik weranda:
ReadSensor $SENSTYPE "22"
READINGS="$READINGS $DHT_CURR_TEMP $DHT_CURR_HUM"

## zamiana na HTML + wykres
/usr/bin/perl /home/pi/bin/dht2ht.pl > /var/www/stats/DHT22.html

# Wyslanie na google
/home/pi/bin/DHT_googledocs.ex.py $READINGS

Podobnie jak w przypadku magistrali 1-Wire zdarzają się problemy z odczytaniem wartości czujnika. Na tą okoliczność funkcja ReadSensor próbuje odczytu kilkukrotnie. Maksymalna nieudana liczba prób, którą zaobserwowałem w ciągu kilkudniowej eksploatacji to 3.

Skrypt jest uruchamiany co 30 min przez crona:


1,31 * * * * /home/pi/bin/dht2ht.sh

Plik LOG wygląda jakoś tak:


@20121113230101
24=1 Using pin #24 Data (40): 0x2 0x22 0x0 0xc9 0xed Temp = 20.1 *C, Hum = 54.6 %
25=1 Using pin #25 Data (40): 0x3 0xe7 0x0 0x1c 0x6 Temp = 2.8 *C, Hum = 99.9 %
22=4 Using pin #22 Data (40): 0x2 0x73 0x0 0xb0 0x25 Temp = 17.6 *C, Hum = 62.7 %

Wiersz zaczynający się od @ zawiera datę i czas odczytu. Wiersze zaczynające się od nn=m zawierają odczytane dane (nn to numer czujnika, m numer próby w której odczytano wartości lub ? jeżeli wszystkie próby były nieudane)

Uwaga: zauważyłem, że przy intensywnych operacjach I/O są duże problemy z odczytaniem wskazań czujników.

Skrypt dht2ht.pl

Perlowy skrypt dht2ht.pl tworzy tabelę oraz wykresy prezentujące odczytane wartości plus obliczoną na ich podstawie temperaturę punktu rosy. Rezultat działania można oglądać tutaj. Omawiane w tym wpisie skrypty są zaś tutaj.

Arkusz google.docs zawierający odczyty z moich trzech czujników jest dostępny tutaj. (Uwaga: z jakiś ważnych powodów skrypt Adafruit_DHT_googledocs.ex.py zaczął dopisywanie danych od 162 wiersza arkusza.)

wtorek, 13 listopada 2012

Raspberry Pi: tworzenie kopii systemu z wykorzystaniem rsync

W temacie kopii zapasowej na stronach poświęconych Raspberry Pi znaleźć można wyłącznie(?) opisy jak to zrobić za pomocą dd. Ten sposób nie podoba mi się na dłużą metę z uwagi na czas -- kopiowanie karty 8Gb z kompresją przez sshfs zajęło około 1 godziny. Tworzenie kopii przyrostowych (za pomocą rsync) wydaje się lepszym pomysłem...

Wariant #1: dysk USB podłączony do RPi

Załóżmy, że do /etc/fstab wpisano:


/dev/disk/by-id/usb-WD_5000AAV_External_57442D574341535535303634313031-0:0-part1 \
/mnt/external-disk ext4 noauto,user,rw 0 0

Utworzenie kopii systemu sprowadza się wykonania:


rsync -av --exclude=/proc/ --exclude=/sys/ --exclude=/tmp/ \
--exclude=/mnt/ --exclude=/home/pi/Dist/ --delete / /mnt/external-disk/backup/rpi

Opcja --exclude pomija wymienione pliki/katalogi. W szczególności należy koniecznie umieścić tam katalogi, w których są/mogą być montowane inne systemy plików, np. /mnt/ (uniknięcie pętli, bo przecież /mnt/ zawiera zamontowany dysk USB) oraz /home/pi/Dist/ (moje zwyczajowe miejsce montowania systemów plików przez sshfs)

Wariant #2: kopia systemu tworzona na innym komputerze

Kopia systemu z raspberry będzie tworzona na innym komputerze dostępnym poprzez sieć.

Konfigurowanie rsynca należy rozpocząć od jego zainstalowania na obu komputerach (źródłowym i odbiorcy):


apt-get install rsync

Zawartość pliku /etc/rsyncd.conf po stronie źródła (czyli raspberry):


uid = 0
gid = 0
hosts allow = 192.168.1.***
transfer logging = no
read only = yes

[wholefs]
path = /
comment whole root fs

W pliku /etc/default/rsync (także po stronie źródła, tj. raspberry) należy wpisać lub ,,odhaszować'':


RSYNC_ENABLE=true

Teraz trzeba wystartować rsync (po ustawnieniu RSYNC_ENABLE, rsync będzie już uruchamiany w momencie startu systemu -- nie potrzeba do tego żadnych dodatkowych zabiegów w konfiguracji)


# /etc/init.d/rsync restart

Można sprawdzić czy działa podając polecenie (moje raspberry nazywa się raspberrystar) na komputerze odbiorcy (jako root):


rsync raspberrystar::wholefs/

Jeżeli wszystko jest OK to wyświetlona zostanie zawartość katalogu / na raspberrystar (czyli źródle).

Utworzenie kopii systemu sprowadza się wykonania:


rsync -av --exclude=/proc/ --exclude=/sys/ --exclude=/tmp/ \
--exclude=/mnt/ --exclude=/home/pi/Dist/ --delete \
raspberrystar::wholefs/ /public/sheeva/backup/raspberrystar/rootfs

Przywracanie systemu z kopii zapasowej

W razie potrzeby kopia może być szybko przeniesiona na inną kartę SDHC.


# uwaga: nazwy katalogów odpowiadają wariantowi #2 tworzenia kopii:
rsync -av --log-file=rsync_`date +%Y%m%d%H`.log --delete \
/public/sheeva/backup/raspberrystar/rootfs/ /public/sdX

Gdzie /public/sdX oznacza miejsce zamontowania karty SDHC. Karta musi być wcześniej sklonowana z ,,pierwszej'' kopii systemu wykonanej za pomocą dd, tj.:


zcat sd_backup.img.gz > /dev/sdX

Teraz można gdybać czy użycie samego dd nie będzie prostsze. Być może -- ja wolę korzystać na co dzień z rsynca.

Zauważyłem, że przy intensywnych operacjach I/O są problemy z odczytem danych przez GPIO (temperatura/wilgotność). Problem nie jest duży, ponieważ tworzenie pierwszej kopii systemu zajęło mi jakieś 20 min (można się spodziewać, że kolejne będą tworzone w ciągu kilku minut), ale dla większej pewności dodałem ionice--bwlimit (specjalnie tego nie testując, oprócz sprawdzenia, że działa)


ionice -c3 rsync -av --bwlimit=500 --exclude=/proc/ --exclude=/sys/ --exclude=/tmp/ \
--exclude=/mnt/ --exclude=/home/pi/Dist/ --delete \
raspberrystar::wholefs/ /public/sheeva/backup/raspberrystar/rootfs

Jeżeli powyższe zapisane zostanie do skryptu, np. o nazwie backup_raspberry.sh, to teraz aby ten skrypt był uruchamiany raz na tydzień, np. w niedzielę o 4:00 należy wpisac do pliku crontab (na komputerze odbiorcy) coś takiego:


0 4 * * 7 /root/bin/backup_raspberry.sh >> /root/logs/RSync/RSync.log 2>&1