이번에는 서보모터를 구동해보도록 하자. 이 서보모터의 경우에도 데이터시트를 확인할 필요가 있는데, 사용자가 많은 제품을 이용하여 샘플 코드를 보고 PWM을 이용해서 서보모터를 제어하는 방법에 대해서 알아보도록 하자. 필자는 아주 오래전에 ATMEGA 128을 이용하여 트래킹 카메라를 만든적이 있다. 2005년 정도인데.. 서보모터 2개와 기구물 그리고 ATMEGA 128 마이크로 컨트롤러를 이용하였는데... 요즘에는 아예 모듈로 나온 제품도 있는것 같다. 

     

    --- contents ---

    01. [ESP32] VS Code 개발환경 구성 : https://makeutil.tistory.com/303

    02. [ESP32] 첫 프로젝트 생성하기 : https://makeutil.tistory.com/304

    03. [ESP32] 멀티 테스크 예제 (2 Task) : https://makeutil.tistory.com/305

    04. [ESP32] Task간 데이터 공유 (Queue, Mutex) : https://makeutil.tistory.com/306

    05. [ESP32] 개발보드 별 형상 및 I/O (ESP32/ESP32-S3) : https://makeutil.tistory.com/307

    06. [ESP32] UART 통신 예제 #1 : https://makeutil.tistory.com/309 

    07. [ESP32] UART 통신 예제 #2 : https://makeutil.tistory.com/313

    08. [ESP32] GPIO LED 켜기 : https://makeutil.tistory.com/311

    09. [ESP32] Timer와 PWM을 이용한 LED 점멸 : https://makeutil.tistory.com/312

    10. [ESP32] WiFi SoftAP와 WiFi Station (기본예제) : https://makeutil.tistory.com/314

    11. [ESP32] 무선 시리얼 통신장치 만들기 1 (SoftAP) : https://makeutil.tistory.com/315

    12. [ESP32] 무선 시리얼 통신장치 만들기 2 (SoftAP STA) : https://makeutil.tistory.com/316

    13. [ESP32] I2C Temperature Sensor (DHT21) : https://makeutil.tistory.com/317

    14. [ESP32] ESP32-S3 구매 할 때 고려할 점 :  https://makeutil.tistory.com/318

    15. [ESP32] Web Server - HTML, Javascript : https://makeutil.tistory.com/320

    16. [ESP32] Web Server - GPIO Control (LED 제어) : https://makeutil.tistory.com/321

    17. [ESP32] I2C EEPROM (AT24C0x) : https://makeutil.tistory.com/322

    18. [ESP32] PWM 서보모터 제어 (HS-311, SG90) : 현재글

    A1. [ESP32] 오류 - Fatal Error : No such file or directory : https://makeutil.tistory.com/308

    ------------------

     

     

    예전 부품상자를 뒤지다가 SG90과 HS-311 두개의 서보모터가 있는 것을 확인하였다. 그래서 해당 모터를 제어하려고 했더니, 대략 입력전압이 5V라서... 알리에서 구매한 걸로는 안될것 같아 정품 ESP32를 이용하여 테스트를 진행하였다. 별도로 하중을 주지 않을 것이라서 전원회로를 따로 만들 필요는 없을 것 같고, 동작하는 것만 확인하는 정도에서 마무리하려한다. 

     

    1. 연결

    1.1. 서보모터 종류

      HS-311은 아마도 2014년도에 회사 직원스터디 그룹 수업용으로 구매한것같고, SG90은 아마도 라즈베리파이를 이용해서 간단한 뭔가를 만들려고 샀다가 바빠서 못한것 같다. 개봉도 안된거 보니... 스펙상 토크는 HS-311이 좀더 좋다 하더라도 옜날 기억을 떠올려보면 이녀석 백래시가 꽤 있어서, 정확한 트래킹이 안되서.. 아마 울트라토크 제품으로 바꿔서 썼던거 같다. 그래도 SG90에 비하면 토크가 있는편이다. 다만, 알리발 ESP32는 확실히 못받쳐줄 것 같다. 두 서보모터는 3.3v에서는 다행히 동작은된다. 그런데 모터에서 소음이 발생되는 걸로 보아.. 힘이 모자라는 것 같다. 따라서, 제대로 사용하려면 빵판등을 이용해서 전원을 직접 공급해주는 방법을 이용하는 것을 추천한다. 

     

     

     

    1) HS-311

      아직도 팔고 있다. 필자가 20년도 더 전에 구매했었는데, 당시에는 11,000원 정도였다. 

     

    2) SG90 - 9g

      가격은 5000~7000원 사이인것 같다. 그리고 소형이라... 이걸 7000원... ㅡㅡ?

     

     

    1.2. 연결

      연결은 핀 18번을 이용했다. 이유는 딱히 없는데 동작이 잘안되서 바꾸다보니 동작될 때 18번이 되었다. 정도로 이해해주면되고, 독자분들께서는 다른핀을 이용해도 된다. 

     

      서보모터는 VIN/GND/DATA 이렇게 총 3가닥을 이용하고 DATA에 PWM을 넣어주면되겠다. 위의 두 모터는 모두 5V전압이 요구되고, HS-311은 500~600mAh 정도의 전압이 필요하다. 반대로 SG90은 250~500mAh 정도면 정상 구동이 가능하다. 그리고 페이로드(하중)없이는 적당히 나와도 동작은 된다. 

     

     

    2. 소스코드

      소스코드를 확인해보자. 

    #include <stdio.h>
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #include "driver/ledc.h"
    #include "esp_err.h"
    
    #define _HS311_
    
    // 공통 설정
    #define SERVO_PWM_GPIO           18            // SG90의 제어 핀
    #define SERVO_MAX_DEGREE         180           // 최대 회전 각도
    #define SERVO_PWM_FREQ           50            // 50Hz (20ms 주기 사용)
    
    // 모터별 Pulse Width 설정
    // HS-311 서보모터 설정. 
    #ifdef _HS311_
    #define SERVO_MIN_PW_US  600           // 0도에 해당하는 최소 펄스폭(us)
    #define SERVO_MAX_PW_US  2400          // 180도에 해당하는 최대 펄스폭
    #else // _SG90_ 서보모터 설정
    #define SERVO_MIN_PW_US  500           // 0도에 해당하는 최소 펄스폭 
    #define SERVO_MAX_PW_US  2500          // 180도에 해당하는 최대 펄스폭 
    #endif
    
    // 타이머 분해능은 ESP 종류에 따라 다르다. 14비트로 만들수 있는 값은 0~16384까지이다.
    #define SERVO_DUTY_RES           LEDC_TIMER_14_BIT 
    
    // 각도에서 펄스폭으로 변경하기 위한 함수 
    uint32_t angle_to_pulse_us(int angle) 
    {
        uint32_t retv=0;
        if (angle < 0) 
            angle = 0; 
        if (angle > SERVO_MAX_DEGREE) 
            angle = SERVO_MAX_DEGREE;
    
        // 각도변환 공식 
        retv=SERVO_MIN_PW_US+((SERVO_MAX_PW_US - SERVO_MIN_PW_US) * angle) / SERVO_MAX_DEGREE;
        return retv;
    }
    
    // PWM 초기화
    void servo_pwm_init(void) {
        ledc_timer_config_t timer = {
            .speed_mode       = LEDC_LOW_SPEED_MODE,
            .duty_resolution  = SERVO_DUTY_RES,
            .timer_num        = LEDC_TIMER_0,
            .freq_hz          = SERVO_PWM_FREQ,
            .clk_cfg          = LEDC_AUTO_CLK
        };
        ledc_timer_config(&timer);
    
        ledc_channel_config_t channel = {
            .gpio_num       = SERVO_PWM_GPIO,
            .speed_mode     = LEDC_LOW_SPEED_MODE,
            .channel        = LEDC_CHANNEL_0,
            .intr_type      = LEDC_INTR_DISABLE,
            .timer_sel      = LEDC_TIMER_0,
            .duty           = 0,
            .hpoint         = 0
        };
        ledc_channel_config(&channel);
    }
    
    // 각도 설정
    void set_servo_angle(int angle) 
    {
        uint32_t pulse_width_us = angle_to_pulse_us(angle);
        uint32_t duty = (pulse_width_us * (1 << 14)) / 20000; // 1주기는 20ms.
    
        printf("ANGEL:%3d° | PULSE WIDTH:%4ldus | DUTY: %5lu\n", angle, pulse_width_us, duty);
    
        // 듀티비를 적용하고 업데이트 한다.
        ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty);
        ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
    }
    
    // 메인 함수 
    void app_main(void) 
    {
        servo_pwm_init();
    
        while (1) 
        {
            // 0도부터 180도 까지 증가
            for (int angle = 0; angle <= 180; angle += 1) {
                set_servo_angle(angle);
                vTaskDelay(pdMS_TO_TICKS(200));
            }
            
            // 180도에서 0도 까지 감소 
            for (int angle = 180; angle >= 0; angle -= 1) {
                set_servo_angle(angle);
                vTaskDelay(pdMS_TO_TICKS(200));
            }
        }
    }

     

      소스에를 보면 200ms 단위로 증가 후 감소 하는 것을 확인할 수 있다. MIN이 500이면 0도에 해당하는 시간당 펄스는 0.5ms가 된다.반대로 180인 경우 MAX값인 2500이 된다. 그리고 앞서 PWM주기는 50Hz이므로 20ms이고, 해상도는 14비트니까 16384(2^14)가 된다. 따라서, 각도에서 펄스폭은 다음과 같다. 

     

     

     

    * 각도 0도 일때, 펄스폭 500us, 듀티값 409  => (500*16384) / 20000 = 409

    * 각도 90도 일때, 펄스폭 1500us, 듀티값 1228 => (1500*16384) / 20000 = 1228

    * 각도 180일 때, 펄스폭 2500us, 듀티값 2048 => (2500*16384) / 20000 = 2048

     

      독자 여러분들이 가진 서보모터도 이런 방식으로 사용하면 쉽게 사용이 가능하다. 사실 쉬운지는 모르겠고.. MCU마다 쓰는 방법이 달라서 할때마다 새롭다. 뭐 우선 샘플이라도 건져놨으니 필요할 때 쓰면 되겠다. 

    반응형
    • 네이버 블러그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기