Home Automation

Domotica a basso costo

Creare una casa “domotica”, ovvero installare nella propria abitazione degli apparecchi di home automation, per controllare, ad esempio, luci, caldaia, condizionatore, sistemi di allarme da remoto col proprio cellulare o in maniera del tutto automatica, è una cosa molto divertente ma, se non controllata, rischia di compromettere seriamente il vostro portafogli.

È per questo motivo che mi sono deciso a scrivere il primo articolo sulla home automation low cost. È ovvio che, per rendere qualsiasi cosa divertente, il buon nerd deve limitarsi ad acquistare il minimo indispensabile per poter far funzionare tutto e creare tutto quello che manca seguendo la propria creatività.. Sennò, il divertimento dov’è ?

Cominciamo con una cosa semplice: la lampada vocale.

Il video mostra una normale lampada Ikea a cui è stata apportata qualche piccola modifica per renderla comandabile con la voce. La lampada è collegata direttamente alla presa elettrica e possiede anche un interruttore di accensione e spegnimento meccanico, ma, tra la lampada e la voce si trovano i seguenti dispositivi…

Dispositivi Hardware

Uno smartphone Android

Nexus 5Il mio è un nexus 5, ma va bene un qualsiasi telefono Android Froyo+ (se volete, potete provare a fare tutto con il vostro amato Windows Phone..).

Il telefono si occupa di convertire la voce in testo (sfruttando i servizi di riconoscimento vocale offerti da Google), testo che verrà usato per riconoscere i comandi e attivare il segnale di accensione e spegnimento della lampada. Ovviamente, quando parlavo di domotica low cost, lo smartphone non era incluso  😆

Il router di casa e la Raspberry PI

Raspberry PI Model BLa Raspberry PI è un pezzo fondamentale di tutti gli impianti di domotica fai da te. Io ho comprato una model B, ma anche la model A può essere usata per lo scopo. Per chi vivesse fuori da questo universo nerd, si tratta di un mini computer con SO Linux (raspbian) che verrà usato per lanciare il software server che si occupa di recepire le richieste provenienti dallo smartphone e tradurle in comandi per la lampada. Sulla raspberry ho caricato anche un semplice sito web mobile responsive con un tasto per accendere spegnere la lampada anche da browser. Per far funzionare i comandi sia dall’esterno che dall’interno occorre fare un po’ di lavoro con il DNS del router: configurare un nome DNS dinamico è il primo passo, poi bisogna fare in modo che lo smartphone e gli altri dispositivi accedano alla Raspberry PI tramite l’IP della LAN quando sono collegati in wireless, mentre devono usare l’IP pubblico quando sono collegati a reti esterne (trovatevi una bella guida BIND9 per configurare un server DNS sulla raspberry, impostate una DMZ sulla Raspberry se sapete usare iptables, oppure girate solo la porta 80 del router, per poter usare i dispositivi domotici dall’esterno).

Un trasmettitore X10: Marmitek CM15PRO

Marmitek CM15PROX10 è una tecnologia vecchia che ha avuto un grande successo in ambito di automazione industriale. Il fatto che sia vecchia, per noi, significa due cose: 1) È stabile ! 2) Costa poco ! Tutte le tecnologie moderne di home automation (Z-Wave, ZigBee) hanno delle potenzialità decisamente maggiori rispetto a X10, ma: costano tantissimo ! Una cosa è fare un esperimento con una lampada, un’altra cosa è controllare tutte le lampade della casa.

Il trasmettitore CM15PRO si collega alla Raspberry PI tramite la porta USB integrata e si può utilizzare con il software/driver Mochad. Una volta installato Mochad, è possibile collegare il dispositivo ed utilizzarlo direttamente per mandare segnali sulla rete elettrica.

I creatori di Mochad hanno reso molto semplice l’invio di comandi X10 su linea elettrica (ma anche in radiofrequenza, per chi volesse sperimentare):

[codesyntax lang=”bash” title=”Script per eseguire comandi X10″]

echo "pl A1 on" | nc localhost 1099
echo "pl A1 off" | nc localhost 1099

[/codesyntax]

 

Mochad ascolta sulla porta 1099 e, con i due comandi di cui sopra, si controlla il trasmettitore per inviare sulla linea elettrica il segnale di accensione o spegnimento del dispositivo A1 (per chi volesse approfondire, i dispositivi X10 sono identificati da una lettera che dovrebbe indicare un gruppo di dispositivi, come quelli di una intera casa/appartamento/stanza, e da un numero che identifica il dispositivo nel gruppo).

Questi comandi verranno eseguiti da uno script python installato sulla Raspberry, come vedremo dopo.

Il ricevitore dimmer X10: Taiyito TDXE4401

Tayito TDXE4401Questo dispositivo si occupa di ricevere i segnali X10 e di tradurli in corrente elettrica per la lampadina Ikea.

L’oggetto è difficile da reperire in europa e, dalle ricerche che ho fatto, gli equivalenti europei costano minimo il triplo. Considerando l’intenzione (folle ?) di inserire questi dispositivi per ogni lampada di casa, meglio risparmiare. Il costo dell’oggetto (esclusa dogana, se sapete come evitarla) varia tra i € 8 e i € 16 per pezzo.

Tenete conto che questo dispositivo non funge da interruttore di corrente ma da dimmer, ovvero, fa un gradevole effetto di sfumatura nell’accendere e spegnere la luce, consente di regolare la luminosità, ma la sfumatura non può essere evitata e questo può diventare un problema (vedi dopo..). Esistono dei dispositivi parenti del TDXE4401 che invece fanno da semplici interruttori di corrente, esempio, il TDXE4403 (solo switch). Una caratteristica di entrambi i dispositivi, a differenza di molti prodotti europei che costano il triplo, è che rispondono anche ai comandi di status (esempio, se accendo o spengo la luce manualmente, tramite l’interruttore manuale, posso interrogare il dispositivo per sapere se è acceso o spento in qualsiasi momento).

Lo switch TDXE4401 può essere anche utilizzato per dispositivi diversi da lampadine, purché non si superi il limite di consumo di 300 W (non ci potete attaccare la lavatrice o il forno direttamente… ma, usando un semplice relé…).

La lampada

Lampada IkeaLa lampada è il pezzo più economico di tutto il sistema… forse…

Il forse è dovuto al fatto che, mentre le vecchie lampade ad incandescenza andavano tutte bene per un dimmer come il TDXE4401, le lampade a neon non possono essere “dimmerate” nella maniera più assoluta, mentre le lampade a led devono essere scelte tra quelle “dimmerabili”. È ovvio che quelle dimmerabili costano di più.. Quindi prendete un semplice switch X10 (TDXE4403) se non volete spendere troppo.

Il collegamento col dispositivo (nella foto si vede l’obbrobrio…) è un po’ brutto, ma si trattava di un prototipo…

Tenete conto che, nel collegamento con il dispositivo, è stato necessario collegare il vecchio interruttore della lampada Ikea ad un circuito a corrente continua (prodotta dal dispositivo stesso) ad un voltaggio molto basso (non l’ho misurato). Non pensate di collegarci degli interruttori elettrici che si aspettano una tensione di 220V con corrente AC, esempio, quelli con la lucetta per vedere dove sono anche di notte. Va bene, invece, se avete dei semplici interruttori meccanici.

Software

Veniamo ora alla parte più bella, perché collegare l’hardware è interessante, ma è sul software che si può mettere in pratica la fantasia. Io parlerò di come è configurato un semplice riconoscitore vocale che arriva ad attuare un comando ma, con questa base si possono realizzare molte cose simpatiche.

Parte Server

La parte server viene eseguita sulla Raspberry PI e, come detto, ha il compito di ricevere tramite chiamata REST HTTP una istruzione e tradurla in un comando per Mochad che, a sua volta, invierà un segnale powerline al ricevitore X10 che, a sua volta, accenderà la lampadina.

Per la parte server, ho usato Python 2.7 con server WSGI di default su micro framework Flask.

Parte di inizializzazione:

[codesyntax lang=”python” title=”File x10/__init__.py”]

from flask import Flask

app = Flask(__name__)
app.config.from_object('settings')

from x10.lights import mod as lights_mod 
app.register_blueprint(lights_mod, url_prefix='/lights')

[/codesyntax]

 

File di definizione dei servizi REST esposti:

[codesyntax lang=”python” title=”File x10/lights.py”]

from flask import Blueprint, jsonify, request
from x10.commands import X10Manager

mod = Blueprint('lights', __name__)

@mod.route('', methods = ['GET'])
def list():
	X10Manager.listLights()
	return jsonify({'devices': X10Manager.listLights()})

@mod.route('/<lightid>', methods = ['POST'])
def changeLight(lightid):
	json = request.get_json();
	status = json["on"]
	if status==True:
		success = X10Manager.lightOn(lightid)
	else:
		success = X10Manager.lightOff(lightid)

	return jsonify({
		'success': success
	})

@mod.route('/<lightid>/brightness', methods = ['PUT'])
def bright(lightid):

	success = X10Manager.lightBright(lightid)

	return jsonify({
		'success': success
	})

@mod.route('/<lightid>/darkness', methods = ['PUT'])
def dark(lightid):

	success = X10Manager.lightDim(lightid)

	return jsonify({
		'success': success
	})

[/codesyntax]

 

Classe che implementa le funzioni X10, tramite comunicazione con Mochad sulla porta 1099. Ho usato una Queue per serializzare i comandi ricevuti ed evitare i problemi di sincronizzazione di Mochad.

[codesyntax lang=”python” title=”File commands.py”]

from subprocess import Popen, PIPE
import time
import settings
import Queue
from threading import Thread
import logging

class X10ManagerType(object):

	def __init__(self):
		self.logger = logging.getLogger("X10ManagerType")

		self.logger.info("Initializing X10Manager")

		self.workqueue = Queue.Queue()

		self.laststatusupdate=0
		self.lights = []

		light = {}
		light["id"] = "abatjour"
		light["on"] = False
		light["x10house"] = "A"
		light["x10number"] = "1"
		light["x10code"] = light["x10house"] + light["x10number"]
		light["description"] = "Abat-jour"
		self.lights.append(light);

		self.logger.info("X10Manager initialized: " + str(len(self.lights)) + " light(s)")

		self.logger.info("Creating worker thread")
		w = Thread(target=self.__worker__)
		w.daemon = True
		w.start()

		self.logger.info("Worker thread created")

	def listLights(self):
		self.__checkupdatestatus__()
		return self.lights

	def lightOn(self, lightid):
		light = self.__findlight__(lightid)
		if light:
			self.__command__("pl " + light["x10code"] + " on")
			return True
		return False

	def lightOff(self, lightid):
		light = self.__findlight__(lightid)
		if light:
			self.__command__("pl " + light["x10code"] + " off")
			return True
		return False

	def lightBright(self, lightid):
		light = self.__findlight__(lightid)
		if light:
			self.__command__("pl " + light["x10code"] + " bright")
			return True
		return False

	def lightDim(self, lightid):
		light = self.__findlight__(lightid)
		if light:
			self.__command__("pl " + light["x10code"] + " dim")
			return True
		return False

	def __checkupdatestatus__(self):
		#timenow = int(round(time.time() * 1000))
		#update = True
		#if timenow-self.laststatusupdate<15000:
		#	update = False

		#if update:
		#	self.laststatusupdate = timenow
		#	self.__updatestatus__()
		self.__updatestatus__()

	def __updatestatus__(self):
		status = self.__direct_command__("st")
		for line in status:
			for light in self.lights:
				if "House " + light["x10house"] + ":" in line:
					if light["x10number"] + "=" in line:
						onoffpos = line.find(light["x10number"] + "=")+2
						onoff = line[onoffpos]
						if(onoff=='1'):
							light["on"] = True
						else:
							light["on"] = False

	def __findlight__(self, lightid):
		for light in self.lights:
			if light["id"]==lightid:
				return light
		return

	def __command__(self, command):
		self.workqueue.put(command)

	def __worker__(self):
		while True:
			command = self.workqueue.get()
			self.__direct_command__(command)
			time.sleep(0.01)

	def __direct_command__(self, command):
		self.logger.info("Executing command '" + command + "'")

		echoout = Popen(["echo", command], stdout=PIPE).stdout
		ncout = Popen(["nc", settings.MOCHAD_HOST_NAME, str(settings.MOCHAD_PORT)], stdin=echoout, stdout=PIPE).stdout

		finalout = ncout
		result = []
		while True:
			line = finalout.readline()
			if line != '':
				result.append(line.rstrip())
			else:
				break

		return result

X10Manager = X10ManagerType()

[/codesyntax]

Il codice prevede la presenza di una sola lampada in casa con codice identificativo “abatjour”. La lampada è installata sulla posizione X10 A1 (il manuale del TDXE4401 vi spiegherà come configurare la posizione di ascolto).

Il server è molto semplice, ma può essere migliorato a piacere. Oltre alla funzione di accendi e spegni, potete notare la funzione di aumento e diminuzione di luminosità. Mancano la gestione (DB) delle luci controllate della casa, e le funzioni di controllo e scoperta di nuovi dispositivi, oltre a tutto quello che ci si può inventare…

Per far partire tutto, si può far girare il server web su porta 80 direttamente, oppure su una porta alta (di solito, 5555) con un reverse proxy davanti.

[codesyntax lang=”python” title=”Script di avvio”]

#!/usr/bin/env python

from x10 import app
app.run(host='127.0.0.1', port=80)

[/codesyntax]

 

Parte Android

Veniamo ora alla parte Android. Qui i file sono di più, quindi mi limiterò a sottolineare le parti rilevanti.

L’oggetto di riconoscimento vocale, da attivare alla pressione di un tasto sull’interfaccia viene creato nel seguente modo.

[codesyntax lang=”java5″ title=”SpeechRecognizer”]

SpeechRecognizer speechRecognizer = SpeechRecognizer.createSpeechRecognizer(MainActivity.this);
speechRecognizer.setRecognitionListener(new HomeAutomationSpeechRecognitionListener(this,this);

// Alla pressione del tasto "Ascolta"
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);        
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_SUPPORTED_LANGUAGES, "it-IT");
intent.putExtra( RecognizerIntent.EXTRA_PROMPT, "Cosa vuoi fare ?");

speechRecognizer.startListening(intent);

[/codesyntax]

 

La classe HomeAutomationSpeechRecognitionListener si occupa di prendere il testo letto e tradurlo in comandi al server. Ne ho realizzato una versione molto semplice che evito di includere nell’articolo… Includo invece il comando di accensione luce.

[codesyntax lang=”java5″ title=”Comando di accensione luce”]

int TIMEOUT_MILLIS = 15000;  // = 15 seconds
HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_MILLIS);
HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_MILLIS);
HttpClient client = new DefaultHttpClient(httpParams);

HttpPost request = new HttpPost("http://mioindirizzoip/lights/abatjour");
request.setHeader("Content-Type", "application/json;charset=UTF-8");
request.setHeader("Authorization", "Basic [user:password di autenticazione, se presente, in b64]");

JSONObject json = new JSONObject();
json.put("on", true);

request.setEntity( new ByteArrayEntity(json.toString().getBytes("UTF-8")));
HttpResponse response = client.execute(request);

[/codesyntax]

E con questo, è tutto. Potete iniziare a fare cose divertenti !

5 pensieri su “Domotica a basso costo”

  1. Salve sarei interessato al vostro progetto del controllo vocale con raspberry x10 cm15 marmitek e raspberry ho tutto il necessario ho tutta la casa con i micromoduli per luci serrande marmitek solo che nn ho molta dimistichezza con il raspberry è possibile avere una guida? io uso homeseer con i comandi vocali ma mi pesa tenere il pc sempre acceso la ringrazio e la saluto

    1. Ciao Matteo!
      Ho visto che sei interessato al tema della casa domotica! Faccio parte di un gruppo di lavoro di studenti dell’università Bocconi e stiamo svolgendo un progetto a proposito. Ci interesserebbe molto avere uno scambio di opinioni a proposito per poterlo svolgere al meglio, saresti interessato?

  2. Salve, ho preso da poco il raspberry pi 2. ho una buona esperienza nel settore elettrico ( relè, collegamenti e altro) ma non ho conoscenza di programmazione.
    Posso mettere a disposizione la mia esperienza elettrica.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *