Category Archives: Other

other stuff

Monitor Temperature and Humidity using Raspberry Pi

Today I would like to share you the way to monitor via web page temperature and humidity using raspberry Pi and Sensor for humidity and temperature. You could find a lot of similar articles in the Internet. Mine solution is just combination of several and small adjustment.

Level of moisture in our house is too high and this requires intervention from our side to reduce it. Recommended value of moisture should be between 35% and 55%. High level of moisture increase probability to get ingress on our things, clothes, walls..

Moisture level depends on air flows and temperature in your house. If in the winter temperature is below 20 Celsius degree the level of humidity will be higher than recommended value. Minimum recommended level of temperature should be 21 Celsius degrees.  In other words you need to have smart heating system and good ventilation to reduce fast humidity when you are cooking or after you used bathroom. So let’s see how to get data which we need for decisions.

We need equipment:

  • DHT22 AM2302 Digital Temperature and Humidity Sensor module Replace SHT11 SHT15, Price – 5.97 USD
  • Raspberry Pi-2 + Acrylic Enclosure Case + Heat sink, Price – 42.27 USD 

Let’s connect sensor to the raspberry. I did everything like in this link http://www.home-automation-community.com/temperature-and-humidity-from-am2302-dht22-sensor-displayed-as-chart/ except the thing that I didn’t used resistor and development plate. My sensor has resistor built in. Here is my equipment and connections:

Before to collect data let’s update our raspberry:

sudo aptget update

Note: Many steps in this tutorial require internet acces on raspberry PI (If you don't know how to do it see my previous article how to connect raspbery to the internet via Wi-Fi)

Raspberry collect data from sensor on the GPIO pin 4. To read data we will use python library called Adafruit_Python_DHT.

To install library, get the dependencies with:

sudo aptget install y buildessential pythondev git

and then download and install the library with:

mkdir -p /home/pi/sources 
cd /home/pi/sources 

git clone https://github.com/adafruit/Adafruit_Python_DHT.git 

cd Adafruit_Python_DHT 

sudo python setup.py install 

 Here we can do fast check to see that Pi is ready to collect data from sensor:

sudo /home/pi/sources/Adafruit_Python_DHT/examples/AdafruitDHT.py 2302 4

The first argument is the sensor type, it can be 11 or 22 or 2302. The second argument is the RPi GPIO pin which is connected to the sensor data pin.

Output will be like:

sudo /home/pi/sources/Adafruit_Python_DHT/examples/AdafruitDHT.py 2302 4

Temp = 24.2*C Humidity=26.3%

Let’s create sql database to collect our data

Install sqlite

sudo apt-get install sqlite3

  Create DB schema

BEGIN;

CREATE TABLE temp_hum (timestamp DATETIME, temp NUMERIC, hum NUMERIC);

COMMIT;

Great, now we have DB where we could write data. We created table called temp_hum. This table has 3 columns: timestamp, temp and hum. First column will contain actual date and time. So let’s assure that time and data is right. Use date command to check it.

If something is wrong let’s install ntpdate packet and adjust time:

sudo apt-get install ntpdate

sudo service ntpd stop

sudo ntpdate time.nist.gov

sudo service ntpd start

Last step is to adjust timezone. For me timezone is GMT+3. Find your on the internet if you don’t know which one is yours.

sudo cp /usr/share/zoneinfo/Etc/GMT+3 /etc/timezone

Let’s install and configure web server

sudo apt-get install apache2

We will use cgi-bin scripts to display collected data on web page. So let's say apache web server to use them. This step could be optional for you:

a2enmod cgi

a2enmod cgid

service apache2 restart

Move created DB to the /var/www/ folder and change owner of DB:

sudo cp templog.db /var/www/
sudo chown www-data:www-data /var/www/templog.db 

Now we have to do little bit programming or just copy ready solution from the internet (as I did, thanks this author for help). I adjusted liitle bit found code in the internet in order to get expected results.

nano /root/monitor.py

and insert following text inside:

#!/usr/bin/env python

import sqlite3
import Adafruit_DHT
import os
import time
import glob

# global variables
dbname='/var/www/templog.db'

# store the temperature and hummidity in the database
def log_temperature(temp,humm):

    conn=sqlite3.connect(dbname)
    curs=conn.cursor()

    curs.execute("INSERT INTO temp_hum values(datetime('now'), (?), (?))", (temp,humm))
    #curs.execute("INSERT INTO temp_hum values(datetime('now'), (?))", (humm,))
    # commit the changes
    conn.commit()

    conn.close()


# display the contents of the database
def display_data():

    conn=sqlite3.connect(dbname)
    curs=conn.cursor()

    for row in curs.execute("SELECT * FROM temp_hum"):
        print str(row[0])+”     “+str(row[1])+”        “+str(row[2])

    conn.close()

# get temerature and humidity
# returns None on error, or the temperature,humidity as a float
def get_temp_hum():
        try:
                humm, temp = Adafruit_DHT.read_retry(Adafruit_DHT.DHT22, 4)
                humm = round (humm, 2)
                temp = round (temp, 2)
                return humm, temp
        except:
                return None, None

# main function
# This is where the program starts
def main():
        # get the temperature from the device file
        humm, temp = get_temp_hum()
        # Store data to DB in case we have values
        if (humm != None and temp !='None'):
                log_temperature(humm, temp)
#       display_data()

if __name__=="__main__":
    main()

Let me add several comments to the code. First of all we defined database location dbname='/var/www/templog.db'. After that we defined function log_temperature to insert data in our DB. We have function called get_temp_hum to read data from the sensor using python library installed at the beginning of this article.The most important part is main function where we are calling get_temp_hum, check that we have valid data and insert them to DB using log_temperature. One more function display_data() is commented and we need it only for debug purposes. Uncomment this line and execute the script to assure that you have inserted data in the DB.

Don't forget to make file monitor.py executable:

chmod 755 /root/monitor.py

Let's collect data every 15 minutes. For this print:

crontab -e

Insert this line:

*/15 * * * * /root/monitor.py

Note: If execution of script fail for some reason try to excute script manually with sudo /root/monitor.py. Also you could monitor script execution in crontab using postfix mail service. To install it use sudo apt-get install postfix.

Last step is create script which will display data on the web page.

nano /usr/lib/cgi-bin/webgui.py

Insert there following text:

#!/usr/bin/env python
import sqlite3
import os
import sys
import cgi
import cgitb

# global variables
dbname='/var/www/templog.db'

# print the HTTP header
def printHTTPheader():
    print "Content-type: text/html\n\n"

# print the HTML head section
# arguments are the page title and the table for the chart
def printHTMLHead(title, table):
    print "<head>"
    print "    <title>"
    print title
    print "    </title>"

    print_graph_script(table)

    print "</head>"


# get data from the database
# if an interval is passed,
# return a list of records from the database
def get_data(interval):

    conn=sqlite3.connect(dbname)
    curs=conn.cursor()

    if interval == None:
        curs.execute("SELECT * FROM temp_hum")
    else:
        curs.execute("SELECT * FROM temp_hum WHERE timestamp>datetime('now','-%s hours') AND timestamp<=datetime('now')" % interval)

    rows=curs.fetchall()

    conn.close()
    return rows


# convert rows from database into a javascript table
def create_table(rows):
    chart_table=""

    for row in rows[:-1]:
        rowstr="[‘{0}’, {1}, {2}],\n”.format(str(row[0]),str(row[1]),str(row[2]))
        chart_table+=rowstr

    row=rows[-1]
    rowstr="[‘{0}’, {1}, {2}]\n”.format(str(row[0]),str(row[1]),str(row[2]))
    chart_table+=rowstr

    return chart_table


# print the javascript to generate the chart
# pass the table generated from the database info
def print_graph_script(table):

    # google chart snippet
    chart_code="""
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load("visualization", "1", {packages:[“corechart”]});
      google.setOnLoadCallback(drawChart);
      function drawChart() {
        var data = google.visualization.arrayToDataTable([
          [‘Time’, ‘Humidity’, ‘Temperature’],
%s
        ]);
        var options = {
          title: 'Temperature and Humidity'
        };
        var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
        chart.draw(data, options);
      }
    </script>"""

    print chart_code % (table)

 


# print the div that contains the graph
def show_graph():
    print "<h2>Temperature Chart</h2>"
    print '<div id="chart_div" style="width: 900px; height: 500px;"></div>'

# connect to the db and show some stats
# argument option is the number of hours
def show_stats(option):

    conn=sqlite3.connect(dbname)
    curs=conn.cursor()

    if option is None:
        option = str(24)

    curs.execute("SELECT timestamp,max(temp) FROM temp_hum WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now')" % option)
    tempmax=curs.fetchone()
    tempmax="{0}&nbsp&nbsp&nbsp{1} %".format(str(tempmax[0]),str(tempmax[1]))

    curs.execute("SELECT timestamp,min(temp) FROM temp_hum WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now')" % option)
    tempmin=curs.fetchone()
    tempmin="{0}&nbsp&nbsp&nbsp{1} %".format(str(tempmin[0]),str(tempmin[1]))

    curs.execute("SELECT avg(temp) FROM temp_hum WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now')" % option)
    tempavg=curs.fetchone()

    curs.execute("SELECT timestamp,max(humm) FROM temp_hum WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now')" % option)
    hummmax=curs.fetchone()
    hummmax="{0}&nbsp&nbsp&nbsp{1} C".format(str(hummmax[0]),str(hummmax[1]))

    curs.execute("SELECT timestamp,min(humm) FROM temp_hum WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now')" % option)
    hummmin=curs.fetchone()
    hummmin="{0}&nbsp&nbsp&nbsp{1} C".format(str(hummmin[0]),str(hummmin[1]))

    curs.execute("SELECT avg(humm) FROM temp_hum WHERE timestamp>datetime('now','-%s hour') AND timestamp<=datetime('now')" % option)
    hummavg=curs.fetchone()


    print "<hr>"


    print "<h2>Minumum Value&nbsp</h2>"
    print tempmin
    print hummmin
    print "<h2>Maximum Value</h2>"
    print tempmax
    print hummmax
    print "<h2>Average Value</h2>"
    print "%.1f" % tempavg+"%"
    print "%.1f" % hummavg+"C"
    print "<hr>"

    print "<h2>In the last hour:</h2>"
    print "<table>"
    print "<tr><td><strong>Date/Time</strong></td><td><strong>Temperature</strong><td><strong>Humidity</strong></td></td></tr>"

    rows=curs.execute("SELECT * FROM temp_hum WHERE timestamp>datetime('now','-1 hour') AND timestamp<=datetime('now')")
    for row in rows:
        tempstr="<tr><td>{0}&emsp;&emsp;</td><td>{1} %</td><td>{2} C</td></tr>".format(str(row[0]),str(row[1]),str(row[2]))
        print tempstr
    print "</table>"

    print "<hr>"

    conn.close()

def print_time_selector(option):

    print """<form action="/cgi-bin/webgui.py" method="POST">
        Show the temperature logs for
        <select name="timeinterval">"""


    if option is not None:

        if option == "6":
            print "<option value=\"6\" selected=\"selected\">the last 6 hours</option>"
        else:
            print "<option value=\"6\">the last 6 hours</option>"

        if option == "12":
            print "<option value=\"12\" selected=\"selected\">the last 12 hours</option>"
        else:
            print "<option value=\"12\">the last 12 hours</option>"

        if option == "24":
            print "<option value=\"24\" selected=\"selected\">the last 24 hours</option>"
        else:
            print "<option value=\"24\">the last 24 hours</option>"

        if option == "168":
            print "<option value=\"168\" selected=\"selected\">the last 168 hours</option>"
        else:
            print "<option value=\"168\">the last 168 hours</option>"

    else:
        print """<option value="6">the last 6 hours</option>
            <option value="12">the last 12 hours</option>
            <option value="24">the last 24 hours</option>
            <option value="168">the last 168 hours</option>"""

    print """        </select>
        <input type="submit" value="Display">
    </form>"""


# check that the option is valid
# and not an SQL injection
def validate_input(option_str):
    # check that the option string represents a number
    if option_str.isalnum():
        # check that the option is within a specific range
        if int(option_str) > 0 and int(option_str) <= 24:
            return option_str
        else:
            return None
    else:
        return None


#return the option passed to the script
def get_option():
    form=cgi.FieldStorage()
    if "timeinterval" in form:
        option = form[“timeinterval”].value
        return validate_input (option)
    else:
        return None

# main function
# This is where the program starts
def main():

    cgitb.enable()

    # get options that may have been passed to this script
    option=get_option()

    if option is None:
        option = str(24)

    # get data from the database
    records=get_data(option)

    # print the HTTP header
    printHTTPheader()

    if len(records) != 0:
        # convert the data into a table
        table=create_table(records)
    else:
        print "No data found"
        return

    # start printing the page
    print "<html>"
    # print the head section including the table
    # used by the javascript for the chart
    printHTMLHead("Raspberry Pi Temperature and Humidity Logger", table)

    # print the page body
    print "<body>"
    print "<h1>Raspberry Pi Temperature and Humidity Logger</h1>"
    print "<hr>"
    print_time_selector(option)
    show_graph()
    show_stats(option)
    print "</body>"
    print "</html>"

    sys.stdout.flush()

if __name__=="__main__":
    main()

Let me again comment the script. First of all we need to know how much hours to display in graph using get_option function. By default 24 hours will be displayed. Function get_data will collect dates from DB. If you have at least one string in the DB data will be converted create_table function to display everything on the web. Actual string is not converting data for 1 week. This is what I have to fix after.

Don't forget to make file webgui.py executable:

chmod 755 /usr/lib/cgi-bin/webgui.py

To see data in the web page open web browser on the PC in the same network with raspberry and insert in the address field:

192.168.1.10/cgi-bin/webgui.py

192.168.1.10 is the ip address of Pi

Final result look like this:

After 10 AM I moved sensor closer to the heating system. This is why we have so big changes in the graph.

Links

  1. http://www.home-automation-community.com/temperature-and-humidity-from-am2302-dht22-sensor-displayed-as-chart/
  2. https://github.com/adafruit/Adafruit_Python_DHT
  3. http://raspberrywebserver.com/cgiscripting/rpi-temperature-logger/building-a-web-user-interface-for-the-temperature-monitor.html

Set nou de carte pentru studenții orelor de VMware

Devine deja o tradiție ca studenții claselor de VMware să primească cadouri sub formă de carte.  La fel s-a întâmplat și cu grupa curentă pentru care tocmai am primit noul set de cărți. De remarcat silința administratorilor academiei care au depus tot efortul ca cadourile să existe și ca acestea să ajungă la studenții noștri într-un timp cât mai scurt de la lansarea grupei. 

WP_20150303_012

Sper să le fie de mare ajutor în inițiativa lor de studiu și le urez într-un ceas bun.

 

[How To] Monitorizare statut conexiune TCP

In caz că vă doriți să monitorizați statutul unei conexiuni TCP dintre o aplicație pe client și un server de aplicații puteți considera mecanismul pe care îl voi descrie mai jos.  Imaginativă o aplicație (critică) instalată pe o stație la un utilizator. Aplicația trebuie monitorizată ca tot timpul să țină stabilită o conexiune TCP intre ea și serverul de aplicație. În caz că conexiunea cade, utilizatorul pe stație trebuie înștiințat astfel încât acesta să reacționeze cât mai repede și eventual să intervină manual pentru remediere. Motive pentru care conexiunea se poate pierde pot fi multiple de la crash de aplicație până la probleme de network.

Înțeleg că soluții pot fi multe, dar una simplă și fără investii (timp/bani) este cea pe care v-o propun mai jos. Ideea se bazează pe verificare periodică a conexiunii TCP analizând output-ul rulării utilitarului netstat intr-un fișier de comenzi BAT. Rezultatul netstat se filtrează în așa fel încât să afișeze doar conexiunea dintre client și respectivul server de aplicație după care se caută secvența cu string-ul ESTABLISHED.  Dacă secvența ESTABLISHED lipsește utilizatorul primește un message-box cu atenționare. Periodicitatea se asigură cu un task programat in Windows Task Scheduler să zicem odată la 5 minute.

Așadar codul fișierului de comenzi BAT este: 

tcp_monitoring_source_code

Las fără explicații 1,2,8, pentru restul:

  • In linia 3 se execută netstat-ul cu parametri –an (all connections, host-uri ca adrese IP) peste care se aplică un filtru pentru conexiunile spre IP-ul serverului de aplicație. În continuare din rezultat se numără de câte ori se întâlnește secvența ESTABLISHED. Suma este aruncată în fișierul tmpfile care poate fi zero dacă nu sunt conexiuni și diferit de zero dacă sunt. TmpFile e creat și distrus pe durata executării scriptului.
  • În următoarele două linii (4,5) numărul conexiunilor se scoate din tmpfile după care se încarcă în variabila number_of_connections. De ce nu sa făcut direct din output-ul netstat in variabilă și sa mers prin fișierul tmpFile ? Răspunsul e pentru că nu am știut cum se face J, intuiesc totuși că se face cumva.
  • In linia 6 se verifică dacă numărul de conexiuni este zero fapt ce ar înseamna conexiune ruptă intre aplicația client și server. Dacă e zero utilizatorul primește o atenționare printr-un message-box. Message-box-ul trebuie confirmat pentru a fi închis.

tcp_monitoring_warrning_message

  • In linia 7 se face același lucru doar că evenimentul este înregistrat într-un fișier log: connection-report.txt. Locația se decide aici, oricare, important ca user-ul se aibă acces R/W.

tcp_monitoring_loged_messages

Dacă conexiune există scriptul pornește și închide imediat fără a deranja utilizatorul.  Nu se afișează nici un mesaj și nici nu se lasă mesaje in fișierul log.

Acum, pentru a asigura verificarea periodică a conexiunii fișierul de comenzi se programează ca task in Windows Task Scheduler pentru care se fixează periodicitate corespunzătoare (minim de 5 min).

Până la a include un task in Task Scheduler va trebui de asigurat în prealabil dreptul de  Logon as a batch job pentru utilizatorul pe stație. Fără acest settings job-ul din Task Scheduler intră in hung cu statut running și nu mai finalizează.  Pentru asta, din Control Pannel – Administrative Tools se deschide snap-in-ul pentru Local Security Policy după care se accesează politica Logon as a batch job în Local Policies – User Right Assignment la care se adaugă în permissions utilizatorul pe stație.

tcp_monitoring_logonasabatchjob

De remarcat că configurația de mai sus se execută cu privilegii de administrator. Task-ul în Windows Task Scheduler (mai jos) se configurează din numele utilizatorului pe stație. Utilizatorul pe stație simplu user, fără privilegii în plus față de grupul users.

Pe final configurăm task-ul in Windows Task Scheduler:

  • Inițiem Wizardul pentru un task nou. Din Task Scheduler click pe Create Basic Task ..
  • In Create a Basic Task specificăm un nume relevant pentru task.
  • În Trigger lăsăm implicit pe Daily
  • În Action – Start a Program specificăm fișierul de comenzi bat creat anterior. Atenție, e important in Start in să specificăm folderul in care rulează BAT-ul.
  • Click Finish, după care se deschide fereastra Properties la task.
  • Pe pagina Triggers click pe Edit, iar în fereastra deschisă se bifează Repeat task every .. iar in drop down se alege intervalul corespunzător, exemplu de 5 minute. După, în for a duration of se selectează indefinitely.

Cu asta tot, task-ul va porni periodic fișierul de comenzi care dacă va detecta că nu există conexiune cu serverul de aplicație va înștiința printr-un mesaj utilizatorul pe stație și va lăsa o înregistrare în fișierul log.

DYI – nixie clock, design printed circuit board (PCB)

De parca nu eram suficient de încărcat mi-am mai gasit o treaba in plus J – acum mai sunt ocupat si cu un mic proiect DYI pe electronică. E ceea de ce eram cândva (tot timpul) pasionat și cam din ce am studiat la facultate așa că până la urmă nu mi-a fost greu sa revin. M-am pornit cu un proiect mai vechi de-al meu (ce trebuia sa fie controller cu microprocesor) care abia de așteapta atenție ca pe parcurs să înțeleg că-mi va lua mai mult timp decât aveam așa că am sărit pe un proiect mai mic, unul asa just for fun. În plus, cu ăsta învăț și cum de făcut comandă pentru un cablaj imprimat PCB (printed circuit board) – util pe viitor in caz ca ma introc la proiectul inițial.

Așadar, proiectul DYI de azi e un NIXIE CLOCK construit pe elemente de logică discretă TTL: elementare, bistabile și contoare, pe scurt ține de old school. Fără prea mari dificultăți am găsit o schemă potrivită, pe unde am mai ajustat-o – potrivit alte tuburi NIXIE, pe unde am mai combinat – introdus invertor DC/DC pentru tensiune de 180V alimentare anod-uri tuburi, așa că pe final sper să fi ieșit ceva funcțional.

Ca orice proiect, l-am împărțit pe ăsta pe etape (aici si statutul):

  1. Done – Identificare schemă electronică potrivită pentru scopul proiectului.
  2. Done – Identificare/studiere tool CAD pentru proiectarea placajelor imprimate PCB.
  3. Done – Proiectare si verificare placaj imprimat PCB pentru schema din p.1.
  4. Done – Identificare furnizor și comandă placaj imprimat PCB
  5. Achiziție componente electronice (componente active/pasive, mci)
  6. Montare componente pe placaj, depanare și pornire schemă.

Pe moment 1-4 sunt gata, 5-6 când am timp liber continui.

Foarte pe scurt un rezumat pentru 1-4:

  • Schemă pe circuite TTL (SU: serie 155, 18 buc), convertor DC/DC(180V) MCU34063, tuburi NIXIE (SU: IH-14, 6 buc), clock quartz 100KHz
  • PCB CAD: NOVARM DIP Trace – din toate analizate, mi sa părut cel mai simplu de studiat și de comod de lucrat. Studiu: o săptămână e suficient. Are: editor scheme, PCB manual/auto, editor componente, bibliotecă bogată de componente, export Gerber/Excellon, 3D preview. PE deasupra, DIP TRACE e bine documentat.
  • PCB proiectat pe 15x20cm, double side cu cea mai mare parte a parametrilor pe default: pad size, track size s.a.m.d. Pentru componentele ce lipseau in bibliotecile standard – cea mai mare parte din circuitele integrate, tuburi nixe s.a.m.d (10 buc) am format o biblioteca cu componente de la zero. Pentru toate microcircuitele am folosit carcasa DIP cu lățimea intre piciorușe după standardul sovietic de 2.5mm in loc de 2.54 (toate MCI sunt încă de atunci) – acum pentru DIP-15/16 mai puțin contează, pentru celălalt proiect cu carcase DIP-40 va fi esențial.
  • pe forumuri am înțeles că pentru un cost acceptabil și de calitate PCB-ul poate fi comandat mai ales in China. Ales seeedstudio (fusion PCB) ca cel mai popular cu o ofertă ce include: (a) pină la 4x straturi (b) cantitate de min. 5 buc  (c) e-test s.a.m.d  more info aici : http://www.seeedstudio.com/service/index.php?r=pcb. Design-ul meu de 15x20cm / double layer / 5 buc  cu tot cu livrare (singaporepost) m-a costat 53 USD. Ca durată, până a fost dat in producție a durat 1 săptămână după care livrarea aproape o lună.

Așadar, proiectat mi-a ieșit ca in imaginea de mai jos:

DYI_nixie_clock_PCB_topdown

sau cu preview în 3D:

DYI_nixie_clock_PCB_3D_preview

iar după o lună am obținut placajul PCB gata realizat:

DYI_nixie_clock_PCB_production

Până aici nice experience, mai am de achiziționat componentele (mouser/jameco) după care montare și pornire, dar asta-i alată istorie.