ESP32 - MicroPythonを使い、ステッピングモータを制御する


# はじめに

ステッピングモータを使いたくなったので、購入をしました。
Arduinoスケッチで制御している記事は多くあるのですが、
MicroPythonで制御しているものがなかったので、
紹介させていただきます。

![out_sp1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/ac156516-79c8-2bd4-4c0c-7c0cfd38770d.gif)


# 作業環境

- ホストPC
  - windows10 64bit Home
  - VSCode - 1.47.3
  - NodeJS - 12.14.1 LTS
- ターゲットボード
  - ESP32-WROOM-32 開発ボード
    - MicroPython 1.12(esp32-idf4-20191220-v1.12.bin)

ESP32におけるMicroPythonの環境構築は、以前記載した
- [[備忘録]ESP32-VSCode-microPythonでの開発環境の構築](https://qiita.com/kotaproj/items/b53006aef9d04053a5ee)
を参考にしてください。


# 機材

|部品名  |説明|備考  |
|---|---|---|
|ESP32-WROOM-32 開発ボード  |5V   |  |
|ステッピングモーター |型番:28BYJ-48、12V駆動    | 購入先[Amazon](https://www.amazon.co.jp/gp/product/B07MNP1SBS/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1) |
|ULN2003ステッピングモーター駆動テストモジュールボード |モータ駆動のためのボード   | モータとセット買い |
|DCアダプタ 12V  |モータが12V駆動のため   | 購入先[Amazon](https://www.amazon.co.jp/gp/product/B07Y56WXK9/ref=ppx_yo_dt_b_asin_title_o05_s00?ie=UTF8&psc=1) |
|ジャンパーケーブル  |10本程度   |  |


# 接続

|部品名  |説明|備考  |
|---|---|---|
|ESP32-PIN25 | テストモジュールボード - IN1 | - |
|ESP32-PIN26 | テストモジュールボード - IN2 | - |
|ESP32-PIN27 | テストモジュールボード - IN3 | - |
|ESP32-PIN13 | テストモジュールボード - IN4 | - |
|ACアダプタ - GND | テストモジュールボード - GND | ESP32-GND |
|ACアダプタ - 12V | テストモジュールボード - 12V |
|テストモジュールボード - モータコネクタ | ステッピングモータ | - |


![setuzoku.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/3afcfd31-620f-cb23-9596-46ad42639475.png)



# コードの作成

[Arduino - Stepperライブラリ](https://github.com/arduino-libraries/Stepper)をベースにMicroPythonに移植します。

本ライブラリをベースしようと思ったのは、

- Arduinoサイトでよく使われているため、極力インタフェースを同じにしたい
- （そこまで）シビアなタイミングがない

となります。


また、本家との違いは以下となります。

- stop()メソッドを追加している
- step()にて、動作させた後、stop()メソッドをコール
  - 本家のライブラリでは、ユーザが能動的にstopさせないと常に電流が流れ続ける
  - 熱くなってしまうので、stop()をコールしている
- 5pinモータ機能を削除
  - 今回は4pin構成なので、5pin部分は移植なし


## ステッピングモータを制御するためのコード

以下がステッピングモータを制御するためのクラスとなります。


```python:stepper.py
import time
from machine import Pin


class Stepper():
    def __init__(self,  number_of_steps,
                 motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4):
        self.step_number = 0                   # which step the motor is on
        self.direction = 0                     # motor direction
        self.last_step_time = 0                # time stamp in us of the last step taken
        self.number_of_steps = number_of_steps  # total number of steps for this motor

        # setup the pins on the microcontroller:
        self.motor_pin_1 = Pin(motor_pin_1, Pin.OUT)
        self.motor_pin_2 = Pin(motor_pin_2, Pin.OUT)
        self.motor_pin_3 = Pin(motor_pin_3, Pin.OUT)
        self.motor_pin_4 = Pin(motor_pin_4, Pin.OUT)

        # pin_count is used by the stepMotor() method:
        self.pin_count = 4

        self.set_speed()
        return

    def set_speed(self, what_speed=10):
        ''' Sets the speed in revs per minute
        '''
        self.step_delay = 60 * 1000 * 1000 // self.number_of_steps // what_speed
        return

    def step(self, steps_to_move, auto_stop=True):
        ''' Moves the motor steps_to_move steps.  If the number is negative,
            the motor moves in the reverse direction.
        '''
        steps_left = abs(steps_to_move)  # how many steps to take

        # determine direction based on whether steps_to_mode is + or -:
        self.direction = 1 if steps_to_move > 0 else 0

        # decrement the number of steps, moving one step each time:
        while steps_left > 0:
            now = time.ticks_us()
            # move only if the appropriate delay has passed:
            if time.ticks_diff(now, self.last_step_time) >= self.step_delay:
                # get the timeStamp of when you stepped:
                self.last_step_time = now
                # increment or decrement the step number,
                # depending on direction:
                if self.direction == 1:
                    self.step_number += 1
                    if self.step_number == self.number_of_steps:
                        self.step_number = 0
                else:
                    if self.step_number == 0:
                        self.step_number = self.number_of_steps
                    self.step_number -= 1

                # decrement the steps left:
                steps_left -= 1
                # step the motor to step number 0, 1, 2, 3
                self._step_motor(self.step_number % 4)
        
        if auto_stop:
            self.stop()
        return

    def _step_motor(self, this_step):
        ''' Moves the motor forward or backwards.
              if (this->pin_count == 4) {
        '''
        # 1010
        if this_step == 0:
            self.motor_pin_1.value(True)
            self.motor_pin_2.value(False)
            self.motor_pin_3.value(True)
            self.motor_pin_4.value(False)
        # 0110
        elif this_step == 1:
            self.motor_pin_1.value(False)
            self.motor_pin_2.value(True)
            self.motor_pin_3.value(True)
            self.motor_pin_4.value(False)
        # 0101
        elif this_step == 2:
            self.motor_pin_1.value(False)
            self.motor_pin_2.value(True)
            self.motor_pin_3.value(False)
            self.motor_pin_4.value(True)
        # 1001
        elif this_step == 3:
            self.motor_pin_1.value(True)
            self.motor_pin_2.value(False)
            self.motor_pin_3.value(False)
            self.motor_pin_4.value(True)
        return

    def stop(self):
        self.motor_pin_1.value(False)
        self.motor_pin_2.value(False)
        self.motor_pin_3.value(False)
        self.motor_pin_4.value(False)
        return
```


## 使い方

### 使用するための準備

```
>> from stepper import Stepper

>> MOTOR_STEPS = (2048)
>> PIN_MOTOR_1 = (25)
>> PIN_MOTOR_2 = (26)
>> PIN_MOTOR_3 = (27)
>> PIN_MOTOR_4 = (13)

>> my_motor = Stepper(MOTOR_STEPS, PIN_MOTOR_1,
                   PIN_MOTOR_3, PIN_MOTOR_2, PIN_MOTOR_4)
>> my_motor.set_speed(10)
```

### 回転させる

```
my_moter.step(512)
# =>ステッピングモータが90度、時計回りに回転
my_moter.step(2048)
# =>ステッピングモータが360度、時計回りに回転
my_moter.step(-512)
# =>ステッピングモータが90度、反時計回りに回転
```

- MOTOR_STEPSを2048としているため、一回転あたり2048となる
- step()にマイナス方向を指定すると反時計回りになる
- set_speed()には、1-20あたりまでが有効


# さいごに

- 現状、回転を実行すると、止めることができません

---

# 参考

- [Arduino - Stepperライブラリ](https://github.com/arduino-libraries/Stepper)
- [MicroPythonドキュメント - utime - 時間関連の関数](https://micropython-docs-ja.readthedocs.io/ja/latest/library/utime.html)
- [Arduinoでステッピングモーターを回す。](https://electronic.tousekice.com/arduino%E3%81%A7%E3%82%B9%E3%83%86%E3%83%83%E3%83%94%E3%83%B3%E3%82%B0%E3%83%A2%E3%83%BC%E3%82%BF%E3%83%BC%E3%82%92%E5%9B%9E%E3%81%99%E3%80%82/)