Dennis2019: XMLHttpRequest von anderer IP

Hallo,

ich habe eine HTML Datei mit eingebetetem JAVASCRIPT Code auf meinem Webserver liegen 192.168.178.108, welche GET and Post Befehle zu einem Microcontrollerboard 192.168.178.104 versendet.

Die Post-Befehle funktionieren. Allerdings bekomme ich die Werte nur zurück geliefert, wenn sich die HTML Datei auf dem Microcontrollerboard befindet. Ich möchte allerdings auch die Werte auf dem entfernten Webserver erhalten.

Wie muss ich den Code dafür verändern, dass er sich die Werte von einer bestimmten IP (192.168.178.108) holt?

Vielen Dank!

LG

<!DOCTYPE html>
<html>
    <head>
        <title>Arduino Ajax I/O</title>
        <script>
        strLED1 = "";
        strLED2 = "";
        strLED3 = "";
        strLED4 = "";
        var LED3_state = 0;
        var LED4_state = 0;
        function GetArduinoIO()
        {
            nocache = "&nocache=" + Math.random() * 1000000;
            var request = new XMLHttpRequest();
            request.onreadystatechange = function()
            {
                if (this.readyState == 4) {
                    if (this.status == 200) {
                        if (this.responseXML != null) {
                            // XML file received - contains analog values, switch values and LED states
                            var count;
                            // get analog inputs
                            var num_an = this.responseXML.getElementsByTagName('analog').length;
                            for (count = 0; count < num_an; count++) {
                                document.getElementsByClassName("analog")[count].innerHTML =
                                    this.responseXML.getElementsByTagName('analog')[count].childNodes[0].nodeValue;
                            }
                            // get switch inputs
                            var num_an = this.responseXML.getElementsByTagName('switch').length;
                            for (count = 0; count < num_an; count++) {
                                document.getElementsByClassName("switches")[count].innerHTML =
                                    this.responseXML.getElementsByTagName('switch')[count].childNodes[0].nodeValue;
                            }
                            // LED 1
                            if (this.responseXML.getElementsByTagName('LED')[0].childNodes[0].nodeValue === "checked") {
                                document.LED_form.LED1.checked = true;
                            }
                            else {
                                document.LED_form.LED1.checked = false;
                            }
                            // LED 2
                            if (this.responseXML.getElementsByTagName('LED')[1].childNodes[0].nodeValue === "checked") {
                                document.LED_form.LED2.checked = true;
                            }
                            else {
                                document.LED_form.LED2.checked = false;
                            }
                            // LED 3
                            if (this.responseXML.getElementsByTagName('LED')[2].childNodes[0].nodeValue === "on") {
                                document.getElementById("LED3").innerHTML = "LED 3 is ON (D8)";
                                LED3_state = 1;
                            }
                            else {
                                document.getElementById("LED3").innerHTML = "LED 3 is OFF (D8)";
                                LED3_state = 0;
                            }
                            // LED 4
                            if (this.responseXML.getElementsByTagName('LED')[3].childNodes[0].nodeValue === "on") {
                                document.getElementById("LED4").innerHTML = "LED 4 is ON (D9)";
                                LED4_state = 1;
                            }
                            else {
                                document.getElementById("LED4").innerHTML = "LED 4 is OFF (D9)";
                                LED4_state = 0;
                            }
                        }
                    }
                }
            }
            
            // send HTTP GET request with LEDs to switch on/off if any
            //const url='192.168.178.104:80/ajax_inputs';
            request.open("GET", "http://192.168.178.104", "ajax_inputs" + strLED1 + strLED2 + strLED3 + strLED4 + nocache, true);
            request.send(null);
            setTimeout('GetArduinoIO()', 1000);
            strLED1 = "";
            strLED2 = "";
            strLED3 = "";
            strLED4 = "";
        }
        // service LEDs when checkbox checked/unchecked
        function GetCheck()
        {
            if (LED_form.LED1.checked) {
                strLED1 = "http://192.168.178.104/&LED1=1";
            }
            else {
                strLED1 = "http://192.168.178.104/&LED1=0";
            }
            if (LED_form.LED2.checked) {
                strLED2 = "http://192.168.178.104/&LED2=1";
            }
            else {
                strLED2 = "http://192.168.178.104/&LED2=0";
            }
        }
        function GetButton1()
        {
            if (LED3_state === 1) {
                LED3_state = 0;
                strLED3 = "http://192.168.178.104/&LED3=0";
            }
            else {
                LED3_state = 1;
                strLED3 = "http://192.168.178.104/&LED3=1";
            }
        }
        function GetButton2()
        {
            if (LED4_state === 1) {
                LED4_state = 0;
                strLED4 = "http://192.168.178.104/&LED4=0";
            }
            else {
                LED4_state = 1;
                strLED4 = "http://192.168.178.104/&LED4=1";
            }
        }
    </script>
    <style>
        .IO_box {
            float: left;
            margin: 0 20px 20px 0;
            border: 1px solid blue;
            padding: 0 5px 0 5px;
            width: 120px;
        }
        h1 {
            font-size: 120%;
            color: blue;
            margin: 0 0 10px 0;
        }
        h2 {
            font-size: 85%;
            color: #5734E6;
            margin: 5px 0 5px 0;
        }
        p, form, button {
            font-size: 80%;
            color: #252525;
        }
        .small_text {
            font-size: 70%;
            color: #737373;
        }
    </style>
    </head>
    <body onload="GetArduinoIO()">
        <h1>Arduino Ajax I/O</h1>
        <div class="IO_box">
            <h2>Analog Inputs</h2>
            <p class="small_text">A0 used by Ethernet shield</p>
            <p class="small_text">A1 used by Ethernet shield</p>
            <p>A2: <span class="analog">...</span></p>
            <p>A3: <span class="analog">...</span></p>
            <p>A4: <span class="analog">...</span></p>
            <p>A5: <span class="analog">...</span></p>
        </div>
        <div class="IO_box">
            <h2>Switch Inputs</h2>
            <p class="small_text">D0: used by serial RX</p>
            <p class="small_text">D1: used by serial TX</p>
            <p>Switch 1 (D2): <span class="switches">...</span></p>
            <p>Switch 2 (D3): <span class="switches">...</span></p>
            <p class="small_text">D4: used by Ethernet shield</p>
            <p>Switch 3 (D5): <span class="switches">...</span></p>
        </div>
        <div class="IO_box">
            <h2>LEDs Using Checkboxes</h2>
            <form id="check_LEDs" name="LED_form">
                <input type="checkbox" name="LED1" value="0" onclick="GetCheck()" />LED 1 (D6)<br /><br />
                <input type="checkbox" name="LED2" value="0" onclick="GetCheck()" />LED 2 (D7)<br /><br />
            </form>
        </div>
        <div class="IO_box">
            <h2>LEDs Using Buttons</h2>
            <button type="button" id="LED3" onclick="GetButton1()">LED 3 is OFF (D8)</button><br /><br />
            <button type="button" id="LED4" onclick="GetButton2()">LED 4 is OFF (D9)</button><br /><br />
            <p class="small_text">D10 to D13 used by Ethernet shield</p>
        </div>
    </body>
</html>
  1. Wie muss ich den Code dafür verändern, dass er sich die Werte von einer bestimmten IP (192.168.178.108) holt?

    Die IP ändern. Und das da beachten. MfG

    1. Hallo vielen Dank für die Antwort also muss ich den Code so verändern?:

        // send HTTP GET request with LEDs to switch on/off if any
                  //const url='192.168.178.104:80/ajax_inputs';
                  request.open("GET", "http://192.168.178.104", true);
                  request.setRequestHeader("ajax_inputs" + strLED1 + strLED2 + strLED3 + strLED4 + 
                  nocache);
                  request.send(null);
                  setTimeout('GetArduinoIO()', 1000);
                  strLED1 = "";
                  strLED2 = "";
                  strLED3 = "";
                  strLED4 = "";
              }
      
  2. Noch eine Anmerkung, µC bilden Zustände auf Zahlen ab. Also, zur Übertragung der Zustände von 4 LEDs, Ein/Aus würde eine einziges Byte genügen. Da wäre nur die Zahl festzustellen und das würde Deine Kontrollstruktur erheblich vereinfachen. MfG

  3. Tach!

    ich habe eine HTML Datei mit eingebetetem JAVASCRIPT Code auf meinem Webserver liegen 192.168.178.108, welche GET and Post Befehle zu einem Microcontrollerboard 192.168.178.104 versendet.

    Die Post-Befehle funktionieren. Allerdings bekomme ich die Werte nur zurück geliefert, wenn sich die HTML Datei auf dem Microcontrollerboard befindet. Ich möchte allerdings auch die Werte auf dem entfernten Webserver erhalten.

    Wie muss ich den Code dafür verändern, dass er sich die Werte von einer bestimmten IP (192.168.178.108) holt?

    Ist das nicht logisch, dass man die Adresse ändern muss, wenn man anderswo die Daten abfragen möchte? Irgendwie ist mir die Fragestellung zu einfach. Steckt da vielleicht eigentlich mehr dahinter? Gibt es ein Problem, wenn du die andere Adresse nimmst? Vielleicht ist eine Firewall oder andere Netzwerk-Geschichte zu beachten?

    Ich würde die Basisadresse nicht übers gesamte Script verteilt notieren, sondern eine Variable (oder Konstante in modernem Javascript) nehmen, die einmalig am Scriptanfang definiert ist, und so sehr einfach anzupassen geht.

    dedlfix.

    1. Guten Morgen,

      ohh da habe ich mich wohl falsch ausgedrückt. Die IP Adresse ist richtig, aber ich bekomme kein Request zurück.

      Nur wenn ich die IP Adresse weg lasse und die HTML Datei auf das Board selber packe läuft der Code:

           // send HTTP GET request with LEDs to switch on/off if any
                  request.open("GET", "ajax_inputs" + strLED1 + strLED2 + strLED3 + strLED4 + nocache, true);
                  request.send(null);
                  setTimeout('GetArduinoIO()', 1000);
                  strLED1 = "";
                  strLED2 = "";
                  strLED3 = "";
                  strLED4 = "";
              }
      
      1. Tach!

        ohh da habe ich mich wohl falsch ausgedrückt. Die IP Adresse ist richtig, aber ich bekomme kein Request zurück.

        Request ist das was hingeht, Response ist die Antwort. Schau in den Entwicklertools des Browsers nach, was dort als Netzwerkverkehr zu sehen ist. Meist mit F12 zu erreichen, oder über den kleinen Umweg: Rechtsklick auf ein Element der Seite (auch Hintergrund) und Inspect Element (oder ähnlich), dann in den Netzwerk-Tab wechseln und den Request ausführen.

        dedlfix.

      2. Du setzt Deinen URL falsch zusammen. Damit sind die Argumente für die open()-Funktion ebenfalls falsch. MfG

        1. Hallo,

          wie wäre es denn nichtig?

          LG

  4. Hallo Dennis2019,

    wenn ich mir dein JS so anschaue, hast Du mehrere Probleme. Eins ist das Absetzen des XMLHttpRequests. Da schreibst Du:

    request.open("GET", "http://192.168.178.104", "ajax_inputs" + strLED1 + strLED2 + strLED3 + strLED4 + nocache, true);
    

    Das ist in vielerlei Hinsicht ungeschickt bzw. falsch.

    1. Die Query-Parameter musst Du per Stringverkettung an die Host-Adresse (also http://192.168.178.104) anhängen. Der dritte Parameter von open() heißt async (guck ins Wiki) und steuert, ob der Ajax-Aufruf synchron oder asynchron laufen soll. Lass ihn weg. Der default ist false (asynchron), und das ist richtig.

    2. Du befüllst strLED1 bis strLED4 nicht nur mit den Parametern, sondern mit einer vollständigen URL. Das führt zu Unsinn, wenn Du eine Gesamt-URL zusammensetzen willst. Es ist aber gar nicht nötig, diese Variablen zu benutzen.

    3. Es ist Unsinn, als Reaktion auf Checkbox-Änderungen oder Button-Klicks sofort die URL-Parameter zu ändern. Damit vermischst Du Datenmodell und Arduino-Interface. Deine Checkboxen haben bereits einen internen State. Und für deine Buttons hat Du State-Variablen. Das reicht. Die Parameter für den Ajax-Request baust Du genau dann zusammen, wenn der Ajax-Call gemacht wird. Und den nocache-Parameter brauchst Du nicht, wenn Du es mittels des entsprechenden HTTP-Headers richtig machst. Besser noch ist es, auf dem Arduino diesen Header in die Response zu setzen, dann cached der Browser gar nicht erst.

       let url = "http://192.168.178.104"
               + "?LED1=" + (LED_form.LED1.checked ? "1" : "0")
               + "&LED2=" + (LED_form.LED2.checked ? "1" : "0")
               + "&LED3=" + (LED3_state ? "1" : "0")
               + "&LED4=" + (LED4_state ? "1" : "0");
       
       request.open("GET", url);
       request.setRequestHeader("Cache-Control", "no-cache");
       request.send();
    

    Nix mehr mit strLED1 bis strLED4. Die Funktion GetCheck entfällt. Der click-Eventhandler auf den Checkboxen entfällt. Das ist auch gut so, weil click eigentlich falsch ist - richtig wäre das change Event. Du hast Glück, dass der Browser etwas zur Rettung von Leuten ohne Maus tut und auch bei Tastaturbedienung das click-Event auslöst.

    Und wenn Du LED3_state und LED4_state mit false statt 0 initialisierst, reduziert sich der click-Handler der Buttons 3 und 4 zunächst einmal auf

       ClickButton3() {
          LED3_state = !LED3_state;
       }
       ClickButton4() {
          LED4_state = !LED4_state;
       }
    

    aber das reicht noch nicht, weil Du ja noch ein optisches Feedback einbauen musst, ob die LED an oder aus ist. Checkbox ist einfacher...

    Ach so, Checkbox. Das hier ist übrigens auch falsch:

    <input type="checkbox" name="LED1" value="0" onclick="GetCheck()" />LED 1 (D6)<br /><br />
    

    Richtig ist:

    <label><input type="checkbox" name="LED1" value="0"  /> LED 1 (D6)</label>
    

    und etwas CSS, das dem Label die Eigenschaften display: block; und margin-bottom: 1em; gibt. Vorteil: Du kannst auch auf den Text klicken um die Checkbox umzuschalten, so wie es ein GUI-verwöhnter User erwartet. Die <br> sind präsentationsbezogenes Markup und sollten vermieden werden. <br> reicht übrigens, <br /> brauchst Du nur bei XHTML.

    Soooo. Nachdem wir nun einiges am Client gemacht haben, stellt sich die Frage nach dem Verbleib der Antwort. PL hat bereits CORS angesprochen: Cross Origin Resource Sharing. Dein Arduino erlaubt nicht, dass seine Ressourcen von einer Seite genutzt werden, die nicht vom Arduino geladen wurde. Probiere doch zuerstmal, aus GET ein POST zu machen. Aus mehreren Gründen. Zum einen liest Du nicht nur, sondern änderst was, nämlich die LEDs. Zum zweiten werden POST Requeste nicht gecached, d.h. die Akrobatik mit Cache-Control könnte entfallen. Und drittens ist CORS nur bei Lesezugriffen sensibel, bei Schreibzugriffen weniger. Ich habe hier kein Testsystem dafür und weiß nicht, ob das reicht. Ansonsten guck mal in die MDN: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS.

    Rolf

    --
    sumpsi - posui - clusi
    1. Moin,

      diesen 4 verschiedenen Zuständen (1//0) genügt ein Parameter. Ein µC kriegt da eh nur eine Zahl dafür. MFG

      1. Hallo pl,

        let url = "http://192.168.178.104"
                  + "?ledcontrol=" + ( (LED_form.LED1.checked ? 1 : 0)
                                     + (LED_form.LED2.checked ? 2 : 0)
                                     + (LED3_state ? 4 : 0)
                                     + (LED4_state ? 8 : 0) );
        

        Ja. Kann man machen. Aber sollte man? Sollte man die technischen Details des Mikrocontrollers auf dem API sichtbar machen?

        Das ist die gleiche Diskussion wie JSON vs PL-special-binary. Sollte man technische Details in der Schnittstelle sichtbar machen? Oder eine Serviceschnittstelle so abstrakt wie möglich halten?

        Letztlich ist das eine Frage, die Dennis für sich beantworten muss. Wir beide können uns darauf einigen, dass wir das unterschiedlich sehen 😉

        Rolf

        --
        sumpsi - posui - clusi
        1. moin,

          Ja. Kann man machen. Aber sollte man? Sollte man die technischen Details des Mikrocontrollers auf dem API sichtbar machen?

          Es ist ja genau andersherum: Ein transparenter Transportlayer macht die Schnittstelle unsichtbar.

          MFG

          Demo