Domotiser un poêle à granulés ( partie 2 : Le code )

Nous avons vu dans un précédent article le montage électronique pour contrôler le poêle grâce à un raspberry et un potentiomètre digital. Nous allons voir maintenant le code utilisé pour cela.

Nous allons avoir 2 processus qui tournent en tache de fond :

  • L’alimentation en continu de la température en faisant varier la résistance du potentiomètre digital.
  • L’interface web qui me permettra de piloter tout cela.

J’ai choisi de tout programmer en python pour diverses raisons. La principale étant que je ne connais pas et que le meilleur moyen d’apprendre est d’avoir quelque chose de concret à réaliser. Il se peut donc qu’il y ai des améliorations à apporter au code qui vous sera présenté ci dessous.

Communication avec le potentiomètre digital

Le potentiomètre digital choisi est le MCP4162. Voici la datasheet de ce potentiomètre.

Comme vous pourrez le constater, ce potentiomètre communique via SPI. J’ai choisi d’utiliser la librairie python spidev.

import spidev

# Ouverture du bus SPI
spi = spidev.SpiDev()
spi.open(0,0) # car j'utilise la pin CE0: serait spi.open(0,1) si j'utilisais la pin CE1

# Transfert de la temperature (plus exactement un pas sur le digipot)
resp = spi.xfer2([0, index])

Dans notre cas, le potentiomètre digital possède 256 valeur de 0 à 255, index prendra l’une de ces 256 valeurs.

Ce qui donne le code complet :

!/usr/bin/python
# -*- coding: utf-8 -*-
 
import spidev
import time
import urllib2
import json

# Ouverture du bus SPI
spi = spidev.SpiDev()
spi.open(0,0) # car j'utilise la pin CE0: serait spi.open(0,1) si j'utilisais la pin CE1

coeff = 0.087
TEMP_OFF = 25
CONSIGNE_POELE = 19

try:
    while True :
        content = urllib2.urlopen("http://ip_raspberry:5000/status").read()
        d = json.loads(content)    
        print d
        if d[u'Etat'] == u'off' :
            print "poele eteint"
            index = 0
        else :
            print "poele allume"
            print (d[u'Temperature']- d[u'Consigne'] + CONSIGNE_POELE)
            index = int((TEMP_OFF - (d[u'Temperature']- d[u'Consigne'] + CONSIGNE_POELE))/coeff)
            if index < 0 :
                index = 0
            if index > 255 :
                index = 255
        resp = spi.xfer2([0, index])
        time.sleep(10)

finally:
    spi.close()

Voici le code, celui ci n’est pas indépendant du service web. C’est grâce au service web que cette boucle déduit la température à donner au poêle.

L’appel au « status » retourne le json suivant :

{
  "Consigne": 19.0, 
  "Etat": "on", 
  "Exterieur": 17.6, 
  "Temperature": 23
}

Ce json est ensuite converti en dict pour être utilisé simplement dans la suite du traitement.

On demande au poêle de se mettre en route si la température est inférieure à 19°C. La température dans la pièce est actuellement de 23°C. La température extérieure n’est actuellement pas utilisée, elle le sera peut-être plus tard si je trouve comment l’utiliser convenablement.

L’interface web

Pour l’interface web, j’ai fait le choix d’utiliser Flask.

L’objectif est de pouvoir piloter le poêle directement depuis un smartphone, ou de pouvoir l’inclure dans ma solution domotique existante (Zibase pour le moment).

Récupération de la température

La température de la pièce est fournie par la station météo Netatmo, qui fournit une API. j’utilise une API python déjà existante.

Ce qui se traduit par le bout de code suivant:

# Connexion à la Netatmo
authorization = lnetatmo.ClientAuth()
devList = lnetatmo.DeviceList(authorization)

#Récupération de la température
temperature_interieur = devList.lastData()[u'Intérieur']['Temperature']
temperature_exterieur = devList.lastData()[u'Extérieur']['Temperature']

Communication avec la zibase

Je veux pouvoir connecter le poêle à la zibase, parce que celle-ci fournit directement une fonctionnalité de thermostat.

En réalité ce dont j’ai besoin que la zibase connaisse, c’est la température relevée par la station météo Netatmo.

La Zibase peut peut effectuer des commandes http, mais elle est plutôt limitée de ce coté. Elle sait lire du XML. Le service web fourni donc une page qui transforme le dict retourné par l’API netatmo en XML compréhensible par la Zibase.

#Appel par la zibase pour récupérer les infos de la Netatmo    
@app.route("/netatmo")
def netatmo():
    global devList
    xml = unicode(dicttoxml(devList.lastData()), "utf-8")
    xml = re.sub(r" type=(.*?)>",">",xml)
    xml = unicodedata.normalize('NFKD', xml).encode('ascii','ignore')
    return Response(xml, mimetype='text/xml')

Pour simplifier la configuration coté Zibase, j’ai retiré les accents par exemple « Extérieur » devient « Exterieur » et simplifier au maximum le xml produit en retirant les types.

La zibase ne permet que d’allumer/éteindre, j’ai donc un service qui recoit le on/off :

#Appel par la zibase pour allumer/eteindre le thermostat    
@app.route("/thermostat/<consigne>")
def thermostat(consigne):
    global VAR_ZIBASE_TEMP_INT
    global VAR_ZIBASE_CONSIGNE
    global zibase_control
    global Statut
    if zibase_control :
        Statut['Etat'] = consigne
        content = urllib2.urlopen("http://ip_zibase/sensors.xml").read()
        root = ET.fromstring(content)
        for var in root.iter('var'):
            if var.attrib['num'] == VAR_ZIBASE_TEMP_INT :
                Statut['Temperature'] = float(var.attrib['val'])/10
            if var.attrib['num'] == VAR_ZIBASE_CONSIGNE :
                Statut['Consigne'] = float(var.attrib['val'])/10
    return "OK"

Par contre, heureusement pour moi, les variables qui servent au thermostat de la zibase, sont accessibles dans sensors.xml. J’utilise ElementTree pour lire ce XML et affecter mes variables gloables qui sont retournées dans la page status (utilisée par l’autre processus).

Le statut global

C’est donc dans les variables globales du service Web que l’état du poêle est connu : température de la pièce, température demandée, thermostat activé ou non, température extérieure.

@app.route(« /status »)
def status():
global Statut
Statut['Temperature'] = devList.lastData()[u'Intérieur']['Temperature']
Statut['Exterieur'] = devList.lastData()[u'Extérieur']['Temperature']
return jsonify(**Statut)

Cette page status se contente donc juste d’afficher un json du dict global qui contient toutes les infos nécessaires.

Interface indépendante de la zibase

Parce que je n’ai pas envie de dépendre uniquement de la zibase, j’ai aussi réalisé une petite interface web. J’utilise Jquery Mobile pour que celle ci soit accessible de n’importe quel type de matériel que ce soit mon ordinateur, smartphone ou tablette.

Voici un petit aperçu de cette page :

Capture d'écran de 2014-06-19 14:13:14

Comme vous pouvez le constater, l’interface web prend en compte le niveau de granulés dans la réserve. Cela sera réalisé via un émetteur à ultra-son HC-SR04.

Mais au même titre qu’il me manque un boitier pour le raspberry qui accueille la Pi Plate, il me manque aussi une mise en place discrète sous le couvercle des granulés de l’émetteur à ultra-son.

Il est bien souvent plus simple de faire les prototypes et de programmer que d’intégrer cela proprement chez soi.

 

Vus : 1947
Publié par C-quad : 36