페이지

글목록

2016년 2월 6일 토요일

[dialog] BLE protocol Test with DSPS profile (3/3)

이번에는 스마트폰에 DSPS 앱을 깔아서 DA14580 과 서로 연결한 후, 특정 프로토콜에 응답하는 프로그램을 만들어 보겠습니다.

물론 기본 DSPS는 동작하고, 추가로 기능을 넣었습니다.

이전에는 BLE에서 데이터가 들어온 것처럼 속여서 UART로 내보냈는데, 이번에는 UART에서 데이터가 들어온 것으로 속이면 BLE로 데이터가 전송이 되겠죠?

그런데 이번에는 조금 더 신경 쓸 내용이 있습니다. BLE에서 받은 데이터가 그대로 UART로 나가 버리므로 어떻게 BLE에서 수신된 데이터를 가로채야 할지 막막했습니다.

여러가지 생각을 해 보다가, 그냥 버퍼를 하나 더 만들었습니다. 그리고 나서 , BLE 수신 링버퍼에 데이터를 집어 넣을 때 만든 버퍼에도 넣었(push)습니다.
이렇게 하니 BLE에서 받은 데이터가 새로 만든 링버퍼에 들어가서 자유롭게 BLE로부터 수신된 데이터를 분석할 수 있었습니다.

난 나쁜놈인가? da14580을 속이고 가로채고 등쳐먹는 느낌이라, 왠지 기분이 좋지 않네요. ^^. 

1. BLE로 부터 데이터를 가로채기 위해서 먼저, 링버퍼를 하나 만듭니다. DSPS에서 링버퍼 struct 와 함수를 지원하니 같은 타입으로 1개 만들었습니다.
app_sps_scheduler.c 의 윗 부분으로 가서 다음과 같이 user_buffer 라는 이름으로 1개 만들었습니다.
/// SPS application buffer pointers
RingBuffer bletouart_buffer __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY
RingBuffer uarttoble_buffer __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY
RingBuffer user_buffer __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY

2. 또, 이 링버퍼로부터 데이터를 몇개 가져와서 처리하는데 사용할 byte 타입 버퍼도 한개 추가합니다.
/// UART callback pointers
uint8_t tx_write_pointer[TX_CALLBACK_SIZE+1] __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTIONMEMORY
uint8_t rx_read_pointer[RX_CALLBACK_SIZE] __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY
uint8_t rx_user_read_pointer[RX_USER_CALLBACK_SIZE+1] __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY
uint8_t tx_state_ptr __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY
uint8_t rx_state_ptr __attribute__((section("retention_mem_area0"),zero_init)); //@RETENTION MEMORY 

3. 아직은 버퍼가 만들어진 것은 아니고, app_sps_scheduler.c 의 다음과 같이 app_buffer_init 함수를 통해 초기화와 링버퍼를 만듭니다.
void app_buffer_init(void)
{
    if(!app_buffer_initialized(&bletouart_buffer) && !app_buffer_initialized(&uarttoble_buffer) &&!app_buffer_initialized(&user_buffer) )
    {
        //initialize buffers
        app_buffer_create(&bletouart_buffer, TX_BUFFER_ITEM_COUNT, TX_BUFFER_ITEM_SIZE, TX_BUFFER_LWM, TX_BUFFER_HWM);
        app_buffer_create(&uarttoble_buffer, RX_BUFFER_ITEM_COUNT, RX_BUFFER_ITEM_SIZE, RX_BUFFER_LWM, RX_BUFFER_HWM);
        app_buffer_create(&user_buffer, RX_BUFFER_ITEM_COUNT, RX_BUFFER_ITEM_SIZE, RX_BUFFER_LWM, RX_BUFFER_HWM);
    }
}

4. app_sps_scheduler.c 의 함수들이 static type들이라서 다른곳에서 함수를 사용하려면 h 파일에 프로토타입을 선언해야하고, 어쩌다 보면 중복 선언되서 에러도 나서 app_sps_scheduler.c 에 함수를 만들었습니다.

5. 새로 만든 링버퍼에 데이터를 집어(push)넣는 app_user_push()함수
void app_user_push(uint8_t* wrdata, uint8_t writeAmount)
{
    //write items to buffer;
    app_write_items(&user_buffer, wrdata, writeAmount);
}

6. 링버퍼에서 데이터를 끄집어(pull) 내는 app_user_pull 함수 
static uint8_t app_user_pull(uint8_t* rddata, uint8_t readamount)
{
    uint8_t readcount = 0;
        //pull data
        readcount = app_read_items(&user_buffer, rddata, readamount);
    return readcount;
}

7. da14580 을 속여서 uart 버퍼에 데이터를 집어넣어서 BLE에 출력하게 하는 user_BLE_txbuffer 함수
void user_BLE_txbuffer(uint8_t* wrdata, uint8_t writeAmount)
{
app_write_items(&uarttoble_buffer,wrdata,writeAmount);
}

8. 다음은 타이머 인터럽트 callback 함수에 넣고, 일정 시간마다 BLE로부터 수신된 사용자 링버퍼 데이터를 분석해서 처리하는 app_user_callback 함수도 만듭니다.
이 함수는 제가 단순하게 테스트를 위해 만든 함수로, 아주 어설프게 만들었으므로... 쓰시고자 하는 분들이 적절하게 좀 더 고쳐서 잘 만들면 되겠습니다. ^^
일단은, 전체 프로토콜 사이즈를 검사해서 스트링 사이즈가 RX_USER_CALLBACK_SIZE 보다 크면 
첫번째 캐릭터를 1개 링버퍼에서 꺼내서 '[' 캐릭터가 있다면 나머지 스트링도 다 끄집어 내어 2,3,4 번째 캐릭터가 "cmd"이면 잘 수신되었다고 BLE로 송신하는 내용입니다.

 void app_user_callback(void)
{
        //get data and pointer
        //uint8_t size = app_uart_pull(user_tx_write_pointer, TX_CALLBACK_SIZE, &tx_state_ptr);
int size;
char try_test[100];
 while(1) { size = app_item_count(&user_buffer); if (size >= RX_USER_CALLBACK_SIZE) { app_user_pull(rx_user_read_pointer, 1); if (rx_user_read_pointer[0] == '[') { app_user_pull(rx_user_read_pointer, RX_USER_CALLBACK_SIZE-1); if (strncmp(rx_user_read_pointer,"cmd",3) == 0) { sprintf(try_test,"cmd recieved OK\n\r"); app_ble_push(try_test,17); // UART Tx out user_BLE_txbuffer(try_test, 17); // BLE Tx out } else { sprintf(try_test,"cmd recieved FAIL\n\r"); app_ble_push(try_test,19); // UART Tx out user_BLE_txbuffer(try_test, 19); // BLE Tx out } break; } } else { break; } } 
}


9. 제일 중요한 부분을 빼 먹었군요. 실제로 BLE로 받은 데이터를 사용자 버퍼로 가져오는 부분인데, ble로부터 받은 데이터를 ble 버퍼에 집어넣는 함수와 같은 위치에 넣으면 됩니다.
찾아보니 gattc_write_cmd_ind_handler 함수 안에 있어서 사용자 버퍼에 집어넣는 함수를 같이 위치해 놓았습니다.
이렇게 하면 BLE로 수신된 데이터가 ble 버퍼와 사용자 버퍼에 동시에 들어갑니다.
static int gattc_write_cmd_ind_handler(ke_msg_id_t const msgid,
                                      struct gattc_write_cmd_ind const *param,
                                      ke_task_id_t const dest_id,
                                      ke_task_id_t const src_id)
{    
    if (param->handle == sps_server_env.shdl + SPS_SERVER_TX_DATA_CFG) 
    {
        //send write response if required
        if (param->response==0x01)
        {
            atts_write_rsp_send(sps_server_env.con_info.conidx, param->handle, PRF_ERR_OK);
        }
    }
    else if (param->handle == (sps_server_env.shdl + SPS_SERVER_RX_DATA_VAL))
    {
        // send data to the application
        app_ble_push((uint8_t*)&(param->value[0]), param->length);
        app_user_push((uint8_t*)&(param->value[0]), param->length);
    }
    else if (param->handle == (sps_server_env.shdl + SPS_SERVER_RX_DATA_CFG))
    {
        //send write response if required
        if (param->response==0x01)
        {
            atts_write_rsp_send(sps_server_env.con_info.conidx, param->handle, PRF_ERR_OK);
        }
    }
    else if (param->handle == sps_server_env.shdl + SPS_FLOW_CTRL_VAL)
    {
        //update ble flow control flag
        app_update_transmit_allowed(&ble_flags, param->value[0]);
    }
    else if (param->handle == sps_server_env.shdl + SPS_FLOW_CTRL_CFG)
    {
        //provide ble flow control state when flow control att is configured to 0x0100
        if (param->value[0]==0x01 && param->value[1]==0x00)
        {
            struct sps_server_request_flow_control_ind * msg = KE_MSG_ALLOC(SPS_SERVER_REQ_FLOW_CONTROL_IND, sps_server_env.appid,
                                                    TASK_SPS_SERVER, sps_server_request_flow_control_ind);
    
            ke_msg_send(msg);
        }
        //send write response if required
        if (param->response==0x01)
        {
            atts_write_rsp_send(sps_server_env.con_info.conidx, param->handle, PRF_ERR_OK);
        }
    }
    return (KE_MSG_CONSUMED);
}


10. 자 마지막으로 periph_setup.c의  timer callback 함수에 프로토콜 처리함수인 app_user_callback 함수를 실행하도록 하면 됩니다.
void timer0_general_user_callback_function(void) 
{
    static uint8_t n = 0;
    static uint32_t cnt_100ms = 0;
uint8_t try_test[100];
uint16_t port_Input=0;

cnt_100ms++;
    // when pass  10 * 100ms
    if ( 10 == n ) 
    {
port_Input = GPIO_GetPinStatus(GPIO_PORT_0,GPIO_PIN_6) + 
                          ((GPIO_GetPinStatus(GPIO_PORT_1,GPIO_PIN_1))<<1 span="">

sprintf((char *)try_test,"time = %8d ms , Port state : %01X\n\r\0",(cnt_100ms*100),port_Input);
app_ble_push(try_test,size_user_str(try_test));
app_user_callback();
       n = 0;
        //timeout_expiration--;
        if (GPIO_GetPinStatus(LED_PORT, LED_PIN))
        {
            GPIO_SetInactive(LED_PORT, LED_PIN);
        }
        else
        {
            GPIO_SetActive(LED_PORT, LED_PIN);
        }
     }
     n++;
}

11. 테스트 결과

동작 잘 돼네요. ^^


파일은 다음 링크에 첨부되어 있습니다.

댓글 없음:

댓글 쓰기