• Playing w/ grandkids allows me to go back and be a kid again... now with things I could only have dreamt then... so it is Back to the future.

    Introducing one of my grandsons to STOKYS / Swiss-version of Meccano / Erector sets of metal as a next step after the wooden versions, 'we' built a basic train engine to run on garden train tracks (45mm / 1 3/4 - G - 1:24 since it models narrow gauge trains... LGB /accucrafthttp://www.accucraft.com/ )/ far-East knock-offs.

    Some scavenged Espruino code from a previous project of a classic / retro sheet metal robot - not yet published - made it moveable... as you can see in the attached shot and clips... The step indicator lights of the driver can be used as the fire box glow of a real steam engine... especially when it is (backing) slow...

    The motorization is just a simple-got-going and not adequate yet in regard of speed... also the efficiency is questionable: first a reduction gear as built into the stepper and after that a boost gear... but never mind, it made the show - ...show-up. In a follow-up post I present more details...

    3 Attachments

  • The code is very simple... what is not in place yet for the engine are the lights in the front and back... (which controlled the piercing eyes when it was part of the human sized robot). The UI is Web retro as well: plain html grey in grey... (suggestion of enhanced look in form of working code are very welcome). Since the robot lived before puck time, wifi was chosen for communication... now, it would probably be BLE... and a nice phone app...

    The electronics is protected form short cuts by the metallic context in a recycled trow-away blister-pack that had packed something else in its previous life. So I could it even call ecologically responsible...

    The power options are:

    • USB 5V Powerbank as used for phones and other USB powered devices, connected to Espruino-Wifi's USB connector.
    • 3 AA Alkaline Cells in a battery holder w/ 3.6...4.5V(as pictured), connected to Espruino-Wifi's GND and VBAT.

    The ULN2003 allows its own - higher 'volted' - supply, which may become an option - either for 6..12V battery or similar supply thru tracks / DC 2-rail. (Currently, the wheels are not isolated against one another (side-ways, so it produces for now a short-cut between the 2-rail DC technique as used. Therefore either the bought stuff runs or the DIY runs...).

    Here the code:

    // robotTrainEngine.js
    // - espruino-wifi EW.1=GND (USB left), 2=VUSB/VBAT, 3=3.3V
    // - SSID:     robot
    // - Password: password
    // - stepper - 28BYJ-48 5V w/ ULN2003APG driver chip (DC) based driver board (DB)
    //   - EW.13,14,15,16 - A4,A5,A6,A7 - green,blue,purple,grey
    //   - DB.1,2,3,4 - IN1,IN2,IN3,IN4 (IN1 left, power right)
    //   - DB.8=DC.8=GND, DB.9=5V=DB.10 - black, white
    //   - DC.9=DB.11 powered 5V w/ DB.11-10 jumper)
    // - power by 5V USB powerbank
    var fastest = 1; // ms per step - (3=safe, 1,2 may be challenging...)
    var lon = false; // logging on
    var pag
    +'td {text-align:center; vertical-align:middle; }'
    +'<meta name="viewport" content="width=device-width, initial-scale=1">'
    +'<script>var ifrm;'
    +'function eb(b) { ifrm.src = "/ifrm.html?e=" + b.id; }'
    +'</head><body onload="ifrm = document.getElementById(\'ifrm\');">'
    +'<h3>Hello from <i><b>robot</b> train engine!</i></h3>'
    +'<table width="100%"><tr>'
    +'<td><button id="le"  onclick="eb(this)"><br>FRONT LIGHT<br><br></button></td>'
    +'<td><button id="be"  onclick="eb(this)"><br>BOTH LIGHTS<br><br></button></td>'
    +'<td><button id="re"  onclick="eb(this)"><br>BACK LIGHT<br><br></button></td>'
    +'<td><button id="lb"  onclick="eb(this)"><br>FRONT BLINK<br><br></button></td>'
    +'<td><button id="bl"  onclick="eb(this)"><br>BOTH BLINK<br><br></button></td>'
    +'<td><button id="rb"  onclick="eb(this)"><br>BACK BLINK<br><br></button></td>'
    +'</tr></table><table width="100%"><tr>'
    +'<td><button id="s-2" onclick="eb(this)"><br>-f<br><br></butto­n></td>'
    +'<td><button id="s-5" onclick="eb(this)"><br>-m<br><br></butto­n></td>'
    +'<td><button id="s-9" onclick="eb(this)"><br>-s<br><br></butto­n></td>'
    +'<td><button id="s+0" onclick="eb(this)">Engine<br>STOP<br></b­utton></td>'
    +'<td><button id="s+9" onclick="eb(this)"><br>+s<br><br></butto­n></td>'
    +'<td><button id="s+5" onclick="eb(this)"><br>+m<br><br></butto­n></td>'
    +'<td><button id="s+2" onclick="eb(this)"><br>+f<br><br></butto­n></td>'
    +'<iframe id="ifrm" width="100%" height="30"></iframe>'
    var WebServer = require('WebServer')
      , wifi = require('EspruinoWiFi')
    function onInit() {
      if (wifi)
        wifi.startAP('robot', {
          authMode:'open', password:'password'
        }, startServer);
    function startServer() {
      var ws = new WebServer(
    { port: 80
    , file_system: '/var/www/'
    , memory:
    {'index.html':{'type':'text/html','conte­nt': pag }
        '<html><body>e: XX</body></html>' }
    ws.on('start', function (WebServer) {
      if (lon) console.log('WebServer listening on port ' + WebServer.port); });
    ws.on('request', function (request, response, parsedUrl, WebServer) {
      var e = (parsedUrl.query) ? parsedUrl.query.e : null, v;
      if (lon) console.log('WebServer requested', parsedUrl, response, "e="+e);
      if (e=="le"||e=="be") { v = !digitalRead(LED2);
        digitalWrite(LED2,v); digitalWrite(B0,v); }
      if (e=="re"||e=="be") { v = !digitalRead(LED1);
        digitalWrite(LED1,v); digitalWrite(A0,v); }
      if (e=="lb") { setTimeout(blnkl,50,13); }
      if (e=="bl") { setTimeout(blnkb,50,9); }
      if (e=="rb") { setTimeout(blnkr,50,13); }
      if ((e+" ").substr(0,1)=="s") { var s = Math.round(1 * e.substr(1));
         s=(s<0&&s>-fastest)?-fastest:(s>0&&s<3)?­fastest:s; e="s"+s; r(s); }
      response.dSnd = response.dSnd.replace("XX",e);
    ws.on('error', function (err, WebServer) {
      if (lon) console.log('WebServer error', err); });
    function blnkl(c) { var v;
      v = !digitalRead(LED2); digitalWrite(LED2,v); digitalWrite(B0,v);
      if (c) setTimeout(blnkl, 50, c - 1);
    function blnkb(c) { var v;
      v = !digitalRead(LED1); digitalWrite(LED1,v); digitalWrite(A0,v);
      v = !digitalRead(LED2); digitalWrite(LED2,v); digitalWrite(B0,v);
      if (c) setTimeout(blnkb, 50, c - 1);
    function blnkr(c) { var v;
      v = !digitalRead(LED2); digitalWrite(LED2,v); digitalWrite(A0,v);
      if (c) setTimeout(blnkr, 50, c - 1);
    // mirror stepper code from http://forum.espruino.com/comments/12055­381/
    // stepper.js
    // stPs stepper pins
    // st   step 0..8
    // stT  step Time in milliseconds [ms]
    // stI  step Interval (from setInterval() and for clearInterval()
    // sts  steps 0001,0011,0010,... pin signals
    // stBW sts - steps Backwards
    // stFW sts - steps Forward
    // dmy  ...because of (cond) ? exprT : exprF needs something to assign to
    var run = false;
    var st = 0;
    var stT = 0;
    var stI = null;
    var sts = null;
    var stSt =  0b0000;
    var stFW = [0b1000,0b1100,0b0100,0b0110,0b0010,0b00­11,0b0001,0b1001];
    var stBW = [0b1001,0b0001,0b0011,0b0010,0b0110,0b01­00,0b1100,0b1000];
    var stPs = [A4,A5,A6,A7];
    // setI setInterval(i,stsC) i in [ms] with (optionl) step Change (if not null), 
    // and direction info (string)
    var setI = function(t,stsN,d) {
      if (lon) console.log("t = ",t, d); 
      if (stI) clearInterval(stI);
      if (stsN) sts = stsN;
      run = true;
      stI = setInterval(stp,t);
    // stp step
    var stp = function() { digitalWrite(stPs, sts[st = ++st % 8]); };
    // _sFW step ForWard
    var _sFW = function(t) {
      if (lon) console.log("FW w/ " + t);
      if (stT > 0) { setI((stT = t),null," ~F");
      } else { if (stT) { st = Math.abs(st - 7); } if (!stI) { st--; } setI((stT = t),stFW," FW"); }
    // _sBW step BackWards
    var _sBW = function(t) {
      if (lon) console.log("BW w/ " + t);
      if (stT < 0) { setI(-(stT = t),null," ~B"); 
      } else { if (stT) { st = Math.abs(st - 7); } if (!stI) { st--; } setI(-(stT = t),stBW," BW"); }
    // stop
    var stop = function() {
      if (stI) { stI = clearInterval(stI); stI = null; }
      if (lon) console.log(stI);
      run = false;
      digitalWrite(stPs, stSt);
    // run function - t is stepping interval in [ms]
    var r = function(t) {  
      if (typeof t === "undefined" ) {
        if (stT) {
          if (lon) console.log((stT > 0) ? "F>B" : "B>F");
        } else {
          if (lon) console.log("What ?");
      } else { 
        dmy = (t) ? (t>0) ? _sFW(t) : _sBW(t) : stop(); 

    1 Attachment

    • robotTrainEngineControlUI.jpg
  • Wow, looks great!

    If you wanted to do something fancy-looking reasonably easily you could take a look at this? http://www.espruino.com/Graphical+Web+In­terface Basically just make an SVG with Inkscape and then use that.

    I've started using template literals for HTML pages too so you can avoid the ' + ' stuff, so:

    var pag = `<html><head><style>
    td {text-align:center; vertical-align:middle; }
    // as opposed to
    var pag
    +'td {text-align:center; vertical-align:middle; }'
  • I was going to suggest the same regarding the literals - as you can paste the HTML source directly in, so it's easier to make changes and not escape ' and " etc.

    Oh - and it's pretty cool. I was going to look at controlling some of my sons blu lego and possibly bb8 with web Bluetooth now there is windows 10 support.

  • @Gordon and @Wilberforce, thanks for the input. Yes, I already used the literal. This was some 'old' code, I guess it was before literals were available... The only change to the code was the EYE/S to LIGHT and RADAR to Engine (STOP). ...Never happened to me that I could reuse code this easy.

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

Espruino-Wifi controls a STOKYS robot train engine, G Scale

Posted by Avatar for allObjects @allObjects