obniz Boardで4台のDC Motorを直接ドライブする方法(4WDメカナムホイール車)

#■物語の始まり、想定外のエラーに遭遇
obniz Boadって1つの端子で電流1Aまでとれるので、DC Motorも直接ドライブできてとても便利ですよね。ライブラリも用意されていて、正転も逆転もスピードも簡単にコントロールできるんですよね。
ということで、DC Motor4台をobnizに直接接続してライブラリを使ってメカナムホイール車を走らせるべく、次のようなコードを書きました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234533/47d43577-c139-37d3-d704-2543ad993fe9.png)
ところが、下記のエラーで拒否られてしまいました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234533/191a5956-957c-414f-fc08-4d41583c3e44.png)

#■ライブラリで制御できるDC Motorは3台まで!!
obnizのDC MotorライブラリはPWMで動いていて、**1台のDC Motorで正転と逆転でそれぞれ1つづつ計2つのPWMを占有**するようです。obnizの**PWMの総数は6個というH/W制約**があるため、**ライブラリで制御できるDC Motorは3台まで**ということになります。よって、4台目を宣言すると上記のエラーになるわけです。
#■4台の直刺しDC Motorを制御する代替Tips
#正転と逆転で動的に+/-のピンアサインを変更することで、1台のDC Motorを1つのPWMで制御します。
具体的には、+側にしたいピンにPWMを割り当て、-側にしたいピンをoutputでfalseにします。PWMのduty比を変える事でスピードを指定します。これにより4台のDC Motorを接続した場合でも、使用するPWMは4つで済みPWM最大6っまでという制約内で動作させることがてきます。
(原理的には、この方式で最大6台までのDC Motorを直刺しで制御できます。)

##以下0番ピンにDC Motorの-を、1番ピンに+を接続し、スピード50%で制御する場合のコード例です。pwm番号は、DC Motor毎に変えます。
##正転
```javascript
await obniz.io0.output(false);
await obniz.pwm0.start({io:1});await obniz.pwm0.duty(50);
```
##逆転
```javascript
await obniz.io1.output(false);
await obniz.pwm0.start({io:0});await obniz.pwm0.duty(50);
```
##停止
```javascript
await obniz.pwm0.end();  
```
#■(参考)4WDメカナムホイール車への応用例
##動画
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">とある制約で意外とできそうでできない <a href="https://twitter.com/hashtag/obniz?src=hash&amp;ref_src=twsrc%5Etfw">#obniz</a> にDC Motor 4台直刺しての制御、TipsとしてQiita投稿準備中。メカナムホイール 4WD Carに応用してみた。 <a href="https://t.co/hL8ZowalK4">pic.twitter.com/hL8ZowalK4</a></p>&mdash; Google Homer (@google_homer_) <a href="https://twitter.com/google_homer_/status/1192259721211678721?ref_src=twsrc%5Etfw">November 7, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

##車体
![WIN_20191107_12_02_46_Pro.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234533/4c54caa4-ee10-7f3e-de3c-1870409cdaee.jpeg)

![WIN_20191107_12_01_08_Pro.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234533/2a9fd17b-26e8-2bb6-3a0e-26a825a372cf.jpeg)

##コード
```html
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
    <script src="https://unpkg.com/obniz@2.4.0/obniz.js" crossorigin="anonymous"></script>
   </head>
<body>
  <br>
  <font size="5" color="#000000">
  メカナムカー 4WD Direct
  </font>
  <br><br>
  <button id="FORWARD_LEFT"  class="btn btn-warning" style="width:30%;height:50px;font-size:30px;">↖</button>
  <button id="FORWARD"       class="btn btn-warning" style="width:30%;height:50px;font-size:30px;">↑</button>
  <button id="FORWARD_RIGHT" class="btn btn-warning" style="width:30%;height:50px;font-size:30px;">↗</button>
  <br><br>
  <button id="LEFT"          class="btn btn-warning" style="width:30%;height:50px;font-size:30px;">←</button>
  <button id="STOP"          class="btn btn-warning" style="width:30%;height:50px;font-size:30px;">停止</button>  
  <button id="RIGHT"         class="btn btn-warning" style="width:30%;height:50px;font-size:30px;">→</button>
  <br><br>  
  <button id="BACK_LEFT"     class="btn btn-warning" style="width:30%;height:50px;font-size:30px;">↙</button>  
  <button id="BACK"          class="btn btn-warning" style="width:30%;height:50px;font-size:30px;">↓</button>
  <button id="BACK_RIGHT"    class="btn btn-warning" style="width:30%;height:50px;font-size:30px;">↘</button>
  <br><br> 
  <button id="TURN_LEFT"     class="btn btn-warning" style="width:30%;height:50px;font-size:30px;">↰</button>
  <button id="NULL2"         class="btn btn-warning" style="width:30%;height:50px;font-size:30px;"></button>  
  <button id="TURN_RIGHT"    class="btn btn-warning" style="width:30%;height:50px;font-size:30px;">↱</button>
  
<script>
const SP =50;   //走行スピード、上げ過ぎるとLow batteryになるので注意
var obniz = new Obniz("OBNIZ_ID_HERE"); 
obniz.onconnect = async function () {
   /**************/
   /* BUTTON操作 */
   /**************/
   //前進
   $("#FORWARD").on('touchstart mousedown',      async ()=> { await FORWARD(SP);       })
   $("#FORWARD").on('touchend mouseup',          async ()=> { await STOP();            })
   //左前
   $("#FORWARD_LEFT").on('touchstart mousedown', async ()=> { await FORWARD_LEFT(SP);  })
   $("#FORWARD_LEFT").on('touchend mouseup',     async ()=> { await STOP();            })
   //右前
   $("#FORWARD_RIGHT").on('touchstart mousedown',async ()=> { await FORWARD_RIGHT(SP); })      
   $("#FORWARD_RIGHT").on('touchend mouseup',    async ()=> { await STOP();            })
   //左横
   $("#LEFT").on('touchstart mousedown',         async ()=> { await LEFT(SP);          })
   $("#LEFT").on('touchend mouseup',             async ()=> { await STOP();            })
   //右横
   $("#RIGHT").on('touchstart mousedown',        async ()=> { await RIGHT(SP);         })
   $("#RIGHT").on('touchend mouseup',            async ()=> { await STOP();            })
   //左回転
   $("#TURN_LEFT").on('touchstart mousedown',    async ()=> { await TURN_LEFT(SP);     })
   $("#TURN_LEFT").on('touchend mouseup',        async ()=> { await STOP();            })
   //右回転
   $("#TURN_RIGHT").on('touchstart mousedown',   async ()=> { await TURN_RIGHT(SP);    })  
   $("#TURN_RIGHT").on('touchend mouseup',       async ()=> { await STOP();            })
   //左後
   $("#BACK_LEFT").on('touchstart mousedown',    async ()=> { await BACK_LEFT(SP);     })
   $("#BACK_LEFT").on('touchend mouseup',        async ()=> { await STOP();            })
   //右後
   $("#BACK_RIGHT").on('touchstart mousedown',   async ()=> { await BACK_RIGHT(SP);    })     
   $("#BACK_RIGHT").on('touchend mouseup',       async ()=> { await STOP();            })
   //後進
   $("#BACK").on('touchstart mousedown',         async ()=> { await BACK(SP);          })
   $("#BACK").on('touchend mouseup',             async ()=> { await STOP();            })
   //停止
   $("#STOP").on('touchend mouseup',             async ()=> { await STOP();            })

   /*******************/
   /* Motion Controll */
   /*******************/
   async function STOP(){
      await MOVE( "  " , "  " ,
                  "  " , "  " );
   }
   async function FORWARD(speed){
      await MOVE( "↑" , "↑" ,
                  "↑" , "↑" , speed );
   }  
   async function BACK(speed){
      await MOVE( "↓" , "↓" ,
                  "↓" , "↓" , speed );
   }    
   async function LEFT(speed){
      await MOVE( "↓" , "↑" ,
                  "↑" , "↓" , speed );     
   }      
   async function RIGHT(speed){
      await MOVE( "↑" , "↓" ,
                  "↓" , "↑" , speed );     
   } 
   async function TURN_LEFT(speed){
      await MOVE( "↓" , "↑" ,
                  "↓" , "↑" , speed );    
   }          
   async function TURN_RIGHT(speed){
      await MOVE( "↑" , "↓" ,
                  "↑" , "↓" , speed );      
   }
   async function FORWARD_LEFT(speed){
      await MOVE( "  " , "↑" ,
                  "↑" , "  " , speed );     
   }
   async function FORWARD_RIGHT(speed){
      await MOVE( "↑" , "  " ,
                  "  " , "↑" , speed );     
   }
   async function BACK_LEFT(speed){
      await MOVE( "↓" , "  " ,
                  "  " , "↓" , speed );     
   }
   async function BACK_RIGHT(speed){
      await MOVE( "  " , "↓" ,
                  "↓" , "  " , speed );   
   }
   async function MOVE( FL,FR,
                        BL,BR,speed ){
      if ( BR == "↑" ) { await obniz.io0.output(false);await obniz.pwm0.start({io:1});await obniz.pwm0.duty(speed); }     
      if ( BR == "↓" ) { await obniz.io1.output(false);await obniz.pwm0.start({io:0});await obniz.pwm0.duty(speed); }  
      if ( BR == "  " ) { await obniz.pwm0.end(); }
     
      if ( BL == "↑" ) { await obniz.io2.output(false);await obniz.pwm1.start({io:3});await obniz.pwm1.duty(speed); }     
      if ( BL == "↓" ) { await obniz.io3.output(false);await obniz.pwm1.start({io:2});await obniz.pwm1.duty(speed); }
      if ( BL == "  " ) { await obniz.pwm1.end(); }     

      if ( FR == "↑" ) { await obniz.io4.output(false);await obniz.pwm2.start({io:5});await obniz.pwm2.duty(speed); }
      if ( FR == "↓" ) { await obniz.io5.output(false);await obniz.pwm2.start({io:4});await obniz.pwm2.duty(speed); }
      if ( FR == "  " ) { await obniz.pwm2.end(); }      

      if ( FL == "↑" ) { await obniz.io6.output(false);await obniz.pwm3.start({io:7});await obniz.pwm3.duty(speed); }
      if ( FL == "↓" ) { await obniz.io7.output(false);await obniz.pwm3.start({io:6});await obniz.pwm3.duty(speed); }  
      if ( FL == "  " ) { await obniz.pwm3.end(); }
   }

}
</script>
</body>
</html>
```

#■まとめ
DC Motorを直刺しできるのはobniz Boardの大きなアドバンテージなので、これを最大限生かす意味で、PWM制約のMAX6台まで直刺しで制御できるようDC Motorライブラリー自体を更新してくれたらうれしいですね。(電源が耐えられないような気もするが。。。)