• Advertising Sensor Values Over BLE

    parasquid08/23/2020 at 07:32 0 comments

    One of the things I really like about the Espruino, as opposed to doing my projects in Arduino or in the mcu's original SDK, is the ease of prototyping. Making adjustments is very easy without having to recompile and upload, or temporarily adding pots or buttons to dynamically adjust settings - you just do them all in your console.

    This is especially true for the nrf52-based Espruino boards. Having a wireless console available means (in this case) I can place the sensor/mcu project somewhere away from the computer while still having access. That means I can test whether the sensor actually detects particulate matter by moving it around the house and then looking at the console logs that have been generated.

    Another reason is that Espruino have very well-built and comprehensive Bluetooth Low Energy libraries.

    I've worked on a few BLE projects in Espruino before, and this case was no exception: it was really easy to add in BLE support. For example, below is the current code that's uploaded in the MDBT42Q breakout with Espruino and you'll notice that the only difference (aside from some error bypass code) is the bottom section where I set the advertising interval.

    var s = new Serial();
    s.setup(9600,{rx: D15, tx: D14});
    
    let buffer = '';
    const header = String.fromCharCode(0x42) + String.fromCharCode(0x4d);
    let advertisingData = [];
    
    s.on('data', function (data) {
      buffer = buffer + data;
      if(buffer.length < 32) {
        // get at least 32 bytes
      } else  {
        // find header and discard any previous bytes in buffer
        const index = buffer.indexOf(header);
        if(index != -1) { // found the header
          buffer = buffer.substr(index); // discard previous bytes until header
          if(buffer.length >= 32) {
            buffer = buffer.substr(0, 32);  // get a complete packet
            const arrayBuffer = E.toArrayBuffer(buffer);
            buffer = buffer.substr(32); // set buffer to leftover bytes
    
            const dataView = new DataView(arrayBuffer);
            const data = {
              header: dataView.getUint16(0),
              length: dataView.getUint16(2),
              pm25: dataView.getUint16(6),
              pm10: dataView.getUint16(8),
              checksum: dataView.getUint16(30),
            };
    
            const calculatedChecksum = calculateChecksum(new Uint8Array(arrayBuffer));
            console.log(data);
            console.log(calculatedChecksum);
    
            // early return on bad data
            if (data.pm25 < 0 || data.pm25 > 1000) return;
            if (data.pm10 < 0) data.pm10 = 0;
            if (data.pm10 > 255) data.pm10 = 255;
    
            if (data.length != 28) return;
            if (calculatedChecksum != data.checksum) return;
    
            advertisingData = [data.pm25, data.pm10];
            digitalPulse(LED, true, 50);
          }
        } else { // header not found
        }
      }
    });
    
    const calculateChecksum = (arr) => {
      return arr.reduce((acc, cur) => (acc + cur), 0) - arr[30] - arr[31];
    };
    
    setInterval(function() {
      if (advertisingData.length != 0) {
        NRF.setAdvertising({
          0x2A3D: advertisingData
        }, {name: "HPMS"});
      }
    }, 1000);
    
    E.onInit(() => {
      NRF.setTxPower(4);
    });

    In fact, most of the time I spent trying to advertise was not even in Espruino, but in the Bluetooth SIG website https://www.bluetooth.com/ trying to find a suitable service or characteristic for the sensor.

    Unfortunately https://www.bluetooth.com/specifications/assigned-numbers/environmental-sensing-service-characteristics/ environmental sensing specs doesn't have anything related to particulate matter (there's pollen, but that's not really the same) and many of the other characteristics are geared more towards fitness tracking, like heart rate or blood pressure. In fact, there isn't even a characteristic for lights nor buttons!

    So I ended up with probably my favorite characteristic to use if I just need to expose some custom data: 0x2A3D org.bluetooth.characteristic.string :P

    Chdcking with the nrfconnect app confirms that I'm getting advertised pm values as expected.

    What can we do now that we have advertised data at regular intervals? We track them of course.

    The creators of Espruino also has a project called EspruinoHub https://github.com/espruino/EspruinoHub that I've been using for some home automation projects that...

    Read more »

  • From Breadboard to Protoboard

    parasquid08/14/2020 at 19:21 0 comments

    Having confirmed that all components are working (it was good that I breadboarded first; it turned out a previous cable I was using didn't have all the wires in continuity so I had to replace it) it's time to cook something up with a protoboard.

    Magnet wire has recently been my go-to for point to point wiring, because it looks neat and is relatively easy to work with. This magnet wire has the insulation that burns off with a drop of solder.

    And here are our actors. The star of the show is the MDBt42Q breakout board running Espruino. I opted to use some make header pins as some sort of nest for the sensor to keep in in place, while giving a bit of room underneath for various connectors.

    Fully assembled, all components fit almost within the protoboard itself. I opted to mount the breakout sideways so as not to obstruct the sensor fan's airflow.

    And here's a quick test of the current consumption. According to the USB tester, the whole thing consumes about 80mA at 5v (the sensor requires 5v and has a Vout for 3v3 which I'm using to power the MDBT42Q) when running at the default once-a-second-reading mode.

    I also remembered why I initially shelved this project from before: the serial output isn't very clean since the 32-byte packet length isn't sent all at once, and can sometimes even contain stray bytes in the middle which forces you to do buffering of the stream. Fortunately Javascript is a high enough level language to prototype with, so I think I finally got a stream parser that I'm happy with:

    var s = new Serial();
    s.setup(9600,{rx: D15, tx: D14});
    
    let buffer = '';
    const header = String.fromCharCode(0x42) + String.fromCharCode(0x4d);
    
    s.on('data', function (data) {
      buffer = buffer + data;
      if(buffer.length < 32) {
        // get at least 32 bytes
      } else  {
        // find header and discard any previous bytes in buffer
        const index = buffer.indexOf(header);
        if(index != -1) { // found the header
          buffer = buffer.substr(index); // discard previous bytes until header
          if(buffer.length >= 32) {
            buffer = buffer.substr(0, 32);  // get a complete packet
            const arrayBuffer = E.toArrayBuffer(buffer);
            const dataView = new DataView(arrayBuffer);
            console.log({
              header: dataView.getInt16(0),
              length: dataView.getUint8(3), // only get LSB
              pm25: dataView.getUint8(7), // only get LSB
              pm10: dataView.getUint8(9), // only get LSB
              checksum: dataView.getUint16(30),
            });
            digitalPulse(LED, true, 100);
            buffer = buffer.substr(32); // set buffer to leftover bytes
          }
        } else { // header not found
        }
      }
    });
    
    

    I'm running a battery test right now to see how long it would last on a continuous draw (worst case scenario). Depending on the results, I might add instructions to get the sensor to sleep and only do a reading for five seconds a minute, and/or add some sort of energy gathering device like a solar panel or an induction coil to help charge the battery.

  • Breadboarding and Proof of Concept

    parasquid08/13/2020 at 13:11 0 comments

    Past experience have made me realize I really should be strict in breadboarding components first before immediately jumping into soldering them onto some protoboards.

    So here it is: the Honeywell PMS and an Espruino MDBT42Q breakout, together with a lipo battery and a boost converter.

    The test code was easy and simple as well (copied almost verbatim from the Espruino UART docs):

    var s = new Serial();
    s.setup(9600,{rx: D14, tx: D15});
    s.on('data', function (data) {
      print("<Serial> "+data);
    });

     Data sheet of the PM sensor is here: https://sensing.honeywell.com/honeywell-sensing-particulate-hpm-series-datasheet-32322550.pdf