Automation With AppFernandoK and ESP32

by Fernando Koyanagi in Circuits > Microcontrollers

1200 Views, 2 Favorites, 0 Comments

Automation With AppFernandoK and ESP32

Automação com AppFernandoK e ESP32 - Pt1
frame.png
1.png

This is a tutorial on how to use AppFernandoK, which is a generic automation application for Android. I developed AppFernandoK with the purpose of enabling you to automate a multitude of things while using ESP32 or ESP8266. In this video, I’ll also show a multiclient example with ESP32 that sends and receives messages to the application.

Click HERE and download the App.

Demonstration

MVI_0113.00_59_38_05.Quadro001.jpg
MVI_0113.01_02_40_11.Quadro002.jpg

Using the Application

7.png

Icon

Home

8.png

The home screen consists of the following components

Setting Up a New Connection

9.1.png
10.2.png
11.3.png
12.4.png

1. Select Settings

2. Select Connection

Click the button to add

Fill in the fields

A new item in the list of connections will appear as shown in Figure 1, select the connection according to Figure 2, and return to the initial screen by clicking on the X.

Shipment Via Terminal

13.png

Type a * command in the text field and click OK

* The commands must be properly programmed in the source code of the ESP32, both for the relay output and for sending response to the application.

Creating a Button

14.png

1. Select Settings

2. Select Add Button

Creating Scripts (buttons)

15.1.png
16.2.png
17.3.png
18.4.png

Fill in the fields as shown

* The list of reserved commands (such as Wait) is available in the application user manual

A new button will be created according to the image

You can enable button movement by clicking EnableDragButton

An arrow icon will appear indicating the movement of the object.

To edit / delete, use the drag button of the gear located in the upper right corner.

Buttons Used in Our Example

19.png

The buttons used in our example are highlighted in the image

Assembly

20.png
22.png

Resources Used

21.png

· ESP32 WROOM Dev Module

· TFT 1.8 '' Display

· 4-relay module

· BME280

· Reed Switch

· Neodymium's magnet

· 10k ohm resistor

· Smartphone

· AppFernandoK application

· Jumpers

Pinout

23.png

Code

Diagram

26.1.png
27.2.png
28.3.png
29.4.png

Task responsible for reading the reed switch sensor and sending warnings to clients if the sensor is activated.

Task responsible for adding new clients to the list of connected clients (vector) used by ESP.

Task responsible for reading commands received from clients, performing an action, and sending a response.

The loop function is responsible for reading the BME280 temperature and humidity sensor, displaying the values on the display every 5 seconds, and displaying the number of connected clients.

Declarations and Variables

#include <Arduino.h> //Lib Arduino (opcional)

#include <Adafruit_GFX.h> // Biblioteca de gráficos

#include <Adafruit_ST7735.h> // Biblioteca do hardware ST7735

#include <Fonts/FreeSerif9pt7b.h> // Fonte Serif que é usada no display

#include <WiFi.h> // Lib WiFi

#include <Wire.h> // Necessário apenas para o Arduino 1.6.5 e posterior

#include <SPI.h> // Lib de comunicação SPI

#include <Adafruit_BME280.h> // Lib do sensor BME

#include <vector> // Lib com as funções de vetor (vector)

const char* ssid = "SSID"; // Coloque o nome da sua rede wifi aqui const char* password = "PASSWORD"; // Coloque a sua senha wifi aqui const IPAddress IP = IPAddress(111,111,1,111); // IP fixo que o ESP utilizará const IPAddress GATEWAY = IPAddress(111,111,1,1); // Gateway const IPAddress SUBNET = IPAddress(255,255,255,0); // Máscara const int port = 80; // Porta // Objeto WiFi Server, o ESP será o servidor WiFiServer server(port); // Vetor com os clientes que se conectarão no ESP std::vector clients; // Objeto do sensor BME280 Adafruit_BME280 bme; //SDA = GPIO21, SCL = GPIO22 // ESP32-WROOM #define TFT_CS 12 // CS #define TFT_DC 14 // A0 #define TFT_MOSI 27 // SDA #define TFT_CLK 26 // SCK #define TFT_RST 0 // RESET #define TFT_MISO 0 // MISO // Objeto do display tft Adafruit_ST7735 display = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST); // Pinos ligados nos relés const int relay1 = 17, relay2 = 5, relay3 = 18, relay4 = 19; // Pino ligado no reed switch const int reedSwitch = 15; // Variável que obtém a temperatura e umidade String climate = ""; // Flag que indica se o display está ocupado bool displayIsBusy = false; // Altura da fonte que é usada no display (9 + 5 espaçamento) int fontHeight = 14; // Task de leitura do sensor reed switch void taskReedSwitch(void *); // Task que insere novos clientes (recém conectados) no vector void taskNewClients(void *); // Task que recebe executa comandos dos clientes void handleClients(void *);

Setup

void setup()
{ // Iniciamos a serial com 9600 de velocidade Serial.begin(9600); // Iniciamos o display com fundo preto display.initR(INITR_BLACKTAB); // Exibimos a mensagem "Starting..." showDisplay("Starting...", true); // Configuramos os pinos dos relés e reed switch (pinMode) setPins(); // Iniciamos o sensor BME280 bmeBegin(); // Iniciamos o servidor (WiFi Server) serverBegin(); // Criamos 3 tasks (mais detalhes no escopo da função) createTasks(); }

Loop

34.png

void loop()
{ // Exibimos o total de clientes conectados showClients(); // Lemos o sensor BME280 obtendo a temperatura e umidade readBMESensor(); // Aguardamos 5 segundos delay(5000); }

Setup - CreateTasks

// Criamos as 3 Tasks
void createTasks() { //Criamos a task de leitura do reed switch xTaskCreatePinnedToCore ( taskReedSwitch, //Função que será executada "readReedSwitch", //Nome da tarefa 10000, //Tamanho da pilha NULL, //Parâmetro da tarefa 2, //Prioridade da tarefa NULL, //Caso queria manter uma referência para a tarefa que vai ser criada (no caso não precisamos) 0 //Número do core que será executada a tarefa (usamos o core 0 para o loop ficar livre com o core 1) ); //Criamos a task que insere os novos clientes no vector xTaskCreatePinnedToCore(taskNewClients, "taskNewClients", 10000, NULL, 2, NULL, 0); //Criamos a task que recebe e executa os comandos dos clients conectados xTaskCreatePinnedToCore(handleClients, "handleClients", 10000, NULL, 2, NULL, 0); }

Task 1: Core 0 - TaskReedSwitch

36.png

// Task que lê o reed switch
void taskReedSwitch(void* p) { // Valor que representa 1ms, usado nos vTaskDelays TickType_t taskDelay = 1 / portTICK_PERIOD_MS; // O loop deve ser infinito (para chamarmos constantemente o vTaskDelay que, por sua vez, alimenta o Watchdog e evita a reinicialização indesejada do ESP pelo Watchdog) while (true) { // Se o reed switch estiver com sinal alto if(digitalRead(reedSwitch) == HIGH) { // Exibe mensagem na serial e no display Serial.println("reed high"); showDisplay("Reed switch\nActivated!", true); // Envia a mensagem "The sensor has been activated!" para todos os clientes conectados for(int i=0; i

Task 2: Core 0 - TaskNewClients

38.png

// Task que insere novos clientes conectados no vector
void taskNewClients(void *p) { // Objeto WiFiClient que receberá o novo cliente conectado WiFiClient newClient; // Tempo esperado no delay da task (1 ms) TickType_t taskDelay = 1 / portTICK_PERIOD_MS; while(true) { // Se existir um novo client atribuimos para a variável newClient = server.available(); // Se o client for diferente de nulo if(newClient) { // Inserimos no vector clients.push_back(newClient); // Exibimos na serial indicando novo client e a quantidade atual de clients Serial.println("New client! size:"+String(clients.size())); } // Aguardamos 1ms vTaskDelay(taskDelay); } }

Task 3: Core 0 - HandleClients

39.png

// Função que verifica se um cliente enviou um comando
void handleClients(void *p) { // String que receberá o comando String cmd; // Tempo aguardado pela task (1 ms) TickType_t taskDelay = 1 / portTICK_PERIOD_MS; // Loop infinito while(true) { // Atualizamos o vector deixando somente os clientes conectados refreshConnections(); // Percorremos o vector for(int i=0; i

RefreshConnections (function Called by Task HandleClients)

// Função que verifica se um ou mais clients se desconectaram do server e, se sim, estes clients serão retirados do vector
void refreshConnections() { // Flag que indica se pelo menos um client ser desconectou bool flag = false; // Objeto que receberá apenas os clients conectados std::vector<WiFiClient> newVector; // Percorremos o vector for(int i=0; i<clients.size(); i++)

{ // Verificamos se o client está desconectado if(!clients[i].connected()) { // Exibimos na serial que um cliente se desconectou e a posição em que ele está no vector (debug) Serial.println("Client disconnected! ["+String(i)+"]"); // Desconectamos o client clients[i].stop(); // Setamos a flag como true indicando que o vector foi alterado flag = true; } else newVector.push_back(clients[i]); // Se o client está conectado, adicionamos no newVector } // Se pelo menos um client se desconectou, atribuimos ao vector "clients" os clients de "newVector" if(flag) clients = newVector; }

ExecuteCommand (function Called by Task HandleClients)

43.png

// Função que executa um comando de acordo com a String "cmd"
void executeCommand(String cmd, WiFiClient client) { // String que guarda a resposta que será enviada para o client String response = ""; // Vetor com os 4 pinos dos relés usados neste exemplo, que facilitará a escrita do código fonte int relays[4] = {relay1, relay2, relay3, relay4}; // Se a String estiver vazia, abortamos a função if (cmd.equals("")) return; // Exibimos o comando recebido na serial e no display Serial.println("Recebido: ["+cmd+"]"); showDisplay("Command\nreceived:", true); showDisplay(cmd, false); // Deixamos a string toda em maiúsculo cmd.toUpperCase(); // Se o comando for igual a status if(cmd.equals("STATUS")) response = getStatus(); // Montamos a String de resposta com o status de cada relé else if(cmd.equals("RELAYS ON")) // Se for igual a Relays on, ligamos todos os relés { for(int i=0; i<4; i++) digitalWrite(relays[i], LOW); // Lógica inversa response = "OK"; } else if(cmd.equals("RELAYS OFF")) // Se for igual a Relays off, desligamos todos os relés { for(int i=0; i<4; i++) digitalWrite(relays[i], HIGH); response = "OK"; } else // Se for igual a relay 1 on, relay 1 off, relay 2 on... e assim por diante, executamos uma ação de acordo com o comando if(cmd.equals("RELAY 1 ON") || cmd.equals("RELAY 2 ON") || cmd.equals("RELAY 3 ON") || cmd.equals("RELAY 4 ON") || cmd.equals("RELAY 1 OFF") || cmd.equals("RELAY 2 OFF") || cmd.equals("RELAY 3 OFF") || cmd.equals("RELAY 4 OFF")) { int index = atoi((cmd.substring(cmd.indexOf(" ")+1, cmd.indexOf(" ")+2)).c_str()); if(cmd.indexOf("ON")>=0) digitalWrite(relays[index-1], LOW); else digitalWrite(relays[index-1], HIGH); response = "OK"; } else // Se for igual a climate, atribuimos a String response a última leitura de temperatura e umidade feita pelo BME280 if(cmd.equals("CLIMATE")) response = climate; else response = "Invalid command"; // Se não for nenhum dos comandos acima, retornamos "Comando inválido" // Exibimos a resposta na serial (debug) Serial.println("Resposta: ["+response+"]"); // Enviamos para o client passado por parâmetro e exibimos sucesso ou falha na serial if(client.print(response)>0) Serial.println("Resposta enviada"); else Serial.println("Erro ao enviar resposta"); }

GetStatus (function Called by "executeCommand")

// Função que lê os pinos dos relés e retorna o estado atual em uma String
String getStatus() { String status = "Relay 1: "; if(digitalRead(relay1) == LOW) status+="ON"; else status+="OFF"; status += "\n Relay 2: "; if(digitalRead(relay2) == LOW) status+="ON"; else status+="OFF"; status += "\n Relay 3: "; if(digitalRead(relay3) == LOW) status+="ON"; else status+="OFF"; status += "\n Relay 4: "; if(digitalRead(relay4) == LOW) status+="ON"; else status+="OFF"; return status; }

Setup - SetPins

// Configuramos os pinos INPUT e OUTPUT
void setPins() { pinMode(reedSwitch, INPUT); pinMode(relay1, OUTPUT); pinMode(relay2, OUTPUT); pinMode(relay3, OUTPUT); pinMode(relay4, OUTPUT); digitalWrite(relay1, HIGH); digitalWrite(relay2, HIGH); digitalWrite(relay3, HIGH); digitalWrite(relay4, HIGH); }

Setup - BmeBegin

// Iniciamos o sensor BME280 exibindo sucesso ou falha
void bmeBegin() { //0x76 = o pino SD0 do sensor deve ser ligado no GND //0x77 = o pino SD0 do sensor deve ser ligado no VCC if (!bme.begin(0x76)) { Serial.println("Sensor bme failed!"); showDisplay("Sensor bme failed!", false); } else { Serial.println("Sensor bme ok"); showDisplay("Sensor bme ok", false); } }

Setup - ServerBegin

// Conectamos no WiFi e iniciamos o servidor
void serverBegin() { // Iniciamos o WiFi WiFi.begin(ssid, password); //Exibimos na serial e no display showDisplay("WiFi Connecting", false); Serial.println("Connecting to WiFi"); // Enquanto não estiver conectado exibimos um ponto while (WiFi.status() != WL_CONNECTED) { display.print("."); Serial.print("."); delay(1000); } // Exibimos na serial e no display a mensagem OK Serial.println("OK"); showDisplay("OK", true); // Configuramos o WiFi com o IP definido anteriormente WiFi.config(IP, GATEWAY, SUBNET); // Iniciamos o servidor server.begin(port); // Printamos o IP (debug) Serial.println(WiFi.localIP()); }

Loop - ShowClients and ReadBMESensors

// Função que exibe a quantidade de clientes conectados no servidor do ESP
void showClients() { showDisplay("Clients: "+String(clients.size()), true); }

// Função que lê a temperatura e umidade do sensor BME280
void readBMESensor() { float t = bme.readTemperature(); float h = bme.readHumidity(); if(isnan(t) || isnan(h)) { Serial.println("Erro ao ler sensor"); showDisplay("-\n-", false); climate = " -\n -"; } else { Serial.println((String(t))+";"+(String(h))); showDisplay("\nClimate:\n"+String(t)+" C", false); showDisplay(String(h)+" %", false); climate = String(t)+" C"; climate += "\n "+String(h)+" %"; } }

ShowDisplay

// Verifica se o display está ocupado e exibe a mensagem no display
// Se clear estiver como true então limpamos o display antes da exibição void showDisplay(String msg, bool clear) { if(!displayIsBusy) { displayIsBusy = true; if(clear) resetDisplay(); display.println(msg); displayIsBusy = false; } }

ResetDisplay

// Limpa e reconfigura o display
void resetDisplay() { display.setFont(&FreeSerif9pt7b); display.fillScreen(ST77XX_BLACK); display.setTextColor(ST7735_WHITE); display.setCursor(0,fontHeight); display.setTextSize(1); }

Files

51.png
frame.png