ADC까지, 신호 확인 필요(반사 신호 약함 or 뒤쪽을 찍고 있음)

This commit is contained in:
2026-05-08 18:09:04 +09:00
parent 17a78d4418
commit d7c5b34da3
23 changed files with 1992 additions and 83 deletions
+156 -15
View File
@@ -30,8 +30,92 @@ LOG_MODULE_REGISTER(ble_svc, LOG_LEVEL_INF);
static struct bt_conn *current_conn;
static ble_data_rx_cb_t rx_callback;
/*
* NUS 전송은 "보냈다" 호출만 한다고 바로 끝나는 게 아니다.
* 내부 TX 버퍼에 들어간 뒤 실제로 무선으로 나가고,
* Zephyr가 sent 콜백을 줄 때 비로소 한 건이 끝났다고 볼 수 있다.
*
* 그래서 아래 2개를 같이 쓴다.
* - mutex: 여러 곳에서 동시에 NUS 전송을 시작하지 못하게 막음
* - sem : "직전 전송이 정말 끝났는지" 기다리는 신호
*
* mbb?처럼 큰 패킷을 여러 개 연속으로 보내는 경우 이 보호가 없으면
* - 중간에 -ENOMEM이 뜨거나
* - 몇 개는 나가고 몇 개는 실패하거나
* - 앱 쪽에서 응답이 꼬여 보일 수 있다.
*/
static struct k_mutex nus_tx_lock;
static struct k_sem nus_tx_done_sem;
/* BLE API는 ISR/콜백에서 직접 호출하면 안전하지 않을 수 있으므로 k_work 사용 */
static struct k_work adv_restart_work;
static const bt_addr_le_t fixed_identity_addr = {
.type = BT_ADDR_LE_RANDOM,
.a = {
.val = { 0xF1, 0x8E, 0x81, 0xFA, 0x87, 0xC5 },
},
};
static const char *ble_addr_type_str(uint8_t type)
{
switch (type)
{
case BT_ADDR_LE_PUBLIC:
return "public";
case BT_ADDR_LE_RANDOM:
return "random";
case BT_ADDR_LE_PUBLIC_ID:
return "public-id";
case BT_ADDR_LE_RANDOM_ID:
return "random-id";
default:
return "unknown";
}
}
static void ble_log_local_identity(void)
{
bt_addr_le_t addr = {0};
size_t count = 1;
char addr_str[BT_ADDR_LE_STR_LEN];
bt_id_get(&addr, &count);
if (count == 0U)
{
DBG_ERR("[BLE] No local identity address\r\n");
return;
}
bt_addr_le_to_str(&addr, addr_str, sizeof(addr_str));
DBG_CORE("[BLE] Local identity: %s (%s)\r\n",
addr_str,
ble_addr_type_str(addr.type));
}
static int ble_configure_fixed_identity(void)
{
bt_addr_le_t addr = fixed_identity_addr;
char addr_str[BT_ADDR_LE_STR_LEN];
int id;
bt_addr_le_to_str(&addr, addr_str, sizeof(addr_str));
DBG_CORE("[BLE] Request fixed identity: %s\r\n", addr_str);
id = bt_id_create(&addr, NULL);
if (id < 0)
{
DBG_ERR("[BLE] bt_id_create failed (err %d)\r\n", id);
return id;
}
if (id != BT_ID_DEFAULT)
{
DBG_ERR("[BLE] Unexpected identity slot %d\r\n", id);
return -EINVAL;
}
return 0;
}
static void adv_restart_handler(struct k_work *work)
{
@@ -61,7 +145,7 @@ static void connected(struct bt_conn *conn, uint8_t err)
{
if (err)
{
DBG_PRINTF("[BLE] Connect failed (err %d) -> re-adv\r\n", err);
DBG_ERR("[BLE] Connect failed (err %d) -> re-adv\r\n", err);
k_work_submit(&adv_restart_work);
return;
}
@@ -72,7 +156,7 @@ static void connected(struct bt_conn *conn, uint8_t err)
/* 연결 정보 출력 */
char addr_str[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
DBG_PRINTF("[BLE] Peer: %s\r\n", addr_str);
DBG_CORE("[BLE] Peer: %s\r\n", addr_str);
/* Connection parameters (30ms interval) */
struct bt_le_conn_param conn_param =
@@ -85,7 +169,7 @@ static void connected(struct bt_conn *conn, uint8_t err)
bt_conn_le_param_update(conn, &conn_param);
led_set_state(LED_STATE_OFF);
DBG_PRINTF("[BLE] Connected\r\n");
DBG_CORE("[BLE] Connected\r\n");
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
@@ -97,7 +181,7 @@ static void disconnected(struct bt_conn *conn, uint8_t reason)
}
ble_connection_st = false;
DBG_PRINTF("[BLE] Disconnected (reason 0x%02x)\r\n", reason);
DBG_CORE("[BLE] Disconnected (reason 0x%02x)\r\n", reason);
/* 워크큐에서 advertising 재시작 */
k_work_submit(&adv_restart_work);
@@ -122,6 +206,9 @@ BT_CONN_CB_DEFINE(conn_callbacks) =
*============================================================================*/
static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len)
{
ARG_UNUSED(conn);
DBG_CORE("[NUS RX] len=%u\r\n", len);
if (rx_callback)
{
rx_callback(data, len);
@@ -130,7 +217,10 @@ static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len
static void nus_sent(struct bt_conn *conn)
{
DBG_PRINTF("[NUS TX] complete\r\n");
ARG_UNUSED(conn);
/* 이 시점이 "직전 NUS 패킷 전송이 끝났다"는 완료 신호다. */
k_sem_give(&nus_tx_done_sem);
DBG_CORE("[NUS TX] complete\r\n");
}
static struct bt_nus_cb nus_cb =
@@ -147,26 +237,36 @@ int ble_service_init(ble_data_rx_cb_t rx_cb)
int err;
rx_callback = rx_cb;
k_mutex_init(&nus_tx_lock);
k_sem_init(&nus_tx_done_sem, 0, 1);
k_work_init(&adv_restart_work, adv_restart_handler);
err = ble_configure_fixed_identity();
if (err)
{
return err;
}
/* Enable BLE stack */
err = bt_enable(NULL);
if (err)
{
DBG_PRINTF("[BLE] bt_enable failed (err %d)\r\n", err);
DBG_ERR("[BLE] bt_enable failed (err %d)\r\n", err);
return err;
}
DBG_PRINTF("[BLE] Stack enabled\r\n");
DBG_CORE("[BLE] Stack enabled\r\n");
ble_log_local_identity();
/* Initialize NUS */
err = bt_nus_init(&nus_cb);
if (err)
{
DBG_PRINTF("[BLE] NUS init failed (err %d)\r\n", err);
DBG_ERR("[BLE] NUS init failed (err %d)\r\n", err);
return err;
}
DBG_PRINTF("[BLE] NUS initialized\r\n");
DBG_CORE("[BLE] NUS initialized\r\n");
return 0;
}
@@ -185,7 +285,7 @@ int ble_advertising_start(void)
err = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
if (err)
{
DBG_PRINTF("[BLE] Adv start failed (err %d)\r\n", err);
DBG_ERR("[BLE] Adv start failed (err %d)\r\n", err);
return err;
}
@@ -198,7 +298,7 @@ int ble_advertising_stop(void)
int err = bt_le_adv_stop();
if (err)
{
DBG_PRINTF("[BLE] Adv stop failed (err %d)\r\n", err);
DBG_ERR("[BLE] Adv stop failed (err %d)\r\n", err);
return err;
}
@@ -208,18 +308,59 @@ int ble_advertising_stop(void)
int ble_data_send(const uint8_t *data, uint16_t len)
{
int err;
if (!current_conn)
{
DBG_PRINTF("[NUS TX] not connected\r\n");
DBG_ERR("[NUS TX] not connected\r\n");
return -ENOTCONN;
}
DBG_PRINTF("[NUS TX] %d bytes\r\n", len);
int err = bt_nus_send(current_conn, data, len);
/*
* NUS는 한 번에 한 패킷씩 질서 있게 보내는 편이 안전하다.
* 특히 mbb?는 rbb + reb x6 + raa처럼 여러 응답을 연속 전송하므로
* 먼저 들어온 전송이 끝나기 전 다음 전송이 겹치지 않게 잠근다.
*/
k_mutex_lock(&nus_tx_lock, K_FOREVER);
/* 혹시 남아 있는 완료 신호가 있으면 비워서 "이번 전송 전용" 상태로 만든다. */
while (k_sem_take(&nus_tx_done_sem, K_NO_WAIT) == 0) {
}
DBG_CORE("[NUS TX] send len=%u\r\n", len);
for (int retry = 0; ; retry++)
{
err = bt_nus_send(current_conn, data, len);
if (err != -ENOMEM || retry >= 20) {
break;
}
/*
* -ENOMEM은 지금 당장 보낼 자리(TX 버퍼)가 없다는 뜻이다.
* 바로 포기하지 않고 짧게 쉬었다가 다시 시도한다.
*/
DBG_ERR("[NUS TX] busy, retry=%d\r\n", retry + 1);
k_msleep(5);
}
if (err)
{
DBG_PRINTF("[NUS TX] failed (err %d)\r\n", err);
DBG_ERR("[NUS TX] failed (err %d)\r\n", err);
k_mutex_unlock(&nus_tx_lock);
return err;
}
/*
* bt_nus_send()가 성공했다고 해서 공중으로 다 나간 것은 아니다.
* sent 콜백이 올 때까지 조금 기다려서 "다음 패킷을 보내도 되는 시점"을 맞춘다.
*/
err = k_sem_take(&nus_tx_done_sem, K_MSEC(1000));
if (err)
{
DBG_ERR("[NUS TX] completion timeout (err %d)\r\n", err);
}
k_mutex_unlock(&nus_tx_lock);
return err;
}