Pierwszy problem pojawił się przy próbie kompilacji aplikacji Digitemp. Otóż wina leżała w nieprawidłowej ścieżce do jednego z plików nagłówkowych, tą kwestię szybko udało mi się rozwiązać patchem:
--- Digitemp-3.6.0.Orig/src/digitemp.C 2008-08-28 06:42:48.000000000 +0200 +++ digitemp-3.6.0/src/digitemp.c 2009-12-22 23:05:46.000000000 +0100 @@ -79,10 +79,10 @@ #if DARWIN #include <machine/endian.h> #endif -#if FREEBSD +#if FREEBSD || NETBSD #include <sys/endian.h> #endif -#if !defined(DARWIN) && !defined(FREEBSD) +#if !defined(DARWIN) && !defined(FREEBSD) && !defined(NETBSD) #include <endian.h> #endif
Po zaaplikowaniu patcha, kompilacja przebiegła bez błędów. Lecz przy próbie uruchomienia z wybranym urządzeniem portu szeregowego (/dev/ttypU0) program przestał odpowiadać.
Po dłuższych poszukiwaniach udało mi się znaleźć wpis na liście mailingowej gdzie opisywany jest identyczny błąd. Z kolejnych wpisów wynikło, że problem został rozwiązany, lecz niestety nie zostało napisane w jaki sposób. Napisałem więc maila do autora z nadzieją, że otrzymam rozwiązanie tego problemu. Po kilku dniach otrzymałem odpowiedź, okazało się, że problem leżał w błędnej inicjacji portu szeregowego.
Na tej podstawie stworzyłem kolejnego patcha:
W końcu udało mi się dokonać odczytu temperatury z czujnika, tu już bez zgrzytów i niespodzianek.--- digitemp-3.6.0.old/userial/ds9097/linuxses.c 2007-06-07 23:25:58.000000000 +0200 +++ digitemp-3.6.0/userial/ds9097/linuxses.c 2009-12-24 04:27:25.000000000 +0100 @@ -55,7 +55,7 @@ SMALLINT owAcquire(int portnum, char *port_zstr) { /* Open the serial port */ - if ((fd[portnum] = open(port_zstr, O_RDWR)) == -1) + if ((fd[portnum] = open(port_zstr, O_RDWR|O_NONBLOCK)) == -1) { OWERROR(OWERROR_GET_SYSTEM_RESOURCE_FAILED); perror("owAcquire: failed to open device");
Kolejnym etapem była konfiguracja digitempa, oto mój plik konfiguracyjny programu:
Najważniejsze parametry do ścieżka do portu, pliku w którym zapisywane będą odczyty i format zapisu. Jako format wybrałem unixowy timestamp i wartość temperatury z dwoma miejscami po przecinku oddzielone średnikem, czyli coś na kształt formatu CSV.TTY /dev/ttyU0 LOG /var/log/temperature READ_TIME 1000 LOG_TYPE 0 LOG_FORMAT "%N;%.2C" CNT_FORMAT "%b %d %H:%M:%S Sensor %s #%n %C" HUM_FORMAT "%b %d %H:%M:%S Sensor %s C: %.2C F: %.2F H: %h%%" SENSORS 1 ROM 0 0x10 0x11 0xB4 0x3B 0x00 0x08 0x00 0x6D
Aplikacja wywoływana jest z crontaba co minutę poleceniem: digitemp_DS9097 -s /dev/ttyU0 -q -t 0
Nadszedł czas, aby aktualną temperaturę można było odczytać w jakiś dogodny sposób. Jako, że jestem wyznawcą Perla, napisałem mały skrypt CGI, który generuje stronę dostępną w intranecie za pomocą bardzo prostego serwera HTTP bozohttpd.
Dalszym etapem była wizualizacja przebiegu temperatury na wykresie, tutaj posłużyłem się kombajnem do generowania wykresów: GNUplot. Jest to nieocenione narzędzie, dzięki któremu rysowanie wykresów jest bardzo proste, lecz aby tego dokonać trzeba się zapoznać ze składnią tego programu.
Stworzony przeze mnie plik wykonawczy rysujący wykres ma postać:
Z powyższego zapisu wynika, że wykres ma być zapisywany do formatu PNG, format odczytywanych danych oddzielany jest średnikiem, wartości na osi Y obracane są w pozycji pionowej, format danych osi X to czas, i jest on w postaci : "%H:%M:%S/%j" (%j przestawia liczbę reprezentującą ilość dni od początku roku. Dodałem to po to, aby wykres generowany z przestrzeni kilku dni był poprawnie interpretowany), ostatnia linia pobiera dane z pliku i formatue je, nadaje nazwę przebiegowi i określa w jaki sposób ma być on wykreślony.#!/usr/pkg/bin/gnuplot -persist set terminal png nocrop medium size 800,480 set output '/var/www/wykres.png' set datafile separator ";" set title 'Wykres temperatury' set xlabel 'Czas' set ylabel 'Temperatura' set xtics rotate set xdata time set timefmt "%H:%M:%S/%j" set grid plot '/root/meteo/temp' using 1:2 title "\xb0C" with lines
Dane wejściowe do tego wykresu są generowane z głównego loga za pomocą kolejnego skryptu, tym razem powłokowego:
Skrypt pobiera ilość godzin do odczytania z loga jako parametr i porównuje czy jest to liczba, jeśli tak - mnoży daną wartość przez ilość minut w godzinie, czyli tak na prawdę ile linii należy odczytać z loga. Za odczyt odpowiedzialna jest komenda tail z podaną wartością linii do odczytu, kolejną częścią strumienia jest formatowanie odczytanej linii do czytelnej postaci czasu i przepisanie wartości temperatury zachowując format CSV. Dane są przekierowane do pliku, z którego generowany jest wykres.#!/bin/sh if [ $1 -eq $1 2> /dev/null ]; then lines=`expr $1 \* 60` else exit 1 fi tail -n $lines /var/log/temperature | awk -F';' '{ print strftime("%H:%M:%S/%j", $1) ";" $2; }' > /root/meteo/temp /usr/pkg/bin/gnuplot -persist /root/meteo/plot
Skrypt ten również wywoływany jest z parametrem "12", czyli generuje przebieg z dwunastu ostatnich godzin przez crontaba, z tym, że co godzinę.
Wcześniej wspomniana strona, generowana jest przez ten oto skrypt:
W tym miejscu warto dodać jedynie, że strona jest automatycznie odświeżana co 60s. Wynik końcowy przedstawia się tak:#!/usr/bin/perl use warnings; use strict; use CGI; my $cgi = new CGI; open (IN, "</var/log/temperature") || die; my @values = <IN>; close IN; my $temp = $values[$#values]; $#values = -1; @values = split(/;/, $temp); print $cgi->header(-type=>'text/html', -charset=>'UTF-8', -refresh=>60); print $cgi->start_html(-title=>'Temperatura za oknem'); print "<center>", $cgi->h1({-style=>'font-family:Georgia,serif'}, "Temperatura"), $cgi->span({-style=>'color:#00f;font-family:Tahoma,sans-serif;font-size:66px'}, "$values[1]°C"), $cgi->pre("Czas odczytu: ".scalar(localtime($values[0]))); print $cgi->img({src=>'wykres.png', align=>'center'}), "<br/>"; print $cgi->img({src=>'img/powered-by-NetBSD.png', align=>'center'}), $cgi->img({src=>'img/maxim_dallas_logo.jpg', align=>'center'}); print "</center>", $cgi->end_html;
Odczyty temperatury są również przekazywane systematycznie do innego serwera, po to aby mieć możliwość sprawdzenia pomiaru zdalnie. W tym celu po stronie klienta powstał prosty skrypt powłoki formatujący dane i obliczający ich sumę SHA1 w celu zabezpieczenia przesyłanych danych, przetworzone dane są wysyłane do serwera curlem, gdzie za sprawdzenie poprawności i zapis do pliku odpowiada perlowy skrypt.
W planach mam jeszcze do zrobienia archiwizację pomiarów, i bardziej interaktywny interfejs odczytu temperatury.
Mam nadzieję, że informacje, które tutaj zamieściłem przydadzą się komuś kto chciałby uruchomić swój własny miniserwer temperatury. Wszystkie skrypty są mojego autorstwa, udostępniam je na zasadach licencji Creative Commons BY-SA 2.5.


