2017年11月14日火曜日

CHIRIMEN RPi3でモータを回してみよう

はじめに

CHIRIMEN RPi3に入力装置を付けるだけでは面白くない!、今回は動くものをWeb画面から操作する方法を紹介いたします。

今回は魔改造レベル1です、既製品を分解したり電源コードをちょん切ったりします。

1.CHIRIMEN RPi3について

CHIRIMEN RPi3のインストール方法や基本的な利用方法については本家サイトを参照ください。
CHIRIMEN RPi3本家サイト: https://github.com/chirimen-oh/chirimen-raspi3


1-1.インストール方法
以下のページにインストール方法についての記載があります。
https://github.com/chirimen-oh/chirimen-raspi3/blob/master/env/setup-ja.md


とりあえず試してみたい方はビルド済みイメージがあります。
今回は10/19版イメージを利用しました。
https://drive.google.com/open?id=0B4k_ph7hQTADcVVlYzR6a0lqX3M

zipファイルを展開したものをmicroSDカードにddコマンドなどで書き込みを行うとすぐ使用することができます。

2.モーターを動かしてみよう

今回動かすものはDCブラシモーター(いわゆるマブチモーター)です。
モーター単品を回しても可愛くないのでファンシーUSB扇風機を回します。羽がぐるぐる回るのが分かりやすいのが便利だと思います。


この製品は単3乾電池3本もしくはUSBで動作することから、5V動作するモーターが入っているようです。

2-1.モーターを動かすための回路

モーターを動かすための回路はDRV8830というI2C制御モータードライバICを選びました。
基板にICが載っている基板が秋月電子さんで売られています。
http://akizukidenshi.com/catalog/g/gK-06489/

DVR8830というICはモーター駆動用の電源がI2C回路の電源になっている癖のあるICなので、RaspberryPiに接続するためにはレベル変換回路も必要となります。
こちらも基板に載っているものが秋月電子さんで売られています
http://akizukidenshi.com/catalog/g/gM-05452/

回路はこんな感じになります。

回路ではオレンジ色の「5V input」はUSBから取ることができます。
今回はモーターをPWM(パルス幅制御)で動かしますのでノイズが多少出ます。ノイズ対策としてモータの端子近くに1000pFのコンデンサーを付けておきましょう。コンデンサーはモータードライバ基板に付いています。

Tips: レベル変換ICについて 
RaspberryPiとセンサー類を繋ぐときにはRaspberryPiと同じ3.3Vで動作するものを繋げばよいのですが、モーター制御など異なる電圧で動くICと繋ぐときにはレベル変換ICが必要となります。

 
今回使用したPCA9306は低圧側(SCL1,SDA1)は1.0~5V、高圧側(SCL2,SDA2)は低圧側より高い電圧から5Vまでの範囲で使うことができます。DRV8830のようにモーター電圧の変動とともにI2C信号レベルが変動する回路にも追従可能です。
またPCA9306のEN端子が高圧側の電源に繋がっており、5V電源がダウンしてもRaspberryPi側のI2Cバスには影響がないようになっています。

2-2.回路を繋ごう

実際の回路はこんな感じになります。
電源の配線を間違えるとRaspberryPiが破壊されますので注意しましょう!
今回はUSB扇風機に付いていた電源コードをちょん切ってモータードライバ基板を挟み込みました。
 


秋月のI2Cレベル変換基板にはプルアップ抵抗が強め(1KΩ)に入っていますので基板上で切り離します。


扇風機からモータを取り出し端子間にコンデンサーを取り付けます。


2-3.ちょっと動作確認

アプリを入れる前に軽く動作確認してみましょう。画面上部にあるLXTerminalアイコンを押してターミナルを起動します。
sudo i2cdetect -y 1 と打ち込んで、出てきたリストに64があれば正しく接続できています。

pi@raspberrypi:~ $ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- 64 -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

何も出てこないときは、もう一度配線を見直してみましょう。

3.アプリとドライバ

今回作成したアプリとドライバは次の場所からダウンロード可能です。
https://drive.google.com/open?id=1DBbGVKgCQhHUAkcea-KMwvK4Yr1-xT08

ダウンロードしたファイルをCHIRIMEN RPi3のデスクトップにあるgcフォルダ上で展開してください。

3-1.ドライバコード(drivers/i2c-DRV8830.js)

今回は少しだけコード量が増えます。
var DRV8830 = function(i2cPort,slaveAddress){
  this.i2cPort = i2cPort;
  this.i2cSlave = null;
  this.slaveAddress = slaveAddress;
};

DRV8830.prototype = {
  sleep: function(ms){
    return new Promise((resolve)=>{setTimeout(resolve,ms);});
  },

  init: function(){
    return new Promise((resolve, reject)=>{
      this.i2cPort.open(this.slaveAddress).then((i2cSlave)=>{
        this.i2cSlave = i2cSlave;
        console.log("init ok:"+this.i2cSlave);
        resolve();
      },(err)=>{
        reject(err);
      });
    });
  },
  // write speed setting
  // 1to100 forward(%)
  // -1to-100 reverse(%)
  // 0 is free
  // over 100 or -100 --> motor breake
  write: function(speed) {
    return new Promise(async (resolve, reject)=>{
      if(this.i2cSlave == null){
        reject("i2cSlave Address does'nt yet open!");
      }else{
        var set_val;
        if ((speed <= 100) & (speed >= -100)) {
          if (speed ==  0) {
            set_val = 0; // free
          }
          if (speed > 0) {
            set_val = ((speed *63 /100) & 0x3f) << 2;
            set_val = set_val + 1; // forward
          }
          if (speed < 0) {
            set_val = ((speed *-63/100) & 0x3f) << 2;
            set_val = set_val + 2; // reverse
          }

          // Set Control
          await this.i2cSlave.write8(0x00, set_val & 0xff);
          await this.sleep(10);
          if (set_val ==0) {
            // clear falut
            await this.i2cSlave.write8(0x01, 0x1);
          }
        } else {
          // breake
          await this.i2cSlave.write8(0x00, 0x3);
          await this.sleep(10);
        }
      }
    });
  },
  // read  status
  status: function() {
    return new Promise(async (resolve, reject)=>{
      if(this.i2cSlave == null){
        reject("i2cSlave Address does'nt yet open!");
      }else{

        // READ status
        await this.i2cSlave.writeByte(0x1);
        this.i2cSlave.readBytes(1).then((v)=>{
          var status = v[0];
          var err = "Ready";
          if (status & 1) {
             err = "FAULT";
             if (status & 0x2) {
                 err = err + " OCP";
             }
             if (status & 0x4) {
                 err = err + " UVLO";
             }
             if (status & 0x8) {
                 err = err + " OTS";
             }
             if (status & 0x10) {
                 err = err + " LIMIT";
             }
          }
          resolve([status,err]);

       },(err)=>{
          reject(err);

       });

      }
    });
  }

};


ドライバコード内で重要な部分について解説いたします。

初期化 init

polyfillに対してI2Cの使用を宣言します。
  init: function(){
    return new Promise((resolve, reject)=>{
      this.i2cPort.open(this.slaveAddress).then((i2cSlave)=>{
        this.i2cSlave = i2cSlave;
        console.log("init ok:"+this.i2cSlave);
        resolve();
      },(err)=>{
        reject(err);
      });
    });
  },


設定書き込み write

モータ制御値を設定します。
第一引数がモータの速度、-100から100までの値を出力%として設定します。
1~100は正回転、-1~-100は逆回転です、0はフリー動作
それ以外の値はブレーキ動作となります。
  write: function(speed) {
    return new Promise(async (resolve, reject)=>{
      if(this.i2cSlave == null){
        reject("i2cSlave Address does'nt yet open!");
      }else{
        var set_val;
        if ((speed <= 100) & (speed >= -100)) {
          if (speed ==  0) {
            set_val = 0; // free
          }
          if (speed > 0) {
            set_val = ((speed *63 /100) & 0x3f) << 2;
            set_val = set_val + 1; // forward
          }
          if (speed < 0) {
            set_val = ((speed *-63/100) & 0x3f) << 2;
            set_val = set_val + 2; // reverse
          }

          // Set Control
          await this.i2cSlave.write8(0x00, set_val & 0xff);
          await this.sleep(10);
          if (set_val ==0) {
            // clear falut
            await this.i2cSlave.write8(0x01, 0x1);
          }
        } else {
          // breake
          await this.i2cSlave.write8(0x00, 0x3);
          await this.sleep(10);
        }
      }
    });
  },


モーター情報取得 status

モータードライバICの状態を取得します。
戻り値は2つあり、一つ目はモーター状態を文字列で戻します
Readyの文字列があれば正常に動作しています。エラーの場合はFAULTの文字列とともに次の文字列が返されます。
OCP 過電流保護
UVLO 低電圧検出
OTS モータードライバIC加熱
LIMIT 動作電流オーバー
FAULT状態はモータ制御値が0に戻ると解除されます。
二つ目の戻り値はモータ状態を数値で返します
  // read  status
  status: function() {
    return new Promise(async (resolve, reject)=>{
      if(this.i2cSlave == null){
        reject("i2cSlave Address does'nt yet open!");
      }else{

        // READ status
        await this.i2cSlave.writeByte(0x1);
        this.i2cSlave.readBytes(1).then((v)=>{
          var status = v[0];
          var err = "Ready";
          if (status & 1) {
             err = "FAULT";
             if (status & 0x2) {
                 err = err + " OCP";
             }
             if (status & 0x4) {
                 err = err + " UVLO";
             }
             if (status & 0x8) {
                 err = err + " OTS";
             }
             if (status & 0x10) {
                 err = err + " LIMIT";
             }
          }
          resolve([status,err]);

       },(err)=>{
          reject(err);
       });

      }
    });
  }


3-2.ブラウザアプリ(Javascript: i2c/i2c-DRV8830/main.js)

HTML表示の読み込み後に起動するコードです。
画面に操作用スライダーを表示します。スライダーにはjqueryを使っています。
'use strict';

window.addEventListener('load', function (){
  var head = document.querySelector('#head');

  navigator.requestI2CAccess().then((i2cAccess)=>{
    var port = i2cAccess.ports.get(1);
    var motor = new DRV8830(port,0x64);
    motor.init().then(()=>{
      setInterval(()=>{
        motor.status().then((values)=>{
          var stat = values[0];
          var err = values[1];
          document.getElementById("status").textContent = err + " stat:"+ stat;
        }).catch(function(reason) {
          console.log("READ ERROR:" + reason);
        });

      },100);

      $('#slider').slider({
        min: -100,
        max: 100,
        step: 2,
        value: 0,

        slide: function(e, ui) {
          var slider_val = ui.value;
          document.getElementById("speed").textContent = "Speed: " + slider_val+"%";
          motor.write(slider_val);
        },

        create: function(e, ui) {
          document.getElementById("speed").textContent = "Stop";
          motor.write(0);
        }
      });

    },(err)=>{
      console.log("DRV8830 init error");
    });

  }).catch(e=>{
     console.error('error', e)
  });

}, false);


アプリコード内で重要な部分について解説いたします。

I2C利用宣言

このJavascriptコードでI2Cを使う宣言を行います。
  navigator.requestI2CAccess().then((i2cAccess)=>{
.
.
.
  }).catch(e=> console.error('error', e));

I2Cバスのリソース取得とドライバ初期化

RaspberryPiのI2Cバスは1番目であるため1を設定します。またモータードライバICはI2Cアドレス0x64であるためドライバにI2Cアドレスを設定し初期化を行います
成功すればDRV8830を使用することができるようになります。
    var port = i2cAccess.ports.get(1);
    var motor = new DRV8830(port,0x64);
    motor.init().then(()=>{
.
.
.
.
    },(err)=>{
      console.log("DRV8830 init error");
    });


モータドライバ状態取得

100ms周期のインターバルタイマーを起動し、DRV8830ドライバから状態取得した値を表示します。
      setInterval(()=>{
        motor.status().then((values)=>{
          var stat = values[0];
          var err = values[1];
          document.getElementById("status").textContent = err + " stat:"+ stat;
        }).catch(function(reason) {
          console.log("READ ERROR:" + reason);
        });

      },100);


モーター制御

画面に操作用スライダーを表示します。スライダーにはjqueryを使っています。
操作範囲は-100から100までで初期値は0としています。 スライダー操作が行われたらDRV8830ドライバに設定を行っています。
      $('#slider').slider({
        min: -100,
        max: 100,
        step: 2,
        value: 0,

        slide: function(e, ui) {
          var slider_val = ui.value;
          document.getElementById("speed").textContent = "Speed: " + slider_val+"%";
          motor.write(slider_val);
        },

        create: function(e, ui) {
          document.getElementById("speed").textContent = "Stop";
          motor.write(0);
        }
      });


3-3.ブラウザアプリ(HTML: i2c/i2c-DRV8830/index.html)

お馴染みのHTMLコードです。id="speed"とid="status"の部分に設定値とモータードライバIC状態の文字列がセットされます。 また画面に操作用スライダーを表示します。スライダーにはjqueryを使っておりネットからダウンロードするようになっています。もしオフライン動作をしたい場合は、jqueryをダウンロードして同じフォルダに配置するとよいでしょう。
<!DOCTYPE html>
<html>
  <head>
    
    
    i2c-DRV8830
    <link type="text/css" rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/cupertino/jquery-ui.min.css" />
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
    <script type="text/javascript" src="http://code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
    
    
    
    

  </head>
  <body>
    DRV8830 motor driver kit


    
    
test
test
</body> </html>



4.動作させてみよう

デスクトップにあるgcフォルダを開き i2c/i2c_DRV8830フォルダまで移動します。
index.htmlをダブルクリックするとchromeブラウザが開き操作画面になります。
スライダーを左右に動かすとモーターのスピードや回転方向が変えられます。
今回のアプリはjqueryをダウンロードして使いますのでネット接続が必要となります。


モータによっては1~20%で回らないものがあります。これはモーターの特性や潤滑油の劣化によるものです。一度30%ぐらいで回し始めてから12%まで落とすとよいでしょう。


まとめ

実際にモーターのように動きがあるものが動作できるようになるとWebプログラムをモチベーションが上がりますよね。次回はさらに面白味のあるデバイスを使ってみる予定です。

0 件のコメント:

コメントを投稿