Ací es mostren les diferències entre la revisió seleccionada i la versió actual de la pàgina.
| Ambdós costats versió prèvia Revisió prèvia Següent revisió | Revisió prèvia | ||
|
iot_django_aproximacio_un_dashboard_sobre_raspberry_pi [2023/06/24 15:51] jordi_gual_purti |
iot_django_aproximacio_un_dashboard_sobre_raspberry_pi [2023/06/25 06:36] (actual) jordi_gual_purti |
||
|---|---|---|---|
| Línia 98: | Línia 98: | ||
| </ | </ | ||
| - | === Instal·lació de la biblioteca | + | <WRAP info> |
| + | Cal dir que l' | ||
| + | </ | ||
| + | |||
| + | ==== Instal·lació de la biblioteca FusionCharts | ||
| Abans de continuar veient com implementar la resta de components, cal dur a terme la instal·lació de la llibreria [[https:// | Abans de continuar veient com implementar la resta de components, cal dur a terme la instal·lació de la llibreria [[https:// | ||
| + | |||
| [[https:// | [[https:// | ||
| - | El nivell de detall d' | + | |
| + | El nivell de detall d' | ||
| + | |||
| + | ==== Creació del dashboard senzill ==== | ||
| + | |||
| + | L' | ||
| + | |||
| + | - **Pàgina principal**: | ||
| + | - **Formulari selecció de data**: on escollirem la data sobre la qual volem generar la gràfica de les mesures registrades, | ||
| + | - **Visualització de la gràfica**: pàgina que visualitzarà les mesures que tenim registrades del sensor escollit i de la data indicada en forma de gràfica temporal. Disposa d'un botó per tornar a la pàgina principal. | ||
| + | |||
| + | <WRAP info> | ||
| + | En aquesta documentació, | ||
| + | </ | ||
| + | |||
| + | Aquests tres components són vistes de Django que estan implementades com es pot veure en el codi font que teniu a continuació: | ||
| + | <code python> | ||
| + | from django.shortcuts import render, get_object_or_404 | ||
| + | from django.http import HttpResponse, | ||
| + | from django import forms | ||
| + | from .models import TemperatureSample, | ||
| + | from .fusioncharts import FusionCharts, | ||
| + | from django.template import loader | ||
| + | from django.urls import reverse | ||
| + | |||
| + | # Vista inicial (root). | ||
| + | def index(request): | ||
| + | latest_sensor_list = TemperatureSensor.objects.order_by(" | ||
| + | template = loader.get_template(" | ||
| + | context = { | ||
| + | " | ||
| + | } | ||
| + | return HttpResponse(template.render(context, | ||
| + | |||
| + | |||
| + | # Vista formulari per configurar la gràfica que volem visualitzar (escollir data). | ||
| + | class GraphForm(forms.Form): | ||
| + | data = forms.DateField(label=" | ||
| + | |||
| + | def graph_form(request, | ||
| + | gf = GraphForm() | ||
| + | return render(request, | ||
| + | |||
| + | |||
| + | # Vista de la gràfica que recull la data del formulari i genera la gràfica d' | ||
| + | def graph_view(request, | ||
| + | if request.method==" | ||
| + | |||
| + | form = GraphForm(request.POST) | ||
| + | |||
| + | if form.is_valid(): | ||
| + | # Preparem les dades i configuracions generals de la gràfica. | ||
| + | sensor = get_object_or_404(TemperatureSensor, | ||
| + | data = form.cleaned_data[" | ||
| + | |||
| + | start = str(data) + ' 00: | ||
| + | end = str(data) + ' 23: | ||
| + | |||
| + | # Preparem l' | ||
| + | # una gràfica de línia, amb proporcionalitat temporal. | ||
| + | schema = [{ | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | }, { | ||
| + | " | ||
| + | " | ||
| + | }] | ||
| + | dades_temp = [] | ||
| + | |||
| + | # Les dades, les afegim consultant-les de la BD. | ||
| + | for key in TemperatureSample.objects.filter(timestamp__range=(start, | ||
| + | mostra = [] | ||
| + | mostra.append(key.timestamp.strftime(' | ||
| + | mostra.append(float(key.value)) | ||
| + | dades_temp.append(mostra) | ||
| + | |||
| + | # Creem els objectes que necessita el constructor de la gràfica. | ||
| + | fusionTable = FusionTable(schema, | ||
| + | timeSeries = TimeSeries(fusionTable) | ||
| + | |||
| + | # Creem un objecte per a la gràfica utilitzant el constructor de FusionCharts: | ||
| + | # El cinquè paràmetre, ha de coincidir amb el nom del <div> de la template HTML on | ||
| + | # volem que es visualitzi la gràfica. | ||
| + | grafica = FusionCharts(" | ||
| + | |||
| + | # " | ||
| + | return render(request, | ||
| + | |||
| + | return HttpResponseRedirect(reverse(" | ||
| + | |||
| + | </ | ||
| + | |||
| + | Les templates HTML que s' | ||
| + | |||
| + | '' | ||
| + | <code html> | ||
| + | < | ||
| + | {% if latest_sensor_list %} | ||
| + | < | ||
| + | {% for s in latest_sensor_list %} | ||
| + | < | ||
| + | {% endfor %} | ||
| + | </ | ||
| + | {% else %} | ||
| + | <p>No hi ha sensors disponibles</ | ||
| + | {% endif %} | ||
| + | </ | ||
| + | |||
| + | '' | ||
| + | <code html> | ||
| + | < | ||
| + | <form action="/ | ||
| + | {% csrf_token %} | ||
| + | {{form.as_p}} | ||
| + | <input type=" | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | '' | ||
| + | <code html> | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | {% load static %} | ||
| + | <script type=" | ||
| + | <script type=" | ||
| + | <script type=" | ||
| + | </ | ||
| + | < | ||
| + | <div id=" | ||
| + | <div id=" | ||
| + | <a href="/ | ||
| + | < | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== Automatització de la captura de dades ==== | ||
| + | |||
| + | La captura de dades s'ha de fer partint de la base que disposem d'un sensor. El muntatge que hem fet per a la implementació d' | ||
| + | {{ : | ||
| + | |||
| + | Tenint això, cal preparar un petit programa **python** que s' | ||
| + | <code python> | ||
| + | # | ||
| + | from django.core.wsgi import get_wsgi_application | ||
| + | import os | ||
| + | |||
| + | # Establim l' | ||
| + | os.environ.setdefault(" | ||
| + | |||
| + | # Instanciem un web service WSGI per tenir-lo com a passarel·la per al nostre django. | ||
| + | application = get_wsgi_application() | ||
| + | |||
| + | from temperature.models import TemperatureSensor, | ||
| + | import glob | ||
| + | from datetime import datetime, timedelta | ||
| + | import time | ||
| + | |||
| + | # Carreguem els drivers del sensor de temperatura. | ||
| + | os.system(' | ||
| + | os.system(' | ||
| + | |||
| + | # Configurem l' | ||
| + | # el funcionament del sensor Dallas DS18B20. | ||
| + | base_dir = '/ | ||
| + | device_folder = glob.glob(base_dir + ' | ||
| + | device_file = device_folder + '/ | ||
| + | |||
| + | # Aquestes dues funcions implementen la lectura de mostres de temperatura | ||
| + | # del sensor que tenim connectat al RPI. | ||
| + | def read_temp_raw(): | ||
| + | f = open(device_file, | ||
| + | lines = f.readlines() | ||
| + | f.close() | ||
| + | return lines | ||
| + | |||
| + | def read_temp(): | ||
| + | lines = read_temp_raw() | ||
| + | while lines[0].strip()[-3: | ||
| + | time.sleep(0.2) | ||
| + | lines = read_temp_raw() | ||
| + | equals_pos = lines[1].find(' | ||
| + | if equals_pos != -1: | ||
| + | temp_string = lines[1][equals_pos+2: | ||
| + | temp_c = float(temp_string) / 1000.0 | ||
| + | return temp_c | ||
| + | |||
| + | # Cada cop que executem aquest programa, agafem una mostra de la temperatura del | ||
| + | # sensor i la guardem a la BD a través del model de dades de l' | ||
| + | ts = datetime.now() + timedelta(hours=2) | ||
| + | mostra = TemperatureSample(sensor_id=1, | ||
| + | mostra.save() | ||
| + | </ | ||
| + | |||
| + | Per automatitzar la presa de mesures de temperatura només falta configurar el servei **cron** a través d'un arxiu **crontab**. Per programar una lectura de la temperatura cada 2 minuts tindríem el seguent arxiu '' | ||
| + | < | ||
| + | */2 * | ||
| + | |||
| + | </ | ||
| + | Per introduir aquesta configuració a la planificació de tasques del servei **cron** del sistema haurem d' | ||
| + | < | ||
| + | $ crontab crontab.cfg | ||
| + | </ | ||
| + | |||
| + | ==== Algunes imatges ==== | ||
| + | |||
| + | Per tenir una idea més detallada dels resultats obtinguts es faciliten algunes imatges del muntatge del hardware i de la visualització de la gràfica com a mostra del que hem d' | ||
| + | |||
| + | '' | ||
| + | {{ : | ||
| + | |||
| + | '' | ||
| + | {{ : | ||
| + | |||
| + | '' | ||
| + | {{ : | ||
| + | |||
| + | ===== Consideracions finals ===== | ||
| + | |||
| + | Per acabar, es fan constar algunes consideracions importants que cal tenir presents sobre aquest material, tenint en compte la seva naturalesa didàctica: | ||
| + | |||
| + | * Es tracta, com hem dit, d'un material que té un objectiu didàctic. Per tant, no contempla molts dels aspectes que caldria tenir presents si es tractés d'un projecte orientat a la posada en producció real. | ||
| + | * No s'ha dedicat cap esforç a fer-lo bonic aplicant, per exemple, estils CSS. | ||
| + | * Els mecanismes de control d' | ||
| + | * No es toca la qüestió de la posada en producció del sistema. | ||
| + | |||
| + | |||
| + | Aquest projecte s'ha implementat, | ||
| + | * Tenir els sensors associats a un hardware més senzill i barat (Arduino, ESP32, etc.), amb capacitat de connexió a xarxa. | ||
| + | * Tenir la base de dades en un sistema més potent que SQLite: MySQL, PostgresQL, Oracle, etc. | ||
| + | * Tenir el servidor Django en un hardware més potent que no pas un Raspberry Pi. | ||
| + | * Establir les passarel·les adients d' | ||
| + | |||
| + | {{tag> #FpInfor #DamMp09 #DawMp07 django iot raspberry fusioncharts }} | ||