Cours du Bitcoin avec Arduino

Récupération du cours du Bitcoin

La récupération du cours du Bitcoin commence par l’interrogation de CoinGecko.

Les échanges https avec l’API CoinGecko (émission et réception) sont réalisés par la fonction “DemandeCoinGecko” déclarée comme suit:

void DemandeCoinGecko(char*, char*);

Étudions les différentes étapes à réaliser dans la fonction “DemandeCoinGecko”.

Avant d’envoyer une requête à l’API il est d’abord nécessaire de s’y connecter. Cette opération s’effectue à l’aide de la méthode “connect” de l’objet “ClientWeb_G” (le client de la connexion HTTPS ) en lui passant en paramètre l’adresse de l’API ainsi que le port Ethernet utilisé (le port 443 est le port standard pour le trafic HTTPS):

ClientWeb_G.connect("api.coingecko.com", 443)

Nous nous intéressons maintenant à la construction de la requête permettant de récupérer le cours de la cryptomonnaie.

D’après la documentation, la requête permettant de récupérer la valeur d’une cryptomonnaie a la forme suivante:

​”simple​/price/”

avec les paramètres:

  • “ids” pour spécifier l’identifiant de la crypto qui nous intéresse (“bitcoin” dans ce cas)
  • “vs_currencies” pour définir la monnaie dans laquelle nous voulons que sa valeur soit exprimée (“eur” pour l’euro €).

L’URL de cette requête est à ajouter à la suite de l’URL de base, commune à toutes les requête de l’API CoinGecko:

“api.coingecko.com/api/v3”

La requête https permettant de récupérer la valeur actuelle du bitcoin s’écrit donc:

"GET https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=eur HTTP/1.0"

L’objet “ClientWeb_G” fournit les méthodes:

  • println pour envoyer les commandes html au serveur HTTPS,
  • available pour déterminer si une réponse serveur a été reçue et la taille de cette réponse en octets,
  • read pour lire caractère par caractère la réponse du serveur,
  • readStringUntil pour lire tout les caractères de la réponse jusqu’à trouver le caractère passé en paramètre de la fonction.

Pour fermer la connexion à l’API CoinGecko une fois la requête envoyée et la réponse reçue, nous utiliserons la méthode “close” de l’objet “ClientWeb_G”:

ClientWeb_G.stop();

La fonction “DemandeCoinGecko” que je vous propose prend en premier paramètre l’adresse de la chaîne de caractères destination de le réponse de l’API CoinGecko et en second paramètre l’identifiant CoinGecko de la cryptomonnaie concernée. Cette fonction permet donc de gérer d’autre cryptomonnaies que le bitcoin. Voici le code de la fonction:

void DemandeCoinGecko(char *reponse_p, char *crypto_id_p)
{
  char requete_l[150], id_minuscule_l[20];
  int index_donnees_l;
  
  while(!ClientWeb_G.connected())
  {
    if(WiFi.status()==WL_CONNECTED)
    {    
      ClientWeb_G.connect("api.coingecko.com", 443);
      Serial.println("Connexion à CoinGecko ...");
    }
    else
    {
      GestionConnexionWifi();
    }
  }
  ConversionMinuscule(id_minuscule_l, crypto_id_p);
  sprintf(requete_l, "GET https://api.coingecko.com/api/v3/simple/price?ids=%s&vs_currencies=eur HTTP/1.0", id_minuscule_l);
  Serial.print("requete_l=");
  Serial.println(requete_l);
  ClientWeb_G.println(requete_l);
  ClientWeb_G.println("Host: api.coingecko.com");
  ClientWeb_G.println("Connection: close");
  ClientWeb_G.println();
  
  while(ClientWeb_G.connected() && !ClientWeb_G.available())
  {
    delay(1); // On attend la réponse du serveur
  }
  while (ClientWeb_G.available()&&(ClientWeb_G.readStringUntil('\n')!="\r")); // On évacue l'entête de la réponse
  for(index_donnees_l=0; ClientWeb_G.available(); index_donnees_l++)
  {
    reponse_p[index_donnees_l] = (char)ClientWeb_G.read(); 
  }
  ClientWeb_G.stop();
  reponse_p[index_donnees_l] = NULL;  // Ajout du caractère de fin de chaine
  Serial.print("reponse_p=");
  Serial.println(reponse_p);
}

Vous remarquerez que la fonction “DemandeCoinGecko” fait appel à une sous-fonction pour formater l’identifiant de la crypto avant de l’envoyer à CoinGecko. En effet, n’ayant pas trouvé de fonction Arduino de conversion d’une chaine de caractères en minuscules, j’ai créé la fonction  “ConversionMinuscule”.

Voici sa déclaration:

void ConversionMinuscule(char*, char*);

Et le corps de la fonction:

void ConversionMinuscule(char *destination_p, char *source_p)
{
  for(int i = 0; source_p[i]; i++)
  {
    destination_p[i] = tolower(source_p[i]);
  }
  destination_p[strlen(source_p)] = NULL; // Ajout du caractère de fin de chaîne
}

La trame de réponse envoyée par CoinGecko aura la forme {“bitcoin”:{“eur”:?????}} où les points d’interrogation symbolise la valeurs actuelle du cours.

Dans cette trame, seule la valeur du Bitcoin nous intéresse . La fonction “ExtractionCours” permet:

  • D’extraire cette valeur en utilisant le caractère “:” comme indicateur de début et l’accolade fermante “}” comme indicateur de fin.
  • De remplacer l’éventuel séparateur de décimales anglo-saxon (le point) par un séparateur de décimales français (la virgule).
  • D’ajouter le symbole de l’unité monétaire pour la valeur du cours (l’euro).

Et voici le résultat:

bool ExtractionCours(char *valeur_p, char *reponse_p)
{
  bool resultat_l = false;
  char partie_entiere[10], partie_decimale[10]; 
  const char* index_2points_l = strrchr(reponse_p, ':');
  const char* index_accolade_fermante_l = strchr(reponse_p, '}');
  int longueur_valeur_l;
  char* index_point_l;
  
  if ((index_2points_l != NULL)&&(index_accolade_fermante_l != NULL))
  {
    longueur_valeur_l=index_accolade_fermante_l-(index_2points_l+1);
    strncpy(valeur_p, index_2points_l+1, longueur_valeur_l);
    valeur_p[longueur_valeur_l] = NULL; // Ajout du caractère de fin de chaîne
    strcat(valeur_p, "€");
    index_point_l =  strchr(valeur_p, '.');
    if (index_point_l != NULL)
    {
      *index_point_l=',';  
    }
    resultat_l = true;  
  }
  return(resultat_l);
}

Compilation et vérification de la récupération du cours du Bitcoin

Nous avons terminé de coder la récupération du cours du Bitcoin auprès de CoinGecko. Voici l’état du programme Arduino à la fin de cette étape:

// Déclaration des bibliothèques utilisées
#include <WiFiClientSecure.h>

// Définition des constantes globales
#define SSID_L          "LiveBox_1324" // Nom du réseau Wi-Fi
#define MOT_DE_PASSE_L  "12345?ABCDE" // Mot De Passe du réseau Wi-Fi

// Prototypes de fonction
void GestionConnexionWifi(void);
void DemandeCoinGecko(char*, char*);
void ConversionMinuscule(char*, char*);
bool ExtractionCours(char*, char*);

// Déclarations des variables globales
WiFiClientSecure ClientWeb_G;

// Fonction de démarrage, s'exécute une seule fois:
void setup()
{
  Serial.begin(115200); // Ouverture du port série à 115200 bauds
  
  GestionConnexionWifi();
}

// Fonction principale du programme, s'exécute en boucle:
void loop()
{
  // Variables de la fonction
   char cours_l[20], reponse_coingecko[150];

  GestionConnexionWifi();
  DemandeCoinGecko(reponse_coingecko, "Bitcoin");
  if(ExtractionCours(cours_l, reponse_coingecko))
  {
    Serial.print("Valeur du Bitcoin: ");
    Serial.println(cours_l);
  }
  else
  {
    Serial.print("Impossible d'obtenir la valeur du Bitcoin");
  }
  delay(10000); // Attente de 10 secondes
}

void GestionConnexionWifi()
{
  // Variables de la fonction
  static wl_status_t StatutConnexion_L = WL_DISCONNECTED; // Pour mémoriser l'état de la connexion

  // Verification du statut d ela connexion
  StatutConnexion_L = WiFi.status();

  if(WiFi.status()!=WL_CONNECTED)
  {
    Serial.println("Connexion au point d'accès Wi-Fi ...");
    WiFi.begin(SSID_L, MOT_DE_PASSE_L); // Tentative de connexion au point d'accès Wi-Fi
    StatutConnexion_L = WiFi.status(); // Lecture de l'état de la connexion et mémorisation dans la variable "StatutConnexion_L"
    while ((StatutConnexion_L != WL_NO_SSID_AVAIL)&&(StatutConnexion_L != WL_CONNECTED)&&(StatutConnexion_L != WL_CONNECT_FAILED))
    {
      delay(1200);
      StatutConnexion_L = WiFi.status(); // Lecture de l'état de la connexion et mémorisation dans la variable "StatutConnexion_L"
    }
    // Affichage du résultat de la tentative de connexion
    if (StatutConnexion_L == WL_CONNECTED)
    {
      Serial.println("Connection OK");
      ClientWeb_G.setInsecure();  //  On ne vérifie pas les certificats ni les empreintes https 
    }
    else if (StatutConnexion_L == WL_NO_SSID_AVAIL)
    {
      Serial.println("SSID introuvable");
    }
    else if (StatutConnexion_L == WL_CONNECT_FAILED)
    {
      Serial.println("Mot de passe KO");
    }
    else
    {
      Serial.println("Autre erreur");
    }   
  }  
}

void DemandeCoinGecko(char *reponse_p, char *crypto_id_p)
{
  char requete_l[150], id_minuscule_l[20];
  int index_donnees_l;
  
  while(!ClientWeb_G.connected())
  {
    if(WiFi.status()==WL_CONNECTED)
    {    
      ClientWeb_G.connect("api.coingecko.com", 443);
      Serial.println("Connexion à CoinGecko ...");
    }
    else
    {
      GestionConnexionWifi();
    }
  }
  ConversionMinuscule(id_minuscule_l, crypto_id_p);
  sprintf(requete_l, "GET https://api.coingecko.com/api/v3/simple/price?ids=%s&vs_currencies=eur HTTP/1.0", id_minuscule_l);
  Serial.print("requete_l=");
  Serial.println(requete_l);
  ClientWeb_G.println(requete_l);
  ClientWeb_G.println("Host: api.coingecko.com");
  ClientWeb_G.println("Connection: close");
  ClientWeb_G.println();
  
  while(ClientWeb_G.connected() && !ClientWeb_G.available())
  {
    delay(1); // On attend la réponse du serveur
  }
  while (ClientWeb_G.available()&&(ClientWeb_G.readStringUntil('\n')!="\r")); // On évacue l'entête de la réponse
  for(index_donnees_l=0; ClientWeb_G.available(); index_donnees_l++)
  {
    reponse_p[index_donnees_l] = (char)ClientWeb_G.read(); 
  }
  ClientWeb_G.stop();
  reponse_p[index_donnees_l] = NULL;  // Ajout du caractère de fin de chaine
  Serial.print("reponse_p=");
  Serial.println(reponse_p);
}

void ConversionMinuscule(char *destination_p, char *source_p)
{
  for(int i = 0; source_p[i]; i++)
  {
    destination_p[i] = tolower(source_p[i]);
  }
  destination_p[strlen(source_p)] = NULL; // Ajout du caractère de fin de chaîne
}

bool ExtractionCours(char *valeur_p, char *reponse_p)
{
  bool resultat_l = false;
  char partie_entiere[10], partie_decimale[10]; 
  const char* index_2points_l = strrchr(reponse_p, ':');
  const char* index_accolade_fermante_l = strchr(reponse_p, '}');
  int longueur_valeur_l;
  char* index_point_l;
  
  if ((index_2points_l != NULL)&&(index_accolade_fermante_l != NULL))
  {
    longueur_valeur_l=index_accolade_fermante_l-(index_2points_l+1);
    strncpy(valeur_p, index_2points_l+1, longueur_valeur_l);
    valeur_p[longueur_valeur_l] = NULL; // Ajout du caractère de fin de chaîne
    strcat(valeur_p, "€");
    index_point_l =  strchr(valeur_p, '.');
    if (index_point_l != NULL)
    {
      *index_point_l=',';  
    }
    resultat_l = true;  
  }
  return(resultat_l);
}

Après avoir compilé puis transféré ce programme Arduino de récupération du cours du Bitcoin, je vous invite à visualiser de nouveau les traces de déverminage:

Traces des échanges avec CoinGecko lors de la récupération du cours du Bitcoin

Dans la copie d’écran ci-dessus sont encadrées les 3 nouvelles traces produites par le code que nous venons d’ajouter:

  • La première trace affiche la trame complète envoyée à CoinGecko. Nous pouvons vérifier sa syntaxe ainsi que les paramètres “bitcoin” et “eur” transmis.
  • La deuxième trace affiche la trame reçue de CoinGecko.
  • La troisième trace affiche le résultat de la conversion réalisée à l’aide de la fonction “ExtractionCours”.

Passons maintenant à l’affichage graphique (page suivante).

Laisser un commentaire