Avatar for allObjects


Member since Jul 2014 • Last active Jan 2018

Very excited about Espruino! JS is just a great, universal language that - for me - implements - thank's @Gordon on an ARM3/4/... MC and with a Web IDE - all essential programming concepts there are and makes them easy to use, including the hardware access. My relationship with The Flintstones is about the same as Espruino and Arduino..., or JS and C... not that C-like things are not of use or needed anymore, but I can get most of them close enough with compiled JS.

Most recent activity

  • in Interfacing
    Avatar for allObjects

    ... did not share the code I used... let me do it in the state when I hit the wall with the spike caused by Espriuno pin mode change. It is now - 1v95 - fixed. Did not have the time to resume... :( ...yet... :]. Note the rewrite of the rewrite of the MCP23017 module...

    // MCP23017i2WATCHED8.js
    // Work in progress: Adding setWatch/clearWatch
    // to MCP23017.js module pins by allObjects
    // NOTE: MCP23017 w/ I2C used in reset/default mode:
    //       16 bit, mirror int, seq r/w (BANK=0, MIRROR, SEQOP)
    //       Port A = LSByte, Port B MSByte of 16 bit word
    var exports = {};  // module business
    Modules.addCached("MCP23017i2W", function() {
    exports.connect = function(i2,rst,i2a,irq,ioc) {
        return new MCP23017i2W(i2,rst,i2a,irq,ioc);
    // PortExpander (PX) MCP23017 w/ i2c interface and watched irq
    // constructor
    var MCP23017i2W = function
      (i2  // Espruino (ESP) i2c appliance driving PortExpander (PX)
      ,rst // (optional/falsy) ESP pin PX's Reset pin is connected to
      ,i2a // 3 bit (0..7) PX Address
      ,irq // ESP pin PX's Interrupt pin is connected to
      ,ioc // (optional) IOCONfig, enforced to 16bit, mirror, seq r/w
    ){this.i2  = i2;         // ESP i2c appliance
      if (rst!==null) rst.write(0); // activate PX reset if ESP pin 
      if ((this.rst=rst)!==null) rst.write(1); // de-activate  PX rst
      this.i2a = (i2a)?32+i2a:32; // PX i2c addr (7 bit)
      this.irq = irq;        // ESP interrupt pin (w)
      if ((irq!==undefined)&&(irq!==null)) irq.mode("input_pullup");
      this.ioc = ((ioc)?ioc&22:0)|64; // enforced 16b, mirr, seq r/w
      this.wR(10,this.ioc);  // IOCONfig reg BANK=0, MIRROR, SEQOP
      this.io  = 65535;      // IODIR BA regs int val, 0/1=out/input
      this.pu  = 0;          // GPPU  BA regs int val, 0/1=no/pull-up
      this.ol  = 0;          // OLAT  BA regs int val, 0b16 (0.16.0)
      this.pMs = ["output","input","input_pullup"];
      this.wCB = this._wCb.bind(this); // watch cb bound to this (w)
      this.wId = null; // watch ID (w)
      this.ie  = 0;    // GPINTEN BA regs (IRQ/INTerrupt ENAbled) (w)
      this.ps  = [];   // pins B7..B0,A7..A0 for iteration (w)
      var i=65536,j=16,p; while(i>>=1){ // create Pins (P) B7..0,A7..0 
        this[((--j>7)?"B":"A")+j%8] = (p = new PXP // new Pin|Port:
          (this  // ref to MCP23017 - owning PortExpander (PX)
          ,i     // id of Pin|Port  - (single bit in 16 bit value)
          )); this.ps.splice(0,0,p); } // ...; (w)
    }, x = MCP23017i2W.prototype;
    x.read=function // read all P's @ once (a word, BA of an integer)
    (){return this.rR(18); }; // all 16 PX Ps @ once (8..15,0..7)
    x.write=function // write all P's @ once (a word, BA of an int)
        (w // a 16 bit word (2 LSBytes BA of the integer)
    ){return this.wR(18,w); };
    x.rBA=function // read all P's @ once as ByteArray/Uint8Array[2]
    (){return this.rRBA(18); };
    x.rR=function // read from r AB a word (2 bytes) as (BA) integer
        (r // reg in 16 bit / BANK=0 mode and evens of 0x00..0x15
    ){this.i2.writeTo(this.i2a,r);        // select reg to read from
      var w=this.i2.readFrom(this.i2a,2); // read Uint8Array[2] 
      return w[0]|w[1]<<8; };             // return int (BA)
    x.wR=function // write to AB r a word (2 LSBytes BA of an int)
        (r // reg in 16 bit / BANK=0 mode and evens of 0x00..0x15
        ,w // word as integer (2 LSBytes of an integer)
    ){this.i2.writeTo(this.i2a,r,[w&255,w>>8­]); // w A,B of (BA) int
      return this; };
    x.rRBA=function // read from r AB 2 bytes (a word) as Byte Array
        (r // reg in 16 bit / BANK=0 mode and evens of 0x00..0x15
    ){this.i2.writeTo(this.i2a,r);            // select reg to read
      return this.i2.readFrom(this.i2a,2); }; // ret read Uint8Arr[2]
    x.wRBOA=function // w n bytes (byte|array in 8|16b, no|seq r/w 
        // always starts/ends w/ cfg @ connect (16b,BANK=0, seq r/w)
        (ioc // can only contol BANK (128), SEQOP (32), DISSLW (16)
        ,r   // register w/ BANK=x as given by ioc
        ,boa // byte or array (write 1 or n bytes)
    ){this.i2.writeTo(10,(ioc&176)|(this.ioc­&70)); // MIR,ODR,INTPOL
      this.i2.writeTo(this.i2a,r,boa); // write byte or array of bts
      this.i2.writeTo(this.i2a,(ioc&128)?5:10,­this.ioc); // ioc back
      return this;};
    x.pM=function // set Pin mode by P's id: IODIR i/o, GPPU pull-up
        (i // id of Pin (single bit in 16 bit value)
        ,m // mode of Pin: ""output"|input"|"input_pullup"
    ){var x=this.pMs.indexOf(m);
      if (x<0) { throw "Pin mode '"+m+"' invalid ("+this.pMs+")"; }
      this.wR( 0,(x&1) ? this.io&=~i : this.io|= i );
      this.wR(12,(x&2) ? this.pu|= i : this.pu&=~i );
      return this; };
    x.gPM=function // get pin mode by P id
        (i // id of Pin (single bit in 16 bit value)
    ){var v=this.rR(0)&i; // IODIR i/o bit, if 0 GPPUP pull-up bit
      return ((!v)?this.pMs[0]:this.pMs[1+(this.rR(12­)&i)?1:0]); };
    x.rP=function // read P's pin input value by P id
        (i // id of Port (single bit in 16 bit value)
    ){return (this.rR(18)&i) ? true : false; };
    x.wP=function // write P's pin output (latched) value by P id
        (i // id / value of Port (single bit in 16 bit value)
        ,v // truey/falsy output value
    ){this.wR(18,this.ol=(this.ol&~i)|(v)?i:­0); return this; };
    x.wX=function // write to all 16 PX Ps @ once (0..15, A+B 0..7)
        (v // value as int (16 bit value)
    ){this.wR(18,this.ol=v); return this; };
    x.gP = function // get pin by ID i (w)
        (i // id of Port (single bit in 16 bit value)
        ,p // do not supply (implicit var p)
    ){return this.ps.reduce((pr,px)=>{
          return (pr===undefined)?(px.i===i)?px:pr:pr; },p); };
    // setWatch / fire watch / clearWatch additions to PX 
    x.sPW=function // set pin watch (w)
        (cb // watch callback
        ,p  // pin / port
        ,os // watch options
    ){var i=p.i;
      if ((this.ie&i)||(!this.io&i)) throw "Portexpander pin "+i
        +" already watched or not in input or input_pullup mode";
    l(".sPW(): enable interrupt on pin "+i);
      (os=(p.wOs=os||{}))._cb=cb; // set/create os obj w/ cb,...
      os._r=!!os.repeat;          // ...watch opt repeat,...
      os._e=(os.edge=="rising" )?true       // ...edge rising
           :(os.edge=="falling")?false      // ...edge falling
           :                     undefined; // ...edge both
      os._t=-1;                             // ...irg last time
      if (this.ie) { // already irq enabled pins: clear irq watch
        this.wId = clearWatch(this.wId);
    l(".sPW(): ad pin "+i+" to other watched ones");
      } else { // this is first (and only) irq enabled Pin p
    l(".sPW(): add pin "+i+" as 1st watched");
      this.wR(4,this.ie|=i); // enable irqs incl Pin p w/ ID i
      this.rR(16); // clear pend irgs INTCAP, setWatch on .irq
      this.wId=setWatch(this.wCB,this.irq,{rep­eat:true, edge:"falling",debounce:0});
      return p; };
    x._wCb=function(s,t,lt){ // internal watch irq callback (w)
      var irqLast=this.irq.read()
        , iFs=this.rR(14) // read irq flags INTF AB regs
        , iSs=this.rR(16) // read irq states INTCAP, clears irq
    //    , i=this.wR(4,0), os;  // dis irq GPINTEN (v decl i,os)
        , os;
    l("._wCb: iFs="+iFs+" iSs="+iSs+" irq:"+irqLast+"/"+this.irq.read());
      // this.wId=null; // dump ID of irq watch since it happend
      this.ps.forEach(function(p){ // iterate all pins for iFs
    l("._wCB: " + p.i);
        if (iFs&p.i) { i=p.i;os=p.wOs;lt=0; // p i caused irq
          if (os._e===undefined) { // watch opt edge = both
    l("._wCB: irq on pin "+i+" edge:both");
          } else if (os._e) {      // watch opt edge = rising
    l("._wCB: irq on pin "+i+" edge:rising");
            if (iSs&i) {           // ...and state turned true
              lt=os.t;setTimeout(os._cb,1,true,os.t=t,­lt); }
          } else {                 // watch opt edge = falling
    l("._wCB: irq on pin "+i+" edge:falling");
            if (i&~iSs) {          // ...and state turned false
              lt=os.t;setTimeout(os._cb,1,false,os.t=t­,lt); } }
          if (lt && ! os._r) {     // watch opt ! rep
    l("._wCB: remove irq from pin "+i);
            this.ie&=~i; delete p.wOs; } // remove watch from p
      } },this);
      if (this.ie) { // still irq enabled pin(s)
    l("._wCb: enable interrupts for " + this.ie);
        this.wR(4,this.ie); // re-enable irqs on 'these' pins
        this.rR(16); // clear pend irgs INTCAP, setWatch on .irq
        this.wId=setWatch(this.wCB,this.irq,{rep­eat:true, edge:"falling",debounce:0});
      } };
    x.cPW=function // claer watch (w)
        (p // watch handle = setWatch result (= PX P pin)
    ){ if (p) { var i=p.i; if (this.ie&i) {
    l(".cPW: disable interrupt for pin "+i);
      this.wR(4,this.ie&=~i); delete p.wOs; // rm watch from i
      if ( ! this.ie) {
    l(".cPW: remove watch since no pin is watched anymore");
        clearWatch(this.wId);this.wId=null; } } } };
    // Port / Pin (P/PXP) of PortExpander (PX) - Port 'class'
    var PXP = function
      (x // owning PortEexpander (PX)
      ,i // id of Pin | Port (P) (single bit in 16 bit value)
    ){this.x=x; // owning expander
    }, p = PXP.prototype;
    p.set  =function() { return this.x.wP(this.i,1); };
    p.reset=function() { return this.x.wP(this.i,0); };
    p.write=function(v){ return this.x.wP(this.i,v); };
    p.read =function() { return (this.x.rR(18)&this.i)?true:false;};
    p.mode =function(m){ return this.x.pM(this.i,m); };
    p.getMode=function(){return this.x.gPM(this.i);  };
    // add watchable to PXP (PorteXpander Pin)
    p.setWatch = function
      (cb // watch callback
      ,os // watch options
      return this.x.sPW(cb,this,os); };
    p.clearWatch = function
      (wId // watch Id (PX P pin object / this)
    ){this.x.cPW(wId); };
    // ***** modified setWatch / clearWatch
    (function() {
     var _setWatch = setWatch;
     global.setWatch = function(cb,pin,opts) {
       return (pin instanceof Pin)
        ? _setWatch(cb,pin,opts)
        : pin.setWatch(cb,opts);
    (function() {
     var _clearWatch = clearWatch;
     global.clearWatch = function(id) {
       return (!isNaN(id))
        ? _clearWatch(id)
        : id.clearWatch(id);
    // ***** application / test begin *****
    // --- configuration (Espruno(-WiFi))
    var i2   = I2C1 // i2c appliance portexpander is connected to
      , i2c  =   B8 // i2c clock
      , i2d  =   B9 // i2c data
      , i2s  = 100000 // i2c speed (bitrate per second 100/400/1700k)
      , i2a  =    0 // portexpander 3-bit address (0..7)
      , irq  =   B0 // interrupt - required w/ setWatch
      , rst  =   A0 // reset (active low, 'clean' alternatives work too)
      , x    = null // portexpander instance
      , xMN  = "MCP23017i2W"  // portexpander module name
      , xM   = require(xMN)   // portexpander module
    // --- init / app ---
    function onInit() {
      i2.setup({scl:i2c, sda:i2d, bitrate:i2s});
      x = xM.connect(i2,rst,i2a,irq); // opt ioc ommitted / default
      rr(); // run readWatch BTN / C13 (Espruino-Wifi)
      rg(); // run greenWatch x.A7 falling
    var redWatch = null;
    function rr() { // run red LED1 toggle when BTN released
      // setWatch w/ built-in port BTN (...C13 - Espruino-Wifi)
      var redOn = false; // red LED on
      redWatch = setWatch(function(s,t,lt) {
          digitalWrite(LED1,redOn = !redOn);
        }, BTN, { repeat:true, edge:"rising", debounce:10 }); }
    function hr() { clearWatch(redWatch); } // halt red LED1 toggling
    var greenWatch = null;
    function rg() { // run green LED2 toggle when x.A7 disconn from GND
      // setWatch w/ Portexpander Port x.A7 (w/ pullup)
      var greenOn = false; // green LED on
      greenWatch = setWatch(function(s,t,lt) {
          digitalWrite(LED2,greenOn = !greenOn);
        }, x.A7, { repeat:true, edge:"falling" }); }
    function hg() { clearWatch(greenWatch); } // halt green LED2 toggling
    var redWatch2 = null;
    function rr2() { // run red LED1 toggle when x.A6 disconn from GND
      // setWatch w/ Portexpander Port x.A6 (w/ pullup)
      var redOn = false; // red LED on
      redWatch2 = setWatch(function(s,t,lt) {
          digitalWrite(LED1,redOn = !redOn);
        }, x.A6, { repeat:true, edge:"falling" }); }
    function hr2() { clearWatch(redWatch2); } // halt red LED1 toggling
    // *** development / test helper / conveniences
    function l() { console.log.apply(console,arguments); }
    function lWT() {
      var args=[getTime()]; var i=-1; j=arguments.length;
      while(++i<j) args.push(arguments[i]);                      
      console.log.apply(console,args); }
    var tId = false, iVLast = -1, iV;
    function r() { // run - continuously reading all ports in 16 bit mode
      tId = setInterval(function(){
          iV = x.read();
          if (iV != iVLast) console.log(iVLast = iV);
    function h() { if (tId) tId = clearInterval(tId); } // halt - cont reading
    var mId = false, iXA7=null,sIRQ=null,sFs=-1,v,s;
    B13.mode("input"); // probe on x.A7
    function rm() { // run - monitoring
      mId = setInterval(function(){ s="";
          v=B13.read(); if (v!=iXA7) s+=" iXA7:"+(iXA7=v);
          v=irq.read(); if (v!=sIRQ) s+=" sIRQ:"+(sIRQ=v);
          v=x.rR(14);   if (v!=sFs)  s+=" sFs:" +(sFs =v);
          if (s.length>0) console.log(s);
        }, 100);
    function hm() { if (mId) mId = clearInterval(mId); }  // halt - monitoring
    B14.mode="output"; B14.set(); // driving x.A7 (on input_pullup)
    function tg() { // trigger green_watch x.A7
      digitalPulse(B14,0,0.5); }
    function rtg(n) { // repeated tg() - green trigger
      n = ((n) ? n : 10) + 1; l(n);
      var rtgId = setTimeout(function(){
        tg(); if (!--n) clearTimeout(rtgId); },1000); }

    Still to be retested w/ 1v95

  • in Pico / Wifi / Original Espruino
    Avatar for allObjects

    I did quite extensive work on the MCP23017... the only challenge I faced was a spike when I added interrupt extension caused by Espruino pin mode change. This is now though fixed (1v95). I started with the available module to learn the MCP23017. Wading through the code made me though apply some changes... last but not least because I wanted add the interrupt functionality.

    Take a look at Exploring adding setWatch/clearWatch (inerrupt handling) to MCP23017 (MCP2308) Portexpander Ports. It includes a rewrite of the module. You can ignore the interrupt part...

  • in Pico / Wifi / Original Espruino
    Avatar for allObjects

    Espruino and Arduino run under quite different premises... if you already have all in one environment - the Arduino - environment, you may go with that.

    I'd though consider taking a closer look what you *really+ have to port over, because Espruino way more powerful and not only from computing power assuming you talk Arduino Uno or a like - which is 8 bit vs 32 Espruino - but also from high-level programming and programming style: Espruino gives you the event-drivenness, where as with Arduino you are stuck in the execution loop. Agreed, the loop is faster as long as you do not over-load it... but when you get there, it is a hard limit.

    Espruino has tons of modules that most likely cover what have to cover, such as the Arduino libs do. When it though comes to do the higher level programming, you will be glad have event-driven-ness available. Furthermore - while connected - you have a much more powerful debugging compared to Arduino, because JavaScript is available to you to do anything to your code in the robot: inspect, give commands, do step-by-step debugging... etc. When you have some experience in JavaScript programming using the event model, you may be surprised how quickly you will advance in your project.

  • in News
    Avatar for allObjects

    ...like the sequence in which the working platforms are mentioned:

    1. Espruino
    2. Nordic

    Congrats, @Gordon!

  • in ESP8266
    Avatar for allObjects

    @MaBe, thanks for the in-depth information... not a stack overflow but something of a similar pattern: In Espruino automatic memory management / garbage collection support, a variable can obviously only 'be held hostage' (be locked 'up') by max. 15 running contexts... (or what ever the context's naming by @Gordon is)... which at the same time limits also the calling / call stack depth. This may be variable per platform depending on available memory?

  • in ESP8266
    Avatar for allObjects

    looks like stack overflow... I though assume @Gordon would make that fail more gracefully. So I go for the cpu hog-ing for longer then allowed on the ESP8266 platform. There is a limit - in ms - that another process than the ESP8266 core (tending to WiFi) can hog the CPU...

    Normally, a piece of JavaScript is triggered by a Hardware, for example, pin state change (setWatch), timer (incl. setTimeout(), setInterval()), and communication (data sent, received data available) event, and ends within that time frame serving these events. After serving by the JS interpreter, control returns to ESP8266 core first, and if it goes idle, the next piece of JS is picked up if there are still events for it in the event queue.

    ESP8266 is just not the platform to run two sophisticated systems on a single processor...Wifi is - time wise -just too demanding and leaves only little computing resources to other activities. Therefore, Espruino-Wifi or a plain Original or Pico combined with a ESP8266 (ESP-01) are the solution: ESP8266 has enough juice to tend to Wifi and have communication with the host-mc over serial. The host-mc works the application code and communicates to ESP8266 over the serial.

  • in ESP8266
    Avatar for allObjects

    ...it is always the same issue: Active executing code in level 0 messes with the upload completion detection from editor as well as console, and in your case it is even worse: your console.log() goes directly after that upload process. Reason: the (default, regular) console connection is used to upload... and because arriving code is executed as soon as it is a complete JavaScript statement or expression. The upload uses a timeout to check for a 'completion prompt' from Espruino. It does not get it but rather gets stuff from your application.

    Put your function invocation code into a function (which you call within) onInit() function. This has other advantages: you will be able to save() your code in flash (enter save() in the console after upload), and the code will run on power on.

    PS: without the convenience of last code line (setTimeout(onInit,...), you have to start your code every time after upload by entering onInit(); in the console... Helpful read up on these subjects is the conversation about simple explanation how to save code that espruino run on start?.

    var temp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
    var send = function(times, i) {
      console.log('foobar', i);
      if (i < times.length) {
        send(times, i + 1);
    function onInit() {
      send(temp, 1);
  • in Puck.js
    Avatar for allObjects

    @Jean-Philippe_Rey and @ptone, so I'm mistaken... I kind of knew that it could be different... should have tried it on Puck as well... :(((> --- good to know, that may change some application code to become used on either devices.

  • in Puck.js
    Avatar for allObjects

    Espruino has a nice feature on pins: it has a configuration - called auto - where it configures itself as input or output depending on the read or write operation...

    This nice feature plays now dirty tricks on you.... ;)

    When you define the pin as output, you will be able to read from the pin and you get the output register, because the driver of pin stays on (not three-state) and drives the input driver. ...and you get what you are looking for: status of the pin / LED... to be more precise: the output register... (if you short cut your pin, I bet you get the either rail... close to 0 or 3.3, I expect... I did not tell you to try... that's why I just guess. (Tried to find details about the inner circuitry to confirm, but could not find it).

    Give it a try...

    EDIT: just validated (on Espruino Wifi, expect Puck not to be different, but I may be mistaken... :( ...) ...in the console (on Espruino-Wifi, LED / LED1 - red LED - is on B2, therefore, B2 and LED and LED1 are interchangeable...):

    >getPinMode(LED1); // asking pin mode on LED / LED1 / B2 pin
    >pinMode(B2,"output"); // setting pin mode on LED / LED1 /B2 pin to plain ouput
    >getPinMode(LED1); //  asking again, and it shows set mode
    >digitalRead(LED1); // reading, 0 expected, because of wiring and LED is off
    >LED1.set(); // turning LED / LED1 / B2 pin on
    >digitalRead(LED1);  // reading, 1 expected, because of wiring and LED is on

    As noticed, pin mode (of B2, LED, LED1 on Espruino) is by default "analog" mode (Espuio-Wifi after reset / power-on / connect with 'factory' setup - no saved code of mine running on start up).

    Analog mode may have another twist: since it can be PWM and not really analog, reading may be fickle... If you for straight, plain "output", it just works...

    Without setting the pin mode, but turning the LED on, what happens when you read? ...I expect the LED turns of... (so is behavior of Espruino-Wifi):

    >pinMode(B2,"auto"); // set outo
    >getPinMode(B2); // returns what it was from before... 
    >B2.set(); // turns LED on
    >digitalRead(B2); // switches to input, but on 1st read I get 1 because it reads so fast and 'sensitively' that the voltage has not yet collapsed... very interesting - BUT LED EVENTUALLY TURNS OFF
    >digitalRead(B2); // 2nd time I get 0... as expected... because all the electrons have left the scene / left the building...

    (...my little Christmas present to you - Merry Christmas!)