前回から,MINDSTORMS ROBOTICS INVENTION SYSTEM(RIS1.0)に付属しているSpirit.ocxというActiveXコントロールを使ってExcelのVBA(Visual Basic for Applications)でプログラミングを行っている。前回はキー操作で車を動かすリモートコントロールプログラムを作った。今回はMind Stormsの本体RCXにダウンロードして実行するマルチタスクのプログラムを作ってみたい。

 なぜか近頃,私はMindStormsがやりたくてたまらないのだ。かずとこうしろうは,「何をしているんだろう」と少し距離をおいて見ているだけだ。ほのちゃんは私と一緒に,小さなものを作っては「さくひん,さくひん。とっといてね。」と,壊すわけにはいかない,なんだかわからないものを大量生産している。

 MindStormsには,標準で2種類のセンサーが付いている,何かにぶつかったことを検知するタッチセンサーと,光の反射を読み取り,暗い明るいの判断をするライトセンサーである。RCXはIRタワーと通信するために赤外線の入出力機能を持っている。赤外線の出力機能とライトセンサーを組み合わせると,ぶつかる前に障害物をよける接近センサーが作れる。赤外線を定期的に出力し,反射する光の値をチェックして大きな変化が現れたら,何かに近づいたと判断し,ぶつかる前に回避することができるのだ。



 お掃除ロボットの作りかけを改造して,赤外線の反射をライトセンサーで読み取るようにした。



 ライトセンサーを支えているのは2人の「レゴのおじちゃん」である。このロボットを見て,こうしろうは「細かいところに,こっとるね」といかにもレゴ好きらしいコメントをした。かずは「部品探すが面倒だっただけやろ」と冷たいが的確なコメントを残した。

 Spirit.ocxを使って,VBAでプログラムを作ると,RCXがマルチタスクで動くことが実感できる。



 まずはユーザーフォーム(UserForm)を作りDownLoadボタンでプログラムをRCXにダウンロードできるようにする。
-------------------------------------------------------------------
Private Sub UserForm_Activate()
  Spirit1.InitComm
End Sub

-------------------------------------------------------------------
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
  Spirit1.CloseComm
End Sub

-------------------------------------------------------------------
 ユーザーフォーム(UserForm)のActivate時にシリアルポートを初期化し,QueryCloseでシリアルポートをクローズするのはお約束である。

 DownLoadボタンクリック時イベントに,RCXにダウンロードするプログラムを記述していく。まず,いちいち,Spirit1と書かなくてもすむように,With修飾を行う。RCXには0~4の5つのプログラムを入れる場所があるので,SelectPrgm 4で選択する。

 タスクは3つ定義した。タスク0がmainタスクであり,プログラムはタスク0から開始される。
-------------------------------------------------------------------
Private Sub btnDownLoad_Click()
  With Spirit1
    .SelectPrgm 4         'プログラムスロット5を指定
    .BeginOfTask 0        'タスク0の開始
      .SetSensorType 0, 3   'ポート1のセンサーを3:光センサーに設定
      .SetSensorMode 0, 0, 0 'ポート1のセンサーが未加工の値を返すように設定
      .SetFwd "02"      '回転方向を順方向に
      .On "02"         'モーターをオン
      .StartTask 1       'タスク1開始
      .StartTask 2       'タスク2開始
    .EndOfTask

-------------------------------------------------------------------
 タスク0ではまず,ポート1につないだセンサーは光センサーであり,未加工の値を返すことを指定している。そしてモーターの回転方向を順方向に設定し,車をスタートさせると同時にタスク 1と2を開始している。
-------------------------------------------------------------------
    .BeginOfTask 1
      .Loop 2, 0           '無限ループ
        .SendPBMessage 2, 0  '赤外線出力
        .Wait 2, 10        '1/10秒 Wait
      .EndLoop
    .EndOfTask

-------------------------------------------------------------------
 タスク1では, Loop文で無限ループを作り,SendPBMessageとWaitの組み合わせで,0.1秒ごとに赤外線を出力している。
-------------------------------------------------------------------
    .BeginOfTask 2
      .Loop 2, 0           '無限ループ
        .SetVar 0, 12, 0     '光センサーの値を変数0に
        .SumVar 0, 2, 100    '変数0の値に100加算
        .If 12, 0, 0, 0, 0     '光センサーの値と変数0の値を比較
          .AlterDir "02"    '方向を変える
          .Wait 2, 50       '0.5秒間バック
          .SetFwd "0"     '左右の方向を変えて,曲がる
          .SetRwd "2"
          .Wait 2, 50
          .SetFwd "02"    '回転方向を順方向に戻す
        .EndIf
      .EndLoop
    .EndOfTask
    .PlaySystemSound 2       'ダウンロード完了のサウンド
  End With
  MsgBox "DownLoad 完了!"
End Sub

-------------------------------------------------------------------
 タスク2では,光センサーの値の変化により障害物に近づいたことを検知し,車の進行方向を変えている。まず,SetVarで光センサー(12)の値を変数(0)に記憶し,SumVarでその値に100を足している。

 次のIf文がちょっとわかりにくい。12,0,0,0,0の真ん中の0が>を意味する比較演算子なのである。つまり12,0 > 0,0,左辺の12,0は光センサーの最新の値であり,右辺が変数0の値である。変数0の値は光センサーの直前の値+100なので,左辺と右辺に100を超える違いが認められる場合に,AlterDirでバックし方向を変え,また進み出す。

 テストしてみると,タンスや壁の5cmぐらい前で,何かに接近したことを感知し方向を変え,部屋の中を四角く動き回った。

 これにて,父の夏休みの自由研究は終了。