banner

Automação com ESP32 e interface Gráfica com Golang


Desenvolvimento de interface gráfica com linguagem GO. Você pode rodar no Linux, IOS, Android, ou seja, ele roda em todo lugar sem modificação! É desse assunto que vamos falar no vídeo de hoje, no qual vamos controlar um ESP32 com C++ do Arduino IDE, o que te possibilita criar automações poderosíssimas.
Vou, então, te apresentar um exemplo de user interface com Fyne feito em linguagem Go, isso em uma aplicação com ESP32.


Quer comprar seu ESP32? 
Compre nesse link e receba em 10 dias: https://s.click.aliexpress.com/e/_d8KYBUq




RECURSOS USADOS

  • ESP32
  • 6 Leds
  • 6 Resistores 330 ohm
  • Protoboard
  • Jumpers
  • Computador (servidor windows)






CLIENTE - SERVIDOR






MONTAGEM





INSTALAÇÃO DO AMBIENTE





  • Instalação do Ambiente Plugins VSC


  • Instalação do Ambiente  FYNE


Variável de Ambiente GOPATH



Verificando a Variável de Ambiente GOPAT



Testando Golang






CÓDIGO SERVER

Fluxograma



Código Servidor

Imports e Variáveis

package main

// Imports necessários
import (
 "bufio"
 "fmt"
 "net"
 "strings"
 "time"

 "fyne.io/fyne"
 "fyne.io/fyne/app"
 "fyne.io/fyne/dialog"
 "fyne.io/fyne/layout"
 "fyne.io/fyne/widget"
)

// Objeto de conexão http com o ESP
var ESP32 net.Conn

// Label que indica o estado de conexão do ESP
var lblConn *widget.Label

// Label que indica o estado de envio de comandos
var lblEnviando *widget.Label

// Variável auxiliar
var aguardaConfirmação = false


main


func main() {
 // Iniciamos o ponteiro que receberá a conexão do ESP como nulo
 ESP32 = nil
 // Criamos uma goroutine que inicia a conexão com o ESP
 go conexaoESP32()
 // Construímos a janela principal
 initWindow()
}


conexaoESP32


// Função que inicia as conexões com o ESP
func conexaoESP32() {

 // Porta de conexão
 PORT := ":8001"

 // Inicia o listen
 // Possui 2 retornos, o primeiro é o objeto Listen e o segundo é o erro caso exista
 l, err := net.Listen("tcp4", PORT)
 // Se acontecer algum erro, aborta o programa
 if err != nil {
  fmt.Println(err)
  return
 }

 fmt.Println("Listen ok")

 // Antes de sair da função, fechamos o listen
 defer l.Close()
 fmt.Println("Waiting for connections...")



 for {
  // Aguarda conexões (Accept)
  // Possui 2 retornos, o primeiro é o objeto referente a conexão com o client e o segundo é o erro caso exista
  ESP32, err = l.Accept()

  if err != nil {
   fmt.Println(err)
   break
  }

  // Chamamos a função que recebe as mensagens do client que se conectou
  handleConnection()
 }
 // Caso ocorra algum erro, reiniciamos o servidor
 go conexaoESP32()
}


handleConnection


// Função que recebe as mensagems do ESP
func handleConnection() {

 // Ao terminar a função, fecha a conexão com o ESP
 defer ESP32.Close()
 // Ao terminar a função, indica ESP desconectado
 defer lblConn.SetText("Desconectado")

 // Se o ESP não foi instanciado, aborta função
 if ESP32 == nil {
  fmt.Println("handleConnection aborted")
  return
 }

 // Seta o texto da label indicando ESP conectado
 lblConn.SetText("Conectado")

 // Exibe mensagem informando o IP do client que se conectou no servidor
 fmt.Printf("Serving %s\n", ESP32.RemoteAddr().String())



 // Loop infinito
 for {
  // Declaramos uma variável que receberá as mensagens do ESP
  msg := ""
  // Variável que receberá a mensagem de erro
  var err error

  // Goroutine que recebe a mensagem do ESP
  go func() {
   msg, err = bufio.NewReader(ESP32).ReadString('\n')
  }()

  // Aguardamos a mensagem até 3 segundos de timeout
  start := time.Now()
  for msg == "" {
   if time.Second*3 < time.Since(start) {
    // Se atingiu 3 segundos, abortamos a função
    fmt.Println("Timeout, desconectando...")
    return
   }
  }


  // Se ocorreu algum erro
  // Exibimos e abortamos a função
  if err != nil {
   fmt.Println(err)
   break
  }

  // Retiramos o espaço da mensagem
  msg = strings.TrimSpace(string(msg))

  // Se a mensagem corresponde a "STOP"
  if msg == "STOP" {
   // Abortamos a função
   break
  }

  // Se estamos aguardando uma confirmação
  if aguardaConfirmação && msg == "OK" {
   // Indicamos pela label de estado de envio "OK"
   lblEnviando.SetText("OK")
   // Mudamos o estado da variável auxiliar para false
   aguardaConfirmação = false
  } else {
   // Se não for nenhuma mensagem esperada, exibimos para debug
   fmt.Println(msg)
  }
 }
}


initWindow


// Função que constrói a janela principal
func initWindow() {

 // Iniciamos uma aplicação
 app := app.New()
 // Iniciamos uma janela
 w := app.NewWindow("Hello")
 // Criamos a label responsável por indicar o estado de conexão do ESP
 lblConn = widget.NewLabel("Desconectado")
 // Criamos a label responsável por indicar o estado de envio de comandos para o ESP
 lblEnviando = widget.NewLabel("Status do envio")

 // Variáveis auxiliares que indicam os estados dos leds
 statusLed1 := false
 statusLed2 := false
 statusLed3 := false
 statusLed4 := false
 statusLed5 := false
 statusLed6 := false

 // Label de boas vindas
 labelTitle := widget.NewLabel("Hello Fyne!")


 // Criamos o botão que envia ON/OFF referente ao led 1
 btnLed1 := widget.NewButton("Led 1", func() {
  var msg = ""
  if ESP32 == nil {
   msg = "Erro ao enviar comando (Desconectado)"
  } else {
   statusLed1 = !statusLed1
   if statusLed1 {
    msg = enviaComando("ON1")
   } else {
    msg = enviaComando("OFF1")
   }
  }
  d := dialog.NewInformation("Envio de comando", msg, w)
  d.Show()
 })


Criamos os 6 botões conforme o código acima
Mudando apenas o nome do botão, os comandos enviados e a variável statusLed


 // Criamos o botão que fecha o programa
 btnQuit := widget.NewButton("Quit", func() {
  app.Quit()
 })

 // Inserimos a label "Hello Fyne" dentro de um container
 // Para podermos centralizá-la
 lblTitleHContainer := fyne.NewContainerWithLayout(layout.NewHBoxLayout(),
  layout.NewSpacer(), labelTitle, layout.NewSpacer())



 // Setamos o conteúdo da janela principal
 // Separando todos os elementos por vírgula
 // A ordem em que eles são adicionados é a ordem que aparecerão na janela verticalmente
 w.SetContent(widget.NewVBox(
  lblConn,
  lblEnviando,
  layout.NewSpacer(),
  lblTitleHContainer,
  layout.NewSpacer(),
  btnLed1,
  btnLed2,
  btnLed3,
  btnLed4,
  btnLed5,
  btnLed6,
  btnQuit,
 ))

 // Centralizamos a janela na tela
 w.CenterOnScreen()

 // Setamos o tamanho fixo da janela
 w.Resize(fyne.NewSize(300, 400))

 // Mostramos/executamos a janela
 w.ShowAndRun()
}


EnviaComando


// Função que envia um comando para o ESP
// Retorna a mensagem a ser exibida na janela
func enviaComando(cmd string) string {

 // Indica na label que estamos enviando o comando
 lblEnviando.SetText("Enviando...")

 // Envia o comando
 _, err := ESP32.Write([]byte(cmd))

 // Se ocorreu algum erro
 if err != nil {
  // Exibe mensagens
  lblConn.SetText("Desconectado")
  lblEnviando.SetText("Falhou")
  fmt.Println(err.Error())
  // Retornamos uma mensagem a ser exibida pela janela principal
  return "Erro ao enviar comando"
 }

 // Se não ocorreu nenhum erro
 // Setamos a variável auxiliar como true
 aguardaConfirmação = true
 // Retornamos uma mensagem a ser exibida pela janela principal
 return "O comando " + cmd + " foi enviado"
}




CÓDIGO CLIENT

Fluxograma



Declarações e variáveis

// Bibliotecas necessárias
#include <WiFi.h>
#include <WiFiMulti.h>

// Dados de acesso a rede wifi
#define SSID "SSID"
#define PASSWORD "SENHA"

// Dados de conexão com o servidor go
const uint16_t port = 8001; // porta
const char * host = "192.168.0.103"; // ip

// Objeto utilizado para conexão com a rede
WiFiMulti wiFiMulti;

// Objeto utilizado para conexão com o servidor
WiFiClient client;

// Relé usado como exemplo
const int led1 = 23, led2 = 22, led3 = 21, led4 = 32, led5 = 33, led6 = 25;



Setup


void setup()
{
  // Inicia velocidade da serial
  Serial.begin(115200);
  
  // Seta os pinos dos leds como pinos de saída
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
  pinMode(led4, OUTPUT);
  pinMode(led5, OUTPUT);
  pinMode(led6, OUTPUT);
  
  // Conecta WiFi
  setupWiFi();

  // Conecta com o servidor
  Serial.print("Connecting to ");
  Serial.println(host);

  if(client.connect(host, port))
    Serial.println("Conectado com o servidor Go");
  else
    Serial.println("Erro ao conectar com o servidor Go");  
}


Loop


void loop()
{
  // Verifica se existem mensagens a serem lidas
  if (client.available() > 0)
  {
    // Efetua a leitura das mensagens
    String cmd = client.readStringUntil('\r');
    
    Serial.println(cmd);

    // Executa o comando de acordo com a mensagem
    executeCommand(cmd);
  }

  // Verifica se a conexão está ativa
  verifyConnection();
  
  // Enviamos um "OK" indicando que o ESP está ativo
  client.print("OK\n");
  // A cada 1 segundo
  delay(1000);
}


executeCommand


// Função que atua nos leds de acordo com o comando
void executeCommand(String cmd)
{
  if(cmd == "ON1")
    digitalWrite(led1, HIGH);
  else
  if(cmd == "OFF1")
    digitalWrite(led1, LOW);
  else
  if(cmd == "ON2")
    digitalWrite(led2, HIGH);
  else
  if(cmd == "OFF2")
    digitalWrite(led2, LOW);
  else
  if(cmd == "ON3")
    digitalWrite(led3, HIGH);
  else
  if(cmd == "OFF3")
    digitalWrite(led3, LOW);
  else
  if(cmd == "ON4")
    digitalWrite(led4, HIGH);
  else
  if(cmd == "OFF4")
    digitalWrite(led4, LOW);
  else
  if(cmd == "ON5")
    digitalWrite(led5, HIGH);
  else
  if(cmd == "OFF5")
    digitalWrite(led5, LOW);
  else
  if(cmd == "ON6")
    digitalWrite(led6, HIGH);
  else
  if(cmd == "OFF6")
    digitalWrite(led6, LOW);    
}


verifyConnection


// Função que verifica se caiu a conexão com o servidor, se sim, efetua a reconexão
void verifyConnection()
{
  if(!client.connected())
  {  
    client.stop();
    Serial.println("Desconectado, reconectando...");
    if(client.connect(host, port))
      Serial.println("Conectado com o servidor Go");
    else
      Serial.println("Erro ao conectar com o servidor Go");
  }
}


setupWiFi


// Função que inicia o WiFi
void setupWiFi()
{ 
  // Modo Access Point
  wiFiMulti.addAP(SSID, PASSWORD);

  Serial.println();
  Serial.print("Waiting for WiFi... ");

  while(wiFiMulti.run() != WL_CONNECTED) 
  {
    Serial.print(".");
    delay(500);
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}




FAÇA O DOWNLOAD DOS ARQUIVOS

PDF

CÓDIGOS




Um comentário:

  1. Fernando, meu amigo, desculpe minha ignorância, eu me interessei muito pelo assunto, tenho condições de implementar tudo (soft + hard) mas não consegui entender do que se trata. Qual é a finalidade disso tudo? É uma automação de que função, processo ou atividade?
    Novamente peço desculpas por minha deficiência, porém você é uma fonte rica demais para eu desprezar a chance de evoluir minhas habilidades com seus conhecimentos. Por favor me esclareça em gleinerteruel@gmail.com.

    ResponderExcluir

Tecnologia do Blogger.