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で「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”に変更しています。
サンプルコード
リモート側(End Device)のXBeeにコマンドを送って、ADC3の読み取りと、DIO1(Digital Outに設定)のトグルによるLチカを行うコードを作ってみました。
#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の設定が両端でずれていたことあたりが原因みたいです。とは言え、プロフェッショナルな皆さんとお話ができ、終了後の飲み会も含めて楽く有意義なひと時が過ごせました。
最近のコメント