Controlling Esp unit from cloud using websockets?

Posted on
Page
of 2
/ 2
Next
  • I want to be able to send commands to my unit from anywhere in the world. I was thinking I could use websockets to do this.

    I would have a server running ws and express which would server up an html page which i could use to interact with the esp unit.

    The esp unit or units would serve as clients to the server? Does this sound doable?

  • Yes, absolutely. There's a bit of information on doing that here: http://www.espruino.com/ws

    If by 'Esp unit' you mean Espruino WiFi or Pico then you can use HTTPS/secure websockets, which will be a bit more secure when you're sending data over the internet.

    Otherwise if you're just talking about bare ESP8266 you're stuck with standard HTTP, in which case some could conceivably spy on the commands that you send.

  • Thanks Gordon, I will take a look and post back instructions on how I did it or if I need help. So that the community can grow.

  • Hey Gordon,
    I looked at the docs, and I am confused as to which code is running where? It seems that the server is running on the ESP device and clients are connecting to it. Are there any examples with the esp unit being the client and a server being the host?

  • as per the link above:

    http://www.espruino.com/ws
    section:

    WebSocket Client

  • Ok so I created a basic webpage from which you can send commands to the device over a WS connection. The node js server acts as the host and the device + browser app act as clients. When a command is sent, it is broadcasted to all devices(Will change later). The command is then checked using a switch statement and an associated function is called on the device.

    I will add functionality to send msgs to specific devices later on and post back to help the community.

    My original idea was to be able to control device over websockets from my cloud server and have total remote control over it by building a front end API. I'm not sure if websockets is the right protocol for this though, I might need to switch to MQTT or a different communication protocol. As of right now, I'm only using 1 device, but I when I scale to maybe 1000 devices this setup might get messy.

    What do you recommend Gordon?

    Server Code (NODE.JS)

    var WebSocketServer = require('ws').Server;
    var express = require('express');
    var path = require('path');
    var app = express();
    var server = require('http').createServer();
    
    //express setup
    app.use(express.static(path.join(__dirna­me, '/public')));
    var wss = new WebSocketServer({server: server});
    
    
    // Broadcast to all.
    wss.broadcast = function broadcast(data) {
      wss.clients.forEach(function each(client) {
        if (client.readyState === WebSocket.OPEN) {
          client.send(data);
        }
      });
    };
    
    //On Initial Connection
    wss.on('connection', function (ws) {
      ws.send(JSON.stringify("Connection:Estab­lished"));
      console.log('started client interval');
    
    //Broadcast incoming Command
      ws.on('message', function incoming(message) {
        console.log('received: %s', message);
        broadcast(message);
      });
    //On Close
      ws.on('close', function () {
        console.log('stopping client interval');
      });
    });
    //Server settings
    server.on('request', app);
    server.listen(8080, function () {
      console.log('Listening on http://localhost:8080');
    });
    //Broadcast Function
    function broadcast(message) {
        wss.clients.forEach(function each(client) {
                client.send(message);
        });
    }
    
    

    WebApp Code
    index.html

    <!DOCTYPE html>
    <html>
      <head>
        <style>
          body {
            font-family: Tahoma, Geneva, sans-serif;
          }
    
          div {
            display: inline;
          }
          '#message-form {
    
          }'
    
    //remove '' from above, I put them in to allow the post to be visible 
        </style>
      </head>
    
      <body>
        <form id="message-form">
          <input name="command" type="text" placeholder="Command"/>
          <button>Send</button>
      </form>
    <script src="https://ajax.googleapis.com/ajax/li­bs/jquery/3.2.1/jquery.min.js"></script>­
    <script src="frontend.js"></script>
      </body>
    </html>
    
    

    front-end ws setup

      var host = window.document.location.host.replace(/:­.*/, '');
      var ws = new WebSocket('ws://' + host + ':8080');
      ws.onopen =  function() {
        console.log("Connected to server");
      };
    
      ws.onmessage =  function(msg) {
          console.log(msg.data);
    
      };
    
      jQuery('#message-form').on('submit', function (e){
        e.preventDefault();
        ws.send(jQuery('[name = command]').val());
      });
    
    

    ESPRUINO Code

    var host = "192.168.2.106";
    var WebSocket = require("ws");
    var ws = new WebSocket(host, {
      path: '/',
      port: 8080, // default is 80
      protocol: "echo-protocol", // websocket protocol name (default is none)
      protocolVersion: 13, // websocket protocol version, default is 13
      origin: 'Espruino',
      keepAlive: 60,
      headers: {
        some: 'header',
        'ultimate-question': 42
      } // websocket headers to be used e.g. for auth (default is none)
    });
    
    ws.on('open', function() {
      console.log("Connected to server");
    });
    
    ws.on('message', function(msg) {
      console.log(msg);
    
      switch (msg) {
        case "ledOn":
          {
            digitalWrite(D5, 1);
            break;
          }
        case "ledOff":
          {
            digitalWrite(D5, 0);
            break;
          }
        default: 
          {
          }
        }
      });
    
    
  • If you're happy with websockets and they work for you then I'd use them.

    MQTT is lighter weight and you can use an off the shelf server for it - however you couldn't access it directly from a webpage... If you used that you'd have to set up your MQTT server to allow MQTT over websockets as well - and then you could have your webpage use MQTT as well.

  • I see I am going to further develop the very simple set up I have going. Basically, by the end, I want a nice looking UI and an underlying API which has full control over an esp unit from anywhere in the world. This would include calling functions, flashing device, and writing a custom library for some specific applications that I have in mind. Once I am done I will upload to github to share with others.

    Gordon I was looking at the docs for the Web IDE, and it mentions flashing/programming over websockets?! I am really interested in this idea, what is your progress? I am willing to put in time to help make that happen?!

  • instead of a switch statement and parsing route, you can also just send functions as strings and have them be evaluated by the esp unit like so. (U can also use the eval function for this)

    ws.on('message', function(msg) {
      console.log(msg);
      var command = new Function(msg);
      command();
      });
    
    
  • The only thing I am confused about is how to send the return output of a function back to my webpage?! For example if I call digitalRead(D5); The function logs either a 0 or 1 to the console. Instead I want to send the response using

    ws.send(response);
    

    I tried setting the console to a variable and then sending the variable over ws, but the msg is always empty.

    clog = console.log;
    ws.send(clog);
    
  • You can also use eval, or could even use LoopbackA and LoopbackB to put the complete Espruino REPL over websockets if you want - in which case you could program the device with the Web IDE.

    Which type of device are you actually using? I'm working on some lower level support for the Wifi in Espruino Wifi that should make it easier to expose that kind of functionality without having to add JS code specifically for it

  • I am using a esp8266 12e developing board by NODEMCU given to me by my professor to get an initial setup going. I plan on buying ESPRUINO WIFIs so I can use https and other functionality so i can scale my device network.

    I wrote my own simple web ide and am using a websocket server. I'll look at the docs for loopbackA and loopbackB and see if i can figure it out!

    Thanks, once I'm done ill put up a tutorial.

  • would it be something like this? I would set the console to loopbackA and then call an on data listener for loopbackB and send that via ws?

    LoopbackA.setConsole();
    LoopbackB.on('data',function(msg){
     ws.send(msg);
    });
    
  • Yes, that's right - and then when you receive websocket data you write it to LoopbackB, and job done :)

  • hmm it makes sense, but when I execute the code and send over something along the lines of console.log("hi"); the loopback wont trigger the ws.send(msg); to send me back hi I'm not sure if I just misunderstood the concept.

    My understanding is that LoopbackA and LoopbackB are connected, data sent to one comes out the other one. In this manner, if I set console to LoopbackA, and then write my ws incoming command the following function should work.

    I've been testing with console.log("Hello"); from my ws server and I expect Hello back. I know the ws stuff is working fine, so it's probably that i miscoded the loopbacks.

    LoopbackA.setConsole();
    
    var host = "192.168.1.103";
    var WebSocket = require("ws");
    var ws = new WebSocket(host, {
      path: '/',
      port: 8080, // default is 80
      protocol: "echo-protocol", // websocket protocol name (default is none)
      protocolVersion: 13, // websocket protocol version, default is 13
      origin: 'Espruino',
      keepAlive: 60,
      headers: {
        some: 'header',
        'ultimate-question': 42
      } // websocket headers to be used e.g. for auth (default is none)
    });
    
    ws.on('open', function() {
      console.log("Connected to server");
      ws.send("esp:Connected");
    });
    
    ws.on('message', function(msg) {
      console.log(msg);
      var command = new Function(msg);
      LoopbackA.write(command());
      });
    
    
    LoopbackB.on('data',function(msg){
     ws.send(msg);
    });
    
  • Try LoopbackB.write(command()); - LoopbackA goes to B and vice-versa. The console's reading and writing on A, so you need to do everything else on B.

  • That didn't work either, I will start debugging now, and will let you guys know when I find the solution.

  • I was messing around and shifting code, I moved LoopbackA.setConsole(); to the bottom, and the device started to transmit error messages to my webUI over websockets!!! So it's possible just not configured correctly.

    in function "command" called from line 2 col 27
      LoopbackB.write(command());
                              ^
    in function called from system
    >
    received:Uncaught SyntaxError: Got ?[8] expected EOF
     at line 1 col 1
    Uncaught SyntaxError: Got ?[8] expected EOF
    ^
    
    

    I am very EXCITED will keep you guys posted.

  • The issue seems to be either LoopbackA.setConsole(); or LoopbackB.write(command());
    The function we wrote works when testing using the usb console LoopbackA.write("Hello");

    Im going to try and use the eval function and see if it makes a difference.

  • Ok I think I know what the issue is basically the write method has an issue with passing it functions. Feel free to look at the screenshot or logs below.

    When I pass LoopbackA.write("Hello"); , Hello is transmitted across ws.

    When I pass LoopbackB.write(digitalWrite(D5,1)); The LED is turned on, but the error below is given.

    When I pass LoopbackB.write(digitalWrite(D5,0)); The LED is turned off, but the error below is given.

    When I pass console.log("test"); test prints out to the ESPRUINO IDE but is not transmitted and the same error is given.

    I also tried directly passing console.log("test"); into LoopbackB but got the same error.

    Check out the attached screenshot, it's much easier to see.
    Console from ESPRUINO WEB IDE

    >LoopbackA.write("Hello");
    =undefined
    >LoopbackA.write(digitalWrite(D5,1));
    Uncaught TypeError: Expecting a number or something iterable, got undefined
     at line 1 col 35
    LoopbackA.write(digitalWrite(D5,1));
                                      ^
    > 
    

    CONSOLE FROM MY WEB APP

    >>Server:Ready!
    >>Hello
    

    1 Attachment

    • Screenshot from 2017-07-18 17:09:13.png
  • Hi - yes, LoopbackB.write(digitalWrite(D5,1)); will fail because digitalWrite returns undefined. However if you're using loopback normally (just passing data from websockets) it shouldn't be an issue.

    I'd be pretty sure your issue is that you're calling LoopbackA.setConsole(); at the start of upload.

    What's happening is you call that, then the console moves out of the way to Loopback, and all code after that command gets ignored! :)

    Add setTimeout(function() {LoopbackA.setConsole();},1o0); at the end of your code - or call setConsole when you actually get a websocket connection.

  • Ok so I put in the `setTimeout(function() { LoopbackA.setConsole();},100); and it WORKS!!! HUZZAH, but there is an issue, after sending over the response, it sends over an infinite stream of the error after logging the return. I have to reset manually to kill the stream.

    I think this is because the error msg itself counts as data and so the LoopbackB.on('data',function(msg){ ws.send(msg); }); function goes into an infinite loop.

    Webconsole below.

    >>Server:Ready!
    >>console.log("Hello");
    >>Hello
     Uncaught TypeError: Expecting a number or something iterable, got undefined
     at line 2 col 29
     LoopbackB.write(command());
     ^
     in function called from system
    >>Uncaught SyntaxError: Got ':' expected EOF
     at line 2 col 19
     Uncaught TypeError: Expecting a number or something iterable...
     ^
     in function "command" called from line 2 col 28
     LoopbackB.write(command());
     ^
     in function called from system
    >>Uncaught SyntaxError: Got ':' expected EOF
     at line 1 col 21
     Uncaught SyntaxError: Got ':' expected EOF
    
     ^
     in function "command" called from line 2 col 28
     LoopbackB.write(command());
     ^
     in function called from system
    >>Uncaught SyntaxError: Got ':' expected EOF
     at line 1 col 21
     Uncaught SyntaxError: Got ':' expected EOF
    
     ^
     in function "command" called from line 2 col 28
     LoopbackB.write(command());
     ^
     in function called from system
    >>Uncaught SyntaxError: Got ':' expected EOF
     at line 1 col 21
     Uncaught SyntaxError: Got ':' expected EOF
    
     ^
     in function "command" called from line 2 col 28
     LoopbackB.write(command());
     ^
     in function called from system
    >>Uncaught SyntaxError: Got ':' expected EOF
     at line 1 col 21
     Uncaught SyntaxError: Got ':' expected EOF
    
     ^
     in function "command" called from line 2 col 28
     LoopbackB.write(command());
     ^
     in function called from system
    >>Uncaught SyntaxError: Got ':' expected EOF
     at line 1 col 21
     Uncaught SyntaxError: Got ':' expected EOF
    
     ^
     in function "command" called from line 2 col 28
     LoopbackB.write(command());
     ^
     in function called from system
    >>Uncaught SyntaxError: Got ':' expected EOF
     at line 1 col 21
     Uncaught SyntaxError: Got ':' expected EOF
    
     ^
     in function "command" called from line 2 col 28
     LoopbackB.write(command());
     ^
     in function called from system
    
  • I think what you want is simply:

    ws.on('message', function(msg) { LoopbackB.write(msg); });
    LoopbackB.on('data',function(msg) { ws.send(msg); });
    

    No function execution or anything.

  • Ok Ill try that, but I think the infinite loop is being caused by the ws server broadcasting the msg to everyone. So basically if the device sends something it gets something back which it then sends out again... I'll have to update my server for individual addressing.

    edit. I tried that and there was no response at all. Im going to work on the indvidual addressing for ws to fix the bounce back of msgs for the esp8266.

  • Sorry for such a late response, my laptop broke and I had to reset up everything from scratch on a new machine.

    So one of my goals here was to get the REPL to be accessible over websockets. I tried the following as suggested, and it resulted in the program emitting the following error messages over websockets to my webIDE.

    ws.on('message', function(msg) { LoopbackB.write(msg); });
    LoopbackB.on('data',function(msg) { ws.send(msg); });
    

    complete code

    var host = "192.168.1.***";
    var WebSocket = require("ws");
    var ws = new WebSocket(host, {
      path: '/',
      port: 8080, // default is 80
      protocol: "echo-protocol", // websocket protocol name (default is none)
      protocolVersion: 13, // websocket protocol version, default is 13
      origin: 'Espruino',
      keepAlive: 60,
      headers: {
        some: 'header',
        'ultimate-question': 42
      } // websocket headers to be used e.g. for auth (default is none)
    });
    
    ws.on('open', function() {
      console.log("Connected to server");
      ws.send("esp:Connected");
    });
    
    ws.on('message', function(msg) {
      LoopbackB.write(msg.toString()); 
    });
    
    LoopbackB.on('data',function(msg) {
      ws.send(msg); 
    });
    
    setTimeout(function() {
      LoopbackA.setConsole();
    },100);
    

    Error transmitted over websockets continually until program is forced to stop

    <- Telnet
     UncauUncaught SyntaxError: Got ':' expected EOF
     at Uncaught SyntaxError: Got ':' expected EOF
     at =19
     =un>=un>=un>=un>=un>=un>=un>=un>=un>=un>­=un>=unUncaught Syn>> at Uncaught Erro> at line 1 c> at line 1 cUncaught SyntaxError: Got '^' exUncaught Syn>>=und=und=und=undUncaught SyntaxError: Got ':' expected E=und=undUncaught SyntaxError: Got ':' expected E=und=undUncaught SyntaxError: Got ':' expected E=und=undUncaught SyntaxError: Got ':' expected E=und=und=und=undUnca=und=undUncaught Synt
    

    The exact error msg is the following

    =un>=un>=un>=un>=un>=un>=un>=un>=un>=un>­=un>=un>=un>=un>=un>=un>=un>=un>=un>=un>­=un>=un>=un>=un>=un>=un>=un>=un>=un>=un>­=un>=un>=un>=un>=un>=un>=un>=un>=un>=un>­=un>=un>=un>=un>=un>=un>=un>=un>=un>=un>­=un>=un>=un>=un>=un>=un>=un>=un>=un>=un>­=un>=un>=un>=un>=un>=un>=un>=un>=un>=un>­=un>=un>=un>=un>=un>=un>=un>=un>=un>=un>­=un>=un>=un>=un>=un>=un>=un>=un>=un>=un>­=un>=un>=un>=un>=un>=un>=un>=un
    received: <- Telnet
    UncauUncaught SyntaxError: Got ':' expected EOF
     at Uncaught SyntaxError: Got ':' expected EOF
     at =19
    
    

    I'm not sure what I'm doing wrong. Any insight? line 19 is just closing braces.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Controlling Esp unit from cloud using websockets?

Posted by Avatar for Esper @Esper

Actions