Recents in Beach


Receba o meu conteúdo GRATUITAMENTE


STM32 F746G Automação com relé e ESP32


Um dos maiores chips da linha STM32: F746G. Com arquitetura ARM, seu módulo já vem embutido o STM32, que é um córtex M7, realmente poderoso, e ele tem muitos IOs. Vamos, portanto, programar esse “gigante” usando o Mbed para criar um controle de relés ligado a um ESP32. Ainda, vamos programar o ESP32 para se conectar ao server e receber comandos de controle dos relés.


No nosso projeto de hoje, portanto, alimentamos o módulo com USB e um RJ45, ou seja, um conector modular usado em terminações de telecomunicação. No circuito, então, o STM32 vem pela porta Ethernet e entra num switch, onde teremos um access point, o qual se comunica por WiFi com o ESP32.
Quero destacar, pessoal, que, neste projeto, não utilizei nenhuma biblioteca gráfica comprada do STM32. Sabemos que tem algumas que chegam a custar R$ 20 mil. Mas, para baratear e facilitar o acesso a todos, utilizei apenas a linguagem C.



STM32 F746G

Esse modelo possui um display que considero muito bom, assim como o touch capacitivo. Possui ainda entrada para câmera e para micros.


Neste kit do STM32 F7, da linha Discovery, a STMicroelectronics fez o seguinte: ela colocou a pinagem do Arduino Uno! Isso só mostra a importância do arduino no mundo profissional dos microcontroladores.




WiFi NodeMCU-32S ESP-WROOM-32




Montagem





Programa – ESP32

Começamos programando o ESP32!

Vamos criar um programa para se conectar a um server, neste caso, o STM32F746G, e tratar os comandos recebidos do servidor para controlar os relés.

Bibliotecas e Variáveis

Incluímos a biblioteca WiFi e definimos os pinos que controlarão os relés 1 e 2. Instanciamos o controle do temporizador, ou seja, colocamos um WatchDog, e apontamos as credenciais da rede que desejamos conectar ao ESP32. Apontamos ainda os dados relacionados ao server, como o IP e a porta.

#include <WiFi.h>

#define RELAY_1 22 //pino controla o relé 1
#define RELAY_2 23 //pino controla o relé 2

#define RELAY_ON  '1' 
#define RELAY_OFF '0'

hw_timer_t *timer = NULL; //faz o controle do temporizador (interrupção por tempo)

//credenciais da rede que desejamos conectar o ESP32
const char* ssid = "SSID_rede";
const char* password =  "Senha_rede";

//dados do server (ip, porta)
const uint16_t port = 80;
const char * host = "IP_SERVER";

Setup

Inicializamos os pinos dos relés e tentamos conectar na rede desejada.

void setup()
{
  pinMode(RELAY_1, OUTPUT);
  pinMode(RELAY_2, OUTPUT);
  
  digitalWrite(RELAY_1, LOW);
  digitalWrite(RELAY_2, LOW);
  
  Serial.begin(115200);

  //tenta conectar na rede desejada
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("...");
  }
 
  Serial.print("WiFi connected with IP: ");
  Serial.println(WiFi.localIP());

  //hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp)
  /*
     num: é a ordem do temporizador. Podemos ter quatro temporizadores, então a ordem pode ser [0,1,2,3].
    divider: É um prescaler (reduz a frequencia por fator). Para fazer um agendador de um segundo, 
    usaremos o divider como 80 (clock principal do ESP32 é 80MHz). Cada instante será T = 1/(80) = 1us
    countUp: True o contador será progressivo
  */
  timer = timerBegin(0, 80, true); //timerID 0, div 80
  //timer, callback, interrupção de borda
  timerAttachInterrupt(timer, &resetModule, true);
  //timer, tempo (us), repetição
  timerAlarmWrite(timer, 40000000, true); //40 segundos
  timerAlarmEnable(timer); //habilita a interrupção 

}

Loop

Resetamos o temporizador, o que alimenta o Watchdog. Tentamos nos conectar ao server, trabalhando suas condicionais. Fazemos o parser dos dados vindos do server, enviamos respostas e fechamos o socket.

void loop()
{
    timerWrite(timer, 0); //reseta o temporizador (alimenta o watchdog) 
    WiFiClient client;
    //tenta se conectar ao server
    if (!client.connect(host, port)) { 
        Serial.println("Connection to host failed"); 
        delay(1000);
        return;
    }
    Serial.printf("Connected to server successful! %s\n",host);

//    char bufPins[3];
//    bufPins[0] = statusRelay_1;
//    bufPins[1] = statusRelay_2;
//    bufPins[2] = '\0';
//    client.write(bufPins, sizeof(bufPins));

    //enquanto estiver conectado ao server
    while(client.connected())
    {       
//      Serial.println("client.connected()"); 
      //se temos dados vindo do server
      if(client.available()){
//      while(!client.available() && client.connected()){
//          timerWrite(timer, 0); //reseta o temporizador (alimenta o watchdog) 
//      }
          String str = "";
          // Read all the lines of the reply from server and print them to Serial
          //enquanto tiver dados para serem lidos do server
          while(client.available()) {
              String line = client.readStringUntil('\r');
              str += line;
              Serial.print(line);
              timerWrite(timer, 0); //reseta o temporizador (alimenta o watchdog) 
          }
          Serial.println();
          //faz o parser dos dados vindos do server
          parserPacket(str);
          //envia uma resposta de 'ok'
          char buf[3];
          buf[0] = 'o';
          buf[1] = 'k';
          buf[2] = '\0';
          client.write(buf, sizeof(buf)); //envia o 'ok'
      }//if client.available
      timerWrite(timer, 0); 
    }//while
    client.stop(); //fecha o socket
    Serial.println("Client Disconnected.");
}

parserPacket

Nesta etapa trabalhamos com o pacote que foi enviado pelo server, ou seja, o STM32. Esses dados devem ser do tipo #R1|0. Verifico se o primeiro byte é o que esperamos. Então, pegamos o relé que devemos aplicar o comando e apontamos o valor para o relé.

//faz o parser dos dadosvindos do servidor
//dados devem ser do tipo //ex: #R1|0 -> #R_|_ onde o primeiro _ é o número do relé (1/2) 
//                                             e o segundo _ é o valor para ele (0/1 {on/off})  
void parserPacket(String packet)
{  
  if(packet[0] == '#') //verifica se o primeiro byte é o que esperamos
  {
    String relay = packet.substring(1,3); //pega o relé que devemos aplicar o comando
    char value = packet.charAt(4); //pega o valor para o relé
    if(relay == "R1")
    {
      digitalWrite(RELAY_1, value-'0');
      statusRelay_1 = char(value-'0');
    }
    else if(relay == "R2")
    {
      digitalWrite(RELAY_2, value-'0');
      statusRelay_2 = char(value-'0');
    }
  }
}

IRAM_ATTR

Temos aqui a função que o temporizador irá chamar para reiniciar o ESP32.

//função que o temporizador irá chamar, para reiniciar o ESP32
void IRAM_ATTR resetModule(){
    ets_printf("(watchdog) reiniciar\n"); //imprime no log
    esp_restart_noos(); //reinicia o chip
}


Programa – STM32

Vamos agora criar o programa para o STM32F746G, lembrando que esse programa é feito no compilador Mbed. Ainda vamos criar um programa que transforma o STM32F746G em um controlador de relés através de sua interface touch.

Bibliotecas e Variáveis

Incluímos sete bibliotecas e definimos coordenadas dos botões de relés, bem como sua altura e largura. Declaro o objeto que controla o display e o touch screen. Temos ainda o ponteiro para um socket TCP e variáveis de controle de estado dos relés.

#include "mbed.h"
#include "TS_DISCO_F746NG.h"
#include "LCD_DISCO_F746NG.h"
#include "EthernetInterface.h"
#include "TCPServer.h"
#include "TCPSocket.h"
#include <string>

#define BTN_R1_X 60 //coordenada X do botão relé 1
#define BTN_R1_Y 130 //coordenada Y do botão relé 1

#define BTN_WIDTH  150 //largura do botão dos relés
#define BTN_HEIGHT 75 //altura do botão dos relés

#define BTN_R2_X 50 + (BTN_R1_X + BTN_WIDTH) //coordenada X do botão relé 2
#define BTN_R2_Y 130 //coordenada Y do botão relé 1

LCD_DISCO_F746NG lcd; //objeto que controla o display
TS_DISCO_F746NG ts;  //objeto que controla o touch do display

TCPSocket *clt_sock; //ponteiro para um socket TCP

//variáveis de controle de estado dos relés
bool btnRelay1 = false;
bool btnRelay2 = false;

Protótipos

Nesta parte do código temos o protótipo das funções.

/* PROTÓTIPO DAS FUNÇÕES */
//desenha um um botão na tela com uma escrita no meio
void drawButton(int x, int y, int width, int height, uint32_t color, char* title);
//verifica se ocorreu um toque na tela
void verifyTouch(int x, int y);
//verifica se o toque foi em algum dos botões
bool verifyTouchButton(int x, int y, int rectX, int rectY);
//envia um pacote de comandos para o client
bool sendPacket(char* packet);
//escreve na tela o status da conexão (client conectado ou desconectado)
void writeStatus(char* status, uint32_t color);

main

Guardamos o estado do touch e apontamos os passos de acordo com o status. Ainda tratamos de outros detalhes de impressão no display.

int main()
{
    TS_StateTypeDef TS_State; //estado do touch
    uint8_t status;

    status = ts.Init(lcd.GetXSize(), lcd.GetYSize()); //inicializa o touch na tela toda
    //se deu erro ao inicializar -> mensagem de falha e pára a execução do programa 
    if (status != TS_OK) {
        lcd.Clear(LCD_COLOR_RED);
        lcd.SetBackColor(LCD_COLOR_RED);
        lcd.SetTextColor(LCD_COLOR_WHITE);
        lcd.DisplayStringAt(0, LINE(5), (uint8_t *)"TOUCHSCREEN INIT FAIL", CENTER_MODE);
        wait(2);
        return 0;
    }

    lcd.Clear(LCD_COLOR_BLUE); //limpa a tela e pinta de azul
    lcd.SetBackColor(LCD_COLOR_BLUE); //cor de fundo de texto
    lcd.SetTextColor(LCD_COLOR_YELLOW); //cor do texto
    lcd.DisplayStringAt(0, LINE(5), (uint8_t *)"TOUCHSCREEN INIT OK", CENTER_MODE);


Prosseguimos com detalhes sobre funções do display, bem como sobre a criação de botões.

wait(1); //aguarda um segundo
    lcd.Clear(LCD_COLOR_BLUE); //limpa a tela
    lcd.SetTextColor(LCD_COLOR_WHITE); //seta cor do texto
    lcd.SetFont(&Font24); //seta tamanho da fonte
    lcd.DisplayStringAt(0, LINE(1), (uint8_t *)"AUTOMATION", CENTER_MODE);

    lcd.DisplayStringAt(BTN_R1_X, LINE(4), (uint8_t *)"RELAY 1", LEFT_MODE);
    lcd.DisplayStringAt(BTN_R2_X, LINE(4), (uint8_t *)"RELAY 2", LEFT_MODE);

    //cria os botões
    drawButton(BTN_R1_X, BTN_R1_Y, BTN_WIDTH, BTN_HEIGHT, LCD_COLOR_RED, "OFF");
    drawButton(BTN_R2_X, BTN_R2_Y, BTN_WIDTH, BTN_HEIGHT, LCD_COLOR_RED, "OFF");


Temos aqui o objeto que controla a rede Ethernet. Conectamos à rede, pegamos o IP recebido, verificamos se o mesmo é válido e o imprimimos na tela. Trabalhamos ainda nesta fase com o objeto de controle do server.

//objeto que controla a rede ethernet
    EthernetInterface eth;
    eth.connect(); //conecta à rede

    string ip = eth.get_ip_address(); //pega o IP recebido
    lcd.SetBackColor(LCD_COLOR_BLUE);
    lcd.SetFont(&Font8);
    //verifica se o IP é válido
    if(ip.length() <= 16 && ip.length() >= 7 ) {
        uint8_t text[18];
        sprintf((char*)text, "%s", eth.get_ip_address());
        lcd.DisplayStringAt(0, LINE(1), (uint8_t *)&text, LEFT_MODE); //imprime na tela o IP
    } else {
        lcd.DisplayStringAt(0, LINE(1), (uint8_t *)"IP Invalido", LEFT_MODE);
    }

   
    TCPServer srv; //objeto de controle do server

    //abre um server na rede
    srv.open(&eth);
    //configura a porta TCP 80 para o server
    srv.bind(eth.get_ip_address(), 80);
    /* Can handle 1 simultaneous connections */
    //aguarda uma única conexão
    srv.listen(1);


while(1)

Criei aqui um loop infinito, do qual vou sair só quando eu quiser. Neste processo imprimimos o status. Criamos um objeto socket, um clt_addr e, no objeto cliente socket, instancio o endereço do socket. Imprimimos novamente o status, desta vez de conectado. Enquanto o socket estiver aberto, registro o estado do touch e se algum toque for detectado na tela.

while(1) {
        writeStatus("Desconectado", LCD_COLOR_RED); //imprime o estado de desconectado
        TCPSocket socket; //objeto TCPsocket
        SocketAddress clt_addr; 
        clt_sock = &socket; 
        printf("waiting \n");
        //fica aguardando um client conectar
        srv.accept(clt_sock, &clt_addr);        
        printf("accept %s:%d\n", clt_addr.get_ip_address(), clt_addr.get_port());
        
        //char buffer[3];
        //int n = clt_sock->recv(buffer, sizeof(buffer));
        //printf("N : %d\n",n);
        //buffer[n] = '\0';
        //printf("Received message from Client : %s\n",buffer);
        
        
        writeStatus("Conectado", LCD_COLOR_GREEN); //imprime o estado de conectado
        clt_sock->set_timeout(5000); //seta o timeout para o socket
        //enquanto o socket estiver aberto
        while(clt_sock != NULL) {
            ts.GetState(&TS_State); //registra o estado do touch
            //se algum toque na tela foi detectado
            if (TS_State.touchDetected) {
                uint8_t idx;
                //ao tocar a tela, pode ser que múltiplos toques foram dados, portanto faremos uma verificação
                for (idx = 0; idx < TS_State.touchDetected; idx++) { 
                    //se o evento do toque foi PRESS
                    if(TS_State.touchEventId[idx] == TOUCH_EVENT_PRESS_DOWN) {
                        verifyTouch(TS_State.touchX[idx], TS_State.touchY[idx]);//verifica se tocou em algum botão
                        break;
                    }//if
                }//for
            }//if
        }//while NULL

    }//while 1
}

drawButton


Esta função desenha um botão na tela com uma escrita no meio.

//desenha um um botão na tela com uma escrita no meio
void drawButton(int x, int y, int width, int height, uint32_t color, char* title)
{
    lcd.SetFont(&Font24);
    lcd.SetTextColor(color);
    lcd.SetBackColor(color);
    lcd.FillRect(x, y, width, height);
    lcd.SetTextColor(LCD_COLOR_WHITE);
    uint8_t text[30];
    sprintf((char*)text, "%s", title);
    lcd.DisplayStringAt(x+50, y+(height/2)-10, (uint8_t *)&text, LEFT_MODE);
}

verifyTouch

Verificamos nesta função se ocorreu toque na tela e os comandos dos botões dos relés.

//verifica se ocorreu um toque na tela
void verifyTouch(int x, int y)
{
    bool response = false; //guarda o status do envio da mensagem para o client
    //verifica se tocou no botão do relé 1
    if( verifyTouchButton(x, y, BTN_R1_X, BTN_R1_Y) ) {
        char* text;
        uint32_t color;
        //se o relé está ligado então desliga
        if(btnRelay1) {
            text = "OFF";
            color = LCD_COLOR_RED;
            response = sendPacket("#R1|0"); //envia comando para desligar o relé
        } else {
            //se relé está desligado, então liga
            text = "ON";
            color = LCD_COLOR_GREEN;
            response = sendPacket("#R1|1");//envia comando para ligar o relé
        }
        //se o envio foi confirmado
        if(response) {
            drawButton(BTN_R1_X, BTN_R1_Y, BTN_WIDTH, BTN_HEIGHT, color, text); //atualiza o botão
            btnRelay1 = !btnRelay1;
        }

Ainda, trabalhamos com as funções que envolvem a ligação e o desligamento dos relés.

}
    //verifica se tocou no botão do relé 1 
    else if( verifyTouchButton(x,y,BTN_R2_X,BTN_R2_Y) ) {
        char* text;
        uint32_t color;
         //se o relé está ligado então desliga
        if(btnRelay2) {
            text = "OFF";
            color = LCD_COLOR_RED;
            response = sendPacket("#R2|0"); //envia comando para desligar o relé
        } else {
            //se relé está desligado, então liga
            text = "ON";
            color = LCD_COLOR_GREEN;
            response = sendPacket("#R2|1");//envia comando para ligar o relé

        }
        //se o envio foi confirmado
        if(response) {
            drawButton(BTN_R2_X, BTN_R2_Y, BTN_WIDTH, BTN_HEIGHT, color, text);//atualiza o botão
            btnRelay2 = !btnRelay2;
        }
    }
}


verifyTouchButton & writeStatus

Nesta primeira função, verifica se houve toque em algum dos botões. Na segunda, imprimimos na tela o status da conexão (client conectado ou desconectado).

//verifica se o toque foi em algum dos botões
bool verifyTouchButton(int x, int y, int rectX, int rectY)
{
    printf("tocou : %d,%d     %d,%d\n",x,y,rectX,rectY);
    if( (x >= rectX) && (x <= rectX + BTN_WIDTH) )  {
        if( (y >= rectY) && (y <= rectY + BTN_HEIGHT) )
            return true;
    }
    return false;
}

//escreve na tela o status da conexão (client conectado ou desconectado)
void writeStatus(char* status, uint32_t color)
{
    lcd.SetTextColor(color);
    lcd.SetBackColor(LCD_COLOR_BLUE);
    lcd.SetFont(&Font16);
    lcd.ClearStringLine(16); //limpa a linha que escreveremos
    uint8_t text[30];
    sprintf((char*)text, "%s", status);
    lcd.DisplayStringAtLine(16, (uint8_t *)&text);
}


sendPacket

Por fim, enviamos um pacote de comandos para o cliente e aguardamos confirmação.

//envia um pacote de comandos para o client
bool sendPacket(char* packet)
{
    char buffer[256];
    clt_sock->send(packet, strlen(packet)); //envia o comando
    int n = clt_sock->recv(buffer, sizeof(buffer)); //aguarda confirmação
    printf("N : %d\n",n);
    //se não chegou bytes então client não recebeu o pacote
    if (n <= 0) {
        clt_sock->close(); //fecha o socket
        clt_sock = NULL;
        return false;
    }
    // print received message to terminal
    buffer[n] = '\0';
    printf("Received message from Client : %s\n",buffer);
    return true;
}





Você pode fazer o download do programa através do link:



Baixe os outros arquivos:


Preparei também um passo a passo de como adquirir um STM32, por exemplo, direto da STMicroeletronics: Baixe aqui o PDF


Postar um comentário

2 Comentários

  1. hola amigo
    No logre implementarlo en mi placa no le falta algún archivo en el programa con lo debugea con stm32cube ide lo corre??
    El cable ethernet de la placa a donde lo conecta??
    De casualidad tiene el programa completo, seria de gran ayuda.

    Otra consulta a usado el modulo de wifi ISM43340-M4G-L44-10 CF de la placa stm32h7b3i-dk?

    conexión de placa stm32h7b3i-dk con módulo wifi ISM43340-M4G-L44-10 CF
    Cuando conecto la placa de desarrollo stm32h7b3i-dk al pc a través del usb a tera para usar el módulo wifi (ISM43340-M4G-L44-10 CF), qué velocidades de baudios tengo que poner o cuál es la configuración, no aparece nada en la comunicación serial?

    ¿Sabes cómo utilizar el módulo wifi (ISM43340-M4G-L44-10 CF) de la placa stm32h7b3i-dk?

    ¿Algún ejemplo de un servidor http WIFI con stm32h7b3i-dk ???

    ResponderExcluir