Move servo with speed and hold position

Posted on
  • I am playing around with a servo.

    I get that I can do

    const s = require("servo").connect(B14);
    s.move(0.5, 3000);

    This will move the servo to position 0.5 over 3 seconds, however, it doesn't hold the position, so in this case, I am opening a chest, and once the chest is open the weight of it makes the lid slowly close again which is not what I want.

    I also get that I can do

    analogWrite(pin, (1+pos) / 50.0, {freq:20, soft: false });

    This will move the servo to the position taking 1 second and will hold the position, but I can't seem to find a way to set the speed/time by which it moves.
    I've tried with interval and moving it a set amount but that results in jaggery movement (I set it with an interval of 20ms, that should not be perceptible, but it was)

    Does anyone have any ideas of what I should do to get both the speed/time control, and get it to hold the position once it reaches it? :)

  • I found a "workaround", not perfect at all, but it sort of does what I want.

    moveTo = (deg: number, ms: number = 1000) => {
        const deg2 = 0.2 + (0.2 - 0.09)/(0.9 - 0.7) * (deg - 0.9);
        this.servo.move(deg2, ms, () => {
          analogWrite(this.sId, (deg) / 50.0, { freq: 20, soft: false });
  • You might just be able to use:

    s.move(0.5, 1000, function() { s.move(0.5, Infinity); // hold position }); `

    It feels a bit hacky though! Maybe the servo library should provide a function for it

  • That would be the best solution.
    I'd be happy to try to contribute a PR if you can point me at it :)

  • So this is the actual servo script?­/blob/master/devices/servo.js

    I thought it was just part of the documentation, I didn't think to look for the file in the EspruinoDocs repo xD
    Anyway, I'll have a look at it this afternoon! :)

  • Yes, that's it! Yes, pretty much all the module source is also in EspruinoDocs!


    I think this is the first pull request I've ever done between forks and the first one on GitHub for that sake. I hope I did it correctly xD

  • Looks great! Just merged! It won't be live immediately, but give it a few days :)

  • Great, looking forward to it :)

  • Tbh, I'm not a fan of the merge:
    The now optional parameter 'soft' is neither clear nor concise, it can just mean anything. In the context of Espruino it usually means a software implemented function (serial, i2c, pwm, timer interrupt). It's difficult to understand that 'soft: false' in this case actually means 'move the servo slowly and then hold the position'. And the result of 'soft: true' isn't documented at all.

    In line line 116 in devices/Servo was changed to use software PWM instead of hardware PWM, besides the fact that the description above says 'You can also use a hardware timer to produce the pulse...'. And in line 119 in devices/Servo only the comment was modified, now it includes '... devices/Servo'. I'd say '// move to position 0.5 over 1 second' would be correct.

  • I can totally see your point, I may have misunderstood what is happening.
    I tried using the convention here:

    analogWrite(pin, (1+pos) / 50.0, {freq:20, soft: false });

    Where what I actually saw was: {soft: false} made the motor move to the position and hold that position, while soft: true, or no value would make the servo move to the position and then it would just go slack. I figured that soft in this case meant whether or not it should go slack.

    How would you suggest the change be implemented? :)

  • Oh yeah, the soft option, and the analogWrite that is actually a PWM - it's not self-explaining. As a quick hack something like this:

        interval = setInterval(function() {
            currentPos = pos*amt + initial*(1-amt);
            digitalPulse(pin, 1, offs+E.clip(currentPos,0,1)*mul);
            if (amt >= 1) {
              interval = undefined;
              // hold the position if option hold is set
              if (options.hold == true) {
                // move the calculation of the pulse width out of the interval
                // we might re-use 'initial' instead of adding 'pulseWidth' for low memory devices
                var pulseWidth = offs+E.clip(pos,0,1)*mul;
                // interval to hold the position (must use 'interval' as reference)
                interval = setInterval(function() {
                    digitalPulse(pin, 1, pulseWidth); 
                }, 20);
              if (callback) callback();
            } else {
              amt += 50*time);  // equals amt += 1000.0 / (20*time);
          }, 20);
    • Using option 'hold' instead of 'soft'
    • Use a first interval to move the servo (as it was before this change), and conditionally add a second interval to hold the position

    It would be possible to add the option 'hold' to the 'connect' function, then there's no need to include it in every call to 'move'. The code is not tested.

  • I'll look it over and test it tomorrow :)

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

Move servo with speed and hold position

Posted by Avatar for TheLogan @TheLogan