Creation reseau wifi

il faut faire quelques adaptations à ce que j'ai dit ci dessus

  • passer l'adresse de la structure
  • faire un cast du type pour mettre une const char*
    Modifications tapées ici, à tester
// FOURNISSEUR DE DONNEES
#include <WiFi.h>
#include <AsyncTCP.h>

const uint16_t serverPort = 7050;
AsyncServer server(serverPort); // écoute le port tcp 7050

const char* ssid = "Livebox-XXX";
const char* password = "XXXXXXXXXXXXXXXX";

struct t_message { // Infos a envoyer
  unsigned long compteur;
  unsigned long chrono;
} monMessage;

void reception(void* arg, AsyncClient* client, void *data, size_t len) {
  Serial.println(F("Demande "));
  if (client->space() > sizeof(monMessage) && client->canSend()) {
    monMessage.compteur++;
    monMessage.chrono = millis();
    client->add((const char*) &monMessage, sizeof(monMessage));
    client->send();
  }
}

void gestionClient(void* arg, AsyncClient* client) {
  Serial.print(F("Nouveau client: "));  Serial.println(client->remoteIP());
  client->onData(&reception, NULL);  // attacher le callback appeler reception() quand on reçoit des données
}

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.printf("Serveur: Erreur connexion WiFi\n");
    while (true) yield();
  }
  Serial.print("Serveur IP: "); Serial.println(WiFi.localIP());

  server.onClient(gestionClient, &server);
  server.begin();
}

void loop() {}

il faut bien sûr mettre la structure aussi côté réception et modifier l'impression du message pour imprimer le contenu de la structure (vous devriez voir un compteur qui augmente et la valeur de millis())

J'essaie ça
Je met la même structure coté client, en espérant que cela ne sera pas trop difficile a adapter.

Non ce n'est pas insurmontable, ça ressemblera à cela (tapé ici aussi)

// DEMANDE DE DONNEES
#include <WiFi.h>
#include <AsyncTCP.h>

IPAddress serverIP(10, 0, 0, 23); // <<=== ICI RENTRER L'ADRESSE IP DU SERVEUR
const uint16_t serverPort = 7050;

AsyncClient client;

const char* ssid = "*********";  // A REMPLIR
const char* password = "*********";  // A REMPLIR

struct t_message { // Infos a recevoir
  unsigned long compteur;
  unsigned long chrono;
} monMessage;

void demande(char variable) {
  if (client.space() > sizeof(variable) && client.canSend()) {
    Serial.print(F("envoi req pour ")); Serial.println(variable);
    client.add(&variable, sizeof(variable));
    client.send();
  } else Serial.println(F("MASTER: CAN'T SEND A REQUEST"));
}

void reception(void* arg, AsyncClient* client, void *data, size_t len) {
  if (len == sizeof(monMessage)) {
    memcpy(&monMessage, data, len); // on remplit la structure avec les données reçues
    Serial.print(F("Compteur = ")); Serial.print(monMessage.compteur);
    Serial.print(F(", chrono = ")); Serial.println(monMessage.chrono);
  } else Serial.println(F("Erreur de message. Taille incorrecte"));
}

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.printf("Master: Erreur connexion WiFi\n");
    while (true) yield();
  }

  client.onData(&reception, &client); // appeler reception() quand on reçoit des données
  client.connect(serverIP, serverPort);

  Serial.println(F("\n-----\nEntrez une variable entre 'a' et 'z'\n"));
}

void loop() {
  int r = Serial.read();
  if ((r >= 'a') && (r <= 'z')) demande(r); // simulation de demande d'une variable
}

la demande se fait toujours en tapant une lettre de a à z mais la lettre ne sert à rien, juste à déclencher la requête.

L'exemple fonctionne très bien.

Je vais intégrer ça a mon programme sur l'esp1, et faire une petite page web dessus aussi, pour voir la mémoire prise.

Pour la demande, je vais un appel avec millis (genre toutes les minutes).

Encore merci, et surement a bientôt :))

Bonne journée

pour la structure utilisez l'extension __attribute__ du compilateur GCC qui permet d'obtenir une représentation plus compacte (à définir de manière identique des 2 côtés)

struct __attribute__ ((packed)) t_message {
  unsigned long compteur;
  unsigned long chrono;
} monMessage;

ça optimisera le nombre d'octets à envoyer

Ok je vais faire comme ça.
A la louche, j'ai une quinzaine de floats et une dizaine d'int a envoyer.
Cela ne posera pas de problèmes?
Le temps que ça prend, n'est pas important.

Non pas de soucis je pense avec cette taille d’une centaine d’octets

J'avance peu a peu.
Les échanges de structure, c'est bon.

La j'ai crée une petite page web, en partant de l'exemple
d'allumer une led.

J'arrive a afficher le texte, mais je ne sais pas "intégrer" une variable.

Par exemple mettre la variable compteur au bout du texte "compteur"
Je remet le prog que j'utilise

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);

const char* ssid = "Livebox-XXX";
const char* password = "XXXXXXXXXXXXXXXX";

const byte ledPin = 5; // led bleue embarquée sur ma carte ESP32

unsigned long compteur;
unsigned long chrono;


  const char index_html[] PROGMEM = "<p>1ere Page de Gandalf60:</p><ul><li><a href=\"/on\">Compteur = </a></li><li><a href=\"/off\">Chrono = </a></li></ul>";
 // const char compteur_html[] PROGMEM = "<p>COMPTEUR= </p><ul><li><a href=\"/off\">Led OFF</a></li></ul>";
 // const char chrono_html[] PROGMEM = "<p>CHRONO=</p><ul><li><a href=\"/on\">Led ON</a></li></ul>";

void allumer() {
  digitalWrite(ledPin, HIGH);
}

void eteindre() {
  digitalWrite(ledPin, LOW);
}

void notFound(AsyncWebServerRequest *request) {
  request->send_P(200, "text/html", index_html);
}

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.printf("Erreur connexion WiFi\n");
    while (true) yield();
  }

  Serial.print("site web: http://"); Serial.println(WiFi.localIP());

  // définition des pages
  /*
    server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, "text/html", index_html);
    });

    server.on("/on", HTTP_GET, [](AsyncWebServerRequest * request) {
    allumer();
    request->send_P(200, "text/html", temp_html);
    });

    server.on("/off", HTTP_GET, [](AsyncWebServerRequest * request) {
    eteindre();
    request->send_P(200, "text/html", compteur_html);
    });
  */
  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, "text/html", index_html);
  });

 // server.on("/off", HTTP_GET, [](AsyncWebServerRequest * request) {
  //  request->send_P(200, "text/html", compteur_html);
  //});

// server.on("/off", HTTP_GET, [](AsyncWebServerRequest * request) {
  //  request->send_P(200, "text/html", chrono_html);
  //});
  

  server.onNotFound(notFound);
  server.begin();
}

void loop() {}

Question sup.
Dans le prog d'échange de structures on utilise le port
const uint16_t serverPort = 7050;

Et dans le prog allumer la led
AsyncWebServer server(80);

Il faut laisser comme cela, ou ne prendre qu'un port?

Merci

First page.JPG

First page.JPG

Il faut générer le html dynamiquement si vous voulez que la valeur soit affichée

AsyncWebServer sait faire des trucs pour remplacer des variables que vous auriez codé en dur dans le HTML, lisez la doc sur toutes les possibilités et la partie template-processing

Il faut bien conserver deux ports séparés, quand on parle au port 80 c’est envoyé au serveur http, quand on parle à l’autre port c’est traité directement au niveau TCP

Bonjour

Je vais étudier ça.

Merci et bonne journée

pour faire simple, vous déclarez dans une variable (ou un fichier) un bout de code HTML qui contient un mot clé entre des symboles %

const char etat_html[] PROGMEM = "<p>ETAT DE LA LED:</p>[color=red]%ETAT%[/color]";[tt]

lors d'une requête où vous devez fournir l'état de la LED, vous allez passer cette réponse mais vous fournissez une fonction qui sera appelée pour remplacer chaque mot clé (ici on n'en a qu'un seul).

[code]const byte ledPin = 13;
...
String processeurEtat(const String& var)
{
  if(var == "ETAT")  return F(digitalRead(ledPin) == HIGH ? "HIGH" : "LOW");
  return String();
}
...
// envoi d'une réponse avec un  template processor => la fonction processeurEtat()
request->send_P(200, "text/html", etat_html, processeurEtat);[/code]

ici donc j'envoie une réponse de type html, qui est contenu dans la variable etat_html mais avant de balancer ce texte au navigateur le système va analyser le contenu du texte, chercher tous les %xxx% et appeler processeurEtat() avec comme paramètre le xxx en question. la fonction retourne un texte qui est ce par quoi le mot clé doit être remplacé. Donc ici je renvoie le mot "HIGH" ou "LOW" en fonction de l'état de la led et donc la phrase qui sera vraiment envoyée au navigateur client c'est par exemple (si la led était allumée)
[tt]"<p>ETAT DE LA LED:</p>[color=red]HIGH[/color]"

Bonjour

Les essais d'envoi de variables Int et Float sont concluantes, mais seulement une a la fois.

Si j'essaie d'en envoyer 3 en même temps, seule la 1ere rencontrée par
le programme s'affiche ! sur la page web.

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);

const char* ssid = "Livebox-XXX";
const char* password = "XXXXXXXXXXXXXXXXXXXXX";

unsigned long compteur;
unsigned long chrono;

int potarPin = 33;//GPIO33   la 4 marche pas

int niveau = 367;
char chaine_niveau[5];// Pour conversion en String
float temperature = 19.6;
char chaine_temperature[8];// Pour conversion en String
int potarValue = 0;
char chaine_potar[5];// Pour conversion en String

const char index_html[] PROGMEM = "<p>1ere Page de Gandalf60:</p><ul><li><a href=\"/on\">Compteur = </a></li><li><a href=\"/off\">Chrono = </a></li></ul>";

const char niveau_html[] PROGMEM = "<p>NIVEAU:</p>%NIVEAU%";
const char temperature_html[] PROGMEM = "<p>TEMPERATURE:</p>%TEMPERATURE%";
const char potar_html[] PROGMEM = "<p>POTAR:</p>%POTAR%";

// const char compteur_html[] PROGMEM = "<p>COMPTEUR= </p><ul><li><a href=\"/off\">Led OFF</a></li></ul>";
// const char chrono_html[] PROGMEM = "<p>CHRONO=</p><ul><li><a href=\"/on\">Led ON</a></li></ul>";

void notFound(AsyncWebServerRequest *request) {
  request->send_P(200, "text/html", index_html);
}
//==========================================================================================================================
String processeurEtat(const String& var)
{
  if (var == "NIVEAU") {
    itoa(niveau, chaine_niveau, 10);// convertir l'entier en chaine de character "XXX" en base 10 (en décimale).
    return F(chaine_niveau);
  }

  if (var == "TEMPERATURE") {
    dtostrf (temperature, 8, 1, chaine_temperature);
    return F(chaine_temperature);
  }

  if (var == "POTAR") {
    itoa(potarValue, chaine_potar, 10);// convertir l'entier en chaine de character "XXX" en base 10 (en décimale).
    return F(chaine_potar);
  }
  return String();
}
//============================================================================================================================
void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.printf("Erreur connexion WiFi\n");
    while (true) yield();
  }

  Serial.print("site web: http://"); Serial.println(WiFi.localIP());

  // ==================================== définition des pages =============================================

  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, "text/html", potar_html, processeurEtat);
  });

  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, "text/html", temperature_html, processeurEtat);
  });

  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send_P(200, "text/html", niveau_html, processeurEtat);
  });

  server.onNotFound(notFound);
  server.begin();
}

void loop() {
  potarValue = analogRead(potarPin);

}

Bonne journée

vous avez trois fois server.on([color=red]"/"[/color]...
ça veut dire "si l'URL tapée dans le navigateur est pour la racine du site web alors faire..." --> vous ne pouvez pas avoir 3 pages différentes pour la racine, il n'en faut qu'une seule

faites une chaîne du genre

const char info_htm[] PROGMEM = "<p>1ere Page de Gandalf60:</p><p>NIVEAU:</p>%NIVEAU%<p>TEMPERATURE:</p>%TEMPERATURE%<p>POTAR:</p>%POTAR%";

et balancez cette réponse avec un

request->send_P(200, "text/html", info_htm, processeurEtat);

lors de l'accès à l'URL qui va bien

Bonjour

Je vois..., désolé de faire le boulet :slight_smile:

Je pars de zéro, et essaie de me débrouiller avec les exemples fournis.

Encore merci

pas de souci.

il faut juste se mettre dans l'idée qu'une page web = une URL = une seule réponse
si vous voulez plusieurs pages web pour votre site vous allez gérer une navigation
/ va être la racine qui permet d'accéder aux autres pages
/controle serait par exemple la gestion des paramètres
/chambre serait la vue sur la chambre
/salon serait la vue sur le salon

etc

Grace a vos explications très claires, j'ai réussi a "gérer" 2 pages (bon, c'est un peu austère).

Y a t'il une limite en longueur pour le const char?

const char info_htm[] PROGMEM = "<p>1ere Page de Gandalf60:</p><p>NIVEAU:</p>%NIVEAU%<p>TEMPERATURE:</p>%TEMPERATURE%<p>POTAR:</p>%POTAR%";

ou c'est juste une question de mémoire?

Encore un grand merci

c'est juste une question de mémoire (et il doit y avoir une limite aussi mais la mémoire sera le facteur limitant)

pour ne pas s'ennuyer avec ces chaînes dans le code principal, souvent on utilise ce qu'on appelle en C++ des raw string literals

  • Vous créez dans l'IDE un second onglet que vous appelez pageweb.h par exemple

  • Dans le sketch principal, le .ino vous rajoutez #include "pageweb.h"

  • Dans l'onglet pageweb.h vous écrivez

const char page1[] PROGMEM = R"--8<--(<p>1ere Page de Gandalf60:</p>
<p>NIVEAU:</p>%NIVEAU%
<p>TEMPERATURE:</p>%TEMPERATURE%
<p>POTAR:</p>%POTAR%)--8<--";

ça se lit comme cela en couleur:

const char page1[] PROGMEM = R"[color=blue]--8<--([/color][color=red]<p>1ere Page de Gandalf60:</p>
<p>NIVEAU:</p>%NIVEAU%
<p>TEMPERATURE:</p>%TEMPERATURE%
<p>POTAR:</p>%POTAR%[/color][color=blue])--8<--[/color]";

Un raw littéral va prendre tout ce qui est entre le symbole de début et de fin (le texte en rouge), entre la ( et avant la ) (-> le petit ciseau [color=blue]--8<--[/color] est juste là pour faire joli, vous pouvez mettre ce que vous voulez du moment qu'il y a la même chose des 2 côtés)

C'est pratique car vous n'avez pas besoin de tout tasser, vous pouvez passer à la ligne et donc juste coller du HTML qui viendrait d'un éditeur. (les passages à la lignes seront aussi envoyés; il font partie du texte)

Vous n'avez pas besoin non plus de mettre des \ pour les guillemets, donc vous pouvez mettre cela directement

const char page1[] PROGMEM = R"--8<--(<p>1ere Page de Gandalf60:</p>
<p>"NIVEAU":</p>%NIVEAU%
<p>"TEMPERATURE":</p>%TEMPERATURE%
<p>"POTAR":</p>%POTAR%)--8<--";

testez par exemple ceci:

sketch.ino

#include "pageweb.h"

void setup() {
  Serial.begin(115200);
  Serial.println(F("--------"));
  Serial.println((__FlashStringHelper *) page1);
  Serial.println(F("--------"));
}

void loop() {}

onglet pageweb.h:

const char page1[] PROGMEM = R"--8<--(<p>1ere Page de Gandalf60:</p>
<p>"NIVEAU":</p>%NIVEAU%
<p>"TEMPERATURE":</p>%TEMPERATURE%
<p>"POTAR":</p>%POTAR%)--8<--";

le moniteur série (à 115200 bauds) affichera

[color=purple]
--------
<p>1ere Page de Gandalf60:</p>
<p>"NIVEAU":</p>%NIVEAU%
<p>"TEMPERATURE":</p>%TEMPERATURE%
<p>"POTAR":</p>%POTAR%
--------
[/color]

==> on voit bien que les guillemets font partie du texte

bien sûr vous pouvez déclarer plusieurs variables dans le fichier des pages webs

const char page1[] PROGMEM = R"--8<--(....)--8<--";
const char page2[] PROGMEM = R"--8<--(....)--8<--";
const char page3[] PROGMEM = R"--8<--(....)--8<--";
...

C'est très intéressant, ça va alléger le code.ino.

J'essaie ça demain.

Bonne fin de journée

Bonjour

L'exemple fonctionne très bien, ça va bien alléger le ino.

Du coup, pour alléger encore plus, j'ai essayé de mettre la "routine" processeur dans un onglet
séparé car a terme il sera assez long.

Je l'ai bien mis en include dans l'ino, mais a la compil, il ne reconnait pas les variables
" error: 'temperature' was not declared in this scope"

Il y a une possibilité, ou pas?

oui vous rajoutez dans le .h une déclaration des variables qui sont définies par ailleurs. ça se fait avec le mot clé extern

Le buffer temporaire qui sert à générer la chaîne n'a pas besoin d'être dans une variable globale, vous pouvez le mettre juste dans la fonction (avec la taille adaptée)

donc le .h contiendrait

// les dépendances aux variables externes
extern int niveau;
extern float temperature;
extern int potarValue;

// les processeurs
String processeurEtat(const String& var)
{
  char bufferTemporaire[10];
  if (var == "NIVEAU") {
    itoa(niveau, bufferTemporaire, 10);// convertir l'entier en chaine de character "XXX" en base 10 (en décimale).
    return bufferTemporaire;
  }

  if (var == "TEMPERATURE") {
    dtostrf (temperature, 8, 1, bufferTemporaire);
    return bufferTemporaire;
  }

  if (var == "POTAR") {
    itoa(potarValue, bufferTemporaire, 10);// convertir l'entier en chaine de character "XXX" en base 10 (en décimale).
    return bufferTemporaire;
  }
  return String();
}

// les pages web
const char page1[] PROGMEM = R"--8<--(....)--8<--";
const char page2[] PROGMEM = R"--8<--(....)--8<--";
const char page3[] PROGMEM = R"--8<--(....)--8<--";