Estação meteorológica no Arduino de A a Z. Parte 5

O final. A parte anterior .


Sumário:



Sensor externo. De software


Fale sobre o software para o sensor no exterior. Depois disso, você obterá um sistema completo com o qual você já pode experimentar.


Deixe-me lembrá-lo de que o servidor é uma unidade doméstica central que pode se comunicar com a Internet via Wi-Fi e que o cliente é um sensor remoto e pronto para uso que transmite dados para o servidor pelo ar.


O código fonte do servidor e do cliente está aqui .
Os textos de origem são fornecidos com comentários detalhados.


Quase nada precisa ser configurado no cliente.


O transmissor de rádio nRF24L01 +, mais precisamente a biblioteca RadioHead, exige que os endereços do servidor e do cliente sejam especificados. Os endereços são fornecidos caso você tenha mais de um servidor e cliente. Um endereço é qualquer número inteiro. Quando um cliente envia um pacote de dados para um servidor, indica para qual servidor esse pacote se destina. O servidor, sabendo seu próprio endereço, por sua vez, determina se esse pacote é destinado a ele.


Portanto, SERVER_ADDRESS no servidor e no cliente deve ser o mesmo, mas CLIENT_ADDRESS para clientes diferentes deve ser diferente. Em outras palavras, se você conectar outro novo sensor ao nosso sistema no futuro, CLIENT_ADDRESS precisará ser alterado para ele.


 //     #define SERVER_ADDRESS 10 #define CLIENT_ADDRESS 20 //     !!! 

O RF_CHANNEL canal de rádio RF_CHANNEL deve ser o mesmo para todos. Por padrão, é 2. Alterei o número padrão, você pode escolher qualquer outro.


 //  .       #define RF_CHANNEL 73 

As configurações do voltímetro para medir a tensão de alimentação da bateria devem ser alteradas:


 //   ,   const float r1 = 100400; // 100K const float r2 = 9960; // 10K //      //    http://localhost/arduino-secret-true-voltmeter/ const float typVbg = 1.082; //    1.0 -- 1.2  

Para economizar energia, a biblioteca Lightweight low power para Arduino é usada .


Aqui estão minhas medições de consumo reais para o Arduino Pro Mini com esta lib:


  • geralmente 25mA
  • ao trabalhar com DHT o mesmo
  • com transmissão de rádio 38 mA
  • em LowPower.idle 15 mA
  • em LowPower.powerdown 7,5 mA

O cliente faz medições de temperatura, umidade e tensão de alimentação, agrupa tudo isso em uma estrutura de dados, envia os dados para o servidor e "adormece". Se ocorrerem erros durante a transferência, a transferência é repetida imediatamente.


O servidor (central, unidade doméstica), por sua vez, recebe dados, confirma a recepção e os processa.


Banco de dados, MySQL, PHP, servidor WWW


Após o trabalho, temos um design totalmente funcional da estação meteorológica. Mas agora há uma dúzia dessas estações meteorológicas, o artesanato local não está mais na moda. Afinal, temos uma Internet das coisas.


Portanto, falaremos sobre como o acesso a essa Internet é realizado, anexaremos um banco de dados e uma face da Web a nossa estação meteorológica.


A declaração do problema para a "webcam":


  • receber e armazenar dados da estação meteorológica: temperatura, umidade, pressão atmosférica, tensão de alimentação
  • exibir esses dados
  • construir gráficos.

Neste caso, precisamos de hospedagem com suporte para Apache, PHP e MySQL com o módulo mysqli. E essas condições são satisfeitas por quase qualquer hospedagem no planeta Terra. Ou, em vez de hospedar, o computador desempenhará o papel de servidor conectado a um roteador de rede doméstica e terá acesso à Internet.


Criação de banco de dados


Vamos começar desde o início, ou seja, com o design e a criação do banco de dados.


Os bancos de dados são o seu mundo e você pode estudá-lo por um longo período de tempo. Assim, tocaremos brevemente apenas no que precisamos diretamente.


Todos os scripts SQL estão no diretório weather-station/server/php-sql/


Onde o design do banco de dados começa? Com uma representação lógica e física.


Visualização lógica ou esquema do banco de dados:


  • Tabela de temperatura e umidade DHT
  • tabela de dados BMP do sensor de pressão e temperatura
  • as tabelas indicadas não têm nenhum relacionamento entre si; mais precisamente, os links não são necessários.

O esquema físico se baseia em um DBMS e tipos de dados específicos. É mais fácil desmontar com um exemplo específico. O script SQL make_tables.sql os esquemas lógicos e físicos.


Cada tabela deve ter um campo de tipo


 id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT 

O nome do campo pode diferir em bancos de dados diferentes, mas há apenas um sentido - este é um identificador exclusivo, uma chave de registro. Para o futuro, se você vir um banco de dados em tabelas que não possuem esse contador, saiba que esse banco de dados foi projetado por uma pessoa muito longe da programação, provavelmente de humanidades.


Armazenamos os dados dos sensores do mesmo tipo em uma tabela; para sensores de outro tipo, criamos outra tabela. Isso complica um pouco o banco de dados e a ligação do PHP a ele, mas simplifica a extensão ou modificação de todo o sistema no futuro.


Existem duas tabelas em nosso projeto. A tabela arduino_dht armazena dados do (s) sensor (es) do tipo DHT (temperatura, umidade), a tabela arduino_bmp armazena dados do (s) sensor (es) do tipo BMP (temperatura, pressão). Se no futuro você quiser ter, por exemplo, um sensor de gás ou um detector de movimento, crie tabelas adicionais, não seja preguiçoso. Se você conectar outro sensor do tipo DHT11 ou DHT22, não precisará criar uma tabela adicional, use a tabela arduino_dht . Espero que o princípio seja claro: uma entidade física separada é uma tabela separada.


Se os dados de vários sensores do mesmo tipo serão armazenados em uma tabela, como eles podem ser distinguidos? Para fazer isso, insira um campo em cada tabela


 idSensor INTEGER 

De fato, é CLIENT_ADDRESS que registramos no client/client.ino para cada instância do cliente-cliente remoto e em server/server.ino para o sensor conectado diretamente ao servidor - a unidade central.


Nos sistemas industriais, deve haver outra tabela - a correspondência do idSensor e sua descrição verbal legível por humanos. Por exemplo, um sensor com idSensor = 2 é "Temperatura, umidade no apartamento" , etc. Mas em nosso projeto não vamos complicar, basta lembrar que:


  • um sensor com idSensor , é CLIENT_ADDRESS , igual a 11 - este é o sensor doméstico no servidor - a unidade central,
  • um sensor com idSensor , também é CLIENT_ADDRESS , igual a 20 - este é o primeiro (em nosso projeto e o único) cliente de sensor baseado em janela.

Próximo. As tabelas armazenam os seguintes dados:


  • ipRemote - endereço IP da estação meteorológica (servidor) da qual os dados vieram, útil para depuração e monitoramento,
  • dateCreate - data em que o registro foi criado,
  • millis - útil para depuração, é o tempo em milissegundos desde o início do esboço no Arduino,
  • temperatura - temperatura
  • umidade - umidade
  • tensão - tensão de alimentação,
  • pressão - pressão
  • erros - o número de erros (não usado). O objetivo era armazenar o número de erros de transmissão, etc., para que você pudesse avaliar remotamente o estado de todo o sistema.

Como você pode ver, as arduino_bmp e arduino_bmp muito semelhantes, a diferença está apenas nos campos de pressão e umidade, e há um desejo de despejar tudo em uma pilha (tabela). Mas a primeira forma normal não ordena fazer isso, muitos programadores iniciantes tentaram contorná-la, mas nenhum deles conseguiu, e nós não. É assim que não se deve observar a lei da gravitação universal, por enquanto ela pode resultar.


A tabela arduino_error_log útil para depuração - é um log de erros e outras mensagens do sistema.


A criação de um banco de dados e de seu usuário com direitos é descrita em make_db.sql


 -- .  config.php --   CREATE DATABASE IF NOT EXISTS db_weather; --   CREATE USER 'u_weather'@'localhost' IDENTIFIED BY '***PASSWORD***'; GRANT ALL ON db_weather.* TO 'u_weather'@'localhost'; 

Isso é feito uma vez, o nome do banco de dados e o nome de usuário podem aparecer com os seus. E o que exatamente precisa ser feito é definir sua senha.


PHP e servidor web


Todas as configurações da interface da web são armazenadas em config.php . Modifique-o de acordo com as configurações do seu banco de dados.


Defina seu fuso horário no formato PHP


 date_default_timezone_set('Europe/Prague'); 

Todos os fusos horários disponíveis são descritos aqui .


Defina sua chave secreta para acesso (como um número) que deve corresponder à constante SOURCE_KEY no SOURCE_KEY de server.ino


 $access_key = '***KEY***'; 

No nosso servidor web, não há autorização, entrada de senha, isso complicaria todo o design. Para um protótipo, isso não é necessário. Portanto, toda a proteção é construída no arquivo robots.txt , na ausência de index.php e nessa chave secreta de acesso.


O principal script PHP weather.php aceita uma solicitação HTTP GET simples com dados e a armazena nas tabelas correspondentes do banco de dados. Se a chave $access_key não corresponder, a solicitação será rejeitada.


O weather-view.php usado para exibir tabelas de dados e contém links para outros scripts de interface da web. Ligue para ele assim


 http:// / /weather-view.php?k= access_key 

Por exemplo


 http://yourhost/iot/weather-view.php?k=12345 

weather-view.php exibe rótulos simples, nos quais é necessário lembrar que:


  • o sensor com o ID 11 é o sensor doméstico no servidor,
  • O sensor com id 20 é um sensor de janela.

O script function.php contém funções comuns a todos os scripts PHP.


O chart-dht.php é responsável pelos gráficos usando o Google Charts . Aqui, por exemplo, está um gráfico da tensão de alimentação do sensor no exterior. A tensão aumenta em um dia ensolarado devido à bateria solar e, em seguida, a fonte de alimentação das baterias é descarregada gradualmente.


Graph


export-dht.php exporta dados das tabelas do banco de dados MySQL para um arquivo CSV. Para importação e análise adicionais em planilhas.


export-voltage.php exporta dados sobre a tensão de alimentação do sensor de janela do banco de dados MySQL para um arquivo CSV. Útil para depuração.


truncate.php limpa todas as tabelas, ou seja, exclui todos os nossos dados. Útil para depuração. Como não há links para este script no weather-view.php , é necessário chamá-lo através de um link direto na barra de endereços do seu navegador com $access_key .


Ao receber dados, a função mysqli_real_escape_string() é comumente usada para impedir que valores incorretos entrem no banco de dados.


Não se esqueça de colocar o robots.txt na raiz do seu site para impedir que ele entre nos mecanismos de pesquisa.


ESP8266, WiFi e transferência de dados


E agora, de volta ao esboço server.ino , à parte dele que se conecta ao ponto de acesso WiFi e envia dados ao servidor web.


Como já escrevi, não consegui encontrar uma biblioteca normal para o Arduino controlar o módulo ESP8266 usando os comandos AT, tive que "fazer uma fazenda coletiva". Deixe-me lembrá-lo também de que você precisa atualizar uma versão específica do firmware no ESP8266-01. E agora, quando tudo estiver pronto, vamos ver como funciona.


Para acessar o servidor da web no esboço server.ino , server.ino necessário alterar essas constantes


 const String DEST_HOST = " "; //  habr.com const String DEST_PORT = " "; //  80 const String DEST_URL = "/ /weather.php"; const String SOURCE_KEY= "  "; //    $access_key  config.php 

No server.ino na função void setup() , o ESP8266 é primeiro alternado para o modo Station, ou seja, começa a trabalhar como cliente WiFi


 espSendCmd(«AT+CWMODE_CUR=1», «OK», 3000); 

e segue a conexão com o ponto de acesso


 espState = espConnectToWiFi(); 

Se a conexão não ocorrer, a tentativa será repetida (uma vez)


 if ( espState != ESP_SUCCESS ) { delay(5000); Serial.println("WiFi not connected! Try again ..."); espConnectToWiFi(); } 

Em seguida, selecione o modo de conexão única TCP / IP


 espSendCmd("AT+CIPMUX=0", "OK", 2000); 

Ao enviar dados de sensores do tipo DHT para o servidor da web, é usada uma função que indica o type=dht dados como type=dht


 espSendData( "type=dht&t=" + String(dhtData.temperature) + "&h=" + String(dhtData.humidity) + "&v=" + String(dhtData.voltage) + "&s=" + String(CLIENT_ADDRESS) ); 

Ao enviar dados de sensores BMP para um servidor da web, a mesma função é usada com o tipo de dados indicado como type=bmp


 espSendData( "type=bmp&t=" + String(temperature_bmp) + "&p=" + String(pressure_bmp) + "&s=" + String(CLIENT_ADDRESS) ); 

A função espSendData() aceita uma string de solicitação HTTP GET e a envia para o servidor da Web conforme pretendido.


Dentro de si, espSendData() verifica a disponibilidade do módulo ESP enviando o comando “AT”, depois verifica a conexão WiFi e reconecta, se necessário. Em seguida, os dados são enviados e a conexão TCP é fechada.


Aplicativo para Android


Atualmente, quando todos podem piscar um LED, você não surpreende ninguém com uma estação meteorológica. Mas se o ofício sabe como se comunicar com o servidor via Wi-Fi, possui uma face da Web e um aplicativo móvel, isso é algo! Por servidor aqui, queremos dizer, é claro, o servidor de aplicativos, ou seja, no nosso caso, isso é uma ligação PHP e um MySQL DBMS. Não há cerejas suficientes no bolo, a saber, o aplicativo Android, que escreveremos agora.


Arquitetura


A arquitetura de toda a plataforma de software das estações meteorológicas é simples:


  • a parte do servidor (unidade central) da estação meteorológica coleta dados de clientes de sensores remotos
  • depois transfere dados para o servidor web de aplicativos, que salva esses dados no banco de dados
  • O aplicativo Android (ou qualquer outro aplicativo remoto: iOS, navegador) solicita dados de um servidor da Web e os exibe na tela.

Na tela do dispositivo Android, exibiremos as leituras atuais e mais recentes dos sensores.


HTTP GET e JSON


A questão que precisa ser resolvida em primeiro lugar é como os dados serão transferidos do servidor da Web para o aplicativo Android.


Não há necessidade de inventar nada aqui, tudo já foi inventado para nós - estes são HTTP GET e JSON.


No nosso caso, uma simples solicitação GET para o servidor da Web pode ser compilada e depurada manualmente enquanto o aplicativo Android ainda não está pronto.


Em Java e Android, existem bibliotecas prontas para o processamento de dados no formato JSON. JSON é um formato de texto legível por humanos, útil para depuração.


Para gerar dados atuais dos sensores das estações meteorológicas, crie um novo script PHP last-data-to-json.php no servidor da web.


Chamada de script:


 http://<>/last-data-to-json.php?k=<access_key> 

onde <access_key> , como lembramos, é a chave de acesso secreta ao banco de dados.


Exemplo de resposta no formato JSON:


 { "DHT 11":{ "idSensor":"11", "dateCreate":"2016-04-20 18:06:03", "temperature":"19", "humidity":"26", "voltage":"5.01" }, "DHT 20":{ "idSensor":"20", "dateCreate":"2016-04-18 07:36:26", "temperature":"10", "humidity":"26", "voltage":"3.7" }, "BMP 11":{ "idSensor":"11", "dateCreate":"2016-04-20 18:06:22", "temperature":"19", "pressure":"987.97" } } 

Deve-se lembrar que temos 3 sensores. Seu ID e tipo (DHT ou BMP) são codificados em todo o código da estação meteorológica. Esse método de codificação hardcore é ideologicamente incorreto, mas para um protótipo de joelho (onde é necessária uma solução rápida e fácil), esse é um compromisso razoável.


 $idSensor = 11; //  DHT  $idSensor = 11; //  BMP  $idSensor = 20; //  DHT  

O last-data-to-json.php pega os dados mais recentes desses sensores heterogêneos do banco de dados e os compacta no formato JSON. A seleção dos dados do banco de dados "do final" prossegue desta maneira:


 SELECT <> FROM <> ORDER BY id DESC LIMIT 1; 

Android


Agora, escreveremos um aplicativo Android simples que solicita, recebe, decodifica dados JSON e exibe informações na tela.


Nosso aplicativo para Android será o mais simples possível, apenas a essência da tecnologia. Mais adiante neste "esqueleto" já será possível terminar várias "belezas".


Aqui está uma captura de tela do que deve resultar em


Android


Como você pode ver, a interface do usuário é apenas espartana, baseada em LinearLayout, nada mais.


No topo do TextView, mostra o ID dos sensores e seus dados meteorológicos. O botão Atualizar inicia uma segunda solicitação para o servidor web. A seguir, no EditText, é a única configuração do programa - este é o URL da solicitação no formulário


 http://< >/last-data-to-json.php?k=<access_key> 

O que deve ser observado?


No manifesto, adicione linhas permitindo a Internet e verificando o status da conexão de rede:


 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> 

Trabalhar com a rede e receber dados do site é o seguinte.


Usamos o AsyncTask para criar uma tarefa em segundo plano separada do thread principal da interface do usuário. Essa tarefa em segundo plano pega a URL da solicitação e a usa para criar a HttpURLConnection .


Depois que a conexão é estabelecida, o AsyncTask carrega o conteúdo da página da web (JSON) como um InputStream. Em seguida, o InputStream é convertido em uma sequência, que é decodificada usando JSONObject e exibida na interface do usuário usando o método onPostExecute() .


Em MainActivity.java, altere o URL para:


 private static final String defUrl = "http://host/dir/last-data-to-json.php?k=< >"; 

ele será usado por padrão na primeira vez que você executar um aplicativo Android.


Epílogo


Bem, algo já funcionou. Então você pode otimizar algo, substituir algo, você pode jogar tudo fora, mas também emprestar algo.


Um grande ponto dolorido separado é o consumo de energia . Eu recomendo a leitura de comentários em postagens onde existem muitas dicas práticas.


Para o infinito ... e além.

Source: https://habr.com/ru/post/pt426019/


All Articles