BTStackの改造方法について

05 May 2014

AndroidからBluetoothを使ってmbedを操作しようと思い、その先駆けとして、 BTStackを改造して4つのボタンそれぞれの押下に合わせてmbed上のLED1~4を点灯させる(もう1度押すと消える)ソースを作り実験しています。 ソース中ではsscanfでAndroidから送信された文字を受け取り、それに対応させようとしていますが、 Android中のどのボタンを押してもどういうわけかLED4のみが点灯してしまいます。

ソースはi以下のようになります。

spp_demo.cppをベースとしてこちらを参考にしています。

#if 1
/*
 * spp_demo
 */
#include "mbed.h"
#include <btstack/hci_cmds.h>
#include <btstack/run_loop.h>
#include <btstack/sdp_util.h>
#include "hci.h"
#include "l2cap.h"
#include "btstack_memory.h"
#include "remote_device_db.h"
#include "rfcomm.h"
extern "C" {
#include "sdp.h"
}
#include "config.h"
#include "debug.h"
#include "bd_addr.h"  // class bd_addr

Serial pc(USBTX, USBRX);
DigitalOut led1(LED1), led2(LED2), led3(LED3),led4(LED4);
/*
#define HEARTBEAT_PERIOD_MS 500
*/
static uint8_t   rfcomm_channel_nr = 1;
static uint16_t  rfcomm_channel_id = 0;
static uint8_t   spp_service_buffer[128];

static int command(uint8_t *packet,uint16_t size){
        
    char cmd;
    unsigned long int sign1;
    unsigned long int sign2;
    int ret=0;
    int err=1;
    
    if(size<2)return err;
    if(packet[size-1]!=0x0d && packet[size-1]!=0x0a)return err;
    packet[size]=0x00;
    
        switch(packet[0]){
            case 'A':
              ret=sscanf((char *)packet,"%c,%x,%x",&cmd,&sign1,&sign2);
              pc.printf("ret=%d,cmd=%c,sign1=%x,sign2=%x\n",ret,cmd,sign1,sign2);
              if(ret==3){
              if(sign1==0x0001)led1=!led1;
               err=0;
              }
            break;
            case 'B':
                ret=sscanf((char *)packet,"%c,%x,%x",&cmd,&sign1,&sign2);
                pc.printf("ret=%d,cmd=%c,sign1=%x,sign2=%x\n",ret,cmd,sign1,sign2);
                if(ret==3){
                if(sign1==0x0002)led2=!led2;
                 err=0;
                }
            break;
            case 'C':
                ret=sscanf((char *)packet,"%c,%x,%x",&cmd,&sign1,&sign2);
                pc.printf("ret=%d,cmd=%c,sign1=%x,sign2=%x\n",ret,cmd,sign1,sign2);
                if(ret==3){
                if(sign1==0x0003)led3=!led3;
                 err=0;
                }
                break;
            case 'E':
                ret=sscanf((char *)packet,"%c,%x,%x",&cmd,&sign1,&sign2);
                pc.printf("ret=%d,cmd=%c,sign1=%x,sign2=%x\n",ret,cmd,sign1,sign2);
                if(ret==3){
                if(sign1==0xFFFF)led4=0;
                 err=0;
                }
                break;
                default:
                }
    return err;
    }


// Bluetooth logic
static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
    bd_addr_t event_addr;
    uint8_t   rfcomm_channel_nr;
    uint16_t  mtu;
    int err;
    switch (packet_type) {
        case HCI_EVENT_PACKET:
            switch (packet[0]) {
                    
                case BTSTACK_EVENT_STATE:
                    // bt stack activated, get started - set local name
                    if (packet[2] == HCI_STATE_WORKING) {
                        hci_send_cmd(&hci_write_local_name, "mbed");
                    }
                    break;
                
                case HCI_EVENT_COMMAND_COMPLETE:
                    if (COMMAND_COMPLETE_EVENT(packet, hci_read_bd_addr)){
                        bt_flip_addr(event_addr, &packet[6]);
                        log_info("BD-ADDR: %s\n\r", bd_addr_to_str(event_addr));
                        break;
                    }
                    if (COMMAND_COMPLETE_EVENT(packet, hci_write_local_name)){
                        hci_discoverable_control(1);
                        break;
                    }
                    break;

                case HCI_EVENT_LINK_KEY_REQUEST:
                    // deny link key request
                    log_info("Link key request\n\r");
                    bt_flip_addr(event_addr, &packet[2]);
                    hci_send_cmd(&hci_link_key_request_negative_reply, &event_addr);
                    break;
                    
                case HCI_EVENT_PIN_CODE_REQUEST:
                    // inform about pin code request
                    log_info("Pin code request - using '0000'\n\r");
                    bt_flip_addr(event_addr, &packet[2]);
                    hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000");
                    break;
                
                case RFCOMM_EVENT_INCOMING_CONNECTION:
                    // data: event (8), len(8), address(48), channel (8), rfcomm_cid (16)
                    bt_flip_addr(event_addr, &packet[2]); 
                    rfcomm_channel_nr = packet[8];
                    rfcomm_channel_id = READ_BT_16(packet, 9);
                    log_info("RFCOMM channel %u requested for %s\n\r", rfcomm_channel_nr, bd_addr_to_str(event_addr));
                    rfcomm_accept_connection_internal(rfcomm_channel_id);
                    break;
                    
                case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE:
                    // data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16)
                    if (packet[2]) {
                        log_info("RFCOMM channel open failed, status %u\n\r", packet[2]);
                    } else {
                        rfcomm_channel_id = READ_BT_16(packet, 12);
                        mtu = READ_BT_16(packet, 14);
                        log_info("\n\rRFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n\r", rfcomm_channel_id, mtu);
                    }
                    break;
                    
                case RFCOMM_EVENT_CHANNEL_CLOSED:
                    rfcomm_channel_id = 0;
                    break;
                
                default:
                    break;
            }
            break;
            
        case RFCOMM_DATA_PACKET:
            // loopback
            if (rfcomm_channel_id) {
 /*               err = rfcomm_send_internal(rfcomm_channel_id, packet, size);
                if (err) {
                    log_info("rfcomm_send_internal -> error %d", err);*/
                command(packet,size);
                }
            //led3 = !led3;
            break;
        default:
            break;
    }
}
/*
static void  heartbeat_handler(struct timer *ts){
    run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS);
    run_loop_add_timer(ts);
    led2 = !led2;
} */

// main
int main(void)
{
    pc.baud(921600);
    log_info("%s\n", __FILE__);

    // init LEDs
//    led1 = led2 = led3 = 1;
    
    /// GET STARTED with BTstack ///
    btstack_memory_init();
    run_loop_init(RUN_LOOP_EMBEDDED);
    
    // init HCI
    hci_transport_t* transport = hci_transport_usb_instance();
    remote_device_db_t * remote_db = (remote_device_db_t *) &remote_device_db_memory;
    hci_init(transport, NULL, NULL, remote_db);
    
    // init L2CAP
    l2cap_init();
    l2cap_register_packet_handler(packet_handler);
    
    // init RFCOMM
    rfcomm_init();
    rfcomm_register_packet_handler(packet_handler);
    rfcomm_register_service_internal(NULL, rfcomm_channel_nr, 100);  // reserved channel, mtu=100

    // init SDP, create record for SPP and register with SDP
   sdp_init();
    memset(spp_service_buffer, 0, sizeof(spp_service_buffer));
    service_record_item_t * service_record_item = (service_record_item_t *) spp_service_buffer;
    sdp_create_spp_service( (uint8_t*) &service_record_item->service_record, 1, "loopback");
    log_info("SDP service buffer size: %u\n\r", (uint16_t) (sizeof(service_record_item_t) + de_get_len((uint8_t*) &service_record_item->service_record)));
    sdp_register_service_internal(NULL, service_record_item);
    
/*    // set one-shot timer
    timer_source_t heartbeat;
    heartbeat.process = &heartbeat_handler;
    run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS);
    run_loop_add_timer(&heartbeat);
  */  
    log_info("SPP loopback demo...\n\r");

     // turn on!
    hci_power_control(HCI_POWER_ON);

    // go!
    run_loop_execute();    

    return 0;
}
#endif

Androidからの受信方法に不備があるのでしょうか。それともAndroid側のプログラムの問題でしょうか。 ちなみにAndroid側からsendmessage()関数で文字を指定して直接mbedへ送る方式にしています。

04 May 2014

こんにちは。

<<quote>> ソース中ではsscanfでAndroidから送信された文字を受け取り、それに対応させようとしていますが、 Android中のどのボタンを押してもどういうわけかLED4のみが点灯してしまいます。 <</quote>>

BTstackの処理については分かりませんが、まずは想定しているパケットが飛んできているかを確認するのが良いのではないでしょうか?

例:packet[0] が 'D' 以外で、 sign1が0x4以外の場合など

04 May 2014

確認はTeraTermを用いています。 pc.printf()で受け取った文字をTeraTermに飛ばしていますが、こちらもどういうわけかTeraTerm上では表示されないので確認自体ができません(リセットボタンの反応やAndoridとつなげた際の反応は表示される)。 結果を見る限りは想定外のパケットだとは思いますが、それ以上のことはわからず困っています。

04 May 2014

続報です。

packet[0]をD→E、sign1=0xFFFFにして走らせても結果は同じでした。 また、Androidからbluetooth接続を切り離したり、mbed上のリセットボタンを押すとそのたびにled4が点灯します。 こちらとの関係もあるのでしょうか。

04 May 2014

pc.printfに関して、「リセットボタンの反応やAndoridとつなげた際の反応は表示される」という事であれば、該当する関数(command関数)が呼び出されていないか、または最初の部分のエラーチェックによってリターンしている(その後の処理が行われてない)という事は無いでしょうか?

LED4の点灯処理部分は公開されたコードには含まれていませんし、デバッグログのようなものありませんので、想像でしかコメントすることが出来ません。

プロジェクト全体をpublishして頂ければ、もう少しコメントできるかも知れません。

05 May 2014

ソースコード全体を示しました。 改変したソースはBTStack内のspp_demo.cppのみです。

08 May 2014

ソースコードを見せて頂きました。

spp_demo.cpp 中の led4 に対しては、0 しか代入されていないので、他のコードを見たところ、USBHost.cpp の USBHost::usb_process() 内でオンに設定している部分がありました。この動作については、USBHost ライブラリ中で意図された物であると思います。

DigitalOut l4(LED4);

// 中略
void USBHost::usb_process() {
    
    bool controlListState;
    bool bulkListState;
    bool interruptListState;
    USBEndpoint * ep;
    uint8_t i, j, res, timeout_set_addr = 10;
    uint8_t buf[8];
    bool too_many_hub;
    int idx;

#if DEBUG_TRANSFER
    uint8_t * buf_transfer;
#endif
        
#if MAX_HUB_NB
    uint8_t k;
#endif
    
    while(1) {
        osEvent evt = mail_usb_event.get();
        
        if (evt.status == osEventMail) {
            
            l4 = !l4;
            message_t * usb_msg = (message_t*)evt.value.p;

なお、手持ちの Bluetooth USB ドングルを使用したところ、以下のようなログが出力されました(TeraTerm で確認)。

/src/spp_demo.cpp
registering packet handler
SDP service buffer size: 108
SPP loopback demo...
hci_power_control: 1, current mode 0
usb_open
[USB_INFO: /src/USBHost/USBHost/USBHost.cpp:158]New device connected: 10002aec [hub: 0 - port: 1]
[USB_INFO: /src/USBHostBTstack/USBHostBTstack.cpp:68]NLocal Address, Status: 0x00: Addr: 00:1b:dc:09:19:bb
BD-ADDR: 00:1b:dc:09:19:bb
hci_read_buffer_size: used size 52, count 10, packet types 331e

ログを見る限りはエラー等は発生していないので、問題ないように思えます。

今回修正されたコード部分で、期待された処理がされていないという点では、前回コメントしたように command() 関数が呼び出されていないか、該当する case ブロックが実行される前にエラーでリターンしてしまっている事などが考えられないでしょうか?

pc.printf() は動作するようなので、コード中にデバッグ文などを挿入して、確認していただないでしょうか。

08 May 2014

回答ありがとうございます。

pc.printf()で動作確認してみました。 command()関数の前後にpc.printf()を書き込んで走らせたのですが、TeraTermのログから見ると関数自体が飛び越されているようです。

該当箇所のソースコードを下に示します。

       case RFCOMM_DATA_PACKET:
            // loopback
            if (rfcomm_channel_id) {
 /*               err = rfcomm_send_internal(rfcomm_channel_id, packet, size);
                if (err) {
                    log_info("rfcomm_send_internal -> error %d", err);*/
                pc.printf("command error1\n");
              command(packet,size);
            pc.printf("running---\n");
                }
            else{
                pc.printf("command error\n");
                }
        ret=sscanf((char *)packet,"%c,%0x,%0x",cmd,&sign1,&sign2);
        pc.printf("running??\n");
          command(packet,size);
        pc.printf("running\n");
            break;
        default:
            break;
    }
}

下記はTeraTermのログです(androidとつないだ際のログも一部含みます)。

Sending MSC RSP for #2
Providing credits for #2
rfcomm_channel_opened!

RFCOMM channel open succeeded. New RFCOMM Channel ID 1, max frame size 43
RFCOMM data UIH_PF, new credits: 33, now 40
L2CAP signaling handler code 6, state 7
command error1
running---
running??
running
command error1
running---
running??
running
08 May 2014

「関数自体が飛び越されている」という事の根拠はありますか?

繰り返しになりますが、command() 関数の中でエラーになって(printfが実行される前に)リターンしているいるということは無いでしょうか?

scanf で packet に読み込んだ文字列に対して、適切な size 値が設定されていない様に見えます。

09 May 2014

御世話様でございます。

横槍失礼致します。やり取りのログ拝見致しましたが、どうもWataraiさんに余計な御手間取らせているような感じがします。もう少し「条件を切り分けて」入力側(一番最初はTeratermなどのキーボードだと思われますが)から、どこまで「確実に」届いているのかを御自身で調べていく必要があるように感じました。 出口側からサポート頂ける方に追いかけさせるのは、条件、ソース等いちいち要求せねばならず非常に非効率だと思います。 例)TeraTermの出力確認(方法は御自身で、HEXで終端も含めて意図通りか確認)→OK、Arudino受信→OK、BTStack→....など

09 May 2014

横槍失礼します。

私もBTstackを試してみましたが、ちゃんと動きました。 文字'a'~'d'に対応してLED1~4が光るコードを動かした際のログは以下の通りです。 (Android端末から接続し、a b c d と1文字ずつ、計4バイトを送信。)

Yam Yamさんのアドバイスにもあります通り、1つ1つ段階的に調べる事をお勧めします。

Terminal Log

### 長いので最初は省略 ###
Sending MSC CMD for #2
Received MSC RSP for #2
Sending MSC RSP for #2
Providing credits for #2
rfcomm_channel_opened!

RFCOMM channel open succeeded. New RFCOMM Channel ID 1, max frame size 43
RFCOMM data UIH_PF, new credits: 3, now 10
packet(size:1) = a
RFCOMM data UIH_PF, new credits: 1, now 10
packet(size:1) = b
RFCOMM data UIH_PF, new credits: 1, now 10
packet(size:1) = c
RFCOMM data UIH_PF, new credits: 1, now 10
packet(size:1) = d

プロジェクトもpublishしておきます。(変更点はHistoryを参照してください。)

Import programBTstack_test

https://mbed.org/forum/ja/topic/4890/

10 May 2014

皆様フォローありがとうございます。

androidから受信した文字はpacket[]配列に1文字ずつ格納されていることが分かったので、 ソース中のsscanfを廃止して純粋にpacket[]の文字で分岐させたら成功しました。

また、USBHost.cppでLED4を使用しない設定にして(Wataraiさんのソースに則ってUSBHost.cppのl4=!l4をコメントアウトしました)実行したところLED4についても意図したとおりに成功できました(USBHost.cppを変更しないとLED4も点灯しようとするようです)。

上記の通り一応成功はしたのですが、デバッグ中に出た警告で少々気にかかる点がございます。

各LEDの変数に代入した数を下の関数で表示させようとしていたのですが、

pc.printf("led1=%c,led2=%c,led3=%c,led4=%c\n",led1,led2,led3,led4);

その際にWarning: Non-POD class type passed through ellipsis in "spp_demo.cpp",(行番号など以下略)といった警告が出ています。 調べても何がどう問題なのかが分からず対処法が分かりません。 ログでは以下のようになっています。

led1=2,led2=,led3= ,led4=8

pc.printfで%cを%dにすると、以下のようになります。

led1=537509938,led2=262144,led3=537509920,led4=537509944

いずれもどのLEDを点灯させても同じ反応です。 mbedのLEDの点灯確認でprintf関数を使う場合、代入する変数の型をどのようにすれば表示できるのでしょうか。 ご教授お願いします。

10 May 2014

ご報告ありがとうございます。無事に動作したようで、安心しました。

ledの値の表示ですが、led1, led2... は変数名ですので、このままだとDigitalOutクラスのインスタンス(オブジェクト)のアドレスの参照になってしまうと思います。

以下のように、DigitalOutクラスのread() メンバ関数を使うことで、値の読み出しが出来ますのでお試し下さい。

pc.printf("led1=%d,led2=%d,led3=%d,led4=%d\n",led1.read(),led2.read(),led3.read(),led4.read());


DigitalOut APIの仕様は、ここから参照できます:
https://mbed.org/handbook/DigitalOut#api