ESP8266 (ESP-WROOM-02) でステッピングモータをいい感じに回す

## 概要
ESP8266 (以下、WROOM02) を使って、ステッピングモータを制御する。

## はじめに

年末に購入した <a rel="nofollow" href="http://www.amazon.co.jp/gp/product/B00UKXHKXC/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=B00UKXHKXC&linkCode=as2&tag=exabugs-22" target="_blank">Arduinoをはじめよう 互換キット UNO R3対応互換ボード 初心者専用実験キット 基本部品セット20 in 1</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=exabugs-22&l=as2&o=9&a=B00UKXHKXC" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> 。電子工作初心者の私は、ごく普通の回路設計をし、ごく普通にブレボーにジャンパー線を刺しました。でも、ただひとつ違っていたのは、UNO は未開封で… いきなり、WROOM02 だったのです。

今回は懐かしい「奥さまは魔女」風のオープニングにしてみましたが、実はこのキット、けっこう、モリモリに部品が盛り込まれていて、買ってすぐに脱落してました。そんな折、WROOM02 と出会い、「こ、こいつで、部品を一通り使ってみよう」と一大決心したわけです。

その長い旅路の、そろそろゴールが見えてきました。

あと残すところ、赤外線関連とLCDディスプレイ、そして、ステッピングモータです。

## 部品調達
ステッピングモータは「<a href="http://robocraft.ru/files/datasheet/28BYJ-48.pdf" target="_blank">28BYJ-48</a>」
![IMG_2405.JPG](https://qiita-image-store.s3.amazonaws.com/0/69410/5aa5a2b2-f584-b91c-35e7-dfd976e8e39d.jpeg)

仕様は以下になります

 - 4相
 - 1 / 64 に回転数を落としているモデルのようです。
 - 1回転は 360 / 5.625 x 64 = 4,096 ステップ (1-2相励磁)
 - 無負荷では 1,000Hz で脱調

|Key | Value|
|------------|-----|
|Rated voltage|5VDC|
|Number of Phase|4|
|Speed Variation Ratio|1/64|
|Stride Angle|5.625° / 64|
|Frequency|100Hz|
|DC resistance|50Ω±7%(25℃)|
|Idle In-traction Frequency| > 600Hz|
|Idle Out-traction Frequency| > 1000Hz|
|In-traction Torque| >34.3mN.m(120Hz)|
|Self-positioning Torque| >34.3mN.m|
|Friction torque| 600-1200 gf.cm|
|Pull in torque| 300 gf.cm|
|Insulated resistance| >10MΩ(500V)|
|Insulated electricity power| 600VAC/1mA/1s|
|Insulation grade| A|
|Rise in Temperature| <40K(120Hz)|
|Noise| <35dB(120Hz,No load,10cm)|
|Model| 28BYJ-48 – 5V |

##### 参考 Youtube
 - <a href="https://www.youtube.com/watch?v=14jF8umwJLI" target="_blank">トルクテスト</a>
 - <a href="https://www.youtube.com/watch?v=15K9N1yVnhc" target="_blank">分解</a>

もう一方の、基板のICには「ULN2003APG」と書かれています。
![20124283635160.jpg](https://qiita-image-store.s3.amazonaws.com/0/69410/cb3116b4-0d28-235e-9d7b-b5aaefeaefc7.jpeg)
こ、これは、<a href="http://qiita.com/exabugs/items/d53c5ad1bf7487af5024" target="_blank">ESP8266 (ESP-WROOM-02) で7セグメントLEDを制御する</a>で使った、反転型トランジスタアレイ(シンクドライバ)の7ch版ではないですか。フムフム。(データシートには「<a href="http://www.mouser.jp/ProductDetail/Toshiba/ULN2003APGCNHZA/?qs=4XvofpJG0dqmIyCLC%2FV9Qg%3D%3D" target="_blank">製造中止: メーカーにてモデル廃止予定のため、製造中止となります</a>」と書かれてます。そういうこともあるのですね。)
折角なので、この基板は使わないで、手持ちのトランジスタアレイ <a href="http://akizukidenshi.com/catalog/g/gI-09634/" target="_blank">TD62083APG</a> を使いましょう。

他には、8bitシフトレジスタ <a href="http://akizukidenshi.com/catalog/g/gI-08605/" target="_blank">SN74HC595N</a> と、温度センサー を使います。用途は後ほど。

## 回路

 - 4相それぞれに1ピンを直接割り当てるのではなく、シストレジスタを使って割り当ててます。
   - シフトレジスタ操作には4ピン必要なので、シフトレジスタ使わずに直接割り当てても同数のピンを消費しますねwww
 - A/D変換ピン(tout端子)に温度センサーをつないでいます。
   - tout端子の入力は 0V〜1,000mV なので、温度センサーの出力を1/3に降圧します。
 - ステッピングモータは、5V で駆動しています。


#### 回路図



![stepping.png](https://qiita-image-store.s3.amazonaws.com/0/69410/591d2f3f-caa9-609e-4acd-9f41db4ca753.png)




#### Eagle
https://github.com/exabugs/eagleSteppingMoter

## スケッチ

 - そのものズバリの「Stepper」ライブラリがありますが、挙動を理解したいので、今回は自前で作成してみます。
 - A/D変換ピン(tout)の値(0-1024)を、-RANGE〜+RANGE ステップにマッピング。
 - その位置になるように、ステッピングモータを回転させます。
 - つまり、ステッピングモータによる「アナログ温度計」的な動作になります。

```cpp
extern "C" {
#include <user_interface.h>   // A/D (system_adc_read) 用
}

#include <Ticker.h>

Ticker ticker;
Ticker moter;

#define SCK 14                // CLK       クロック信号出力ピン
#define RCK 12                // LOAD      ラッチ動作出力ピン
#define SI  13                // MOSI - DI データ信号出力ピン
#define CLR 15                // クリア

// 2相励磁 (4096ステップで2回転)
const int S = 4;
const int steps[S] = {
//  HGFEDCBA
  0b00001100,
  0b00000110,
  0b00000011,
  0b00001001
};

// 初期化
void setup() {
  Serial.begin(115200);
  Serial.println("");

  pinMode(SCK, OUTPUT) ;
  pinMode(RCK, OUTPUT) ;
  pinMode(SI,  OUTPUT) ;
  pinMode(CLR, OUTPUT) ;

  // 温度センサー
  // 1,000 msec 毎に取得
  ticker.attach_ms(1000, timer);

  // 回転速度調整
  // 回転速度遅すぎる(1/64)のでもっと高速に回転させたいが
  // 2 msec だと脱調することがある
  // 3 msec が限界だった
  moter.attach_ms(3, drive);
}

// ループ
void loop() {
  // Do nothing.
}

// シリアル出力
void out(int x) {
  digitalWrite(CLR, LOW); // クリア信号
  digitalWrite(CLR, HIGH);

  digitalWrite(RCK, LOW);
  shiftOut(SI, SCK, MSBFIRST, x);
  digitalWrite(RCK, HIGH); // ラッチ信号
}

const int RANGE = 1024;

volatile int P_ref = 0; // 目標位置
volatile int P_now = 0; // 現在位置

// A/Dセンサー値取得
void timer() {

  // A/Dセンサー値取得
  int tout = system_adc_read();
  Serial.println(tout);

  // A/Dセンサーの値(tout)を、-RANGE〜RANGE にマップする
  P_ref = map(tout, 0, 1024, -RANGE, RANGE);
}

// モータードライブ
void drive() {
  // 目標位置 と 現在位置 との 差
  int d = P_ref - P_now;

  if (d != 0) {

    // P_ref に近づくように ±1 する
    P_now += d / abs(d);

    Serial.print(" P_ref:");
    Serial.print(P_ref);
    Serial.print(" P_now:");
    Serial.print(P_now);

    // P_now にするには、どの方向に回せばよいか
    int i = (P_now + RANGE) % S;

    Serial.print( " i:");
    Serial.print(i);
    Serial.println("");
  
    // 1ステップ移動する
    out(steps[i]);

  } else {
    // 0を出力すれば電流が流れない
    out(0);
  }
}
```





## 動作確認
 - このモータで、2相励磁 で駆動する場合、4,096 ステップで 2回転 になります。
 - 無負荷での、脱調しない最高速は 1ステップ 3msec でした。360度回転するには 6.144秒 かかります。

センサー等なしで、周回テストしたい場合は、drive関数を適当に修正して下さい。
<例>

```cpp
void drive() {
  static int n = 0;
  n++;
  if (n < 4096) {
    // 2回転 (2相励磁)
    out(steps[n % S]);
  } else {
    out(0);
  }
}
```

![IMG_2417.JPG](https://qiita-image-store.s3.amazonaws.com/0/69410/030bcc33-3c00-766b-3951-c54a095bf6d6.jpeg)


## カスタマイズ

1相励磁 にしたい場合は、以下。

 - 4,096 ステップで 2回転
 - 消費電力は下がりますが、その分、トルクも下がります。

```cpp
const int S = 4;
const int steps[S] = {
//  HGFEDCBA
  0b00001000,
  0b00000100,
  0b00000010,
  0b00000001
};
```

1-2相励磁 にしたい場合は、以下。

 - 4,096 ステップで 1回転 (2倍のステップが必要) 
 - 回転が滑らかになりますが、そもそも、1 / 64 なので必要ないです。

```cpp
const int S = 8;
const int steps[S] = {
//  HGFEDCBA
  0b00001000,
  0b00001100,
  0b00000100,
  0b00000110,
  0b00000010,
  0b00000011,
  0b00000001,
  0b00001001
};
```

## まとめ
 - このモータは 1 / 64 に回転が落ちている(その分トルクは大きい)ので、とてもゆっくり回転します。
 - 静止時は(0を指定して)電流を切断しておけば、発熱もなく、理想的に静止します。
 - ステッピングモータは角度を正確に指定できますが、初期状態で自分自身の角度を知る手段が無い部分が悩みどころです。
 - サーボモータと比較して、使う場面が難しい気がします。<br><a href="http://qiita.com/exabugs/items/1c454d3d31d7561dcdd0" target="_blank">ESP8266 (ESP-WROOM-02) でサーボを制御する</a>
 - **静止時に電流を流さなくても良いので、スリープモードと相性が良さそう**です。
 - 目覚めた時に記憶が戻るよう、『掟上今日子の備忘録』的に、眠る直前の状態(角度)を保存しておく必要がありますが。
   - <a href="http://qiita.com/exabugs/items/2f67ae363a1387c8967c" target="_blank">ESP8266 (ESP-WROOM-02) でWi-Fi使用時のSSID/パスワードをブラウザで設定できるように</a>で使った、SPIFFS に保存しておけばよいでしょう。