Esse site utiliza cookies
Nós armazenamos dados temporariamente para melhorar a sua experiência de navegação e recomendar conteúdo do seu interesse.
Ao utilizar os nossos serviços, você concorda com as nossas políticas de privacidade.
Esse site utiliza cookies
Nós armazenamos dados temporariamente para melhorar a sua experiência de navegação e recomendar conteúdo do seu interesse.
Ao utilizar os nossos serviços, você concorda com as nossas políticas de privacidade.
Postado em 15 outubro 2022
Atualizado em 15 outubro 2022
Projeto de comunicação entre dispositivos usando o protocolo MQTT. Esse projeto irá usar o sensor DHT11 para medir a umidade e a temperatura do ambiente. Os dados coletados pelo sensor serão enviados pelo NodeMCU ESP8266 através do protocolo citado e serão recebidos por um Raspberrypi que irá registrar esses dados no banco de dados local.
O acesso e visualização aos dados serão proporcionados pelo raspberrypi que terá a porta 80 aberta para tais fins.
Os seguintes hardwares serão utilizados:
Dispositivo | Papel |
---|---|
Raspberrypi 4 | Receptor e disponibilizador de dados |
ESP8266 | Responsável pelo envio dos dados |
DHT11 | Sensor de temperatura e umidade |
LED | Apaga quando não há energia fluindo através do circuito |
Fonte de energia | Fornece eletricidade |
Cabos e resistores | Circuito |
A conexão entre os dispositivos irá ser realizada da seguinte forma:
Principais hardwares:
O circuito será estabelecido da seguinte forma:
O ESP8266 será programado em linguagem C, enquanto o programa no raspberrypi será programado com o PHP. Para facilitar e economizar tempo de configuração, o programa receptor irá ser desenvolvido com o Laravel.
Linguagem | Papel |
---|---|
Linguagem C | Programação ESP8266 |
PHP | Programação software raspberrypi |
Javascript | Frontend |
Laravel | Auxiliar a programação no PHP |
Apache | Servidor raspberrypi |
MariaDB | Banco de dados raspberrypi |
Composer | Gerenciamento de pacotes PHP |
Node.js | Requerimento para NPM |
npm | Gerenciamento de pacotes Javascript |
O sistema operacional utilizado no raspberry é o citado abaixo:
pipipi@raspberrypi:~ $cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 11 (bullseye)"
NAME="Raspbian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=raspbian
ID_LIKE=debian
As versões das ferramentas utilizadas nesse projeto são:
pipipi@raspberrypi:~ $ apachectl -v
Server version: Apache/2.4.54 (Raspbian)
Server built: 2022-06-09T04:26:43
pipipi@raspberrypi:~ $ php --version
PHP 8.1.11 (cli) (built: Sep 29 2022 22:17:15) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.11, Copyright (c) Zend Technologies
with Zend OPcache v8.1.11, Copyright (c), by Zend Technologies
pipipi@raspberrypi:~ $ mysql --version
mysql Ver 15.1 Distrib 10.5.15-MariaDB, for debian-linux-gnueabihf (armv8l) using EditLine wrapper
pipipi@raspberrypi:~ $ composer --version
Composer version 2.4.2 2022-09-14 16:11:15
pipipi@raspberrypi:~ $ node -v
v14.20.1
pipipi@raspberrypi:~ $ npm -v
6.14.17
O ESP8266 irá conectar-se a internet e publicará os dados coletados no canal do MQTT em formato JSON.
O protocolo MQTT permite o envio e o recebimento de dados através de um canal.
Nesse projeto, o ESP8266 enviará dados para o “MainChannel”. O aplicativo do raspberrypi irá se inscrever nesse canal para receber os dados.
O ESP8266 utilizará as seguintes bibliotecas:
Importação de bibliotecas, definição e inicialização de variáveis:
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <dht.h>
#include <ArduinoJson.h>
#define DHT11_PIN D1
IPAddress local_IP(192, 168, 11, 184);
IPAddress gateway(192, 168, 11, 1);
IPAddress subnet(255, 255, 255, 0);
dht DHT;
const char* ssid = "xxxxxxxxxx";
const char* password = "xxxxxxxxxx";
const char* mqtt_server = "broker.emqx.io";
WiFiClient espClient;
PubSubClient client(espClient);
O ESP8266 irá continuar tentando se conectar com a internet até ter sucesso. O endereço IP do ESP8266 na rede interna será fixo.
void setupWifi() {
delay(100);
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) {
delay(500);
}
randomSeed(micros());
if (!WiFi.config(local_IP, gateway, subnet)) {
Serial.println("STA Failed to configure");
}
}
Os dados captados pelo DHT11 serão obtidos através do pino definido acima. Esses dados serão armazenados em estrutura JSON, sendo enviados através do canal “MainChannel”.
void sendMQTTMsg() {
if (!client.connected()) {
reconnect();
}
client.loop();
int chk = DHT.read11(DHT11_PIN);
StaticJsonDocument<256> staticJsonDocument;
staticJsonDocument["temperature"] = DHT.temperature;
staticJsonDocument["humidity"] = DHT.humidity;
char requestBody[128];
serializeJson(staticJsonDocument, requestBody);
client.publish("MainChannel", requestBody);
}
Uma vez que as funções necessárias já foram definidas, serão invocadas.
void setup() {
Serial.begin(115200);
setupWifi();
sendMQTTMsg();
delay(10000);
ESP.deepSleep(30e6);
}
void loop() {}
O ESP8266 irá entrar em modo de hibernação após enviar os dados a cada 30 segundos para economizar energia.
O Raspberrypi irá ser o receptor de dados. Responsável por armazenar e exibir os dados recebidos.
Criação de projeto usando composer.
composer create-project laravel/laravel mqtt-web-server
Instalação de repositório para as comunicações em MQTT. Repositório MQTT dedicado ao laravel será instalado.
composer require php-mqtt/laravel-client
php artisan vendor:publish --provider="PhpMqtt\Client\MqttClientServiceProvider" --tag="config"
Após publicar o arquivo de configuração acima, trocamos a conexão do MQTT para conexão privada.
// config/mqtt-client.php
...
return [
'default_connection' => 'private',
'connections' => [
'private' => [
'host' => "broker.emqx.io",
'port' => 1883,
],
...
]
]
Os dados irão ser gravados no banco de dados e serão também usados para visualização. Por isso criaremos todos os arquivos necessários com um comando:
php artisan model:Message -a
O comando acima irá criar os seguintes arquivos:
Apenas uma porção desses arquivos serão utilizados.
A estrutura da tabela do banco de dados será bastante simples, possuindo apenas 5 colunas.
// arquivo migration
public function up()
{
Schema::create('messages', function (Blueprint $table) {
$table->id();
$table->string('topic');
$table->text('content');
$table->timestamps();
});
}
Uma vez que as configurações de conexão do MQTT foram estabelecidas, criamos um comando no laravel.
php artisan make:command ReceiveMessage
Com o comando acima, um arquivo será criado. Nesse arquivo, adicionamos o algoritmo para receber mensagens do ESP8266 através do canal “MainChannel”.
public function handle()
{
$mqtt = MQTT::connection();
$mqtt->subscribe('MainChannel', function (string $topic, string $message) use ($mqtt) {
$message = Message::create([
'topic' => $topic,
'content' => $message
]);
if ($message->exists()) {
$mqtt->interrupt();
}
}, 1);
$mqtt->loop(true);
$mqtt->disconnect();
return Command::SUCCESS;
}
Certificamos se o registro foi adicionado ao banco de dados e interrompemos a conexão.
Laravel fornece ferramentas para invocar diversos comandos em certos períodos de tempo.
Adicionaremos o comando criado acima e invocaremos a cada minuto.
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->command('receive:message')->everyMinute();
}
Em seguida, testaremos usando o seguinte comando:
php artisan schedule:work
Caso não haja problemas, será exibido logs contendo informação de sucesso.
2022-10-08 23:00:00 Running ['artisan' receive:message] ...... 27,718ms DONE
⇂ '/usr/bin/php8.1' 'artisan' receive:message > '/dev/null' 2>&1
Queremos que o comando acima seja invocado automaticamente sem a nossa intervenção. Para fazer isso, utilizaremos o supervisor.
Para instalar o supervisor é necessário acessar o raspberry. Aqui, o acesso será feito remotamente usando o SSH.
ssh [email protected]
Instalação do supervisor:
sudo apt-get install supervisor
Após a instalação do supervisor, a configuração da conexão em plano de fundo será realizada. O supervisor irá executar o task scheduling do laravel em segundo plano.
[program:mqtt]
process_name=%(program_name)s_%(process_num)02d
directory=/var/www/html/mqtt-web-server
command=php artisan schedule:work
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=pipipi
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/html/mqtt-web-server/storage/logs/worker.log
stopwaitsecs=3600
Uma vez que a configuração foi efetuada, atualizamos e executamos o supervisor.
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start mqtt:*
O resultado do comando será gravado no arquivo definido no arquivo de configuração do supervisor.
Após algum tempo, se verificarmos o banco de dados teremos os registros abaixo:
MariaDB [mqtt]> select * from messages order by created_at desc limit 10;
+------+-------------+----------------------------------+---------------------+---------------------+
| id | topic | content | created_at | updated_at |
+------+-------------+----------------------------------+---------------------+---------------------+
| 1554 | MainChannel | {"temperature":20,"humidity":70} | 2022-10-09 19:39:39 | 2022-10-09 19:39:39 |
| 1553 | MainChannel | {"temperature":20,"humidity":70} | 2022-10-09 19:38:10 | 2022-10-09 19:38:10 |
| 1552 | MainChannel | {"temperature":20,"humidity":70} | 2022-10-09 19:37:26 | 2022-10-09 19:37:26 |
| 1551 | MainChannel | {"temperature":21,"humidity":71} | 2022-10-09 19:36:42 | 2022-10-09 19:36:42 |
| 1550 | MainChannel | {"temperature":21,"humidity":71} | 2022-10-09 19:35:13 | 2022-10-09 19:35:13 |
| 1549 | MainChannel | {"temperature":21,"humidity":71} | 2022-10-09 19:34:29 | 2022-10-09 19:34:29 |
| 1548 | MainChannel | {"temperature":21,"humidity":71} | 2022-10-09 19:33:45 | 2022-10-09 19:33:45 |
| 1547 | MainChannel | {"temperature":21,"humidity":71} | 2022-10-09 19:32:16 | 2022-10-09 19:32:16 |
| 1546 | MainChannel | {"temperature":21,"humidity":71} | 2022-10-09 19:31:32 | 2022-10-09 19:31:32 |
| 1545 | MainChannel | {"temperature":21,"humidity":71} | 2022-10-09 19:30:03 | 2022-10-09 19:30:03 |
+------+-------------+----------------------------------+---------------------+---------------------+
10 rows in set (0.006 sec)
Se olharmos a data do registro é possível perceber que cada registro tem um valor com intervalo de 1 minuto de diferença.
A comunicação em MQTT termina aqui.
Após certificar-se de que os dados estão sendo registrados a cada minuto, vamos criar uma interface básica para a visualização da informação.
Para agilizar, usaremos as seguintes extensões:
A instalação do bootstrap será feita com os comandos abaixo:
composer require laravel/ui
php artisan ui bootstrap
O segundo comando instala a versão mais recente do bootstrap disponível.
O chart.js é uma biblioteca baseada javascript. Biblioteca excelente para a exibição de gráficos.
npm install chart.js
Após a instalação, a configuração é realizada.
Criamos um arquivo de javascript dentro da pasta resources/js com o nome de chart.js. O seguinte conteúdo
foi adicionado ao arquivo:
import Chart from 'chart.js/auto';
window.make_chart = function make_chart(id, labels, temperature, humidity) {
var ctx = document.getElementById(id).getContext('2d');
var chart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'temperatura',
data: temperature,
borderColor: 'rgba(255, 0, 0, 1)',
backgroundColor: 'rgba(255, 102, 102, 1)',
},
{
label: 'umidade',
data: humidity,
borderColor: 'rgba(0, 0, 255, 1)',
backgroundColor: 'rgba(102, 102, 255, 1)',
}
]
},
options: {}
});
};
O arquivo acima simplesmente exibi o gráfico com os parâmetros recebidos.
Em seguida, executamos o seguinte comando para compilar e extrair o arquivo js no diretório público do laravel (esse projeto não usa laravel-mix):
npm run build
No arquivo MessageController.php, criaremos uma função com o nome de index. Essa função será responsável por exibir os dados do banco de dados quando acessado.
public function index()
{
$messages = Message::orderByDesc('created_at')->simplePaginate(15);
$data = $messages->items();
$collectedData = collect($data);
$contents = $collectedData->pluck('content');
$collectedContents = collect($contents);
$temperatures = $collectedContents->pluck('temperature');
$humidities = $collectedContents->pluck('humidity');
$datetimes = $collectedData->pluck('created_at');
$formattedDatetimes = $datetimes->map(function ($datetime, $key) {
return $datetime->format('H:i:s');
});
return response()->view('message.index', [
'messages' => $messages,
'keys' => $formattedDatetimes,
'temperatures' => $temperatures,
'humidities' => $humidities
]);
}
O algoritmo acima extrai do banco de dados, os 15 registros mais recentes. A cada página, 15 registros serão exibidos. No fim, esses dados são enviados para o view, responsável pelo interface do usuário.
O view responsável pela exibição será chamado de “index.blade.php” e será colocado dentro do diretório “messages”.
@extends('layouts.app')
@section('content')
<div class="container">
<h1>Messages</h1>
<h1>{{ now()->format('Y/m/d H:i:s') }}</h1>
<div class="row">
<div class="col-sm-8">
<canvas id="chart"></canvas>
</div>
<div class="col-sm-4"></div>
</div>
<script type="module">
let id = 'chart';
let labels = @json($keys);
let temperatures = @json($temperatures);
let humidities = @json($humidities);
make_chart(id, labels, temperatures, humidities);
</script>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Temperatura</th>
<th scope="col">Umidade</th>
<th scope="col">Data</th>
</tr>
</thead>
<tbody>
@foreach($messages as $message)
<tr>
<th scope="row">{{ $message->id }}</th>
<td>{{ $message->content['temperature'] }}</td>
<td>{{ $message->content['humidity'] }}</td>
<td>{{ $message->created_at->format('Y/m/d H:i:s') }}</td>
</tr>
@endforeach
</tbody>
</table>
{{ $messages->links() }}
</div>
@endsection
O arquivo de exibição acima é bastante simples. Ele extende a classe view parente, importa o chart.js e exibi os 15 registros de cada página.
O resultado será o seguinte:
O protocolo MQTT é bastante adequado para a comunicação entre dispositivos devido ao seu baixo consumo de energia e eficiência na transferência de dados mesmo com uma largura de banda pequena.
Por ser um protocolo econômico, o MQTT é um protocolo bastante utilizado na área de IoT, uma vez que grande parte dos dispositivos IoT usam bateria como fonte de energia.
Esse projeto é bastante simples. O ESP8266 e o Raspberrypi são dois dispositivos separados fisicamente, porém se encontram na mesma rede interna. Essa prática lembra bastante o Edge Computing, mas tem apenas um servidor que exibi e armazena os dados(raspberrypi).
Usando os dados coletados como referência, algoritmos de decisões podem ser implementados no sistema receptor. Por exemplo, temperaturas muito altas pode representar um perigo para dispositivos. Por isso, ao invés de apenas receber (subscribe) os dados, o raspberrypi também pode exercer o papel de enviar(publish) dados ao ESP8266, pois isso é permitido no protocolo MQTT. Assim, o ESP8266 pode receber esses dados e executar instruções para outros componentes, como atuadores para baixar a temperatura.
Postagens mais vistas
Os 5 principais componentes do computador são a unidade de controle, unidade aritmética e lógica, memória, dispositivo de entrada e dispositivo de saída.
A porta é um número de 16 bits que é adicionado no final do endereço IP, insinuando qual aplicativo está vinculado e atuando nessa porta.
Rede local de computadores (LAN) é um conjunto de computadores ou dispositivos conectados uns aos outros de forma isolada em um pequeno local.