ESP8266 mit HTML AJAX

Hallo Kollegen,

ich bin am Zweifeln, ob ich diese Problem jemals gelöst bekomme.
Ich würde mich über eure Hilfe sehr freuen.

Ich verwende einen ESP8266 mit einem GY-521 Gyro um an Modellflugzeugen die Ruderwege zu vermessen.
Funktioniert alles super, nur leider habe ich bei der Website einige Probleme. Diese habe ich aus verschiedenen Beispielen zusammenkopiert.

Immerhin habe ich es nun geschafft, dass mir die im ESP berechneten Werte Ruderweg und Ruderwinkel mit 5Hz aktualisiert werden, nur leider wird die gesamte Website aktualisiert.

Dadurch können die Buttons nicht mehr sicher gedrückt werden und auch eine Eingabe in dem Input Feld ist unmöglich.

Anbei ein Screenshot von meiner Website.

In der Inputbox möchte ich die Rudertiefe eingeben, die ich für die Berechnung des Ruderweges benötige. Durch drücken des Button Übernehmen, möchte ich die Zahl auf eine Variable schreiben.

Durch drücken des Button Kalibrieren, möchte ich ein Offset in die Berechnung übernehmen, welches die aktuelle Werte nullt.

Hier mein Sketch gekürzt um die Berechnung:

#include <MPU6050.h>

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <Wire.h>


/*Put your SSID & Password*/
const char* ssid = "xxxx";  // Enter SSID here
const char* password = "xxxxxx";  //Enter Password here

// MPU6050 Slave Device Address
const uint8_t MPU6050SlaveAddress = 0x68;

// Select SDA and SCL pins for I2C communication 
const uint8_t scl = D6;
const uint8_t sda = D7;

// sensitivity scale factor respective to full scale setting provided in datasheet 

ESP8266WebServer server(80);  
             
float strRW,strWinkel;

#define taster1 10 //taster tare
#define led 5 //test LED

 
void setup() {
  Serial.begin(115200);
   Wire.begin(sda, scl);
  MPU6050_Init();
  pinMode(led, OUTPUT);
  pinMode(taster1, INPUT);
  delay(1000);
   
  Serial.println("Connecting to ");
  Serial.println(ssid);

  //connect to your local wi-fi network
  WiFi.begin(ssid, password);

  //check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected..!");
  Serial.print("Got IP: ");  Serial.println(WiFi.localIP());

  server.on("/", handle_OnConnect);
//  server.on("/?RT=25&ACTION=test", handle_test);
  server.onNotFound(handle_NotFound);

  server.begin();
  Serial.println("HTTP server started");
delay(1000);
}
void loop() {
  
  double Ax, Ay, Az, T, Gx, Gy, Gz;
  
  Read_RawValue(MPU6050SlaveAddress, MPU6050_REGISTER_ACCEL_XOUT_H);
  
  
if (tare==HIGH)
{
offset=mw_tilt_y;
digitalWrite(led, HIGH);
}
else
{
  digitalWrite(led, LOW);
}

/
Serial.print("Ruderweg: "); Serial.print(RW,1);
Serial.print("mm;  Winkel: "); Serial.print(mw_tilt_y-offset,1);  Serial.println("°");

  
 // strRW=(String)RW;
 // strWinkel=(String)(mw_tilt_y-offset);

 strRW=RW;
 strWinkel=(mw_tilt_y-offset);
  server.handleClient();
 delay(5);
}
  


void I2C_Write(uint8_t deviceAddress, uint8_t regAddress, uint8_t data){
  Wire.beginTransmission(deviceAddress);
  Wire.write(regAddress);
  Wire.write(data);
  Wire.endTransmission();
}

// read all 14 register
void Read_RawValue(uint8_t deviceAddress, uint8_t regAddress){
  
}


void handle_OnConnect() {


  server.send(200, "text/html", SendHTML1(strRW,strWinkel)); 
}
void handle_test() {


  server.send(200, "text/html", SendHTML1(strRW,strWinkel)); 
}
void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}


String SendHTML1(float(strRW1),float(strWinkel1)){
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>Ruderweglehre by MWi</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: left;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";
  ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  


  //*******************************************************************
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<div id=\"webpage\">\n";
  ptr +="<h1>Ruderweglehre by MWi</h1>\n";
  ptr +="<h1>Ruder 1</h1>\n";
 
  ptr +="<form>";
  ptr +="<table>";
  ptr +="<col width=\"180""\">";
  ptr +="<col width=\"120""\">";
  ptr +="<col width=\"120""\">";
  ptr +="<col width=\"0""\">";
  ptr +="<col width=\"0""\">";
  ptr +="<tr>";
  ptr +="<td><b>Rudertiefe in mm</b></td>";
  ptr +="<td>";
  ptr +="<input type=\text\ style= \width:100px\ name=\RT\ maxlength=\5\ Value =";
  ptr +=RT;
  ptr +="\></td>";
  ptr +="<td><button style= \width:100px\ name=\ACTION\ value=R1Uebernehmen\>&Uuml;bernehmen</button></td></tr><tr>";
  ptr +="<td><b>Ruderwikel in Grad</b></td><td><b>";
  ptr +=strWinkel1;
  ptr +="</b></td>";
  ptr +="<td><button style= \width:100px\ name=\ACTION\ value=R1Kalibrieren\>Kalibrieren</button></td></tr>";
  ptr +="<td><b>Ruderweg in mm</b></td><td><b>";
  ptr +=strRW1;
  ptr +="</b></td>";
  
  ptr +="</tr>";
  ptr +="</table>";
  ptr +="</form>";
  ptr +="
";
  
  
  ptr +="</div>\n";
  ptr +="</body>\n";
  ptr +="</html>\n";

ptr +="<script>\n";
ptr +="setInterval(loadDoc,200);\n";
ptr +="function loadDoc() {\n";
ptr +="var xhttp = new XMLHttpRequest();\n";
ptr +="xhttp.onreadystatechange = function() {\n";
ptr +="if (this.readyState == 4 && this.status == 200) {\n";
ptr +="document.getElementById(\"webpage\").innerHTML =this.responseText}\n";
ptr +="};\n";
ptr +="xhttp.open(\"GET\", \"/\", true);\n";
ptr +="xhttp.send();\n";
ptr +="}\n";
ptr +="</script>\n";
  
  return ptr;
}






}

Wie gesagt, ich wäre über jede Hinweis dankbar.

2019-04-01 22_50_20-Ruderweglehre by MWi.png

Marcus0815:
Wie gesagt, ich wäre über jede Hinweis dankbar.

Mit Ajax kann ich nicht dienen, ich mach sowas mit der fetch Api.

Gruß Fips

Marcus0815:
nur leider wird die gesamte Website aktualisiert.

das hast du ja genau so definiert. Fast die ganze Webseite ist in einem div "website" und dein JavaScript ersetzt genau diesen div. Kein Wunder dass es für dich aussieht, als würde die ganze website aktualisiert werden.

Wenn du noch einmal von vorne beginnen möchtest, kannst du dir mal meine Seite durchlesen:
http://werner.rothschopf.net/201809_arduino_esp8266_server_client_0.htm

Schwerpunkt ist u.a. Aktualiserung mittels Ajax. Aber eigentlich brauchst du nur die Werte die du auf der Seite aktualisieren willst eindeutige id's geben und im Payload nur den Feldinhalt übertragen. Ich verwende dazu JSON.

Hallo,

wie ich das sehe hast Du zwei Werte als Ausgabe auf die Webseite. Eine Eingabe und zwei Buttons. Du könntest das auf 2 Seiten unterbringen. Anzeige / Eingabe . Aber es geht ja auch auf einer Seite.

Zunächst mal würde ich die Webseite auf dem Filesystem des ESP ablegen. Vorteil du kannst alles mit dem Brause testen ohne ESP. Ich benutze als Editior dazu Notpad++. Vorteil Syntax und klammern werden farblich dargestellt.

Das eigendliche Problem das du jetzt hast ist das die Seite immer komplett aufgebaut wird und du damit keine Eingaben und Buttons drücken kannst, soweit hast Du das richtig erkannt. Die Eingabe und die Buttons könntest Du in einer Form auf der Seite behandeln und dann die Seite dann komplett neu laden. Die beiden Werte die Du zyklisch aktualisieren willst kannst Du mit Ajax und JSON String aktualisieren. Kannst aber auch die Eingaben und Buttons mit Ajax machen.

Zum lernen kann ich dir Die Seite empfehlen.

www.w3schools

ich habe mich auch gerade in den letzten Tagen mit dem Thema beschäftigt. Die meisten Beispiele die man findet senden mit Ajax einen Wert als Text and die Webseite. Nun geht es aber nur als Text, die Frage ist was steht drin und wie kommt man dran. Wenn es mehr als ein Wert sein soll geht das z.B mit JSON Objekt oder JSON Array. Letzteres sollte dann z.B so vorliegen:

["20.0","30.3","40,4"]

das lässt sich auf ESP Seite recht einfach mit sprintf() machen.

Auf der Webseite innerhalb des JS skrips geht das auch recht einfach, hat mich allerdings 2 Abende gekoste um es zu begreifen. :slight_smile: Also hier nur mal die Webseite mit dem skript.

HTML mit Java

<!DOCTYPE HTML>
<html>
<head>
<meta name = "viewport" content = "width = device-width, initial-scale = 1.0">
<title>Index Test Ajax</title>
</head>
<body>

<script>
var dummy = setInterval(loadDaten, 2000);

function loadDaten(){
    var ajax = new XMLHttpRequest();
	ajax.onreadystatechange = function(){
	
	if (this.readyState == 4 && this.status == 200 ){
		var myArr = JSON.parse(this.responseText);
		document.getElementById("value1").innerHTML=myArr[0];
		document.getElementById("value2").innerHTML=myArr[1];
		document.getElementById("value3").innerHTML=myArr[2];
		
		}
	};
		ajax.open("GET","daten",true);
		ajax.send(); 			
}

</script>

<h1>Messwerte</h1>

<p>
Messwert 1 __: <span Id = "value1">0</span>
	
Messwert 2 __: <span Id = "value2">0</span>

Messwert 3 __: <span Id = "value3">0</span>

</p>	

	
<button type="button" onclick="loadDaten()">holen</button>
</body>
</html>

Wenn man das ohne ESP auf dem Brauser testen will muss man lediglich 2 zeilen ändern.

ajax.open("GET","daten.txt");
ajax.send(null);

wenn man nun eine Datei daten.txt im gleiche Verzeichniss mit der folgenden Zeile ablegt läuft das auf dem Brauser ohne ESP. Man kann dann die Zahlen in der Datei ändern, speichern und bekommt das direkt im Brauser angezeigt.

["11.1" ,"20.0", "31.0"]

und den Teil zum auslesen aus dem Filesystem und den Handle für den ESP dazu
Die C Zeichenkette sendbuffer ist global mit 2000 Zeichen dimensioniert, da muss ja die ganze HTML Seite rein

char sendbuffer[2000]; // buffer Seite senden
char filename[20];

//--------------- HTML Datei aus Filesystem lesen
void fileread() {

  Serial.print("Datei lesen "); Serial.println(filename);
  File f = SPIFFS.open(filename, "r");
  sendbuffer[0] = char(0);  // buffer leeren
  unsigned int i;
  for ( i = 0 ; i < f.size(); i++) {
    char s = f.read(); // Byte auslesen
    sendbuffer[i] =  s;    // bufferdaten  bilden
  }
  f.close();
  sendbuffer[i] = char(0);// Endzeichen setzen
  //Serial.print(sendbuffer);
  //Serial.println(strlen(sendbuffer));
}
// ----------------- HTML Seiten Handles -----------------
void handleIndex_HTML() {
  strcpy(filename, "/index.html");
  fileread();// Datei liegt in der Zeichenkette sendbuffer
  //Serial.print (sendbuffer);
  //Serial.println (strlen(sendbuffer));
  server.send(200, "text/html", sendbuffer);

}
// ------------ handle daten sende ------------
void handleSendDaten() {
  
  wert1 = 20.0;  // Testwerte
  wert2 = millis() / 1000.0;
  wert3 = wert3 + 1;
  
// JSON String als Array anlegen ["20.0","572.6","24.0"]

  sprintf(sendbuffer, "[\"%4.1f\",\"%4.1f\",\"%4.1f\"]", wert1, wert2, wert3);
  server.send(200, "text/plane", sendbuffer);
  Serial.print(sendbuffer);
  Serial.println(strlen(sendbuffer));

}

Heinz

Hallo zusammen.
Zunächst vielen Dank für die ausführlichen Antworten!!!

Ich werde versuchen mich im Laufe der Woche damit auseinander zu setzen.
Würde mich freuen, wenn ich bei Fragen noch einmal Hilfe bekommen kann...

Danke!

Gruß, Marcus

Hallo Rentner,

ich habe mich mit dem Thema beschäftigt und es scheint genau das zu sein, was ich suche!
Zunächst habe ich mich nun mit dem Spiffs beschäftigt und verstanden wie das funktioniert.
Mach absolut Sinn, toll. Auch mit dem Browser vorher direkt testen zu können!

Ich habe es aber aktuell noch nicht geschafft die Werte zu aktualisieren.

Der von dir beschriebene HTML mit JAVA Bereich ist für mich verständlich und klar, funktioniert ja auch ohne ESP mit der Datei und den gespeicherten Werten.

Aber die Handle für den ESP, da hakt es.
Konkret:

  • wo rufe ich die HandleIndex und HandleSendData auf?
    -> HandleIndex denke ich mal bei der Initialisierung also server.on
    -> aber wo HandleSendData

  • sowohl bei HandleIndex als auch bei HandleSendData wird der Array sendbuffer verwendet. Ist das absichtlich? kann ich die Index Seite nicht in einen Textstring mit
    data = f.readString(); // Inhalt der Textdatei wird gelesen
    lesen? Oder ist genau das mein Gedankenfehler?

Hier mal ein paar Codeschnipsel:

SPIFFS lesen und Server Handle im VoidSetup:

SPIFFS.begin(); // Filesystem mounten
  File f = SPIFFS.open( "/index.html", "r"); // Datei zum lesen öffnen
  if (!f) {
    Serial.println("file open failed");
  }
  data = f.readString(); // Inhalt der Textdatei wird gelesen...
  Serial.println("Inhalt der geöffneten Datei:");
  Serial.println(data); // ... und wieder ausgegeben
  f.close(); // Wir schließen die Datei

  server.on("/", handle_OnConnect);

Void Loop:

 server.handleClient();
void handle_OnConnect() {
  server.send(200, "text/html", data); 
}

Die html Seite wird richtig dargestellt.
Noch einmal danke vorab für die Unterstützung.
Gruß, MArcus

Hallo
Bin derzeit nur mobil unterwegs habe also keinen Zugriff auf meinen PC

Also es gibt in dem Beispiel hallo Server

Irgendwo die Zeile

server.on("/", handle Webseite)
Danach machst du

server.on("/daten", handlesenddaten)

Das ist dann sozusagen der Aufruf für die Funktion handle send date aus meinem Schnipsel

Das "/daten" ist der Verweis aus dem

ajax.open("GET","daten",true)
In dem Skript Teil der Webseite. Der Teil Ajax.open und ajax.send ruft also die Zeile Server.on("/daten",handlesenddaten ) auf und dann geht's da weiter.
Ich hoffe das war verständlich. Die Spezis hier im Forum mögen mir verzeihen wenn ich mich etwas unglücklich ausgedruckt habe.

Zu dem anderen Thema , natürlich kannst du wenn du magst auch mit String Objekten arbeiten , ich Versuche das aber zu vermeiden da gerade bei langen Strings schnell Speicherprobleme auftreten können .

Tommy hat dazu Mal was geschrieben was mich ziemlich überzeugt hat, also Versuche ich mit c Zeichenketten zu arbeiten damit wird Speicher reserviert und nicht dynamisch verwaltet wie bei String Objekten. Tommy liest hier sicher mit und stellt gern den Link noch Mal rein.

Heinz

Hallo Heinz,

das klingt logisch und hatte ich in anderem Zusammenhang auch schon mal.
Stand wohl auf der Leitung!

Danke für den super Support hier im Forum! Werde das gleich heute Abend ausprobieren.

Gruß, Marcus

Rentner:
Tommy hat dazu Mal was geschrieben was mich ziemlich überzeugt hat, also Versuche ich mit c Zeichenketten zu arbeiten damit wird Speicher reserviert und nicht dynamisch verwaltet wie bei String Objekten. Tommy liest hier sicher mit und stellt gern den Link noch Mal rein.

Meinst Du Zeichenketten in C?

Gruß Tommy

Hallo
@Tommy56

Richtig danke

Gruß
Heinz

Marcus0815:
Hallo Heinz,

das klingt logisch und hatte ich in anderem Zusammenhang auch schon mal.
Stand wohl auf der Leitung!

Danke für den super Support hier im Forum! Werde das gleich heute Abend ausprobieren.

Gruß, Marcus

Hallo Marcus,

der Rentner versucht das Rad neu zu erfinden!
Warum du ihm dabei folgen willst, verstehe ich nicht!

Es gibt seit Jahren zwei fertige Funktionen in der ESP8266WebServer Class um Dateien aus dem Spiffs an den Clienten auszuliefern.

void serveStatic();
size_t streamFile();

Beispiele:

server.on("/index.html", []() {
    File f = SPIFFS.open("/index.html", "r");
    server.streamFile(f, "text/html");
  });

  // oder

server.serveStatic("/index.html", SPIFFS, "/index.html");

Rentner:
HTML mit Java

Was hat HTML mit Java zu tun?

Marcus0815:
Der von dir beschriebene HTML mit JAVA Bereich ist für mich verständlich und klar..

Du solltest nicht alles blind übernehmen was der Rentner so schreibt.

Unterschied zwischen Java und JavaScript

Gruß Fips

Derfips:
Mit Ajax kann ich nicht dienen, ich mach sowas mit der fetch Api.

Gruß Fips

Guter Hinweis, darauf wollte ich gerade auch verlinken.

dieterr:
Guter Hinweis, darauf wollte ich gerade auch verlinken.

Der Link alleine scheint dem TO aber leider nicht zu helfen.

Gruß Fips

Hallo
Na die beiden Funktionen sind ja spitze , nein die kannte ich nicht bisher.

@Fips du hast natürlich Recht in der HTML seite ist ein Javascript hat nix mit Java zu tun.
Allerdings schreibe ich in dem Satz voher auch explizit Javascript im Text.

Wenn ich ansonsten noch Müll beschreiben habe bitte korrigiert mich, bin ja selbst noch in der Lernphase :slight_smile:
Gruß Heinz

Rentner:
bin ja selbst noch in der Lernphase :slight_smile:
Gruß Heinz

Hier noch ein Hilfsmittel für dich.

https://links2004.github.io/Arduino/classes.html

Beim ESP8266WebServer findest du beide Funktionen.

Gruß Fips

Hallo zusammen,

ich habe die Funktion nun genutzt:

void handle_OnConnect() {
  //server.send(200, "text/html", data);
  File f = SPIFFS.open( "/index.html", "r"); // Datei zum lesen öffnen
  server.streamFile(f, "text/html");
  f.close(); // Wir schließen die Datei
}

Funktionier sehr kurzzeitig, dann bekomme ich immer eine Fehlermeldung, auch wenn ich wieder umstelle auf das alte Verfahren:

Exception (9): --> meistens (3)
epc1=0x4020dc09 epc2=0x00000000 epc3=0x00000000 excvaddr=0x5d22362e depc=0x00000000

ctx: cont
sp: 3ffffcc0 end: 3fffffd0 offset: 01a0

stack>>>
3ffffe60: 00000001 401960c2 3ffffea0 401006dc
3ffffe70: 00000000 00000000 00000000 4020c108
3ffffe80: 3ffffeac c02d28fd e0000000 4020c124
3ffffe90: 00000001 401960c2 e0000000 40207a71
3ffffea0: 00000000 00000000 00000000 3620225b
3ffffeb0: 2c22332e 34312d22 5d22362e 4020dc00
3ffffec0: 00000012 00000001 3fff0074 4020a20a
3ffffed0: 3fff0074 3ffeeeac 3fffff00 4020a246
3ffffee0: 00000000 00000000 00000000 4020c2f8
3ffffef0: 3fff0074 3ffeeeac 3ffeee68 4020a2cd
3fffff00: 3fff040c 0000000f 00000006 00000000
3fffff10: 00000000 00d69e5e 40106520 00007d29
3fffff20: 3ffeeeac 00000001 00000019 402095d8
3fffff30: 00000001 00000000 40209768 0000000f
3fffff40: 00000000 3fff016c 3ffeee68 3ffeef78
3fffff50: 00000001 3ffeee90 3ffeee68 4020a51c
3fffff60: 4020e1e8 00000000 00001388 40214871
3fffff70: 00000000 3fff016c 00000005 3ffeef78
3fffff80: 3ffeef8c 00000000 00000019 4020808b
3fffff90: 597d5d26 3feef833 00000000 40938800
3fffffa0: feefeffe feefeffe feefeffe 3ffef10c
3fffffb0: 3fffdad0 00000000 3ffef104 4020cc3c
3fffffc0: feefeffe feefeffe 3ffe85f8 40100739
<<<stack<<<

ets Jan 8 2013,rst cause:2, boot mode:(1,3)

Erste Recherchen reden von einem Speicherproblem und man solle folgendes Kommando absetzen:

  WiFi.persistent(false);

hat aber nichts gebracht.

Jemand ne Idee?

Nutze den ESP Exception Decoder um mehr zu erfahren.

Gruß Tommy

Hallo Tommy und danke für die schnelle Rückmeldung.

Ich will hier nicht mit Themen langweilen, die im Forum schon x Fach beschrieben sind, aber für einen Anfänger wie mich ist das alles andere als nachvollziehbar und ich bin somit auf die Hilfe angewiesen.

Ich habe den Exception Decoder installiert.
Beim Start kam eine Fehlermeldung "...xtensa..exe" is missing.
Ich habe die Vorgehensweise im Forum gefunden, die Datei gefunden und im angegeben Verzeichnis installiert.
Danach funktioniert alles, es kommt aber die Meldung beim Übersetzen: "Decode Failed".

Mach ich noch etwas falsch, oder kann der Stack nicht decodiert werden?

LG Macrus

Du solltest alles ab Exception (ohne Dein meistens...) da einwerfen.
Ansonsten kann ich nur sagen, dass es bei mir immer funktioniert hat.

Gruß Tommy

Hallo,

anbei ein Screenshot...

Marcus