Connecting EspruinoWiFi to computer over WiFi

Posted on
  • I'd like to connect Espruino WiFi to my computer over WiFi so that I can send data from (f.e. accelerometer data) from Espruino to the computer without a wired connection.

    I could easily set up website and communicate through it, but that would be too slow.

    When both devices are connected to a router, I am thinking to have the computer scan the network for IPs and send a request to each one, then Espruino will reply when the computer hits its IP and then the computer can know which one is Espruino.

    Is there a better way? Maybe I can pre-configure the IPs and just hard code them? Any tips on how to do it?

    Maybe it is possible to do it directly from computer to Espruino with Espruino is a WiFi AP? Would that work and be better?

  • ...get Web sockets going... or on a higher level mqqt... there are plenty examples out there...

  • Hmmm, maybe I can request a predefined IP on the computer DHCP, then just have espruino talk to that IP hard coded. Gonna try that out...

  • @allObjects I see the ws module for example, but how do I make the connection without knowing IP addresses?

  • Seems to me like the client (espruino using ws module) needs to know server (computer) IP? So gonna try that DHCP idea first or something.

  • Alright, I think I found a way, Chrome OS has a "network" tab for my wifi connection, and I can turn of automatic configuration and specify an IP address. I think now I can just use the ws module to connect to the computer...

  • I know the IP of my computer, but when I try

            require("http").get('http://192.168.0.5:3000', function(res) {
                res.on('data', function(d) {
                    console.log("--->"+d);
                });
            }).on('error', function(e) {
                console.log(' --- ERROR: ', e)
            });
    

    it shows the error

     --- ERROR:  { "code": -15,
      "message": "no response"
     }
    

    But I can access http://192.168.0.5:3000 on my desktop browser.

    Any thoughts on why Espruino might not be able to access it?

    EDIT: hmm, I can access the Espruino if I know it's IP:

    ┌─[00:41:15/localhost/chronos/.../src/trusktr+trusktr.io]
    └─╼ ping 192.168.0.8
    PING 192.168.0.8 (192.168.0.8) 56(84) bytes of data.
    64 bytes from 192.168.0.8: icmp_seq=1 ttl=255 time=15.1 ms
    64 bytes from 192.168.0.8: icmp_seq=2 ttl=255 time=35.6 ms
    64 bytes from 192.168.0.8: icmp_seq=3 ttl=255 time=61.1 ms
    

    Is there a way to make Espruino's IP static?

  • I just posted a workaround for setting a static IP address here:
    https://github.com/espruino/EspruinoDocs/issues/363

    Assuming that works for you it could easily be rolled into the EspruinoWiFi library.

    I'm not sure why you're not getting a response from .get('http://192.168.0.5:­3000' though - did it just happen the once, or does it happen multiple times? Do you see the request appearing on your server at all?

  • @Gordon, great idea... and the IP can be put into a module to externalize the same way as the network credentials - SSID and password - as mentioned in this (and another) post.

    You place the module code

    /* credsXYZ.js */
    exports = { id: "XYZ", ssid: "...", pw: "...", ip: "..." };
    

    as credsXYZ.js into your project's sandbox .../modules folder and pull it into your code and use it this way:

    var creds = require("credsXYZ");
    // ...
    ... .connect(creds.ssid,creds.pwd,...
    //  
    ... .get(creds.ip,...
    

    The creds object can be complemented with other information you need, but at some point you will just create another object of less data privacy and call it cfg and store and use it the same way as the creds object.

    Giving the module a suffix (and id to the object) - such as XYZ - you can have multiples and load the code to the different devices with respective credentials and configuration.

    2cts about code modularity / reuse / 'hiding'.

  • @Gordon

    I'm not sure why you're not getting a response from .get('http://192.168.0.5:3000' though - did it just happen the once, or does it happen multiple times? Do you see the request appearing on your server at all?

    That didn't seem to work at all on the Espruino end, but I didn't check if the server received anything.

    However, now I'm connecting from computer to Espruino. What I'm currently doing is the browser sends a websocket request to the local Meteor server (simply calling an RPC method defined on the server, not needing to know Meteor's DDP websocket API), then the server does a GET on the Espruino chip, and the Espruino sends back accelerometer/gyro data.

    It takes 120 to 200 milliseconds for the server to receive a response back from the Espruino. This includes the time it takes for Espruino to request data from the MPU6050 module. So it's not really ideal for realtime visuals. I haven't timed how long the MPU6050 request takes yet.

    At first I tried the browser's new fetch() API, but it would not work due to this problem: https://bugs.chromium.org/p/chromium/issues/detail?id=728953

    So instead of making direct HTTP requests from the browser I am going through the local Meteor server which obviously will take longer.

    Do you know if the Espruino WiFi is capable of replying with data over WiFi at a speed closer to something usable with a graphical application? If not, maybe can you recommend a chip that is easy to install Espruino firmware on, so that it can be faster (and just as easy to connect to in Web IDE)?

  • I think you'd struggle with sensible realtime speeds using individual HTTP requests... On pretty much any device running Espruino.

    What about just using websockets, or opening a direct socket connection to Espruino? Those would be really quick (especially sockets).

  • Thanks for the tips @Gordon. I gave websockets a try on Espruino Wifi, like the following, and it seems to work at a reasonable rate that could be used for animation, however every few seconds or so the incoming values on the UI seems to pause for a very noticeable while. Could it be GC on Espruino? Maybe there's some way to smooth it out?

    By values, I meant that Chrome console shows a counter for every time the same thing is logged. So this counter increments, it isn't really a value sent from Espruino, but the counter pauses every short while, which could cause "jank". I wonder if it is possible to fix that.

    function getMPU() {
        I2C1.setup({scl:B6,sda:B7, bitrate: 100000});
    
        var MPU6050 = require("MPU6050");
        console.log(MPU6050);
    
        var mpu = MPU6050.connect(I2C1);
    
        return mpu
    }
    
    function connectToWifi(wifiName, options) {
        var resolve, reject
        var promise = new Promise(function(res, rej) {resolve = res; reject = rej})
    
        var wifi = require("EspruinoWiFi");
    
        wifi.connect(wifiName, options, function(err) {
            if (err) reject(err);
            resolve();
        });
    
        return promise
    }
    
    connectToWifi("starlancer", { password : "Next stop: Mars." })
    .then(function() {
        const mpu = getMPU();
    
        const wifi = require("EspruinoWiFi");
        wifi.getIP(function(err, info) {
            if (err) throw err
            console.log('IP:', info.ip)
        });
      
        var page = '<html><body><script>var ws;setTimeout(function(){';
        page += 'ws = new WebSocket("ws://" + location.host + "/my_websocket", "protocolOne");';
        page += 'console.log("host", location.host);'; // <--------------------- this is logged slowly.
        page += 'ws.onmessage = function (event) { console.log("MSG:"+event.data); };';
        page += 'setTimeout(function() { ws.send("Hello to Espruino!"); }, 1000);';
        page += '},1000);</script></body></html>';
    
        function onPageRequest(req, res) {
          res.writeHead(200, {'Content-Type': 'text/html'});
          res.end(page);
        }
    
        var server = require('ws').createServer(onPageRequest);
        server.listen(8000);
        server.on("websocket", function(ws) {
          ws.on('message',function(msg) { print("[WS] "+JSON.stringify(msg)); });
          setInterval(function() {ws.send("Hello from Espruino!");}, 42);
        });
    });
    
    function onInit() {
        console.log('Espruino started!');
    }
    

    Thanks for all the help! I am loving to learn, but I have a long ways to go.

  • I wonder what happens if the Espruino sends too many values faster than the network can handle? I wonder if it can crash if it is sending too fast, and how to prevent that. Could that be a problem?

  • Cool, even a higher rate for the interval like 20ms is nice and fast, but still with an occasional very noticeable pause. Hmmm...

  • Alright, so I'm trying it with actual values from the accelerometer, but that really slows it down. For example:

    function getMPU() {
        I2C1.setup({scl:B6,sda:B7, bitrate: 100000});
    
        var MPU6050 = require("MPU6050");
        console.log(MPU6050);
    
        var mpu = MPU6050.connect(I2C1);
    
        return mpu
    }
    
    function connectToWifi(wifiName, options) {
        var resolve, reject
        var promise = new Promise(function(res, rej) {resolve = res; reject = rej})
    
        var wifi = require("EspruinoWiFi");
    
        wifi.connect(wifiName, options, function(err) {
            if (err) reject(err);
            resolve();
        });
    
        return promise
    }
    
    function getIP() {
        const wifi = require("EspruinoWiFi");
        wifi.getIP(function(err, info) {
            if (err) {
                console.log('Wifi connection failed, trying again in a sec...')
                setTimeout(getIP, 1000)
            }
            console.log('IP:', info.ip)
        });
    }
    
    connectToWifi("starlancer", { password : "Next stop: Mars." })
    .then(function() {
        getIP();
    
        var server = require('ws').createServer(function() {});
    
        server.listen(80);
        server.on("websocket", function(ws) {
            ws.on('message', function(msg) {
                print("[WS] "+JSON.stringify(msg));
            });
            const mpu = getMPU();
            setInterval(function() {
                let result = {
                    acceleration: mpu.getAcceleration(), // returns an [x,y,z] array with raw accl. data
                    gravity: mpu.getGravity(),  // returns acceleration array in G's
                    rotation: mpu.getRotation(), // returns an [x,y,z] array with raw gyro data
                    degreesPerSecond: mpu.getDegreesPerSecond(), // returns gyro array in degrees/s
                }
                result = JSON.stringify(result)
                ws.send(result);
            }, 16.666);
        });
    
    });
    
    function onInit() {
        console.log('Espruino started!');
    }
    

    And the browser just does something like

        let mpuData = {...defaults};
        let ws = new WebSocket("ws://192.168.43.247/my_websocket", "protocolOne");
        ws.addEventListener('message', ({data}) => {
            mpuData = parseEspruinoJson(data)
        });
    
        // ... use mpuData in requestAnimationFrame loop ...
    

    The rendering in my animation loop is slow now that I'm sending actual MPU data.

    Also after a short while I start to see some out-of-memory output until it crashes:

     _____                 _
    |   __|___ ___ ___ _ _|_|___ ___
    |   __|_ -| . |  _| | | |   | . |
    |_____|___|  _|_| |___|_|_|_|___|
              |_| http://espruino.com
     1v91 Copyright 2016 G.Williams
    >
    =undefined
    IP: 192.168.43.247
    {
      "connect": function (a,c) {return new b(a,c)}
     }
    [WS] "Hello to Espruino!"
    ERROR: Out of Memory!
    Execution Interrupted
    Execution Interrupted
    Execution Interrupted
    Execution Interrupted
    Execution Interrupted
    Execution Interrupted
    Execution Interrupted
    Uncaught InternalError: Timeout on I2C Read Receive
     at line 1 col 66
    ...is.i2c.readFrom(this.addr,6);a=c[0]<<8|c[1];var b=c[2]<<8|c[...
                                  ^
    in function "readSXYZ" called from line 1 col 17
    this.readSXYZ(59)
                    ^
    in function "getAcceleration" called from line 2 col 51
    ...ation: mpu.getAcceleration(), // returns an [x,y,z] array wi...
                                  ^
    in function called from system
    

    It seems like trying to read MPU and send over socket every 16.666ms is too much. Is that right?

    What might be the best way to do this to get values as fast as possible and not crash it? Maybe I need to be able to somehow detect how many network request and/or MPU requests are queued, and not request faster than is possible, somehow?

  • Yes, I imagine every 16ms is too quick, and likely the data is just getting buffered until you run out of memory.

    It's a difficult one - it is possible to actually look at the internals of the socket connection and see how much data there is in the buffer, but personally I'd say just back off a bit until you get something that works reliably. Obviously if you've got the buffer even at all full then that's data that is being delayed from being sent - so having slower data is better than having fast data that's out of date.

    Also - try sending the data as binary, or at the very least change the existing JSON so you sent a a field rather than acceleration and so on - which should reduce the amount of bytes that need to be sent.

    As for the pause, I don't know what to suggest - maybe blink an LED each time Espruino takes a reading so you can see if it's the Espruino or something else that's causing the delay. Espruino's GC is called very rarely, and is designed to be super-fast (it should be well under 2ms) so you shouldn't notice it at all.

  • Just an update on what you can expect:

    • The connection to WiFI is 115200 baud - so around 11k bytes a second absolute maximum
    • There's a few characters overhead when sending data, and then there's a bit of a handshake so Espruino can't be sending data all the time
    • Websockets add some overhead too
    • You're sending ~80 bytes of JSON by the look of it? at 60fps that's around 5kB/sec but not including any of the overheads.
  • Might be worth noting that you can get a fair bit more out of the WiFi in terms of baud rate if you configure it.

  • Yes - but to do it reliably you'd need a build for the Espruino WiFi from Git, and to tweak the EspruinoWiFi libs to enable flow control. That's opening a whole new can of worms :)

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

Connecting EspruinoWiFi to computer over WiFi

Posted by Avatar for trusktr @trusktr

Actions