LoginSignup
27
23

More than 5 years have passed since last update.

BLE腕輪にAndroid端末から通知を送ってみる

Last updated at Posted at 2015-06-25

Bluetooth 4.0(BLE)対応のリストバンド型活動量計を、自作の Android アプリから操作して遊んでみようという試みです。

手頃な値段の腕輪を探す

今回は、 Amazon で ¥2.5k ほどで売っていた、こちらをターゲットとしました。

Excelvan スマートブレスレット Bluetooth 4.0
腕輪
購入先:その1
購入先:その2

腕輪について調べてみる

使えそうな Android 用のアプリケーションが2つほどありました。
Navig8r Move(Google Play)
ZERONER(Google Play)

Move は活動量(歩数、距離、カロリー)履歴の取得のみ、ZERONER は履歴の取得に加えて、Android の端末の通知をリストバンドに送ることができます(ただし、あらかじめ決められたアプリ(Twitter, Facebook, Skype, Whatsapp)のみで、好きなアプリの通知はできない)。

取扱説明書的なものは、こちらが使えそうです
Viddon X5(PDFへの直接リンク)

サービスの一覧を取得してみる

どんな UUID が使えるのか調べるため、 BluetoothGatt の discoverServices を呼んでみます。

サービス(Service)

以下はBLE標準のものです。

Service Name UUID
Generic Access 00001800-0000-1000-8000-00805f9b34fb
Generic Attribute 00001801-0000-1000-8000-00805f9b34fb
Device Information Service 0000180a-0000-1000-8000-00805f9b34fb
Battery Service 0000180f-0000-1000-8000-00805f9b34fb

以下は独自のものです。

Service Name UUID
PHONE ALERT f000ff10-0451-4000-b000-000000000000
MAIN f000ff00-0451-4000-b000-000000000000

キャラクタリスティック(Characteristic)

以下はBLE標準のものです。

Characteristic Name UUID
Device Name 00002a00-0000-1000-8000-00805f9b34fb
Appearance 00002a01-0000-1000-8000-00805f9b34fb
Peripheral Privacy Flag 00002a02-0000-1000-8000-00805f9b34fb
Reconnection Address 00002a03-0000-1000-8000-00805f9b34fb
Peripheral Preferred Connection Parameters 00002a04-0000-1000-8000-00805f9b34fb
Service Changed 00002a05-0000-1000-8000-00805f9b34fb
System ID 00002a23-0000-1000-8000-00805f9b34fb
Model Number String 00002a24-0000-1000-8000-00805f9b34fb
Serial Number String 00002a25-0000-1000-8000-00805f9b34fb
Firmware Revision String 00002a26-0000-1000-8000-00805f9b34fb
Hardware Revision String 00002a27-0000-1000-8000-00805f9b34fb
Software Revision String 00002a28-0000-1000-8000-00805f9b34fb
Manufacturer Name String 00002a29-0000-1000-8000-00805f9b34fb
IEEE 11073-20601 Regulatory Certification Data List 00002a2a-0000-1000-8000-00805f9b34fb
Battery Level 00002a19-0000-1000-8000-00805f9b34fb

以下は独自のものです。

Characteristic Name UUID
PHONE ALERT f000ff11-0451-4000-b000-000000000000
ALARM f000ff01-0451-4000-b000-000000000000
USER f000ff02-0451-4000-b000-000000000000
SPORT f000ff03-0451-4000-b000-000000000000
LED f000ff04-0451-4000-b000-000000000000
DATE f000ff05-0451-4000-b000-000000000000
PAIR f000ff06-0451-4000-b000-000000000000
DAILY f000ff07-0451-4000-b000-000000000000
SEDENTARY f000ff08-0451-4000-b000-000000000000
POWER SAVING f000ff09-0451-4000-b000-000000000000

ディスクリプタ(Descriptor)

Descriptor Name UUID
- 00002902-0000-1000-8000-00805f9b34fb
- 00002901-0000-1000-8000-00805f9b34fb
- 00002902-0000-1000-8000-00805f9b34fb
- 00002901-0000-1000-8000-00805f9b34fb
- 00002901-0000-1000-8000-00805f9b34fb
- 00002901-0000-1000-8000-00805f9b34fb
- 00002901-0000-1000-8000-00805f9b34fb
- 00002901-0000-1000-8000-00805f9b34fb
- 00002901-0000-1000-8000-00805f9b34fb

Service MAIN(f000ff00-0451-4000-b000-000000000000)
Characteristic PHONE ALERT(f000ff11-0451-4000-b000-000000000000)
が、今回お目当ての UUID となりそうです。

通知を送るための電文を解析してみる

前章で探し当てた UUID に、どのようなデータを writeCharacteristic すると通知が送られるのか、 ZERONER のアプリケーションで実際に通知を送ってみました。

実際に、送られたデータをダンプしたものです。

02C0010100003FE0200020002000200020003FC0
02C001023FC02000200020002000200020000000
02C0010300000000000000001C003E0063004100
02C001041F0079006300430047007F0039000000
02C0010500000000000000000C003E0063004100
02C00106400040004000410063003E0000000000
02C0010700000000000000000C003E0063006100
02C0010841007F004000610063003E0000000000
02C0010900004000400040005C007E0063006100
02C0010A410041004100610063007E0000000000
02C0010B000000000000000018003E0066004300
02C0010C430043004300620076003C0000000000
02C00110

横96dot x 縦16dotのモノクロビットマップが送れるようです。

1〜12行目はビットマップデータが含まれています。
1行目のデータを例にすると、

data description
02 start mark
C0 bitmap data size (16x12=192)
01 icon type (1=mail, 0=phone)
01 sequence (1, 2, 3 ... 12)
0000 3FE0 2000 2000 2000 2000 2000 3FC0 bitmap data (16x8 big endian。データの並び順については後述)

ビットマップデータは、横16dot x 縦8dot x 12個に分割して、以下の順に転送します。
①③⑤⑦⑨⑪
②④⑥⑧⑩⑫

13行目はフッターです。

解析結果を元に通知を送ってみる

実際に Android アプリを作ってみましょう。
以下の説明は、見やすくするため、エラー処理は省略しています。

接続

今回は実験なので、デバイスの検索などは行わず直接接続します。

BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBtAdapter = mBluetoothManager.getAdapter();
BluetoothDevice mDevice = mBtAdapter.getRemoteDevice(DEVICE_ADDRESS);
BluetoothGatt mBluetoothGatt = mDevice.connectGatt(this, false, new BluetoothGattCallback() {
    ...(省略)...
});

サービス一覧の取得

connectGatt が成功すると、 BluetoothGattCallback の onConnectionStateChange が呼ばれます。
discoverServices を呼んで、 BluetoothGattCallback の onServicesDiscovered が呼ばれれば準備完了です。

@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
    switch (newState) {
        case BluetoothProfile.STATE_CONNECTED:
            mBluetoothGatt.discoverServices();
            break;
        case BluetoothProfile.STATE_DISCONNECTED:
            break;
    }
}

@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        // 準備OK
    }
}

通知の送信

文字列から、モノクロビットマップを作成して、腕輪に送信します。
通知
腕輪にメッセージが表示されて、振動しました!!成功です!!

private void sendNotification(String text) {
    byte[][] r = makeBitmapPacket(text);
    for (int i = 0; i < r.length; i++) {
        BluetoothGattCharacteristic c = characteristic(BAND_SERVICE_PHONE_ALERT_UUID, BAND_CHARACTERISTIC_PHONE_ALERT_UUID);
        c.setValue(r[i]);
        mBluetoothGatt.writeCharacteristic(c);
        try {
            Thread.sleep(30);
        } catch (InterruptedException e) {
        }
    }
    BluetoothGattCharacteristic c = characteristic(BAND_SERVICE_PHONE_ALERT_UUID, BAND_CHARACTERISTIC_PHONE_ALERT_UUID);
    c.setValue(new byte[]{2, (byte) 192, 1, 16});
    mBluetoothGatt.writeCharacteristic(c);
}

private BluetoothGattCharacteristic characteristic(UUID sid, UUID cid) {
    if (mBluetoothGatt == null) return null;
    BluetoothGattService s = mBluetoothGatt.getService(sid);
    if (s == null) {
        Log.w(TAG, "Service NOT found :" + sid.toString());
        return null;
    }
    BluetoothGattCharacteristic c = s.getCharacteristic(cid);
    if (c == null) {
        Log.w(TAG, "Characteristic NOT found :" + cid.toString());
        return null;
    }
    return c;
}

private byte[][] makeBitmapPacket(String c) {
    int len = 6;
    byte size = (byte) (len * 16 * 2);

    Paint w_paint = new Paint();
    w_paint.setAntiAlias(false);
    w_paint.setColor(Color.BLACK);
    w_paint.setTextSize(14f);
    w_paint.setTextScaleX(10f / 14f);
    Paint.FontMetrics fm = w_paint.getFontMetrics();
    Bitmap bmp = Bitmap.createBitmap(16 * len, 16, Bitmap.Config.ARGB_8888);
    Canvas cv = new Canvas(bmp);
    cv.drawText(c, 0, 8 - (fm.ascent + fm.descent) / 2, w_paint);

    byte[][] r = new byte[len * 2][4 + 32];
    for (int d = 0; d < len * 2; d++) {
        for (int y = 0; y < 8; y++) {
            int d1 = 0, d2 = 0;
            for (int x = 0; x < 8; x++) {
                d1 |= bmp.getPixel((d / 2) * 16 + x, (d & 1) * 8 + y) == Color.BLACK ? 1 : 0;
                d2 |= bmp.getPixel((d / 2) * 16 + x + 8, (d & 1) * 8 + y) == Color.BLACK ? 1 : 0;
                if (x < 7) {
                    d1 <<= 1;
                    d2 <<= 1;
                }
            }
            r[d][4 + y * 2] = (byte) d1;
            r[d][4 + y * 2 + 1] = (byte) d2;
        }
        r[d][0] = 2;
        r[d][1] = size;
        r[d][2] = 1;
        r[d][3] = (byte) (d + 1);
    }
    bmp.recycle();
    return r;
}

切断

実験が終わったら、切断処理を行います。

mBluetoothGatt.disconnect();

 

今回のサンプルソースのプロジェクト一式はここからダウンロードできます。
(自分で試す際は、 MainActivity.java の DEVICE_ADDRESS を自分の腕輪の Mac アドレスに書き換えてください)

 

他にも、

  • バッテリー残量の取得
  • 活動履歴(歩数、距離、カロリー)の取得
  • 時間の設定、取得
  • アラームの設定、取得

などが行えるので、解説していきたいと思います。

27
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
23