mbed
BLE
IoT

mbed HRM 1017をつかってサーボを回すまで(本編)

More than 3 years have passed since last update.

この記事は

mbed HRM 1017をつかってサーボを回すまで(始めてのmbed編)

の続きです。

また、上記はmbedを始めて右も左もわからなかったので

そういう本当に始めての方向けかと思います。


前段

前回までで、無事にHello Worldっぽいことはできて、

検索でトップに引っかかる記事はだいたい上記で止まってそのままだった。

そしてこれをどうやっていじれば自分でサービスつくって

キャラクタリスティックつくって、アドバタイズできるのか全然わからなかった。

というかHello Worldにしては長くないですか

mbedの仕様もまだわかってないのに

とおもったので、


とりあえずLチカできるようになる

BLE_HTM_HRM_1017を複製して

main.ccpを要らなさそうな部分ばっさりカットしまくって

Lチカのみの最小限にしたものがこちら


main.ccp

#include "mbed.h"

#include "TMP102.h"
#include "BLE.h"

#define NEED_CONSOLE_OUTPUT 1

#if NEED_CONSOLE_OUTPUT
Serial pc(USBTX, USBRX);
#define DEBUG(...) { pc.printf(__VA_ARGS__); }
#else
#define DEBUG(...) /* nothing */
#endif /* #if NEED_CONSOLE_OUTPUT */

DigitalOut oneSecondLed(LED1);

int main(void){
DEBUG("Start\n");

while (true) {
wait(0.3);
DEBUG("on\n");
oneSecondLed = 1;
wait(0.3);
DEBUG("off\n");
oneSecondLed = 0;
}
}


mbedには(たぶん)loop関数みたいなのはなくてループさせたいときは

while文で永遠回すという書き方みたい。


サーボ回した


main.ccp

#include "mbed.h"

#include "TMP102.h"
#include "BLE.h"

//サーボ用ライブラリ?
#include "Servo.h"

#define NEED_CONSOLE_OUTPUT 1

#if NEED_CONSOLE_OUTPUT
Serial pc(USBTX, USBRX);
#define DEBUG(...) { pc.printf(__VA_ARGS__); }
#else
#define DEBUG(...) /* nothing */
#endif /* #if NEED_CONSOLE_OUTPUT */

Servo servo(P0_28);

DigitalOut oneSecondLed(LED1);

int main(void){
DEBUG("Start\n");

float angleCount = 0.1;
float angle = 0.0;
servo = angle;

while (true) {
angle += angleCount;
if (angle > 1 || angle < 0){
angleCount *= -1;
}
DEBUG("angleCount:%f\n", angleCount);
DEBUG("angle :%f\n", angle);
wait(0.01);
servo = angle;
}
}


hrm1017でサーボモーターを回すにあたってはこちら

参考にさせていただきました。

Servoっていうライブラリっぽいものをそのままひっぱてきて

includeしました。

サーボの配線は28ピンに白い線、赤は電源、黒はGNDに繋ぎました。

何も考えずにかったので、ここで始めて知ったことで、電源電圧が3.3Vしかない

らしいです。

さらにどこかの記事でみたのですが、電流が最大0.5mAでLEDすらろくに

つけられないらしいです。

大丈夫なのかなぁこれ。


BLEまわりを整理しながら実装

一気に進んだように見えるが一応コメントを残しながらすすめた。


main.ccp

#include "mbed.h"

#include "TMP102.h"
#include "BLE.h"
#include "Servo.h"

#define NEED_CONSOLE_OUTPUT 1

#if NEED_CONSOLE_OUTPUT
Serial pc(USBTX, USBRX);
#define DEBUG(...) { pc.printf(__VA_ARGS__); }
#else
#define DEBUG(...) /* nothing */
#endif /* #if NEED_CONSOLE_OUTPUT */

Servo servo(P0_28);

//デバイスの名前
const static char DEVICE_NAME[] = "MY_HRM1017_HTM";
//お決まりのやつ
BLEDevice ble;
//コネクションの状態を入れる変数(たぶん)
static Gap::ConnectionParams_t connectionParams;

//サービスとキャラクタリスティックをつくるよ

//発信したい情報
uint8_t test = 50;
//キャラクタリスティックのUUIDをつくる
static const uint8_t UUID_CHAR_DATA[] = {0xe5};
//キャラクタリスティックつくる引数は(UUID, データ, データのサイズ, データのサイズ, 役割(read/write/notity))と予想
GattCharacteristic testCharastic ( UUID_CHAR_DATA,
(uint8_t *)&test, sizeof(test), sizeof(test),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
//これよくわかんないけどこうするらしい
GattCharacteristic *testChars[] = {&testCharastic};
//サービスをつくる(引数はサンプルをみて適当にそれっぽくいれた)
GattService testService = GattService(GattService::UUID_DEVICE_INFORMATION_SERVICE, testChars, sizeof(testChars) / sizeof(GattCharacteristic *));
//たぶんサービスのリスト
uint16_t uuid16_list[] = {GattService::UUID_DEVICE_INFORMATION_SERVICE};

DigitalOut led(LED1);

//接続された時に呼ばれる
void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) // Mod
{
DEBUG("Disconnected handle %u, reason %u\r\n", handle, reason);
DEBUG("Restarting the advertising process\r\n");
ble.gap().startAdvertising();
}

//接続が切れた時に呼ばれる
void onConnectionCallback(const Gap::ConnectionCallbackParams_t *params) //Mod
{
DEBUG("connected. Got handle %u\r\n", params->handle);

if (ble.gap().updateConnectionParams(params->handle, &connectionParams) != BLE_ERROR_NONE) {
DEBUG("failed to update connection paramter\r\n");
}
}

int main(void){
DEBUG("Start\n");
//bleはじめ
ble.init();
DEBUG("Init done\r\n");
//接続時と切れた時のコールバック関数を指定
ble.gap().onDisconnection(disconnectionCallback);
ble.gap().onConnection(onConnectionCallback);

//おまじない(セントラルにこれはBLEデバイスですよーとお知らせしているらしい)
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
//おまじない2(セントラルに接続できますよーとお知らせしているらしい)
ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
//うえのほうでつくったサービスのUUIDを格納している(たぶん)
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t*)uuid16_list, sizeof(uuid16_list));
//デバイスのなまえの登録(たぶん)
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));

//アドバタイズの頻度を指定してスタート
ble.gap().setAdvertisingInterval(160);
ble.gap().startAdvertising();
DEBUG("Start Advertising\r\n");

//サービスを追加する
ble.gattServer().addService(testService);

while (true) {

}
}


いろいろ考えたところ

GattCharacteristic testCharastic ( UUID_CHAR_DATA,

(uint8_t *)&test, sizeof(test), sizeof(test),

GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);

これの最後の引数はキャラクタリスティックの役割を決めるもので

サンプルにはReadしかなかったが外からサーボうごかしたいのでWriteを追加した。

また第一引数はUUID_CHAR_DATAを自分で定義してみた。

元々はGattCharacteristic::UUID_BATTERY_LEVEL_CHARなるものが入っており、

これはよくわからないけどmbedのbleライブラリ側でよく使うキャラクタリスティックの名前とか

UUIDを使えるようにしてあるものっぽい。たぶん。。

GattService testService = GattService(GattService::UUID_DEVICE_INFORMATION_SERVICE, testChars, sizeof(testChars) / sizeof(attCharacteristic *));

ここの第一引数も上記のような感じであらかじめ使いそうなサービスが色々登録?

されているっぽかったです。

こんなかんじに

試しに"UUID_DEVICE_INFORMATION_SERVICE"っていうもの使ってみたけど

今思うとなんかニュアンス違う、、

実はここも自分でUUIDつくって最初試していたんだけれど、

外から"0services"って見えてて接続するとサービス見えるというような

状態になった(確か)ので、一旦これで済ませた。

後日いろいろ試す価値がありそう。

あとはほとんどコピペです。

前に少しiOSのBLEを入門したときも思ったけれど、

BLE周りの実装って一旦通信して、接続されたら呼ばれる関数があって、、

みたいにプログラム内の情報だけじゃ追えない感じがして最初は

読みづらいという印象。


BLE通信でサーボモーター回った

サーボ回す用に少し書き足して完成です。

こんな感じ

#include "mbed.h"

#include "TMP102.h"
#include "BLE.h"
#include "Servo.h"

#define NEED_CONSOLE_OUTPUT 1

#if NEED_CONSOLE_OUTPUT
Serial pc(USBTX, USBRX);
#define DEBUG(...) { pc.printf(__VA_ARGS__); }
#else
#define DEBUG(...) /* nothing */
#endif /* #if NEED_CONSOLE_OUTPUT */

Servo servo(P0_28);

//デバイスの名前
const static char DEVICE_NAME[] = "MY_HRM1017_HTM";
//お決まりのやつ
BLEDevice ble;
//コネクションの状態を入れる変数(たぶん)
static Gap::ConnectionParams_t connectionParams;

//サービスとキャラクタリスティックをつくるよ

//発信したい情報
uint8_t test = 0;
//キャラクタリスティックのUUIDをつくる
static const uint8_t UUID_CHAR_DATA[] = {0xe5};
//キャラクタリスティックつくる引数は(UUID, データ, データのサイズ, データのサイズ, 役割(read/write/notity))と予想
GattCharacteristic testCharastic ( UUID_CHAR_DATA,
(uint8_t *)&test, sizeof(test), sizeof(test),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
//これよくわかんないけどこうするらしい
GattCharacteristic *testChars[] = {&testCharastic};
//サービスをつくる(引数はサンプルをみて適当にそれっぽくいれた)
GattService testService = GattService(GattService::UUID_DEVICE_INFORMATION_SERVICE, testChars, sizeof(testChars) / sizeof(GattCharacteristic *));
//たぶんサービスのリスト
uint16_t uuid16_list[] = {GattService::UUID_DEVICE_INFORMATION_SERVICE};

DigitalOut led(LED1);

//接続が切れた時に呼ばれる
void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) // Mod
{
DEBUG("Disconnected handle %u, reason %u\r\n", handle, reason);
DEBUG("Restarting the advertising process\r\n");
ble.gap().startAdvertising();
}

//接続された時に呼ばれる
void onConnectionCallback(const Gap::ConnectionCallbackParams_t *params) //Mod
{
DEBUG("connected. Got handle %u\r\n", params->handle);

if (ble.gap().updateConnectionParams(params->handle, &connectionParams) != BLE_ERROR_NONE) {
DEBUG("failed to update connection paramter\r\n");
}
}
void DataWrittenCallback(const GattWriteCallbackParams *params){
DEBUG("DataWritten\n\r");

//キャラクタリスティックに使う値にデータ入れる
test = params->data[0];
//どこかからひっぱてきたけどdata[1]ってどうやって使うんだろう
DEBUG("%02x%02x\n",params->data[0],params->data[1]);

DEBUG("write:%d\n", test);
}

//tickerでattachしたら定期的に呼ばれる
void periodicCallback(){
test++;
DEBUG("timer:%d\n", test);
ble.gattServer().write(testCharastic.getValueAttribute().getHandle(), (uint8_t *)&test, sizeof(test));
}

int main(void){
DEBUG("Start\n");

//ticker = タイマー(たぶん) 0.5sごとにperiodicCallbackが呼ばれる
Ticker ticker;
ticker.attach(periodicCallback, 0.5);
//bleはじめ
ble.init();
DEBUG("Init done\r\n");
//接続時と切れた時のコールバック関数を指定
ble.gap().onDisconnection(disconnectionCallback);
ble.gap().onConnection(onConnectionCallback);
ble.onDataWritten(DataWrittenCallback);

//おまじない(セントラルにこれはBLEデバイスですよーとお知らせしているらしい)
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
//おまじない2(セントラルに接続できますよーとお知らせしているらしい)
ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
//うえのほうでつくったサービスのUUIDを格納している(たぶん)
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t*)uuid16_list, sizeof(uuid16_list));
//デバイスのなまえの登録(たぶん)
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));

//アドバタイズの頻度を指定してスタート
ble.gap().setAdvertisingInterval(160);
ble.gap().startAdvertising();
DEBUG("Start Advertising\r\n");

//サービスを追加する
ble.gattServer().addService(testService);

}