Go Down

Topic: ESP8266 Webserver als serielle Konsole (Read 873 times) previous topic - next topic

volvodani

Ich habe hier vor einiger Zeit das Projekt gesehen:
Es war eine kleiner Webserver der auf dem RX-PIN des ESP8266 Daten Empfing und auf einem Webserver 1:1 darstelle ganz analog zum seriellen Monitor. Als eine Browser serielle Konsole.
Ich weiß nur das es einer der üblichen "Verdächtigen" war. Ich dachte es war indem zeigt her eure geilen Projekte aber ich habe den durchgesehen aber nix gefunden.
Habt ihr ne Ahnung wer/wo es war?

Gruß
DerDani
"Komm wir essen Opa!" - Satzzeichen retten Leben!

Tommy56

Gesehen habe ich das nicht aber ich könnte mir vorstellen, auf der Weboberfläche eine Textarea zu haben, an die die Daten von RX angehängt werden und ein Clear-Button, um die Textarea zu löschen.

Entweder ein Refresh von 1 Sekunde einstellen oder vom Browser her eine Websock-Verbindung eröffnen und die Daten pushen (also die Zeichen direkt von RX zum Browser zu schieben und dort zusammen setzen). Die 2. Variante ist eleganter.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

Tommy56

Hier mal eine kleine Designstudie dazu. Das ist als Entwurf zu betrachten und noch weitab von Perfektion.

Auf dem ESP8266:
Code: [Select]

#include <ESP8266WiFi.h>
#include <WebSocketsServer.h> // https://github.com/Links2004/arduinoWebSockets

// WiFi-Settings
// <= 31 Zeichen
const char *ssid = "ssid";
// >= 8 oder <= 63 Zeichen oder NULL
const char *password = "passwort";

// WiFi-Config anpassen
// feste IP zuweisen, für den Aufruf
const IPAddress myIP(192,168,178,47); // Der ESP8266
const IPAddress gateway(192,168,178,1);
const IPAddress dnsServer(192,168,178,1);
const IPAddress subnet(255,255,255,0);


// Port, unter dem der Websocket erreichbar ist
#define PORT 81

// Hostname, um die ESPs unterscheiden zu können
const char *myhostname = "Wemos D1";

// besteht eine Verbindung?
boolean isConnected = false;

// WebSocketServer
WebSocketsServer ws = WebSocketsServer(PORT);

// Sendepuffer
const uint8_t bufSize = 32;
uint8_t buf[bufSize];

// Wir nutzen nur Connect/ Disconnect
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) {
  switch(type) {
        case WStype_DISCONNECTED:
            Serial.printf("[%u] Disconnected!\n", num);
            isConnected = false;
            ws.sendTXT(num, "MSG:Disonnected");
            break;
        case WStype_CONNECTED:
            {
              IPAddress ip = ws.remoteIP(num);
              Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
              isConnected = true;
      // send message to client
        ws.sendTXT(num, "MSG:Connected to " + String(myhostname));
            }
            break;
  } 
}

void setup() { 
byte counter = 0;
  Serial.begin(115200);
  Serial.println("\nStart");
  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  // Eigene Config, besonders IP
  WiFi.config(myIP, gateway, subnet, dnsServer);
  WiFi.begin(ssid, password);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay (500);
Serial.print (".");
    counter++;
    if (counter > 100) {
      Serial.println("Kein WLAN. Restart!");
      ESP.restart();
    }
}
Serial.print("\nConnected to ");
Serial.println(ssid );
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
  // WebSocket
  ws.begin();
  ws.onEvent(webSocketEvent);
}

size_t writeChar(uint8_t c) {
static uint8_t idx = 0;
byte len;
  if (!isConnected) return 0;
  buf[idx++] = c;
  // buf[idx] = '\0';
  // Serial.print(idx); Serial.print(".");Serial.println((char *)buf);
  if (idx == bufSize -1 || c == 0xd) {
    // Senden
    buf[idx] = '\0';
    // ich verwende broadcastTXT, um an alle Clients zu senden, ansonsten die Zeilen wechseln
    // ws.sendTXT(0, buf, idx-1);
    ws.broadcastTXT(buf, idx-1);
    len = idx - 1;
    idx = 0;   
    return len;
  }
  return 0;
}

void loop() {
  ws.loop(); // eingehende Daten WebSocket
  // Daten von Serial nach WebSocket schreiben
  while (Serial.available()) {
    char c = Serial.read();
    Serial.print(c,HEX);
    writeChar(c);
  }
}


Als lokale html-Seite, die im Browser aufgerufen wird (z.B. WebSerial):

Code: [Select]

<!DOCTYPE html>
<html style="height:100%;">
    <head>
        <meta charset="utf-8">
        <title>
            WebSerial
        </title>
        <style>
        table{border-collapse: collapse;border:none;}
        td {padding:5px;}
        button {width:100px;}
        </style>

<script language = "javascript" type = "text/javascript">
var wsUri;
var output;
      var zidx = 0;
      var websocket;
      zarr = [];
      var isStop = false;
     
      // Ausgabe anhalten und weiterführen
      function setStop() {
        var ele = document.getElementById('stop');
        if (ele.innerText == 'Stop') {
          isStop=true;
          ele.innerText = 'Weiter';
        }
        else {
          isStop = false;
          ele.innerText = 'Stop';
        }
      }
     
      // Statusmeldungen ausgeben
      function setStatus(text) {
        var status = document.getElementById('msg');
        status.textContent = text;
      }
     
      // Check input IP und Port
      function checkIn() {
        var ele1 = document.getElementById('ip');
        var ele2 = document.getElementById('port');
        var ele3 = document.getElementById('btn');
        if (ele1.value > ' ' && ele2.value > ' ') ele3.disabled = false;
        else ele3.disabled = true;       
      }
     
      function init() {
        setStatus("DISCONNECTED");
        checkIn();
      }

      // Connect und Disconnect 
      function connect() {
        var ele = document.getElementById('btn');
        var txt = ele.innerText;
        if (ele.innerText == 'Connect') {
          setStatus('Connecting');
          var ip = document.getElementById('ip').value;
          var port = document.getElementById('port').value;
          wsUri='ws://'+ip+':'+port+'/';
          ele.innerText = 'Disconnect'
          zidx = 0;
          zarr = [];
          document.getElementById('ausgabe').value = '';
          testWebSocket();           
        }
        else {
          setStatus('Disconnecting');
          ele.innerText = 'Connect';
          websocket.close();
        }
      }
           
      // Websocketfunktionen
function testWebSocket() {
    websocket = new WebSocket(wsUri);
    websocket.onopen = function(evt) {
        // console.log("CONNECTED");
              setStatus("CONNECTED");
    };
    websocket.onclose = function(evt) {
        // console.log("DISCONNECTED");
              setStatus("DISCONNECTED");
    };
          // hier kommt was vom ESP
    websocket.onmessage = function(evt) {
            var daten;
            // console.log("Message");
            // console.log(evt.data);
            // Eine Statusmeldung?
            if (evt.data.substr(0,4) == 'MSG:') {
              setStatus(evt.data);
              // Reboot angeforder und ist erlaubt
              if (evt.data.substr(0,22) =='MSG:Reboot vorbereitet') {
                connect();
              }
            }
            else {
              // ein "normaler" output
              if (evt.data.substr(0,4) == 'TXT:') zarr[zidx++] = evt.data.substr(4);
              else zarr[zidx++] = evt.data;
              if (zidx > 500) { // max. 500 Zeilen speichern
                zarr.splice(0,1); // erstes Element löschen
                zidx--;
              }
            }
            if (!isStop) {
              var ele = document.getElementById('ausgabe');
              ele.value = zarr.join("\n");
              if (document.getElementById('scroll').checked) ele.scrollTop = ele.scrollHeight;
            }
    };
    websocket.onerror = function(evt) {
        console.log("ERROR: " + evt.data);
    };
}

    </script>
    </head>
    <body style="height:95%;" onload="init();">
    <table id="tab" style="width:100%;height:100%;">
      <tr style="background-color: #dadfcb"><td style="width:100px; height:20px;">IP: </td><td style="width:140px"><input type="text" size="16" id="ip" value="192.168.178.47" onkeyup="checkIn();"/></td>
          <td style="width:60px;">Port: </td><td style="width:60px;"><input type="text" size="5" id="port" value="81"  onkeyup="checkIn();"/></td><td></td>
          <td style="text-align:right;"><button type="button" id="btn" onclick="connect();">Connect</button></td></tr>
      <tr style="background-color: #fecc80;"><td>Status: </td><td colspan="5"><span id="msg" style="width:100%;"></span></td></tr>
      <tr style="background-color: #dadfcb"><td>Ausgabe: </td><td><button type="button" onclick="setStop();" id="stop">Stop</button></td>
      <td colspan="4"><label><input type="checkbox" id="scroll" checked> nach unten scrollen</label></td></tr>
      <tr style="background-color: #fecc80;"><td  style="height:97%;" colspan="6"><textarea id="ausgabe" style="width:99%; height:96%;"></textarea></td></tr>
    </table>
    </body>
</html>


Das mal als schnell (und wohl auch etwas unsauber) zusammengeklöppelte Anregung. Ich habe nur mit dem seriellen Monitor getestet.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

uxomm

Always decouple electronic circuitry.

io2345

Kann man doch sicherlich auch dafür verwenden, die seriellen Ausgaben auszugeben, wenn der ESP8266-01 schon als Webserver benutzt wird, oder? Wie geht das dann, gibt der die Konsolenmeldungen auf einer anderen Webseite aus? Bin leider noch nicht so fit in der Programmierung, dass ich das selbst machen könnte.
Habe bisher den ESP-01 so im Einsatz: Ich verbinde mich per Handy mit dem WiFi-Netzwerk, das der Winzling bereitstellt, und schalte dann über eine Webseite 192.168.4.1 einen "Schalter". Würde nun gerne zusätzlich noch die seriellen Ausgaben umleiten, damit ich den Controller wasserfest vergießen und unsichtbar verbauen kann.

Tommy56

Mit dem ESP8266-01 habe ich noch nicht gearbeitet, ich nehme lieber die ESP8266-Varianten mit USB und mehr Speicher, wie den Wemos D1 mini.
Ansonsten habe ich mal 2 Debug-Varianten übers WLAN gebaut. Mit Websockets zum Browser oder über Telnet zum Terminal.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

io2345

#6
Jul 31, 2019, 02:24 pm Last Edit: Jul 31, 2019, 02:24 pm by io2345
Hallo,
das mit "Telnet zum Terminal" liest sich gut. Man kann also ganz normal die Befehle "Print" und Println" im Script verwenden und erhält die Ausgabe in einer Putty-Session?
Einbinden muss man die beiden Dateien

include DebugClass.h
include DebugClass.cpp

die man im Verzeichnis des Speicherorts des zu erstellenden Scripts mit ablegt. Soweit ist es mir klar. Muss ich die "Print.h" ebenfalls mit einbinden?

Jetzt verbinde ich mich aber auf keinen Router, sondern lasse den ESP ein WLAN aufmachen mit einer Funktionsseite 192.168.4.1. Findet eine Telnetsession auf diese IP-Adresse mit Port 23 dann die Debug-Ausgabe?
Auskommentieren im Beispielscript müsste ich dann wahrscheinlich diese drei Zeilen

#WiFi.mode(WIFI_STA);
#WiFi.config(myIP, gateway, subnet, dnsServer);
#WiFi.begin(ssid, password);

stimmt das? Bin noch nicht so weit fortgeschritten in der Arduino-Programmierung, bitte daher um Nachsicht, falls es dumme Fragen sein sollten...

Tommy56

Es ist ein Beispielprogramm dabei. Orientiere Dich an diesem, da siehst Du, dass Du nur die DebugClass.h includen musst.

Auskommentieren geht aber anders, da solltest Du ein paar Grundlagen lernen. Du musst ihn dann als AP betreiben. In dem Modus habe ich ihn nicht betrieben/getestet, da ein PC mit Putty bei mir kein direktes WLAN hat, mit dem er auf den ESP8266 zugreifen kann.
Das musst Du probieren.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

volvodani

Da muss ich auch mal ran hört sich nach einer ziemlich coolen Lösung an...
Respekt Tommy für die Arbeit.

Gruß
DerDani
"Komm wir essen Opa!" - Satzzeichen retten Leben!

ElEspanol

Jetzt verbinde ich mich aber auf keinen Router, sondern lasse den ESP ein WLAN aufmachen mit einer Funktionsseite 192.168.4.1. Findet eine Telnetsession auf diese IP-Adresse mit Port 23 dann die Debug-Ausgabe?
Warum lässt du den ESP nicht als Station an deinem Netz? Ist doch viel einfacher

io2345

Der ist ja in einem Moped eingebaut - da kann man per WLan und Handy was schalten

io2345

#11
Aug 12, 2019, 03:33 pm Last Edit: Aug 13, 2019, 02:32 pm by io2345
Hier der Beispiel-Code der Telnet-Lösung (Debug-mit-Telnet) angepasst an AccessPoint-Modus. (All credits go to Tommy56)
Die Einstellungen für IP-Adresse, Hostname, SSID und Passwort lassen sich natürlich auch wieder in eine Settings.h auslagern, aber das verwirrt Anfänger (wie mich) eher.


Code: [Select]

#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino

#include <ArduinoOTA.h>  // OTA-Handling
//#include "settings.h"

#define HOST_NAME "ESP123"  //Host-Name nach Bedarf anpassen

#include "DebugClass.h"        

uint32_t aktMillis, lastMillis, lastMillis_h;
const uint32_t INTERVALL = 1 * 3 * 1000;  // Neue Ausgabe alle drei Sekunden


// DebugClass Debug;

void setup() {
uint8_t counter = 0;
Serial.begin(115200);
Serial.println("\n\nStart");

IPAddress apIP(192, 168, 4, 10);  //IP-Adresse kann nach Wunsch angepasst werden

// Betrieb als AccessPoint
WiFi.mode(WIFI_AP);
WiFi.softAP("ESPdebug", "password");   //Name des erstellten WLans und Passwort hier einstellen
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));

initOTA();

// Debug initialisieren
Debug.begin(HOST_NAME);
// Debug.setRebootEnabled(true); // Enable the reset command?

}

void loop() {
ArduinoOTA.handle();  // Wenn OTA genutzt wird
// Debug-Handler
Debug.handle();
aktMillis = millis();
if (aktMillis - lastMillis >= INTERVALL) {
 // Debug.print("Text123456");
 // Debug.println("7890");
 // Debug.println("Text3");
 // Debug.printf("%lu %s\n",micros(),"Test");
 debPrint("Text123456");      //diese drei Zeilen sind nur eine Demo-Ausgabe
 debPrintln("7890");          //um zu zeigen, dass es funktioniert
 debPrintln("Text3");         //hier kann man natürlich etwas sinnvolleres ausgeben lassen
 debPrintf("%lu %s\n",micros(),"Test");
 lastMillis = aktMillis;
}
}



Beim Zugriff über Putty dort den Modus umstellen auf "Telnet" bei "Connection Type" (steht standardmäßig auf "SSH")

Tommy56

#12
Aug 12, 2019, 03:43 pm Last Edit: Aug 12, 2019, 03:50 pm by Tommy56
Danke für die Anpassung und den Test.

Setze Deinen Code bitte in Codetags (</>-Button oben links im Forumseditor oder [*code] davor und [*/code] dahinter ohne *).
So ist er auch auf portablen Geräten lesbar. Das kannst Du auch noch nachträglich ändern.

Gruß Tommy

Edit: Ich habe im Ursprungsthread auf Deinen Beitrag verlinkt.
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

ElEspanol

Ich wollte das nun mal austesten, bekomme aber keine laufenden Ausgaben in Putty.
Das mit den komandos kommt, sowas : "1T1T7P123) 1T7" auch ab und zu mal. Aber das was ich alle 5 Sekunden sende, nicht. Das auf der seriellen kommt an. Wemos D1 mini. Habe einmal Putty per Telnet dran, und auch per seriell. Was habe ich nicht bedacht?
Code: [Select]

#include <INTERVAL.h>

// für ESP8266 und ESP32
#if defined(ESP8266)
#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
#elif defined(ESP32)
#include <WiFi.h>
#else
#error Only for ESP8266 or ESP32
#endif

#include <ArduinoOTA.h>  // OTA-Handling
#include "settings.h"

#define HOST_NAME "ESP123"

#include "DebugClass.h"

uint32_t aktMillis, lastMillis, lastMillis_h;
const uint32_t INTERVALL = 1 * 10 * 1000;  // 1 Minute


//DebugClass Debug;




void setup() {
  uint8_t counter = 0;
  Serial.begin(115200);
  Serial.println("\n\nStart");

  // Betrieb als Station
  WiFi.mode(WIFI_STA);
  //  WiFi.config(myIP, gateway, subnet, dnsServer);     // ich will dhcp
  WiFi.begin(ssid, password);
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay (500);
    Serial.print(".");
    counter++;
    if (counter > 100) {
      Serial.println("Kein WLAN. Restart!");
      ESP.restart();
    }
  }
  Serial.print("\nConnected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  initOTA();

  // Debug initialisieren
  Debug.begin(HOST_NAME);

  // Debug.setRebootEnabled(true); // Enable the reset command?

}

void loop() {
  ArduinoOTA.handle();  // Wenn OTA genutzt wird
  // Debug-Handler
  Debug.handle();
  INTERVAL(5000)
  {
    /**/
    Debug.println("Text123456");
    Serial.println("7890");
    Serial.println(millis() / 1000);
    Debug.printf("%lu %s\n", micros(), "Test");
    debPrint("Text123456");
    debPrintln("7890");
    debPrintln("Text3");
    debPrintf("%lu %s\n", micros(), "Test");
    lastMillis = aktMillis;

  }
}

Tommy56

Funktioniert der Beispielsketch im Originalposting?
Wenn Du DHCP machst, kann die IP sich ändern. Das musst Du in Deiner Telnet-Verbindung mit Putty berücksichtigen.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

Go Up