2012. augusztus 18., szombat

Digitális hőmérés és annak grafikai ábrázolása


Sokat hagyatkoztam több webhely aktuális hőmérséklet kiírására amit sosem éreztem igaznak. Sőt többször fel is idegesítettem magam rajta, nos ezen gondoltam változtatok.
Másik, úgyis fut otthon állandóan egy gép, rá kéne venni egy kis statisztika gyűjtésre.


Harmadik, jó volt már megtanulni egy újdonságot, majd ha akarom tudom ábrázolni a teljes éves statisztikát mert megvannak a legyűjtött adatok.
Ami kellett:

Szoftver:
  • egy linux alapú masina, ha szeretnél elég adatot gyűjteni, nem árt ha fut is 0-24.
  • Legyen rajta hardveres soros port. Nem feltétlen kell, jó az USB-s is, de én nem szeretek szenvedni ezekkel
  • DIGITEMP című szoftver (ez letölthető is) de a disztribúciók többsége tartalmazza.
  • RRDTool
  • RRDTool-hoz minden amit az megkövetel. A mai disztribúciókon a csomagkezelővel telepíthető, installáláskor magával húzza a többi csomagot amire szüksége van.
  • perl környezet
Hardver:
  • Valamilyen http server, nekem lighthttpd volt eddig, ma épp kicsináltam a szervert, mostantól apache lesz, mert más dolgokra is akarom használni, amiket a lighthttpd nem kezel.
  • DS 18S20 típusú érzékelő. No ez a lelke mindennek, digitális, egyvonalas érzékelő. Annyi amennyit használni kívánsz
  • D1 1N5228 3.9v zener
  • D2 1N5234 6.2v zener
  • D3,D4 1N5818 Schottky
  • R1 1.5kohm
  • mama DB9 soros csatlakozó

Itt volna egy  egy kapcsolás a dologhoz, én is ezt használtam:


Ami fontos, a felsorolást másoltam, van hogy ilyen diódákat nem kapni, de közelit igen. A schottky az stimmeljen a zennereknek nem kell, elég ha a feszültség szintek megfelelnek, és a minimum áramerősség legyen azért 0,25A.

Az összeszerelés lehet lyukpaneles is, és egyedi is, sőt össze is hegesztheted a lábakat, de ezt nem ajánlom, mert nem bírja sokáig. Én egy kis lyukpanelre raktam fel, igaz ez nem az én képem de így néz ki:

Egy a lényeg, két vezeték jöjjön ki. A poén az egészben, hogy 64 ilyen egyvonalas hőmérő akasztható egymással párhozamosan, amelyeket a Digitemp az egyedi ID-jük alapján meg tud különböztetni a címzésnek köszönhetően.

Igaziból ez volt az egyszerű része a dolognak, ezekről ezen az oldalon olvashatsz bővebben, sőt fog is kelleni, igaziból én a grafikus ábrázolást szeretném megosztani, mert azt eddig senki nem tette meg alaposan, amit sajnálok.

No, tehát, lényegében Perl és RRDTool amivel dolgozunk, a végeredmény egy vagyis esetemben több png állomány, amelyet a crontab segítségével folyamatosan frissítünk.
A script első körben lefuttatja a digitemp-et, kiolvassa az értékeket, majd azokat lementi külön .rrd fileokba. Ezek után grafikusan ábrázoljuk, jelenleg ugyanebben a script állományban, ezt később módosítani szeretném, hogy a feldolgozás gyorsabb legyen.

Így kell elképzelni a végeredményt:

 
6 órás felvázolás, 12 órás felvázolás csak a két akváriumra vonatkozóan kritikus jelölőkkel együtt.


 
Egy hetes, és havi mérések hőmérsékletet

Éves mérés, ez már csak akkor tud érdekes lenni amikor több hónap megvan, mert gyakorlatban csak azt látjuk hogy nyár van vagy tél.

Én mint fentebb látható egy benti érzékelőt használok, két külsőt, és az akváriumokba külön külön egyet. Ezutóbbinak köszönhető volt pár brutálisabb vízfelmelegedés megelőzése. Ez persze fog bővülni, ahogy egyre több akvárium kerül üzembe, stb.. :)

A digitemp konfigurációt nem akarom taglalni, az a fentebbi linkben benne van, amit én hiányoltam az az rrdtool kezelése, perl-el. A perl-t nem én választottam, igaziból az eredeti oldal adta az alapot, sosem voltam programozó, nem is akarok az lenni, használom ami van.

A script részei:

  • perl vagyok!
#!/usr/bin/perl
  • rrd és http&image helyek meghatározása
use RRDs;
# define location of rrdtool databases
my $rrd = '/var/lib/temp';
# define location of images
my $img = '/srv/http/temp';
  • annyi hőmérőt adunk a levesbe, amennyi van.
# process data for each interface (add/delete as required)
&ProcessSensor(0, "1");
&ProcessSensor(1, "2");
&ProcessSensor(2, "ext1");
&ProcessSensor(3, "3");
&ProcessSensor(4, "int1");
&ProcessSensor(5, "5");
&ProcessSensor(6, "6");
&ProcessSensor(7, "7");
&ProcessSensor(8, "zero line");
  • kiolvasunk, és letárolunk mindent szépen külön. Ehhez több beállítási lehetőség is van, főleg fontos az adatgyűjtés felbontása.
sub ProcessSensor
{
# process sensor
# inputs: $_[0]: sensor number (ie, 0/1/2/etc)
# $_[1]: sensor description 
# get temperature from sensor

my $temp = `/usr/bin/digitemp_DS9097 -t $_[0] -q -c /etc/digitemp.conf -o%C`;

# remove eol chars
chomp($temp);

print "sensor $_[0]: $temp degrees C ($_[1])\n";

# if rrdtool database doesn't exist, create it
if (! -e "$rrd/temp$_[0].rrd")
{
print "creating rrd database for temp sensor $_[0]...\n";
RRDs::create "$rrd/temp$_[0].rrd",
"-s 180",
"DS:temp:GAUGE:240:U:U",
"RRA:AVERAGE:0.5:1:2016",
"RRA:AVERAGE:0.5:6:1344",
"RRA:AVERAGE:0.5:24:2190",
"RRA:AVERAGE:0.5:144:3650"
}
if ($ERROR = RRDs::error) { print "$0: failed to create $_[0] database file: $ERROR\n"; }

# check for error code from temp sensor
if (int $temp eq 85)
{
print "failed to read value from sensor $_[0]\n";
$temp = "U";
}
# insert values into rrd
RRDs::update "$rrd/temp$_[0].rrd",
"-t", "temp",
"N:$temp";
if ($ERROR = RRDs::error) { print "$0: failed to insert $_[0] data into rrd: $ERROR\n"; }
}
  • Grafikázzunk!
#########################################################################################################
##### graph all in
#########################################################################################################

&CreateGraph($_[0], "day", $_[1]);
&CreateGraph($_[0], "week", $_[1]);
&CreateGraph($_[0], "month", $_[1]);
&CreateGraph($_[0], "year", $_[1]);

sub CreateGraph
{
# creates graph
# inputs: $_[0]: n/a
#         $_[1]: interval (ie, day, week, month, year)
#         $_[2]: graph description

        RRDs::graph "$img/temps_all-$_[1].png",
                "-s -1$_[1]",
                "-u", "30",
                "-l", "-15",
#               "-t :: last $_[1] - HU-BP-XIV ::",
# "--lazy",
                "-h", "300", "-w", "900",
"-l", "0",
                "-a", "PNG",
                "-v Celsius fok",
                "-i",
#               "--slope-mode",

######## ki kicsoda, EZT KELL VÁLTOZTATNI!:
"DEF:ext1=$rrd/temp2.rrd:temp:AVERAGE",
"DEF:ext2=$rrd/temp5.rrd:temp:AVERAGE",
"DEF:int1=$rrd/temp3.rrd:temp:AVERAGE",
# "DEF:int2=$rrd/temp8.rrd:temp:AVERAGE",
"DEF:aqua72-1=$rrd/temp0.rrd:temp:AVERAGE",
"DEF:aqua72-2=$rrd/temp1.rrd:temp:AVERAGE",
"DEF:aqua40=$rrd/temp4.rrd:temp:AVERAGE",
"DEF:zero=$rrd/temp8.rrd:temp:AVERAGE",

## external - 1
"LINE2:ext1#fff:External - 1\\:",
"GPRINT:ext1:MIN: Min\\: %2.2lf C",
"GPRINT:ext1:MAX:   Max\\: %2.2lf C",
"GPRINT:ext1:AVERAGE:   Average/Atlag\\: %2.2lf C",
"GPRINT:ext1:LAST:   Current/Jelenlegi\\: %2.2lf C\\n",

## external - 2
"LINE2:ext2#b6b6b6:External - 2\\:",
"GPRINT:ext2:MIN: Min\\: %2.2lf C",
"GPRINT:ext2:MAX:   Max\\: %2.2lf C",
"GPRINT:ext2:AVERAGE:   Average/Atlag\\: %2.2lf C",
"GPRINT:ext2:LAST:   Current/Jelenlegi\\: %2.2lf C\\n",

## internal - 1
"LINE1:int1#ffe400:Internal - 1\\:",
"GPRINT:int1:MIN: Min\\: %2.2lf C",
"GPRINT:int1:MAX:   Max\\: %2.2lf C",
"GPRINT:int1:AVERAGE:   Average/Atlag\\: %2.2lf C",
"GPRINT:int1:LAST:   Current/Jelenlegi\\: %2.2lf C\\n",

## internal - 2
#"LINE1:int2#ffc600:Internal - 2\\:",
#"GPRINT:int2:MIN: Min\\: %2.2lf C",
#"GPRINT:int2:MAX:   Max\\: %2.2lf C",
#"GPRINT:int2:AVERAGE:   Average/Atlag\\: %2.2lf C",
#"GPRINT:int2:LAST:   Current/Jelenlegi\\: %2.2lf C\\n \\n",
## aqua - 72 - 1
"LINE1:aqua72-1#0037ff:Aqua 72 - 1\\: ",
"GPRINT:aqua72-1:MIN: Min\\: %2.2lf C",
"GPRINT:aqua72-1:MAX:   Max\\: %2.2lf C",
"GPRINT:aqua72-1:AVERAGE:   Average/Atlag\\: %2.2lf C",
"GPRINT:aqua72-1:LAST:   Current/Jelenlegi\\: %2.2lf C\\n",
## aqua - 72 - 2
"LINE1:aqua72-2#008fff:Aqua 72 - 2\\: ",
"GPRINT:aqua72-2:MIN: Min\\: %2.2lf C",
"GPRINT:aqua72-2:MAX:   Max\\: %2.2lf C",
"GPRINT:aqua72-2:AVERAGE:   Average/Atlag\\: %2.2lf C",
"GPRINT:aqua72-2:LAST:   Current/Jelenlegi\\: %2.2lf C\\n",
## zero line
"LINE1:zero#24b285",
# graphical addons
"--color", "SHADEA#000",
"--color", "SHADEB#000",
"--color", "BACK#000000",
"--color", "CANVAS#1d1d1d",
"--color", "MGRID#ffffff",
"--color", "GRID#6d6d6d",
"--color", "AXIS#ffffff",
"--color", "FONT#cbcbcb",
"--color", "ARROW#ffffff",
"--grid-dash", "1:0";

        if ($ERROR = RRDs::error) { print "$0: unable to generate sensor $_[0] $_[1] graph: $ERROR\n"; }
}

Nem feltétlen egyértelmű a fenti ábrázolás, mivel nem sorrendbe raktam a hőmérőket, mivel más-más kell nekem.

Én külön bontottam a standard formákat, nap, hét, hónap, év, 3óra, 6 óra, stb Ebből lehet külön generálni a más-más szenzorokra egy-egy grafikont, így amikor ránéz az ember nem tűnik az egész egy katyvasznak. 



TODO Folytatni.