BLE switch interface with GROVE joystic for micro:bit http://mahoro-ba.net/e2073.html

Dependencies:   microbit

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 //=================================
00002 // microbit_switch_if_joy
00003 //=================================
00004 //    BLE switch interface with GROVE joystic for micro:bit
00005 //    It is intended for use with ios devices.
00006 //
00007 //    The MIT License (MIT)   Copyright (c) 2019 Masatomo Kojima
00008 //
00009 //  LED message
00010 //    S  key code setting
00011 //    C  Conected
00012 //    P  success Pearing
00013 //    F  Fail pearing
00014 //    E  Error at sending data by ble
00015 //    e  Error at writting data to flash memory
00016 //    I  Error by Incorrect cording
00017 //    G  GROVE sensor connect error
00018 //---------------------------------
00019 
00020 #define VERSION     "JOY-190330"
00021 //#define NO_DEBUG
00022 
00023 #include "microbit_switch_if_joy.h"
00024 #include "KeyValueInt.h"
00025 
00026 //---------------------------------
00027 //  Display
00028 //---------------------------------
00029 MicroBitDisplay display;
00030 int  State;                   // 状態遷移
00031 char DispChar  = 0;           // LEDに表示する文字コード
00032 char DispCharLast = 0;        // 最後に表示した文字コード
00033 bool TurnOffMode = false;     // LED非表示モードのフラグ
00034 
00035 //---------------------------------
00036 //  GROVE Joystick
00037 //---------------------------------
00038 
00039 AnalogIn AnalogP1(MICROBIT_PIN_P1);
00040 AnalogIn AnalogP2(MICROBIT_PIN_P2);
00041     
00042 /** ----------
00043  * @brief 整数値をLEDに表示する
00044  * @param data 整数値
00045  */
00046 static void displayNumber(int data)
00047 {
00048     if (0<=data && data<=9) {  // 1桁はスクロールしない
00049         display.print(data);
00050     } else {
00051         display.scroll(data);
00052     }
00053 }
00054 
00055 //---------------------------------
00056 //  Flash Memory
00057 //---------------------------------
00058 /** ----------  
00059  * @brief   キー名を指定して不揮発メモリから値を読み出す
00060  * @param   key  キー名
00061  * @param   init  データが保存されていなかった時の初期値
00062  * @return  int 取得したデータ
00063  */
00064 static int FlashGet(MicroBitStorage storage, const char* key, int init=0)
00065 {
00066     KeyValuePair* kvp = storage.get(key);
00067     if(kvp != NULL) {
00068         int data;
00069         memcpy(&data, kvp->value, sizeof(int));
00070         DEBUG("== FlashGet exist %s = %d\r\n", key, data);
00071         return data;
00072     }
00073     return init;
00074 }
00075 
00076 /** ----------  
00077  * @brief   不揮発メモリに値を書き込む
00078  * @param   storage  不揮発メモリインスタンス
00079  * @param   key      キー名
00080  * @param   data     値
00081  * @return  int MICROBIT_OK = 0
00082  *              MICROBIT_INVALID_PARAMETER = -1001,
00083  *              MICROBIT_NO_RESOURCES = -1005,
00084  */
00085 static int FlashPut(MicroBitStorage storage, const char* key, int data)
00086 {
00087     int ret = storage.put(key, (uint8_t *)&data, sizeof(int));
00088     DEBUG("== FlashPut %s %d %d\r\n", key, data, ret);
00089     return ret;
00090 }
00091 
00092 //---------------------------------
00093 //  Setting
00094 //---------------------------------
00095 bool setting_inc;           // 設定値を増加
00096 bool setting_enter;         // 設定値を入力
00097 bool setting_next;          // 次の設定値に移動
00098 
00099 KeyValueInt kviKeyCode1("keycode1",'S', 1, 1, NUM_GROUP1, true);
00100 
00101 /** ----------  
00102  * @brief   1つのパラメータの表示と変更
00103  * @param   storage  不揮発メモリインスタンス
00104  * @param   para     パラメータのキーと値の組
00105  * @param   change   変更する時 true
00106  * @return  bool true : Success    false : Failed
00107  */
00108 static bool paraSettingOne(MicroBitStorage storage, KeyValueInt* para, bool change)
00109 {
00110     display.print(para->disp);    // 識別文字をLED表示
00111     wait(SETTING_DISPLAY_WAIT);
00112     displayNumber(para->value);   // 値をLED表示
00113     if (!change) {
00114         wait(SETTING_DISPLAY_WAIT);
00115         return true;
00116     }
00117 
00118     DEBUG("== paraSetting\r\n");
00119     setting_inc = false;
00120     setting_next = setting_enter = false;
00121     while( ! setting_next) {      // Bボタンが離されるまで
00122         if (setting_inc) {        // Aボタンが押されたら
00123             setting_inc = false;
00124             para->inc();
00125             displayNumber(para->value);
00126         }
00127         wait(0.2);
00128     }
00129     int ret = FlashPut(storage, para->key, para->value);
00130     if (ret) DEBUG("(strage)Error %d\r\n",ret);
00131     return (ret == MICROBIT_OK);
00132 }
00133 
00134 /** ----------  
00135  * @brief  パラメータの表示と変更
00136  * @param  change   true:変更する時
00137  */
00138 static bool paraSetting(bool change=false)
00139 {
00140     MicroBitStorage storage;
00141 
00142     kviKeyCode1.set(FlashGet(storage, kviKeyCode1.key, 1));
00143     return paraSettingOne(storage, &kviKeyCode1, change);
00144 }
00145 //---------------------------------
00146 //  BLE & HID
00147 //---------------------------------
00148 BLE ble;
00149 KeyboardService *kbdServicePtr;
00150 BLE_MESSAGE bleMessage;
00151 
00152 /** ----------  
00153  * @brief  BLE接続が切断された時のコールバック関数
00154  */
00155 static void onDisconnect(const Gap::DisconnectionCallbackParams_t *params)
00156 {
00157     DEBUG("(BLE)disconnected\r\n");
00158     ble.gap().startAdvertising(); // restart advertising
00159     bleMessage = BLE_NO_MESSAGE;
00160 }
00161 
00162 /** ----------  
00163  * @brief  BLE接続が接続された時のコールバック関数
00164  */
00165 static void onConnect(const Gap::ConnectionCallbackParams_t *params)
00166 {
00167     if(kbdServicePtr->isConnected()) {
00168         DEBUG("(BLE)connected\r\n");
00169         ble.gap().stopAdvertising();
00170         bleMessage = BLE_CONNECTED;
00171     }
00172 }
00173 
00174 /** ----------  
00175  * @brief  パスキー入力を求められた時のコールバック関数
00176  */
00177 static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
00178 {
00179     printf("(BLE)Input passKey: ");
00180     for (unsigned i = 0; i < Gap::ADDR_LEN; i++) {
00181         printf("%c", passkey[i]);
00182     }
00183     printf("\r\n");
00184 }
00185 
00186 /** ----------  
00187  * @brief   セキュリティ設定完了時のコールバック関数
00188  */
00189 static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status)
00190 {
00191     if (status == SecurityManager::SEC_STATUS_SUCCESS) {
00192 //        DEBUG("(BLE)Security success %d\r\n", status);
00193         bleMessage = BLE_PAIRING_SUCCESS;
00194 
00195     } else {
00196 //        DEBUG("(BLE)Security failed %d\r\n", status);
00197         bleMessage = BLE_PAIRING_FAILED;
00198     }
00199 }
00200 
00201 /** ----------  
00202  * @brief  セキュリティ初期化
00203  * @para   _ble  BLEインスタンス
00204  */
00205 static void initializeSecurity(BLE &_ble)
00206 {
00207     bool enableBonding = true;
00208     bool requireMITM = HID_SECURITY_REQUIRE_MITM;
00209 
00210     _ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback);
00211     _ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
00212 
00213     _ble.securityManager().init(enableBonding, requireMITM, HID_SECURITY_IOCAPS);
00214 }
00215 
00216 /** ----------  
00217  * @brief   HOGP (HID Over GATT Profile) 初期化
00218  * @para   _ble  BLEインスタンス
00219  */
00220 static void initializeHOGP(BLE &_ble)
00221 {
00222     static const uint16_t uuid16_list[] =  {GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE,
00223                                             GattService::UUID_DEVICE_INFORMATION_SERVICE,
00224                                             GattService::UUID_BATTERY_SERVICE
00225                                            };
00226 
00227     PnPID_t pnpID;
00228     pnpID.vendorID_source = 0x2;    // from the USB Implementer's Forum
00229     pnpID.vendorID = 0x0D28;        // NXP
00230     pnpID.productID = 0x0204;       // CMSIS-DAP (well, it's a keyboard but oh well)
00231     pnpID.productVersion = 0x0100;  // v1.0
00232     HIDDeviceInformationService deviceInfo(_ble, "ARM", "m1", "abc", "def", "ghi", "jkl", &pnpID);
00233 
00234     BatteryService batteryInfo(_ble, 80);
00235 
00236     _ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED |
00237                                            GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
00238     _ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS,
00239                                            (uint8_t *)uuid16_list, sizeof(uuid16_list));
00240 
00241     // see 5.1.2: HID over GATT Specification (pg. 25)
00242     _ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
00243     // 30ms to 50ms is recommended (5.1.2)
00244     _ble.gap().setAdvertisingInterval(50);
00245 }
00246 
00247 /** ----------  
00248  * @brief  BLE接続のHID(human interface device) の初期化
00249  */
00250 static void initialize_BLE_HID()
00251 {
00252     static const char SHORT_DEVICE_NAME[] = "micro:bit";
00253     static char DeviceName[20];
00254 
00255     int id = microbit_serial_number()& 0xfff;    // シリアル番号の下12桁を4桁の10進で表示
00256     sprintf(DeviceName, "micro:bit %04d", id);
00257     DEBUG("(BLE)id %s\r\n", DeviceName);
00258 
00259 //    DEBUG("(BLE)initialising ble\r\n");
00260     ble.init();
00261 
00262     ble.gap().onDisconnection(onDisconnect);
00263     ble.gap().onConnection(onConnect);
00264 
00265     initializeSecurity(ble);
00266 
00267     DEBUG("(BLE)adding hid service\r\n");
00268     KeyboardService kbdService(ble);
00269     kbdServicePtr = &kbdService;
00270 
00271     DEBUG("(BLE)adding device info and battery service\r\n");
00272     initializeHOGP(ble);
00273 
00274     DEBUG("(BLE)setting up gap\r\n");
00275     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD);
00276     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME,
00277                                            (uint8_t *)DeviceName, sizeof(DeviceName));
00278     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
00279                                            (uint8_t *)SHORT_DEVICE_NAME, sizeof(SHORT_DEVICE_NAME));
00280     ble.gap().setDeviceName((const uint8_t *)DeviceName);
00281 
00282     DEBUG("(BLE)advertising\r\n");
00283     ble.gap().startAdvertising();
00284 }
00285 
00286 //---------------------------------
00287 //  Button
00288 //---------------------------------
00289 MicroBitMessageBus bus;
00290 MicroBitButton buttonA(MICROBIT_PIN_BUTTON_A, MICROBIT_ID_BUTTON_A);
00291 MicroBitButton buttonB(MICROBIT_PIN_BUTTON_B, MICROBIT_ID_BUTTON_B);
00292 //MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL);
00293 MicroBitButton P0(MICROBIT_PIN_P0, MICROBIT_ID_IO_P0, MICROBIT_BUTTON_ALL_EVENTS, PullUp);
00294 
00295 /** ----------  
00296  * @brief  キーコードを送信する
00297  * @para   code 上位8bit:修飾キー  下位8bit:キーコード    
00298  */
00299 static void sendKeyCode(int code) {
00300     if (code) {
00301         uint8_t key = code & 0xff;
00302         uint8_t modif = code >> 8;
00303                 
00304         if (key > KEYMAP_SIZE ) {
00305             DispChar  ='I';      // キーコード設定間違い
00306         } else {
00307             ble_error_t ret = kbdServicePtr->keyDownCode(key, modif);
00308 //                    DEBUG("  code=%d  modif=%d\r\n",key , modif);
00309             if (ret) {
00310                 DispChar  ='E';
00311                 DispCharLast =0;   // E が続く時に表示
00312                 DEBUG("(BLE)Error %d\r\n",ret);
00313             }
00314         }
00315     }
00316 }
00317 /** ----------  
00318  * @brief  ボタン保持時イベント
00319  * @para   e  イベント
00320  */
00321 static void onButtonHold(MicroBitEvent e)
00322 {
00323     if ((e.source == MICROBIT_ID_BUTTON_A && buttonB.isPressed()) ||
00324         (e.source == MICROBIT_ID_BUTTON_B && buttonA.isPressed()) ) {
00325            DispChar  = 'H';
00326            sendKeyCode(keyCodeGroup0[2]);
00327      }
00328 }
00329 
00330 /** ----------  
00331  * @brief  ボタン押下げ時イベント
00332  * @para   e  イベント
00333  */
00334 static void onButtonDown(MicroBitEvent e)
00335 {
00336 //   DEBUG("  Button Down %d  State=%d\r\n", e.source, State);
00337 
00338     switch (State) {
00339         case STATE_SETTING :
00340             switch(e.source) {
00341                 case MICROBIT_ID_BUTTON_A :
00342                     setting_inc = true;
00343                     break;
00344                 case MICROBIT_ID_BUTTON_B :
00345                     setting_enter = true;
00346                     break;
00347             }
00348             break;
00349         case STATE_OPERATING :
00350             int code = 0;
00351             switch(e.source) {
00352                 case MICROBIT_ID_BUTTON_A :
00353                     DispChar  = 'A';
00354                     code = keyCodeGroup0[0];
00355                     break;
00356                 case MICROBIT_ID_BUTTON_B :
00357                     DispChar  = 'B';
00358                     code = keyCodeGroup0[1];
00359                     break;
00360                 case MICROBIT_ID_IO_P0 :
00361                     DispChar  = '0';
00362                     code = keyCodeGroup1[kviKeyCode1.value-1][JOY_CENTER_PRESS];
00363                     break;
00364             }
00365             if (code != 0 )sendKeyCode(code);
00366     }
00367 }
00368 
00369 /** ----------  
00370  * @brief  ボタン離し時イベント
00371  * @para   e  イベント
00372  */
00373 static void onButtonUp(MicroBitEvent e)
00374 {
00375 //    DEBUG("  Button up %d\r\n", e.source);
00376     switch (State) {
00377         case STATE_SETTING :
00378             if(setting_enter) setting_next = true;    // 決定ボタンを離したら次へ
00379             break;
00380         case STATE_OPERATING :
00381             DispChar  = 0;
00382             ble_error_t ret = kbdServicePtr->keyUpCode();
00383             if (ret) {
00384                 DispChar  ='E';
00385                 DispCharLast =0;   // E が続く時に表示
00386             }
00387     }
00388 }
00389 
00390 
00391 /** ----------  
00392  * @brief   ジョイスティックの接続をチェックする
00393  * @return  false:接続エラー
00394  */
00395 static bool checkJoy() 
00396 {
00397     float joyX = AnalogP1.read();
00398     float joyY = AnalogP2.read();
00399 
00400     if (joyX < JOY_ERR_THRE) return false;       
00401     if (joyY < JOY_ERR_THRE) return false;   
00402 
00403     return true;
00404 }
00405 
00406 /** ----------  
00407  * @brief   ジョイスティックの状態を返す
00408  * @return  JOY_STATUS
00409  */
00410 static JOY_STATUS readJoyStatus(JOY_STATUS last) 
00411 {
00412     JOY_STATUS js = JOY_NEUTRAL;
00413 
00414     float joyX = AnalogP1.read();
00415     float joyY = AnalogP2.read();
00416 
00417     if (joyX > JOY_CENTER_THRE) js = JOY_CENTER_PRESS;       
00418     else {
00419         if (joyX < JOY_LOW_THRE)  js = JOY_XLOW_PRESS;        
00420         if (joyX > JOY_HIGH_THRE) js = JOY_XHIGH_PRESS;        
00421         if (joyY < JOY_LOW_THRE)  js = JOY_YLOW_PRESS;        
00422         if (joyY > JOY_HIGH_THRE) js = JOY_YHIGH_PRESS;        
00423     }
00424              
00425 //    DEBUG("%d, %f, %f\r\n", js, joyX, joyY);
00426     return js;
00427 }
00428 
00429 static void joyAction(JOY_STATUS last, JOY_STATUS now) 
00430 {
00431     int code = 0;
00432 //   DEBUG("%d, %d\r\n", now, last);
00433             
00434     if( now != last){              
00435         if(now == JOY_NEUTRAL) {
00436             DispChar  = 0;
00437             ble_error_t ret = kbdServicePtr->keyUpCode();
00438             if (ret) {
00439                 DispChar  ='E';
00440                 DispCharLast =0;   // E が続く時に表示
00441             }
00442         } else {
00443             DispChar  = '0' + now;
00444             code = keyCodeGroup1[kviKeyCode1.value-1][now];
00445         }
00446         if (code != 0 )sendKeyCode(code);
00447     }
00448 }
00449 //---------------------------------
00450 //  Main
00451 //---------------------------------
00452 int main()
00453 {
00454     State = STATE_DESABLE_INPUT;
00455 
00456     wait(0.1);           // ボタン状態の更新
00457     bool bA = buttonA.isPressed();
00458     bool bB = buttonB.isPressed();
00459 
00460     if (bA && bB) {     // ボタンABを押しながら起動
00461         for(int i=0;i<2;i++) {
00462             display.scroll(VERSION);
00463             wait(0.5);
00464         }
00465     } else if(bA)       // ボタンAを押しながら起動
00466         State = STATE_SETTING;
00467 
00468 //----- Display
00469     display.setDisplayMode(DISPLAY_MODE_BLACK_AND_WHITE);
00470     display.clear();
00471     Timer dispTime;         // 連続表示タイマー
00472     dispTime.start();
00473 
00474 //----- Button
00475     bus.listen(MICROBIT_ID_ANY, MICROBIT_BUTTON_EVT_DOWN, onButtonDown);
00476     bus.listen(MICROBIT_ID_ANY, MICROBIT_BUTTON_EVT_UP,   onButtonUp);
00477     bus.listen(MICROBIT_ID_ANY, MICROBIT_BUTTON_EVT_HOLD, onButtonHold);
00478 
00479 // Put the P0 pins into touch sense mode.
00480 //    P0.isTouched();
00481 
00482 //----- Joystick
00483     JOY_STATUS joyStatus;
00484     JOY_STATUS joyStatusLast = JOY_NEUTRAL;
00485     while (!checkJoy())  {
00486         DispChar  ='G';
00487         display.printChar(DispChar );
00488         wait(1);
00489     }   
00490     
00491 //----- Setting
00492     if (paraSetting(State == STATE_SETTING)) DispChar  =0;
00493     else                                     DispChar  ='e';   
00494 
00495 //----- BLE & HID
00496     State = STATE_DESABLE_INPUT;
00497     initialize_BLE_HID();
00498     display.clear();
00499 
00500 //----- Loop
00501     State = STATE_OPERATING;
00502     while (true) {
00503         wait(0.15);
00504         ble.waitForEvent();        // BLEイベントを待つ
00505 
00506         joyStatus = readJoyStatus(joyStatusLast);  // Joystick の状態を読む
00507         joyAction(joyStatusLast, joyStatus);
00508         joyStatusLast = joyStatus;      
00509 
00510         if(bleMessage != BLE_NO_MESSAGE) {    // BLEの状態を表示する
00511             DispChar  = bleDispChar[bleMessage];
00512             bleMessage = BLE_NO_MESSAGE;    
00513         }
00514 
00515         if (DispChar  != DispCharLast) {  // 表示文字が変更
00516             TurnOffMode = false;
00517             dispTime.reset();
00518             display.enable(); 
00519             if (DispChar ) display.printChar(DispChar );
00520             else          display.clear();
00521         }
00522         if (!TurnOffMode) {
00523             if (dispTime.read() > TIME_TURN_OFF) {  // 長時間表示
00524                 TurnOffMode = true;
00525                 display.disable();
00526             }
00527         }
00528         
00529         DispCharLast = DispChar ;
00530     }
00531 }