• This detector is able to notice sudden light changes and calls a callback function with true and false for increase and decrease of a value beyond a slope (dv / dt). The detector accepts a function for reading the value to adjust to any input: light for Puck, analog values, etc.

    Because I did not have the Puck at hand yet, I cross developed in an HTML5 document in the browser. The only failure was that the activity indicator flashing of the red LED impacted the light sensing... all depending the light conditions. Switching to the green LED allowed me to make a clip.

    HTML file is attached and can be run from link below. Clicking Create and Start button and then various Light Intensity buttons shows wether an on - increase - or off was detected. For subsequent increases only the first is triggering the callback.

    The detecter is part of another project which is also 'complete' but subject of a different conversation some time later: A Morse Code detector built with a Puck that interprets the dot-dash sequences and prints the message in clear text...

    From below HTML5/JavaScript code you make a straight copy-paste of the portion between ---BEG--- and ---END--- markers into the Espruino Puck Web IDE and uploaded it to the Puck.

    <html><head><title>LightDetector - DvDtDetector</title>
    <script> // Espruino Emulation setup
    var LED1 = // red LED1 emulation
    { id: "LED1"
    , setValue: function(value) {
        this.value = value;
        var node = document.getElementById(this.id);
        if (node) { node.style.backgroundColor = ((this.value) ? "red" : ""); } }
    , getValue: function() { return this.value; }
    };
    var LED2 = // green LED2 emulation
    { id: "LED2"
    , setValue: function(value) {
        this.value = value;
        var node = document.getElementById(this.id);
        if (node) { node.style.backgroundColor = ((this.value) ? "lightgreen" : ""); } }
    , getValue: function() { return this.value; }
    };
    function getTime() { return (new Date().getTime()) / 1000; }
    function digitalPulse(pin, value, time) { // digitalPulse() emulation
      pin.setValue(value);
      setTimeout(function() { pin.setValue(!value) }, time); } 
    var Puck = // Puck emulation
    { _light: 0
    , light: function() { return this._light; }
    };
    var logger = // console 'helper'
    { log: function(s) {
        var logNode = document.getElementById("log");
        logNode.value = s + "\n" + logNode.value;
      }
    };
    </script>
    <script> // Espruino / Puck Code
    
    // Espruino / Puck Code
    /* 
    DvDtDetector constructor function taking:
    - read: function(detector, time) to return a number
          taking (optionally):
            - detector: to have access to last time, value
            - time: current time getTime() (system time, secs)
          for example:
            function(){ return Puck.light(); }
    - rIntrv: read interval in milliseconds
    - slope: slope of increase / -decrease over 1 second
          (min change 'rate') to detected a 'switch'
          to overcome gradual changes over time,
          such as daylight / ambient light changes
    - cb: callback function(detected, value, value0)
          taking:
            - detected:
               - true for increase > slope
               - false for decrease > -slope
            - value: current value
            - time: current time (at value taken)
            - value0: previous value
            - time0: previous time
    - aIntrv  // active indicator interval in reads, optional, (pos int)     
    - active  // active indicator function(), optional, for example,
              //   function(b) { digitalPulse(LED2,1,3); } 
     */
     
    // --- BEG ------- 
     
    var DvDtDetector = function(read, rIntrv, slope, cb, aIntrv, active) {
      this.read = read;
      this.rIntrv = rIntrv;
      this.slope = slope;
      this.cb = cb;
      this.aIntrv = aIntrv;
      this.active = active;
      this._slp = this.slope / this.rIntrv * 1000;
      this._sSt = null;
      this._iId = null;
      this._val = null;
      this._tim = null;
      this._aTC = 0;
    };
    DvDtDetector.prototype.start = function() {
      if (!this._iId) {
        this._val = this.read();
        this._tim = getTime();
        this._iId = setInterval(
          this._dtct.bind(this), this.rIntrv); } };
    DvDtDetector.prototype.stop = function() {
      if (this._iId) { clearInterval(this._iId); }
      this._iId = null; };
    DvDtDetector.prototype._dtct = function() {
      var v0 = this._val, t0 = this._tim, t = getTime(),
        s = ((this._val = this.read(this, t)) - v0)
          / ((this._tim = t) - t0);
      if (Math.abs(s) > this._slp) {
        if (s > 0) { s = null; if (this._sSt !== true) { s = true; }
        } else { s = null; if (this._sSt !== false) { s = false; } }
        if (s !== null) {
          this.cb(this._sSt=s,this._val,this._tim,v0,t0); } }
      if (this.aIntrv && (++this._aTC >= this.aIntrv)) {
        this.active(); this._aTC = 0; } };
    exports = DvDtDetector;
    
    var detector = null;
    function lightDetector(logger) {
      var d = new DvDtDetector(
          function(){ return Puck.light(); }
        , 200
        , 0.1
        , function(detected, value, time){
              logger.log( ((detected) ? "on " : "off") + " - v: " + value
                + " - t: " + time);
            }
        , 15
        , function(){ digitalPulse(LED2, 1, 50); }
        );
        return d;
    }
    
    var detector = null;
    // short cuts to command from console:
    function l() { if (!detector) { detector = lightDetector(console); } }
    function r() { detector.start(); }
    function s() { detector.stop(); }
    
    // --- END -------
    
    </script>
    </head>
    <body>
    <h3>LightDetector - DvDtDetector</h3>
    <button onclick="if (!detector) { detector = lightDetector(logger); }">Create</button><br>
    <button onclick="detector.start();">Start</button>
    <button onclick="detector.stop();">Stop</button>
    <br>
    Light values:<br>
    <button onclick="Puck._light=0.04;">0.04</button>
    <button onclick="Puck._light=0.05;">0.05</button>
    <button onclick="Puck._light=0.06;">0.06</button>
    <button onclick="Puck._light=0.10;">0.10</button>
    <button onclick="Puck._light=0.20;">0.20</button>
    <button onclick="Puck._light=0.60;">0.60</button>
    <button onclick="Puck._light=0.99;">0.99</button>
    <br>
    <table><tr><td valign="top" align="left">
      <table>
        <tr><td><div id="LED1" style="width:3em; height:3em; 
            border-radius:50%; border: 0.3em red solid;"><div
            style="margin: 1em 0 0 0.25em;">LED1</div></div>
        </td></tr>
        <tr><td><div id="LED2" style="width:3em; height:3em; 
            border-radius:50%; border: 0.3em lightgreen solid;"><div
            style="margin: 1em 0 0 0.25em;">LED2</div></div>
        </td></tr>
      </table>
    </td><td>
     <textarea id="log" rows="20" cols="40" readonly></textarea>
     </td></tr></table>
    <script>
    </script>
    </body>
    </html>
    

    After upload, the light detector is created by entering l() in the console, and started with r() (for run). Entering s() stops the detector, r() gets it going again.

    Shining light on the Puck or obstructing ling from reaching it creates different values. For the right slope the the callback reports an on or off in the console with the read value and time.


    4 Attachments

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

DvDtDetector for detecting sudden value changes for switching/triggering (on Puck: Light Intensity Changes)

Posted by Avatar for allObjects @allObjects

Actions