Forum: Mikrocontroller und Digitale Elektronik Websocket, .send geht, wie funktioniert ".receive"?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Sauer (Gast)


Angehängte Dateien:

Lesenswert?

Moin Leute,
um einen kleinen Verstärker neben dem Drehencoder und TFT an der 
Frontplatte auch über WEB steuern zu können, habe ich (widerwillig) die 
Arduino-IDE installiert und übertrage die Slider- und anderen Werte von 
der Webseite mittels websocket an einen nachgerüsteten ESP8266, der 
wiederum über die serielle Schnittstelle 'Variable=Wert'-Paare ausgibt.
Der STM32-Prozessor auf dem Verstärker könnte entfallen und die Sache 
allein mit dem ESP8266 geregelt werden, das ist klar. Andrerseits war 
die Chronologie der Entstehung des Verstärkers anders und die Idee mit 
dem Webfrontend entstand erst vor kurzem. Der STM32 soll auch aus 
anderen Gründen drinbleiben.

Gelöst ist es so: Ein Javascript liest die Werte von den Slidern und 
anderen Steuerelementen und überträgt sie als payload an einen 
websocket, der wiederum die etwas vorgefilterte payload über die 
serielle Schnittstelle des ESP (.onmessage) ausgibt. Der STM32 empfängt 
diese und steuert das TFT, den Klangregler und Anderes. Das funktioniert 
auch soweit und ist prinzipiell dann doch auch nicer Schice für 
leidenschaftliche HTML-, Javascript- und Arduinohasser wie mich.

Es ist also möglich, Daten vom Webfrontend an den STM32 zu senden. Was 
aber noch fehlt, ist der Rückkanal. Der STM32 speichert die 
Einstellungen im Backup-Ram. Wenn der Websocket nun geöffnet wird (weil 
die Webseite zur Steuerung aufgerufen wird, siehe angehängtes Bild), 
bekommt der STM32 eine Mitteilung (.onopen) und sollte diese Daten 
eigentlich an die Slider des Frontends schicken. Im günstigsten Fall 
kann man dann auch die Slider der Webseite über den Drehencoder, der an 
der Frontplatte sitzt, synchron bewegen - das wäre der feuchteste Traum 
meiner Hoffnungen.


Das Problem ist, daß ich diesen Rückkanal vom STM32 (bzw. ESP) zum 
Webfrontend nicht kenne, und beim Googeln auch nicht recht weiterkomme. 
Websockets sind Vollduplex, soviel habe ich inzwischen verstanden. 
'.send' funktioniert auch, aber das entsprechende Wissen über ein 
'.receive' fehlt mir beim socket. Das Einzige, was ich gefunden habe, 
ist ein Console.log(), aber das wird es wohl nicht sein.
Ich stochere da etwas im Nebel.

Habt ihr vielleicht ein Stichwort, wie man diesen mysteriösen Rückkanal 
nennt oder seine Funktionsweise ausfindig machen kann? Den Rest mache 
ich dann selba :-)

von Paul F. (zwanni)


Lesenswert?

Ich weiss nicht ob ich das Problem wirklich verstehe. Du hast eigentlich 
alle Dinge schon genannt die du brauchst.
1
var socket = new WebSocket("ws://....");
2
3
// zum zurücksenden an den "server"
4
socket.send("your message");
5
6
// zum empfangen vom server
7
socket.onmessage = function (event) {
8
  console.log(event.data);
9
}

: Bearbeitet durch User
von Sauer (Gast)


Lesenswert?

Ok, dann stoeber ich mal weiter an dem console.log-Dingsbums rum. Ist ja 
schon ein Hinweis, daß man da weitersuchen kann :-)
Den Pfad zu finden, wie ich von der seriellen Schnittstelle des STM32 
bis zum Slider komme, das ist die Herausforderung. Andersherum geht es 
schon.

Breaking News: Wie durch ein Wunder kann ich schon einen Slider 
verschieben, wenn ich ihm im Javascript einen Wert zuweise (dachte, das 
geht nur andersherum). Wenn das jetzt noch vom STM32 aus geht, bin ich 
glücklich g.

von Paul F. (zwanni)


Lesenswert?

console.log ist nicht die Lösung, das gibt in diesem Beispiel einfach 
nur die ankommende Zeichenkette in der Konsole vom Browser aus. Das 
benutzt man zum debuggen. Die meisten Browser öffnen so eine Art 
developer Tool mit F12 und da gibt es einen reiter "Konsole", dort sieht 
man die Ausgabe.
Die Empfangene Nachricht liegt in "event.data" (im onmessage event wie 
oben in meinem Beispiel), damit musst du dann weiterarbeiten.

: Bearbeitet durch User
von Sauer (Gast)


Lesenswert?

Danke Paul, das Problem liegt/lag - glaube ich - daran, daß der Weg von 
dem seriellen Port bis zum "event" im Javascript noch nicht klar war.
Prinzipiell sollen Frontplatte mit TFT und Drehencoder mit dem 
Webfrontend synchronisiert sein (also jeweils die gleichen Werte für 
Volume, Bass etc. haben und anzeigen).
Es bleibt spannend, aber es ist Licht am Ende des Tunnels zu sehen g

Dieses Video wird des Lötsels endgültige Räsung bringen, hoffe ich
https://www.youtube.com/watch?v=cxbXyTwOl-M
Da werden die Daten der Sensoren vom ESP über websocket zur Anzeige 
gebracht, und genau da hakt es bei mir. Das bastel ich nach, und lerne 
nebenbei noch was über die Vorteile der JSON-Sache.
Vermutlich werde ich dabei wieder auf die event-Methode stoßen, und dann 
ist der Kreis geschlossen.


Das macht inzwischen schon Laune mit dem ESP/Arduino-Zeugs.
Wenn mal im Ansatz klar ist, wie JSON, Javascript und die Funktionen auf 
der html-Seite zusammenspielen, ist schon was gewonnen. Weiß nicht, ob 
es anderen auch so geht, aber die Verknotungen im Hirn beim 
Nachvollziehen, wer bei diesem Server/Client-Zeug was wann wie warum und 
wohin sendet, sind bei diesem Webgedöns nicht ganz ohne.

von Paul F. (zwanni)


Lesenswert?

Das ist völlig normal, wenn man sich mit einer neuen Materie 
beschäftigt. Man hat nur irgendwann mehr Erfahrung wie man sich 
schneller solche Dinge aneignet. Mit websocket und JSON hast du auf 
jeden Fall schon genau die Dinge gefunden die du brauchst.

Beispiel JSON:
Zahlen können ohne Anführungsstriche geschrieben werden, Text immer mit.
Ein einfacher JSON-String mit Key, Value pärchen sieht so aus:
{"Volume": 22, "Treble": 8, "Bass": 8}

Im onmessage event musst du den Json string als erstes in ein Objekt 
parsen:
JSONobj = JSON.parse(event.data);

dann kannst du auf die Werte einfach zugreifen z.b.
var volume = JSONobj.Volume;

Als Anfänger ist es etwas tricky wie man nun auf die HTML Elemente 
zugreift, weil es je nach Typ unterschiedlich ist. Wichtig ist, dass im 
HTML all deine Elemente eine eindeutige id haben.
Um einen Slider zu aktualisieren zum Beispiel so:
document.getElementById("volumeslider").value = volume;

bei einer checkbox oder radio button
document.getElementById("chkbox1").checked = true;

bei einfachem Text in einem HTML Element
document.getElementById("divxyz").innerHTML = "was auch immer";

: Bearbeitet durch User
von Sauer (Gast)


Lesenswert?

Ah gut, das mit dem JSON.parse werde ich auch noch ausprobieren.
Die Wertepaare hatte ich noch selbst gebaut, aber jetzt ist klar, daß 
JSON und die entsprechenden Werkzeuge einem genau diese Dinge abnehmen.

Der Zugriff auf HTML über die IDs ist für die ".send" Richtung (also die 
Sliderstellung zum STM32 übermitteln) genauso gemacht. Da gab es einige 
gute Lern-Beispiele von randomnerdtutorials, tttapa und anderen.


Inzwischen ist der Knackpunkt auch gefunden: Der 
"socket.broadcastTXT(x)" ist das, wonach ich gesucht habe. Der versendet 
ein vorbereitetes JSON-Paket x, das aus den Daten der seriellen 
Schnittstelle zusammengestellt wird. Diese Funktion/Methode kannte ich 
nicht.

Soweit die Theorie. In den nächsten Tagen werde ich's testen :-). Danke 
nochmal für die Unterstützung.

von Sauer (Gast)


Lesenswert?

Jou, das geht so. Checkboxen, Slider, alles läßt sich jetzt von beiden 
Seiten (Webseite und Frontplatte) steuern.

Das "JSON.parse" hat die Sache sehr vereinfacht. Ich habe auf Variablen 
im Javascript verzichtet. So ungefähr sieht das Script jetzt aus
[c]
var power;
var connection = new WebSocket('ws://'+location.hostname+':81/', 
['arduino']);

// -------------------------
// functions
// -------------------------
connection.onopen = function () {
  connection.send('socket=true');
  connection.send('Date=' + new Date());        // Wed Nov 24 2021 
13:59:14 GMT+0100 (Mitteleuropäische Normalzeit)
  connection.send('Unix=' + new Date().getTime());  // unixtime mit ms 
(durch 1000 teilen)
};
connection.onerror = function (error) {
    console.log('WebSocket Error ', error);
};
connection.onmessage = function (event) {
    console.log('Server: ', event.data);

  JSONobj = JSON.parse(event.data);

  document.getElementById('vol').value = JSONobj.Vol;
  document.getElementById("voltxt").innerHTML = JSONobj.Vol;
  document.getElementById('treb').value = JSONobj.Treb;
  document.getElementById("trebtxt").innerHTML = JSONobj.Treb;
  document.getElementById('bass').value = JSONobj.Bass;
  document.getElementById("basstxt").innerHTML = JSONobj.Bass;
//[.....]
  document.getElementById('loud').checked = JSONobj.Loud;
  document.getElementById('mute').checked = JSONobj.Mute;
  document.getElementById('inp'+JSONobj.Input).checked = 1;
  document.getElementById('gain'+JSONobj.Gain).checked = 1;
  power = JSONobj.Power;
  document.getElementById('power').value = power;
};
connection.onclose = function(){
  connection.send('socket=false');
    console.log('WebSocket connection closed');
};

// -------------------------
// functions
// -------------------------
function VolSli() {
  v_vol = document.getElementById('vol').value;
  connection.send('Vol=' + v_vol);
  document.getElementById("voltxt").innerHTML = v_vol;
}
//[.....]
[/code]
Der broadcastTXT(), der das .onmessage-event auslöst, läuft in der 
websocket.loop() in der sketch loop().
So funktioniert das also :-)

Was Lustiges: Der broadcastTXT ballert zurück, was man bei der 
power-Variable merkt: Man drückt den Powerknopf auf der Webseite, das 
wird zum STM32 gesendet, und weil der alternierend schaltet, wird der 
Powerknopf einmal negiert, wenn das Zeug rausgeht, andrerseits merkt der 
STM32, daß sich was geändert hat, und sendet alle Variablen zurück, 
wobei der Knopf dann nochmal negiert wird. Äh, tja. Damit läßt sich das 
Ding also erstmal nicht abschalten gg. Aber das ist ja der übliche 
Mistkram, evtl. geht es auch mit sendTXT(client,str) anstatt 
broadcastTXT(str) oder man nimmt halt einfach eine checkbox anstatt 
einem alternierenden Schalter.

Was allerdings zu erwarten war: Wenn der Drehencoder nicht langsam genug 
gedreht wird, aktualisieren sich die Daten auf der Webseite nicht und 
man sieht in der Konsole, daß nur ein Teil des JSON-Strings ankommt.
Da muß man diesen Riesenstring, der ja alle Variablen beinhaltet, 
nochmal irgendwie stückeln. Es wird ja auch am Drehencoder immer nur 
eine Variable geändert, das ist also sinnloser Overhead jedesmal.
Das kommt dann später mal dran.

Grüße Sauer

von Sauer (Gast)


Lesenswert?

Shit, Formatierung versaut.
v_vol ist außerdem unsichtbar viel weiter oben definiert, aber noch 
experimentell und kann direkt durch 
'document.getElementById('vol').value' ersetzt werden.

Die "document.getElementById("voltxt").innerHTML = JSONobj.Vol"-Elemente 
beziehen sich auf Labels auf der Webseite, die über die id adressiert 
werden und die Werte der Sliderstellung anzeigen. Die Zahlen sieht man 
sonst nicht.

von Sauer (Gast)


Lesenswert?

Inzwischen habe ich nach JSON.Parse mit if-Abfragen auf 
JSONObj.hasOwnProperty erreicht, daß auch Teilstrings akzeptiert werden.

An der Geschwindigkeit in dieser Richtung hat sich aber nichts geändert. 
Wenn man eine Variable am Drehencoder ändert, vergehen ca. 500ms, bis 
der Slider auf der Webseite nachzieht. Obwohl nur ein JSON-String mit 
einer payload von 8 chars übertragen wird ("vol":xx). Andere 
Erfahrungsberichte jammern schon über nur 150ms.
Mal checken, ob das Synchronisieren verbessert werden kann, indem die 
Daten, die vom STM32 zur Webseite gehen, quittiert werden. Wenn der 
Encoder gedreht wird, müssen dann vom Javascript mit connection.send() 
die Werte zurückgesendet werden - und wenn da nichts zum STM32 
zurückkommt, sendet dieser halt mit geeignetem delay solange, bis die 
Werte im Javascript und im STM32-Speicher übereinstimmen. Vermutlich 
wird sich an der Geschwindigkeit selbst nicht viel ändern lassen.

Die umgekehrte Richtung, also vom Bewegen eines Sliders bis zur 
Umsetzung im STM32 geht dagegen mit einer gefühlten Verzögerung von max. 
50ms in Ordnung.

Bisher war ich auf einem anderen System mit NO_SYS=1-lwip immer mit 
'GET' unterwegs, da mußte man alle Änderungen noch mit einem 
submit-Button übertragen.
Bei den Beispielen für den ESP mit AJAX und asynchronem Webserver muß 
zur Werteübernahme der Slider immer losgelassen werden.
Da ist diese socket-Geschichte schon besser. Da schiebt man den Slider 
und die Änderung passiert ohne Absetzen fast gleichzeitig, ist ein viel 
besseres 'look-and-feel'.

von Sascha W. (sascha-w)


Lesenswert?

Moin,

also der Websocket ist für solche Sachen eigentlich recht schnell. 
Vielleicht bremst bei dir schon die serielle Übertragung zwischen STM 
und ESP? Hier muss man ggf. dafür sorgen das bei häufigen Änderungen 
nicht alle übertragen werden.

Sascha

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.