Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
.NET対応組み込みデバイス「Netduino」入門(11)

.NET対応組み込みデバイス「Netduino」入門(11)

Bluetoothで遠隔操作しよう

2015年9月17日

BluetoothでPCと連携し、電源もバッテリー給電とすることで、移動体などへの搭載も可能なコードレス仕様のDCモーター制御を実現してみよう。

Microsoft MVP for Windows Platform Development 初音 玲
  • このエントリーをはてなブックマークに追加

 前回はDCモーターの制御を取り上げたが、制御といってもあらかじめプログラミングしたとおりに回転数を制御するだけだった。今回は、Netduinoデバイス上にBluetoothデバイスを搭載して、PC(Bluetoothデバイス搭載)側のUWP(Universal Windows Platform)アプリからBluetooth経由でのDCモーター制御にチャレンジする。

Bluetooth接続について

 NetduinoでBluetooth通信を行う方法はいくつかあるが、今回はランニングエレクトロニクスのSBDBTを使ってBluetooth通信シリアル通信に変換して接続する方法を使う。

 SBDBTの特徴は、USBコネクターにBluetoothドングルを挿すことで、シリアル接続によりPCとBluetooth通信が行える点である。また、SBDBT自体が小型のマイコンで、出荷時にはBTstack(=公式Bluetoothスタックの一実装)を使用したBluetooth用のファームウェアが書き込まれているが、ファームウェアを書き換えることで、さまざまな用途に転用できる点も面白い。例えば今回のサンプルでは、初期出荷のBluetooth用ファームウェア(正確にはBluetooth SSPサーバー用ファームウェア)を使用したが、Bluetooth Low Energy(以下、BLE)用のファームウェアに書き換えてBLE対応のドングルを使うことで、より消費電力の少ないBLE接続を行うことも可能だ。

SBDBTのスペック

項目SBDBTSBDBT 5V
CPU PIC24FJ64GB004T-I/PT PIC24FJ64GB004T-I/PT
動作周波数 32MHz 32MHz
入力電圧 2.55~3.6V 2.6~5.5V
消費電流 30mA 30mA
USB電圧 3.3V 5.0V
表1 SBDBTスペック表

SBDBTのピンについて

 SBDBTの各ピンの意味をデータシートからピックアップした結果が次のようだ。

ピン用途接続先
1 - -
2 VDD 3.3V
3 GND GND
4 - -
5 - -
6 RTS CTSと直結
7 TX デジタル0番ピン(RX受信)
8 RX デジタル1番ピン(TX送信)
9 CTS RTSと直結
10 - -
表2 SBDBTのピン接続

 SBDBTは3.3V駆動であり、接続するBluetoothドングルも3.3V駆動が前提だ。PC用のBluetoothドングルのスペックは5Vだが、SBDBTのユーザーマニュアルには3.3V駆動で動作確認済みのBluetoothドングルが掲載されている。今回はその中から、PLANEX BT-Micro4を使ってみた。

SBDBTを使った回路例

 前回のMP4207の回路が組んであるブレッドボードにSBDBTのピンに合わせて配線を行うと次のようになる。

図1 SBDBT配線例

 今回もモーター部分についてはプラレール車両に搭載されているFA130モーターに対してブレッドボートを経由してMP4207の出力ピンを接続する。

図2 SBDBT接続例

SBDBT制御用ソースコード

 プロジェクトの作成方法は、これまでと同様なので割愛する(第3回などを参考にされたい。本稿の例では、プロジェクト名はVB.NET用は「SBDBTMotorVB」、C#用は「SBDBTMotorCS」とした)。もし、Visual Studio 2015をお使いの場合は番外コラム01も参照にしてほしい。

 MP4207を使うので、PWM出力用クラスライブラリはSecretLabs.NETMF.Hardware.PWMライブラリを使用する。そのため、参照設定で「SecretLabs.NETMF.Hardware.PWM」ライブラリの参照を削除し、「SecretLabs.NETMF.Hardware.PWM」ライブラリの参照を追加する必要がある。

 このサンプルのコードは次のとおりだ。

Visual Basic
Imports System.IO.Ports
Imports Microsoft.SPOT.Hardware
Imports SecretLabs.NETMF.Hardware
Imports SecretLabs.NETMF.Hardware.Netduino

Module Module1
  Private MotorPower As UInteger = 0
  Sub Main()
    Dim power = New PWM(Pins.GPIO_PIN_D9)
    Dim serial = New SerialPort("COM1", 9800, Parity.None, 8, StopBits.One)
    Dim motorIn8 = New OutputPort(Pins.GPIO_PIN_D8, False)

    AddHandler Serial.DataReceived, AddressOf serial_DataReceived
    motorIn8.Write(True)
    Do While (True)
      Try
        Dim value = MotorPower
        If (Not Serial.IsOpen) Then
          Serial.Open()
        End If
        Power.SetPulse(20000, value)
        Thread.Sleep(1000)
      Catch ex As Exception
        Dim mes = ex.Message
      End Try
    Loop
  End Sub

  Private Sub serial_DataReceived(sender As Object, e As SerialDataReceivedEventArgs)
    Try
      Dim serial = CType(sender, SerialPort)
      Dim data(10) As Byte
      Dim cmd = serial.Read(data, 0, data.Length)

      MotorPower = CType(CType(data(0), UInteger) * 100, UInteger)
      If (MotorPower > 20000) Then
        MotorPower = 20000
      End If
    Catch ex As Exception
      Dim mes = ex.Message
    End Try
  End Sub
End Module

Namespace Global.System
  <AttributeUsageAttribute(AttributeTargets.Method)>
  Public NotInheritable Class STAThreadAttribute
    Inherits Attribute

  End Class
End Namespace
C#
using System.IO.Ports;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;
using System.Threading;
using System;

namespace SBDBTMotorCS
{
  public class Program
  {
    private static uint MotorPower = 0;
    public static void Main()
    {
      var serial = new SerialPort("COM1", 9800, Parity.None, 8, StopBits.One);
      var power = new PWM(Pins.GPIO_PIN_D9);
      var motorIn8 = new OutputPort(Pins.GPIO_PIN_D8, false);

      serial.DataReceived += Serial_DataReceived;
      motorIn8.Write(true);
      while (true)
      {
        try
        {
          var value = MotorPower;
          if (!serial.IsOpen)
          {
            serial.Open();
          }
          power.SetPulse(20000, value);
          Thread.Sleep(1000);
        }
        catch (Exception ex)
        {
          var mes = ex.Message;
        }
      }
    }

    private static void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
      try
      {
        var serial = (SerialPort)sender;
        var data = new byte[10];
        var cmd = serial.Read(data, 0, data.Length);

        MotorPower = (uint)((uint)data[0] * 100);
        if (MotorPower > 20000)
        {
          MotorPower = 20000;
        }
      }
      catch (Exception ex)
      {
        var mes = ex.Message;
      }
    }
  }
}
リスト1 SBDBT制御コード(上:Module.vb、下:Program.cs)より抜粋

 今回のサンプルではデジタル0番ピンとデジタル1番ピンを使っているので、シリアルポートはCOM1になる。

PC側コード

 PC側のアプリはUWPアプリとして作成する。Windows 10やWindows 10 MobileなどUniversal Windows Platformが稼働するデバイスでBluetooth通信ができるものならば大きさは問わず動くはずだ。作成方法やコードの全体像は割愛するが、Bluetoothでデータを送信する部分は次のようになる。

Visual Basic
Public Async Function Connect() As Task
  Dim name = Common.Settings.DeviceName
  If (name IsNot Nothing) Then
    ' 保存されたBluetoothデバイス名と一致するデバイス情報を取得しデータを送信する
    Dim serviceInfos = Await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort))
    For Each serviceInfo In serviceInfos
      If (serviceInfo.Name = name) Then
        Await Connect(serviceInfo)
        Exit For
      End If
    Next
  End If
End Function

Public Async Function Connect(serviceInfo As DeviceInformation) As Task
  Try
    ' 指定されたデバイス情報で接続を行う
    If (DeviceService Is Nothing) Then
      DeviceService = Await RfcommDeviceService.FromIdAsync(serviceInfo.Id)
      BtSocket = New StreamSocket()
      Await BtSocket.ConnectAsync(
        Me.DeviceService.ConnectionHostName,
        Me.DeviceService.ConnectionServiceName,
        SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication)
      Writer = New DataWriter(BtSocket.OutputStream)
      Me.Message = "Connected " + DeviceService.ConnectionHostName.DisplayName
    End If
    ' 接続されたBluetoothデバイスにデータを送信する
    SetPower(Me.Power)
  Catch ex As Exception
    Me.Message = ex.Message
    DeviceService = Nothing
  End Try
End Function

Private Async Sub SetPower(power As Integer?)
  Try
    If (DeviceService IsNot Nothing) Then
      ' 0~200の値を送信する
      Dim moterPower = If(power <= 200, power, 200)
      Dim byteArray() As Byte = {CType(moterPower, Byte)}
      Writer.WriteBytes(byteArray)
      Dim sendResult = Await Writer.StoreAsync()
    End If
  Catch ex As Exception
    Me.Message = ex.Message
    DeviceService = Nothing
  End Try
End Sub
C#
public async Task Connect()
{
  var name = Common.Settings.DeviceName;
  if (name != null)
  {
    // 保存されたBluetoothデバイス名と一致するデバイス情報を取得しデータを送信する
    var serviceInfos = await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort));
    foreach (var serviceInfo in serviceInfos)
    {
      if (serviceInfo.Name == name)
      {
        await Connect(serviceInfo);
        break;
      }
    }
  }
}

private async Task Connect(DeviceInformation serviceInfo)
{
  try
  {
    // 指定されたデバイス情報で接続を行う
    if (DeviceService == null)
    {
      DeviceService = await RfcommDeviceService.FromIdAsync(serviceInfo.Id);
      BtSocket = new StreamSocket();
      await BtSocket.ConnectAsync(
        this.DeviceService.ConnectionHostName,
        this.DeviceService.ConnectionServiceName,
        SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
      Writer = new DataWriter(BtSocket.OutputStream);
      this.Message = "Connected " + DeviceService.ConnectionHostName.DisplayName;
    }
    // 接続されたBluetoothデバイスにデータを送信する
    SetPower(this.Power);
  }
  catch (Exception ex)
  {
    this.Message = ex.Message;
    DeviceService = null;
  }
}

private async void SetPower(int? power)
{
  try
  {
    if (DeviceService != null)
    {
      // 0~200の値を送信する
      var moterPower = (power <= 200 ? power : 200);
      byte[] byteArray = new byte[] { (byte)moterPower };
      Writer.WriteBytes(byteArray);
      var sendResult = await Writer.StoreAsync();
    }
  }
  catch (Exception ex)
  {
    this.Message = ex.Message;
    DeviceService = null;
  }
}
リスト2 PC側コード(上:BleModel.vb、下:BleModel.cs)より抜粋

アプリ実行の前準備

 本サンプルを実行するには、まず、Netduino側サンプルを実行しておく。このとき、通信が正しく行われるかが判断しやすいように、USBでNetduinoに接続し、Netduino側コードのSerial_DataReceivedプロシージャの先頭にブレークポイントを設定した上でNetduino側サンプルをデバッグ実行しておくとよいだろう。

 Netduino側が動いたら、Windows 10の[スタート]メニューの[設定]から[デバイス]-[Bluetooth]の設定で、NetduinoのBluetoothデバイスとペアリングする。SBDBTを使っている場合、デバイス名は必ず「SBDBT-」から始まっているので識別しやすい(図3)。また、パスコードは「0000」となる。

図3 Bluetoothデバイスのペアリング

アプリ実行

 ペアリングが終わったらWindows側サンプルを実行する。

 画面上のBluetoothデバイス名にはペアリングしたデバイス名を指定する(図4)。ボタンをクリックすれば、Bluetooth通信が行われ、ボタンに応じてNetduino側のモーター回転数が変化する。

 なお、最初に通信が行われるときにアクセス許可の画面が表示されるので「はい」をクリックする。

図4 Windows側アプリの実行

【動画】Bluetooth制御でのモーター制御

まとめ

 今回はPCからNetduino側にデータを送信していたが、Netduino側からPCにデータ送信するのも同様に簡単で、Readメソッドの代わりにWriteメソッドを実行すればよい。

 このようにSBDBTのおかげでNetduino側ではBluetooth通信ということを全く意識せずにPCと接続することができた。

 次回は、littleBitsとNetduinoを組み合わせて、ブレッドボード以上に簡単な回路作成にチャレンジしてみたい。本連載で使用する予定にしているのは、littleBits_Base Kitに加えて、追加購入が必要な2個のPROTOモジュールだ(あくまで予定であり、実際の記事では異なるパーツが必要になる場合がある点をご了承いただきたい)。

 なお、今回、littleBitsの代理店であるゼッタリンクスさんのご厚意により、2015年12月31日までの期間限定で何回でも利用可能なBuild Insider読者専用の割引クーポンを提供していただけた。ゼッタリンクス・オンラインショップの[お支払方法の設定]画面でクーポンコード「Build_Insider」を入力すれば、オンラインショップ内の全製品が8%割引となるので、ぜひご利用いただきたい。

※以下では、本稿の前後を合わせて5回分(第10回~第14回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

.NET対応組み込みデバイス「Netduino」入門(11)
10. Windows 10+Visual Studio 2015でNetduinoアプリを動かすには?

本シリーズのNetduinoサンプルをWindows 10+Visual Studio 2015で動かす方法と注意点を番外編のコラムとしてまとめた。

.NET対応組み込みデバイス「Netduino」入門(11)
11. NetduinoでDCモーターをコントロールしよう

ミニ四駆やプラレールに使われるなど、電池やバッテリーを動力源にできる「DCモーター」を、Netduinoから制御する方法を解説する。

.NET対応組み込みデバイス「Netduino」入門(11)
12. 【現在、表示中】≫ Bluetoothで遠隔操作しよう

BluetoothでPCと連携し、電源もバッテリー給電とすることで、移動体などへの搭載も可能なコードレス仕様のDCモーター制御を実現してみよう。

.NET対応組み込みデバイス「Netduino」入門(11)
13. littleBitsではんだ付けなしの電子工作を実現

磁石で電子回路をつないで電子工作が行えるlittleBits。Netduinoと組み合わせると、どのような電子工作が実現できるのか? その可能性を探る。

.NET対応組み込みデバイス「Netduino」入門(11)
14. MESHとNetduinoでiOSデバイスとの連携を実現

SONYのスマートDIYキット「MESH」とは? さまざまな連携が実現可能なMESHを使って、NetduinoとiPadを連携をさせてみよう。

サイトからのお知らせ

Twitterでつぶやこう!