ble nano hid over gatt

Dependencies:   BLE_API mbed-dev nRF51822

HIDController_BLE.cpp

Committer:
cho45
Date:
2016-07-21
Revision:
10:1aed2481a743
Parent:
9:d1daefbf1fbd
Child:
13:b0ffdf2012b9

File content as of revision 10:1aed2481a743:

#include "mbed.h"
#include "BLE.h"
#include "KeyboardService.h"
#include "BatteryService.h"
#include "DeviceInformationService.h"
#include "HIDController_BLE.h"

static const char MODEL_NAME[] = "keyboard";
static const char SERIAL_NUMBER[] = "X00000";
static const char HARDWARE_REVISION[] = "0.1";
static const char FIRMWARE_REVISION[] = "0.1";
static const char SOFTWARE_REVISION[] = "0.0";

static const uint8_t DEVICE_NAME[] = "my keyboard";
static const uint8_t SHORT_DEVICE_NAME[] = "kbd1";

static const bool ENABLE_BONDING = true;
static const bool REQUIRE_MITM = true;
static const uint8_t PASSKEY[6] = {'1','2','3','4','5','6'}; // must be 6-digits number

static const uint16_t uuid16_list[] =  {
	GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE,
	GattService::UUID_DEVICE_INFORMATION_SERVICE,
	GattService::UUID_BATTERY_SERVICE
};

static KeyboardService* keyboardService;
static BatteryService* batteryService;
static DeviceInformationService* deviceInformationService;

static void updateBatteryLevel() {
	if (!batteryService) return;
	static const float BATTERY_MAX = 2.4;
	static const float REFERNECE = 1.2;
	static const float PRESCALE = 3;

	NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Enabled;

	// Use internal 1.2V reference for batteryInput
	//	1/3 pre-scaled input and 1.2V internal band gap reference
	// ref. mbed-src/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/analogin_api.c
	NRF_ADC->CONFIG =
		(ADC_CONFIG_RES_10bit << ADC_CONFIG_RES_Pos) |
		// Use VDD 1/3 for input
		(ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
		// Use internal band gap for reference
		(ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |
		(ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos);

	// Start ADC
	NRF_ADC->TASKS_START = 1;
	while (((NRF_ADC->BUSY & ADC_BUSY_BUSY_Msk) >> ADC_BUSY_BUSY_Pos) == ADC_BUSY_BUSY_Busy) {
		// busy loop
	}

	// Read ADC result
	uint16_t raw10bit = static_cast<uint16_t>(NRF_ADC->RESULT);
	float ratio = raw10bit / static_cast<float>(1<<10);

	float batteryVoltage = ratio * (REFERNECE * PRESCALE);
	float percentage = (batteryVoltage / BATTERY_MAX) * 100;
	if (percentage > 100) {
		percentage = 100;
	}
	printf("updateBatteryLevel %f V : %d/100\r\n", batteryVoltage, static_cast<uint8_t>(percentage));
	batteryService->updateBatteryLevel(static_cast<uint8_t>(percentage));
}


static void onConnect(const Gap::ConnectionCallbackParams_t *params) {
	printf("onConnect\r\n");
	// If a bond is created, the HID Device should write the address of the HID Host in the HID Device controller’s white list and set the HID Device controller’s advertising filter policy to ‘process scan and connection requests only from devices in the White List’.
}

static void onDisconnect(const Gap::DisconnectionCallbackParams_t *params) {
	printf("onDisconnect\r\n");
	BLE::Instance(BLE::DEFAULT_INSTANCE).gap().startAdvertising();
}

static void onTimeout(const Gap::TimeoutSource_t source) {
	printf("onTimeout\r\n");
	BLE::Instance(BLE::DEFAULT_INSTANCE).gap().startAdvertising();
}

static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey) {
	printf("Input passKey: ");
	for (unsigned i = 0; i < Gap::ADDR_LEN; i++) {
		printf("%c", passkey[i]);
	}
	printf("\r\n");
}

static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status) {
	if (status == SecurityManager::SEC_STATUS_SUCCESS) {
		printf("Security success %d\r\n", status);
	} else {
		printf("Security failed %d\r\n", status);
	}
}

static void securitySetupInitiatedCallback(Gap::Handle_t, bool allowBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps) {
	printf("Security setup initiated\r\n");
}

static void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) {
	// https://developer.mbed.org/compiler/#nav:/keyboard/BLE_API/ble/blecommon.h;
	ble_error_t error;
	BLE &ble          = params->ble;
	
	error = params->error;
	if (error != BLE_ERROR_NONE) {
		printf("error on ble.init() \r\n");
		goto return_error;
	}

	ble.gap().onDisconnection(onDisconnect);
	ble.gap().onConnection(onConnect);
	ble.gap().onTimeout(onTimeout);

	printf("setup ble security manager\r\n");
	ble.securityManager().onSecuritySetupInitiated(securitySetupInitiatedCallback);
	ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback);
	ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
	// bonding with hard-coded passkey.
	error = ble.securityManager().init(ENABLE_BONDING, REQUIRE_MITM, SecurityManager::IO_CAPS_DISPLAY_ONLY, PASSKEY);
	if (error != BLE_ERROR_NONE) {
		printf("error on ble.securityManager().init()");
		goto return_error;
	}

	keyboardService = new KeyboardService(ble);
	deviceInformationService = new DeviceInformationService(ble, "lowreal.net", MODEL_NAME, SERIAL_NUMBER, HARDWARE_REVISION, FIRMWARE_REVISION, SOFTWARE_REVISION);
	batteryService = new BatteryService(ble, 100);
	updateBatteryLevel();

	printf("general setup\r\n");
	error = ble.gap().accumulateAdvertisingPayload(
		GapAdvertisingData::BREDR_NOT_SUPPORTED |
		GapAdvertisingData::LE_GENERAL_DISCOVERABLE
	);
	// shoud be LE_LIMITED_DISCOVERABLE
//	error = ble.gap().accumulateAdvertisingPayload(
//		GapAdvertisingData::BREDR_NOT_SUPPORTED |
//		GapAdvertisingData::LE_LIMITED_DISCOVERABLE
//	);
	if (error != BLE_ERROR_NONE) goto return_error;

	printf("set COMPLETE_LIST_16BIT_SERVICE_IDS\r\n");
	error = ble.gap().accumulateAdvertisingPayload(
		GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS,
		(uint8_t*)uuid16_list, sizeof(uuid16_list)
	);
	if (error != BLE_ERROR_NONE) goto return_error;

	printf("set advertising\r\n");
	// see 5.1.2: HID over GATT Specification (pg. 25)
	ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);

	printf("set advertising interval\r\n");
	ble.gap().setAdvertisingInterval(30);
	printf("set advertising timeout\r\n");
	ble.gap().setAdvertisingTimeout(180);

	printf("set keyboard\r\n");
	error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD);
	if (error != BLE_ERROR_NONE) goto return_error;

	printf("set complete local name\r\n");
	error = ble.gap().accumulateAdvertisingPayload(
		GapAdvertisingData::COMPLETE_LOCAL_NAME,
		DEVICE_NAME, sizeof(DEVICE_NAME)
	);
	if (error != BLE_ERROR_NONE) goto return_error;

	printf("set device name\r\n");
	error = ble.gap().setDeviceName(DEVICE_NAME);
	if (error != BLE_ERROR_NONE) goto return_error;
	// ble.gap().setTxPower(-12);


	printf("advertising\r\n");
	error = ble.gap().startAdvertising();
	if (error != BLE_ERROR_NONE) goto return_error;
	return;

return_error:
	printf("error with %d\r\n", error);
	return;
}

void HIDController::init() {
	// https://github.com/jpbrucker/BLE_HID/blob/master/examples/examples_common.cpp
	printf("ble.init\r\n");

	BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
	ble.init(bleInitComplete);

	while (!ble.hasInitialized()) { }

	printf("ble.hasIntialized\r\n");
}

void HIDController::waitForEvent() {
	BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
	ble.waitForEvent();
}

void HIDController::appendReportData(uint8_t key) {
	if (keyboardService) {
		keyboardService->appendReportData(key);
	}
}

void HIDController::deleteReportData(uint8_t key) {
	if (keyboardService) {
		keyboardService->deleteReportData(key);
	}
}

void HIDController::queueCurrentReportData() {
	if (keyboardService) {
		keyboardService->queueCurrentReportData();
	}
}