Lampor i lägenheten skall tändas och släckas när jag kommer in i rummet eller lämnar rummet. Det skall gå att ställa in en veckarklocka från mobilappen, när larmet går så skall taklampor och sänglampa tändas och radion skall startas. Lampor skall även kunna styras manuellt från app eller internetsida. Temperatur och övriga loggade mätdata skall kunna ses i grafer på internetsida eller i app.
För att göra projektet lite roligare och trigga mig till att spara på el så är min idé att göra en livssimulator i telefonen. En autonom "gubbe" bor i en omgivning. Om elförbrukningen är hög blir det dåligt klimat, översvämningar ont om mat osv. Inte bra för gubben som blir ledsen alltså. Istället för att bara vänta på att klimatet skall bli bättre när elförbrukningen går ner kan man snabba på det och "köpa" t.ex. en matbit till gubben mot att alla lampor i lägenheten slocknar i en timme.
Tillvägagångssätt:
Allt kommer att styras centralt från en Raspberry Pi, en liten dator stor som ett kreditkort. Denna kommer att ha servermjukvara (mySQL och Apache2) installerat och vara tillgänglig från internet (forwardad port och dynDNS via noip.com).

Till denna skall ett 1-wire nätverk kopplas med:
- inne temp
- ute temp
- temp i fiskdamm (ytvatten)
- temp i fiskdamm (bottenvatten)
- lufttryck
- hygrometer ute
- hygrometer inne
NEXA-brytare eller Julas ANSLUT-brytare kommer kunna styras från servern via 433 Mhz radio. Rörelsedäckare (PIR-sensorer) samt dörrbrytare kommer kommunicera med servern trådlöst på samma våglängd.

Var jag står nu:
Jag har i några dagar haft en tråd i allmänt kategorin om detta ämnet men jag tycker att det är dags för en projekttråd nu. För att få den ordentligt uppstrukturerad och lättläst valde jag att göra en ny tråd här i projektdelen. Hoppas det är ok!
Jag har kopplat in en temperaturgivare, DS1820, enligt detta enkla (lånade) schema.

Nu har jag ett pythonscript, körs på Raspbarryn så fort den startar, som tar en temperaturmätning var tionde sekund och kallar på en php-fil som sparar mätvärdet i en MySQL databas. Sen har jag en php-fil som presenterar det i en graf med hjälp google charts.
Python skriptet:
Kod: Markera allt
import urllib2
import os
import time
# Adresses for the write-to-database php file
URL = "http://localhost/addSensorReadings.php"
GET1 = "?zone="
GET2 = "&value="
## ENABLE GPIO IN THE /etc/rc.local SCRIPT
## ALSO ADD /home/pi/readSensors.py SO THIS SCRIPT STARTS
## EVERY TIME RPI STARTS
# Adress to the therometers
BASE_DIR = '/sys/bus/w1/devices/'
###ALL_DIR = BASE_DIR + "w1_bus_master1/w1_master_slaves/"
###DEVICE_ALL = glob.glob(ALL_DIR)
###DEVICE_ID_LIVINGROOM = DEVICE_ALL[0]
DEVICE_ID_LIVINGROOM = '10-0008014c61ed'
TOP_DIR = '/w1_slave'
DEVICE_LIVINGROOM = BASE_DIR + DEVICE_ID_LIVINGROOM + TOP_DIR
# Read lines from file
def readTempRaw():
f = open(DEVICE_LIVINGROOM, 'r')
lines = f.readlines()
f.close()
return lines
# Extract temperature from lines
def readTemp():
lines = readTempRaw()
while lines[0].strip()[-3:] != 'YES': # Ensure temp sensor is read correct
time.sleep(0.2)
lies = readTempRaw()
indexOfEq = lines[1].find('t=')
if indexOfEq != -1:
temp = float(lines[1][indexOfEq + 2 :])/1000.00
return temp
# Main loop
while True:
# Read temp
tempLivingroom = readTemp();
#Print it on screan
#print(tempLivingroom)
# Insert it into MYSQL database
response = urllib2.urlopen(URL + GET1 + "Livingroom" + GET2 + str(tempLivingroom))
s = response.read()
# Check if insertion was sucessfull
#if s == b'Sucess':
#print("Sucess!")
#else:
#print("Failed to insert into database")
# Sleep for 10 seconds
time.sleep(10)
Kod: Markera allt
<?php
$TABLE_NAME = "readings";
$con = mysqli_connect('localhost', '***', '***', 'sensors');
if (mysqli_connect_errno()) {
echo "Failed to connect to mysql: " . mysqli_connect_errno();
}
$zone = $_GET["zone"];
$value = $_GET["value"];
if ($zone == "") {
echo "Error: Zone is not set";
}else if ($value == "") {
echo "Error: Value is not set";
}else {
$query = "INSERT INTO " . $TABLE_NAME . " (zone, value) VALUES ('$zone', '$value')";
$ok = mysqli_query($con, $query);
echo $ok==1?"Sucess":"Failed";
}
mysqli_close($con);
?>
Kod: Markera allt
<?php
$patterns = array ('/(19|20)(\d{2})-(\d{1,2})-(\d{1,2})/',
'/^\s*{(\w+)}\s*=/');
$replace = array ('\3/\4/\1\2', '$\1 =');
if (isset($_GET['from']))
$from = preg_replace($patterns, $replace, $_GET['from']);
else
$from = "";
if (isset($_GET['to']))
$to = preg_replace($patterns, $replace, $_GET['to']);
else
$to = "";
// String for html output
$str1 = "";
// Connect to server
$con = mysqli_connect('localhost', '***', '***', 'sensors');
if (mysqli_connect_errno()) {
$str1 = "Failed to connect to mysql: " . mysqli_connect_errno();
}
// Look up which sensors are saved
$query = "SELECT DISTINCT(zone) AS zone FROM readings ORDER BY zone";
$result = mysqli_query($con, $query);
$sensors = array();
while ($row = mysqli_fetch_array($result))
$sensors[] = $row['zone'];
$str1 .= ("<table style='border:0px;'><tr><td><table style='width: 300px; padding:10px; border:1px solid #3D3D3D;'><tr><td><b>Found " . mysqli_num_rows($result) . " sensor(s)</b></td></tr>");
foreach ($sensors as $sensor) {
$str1 .= "<tr><td> - " . $sensor . "</td></tr>";
}
$str1 .=("</table>");
// Table to store everything for graph..
if ($from == "")
$queryFrom = " datetime >= '" . date('Y-m-d H:m:s', strtotime('-1 hour')) . "'";
else
$queryFrom = " datetime >= '" . date('Y-m-d H:m:s', strtotime($from)) . "'";
if ($to == "")
$queryTo = "";
else
$queryTo = " AND datetime <= '" . date('Y-m-d H:m:s', strtotime($to)) . "'";
$query = "SELECT * FROM readings WHERE" . $queryFrom . $queryTo . " ORDER BY datetime DESC";
$result = mysqli_query($con, $query);
$num_plotted_times = mysqli_num_rows($result);
$last_read_temp = 999;
$table = array();
$i = 0;
while($row = mysqli_fetch_array($result)){
// Data table
//$table[$i] = array();
$table[$i][0] = (string)$row['datetime'];
$table[$i][1] = (double)$row['value'];
$i ++;
if ($i == 1)
$last_read_temp = $row['value'];
}
$table = array_reverse($table);
// Find min temperature in intervall
$query = "SELECT MIN(value) AS min FROM readings WHERE" . $queryFrom . $queryTo;
$result = mysqli_query($con, $query);
$row = mysqli_fetch_array($result);
$min_temp = $row['min'];
// Find max temperature in intervall
$query = "SELECT MAX(value) AS max FROM readings WHERE" . $queryFrom . $queryTo;
$result = mysqli_query($con, $query);
$row = mysqli_fetch_array($result);
$max_temp = $row['max'];
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Home Tempereture</title>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
var handle=setTimeout(function(){window.location.reload(1);}, 10000);
// Load the Visualization API and the piechart package.
google.load('visualization', '1', {'packages':['corechart']});
// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(drawChart);
function drawChart() {
// Create our data table out of JSON data loaded from server.
var data = new google.visualization.DataTable();
data.addColumn('string', 'datetime');
data.addColumn('number', '<?php echo $sensor; ?>');
data.addRows(<?php echo json_encode($table); ?>);
var options = {
'height':500
};
// Instantiate and draw our chart, passing in some options.
// Do not forget to check your div ID
var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
</script>
</head>
<body>
<center>
<input type="checkbox" name="refresh" value="refresh" onclick="if(this.checked){handle=setTimeout(function(){window.location.reload(1);}, 10000)}else{clearTimeout(handle)}" checked> Dynamic page refresh
<form name="input" action="index.php" method="get">
Show measurements from: <input type="text" name="from">
to: <input type="text" name="to"> <input type="submit" value="Submit">
</form></center>
<div id="chart_div" style="width:100%"></div>
<?php
// How many sensors where found?
echo "<center>" . $str1 . "</td><td>";
echo "<table style='padding:10px; border:0px;'><tr><td>";
echo "Last read temperature: <b>" . number_format($last_read_temp,1) . "°C</b>";
echo "</br>Min temperature in interval: <b>" . number_format($min_temp,1) . "°C</b>";
echo "</br>Max temperature in interval: <b>" . number_format($max_temp,1) . "°C</b>";
echo "</td></tr></table></td></tr></table>";
$query = "SELECT COUNT(*) AS count FROM readings"; // Check how many readings..
$result = mysqli_query($con, $query);
$row = mysqli_fetch_array($result);
echo "Plotted " . $num_plotted_times . " out of " . $row['count'];
echo ("</center>");
echo ("</body>");
echo ("</html>");
mysqli_close($con);
?>
(Nej, jag har inte sååå varmt här inne... Det måste vara denna tempsensorn som är trasig. I och för sig sitter den direkt på raspbarryn så det blir kanske lite varmare där men inte så mycket tycker jag.. Får se när jag kopplar in fler om de visar annorlunda!)
Nu har jag forwardat portarna och skaffat dynDNS hos noip.com så att jag kommer åt Raspberryn från internet.
Jag har också utvidgat både webinterfacet och androidappen så att jag kan styra en lysdiod samt att jag hela tiden får uppdaterad information om ifall pinnen är hög eller ej (så att man ser på appen tex om man släcker lysdioden från datorn).
Android appen hämtar data från ett XML-formaterat php dokument på servern (som laddas ned av en bakgrundsprocess). Det är även detta dokument som styr lysdioden genom GET dataöverföring.
PHP/XML dokumentet ser ut såhär:
Kod: Markera allt
<?php
// Set filetype to XML
header('Content-type: application/xml');
// Controll LED
if (isset($_GET['led']))
$led = $_GET['led'];
else
$led = "";
system('gpio -g mode 7 out'); // Set LED IO to output
if($led == "1")
system('gpio -g write 7 1'); // Turn on LED
else if ($led == "0")
system('gpio -g write 7 0'); // turn off LED
$readLed = "";
exec("gpio -g read 7", $readLed);
$strLed = implode('', $readLed);
// Open temperaturesensor
$file = '/sys/bus/w1/devices/10-0008014c61ed/w1_slave';
// Read all text
$text = file($file);
// get the temperature
$temp = explode('=',$text[1]);
$temp = number_format($temp[1] / 1000, 2, '.', '');
// Write XML Structure
$writer = new XMLWriter();
$writer->openURI('php://output');
$writer->startDocument('1.0','UTF-8');
$writer->startElement('records');
$writer->startElement('temperatureSensor');
$writer->writeAttribute('value', $temp);
$writer->writeAttribute('date', date('y-m-d', time()));
$writer->writeAttribute('time', date('H:i', time()));
$writer->endElement();
$writer->startElement('led');
$writer->writeAttribute('value', $strLed);
$writer->endElement();
$writer->endElement();
$writer->endDocument();
$writer->flush();
?>
Kod: Markera allt
<records>
<temperatureSensor value="27.63" date="14-06-21" time="21:01"/>
<led value="1"/>
</records>
Kod: Markera allt
package com.example.hometemperature;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.graphics.PorterDuff;
public class MainActivity extends ActionBarActivity {
private static final String TAG = "Home Temperature";
public static final String SERVER_URL = "http://*******.noip.me/";
public static final String QUERY_FILE = "temperatur.php";
public static final String QUERY_URL = SERVER_URL + QUERY_FILE;
LinearLayout container;
Button updateBtn;
Button ledOnBtn;
Button ledOffBtn;
TextView temp1Tv;
TextView temp1Tv2;
TextView ledTv;
AsyncDownloader downloader;
Bitmap bg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
container = new LinearLayout(this);
container.setOrientation(LinearLayout.VERTICAL);
container.setPadding(30, 30, 30, 30);
container.setBackgroundResource(R.drawable.bg);
updateBtn = new Button(this);
updateBtn.setOnClickListener(updateBtnListener);
updateBtn.setText("Update");
ledOnBtn = new Button(this);
ledOnBtn.setOnClickListener(ledOnBtnListener);
ledOnBtn.setText("Turn on LED");
ledOffBtn = new Button(this);
ledOffBtn.setOnClickListener(ledOffBtnListener);
ledOffBtn.setText("Turn off LED");
temp1Tv = new TextView(this);
temp1Tv.setText("");
temp1Tv.setBackgroundColor(Color.WHITE);
temp1Tv.setTextSize(35);
temp1Tv2 = new TextView(this);
temp1Tv2.setText("");
temp1Tv2.setBackgroundColor(Color.WHITE);
temp1Tv2.setTextSize(25);
ledTv = new TextView(this);
ledTv.setText("LED Status...");
ledTv.setBackgroundColor(Color.WHITE);
ledTv.setTextSize(20);
container.addView(updateBtn);
container.addView(temp1Tv);
container.addView(temp1Tv2);
container.addView(ledOnBtn);
container.addView(ledOffBtn);
container.addView(ledTv);
downloader = new AsyncDownloader();
new Timer().scheduleAtFixedRate(update, 10, 2000);
setContentView(container);
}
TimerTask update = new TimerTask(){ // If timer interrupted, execute update if not already in progress.
@Override
public void run() {
if (downloader.getStatus() != AsyncTask.Status.RUNNING){
downloader = new AsyncDownloader(); // A task can only be executed one...
downloader.execute("");
}
}
};
View.OnClickListener updateBtnListener = new View.OnClickListener() { // If button was clicked, execute update if not already in progress.
@Override
public void onClick(View v) {
if (downloader.getStatus() != AsyncTask.Status.RUNNING){
downloader = new AsyncDownloader(); // A task can only be executed one...
downloader.execute("");
}
}
};
View.OnClickListener ledOnBtnListener = new View.OnClickListener() { // If button was clicked, execute update if not already in progress.
@Override
public void onClick(View v) {
downloader = new AsyncDownloader(); // A task can only be executed one...
downloader.execute("?led=1");
}
};
View.OnClickListener ledOffBtnListener = new View.OnClickListener() { // If button was clicked, execute update if not already in progress.
@Override
public void onClick(View v) {
downloader = new AsyncDownloader(); // A task can only be executed one...
downloader.execute("?led=0");
}
};
private class AsyncDownloader extends AsyncTask<String, String, Integer> {
@Override
protected Integer doInBackground(String... params) {
XmlPullParser receivedData = tryDownloadingXmlData(params[0]);
int recordsFound = tryParsingXmlData(receivedData);
return recordsFound;
}
private XmlPullParser tryDownloadingXmlData(String query) {
try {
URL xmlUrl = new URL(QUERY_URL + query);
XmlPullParser receivedData = XmlPullParserFactory.newInstance().newPullParser();
receivedData.setInput(xmlUrl.openStream(), null);
return receivedData;
} catch (XmlPullParserException e) {
Log.e(TAG, "tryDownloadingXmlData Exception:", e);
} catch (IOException e) {
Log.e(TAG, "tryDownloadingXmlData Exception:", e);
}
return null;
}
private int tryParsingXmlData(XmlPullParser receivedData) {
if (receivedData != null){
try {
return processReceivedData(receivedData);
} catch (XmlPullParserException e) {
Log.e(TAG, "tryParsingXmlData Exception:", e);
} catch (IOException e) {
Log.e(TAG, "tryDownloadingXmlData Exception:", e);
}
}
return 0;
}
private int processReceivedData(XmlPullParser receivedData) throws XmlPullParserException, IOException {
int eventType = -1;
int recordsFound = 0;
// Find values in the XML records
String value = "";
String date = "";
String time = "";
while (eventType != XmlResourceParser.END_DOCUMENT) {
String tagName = receivedData.getName();
switch (eventType) {
case XmlResourceParser.START_TAG:
if (tagName.equals("temperatureSensor")){
value = receivedData.getAttributeValue(null, "value");
date = receivedData.getAttributeValue(null, "date");
time = receivedData.getAttributeValue(null, "time");
}
if (tagName.equals("led")){
value = receivedData.getAttributeValue(null, "value");
}
break;
case XmlResourceParser.END_TAG:
if (tagName.equals("temperatureSensor")){
//Log.i(TAG, "Data: " + value + date + time );
recordsFound ++;
publishProgress(value, date, time); // Send values to the main thread.
}
if (tagName.equals("led")){
recordsFound ++;
publishProgress(value);
}
break;
}
eventType = receivedData.next();
}
if (recordsFound == 0) {
publishProgress();
}
return recordsFound;
}
@Override
protected void onProgressUpdate(String... values) {
if (values.length == 0) {
Log.i(TAG, "No data downloaded");
} if (values.length == 3){
String value = values[0];
String date = values[1];
String time = values[2];
temp1Tv.setText(value + (char) 0x00B0 + "C");
temp1Tv2.setText("Last update " + time + " " + date);
} else if (values.length == 1){
ledTv.setText("Led status: " + values[0]);
}
super.onProgressUpdate(values);
}
}
public static Bitmap getBitmapFromAsset(Context context, String filePath) {
AssetManager assetManager = context.getAssets();
InputStream istr;
Bitmap bitmap = null;
try {
istr = assetManager.open(filePath);
bitmap = BitmapFactory.decodeStream(istr);
} catch (IOException e) {
Log.e(TAG, "EXCEPTION ON LOAD BITMAP", e);
}
return bitmap;
}
}
Mvh. Daniel