Automação com Google Home, DialogFlow e Firebase
H S M i
Por Fernando Koyanagi
Intenção da Aula
● Automação com Google Home Mini
○ Automação via comando de voz
1. Conectar o Google Home Mini
2. Criar Realtime Database no Firebase
3. Setup DialogFlow
■ Criar Entidades e Intenções
4. Setup Google Actions
5. Código
Demonstração
Seu e-mail
Em
[Link]
[Link]
Instagram
fernandok_oficial
Telegram
fernandok_oficial
6
Recursos usados
• ESP32S WROOM (38 pinos) • Assistente Pessoal Google Home Mini
• Sensor HTU21D
• Módulo de 4 Relés
• Protoboard
• Jumpers
• 2 Lâmpadas
• Extensão de Tomadas
• Cabos
Montagem
ESP32 HTU21D
Montagem
GPIO22 SCL
GPIO21 SDA
3V3 VCC
GND GND
ESP32 Mód. Relés
GPIO19 IN1
GPIO18 IN2
GPIO5 IN3
5V VCC
GND GND
Criar Realtime Database
Criar Realtime Database Firebase
Já criamos o banco de dados no Firebase nesse vídeo aqui.
DialogFlow
Banco utilizado no projeto:
Conectar o Google Home Mini
Google Home Mini
Antes é necessário conectar o Google Home Mini a rede Wi-Fi e também
parear com a mesma conta do google utilizada no DialogFlow, Firebase e Google Actions.
Setup DialogFlow
DialogFlow
DialogFlow é uma plataforma para construir interfaces de conversação para bots,
aplicativos e dispositivos.
● Possui uma das melhores interfaces para construção de aplicações com NLU
(Natural Language Understanding).
● É 100% grátis.
DialogFlow
Primeiro, entre no site do DialogFlow.
Criando conta DialogFlow
Clique em “Go to Console”.
Criando conta DialogFlow
Clique em “Google” e faça seu login.
Criando conta DialogFlow
Clique em “Permitir”.
Criando conta DialogFlow
Aceitei os termos de serviço e clique em “Accept”.
Welcome to DialogFlow!
Criar Agent
Clique em “Create Agente”.
Criar Agent
Coloque o nome do Agente, escolha a linguagem dos diálogos e clique em “Create”.
Import Intenções e Entidades
Clique na ferramenta ao lado do nome do Agent.
Import Intenções e Entidades
Clique em “Export and Import”.
Import Intenções e Entidades
Selecione o arquivo .zip escreva “IMPORT” no campo de texto e depois clique em
“IMPORT”. Assim importará um projeto pronto com as entidades e intenções já criadas.
OBS: Vou fazer um vídeo para falar
Sobre criar entidades e intenções
Uso DialogFlow
É possível adicionar novas intenções e entidades para controle do banco de dados.
Primeiro crie uma entidade e depois faça o controle da entidade com o uso das
intenções.
Exemplo de intenção citando entidades:
Token
DialogFlow Fulfillment
Fulfillmente é o processo total, que começa ao cliente falar algo, e termina até
receber uma resposta.
Clique em Fulffilment.
DialogFlow Fulfillment
Vamos usar o Inline Editor para usarmos o cloud functions do Firebase.
Clique em “Disable” para habilitar.
Código Fulfillment DialogFlow
Código Fulfillment
//Imports
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const admin = require('firebase-admin');
//inicialização do firebase
[Link]({
credential: [Link](),
databaseURL: '[Link]
projectId: "NOMEDOPROJETOFIREBASE",
});
[Link] = 'dialogflow:debug';
[Link] = [Link]((request, response) => {
//Instanciando Webhook
const agent = new WebhookClient({ request, response });
Código Fulfillment
[Link]('Dialogflow Request headers: ' + [Link]([Link]));
[Link]('Dialogflow Request body: ' + [Link]([Link]));
//função que lida com os dispositivos
function handleDevice(agent) {
//variáveis com os parâmetros reconhecidos pela voz
const pegar = [Link];
const device = [Link];
const status = [Link];
//se tiver dispositivo e status significa que ele vai mudar
if(device && status){
//se vai mudar todos os dispositivos
if(device=='tudo'){
var novoLuz,novoLed,novoVentilador;
//seta todos para ligado
if(status =='ligar' || status =='on'){
novoLuz='ligada';
novoLed='ligado';
novoVentilador='ligado';
}
Código Fulfillment
else{
//seta todos para desligado
novoLuz='desligada';
novoLed='desligado';
novoVentilador='desligado';
}
//seta o valor da luz SET DO FIREBASE
[Link]().ref('automation/luz').set({
status: novoLuz,
});
//seta o valor do led
[Link]().ref('automation/led').set({
status: novoLed,
});
//seta o valor do ventilador
[Link]().ref('automation/ventilador').set({
status: novoVentilador,
});
//manda uma resposta para o cliente por voz
[Link](`Feito!`);
}
Código Fulfillment
else{
//mudar apenas um dispositivo
var novo;
//se for luz utilizar o valor ligada/desligada
if(device == 'luz'){
if(status == "ligar" || status == 'on') // LIGAR E ON SÃO SINÔNIMOS
novo="ligada";
else
novo="desligada";
}
else{
//se for ventilador ou led utilizar ligado/desligado
if(status == "ligar" || status == 'on')
novo="ligado";
else
novo="desligado";
}
//setar o valor do dispositivo NO FIREBASE
[Link]().ref('automation/'+device).set({
status: novo,
});
//manda uma resposta para o cliente por voz
[Link](`Feito!`);
}
Código Fulfillment
else{
//se não tiver o status e somente o dispositivo, ou seja, ta pedindo informação
if(device){
//busca o status do dispositivo
return [Link]().ref('/automation/'+device+'/status').once('value').then(function(snapshot) {
//recebe o valor do status
var valor = [Link]();
[Link]('valor ='+valor);
[Link]('device = '+device);
//se pedir a temperatura
if(device =="temperatura"){
[Link]('entrou');
//manda uma resposta para o cliente por voz
[Link]('O valor da temperatura é de '+valor+' graus Celsius');
}
else{
//se pedir a umidade
if(device == "umidade"){
//manda uma resposta para o cliente por voz
[Link](`O valor da umidade é de `+valor+` porcento`);
}
Código Fulfillment
else{
//se pedir o status da luz
if(device=="luz"){
//manda uma resposta para o cliente por voz
[Link](`A luz está `+valor);
}
else{
//se pedir o valor do led
if(device=="led"){
//manda uma resposta para o cliente por voz
[Link](`O led está `+valor);
}
else{
//se pedir o status do ventilador
if(device=="ventilador"){
//manda uma resposta para o cliente por voz
[Link](`O ventilador está `+valor);
}
}
}
}
Código Fulfillment Fim
}
});
}
else{
//se não entender nenhum tipo de dispositivo
//manda uma resposta para o cliente por voz
[Link](`Desculpe, não entendi`);
}
}
}
//executa o manipulador de função apropriado com base no nome da intenção correspondente do Dialogflow
let intentMap = new Map(); // relaciona uma função com uma intenção
[Link]('controla', handleDevice); // relaciona a função handleDevice com as intenções (controla)
[Link](intentMap); // Passa o intentMap Quando Google Home é requisitado
});
DialogFlow Fulfillment
Coloque o código na caixa de texto e clique em “DEPLOY”.
DialogFlow Fulfillment
Clique em “Integrations”.
Integração Google Assistent
Integração com Google Assistant
Clique em Integration Settings
Action Google
Clique em “MANAGE ASSISTANT APP” e será direcionado pra outra página do
Actions on Google.
Action Google
Aceite os termos e clique em “Agree and Continue”.
Action Google
Para fazer o deploy da sua action é necessário cumprir todos os requisitos e
preencher todos os campos obrigatórios.
Action Google
Clique em “Decide how your Actions is invoked”.
Action Google
Escolha o nome como deseja chamar, escolha a voz da assistente e clique em Save
Action Google
Preencha todas as informações do “Directory Information” e clique em “Save”
Action Google
Após preencher todas as informações obrigatórias clique em “Release” no canto
esquerdo da tela.
Agora para testar iremos enviar a versão Alpha, então clique em “Submit for Alpha”.
Action Google
Aceite os termos e clique em “Submit”.
Demora cerca de 3 horas para ser aceito caso tudo esteja certo.
Simulações no Actions on Google
Clique em “Simulator” no canto esquerdo para abrir o simulador.
Simulações no Actions on Google
Basta simular diálogos para fazer melhorias. Mudando as intenções, entidades ou
código para obter melhor resultado em suas aplicações.
Código
Configurações
Bibliotecas necessárias
• Biblioteca do Firebase
[Link]
• Biblioteca do sensor HTU21D
[Link]
blob/master/src/SparkFunHTU21D.h
Código
Fluxograma Setup Callback Firebase
(Stream)
Task Send
código fonte
Sensor Data
Task Core
Task Send Sensor Data 0
Callback Firebase 1
Código Declarações e variáveis
#include "esp_task_wdt.h" // Lib do watchdog
#include <IOXhop_FirebaseESP32.h> // Lib do Firebase
#include <Wire.h> // Lib necessária para comunicação i2c
#include <SparkFunHTU21D.h> // Lib do sensor HTU21D
// Pinos do sensor (i2c - canal 1)
#define SDA 21
#define SCL 22
// Objeto referente ao sensor
HTU21D htudSensor;
// Objeto de conexão (wire) do sensor (O sensor usará o canal 1)
TwoWire wireSensor = TwoWire(1);
// Intervalo de envios de temperatura e umidade (5 segundos)
#define SEND_DATA_INTERVAL 5000
// Url do Firebase
#define FIREBASE_HOST "URL_FIREBASE.[Link]/"
#define FIREBASE_AUTH ""
Código Declarações e variáveis
// Nome da rede WiFi e senha
const char *ssid = "NOME_DA_REDE";
const char *password = "SENHA_DA_REDE";
// Pinos dos relés
const int luzLed = 19, luz = 18, ventilador = 5;
// Valores de temperatura e umidade que serão enviados para o Firebase
String temp = "", humd = "";
Código Setup Setup
void setup()
{
// Iniciamos a serial
[Link](115200);
// Inicializamos o watchdog com 15 segundos de timeout
esp_task_wdt_init(15, true);
// Setamos os pinos dos relés como saída
pinMode(luz, OUTPUT);
pinMode(luzLed, OUTPUT);
pinMode(ventilador, OUTPUT);
// Iniciamos o sensor
htu21Begin();
// Iniciamos o WiFi
wifiBegin();
// Iniciamos a conexão com o Firebase
firebaseBegin();
// Iniciamos a task de envio de temperatura e umidade
xTaskCreatePinnedToCore(sendSensorData, "sendSensorData", 10000, NULL, 1, NULL, 0);
}
Código firebaseBegin Callback Firebase (Stream)
// Função que inicia a conexão com o Firebase
void firebaseBegin()
{
// Iniciamos a comunicação Firebase
[Link](FIREBASE_HOST, FIREBASE_AUTH);
// Setamos o callback (Executado a cada alteração do firebase)
[Link]("/", [](FirebaseStream stream)
{
String path, value;
// Se o evento que vem do callback é de alteração "put"
if([Link]() == "put")
{
// Obtemos os valores de path e value
path = [Link]();
value = [Link]();
// Deixamos em maiúsculo
[Link]();
[Link]();
Código firebaseBegin Callback Firebase (Stream)
// Se for a mensagem inicial enviada pelo firebase assim que o ESP inicia sua comunicação
if([Link]("{\"AUTOMATION\""))
{
// Sincronizamos as saídas para os relés de acordo com a mensagem recebida
syncFirebase(value);
[Link]("Firebase sincronizado");
}
else
//Se não for uma mensagem referente a temperatura e umidade, executamos um comando e exibimos na serial
if( && )
[Link](executeCommandFromFirebase(path, value));
}
});
}
Código syncFirebase Callback Firebase (Stream)
// Função que ativa/desativa todos os relés de acordo com a mensagem recebida ao iniciar o Firebase
(Sincronizando as saídas do ESP com os valores atuais do Firebase)
void syncFirebase(String value)
{
/*
EXEMPLO DE MENSAGEM RECEBIDA:
"LED":{"STATUS":"LIGADO"},"LUZ":{"STATUS":"LIGADA"},"TEMPERATURA":{"STATUS":"26.27"},"UMIDADE":{"STATUS":"36.76"},"VENTILADOR":{"STATUS":"LIGADO"}}}
*/
// Obtendo a substring referente a lampada led
String aux = [Link]([Link]("LED"));
int posVar = [Link]("STATUS\":\"")+9;
String status = "";
for(int i = posVar; [Link](i)!='\"'; i++)
status += [Link](i);
[Link]("Status LED:"+status);
// Sincronizando saída referente a lampada led
if([Link]("LIGADO"))
digitalWrite(luzLed, LOW);
else
if([Link]("DESLIGADO"))
digitalWrite(luzLed, HIGH);
Código syncFirebase Callback Firebase (Stream)
status = "";
// Obtendo a substring referente a lampada incandescente
aux = [Link]([Link]("LUZ"));
posVar = [Link]("STATUS\":\"")+9;
for(int i = posVar; [Link](i)!='\"'; i++)
status += [Link](i);
[Link]("Status LUZ:"+status);
// Sincronizando saída referente a lampada incandescente
if([Link]("LIGADA"))
digitalWrite(luz, LOW);
else
if([Link]("DESLIGADA"))
digitalWrite(luz, HIGH);
status = "";
// Obtendo a substring referente ao ventilador
aux = [Link]([Link]("VENTILADOR"));
posVar = [Link]("STATUS\":\"")+9;
for(int i = posVar; [Link](i)!='\"'; i++)
status += [Link](i);
[Link]("Status VENTILADOR:"+status);
// Sincronizando saída referente ao ventilador
if([Link]("LIGADO"))
digitalWrite(ventilador, LOW);
else
if([Link]("DESLIGADO"))
digitalWrite(ventilador, HIGH);
}
Código executeCommandFromFirebase Callback Firebase (Stream)
// Função que executa um comando ativando e desativando os relés
String executeCommandFromFirebase(String cmd, String value)
{
// Retira a primeira barra
if([Link](0) == '/')
cmd = [Link](1);
// Exibe na serial o comando e o valor
[Link](cmd);
[Link](value);
// Caso for um comando referente a lampada incandescente
if([Link]("AUTOMATION/LUZ"))
{
// Se o valor for "LIGADA"
if([Link]("{\"STATUS\":\"LIGADA\"}"))
// Ativamos o relé (lógica inversa)
digitalWrite(luz, LOW);
else // Caso contrário desativamos o relé
// Desativamos o relé (lógica inversa)
digitalWrite(luz, HIGH);
}
Código executeCommandFromFirebase Callback Firebase (Stream)
else
// Caso for um comando referente a lampada led
if([Link]("AUTOMATION/LED"))
{
if([Link]("{\"STATUS\":\"LIGADO\"}"))
digitalWrite(luzLed, LOW); // Ativamos o relé
else // Caso contrário desativamos o relé
digitalWrite(luzLed, HIGH);
}
else
if([Link]("AUTOMATION/VENTILADOR"))
{
if([Link]("{\"STATUS\":\"LIGADO\"}"))
digitalWrite(ventilador, LOW); // Ativamos o relé
else // Caso contrário desativamos o relé
digitalWrite(ventilador, HIGH);
}
else
// Se não entrou em nenhum "if" acima, retornamos uma mensagem erro
return "Invalid command";
// Se for um comando válido retornamos a mensagem "OK"
return "OK";
}
Código Declarações e variáveis Task Send Sensor Data
// Task que envia os dados de temperatura e umidadade para o Firebase
void sendSensorData(void *p)
{
// Adicionamos a tarefa na lista de monitoramento do watchdog
esp_task_wdt_add(NULL);
while(true)
{
// Resetamos o watchdog
esp_task_wdt_reset();
// Lê os valores do sensor
readClimate();
// Se foi possível ler o sensor
if(temp != "" && humd != "")
{
// Enviamos para o Firebase
[Link]("/automation/temperatura/status", temp);
[Link]("/automation/umidade/status", humd);
[Link]("sent");
}
// Aguardamos um tempo de 5 segundos
delay(SEND_DATA_INTERVAL);
}
}
Código readClimate Task Send Sensor Data
// Função que lê os valores do sensor
void readClimate()
{
// Retorna 999 caso dê erro de leitura
float h = [Link]();
float t = [Link]();
if(t < 900)
temp = String(t);
else
temp = "";
if(h < 900)
humd = String(h);
else
humd = "";
}
Código htu21Begin e WifiBegin Setup
// Função que inicializa o sensor HTU21D
void htu21Begin()
{
// Iniciamos a comunicação i2c
[Link](SDA, SCL, 400000);
// Inicializamos o sensor
[Link](wireSensor);
}
// Função que inicia o WiFi
void wifiBegin()
{
// Iniciamos o WiFi
[Link](ssid, password);
[Link]("Wifi Connecting");
// Enquanto não estiver conectado exibimos um ponto
while ([Link]() != WL_CONNECTED)
{
[Link](".");
esp_task_wdt_reset();
delay(1000);
}
// Exibimos "OK"
[Link]("OK");
}
Código Loop
void loop()
{
// Não faz nada...
}
Configurações de compilação/upload
Em [Link]
Download arquivos PDF e INO do código fonte
71