Motivation

Für ein ESP-Projekte wollte ich ein ansprechendes Web-Interface zur Anzeige von Messdaten implementieren. Im Netz findet man eine große Anzahl von Control-Bibliotheken. Die können fast alles, sind aber auch entsprechend riesig. Große Dateien drücken die Performance eines ESP-Projekts nicht unwesentlich. Der Grund ist, dass diese Dateien nur in relativ kleinen Stücken übertragen werden. Wegen des TCP-Overheads dauert das dann relativ lange.

Aus diesem Grund habe ich eigene Controls entwickelt und dabei insbesondere auf die Code-Größe geachtet. Die meisten sind aus frei zugänglichen Bibliotheken extrahiert und abgewandelt.

Als erstes werden technische Möglichkeiten aufgezeigt, den Datenverkehr zu reduzieren.

In­halts­ver­zeich­nis 

Traffic-Reduzierung

HTTP-Header max-age

Kompressor / Minifier

HTTP-Daten-Request

UrsOdometer

Verwendungsbeispiel

Download

UrsLCDDisplay

Verwendungsbeispiel

Download

UrsSlidingScale

Verwendungsbeispiel

Download

Animationen

Animiertes Beispiel

Download


Traffic-Reduzierung

HTTP-Header max-age

Eine Performance-Steigerung erreicht man dadurch, dass Dateien, die sich eigentlich nie ändern, nicht bei jedem neuen Laden der Seite übertragen werden müssen. Die Header-Angabe max-age im HTTP-Response gibt eine Lebensdauer für das übertragene Objekt an. Angegeben wird die Anzahl Sekunden, während derer das zurück gelieferte Objekt gültig ist. Bis zum Ablauf dieser Zeit wird, wenn möglich, das Objekt aus dem Browser-Cache geladen. Mit dem AsyncWebServer kann dies z.B. so erreichen:

serveStatic("/ein.png", SPIFFS, "/ein.png").setCacheControl("max-age=3600"); 

Die Datei "ein.png" besitzt eine Lebensdauer von 3600 s = 1 h. Wenn man mehrere Dateien mit einer Lebensdauer versieht, sollten diese leicht unterschiedlich sein, damit zu einem späteren Zeitpunkt nicht alle Dateien gleichzeitig abgerufen werden.

Im Netzt findet man, dass man die Lebensdauer auf bis zu ein Jahr (= 31.536.000) setzen kann. Jedoch begrenzen einige Browser diese Zeit, Firefox z.B. auf 86400 (= 1 Tag).

Kompressor / Minifier

JavaScript- und CSS-Dateien enthalten eine Menge Text, der den Inhalt lesbar macht, aber für die Verarbeitung nicht notwendig ist. Dieser Text wird natürlich mit übertragen und verschwendet Ressourcen. Minimierer oder Kompressoren schaffen Abhilfe: siehe Tipps zum spinnen: JavaScript-Minifier.

HTTP-Daten-Request

Um veränderte Daten anzuzeigen besteht die Möglichkeit, die Web-Seite regelmäßig in kurzen Abständen neu zu laden. Dies geht z.B. über den Eintrag <META HTTP-EQUIV="refresh" CONTENT="15"> im <head>-Tag der Seite. Im Content-Attribut steht die Anzahl an Sekunden, nach der der Browser ein Neuladen der Seite auslösen soll. Diese Methode ist einfach, sorgt aber dafür, dass die Seite neu übertragen und aufgebaut werden muss. Besser ist es, per JavaScript ein separaten HTTP-Request abzusetzen, der nur die veränderten Daten lädt.

Zunächst definiert man eine Funktion, die den Request auslöst und auf die Antwort reagiert:

function refreshData() {
  var request = new XMLHttpRequest();

  request.open("GET","data");
  request.addEventListener('load', function(event) {
    if (request.status >= 200 && request.status < 300) {
       // request.responseText steht der zurück gelieferte Text
       // ToDo: Text auswerten und Seiteninhalte anpassen
       // z.B.: if (request.responseText == "on") … (s.u.)
    } else {
      // keine gültige Antwort
      // ToDo: Seiteninhalte anpassen
      // z.B.: console.log("No answer");
    }
  });
  request.send();
 }

Dann sorgt man dafür, dass die Funktion regelmäßig ausgelöst wird:

refreshData();
window.setInterval(refreshData, 1000);

Diesen Code verknüpft man mit dem Load- oder dem DOMContentLoaded-Ereignis, z.B.:

document.addEventListener('DOMContentLoaded', function(event) {
  refreshData();
  window.setInterval(refreshData, 1000);
});

Der Web-Server muss auf die HTTP-Anfrage zur URL "data" entsprechend antworten. z.B.:

on("/data", HTTP_ANY, [](AsyncWebServerRequest *request) {
  request->send(200, "text/plain", Relais.isOn() ? "on" : "off");
});

liefert "on" bzw. "off" als Antwort, je nach Stellung des Relais.


UrsOdometer

UrsOdometer

Das Odometer-Control habe ich aus den SteelSeries-Canvas von Gerrit Grunwald (HanSolo) extrahiert und ein wenig angepasst. Das Steuerelement zeichnet sich auf ein existierendes <canvas>-Element.

<canvas id="canvasOdometer" width="240" height="90" style="background-color:#D0D0D0"></canvas>

Es lässt sich über den Konstruktor konfigurieren:

var UrsOdometer = function (canvas, parameters)

z.B.:

odometer1 = new UrsOdometer('canvasOdometer', {height:60, decimals:1, digits:4, left:15, top:15});

Konstruktor

Argument Bedeutung Anmerkung
canvas <canvas>-Element oder ID eines <canvas>-Elements. Wird ein String übergeben, wird das <canvas>-Element per document.getElementByID gesucht. Ansonsten wird direkt mit dem Element gearbeitet. Wird das Parameter-Member context (s.u.) angegeben, wird dieses Argument nicht ausgewertet.
parameters Ein Objekt, dessen Member Konfigurationsangaben für das Steuerelement bereit stellen. Am einfachsten geschieht dies, indem man das Objekt im Konstruktor erzeugt:
{<Name>:<Wert> [, <Name>:<Wert>]} (s. Beispiel).
Die Möglichen Parameterangaben sind in der folgenden Tabelle aufgelistet.

Folgende Parameter-Angaben im Konstruktor werden ausgewertet:

Parameter Typ Bedeutung Voreinstellung
context CanvasRenderingContext2D Das Konstruktor-Argument canvas wird nicht ausgewertet. Statt dessen erfolgt die Ausgabe über das hier angegebene Objekt. NULL
left Number Position der linken Kante des Steuerelements. 0
top Number Position der Oberkante des Steuerelements. 0
height Number Höhe der Ziffern.
Die Weite ergibt sich automatisch.
Wenn top angegeben wird, sollte auch height angegeben werden.
Höhe des <canvas>-Elements.
resize Boolean Die Größe des <canvas>-Elements wird auf die Größe des Steuerelements eingestellt.
Die Angabe ist nur dann sinnvoll, wenn left und top nicht angegeben werden.
false
digits Integer Anzahl Vorkommastellen. 6
decimals Integer Anzahl Nachkommastellen. 1
valueForeColor Color Farbe der Vorkommastellenziffern. '#F8F8F8' (nahezu weiß)
valueBackColor Color Farbe des Hintergrunds der Vorkommastellenziffern. '#050505' (nahezu schwarz)
decimalForeColor Color Farbe der Nachkommastellenziffern. '#F01010' (nahezu rot)
decimalBackColor Color Farbe des Hintergrunds der Nachkommastellenziffern. '#F0F0F0' (sehr helles grau)
font String Schriftart 'sans-serif'
wobbleFactor Number Vertikaler Zufallsversatz der einzelnen Ziffern 0.07
value Number Anzeigewert.
Teile der letzten Stelle werden durch teilweise Rotation der letzten Ziffer angezeigt.
Bei Werten, die größer als der maximale Anzeigewert sind, werden die führenden Ziffern abgeschnitten.
0

Methoden

Methode Bedeutung Anmerkung
setValue (NewVal) Legt einen neuen Anzeigewert fest. Das Steuerelement wird neu gezeichnet.
getValue() Ruft den aktuellen Anzeigewert ab.  
repaint() Bewirkt ein Neuzeichnen des Steuerelements.  

Verwendungsbeispiel

Das folgende Beispiel zeigt die Verwendung dieses Steuerelements:

<!DOCTYPE html>
<html manifest="demo.manifest"><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <meta charset="utf-8">
  <title>UrsOdometer</title>
  <script src="UrsOdometer-min.js"></script>
</head>

<body onload="init()" >

<canvas id="canvasOdometer" width="240" height="90" style="background-color:#D0D0D0"></canvas>

<script>
  var odo, n = 999997.76;

  function init() {
    odo = new UrsOdometer('canvasOdometer', {height:60, decimals:1, digits:4,  left:15, top:15});
    updateOdo();
  }

  function updateOdo() {
    n += 0.005;
    odo.setValue(n);
    setTimeout("updateOdo()", 50);
  }

</script>

</body>
</html>

Download

Das RAR-Archiv enthält folgende Dateien:

Download Download


UrsLCDDisplay

UrsLCDDisplay

Das LCD-Display habe ich aus den SteelSeries-Canvas von Gerrit Grunwald (HanSolo) extrahiert (dort heißt das entsprechende Element 'single') und ein wenig angepasst. Das Steuerelement zeichnet sich auf ein existierendes <canvas>-Element.

<canvas id="canvasLCD" height="150" width="200" style="background-color: #c0c0c0"></canvas>

Es lässt sich über den Konstruktor konfigurieren:

var UrsLCDDisplay = function (canvas, parameters)

z.B.:

var LCD1 = new UrsLCDDisplay('canvasLCD', {height:60, width:155, left:15, top:15});

Konstruktor

Argument Bedeutung Anmerkung
canvas <canvas>-Element oder ID eines <canvas>-Elements. Wird ein String übergeben, wird das <canvas>-Element per document.getElementByID gesucht. Ansonsten wird direkt mit dem Element gearbeitet. Wird das Parameter-Member context (s.u.) angegeben, wird dieses Argument nicht ausgewertet.
parameters Ein Objekt, dessen Member Konfigurationsangaben für das Steuerelement bereit stellen. Am einfachsten geschieht dies, indem man das Objekt im Konstruktor erzeugt:
{<Name>:<Wert> [, <Name>:<Wert>]} (s. Beispiel).
Die Möglichen Parameterangaben sind in der folgenden Tabelle aufgelistet.

Folgende Parameter-Angaben im Konstruktor werden ausgewertet:

Parameter Typ Bedeutung Voreinstellung
context CanvasRenderingContext2D Das Konstruktor-Argument canvas wird nicht ausgewertet. Statt dessen erfolgt die Ausgabe über das hier angegebene Objekt. NULL
left Number Position der linken Kante des Steuerelements. 0
top Number Position der Oberkante des Steuerelements. 0
height Number Höhe des Steuerelements. Höhe des <canvas>-Elements.
width Number Breite des Steuerelements. Breite des <canvas>-Elements.
align String Textausrichtung: "left", "center" oder "right" "right"
value String Anzeigewert.
'0'

Methoden

Methode Bedeutung Anmerkung
setValue (newValue) Legt einen neuen Anzeigewert fest. Das Steuerelement wird neu gezeichnet.
setAlign (newAlign) Textausrichtung festlegen, "left", "center" oder "right" sind möglich.
repaint () Bewirkt ein Neuzeichnen des Steuerelements.  

Verwendungsbeispiel

Das folgende Beispiel zeigt die Verwendung dieses Steuerelements:

<!DOCTYPE html>
<html>

<head>
   <meta content="text/html; charset=UTF-8" http-equiv="content-type">
   <title>URS LCD Display</title>
</head>

<body onload="init()">

<canvas id="canvasLCD" height="150" width="200" style="background-color: #c0c0c0">
</canvas>

<script>
    var LCD1;

    function init() {
         // Anzeige initialisieren
         LCD1 = new UrsLCDDisplay('canvasLCD', {
                            width: 155,
                            height: 40,
                            left: 30,
                            top: 50
                            });
        // Wert setzen
        LCD1.setValue("3d 18h 44s")
   }

</script>
<script src="UrsLCDDisplay-min.js"></script>

</body>

</html>

Download

Das RAR-Archiv enthält folgende Dateien:

Download Download


UrsSlidingScale

UrsSlidingScale

Die Quelle für dieses Steuerelement ist das Sliding Scale Gauge von Tefik Becirovic auf Code Project. Das Steuerelement zeichnet sich auf ein existierendes <canvas>-Element.

<canvas id="slideCanvas" width="330" height="90" style="background-color:#D0D0D0"></canvas><br>

Es lässt sich über den Konstruktor konfigurieren:

var UrsSlidingScale = function (canvas, parameters)

z.B.:

SlidingScale = new UrsSlidingScale('slideCanvas', {height:60, width:300, top: 15, left: 15, scaleColor: '#4682b4'});

Konstruktor

Argument Bedeutung Anmerkung
canvas <canvas>-Element oder ID eines <canvas>-Elements. Wird ein String übergeben, wird das <canvas>-Element per document.getElementByID gesucht. Ansonsten wird direkt mit dem Element gearbeitet. Wird das Parameter-Member context (s.u.) angegeben, wird dieses Argument nicht ausgewertet.
parameters Ein Objekt, dessen Member Konfigurationsangaben für das Steuerelement bereit stellen. Am einfachsten geschieht dies, indem man das Objekt im Konstruktor erzeugt:
{<Name>:<Wert> [, <Name>:<Wert>]} (s. Beispiel).
Die Möglichen Parameterangaben sind in der folgenden Tabelle aufgelistet.

Folgende Parameter-Angaben im Konstruktor werden ausgewertet:

Parameter Typ Bedeutung Voreinstellung
context CanvasRenderingContext2D Das Konstruktor-Argument canvas wird nicht ausgewertet. Statt dessen erfolgt die Ausgabe über das hier angegebene Objekt. NULL
left Number Position der linken Kante des Steuerelements. 0
top Number Position der Oberkante des Steuerelements. 0
height Number Höhe des Steuerelements.
Wenn top angegeben wird, sollte auch height angegeben werden.
Höhe des <canvas>-Elements.
width Number Breite des Steuerelements.
Wenn left angegeben wird, sollte auch width angegeben werden.
Breite des <canvas>-Elements.
resize Boolean Die Größe des <canvas>-Elements wird auf die Größe des Steuerelements eingestellt.
Die Angabe ist nur dann sinnvoll, wenn left und top nicht angegeben werden.
false
scaleRange Integer Wertebereich des Skalenteils, der im Steuerelementfenster angezeigt wird. 100
largeTicksCount Integer Anzahl der Hauptstriche innerhalb von scaleRange. 5
smallTicksCount Integer Anzahl der Teilstriche innerhalb von largeTicksCount. 4
largeTicksLength Integer Länge der Hauptstriche. 15
smallTicksLength Integer Länge der Teilstriche. 10
scaleColor Color Farbe der Skalenbeschriftung. '#00000' (schwarz)
value Number Anzeigewert.
Teile der letzten Stelle werden durch teilweise Rotation der letzten Ziffer angezeigt.
Bei Werten, die größer als der maximale Anzeigewert sind, werden die führenden Ziffern abgeschnitten.
0

Methoden

Methode Bedeutung Anmerkung
setValue (NewVal) Legt einen neuen Anzeigewert fest. Das Steuerelement wird neu gezeichnet.
getValue() Ruft den aktuellen Anzeigewert ab.  
repaint() Bewirkt ein Neuzeichnen des Steuerelements.  

Verwendungsbeispiel

Das folgende Beispiel zeigt die Verwendung dieses Steuerelements:

<!DOCTYPE html>
<html manifest="demo.manifest">
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <title>UrsSlidingScale</title>
    <script src="UrsSlidingScale-min.js"></script>
</head>
<body onload="init()">
    <canvas id="slideCanvas" width="330" height="90" style="background-color:#D0D0D0"></canvas><br>


<script>
   var sld;
   var sldVal = 124;
   var UpDown = 1;
   function init() {
      sld = new UrsSlidingScale('slideCanvas',
                                {height:60, width:300, top: 15, left: 15, scaleColor: '#4682b4', value: sldVal});
      updateSld();
   }

  function updateSld() {
    if (UpDown == 1) { // steigende Werte
       if (sld.getValue() < sldVal) { // weiter erhöhen
          sld.setValue(sld.getValue() + 0.1);
       } else { // neuer niedriger Wert
         UpDown = -1;
         sldVal -= Math.random() * 20;
         if (sldVal < 0)
            sldVal = 0;
       }
    } else { // fallende Werte
       if (sld.getValue() > sldVal) { // weiter reduzieren
          sld.setValue(sld.getValue() - 0.1);
       } else { // neuer höherer Wert
         UpDown = 1;
         sldVal += Math.random() * 20;
       }
    }
    setTimeout("updateSld()", 1);
  }
</script>

</body>
</html>

Download

Das RAR-Archiv enthält folgende Dateien:

Download Download


Animationen

Wenn sich die Werte der im folgenden beschriebenen Controls instantan ändern sieht das nicht immer gut aus. Schöner wäre ein gleitender Übergang. Von den Controls UrsOdometer und UrsSlidingScale gibt es deshalb eine erweiterte Version UrsOdometerEx und UrsSlidingScaleEx. Diese besitzen eine zusätzliche Methode setValueAninamted(newValue, duration). Bei der Verwendung dieser Methoden wird der neue Wert in mehreren Schritten eingestellt, um das physikalische Verhalten entsprechender Anzeigegeräte nachzubilden. Dies nennt man Tweening.

Die SteelSeries-Canvas-Bibliothek enthält auch ein Modul tween.js, durch dass die Zwischenschritte elegant berechnet werden können. Leider habe ich keine komplette Dokumentation von dieses Typs gefunden. Aus diversen lässt sich aber das Folgende ableiten.

Verwendung von Tween in UrsOdometerEx und UrsSlidingScaleEx

Wenn das Anzeige-Element einen neuen Wert erhalten soll, wird dessen Methode setValueAninamted(newValue, duration) aufgerufen. newValue ist der neue Anzeigewert und duration ist die Zeit in Sekunden innerhalb der der neue Wert erreicht werden soll.

In setValueAninamted wird ggf. eine bereits eine aktive Animation gestoppt. Dann wird ein neues Objekt des Typs Tween angelegt. Dem Konstruktor werden in dieser Anwendung mitgegeben:

Die Übergangsfunktion legt fest, wie sich der aktuelle Wert zum neuen entwickelt. Setzt man z.B. hier die in tween.js definierte Funktion Tween.regularEaseOut ein, erfolgt zunächst eine schnelle Annäherung an den Zielwert. Mit der Zeit nimmt die Änderung jedoch ab (Abbremsung). Der Werteverlauf ist dann etwa folgender:

easeOut Weitere Funktionen unter WEB-Controls: Tweening.

Bevor das Tweening gestartet wird, wird dem Tween-Objekt ein Event-Handler für das Ereignis onMotionChanged mitgegeben. Der Parameter der Handler-Methode enthält u.a. den neu einzustellenden Wert. Dieser wird auf die entsprechende Eigenschaft des Controls übertragen (value) und eine Neuzeichnung veranlasst.

Wenn die Anzeigedaten regelmäßiges von einem Server nachgeladen werden, empfiehlt es sich, die duration etwas größer als das Nachlade-Interval anzugeben. Die Anzeige stoppt dann nicht zwischen zwei Messwerten.

Beim Odometer zeigt die lineare Tweening-Funktion ein ansprechendes Verhalten, beim SlidingScale liefert Tween.strongEaseOut gute Ergebnisse. Andere Funktionen können im Quellcode angegeben werden.

Animiertes Beispiel

NoCanvas

Hinweis: Die angezeigten Werte sind nicht synchronisiert sondern Zufallswerte.

Download

Das RAR-Archiv enthält folgende Dateien:

Download Download