micro:bitでラジコン

作ってみる

スマホをコントローラにしてmicro:bitをラジコンにします。必要な部品はアマゾンで売ってました。便利ですね・・・というかこんなもの需要有るのでしょうか?使い道が思い浮かびません。

micro:bit

タイヤ付きギアードモーター

micro:bit用DCモータードライバボード

部品が届いたのでまずは組み立ててみます。タイヤを取り付けるケースがないのでレゴを使いました。

電源はモバイルバッテリをUSB接続で使います。電流のON→OFFの時に充電完了と判定されるようで、電流出力が停止されてしまいました。これを避けるため、無駄に電流を流すための200Ωの抵抗を追加しています。モバイルバッテリにもよると思いますが、100Ωくらいの方が良さそうでした。

USBのケーブルですが、モバイルバッテリに付属のケーブルを切って使いましたが、当然赤が+側だと思っていたら逆でした。モータードライバに電源の逆接続保護回路があったので助かりましたが、ちゃんと確認しましょう。(かなりあせった)

スマホ側Javascript

制御はJavaScriptで行います。スマホの加速度センサをタイマーで取得してmicro:bitにBLE-UARTで送信します。

前回調べたコードを流用しようと思ってたのですが、xとyの加速度を連続して送信すると、micro:bit側でUARTを受信できなくなる現象に遭遇しました。なかなかうまくいきません。パケットをウェイト無しで連続して送ると止まるようです。

この症状を回避するために、xとyの加速度を「/」区切りでまとめて送信することにしました。結果的にうまくいきました。コードを以下に掲載します。


<!DOCTYPE html><html><head><meta charset="utf-8" />
<title></title></head><body>

<form name="test">
<input type="button" value="接続" onclick="connect();"/>
<input type="button" value="切断" onclick="disconnect();"/>
</form>
<div id="x">x</p>
<div id="y">y</p>
<div id="z">z</p>
<script>
var bluetoothDevice;
var characteristic;
var x;
var y;
var z;

//接続
function connect() {
  // micro:bitを探す
  navigator.bluetooth.requestDevice( {acceptAllDevices: true, optionalServices:['6e400001-b5a3-f393-e0a9-e50e24dcca9e']} )
  .then(device => { bluetoothDevice = device; return device.gatt.connect(); })
  .then(server => { return server.getPrimaryService ('6e400001-b5a3-f393-e0a9-e50e24dcca9e'); }) // UART
  .then(service => { return service.getCharacteristic('6e400003-b5a3-f393-e0a9-e50e24dcca9e'); }) // microbitのRX
  .then(chara  => { characteristic = chara; alert("接続"); })  
  .catch(error => { alert("接続失敗"); });    
}

//切断
function disconnect() {
  if (!bluetoothDevice || !bluetoothDevice.gatt.connected) return ;
  bluetoothDevice.gatt.disconnect();
  bluetoothDevice=0;
  alert("切断しました")
}

//タイマー
function frame() {
  // 加速度を取得
  document.getElementById("x").firstChild.nodeValue = x.toFixed();
  document.getElementById("y").firstChild.nodeValue = y.toFixed();

  if (!bluetoothDevice || !bluetoothDevice.gatt.connected || !characteristic) return ;
  characteristic.writeValue(new Uint8Array(new TextEncoder().encode(x.toFixed()+"/"+y.toFixed()+",")));
  document.getElementById("z").firstChild.nodeValue = x.toFixed()+"/"+y.toFixed()+",";
}
setInterval(frame, 100);


// DeviceMotion Event
window.addEventListener('devicemotion', Handler);

// 加速度が変化
function Handler(event) {

  // 加速度を取得 -100~100に変換
  var vec3d = event.accelerationIncludingGravity;
  x = vec3d.x*10;
  y = vec3d.y*10;
//  z = vec3d.z*10;
  if (x < -100) {x=-100;}
  if (x >  100) {x= 100;}
  if (y < -100) {y=-100;}
  if (y >  100) {y= 100;}
}


</script>
</body></html>

スマホの加速度はdevicemotionを使用しています。-10~10の少数有の値が取れるようなので、10倍して-100~100の少数無しでmicro:bitに送信するようにしています。

micro:bit側Javascript

ちょっと長くなったので、ブロックエディタでは無く、JavaScriptで掲載します。

コピペして「ブロック」を押すとブロックに変換できるので、JavaScriptが読めない人は変換してみてください。


let r = 0
let l = 0
let b = 0
let a = 0
let y = 0
let j = 0
let x = 0
let i = 0
let str = ""
// コンマまで受信する。受信後/で分割し、xとy数値化して格納する
bluetooth.onUartDataReceived(serial.delimiters(Delimiters.Comma), function () {
    str = bluetooth.uartReadUntil(serial.delimiters(Delimiters.Comma))
    i = 0
    for (let i0 = 0; i0 < str.length; i0++) {
        if (str.substr(i, 1) == "/") {
            j = i
        }
        i += 1
    }
    x = parseFloat(str.substr(0, j))
    y = parseFloat(str.substr(j + 1, 20))
})
bluetooth.startUartService()
// モータードライバの制御端子をPWNに設定する
pins.analogSetPeriod(AnalogPin.P8, 20000)
pins.analogSetPeriod(AnalogPin.P12, 20000)
pins.analogSetPeriod(AnalogPin.P0, 20000)
pins.analogSetPeriod(AnalogPin.P16, 20000)

// 周期処理
basic.forever(function () {
    basic.pause(200)
    if (x < -10) {
        led.plot(1, 2)
    } else {
        led.unplot(1, 2)
    }
    if (x < -50) {
        led.plot(0, 2)
    } else {
        led.unplot(0, 2)
    }
    if (x > 10) {
        led.plot(3, 2)
    } else {
        led.unplot(3, 2)
    }
    if (x > 50) {
        led.plot(4, 2)
    } else {
        led.unplot(4, 2)
    }
    if (y < -10) {
        led.plot(2, 1)
    } else {
        led.unplot(2, 1)
    }
    if (y < -50) {
        led.plot(2, 0)
    } else {
        led.unplot(2, 0)
    }
    if (y > 10) {
        led.plot(2, 3)
    } else {
        led.unplot(2, 3)
    }
    if (y > 50) {
        led.plot(2, 4)
    } else {
        led.unplot(2, 4)
    }
    led.toggle(2, 2)
    a = x
    b = y
    if (a < 0) {
        a = a * -1
    }
    if (b < 0) {
        b = b * -1
    }
    if (x < 0) {
        l = b - 0
        r = b - a
    } else {
        l = b - a
        r = b - 0
    }
    l = Math.constrain(l, 0, 100)
    r = Math.constrain(r, 0, 100)
    l = pins.map(
    l,
    0,
    100,
    0,
    20000
    )
    r = pins.map(
    r,
    0,
    100,
    0,
    20000
    )
    if (y < 0) {
        pins.servoSetPulse(AnalogPin.P8, l)
        pins.servoSetPulse(AnalogPin.P12, 0)
        pins.servoSetPulse(AnalogPin.P0, r)
        pins.servoSetPulse(AnalogPin.P16, 0)
    } else {
        pins.servoSetPulse(AnalogPin.P8, 0)
        pins.servoSetPulse(AnalogPin.P12, l)
        pins.servoSetPulse(AnalogPin.P0, 0)
        pins.servoSetPulse(AnalogPin.P16, r)
    }
})

今回使用したモータードライバボードにはTIのDRV8833が載っていました。モーターの逆起電力とかの処置をしてくれる物なので、めんどくさいことは考えなくてよさそうです。

モーター1はP8=1 P12=0で前進、P8=0 P12=1で後退です。モーター2は。P0=1 P16=0で前進、P0=0 P16=1で後退です。1を設定している方をPWM化するとスピードが調整できます。

スマホから送られてきた-100~100の値を PWMの0~20000usecにマッピングしてPWM出力の設定を行っています。

動かしてみる

こんな感じで動きます。PWMもちゃんと効いてます。

micro:bitの府に気はだいぶ分かったのでまた何か作ってみようかと思います。今度は飛ぶやつがいいかなぁ。