BLE switch interface using micro:bit with 3 tact switches or 3 Makey Makey sensors
main.cpp
- Committer:
- masakjm
- Date:
- 2019-06-08
- Revision:
- 2:8e2e6c6658be
- Parent:
- 1:9d0e2e5b5d25
- Child:
- 3:d8fd4efb63cc
File content as of revision 2:8e2e6c6658be:
//================================= // microbit_switch_if_3sw //================================= // BLE switch interface using micro:bit with 3 tact switches // It is intended for use with ios devices. // // The MIT License (MIT) Copyright (c) 2019 Masatomo Kojima //--------------------------------- #define VERSION "3SW-190608" #define NO_DEBUG #include "microbit_switch_if_3sw.h" #include "KeyValueInt.h" #include <queue> //--------------------------------- // KeyBuff //--------------------------------- std::queue<int> KeyBuff; //--------------------------------- // Display //--------------------------------- MicroBitDisplay display; int State; // 状態遷移 char DispChar = DISP_NO_MESSAGE; // LEDに表示する文字コード char DispCharLast = 0; // 最後に表示した文字コード bool TurnOffMode = false; // LED非表示モードのフラグ int Cnt = 0; // カウンター /** ---------- * @brief 整数値をLEDに表示する * @param data 整数値 */ static void displayNumber(int data) { if (0<=data && data<=9) { // 1桁はスクロールしない display.print(data); } else { display.scroll(data); } } //--------------------------------- // Flash Memory //--------------------------------- /** ---------- * @brief キー名を指定して不揮発メモリから値を読み出す * @param key キー名 * @param init データが保存されていなかった時の初期値 * @return int 取得したデータ */ static int FlashGet(MicroBitStorage storage, const char* key, int init=0) { KeyValuePair* kvp = storage.get(key); if(kvp != NULL) { int data; memcpy(&data, kvp->value, sizeof(int)); DEBUG("== FlashGet exist %s = %d\r\n", key, data); return data; } return init; } /** ---------- * @brief 不揮発メモリに値を書き込む * @param storage 不揮発メモリインスタンス * @param key キー名 * @param data 値 * @return int MICROBIT_OK = 0 * MICROBIT_INVALID_PARAMETER = -1001, * MICROBIT_NO_RESOURCES = -1005, */ static int FlashPut(MicroBitStorage storage, const char* key, int data) { int ret = storage.put(key, (uint8_t *)&data, sizeof(int)); DEBUG("== FlashPut %s %d %d\r\n", key, data, ret); return ret; } //--------------------------------- // Setting //--------------------------------- bool Setting_inc; // 設定値を増加 bool Setting_enter; // 設定値を入力 bool Setting_next; // 次の設定値に移動 KeyValueInt kviKeyCode1("keycode1",DISP_SETTING_FREQ, 1, 1, NUM_GROUP1, true); /** ---------- * @brief 1つのパラメータの表示と変更 * @param storage 不揮発メモリインスタンス * @param para パラメータのキーと値の組 * @param change 変更する時 true * @param disp 今の値を表示する時 true * @return bool true : Success false : Failed */ static bool paraSettingOne(MicroBitStorage storage, KeyValueInt* para, bool change, bool disp) { if(disp){ display.print(para->disp); // 識別文字をLED表示 wait(SETTING_DISPLAY_WAIT); displayNumber(para->value); // 値をLED表示 } if (!change) { wait(SETTING_DISPLAY_WAIT); return true; } DEBUG("== paraSetting\r\n"); Setting_inc = false; Setting_next = Setting_enter = false; while( ! Setting_next) { // Bボタンが離されるまで if (Setting_inc) { // Aボタンが押されたら Setting_inc = false; para->inc(); displayNumber(para->value); } wait(0.2); } int ret = FlashPut(storage, para->key, para->value); if (ret) DEBUG("(strage)Error %d\r\n",ret); return (ret == MICROBIT_OK); } /** ---------- * @brief よく変更するパラメータの表示と変更 * @param change true:変更する時 * @return char 0 : Success 'e' : Failed */ static char paraSettingFreq(bool change=false) { MicroBitStorage storage; kviKeyCode1.set(FlashGet(storage, kviKeyCode1.key, 1)); if (paraSettingOne(storage, &kviKeyCode1, change, true)) return 0; else return 'e'; } /** ---------- * @brief デバイス固有のパラメータの表示と変更 * @param change true:変更する時 * @return char 0 : Success 'e' : Failed */ static char paraSettingSpec(bool change=false) { MicroBitStorage storage; // kviStickDirec.set(FlashGet(storage, kviStickDirec.key, 1)); if(State == STATE_SETTING_DEVICE) { // if (paraSettingOne(storage, &kviStickDirec, true , true)) return 0; // else return 'e'; } else { // if (paraSettingOne(storage, &kviStickDirec, false , false)) return 0; // else return 'e'; } } //--------------------------------- // BLE & HID //--------------------------------- BLE ble; KeyboardService *KbdServicePtr; BLE_MESSAGE BleMessage; /** ---------- * @brief BLE接続が切断された時のコールバック関数 */ static void onDisconnect(const Gap::DisconnectionCallbackParams_t *params) { DEBUG("(BLE)disconnected\r\n"); ble.gap().startAdvertising(); // restart advertising BleMessage = BLE_NO_MESSAGE; } /** ---------- * @brief BLE接続が接続された時のコールバック関数 */ static void onConnect(const Gap::ConnectionCallbackParams_t *params) { if(KbdServicePtr->isConnected()) { DEBUG("(BLE)connected\r\n"); ble.gap().stopAdvertising(); BleMessage = BLE_CONNECTED; } } /** ---------- * @brief パスキー入力を求められた時のコールバック関数 */ static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey) { // printf("(BLE)Input passKey: "); // for (unsigned i = 0; i < Gap::ADDR_LEN; i++) { // printf("%c", passkey[i]); // } // printf("\r\n"); } /** ---------- * @brief セキュリティ設定完了時のコールバック関数 */ static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status) { if (status == SecurityManager::SEC_STATUS_SUCCESS) { // DEBUG("(BLE)Security success %d\r\n", status); BleMessage = BLE_PAIRING_SUCCESS; } else { // DEBUG("(BLE)Security failed %d\r\n", status); BleMessage = BLE_PAIRING_FAILED; } } /** ---------- * @brief セキュリティ初期化 * @para _ble BLEインスタンス */ static void initializeSecurity(BLE &_ble) { bool enableBonding = true; bool requireMITM = HID_SECURITY_REQUIRE_MITM; _ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback); _ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback); _ble.securityManager().init(enableBonding, requireMITM, HID_SECURITY_IOCAPS); } /** ---------- * @brief HOGP (HID Over GATT Profile) 初期化 * @para _ble BLEインスタンス */ static void initializeHOGP(BLE &_ble) { static const uint16_t uuid16_list[] = {GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, GattService::UUID_DEVICE_INFORMATION_SERVICE, GattService::UUID_BATTERY_SERVICE }; PnPID_t pnpID; pnpID.vendorID_source = 0x2; // from the USB Implementer's Forum pnpID.vendorID = 0x0D28; // NXP pnpID.productID = 0x0204; // CMSIS-DAP (well, it's a keyboard but oh well) pnpID.productVersion = 0x0100; // v1.0 HIDDeviceInformationService deviceInfo(_ble, "ARM", "m1", "abc", "def", "ghi", "jkl", &pnpID); BatteryService batteryInfo(_ble, 80); _ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); _ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // see 5.1.2: HID over GATT Specification (pg. 25) _ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); // 30ms to 50ms is recommended (5.1.2) _ble.gap().setAdvertisingInterval(50); } /** ---------- * @brief BLE接続のHID(human interface device) の初期化 */ static void initialize_BLE_HID() { static const char SHORT_DEVICE_NAME[] = "micro:bit"; static char DeviceName[20]; int id = microbit_serial_number()& 0xfff; // シリアル番号の下12桁を4桁の10進で表示 sprintf(DeviceName, "micro:bit %04d", id); DEBUG("(BLE)id %s\r\n", DeviceName); // DEBUG("(BLE)initialising ble\r\n"); ble.init(); ble.gap().onDisconnection(onDisconnect); ble.gap().onConnection(onConnect); initializeSecurity(ble); DEBUG("(BLE)adding hid service\r\n"); KeyboardService kbdService(ble); KbdServicePtr = &kbdService; DEBUG("(BLE)adding device info and battery service\r\n"); initializeHOGP(ble); DEBUG("(BLE)setting up gap\r\n"); ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD); ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DeviceName, sizeof(DeviceName)); ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME, (uint8_t *)SHORT_DEVICE_NAME, sizeof(SHORT_DEVICE_NAME)); ble.gap().setDeviceName((const uint8_t *)DeviceName); DEBUG("(BLE)advertising\r\n"); ble.gap().startAdvertising(); } //--------------------------------- // Button //--------------------------------- MicroBitMessageBus bus; MicroBitButton buttonA(MICROBIT_PIN_BUTTON_A, MICROBIT_ID_BUTTON_A); MicroBitButton buttonB(MICROBIT_PIN_BUTTON_B, MICROBIT_ID_BUTTON_B); //MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL); //MicroBitPin P1(MICROBIT_ID_IO_P1, MICROBIT_PIN_P1, PIN_CAPABILITY_ALL); //MicroBitPin P2(MICROBIT_ID_IO_P2, MICROBIT_PIN_P2, PIN_CAPABILITY_ALL); MicroBitButton button0(MICROBIT_PIN_P0, MICROBIT_ID_IO_P0, MICROBIT_BUTTON_ALL_EVENTS, PullUp); MicroBitButton button1(MICROBIT_PIN_P1, MICROBIT_ID_IO_P1, MICROBIT_BUTTON_ALL_EVENTS, PullUp); MicroBitButton button2(MICROBIT_PIN_P2, MICROBIT_ID_IO_P2, MICROBIT_BUTTON_ALL_EVENTS, PullUp); /** ---------- * @brief キーコードを送信する * @para code 上位8bit:修飾キー 下位8bit:キーコード */ static void sendKeyCode(int code) { if (code) { uint8_t key = code & 0xff; uint8_t modif = code >> 8; if (key > KEYMAP_SIZE ) { DispChar = DISP_ERROR_INCORRECT_CODE; // キーコード設定間違い } else { printf(" Cnt=%d code=%d modif=%d\r\n",Cnt++, key , modif); wait(0.05); ble_error_t ret = KbdServicePtr->keyDownCode(key, modif); if (ret) { DispChar = DISP_BLE_ERROR_SENDDATA; DispCharLast = DISP_NO_MESSAGE; DEBUG("(BLE)Error %d\r\n",ret); } } } } /** ---------- * @brief ボタン保持時イベント * @para e イベント */ static void onButtonHold(MicroBitEvent e) { if ((e.source == MICROBIT_ID_BUTTON_A && buttonB.isPressed()) || (e.source == MICROBIT_ID_BUTTON_B && buttonA.isPressed()) ) { KeyBuff.push(BUTTON_STATUS_HOLD + BUTTON_NAME_A + BUTTON_NAME_B); } } static void ButtonHoldProcess(int button) { int code = 0; switch(button) { case BUTTON_NAME_A + BUTTON_NAME_B : DispChar = DISP_HOLD_BUTTON_AB; code = keyCodeGroup_AB[2]; break; } if (code != 0 ) sendKeyCode(code); } /** ---------- * @brief ボタン押下げ時イベント * @para e イベント */ static void onButtonDown(MicroBitEvent e) { switch (State) { case STATE_SETTING_FREQ : case STATE_SETTING_DEVICE : switch(e.source) { case MICROBIT_ID_BUTTON_A : Setting_inc = true; break; case MICROBIT_ID_BUTTON_B : Setting_enter = true; break; } break; case STATE_OPERATING : switch(e.source) { case MICROBIT_ID_BUTTON_A : KeyBuff.push(BUTTON_STATUS_DOWN + BUTTON_NAME_A); break; case MICROBIT_ID_BUTTON_B : KeyBuff.push(BUTTON_STATUS_DOWN + BUTTON_NAME_B); break; case MICROBIT_ID_IO_P0 : KeyBuff.push(BUTTON_STATUS_DOWN + BUTTON_NAME_P0); break; case MICROBIT_ID_IO_P1 : KeyBuff.push(BUTTON_STATUS_DOWN + BUTTON_NAME_P1); break; case MICROBIT_ID_IO_P2 : KeyBuff.push(BUTTON_STATUS_DOWN + BUTTON_NAME_P2); break; } } } static void ButtonDownProcess(int button) { int code = 0; switch(button) { case BUTTON_NAME_A : DispChar = DISP_PRESS_BUTTON_A; code = keyCodeGroup_AB[0]; break; case BUTTON_NAME_B : DispChar = DISP_PRESS_BUTTON_B; code = keyCodeGroup_AB[1]; break; case BUTTON_NAME_P0 : DispChar = DISP_ACTIVE_IO_P0; code = keyCodeGroup_3SW[kviKeyCode1.value-1][0]; break; case BUTTON_NAME_P1 : DispChar = DISP_ACTIVE_IO_P1; code = keyCodeGroup_3SW[kviKeyCode1.value-1][1]; break; case BUTTON_NAME_P2 : DispChar = DISP_ACTIVE_IO_P2; code = keyCodeGroup_3SW[kviKeyCode1.value-1][2]; break; } if (code != 0 ) sendKeyCode(code); } /** ---------- * @brief ボタン離し時イベント * @para e イベント */ static void onButtonUp(MicroBitEvent e) { switch (State) { case STATE_SETTING_FREQ : case STATE_SETTING_DEVICE : if(Setting_enter) Setting_next = true; // 決定ボタンを離したら次へ break; case STATE_OPERATING : KeyBuff.push(BUTTON_STATUS_UP); } } static void ButtonUpProcess() { DispChar = DISP_NO_MESSAGE; wait(0.05); ble_error_t ret = KbdServicePtr->keyUpCode(); if (ret) { DispChar = DISP_BLE_ERROR_SENDDATA; DispCharLast = DISP_NO_MESSAGE; } } //--------------------------------- // Main //--------------------------------- int main() { State = STATE_DESABLE_INPUT; wait(0.1); // ボタン状態の更新 bool bA = buttonA.isPressed(); bool bB = buttonB.isPressed(); if (bA && bB) { // ボタンABを押しながら起動 for(int i=0;i<2;i++) { display.scroll(VERSION); wait(0.5); } } else if(bA) { // ボタンAを押しながら起動 State = STATE_SETTING_FREQ; } else if(bB) { // ボタンBを押しながら起動 State = STATE_SETTING_DEVICE; } //----- Display display.setDisplayMode(DISPLAY_MODE_BLACK_AND_WHITE); display.clear(); Timer dispTime; // 連続表示タイマー dispTime.start(); //----- Button bus.listen(MICROBIT_ID_ANY, MICROBIT_BUTTON_EVT_DOWN, onButtonDown); bus.listen(MICROBIT_ID_ANY, MICROBIT_BUTTON_EVT_UP, onButtonUp); bus.listen(MICROBIT_ID_ANY, MICROBIT_BUTTON_EVT_HOLD, onButtonHold); //----- Device specific settings DispChar = paraSettingSpec(); //----- Setting DispChar = paraSettingFreq(State == STATE_SETTING_FREQ); //----- BLE & HID State = STATE_DESABLE_INPUT; initialize_BLE_HID(); display.clear(); //----- Wait to connect display.printChar('W'); while (BleMessage != BLE_CONNECTED) { wait(0.15); ble.waitForEvent(); // BLEイベントを待つ if(BleMessage != BLE_NO_MESSAGE) { // BLEの状態を表示する display.printChar(bleDispChar[BleMessage] ); } } wait(3.0); BleMessage = BLE_NO_MESSAGE; display.clear(); //----- Loop State = STATE_OPERATING; while (true) { ble.waitForEvent(); // BLEイベントを待つ if(BleMessage != BLE_NO_MESSAGE) { // BLEの状態を表示する DispChar = bleDispChar[BleMessage]; BleMessage = BLE_NO_MESSAGE; } if(! KeyBuff.empty()) { // キーバッファ処理 // DEBUG(" size=%d front=%x\r\n", KeyBuff.size(), KeyBuff.front()); switch (KeyBuff.front() & 0xFF00) { case BUTTON_STATUS_UP : ButtonUpProcess(); break; case BUTTON_STATUS_DOWN : ButtonDownProcess(KeyBuff.front() & 0xFF); break; case BUTTON_STATUS_HOLD : ButtonHoldProcess(KeyBuff.front() & 0xFF); break; } KeyBuff.pop(); } if (DispChar != DispCharLast) { // 表示文字が変更 TurnOffMode = false; dispTime.reset(); display.enable(); if (DispChar) display.printChar(DispChar); else display.clear(); } if (!TurnOffMode) { if (dispTime.read() > TIME_TURN_OFF) { // 長時間表示 TurnOffMode = true; display.disable(); } } DispCharLast = DispChar; } }