Avatar for ScottyMT

ScottyMT

Member since Jan 2020 • Last active Aug 2022
  • 2 conversations
  • 9 comments

Most recent activity

  • in General
    Avatar for ScottyMT

    Awesome, thanks! I'm not sure where you find the time to attend to such trivial matters however.

  • in General
    Avatar for ScottyMT

    The other day I looked for a repository for the MQTT module but didn't find it. Now I just discovered it with the EspruinoDocs repo. Perhaps it would be better to make an enhancement issue there to get it in the queue and better track the discussion and changes. Yes?

  • in General
    Avatar for ScottyMT

    Yes, sorry, I should have provided more context. In my particular case, I am using an Espruino WiFi, however I can see how some sort of notification might be helpful in other situations.

    In my case, I have a long setInterval of a half-hour or more. At the interval time, the program exits deep sleep, the WiFi connects, then the MQTT connects, and then the message can publish. After the message is published, everything disconnects and the board is put back into deep sleep. The real issue is being sure the message was sent before slipping back into dreamland.

    My issue is actually tangent to the main protocol. Right now, although the module accepts a QoS option, it doesn't really do anything different in such cases. I can see that it does accept PUBREC and PUBREL and replies correctly in those cases, but PUBACK and PUBCOMP are ignored. There should be a mechanism for retransmission if with a duplicate message if the broker doesn't acknowledge receipt. Then maybe the side benefit is emitting an event of success so that the user program can do something useful (such as go back to sleep in my case).

    The good news is that most of the scaffolding is already in place to correctly handle QoS 1 and 2. It just needs to store and retransmit duplicate messages until the proper acknowledgment is received.

  • in General
    Avatar for ScottyMT

    For a battery operated IoT device which requires infrequent updates, it would be great to enter deep sleep, wake up every so often, connect to an mqtt broker and publish update(s), then disconnect and go back to deep sleep.

    In trying to accomplish this goal, using the 'MQTT' module, the first hurdle is knowing when all the messages have been published so that we can go back to sleep. Even for QOS 0, it's not easy knowing exactly when the code can issue the setDeepSleep(1) command.

    Using QOS 1, I thought I could register a 'puback' handler, and sleep on that, but it turns out the current 'MQTT' module doesn't do anything with a PUBACK.

    I don't know if the forum is the right place to suggest this, but what about emitting a puback if that has been received from the broker? Something like...

    else if (type === TYPE.PUBACK) {
        mqo.emit('puback', data.charCodeAt(2) << 8 | data.charCodeAt(3));
    }
    

    Then in the user code, a handler would be something like...

    mqtt.on('puback', (pid) => {
         mqtt.disconnect();
         wifi.disconnect();
         setDeepSleep(1);
    });
    

    (not doing anything with pid right now, since the user code has no method to get the outgoing pid anyway)

  • in Pico / Wifi / Original Espruino
    Avatar for ScottyMT

    My results are far from scientific. This exercise has involved a lot of trial-and-error, some shotgun approaches, finger-crossing, and several desperate prayers to Binarius, the Greek god of bits.

    But upon the very valid request to know which commands were causing the issue, I've pared the code down, and removed most of the delays, and got it reliably working. In my rigorous testing (by that I mean 6 times), I found that I only needed to delay starting the WiFi AP and Station a second after the SPI device.

    This makes the relevant code now look like this:

    function onInit() {
    
      SPI1.setup({mosi:A7, miso:A6, sck:A5});
      bme = require("BME280").connectSPI(SPI1, B1);
    
      setTimeout(startApAndStation, 1000);
    
      wifi.on('disconnected', () => {
        clearInterval(timer);
      });
    
      wifi.on('connected', () => {
        require("http").createServer(pageReq).li­sten(8080);
        mqtt = require("MQTT").connect({host: "xxx.xxx.xxx.xxx"});
        dataInterval();
      });
    }
    
    function startApAndStation() {
      wifi.startAP('AgilatechESP', { password: 'xxxxxxxx', authMode: 'wpa2' }, (err) => {
        if (err) throw err;
        else wifi.setAPIP({ip:"10.0.132.1", gw:"10.0.132.1", netmask:"255.255.255.0"}, (err) => {
          if (err) throw err;
          else wifi.connect("HeartJS", {password : "xxxxxxxxxx"}, (err) => {
            if (err) throw err;
          });
        });
      });
    }
    

    Why? I dunno, but without the delay on the 'startApAndStation' I get the Uncaught CWMODE failed: Timeout error nearly every time.

  • in Pico / Wifi / Original Espruino
    Avatar for ScottyMT

    Thanks for the help. It does seem that the wifi.on('connected' event is only triggered once, and only on wifi.connect and not wifi.startAP (based on login inside the event handler).

    Just from a design perspective, it does make sense to do certain things if and only if the wifi actually made a successful connection, so that's why I've stuck with keeping things inside the handler. Also, now with the into delays, everything has been working 100% of the time.

    Here's what I finally settled on (relevant functions only):

    function onInit() {
    
      SPI1.setup({mosi:A7, miso:A6, sck:A5});
      bme = require("BME280").connectSPI(SPI1, B1);
    
      setTimeout(startApAndStation, 2000);
    
      wifi.on('disconnected', () => {
        mqtt.disconnect();
        wifi.stopAP();
        clearInterval(timer);
      });
    
      wifi.on('connected', () => {
        setTimeout(() => { require("http").createServer(pageReq).li­sten(8080); }, 2000);
        setTimeout(() => { mqtt = require("MQTT").connect({host: "xxx.xxx.xxx.xxx"}); }, 4000);
        setTimeout (() => { dataInterval(); }, 8000);
      });
    }
    
    function startApAndStation() {
      wifi.startAP('AgilatechESP', { password: 'xxxxxxxx', authMode: 'wpa2' }, (err) => {
        if (err) throw err;
        else wifi.setAPIP({ip:"10.0.132.1", gw:"10.0.132.1", netmask:"255.255.255.0"}, (err) => {
          if (err) throw err;
          else wifi.connect("HeartJS", {password : "xxxxxxxxxx"}, (err) => {
            if (err) throw err;
          });
        });
      });
    }
    
    function dataInterval() {
      timer = setInterval( () => {
        loadData();
        mqtt.publish("agt/sensor/bme280/temp", temp);
        mqtt.publish("agt/sensor/bme280/pressure­", pressure);
        mqtt.publish("agt/sensor/bme280/humidity­", humidity);
      }, 10000);
    }
    
  • in Pico / Wifi / Original Espruino
    Avatar for ScottyMT

    I appreciate all the help and suggestions.

    The main source of frustration was the unpredictability of it -- sometimes things would init and work, sometimes not. However, after reading through all the referenced forum threads, and what @Robin mentioned about a delay setting up SPI, I thought maybe I'm expecting too much from the little guy on startup.

    I am starting WiFi, connecting to a router, starting a simultaneous AP, starting a SPI device, initiating an MQTT module and connecting it to a broker. There are quite likely several opportunities for conflict in there. Sometimes, however, things did happen to start and finish with such timing that it all works. Usually though, that was not the case.

    What I tried is to wrap every startup/connection/instantiation in a delay setTimeout. Is this overkill? Maybe, but it does seem to work, and only delays the complete initiation by a few seconds. But best of all, it now starts up with success every time, all the time, whether on battery, or hooked to USB.

    Thanks again for the advice.

  • in Pico / Wifi / Original Espruino
    Avatar for ScottyMT

    Sorry, I need to make it a little more clear. It actually doesn't start without errors on the next boot. In fact, most of the time, a power-cycle will not cause things to work. Every now and then however, IT DOES. This happens so unpredictably that I have not been able to nail down any repeatable behavior, such as a minimum amount of down time.

    Now, originally, I did have the wifi.on('connected' event inside the onInit() function. Then in an attempt to simplify things, I moved it out. I just put that event handler back in the onInit() on the assumption that it needed to be registered again after a reboot.

    No such luck... sometimes things work, sometime not after a boot. But the one thing I have not ever seen fail is proper operation after typing save() on the left, even if there have been absolutely no changes to the code.

    If it will help, here is the entirety of the code in question:

    var wifi = require("Wifi");
    var bme;
    var mqtt;
    var temp;
    var pressure;
    var humidity;
    
    function onInit() {
    
      wifi.startAP('AgilatechESP', { password: 'xxxxxxxx', authMode: 'wpa2' }, (err) => {
        if (err) throw err;
        else wifi.connect("HeartJS", {password : "xxxxxxxxxx"}, (err) => {
          if (err) throw err;
        });
      });
      
      SPI1.setup({mosi:A7, miso:A6, sck:A5});
      bme = require("BME280").connectSPI(SPI1, B1);
      
      wifi.on('connected', () => {
        require("http").createServer(pageReq).li­sten(8080);
        mqtt = require("MQTT").connect({
          host: "xxx.xxx.xxx.xxx"
        });
        loadData();
        dataInterval();
      });
    
    }
    
    function pageReq(req, res) {
      const reqdat = url.parse(req.url, true);
      var ret = "";
      if (reqdat.path === '/') {
        ret = `Temperature: ${tempF}, Pressure: ${pressure}, Humidity: ${humidity}`;
        //ret = "Hello World";
      }
    
      res.writeHead(200);
      res.end(ret);
    }
    
    function seaLevelPressure(pressure_mb, elevation, temp) {
        return pressure_mb * Math.pow((1 - ((0.0065 * elevation) / (temp + 0.0065 * elevation + 273.15))), -5.257);
    }
    
    function dataInterval() {
      var timer = setInterval( () => {
        loadData();
      }, 600000);
    }
    
    function loadData() {
      let data = bme.getData();
      data.pressure = seaLevelPressure(data.pressure, 1750, data.temp);
      pressure = Math.round(data.pressure * 100) / 100;
      temp = Math.round(data.temp * 10) / 10;
      tempF = Math.round(((data.temp * 9/5) + 32) * 10) / 10;
      humidity = Math.round(data.humidity * 10) / 10;
    }
    
    
Actions