Raspberry PI Tutorial

Solutions IoT personnalisées – Comment faire des requêtes HTTP GET

Par Thomas, le 13 novembre 2020
Solutions IoT personnalisées - Comment faire des requêtes HTTP GET

Dans le dernier projet maker, nous avons appris à utiliser HTTP sur un microcontrôleur ESP32 pour envoyer des données à un site PHP. Dans ce projet de création, nous allons apprendre comment faire des requêtes HTTP GET, obtenir la dernière valeur d’une variable et créer une technique de sauvegarde de variable plus avancée qui permet d’enregistrer dans n’importe quelle variable que nous voulons.

Solutions IoT personnalisées Partie 1 – Introduction à l’Internet des objets

Solutions IoT personnalisées, partie 2 – ESP32 vs ESP8266

Solutions IoT personnalisées, partie 3 – HTTP vs MQTT

Solutions IoT personnalisées, partie 4 – Créer un système de publication HTTP

IoT personnalisé, partie 5 – Comment faire des requêtes HTTP GET

Limitations actuelles

Le dernier projet que nous avons créé nous a permis de stocker des données sur un site PHP en utilisant un ESP32. Cependant, cela présentait certaines limites, la première et la plus évidente étant que nous avons codé en dur la variable dans laquelle les données soumises étaient enregistrées. Par conséquent, il serait plus approprié que, au lieu de simplement enregistrer toutes les données entrantes dans la même variable (dans le cas précédent, la température), nous définissions à la place dans quelle variable nous sauvegardons en premier, puis la valeur de ces données. L’autre limitation de l’exemple précédent était qu’il ne permet que le stockage de données et non l’obtention de données. Par conséquent, ce projet implémentera également les valeurs de retour d’une requête POST et examinera la requête HTTP GET afin que nous puissions récupérer le contenu d’une page Web. Le code de cet exemple sera également nettoyé et utilisera des fonctions pour permettre un accès facile aux variables depuis le serveur PHP. Ainsi, au lieu d’avoir tout le code défini dans la boucle principale de code, nous aurons à la place des fonctions qui traiteront les requêtes HTTP en dehors de la boucle principale et retourneront les données.

Qu’est-ce que HTTP GET?

Comme indiqué précédemment, HTTP est l’acronyme de HyperText Transfer Protocol et est le protocole qui pilote toutes les activités Internet basées sur le Web. Cependant, bien que son objectif principal soit les sites Web, il peut facilement être adapté pour une utilisation dans les appareils Internet des objets (IoT) en tant que méthode pour envoyer et recevoir des données depuis un serveur. Mais à quoi ressemble un message HTTP GET et en quoi diffère-t-il des messages HTTP POST? Pour répondre à cette question, jetons un œil à un message HTTP GET!

GET /page.html HTTP/1.0
Host: <SITE DOMAIN>

GET /page.html HTTP / 1.0

La première ligne est presque identique au message POST, la seule différence étant la commande GET au lieu de la commande POST. Assurez-vous que page.html est remplacé par la page à laquelle vous souhaitez accéder. La fin de cette ligne se termine par une paire retour chariot / saut de ligne (0x0A, 0x0D).

Hôte:

La première ligne est presque identique au message POST, la seule différence étant la commande GET au lieu de la commande POST. Assurez-vous que page.html est remplacé par la page à laquelle vous souhaitez accéder. La fin de cette ligne se termine par une paire retour chariot / saut de ligne (0x0A, 0x0D). Hôte: la deuxième ligne est la même que celle trouvée dans le message HTTP POST et définit simplement le site auquel on accède. Par exemple, si la demande concernait Google, cette ligne serait Host: www.google.com. Là encore, cette ligne se termine par une paire retour chariot / saut de ligne (0x0A, 0x0D).

Ligne vide

La troisième ligne est vide et contient uniquement une paire retour chariot / saut de ligne (0x0A, 0x0D). Ceci est crucial et met fin à la fin de tout le message HTTP GET, informant ainsi le serveur que le message est complet et prêt à être traité.

Réponse HTTP

Alors maintenant que nous savons comment envoyer une requête GET, nous devons comprendre à quoi ressemble une réponse et comment la traiter.

HTTP/1.0 200 OK
Date: Mon, 11 May 2020 19:12:54 GMT
Server: Apache
Content-Type: text/html; charset=UTF-8
Content-Length: 2

10

HTTP / 1.0 200 OK

La première ligne indique la version HTTP utilisée par le serveur ainsi que le code de réponse du serveur. Comme indiqué précédemment, le code de réponse nous indique si la réponse a réussi ou si ce n’est pas le cas et pourquoi. Par exemple, si le code est 200, la réponse a fonctionné et les données ont été trouvées avec succès. Si le code était 404, la page à laquelle nous essayons d’accéder n’existe pas. Et si le code était 500, le serveur rencontrait une erreur interne dont il ne pouvait pas faire rapport. Cette ligne se termine par une paire retour chariot / saut de ligne (0x0A, 0x0D).

Deux lignes suivantes (date et type de serveur)

Les deux lignes suivantes sont des métadonnées qui incluent le type de serveur et la date actuelle. Le type de serveur ne sera probablement jamais une information utile, mais la date peut être pratique dans les applications IoT qui n’incluent pas de RTC et souhaitent synchroniser leur heure avec un serveur. Les deux lignes se terminent par des paires retour chariot / saut de ligne (0x0A, 0x0D).

Content-Type: texte / html; jeu de caractères = UTF-8

Cette ligne informe notre appareil connecté du format des données renvoyées. Dans ce cas (et le nôtre), le type de données renvoyé était text / HTML. HTML est combiné dans cette définition car HTML est du texte et il est donc plus simple que les deux ne soient combinés. Nous pouvons ignorer cette ligne car nous savons déjà que les données d’une variable seront au format texte. Cette ligne se termine par une paire retour chariot / saut de ligne (0x0A, 0x0D).

Contenu-Longueur: 2

Cette ligne indique la longueur de la réponse du serveur après le message d’en-tête. Cette ligne se termine par une paire retour chariot / saut de ligne (0x0D, 0x0A);

Ligne vide

La ligne vide contient uniquement une paire retour chariot / saut de ligne (0x0A, 0x0D). Ceci est crucial et indique à notre appareil où se termine l’en-tête du message et où les données commencent.

Les données

Tout ce qui se trouve après la ligne vide est des données et dans ce cas, ce sont des données envoyées à l’aide de la méthode fragmentée. Par conséquent, les premières données à recevoir sont le nombre de caractères de long que les données sont, puis la ligne suivante est les données elles-mêmes. Notez que bien que ce serveur utilise la méthode fragmentée pour la transmission de données, les données ne seront probablement pas fragmentées et donc l’ignoreront.

Nouveau code de serveur PHP

Maintenant que nous comprenons comment les en-têtes HTTP recherchent le message GET, il est temps de changer le code sur notre serveur afin que nous puissions implémenter cette nouvelle fonctionnalité. Nous modifierons également le code afin que les périphériques informent le serveur de la variable dans laquelle ils souhaitent enregistrer et de la valeur des données pour cette variable.

<?php
		
	// Handle POST requests
	if ($_SERVER['REQUEST_METHOD'] === 'POST')
	{
		// Get the variable and data that we are saving to as well as the command
		$variable = $_POST["variable"];
		$data = $_POST["data"];
		$command = $_POST["command"];
		
		// Read Command - Get the latest value of a variable
		if($command == "READ")
		{
			// Get the variable that is being requested
			$variable = $_POST["variable"];
			
			// Create the file name that we will save the data to
			$fname="iot/" . $variable . '.txt';
			
			// Read all lines from the variable file
			$lines = file($fname, FILE_IGNORE_NEW_LINES);
		
			// Get the total number of lines in that file
			$numberOfLines = count($lines);
			
			// Obtain the last line in the file
			$latestEntry = $lines[$numberOfLines - 1];
			
			// Split that entry into its date and variable
			$strSplit = explode(">", $latestEntry);
			
			// Send the data back to the device
			echo($strSplit[1]);
		}
		
		// Write Command - Write a value to a variable
		if($command == "WRITE")
		{				
			// Create the file name that we will save the data to
			$fname="iot/" . $variable . '.txt';
			
			// Get data the data was sent
			$time = date('m/d/Y h:i:s a', time());
			
			// Append the received data into a text file on the server
			// Note that we are now using the '>' symbol as it will make parsing simpler later ;)
			file_put_contents($fname, strval($time) . '>' . $data  . "n", FILE_APPEND);	
			
			echo("WRITE OK");
		}
	}
	
	// Handle GET requests
	else
	{
		echo("Welcome to the IoT Server");
	}
?>

Nouveau code ESP32

Le code de l’ESP32 a été modifié pour inclure trois nouvelles fonctions; readData, writeData et getPage.

  • String readData (String variableName)
    • Cela renverra la dernière valeur de la variable passée à la fonction
  • void writeData (String variableName, String variableData)
    • Cela enregistrera la variableData dans variableName sur le serveur
  • String getPage (chaîne nom_page)
    • Cela renverra le contenu d’une page Web
#include <WiFi.h>

// Wi-Fi details here
const char* ssid     = "Spock-Net";
const char* password = "06426705";

// Website that we will connect to here
const char* host = "mycustomiotplatform.atwebpages.com";
const int httpPort = 80;

// Some variable declarations here
String message;
String serverMessage;
unsigned char tempBuffer[256];

// Functions for website communication
String readData(String variableName);
void writeData(String variableName, String variableData);
String getPage(String pageName);

// Use WiFiClient class to create TCP connections
WiFiClient client;


// Setup function
void setup()
{
  Serial.begin(115200);
  delay(10);

  // We start by connecting to a WiFi network
  WiFi.begin(ssid, password);

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


// Main loop code
void loop()
{
  // Prevent too many data sends!
  delay(2000);
  writeData("temperature", "10");
  delay(2000);
  Serial.println("Latest value = " + readData("temperature"));
  delay(2000);
  Serial.println(getPage("page.php"));
}


// **********************************************************************************
// Read data to our HTTP IoT Server
// Takes a single variable name, returns the current value
String readData(String variableName)
{
    // Attempt to connect to the HTTP server
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return "";
  }

  // Create a message to send to the server
  message = "command=READ&variable=" + variableName;

  // Now that we are connected, lets make the HTTP header message
  client.println("POST /page.php HTTP/1.0");
  client.println("Host: " + (String)host);
  client.println("Content-Type: application/x-www-form-urlencoded");
  client.println("Content-Length: " + (String)message.length());
  client.println();
  client.print(message);

  // Now wait for the response
  serverMessage = "";

  while (serverMessage.indexOf("rnrn") == -1)
  {
    // Clear the buffer and ensure it is full of zeros
    memset(tempBuffer, 0, sizeof(tempBuffer));
    
    // Read data into a tempoary buffer
    client.read(tempBuffer, sizeof(tempBuffer));
    
    // Transfer the contents of the buffer into our string
    serverMessage += (char*)tempBuffer;
  }

  // Find where the header message ends
  int dataPointer = serverMessage.indexOf("rnrn") + 4;

  // Return the string from that point on
  return serverMessage.substring(dataPointer);
}



// **********************************************************************************
// Write data to our HTTP IoT Server
// Takes a variable name and data
void writeData(String variableName, String variableData)
{
  // Attempt to connect to the HTTP server
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return;
  }

  // Create a message to send to the server
  message = "command=WRITE&variable=" + variableName + "&data=" + variableData;

  // Now that we are connected, lets make the HTTP header message
  client.println("POST /page.php HTTP/1.0");
  client.println("Host: " + (String)host);
  client.println("Content-Type: application/x-www-form-urlencoded");
  client.println("Content-Length: " + (String)message.length());
  client.println();
  client.print(message);

  // Now wait for the response
  serverMessage = "";

  while (serverMessage.indexOf("rnrn") == -1)
  {
    // Clear the buffer and ensure it is full of zeros
    memset(tempBuffer, 0, sizeof(tempBuffer));
    
    // Read data into a tempoary buffer
    client.read(tempBuffer, sizeof(tempBuffer));
    
    // Transfer the contents of the buffer into our string
    serverMessage += (char*)tempBuffer;
  }
}


// **********************************************************************************
// Get page from our HTTP IoT Server
// Asks for the page that you want to get
String getPage(String pageName)
{
    // Attempt to connect to the HTTP server
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return "";
  }

  // Now that we are connected, lets make the HTTP header message
  client.println("GET /" + pageName + " HTTP/1.0");
  client.println("Host: " + (String)host);
  client.println();

  // Now wait for the response
  serverMessage = "";

  while (serverMessage.indexOf("rnrn") == -1)
  {
    // Clear the buffer and ensure it is full of zeros
    memset(tempBuffer, 0, sizeof(tempBuffer));
    
    // Read data into a tempoary buffer
    client.read(tempBuffer, sizeof(tempBuffer));
    
    // Transfer the contents of the buffer into our string
    serverMessage += (char*)tempBuffer;
  }
  
  // Find where the header message ends
  int dataPointer = serverMessage.indexOf("rnrn") + 4;

  // Return the string from that point on
  return serverMessage.substring(dataPointer);
}

Comment créer un exemple HTTP GET – Réflexions finales

IoT personnalisé, partie 5 - Comment faire des requêtes HTTP GET

Cet exemple montre un système IoT fonctionnel qui permet à un ESP32 de communiquer avec un serveur PHP où les variables peuvent être enregistrées et récupérées. Cet exemple pourrait être poursuivi avec l’utilisation de la sécurité, des tableaux de bord et des canaux de communication, mais comme indiqué dans un article précédent, nous nous pencherons sur une version hybride de MQTT qui nous permettra d’avoir des connexions permanentes avec une communication inter-appareils et mises à jour instantanées. Mais cet exemple montre comment HTTP peut être utilisé pour d’autres choses et pas seulement pour la transmission de pages Web hébergées d’un serveur à un client.

Thomas

Thomas

Fan de Raspberry Pi de la première heure, je suis l'évolution de ces micro-pc depuis maintenant 5 ans. En plus de l'actualité je propose de nombreux tutoriels pour vous aider à exploiter pleinement votre nano-ordinateur côté domotique ou retro-gaming.