BLE HID keyboard with gesture

Dependencies:   BLE_API BLE_HID PAJ7620U2 mbed nRF51822

https://mtmtechblog.files.wordpress.com/2017/01/mtsense04-pi-on-mbed.jpeg We have full tutorial, please visit our blog

main.cpp

Committer:
bcc6
Date:
2017-01-17
Revision:
3:656f8fb89f05
Parent:
2:6109e375f9a7

File content as of revision 3:656f8fb89f05:

/* Copyright (c) 2016 MtM Technology Corporation, MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 * and associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "mbed.h"
#include "ble/BLE.h"

#include "debug.h"
#include "PAJ7620U2.h"

#include "BLE_HID/DeviceInformationService.h"
#include "BLE_HID/BatteryService.h"
#include "BLE_HID/HIDServiceBase.h"
#include "BLE_HID/KeyboardService.h"


#define DEVICE_NAME     ("MtM_gHID")

#define TX_POWER_dBm    (0) // -40, -20, -16, -12, -8, -4, 0, 4

#define BD_ADDRESS_TYPE (BLEProtocol::AddressType::RANDOM_STATIC)
#define BD_ADDRESS      ((Gap::Address_t){0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC})

#define ENABLE_BONDING  (true)
#define REQUIRE_MITM    (false)
#define IOCAPS          (SecurityManager::IO_CAPS_NONE)
#define PASSKEY         ((SecurityManager::Passkey_t){'1', '2', '3', '4', '5', '6'})

#define MANUFACTURERS_NAME  ("MtM")
#define MODEL_NAME          (NULL)
#define SERIAL_NUMBER       (NULL)
#define HW_VERSION          (NULL)
#define FW_VERSION          (NULL)
#define SW_VERSION          (NULL)
#if 0
#define PNP_ID  &((PnPID_t){0x01, 0xFFFE, 0x0001, 0x0001})  // From Bluetooth SIG
#else
#define PNP_ID  &((PnPID_t){0x02, 0x1915, 0xEEEE, 0x0001})  // From USB-IF
#endif

#define MIN_CONN_INTERVAL   (Gap::MSEC_TO_GAP_DURATION_UNITS(10));  // 10ms
#define MAX_CONN_INTERVAL   (Gap::MSEC_TO_GAP_DURATION_UNITS(30));  // 30ms
#define SLAVE_LATENCY       (6)
#define CONN_SUP_TIMEOUT    (43)    // 430ms

#define ADV_INTERVAL        (50)    // 50ms
#define ADV_TIMEOUT         (60)    // 60sec


/* UART for debug (log, assert) */
Serial dbg(p5, p4);

/* Sensor */
PAJ7620U2 gesture(p3, p2, p0);
volatile bool gestureHasIntEvent = false;

/* HID service */
KeyboardService *kbService = NULL;


static void connectionCallback(const Gap::ConnectionCallbackParams_t *params)
{
    log("connection\n");

    log("role(%d)\n", params->role);
    log("peerAddrType(%d), peerAddr(%02X:%02X:%02X:%02X:%02X:%02X)\n", 
        params->peerAddrType,
        params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0]);
    log("ownAddrType(%d), ownAddr(%02X:%02X:%02X:%02X:%02X:%02X)\n", 
        params->ownAddrType,
        params->ownAddr[5], params->ownAddr[4], params->ownAddr[3], params->ownAddr[2], params->ownAddr[1], params->ownAddr[0]);

    log("connectionParams(%d, %d, %d, %d)\n", 
        params->connectionParams->minConnectionInterval,
        params->connectionParams->maxConnectionInterval,
        params->connectionParams->slaveLatency,
        params->connectionParams->connectionSupervisionTimeout);
}

static void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{    
    BLE::Instance().gap().startAdvertising(); // restart advertising
    
    log("disconnection\n");
}

static void timeoutCallback(const Gap::TimeoutSource_t source)
{
    log("timeout(%d)\n", source);
}

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

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

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

static void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
    log("InitComplete\n");

    BLE &ble          = params->ble;
    ble_error_t error = params->error;

    assert(error == BLE_ERROR_NONE);


    /* Set Tx power */
    error = ble.gap().setTxPower(TX_POWER_dBm);
    assert(error == BLE_ERROR_NONE);

    /* Set address */
    error = ble.gap().setAddress(BD_ADDRESS_TYPE, BD_ADDRESS);
    assert(error == BLE_ERROR_NONE);

    /* Initialize BLE security */
    error = ble.securityManager().init(ENABLE_BONDING, REQUIRE_MITM, IOCAPS, PASSKEY);
    assert(error == BLE_ERROR_NONE);

#if 1   // After reset, clear bonding state
    /* Purge all bonding state */
    error = ble.securityManager().purgeAllBondingState();
    assert(error == BLE_ERROR_NONE);
#endif

#if 0   // Disable white-list
    /* Set white-list */
    BLEProtocol::Address_t addresses[2];
    Gap::Whitelist_t whitelist;
    whitelist.addresses = addresses;
    whitelist.size = 0;
    whitelist.capacity = 2;
    error = ble.securityManager().getAddressesFromBondTable(whitelist);
    assert(error == BLE_ERROR_NONE);
    
    log("getAddressesFromBondTable (%d)\n", whitelist.size);
    for(int i=0; i<whitelist.size; i++) {
        int type = (int)whitelist.addresses[i].type;
        uint8_t *addr = (uint8_t *)whitelist.addresses[i].address;
        log("type(%d), addr(%02X:%02X:%02X:%02X:%02X:%02X)\n",
            type, addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);            
    }
    error = ble.gap().setWhitelist(whitelist);
    assert(error == BLE_ERROR_NONE);
    error = ble.gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST);   // Allow scan requests and connect requests from any device
    assert(error == BLE_ERROR_NONE);
#endif

    /* Set callback functions */
    ble.gap().onConnection(connectionCallback);
    ble.gap().onDisconnection(disconnectionCallback);
    ble.gap().onTimeout(timeoutCallback);
    ble.securityManager().onSecuritySetupInitiated(securitySetupInitiatedCallback);
    ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
    ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback);

    /* Setup primary service */
    DeviceInformationService deviceInfo(ble, MANUFACTURERS_NAME, MODEL_NAME, SERIAL_NUMBER, HW_VERSION, FW_VERSION, SW_VERSION, PNP_ID);
    BatteryService batteryInfo(ble, 80);
    kbService = new KeyboardService(ble);

    /* Connection parameters */
    Gap::ConnectionParams_t conn_params;
    conn_params.minConnectionInterval = MIN_CONN_INTERVAL;
    conn_params.maxConnectionInterval = MAX_CONN_INTERVAL;
    conn_params.slaveLatency = SLAVE_LATENCY;
    conn_params.connectionSupervisionTimeout = CONN_SUP_TIMEOUT;
    ble.gap().setPreferredConnectionParams(&conn_params);

    /* Setup advertising. */
    static const uint16_t uuid16_list[] = {
        GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE
    };
    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.gap().setAdvertisingInterval(ADV_INTERVAL);
    ble.gap().setAdvertisingTimeout(ADV_TIMEOUT);
    error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_LIMITED_DISCOVERABLE);
    assert(error == BLE_ERROR_NONE);
    error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
    assert(error == BLE_ERROR_NONE);
    error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD);
    assert(error == BLE_ERROR_NONE);
    error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (const uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
    assert(error == BLE_ERROR_NONE);
    error = ble.gap().setDeviceName((const uint8_t *)DEVICE_NAME);
    assert(error == BLE_ERROR_NONE);
    error = ble.gap().startAdvertising();
    assert(error == BLE_ERROR_NONE);
}

static void gestureIntEventCallback()
{
    log("gestureIntEventCallback\n");
    gestureHasIntEvent = true;
}

int main(void)
{
    /* Disable the hardware flow control of Serial */
    *((uint32_t *)(0x40002000+0x56C)) = 0;
    log("\n");
    log("~ Hell World ~\n");
    log("\n");

    /* Init BLE */
    BLE& ble = BLE::Instance();
    ble.init(bleInitComplete);
    while(ble.hasInitialized() == false) { /* spin loop */ }

    /* Config sensor */
    log("PID(0x%04X), VID(0x%02X)\n", gesture.PID, gesture.VID);
    gesture.IntEvent(&gestureIntEventCallback);

    /* Main loop */
    while(1){
        /* Process interrupt event */
        if(gestureHasIntEvent && ble.getGapState().connected){
            gestureHasIntEvent = false;

            /* Get sensor data */
            uint16_t int_flag = gesture.ReadIntFlag();
            log("(0x%04X)\n", int_flag);

            /* Send HID code */
            if(kbService!=NULL && kbService->isConnected()){
                switch(int_flag){
                case 0x0001:    // Up
                    log("UpArrow\n");
                    kbService->printf("%c", UP_ARROW);
                    break;
                case 0x0002:    // Down
                    log("DownArrow\n");
                    kbService->printf("%c", DOWN_ARROW);
                    break;
                case 0x0004:    // Left
                    log("LeftArrow\n");
                    kbService->printf("%c", LEFT_ARROW);
                    break;
                case 0x0008:    // Right
                    log("RightArrow\n");
                    kbService->printf("%c", RIGHT_ARROW);
                    break;
                case 0x0010:    // Forward
                    log("PageDown\n");
                    kbService->printf("%c", KEY_PAGE_DOWN);
                    break;
                case 0x0020:    // Backword
                    log("PageUp\n");
                    kbService->printf("%c", KEY_PAGE_UP);
                    break;
                case 0x0040:    // Clockwise
                    log("F5\n");
                    kbService->printf("%c", KEY_F5);
                    break;
                case 0x0080:    // Counter-Clockwise
                    log("ESC\n");
                    kbService->printf("%c", 27);
                    break;
                case 0x0100:    // Wave
                    log("Bye Bye\n");
                    kbService->printf("Bye Bye\n");
                    break;
                }// End of switch
            }
        }

        /* low power wait for event */
//      log("sleep\n");
        ble.waitForEvent();
//      log("wakeup\n");

    }// End of while
}