전원 + BLE + 배터리
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
/*******************************************************************************
|
||||
* @file ble_service.c
|
||||
* @brief BLE NUS service module (Zephyr port)
|
||||
*
|
||||
* Original VesiScan-Basic BLE implementation ported to Zephyr/NCS:
|
||||
* - NUS (Nordic UART Service) for data exchange
|
||||
* - Advertising: 40ms interval, 3-min timeout
|
||||
* - Connection: 30ms interval, 0 latency, 4s supervision timeout
|
||||
* - TX power +8 dBm, 2M PHY preferred
|
||||
* - Dev mode: no security / Production mode: bonding + passkey (TODO)
|
||||
******************************************************************************/
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <bluetooth/services/nus.h>
|
||||
|
||||
#include "ble_service.h"
|
||||
#include "main.h"
|
||||
#include "debug_print.h"
|
||||
#include "led_control.h"
|
||||
|
||||
LOG_MODULE_REGISTER(ble_svc, LOG_LEVEL_INF);
|
||||
|
||||
/*==============================================================================
|
||||
* Module variables
|
||||
*============================================================================*/
|
||||
static struct bt_conn *current_conn;
|
||||
static ble_data_rx_cb_t rx_callback;
|
||||
|
||||
/* BLE API는 ISR/콜백에서 직접 호출하면 안전하지 않을 수 있으므로 k_work 사용 */
|
||||
static struct k_work adv_restart_work;
|
||||
|
||||
static void adv_restart_handler(struct k_work *work)
|
||||
{
|
||||
ARG_UNUSED(work);
|
||||
ble_advertising_start();
|
||||
led_set_state(LED_STATE_ADVERTISING);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* Advertising data
|
||||
*============================================================================*/
|
||||
static const struct bt_data ad[] =
|
||||
{
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
BT_DATA(BT_DATA_NAME_COMPLETE, SERIAL_NUMBER, sizeof(SERIAL_NUMBER) - 1),
|
||||
};
|
||||
|
||||
static const struct bt_data sd[] =
|
||||
{
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
|
||||
};
|
||||
|
||||
/*==============================================================================
|
||||
* Connection callbacks
|
||||
*============================================================================*/
|
||||
static void connected(struct bt_conn *conn, uint8_t err)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
DBG_PRINTF("[BLE] Connect failed (err %d) -> re-adv\r\n", err);
|
||||
k_work_submit(&adv_restart_work);
|
||||
return;
|
||||
}
|
||||
|
||||
current_conn = bt_conn_ref(conn);
|
||||
ble_connection_st = true;
|
||||
|
||||
/* 연결 정보 출력 */
|
||||
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);
|
||||
|
||||
/* Connection parameters (30ms interval) */
|
||||
struct bt_le_conn_param conn_param =
|
||||
{
|
||||
.interval_min = BLE_MIN_CONN_INTERVAL,
|
||||
.interval_max = BLE_MAX_CONN_INTERVAL,
|
||||
.latency = BLE_SLAVE_LATENCY,
|
||||
.timeout = BLE_CONN_SUP_TIMEOUT,
|
||||
};
|
||||
bt_conn_le_param_update(conn, &conn_param);
|
||||
|
||||
led_set_state(LED_STATE_OFF);
|
||||
DBG_PRINTF("[BLE] Connected\r\n");
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
if (current_conn)
|
||||
{
|
||||
bt_conn_unref(current_conn);
|
||||
current_conn = NULL;
|
||||
}
|
||||
|
||||
ble_connection_st = false;
|
||||
DBG_PRINTF("[BLE] Disconnected (reason 0x%02x)\r\n", reason);
|
||||
|
||||
/* 워크큐에서 advertising 재시작 */
|
||||
k_work_submit(&adv_restart_work);
|
||||
}
|
||||
|
||||
static void le_param_updated(struct bt_conn *conn, uint16_t interval,
|
||||
uint16_t latency, uint16_t timeout)
|
||||
{
|
||||
DBG_PRINTF("[BLE] Params: interval=%d latency=%d timeout=%d\r\n",
|
||||
interval, latency, timeout);
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) =
|
||||
{
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
.le_param_updated = le_param_updated,
|
||||
};
|
||||
|
||||
/*==============================================================================
|
||||
* NUS callbacks
|
||||
*============================================================================*/
|
||||
static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
if (rx_callback)
|
||||
{
|
||||
rx_callback(data, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void nus_sent(struct bt_conn *conn)
|
||||
{
|
||||
DBG_PRINTF("[NUS TX] complete\r\n");
|
||||
}
|
||||
|
||||
static struct bt_nus_cb nus_cb =
|
||||
{
|
||||
.received = nus_received,
|
||||
.sent = nus_sent,
|
||||
};
|
||||
|
||||
/*==============================================================================
|
||||
* Public functions
|
||||
*============================================================================*/
|
||||
int ble_service_init(ble_data_rx_cb_t rx_cb)
|
||||
{
|
||||
int err;
|
||||
|
||||
rx_callback = rx_cb;
|
||||
|
||||
k_work_init(&adv_restart_work, adv_restart_handler);
|
||||
|
||||
/* Enable BLE stack */
|
||||
err = bt_enable(NULL);
|
||||
if (err)
|
||||
{
|
||||
DBG_PRINTF("[BLE] bt_enable failed (err %d)\r\n", err);
|
||||
return err;
|
||||
}
|
||||
DBG_PRINTF("[BLE] Stack enabled\r\n");
|
||||
|
||||
/* Initialize NUS */
|
||||
err = bt_nus_init(&nus_cb);
|
||||
if (err)
|
||||
{
|
||||
DBG_PRINTF("[BLE] NUS init failed (err %d)\r\n", err);
|
||||
return err;
|
||||
}
|
||||
DBG_PRINTF("[BLE] NUS initialized\r\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ble_advertising_start(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct bt_le_adv_param adv_param = BT_LE_ADV_PARAM_INIT(
|
||||
BT_LE_ADV_OPT_CONN,
|
||||
APP_ADV_INTERVAL, /* min interval */
|
||||
APP_ADV_INTERVAL + 16, /* max interval (slight window) */
|
||||
NULL /* undirected */
|
||||
);
|
||||
|
||||
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);
|
||||
return err;
|
||||
}
|
||||
|
||||
DBG_PRINTF("[BLE] Advertising started\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ble_advertising_stop(void)
|
||||
{
|
||||
int err = bt_le_adv_stop();
|
||||
if (err)
|
||||
{
|
||||
DBG_PRINTF("[BLE] Adv stop failed (err %d)\r\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
DBG_PRINTF("[BLE] Advertising stopped\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ble_data_send(const uint8_t *data, uint16_t len)
|
||||
{
|
||||
if (!current_conn)
|
||||
{
|
||||
DBG_PRINTF("[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);
|
||||
if (err)
|
||||
{
|
||||
DBG_PRINTF("[NUS TX] failed (err %d)\r\n", err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
bool ble_is_connected(void)
|
||||
{
|
||||
return (current_conn != NULL);
|
||||
}
|
||||
Reference in New Issue
Block a user