研究テーマ->音楽とロボット->オリジナル制作のロボット->VC++によるRCB-4のコントロールプログラム作成  
  近藤科学のRCB−4をVC++で作成したプログラムで制御する方法を紹介します。  
 
RCB-4を制御するプログラムの作成
  RCB-4HVは近藤科学のロボットキットKHR-3などで使用されているコントロールボードです。通常は、HeartToHeartという近藤科学が用意しているプログラミングツールをPCにインストールし、それで作成したプログラムをRCB-4に転送することで、ロボットを動かすことができます。
近藤科学のWebサイトでは、このツールの他に、C#言語を使用してRCB-4を制御するためのDLLやサンプルプログラムも公開されています。そのほか、RCB−4のコマンドリファレンスなどのPDFもダウンロードすることができます。
 
 
シリアルポート経由でRCB-4を制御する
  シリアルUSBアダプター、シリアルUSBアダプターHS、Dual USB Adapter HSなどの製品をPCのUSBポートにつなげると、PCがCOMポートとしてそれを認識します。
RCB-4に送るコマンドの仕様は、「RCB-4コマンドリファレンス20131018.pdf」というドキュメントで公開されていまから、「COMポートにコマンドを書き込んだら、それでサーボモータを制御できるんじゃネ?」ということを思いつきます。実際、RCB−3の時代に、低速のシリアル通信で、これを行うプログラムを書いたことがありますが、それで動いていました。(ショルダーキーボードの操作でPCから無線でコマンドを送出してロボットを操作していました。)今回、 同様のコードをVC++で記述してみましたが、うまく動きませんでした。どうやら、仕様として公開されているコマンドをそのままCOMポートに書き込んでもダメ なのかなと思い、近藤科学に問い合わせたところ、そのようなことは可能ということで、何か設定が間違っているのではないかということでした。 四苦八苦ののち、動作するようになりました。
 
  Visual Studio 2008を使用しての例を紹介します。
新しいプロジェクトで「MFCアプリケーション」を選択します。(他のでもいいです。)RCB4Controlという名前にしてダイアログベースの設定でプロジェクトを作成しました。(他の設定でもいいです。)文字コードはUnicodeではなくマルチバイトを選択しました。ダイアログボックスをデザインする画面で右クリックでメニューを表示し、「ActiveXコントロールの挿入」というのを選びます。「Microsoft Communications Control, version6.0」というのを挿入します。(たぶん、他のバージョンでもいいと思います。プロパティを表示するとSettingの欄が「9600,n,8,1」となっているはずなので、これを「115200,e,8,1」に書き換えますCommPortの欄も近藤科学のUSBに割り当てられた数字に書き換えます。(デバイスマネージャで確認できます。)あとで使うのでボタンを1つ配置しておいてください。
 
   
  電話のアイコンを右クリックして「変数の追加」を選択します。「m_ms_comm1」という変数名をつけます。
再度、電話のアイコンをクリックして、プロパティを表示します。プロパティの雷のアイコンをクリックすると、「OnComm」というのが表示されるので、「追加OnCommMscomm1」の操作を行います。
ダイアログ開始時に、通信を開始する処理を書き加えます。終了の処理は省略していますが、どこかに書いてください。
 
  通信のための設定
BOOL CRCB4ControlDlg::OnInitDialog()
{

〜省略〜

    // TODO: 初期化をここに追加します。
    m_ms_comm1.put_PortOpen(true);
    return TRUE; // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}
 
 
  ダイアログボックスのデザイン画面に戻り、ボタンをダブルクリックすると、ボタンの処理を行うためのOnBnClickedButton1()が追加され、コードの記述画面になります。
 
 
  送信側
void CRCB4ControlDlg::OnBnClickedButton1()
{
    unsigned char msg[7] ={0x07, 0x0F, 0x00, 0x01, 0x4C, 0x1D, 0x80}; // コマンドの例(これの作り方の仕様は公開されている)
    int len = 7-1;
    CByteArray msg_array;

    unsigned short sum=0;
    int i;
    for(i=0;i<len;i++)
    {
        msg_array.Add (msg[i]);
        sum = sum + msg[i];
    }
    unsigned char check_sum = sum%256;
    msg_array.Add (check_sum);
    COleVariant variant_msg (msg_array);
    m_ms_comm1.put_Output(variant_msg);
}
 
 
  受信側
void CRCB4ControlDlg::OnCommMscomm1()
{
    VARIANT variant_msg;
    VariantInit(&variant_msg);
    BYTE receive_msg[1024];

    TRY
    {
        variant_msg= m_ms_comm1.get_Input();
    }
    CATCH_ALL(e) // USBが抜かれた
    {
        AfxMessageBox("error usb");
    }
    END_CATCH_ALL
    int preceive_len=0;
    if(variant_msg.vt == (long)(VT_UI1 | VT_ARRAY))
    {
        long i, lb, ub;
        unsigned char **ps;

        SAFEARRAY *psa = variant_msg.parray;
        SafeArrayLock(psa);
        ps = (unsigned char **)psa->pvData;
        SafeArrayGetLBound(psa, 1, &lb);
        SafeArrayGetUBound(psa, 1, &ub);
        for (i = lb; i <= ub; i++)
        {
            unsigned char elm;
            SafeArrayGetElement(psa, &i, &elm);
            receive_msg[preceive_len] = elm;
            preceive_len++;
        }
        SafeArrayUnlock(psa);
    }
        // メッセージのバイト数 preceive_lenは格納される
        // メッセージreceive_msgは格納される
}
 
  デバッガで確認すると、preceive_lenにメッセージのバイト数が、receive_msgにメッセージが格納されているのが分かります。
たてつづけにコマンドを送ったり、存在しないサーボにコマンドを送ったりすると、正しく動作しなくなるようです。
※追記 下記の変更を行わないとcommが正しく動作しないようです。
// CWinAppEx::InitInstance();
CWinApp::InitInstance();
 
 
DLLを使用する
  近藤科学は、WEBサイトでRCB-4リファレンスセットというのを公開していて、ダウンロードすると、C#のサンプルプログラムと、DLLが2つ付いてきます。C#のサンプルプログラムはこのDLLを経由してコマンドをRCB−4に送っているようです。このDLLを経由しな くても、コマンドはRCB−4に送れるということです。  
  C#のサンプルがついているのなら、それを改造すればいいのですが、どうしてもC++言語を使用したい事情がありましたので、このDLLをC++で作成したプログラムから呼び出して使うことにしました。C#のコードを見ながら、C++のコードを作成する作業を開始しましたが、Visual Studio 2008以前とVisual Studio 2010以降とでは、DLLの参照するときの方法が異なるため、Visual Studio 2010 Express(無料)というのを使用することにしました。Visual Studio 2008以前のものでも作れないことはなさそうですが、面倒なのは嫌です。  
 
プロジェクトの作成と参照設定
  VC++2010Expressを起動し、メニューの「ファイル->新規作成->プロジェクト」を選択します。「Windowsフォームアプリケーション」を選択します。  
  フォームが表示されたら、メニューの「プロジェクト->参照」を選択します。
「新しい参照の追加」というボタンがあるので、クリックします。
「Extensions.dll」と「Rcb4.dll」をプロジェクトのフォルダにコピーしておきます。
コピーしたDLLを参照します。
 
  フォームにシリアルポートのコントロールを追加します。
ツールボックスから「SerialPort」をフォームにドラッグ&ドロップしてください。赤く色づけした部分に注意してプロパティを設定してください。「COM3」の部分は、デバイスマネージャ等で、USBアダプターにどのCOMポートが割り当てられているか調べてください。



フォーム上にボタンを1つ配置してください。
 
 
 
コードの記述
  配置したボタンをダブルクリックすると、C++コードを記述する画面になります。赤い部分を書き足します。
コマンドの生成までプログラム内ですればいいのですが、そこは難しくないと思うので、最初に、手作業で準備したコマンドをRCB−4に送信するサンプルを作ります。次に、DLLを使用してACKコマンドを生成するサンプルもつけています。
 
  #pragma once

using namespace Extensions;
using namespace Rcb4;


namespace RCB4Sample {

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
 
  ちなみに上記はC#言語で提供されているサンプルプログラムだと下記のようになっています。  
  using Extensions.Collections;
using Rcb4;
 
  ボタンのプロシージャ内に下記を書き加えます。  
  private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {

    serialPort1->Open ();
    // コマンドの送り方
    Extensions::Collections::ByteList^ example_cmd1 = gcnew Extensions::Collections::ByteList();
    example_cmd1->CommaText = "07 0F 00 01 4C 1D 80";
    array<Byte> ^recv = gcnew array<Byte>(4);
    ::Rcb4::Command::Synchronize (serialPort1, example_cmd1->Bytes, recv);
    // コマンドの作り方
    array<Byte> ^example_cmd2 =::Rcb4::Command::AcknowledgeCheck();
    serialPort1->Close ();

}
 
  実行してデバッガでみると、example_cmd1->CommaText に文字列を代入するだけで、example_cmd1->Bytesにバイト列が設定されることが分かります。recvには、RCB-4からの返答データが返っていることが分かります。  
  ちなみに上記はC#言語で提供されているサンプルプログラムだと下記のようになっています。  
  ByteList example_cmd1 = new ByteList ();
example_cmd1.Bytes = Command.AcknowledgeCheck ();
byte[] recv = new byte[recv_count];
bool result = Command.Synchronize (serialPort1, example_cmd1.Bytes, ref recv);
ByteList example_cmd2 = new ByteList ();
example_cmd2.Bytes = Command.AcknowledgeCheck ();