물론 기본 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="">1>
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. 테스트 결과
댓글 없음:
댓글 쓰기