Controlling servomotors using WebAudioAPI

Posted on
  • Hi !

    I'm currently trying to get some servos working nicely with an original Espruino board ( and also within a chrome browser ).

    The overall goal is to have a video & synchronized servo movements ( controlled, if can do so, by the audio channel within the video, or within a "tied" audio file, or on-the-fly-generated signals )

    I solved the "45° mystery" thanks to­316585/, which now allows me to move each of the servos I have using the following:

       micro servo SG90 on C8:
       Position "0" ( 1.5 ms pulse ) is middle,
               "90" (~2 ms pulse) is all the way to the right,
               "-90" (~1 ms pulse) is all the way to the left.
       //var s2 = require("servo").connect(C9);
       // servo.move() doesn't work for my microservo ( SG90 ): here's what works
       analogWrite(C9, 0.03, {freq:50}); // -> full left ( -90deg )
       analogWrite(C9, 0.07, {freq:50}); // -> middle ( 0deg )
       analogWrite(C9, 0.115, {freq:50}); // -> full right ( 90deg )

    Now, I'm looking for a replacement for the servo.move() fcn ( or a tweak of it ) that'd allow me to go to a certain position & taking some time to do so - not sure of what to change, one of my guesses is tweaking the following part ?

    digitalPulse(pin, 1, offs+E.clip(currentPos,0,1)*mul);
    // IDEA: clip to 0.03..0.115 ?

    On a close subject, I'm also trying to get the same servos to move:

    • using an audio file ( or an audio channel of a video ) with "recorded" movements from the servos ( when controlled from an Espruino running some code to generate the signals )
    • using the WebAudioAPI to generate the necessary signals

    I stumbled on 2 related posts (­/wav.htm &­dio-PulseOscillator ) which led me to messing around & try stuff, but I didn't achieve ( yet :/ ) my goal of controlling the said servos using purely on-th-fly-generated signals from the WebAudioAPI

    Last but not least, I'm aware of the 'ontimeupdate' evt that's available when a video is playing ( & yup, I plan to use it to trigger generating on-the-fly signals sync-ed with the video content )

    This being said, here's the code I have so far, I'll be hacking around with it today & the coming days, hopefully coming to a success .. ( .. thanks to hints from here ? ;p )

    Thanks in advance for reading this,
    Looking forward to reading anything on the subject

    ps: test files attached ;)

    3 Attachments

  • all right, if anyone needs more or less the same hack for servo.move, I have the following code ( not yet tested but worked flawlessly within a browser ;p )

    var interval, currentPos;
    var offs = 1, mul = 1;
    var options = {};
    //var options = { range: 3};
    if (options && options.range) {
      mul = options.range;
      offs = 1.5-(mul/2);
    // helpers
    // replacement for E.clip when not in Espruino env
    var clip = function(val, min, max){
      if(val < min) val = min;
      if(val > max) val = max;
      return val;
    // the good-ol' Ar map() !
    var map = function(x, in_min, in_max, out_min, out_max){
      //return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
      var mapped = (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
      return mapped.toFixed(3);
    var move = function(pos, time, callback) {
      if (time===undefined) time = 1000; // T: if no time is set as duration for the move, set 1 sec
      var amt = 0;
      if (currentPos===undefined) currentPos = pos; // T: if no currentPos is defined, set it to the goal position
      if (interval) // T: if already existing interval, cancel it
      var initial = currentPos; // T: set initial position for the move
      interval = setInterval(function() { // T: schedule re-calls
        if (amt>1) {  // T: move fully executed ( & maybe a little farther ? )
          clearInterval(interval); // T: no more re-calls
          interval = undefined; // T:  reset interval
          amt = 1; // T: set move to fully executed
          if (callback) callback(); // T: move ended callback
        currentPos = pos*amt + initial*(1-amt); // T: calculate currentPos from initialPos, pos & current amount ( steps already took )
        //digitalPulse(pin, 1, offs+E.clip(currentPos,0,1)*mul); // T: function digitalPulse(pin, value_highOrLow, time_duration), clip to min & max
        //digitalPulse(pin, 1, offs+clip(currentPos,0,1)*mul); // T: function digitalPulse(pin, value_highOrLow, time_duration), clip to min & max
        console.log('HIGH pulse width: ' + ( offs+clip(currentPos,0,1)*mul ) );
        console.log('amt: ' + amt);
        // IDEA: clip/map to 0.03..0.115 ?
        console.log('hacky analogWrite value:' + map(amt, 0, 1, 0.03, 0.15) );
        amt += 1000.0 / (20*time); // T: next step
      }, 20);
    // test-drive ..
    move(1, 500, function(){
      console.log('movement done !');

    I'll test the above as soon as I can, although not that efficient, it should hopefully do the trick :)

    On the "web side of things", still no success using the attached code ( "kinda" works .. sometimes .. for some values .. so actually more random that working ;p ), and the tests I could run using Audacity to generate tones were no that concluant :/ ..

    nb: on a quick try, I also didn't get any signal using the sound card from the pin driven by Espruino "analogWrite" command, even driving it repeatedly ( maybe it needs some amplification ? )

    ps: I'm doing stuff on a macbookpro 2K11 ( don't know yet if it means some specific stuff on audio input/output ( .. ) )

    1 Attachment

  • With the 45 degree issue, why not use require("servo").connect(C7,{range:2}); as that post says? Then you can get the nice smooth movement with the increased range?

    There's some info about controlling Espruino from the sound card at - not sure if it helps? If you keep the Espruino connected to USB then you can use that to ensure that signals are being received correctly.

    To try and decode signals using your sound card, you could also look at­illoscope - which may be some help to check the signal?

    ... or have you looked at Web Bluetooth/Puck.js - that'd probably be the most reliable way to get commands out of the browser at the moment as using Web Audio depends on the volume level on your PC (and some PCs/Macs have the signal inverted too!).

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

Controlling servomotors using WebAudioAPI

Posted by Avatar for stephaneAG @stephaneAG