«

»

Zář 05 2014

1. Zapojení DS2482S-100 (I2C-1wire) a teplotních čidel (DS18B20) – součástky, zapojení, instalace, nastavení, MySQL, PHP, WEB, Android aplikace

 

tak jsem se konečně dostal k odzkoušení a vytvoření jednoduchého návodu na kontrolu teploty v domácnosti za použití 1wire sběrnice DS2482S-100 a teplotních čidel DS18B20. Pro ukládání dat jsem použil DB MySQL a ukládání je řešeno za pomocí PHP skriptu. Pro zobrazení na webu jsem použil doménu raspberry-pi.eu, kterou hostuji doma na svém Pi (mám veřejnou IP), php a java script. Další možností kontroly teplot doma, je přes chytré zařízení běžící pod systémem android (bez veřejné IP to bude fungovat pouze doma na stejné síti)

zapojení do nepájivého p

zapojení do nepájivého p

 

1. Součástky a zapojení

  • DS18B20 teplotní čidlo
  • DS2482S-100 I2C na 1wire
  • MS-DIP na SO-8 redukce
  • 4.7 kOhm odpor
  • kabely a nepájivé pole
  • telefonní kabel na zavedení čidel do místností

 

velikost DS2482S-100

velikost DS2482S-100

 

základním a nejtěžším úkolem je napájení sběrnice  DS2482S-100 na plošňák SO-8, kterým pak můžeme vkládat do nepájivého pole

 

DS2482S-100 na SO-8

DS2482S-100 na SO-8

 

dále je potřeba vše dát dohromady. V mém případě mám jedno teplotní čidlo zapojeno přímo v nepájivém poli a připraven pro napojení dalších teplotních čidel (viz. druhý obrázek níže v pravém rohu)

zapojení do nepájivého pole

zapojení do nepájivého pole

schéma zapojení

schéma zapojení

zapojení do nepájivého p

zapojení do nepájivého p

zde je vidět, jak tam jsou navíc zapojené kabely (pro přenos dat jsem použil telefonní kabel – 4linku a jeden není použit) od ostatních čidel rozmístěných po bytě. Výhodou tohoto zapojení je, že se na jeden kabel dají napojit veškerá čidla (pokud to je jedním směrem), protože jsou zapojené do sběrnice

zapojení

zapojení

 

 2. Nainstalování a nastavení Pi

  • nejprve odstraňte I2C moduly z jádra černé listiny.

Notice

sudo nano /etc/modprobe.d/raspi-blacklist.conf

zakomentujte řádek přidání „#“ na začátku řádku

Notice

#blacklist i2c-bcm2708

uzavřít a uložit soubor (CTRL+x a pak „y“ a enter)

  • pro nastavení nahrání modulů při startu je potřeba upravit tento soubor

Notice

sudo nano /etc/modules

přidat tyto dva řádky

Notice

i2c-bcm2708
i2c-dev

uzavřít a uložit soubor (CTRL+x a pak „y“ a enter)

  •  instalace některých nástrojů pro práci s I2C

Notice

sudo apt-get install i2c-tools python-smbus
  •  teplotní čidlo již komunikuje a lze udělat rychlou kontrolu funkčnosit

Notice

sudo i2cdetect -y 1

i2cdetect

  • pokuď nastane nějaký problém, je možné ještě zkusit přidat text do souboru /boot/config.txt

Notice

sudo nano /etc/owfs.conf

přidat

device_tree=bcm2708-rpi-b-plus.dtb
device_tree_param=i2c1=on
device_tree_param=spi=on

(změna se projeví po restartu)

  • pro získání teploty je potřeba nainstalovat owfs a ow-shell balíček.

Notice

sudo apt-get install owfs ow-shell
  • vytvoření adresáře, kde se potom budou ve složkách objevovat připojené teplotní čidla

Notice

sudo mkdir /mnt/1wire
    •  nyní je potřeba upravit soubor ows.conf

Notice

sudo nano /etc/owfs.conf

přidání „#“ na začátek řádku

Notice

#server: FAKE = DS18S20,DS2405

přidání těchto řádků na konec souboru

Notice

device = /dev/i2c-1
mountpoint = /mnt/1wire
Celsius
allow_other
error_print = 0
error_level = 0

 uzavřít a uložit soubor (CTRL+x a pak „y“ a enter)

    • poslední soubor, který je třeba také upravit je fuse.conf

Notice

sudo nano /etc/fuse.conf

odstranění „#“ na začátku řádku u

Notice

user_allow_other

uzavřít a uložit soubor (CTRL+x a pak „y“ a enter)

  • po těchto změnách je dobré pro jistotu udělat restart Pi

Notice

sudo reboot
  • po naběhnutí Pi se musí spustit „owfs“

Notice

owfs
  •  pak už stačí spustit midnight commander a doklikat se do složky /mnt/1wire/

Notice

sudo mc

zde jsou vidět tři adresy teplotních senzorů

MC_mmt_1wire

MC_mmt_1wire

jeden si vybereme a najedeme na soubor temperature a dáme F3

MC_mmn_1wire

MC_mmn_1wire

a zde je aktuální teplota na daném senzoru

MC_mmn_1wire

MC_mmn_1wire

ještě je potřeba udělat jednu důležitou věc a to nastavení automatického spuštění „owfs“ po startu

Notice

sudo crontab -e

na konec přidáme tento řádek

Notice

@reboot sudo -u root owfs

uzavřít a uložit soubor (CTRL+x a pak „y“ a enter)

 

3. Nastavení automatického ukládání dat z jednotlivých čidel do databáze MySQL pomocí PHP skriptu

 

nejprve se musí nainstalovat appace, mysql a php do Raspberry Pi (pokud již není nainstalováno)

Notice

sudo apt-get install apache2 php5 php5-mysql mysql-server

po instalaci a zadání hesla pro uživatele „root“ pro MySQL vytvoříme databazi „develop“

Notice

mysql -p -u root

Notice

CREATE DATABASE develop;
USE develop;

pro ukládání dat do DB jsem zvolil dvě tabulky a jeden pohled

Notice

CREATE TABLE teplomery (
  rid INT(11) NOT NULL AUTO_INCREMENT,
  nazev VARCHAR(25) COLLATE utf8_czech_ci NOT NULL,
  adresa VARCHAR(255) COLLATE utf8_czech_ci NOT NULL,
  stav TINYINT(4) NOT NULL DEFAULT '1',
  PRIMARY KEY (rid)
);

do této tabulky je potřeba vložit názvy a adresy teploměru

Notice

INSERT INTO teplomery (nazev,adresa) VALUES ('obyvak_pc_stul', '28.869481050000');
INSERT INTO teplomery (nazev,adresa) VALUES ('chodba', '28.909581050000');
INSERT INTO teplomery (nazev,adresa) VALUES ('loznice', '28.93BB81050000');
INSERT INTO teplomery (nazev,adresa) VALUES ('venku', '28.F36782050000');

tabulka pro ukládání teplot

Notice

CREATE TABLE teploty (
  rid INT(11) NOT NULL AUTO_INCREMENT,
  teplomer INT(11) NOT NULL,
  teplota DECIMAL(10,4) NOT NULL,
  InsDT TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (rid),
  KEY FK_teplomer (teplomer),
  CONSTRAINT teploty_ibfk_1 FOREIGN KEY (teplomer) REFERENCES teplomery (rid) ON UPDATE CASCADE
);

a pohled pro výběr dat (stav je označení pro aktivní teploměr  -> 1 aktivní, 0 neaktivní)

Notice

CREATE VIEW teploCsv AS 
SELECT
    DATE_FORMAT(a.InsDT,'%Y-%m-%d %H:%i') AS dt,
    b.rid     AS ID,
    b.nazev   AS nazev,
    a.teplota AS teplota
FROM (teploty a
     JOIN teplomery b
       ON (((a.teplomer = b.rid)
            AND (b.stav > 0))));

defaultní umístění adresáře pro www je /var/www/ kde je potřeba vytvořit PHP skript, který se napojí do DB a pak nastavení v CRONu pro automatické volání tohoto PHP skriptu, který se bude pravidelně spouštět a ukládat teploty

Notice

sudo nano temperaturesensor.php

vložíme tento skript, kde je potřeba změnit heslo pro root uživatele

Notice

<?php    
/*
* Writes temperature Data to DB
* 
*/
$mysqlhost="localhost";
$mysqluser="jmeno";
$mysqlpwd="heslo";
$mysqldb="develop";

$temperatureSensorPath1 = "/mnt/1wire/28.869481050000/temperature";
$temperatureSensorPath2 = "/mnt/1wire/28.909581050000/temperature";
$temperatureSensorPath3 = "/mnt/1wire/28.93BB81050000/temperature";
$temperatureSensorPath4 = "/mnt/1wire/28.F36782050000/temperature";


// --- Read sensor data ---
$tempSensorRawData1 = implode('', file($temperatureSensorPath1));
$temperature1 = $tempSensorRawData1;

$tempSensorRawData2 = implode('', file($temperatureSensorPath2));
$temperature2 = $tempSensorRawData2;

$tempSensorRawData3 = implode('', file($temperatureSensorPath3));
$temperature3 = $tempSensorRawData3;

$tempSensorRawData4 = implode('', file($temperatureSensorPath4));
$temperature4 = $tempSensorRawData4;


// --- Read datetime ---
//$timestamp =date(YmdHis, strtotime($today . ' + 2 hours'));
$timestamp =date(YmdHis);

// --- Write Data to DB ---
$connection=mysql_connect($mysqlhost, $mysqluser, $mysqlpwd) or die ("Could not connect to DB!");
mysql_select_db($mysqldb, $connection) or die("Could not select DB!");
$sql_query_1 = "INSERT INTO teploty (teplomer,teplota) VALUES ('1', $temperature1);";
mysql_query($sql_query_1);
$sql_query_2 = "INSERT INTO teploty (teplomer,teplota) VALUES ('2', $temperature2);";
mysql_query($sql_query_2);
$sql_query_3 = "INSERT INTO teploty (teplomer,teplota) VALUES ('3', $temperature3);";
mysql_query($sql_query_3);
$sql_query_4 = "INSERT INTO teploty (teplomer,teplota) VALUES ('4', $temperature4);";
mysql_query($sql_query_4);


?>

nastavení CRONu

Notice

sudo crontab -e

vložíme tento řádek, kde je nastaveno, že skript bude do DB ukládat data každých 5 minut

Notice

*/5 * * * * php -f /var/www/temperaturesensor.php

 

4. Vytvoření skriptů pro zobrazení dat na webu

najedeme do adresáře pro www a zde se musejí vytvořit tři skripty. dallas.php je skript, který se bude spouštět a volat skript pro získání dat z DB dallas.csv.php a pro graf dygraph-combined.js.

první skript dallas.php

Notice

sudo nano dallas.php

vložíme tento skript a je nutné upravit na local host. (ja mám veřejnou IP a běží mi to na doméně raspberry-pi.eu)

Notice

<?php if(is_int($_GET['period'])) {
	$params = '';
}
?> <html> <head>
	<script type="text/javascript" src="dygraph-combined.js"></script>
	<style>
		div {font: calibri}
	</style>
  <!--link rel="stylesheet" href="bootstrap.min.css">
  <link rel="stylesheet" href="site.css"--> </head> <body>
	<div id="graphdiv" style="width:1000px; height:600px; float:left"></div>
	<div id="labelsdiv" style=""></div>
	<script type="text/javascript">
  	g2 = new Dygraph(
    	document.getElementById("graphdiv"),
    	"http://www.raspberry-pi.eu/dallas.csv.php<?php echo $params; ?>", // path to CSV file
    	{// options
    		hideOverlayOnMouseOut: 0,
    		rollPeriod: 5,
    		showRoller: true,
    		labelsDiv: labelsdiv,
    		labelsSeparateLines: true,
    		legend: "always"
    	}
  	);
	</script> </body>
</html>

druhý skript dallas.csv.php

 

Notice

sudo nano dallas.csv.php

zde je opět nutné změnit heslo

Notice

<?php
	header ("Content-Type:text/csv");
	
	isset($_GET['dtMin']) ? $dtMin = date("Y-m-d H:i:s", intval($_GET['dtMin'])) : $dtMin = 
date("Y-m-d H:i:s", time() - 86400); //den = 86400
	isset($_GET['dtMax']) ? $dtMax = date("Y-m-d H:i:s", intval($_GET['dtMax'])) : $dtMax = 
date("Y-m-d H:i:s", time());
	isset($_GET['T']) ? $tmer = " AND nazev='{$_GET['T']}' " : $tmer = "";
	
	$db = mysqli_connect("localhost", "jmeno", "heslo", "develop");
	mysqli_query($db, "SET NAMES UTF8") or die("chyba set names");
	
	$data = mysqli_query($db, "SELECT * FROM teploCsv WHERE dt BETWEEN '$dtMin' AND 
'$dtMax' ORDER BY nazev, dt");
	
	while($row = mysqli_fetch_array($data))
	{
		$teplomery[$row['nazev']]['nazev'] = $row['nazev'];
		$teploty[$row['dt']][$row['nazev']] = $row['teplota'];
	}
	
	$nazvy = array_keys($teplomery);
	$casy = array_keys($teploty);
	
	echo 'čas';
	foreach($nazvy as $nazev)
	{
		echo ',' . $nazev;
	}
	echo "\n";
	
	foreach($casy as $cas)
	{
		echo $cas;
		foreach($nazvy as $nazev)
		{
			echo ',' . $teploty[$cas][$nazev];
		}
		echo "\n";
	}
?>

ještě poslední skript, který je potřeba vytvořit je pro zobrazení grafu

Notice

sudo nano dygraph-combined.js

a vložit tyto data dygraph-combined.js

 

pak už stačí jenom spustit prohlížeč */dallas.php

www_zobrazení

www_zobrazení

 

5. Vytvoření aplikace pro Android zařízení, extrakce dat z MySQL DB za použití PHP, JSON (toto je možné za předpokladu veřejné IP na Raspberry PI, jinak bude funguvat pouze doma po připjení na domací síť)

vytvoření aplikace pomocí vývojového prostředí Eclipse. Zde v tomto skriptu je nutné změnit na vaši IP (HttpPost httppost = new HttpPost(„http://localhost/android.php“);) na Pi popřípadě na localhost  – jedná se o volaný PHP skript, který vrací data ve formátu JSON (nebudu zde rozvádět krok po kroku vývoj aplikací pro android zařízení) – do tohoto skriptu byl použit font led_real, který se musí přidat do assets do projektu v eclipse

Notice

package cz.androidapk.teplota;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONObject;
import cz.androidapk.teplota.R.id;
import android.os.Bundle;
import android.os.StrictMode;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.graphics.Typeface;
import android.util.Log;
import android.view.Menu;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
public class MainActivity extends Activity {
    TextView resultView;
    TextView resultViewTeplomer1;
    TextView resultViewTeplota1;
    TextView resultViewTeplomer2;
    TextView resultViewTeplota2;
    TextView resultViewTeplomer3;
    TextView resultViewTeplota3;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_main);
//StrictMode.enableDefaults();
// Font path
        String fontPath = "led_real.ttf";
// text view label
        resultView = (TextView) findViewById(id.resultView);
        resultViewTeplomer1 = (TextView) findViewById(id.textViewTemplomer1);
        resultViewTeplota1 = (TextView) findViewById(id.textViewTeplota1);
        resultViewTeplomer2 = (TextView) findViewById(id.textViewTeplomer2);
        resultViewTeplota2 = (TextView) findViewById(id.textViewTeplota2);
        resultViewTeplomer3 = (TextView) findViewById(id.textViewTeplomer3);
        resultViewTeplota3 = (TextView) findViewById(id.textViewTeplota3);
// Loading Font Face
        Typeface tf = Typeface.createFromAsset(getAssets(), fontPath);
// Applying font
// resultViewTeplota1.setTypeface(tf, Typeface.BOLD);
        resultViewTeplota1.setTypeface(tf);
        resultViewTeplota2.setTypeface(tf);
        resultViewTeplota3.setTypeface(tf);
        StrictMode.enableDefaults();
//StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy);
        getData();
    }
    public void getData(){
        String result = "";
        InputStream isr = null;
//get data from HTTP
        try{
            HttpClient httpclient = new DefaultHttpClient();
            HttpPost httppost = new HttpPost("http://localhost/android.php");
            HttpResponse response = httpclient.execute(httppost);
            HttpEntity entity = response.getEntity();
            isr = entity.getContent();
        }catch(Exception e) {
            Log.e("log_tag","Error on HTTP connection " + e.toString());
            resultView.setText("Couldnt connect to DB");
        }
//convert response string
        try{
            BufferedReader reader = new BufferedReader(new InputStreamReader(isr,"iso-8859-1"), 8);
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null){
                sb.append(line + "\n");
            }
            isr.close();
            result = sb.toString();
        }catch(Exception e){
            Log.e("log_tag","Error converting result "+ e.toString());
        }
//parse json data
        try{
//String s = "";
            String cas = "";
            String teplomer1 = "";
            String teplota1 = "";
            String teplomer2 = "";
            String teplota2 = "";
            String teplomer3 = "";
            String teplota3 = "";
            JSONArray jArray = new JSONArray(result);
            for(int i=0; i<jArray.length(); i++){
                JSONObject json = jArray.getJSONObject(i);
                if (i==0){
                    cas = json.getString("dt");
                    teplomer1 = json.getString("nazev");
                    teplota1 = json.getString("teplota");
                }
                if (i==1){
                    teplomer2 = json.getString("nazev");
                    teplota2 = json.getString("teplota");
                }
                if (i==2){
                    teplomer3 = json.getString("nazev");
                    teplota3 = json.getString("teplota");
                }
//s = s +
//"dt : " + json.getString("dt") + "\n" +
//"ID : " + json.getString("ID") + "\n" +
//"nazev : " + json.getString("nazev") + "\n" +
//"teplota : "+ json.getString("teplota")+ "\n\n";
            }
            resultView.setText(cas);
            resultViewTeplomer1.setText(teplomer1);
            resultViewTeplota1.setText(teplota1);
            resultViewTeplota1.setTextSize(80);
            resultViewTeplota1.setTextColor(Color.parseColor("#00ccff"));
            resultViewTeplomer2.setText(teplomer2);
            resultViewTeplota2.setText(teplota2);
            resultViewTeplota2.setTextSize(80);
            resultViewTeplota2.setTextColor(Color.parseColor("#66FF00"));
            resultViewTeplomer3.setText(teplomer3);
            resultViewTeplota3.setText(teplota3);
            resultViewTeplota3.setTextSize(80);
            resultViewTeplota3.setTextColor(Color.parseColor("#FF0033"));
        }catch (Exception e){
            Log.e("log_tag", "Error Parsing data "+ e.toString());
        }
    }
}

 

najedeme do adresáře  na Raspberry Pi pro www a zde je potřeba vytvořit skript – android.php.

první skript android.php

Notice

sudo nano android.php

vložíme tento skript a je nutné upravit na localhost (já mám veřejnou IP a běží mi to na doméně raspberry-pi.eu)

Notice

<?php
$connect=mysql_connect("localhost","user","heslo") or die("Unable to connect DB");
mysql_select_db("develop") or die("Could not open the db");
$query = "SELECT * FROM teploCsv order by Dt desc, ID asc limit 4";
$result = mysql_query($query, $connect);

while ($row = mysql_fetch_assoc($result)) {
	$output[]=$row;
	}
	
print(json_encode($output));
mysql_close($connect);
?>

pak už stačí být připojen s telefonem k internetu a při každém spuštění aplikace se načte poslední datum aktualizace DB spolu s teplotami

android

android

Share Button