INFO: Dieses Forum nutzt Cookies...
Cookies sind für den Betrieb des Forums unverzichtbar. Mit der Nutzung des Forums erklärst Du dich damit einverstanden, dass wir Cookies verwenden.

Es wird in jedem Fall ein Cookie gesetzt um diesen Hinweis nicht mehr zu erhalten. Desweiteren setzen wir Google Adsense und Google Analytics ein.

Antwort schreiben 
 
Themabewertung:
  • 0 Bewertungen - 0 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Remote Ausgaben über Websocket zum Debuggen
14.07.2017, 18:27 (Dieser Beitrag wurde zuletzt bearbeitet: 14.07.2017 18:29 von Tommy56.)
Beitrag #1
Remote Ausgaben über Websocket zum Debuggen
Hallo zusammen,

den Sketch können wir mit OTA drahtlos aufspielen, die Dateien im SPIFFS können wir drahtlos verwalten aber wenn wir Ausgaben sehen wollen, müssen wir den ESP an den PC per Draht anschließen. Das gefiel mir nicht, also habe ich etwas gebaut.

Gleich vorweg: Es ist nicht für schnelle Programme geeignet. Während ein Loop mit
Serial.print("TXT: "); Serial.println(aktMillis); ca. 1 ms braucht, benötigt das Handling des Websockets ca. 220 ms. Wenn kein Client connected ist, ca 20 ms.

Es gibt noch eine Debugmöglichkeit über das Telnet-Protokoll, die habe ich mir aber noch nicht angesehen.

Aber erst mal zu dieser Variante.
Um die volle Leistungsfähigkeit von Print zu erhalten, muß man seine Klasse von Print ableiten (Vererben). Dummerweise kann man (oder zumindest ich) ins Eventhandling des Websocketservers keine Memberfunktion einer Klasse, sondern nur eine einfache Funktion einhängen. Deshalb besteht das Konstrukt aus einer ino, einer h und einer cpp - Datei, die man ins Sketchverzeichnis kopieren kann.

Außerdem müssen die Websockets für den ESP8266 installiert sein.

Nun zu den einzelnen Bestandteilen:
WsDebug.h:
Code:
#include <WebSocketsServer.h>  // https://github.com/Links2004/arduinoWebSockets
/********************************************************************************​********
Copyright by Thomas Kühnert. All rights reserved.

This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
********************************************************************************​*********/
// Größe Ausgabepuffer
const byte bufSize = 32;
// Die Klasse muss von Print abgeleitet sein, um alle Ausgabemöglichkeiten von print/println zu haben
class WsDebug : public Print {
  public:
    WsDebug();
    // Die eigentliche Übertragung
    virtual size_t write(uint8_t c) override;
  private:
    uint8_t buf[bufSize];
    uint8_t idx = 0;  
};
WsDebug.cpp:
Code:
#include <WebSocketsServer.h>
#include <Hash.h>
#include "WsDebug.h"

extern boolean isConnected;
extern WebSocketsServer ws;


WsDebug::WsDebug() {
  
}

size_t WsDebug::write(uint8_t c) {
  if (!isConnected) return 0;
  buf[idx++] = c;
  // buf[idx] = '\0';
  // Serial.print(idx); Serial.print(".");Serial.println((char *)buf);
  if (idx == bufSize -1 || c == 0xa) {
    // Senden
    buf[idx] = '\0';
    // ich verwende broadcastTXT, um an alle Clients zu senden
    // ws.sendTXT(0, buf, idx-1);
    ws.broadcastTXT(buf, idx-1);
    idx = 0;    
  }
}
_WSDebug.ino:
Code:
// Port für den Server
#define PORT 81

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

// Darf ein Reboot über den Websocket ausgelöst werden
const boolean erlaubeReboot = true;
// besteht eine Verbindung?
boolean isConnected = false;

// Verzögerung Reboot
uint16_t verzReboot = 5000;

// Startzeit Reboot
uint32_t startReboot = 0;

WebSocketsServer ws = WebSocketsServer(PORT);

// Das kann keine Memberfunktion sein
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) {
    switch(type) {
        case WStype_DISCONNECTED:
            USE_SERIAL.printf("[%u] Disconnected!\n", num);
            isConnected = false;
            ws.sendTXT(num, "MSG:Disonnected");
            break;
        case WStype_CONNECTED:
            {
              IPAddress ip = ws.remoteIP(num);
              USE_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;
        case WStype_TEXT:
            // USE_SERIAL.printf("[%u] get Text: >%s<\n", num, payload);
            // ist Reboot angefordert?
            if (!strcmp((const char*)payload,"Reboot99")) {
              // USE_SERIAL.println("Reboot empfangen");
              // ja, ist Reboot erlaubt?
              if (erlaubeReboot) {
                // ja
                startReboot = millis();
                ws.sendTXT(num,"MSG:Reboot vorbereitet (in "+String(verzReboot)+" ms");
                // USE_SERIAL.println("Reboot vorbereitet");
              }
              else {
                ws.sendTXT(num,"MSG:Reboot nicht erlaubt");
                // USE_SERIAL.println("Reboot nicht erlaubt");
              }
            }
            // send message to client
            // ws.sendTXT(num, "message here");

            break;
        case WStype_BIN:
            // haben wir nicht
            // USE_SERIAL.printf("[%u] get binary lenght: %u\n", num, lenght);
            // hexdump(payload, lenght);
            break;
    }
}

// in setup()
void wsdInit() {
    ws.begin();
    ws.onEvent(webSocketEvent);
}

// in loop()
void wsdImLoop() {
    ws.loop();
    // Reboot-Handling
    if (startReboot !=0 && millis() - startReboot >= verzReboot) {
       ESP.restart();
    }
}
Als Beispielanwendung habe ich eine WSDebug.ino geschrieben, die die Millis ausgibt.
Code:
#include "WsDebug.h"

#include <ESP8266WiFi.h>
#include "settings.h"

#define USE_SERIAL Serial

WsDebug wsd = WsDebug();
uint32_t aktMillis, lastMillis;

void setup() {
int counter = 0;
    USE_SERIAL.begin(115200);

    //Serial.setDebugOutput(true);
    // USE_SERIAL.setDebugOutput(true);

  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);
        // USE_SERIAL.print (".");
    counter++;
    if (counter > 100) {
      // USE_SERIAL.println("Kein WLAN. Restart!");
      ESP.restart();
    }
    }
    USE_SERIAL.print("\nConnected to ");
    USE_SERIAL.println( ssid );
    USE_SERIAL.print("IP address: ");
    USE_SERIAL.println(WiFi.localIP());
  
  // WS-Debug initialisieren
  wsdInit();
}

void loop() {
  // Connect und Reboot bearbeiten
  wsdImLoop();
  // Mache irgendwas
  aktMillis = millis();
  if (aktMillis - lastMillis >= 1000) {
    wsd.print("TXT: "); wsd.println(aktMillis);
    Serial.print("TXT: "); Serial.println(aktMillis);
    lastMillis = aktMillis;
  }
}
Die settings.h ist wie immer.
Als Gegenstück zur Ausgabe gibt es eine HTML-Datei, die über Javascript den Websocketserver kontaktiert und die Ausgaben ausgibt. Es werden 500 Zeilen gespeichert. Diese Datei braucht einfach nur mit dem Browser von der Platte aufgerufen zu werden. Zusätzlich kann ein Reboot des ESP8266 ausgelöst werden, wenn diese Option in der _WSDebug.ino erlaubt ist.
Code:
<!DOCTYPE html>
<html style="height:100%;">
    <head>
        <meta charset="utf-8">
        <title>
            Debug WebSocket
        </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();
        }
      }
      
      // Reboot an ESP senden
      function sendReboot() {
        websocket.send('Reboot99');
      }
      
      // 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) {
            // 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="" 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="3"><label><input type="checkbox" id="scroll" checked> nach unten scrollen</label></td>
          <td style="text-align:right;"><button type="button" onclick="sendReboot();">Reboot</button></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>
Ich hänge ein ZIP mit ran. Fehlermeldungen und Verbesserungen sind gewünscht. Evtl. kann jemand damit etwas anfangen.

Gruß Tommy


Angehängte Datei(en) Thumbnail(s)
   

.zip  WSDebug.zip (Größe: 4,79 KB / Downloads: 59)

"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
25.07.2017, 08:41
Beitrag #2
RE: Remote Ausgaben über Websocket zum Debuggen
Moin,
erstaunlich, dass Dein Programm keine euphorischen Lobeshymnen ausgelöst hat.
Anscheinend wurden die Möglichkeiten noch nicht erkannt.

Allerherzlichsten Dank für das Programm, Deine Arbeit und die Bereitschaft es zu veröffentlichen !

Durch das Programm macht OTA beim Programmieren erst Sinn.

Gruss
Kurti
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
25.07.2017, 10:41
Beitrag #3
RE: Remote Ausgaben über Websocket zum Debuggen
(25.07.2017 08:41)kurti schrieb:  Durch das Programm macht OTA beim Programmieren erst Sinn.
Deswegen habe ich es ja geschrieben Wink

Gruß Tommy

"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
17.10.2018, 08:50 (Dieser Beitrag wurde zuletzt bearbeitet: 17.10.2018 08:51 von siram.)
Beitrag #4
RE: Remote Ausgaben über Websocket zum Debuggen
Hallo Tommy,
habe jetzt erst dein Programm gefunden und finde es sehr nützlich!

Kleiner Fehler:
Bei der Ausgabe von Text, der länger als bufSize ist, wird das letzte Zeichen im buffer entfernt und der Rest des Textes kommt in die nächste Zeile.
Ich habe in WsDebug.ccp die Zeile 23
Code:
ws.broadcastTXT(buf, idx-1);
durch
Code:
ws.broadcastTXT(buf, idx);
ersetzt, dann wird der Text vollständig ausgegeben.

Gruß
siram
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
17.10.2018, 10:31
Beitrag #5
RE: Remote Ausgaben über Websocket zum Debuggen
Dieses Verhalten ist so gewollt. Du schreibst über die Arraygrenzen hinaus.

Gruß Tommy

"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
Antwort schreiben 


Möglicherweise verwandte Themen...
Thema: Verfasser Antworten: Ansichten: Letzter Beitrag
  NodeMCU und Nano über I2C verbinden Werte sind falsch Franzel007 4 878 11.10.2018 12:16
Letzter Beitrag: Franzel007
  So kurz vor dem Ziel: Funksteckdosen über Alexa steuern. Mr.Purz 9 2.585 21.02.2018 09:32
Letzter Beitrag: amithlon
  Mega2560 send float über i2c zum Wemos D1 Mini Pro ArduTux 3 1.212 15.02.2018 09:45
Letzter Beitrag: ArduTux
  NodeMCU steuert UNO/NANO über I2C Tommy56 7 8.673 11.02.2017 15:30
Letzter Beitrag: Binatone
  Messdatenausgabe auf Webserver über NanoESP RivaDynamite 10 4.248 10.01.2017 15:49
Letzter Beitrag: renid55
  Arduino+ESP8266 über Webinterface ansteuern pet13 1 3.317 29.05.2015 13:23
Letzter Beitrag: pet13

Gehe zu:


Benutzer, die gerade dieses Thema anschauen: 1 Gast/Gäste