Copyright © https://mongoose-os.com

Mongoose OS Forum

frame

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.
Thanks
Hc
:smile:

Comments

  • 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).
    pulseLength=1.0+(1.5-1.0)x(angle/90.0)=1.0+0.5xangle/90.0=1.0+angle/180.0
    Examples:
    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
    duty=(1.0+angle/180.0)/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 2017

    @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.

    Thanks
    Hc

  • 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.

    Thanks
    Hc

  • nliviunliviu Romania
    edited September 2017

    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 2017

    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.

    Hc

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

    Thanked by 1Sergey
  • Hi all,
    All the best for the new year.

    Your function duty(angle) { return 100.0*(1.0+angle/180.0)/20.0; } works well by running PWM.set(5, 50, duty(180)). However I have the following message from mongoose-os:
    [Jan 11 21:03:39.963] mgos_pwm_set === Please use [0, 1] fraction for PWM duty cycle mgos_pwm_set(5, 5.027778) should be changed to mgos_pwm_set(5, 0.050278) ===

    The problem is : sometimes, I got the following mongoose-os dump when I run several times a function PWM.set(5, 50, duty(180)).
    [Jan 11 20:31:01.298] --- BEGIN CORE DUMP ---
    [Jan 11 20:31:01.300] {"arch": "ESP32", "cause":29,
    [Jan 11 20:31:01.302] "REGS": {"addr": 1073544464, "data": "
    [Jan 11 20:31:01.306] W6sIQF6sCICgBfw/LwAAAC8AAAAMAAAA/////wAAAAD+////AAAAAIAF/D8AAAAAMQX8P98E/D8wAAAAAAAAAOkE/D/vvq3e776t3u++rd7vvq3e776t3u++rd7vvq3e776t3u++rd7vvq3e776t3u++rd7vvq3e776t3u++rd7vvq3e776t3u++rd7vvq3e776t3u++rd7vvq3e776t3u++rd7vvq3e776t3u++rd7v

    And if I remove the 100.0 on a function duty(angle) { return (1.0+angle/180.0)/20.0; }, any mgos_pwm-set notification appears but it doesn't work anymore.

    Could you help, please?

    Thanks in advance

  • nliviunliviu Romania

    The warning from the library: https://github.com/mongoose-os-libs/pwm/commit/71dbf82f798991cd61588688941caa28e2b41df0

    There is no reason for the formula you use to not work because after the warning is issued by the pwm library, a division by 100.0 is performed.
    Ref: https://github.com/mongoose-os-libs/pwm/blob/aed33ae41139e77d48015b701e8703945d15a02f/src/esp32/esp32_pwm.c#L166

    You can analyze the core dump following https://mongoose-os.com/docs/book/debug.html#analysing-core-dumps
    You will need to have docker installed.

  • nliviunliviu Romania

    The warning from the library: https://github.com/mongoose-os-libs/pwm/commit/71dbf82f798991cd61588688941caa28e2b41df0

    There is no reason for the formula you use to not work because after the warning is issued by the pwm library, a division by 100.0 is performed.
    Ref: https://github.com/mongoose-os-libs/pwm/blob/aed33ae41139e77d48015b701e8703945d15a02f/src/esp32/esp32_pwm.c#L166

    You can analyze the core dump following https://mongoose-os.com/docs/book/debug.html#analysing-core-dumps
    You will need to have docker installed.

  • nliviunliviu Romania
    edited January 11

    I've just added mJS API to my servo library https://github.com/nliviu/servo
    Feel free to try it and post your comments here.

    I don't have a servo available right now to test it, but it should work.
    Minimum compatible Moongoose OS version is 1.17.

  • @nliviu
    Wow! Great! Thanks a lot for the helpful information.
    Now, I understand the reason of the existence about the number 100.0 in your formula.
    And the core dump issues come from the fact that I used the function Sys.usleep() several times in a loop, is the cause.

    I have just tried your Servo Library (the latest version of mongoose-os). Here is my init.js

    load('api_pwm.js');
    load('api_servo.js');

    let servo = Servo.createFull(5, 1000, 2000);

    for(let step = 0; step <= 180; step++){
    servo.doAngle(step);
    }

    for(let step = 180; step >= 0; step--){
    servo.doAngle(step);
    }

    PWM.set(5, 50, 0);

    As you see in the following logs generated by 'mos console', your library works well. Congratulations!

    [Jan 12 10:27:22.221] esp32_bt_gatts_ev Starting BT service 5f6d4f53-5f52-5043-5f53-56435f49445f
    [Jan 12 10:27:22.231] mg_rpc_channel_uart 0x3ffec140 UART0
    [Jan 12 10:27:22.237] mgos_init Init done, RAM: 188640 total, 80068 free, 80068 min free
    [Jan 12 10:27:22.308] doAngle angle=0, duty=0.050000, onTime=1000.000000 us
    [Jan 12 10:27:22.319] doAngle angle=1, duty=0.050278, onTime=1005.555542 us
    [Jan 12 10:27:22.330] doAngle angle=2, duty=0.050556, onTime=1011.111145 us
    ………………
    [Jan 12 10:27:24.182] doAngle angle=178, duty=0.099444, onTime=1988.888794 us
    [Jan 12 10:27:24.192] doAngle angle=179, duty=0.099722, onTime=1994.444458 us
    [Jan 12 10:27:24.203] doAngle angle=180, duty=0.100000, onTime=2000.000000 us

    [Jan 12 10:27:24.214] doAngle angle=180, duty=0.100000, onTime=2000.000000 us
    [Jan 12 10:27:24.224] doAngle angle=179, duty=0.099722, onTime=1994.444458 us
    [Jan 12 10:27:24.235] doAngle angle=178, duty=0.099444, onTime=1988.888794 us
    ..............
    [Jan 12 10:27:26.114] doAngle angle=0, duty=0.050000, onTime=1000.000000 us
    [Jan 12 10:27:26.123] mgos_aws_shadow_init AWS Device Shadow requires MQTT
    [Jan 12 10:27:26.130] mongoose_poll New heap free LWM: 73336

    However, there is a big problem about system stability. Once the init.js initialized, I can't access anymore to the device by 'mos ui' and I have to re-flash it.

    Could you tell me how to change the speed of rotation? Is it about Tmin and Tmax?

    Thanks again.

  • nliviunliviu Romania

    You can't change the speed of rotation. It is defined in the data sheet of your servo.
    tmin and tmax:

    - ["Servo.tmin", "i", 1000, {title: "Pulse width for 0 degrees in microseconds (default 1000)"}]
    - ["Servo.tmax", "i", 2000, {title: "Pulse width for 180 degrees in microseconds (default 2000)"}]
    

    I've just added a stop function.

    Do you have tight loops in your code?

  • Ok, thanks.
    Not really but I would try some long loops for experimenting, you know?

  • Regarding the system stability, maybe it has to do with the very rapid output to the console?

Sign In or Register to comment.