表題のままです。専用のBLEアプリケーションを作らなくても、簡単な文字列データならば送ることができます。
ESP32 S3をBLEキーボードとして動作させ、接続されると、5秒ごとに1234567890とEnterを押すプログラムです。
ペアリング後、交換した鍵を保持するための仕組みボンド(ボンディング)があると思いますが、ESP32の電源を切った後も保持させるために
NimBLE Optionsの「Persist the BLE Bonding keys in NVS」にチェックを入れること
RPA(Resolvable Private Address)に対応するために
ble_hs_util_ensure_addr(1);
assert(rc == 0);
// アドバタイジング中に使用しているアドレスを把握する
rc = ble_hs_id_infer_auto(
1, // プライベートアドレスをを使うかどうか。0=使わない 1=使う
&my_device.own_addr_type);
ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
の設定を行っています。ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA)が必要だと思ったのですが、ESP32無印でないと使えず、そもそもS3は不要のようでした。
RPAの対応をしないと、iPhoneなどで使えないようです。(手持ちがWindwos10とUbuntuとAndroid端末だったので、これらESPを再起動しても自動的に接続したのは確認しました。)
ソースコード
ble_keyboard.h
#ifndef H_BLE_KEYBOARD_
#define H_BLE_KEYBOARD_
#include "nimble/ble.h"
#include "modlog/modlog.h"
#include "host/ble_gatt.h"
#include "host/ble_uuid.h"
#include "host/ble_hs_pvcy.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "services/bas/ble_svc_bas.h"
//#include "services/dis/ble_svc_dis.h"
#include "os/os_mbuf.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
//////////////////////////////////////////////////
// デバイス情報サービス Device Infomation Service
//////////////////////////////////////////////////
// デバイス情報サービス用UUID
#define GATT_UUID_DIS_SERVICE 0x180A
#define GATT_UUID_DIS_SYSTEM_ID 0x2A23
#define GATT_UUID_DIS_MODEL_NUMBER 0x2A24
#define GATT_UUID_DIS_SERIAL_NUMBER 0x2A25
#define GATT_UUID_DIS_FIRMWARE_REVISION 0x2A26
#define GATT_UUID_DIS_HARDWARE_REVISION 0x2A27
#define GATT_UUID_DIS_SOFTWARE_REVISION 0x2A28
#define GATT_UUID_DIS_MANUFACTURER_NAME 0x2A29
#define GATT_UUID_DIS_PNP_INFO 0x2A50
//////////////////////////////////////////////////
// characteristic presentation format
//////////////////////////////////////////////////
// 名前空間定数(現状は下記の1種類だけ)
#define GATT_NAME_SPACE_BLUETOOTH_SIG 0x01
// キャラクタリスティックで読み取ったときの値のフォーマット
#define GATT_CPF_FORMAT_RFU 0x00
#define GATT_CPF_FORMAT_BOOLEAN 0x01
#define GATT_CPF_FORMAT_2BIT 0x02
#define GATT_CPF_FORMAT_NIBBLE 0x03
#define GATT_CPF_FORMAT_UINT8 0x04
#define GATT_CPF_FORMAT_UINT12 0x05
#define GATT_CPF_FORMAT_UINT16 0x06
#define GATT_CPF_FORMAT_UINT24 0x07
#define GATT_CPF_FORMAT_UINT32 0x08
#define GATT_CPF_FORMAT_UINT48 0x09
#define GATT_CPF_FORMAT_UINT64 0x0A
#define GATT_CPF_FORMAT_UINT128 0x0B
#define GATT_CPF_FORMAT_SINT8 0x0C
#define GATT_CPF_FORMAT_SINT12 0x0D
#define GATT_CPF_FORMAT_SINT16 0x0E
#define GATT_CPF_FORMAT_SINT24 0x0F
#define GATT_CPF_FORMAT_SINT32 0x10
#define GATT_CPF_FORMAT_SINT48 0x11
#define GATT_CPF_FORMAT_SINT64 0x12
#define GATT_CPF_FORMAT_SINT128 0x13
#define GATT_CPF_FORMAT_FLOAT32 0x14
#define GATT_CPF_FORMAT_FLOAT64 0x15
#define GATT_CPF_FORMAT_SFLOAT 0x16
#define GATT_CPF_FORMAT_FLOAT 0x17
#define GATT_CPF_FORMAT_DUINT16 0x18
#define GATT_CPF_FORMAT_UTF8S 0x19
#define GATT_CPF_FORMAT_UTF16S 0x1A
#define GATT_CPF_FORMAT_STRUCT 0x1B
// キャラクタリスティックの値を説明するための情報
typedef struct characteristic_presentation_format{
uint16_t unit; // UUIDで単位を表します。 3.5 Units を参照
uint16_t description; // name_space=1の時は 2.4.2.1 Bluetooth SIG GATT Characteristic Presentation Format Description を参照
uint8_t format; // GATT_CPF_FORMAT_〜定数で選択。数値のフォーマットを表す
uint8_t exponent; // 指数。キャラクタリスティックの値に10の何乗をかけるのか。
uint8_t name_space; // descriptionの名前空間。 0:なし。 1:Bluetooth SIGによる定義 のどちらか。通常は1
} gatt_cpf_struct;
// https://www.bluetooth.com/specifications/assigned-numbers/
// 2.4.2.1 Bluetooth SIG GATT Characteristic Presentation Format Description 抜粋
// 0x0000:不明
// 0x0001〜0x00FF:1〜255番目 という意味
// 0x0100:フロント
// 0x010F:内側
// のようになっていて、例えば温度計を設置したあとに、1番目や、内側などの意味を持たせるための説明に使用する
// 3.5 Units 抜粋
// 0x272F:温度℃
// 0x2704:電流A
// 0x2728:電圧V
// 0x27B0:電池容量Ah
// 0x2727:電荷量C
// 0x27AD:パーセント%
// 0x27AB:電力量kWh
//////////////////////////////////////////////////
// HID
//////////////////////////////////////////////////
// HIDの最新バージョン 1.11
#define HID_USB_VERSION 0x0111
// HID protocol mode values
#define HID_PROTOCOL_MODE_BOOT 0x00 // ブートプロトコルモード
#define HID_PROTOCOL_MODE_REPORT 0x01 // レポートプロトコルモード(デフォルト。通常時はこちら)
// HIDコントロールポイントの値 下位4ビット 上位4ビットは1 = HID_CONTROL request
// Bluetooth SIGのHuman Interface Device Profile 1.0のSection 7.4.2を参照
#define HID_CTRL_POINT_NOP 0 // No Operation
#define HID_CTRL_POINT_HARD_RESET 1 // ハードウェアリセット
#define HID_CTRL_POINT_SOFT_RESET 2 // ソフトウェアリセット
#define HID_CTRL_POINT_SUSPEND 3 // 休止状態へ移行
#define HID_CTRL_POINT_EXIT_SUSPEND 4 // 休止状態から復帰
#define HID_CTRL_POINT_VIRTUAL_UNPLUG 5 // 仮想的にケーブルを引っこ抜く
// HID information flags
#define HID_FLAGS_REMOTE_WAKE 0x01 // RemoteWake
#define HID_FLAGS_NORMALLY_CONNECTABLE 0x02 // NormallyConnectable
// HID Report types
#define HID_REPORT_TYPE_INPUT 1
#define HID_REPORT_TYPE_OUTPUT 2
#define HID_REPORT_TYPE_FEATURE 3
// Bootモード時のレポートID
#define HID_BOOT_REPORT_ID_KEYBOARD 1
#define HID_BOOT_REPORT_ID_MOUSE 2
// Bootモード時のレポートサイズ
#define HID_BOOT_REPORT_SIZE_KEYBOARD 9 // 1-byte Report ID + standard 8-byte keyboard boot report
#define HID_BOOT_REPORT_SIZE_MOUSE 4 // 1-byte Report ID + standard 3-byte mouse boot report
// HIDサービス用UUID
#define GATT_UUID_HID_SERVICE 0x1812
#define GATT_UUID_HID_INFORMATION 0x2A4A
#define GATT_UUID_HID_REPORT_MAP 0x2A4B
#define GATT_UUID_HID_CONTROL_POINT 0x2A4C
#define GATT_UUID_HID_REPORT 0x2A4D
#define GATT_UUID_HID_PROTOCOL_MODE 0x2A4E
#define GATT_UUID_HID_BT_KB_INPUT 0x2A22
#define GATT_UUID_HID_BT_KB_OUTPUT 0x2A32
#define GATT_UUID_HID_BT_MOUSE_INPUT 0x2A33
// Keyboard レポートサイズ (レポートマップで自分で定義したサイズ)
#define HID_REPORT_SIZE_KEYBOARD_IN 8 // キーコードを送る時のサイズ
#define HID_REPORT_SIZE_KEYBOARD_OUT 1 // NumLockなどのLED情報を受け取る時のサイズ
// HID レポートモード用レポートID (レポートマップで自分で定義したID)
enum hid_report_id{
HID_REPORT_ID_KB_OUT, // LED output report ID from report map
HID_REPORT_ID_KB_IN, // Keyboard input report ID from report map
HID_REPORT_ID_CC_IN, // Consumer Control input report ID from report map
HID_REPORT_ID_FEATURE // Feature report ID from report map
};
// ディスクリプタ用UUID
#define GATT_UUID_BAT_PRESENT_DESCR 0x2904
#define GATT_UUID_EXT_RPT_REF_DESCR 0x2907
#define GATT_UUID_RPT_REF_DESCR 0x2908
enum attr_handles_list{
HANDLE_BATTERY_LEVEL,
HANDLE_DIS_MODEL_NUMBER,
HANDLE_DIS_SERIAL_NUMBER,
HANDLE_DIS_HARDWARE_REVISION,
HANDLE_DIS_FIRMWARE_REVISION,
HANDLE_DIS_SOFWARE_REVISION,
HANDLE_DIS_MANUFACTURER_NAME,
HANDLE_DIS_SYSTEM_ID,
HANDLE_DIS_PNP_INFO,
HANDLE_HID_INFORMATION,
HANDLE_HID_CONTROL_POINT,
HANDLE_HID_REPORT_MAP,
HANDLE_HID_PROTOCOL_MODE,
HANDLE_HID_KB_IN_REPORT,
HANDLE_HID_KB_OUT_REPORT,
HANDLE_HID_KB_CCC_DISC,
HANDLE_HID_CC_REPORT,
HANDLE_HID_BOOT_KB_IN_REPORT,
HANDLE_HID_BOOT_KB_OUT_REPORT,
HANDLE_HID_FEATURE_REPORT,
HANDLE_NUM
};
#define MAC2STR_REV(a) (a)[5], (a)[4], (a)[3], (a)[2], (a)[1], (a)[0]
int ble_init_server();
bool ble_is_connected();
void send_text(const char *text);
void ble_store_config_init(void);
#endif
ble_keyboard.c
#include "ble_keyboard.h"
// NimBLE Optionsの下記にチェックを入れる
// Persist the BLE Bonding keys in NVS
#define BLE_SVC_GAP_APPEARANCE 0x03C1 // デバイスの見た目 : 0x03C1:キーボードの絵
const char BLE_DEVICE_NAME[] = "BLE KB"; // アドバタイズで表示されるデバイス名。長い名前は不可。最大31バイト
const int PASS_KEY = 1234;
// デバイス情報
const char GATT_DIS_MODEL_NUMBER[] = "0001";
const char GATT_DIS_SERIAL_NUMBER[] = "0000";
const char GATT_DIS_FIRMWARE_REVISION[] = "Ver.1.1";
const char GATT_DIS_HARDWARE_REVISION[] = "Ver.1.1";
const char GATT_DIS_SOFTWARE_REVISION[] = "Ver.1.1";
const char GATT_DIS_MANUFACTURER_NAME[] = "ESP";
const char GATT_DIS_SYSTEM_ID[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char GATT_DIS_PNP_INFO[] = {0x00, 0x47, 0x00, 0xff, 0xff, 0xff, 0xff};
struct device_status_strct {
SemaphoreHandle_t semaphore;
bool connected;
uint16_t handle;
bool suspended;
uint8_t own_addr_type;
uint8_t protocol_mode;
uint8_t control_point;
bool indicate[HANDLE_NUM];
bool notify[HANDLE_NUM];
uint8_t battery_level;
uint8_t keycode[8];
} my_device = {
.semaphore = 0,
.handle = 0,
.connected = false,
.suspended = false,
.own_addr_type = 0,
.protocol_mode = HID_PROTOCOL_MODE_REPORT,
.control_point = HID_CTRL_POINT_NOP,
.battery_level = 100 // バッテリ残量100%と表示
};
uint8_t hid_info[] = {
(HID_USB_VERSION & 0xFF), // bcdHID:BCD表現のHIDバージョン(1.10なら0x0110)。USB HID version
((HID_USB_VERSION >> 8) & 0xFF),
0x00, // bCountryCode:地域固有デバイスのための国識別番号。必要がなければ0
HID_FLAGS_REMOTE_WAKE // Flags
};
uint16_t hid_ext_report_ref_desc = BLE_SVC_BAS_CHR_UUID16_BATTERY_LEVEL;
// HID Report Map characteristic value
const uint8_t hid_report_map[] = {
// KEYBOARD REPORT
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection: (Application)
0x85, HID_REPORT_ID_KB_IN,
// Report Id (1)
// Input Byte 0: 修飾キー Modifier
0x75, 0x01, // Report Size (1) 1bit を 8 回
0x95, 0x08, // Report Count (8)
0x05, 0x07, // Usage Pg (Key Codes)
0x15, 0x00, // Log Min (0)
0x25, 0x01, // Log Max (1)
0x19, 0xE0, // Usage Min (224)
0x29, 0xE7, // Usage Max (231)
0x81, 0x02, // Input: (Data, Variable, Absolute)
// Key arrays (7 bytes)
0x75, 0x08, // Report Size (8)
0x95, 0x07, // Report Count (7)
0x15, 0x00, // Log Min (0)
0x25, 0x65, // Log Max (101)
0x05, 0x07, // Usage Pg (Key Codes)
0x19, 0x00, // Usage Min (0)
0x29, 0x65, // Usage Max (101)
0x81, 0x00, // Input: (Data, Array)
// LED report
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Pg (LEDs)
0x19, 0x01, // Usage Min (1)
0x29, 0x05, // Usage Max (5)
0x91, 0x02, // Output: (Data, Variable, Absolute)
// LED report padding
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x03, // Output: (Constant)
0xC0 // End Collection
};
// GATTサービスにある全てのキャラクタリスティックのハンドル
uint16_t handles_list[HANDLE_NUM];
int battery_access(uint16_t, uint16_t, struct ble_gatt_access_ctxt*, void*);
int dis_access(uint16_t, uint16_t, struct ble_gatt_access_ctxt*, void*);
int hid_chr_access(uint16_t, uint16_t, struct ble_gatt_access_ctxt*, void*);
int hid_report_access(uint16_t, uint16_t, struct ble_gatt_access_ctxt*, void*);
// セカンダリーサービス
const struct ble_gatt_svc_def gatt_secondary_services[] = {
{ // バッテリーサービス
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(BLE_SVC_BAS_UUID16),
.includes = NULL,
.characteristics = (struct ble_gatt_chr_def[]){
{ // バッテリー残量 キャラクタリスティック
.uuid = BLE_UUID16_DECLARE(BLE_SVC_BAS_CHR_UUID16_BATTERY_LEVEL),
.access_cb = battery_access,
.arg = (void *) HANDLE_BATTERY_LEVEL,
.val_handle = &handles_list[HANDLE_BATTERY_LEVEL],
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE,
.min_key_size = 0,
.descriptors = (struct ble_gatt_dsc_def[]) {
{
.uuid = BLE_UUID16_DECLARE(GATT_UUID_BAT_PRESENT_DESCR),
.att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC,
.access_cb = battery_access,
.arg = NULL,
.min_key_size = 0
}, {0} // ディスクリプタ終端
}
}, {0} // キャラクタリスティック終端
}
},
{ // デバイス情報サービス(DIS)
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_UUID_DIS_SERVICE),
.includes = NULL,
.characteristics = (struct ble_gatt_chr_def[]) {
// キャラクタリスティック
{ // 型番
.uuid = BLE_UUID16_DECLARE(GATT_UUID_DIS_MODEL_NUMBER),
.access_cb = dis_access,
.val_handle = &handles_list[HANDLE_DIS_MODEL_NUMBER],
.flags = BLE_GATT_CHR_F_READ,
.descriptors = NULL,
.arg = NULL,
.min_key_size = 0,
},
{ // シリアル番号
.uuid = BLE_UUID16_DECLARE(GATT_UUID_DIS_SERIAL_NUMBER),
.access_cb = dis_access,
.val_handle = &handles_list[HANDLE_DIS_SERIAL_NUMBER],
.flags = BLE_GATT_CHR_F_READ,
.descriptors = NULL,
.arg = NULL,
.min_key_size = 0,
},
{ // ハードウェアリビジョン
.uuid = BLE_UUID16_DECLARE(GATT_UUID_DIS_HARDWARE_REVISION),
.access_cb = dis_access,
.val_handle = &handles_list[HANDLE_DIS_HARDWARE_REVISION],
.flags = BLE_GATT_CHR_F_READ,
.descriptors = NULL,
.arg = NULL,
.min_key_size = 0,
},
{ // ファームウェアリビジョン
.uuid = BLE_UUID16_DECLARE(GATT_UUID_DIS_FIRMWARE_REVISION),
.access_cb = dis_access,
.val_handle = &handles_list[HANDLE_DIS_FIRMWARE_REVISION],
.flags = BLE_GATT_CHR_F_READ,
.descriptors = NULL,
.arg = NULL,
.min_key_size = 0,
},
{ // ソフトウェアリビジョン
.uuid = BLE_UUID16_DECLARE(GATT_UUID_DIS_SOFTWARE_REVISION),
.access_cb = dis_access,
.val_handle = &handles_list[HANDLE_DIS_SOFWARE_REVISION],
.flags = BLE_GATT_CHR_F_READ,
.descriptors = NULL,
.arg = NULL,
.min_key_size = 0,
},
{ // 製造者名
.uuid = BLE_UUID16_DECLARE(GATT_UUID_DIS_MANUFACTURER_NAME),
.access_cb = dis_access,
.val_handle = &handles_list[HANDLE_DIS_MANUFACTURER_NAME],
.flags = BLE_GATT_CHR_F_READ,
.descriptors = NULL,
.arg = NULL,
.min_key_size = 0,
},
{ // システムID
.uuid = BLE_UUID16_DECLARE(GATT_UUID_DIS_SYSTEM_ID),
.access_cb = dis_access,
.val_handle = &handles_list[HANDLE_DIS_SYSTEM_ID],
.flags = BLE_GATT_CHR_F_READ,
.descriptors = NULL,
.arg = NULL,
.min_key_size = 0,
},
{ // PNP
.uuid = BLE_UUID16_DECLARE(GATT_UUID_DIS_PNP_INFO),
.access_cb = dis_access,
.val_handle = &handles_list[HANDLE_DIS_PNP_INFO],
.flags = BLE_GATT_CHR_F_READ,
.descriptors = NULL,
.arg = NULL,
.min_key_size = 0,
}, {0} // キャラクタリスティック終端
}
}, {0} // サービス終端
};
const struct ble_gatt_svc_def *included_services_array[] = {
&gatt_secondary_services[0],
NULL,
};
// プライマーサービス
const struct ble_gatt_svc_def gatt_primary_services[] = {
{ // HID サービス
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_UUID_HID_SERVICE),
.includes = included_services_array,
.characteristics = (struct ble_gatt_chr_def[]){
{ // HID情報 キャラクタリスティック
.uuid = BLE_UUID16_DECLARE(GATT_UUID_HID_INFORMATION),
.access_cb = hid_chr_access,
.val_handle = &handles_list[HANDLE_HID_INFORMATION],
.flags = BLE_GATT_CHR_F_READ,
.descriptors = NULL,
.arg = NULL,
.min_key_size = 0,
},
{ // HID Control Point キャラクタリスティック
.uuid = BLE_UUID16_DECLARE(GATT_UUID_HID_CONTROL_POINT),
.access_cb = hid_chr_access,
.val_handle = &handles_list[HANDLE_HID_CONTROL_POINT],
.flags = BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_READ,
.descriptors = NULL,
.arg = NULL,
.min_key_size = 0,
},
{ // レポートマップ キャラクタリスティック
.uuid = BLE_UUID16_DECLARE(GATT_UUID_HID_REPORT_MAP),
.access_cb = hid_chr_access,
.val_handle = &handles_list[HANDLE_HID_REPORT_MAP],
.flags = BLE_GATT_CHR_F_READ,
.arg = NULL,
.min_key_size = 0,
.descriptors = (struct ble_gatt_dsc_def[]) {
{ // 外部レポート参照 ディスクリプタ
.uuid = BLE_UUID16_DECLARE(GATT_UUID_EXT_RPT_REF_DESCR),
.att_flags = BLE_ATT_F_READ,
.access_cb = hid_chr_access,
.arg = NULL, .min_key_size = 0,
}, {0} // ディスクリプタ終端
}
},
{ // キーボードHID入力レポート キャラクタリスティック
.uuid = BLE_UUID16_DECLARE(GATT_UUID_HID_REPORT),
.access_cb = hid_report_access,
.arg = (void *)HANDLE_HID_KB_IN_REPORT,
.val_handle = &handles_list[HANDLE_HID_KB_IN_REPORT],
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.min_key_size = 0,
.descriptors = (struct ble_gatt_dsc_def[]) {
{ // レポート参照ディスクリプタ
.uuid = BLE_UUID16_DECLARE(GATT_UUID_RPT_REF_DESCR),
.att_flags = BLE_ATT_F_READ,
.access_cb = hid_report_access,
.arg = (void *)HANDLE_HID_KB_IN_REPORT,
.min_key_size = 0,
}, {0} // ディスクリプタ終端
},
},
{ // キーボードHID出力レポート (LED IN/OUT) キャラクタリスティック
.uuid = BLE_UUID16_DECLARE(GATT_UUID_HID_REPORT),
.access_cb = hid_report_access,
.arg = (void *)HANDLE_HID_KB_OUT_REPORT,
.val_handle = &handles_list[HANDLE_HID_KB_OUT_REPORT],
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_NO_RSP,
.min_key_size = 0,
.descriptors = (struct ble_gatt_dsc_def[]) {
{ // レポート参照ディスクリプタ
.uuid = BLE_UUID16_DECLARE(GATT_UUID_RPT_REF_DESCR),
.att_flags = BLE_ATT_F_READ,
.access_cb = hid_report_access,
.arg = (void *)HANDLE_HID_KB_OUT_REPORT,
.min_key_size = 0,
}, {0} // ディスクリプタ終端
},
},
{ // プロトコルモード キャラクタリスティック
.uuid = BLE_UUID16_DECLARE(GATT_UUID_HID_PROTOCOL_MODE),
.access_cb = hid_chr_access,
.val_handle = &handles_list[HANDLE_HID_PROTOCOL_MODE],
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
.descriptors = NULL,
.arg = NULL,
.min_key_size = 0,
},
{ // キーボード入力BOOTモード時HIDレポート キャラクタリスティック
.uuid = BLE_UUID16_DECLARE(GATT_UUID_HID_BT_KB_INPUT),
.access_cb = hid_report_access,
.arg = (void *)HANDLE_HID_BOOT_KB_IN_REPORT,
.val_handle = &handles_list[HANDLE_HID_BOOT_KB_IN_REPORT],
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE,
.descriptors = NULL,
.min_key_size = 0,
},
{ // キーボード出力BOOTモード時HIDレポート キャラクタリスティック
.uuid = BLE_UUID16_DECLARE(GATT_UUID_HID_BT_KB_OUTPUT),
.access_cb = hid_report_access,
.arg = (void *)HANDLE_HID_BOOT_KB_OUT_REPORT,
.val_handle = &handles_list[HANDLE_HID_BOOT_KB_OUT_REPORT],
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_NO_RSP,
.descriptors = NULL,
.min_key_size = 0,
},
{0} // キャラクタリスティック終端
}
},
{0} // サービス終端
};
// バッテリ残量の単位情報
gatt_cpf_struct battery_level_units = {
.format = GATT_CPF_FORMAT_UINT8,
.exponent = 0,
.unit = 0x27AD, // 単位:パーセント
.name_space = GATT_NAME_SPACE_BLUETOOTH_SIG,
.description = 0
};
int battery_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg){
uint16_t uuid16 = ble_uuid_u16(ctxt->chr->uuid);
int rc = 0;
MODLOG_DFLT(INFO, "%s: UUID %04X attr %04X arg %d op %d", __FUNCTION__, uuid16, attr_handle, (int) arg, ctxt->op);
switch (uuid16){
case BLE_SVC_BAS_CHR_UUID16_BATTERY_LEVEL:
if (ctxt->op != BLE_GATT_ACCESS_OP_READ_CHR){
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
rc = os_mbuf_append(ctxt->om, &my_device.battery_level, sizeof(my_device.battery_level));
if(rc){
MODLOG_DFLT(ERROR, "Failed to read battery buffer : %d", rc);
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
}
break;
case GATT_UUID_BAT_PRESENT_DESCR:
if(ctxt->op != BLE_GATT_ACCESS_OP_READ_DSC){
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
rc = os_mbuf_append(ctxt->om, &battery_level_units, sizeof battery_level_units);
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
}
break;
default:
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
return rc;
}
int dis_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg){
uint16_t uuid16 = ble_uuid_u16(ctxt->chr->uuid);
int rc = 0;
const char *info = NULL;
int len = 0;
MODLOG_DFLT(INFO, "%s: UUID %04X attr %04X arg %d op %d", __FUNCTION__, uuid16, attr_handle, (int) arg, ctxt->op);
if (ctxt->op != BLE_GATT_ACCESS_OP_READ_CHR){
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
return BLE_ATT_ERR_UNLIKELY;
}
switch(uuid16){
case GATT_UUID_DIS_MODEL_NUMBER:
info = GATT_DIS_MODEL_NUMBER;
break;
case GATT_UUID_DIS_SERIAL_NUMBER:
info = GATT_DIS_SERIAL_NUMBER;
break;
case GATT_UUID_DIS_FIRMWARE_REVISION:
info = GATT_DIS_FIRMWARE_REVISION;
break;
case GATT_UUID_DIS_HARDWARE_REVISION:
info = GATT_DIS_HARDWARE_REVISION;
break;
case GATT_UUID_DIS_SOFTWARE_REVISION:
info = GATT_DIS_SOFTWARE_REVISION;
break;
case GATT_UUID_DIS_MANUFACTURER_NAME:
info = GATT_DIS_MANUFACTURER_NAME;
break;
case GATT_UUID_DIS_SYSTEM_ID:
info = GATT_DIS_SYSTEM_ID;
len = sizeof(GATT_DIS_SYSTEM_ID);
break;
case GATT_UUID_DIS_PNP_INFO:
info = GATT_DIS_PNP_INFO;
len = sizeof(GATT_DIS_PNP_INFO);
break;
default:
MODLOG_DFLT(ERROR, "Invalid characteristic.");
rc = BLE_ATT_ERR_UNLIKELY;
}
if(info != NULL){
if(len == 0){
len = strlen(info);
}
rc = os_mbuf_append(ctxt->om, info, len);
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
}
}
return rc;
}
int hid_chr_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg){
uint16_t uuid16 = ble_uuid_u16(ctxt->chr->uuid);
int rc = 0;
uint8_t new_state;
uint16_t len;
MODLOG_DFLT(INFO, "%s: UUID %04X attr %04X arg %d op %d", __FUNCTION__, uuid16, attr_handle, (int) arg, ctxt->op);
switch(uuid16){
case GATT_UUID_HID_INFORMATION:
MODLOG_DFLT(INFO, "HID INFORMATION");
if(ctxt->op != BLE_GATT_ACCESS_OP_READ_CHR){
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
rc = os_mbuf_append(ctxt->om, hid_info, sizeof(hid_info));
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
}
break;
case GATT_UUID_HID_CONTROL_POINT:
MODLOG_DFLT(INFO, "HID CONTROL POINT");
if(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR){
rc = ble_hs_mbuf_to_flat(ctxt->om, &new_state, 1, &len);
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
break;
}
MODLOG_DFLT(INFO, "HID_CONTROL_POINT: new state:%d, old state:%d", (int)new_state, (int)my_device.control_point);
my_device.control_point = new_state;
if(new_state == HID_CTRL_POINT_SUSPEND){
my_device.suspended = true;
}else if(new_state == HID_CTRL_POINT_EXIT_SUSPEND){
my_device.suspended = false;
}
// 値を変えるだけで特に何もする予定なし
}else if(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR){
rc = os_mbuf_append(ctxt->om, &my_device.control_point, sizeof(my_device.control_point));
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
}
}else{
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
break;
case GATT_UUID_HID_REPORT_MAP:
MODLOG_DFLT(INFO, "HID REPORT MAP");
if(ctxt->op != BLE_GATT_ACCESS_OP_READ_CHR){
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
rc = os_mbuf_append(ctxt->om, hid_report_map, sizeof(hid_report_map));
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
}
break;
case GATT_UUID_HID_PROTOCOL_MODE:
MODLOG_DFLT(INFO, "HID PROTOCOL MODE");
if(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
if(my_device.protocol_mode == HID_PROTOCOL_MODE_BOOT){
MODLOG_DFLT(INFO, "Now protocol mode: BOOT");
}else{
MODLOG_DFLT(INFO, "Now protocol mode: REPORT");
}
rc = os_mbuf_append(ctxt->om, &my_device.protocol_mode, sizeof(my_device.protocol_mode));
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
}
}else if(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR){
rc = ble_hs_mbuf_to_flat(ctxt->om, &my_device.protocol_mode, sizeof(my_device.protocol_mode), &len);
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
break;
}
if(my_device.protocol_mode == HID_PROTOCOL_MODE_BOOT){
MODLOG_DFLT(INFO, "New protocol mode: BOOT");
}else{
MODLOG_DFLT(INFO, "New protocol mode: REPORT");
}
}else{
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
rc = BLE_ATT_ERR_UNLIKELY;
}
break;
case GATT_UUID_EXT_RPT_REF_DESCR:
MODLOG_DFLT(INFO, "GATT_UUID_EXT_RPT_REF_DESCR");
if(ctxt->op != BLE_GATT_ACCESS_OP_READ_DSC){
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
rc = os_mbuf_append(ctxt->om, (uint8_t *)&hid_ext_report_ref_desc, sizeof(hid_ext_report_ref_desc));
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
}
break;
default:
MODLOG_DFLT(ERROR, "Invalid UUID %02X", uuid16);
break;
}
return rc;
}
int hid_report_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg){
uint16_t uuid16 = ble_uuid_u16(ctxt->chr->uuid);
int rc = 0;
int handle_num = (int)arg;
uint8_t buffer[16];
uint16_t len;
MODLOG_DFLT(INFO, "%s: UUID %04X attr %04X arg %d op %d", __FUNCTION__, uuid16, attr_handle, (int) arg, ctxt->op);
switch(uuid16){
case GATT_UUID_HID_BT_KB_INPUT :
//キーボード入力BOOTモード時HIDレポート
MODLOG_DFLT(INFO, "Keyboard boot in report");
if(ctxt->op != BLE_GATT_ACCESS_OP_READ_CHR){
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
memset(buffer, 0, HID_BOOT_REPORT_SIZE_KEYBOARD);
buffer[0] = HID_BOOT_REPORT_ID_KEYBOARD;
buffer[1] = my_device.keycode[0]; // 修飾キー
buffer[3] = my_device.keycode[1];
buffer[4] = my_device.keycode[2];
buffer[5] = my_device.keycode[3];
buffer[6] = my_device.keycode[4];
buffer[7] = my_device.keycode[5];
buffer[8] = my_device.keycode[6];
rc = os_mbuf_append(ctxt->om, buffer, HID_BOOT_REPORT_SIZE_KEYBOARD);
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
}
break;
case GATT_UUID_HID_BT_KB_OUTPUT :
MODLOG_DFLT(INFO, "Keyboard boot out report");
// キーボード出力BOOTモード時HIDレポート (LED)
if(ctxt->op != BLE_GATT_ACCESS_OP_WRITE_CHR){
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
rc = ble_hs_mbuf_to_flat(ctxt->om, buffer, 2, &len);
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
break;
}
break;
case GATT_UUID_RPT_REF_DESCR :
// キーボードのレポートIn/Out ディスクリプタ
MODLOG_DFLT(INFO, "Keyboard report discriptor");
if(ctxt->op != BLE_GATT_ACCESS_OP_READ_DSC){
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
if(handle_num == HANDLE_HID_KB_IN_REPORT){
buffer[0] = HID_REPORT_ID_KB_IN;
buffer[1] = HID_REPORT_TYPE_INPUT;
}else if(handle_num == HANDLE_HID_KB_OUT_REPORT){
buffer[0] = HID_REPORT_ID_KB_IN;
buffer[1] = HID_REPORT_TYPE_OUTPUT;
}else{
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
rc = os_mbuf_append(ctxt->om, buffer, 2);
if(rc){
rc = BLE_ATT_ERR_INSUFFICIENT_RES;
}
break;
case GATT_UUID_HID_REPORT :
// キーボード レポートモード レポート
MODLOG_DFLT(INFO, "Keyboard report");
if(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR){
if(handle_num == HANDLE_HID_KB_IN_REPORT){
// キーコードを送信
rc = os_mbuf_append(ctxt->om, my_device.keycode, HID_REPORT_SIZE_KEYBOARD_IN);
MODLOG_DFLT(INFO, "Send keycode %02X %02X %02X %02X %02X %02X %02X %02X",
my_device.keycode[0], my_device.keycode[1], my_device.keycode[2], my_device.keycode[3],
my_device.keycode[4], my_device.keycode[5], my_device.keycode[6], my_device.keycode[7]);
}else if(handle_num == HANDLE_HID_KB_OUT_REPORT){
buffer[0] = 0;
rc = os_mbuf_append(ctxt->om, buffer, HID_REPORT_SIZE_KEYBOARD_OUT);
MODLOG_DFLT(INFO, "Send LED %02X", buffer[0]);
}else{
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
}else if(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR){
// NumLockなどのLED情報を取得
rc = ble_hs_mbuf_to_flat(ctxt->om, buffer, HID_REPORT_SIZE_KEYBOARD_OUT, &len);
MODLOG_DFLT(INFO, "recv %02X", buffer[0]);
}else{
MODLOG_DFLT(ERROR, "Invalid operation %d", ctxt->op);
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
break;
default:
rc = BLE_ATT_ERR_UNLIKELY;
break;
}
return rc;
}
void ble_advertise();
int ble_gap_event_callback(struct ble_gap_event *event, void *arg){
struct ble_gap_conn_desc desc;
struct ble_sm_io pkey = {0};
int rc;
int i;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
// 新規接続が確立したか、失敗したか
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "Connection is established.");
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
MODLOG_DFLT(INFO,"Info\n"
" handle : %d\n"
" our_ota_addr_type : %d\n"
" our_ota_addr : %02x:%02x:%02x:%02x:%02x:%02x\n"
" our_id_addr_type : %d\n"
" our_id_addr : %02x:%02x:%02x:%02x:%02x:%02x\n"
" peer_ota_addr_type : %d\n"
" peer_ota_addr : %02x:%02x:%02x:%02x:%02x:%02x\n"
" peer_id_addr_type : %d\n"
" peer_id_addr : %02x:%02x:%02x:%02x:%02x:%02x\n"
" conn_itvl : %d\n"
" conn_latency : %d\n"
" supervision_timeout : %d\n"
" encrypted : %d\n"
" authenticated : %d\n"
" bonded : %d\n",
desc.conn_handle,
desc.our_ota_addr.type,
MAC2STR_REV(desc.our_ota_addr.val),
desc.our_id_addr.type,
MAC2STR_REV(desc.our_id_addr.val),
desc.peer_ota_addr.type,
MAC2STR_REV(desc.peer_ota_addr.val),
desc.peer_id_addr.type,
MAC2STR_REV(desc.peer_id_addr.val),
desc.conn_itvl, desc.conn_latency,
desc.supervision_timeout,
desc.sec_state.encrypted,
desc.sec_state.authenticated,
desc.sec_state.bonded);
my_device.handle = desc.conn_handle;
my_device.connected = true;
memset(my_device.notify, 0, sizeof(my_device.notify));
memset(my_device.indicate, 0, sizeof(my_device.indicate));
memset(my_device.keycode, 0, HID_REPORT_SIZE_KEYBOARD_IN);
my_device.protocol_mode = HID_PROTOCOL_MODE_REPORT;
my_device.control_point = HID_CTRL_POINT_NOP;
}else{
MODLOG_DFLT(INFO, "connection is failed; status=%d ", event->connect.status);
ble_advertise();
}
break;
case BLE_GAP_EVENT_DISCONNECT:
// 切断時
MODLOG_DFLT(INFO, "Disconnect : 0x%04x ", event->disconnect.reason);
//https://mynewt.apache.org/latest/network/ble_hs/ble_hs_return_codes.html
my_device.connected = false;
ble_advertise();
break;
case BLE_GAP_EVENT_CONN_UPDATE_REQ:
// セントラルから接続パラメタの更新を要求された
MODLOG_DFLT(INFO, "Connection update request");
break;
case BLE_GAP_EVENT_CONN_UPDATE:
// セントラルが接続パラメタを更新した
MODLOG_DFLT(INFO, "Connection updated : %d ", event->conn_update.status);
break;
case BLE_GAP_EVENT_ADV_COMPLETE:
// アドバタイジングが完了
MODLOG_DFLT(INFO, "Advertise complete : %d", event->adv_complete.reason);
ble_advertise();
break;
case BLE_GAP_EVENT_ENC_CHANGE:
// 今回の接続で暗号化が有効/無効された。
MODLOG_DFLT(INFO, "Encryption is changed : %d ",event->enc_change.status);
break;
case BLE_GAP_EVENT_SUBSCRIBE:
MODLOG_DFLT(INFO,
"Subscribe event\n"
" conn_handle : %d\n"
" attr_handle : %04X\n"
" reason : %d\n"
" prev_notify : %d\n"
" cur_notify : %d\n"
" prev_indicate : %d\n"
" cur_indicate : %d\n",
event->subscribe.conn_handle,
event->subscribe.attr_handle,
event->subscribe.reason,
event->subscribe.prev_notify,
event->subscribe.cur_notify,
event->subscribe.prev_indicate,
event->subscribe.cur_indicate);
for(i=0;i<HANDLE_NUM;i++){
if(handles_list[i] == event->subscribe.attr_handle){
my_device.notify[i] = true;
MODLOG_DFLT(INFO,"notify index : %d", i);
break;
}
}
break;
case BLE_GAP_EVENT_NOTIFY_TX:
MODLOG_DFLT(INFO,
"Notify event\n"
" status : %d\n"
" conn_handle : %d\n"
" attr_handle : %04X\n"
" type : %s\n",
event->notify_tx.status,
event->notify_tx.conn_handle,
event->notify_tx.attr_handle,
event->notify_tx.indication?"indicate":"notify");
break;
case BLE_GAP_EVENT_MTU:
// Maximum Transmission Unit 送信パケットの上限サイズの変更
MODLOG_DFLT(INFO,
"MTU update event.\n"
" conn_handle : %d\n"
" cid : %d\n"
" mtu : %d\n",
event->mtu.conn_handle,
event->mtu.channel_id,
event->mtu.value);
break;
case BLE_GAP_EVENT_REPEAT_PAIRING:
MODLOG_DFLT(INFO, "BLE_GAP_EVENT_REPEAT_PAIRING");
// すでにボンド済み(ペアリングで交換した鍵を保存済み)であったが、新しくセキュアリンクの確立が試みられた。
// 古いボンドは削除し、新しいリンクを受け入れる
// 古いボンドを削除
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
assert(rc == 0);
ble_store_util_delete_peer(&desc.peer_id_addr);
// ホストにペアリングを続行するように通知
return BLE_GAP_REPEAT_PAIRING_RETRY;
case BLE_GAP_EVENT_PASSKEY_ACTION:
MODLOG_DFLT(INFO, "PASSKEY_ACTION_EVENT started");
if(event->passkey.params.action == BLE_SM_IOACT_DISP){
MODLOG_DFLT(INFO, "Enter passkey on the peer side");
pkey.action = event->passkey.params.action;
pkey.passkey = PASS_KEY; // ホスト側に入力してもらうパスキー
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
MODLOG_DFLT(INFO, "ble_sm_inject_io result: %d", rc);
}else if (event->passkey.params.action == BLE_SM_IOACT_INPUT ||
event->passkey.params.action == BLE_SM_IOACT_NUMCMP ||
event->passkey.params.action == BLE_SM_IOACT_OOB ) {
MODLOG_DFLT(ERROR, "BLE_SM_IOACT_INPUT, BLE_SM_IOACT_NUMCMP, BLE_SM_IOACT_OOB"
" bonding not supported!");
}
break;
default:
MODLOG_DFLT(INFO, "Unknown GAP event: %d", event->type);
break;
}
return 0;
}
int user_parse(const struct ble_hs_adv_field *data, void *arg){
MODLOG_DFLT(INFO, "Parse data: field len %d, type %x, total %d bytes",
data->length, data->type, data->length + 2); /* first byte type and second byte length */
return 1;
}
void ble_advertise(){
struct ble_gap_adv_params adv_params;
struct ble_hs_adv_fields fields;
const char *name;
int rc;
/**
* Set the advertisement data included in our advertisements:
* o Flags (indicates advertisement type and other general info).
* o Advertising tx power.
* o Device name.
* o 16-bit service UUIDs (alert notifications).
*/
memset(&fields, 0, sizeof fields);
/* Advertise two flags:
* o Discoverability in forthcoming advertisement (general)
* o BLE-only (BR/EDR unsupported).
*/
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
/* Indicate that the TX power level field should be included; have the
* stack fill this value automatically. This is done by assigning the
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
*/
fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
//fields.adv_itvl_is_present = 1;
//fields.adv_itvl = 40;
name = ble_svc_gap_device_name();
fields.name = (uint8_t *)name;
fields.name_len = strlen(name);
fields.name_is_complete = 1;
fields.appearance = BLE_SVC_GAP_APPEARANCE;
fields.appearance_is_present = 1;
fields.uuids16 = (ble_uuid16_t[]) {
BLE_UUID16_INIT(GATT_UUID_HID_SERVICE)
};
fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1;
uint8_t buf[50], buf_sz;
rc = ble_hs_adv_set_fields(&fields, buf, &buf_sz, 50);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error setting advertisement data to buf; rc=%d", rc);
return;
}
if (buf_sz > BLE_HS_ADV_MAX_SZ) {
MODLOG_DFLT(ERROR,
"Too long advertising data.\n"
" name : %s\n"
" appearance : %x\n"
" uuid16 : %x\n"
" advsize : %d\n",
name,
fields.appearance,
GATT_UUID_HID_SERVICE,
buf_sz);
ble_hs_adv_parse(buf, buf_sz, user_parse, NULL);
return;
}
rc = ble_gap_adv_set_fields(&fields);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to set advertisement data : %d", rc);
return;
}
/* Begin advertising. */
memset(&adv_params, 0, sizeof adv_params);
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
rc = ble_gap_adv_start(my_device.own_addr_type, NULL, BLE_HS_FOREVER, &adv_params, ble_gap_event_callback, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to enable advertisement : %d", rc);
return;
}
}
void ble_on_sync(void){
uint8_t addr[6] = {0};
int rc;
//ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA);
rc = ble_hs_util_ensure_addr(1);
assert(rc == 0);
// アドバタイジング中に使用しているアドレスを把握する
rc = ble_hs_id_infer_auto(
1, // プライベートアドレスをを使うかどうか。0=使わない 1=使う
&my_device.own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to determine address type : %d", rc);
return;
}
// 自身のアドレスを取得表示
rc = ble_hs_id_copy_addr(my_device.own_addr_type, addr, NULL);
MODLOG_DFLT(INFO, "Device Address: %02x:%02x:%02x:%02x:%02x:%02x",MAC2STR_REV(addr));
// アドバタイズ開始
ble_advertise();
}
void ble_on_reset(int reason){
MODLOG_DFLT(ERROR, "Resetting state : %d", reason);
}
void ble_hid_host_task(void *param){
MODLOG_DFLT(INFO, "BLE Host Task Started.");
nimble_port_run();
nimble_port_freertos_deinit();
}
void gatt_register_callback(struct ble_gatt_register_ctxt *ctxt, void *arg){
char buff[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
MODLOG_DFLT(INFO, "register service:%s handle:%d",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buff),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
MODLOG_DFLT(INFO, "register characteristic %s def:%d val:%d",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buff),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
MODLOG_DFLT(INFO, "register descriptor %s handle:%d",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buff),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
int ble_init_server(){
esp_err_t ret;
int rc;
// ホストとコントローラーのスタックを初期化
ret = nimble_port_init();
if (ret != ESP_OK) {
MODLOG_DFLT(ERROR, "Failed to init nimble %d", ret);
return ret;
}
// NimBLEホスト設定
ble_hs_cfg.sync_cb = ble_on_sync; // ホストが同期したときに呼ばれる関数(スタート時やリセット時)
ble_hs_cfg.reset_cb = ble_on_reset; // 深刻なエラーが発生でリセットがかかったときに呼ばれる関数
ble_hs_cfg.gatts_register_cb = gatt_register_callback; // GATTサーバーのイベントコールバック
ble_hs_cfg.store_status_cb = ble_store_util_status_rr; // 不揮発性の情報を保存する際のコールバック関数
ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO; // ペアリングする際の入力や確認手段
// セキュアボンディング
ble_hs_cfg.sm_bonding = 1; // ボンディング(ペアリングで交換した鍵を保存する)
ble_hs_cfg.sm_mitm = 0; // 中間者攻撃保護
ble_hs_cfg.sm_sc = 1;
ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
// デバイス名を設定
rc = ble_svc_gap_device_name_set(BLE_DEVICE_NAME);
if(rc){
MODLOG_DFLT(ERROR, "Failed to set device name. %d", rc);
return rc;
}
// 外観
rc = ble_svc_gap_device_appearance_set(BLE_SVC_GAP_APPEARANCE);
if(rc){
MODLOG_DFLT(ERROR, "Failed to set device appearance. %d", rc);
return rc;
}
ble_store_config_init();
//
MODLOG_DFLT(INFO, "Initialize GAT & GATT");
ble_svc_gap_init();
ble_svc_gatt_init();
memset(handles_list, 0, sizeof(handles_list));
// セカンダリーサービスを登録
rc = ble_gatts_count_cfg(gatt_secondary_services);
if(rc){
return rc;
}
rc = ble_gatts_add_svcs(gatt_secondary_services);
if(rc){
return rc;
}
MODLOG_DFLT(INFO, "GATT secondary_services added");
// プライマリーサービスを登録
rc = ble_gatts_count_cfg(gatt_primary_services);
if(rc){
return rc;
}
rc = ble_gatts_add_svcs(gatt_primary_services);
if(rc){
return rc;
}
MODLOG_DFLT(INFO, "GATT primary services added");
nimble_port_freertos_init(ble_hid_host_task);
return 0;
}
bool ble_is_connected(){
return my_device.connected;
}
void send_text(const char *text){
uint8_t numpad[] = {0x27, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26};
uint8_t i;
MODLOG_DFLT(INFO, "Send key.");
if(my_device.notify[HANDLE_HID_KB_IN_REPORT] == false){
MODLOG_DFLT(ERROR, "Not subscribed.");
return;
}
memset(my_device.keycode, 0, HID_REPORT_SIZE_KEYBOARD_IN);
i = 0;
while(text[i]){
switch(text[i]){
case '0':
my_device.keycode[1] = numpad[0];
break;
case '1':
my_device.keycode[1] = numpad[1];
break;
case '2':
my_device.keycode[1] = numpad[2];
break;
case '3':
my_device.keycode[1] = numpad[3];
break;
case '4':
my_device.keycode[1] = numpad[4];
break;
case '5':
my_device.keycode[1] = numpad[5];
break;
case '6':
my_device.keycode[1] = numpad[6];
break;
case '7':
my_device.keycode[1] = numpad[7];
break;
case '8':
my_device.keycode[1] = numpad[8];
break;
case '9':
my_device.keycode[1] = numpad[9];
break;
case '\n':
my_device.keycode[1] = 0x28;
break;
default:
continue;
}
ble_gatts_notify(my_device.handle, handles_list[HANDLE_HID_KB_IN_REPORT]);
vTaskDelay(10/portTICK_PERIOD_MS);
i++;
}
my_device.keycode[1] = 0;
ble_gatts_notify(my_device.handle, handles_list[HANDLE_HID_KB_IN_REPORT]);
return;
}
main.c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/semphr.h"
#include <nvs_flash.h>
#include <esp_system.h>
#include <esp_event.h>
#include "ble_keyboard.h"
void app_main(void){
esp_err_t ret;
// NVS(不揮発性メモリ)を初期化
MODLOG_DFLT(INFO, "Initialize NVS");
ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// BLEサーバーを初期化
ble_init_server();
while(1){
vTaskDelay(5000/portTICK_PERIOD_MS);
if(ble_is_connected()){
vTaskDelay(1000/portTICK_PERIOD_MS);
send_text("1234567890\n");
}
}
}
コメント