Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

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

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:

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)

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)

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):

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:

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:

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 ): timetemp  and hum  as a response to the previous request.

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


You can get the file index.html from my GitHub.

style.css
body{ background:blue; color:yellow; padding:1%}.button { font:bold 15px Arial; text-decoration:none; background-color:#EEEEEE; color:#333333; padding:2px 6px 2px 6px; border-top:1px solid #CCCCCC; border-right:1px solid #333333; border-bottom:1px solid #333333; border-left:1px solid #CCCCCC;} 

You can get the file style.css from my GitHub. The files must be placed in your directory like this:
├── Sensors_Database ├── sensorsData.db ├── logDHT.py ├── dhtWebSensor ├── appDhtWebSensor.py ├── templates │ ├── index.html └── static ├── style.css  

Now, run the python script on the Terminal:
sudo python3 appDhtWebServer.py 

Go to any browser in your network and enter with http://YOUR_RPI_IP  (for example, in my case: http://10.0.1.27)

The above print screen shows what you must see. NOTE:If you are not sure about your RPi Ip address, run on your terminal:
 ifconfig 

At wlan0:section you will find it. In my case:10.0.1.27

Step 13:Making Our Web Front-End Fancier!


Let’s introduce some Gages to present actual Temperature and Humidity values on a better way. Note that our Python script will not change, but using JustGage on our html/css files, will improve a lot how data will be presented.

What is JustGage?

JustGage is a handy JavaScript plugin for generating and animating nice &clean gauges. It is based on Raphaël library for vector drawing, so it’s completely resolution independent and self-adjusting, working in almost any browser.

Installation:

justgage-1.2.2.zip
 DHT Data Sensor    

DHT Sensor Data


Last Sensors Reading:{{ time }} ==> REFRESH


@2018 Developed by MJRoBot.org


Download from my GitHub the file: index_gage.html, and rename it as index.html (do not forget of renaming the previous one with a different name if you want to keep it, for example, index_txt.html).

The final directory tree should look like as below:
├── Sensors_Database ├── sensorsData.db ├── logDHT.py ├── dhtWebServer ├── appDhtWebServer.py ├── templates │ ├── index.html └── static ├── style.css ├── justgage.js ├── raphael-2.1.4.min.js 

Press[Crl-C] on your Terminal to Quit appDhtWebServer.py and just start it again. When you refresh your browser you must see the above print screen.

Look the examples files that you downloaded from JustGage website. Try make changes on your gages. It is very simple.

Step 14:The Full Proccess


The above diagram resumes what we have accomplished so far:2 separate scripts running in parallel, doing their tasks independently:

Capturing data with sensor and load them into a database (logDHT.py )Look for data on the database and present them on a web front-end (appDhtWebServer.py )

In general terms our project of capture data, saving them on a database and displaying those data on a webpage is finish. But make no sense to have a database with historical data and only use it for the last data captured. We must play with historical data and the most basic thing to do is presented them on a graph. Let’s go to it!

Step 15:Graphing the Historical Data


A very good library to graph data is Matplotlib that is a Python 2D plotting library which produces publication quality figures in a variety of hardcopy formats and interactive environments across platforms.

To install matplotlib , run the command below on your Terminal:
sudo apt-get install python3-matplotlib 

Before we start, let’s create a new environment, where we will save the new application to be developed: appDhtWebHist.py  and its correspondent index.html  and style.css
├── Sensors_Database ├── sensorsData.db ├── logDHT.py ├── dhtWebHist ├── appDhtWebHist.py ├── templates │ ├── index.html └── static ├── style.css  

Create the new 3 directories (dhtWebHist; /templates  and /static ) same as we did before and open from my GitHub the 3 files below:

1. appDhtWebHist.py
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvasfrom matplotlib.figure import Figureimport iofrom flask import Flask, render_template, send_file, make_response, requestapp =Flask(__name__)import sqlite3conn=sqlite3.connect('../sensorsData.db')curs=conn.cursor()# Retrieve LAST data from databasedef getLastData():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, humdef getHistData (numSamples):curs.execute("SELECT * FROM DHT_data ORDER BY timestamp DESC LIMIT "+str(numSamples)) data =curs.fetchall() dates =[] temps =[] hums =[] for row in reversed(data):dates.append(row[0]) temps.append(row[1]) hums.append(row[2]) return dates, temps, humsdef maxRowsTable():for row in curs.execute("select COUNT(temp) from DHT_data"):maxNumberRows=row[0] return maxNumberRows# define and initialize global variablesglobal numSamplesnumSamples =maxRowsTable() if (numSamples> 101):numSamples =100# main [email protected]("/")def index():time, temp, hum =getLastData() templateData ={ 'time' :time, 'temp' :temp, 'hum' :hum, 'numSamples' :numSamples } return render_template('index.html', **templateData)@app.route('/', methods=['POST'])def my_form_post():global numSamples numSamples =int (request.form['numSamples']) numMaxSamples =maxRowsTable() if (numSamples> numMaxSamples):numSamples =(numMaxSamples-1) time, temp, hum =getLastData() templateData ={ 'time' :time, 'temp' :temp, 'hum' :hum, 'numSamples' :numSamples } return render_template('index.html', **templateData)@app.route('/plot/temp')def plot_temp():times, temps, hums =getHistData(numSamples) ys =temps fig =Figure() axis =fig.add_subplot(1, 1, 1) axis.set_title("Temperature [°C]") axis.set_xlabel("Samples") axis.grid(True) xs =range(numSamples) axis.plot(xs, ys) canvas =FigureCanvas(fig) output =io.BytesIO() canvas.print_png(output) response =make_response(output.getvalue()) response.mimetype ='image/png' return [email protected]('/plot/hum')def plot_hum():times, temps, hums =getHistData(numSamples) ys =hums fig =Figure() axis =fig.add_subplot(1, 1, 1) axis.set_title("Humidity [%]") axis.set_xlabel("Samples") axis.grid(True) xs =range(numSamples) axis.plot(xs, ys) canvas =FigureCanvas(fig) output =io.BytesIO() canvas.print_png(output) response =make_response(output.getvalue()) response.mimetype ='image/png' return responseif __name__ =="__main__":app.run(host='0.0.0.0', port=80, debug=False) 

A new function was created here: getHistData (numSamples) , that receives as a parameter the number of rows that should be taken from the database. Basically, it is very similar to getLastData(), where numSamples  was “1”. Of course, now we must “append” the return array for all required rows.

In fact, we could use only this last function for both tasks.

The number of samples is set by default as 100, at the beginning (if there are more them 100 rows into the database) and also received it as an input from the webpage, during normal operation. When we receive the number of samples to be retrieved, we must also check if it is lower than the maximum number of rows in the database (otherwise we will get an error). The function maxRowsTable() , returns this number.

With the historical data in hand: times, temps  and hums  that are arrays, we must build the graphs saving them as a .png ímage . Those images will be the return for the routes:

@app.route(‘/plot/temp’)  and @app.route(‘/plot/hum’).

The request for the images is done by index.html, by the IMG TAG.

2. index.html
  DHT Sensor data     

DHT Sensor Data

TEMPERATURE ==> {{ temp }} oC

HUMIDITY (Rel.) ==> {{ hum }} %


Last Sensors Reading:{{ time }} ==> REFRESH


HISTORICAL DATA

Enter number of samples to retrieve:


Image Placeholder Image Placeholder

@2018 Developed by MJRoBot.org


3. style.css
body{ background:blue; color:yellow; padding:1%}.button { font:bold 15px Arial; text-decoration:none; background-color:#EEEEEE; color:#333333; padding:2px 6px 2px 6px; border-top:1px solid #CCCCCC; border-right:1px solid #333333; border-bottom:1px solid #333333; border-left:1px solid #CCCCCC;}img{ display:display:inline-block} 

The above print screen shows the result.

Step 16:Including Gage on History Webpage


If instead of text, you want also to include gages to display the actual data, you must have the 2 .js files that you have used before on /static and change the index.html file on /templates:

Below how the directory tree looks like:
├── Sensors_Database ├── sensorsData.db ├── logDHT.py ├── dhtWebHist ├── appDhtWebHist.py ├── templates │ ├── index.html └── static ├── style.css ├── justgage.js ├── raphael-2.1.4.min.js 

From my GitHub, open index_gage.html and rename it index.html. Replace the actual index.html (text version) and voilá! You will get a beautiful webpage, showing as gages the last captured data of temperature and humidity by the DHT22 and the historical graphs of those data.

Press[Crl-C] on your Terminal to Quit appDhtWebServer.py and just start it again. When you refresh your browser you must see the above print screen.

Step 17:Retrieving Data by Time Instead of Samples


So far we have build our graphics based on historical data, sending as a input parameter the numbers of samples to be retrieved from our database. Alternatively we could use as a parameter the number of past minutes that we want to show on a graph.

In order to do that, the first thing to know is the frequency of logged data on our database. Remember that this task is done for an independent program (in our case, logDHT.py ) One simple way to finfd this frequency is to retrieve the last 2 data logged on database and subtracting their correspondent timeStamp data:

in general terms: frequency =timeStamp(1) – timeStamp(0)

The function below does the work for us, using “datetime.striptime()”:
# Get sample frequency in minutesdef freqSample():times, temps, hums =getHistData (2) fmt ='%Y-%m-%d %H:%M:%S' tstamp0 =datetime.strptime(times[0], fmt) tstamp1 =datetime.strptime(times[1], fmt) freq =tstamp1-tstamp0 freq =int(round(freq.total_seconds()/60)) return (freq) 

Once we we have this frequency parameter in minutes, we will show it on index.html and asking for a “rangeTime” number of minutes to be send back to our server ==> @app.route(‘/’, methods=[‘POST’]):
@app.route('/', methods=['POST'])def my_form_post():global numSamples global freqSamples global rangeTime rangeTime =int (request.form['rangeTime']) if (rangeTime  numMaxSamples):numSamples =(numMaxSamples-1) 

The picture shows the result:

Eliminating Possible errors when constructing the graphs:

Ocasionally, strange (or corrupted) data can be storaged on database, jeopardizing our analysis. Those data can be verified (or cleaneed) on several places (like during the time sensor are capturing the data, etc). But once the script that display data is independent of the one that logged the data, let’s “cap” the maximum and minimum values of our sensors, before using the data to buit the graphs. This can be achieved with the function testData(temps, hums) :
# Test data for cleanning possible "out of range" valuesdef testeData(temps, hums):n =len(temps) for i in range(0, n-1):if (temps[i] <-10 or temps[i]>50):temps[i] =temps[i-2] if (hums[i] <0 or hums[i]>100):hums[i] =temps[i-2] return temps, hums 

The scripts for this new version can be download from my GitHub: dhtWebHist_v2

Step 18:Conclusion


As always, I hope this project can help others find their way into the exciting world of electronics!

For details and final code, please visit my GitHub depository: RPI-Flask-SQLite

For more projects, please visit my blog: MJRoBot.org

Saludos from the south of the world!

See you at my next tutorial!

Obrigada,



Source:From Data to Graph:A Web Journey With Flask and SQLite

Processo de manufatura

  1. Armazene e gerencie dados confidenciais com o gerenciador secreto
  2. Microchip:ADCs de 24 e 16 bits com taxas de dados de até 153,6 kSPS
  3. Monitor de temperatura e umidade da casa (ambiente) com gráfico baseado na web - Raspberry Pi
  4. Construindo robôs com Raspberry Pi e Python
  5. Acelerando a transformação digital com IoT Data, graças à Cisco e IBM
  6. Aproveitamento de dados IoT da borda para a nuvem e vice-versa
  7. A jornada IIoT começa com telemetria remota
  8. Como tornar IOT real com Tech Data e IBM Parte 2
  9. Como tornar a IoT real com Tech Data e IBM Parte 1
  10. Gerando resultados de negócios com projetos de Big Data e IA