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(ð); //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
2 Comentários
O link do pdf está quebrado!!!
ResponderExcluirhola amigo
ResponderExcluirNo 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 ???