スマートロックソフトウェア(BLE nano用)

Dependencies:   BLE_API mbed nRF51822

Revision:
0:fcfa8140d2db
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Sep 04 13:29:59 2015 +0000
@@ -0,0 +1,261 @@
+#include "mbed.h"
+#include "ble/BLE.h"
+#include "Servo.h"
+
+#define LOCAL_NAME             "SmartLock"
+#define TXRX_BUF_LEN           20
+#define SERVO_PIN              P0_8        // RTS
+#define SERVO_PWR_PIN          P0_11       // RXD
+#define SWITCH_PIN             P0_9        // TXD
+
+#define SERVO_ON_TIME          1           // s
+#define SWITCH_WATCH_INTERVAL  2           // s
+#define UNLOCK_PHRASE          "O"
+#define LOCK_PHRASE            "C"
+#define STATUS_PHRASE          "S"
+#define PAIRING_PHRASE         "PAIR"
+#define PASSPHRASE             "PSS_"
+#define SET_PASSPHRASE               "PHRASE"
+#define ALREADY_PAIRING        "ALREADY"
+#define PASSPHRASE_LENGTH      10
+
+#define UNLOCK_SERVO_POS       90
+#define LOCK_SERVO_POS         0
+
+#define ADVERTISING_INTERVAL   1000        // ms
+
+BLE ble;
+Servo servo(SERVO_PIN);
+DigitalOut sw(SERVO_PWR_PIN);
+DigitalIn toggle(SWITCH_PIN);
+DigitalOut led(P0_19);
+
+#define DEBUG 0
+
+#if DEBUG
+Serial pc(USBTX, USBRX);
+#endif
+
+static const uint8_t uart_base_uuid[]     = {0x71, 0x3D, 0x00, 0x00, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
+static const uint8_t uart_tx_uuid[]       = {0x71, 0x3D, 0x00, 0x03, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
+static const uint8_t uart_rx_uuid[]       = {0x71, 0x3D, 0x00, 0x02, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
+static const uint8_t uart_base_uuid_rev[] = {0x1E, 0x94, 0x8D, 0xF1, 0x48, 0x31, 0x94, 0xBA, 0x75, 0x4C, 0x3E, 0x50, 0x00, 0x00, 0x3D, 0x71};
+
+uint8_t txPayload[TXRX_BUF_LEN] = {0,};
+uint8_t rxPayload[TXRX_BUF_LEN] = {0,};
+
+GattCharacteristic  txCharacteristic (uart_tx_uuid, txPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
+GattCharacteristic  rxCharacteristic (uart_rx_uuid, rxPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
+GattCharacteristic *uartChars[] = {&txCharacteristic, &rxCharacteristic};
+GattService         uartService(uart_base_uuid, uartChars, sizeof(uartChars) / sizeof(GattCharacteristic *));
+
+bool isUnlock = false; // ロック状態
+bool isPairing = false; // ペアリング状態
+bool onPairing = false; // ペアリング中フラグ
+char phrase[TXRX_BUF_LEN] = {0,}; // パスフレーズ
+
+/*
+  文字列prefix+s1とs2の比較を行います。
+*/
+bool str_equal(const char* prefix, const char* s1, uint8_t* s2) {
+    int i = 0;
+    while (prefix[i] != '\0') {
+        if (prefix[i] != (char) s2[i]) {
+            return false;
+        }
+        i++;
+    }
+    int j = 0;
+    while(s1[j] != '\0') {
+        if (s1[j] != (char) s2[i]) {
+            return false;
+        }
+        i++;
+        j++;
+    }
+    return true;
+}
+
+/*
+  char*の文字列をuint8_t*の文字列にコピーします。
+*/
+void uintstr_cpy(uint8_t* dest, const char* src) {
+    int i;
+    for (i = 0; src[i] != '\0'; i++) {
+        dest[i] = src[i];
+    }
+    dest[i] = '\0';
+}
+
+/*
+  ロック状態のステータス更新
+*/
+void UpdateStatus() {
+    // ペアリングされていなければUpdateしない。
+    if (!isPairing) return;
+    uint8_t buf[2];
+    uintstr_cpy(buf, isUnlock ? UNLOCK_PHRASE : LOCK_PHRASE);
+    ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, 2);
+}
+
+/*
+    サムターン回転処理。unlock=trueで開錠。
+*/
+void Thumbturn(bool unlock) {
+    isUnlock = unlock;
+    sw.write(1);
+    wait(0.05);
+    servo.write(isUnlock ? UNLOCK_SERVO_POS : LOCK_SERVO_POS);
+    wait(SERVO_ON_TIME);
+    UpdateStatus();
+    sw.write(0);
+    wait(0.05);
+}
+
+/*
+  BLEのコールバック。centralと接続が切れた場合の処理。
+*/
+void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) {
+    ble.startAdvertising();
+}
+
+/*
+  BLEのコールバック。centralから書き込みがあった場合の処理
+*/
+void WrittenHandler(const GattWriteCallbackParams *Handler) {
+    uint8_t buf[TXRX_BUF_LEN];
+    uint16_t bytesRead;
+
+    if (Handler->handle == txCharacteristic.getValueAttribute().getHandle()) {
+        ble.readCharacteristicValue(txCharacteristic.getValueAttribute().getHandle(), buf, &bytesRead);
+        memset(txPayload, 0, TXRX_BUF_LEN);
+        memcpy(txPayload, buf, bytesRead);
+#if DEBUG
+        pc.printf("Receive: %d, %s\r\n", bytesRead, buf);
+#endif
+
+        // ペアリングしていなくて、ステータスチェックが来たら
+        if (!isPairing && str_equal(STATUS_PHRASE, "", buf)) {
+                // ペアリングの必要があるよ。
+            uint8_t buf[TXRX_BUF_LEN];
+            memset(txPayload, 0, TXRX_BUF_LEN);
+            uintstr_cpy(buf, PAIRING_PHRASE);
+            ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, 5);
+            return;
+        }
+
+        // ペアリング要求
+        if (str_equal(PAIRING_PHRASE, "", buf)) {
+#if DEBUG
+        pc.printf("Receive: PAIRING REQUEST\r\n", bytesRead, buf);
+#endif
+            if (!isPairing) {
+                    // パスフレーズ要求
+                onPairing = true;
+                uint8_t buf[TXRX_BUF_LEN];
+                memset(txPayload, 0, TXRX_BUF_LEN);
+                uintstr_cpy(buf, SET_PASSPHRASE);
+                ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, 7);
+            } else {
+                // ペアリング不可。電源を落としてください。
+                uint8_t buf[TXRX_BUF_LEN];
+                memset(txPayload, 0, TXRX_BUF_LEN);
+                uintstr_cpy(buf, ALREADY_PAIRING);
+                ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), buf, 8);
+            }
+            return;
+        }
+
+        // ペアリング処理中
+        if (onPairing) {
+            if (str_equal(PASSPHRASE, "", buf)) {
+                // appから10桁のコードをもらう
+                for (int i = 0; i < PASSPHRASE_LENGTH; i++) {
+                    phrase[i] = buf[i + strlen(PASSPHRASE)];
+                }
+                phrase[PASSPHRASE_LENGTH+1] = '\0';
+                onPairing = false;
+                isPairing = true;
+                led.write(1);
+                wait(0.5);
+            }
+            return;
+        }
+
+        // ペアリング済み
+        if(str_equal(UNLOCK_PHRASE, phrase, buf)) {
+            Thumbturn(true);
+        } else if(str_equal(LOCK_PHRASE, phrase, buf)) {
+            Thumbturn(false);
+        } else if(str_equal(STATUS_PHRASE, phrase, buf)) {
+            UpdateStatus();
+        }
+    }
+}
+
+/*
+  ステータスチェック。
+  タイマーで一定時間ごとに呼び出される。
+*/
+void StatusCheckHundler() {
+#if DEBUG
+    pc.printf("SC %d %d\r\n", isPairing, onPairing);
+#endif
+    if (isPairing) {
+        // ペアリング済みならtoggleボタンでUnlock/Lockできる
+        if (toggle.read() == 0) {
+            if (isUnlock) {
+                Thumbturn(false);
+            } else {
+                Thumbturn(true);
+            }
+        }
+    } else {
+        // 未ペアリング
+
+        // onPairgingになったときにLEDを点滅させる
+        if (onPairing) {
+            if (led.read() != 0) {
+                led.write(0);
+            } else {
+                led.write(1);
+            }
+        }
+    }
+}
+
+int main(void) {
+#if DEBUG
+    pc.baud(9600);
+    pc.printf("INIT\r\n");
+#endif
+
+    Ticker ticker;
+    ticker.attach_us(StatusCheckHundler, SWITCH_WATCH_INTERVAL * 1000 * 1000);
+
+    ble.init();
+    ble.onDisconnection(disconnectionCallback);
+    ble.onDataWritten(WrittenHandler);
+
+    // setup advertising
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
+    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
+                                    (const uint8_t *)LOCAL_NAME, sizeof(LOCAL_NAME) - 1);
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
+                                    (const uint8_t *)uart_base_uuid_rev, sizeof(uart_base_uuid));
+    ble.setDeviceName(LOCAL_NAME);
+    ble.setAdvertisingInterval(ADVERTISING_INTERVAL/0.625);
+    ble.addService(uartService);
+
+    ble.startAdvertising();
+
+    // 起動時に、Lock状態にして、LEDを点灯させる(未ペアリングのため。)
+    Thumbturn(false);
+    led.write(0);
+    toggle.mode(PullUp);
+
+    while(true) {
+        ble.waitForEvent();
+    }
+}