18 de outubro de 2021

Do bit Ao Byte

Embarcados, Linux e programação

Socket client com ESP32

arrays dinâmicos em C++ | Interagir com ponteiros | Alocação de memória | Ponteiros em C/C++ | Socket server com ESP8266 | Socket server com ESP32 | Socket server com Python | Socket client com ESP32 | Sistema de arquivos no ESP8266

No artigo “Socket server com Python” vimos como implementar a ferramenta de teste para desenvolvimento. Não só o server, mas implementamos também o client, que nos auxiliará nos artigos sobre socket server com ESP32 e ESP8266. Nesse artigo veremos como fazer um socket client com ESP32, utilizando uma quantidade ínfima de linhas de código, e tendo rapidamente um bom resultado para uma comunicação leve.

Código básico do socket client com ESP32

Esse código é para a prova de conceito, então use-o como base para a implementação, mas continue a leitura para ver um outro exemplo.

#include <WiFi.h>
 
#define SSID "suaRede"
#define PASSWD "suaSenha"

unsigned long int last_time = millis();

const uint16_t port = 8888;
const char * host = "192.168.10.100";
 
void setup(){
    Serial.begin(9600);
 
    WiFi.begin(SSID,PASSWD);
    while (WiFi.status() != WL_CONNECTED){delay(100);}
 
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());
}
 
void loop(){
    WiFiClient client;
 
    if (!client.connect(host, port)) {
        Serial.println("Falha de conexao");
        delay(1000);
        return;
    }
 
    Serial.println("Conectado!");
 
    client.print("Hell low word");
 
    Serial.println("Desconectando...");
    client.stop();
 
    Serial.print("Aguardando 15 segundos para proximo envio:");
    while ((millis()-last_time) < 15000){delay(100);}
    last_time = millis();
}

Nesse exemplo forçamos uma rotina padrão que prende o fluxo em um loop. Nada mal para prova de conceito, mas tratando-se de um ESP32, podemos criar uma task para ser executada somente quando houver a necessidade da comunicação socket. Com isso, não precisamos temporizar e mandar periodicamente, servindo então como um processo de alarme.

Criando uma task para fazer a comunicação socket

Para tal, basta criar uma função antes de setup():

void vSocket(void *pvParameters){
    WiFiClient client;
    if (!client.connect(host, port)){ 
        Serial.println("Falha de conexao");
        delay(1000); 
        vTaskDelete(NULL);
    } 
    Serial.println("Conectado!");
    client.print("Hell low word");
    Serial.println("Desconectando...");
    client.stop();
    vTaskDelete(NULL);
}

Essa tarefa faz o envio e se encerra. Para executá-la, instancie-a no momento em que for necessário. Para exemplo, ela será executada apenas uma vez ao iniciar, sendo chamada dentro da função setup():

void setup(){
    //...tudo o que for em setup, então a última linha...
    xTaskCreatePinnedToCore(vSocket,"vSocket",10000,NULL,0,NULL,0);
}

Mas podemos iniciar a task em qualquer lugar, dentro da função loop() ou a partir de outra task.

Como testar a conexão socket

Para testar esse código, podemos abrir um socket server com python utilizando esse código:

import socket
HOST = '192.168.1.200'
PORT = 1234            
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
orig = (HOST, PORT)
tcp.bind(orig)
tcp.listen(1)
while True:
    con, client = tcp.accept()
    print( 'Client: ', client)
    while True:
        msg = con.recv(6)
        if not msg: break
        #print( client, msg)
        for i in range(len(msg)):
            print(chr(msg[i]),end=' ')

    print(" ")
    print( 'Closing connection to: ', client)
    con.close()

Troque o IP pelo IP do computador que estiver rodando o script.

Uma dica nesse código é em relação à função print(). Repare que como é lido e impresso byte a byte, é necessário manter tudo na mesma linha no output. Para não ter um line feed a cada byte, passamos o parâmetro end=’ ‘ ao final da função.

Essa tarefa pode ler o valor de uma variável global ou o valor pode ser passado via parâmetro. Se gostaria de saber como passar parâmetros em tasks com ESP32, recomendo a leitura desse artigo.

Os artigos em torno desse devem todos ser relacionados a comunicação socket server e client com ESP32, ESP8266 e Python, para fazer a comunicação de teste a partir do computador. Dê uma verificada nos artigos anteriores e posteriores para mais conteúdo sobre o tema!

Para ver o exemplo dessa comunicação e a utilização de parâmetros na task, acompanhe em nosso canal DobitaobyteBrasil no Youtube – agora com bem mais qualidade. Se não é inscrito e puder fazê-lo, será de imensa alegria tê-lo conosco.

Clique aqui para ir diretamente ao vídeo desse artigo.

Exemplo extra

Caso ainda tenha dúvidas de como iniciar a task, eis um exemplo simples, usando a função loop para fazer a repetição da chamada. Claro que se for pra fazer assim é mais simples criar uma função e executá-la, mas é só pra exemplificar:

#include <Arduino.h>
#include <WiFi.h>
#include "heltec.h"

#define OLED_UPDATE_INTERVAL 500

const char* ssid       = "SuhankoFamily"; 
const char* password   = "fsjmr112"; 

const char* host       = "192.168.1.200";
const uint16_t port    = 1234;

//Opção 2: task
void vSocket(void *pvParameters){
    WiFiClient client;
 
    if (client.connect(host, port) > -1) {
        Serial.println("Conectado!");
 
        client.print("Hell low word");
 
        Serial.println("Desconectando...");
        client.stop();
        vTaskDelete(NULL);

    }
    else{
        client.stop();
        vTaskDelay(pdMS_TO_TICKS(2000));
    }   
}

void setupWIFI(){
  Heltec.display->clear();
  Heltec.display->drawString(0, 0, "Connecting...");
  Heltec.display->drawString(0, 10, String(ssid));
  Heltec.display->display();
  WiFi.disconnect(true);
  delay(1000);
  WiFi.mode(WIFI_STA);
  //WiFi.onEvent(WiFiEvent);
  WiFi.setAutoConnect(true);
  WiFi.setAutoReconnect(true);    
  //WiFi.setHostname(HOSTNAME);
  WiFi.begin(ssid, password);
  byte count = 0;
  while(WiFi.status() != WL_CONNECTED && count < 10)
  {
    count ++;
    delay(500);
    Serial.print(".");
  }
  Heltec.display->clear();
  if(WiFi.status() == WL_CONNECTED){
    Heltec.display->drawString(0, 0, "Conectado");
    Heltec.display->drawString(0, 10, "Ate logo");
    Heltec.display->display();
    delay(5000);
    Heltec.display->clear();
     Heltec.display->display();
  }
  else{
    Heltec.display->drawString(0, 0, "Connect False");
    Heltec.display->display();
  }
  Heltec.display->clear();
}

void setup(){
   pinMode(0,INPUT_PULLDOWN);
    Heltec.begin(true /*DisplayEnable Enable*/, false /*LoRa Disable*/, true /*Serial Enable*/);
    pinMode(25, OUTPUT);
    while (!Serial) {
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    setupWIFI();
    //se escolhida a opção 2, a primeira chamada pode ser no setup:
    //
}

void loop() {
    xTaskCreatePinnedToCore(vSocket,"vSocket",10000,NULL,0,NULL,0);
    vTaskDelay(pdMS_TO_TICKS(4000));
    
}

 

Até o próximo artigo!

 

Revisão: Ricardo Amaral de Andrade