サーボを動作させてみる

サーボを動作させてみる

サーボを動作させてみる

SG-90小型サーボをESP32に接続してみました。

回路図

SG-90くらいの小型サーボならESP32 DEVKITの3.3Vに接続すれば動きます。回路図もご覧の通り簡単です。繋げるだけ。


SG-90小型サーボについて

20msのパルス信号で動作します。
0.5ms~2.5msの範囲のON時間でサーボが180°稼働します。
実際には調整できるように180°より可動範囲は大きいですがそこはトリムなりパルス幅の最小・最大値を制限するなりして調整します。

サーボから伸びている配線の色が説明とちょっと違ってるみたい。
(赤とかないんですけど)

説明
茶色 GND
オレンジ 電源
黄色 信号線

目の錯覚かな、写真で見ると説明書通りにオレンジが赤、黄色がオレンジに見える。

プログラム

main.cを以下のようにします。
PWM用の初期設定が面倒・・・。

#include <stdio.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "nvs_flash.h"
#include "driver/mcpwm_prelude.h"
#include "driver/gpio.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_log.h"

#define TAG "SERVO"

// PWM用パラメータ
#define SERVO_TIMEBASE_RESOLUTION_HZ 1000000    // 1MHz, 1us per tick
#define SERVO_TIMEBASE_PERIOD 20000             // 20000 tick, 20ms

// PWM用
int groupID;
mcpwm_timer_handle_t timer;
mcpwm_oper_handle_t oper;
mcpwm_cmpr_handle_t comparator;
mcpwm_gen_handle_t generator;

// ボリューム用
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_chan_cfg_t adc1_config = {
    .atten = ADC_ATTEN_DB_12,
    .bitwidth = ADC_BITWIDTH_12,    // 12bit = 0xFFF = 4,095  0~4,095に変換されます。
};

void init() {
    ESP_LOGI(TAG, "Init(S)");

    groupID = 0;

    // AIN2初期化 PWMパルス信号
    timer = NULL;
    mcpwm_timer_config_t timer_config = {
        .group_id = groupID,
        .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
        .resolution_hz = SERVO_TIMEBASE_RESOLUTION_HZ,
        .count_mode = MCPWM_TIMER_COUNT_MODE_UP,
        .period_ticks = SERVO_TIMEBASE_PERIOD,
    };
    esp_err_t ret = mcpwm_new_timer(&timer_config, &timer);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "mcpwm_new_timer error %d", ret);
        return;
    }

    ESP_LOGI(TAG, "init(2)");
    oper = NULL;
    mcpwm_operator_config_t operator_config = {
        .group_id = groupID,
    };
    ret = mcpwm_new_operator(&operator_config, &oper);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "mcpwm_new_operator error %d", ret);
        return;
    }
    ret = mcpwm_operator_connect_timer(oper, timer);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "mcpwm_operator_connect_timer error %d", ret);
        return;
    }

    ESP_LOGI(TAG, "init(3)");
    comparator = NULL;
    mcpwm_comparator_config_t comparator_config = {
        .flags.update_cmp_on_tez = true,
    };
    ret = mcpwm_new_comparator(oper, &comparator_config, &comparator);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "mcpwm_new_comparator error %d", ret);
        return;
    }

    ESP_LOGI(TAG, "init(4)");
    generator = NULL;
    mcpwm_generator_config_t generator_config = {
        .gen_gpio_num = CONFIG_SERVO_PIN,
    };
    ret = mcpwm_new_generator(oper, &generator_config, &generator);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "mcpwm_new_generator error %d", ret);
        return;
    }
    if (generator == NULL) {
        ESP_LOGE(TAG, "mcpwm_new_generator error");
    }

    ESP_LOGI(TAG, "init(5)");
    ret = mcpwm_comparator_set_compare_value(comparator, 0);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "mcpwm_comparator_set_compare_value error %d", ret);
        return;
    }

    ESP_LOGI(TAG, "init(6)");
    ret = mcpwm_generator_set_action_on_timer_event(generator,
            MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH));
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "mcpwm_generator_set_actions_on_timer_event error %d", ret);
        return;
    }
    ESP_LOGI(TAG, "init(7)");
    ret = mcpwm_generator_set_action_on_compare_event(generator,
            MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparator, MCPWM_GEN_ACTION_LOW));
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "mcpwm_generator_set_actions_on_compare_event error %d", ret);
        return;
    }
    
    ESP_LOGI(TAG, "init(8)");
    ret = mcpwm_timer_enable(timer);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "mcpwm_timer_enable error %d", ret);
        return;
    }
    ESP_LOGI(TAG, "init(9)");
    ret = mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "mcpwm_timer_start_stop error %d", ret);
        return;
    }

    // ボリューム用ADコンバータ初期化
    adc_oneshot_unit_init_cfg_t init_config1 = {
        .unit_id = ADC_UNIT_2,
        .ulp_mode = ADC_ULP_MODE_DISABLE,
    };
    ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC_CHANNEL_0, &adc1_config));

    ESP_LOGI(TAG, "Init(E)");
}

void app_main(void)
{
    nvs_flash_init();     // Flash初期化  (お約束のようなもの)

    init();

    int maxval = (1 << 12) - 1;

    while(1) {
        int val;
        ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, ADC_CHANNEL_0, &val));
        // サーボのパルスは20ms(20000)の内0.5~2.5ms(500~2500)の間で-90°~90°の回転になります。0.5~2.5msはサーボ個体によって微調整が必要になるかも?
        // 以下は手持ちのサーボで丁度180°になるように設定してます。
        const int min_pulse = 608;
        const int max_pulse = 2352;
        int servo = (int)((double)val / (double)maxval * (max_pulse - min_pulse) + min_pulse);
 
        // int bitwidth = (int)adc1_config.bitwidth;
        // double d = val * 3.3 / (pow(2, bitwidth) - 1);
        // ESP_LOGI(TAG, "volume = %d(%lfv), servo=%d", val, d, servo);

        // サーボ
        esp_err_t ret;
        ret = mcpwm_comparator_set_compare_value(comparator, servo);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "mcpwm_comparator_set_compare_value error %d", ret);
        }

        // 10ms待機
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

複数個接続する場合はもっと容量の大きなレギュレータを使わないとダメかな?
DEVKITだと2個くらいなら動かせました。3個以上はためしてない。

コメント

このブログの人気の投稿

(真)ESP32の開発環境をVSCodeで作る

ESP32にmicroSD接続

ESP32でデバッグ