Dos dados ao gráfico:uma jornada na web com o Flask e o SQLite
Captura de dados reais (RPi / DHT22), salvando-os em banco de dados (SQLite), criando gráficos (Matplotlib) e apresentando-os em página web (Flask).
Introdução:dos dados para o gráfico. um Web Jorney com Flask e SQLite
Em meu tutorial anterior, Python WebServer With Flask e Raspberry Pi, aprendemos como interagir com o mundo físico, através de uma página front-end da web, construída com Flask. Assim, o próximo passo natural é coletar dados do mundo real, disponibilizando-os para nós em uma página da web. Muito simples! Mas, o que vai acontecer se quisermos saber como foi a situação do dia anterior, por exemplo? Ou fazer algum tipo de análise com esses dados? Nesses casos, devemos ter os dados também armazenados em um banco de dados.
Resumindo, neste novo tutorial, iremos:
Capture dados reais (temperatura do ar e umidade relativa) usando um sensor DHT22; carregue esses dados em um banco de dados local , construído com SQLite; Crie gráficos com dados históricos usando Matplotlib; Exibir dados com “medidores” animados, criados com JustGage; Disponibilize tudo online por meio de um servidor da web local criado com Python e Flask;
O diagrama de blocos nos dá uma ideia de todo o projeto:
Etapa 1:BoM - Lista de materiais
- Raspberry Pi V3 - US $ 32,00
- Sensor DHT22 de Temperatura e Umidade Relativa - US $ 9,95
- Resistor 4K7 ohm
Etapa 2:Instalando o SQLite
OK, a ideia geral será coletar dados de um sensor e armazená-los em um banco de dados.
Mas qual “mecanismo” de banco de dados deve ser usado?
Existem muitas opções no mercado e provavelmente as 2 mais utilizadas com o Raspberry Pi e sensores são MySQL e SQLite. O MySQL é muito conhecido, mas um pouco “pesado” para uso em projetos simples baseados no Raspberry (além de ser próprio da Oracle!). SQLite é provavelmente a escolha mais adequada. Porque não tem servidor, é leve, de código aberto e suporta a maioria dos códigos SQL (sua licença é “Domínio Público”). Outra coisa útil é que o SQLite armazena dados em um único arquivo que pode ser armazenado em qualquer lugar.
Mas, o que é SQLite?
SQLite é um sistema de gerenciamento de banco de dados relacional contido em uma biblioteca de programação C. Em contraste com muitos outros sistemas de gerenciamento de banco de dados, o SQLite não é um mecanismo de banco de dados cliente-servidor. Em vez disso, está embutido no programa final.
SQLite é um domínio público popular escolha como software de banco de dados embutido para armazenamento local / cliente em software de aplicação, como navegadores da web. É indiscutivelmente o mecanismo de banco de dados mais amplamente implantado, visto que é usado hoje por vários navegadores, sistemas operacionais e sistemas integrados (como telefones celulares), entre outros. SQLite tem ligações para muitas linguagens de programação como Python, aquela usada em nosso projeto.
(Mais na Wikipedia)
Não entraremos em muitos detalhes aqui, mas a documentação completa do SQLite pode ser encontrada neste link:https://www.sqlite.org/docs.html
Que assim seja! Vamos instalar o SQLite em nosso Pi
Instalação:
Siga as etapas abaixo para criar um banco de dados.
1. Instale o SQLite no Raspberry Pi usando o comando:
sudo apt-get install sqlite3
2. Crie um diretório para desenvolver o projeto:
mkdir Sensors_Database
3. Mova para este diretório:
cd mkdir Sensors_Database /
3. Dê um nome e crie um banco de dados como databaseName.db (no meu caso “sensoresData.db”):
sqlite3 sensoresData.db
Um “shell” aparecerá, onde você pode entrar com comandos SQLite. Voltaremos a ele mais tarde.
sqlite>
Os comandos começam com um “.”, Como “.help”, “.quit”, etc.
4. Saia do shell para retornar ao Terminal:
sqlite> .quit
A tela de impressão do Terminal acima mostra o que foi explicado.
O “sqlite>” acima é apenas para ilustrar como o shell SQLite aparecerá. Você não precisa digitá-lo. Ele aparecerá automaticamente.
Etapa 3:Criar e preencher uma tabela
Para registrar os dados medidos do sensor DHT no banco de dados, devemos criar uma tabela (um banco de dados pode conter várias tabelas). Nossa tabela se chamará “DHT_data” e terá 3 colunas, onde registraremos nossos dados coletados:Data e Hora (nome da coluna: timestamp ), Temperatura (nome da coluna: temp ) e Umidade (nome da coluna: hum )
Criação de uma tabela:
Para criar uma tabela, você pode fazer isso:
- Diretamente no shell SQLite ou
- Usando um programa Python.
1. Usando Shell:
Abra o banco de dados que foi criado na última etapa:
sqlite3 sensoresData.db
E inserindo com instruções SQL:
sqlite> BEGIN; sqlite> CREATE TABLE DHT_data (timestamp DATETIME, temp NUMERIC, hum NUMERIC); sqlite> COMMIT;
Todas as instruções SQL devem terminar com “;”. Além disso, normalmente, essas declarações são escritas com letras maiúsculas. Não é obrigatório, mas uma boa prática.
2. Usando Python
import sqlite3 as liteimport syscon =lite.connect ('sensoresData.db') com con:cur =con.cursor () cur.execute ("DROP TABLE IF EXISTS DHT_data ") cur.execute (" CREATE TABLE DHT_data (timestamp DATETIME, temp NUMERIC, hum NUMERIC) ")
Abra o código acima no meu GitHub:createTableDHT.py
Execute-o em seu Terminal:
python3 createTableDHT.py
Seja qual for o método utilizado, a tabela deve ser criada. Você pode verificar isso no SQLite Shell usando o comando “.table”. Abra o shell do banco de dados:
sqlite3> sensoresData.db
No shell, depois de usar o .table , os nomes das tabelas criadas irão aparecer (no nosso caso será apenas uma:“DHT_table”. Saia do shell depois, usando o .quit comando.
sqlite> .tableDHT_datasqlite> .quit
Inserindo dados em uma tabela:
Vamos inserir em nosso banco de dados 3 conjuntos de dados, onde cada conjunto terá 3 componentes cada:(carimbo de data / hora, temperatura e zumbido). O componente carimbo de data / hora será real e retirado do sistema, usando a função integrada ‘agora’ e temp e hum são dados fictícios em oC e%, respectivamente.
Nota que o horário está em “UTC”, o que é bom porque você não precisa se preocupar com questões relacionadas ao horário de verão e outros assuntos. Se você quiser imprimir a data em um horário localizado, basta convertê-la para o fuso horário apropriado posteriormente.
Da mesma forma que foi feito com a criação de tabelas, você pode inserir dados manualmente via shell SQLite ou via Python. No shell, você faria isso, dados por dados usando instruções SQL como esta (em nosso exemplo, você fará isso 3 vezes):
sqlite> INSERT INTO DHT_data VALUES (datetime ('now'), 20.5, 30);
E em Python, você faria o mesmo, mas de uma vez:
import sqlite3 as liteimport syscon =lite.connect ('sensoresData.db') com con:cur =con.cursor () cur.execute ("INSERT INTO DHT_data VALORES (datetime ('now'), 20.5, 30) ") cur.execute (" INSERT INTO DHT_data VALUES (datetime ('now'), 25.8, 40) ") cur.execute (" INSERT INTO DHT_data VALUES (datetime (' agora '), 30.3, 50) ")
Abra o código acima no meu GitHub:insertTableDHT.py
Execute-o no Terminal Pi:
python3 insertTableDHT.py
Para confirmar que o código acima funcionou, você pode verificar os dados da tabela via shell, com a instrução SQL:
sqlite> SELECT * FROM DHT_DATA;
A tela de impressão do Terminal acima mostra como as linhas da tabela aparecerão.
Etapa 4:inserção e verificação de dados com Python
Para começar, vamos fazer o mesmo que fizemos antes (inserir e recuperar dados), mas fazendo ambos com python e também imprimindo os dados no terminal:
import sqlite3import sysconn =sqlite3.connect ('sensoresData.db') curs =conn.cursor () # função para inserir dados em um tabledef add_data (temp, hum) :curs.execute ("INSERT INTO DHT_data values (datetime ('now'), (?), (?))", (temp, hum)) conn.commit () # chamar a função para inserir dataadd_data (20.5, 30 ) add_data (25.8, 40) add_data (30.3, 50) # print database contentprint ("\ nEntire o conteúdo do banco de dados:\ n") para linha em curs.execute ("SELECT * FROM DHT_data"):print (row) # close the banco de dados após useconn.close ()
Abra o código acima no meu GitHub:insertDataTableDHT.py e execute-o em seu Terminal:
python3 insertDataTableDHT.py
A tela de impressão do Terminal acima mostra o resultado.
Etapa 5:Sensor de temperatura e umidade DHT22
Até agora criamos uma tabela em nosso banco de dados, onde salvaremos todos os dados que um sensor irá ler. Também inserimos alguns dados fictícios lá. Agora é hora de usar os dados reais para serem salvos em nossa tabela, temperatura do ar e umidade relativa do ar. Para isso, usaremos o antigo e bom DHTxx (DHT11 ou DHT22). O site ADAFRUIT fornece ótimas informações sobre esses sensores. Abaixo, algumas informações obtidas a partir daí:
Visão geral
Os sensores de temperatura e umidade DHT de baixo custo são muito básicos e lentos, mas são ótimos para entusiastas que desejam fazer alguns registros básicos de dados. Os sensores DHT são compostos por duas partes, um sensor capacitivo de umidade e um termistor. Há também um chip muito básico interno que faz algumas conversões de analógico para digital e emite um sinal digital com a temperatura e a umidade. O sinal digital é bastante fácil de ser lido usando qualquer microcontrolador.
DHT11 vs DHT22
Temos duas versões do sensor DHT, eles são um pouco semelhantes e têm a mesma pinagem, mas têm características diferentes. Aqui estão as especificações:
DHT11 (geralmente azul)
Bom para leituras de umidade de 20-80% com precisão de 5% Bom para leituras de temperatura de 0-50 ° C precisão de ± 2 ° C Não mais do que 1 Hz de taxa de amostragem (uma vez a cada segundo)
- Custo ultrabaixo
- 3 a 5 V de alimentação e E / S
- 2,5mA máximo de uso atual durante a conversão (durante a solicitação de dados)
- Tamanho do corpo 15,5 mm x 12 mm x 5,5 mm
- 4 pinos com espaçamento de 0,1 ″
DHT22 (geralmente branco)
Bom para leituras de umidade de 0-100% com precisão de 2-5% Bom para leituras de temperatura de -40 a 125 ° C precisão de ± 0,5 ° C Não mais do que 0,5 Hz de taxa de amostragem (uma vez a cada 2 segundos)
- Baixo custo
- 3 a 5 V de alimentação e E / S
- 2,5mA máximo de uso atual durante a conversão (durante a solicitação de dados)
- Tamanho do corpo 15,1 mm x 25 mm x 7,7 mm
- 4 pinos com espaçamento de 0,1 ″
Como você pode ver, o DHT22 é um pouco mais preciso e bom em uma faixa um pouco maior. Ambos usam um único pino digital e são "lentos", pois você não pode consultá-los mais de uma vez a cada segundo (DHT11) ou dois (DHT22).
Ambos os sensores funcionarão bem para que as informações internas sejam armazenadas em nosso banco de dados.
O DHTxx tem 4 pinos (de frente para o sensor, o pino 1 é o mais à esquerda):
- VCC (podemos conectar 5 V externo ou 3,3 V de RPi);
- Saída de dados;
- Não conectado
- Terrestre.
Usaremos um DHT22 em nosso projeto.
Uma vez que normalmente você usará o sensor em distâncias menores que 20m, um resistor de 4 K7 ohm deve ser conectado entre os pinos de Dados e VCC. O pino de dados de saída DHT22 será conectado ao Raspberry GPIO 16.
Verifique o diagrama elétrico acima conectando o sensor aos pinos RPi como abaixo:
- Pino 1 - Vcc ==> 3,3 V
- Pino 2 - Dados ==> GPIO 16
- Pino 3 - não conectado
- Pino 4 - Gnd ==> Gnd
Não se esqueça de instalar o resistor de 4 K7 ohm entre os pinos Vcc e dados. Assim que o sensor estiver conectado, devemos também instalar sua biblioteca em nosso RPi. Faremos isso na próxima etapa.
Etapa 6:Instalando a biblioteca DHT
No Raspberry, começando em / home, vá para / Documentos:
Documentos de cd
Crie um diretório para instalar a biblioteca e vá para lá:
mkdir DHT22_Sensorcd DHT22_Sensor
Em seu navegador, acesse Adafruit GITHub:https://github.com/adafruit/Adafruit_Python_DHT
Baixe a biblioteca clicando no link zip de download à direita e descompacte o arquivo na pasta Raspberry Pi criada recentemente. Em seguida, vá para o diretório da biblioteca (subpasta que é criada automaticamente quando você descompacta o arquivo) e execute o comando:
sudo python3 setup.py install
Abra um programa de teste (DHT22_test.py) no meu GITHUB:
import Adafruit_DHTDHT22Sensor =Adafruit_DHT.DHT22DHTpin =16humidity, temperature =Adafruit_DHT.read_retry (DHT22Sensor, DHTpin) se a umidade não for None e a temperatura não for None:print ('Temp ={0:0.1f} * C Umidade ={1:0.1f}% '. Formato (temperatura, umidade)) else:print (' Falha ao obter leitura. Tente novamente! ')
Execute o programa com o comando:
python3 DHT22_test.py
A tela de impressão do Terminal acima mostra o resultado.
Etapa 7:Captura de dados reais
Agora que temos ambos, o sensor e nosso banco de dados, todos instalados e configurados, é hora de ler e salvar os dados reais.
Para isso, usaremos o código:
import timeimport sqlite3import Adafruit_DHTdbname ='sensoresData.db'sampleFreq =2 # tempo em segundos # obter dados de DHT sensordef getDHTdata ():DHT22Sensor =Adafruit_DHT.DHT22 DHTpin =16 hum, temp =Adafruit_DHT.read_retry (DHT22Sensor, DHTpin) se o zumbido não for Nenhum e a temperatura não for Nenhum:zumbido =redondo (zumbido) temp =redondo (temp, 1) logData (temp, hum) # log dados do sensor no banco de dados logData (temp, hum):conn =sqlite3.connect (dbname) curs =conn.cursor () curs.execute ("INSERT INTO DHT_data values (datetime ('now'), (?), (?))", (temp , hum)) conn.commit () conn.close () # display database datadef displayData ():conn =sqlite3.connect (dbname) curs =conn.cursor () print ("\ nConteúdo do banco de dados completo:\ n") para linha em curs.execute ("SELECT * FROM DHT_data"):print (row) conn.close () # main functiondef main ():para i no intervalo (0,3):getDHTdata () time.sleep (sampleFreq) displayData () # Execute o programa main ()
Abra o arquivo acima em meu GitHub:appDHT.py e execute-o em seu Terminal:
python3 appDHT.py
A função getDHTdata () captura 3 amostras do sensor DHT, testa-os quanto a erros e, se estiver OK, salve os dados no banco de dados usando a função logData (temp, hum) . A parte final do código chama a função displayData () que imprime todo o conteúdo da nossa tabela no Terminal.
A tela de impressão acima mostra o resultado. Observe que as últimas 3 linhas (linhas) são os dados reais capturados com este programa e as 3 linhas anteriores foram aquelas inseridas manualmente antes.
Na verdade, appDHT.py não é um bom nome. Em geral, “appSomething.py” é usado com scripts Python em servidores web, como veremos mais adiante neste tutorial. Mas é claro que você pode usá-lo aqui.
Etapa 8:Captura de dados automaticamente
Neste ponto, o que devemos implementar é um mecanismo para ler e inserir dados em nosso banco de dados automaticamente, o nosso “Logger”.
Abra uma nova janela do Terminal e entre com o código Python abaixo:
import timeimport sqlite3import Adafruit_DHTdbname ='sensoresData.db'sampleFreq =1 * 60 # tempo em segundos ==> Amostra a cada 1 min # obter dados de DHT sensordef getDHTdata () :DHT22Sensor =Adafruit_DHT.DHT22 DHTpin =16 zumbido, temp =Adafruit_DHT.read_retry (DHT22Sensor, DHTpin) se zumbido não for Nenhum e a temperatura não for Nenhum:zumbido =redondo (zumbido) temp =redondo (temp, 1) temperatura de retorno, zumbido # logar dados do sensor em databasedef logData (temp, hum):conn =sqlite3.connect (dbname) curs =conn.cursor () curs.execute ("INSERT INTO DHT_data values (datetime ('now'), (?), ( ?)) ", (temp, hum)) conn.commit () conn.close () # main functiondef main ():enquanto True:temp, hum =getDHTdata () logData (temp, hum) time.sleep (sampleFreq) # ------------ Execute o programa main ()
Ou obtê-lo em meu GitHub:logDHT.py. Execute-o no Terminal:
python3 logDHT.py
O que a função main () faz é:
Chame a função getDHTdata () , que retornará os dados capturados pelo sensor DHT22. Pegue esses dados (temperatura e umidade) e passe-os para outra função: logData (temp, hum) that insert them, together with actual date and time, to our table.And goes to sleep, waiting until the next scheduled time to capture data (defined by sampleFreq , which in this example is 1 minute).
Leave the Terminal window opened.
Until you kill the program with [Ctr+z], for example, the program will continuously capture data, feeding them in our database. I left it running for a while on a frequency of 1 minute for populating the database quicker, changing the frequency after few hours to 10 minutes.
There are other mechanisms much more efficient to perform this kind of “automatic logger” than using “time.sleep”, but the above code will work fine for our purpose here. Anyway, if you want to implement a better “scheduler”, you can use Crontab , which is a handy UNIX tool to schedule jobs. A good explanation of what Crontab is can be found in this tutorial: “Schedule Tasks on Linux Using Crontab”, by Kevin van Zonneveld.
Step 9:Queries
Now that our database is being fed automatically, we should find ways to work with all those data. We do it with queries!
What is a query?
One of the most important features of working with SQL language over databases is the ability to create “database queries”. In other words, queries extract data from a database formatting them in a readable form. A query must be written in SQL language , that uses a SELECT statement to select specific data.
We have in fact use it on a “broad way” on last step:“SELECT * FROM DHT_data”.
Examples:
Let’s create some queries over the data on the table that we have already created. For that, enter with below code:
import sqlite3conn=sqlite3.connect('sensorsData.db')curs=conn.cursor()maxTemp =27.6print ("\nEntire database contents:\n")for row in curs.execute("SELECT * FROM DHT_data"):print (row)print ("\nDatabase entries for a specific humidity value:\n")for row in curs.execute("SELECT * FROM DHT_data WHERE hum='29'"):print (row) print ("\nDatabase entries where the temperature is above 30oC:\n")for row in curs.execute("SELECT * FROM DHT_data WHERE temp>
30.0"):print (row) print ("\nDatabase entries where the temperature is above x:\n")for row in curs.execute("SELECT * FROM DHT_data WHERE temp>
(?)", (maxTemp,)):print (row)
Or get it from my GitHub: queryTableDHT.py, and run it on Terminal:
python3 queryTableDHT.py
You can see the result on the Terminal’s print screen above. Those are simple examples to give you an idea regarding queries. Take a time to understand the SQL statements in above code.
If you want to know more about SQL language, a good source is W3School SQL Tutorial.
Step 10:Last Data Entered on a Table:
A very important query is the one to retrieve the last data entered (or logged) on a table. We can do it directly on the SQLite shell, with the command:
sqlite> SELECT * FROM DHT_data ORDER BY timestamp DESC LIMIT 1;
Or running a simple python code as below:
import sqlite3conn =sqlite3.connect('sensorsData.db')curs=conn.cursor()print ("\nLast Data logged on database:\n")for row in curs.execute("SELECT * FROM DHT_data ORDER BY timestamp DESC LIMIT 1"):print (row)
You can see the result on the first Terminal print screen above.
Note that the result will appear as a “tuple of values”:(‘timestamp’, temp, hum).
The tuple returned the last row content of our table, which is formed with 3 elements on it:
- row[0] =timestamp [string]
- row[1] =temp [float]
- row[2] =hum [float]
So, we can work better our code, to retrieve “clean” data from the table, for example:
import sqlite3conn=sqlite3.connect('sensorsData.db')curs=conn.cursor()print ("\nLast raw Data logged on database:\n")for row in curs.execute("SELECT * FROM DHT_data ORDER BY timestamp DESC LIMIT 1"):print (str(row[0])+" ==> Temp ="+str(row[1])+" Hum ="+str(row[2]))
Open the file from my GitHub: lastLogDataTableDHT.py and run it on Terminal:
python3 lastLogDataTableDHT.py
You can see the result on the 2nd Terminal print screen above.
Step 11:A Web Front-end for Data Visualization
On my last tutorial: Python WebServer With Flask and Raspberry Pi, we learned how to implement a web-server (using Flask) to capture data from sensors and show their status on a web page.
This is what we also want to accomplish here. The difference is in the data to be sent to our front end, that will be taken from a database and not directly from sensors as we did on that tutorial.
Creating a web-server environment:
The first thing to do is to install Flask on your Raspberry Pi. If you do not have it, go to the Terminal and enter:
sudo apt-get install python3-flask
The best when you start a new project is to create a folder where to have your files organized. Por exemplo:
From home, go to our working directory:
cd Documents/Sensors_Database
Create a new folder, for example:
mkdir dhtWebServer
The above command will create a folder named “dhtWebServer”, where we will save our python scripts:
/home/pi/Documents/Sensor_Database/rpiWebServer
Now, on this folder, let’s create 2 sub-folders: static for CSS and eventually JavaScript files and templates for HTML files. Go to your newer created folder:
cd dhtWebServer
And create the 2 new sub-folders:
mkdir static
e
mkdir templates
The final directory “tree”, will look like:
├── Sensors_Database ├── sensorsData.db ├── logDHT.py ├── dhtWebSensor ├── templates └── static
We will leave our created database on /Sensor_Database directory, so you will need to connect SQLite with “../sensorsData.db”.
OK! With our environment in place let’s assemble the parts and create our Python WebServer Application . The above diagram gives us an idea of what should be done!
Step 12:The Python WebServer Application
Starting from the last diagram, let’s create a python WebServer using Flask. I suggest Geany as the IDE to be used, once you can work simultaneously with different types of files (.py, .html and .css).
The code below is the python script to be used on our first web-server:
from flask import Flask, render_template, requestapp =Flask(__name__)import sqlite3# Retrieve data from databasedef getData():conn=sqlite3.connect('../sensorsData.db') curs=conn.cursor() for row in curs.execute("SELECT * FROM DHT_data ORDER BY timestamp DESC LIMIT 1"):time =str(row[0]) temp =row[1] hum =row[2] conn.close() return time, temp, hum# main route @app.route("/")def index():time, temp, hum =getData() templateData ={ 'time':time, 'temp':temp, 'hum':hum } return render_template('index.html', **templateData)if __name__ =="__main__":app.run(host='0.0.0.0', port=80, debug=False)
You can get the python script appDhtWebServer.py from my GitHub. What the above code does is:
With this request, the first thing done in the code is to take data from the database using the function time, temp, hum =getData(). This function is basically the same query that was used before to retrieve a data stored in the table. With the data on hand, our script returnsto the webpage (index.html ): time , temp and hum as a response to the previous request.
- Every time that someone “clicks”‘on “/”, that is the main page (index.html) of our webpage a GET request is generated;
So, let’s see the index.html and style.css files that will be used to build our front-end:
index.html
DHT Sensor data DHT Sensor Data
TEMPERATURE ==> {{ tempLab }} oC
HUMIDITY (Rel.) ==> {{ humLab }} %
Last Sensors Reading:{{ time }} ==> REFRESH
@2018 Developed by MJRoBot.org