それからのBLE (3)

ソースコードのっけときます。

// BGAPI を用いたサンプルコード
// iBuffaro BSHSBTPT01BK の IAS サービスのクライアント
// by bunji2

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <windows.h>

#include "cmd_def.h"
#include "uart.h"

//#define DEBUG

#define CLARG_PORT 1
#define CLARG_ACTION 2

#define UART_TIMEOUT 1000

#define MAX_DEVICES 64
int found_devices_count = 0;
bd_addr found_devices[MAX_DEVICES];

enum actions {
  action_none,
  action_scan,
  action_connect,
  action_info,
};
enum actions action = action_none;

typedef enum {
  state_disconnected,
  state_connecting,
  state_connected,
  state_finish,
  state_last
} states;

states state = state_disconnected;

const char *state_names[state_last] = {
  "disconnected",
  "connecting",
  "connected",
  "finish"
};

uint8 alert_level = 0;

uint16 handle_alert_level = 0x0d;

bd_addr connect_addr;

void usage(char *exe)
{
  printf("%s <list|COMx> <info|scan|address <0|1|2>>\n", exe);
}

void change_state(states new_state)
{
#ifdef DEBUG
  printf("DEBUG: State changed: %s --> %s\n",
    state_names[state], state_names[new_state]);
#endif
  state = new_state;
}

/**
 * Compare Bluetooth addresses
 *
 * @param first First address
 * @param second Second address
 * @return Zero if addresses are equal
 */
int cmp_bdaddr(bd_addr first, bd_addr second)
{
  int i;
  for (i = 0; i < sizeof(bd_addr); i++) {
    if (first.addr[i] != second.addr[i]) return 1;
  }
  return 0;
}

void print_bdaddr(bd_addr bdaddr)
{
  printf("%02x:%02x:%02x:%02x:%02x:%02x",
      bdaddr.addr[5],
      bdaddr.addr[4],
      bdaddr.addr[3],
      bdaddr.addr[2],
      bdaddr.addr[1],
      bdaddr.addr[0]);
}

void print_raw_packet(struct ble_header *hdr, unsigned char *data)
{
  int i;
  printf("Incoming packet: ");
  for (i = 0; i < sizeof(*hdr); i++) {
    printf("%02x ", ((unsigned char *)hdr)[i]);
  }
  for (i = 0; i < hdr->lolen; i++) {
    printf("%02x ", data[i]);
  }
  printf("\n");
}

void output(uint8 len1, uint8* data1, uint16 len2, uint8* data2)
{
  if (uart_tx(len1, data1) || uart_tx(len2, data2)) {
    printf("ERROR: Writing to serial port failed\n");
    exit(1);
  }
}

int read_message(int timeout_ms)
{
  unsigned char data[256]; // enough for BLE
  struct ble_header hdr;
  int r;
  const struct ble_msg *msg;

  r = uart_rx(sizeof(hdr), (unsigned char *)&hdr, timeout_ms);
  if (!r) {
    return -1; // timeout
  }
  else if (r < 0) {
    printf("ERROR: Reading header failed. Error code:%d\n", r);
    return 1;
  }

  if (hdr.lolen) {
    r = uart_rx(hdr.lolen, data, timeout_ms);
    if (r <= 0) {
      printf("ERROR: Reading data failed. Error code:%d\n", r);
      return 1;
    }
  }

  msg = (const struct ble_msg *)ble_get_msg_hdr(hdr);

#ifdef DEBUG
  print_raw_packet(&hdr, data);
#endif

  if (!msg) {
    printf("ERROR: Unknown message received\n");
    exit(1);
  }

  msg->handler(data);

  return 0;
}

// [1] スキャンしたアドバタイズパケットの取得
void ble_evt_gap_scan_response(
  const struct ble_msg_gap_scan_response_evt_t *msg
){
  int i;
  char *name = NULL;
  uint8 type;

  if (found_devices_count >= MAX_DEVICES) change_state(state_finish);

  // Check if this device already found
  for (i = 0; i < found_devices_count; i++) {
    if (!cmp_bdaddr(msg->sender, found_devices[i])) return;
  }
  found_devices_count++;
  memcpy(found_devices[i].addr, msg->sender.addr, sizeof(bd_addr));

  // Parse data
  for (i = 0; i < msg->data.len; ) {
    int8 len = msg->data.data[i++];
    if (!len) continue;
    if (i + len > msg->data.len) break; // not enough data
    type = msg->data.data[i++];
    switch (type) {
    case 0x09: // AD Type == "Complete Local Name"
      name = malloc(len);
      memcpy(name, msg->data.data + i, len - 1);
      name[len - 1] = '\0';
    }

    i += len - 1;
  }

  // アドレス
  print_bdaddr(msg->sender);
  // RSSI
  printf(" RSSI:%u", msg->rssi); // [memo] msg->rssi は int8 (符号あり整数)なのに。。。
  // Complete Local Name
  printf(" Name:%s", (name)?(name):"Unknown");
  printf("\n");

  free(name);
}

// [2] デバイスの情報表示の取得
void ble_rsp_system_get_info(
  const struct ble_msg_system_get_info_rsp_t *msg
){
  printf("#ble_rsp_system_get_info\n");
  printf("major=%u, minor=%u, ", msg->major, msg->minor);
  printf("patch=%u, ", msg->patch);
  printf("build=%u, ", msg->build);
  printf("ll_version=%u, ", msg->ll_version);
  printf("protocol_version=%u, ", msg->protocol_version);
  printf("hw=%u\n", msg->hw);
  if (action == action_info) change_state(state_finish);
}

// [3] BLE機器との接続
void ble_evt_connection_status(
  const struct ble_msg_connection_status_evt_t *msg
){

  printf("#ble_evt_connection_status [%s]\n", state_names[state]);
  
  // 新規接続
  if (msg->flags & connection_connected) {
    change_state(state_connected);
    printf("#Connected\n");

    change_state(state_finish);
    
    // アラートレベルの送信
    ble_cmd_attclient_write_command(
      msg->connection, handle_alert_level, 1, &alert_level);

  }
}

// ここから、このコードでは使用していない関数
void ble_evt_attclient_group_found(
  const struct ble_msg_attclient_group_found_evt_t *msg
){
  uint16 uuid;
  
  printf("#ble_evt_attclient_group_found [%s]\n", state_names[state]);

  if (msg->uuid.len == 0) return;
  uuid = (msg->uuid.data[1] << 8) | msg->uuid.data[0];

  printf("service=0x%04x, handles=%d-%d\n", uuid, msg->start, msg->end);

}

void ble_evt_attclient_procedure_completed(
  const struct ble_msg_attclient_procedure_completed_evt_t *msg
){
  printf("#ble_evt_attclient_procedure_completed [%s]\n",
    state_names[state]);

}

void ble_evt_attclient_find_information_found(
  const struct ble_msg_attclient_find_information_found_evt_t *msg
){
  printf("#ble_evt_attclient_find_information_found [%s]\n",
    state_names[state]);

}

void ble_evt_attclient_attribute_value(
  const struct ble_msg_attclient_attribute_value_evt_t *msg
){
  printf("#ble_evt_attclient_attribute_value [%s]\n",
    state_names[state]);
}

// ここまで、このコードでは使用していない関数

// 切断されたときのコールバック関数
void ble_evt_connection_disconnected(
  const struct ble_msg_connection_disconnected_evt_t *msg
) {
  change_state(state_disconnected);
  printf("Connection terminated, trying to reconnect\n");
  change_state(state_connecting);
  // [3] BLE機器に再接続しようとする
  ble_cmd_gap_connect_direct(
    &connect_addr, gap_address_type_public, 40, 60, 100,0);
}


// Ctrl-C 押下でスキャンを終了させる
static void handler(int sig)
{
  if (sig == SIGINT) {
    printf("Ctrl-C!\n");
    change_state(state_finish);
    if (action == action_scan) {
      ble_cmd_gap_end_procedure();
    }
  }
}

int main(int argc, char *argv[]) {
  char *uart_port = "";

  // シグナルハンドラの登録
  if (signal(SIGINT, handler) == SIG_ERR)
    return 1;
  
  // 引数が不足しているかのチェック
  if (argc <= CLARG_PORT) {
    usage(argv[0]);
    return 1;
  }

  // COM ポート引数のチェック
  if (argc > CLARG_PORT) {
    if (strcmp(argv[CLARG_PORT], "list") == 0) {
      uart_list_devices(); // デバイスのリスト表示
      return 1;
    }
    else {
      uart_port = argv[CLARG_PORT];
    }
  }

  // アクション引数のチェック
  if (argc > CLARG_ACTION) {
    int i;
    for (i = 0; i < strlen(argv[CLARG_ACTION]); i++) {
      // 小文字にしておく
      argv[CLARG_ACTION][i] = tolower(argv[CLARG_ACTION][i]);
    }

    if (strcmp(argv[CLARG_ACTION], "scan") == 0) {
      action = action_scan;    // スキャン
    } else if (strcmp(argv[CLARG_ACTION], "info") == 0) {
      action = action_info;    // デバイス情報の表示
    } else {
      int i;
      short unsigned int addr[6];
      if (sscanf(argv[CLARG_ACTION],
          "%02hx:%02hx:%02hx:%02hx:%02hx:%02hx",
          &addr[5],
          &addr[4],
          &addr[3],
          &addr[2],
          &addr[1],
          &addr[0]) == 6) { // アドレスのとき

        for (i = 0; i < 6; i++) {
          connect_addr.addr[i] = addr[i];
        }
        
        if (argc > CLARG_ACTION+1) { // アラートレベル
          uint8 d;
          if (sscanf(argv[CLARG_ACTION+1], "%u", &d) == 1) {
            if (d == 0 || d == 1 || d == 2) {
              alert_level = d;
            }
          }
        }
        
        action = action_connect;
      }
    }
  }
  if (action == action_none) {
    usage(argv[0]);
    return 1;
  }

  bglib_output = output;

  // COMポートのオープン
  if (uart_open(uart_port)) {
    printf("ERROR: Unable to open serial port\n");
    return 1;
  }

  // BLED のリセット
  ble_cmd_system_reset(0);
  uart_close();
  do {
//    usleep(500000); // 0.5s
    Sleep(500); // 0.5s
  } while (uart_open(uart_port));

  // アクションの実行
  if (action == action_scan) {

    // [1] スキャンの開始
    ble_cmd_gap_discover(gap_discover_observation);

  } else if (action == action_info) {

    // [2] デバイスの情報表示の要求
    ble_cmd_system_get_info();

  } else if (action == action_connect) {

    printf("Connecting to ");
    print_bdaddr(connect_addr);
    printf("\n");
    change_state(state_connecting);
    // [3] BLE機器への接続
    ble_cmd_gap_connect_direct(
      &connect_addr, gap_address_type_public, 40, 60, 100,0);
  }

  // メッセージループ
  while (state != state_finish) {
    if (read_message(UART_TIMEOUT) > 0) break;
  }

  // COMポートのクローズ
  uart_close();

  return 0;
}

使い方。

●コマンドラインヘルプ
C:\work\td>ias
ias <list|COMx> <info|scan|address <0|1|2>>

●BLEDデバイスのリスト表示
C:\work\td>ias list
Bluegiga Bluetooth Low Energy (COM3)

●BLEDデバイスの情報表示
C:\work\td>ias COM3 info
#ble_rsp_system_get_info
major=1, minor=2, patch=2, build=100, ll_version=3, protocol_version=1, hw=3

●スキャン(Ctrl-C で終了)
C:\work\td>ias COM3 scan
00:1b:dc:42:1b:39 RSSI:4294967257 Name:BSHSBTPT01BK
Ctrl-C!

●アラートレベル(0x02)の書き込み
C:\work\td>ias COM3 00:1b:dc:42:1b:39 2
Connecting to 00:1b:dc:42:1b:39
#ble_evt_connection_status [connecting]
#Connected

⇒ブザー鳴動します。

●アラートレベル(0x01)の書き込み
C:\work\td>ias COM3 00:1b:dc:42:1b:39 1
Connecting to 00:1b:dc:42:1b:39
#ble_evt_connection_status [connecting]
#Connected

⇒バイブレーションが振動します。

●アラートレベル(0x00)の書き込み
C:\work\td>ias COM3 00:1b:dc:42:1b:39 0
Connecting to 00:1b:dc:42:1b:39
#ble_evt_connection_status [connecting]
#Connected

⇒ブザーやバイブレーションが停止します。

全コードを github にアップしました。

https://github.com/bunji2/ble112_BSHSBTPT01BK_ias