Copyright ©

Mongoose OS Forum


Servo control mg995

Hi all, I want to control mg995 from esp8266. I tried PWM.set method from api_pwm.js but everytime the servo is moved to around 160-170 degrees on two different events :

PWM.set(SERVO_PIN,100,50); // to move to degree x
and PWM.set(SERVO_PIN,0,0); // to move back to the zero degree position

i don't know how this PWM.set method is working. In general i want to know what values to write if i want to rotate my servo by, lets say 45 degree. If anyone has done something like that or know anything about this then please do comment. Every comment is appreciated.


  • From api_pwm.js

    let PWM = {
      // ## **`PWM.set(pin, freq, duty)`**
      // Set and control the PWM. `pin` is a GPIO pin number, `freq` is
      // frequency, in Hz. `freq` 0 disables PWM on the pin. `duty` is duty in %,
      // how long to spend in "1" state. Must be between 0 and 100 inclusive.
      // 0 is "always off", 100 is "always on", 50 is a square wave.
      // Return: true - success, false - failure.
      // Example:
      // javascript
      // PWM.set(pin, 50, 2.73);
      // Note:
      // on ESP32 we use 8 channels and 4 timers.
      // Each `PWM.set()` call with new pin number assigns a new channel.
      // If we already have a timer running at the specified frequency,
      // we use it instead of assigning a new one.
      set: ffi('bool mgos_pwm_set(int, int, float)'),

    The frequency to control a servo is 50Hz. Period time=20ms.
    Ideally, 1ms pulse width corresponds to 0 degrees, 1.5ms to 90 degrees, 2ms to 180 degrees.

    If you want to move the motor to a certain angle, you have to do some calculations to transform from angle to duty cycle (the third parameter of PWM.set).
    angle=0 -> pulseLength=1.0
    angle=90 -> pulseLength=1.0+90.0/180.0=1.5
    angle=180 -> pulseLength=1.0+180.0/180.0=2.0
    and so on...

    The duty cycle will be pulseLength/20.0

    I'm not sure if my javascript syntax is ok.

    function duty(angle)
    return (1.0+angle/180.0)/20.0;
    PWM.set(SERVO_PIN, 50, duty(30.0));
    PWM.set(SERVO_PIN, 50, duty(120.0));
    Thanked by 3JohnCz Hc95 Sergey
  • Hc95Hc95 India
    edited September 13

    @nliviu A huge thanks for your valuable input. I will try this formula and will update the results here asap.

  • @nliviu
    That formula doesn't worked quite well on Tower Pro mg995. It instantly rotates the servo to its limit and disables the pwm. I have to manually break the power supply to make the motor work again.

    By brute force method i figured out how to rotate the motor forward/backward. The functions are :

    function forWard(){
    PWM.set(SERVO_PIN,50,20); // rotate the servo in anti-clockwise direction to a specified angle(in this case about in between 70 to 80 degree)
    Sys.usleep(400000); //400ms pause to let the servo motor to rotate to the specified position
    PWM.set(SERVO_PIN,50,100); // to stop the pwm

    function backWard(){
    PWM.set(SERVO_PIN,1000,20); //rotate the servo in reverse(clockwise) direction.(i.e back to initial position)
    Sys.usleep(600000); //600ms pause to let the servo motor to rotate to the specified position
    PWM.set(SERVO_PIN,50,100); // to stop the pwm

    The problem with these functions is that i cannot control the motor's rotation precisely to a specified angle.
    @Sergey @rojer Please look into this if you can.


  • Sorry, the duty must be scaled between 0 and 100.

    function duty(angle)
    return 100.0*(1.0+angle/180.0)/20.0;
  • I've just checked with an oscilloscope and the timing provided by my formula is correct.
    There might be some errors when translating the angle into duty cycle due to the imprecision of the servo motor.

  • @nliviu Sorry for replying this late, I have been busy the past two days. I tried your function and it worked but the angle rotation is not precise i guess it may be the problem with the servo motor. Once again a big thanks for helping me out. One more thing i want to know and that is how to stop pwm on the motor in order to consume less electricity.


  • nliviunliviu Romania
    edited September 16

    You might need to calibrate the timings for your servo. A servo I use needs ~700 microseconds for the ~0 degrees and ~2300 microseconds for ~180 degreess.
    I've written a servo library (in C++ and I don't know how to bind it to javascript) which deals with these timings.
    The relevant code looks like this:

    int Servo::doAngle(int angle)
        angle = (angle < 0) ? 0 : ((angle > 180) ? 180 : angle);
        float duty = (_tmin + angle / 180.0 * (_tmax - _tmin)) / 200.0; //100.0 * (_tmin + angle / 180.0 * (_tmax - _tmin)) / 20000.0;
        float onTime = duty * 0.2; //duty * 20.0 / 100.0;
        mgos_pwm_set(_pin, 50, duty);
        return angle;

    _tmin and _tmax are the minimum and maximum pulse widths (in microseconds for 0 and 180 degrees).

    To stop the PWM you have to send the command PWM.set(SERVO_PIN, 0, 0) as it is mentioned in api_pwm.js:

    `freq` 0 disables PWM on the pin.
  • nliviunliviu Romania
    edited September 17

    Thank you.
    I'm following the release channel not latest.
    Is there a way to check the version of Mongoose OS (in mos.yml) and do the scaling of the duty accordingly?

  • The PWM.set(SERVO_PIN,0,0); is working fine to stop the pwm.
    Is there any way to know the current position(or angle) of the servo motor? @nliviu
    Thanks @Sergey for informing about the latest pwm release. You guys are really doing a great work :smile:

  • If you want to know the current position of the servo, you need to manage it by yourself.

  • Ok thanks, no problem :)
    Once again a big thank you for helping me out @nliviu and @Sergey :smile:
    I will post my code asap i figure out how to read current servo angle.


  • You can't read the current position/angle.
    You need to have a variable in your program to store it.

    Thanked by 1Sergey
Sign In or Register to comment.