Bonjour,

j'ai un problème un peu ennuyeux et malgré pas mal de recherches je n'ai pas de solution

Une carte "Pretzel" qui est composé d'un Arduino Nano avec un module Wifi ESP8266 est configurée en mode "server".
Elle génère une page web en HTML.
La page affiche la valeur de quelques capteurs.
Elle contient un formulaire qui permet au visiteur de piloter des sorties (choix de la couleur d'une led RGB + affichage d'un texte sur un afficheur LCD)

Tout fonctionne correctement depuis mon réseau local avec le navigateur Chrome.

J'arrive à y accéder en VB.NET avec un objet WEB BROWSER :
(les codes sources suivants sont en VB.NET, sauf le tout dernier qui est celui de l'Arduino)

Code VB.NET : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
    Private Sub But_Lancer_Click(sender As Object, e As EventArgs) Handles But_Lancer.Click
        WebB1.Navigate(New Uri(TxtUrl.Text))
    End Sub
 
    Private Sub WebB1_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles WebB1.DocumentCompleted
        Dim n As Integer
        TxtResultat.Text = WebB1.DocumentText
        n = FreeFile()
        FileOpen(n, My.Application.Info.DirectoryPath & "Page.html", OpenMode.Output)
        Print(n, WebB1.DocumentText)
        FileClose(n)
    End Sub

Je retrouve bien la page générée par l'Arduino Nano / Esp8266.

Lorsque je prend le fichier "Page.html", que je le met en ligne sur le net, je peux le vérifier avec https://validator.w3.org/ et tout est OK (juste deux warning, mais aucune erreur)

La page : http://electroremy.free.fr/Arduino/Page3.html

et sa validation W3C : https://validator.w3.org/nu/?doc=htt...o%2FPage3.html

NB : c'est juste une copie de la page générée qui est mise en ligne. Ca ne vous permet pas de piloter mon Arduino

Apparemment tout est OK

Maintenant je veux faire les choses plus sérieusement...

Code VB.NET : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    Private Sub But_Lancer_Click(sender As Object, e As EventArgs) Handles But_Lancer.Click
        Dim WB As MyWebClient
        Try
            WB = New MyWebClient
            WB.Headers.Add(Net.HttpRequestHeader.Accept, "text/html")
            WB.Headers.Add("user-agent", "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Safari/537.36")
            TxtResultat.Text = WB.DownloadString(TxtUrl.Text)
        Catch ex As Exception
            TxtResultat.Text = ex.ToString
        End Try
    End Sub
 
    Friend Class MyWebClient
        Inherits WebClient
        Protected Overrides Function GetWebRequest(ByVal address As Uri) As WebRequest
            Dim req As HttpWebRequest = CType(MyBase.GetWebRequest(address), HttpWebRequest)
            req.KeepAlive = False
            Return req
        End Function
    End Class

La ça ne fonctionne plus, j'ai une erreur "System.Net.WebException: Le serveur a commis une violation de protocole. Section=ResponseStatusLine"

Bien évidemment, j'ai testé mon code VB.NET avec une autre page web et il fonctionne correctement.

Après quelques recherches, j'ai trouvé qu'il fallait ajouter ce code dans App.config dans VB.NET :

Code XML : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
            <system.net>
            <settings>
            <httpWebRequest useUnsafeHeaderParsing = "true" />
            </settings>
            </system.net>

J'ai une autre erreur :
"System.Net.WebException: La connexion sous-jacente a été fermée : La connexion a été interrompue de manière inattendue.
Et en plus je constate, en regardant ma carte Arduino, que la page web a été accédée deux fois au lieu d'une.

Bien évidemment, j'ai testé une nouvelle fois mon code VB.NET avec une autre page web et il fonctionne correctement.

J'ai voulu en avoir le coeur net.

J'ai brièvement rendu accessible mon serveur Arduino / ESP 8266 depuis Internet.
Cela m'a permis de tester la page générée par l'Arduino par le juge de paix https://validator.w3.org/
Il me donne une erreur :
IO Error: The server failed to respond with a valid HTTP response
Le coupable est bien mon serveur Arduino / ESP 8266

Le code HTML que génère mon Arduino est OK comme on l'a vu précédemment.
C'est la façon dont l'Arduino envoi la page HTML au client qui ne va pas...


Voici le code source contenu dans l'Arduino :
Particularité : c'est l'Arduino Nano que je programme.
Je pilote le module ESP 8266 qui lui est associé avec des commandes "AT" via une liaison série.


Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
// Entrées :
#define BP A3
#define SENSOR_L A4
#define SENSOR_T A5
#define RND_1 A6
#define RND_2 A7
 
// Sorties :
#define RED 6
#define GREEN 9
#define BLUE 10
#define PIEZO 4
#define RL_RESET 3
#define RL_SET 2
#define LED_WLAN 13
 
#define DEBUG true
 
const char site1[] PROGMEM = "<!DOCTYPE html><HTML><HEAD><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="viewport" content="width=device-width, initial-scale=2.0, user-scalable=yes"><title>iBOX v3</title></HEAD><BODY><H1>iBOX v3</H1>";
const char site2[] PROGMEM = "<BR>Capteur lumi&egrave;re = ";
const char site3[] PROGMEM = "<BR>Capteur temp&eacute;rature = ";
const char site4[] PROGMEM = "<BR>RND = ";
const char site5[] PROGMEM = "<BR>Bouton Poussoir = ";
const char site6[] PROGMEM = "<BR><BR><form method="GET">Tapez une commande :<BR><BR><input type="text" name="text"  size="16" maxlength="16"><BR><BR>Choisissez une couleur :<BR><BR><input type="color" name="rgb" value="#";
const char site7[] PROGMEM = ""><BR><BR><input type="submit"></form><BR>";
const char site8[] PROGMEM = " affichages depuis le ";
const char site9[] PROGMEM = "<BR><BR>Derniers acc&egrave;s :";
const char site10[] PROGMEM = "</BODY></HTML>";
 
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>
#include <TimeLib.h>
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
//#include <string.h>
 
//               (RS,EN,D4,D5, D6, D7)
LiquidCrystal lcd(5, A2, 7, 8, A1, A0);
SoftwareSerial esp8266(11, 12); // RX, TX
 
time_t History[10];
 
char HtmlColor[7] = "000000";
char temps[20];
int NombreAcces;
int NombreAffichages;
 
bool EtatRL;
 
bool CdeEx;
 
int MinutePrec;
 
void setup() {
  Serial.begin(19200);
  esp8266.begin(19200);
 
  digitalWrite(RL_SET,LOW);
  digitalWrite(RL_RESET,LOW);
  EtatRL = false;
 
  pinMode(BP, INPUT);
  pinMode(SENSOR_L, INPUT);
  pinMode(SENSOR_T, INPUT);
  pinMode(RND_1, INPUT);
  pinMode(RND_2, INPUT);
 
  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);
 
  lcd.begin(16, 2);
 
  lcd.print(F("Remy LUCAS iBOX:"));
  lcd.setCursor(0, 1);
  lcd.print(F("Demarrage......."));
 
  if (!espConfig()) serialDebug();
  else digitalWrite(LED_WLAN, HIGH);
 
  lcd.setCursor(0, 1);
  lcd.print(F("WIFI OK :-)     "));
 
  // On obtient la date et l'heure de démarrage :
  getTime();
  debug(temps);
  NombreAcces=0;
  NombreAffichages=0;
  MinutePrec=-1;
 
  // On lance le serveur
  if (configTCPServer())  debug(F("Serveur OK")); else debug(F("Erreur serveur"));
}
 
void loop() {
  char Buffer_Rec[37];
  char LCD_Text[17];
  char Color_Text[7];
 
  int i;
  int j;
 
  if (esp8266.available()) {// check if the esp is sending a message
    if (esp8266.find("+IPD,")) {
      CdeEx = false;
      debug(F("Requête reçue"));
      int connectionId = esp8266.parseInt();
      if (esp8266.findUntil("?text=", "n")) {
        // Lecture :
        esp8266.readBytes(Buffer_Rec,36);
        /*
        // Vidage : // Ne sert à rien !
        do {
          i=esp8266.read();
        } while(i==-1);
        */
 
        // On récupère le texte jusque le caractère '&' dans la limite de 16 caractères :
        j=0;
        for (i=0;i<=strlen(Buffer_Rec);i++) {
          if (Buffer_Rec[i]=='&') {break;}
          if (j==16) {break;}
          if (Buffer_Rec[i]=='+') {
            LCD_Text[j] = ' '; // Les espaces sont remplacés par + dans un formulaire HTML
          } else {
            LCD_Text[j] = Buffer_Rec[i];
          }
          j++;
        }
        LCD_Text[j] = '';
 
        // Nécessaire si le caractère '&' n'a pas été atteint (par exemple si le texte envoyé dépassait 16 caractères) :
        if (Buffer_Rec[i]!='&') {
          for (i=i;i<=strlen(Buffer_Rec);i++) {
            if (Buffer_Rec[i]=='&') {break;}
          }
        }
 
        // On récupère le texte de la couleur :
        j=0;
        for (i=i+8;i<=strlen(Buffer_Rec);i++) {
          if (j==6) {break;}
          Color_Text[j] = Buffer_Rec[i];
          j++;
        }
        Color_Text[j] = '';
 
        debug(Buffer_Rec);
        debug(LCD_Text);
        debug(Color_Text);
 
        //lcd.clear();
        //lcd.print(F("Remy LUCAS iBOX:"));
        for (i=strlen(LCD_Text);i<16;i++) {
          LCD_Text[i] = ' ';
        }
        LCD_Text[16] = '';
        lcd.setCursor(0, 1);
        lcd.print(LCD_Text);
 
        long number = (long) strtol(Color_Text, NULL, 16); //Convert String to Hex http://stackoverflow.com/questions/23576827/arduino-convert-a-sting-hex-ffffff-into-3-int
        int r = number >> 16;
        int g = number >> 8 & 0xFF;
        int b = number & 0xFF;
        analogWrite(RED, r);
        analogWrite(GREEN, g * 0.15);
        analogWrite(BLUE, b * 0.18);
        //debug(String(r) + " "+ String(g) +" "+ String(b));
 
        snprintf_P(HtmlColor, sizeof(HtmlColor), PSTR("%02x%02x%02x"), r, g, b);
 
        // Impulsion minimum 10 ms dans la datasheet du relais bistable
        if (EtatRL) {
          EtatRL = false;
          digitalWrite(RL_SET,HIGH);
          delay(20);
          digitalWrite(RL_SET,LOW);
        } else {
          EtatRL = true;
          digitalWrite(RL_RESET,HIGH);
          delay(20);
          digitalWrite(RL_RESET,LOW);
        }
 
        CdeEx = true;
 
        NombreAcces++;
        // Il faut mettre à jour l'historique :
        if (NombreAcces<11) {
          History[NombreAcces-1] = now();
        } else {
          // Il faut tout décaler :
          for (i=0;i<9;i++) {
            History[i] = History[i+1];          
          }
          History[9] = now();
        }
      }
      NombreAffichages++;
      if (createAndSendWebsite(connectionId)) debug(F("Site internet envoyé")); else debug(F("Erreur envoit"));
      if (CdeEx) {
        tone(PIEZO, 440, 400);
        delay(150);
        tone(PIEZO, 880, 400);
      } else {
        tone(PIEZO, 840, 400);
        delay(150);
        tone(PIEZO, 440, 400);
      }
    }
  } else {
    // Affichage de l'heure et de la date (sans les secondes) :
    if (minute()!=MinutePrec) {
      MinutePrec = minute();
      snprintf_P(LCD_Text, sizeof(LCD_Text), PSTR("%02dh%02d %02d/%02d/%04d"), hour(), minute(), day(),month(),year());
      lcd.setCursor(0, 0);
      lcd.print(LCD_Text);
    }
  }
}
 
boolean createAndSendWebsite(int connectionId) {
  // Ici on évite de créer un gros buffer en RAM de la taille du site, avec une série de .print() on écrit directement les chaines constantes
  int lg;
  boolean success = true;
  char Buffer_Acces[28];
  char Buffer_MesureL[5];
  char Buffer_MesureT[5];
  char Buffer_BP[2];
  char Buffer_RND[11];
  char Buffer_ListeAcces[24];
  int i;
  int nbAccesAffiches;
  time_t t;
 
  char Commande[38];
 
  // Création des parties variables, chacune dans un buffer :
  snprintf_P(Buffer_Acces, sizeof(Buffer_Acces), PSTR("%d acc&egrave;s et %d"), NombreAcces, NombreAffichages);
  // temps : déjà écrite par le code appelant
  snprintf_P(Buffer_MesureL, sizeof(Buffer_MesureL), PSTR("%d"), analogRead(SENSOR_L));
  snprintf_P(Buffer_MesureT, sizeof(Buffer_MesureT), PSTR("%d"), analogRead(SENSOR_T));
 
  snprintf_P(Buffer_RND, sizeof(Buffer_RND), PSTR("%d %d"), analogRead(RND_1),analogRead(RND_2));
 
  snprintf_P(Buffer_BP, sizeof(Buffer_BP), PSTR("%d"), digitalRead(BP));
  // HtmlColor : déjà écrite par le code appelant
 
  if (NombreAcces > 10) {nbAccesAffiches = 10;} else {nbAccesAffiches = NombreAcces;}
 
  // Calcul de la longueur totale lg ATTENTION il faut que ce soit juste à l'octet prêt sinon plantage du navigateur !
  lg = strlen(Buffer_Acces) + strlen(temps) + strlen(Buffer_MesureL) + strlen(Buffer_MesureT) + strlen(Buffer_BP) + strlen(HtmlColor) + strlen(Buffer_RND) + strlen(site1) + strlen(site2) + strlen(site3) + strlen(site4) + strlen(site5) + strlen(site6) + strlen(site7) + strlen(site8) + strlen(site9) + strlen(site10);
  //xx/xx/xxxx 20h00:00<BR>  35 caractères par ligne :
  lg+= nbAccesAffiches * 23;
 
  snprintf_P(Commande, sizeof(Commande), PSTR("AT+CIPSEND=%d,%d"), connectionId, lg);
  if (sendCom(Commande, ">")) {
    // Envoit de chaque partie fixe et variable sans passer par un gros buffer :
    esp8266.print(FPSTR(site1));
    esp8266.print(FPSTR(site2));
    esp8266.print(Buffer_MesureL);
    esp8266.print(FPSTR(site3));
    esp8266.print(Buffer_MesureT);
    esp8266.print(FPSTR(site4));
    esp8266.print(Buffer_RND);
    esp8266.print(FPSTR(site5));
    esp8266.print(Buffer_BP);
    esp8266.print(FPSTR(site6));
    esp8266.print(HtmlColor);
    esp8266.print(FPSTR(site7));
    esp8266.print(Buffer_Acces);
    esp8266.print(FPSTR(site8));
    esp8266.print(temps);
    esp8266.print(FPSTR(site9));
    for (i=0;i<nbAccesAffiches;i++) {
      t = History[i];
      snprintf_P(Buffer_ListeAcces, sizeof(Buffer_ListeAcces), PSTR("<BR>%02d/%02d/%04d %02dh%02d:%02d"), day(t),month(t),year(t), hour(t), minute(t), second(t));
      esp8266.print(Buffer_ListeAcces);
    }
    esp8266.print(FPSTR(site10));
 
    esp8266.find("SEND OK");
    snprintf_P(Commande, sizeof(Commande), PSTR("AT+CIPCLOSE=%d"), connectionId, lg);
    success &= sendCom(Commande, "OK");
  } else {
    success = false;
  }
  return success;
}
 
boolean getTime() {   
  boolean success = true;
  int xyear, xmonth, xday, xhour, xminute, xsecond;  //lokal variables
  success &= sendCom(F("AT+CIPSTART="TCP","chronic.herokuapp.com",80"), "OK");
  success &= sendCom(F("AT+CIPSEND=62"), ">"); //62 = longueur de la chaine en enlevant les ''  +  2
  esp8266.println(F("GET /utc/in+two+hours HTTP/1.1rnHost:chronic.herokuapp.comrn"));
 
  if (esp8266.find("+IPD")) {
    if (esp8266.find("rnrn")) {
      xyear = esp8266.parseInt();
      xmonth = esp8266.parseInt();
      xday = esp8266.parseInt();
      xhour = esp8266.parseInt();
      xminute = esp8266.parseInt();
      xsecond = esp8266.parseInt();
      if (xday < 0) xday *= -1;        //Because of date seperator - parseInt detects negativ integer
      if (xmonth < 0) xmonth *= -1;    //Because of date seperator - parseInt detects negativ integer
 
      setTime(xhour, xminute, xsecond, xday, xmonth, xyear);
      snprintf_P(temps, sizeof(temps), PSTR("%02d/%02d/%04d %02dh%02d:%02d"), xday,xmonth,xyear, xhour, xminute, xsecond);
 
      sendCom(F("AT+CIPCLOSE"), "OK");
      return true;
    } else return false;
  } else return false;
}
 
//-----------------------------------------Config ESP8266------------------------------------
boolean espConfig() {
  boolean success = true;
  esp8266.setTimeout(5000);
  success &= sendCom(F("AT+RST"), "ready");
  esp8266.setTimeout(1000);
  if (configStation()) {
    success &= true;
    debug(F("WLAN Connected"));
    debug(F("My IP is:"));
    debug(sendCom(F("AT+CIFSR")));
  }  else  {
    success &= false;
  }
  success &= sendCom(F("AT+CIPMODE=0"), "OK");  //So rum scheit wichtig!
  success &= sendCom(F("AT+CIPMUX=0"), "OK");
   return success;
}
 
boolean configTCPServer() {
  boolean success = true;
  success &= (sendCom(F("AT+CIPMUX=1"), "OK"));
  success &= (sendCom(F("AT+CIPSERVER=1,80"), "OK"));
  return success;
}
 
boolean configStation() {
  boolean success = true;
  success &= (sendCom(F("AT+CWMODE=1"), "OK"));
  esp8266.setTimeout(20000);
  success &= (sendCom(F("AT+CWJAP="MON_WIFI","MA_CLEF""), "OK"));
  esp8266.setTimeout(1000);
  return success;
}
 
//-----------------------------------------------Controll ESP-----------------------------------------------------
boolean sendCom(const String& command, char respond[]) {
  esp8266.println(command);
  if (esp8266.findUntil(respond, "ERROR")) {
    return true;
  } else {
    debug(F("ESP SEND ERROR: "));
    debug(command);
    return false;
  }
}
 
String sendCom(const String& command) {
  esp8266.println(command);
  return esp8266.readString();
}
 
//-------------------------------------------------Debug Functions------------------------------------------------------
void serialDebug() {
  while (true) {
    if (esp8266.available())
      Serial.write(esp8266.read());
    if (Serial.available())
      esp8266.write(Serial.read());
  }
}
 
void debug(const String& Msg) {
  if (DEBUG) Serial.println(Msg);
}
Les navigateurs semblent être tolérants à une erreur que fait mon code qui envoi la page HTML au client.
Mais je ne sais pas quelle est cette erreur et comment la corriger.
Ce qui est ennuyeux c'est que le site du W3C et VB.NET ne sont pas très explicites dans leurs messages d'erreur...
Je ne comprend pas ce qu'il manque ou ce qu'il y a en trop dans les données que mon Arduino envoi.
D'ailleurs je n'ai pas de moyen de savoir ce que mon Arduino envoi exactement...
...il faudrait que je "capture" les trames TCP/IP émises par mon Arduino ... une occasion de se mettre à Linux Kali

J'ai trouvé des messages en anglais et en chinois dans des forums sur cette erreur mais sans solution.

J'imagine qu'un bout de la réponse à mon problème se trouve ici https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html

Je me demande si dans la fonction createAndSendWebsite() il ne faudrait pas que j'envoi des choses avec esp8266.print() avant d'envoyer le code HTML de la page ?


J'ai acheté cette carte Nano ESP 8266 dans un kit qui contenait un manuel pour apprendre à l'utiliser...
Encore une fois, les exemples de code donnés dans ce kit sont vraiment pourris (voyez également ce post : https://www.developpez.net/forums/d2...t-bug-bizarre/)

A bientôt