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

Dependencies:   BLE_API mbed nRF51822

main.cpp

Committer:
toshiyukisaito
Date:
2015-09-04
Revision:
0:fcfa8140d2db

File content as of revision 0:fcfa8140d2db:

#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();
    }
}