czwartek, 20 grudnia 2012

Perl encoding problem

SW asked me to augment a Perl script that originally processes ISO-8859-2 encoded text (TeX) files only by adding UTF-8 and CP1250 (one byte MS Windows encoding for Central Europe) encodings as well.

I made up it as follows (not sure if correct):


use Getopt::Long;
my $coding = 'utf8'; my $showhelp= '' ;
GetOptions( "coding=s" => \$coding, "help|\?" => \$showhelp,) ;
if ( $showhelp ) { print "*** $0 [-coding=[cp1250|iso88592|utf-8]] file1 file2...\n" ;
exit 1; }

if ( $coding =~ /cp1250/ ) { $coding='cp1250'; use open ':encoding(cp1250)'; }
elsif ( $coding =~ /iso8859\-?2/ ) { $coding='iso-8859-2'; use open ':encoding(iso-8859-2)'; }
elsif ( $coding =~ /utf\-?8/ ) { $coding='UTF-8'; use open ':encoding(UTF-8)'; }
else { die "*** Unknown coding: $coding\n"; exit 1; }

print STDERR "*** Coding: $coding\n";
## rest of the script omitted ....

I reencoded the script from original ISO-8859-2 to UTF-8 as well with iconv, so all strings are UTF-8 encoded now.

poniedziałek, 17 grudnia 2012

GDP per capita in purchasing power standards of EU member states

While GDP per capita is mainly an indicator reflecting the level of economic activity, Actual Individual Consumption (AIC) per capita is an alternative indicator better adapted to describe the material welfare situation of households.

GDP and AIC per capita in PPS, EU27 = 100


+-------------------------------------------------+
GDP per capita AIC per capita
2009 2010 2011 2009 2010 2011
+-------------------------------------------------+
EU27 100 100 100 100 100 100
Euro area (EA17) 109 108 108 107 107 107
+-------------------------------------------------+
Luxembourg 255 267 271 144 141 140
Netherlands 132 131 131 118 114 113
Ireland 130 129 129 103 103 101
Austria 125 127 129 116 118 119
Sweden 120 124 127 116 114 116
Denmark 123 128 125 116 116 113
Germany 115 119 121 115 117 120
Belgium 118 119 119 109 111 111
Finland 114 113 114 110 111 112
United Kingdom 111 111 109 121 120 118
France 109 108 108 113 113 113
Italy 104 101 100 103 102 101
Spain 103 99 98 96 95 94
Cyprus 100 97 94 102 99 98
Malta 83 85 85 85 83 84
Slovenia 87 84 84 81 80 81
Czech Republic 83 80 80 73 71 71
Greece 94 87 79 104 97 91
Portugal 80 80 77 83 84 81
Slovakia 73 73 73 72 71 70
Estonia 63 63 67 58 56 58
Lithuania* 55 57 66 63 61 70
Hungary 65 65 66 62 60 61
Poland 61 63 64 64 67 69
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Latvia 54 54 58 52 53 57
Romania 47 47 49 46 46 47
Bulgaria 44 44 46 43 43 45
+-------------------------------------------------+
Norway 177 181 186 134 136 135
Switzerland 150 154 157 128 129 130
Iceland 120 112 111 111 106 107
Croatia 62 59 61 58 57 59
Turkey 46 50 52 51 54 57
Montenegro 41 42 42 50 52 53
FYR Macedonia 36 36 35 41 40 40
Serbia 36 35 35 44 44 43
Albania** 28 27 30 32 30 34
BosniaHerzegovina 31 30 30 37 36 36
+-------------------------------------------------+

Source: Eurostat newsrelease 180/2012, December 2012 (http://epp.eurostat.ec.europa.eu)

* 2011 population figures adjusted on the basis of the 2011 Census. Therefore the per capita indices for 2011 are not entirely comparable with previous years due to this break in time series.

** Figures for all years based on Eurostat estimate of GDP

The euro area (EA17) consists of Belgium, Germany, Estonia, Ireland, Greece, Spain, France, Italy, Cyprus, Luxembourg, Malta, the Netherlands, Austria, Portugal, Slovenia, Slovakia and Finland.

piątek, 14 grudnia 2012

Grim prospects of EU economy

Total production in EU 2012 Unemployment in EU 2012
Source: Eurostat
Click on image to enlarge

The collapse of production during April-2008--April-2009 period (left figure) corresponds to a significant rise in unemployment (right figure), however in April 2009, when production was at the lowest level, unemployment rate was approx 9% while currently is is approx 12%.

sobota, 8 grudnia 2012

Raspberry Pi/DHT-22 sensor: registering temperature and humidity

Czujnik DHT-22
Fig. #1: DHT-22 sensor

Fig. #2: Testing DHT-22
Instalacja 2x czujnik DHT-22
Fig. #3: Wiring

To build the installation one has to buy:

DHT-22 temperature/humidity sensor (8 USD per sensor). Not cheap...

0,25W 10K OHM carbon resistor (very cheap).

female connectors (pol. kable połączeniowe żeńskie), telephone cables or similar four core cable (pol. kabel czterożyłowy), terminal block (pol. kostka połączeniowa) and heat shrink tubing (pol. rurka termokurczliwa) to insulate and strengthen connections. The recommended way is to use a breadboard (pol. płytka stykowa/prototypowa) as described in learn.adafruit.com. My interest in electronics is limited, I've never used breadboards etc... I had some spare cables and terminal blocks so I designed it that way (cf. pictures).

NOTE: The cheaper version of the DHT-22 is a DHT-11 (aka SHT-11). Tempted by the lower price I bought two DHT-11 sensors but I do not recommend it. First of all, the temperature is measured in the range of 0 °C to 50 °C (with poor accuracy of +/- 2 °C) so is not suitable for outdoor (at least in Europe). Second, the humidity seems to be understated. Third, it does not work when DQ line is connected to other GPIO pins than pin #24 (maybe it's a software problem). For comparison, DHT-22 measures the temperature in the range of-40C to +80 C with an accuracy of +/- 0.5 °C.

I follow the tutorial available at learn.adafruit.com but some details were modified.

Hardware

There are four pins in DHT-22 (see Figure # 1). I connected data line (DQ) of each sensor to pins P22, P24 and P25 respectively (each sensor must have a separate data line). Vdd pin of each sensor to P1 (3.3 V supply). GND (ground) pin of each sensor to P6. In addition, each DQ was connected via the resistor with the power line Vdd.

Pin Null is not used.

The sensors were connected to GPIO pins via terminal blocks, cables and some soldering.

Software

One has to download, compile and install the necessary library:


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

then the application retrieving the data from the sensors has to be installed:


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

One has to modify Makefile file, namely add -l rt at the end of the line that starts with CFLAGS:


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

now:


## in Adafruit_DHT_Driver directory
pi@raspberrystar $ make

If everyting works, then:


# Run ./Adafruit_DHT sensor-type DQ-pin-number
pi@raspberrystar $ sudo ./Adafruit_DHT 22 25
Using pin #25
Data (40): 0x3 0xe7 0x0 0x17 0x1
Temp = 2.3 *C, Hum = 99.9 %

The directory Adafruit_DHT_Driver contains also Adafruit_DHT_googledocs.ex.py Python script which can upload sensor readings directly to google.docs spreadsheet. To run Adafruit_DHT_googledocs.ex.py one has to install gspread module first:


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

Adafruit_DHT_googledocs.ex.py script: 1) in an infinite loop runs every 30 seconds the program Adafruit_DHT, 2) retrieves temperature/humidity, 3) sends temperature/humidity readings to google.docs. A fragment of the script looks like:


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))
## omitted code ...

time.sleep(30)

Because I want to process somehow the data (not only to retrieve and upload to google.docs) I modify Adafruit_DHT_googledocs.ex.py script. My version Adafruit_DHT_googledocs.ex.py is limited to sending to google.docs values passed as arguments to the call:


temp = float(sys.argv[1])
humidity = float(sys.argv[2])
## omitted code ...

The following bash script takes care of the rest:


#!/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=""

## 5 tries with 5s sleep between them
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

## All attempts to read sensors were unsuccessful
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

## A sensor in the room:
ReadSensor $SENSTYPE "24"
READINGS="$DHT_CURR_TEMP $DHT_CURR_HUM"
sleep 12

## Outdoor sensor:
ReadSensor $SENSTYPE "25"
READINGS="$READINGS $DHT_CURR_TEMP $DHT_CURR_HUM"
sleep 12

## A sensor in the porch:
ReadSensor $SENSTYPE "22"
READINGS="$READINGS $DHT_CURR_TEMP $DHT_CURR_HUM"

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

# Upload to google
/home/pi/bin/DHT_googledocs.ex.py $READINGS

As in the case of 1-Wire bus there are problems with the reading of the sensor. That's why the function ReadSensor is trying to read the sensor several times. Maximum number of failed attempts, we have observed during several days of operation is 3.

The script runs every 30 minutes from cron:


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

LOG file looks something like this:


@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 %

Row starting with the @ contains the date and time (@ is added for subsequnt easy parsing). Lines that begin with nn = m contain the data retrived from the sensor (nn is the sensor number, m denotes the number of successful attempt or ? in case when all attempts were unsuccessful)

Note: I noticed that higher system load (including intensive I/O operations) cause problems to retrieve data from the sensors. I tried to run motion detection application (motion) configured to use as little system resources as possible with no success. Rapberry overclocked to 900 Mhz performs significantly better but still only about 20% tries returns some data. Exact nature of the problem is a mystery to me as for example top indicates that still more there 80% of CPU is free.

Other question to consider is: whether the readings are correct during high humidity? My outdoor sensors tend to indicate 99% humidity pretty frequently which seems suspicious. I have compared data obtained from 3 different sensors (namely WH 2080 clone, Oregon Scientific's RMS300 and DHT-22) and some differ significantly.

Conversion to HTML and generating charts with dht2ht.pl

Perl script dht2ht.pl creates a HTML table and charts showing temperature/humidity readings as well as dew point, calculated with the following approximation formula: $$ D_p = (237.7 \cdot \gamma(T, H) ) / (17.271 - \gamma(T, H) ) $$

where: $$ \gamma(T, H) = 17.271 \cdot T / (237.7 + T) + \log (H / 100.0) $$

Script outcome is available here. All scripts and other stuff discussed in this blog post are available here.

Google.docs sheet containing readings from all my 3 sensors is available here. (Note: for some important reasons Adafruit_DHT_googledocs.ex.py script started adding data from the 162th line of the spreadsheet.)

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 i --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

wtorek, 9 października 2012

Zgranie filmu z płyty DVD do formatu AVI

Ponieważ zainstalowany frontend pn. k9copy kończy pracę błędem, zripowałem płytę DVD za pomocą czegoś takiego:


#!/bin/bash
if [ "$1" = "" ] ; then echo "*** Podaj numer ścieżki, np $0 3 ***" ; exit 1 ; fi
mencoder dvd://$1 -ovc lavc -lavcopts vcodec=mpeg4:vhq:vbitrate=2400:aspect=16/9 \
-vf scale=720:576 -oac mp3lame -lameopts br=128 -o Movie-$1.avi

czwartek, 4 października 2012

Raspberry Pi: magistrala 1-Wire i rejestracja temperatury

DS18B20
Rys. #1: Układ DS18B20
zdjęcie
Rys. #2
zdjęcie
Rys. #3
zdjęcie
Rys. #4
zdjęcie
Rys. #5: Kostka
DS18B20
Schemat #6: 2 czujniki

Poniższy opis w dużym stopniu jest oparty na tekście Raspberry Pi odczyt temperatury przez nodejs.

Podłączenie czujników DS18B20 do Raspberry Pi

Czujniki temperatury DS18B20 firmy Dallas należy dołączyć do portu GPIO (styki P1, P6 oraz P7)

Port GPIO

Do wykonania instalacji potrzeba:

Tzw. przewody połączeniowe żeńskie (4 zł za 10 sztuk); w oryginale to się nazywa jumper wire.

REZYSTOR 0,25W 4,7K OHM węglowy (1,50 zł za 100 sztuk).

DS18B20 termometr 1-wire (4,00--5,00 zł za sztukę).

Kostka elektryczna (około 3 zł za 10 sztuk).

Rurka termokurczliwa do izolacji i wzmocnienia połączeń.

Przewód telefoniczny czterożyłowy lub inny podobny.

Trzy wyprowadzenia układu DS18B20 to: masa (GND), linia danych (DQ) i zasilanie (Vdd). Jeżeli położymy układ ,,napisem do góry''/,,stykami do dołu'' (por. rys. #1), to pierwszy styk od lewj (#1) to GND, środkowy (#2) to DQ a prawy (#3) to Vdd.

Styki DQ oraz Vdd należy złączyć za pomocą rezystora 4K7 (4k7 ohm resistor).

Ja to zrobiłem w ten sposób, że obciąłem złącza żeńskie po jednej stronie przewodów połączeniowych i wsadziłem je do kostki elektrycznej. Następnie dolutowałem dwa kilkucentymetrowe przewody po obu stronach rezystora i końce tych przewodów wsadziłem do odpowiednich styków w kostce elektrycznej. W ten sposób połączyłem DQ oraz Vdd za pomocą rezystora (rys. #5).

Styki czujnika DS18B20 dolutowuję do poszczególnych żył przewodu telefonicznego (jedna żyła jest zbędna). Ponieważ DS18B20 jest bardzo mały, to żeby piny się nie stykały izoluję każdą żyłę cienką rurką termokurczliwą (por. zdjęcie #2, umieszczone obok). Żeby ładniej wyglądało, ale przede wszystkim żeby połączenie czujnik-kabel było mocniejsze (żyły są bardzo cienkie) to na cały przewód naciągam szerszą rurkę (por. zdjęcie #3) i też zgrzewam (por. zdjęcie #4). Zgrzewam zapalniczką....

Odpowiednie żyły przewodu telefonicznego z dolutowanym na końcu układem DS18B20 łączę z kostką. Wydaje mi się, że tak jest lepiej--można łatwiej dodać kolejny termometr lub wydłużyć przewód. Układ jest gotowy i można go podłączyć: Vdd do pina P1, GND do pina P6 a DQ do pina P7. Który pin jest P1 jest jasno oznaczone na płytce.

Moja instalacja ma dwa układy DS18B20: jeden do mierzenia temperatury w pokoju a drugi do mierzenia temperatury na zewnątrz (por. rysunek #5/schemat #6).

Aktualizacja systemu (czyli po co robiłem kopię zapasową)

Aby Raspberry obsługiwał 1-Wire należy dokonać aktualizacji systemu, co można wykonać na kilka sposobów. Zgodnie z sugestią ze strony Raspberry Pi odczyt temperatury przez nodejs zdecydowałem się na Hexxeh's easy updater tool:


wget http://goo.gl/1BOfJ -O rpi-update
sudo mv rpi-update /usr/bin/rpi-update
sudo chmod +x /usr/bin/rpi-update

# Na wszelki wypadek:
sudo apt-get install ca-certificates

# rpi-update wymaga zainstalowania gita
sudo apt-get install git-core

Teraz można wykonać aktualizację (zabieg zawsze potencjalnie ryzykowny dlatego lepiej się zabezpieczyć wykonaniem kopii systemu, co opisano tutaj.)


pi@raspberrystar ~ $ sudo rpi-update
Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS
Performing self-update
Autodetecting memory split
Using ARM/GPU memory split of 192MB/64MB
We're running for the first time
Setting up firmware (this will take a few minutes)
Using HardFP libraries
If no errors appeared, your firmware was successfully setup
A reboot is needed to activate the new firmware

Teraz należy dodać do jądra moduły niezbędne do obsługi instalacji pomiaru temperatury:


sudo modprobe w1-gpio
sudo modprobe w1-therm

Uwaga: aby moduły były dodawana automatycznie, przy starcie systemu należy dodać w1-gpio oraz w1-therm do pliku /etc/modules. Mój plik /etc/modules wygląda następująco:


pi@raspberrystar ~ $ less /etc/modules
snd-bcm2835
w1-gpio
w1-therm

Odczyt temperatury

Po podłączeniu ,,magistrali 1-Wire'' (czytaj kabelków z czujnikami DS18B20 w opisany wyżej sposób) wykonanie polecenia ls w katalogu /sys/bus/w1/devices/ powinno ujawnić urządzenia dołączone do magistrali:


pi@raspberrystar ~ $ ls -l /sys/bus/w1/devices/
lrwxrwxrwx 1 root root 0 paź 3 13:45 w1_bus_master1 -> ../../../devices/w1_bus_master1
lrwxrwxrwx 1 root root 0 paź 3 13:58 28-0000032562aa -> ../../../devices/w1_bus_master1/28-0000032562aa
lrwxrwxrwx 1 root root 0 paź 3 19:27 10-000802012743 -> ../../../devices/w1_bus_master1/10-000802012743

W przypadku mojej ,,magistrali'' są to dwa termometry: 28-0000032562aa oraz 10-000802012743.

Do odczytania temperatury korzystamy z pliku w1_slave:


pi@raspberrystar ~ $ cat /sys/bus/w1/devices/28-0000032562aa/w1_slave
60 01 4b 46 7f ff 10 10 b5 : crc=b5 YES
60 01 4b 46 7f ff 10 10 b5 t=22000

Wielkość temperatury to 22,000 stopnie Celsjusza (t=22000).

Aby odczytać dwa termometry na raz, dodać datę i czas odczytu, a następnie zapisać wszystko do pliku LOG można zastosować następujący skrypt (,,prywatne'' skrypty umieszczam w katalogu ~/bin/):


#!/bin/bash
LOG_DIR=/home/pi/Logs/Digitemp
## Data/czas odczytu
echo "@`date "+%Y%m%d%H%M%S"`" >> $LOG_DIR/digitemp.log
## Czujnik nr1
echo "28-0000032562aa `cat /sys/bus/w1/devices/28-0000032562aa/w1_slave | tr '\n' ' '`" >> $LOG_DIR/digitemp.log
## Czujnik nr2
echo "10-000802012743 `cat /sys/bus/w1/devices/10-000802012743/w1_slave | tr '\n' ' '`" >> $LOG_DIR/digitemp.log

Zapis wygląda jakoś tak (przy założeniu, że powyższy skrypt znajduje się w pliku ~/bin/dt2ht.sh):


pi@raspberrystar ~ $ ~/bin/dt2ht.sh
@20121004100001
28-0000032562aa 5f 01 4b 46 7f ff 01 10 9b : crc=9b YES 5f 01 4b 46 7f ff 01 10 9b t=21937
10-000802012743 1e 00 4b 46 ff ff 0d 10 38 : crc=38 YES 1e 00 4b 46 ff ff 0d 10 38 t=14937

Gdzie: @20121004100001 oznacza datę i czas, 28-0000032562aa/10-000802012743 to identyfikatory czujników a zapis w stylu t=21937, to temperatura (21,937 stopni Celsjusza).

Teraz wystarczy dodać odpowiedni wpis do pliku crontab użytkownika pi, aby, przykładowo temperatura była rejestrowana co godzinę (dt2ht.sh to nazwa opisanego wyżej skryptu służącego odczytu temperatury i zapisania wyników do pliku LOG):


pi@raspberrystar ~ $ mkdir ~/Crontab
pi@raspberrystar ~ $ vi ~/Crontab/crontab
# do pliku ~/Crontab/crontab wpisuję jeden wiersz:
# 0 * * * * /home/pi/bin/dt2ht.sh
# zapisuję/kończę pracę vi

# dodanie zawartości ~/Crontab/crontab do zadań crona
pi@raspberrystar ~ $ crontab ~/Crontab/crontab

# sprawdzenie czy wszystko jest OK:
pi@raspberrystar ~ $ crontab -l
0 * * * * /home/pi/bin/dt2ht.sh

Pozostaje tylko wykonanie (np. za pomocą prostego skryptu PeHaPe) jakiś bardziej efektownego sposobu wyświetlania zawartości pliku LOG.

Dopisane 14 października 2012: W zasadzie działa, ale nie do końca. Czasami nie odczytuje temperatury wypisując CRC error, co wygląda następująco:


62 01 4b 46 7f ff 0e 10 03 : crc=03 NO 62 01 4b 46 7f ff 0e 10 03 t=-1250

Nie mam pojęcia czemu tak się dzieje. Jako workaround wymyśliłem kilkukrotny odczyt:


#!/bin/bash

LOG_DIR=/home/pi/Logs/Digitemp

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

## próbuj odczytać max 3 razy
for i in 1 2 3; do
WYNIK=`cat /sys/bus/w1/devices/$sensorId/w1_slave | tr '\n' ' '`
## pole 12 ($12) to wynik odczytu (YES albo NO):
SUCCESS=`echo $WYNIK | awk '{ print $12}'`

if [ "$SUCCESS" = "YES" ] ; then
## zapisuje numer próby oraz odczytaną informację :
echo "$sensorId=$i $WYNIK" >> $LOG_DIR/digitemp.log
## przerwij pętlę odczytano temperaturę:
break
fi
sleep 3;
done

## Trzy próby okazały się nieudane:
if [ $SUCCESS = "NO" ] ; then
echo "$sensorId=? $WYNIK" >> $LOG_DIR/digitemp.log
fi
}

echo "@`date "+%Y%m%d%H%M%S"`" >> $LOG_DIR/digitemp.log

## Czujnik w pokoju:
ReadSensor "28-0000032562aa"

## Czujnik na zewnątrz:
ReadSensor "10-000802012743"

Plik LOG wygląda jakoś tak:


@20121014220001
28-0000032562aa=1 62 01 4b 46 7f ff 0e 10 03 : crc=03 YES 62 01 4b 46 7f ff 0e 10 03 t=22125
10-000802012743=1 0e 00 4b 46 ff ff 0f 10 fd : crc=fd YES 0e 00 4b 46 ff ff 0f 10 fd t=6812

gdzie pierwszy wyraz to numer sensora, znak równości, numer próby w której odczytano temperaturę lub ?, jeżeli temperatury nie odczytano.

wtorek, 2 października 2012

Raspberry Pi: tworzenie kopii karty SDHC

Ustalamy jak nazywa się urządzenie, w którym jest zamontowana karta SDHC:


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

Teraz można zamontować pendrive'a, na którym zostanie zapisana kopia:


pi@raspberrypi ~ $ mkdir /media/sda1
## zakładamy że pendrive jest oznaczony jako /dev/sda1
pi@raspberrypi ~ $ mount /dev/sda1 /media/sda1

Kopiowanie za pomocą dd


pi@raspberrypi ~ $ sudo dd if=/dev/mmcblk0 | gzip -1 > /media/sda1/sd_backup.img.gz
## bez kompresji będzie szybciej:
pi@raspberrypi ~ $ sudo dd if=/dev/mmcblk0 of=/media/sda1/sd_backup.img
## po sieci, w przypadku gdy nie mamy wolnego pendrive'a
ktos@inny-komputer ~ $ sudo ssh root@raspberrystar dd if=/dev/mmcblk0 | gzip -1 | dd of=sd_backup.img.gz

Uwaga: raspberrystar powinno być zamienione na odpowieni adres IP (lub nazwę).

Przywracanie systemu z kopii zapasowej:


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

Gdzie /dev/sdX to nazwa urządzenia z zamontowaną czystą kartą SDHC.

Więcej szczegółów i bardziej detalicznie jest tutaj.

Kopiowanie do pliku zamontowanego za pomocą sshfs:


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

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

## montuję (jako użytkownik tomek) zdalny katalog /public/raspberry na
## komputerze jupiter; punkt montowania to ~/Dist/jupiter
pi@raspberrypi ~ $ sshfs tomek@jupiter:/public/raspberry/ ~/Dist/jupiter
failed to open /dev/fuse: Permission denied

# Za pierwszym strzałem (jak zwykle) nie wyszło, a to dlatego, że
# wyłącznie członkowie grupy "fuse" mogą czytać/pisać do/na /dev/fuse.
# Należy zatem dodać użytkownika pi do grupy "fuse":

pi@raspberrypi ~ $ sudo usermod -a -G fuse pi
## Teraz trzeba się wy/zalogować żeby zadziałały modyfikacje w /etc/group

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

## kopiowanie za pomocą dd (z kompresją `w locie'):
sudo dd if=/dev/mmcblk0 | gzip -1 > /home/pi/Dist/jupiter/raspberrystar.iso