« LPCXpresso LPC1769でFree RTOSを使う | トップページ | LPCXpresso IDEでのC++コードサイズ肥大化の対策 »

XBee APIモードライブラリのLPCXpressoへの移植

XBeeをAPIモードで使うで紹介したmbed用ライブラリ(オリジナルはArduino用で、Suga koubouさんがmbed用に移植)をLPCXpressoに載せてみました。

オリジナルコードはC++ですが、LPCXpresso IDEでC++のプロジェクトを作成してビルドすることができました。表示部分はMARY OLED基板を使っています。5月にポストした「LPCXpresso LPC1769にMARY拡張基板を乗せる」では、XBeeを載せただけのハリボテ写真を掲載していたのですが、やっと動かすことができました。 

   
LPCXpressoでのC++プロジェクトのビルド

LPCXpresso IDEの新規プロジェクト作成ウイザード(New Project)ではC++プロジェクトが作成できませんが、以下のブログを参考にして、ファイルのコピーやプロジェクトプロパティの設定を行うことによってC++コードのビルドができました。 

マイコン風雲録:LPCXpressoで「C++プロジェクト」を作る方法

私のビルド環境では、LPCXpresso v4.1.0のデフォルトに従って、CMSIS v2をライブラリプロジェクトとして参照していますが、CMSISプロジェクトの設定はREDLIBのままで問題ありませんでした(C++プロジェクト側だけをNEWLIBに変える)。  

   

XBee APIモードライブラリの移植 

主要な変更点は、以下の2つです:  

  • mbed版では、SerialクラスとTimerクラスを使っているため、この部分を自作のクラス(実装は最低限の手抜き)で置き換えています。mbed版はXbeeクラスのコンストラクターにシリアルポートのピン番号を指定しますが、今回の実装はUARTの番号(0~2)を指定します。      
  • オリジナルは、XBee.hで送信ステータスとして”SUCCESS”を#define定義しているのですが、CMSISのlpc_types.hに同じ名前のenum定義があるせいか、lpc_types.hでコンパイルエラーが発生してどうしても解決できなかったため、XBee.h側を”OK”に変更しています。   
ソースコードは以下です(スタートアップコード、や表示用に使ったMARYのコードは再配布が微妙なので含みません)。 

XBee_LPCxpresso.zip

       
サンプルコード 

リモート側(End Device)のXBeeにコマンドを送って、ADC3の読み取りと、DIO1(Digital Outに設定)のトグルによるLチカを行うコードを作ってみました。

LPCXpresso-_XBee

   
#include "XBee.h"
extern "C" {
	#include "oled.h"
	#include "systick.h"
}

/*-- AT command and parameters --*/
uint8_t atISCmd[] = {'I', 'S'};      // Forces a read of all enabled digital and analog input lines
uint8_t atD1Cmd[] = {'D', '1'};		 // DIO1 control
uint8_t atDBCmd[] = {'D', 'B'};      // Received Signal Strength
uint8_t cmdVal0[] = {0};             // Clear RSSI register
uint8_t cmdVal4[] = {4};			 // Digital output, low
uint8_t cmdVal5[] = {5};			 // Digital output, High

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

/*-- Create instance of Command and Response object --*/
// Remote ATIS command to read ADC value (ADC3 is enabled by X-CTU tool)
RemoteAtCommandRequest remoteSampleRequest(remoteAddress, atISCmd);
// Remote ATD1 command to turn DIO1 low/high
RemoteAtCommandRequest remoteDIO1_Low(remoteAddress, atD1Cmd, cmdVal4, sizeof(cmdVal4));
RemoteAtCommandRequest remoteDIO1_High(remoteAddress, atD1Cmd, cmdVal5, sizeof(cmdVal5));

// 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(1000)) {
        // 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 ) {
                return remoteResp.getValue();
            }
        }
    }

    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;
    Timer t;

    xbee.begin(9600);

    Init_SysTick(10);   // Set systick interval to 10ms
    Init_OLED();

    OLED_printf_Font(OLED_FONT_SMALL);
    OLED_Clear_Screen(OLED_BLK);

    OLED_printf_Position(0, 0);
    OLED_printf_Color(OLED_GRN, OLED_BLK);
    OLED_printf("RSSI:");
    OLED_printf_Position(0, 1);
    OLED_printf_Color(OLED_WHT, OLED_BLK);
    OLED_printf("ADC :");

    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];
        OLED_printf_Position(5, 0);
        OLED_printf_Color(OLED_GRN, OLED_BLK);
        if (rssiVal == 0)
            OLED_printf("No Signal");
        else
            OLED_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
        }
        OLED_printf_Position(5, 1);
        OLED_printf_Color(OLED_WHT, OLED_BLK);
        if (adcVal == 0)
            OLED_printf("-     ");
        else
            OLED_printf("%x   ", adcVal);

       // Turn DIO1 high/low
       if (loop % 2 == 0) {
    	   xbee.send(remoteDIO1_Low);
       } else {
    	   xbee.send(remoteDIO1_High);
       }
       GetResponse();

       t.start();
       while (t.read_ms() < 1000);
       t.stop();

       loop++;
    }
}

MARY基板用OLEDモジュール(MARY-OB)のライブラリのヘッダファイルを3~4行目でインクルードしていますが、MARY-OBのライブラリはCのコードであるため、extern “C”宣言でくくってやる必要があります。

extern “C”って、意味がよく分かっていなかったのですが、C++のコードからCの関数を呼び出すために、以下の関数はCのだよとC++コンパイラに教えてあげるための記述なんですね。extern “C”なしでは、リンクでundefined referenceエラーが出て、その対処を調べる過程で始めて意味が分かりました。ヤッパリ習うより実践かな。

 

追記

10月8日(土)に@shintamainjpさん主催の「LPCXpresso横浜お楽しみ部会」に参加しました。ネタ切れのおり、MARY拡張基盤を持ち込んでXBeeライブラリの動作を目指したのですが、時間内では動作せず。問題は移植コードでなくXBeeの設定誤りというトホホな内容・・ファームを更新した際に設定内容が初期化されてScan Channelの設定が両端でずれていたことあたりが原因みたいです。とは言え、プロフェッショナルな皆さんとお話ができ、終了後の飲み会も含めて楽く有意義なひと時が過ごせました。

« LPCXpresso LPC1769でFree RTOSを使う | トップページ | LPCXpresso IDEでのC++コードサイズ肥大化の対策 »

XBee」カテゴリの記事

NXP-ARM」カテゴリの記事

コメント

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

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  
無料ブログはココログ