« XBeeを使ってみる | トップページ | LPCXpresso LPC1769の120MHzクロック設定 »

XBeeをAPIモードで使う

前回の続きとして、APIモードへの設定変更と、APIモードを使用したデバイス状態の読み取り・センサー出力(ADCポート)の読み取りを行います。マイコンにはmbedを使っています。


APIモードの種別

前回XBeeモジュールを透過モードに設定しているため、APIモードに設定変更を行います。APIモードには2つのバリエーションがあるため、先ずはこの点を説明します。APIモードのフレーム形式は以下となります(XBeeマニュアルの抜粋)

ApiFrame

このままでは、フレームデーター中に0x7Eが出現するとフレームの先頭を誤認識してしまいます。そのため、フレームデーター中に0x7Eが出現した際に、0x7Eを「0x7Dと0x7E xor 0x20」の2バイトに置き換えるエスケープモードが存在します。0x7D(エスケープコード)自体がフレームデーターに出現した場合は、0x7Dと0x7D xor 0x20に置き換えます。

エスケープモードにはATコマンドATAP2を発行することで遷移します。そのため、エスケープモードは、AP=2 or ATAP=2と記載されることがあります。


APIモードへの設定変更

マイコンからAPIモードの制御を行う際、Arduino用ドライバ(xbee-arduinoプロジェクト)を使用しています。まあ当たり前ですが、xbee-arduinoではエスケープモード(AP=2)での使用が前提となっています。そのため、エスケープモードへの設定変更の手順を示します。以下は透過モードCoordinatorをAP2にする手順です。

①現状の設定情報を読み出します

01_CurrentConfig

 

②Function SetをAPIに変更し、Show Defaultsボタン→ Writeボタンを押す

02_WriteAPImodeDefault

 

③このフェーズでは書き込みが始まる前にエラーで停止することが多々ありますが、デバイスをリセットしたりしてしつこくリトライします。書き込みまで進んだ場合でも、自分の環境では必ず以下のエラーダイアログが出ます(サポート外のWindows 7だからなのかも)。

03_ErrorIndication

 

④OKボタンを押した後で下側のテキスト表示を見ると、パラメーター書き込みは成功しているような感じ

04_ErrorButFwWiteComplete

 

⑤Enable APIをチェックしてTest/Queryを行うと、APIモード対応ファームの2170で認識しています

05_ApiModeEnabled

 

⑥設定パラメーター(デフォルト値)の読み出しもできたのでAPIモードで立ち上がっています。エスケープモードにするために、APパラメーターに2を設定。DH/DLはデフォルトのブロードキャストのままにしておきます(マイコンから制御する際にユニキャストアドレスを指定するので、ここではブロードキャストでも問題なし)

06_SetAPI2

 

⑦X-CTUのUse escape charactersをチェックし、X-CTU側もエスケープモード動作にします

07_X-CTU_AP2mode


End Device側も同様の手順で設定します。End DeviceのDH/DLもデフォルトのDH=0/DL=0(Coordinator宛てを意味する)のままとしておきます。End Deviceでは、サンプルプログラムで使うADC3を有効にしておきます。

08_Adc3Cofnig

 

APIモードを使ったサンプルプログラム

サンプルプログラムを動かすマイコンとしてmbedを使いました。APIモードを動かすためのドライバですが、h.sugaさんがxbee-arduinoプロジェクトをmbedに移植して下さっているので、ありがたく利用させていただきました。

サンプルプルグラムは、mbedが繋がったXBeeにATDBコマンドを投げて電波強度(RSSI)の取得と、リモート側のXBeeにATISコマンドを投げてADCポートのサンプルデーターを取得しています。ADCの分解能は10bitで、温度センサーをつないだポートの生データーを表示しています。

何はともあれ、コードを以下に示します。全体は、mbed.orgにパブリッシュしました。xbee-arduinoはオブジェクト構造が結構複雑ですが、Arduino用のライブラリ添付のサンプルコードを見ながら使い方を覚えるのが手っ取り早そうです。

#include "mbed.h"
#include "XBee.h"
#include "TextLCD.h"

TextLCD lcd(p25, p26, p24, p23, p22, p21); // RS, E, DB4, DB5, DB6, DB7

/*-- AT command and parameters --*/
uint8_t atISCmd[] = {'I', 'S'};      // Forces a read of all enabled digital and analog input lines
uint8_t atDBCmd[] = {'D', 'B'};      // Received Signal Strength
uint8_t cmdVal0[] = {0};             // Clear RSSI regisger

/*-- Create instanse of Xbee object --*/
XBee xbee(p13, p14);
XBeeAddress64 remoteAddress(0x0013A200, 0x406B7111);    // Specify your XBee address

/*-- Create instanse of Command and Response object --*/
// Remot ATIS command to read ADC value (ADC3 is enabled by X-CTU tool)
RemoteAtCommandRequest remoteSampleRequest(remoteAddress, atISCmd);
// Local ATDB command to read signal strength (RSSI)
AtCommandRequest atDB(atDBCmd);
// Local ATDB0 command to clear RSSI
AtCommandRequest atDB0(atDBCmd, cmdVal0, sizeof(cmdVal0));
// Create instanse to handle command response
AtCommandResponse response = AtCommandResponse();
RemoteAtCommandResponse remoteResp = RemoteAtCommandResponse();


/* Receive command response packet
 * If OK response recieved, return pointer to the Response Data Frame
 */
uint8_t* GetResponse() {
    // Read response
    if (xbee.readPacket(5000)) {
        // Got a response! Check if response is AT command respose
        if (xbee.getResponse().getApiId() == AT_COMMAND_RESPONSE) {
            xbee.getResponse().getAtCommandResponse(response);
            if ( response.getStatus() == AT_OK ) 
                return response.getValue();
        } else  if (xbee.getResponse().getApiId() == REMOTE_AT_COMMAND_RESPONSE) {
            xbee.getResponse().getRemoteAtCommandResponse(remoteResp);
            if ( remoteResp.getStatus() == AT_OK ) {
               // Debug print
                printf("Response Data:");
                   for (int i = 0; i < remoteResp.getValueLength(); i++)
                   printf("%02X ", remoteResp.getValue()[i]);
                printf("\n");
                
                return remoteResp.getValue();
            } else {
                printf("Remote Command Error:0x%X\n", response.getStatus());
            }
        }
    }

    return 0;
}


/* Get ADC data
 * Data frame structure of ATIS
 * Offset
 *   0   : Number of Samples (Always 1)
 *   1-2 : Digital Channel Mask
 *   3   : Analog Channel Mask
 *   4-5 : Digital Samples (Omit if no DIO enabled)
 *   6-7 : First ADC Data
 */
uint16_t getAnalog(uint8_t *FrameData, int ADC) {
    // ADC data feild starts 4 bytes offest, if no DIO enabled
    uint8_t start = 4;

    // Contains Digital channel?
    if (FrameData[1] > 0 || FrameData[2] > 0) {
        // make room for digital i/o
        start+=2;
    }

    // start depends on how many ADCs before this ADC are enabled
    for (int i = 0; i < ADC; i++) {
        // Is Analog channel Enabled ?
        if ( (FrameData[3] >> i) & 1 ) {
            start+=2;
        }
    }

    return (uint16_t)((FrameData[start] << 8) + FrameData[start + 1]);
}


int main() {
    unsigned int loop = 0;

    xbee.begin(9600);
    lcd.printf("RSSI:");
    lcd.locate(0, 1);
    lcd.printf("ADC :");
    printf("\nStart.\n");

    while (true) {
        uint8_t *responseVal;
        uint8_t rssiVal = 0;
        uint16_t adcVal = 0;

        // Send ATDB command (Read RSSI register from local Xbee)
        xbee.send(atDB);
        responseVal = GetResponse();
        if ( responseVal != 0 )
            rssiVal = responseVal[0];
        lcd.locate(5, 0);
        if (rssiVal == 0)
            lcd.printf("No Signal");
        else
            lcd.printf("-%ddBm   ", rssiVal);

        // Clear RSSI register, because Xbee hold RSSI value of last received packet even after radio disconneded
        xbee.send(atDB0);
        GetResponse();

        // Read ADC3 value by sending ATIS command
        xbee.send(remoteSampleRequest);
        responseVal = GetResponse();
        if ( responseVal != 0 ) {
            adcVal = getAnalog(responseVal, 3); // Assume ADC3 is enabled
        }
        lcd.locate(5, 1);
        if (adcVal == 0)
            lcd.printf("-     ");
        else
            lcd.printf("%d", adcVal);
    
        printf("Loop:%d\n", loop++);
        wait(1.0);
    }
}

コードの概要は以下です:

  • xbeeオブジェクトを起こす(13行目)
  • xbeeに投げるコマンドやレスポンスのオブジェクトを起こす(16行目から)
  • xbee.send(コマンドオブジェクト)でコマンドを送信
  • xbee.readPacket()でレスポンスを受信(31行目~)
  • xbee.getResponse()でレスポンスデーターを取得、AtCommandResponseオブジェクトに渡してレスポンス情報を取得する、などなど

サンプルプログラムの動作構成ですが、以下のように、XBee 3個構成で行いました。(実験中に壊したXBeeの代替とあわせて追加1個を昨日秋月さんで調達したのです)

SampleProgramRun

真ん中のXBeeがCoordinator(兼Router)で、左右はEnd Deviceです。
mbedがRemoteAtCommandRequestを投げると、真ん中のRouterで左側に中継され、ADCの読み取り値がRouterノード経由で戻ってきます。

End Device間も電波が届く範囲にありますが、直接通信はしてない筈です。ZigBee規格ではEnd Deviceは必ず上位のRouter経由で通信するため、真ん中のrouterをバイパスして左右のEnd Device間が直接通信するパスはないと思っています。このあたりのルート選択の概念やAODV (Ad-hoc On-demand Distance Vector) Routingについては別途お勉強して実験してみたいと思います。リンクコスト(品質)に基づくDistance Vector型の最適ルート選択とか、IPのルーティングと通じるところがあり、通信屋の自分には面白いエリアです。


参考情報

« XBeeを使ってみる | トップページ | LPCXpresso LPC1769の120MHzクロック設定 »

mbed」カテゴリの記事

XBee」カテゴリの記事

コメント

この記事を見て、大変感銘を受け、失礼とは思いましたが、再現テストをさせて頂きました。 その際、このurlを使わせて頂きましたが、もし御気にめされない場合、そのようにご連絡をお願い致します。記事は削除いたします。
宜しくお願い致します。

赤須さん;

古い記事の追加検証ありがとうございました。
転記は問題ございません。

はじめまして。当方、大学の卒業論文で車間通信を研究しています。車間通信の一つの方法としてZigBeeを検討し、この記事を参考にさせていただきました。この場をお借りして感謝申し上げます。

この記事へのコメントは終了しました。

« XBeeを使ってみる | トップページ | LPCXpresso LPC1769の120MHzクロック設定 »

2023年6月
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30  
無料ブログはココログ