Hogyan kapcsolódjunk alap socket szinten az ESP32-vel egy távoli szerverhez

Tutorial ESP32 internet socket

Az internethez való csatlakozáshoz az ESP32 a szokásos internet socket megoldást alkalmazza, az LWIP alkalmazásával. Ha csatlakozni akarunk WiFi-n keresztül a nethez, először létre kell hozni a kapcsolatunkat egy WiFi állomással. Mivel a WiFi-ről a gyártó, azaz az Espressif nem nagyon hozott nyilvánosságra részletes információt, használjuk a szokásos módszert:

 WiFi.begin(ssid, password);  

while (WiFi.status() != WL_CONNECTED) {    

delay(500);    

Serial.print(".");  

}

 Utána, hogy csatlakozni tudjunk egy adott internetes oldalhoz, létre kell hoznunk egy socket-ot, egy internetes csatlakozási pontot, aminek én a socket_descriptor azonosítót adtam.

 int   socket_descriptor   =   socket(AF_INET , SOCK_STREAM , 0);

Létrejön egy socket, ami tulajdonképpen egy file. Visszatérési értékként kapunk egy 64-nél kisebb egész számot, egy socket descriptort, file descriptort, ami egy index. Megmutatja, hogy a fájlok jellemzőit leíró táblában hol található az adott socketra vonatkozó információ.

A socket 3 paramétere rendre:

  1.  : AF_INET ha IP4-et használunk, AF_INET6, ha IPV6-ot.
  2.  : SOCK_STREAM TCP esetén, SOCK_DGRAM UDP esetén.
  3.  : A prokollra adhatnánk információkat, ha szükséges lenne. Hagyjuk automatikuson, azaz 0-n.

Ahhoz, hogy csatlakozni tudjunk, meg kell adni a cél adatait egy sockaddr_in típusú struktúrában, aminek a példában a connect_to_remote_server_addr azonosítót adtam.

 struct    sockaddr_in      connect_to_remote_server_addr;

Megadjuk az elemeit, azaz a protocol típusát (AF_INET=IPv4):
          connect_to_remote_server_addr.sin_family = AF_INET;

A cél IP címét:
          connect_to_remote_server_addr.sin_addr.s_addr = inet_addr("216.58.208.110");
                     //ip address of google.com as a sample

A portot:
          connect_to_remote_server_addr.sin_port = htons( 80 );
                     //htons: translate an unsigned short integer into network byte order

Itt mintaként a Google-nak e sorok írásakori IP címe szerepel. Ezzel már kapcsolódhatunk a távoli szerverhez. Mi egy IPv4-hez passzoló, sockaddr_in struktúrában adtuk meg az adatokat, viszont a connect általánosabb, sockaddr típusú, de ugyanolyan hosszú struktúrát vár, ezért castolni kell.

connect(

socket_descriptor ,

(struct       sockaddr * )  &connect_to_remote_server_addr ,

sizeof(connect_to_remote_server_addr)

)

A kapcsolat felépítése után elküldjük a lekérést, tipikusan a "GET / HTTP/1.1\r\n\r\n" szöveget:

 char *  myMessage2server;  
myMessage2server = "GET / HTTP/1.1\r\n\r\n";

send(

socket_descriptor ,

myMessage2server ,

strlen(myMessage2server) ,

0

)

Mivel a létrehozott socket is egy fájl, használhatjuk a send helyett a write utasítást is (Google : "write to a file descriptor")

write(

socket_descriptor ,

myMessage2server ,

strlen(myMessage2server)

)

A send/write, illetve a recv/read közötti különbség, hogy előbbi esetén van egy negyedik "flag" paraméter, ami általában 0, de pl. peek esetén MSG_PEEK, azaz 1.

A lekérés elküldése után várjuk a választ:

 int received_data_size_max = 5000 ;  
char received_data_from_server [ received_data_size_max ]

recv(

socket_descriptor,

received_data_from_server,

received_data_size_max ,

0

)

Ebben a példában a választ kiíratjuk kiíratjuk:

printf(received_data_from_server)

Válaszként egy így kezdődő üzenetet kapunk:

HTTP/1.1 200 OK

A végén a kapcsolatot be kell zárni:

close(socket_descriptor)

Mivel a socket is file, használhatók a fájlműveletek is. Például a következő megoldás a beérkező első 5 sort kiírja:

  FILE* filePointer = fdopen(socket_descriptor, "r+");
        if (filePointer == nullptr){ printf("\n\nnullpointer\n\n\n");};

  fprintf (filePointer, myMessage2server) ;
 
  char inputChars[200];
  for (int i = 0;i<5;i++){
         fgets(inputChars, 199, filePointer);
        printf("%s", inputChars);
  } ;

Végezetül megjegyzem, hogy az egyes utasítások helyett az LWIP utasításai fordítódnak be, például így:
          #define    lwip_socket    socket
          #define    lwip_write      write

Vissza (egy szinttel fel)

A teljes program:

Tömörebb változat:

/*
 tutorial
 receive data from remote server 
 */
#include <WiFi.h>
#include <lwip/sockets.h>

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

void setup() {

  Serial.begin(115200);
  delay(2000);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  printf("\nconnected to WiFi \n");
  
  int socket_descriptor = socket(AF_INET , SOCK_STREAM , 0);
  if (socket_descriptor == -1)  {
    printf("Could not create socket");
  }

  struct sockaddr_in connect_to_remote_server_addr;

  connect_to_remote_server_addr.sin_family = AF_INET;
  connect_to_remote_server_addr.sin_addr.s_addr = inet_addr("216.58.208.110"); // Google 
  connect_to_remote_server_addr.sin_port = htons( 80 ); 

  if (connect(
              socket_descriptor , 
              (struct sockaddr *)&connect_to_remote_server_addr , 
              sizeof(connect_to_remote_server_addr)
             ) < 0
     )
  {   // if < 0
    printf("Connect error to remote server\n");
  } 
  else  {
    printf("Connected to remote server\n\n");
  } ;  // end if

  FILE* filePointer = fdopen(socket_descriptor, "r+");
        if (filePointer == nullptr){ printf("\n\nnullpointer\n\n\n");};

  fprintf (filePointer, "GET / HTTP/1.1\r\n\r\n") ;
 
  char inputChars[200];
  for (int i = 0;i<5;i++){
         fgets(inputChars, 199, filePointer);
        printf("%s", inputChars);
  } ;

  close(socket_descriptor);

} ; // end setup

void loop() {
}

 

Kommentekkel:

/*
 tutorial
 receive data from remote server 
 */
#include <WiFi.h>
#include <lwip/sockets.h>

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


void setup() {

  Serial.begin(115200);
  delay(2000);

  Serial.println("setup started");

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  printf("\nconnected to WiFi \n");
  
  int socket_descriptor = socket(AF_INET , SOCK_STREAM , 0);
      // socket descriptor = file descriptor
      
  printf("socket_descriptor = %d \n",socket_descriptor);
  // AF_INET : address family IPv4
  // SOCK_STREAM : TCP socket

  //#define AF_INET         2
  //#define AF_INET6        10
  
  //#define SOCK_STREAM     1 //TCP socket
  //#define SOCK_DGRAM      2 //UDP socket
  //#define SOCK_RAW        3 //used to generate/receive packets of a type 
                              //that the kernel doesn't explicitly support.

  if (socket_descriptor == -1)
  {
    printf("Could not create socket");
  }

  struct sockaddr_in connect_to_remote_server_addr;


//members are in network byte order 
//struct sockaddr_in {  //LWIP_IPV4, sockaddr_in for IPv4   
//  u8_t            sin_len;
//  sa_family_t     sin_family;
//  in_port_t       sin_port;
//  struct in_addr  sin_addr;
//  #define SIN_ZERO_LEN 8
//  char            sin_zero[SIN_ZERO_LEN];
//};

  connect_to_remote_server_addr.sin_family = AF_INET;
  connect_to_remote_server_addr.sin_addr.s_addr = inet_addr("216.58.208.110");  
          //ip address of google.com as a sample
  connect_to_remote_server_addr.sin_port = htons( 80 ); 
          //htons: translate an unsigned short integer into network byte order

  //Connect to remote server
  // struct sockaddr {  // = protocol independent ("polymorphism")
  //  u8_t        sa_len;
  //  sa_family_t sa_family;
  //  char        sa_data[14];
  //};
  
  if (connect(
              socket_descriptor , 
              (struct sockaddr *)&connect_to_remote_server_addr , 
              sizeof(connect_to_remote_server_addr)
             ) < 0
     )
  {   // if < 0
    printf("Connect error to remote server\n");
  } else
  {
    printf("Connected to remote server\n");
  } ;  // end if
;
  //Send some data
  char *myMessage2server;
  myMessage2server = "GET / HTTP/1.1\r\n\r\n";


  if( send(socket_descriptor , myMessage2server , strlen(myMessage2server) , 0) < 0)
    // or write(socket_descriptor , myMessage2server , strlen(myMessage2server))
  {
    printf("Send failed to remote server\n");

  } else
    {
    printf("Data send to remote server\n");
  } ;

  
  //Receive data on socket
  //Receive a reply from the server
  int received_data_size_max = 5000 ;
  char received_data_from_server[received_data_size_max];
  if( 
      recv(
            socket_descriptor, 
            received_data_from_server, 
            received_data_size_max , 
            0 // or MSG_PEEK: #define MSG_PEEK 0x01 Peeks at an incoming message
          ) < 0)  // or read(socket_descriptor,received_data_from_server,received_data_size_max) 
  {  // if < 0
    printf("receive from server failed\n");
  }else
  {
      printf("Reply received from remote server:\n");
      printf(received_data_from_server);
  } ;  // end if

//Close socket:
close(socket_descriptor);


} ; // end setup

void loop() {
  // put your main code here, to run repeatedly:

}