Ce travail fait appel à des notions :
Les informations sont données sur la page : [[publish:install_raspberry|Installer et configurer un Raspberry Pi]]
Nous allons utiliser la sortie téléinformation client disponible sur des compteurs assez récents. L'utilisation de cette sortie est libre (même pour les particuliers). Elle doit toutefois être activée. Les signaux sortant du compteur doivent être démodulés puis récupérés sur une interface série du Raspberry. Une documentation de la sortie téléinfo est disponible ici :
Une fois tous les appareils branchés, chercher le port utilisé :
# dmesg ... [ 614.584538] usb 1-1.3: FTDI USB Serial Device converter now attached to ttyUSB0
Dans la suite nous supposerons que le port utilisé est ttyUSB0
# stty -F /dev/ttyUSB0 9600 # cat /dev/ttyUSB0 PAPP 00000 ! TDETAT 000000 B ADCO 031328343332 : OPTARIF BASE 0 ISOUSC 15 < BASE 000076171 ! PTEC TH.. $ IINST 000 W ...
Si rien ne s'affiche, la sortie téléinfo n'est pas activée ou un des appareils est mal branché.
import serial SER = serial.Serial("/dev/ttyUSB0", 9600, timeout=1) for i in range(10): l2 = SER.readline() print(l2.decode("ascii")) SER.close()
ttyUSB0
(module pyserial)
Nous devons disposer d'une fonction getdata
qui prend en paramètre un ensemble de champs à relever (par exemple PAPP ou IINST).
Nous fournirons à getdata
cet ensemble, et getdata
renverrra en retour un dictionnaire contenant
les valeurs mesurées et **datées**. Toutes les vérification (checksum, disponibilité des données) doivent être faites dans getdata
:
>>> d = getdata({'PAPP', 'IINST'}) >>> print(d) { 'PAPP': (datetime.datetime(2014, 6, 4, 11, 32, 50, 236561), '00000'), 'IINST': (datetime.datetime(2014, 6, 4, 11, 32, 50, 220739), '000') }
L'horodatage pourra être fait avec la fonction Python : datetime.datetime.now()
Les données lues sur le compteur représentent un instantané de la consommation électrique, au moment précis où la lecture est faire. Pour ne pas omettre des pics de consommation brefs, il faut donc faire des lectures très fréquentes. En revanche, enregistrer un point de données toutes les 2 secondes dans une base de données risque de la faire croître inutilement (si l'enregistrement doit durer 1 mois, cela représenter 1.3 millions de points). Nous devons disposer d'un système qui permet de n'enregistrer qu'une valeur moyenne calculée à partir de plusieurs mesures.
Pour cela, nous allons écrire une fonction valeur_moyenne
qui prend en paramètre une liste de mesures (une mesure est un couple (horodatage, valeur)) et renvoie un nouvel horodatage et une valeur moyenne. Sans présumer que les mesures seront régulièrement espacées dans le temps, essayez de minimiser les erreurs dues à ce calcul (faut-il simplement calculer des moyennes, intégrer ? que se passe-t-il s'il y a des données manquantes etc..)
Exemple :
>>> data = [ (datetime.datetime(2014, 6, 4, 11, 37, 53, 618051), '00210'), (datetime.datetime(2014, 6, 4, 11, 38, 3, 706097), '00200'), (datetime.datetime(2014, 6, 4, 11, 38, 13, 836844), '00200'), (datetime.datetime(2014, 6, 4, 11, 38, 23, 927376), '00200'), (datetime.datetime(2014, 6, 4, 11, 38, 34, 7777), '00220'), (datetime.datetime(2014, 6, 4, 11, 38, 44, 72780), '00210') ] >>> v = valeur_moyenne(data) >>> print(v) ('2014-06-04 11:38:18.861487', 206.0)
Dans le cas où la grandeur mesurée est l'index (voir la liste des grandeurs disponibles sur la sortie téléinfo), une mise en forme peut être de stocker dans la base la différence entre 2 mesures d'index successifs, afin de refléter la consommation à un moment donné (on pourra ainsi mettre en relation l'index avec la puissance apparente, pas exemple).
Nous utiliserons une base sqlite, facile à créer, ne nécessitant pas de serveur, et facilement transportable (un seul fichier). La base ne contiendra qu'une table, nommé histo
:
CREATE TABLE [histo] ([id] INTEGER PRIMARY KEY ASC AUTOINCREMENT, [type] VARCHAR (10), [valeur] REAL, [date] DATETIME);
L'accès à une base sqlite est facile avec Python. Voici un exemple pour une base de données nommée 'data.db' contenant une table :
CREATE TABLE [tbl] ([id] INTEGER PRIMARY KEY ASC AUTOINCREMENT, [type] VARCHAR (10), [valeur] REAL);
import sqlite3 db = sqlite3.connect('data.db') c = db.cursor() req = "insert into tbl (type,valeur) values (?,?)" c.execute(req, ("PAPP", 230) c.execute(req, ("PAPP", 240) db.commit() db.close()
On peut ensuite vérifier le contenu de la base :
# sqlite data.db # select * from tbl 1|PAPP|230.0 2|PAPP|240.0
Pour l'enregistrement de la date, on dispose de la méthodeisoformat()
utilisable sur une date Python et compréhensible par sqlite :
>>> import datetime >>> d = datetime.datetime.now() >>> d datetime.datetime(2014, 6, 4, 10, 51, 22, 48450) >>> d.isoformat() '2014-06-04T10:51:22.048450'
Une fois cette partie réalisée, nous devons disposer d'une fonction lecture_continuelle_bdd
qui prend en paramètres le nom du fichier de base de données, et l'ensemble des valeurs à consigner (éventuellement les délais de mesure et d'enregistrement, mais vous pouvez aussi les régler par le biais
de variables globales (constantes) dans le code. La fonction pourra être utilisée ainsi:
lecture_continuelle_bdd('historique.db', {'PAPP'})
Voici le type d'enregistrements qu'on trouvera ensuite dans la base de données :
# sqlite3 historique.db > select * from histo where id > 1160; 1161|PAPP|247.0|2014-06-04T06:47:58.423484 1162|PAPP|262.0|2014-06-04T06:48:59.061081 1163|PAPP|248.0|2014-06-04T06:49:59.680574 1164|PAPP|255.0|2014-06-04T06:51:00.292282 1165|PAPP|269.0|2014-06-04T06:52:00.885810 1166|PAPP|451.0|2014-06-04T06:53:01.496588 1167|PAPP|440.0|2014-06-04T06:54:01.840259 1168|PAPP|429.0|2014-06-04T06:55:02.232305 1169|PAPP|462.0|2014-06-04T06:56:02.569334 1170|PAPP|470.0|2014-06-04T06:57:03.185957 1171|PAPP|468.0|2014-06-04T06:58:03.586237 1172|PAPP|388.0|2014-06-04T06:59:03.932325 1173|PAPP|311.0|2014-06-04T07:00:04.535068 1174|PAPP|306.0|2014-06-04T07:01:05.146657 1175|PAPP|311.0|2014-06-04T07:02:05.754935 1176|PAPP|310.0|2014-06-04T07:03:06.353125 1177|PAPP|307.0|2014-06-04T07:04:06.951739 1178|PAPP|303.0|2014-06-04T07:05:07.567461 1179|PAPP|314.0|2014-06-04T07:06:08.177827 1180|PAPP|300.0|2014-06-04T07:07:08.552106
On réalise ici une application Web qui lit des mesures dans la base de données et les présente à l'utilisateur. Nous utiliserons le framework 'bottle' pour réaliser l'application Python. La présentation des données (graphiques) peut être réalisée de plusieurs manières. Une possibilité est d'utiliser la bibliothèque Javascript hicharts
.
Exemple d'application bottle
:
import bottle import datetime @bottle.route("/time") def index() : heure = datetime.datetime.now().strftime("Nous sommes le %d/%m/%Y, il est %H:%M:%S") stri = "<h1>Horloge</h1>"+heure return stri bottle.run(bottle.app(), host=’0.0.0.0’, port=8080, debug= True, reloader=True)
Une fois l'application lancée, faire pointer le navigateur sur : ''http://localhost:8080/time
</file>
La documentation de
bottle est disponible ici : http://bottlepy.org/docs/0.12/
Bottle utilise un système de template qu'il faut utiliser dès que les pages sont un peu conséquentes (sous peine de perdre du temps en débuggage et de créer une application difficile à adapter aux besoins).
La bibliothèque highcharts est documentée ici : http://www.highcharts.com/. On pourra par exemple utiliser un graphique de type http://www.highcharts.com/demo/spline-irregular-time
Une fois ce travail terminé, la consultation de l'URL :
http://localhost:8080/last/5'' affichera les données pour les 5 dernières heures.
Voici le type de représentation qu'on peut obtenir par ce moyen :