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_plug
和 ble_binding_plug.cgi
都寫好也 refactoring 過了
透過 browser 測試的結果讓人蠻滿意的
可以開始寫 app 囉