banner

Inteligência Artificial com Google Home Mini e ESP32



Controle sua casa por voz de qualquer lugar do planeta utilizando Inteligência Artificial. Para isso, basta usar o Google Home, Dialogflow e Firebase. Sei que temos outros programas já prontos que podem facilitar um projeto, mas tenho em mente aqui os engenheiros, desenvolvedores de produtos, que querem, por exemplo, conectar o Google Home com uma máquina de visão artificial, uma rede neural convolucional, isso é possível com esses programas que escolhi.
Portanto, vamos fazer uma automação com Google Home Mini via comando de voz. Além de conectar o Google Home Mini, vamos criar Realtime Database no Firebase, setar o DialogFlow, criar Entidades e Intenções e, por fim, setar o Google Actions.






Recursos usados

·         ESP32S WROOM (38 pinos)
·         Sensor HTU21D
·         Módulo de 4 Relés
·         Protoboard
·         Jumpers
·         2 Lâmpadas
·         Extensão de Tomadas
·         Cabos
·         Assistente Pessoal Google Home Mini






Montagem









Criar Realtime Database

Já criamos o banco de dados no Firebase nesse vídeo:




DialogFlow

Banco utilizado no projeto:






Conectar o 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 é 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



Primeiro, entre no site do DialogFlow.






Criando conta DialogFlow

Clique em “Go to Console”



Clique em “Google” e faça seu login.

Clique em “Permitir”




Aceitei os termos de serviço e clique em “Accept”



“Welcome to DialogFlow!”






Criar Agent

Clique em “Create Agente”



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



Clique em “Export and Import”



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:






DialogFlow Fulfillment

Fulfillmente é o processo total, que começa ao cliente falar algo, e termina até receber uma resposta.
Clique em Fulffilment.



Vamos usar o Inline Editor para usarmos o cloud functions do Firebase.
Clique em “Disable” para habilitar.






Código Fulfillment DialogFlow

Código Fulfillment

const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');

const admin = require('firebase-admin');
admin.initializeApp({
  credential: admin.credential.applicationDefault(),
  databaseURL: 'ws://teste-1172b.firebaseio.com/',
  projectId: "teste-1172b",
});

process.env.DEBUG = 'dialogflow:debug';

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
  const agent = new WebhookClient({ request, response });
  console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
  console.log('Dialogflow Request body: ' + JSON.stringify(request.body));

    function handleDevice(agent) {
      const pegar = agent.parameters.pegar;
      const device = agent.parameters.devices;
      const status = agent.parameters.status;
      
        if(device && status){         
         if(device=='tudo'){
            var novoLuz,novoLed,novoVentilador;
            if(status =='ligar' || status =='on'){
                novoLuz='ligada';
                novoLed='ligado';
                novoVentilador='ligado';
            }
            else{
                novoLuz='desligada';
                novoLed='desligado';
                novoVentilador='desligado';
            }
            admin.database().ref('automation/luz').set({
                status: novoLuz,
            });
            admin.database().ref('automation/led').set({
                status: novoLed,
            });
            admin.database().ref('automation/ventilador').set({
                status: novoVentilador,
            });
            agent.add(`Feito!`);
            
         }
         else{
            var novo;
            if(device == 'luz'){
               if(status == "ligar" || status == 'on')
                novo="ligada";
               else
                novo="desligada";
             }
             else{
                if(status == "ligar" || status == 'on')
                    novo="ligado";
                else
                    novo="desligado";
             }
             
             admin.database().ref('automation/'+device).set({
               status: novo,
              });
              agent.add(`Feito!`);
         }        
        }
        else{
            if(device){
                return admin.database().ref('/automation/'+device+'/status').once('value').then(function(snapshot) {
                var valor = snapshot.val();
                console.log('valor ='+valor);
                console.log('device = '+device);
                if(device =="temperatura"){
                  console.log('entrou');
                  agent.add('O valor da temperatura é de '+valor+' graus Celsius');
                }
                else{
                    if(device == "umidade"){
                      agent.add(`O valor da umidade é de `+valor+` porcento`);
                    }
                    else{
                      if(device=="luz"){
                        agent.add(`A luz está `+valor);
                      }
                      else{
                        if(device=="led"){
                          agent.add(`O led está `+valor);
                        }
                        else{
                            if(device=="ventilador"){
                                agent.add(`O ventilador está `+valor);
                            }
                        }
                      }
                    }
                    
                }
              });
            }
            else{
                agent.add(`Desculpe, não entendi`);
            }
           
        }   
  }
  // Run the proper function handler based on the matched Dialogflow intent name
  let intentMap = new Map();
  intentMap.set('controla', handleDevice);
  agent.handleRequest(intentMap); 
});


DialogFlow Fulfillment

Coloque o código na caixa de texto e clique em “DEPLOY”



Clique em “Integrations”






Integração com Google Assistent

Clique em Integration Settings






Action Google

Clique em “MANAGE ASSISTANT APP” e será direcionado pra outra página do Actions on Google



Aceite os termos e clique em “Agree and Continue”



Para fazer o deploy da sua action é necessário cumprir todos os requisitos e preencher todos os campos obrigatórios



Clique em “Decide how your Actions is invoked”



Escolha o nome como deseja chamar, escolha a voz da assistente e clique em Save



Preencha todas as informações do “Directory Information” e clique em “Save”



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”



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.



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



Biblioteca do sensor HTU21D





Fluxograma – Código fonte





Declarações e variáveis

#include <Arduino.h>
#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 "NOMEDOPROJETO.firebaseio.com/" 
#define FIREBASE_AUTH "" 

// Nome da rede WiFi e senha
const char *ssid     = "SSIDWiFi"; 
const char *password = "SenhaWiFi"; 

// 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 = "";


Setup


void setup() 
{  
  // Iniciamos a serial
  Serial.begin(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);
}


firebaseBegin


// Função que inicia a conexão com o Firebase
void firebaseBegin()
{
  // Iniciamos a comunicação Firebase
  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);  
  
  // Setamos o callback (Executado a cada alteração do firebase)
  Firebase.stream("/", [](FirebaseStream stream) 
  {
    String path, value;  

    // Se o evento que vem do callback é de alteração "put"
    if(stream.getEvent() == "put") 
    {     
      // Obtemos os valores de path e value
      path = stream.getPath();
      value = stream.getDataString();   

      // Deixamos em maiúsculo
      path.toUpperCase();         
      value.toUpperCase();


  // Se for a mensagem inicial enviada pelo firebase assim que o ESP inicia sua comunicação
      if(value.startsWith("{\"AUTOMATION\""))
      {
        // Sincronizamos as saídas para os relés de acordo com a mensagem recebida
        syncFirebase(value);        
        Serial.println("Firebase sincronizado");
      }
      else
      // Se não for uma mensagem referente a temperatura e umidade, executamos um comando e exibimos na serial
      if(!path.equals("/AUTOMATION/UMIDADE/STATUS") && !path.equals("/AUTOMATION/TEMPERATURA/STATUS"))
        Serial.println(executeCommandFromFirebase(path, value));
    }    
  });
}


SyncFirebase


// 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 = value.substring(value.indexOf("LED"));

  int posVar = aux.indexOf("STATUS\":\"")+9;
  String status = "";
  
  for(int i = posVar; aux.charAt(i)!='\"'; i++)
    status += aux.charAt(i);
  
  Serial.println("Status LED:"+status);

  // Sincronizando saída referente a lampada led
  if(status.equals("LIGADO"))
    digitalWrite(luzLed, LOW);
  else
  if(status.equals("DESLIGADO"))
    digitalWrite(luzLed, HIGH);



 status = "";  

  // Obtendo a substring referente a lampada incandescente
  aux = value.substring(value.indexOf("LUZ"));
  posVar = aux.indexOf("STATUS\":\"")+9;

  for(int i = posVar; aux.charAt(i)!='\"'; i++)
    status += aux.charAt(i);

  Serial.println("Status LUZ:"+status);
  // Sincronizando saída referente a lampada incandescente
  if(status.equals("LIGADA"))
    digitalWrite(luz, LOW);
  else
  if(status.equals("DESLIGADA"))
    digitalWrite(luz, HIGH);
    
  status = "";
  
  // Obtendo a substring referente ao ventilador
  aux = value.substring(value.indexOf("VENTILADOR"));
  posVar = aux.indexOf("STATUS\":\"")+9;

  for(int i = posVar; aux.charAt(i)!='\"'; i++)
    status += aux.charAt(i);
    
  Serial.println("Status VENTILADOR:"+status);
  // Sincronizando saída referente ao ventilador
  if(status.equals("LIGADO"))
    digitalWrite(ventilador, LOW);
  else
  if(status.equals("DESLIGADO"))
    digitalWrite(ventilador, HIGH);
}


executeCommandFromFirebase


// Função que executa um comando ativando e desativando os relés
String executeCommandFromFirebase(String cmd, String value)
{
  // Retira a primeira barra
  if(cmd.charAt(0) == '/')
    cmd = cmd.substring(1);

  // Exibe na serial o comando e o valor
  Serial.println(cmd);
  Serial.println(value);
  
  // Caso for um comando referente a lampada incandescente
  if(cmd.equals("AUTOMATION/LUZ"))
  {       
    // Se o valor for "LIGADA"
    if(value.equals("{\"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);
  }


else
  // Caso for um comando referente a lampada led
  if(cmd.equals("AUTOMATION/LED"))
  {    
    if(value.equals("{\"STATUS\":\"LIGADO\"}"))
      // Ativamos o relé (lógica inversa)
      digitalWrite(luzLed, LOW);
    else // Caso contrário desativamos o relé
      digitalWrite(luzLed, HIGH);
  }
  else
  if(cmd.equals("AUTOMATION/VENTILADOR"))
  {    
    if(value.equals("{\"STATUS\":\"LIGADO\"}"))
      // Ativamos o relé (lógica inversa)
      digitalWrite(ventilador, LOW);      
    else // Caso contrário desativamos o relé
      digitalWrite(ventilador, HIGH);
  }
  else // Se não entrou em nenhum "if" acima, retornamos uma mensagem de comando inválido 
    return "Invalid command";

  // Se for um comando válido retornamos a mensagem "OK"
  return  "OK";
}



sendSensorData


// Task que envia os dados de temperatura e umidadade para o Firebase de tempo em tempo
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
      Firebase.setString("/automation/temperatura/status", temp);
      Firebase.setString("/automation/umidade/status", humd);      
      Serial.println("sent");
    }
    // Aguardamos um tempo de 5 segundos
    delay(SEND_DATA_INTERVAL);
  }
}


readClimate


// Função que lê os valores do sensor
void readClimate()
{
  // Retorna 999 caso dê erro de leitura
  float h = htudSensor.readHumidity();
  float t = htudSensor.readTemperature();

  if(t < 900)
    temp = String(t);
  else
    temp = "";
  
  if(h < 900) 
    humd = String(h);
  else
    humd = "";
}


htu21Begin e WifiBegin


// Função que inicializa o sensor HTU21D
void htu21Begin()
{
  // Iniciamos a comunicação i2c
  wireSensor.begin(SDA, SCL, 400000);
  // Inicializamos o sensor
  htudSensor.begin(wireSensor);
}


// Função que inicia o WiFi
void wifiBegin()
{
  // Iniciamos o WiFi
  WiFi.begin(ssid, password);  
    
  Serial.println("Wifi Connecting");

  // Enquanto não estiver conectado exibimos um ponto
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    esp_task_wdt_reset();
    delay(1000);
  }
  // Exibimos "OK"
  Serial.println("OK");
}


Loop

void loop() 
{
  
}


Configurações de compilação/upload





FAÇA O DOWNLOAD DOS ARQUIVOS

PDF

INO

JS



13 comentários:

  1. Boa noite Fernando, acompanho todos seus canais. Parabéns pelo belo trabalho.
    Até a que distância o Google Home Mini reconhece os comandos? Um TV ligada interfere muito no reconhecimento?

    ResponderExcluir
    Respostas
    1. EU TENHO GOOGLE HOME E ELE CAPTA TUDO MESMO COM BARULHO

      Excluir
  2. Boa noite Fernando, mais um vídeo fantástico. Só uma dúvida os arquivos INO e JS estão com mensagem de erro ao tentar abrir com o winrar. Obrigado

    ResponderExcluir
  3. estou afim de fazer o projeto mas falta o vídeo de inteções

    ResponderExcluir
  4. mais uma excelente aula mestre!!!!
    estava querendo muito criar um protocolo de comunicação cambus para pegar algumas informações da central dos veiculos e criar uma interface intuitiva, sei que ja existe um negocio no ml por 30 reais mas quero desenvolver!!!!! curto muito desenvolver algo ainda que seja o mais simples possivel!

    ResponderExcluir
    Respostas
    1. Sim David,
      esse espírito de desenvolver as coisas é que levam a melhoria dos produtos e novas abordagens.
      A tecnologia melhora porque sempre podemos criar algo novo ou também melhorar o que existe.
      continue com essa forma de pensar. Abraços !

      Excluir
  5. Onde encontro o arquivo .zip que vai no intent do dialogflow?

    ResponderExcluir
  6. Fernando, não encontrei o arquivo automation.zip com o projeto pronto com as entidades e intenções já criadas para fazer o download no Dialogflow. Onde posso encontrá-lo?

    ResponderExcluir
  7. Beleza Fernado, como posso fazer que meu device ative o dialogflow para eu escutar a mensagem? por exemplo, quero que quando a temperatura passe de 40C, ele "fale" um alerta tipo "temperatura alta, cuidado".Abraço

    ResponderExcluir
  8. Fernando, não encontrei o arquivo automation.zip com o projeto pronto com as entidades e intenções já criadas para fazer o download no Dialogflow. Onde posso encontrá-lo?

    ResponderExcluir
  9. Fernando,onde está o arquivo .zip ??

    ResponderExcluir

Tecnologia do Blogger.