20160420_jeffrey - silenceuncrio/diary GitHub Wiki

Index

  • 0840 - 目前可以控制兩個 plug
  • 1140 - linkit csr ble_binding_plug
  • 1535 - ble_binding_plug ble_binding_plug.cgi 寫好了

0840

昨天的進度還不錯
目前可以控制兩個 plug 了
來 refactoring 一下

csr_app_gatt_client

void AppInit(sleep_state last_sleep_state)
{

    /* Initialise the UART */
    uartInit();
    ...
    iplugDemoInit();
    ...
    /* Start scanning for advertisements from the first device (0) */
    SetState(0, app_state_scanning);
}

AppInit 是 CSR 的進入點
加入 iplugDemoInit 準備 on/off 要送給 plug 的訊息

/* The MAX_CONNECTED_DEVICES macro defines the number of devices that can be
 * connected at any given time to the Client device. It must not exceed 1.
 */
#define MAX_CONNECTED_DEVICES                     (2)

MAX_CONNECTED_DEVICES 改成 2

void SetState(uint16 dev, app_state new_state) {

    /* Current state */
    const app_state old_state = g_app_data.devices[dev].state;

    if (old_state != new_state) {
        switch (old_state) {
        case app_state_init:
            appInitExit(dev);
            break;
        case app_state_disconnecting:
            break;
        case app_state_connected:
            break;
        case app_state_discovering:
            break;
        default:
            break;
    }

    /* Set new state */
    g_app_data.devices[dev].state = new_state;

    /* Handle entering new state */
    switch (new_state) {
    case app_state_scanning:
        /* Start scanning for advertising devices */
        /* Reset application data */
        appDataInit();
        /* Start scanning */
        appStartScan();
        break;
    case app_state_connecting:
        break;
    case app_state_connected:
        /* Move to the app_state_discovering state */
        appStartDiscoveryProcedure(dev);
        break;
    case app_state_discovering:
        /* Discovery will start in DISCOVERY_START_TIMER ms */
        g_app_data.app_timer = TimerCreate(
            DISCOVERY_START_TIMER, TRUE, appStartDiscoveryTimerExpiry);
        break;
    case app_state_configured:
        iplugDemoSetOn(dev);
        break;
    case app_state_disconnecting:
        GattDisconnectReq(g_app_data.devices[dev].connectHandle);
        break;
    default:
        break;
    }
}

正常流程的話每個 dev 都會經歷以下 state

  • init
  • scanning
  • connecting
  • connected
  • discovering
  • configured

若每個 dev 都停在 configured 的話表示可以對它下命令了
目前對每個 plug 作 iplugDemoSetOn(dev);
這樣 plug(server) 會發 notification 給 csr(client)
csr 便會記錄該 plug 的狀態

void GattDiscoveryEvent(lm_event_code event_code, LM_EVENT_T *p_event_data) {
    switch(event_code) {
    case LM_EV_ADVERTISING_REPORT:
        ...
        break;
    case GATT_DISC_PRIM_SERV_BY_UUID_IND:
        ...
        break;
    case GATT_DISC_PRIM_SERV_BY_UUID_CFM:
        ...
        break;
    case GATT_CHAR_DECL_INFO_IND:
        ...
        break;
    case GATT_DISC_SERVICE_CHAR_CFM:
        ...
        break;
    case GATT_CHAR_DESC_INFO_IND:
        ...
        break;
    case GATT_DISC_ALL_CHAR_DESC_CFM:
        ...
        break;
    case GATT_WRITE_CHAR_VAL_CFM:
        ...
        break;
    case GATT_READ_CHAR_VAL_CFM:
       ... 
       break;
    case GATT_IND_CHAR_VAL_IND:
    case GATT_NOT_CHAR_VAL_IND:
        appGattSignalGattCharValInd((GATT_CHAR_VAL_IND_T *)p_event_data);
        break;
    default:
        break;
    }
}

csr 負責處理 notification 的 function 為 appGattSignalGattCharValInd

static void appGattSignalGattCharValInd(GATT_CHAR_VAL_IND_T *p_event_data) {
    ...
    #if 0 // CSR original
    /* Get the connected device */
    const uint16 dev = GetConnDevice();
    #else
    /* Get the connected device from cid */
    const uint16 dev = GetConnDeviceFromCid(p_event_data->cid);
    #endif
    
    while(index < g_app_gatt_data.totalSupportedServices) {
        pService = g_app_gatt_data.serviceStore[index];
        if (pService != NULL && pService->isServiceFound != NULL && 
           pService->isServiceFound(dev) && /* Service is discovered */
           pService->checkHandle != NULL) {
           if (pService->checkHandle(dev, p_event_data->handle)) {
                /* Handle belongs to this service */
                if(pService->serviceIndNotifHandler != NULL) {
                    pService->serviceIndNotifHandler(dev,
                                                     p_event_data->handle,
                                                     p_event_data->size_value,
                                                     p_event_data->value);
                }
                break;
            }
        }
        index ++;
    }
}

在利用 serviceIndNotifHandler 呼叫到 plug 的 callback function 之前
我們要知道 notification 是哪個 dev 發的

csr 原本的 GetConnDevice 只適用於 MAX_CONNECTED_DEVICES 為 1 的 case
改成 2 之後需要由 connection identification p_event_data->cid 回推是哪個 device
我新加了一支 uint16 GetConnDeviceFromCid(uint16 cid);

實作也不難
因為每個 device 在 connection 之後會將 connection identifiction keep 在 g_app_data.devices[dev].connectHandle

uint16 GetConnDeviceFromCid(uint16 cid) {
    uint16 dev;

    for (dev = 0; dev < MAX_CONNECTED_DEVICES; dev ++) {
        if (g_app_data.devices[dev].connectHandle == cid) {
            return dev;
        }
    }
    return g_app_data.dev_num;
}

被呼叫來處理 plug 來的 notification 的 callback function 為 IplugServiceHandlerNotifInd

bool IplugServiceHandlerNotifInd(uint16 dev, 
                                 uint16 handle,
                                 uint16 size,
                                 uint8  *value)
{
    ...
    if (handle == 0x2e) {
        /*
        "ATR" + status
        the 7th bit represents power status
        the 4th bit represents time table status if be cleared ==> 1 = all timer be cleared, 0=working
        the 5th bit represent 延遲開關 status => 1 = enable, 0=disable
        */
        iplugUpdateStatus(dev, value[3]);
    }
    ...
}

目前的實作是直接判斷 handle == 0x2e 後就直接抓 status 這個 byte 來處理
實作了一支 void iplugUpdateStatus(uint16 dev, uint8 status);

void iplugUpdateStatus(uint16 dev, uint8 status) {
    /*
    ** The following information come from bpoint(http://www.bpoint.com.tw)
    ** "ATR" + status
    ** the 4th bit represents time table status if be cleared ==> 1 = all timer be cleared, 0=working
    ** the 5th bit represent 延遲開關 status => 1 = enable, 0=disable
    ** the 7th bit represents power status
    */
    g_app_data.devices[dev].iplugAtrStatusTimeTable =   N_TH_VAL_OF_BYTE(status, 4);
    g_app_data.devices[dev].iplugAtrStatusDelay =       N_TH_VAL_OF_BYTE(status, 5);
    g_app_data.devices[dev].iplugAtrStatusPower =       N_TH_VAL_OF_BYTE(status, 7);
}

反正就按照 bpoint 的文件來解釋相對應的 bit
並 keep 在 g_app_data.devices[dev]
為了 keep 住這三個 bit 我新增了相對應的 member 在 struct _DEVICE_T 當中

/* Device structure */
typedef struct _DEVICE_T
{
    ...
    /*
    ** The following information come from bpoint(http://www.bpoint.com.tw)
    ** "ATR" + status
    ** the 4th bit represents time table status if be cleared ==> 1 = all timer be cleared, 0=working
    ** the 5th bit represent 延遲開關 status => 1 = enable, 0=disable
    ** the 7th bit represents power status
    */
    uint16                    iplugAtrStatusTimeTable;
    uint16                    iplugAtrStatusDelay;
    uint16                    iplugAtrStatusPower;
} DEVICE_T;

這樣一來
csr 處理來自 linkit 的 command handleIplugGetCurrentStatus
便能利用已經 keep 在 g_app_data.devices[dev] 裡現成的資訊立即作出回應

static bool handleIplugGetCurrentStatus(SLE_REQUEST_T *req, uint16* wordsConsumed) {
    SLE_RESPONSE_T *response = (SLE_RESPONSE_T *)responseMessage;
    response->header.messageId = IPLUG_GET_CURRENT_STATUS_RSP;
    response->header.dataLength = sizeof(IPLUG_GET_CURRENT_STATUS_RSP_T);
	response->header.status = SLE_STATUS_SUCCESS;

    iplugFillCurrentStatus(req->header.status, &(response->u.iplugGetCurrentStatusRsp));

    *wordsConsumed = (MESSAGE_HEADER_LENGTH_WORDS + req->header.dataLength);
    return TRUE;
}

function 為 void iplugFillCurrentStatus(uint16 dev, IPLUG_GET_CURRENT_STATUS_RSP_T *rsp);

void iplugFillCurrentStatus(uint16 dev, IPLUG_GET_CURRENT_STATUS_RSP_T *rsp) {
    /*
    ** The following information come from bpoint(http://www.bpoint.com.tw)
    ** "ATR" + status
    ** the 4th bit represents time table status if be cleared ==> 1 = all timer be cleared, 0=working
    ** the 5th bit represent 延遲開關 status => 1 = enable, 0=disable
    ** the 7th bit represents power status
    */
    rsp->timeTable =    g_app_data.devices[dev].iplugAtrStatusTimeTable;
    rsp->delay =        g_app_data.devices[dev].iplugAtrStatusDelay;
    rsp->power =        g_app_data.devices[dev].iplugAtrStatusPower;
}

1140

將昨天 linkit 上對 csr 作的程式重新封裝成 ble_binding_plug
可以直接在 linkit shell 作操作

cgi 就只是個 interface
骨子裡就是利用 ble_binding_plug
但要經過適當的包裝好讓使用者透過 RESTful HTTP API 的方式來使用
名稱為 ble_binding_plug.cgi

1535

ble_binding_plugble_binding_plug.cgi 都寫好也 refactoring 過了
透過 browser 測試的結果讓人蠻滿意的

可以開始寫 app 囉