From 8274cab154f198447f12028e1686bbe2a6de6ebd Mon Sep 17 00:00:00 2001 From: Dien-Nhung Nguyen-Phu Date: Sun, 23 Jun 2024 19:22:20 +0700 Subject: feat: add initial Bluetooth LE module --- src/ble/peripheral.c | 246 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 src/ble/peripheral.c (limited to 'src/ble/peripheral.c') diff --git a/src/ble/peripheral.c b/src/ble/peripheral.c new file mode 100644 index 0000000..955799a --- /dev/null +++ b/src/ble/peripheral.c @@ -0,0 +1,246 @@ +#include "CH58xBLE_LIB.h" +#include "setup.h" + +#define ADV_UUID (0xFEE0) + +static uint8 taskid = INVALID_TASK_ID; + +typedef struct +{ + uint16 connHandle; // Connection handle of current connection + uint16 connInterval; + uint16 connSlaveLatency; + uint16 connTimeout; +} peripheralConnItem_t; + +#define SBP_PARAM_UPDATE_DELAY 1600 // Parameter update delay (unit of 0.625ms) +#define SLAVE_LATENCY 0 + +#define MIN_ADV_INTERVAL 100 // Advertising interval (units of 0.625ms) +#define MAX_ADV_INTERVAL 200 // Advertising interval (units of 0.625ms) + +#define MIN_CONN_INTERVAL 20 // Connection interval (units of 1.25ms) +#define MAX_CONN_INTERVAL 100 // Connection interval (units of 1.25ms) + +#define CONN_TIMEOUT 100 // Supervision timeout (units of 10ms) + +// GAP - SCAN RSP data (max size = 31 bytes) +static uint8 scanRspData[] = { + // complete name + 16, // length of this section + GAP_ADTYPE_LOCAL_NAME_COMPLETE, + 'L', 'E','D', ' ', + 'B', 'a','d', 'g', 'e', ' ', + 'M', 'a','g', 'i', 'c', + + // connection interval range + 0x05, // length of this section + GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE, + LO_UINT16(MIN_CONN_INTERVAL), + HI_UINT16(MIN_CONN_INTERVAL), + LO_UINT16(MAX_CONN_INTERVAL), + HI_UINT16(MAX_CONN_INTERVAL), + + // Tx power level + 0x02, // length of this data + GAP_ADTYPE_POWER_LEVEL, + 9 // 9dBm +}; + +// GAP - Advertisement data (max size = 31 bytes) +// keep short, save energy, save the planet +static uint8 advertData[] = { + 0x02, // section length + GAP_ADTYPE_FLAGS, + GAP_ADTYPE_FLAGS_GENERAL | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED, + + // advertise UUID + 0x03, // section length + GAP_ADTYPE_16BIT_MORE, + LO_UINT16(ADV_UUID), + HI_UINT16(ADV_UUID) +}; + +// GAP GATT Attributes +static uint8 devName[GAP_DEVICE_NAME_LEN] = "LED Badge Magic"; + +// Connection item list +static peripheralConnItem_t conn_list; + +static void initConn(peripheralConnItem_t *conn) +{ + conn->connHandle = GAP_CONNHANDLE_INIT; + conn->connInterval = 0; + conn->connSlaveLatency = 0; + conn->connTimeout = 0; +} + +static void processTMOSMsg(tmos_event_hdr_t *pMsg) +{ + switch(pMsg->event) { + default: + break; + } +} + +static void enable_advertising(uint8_t enable) +{ + GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8), &enable); +} + +static void link_onEstablished(gapRoleEvent_t *pe) +{ + gapEstLinkReqEvent_t *e = (gapEstLinkReqEvent_t *)pe; + + // If already connected + if(conn_list.connHandle != GAP_CONNHANDLE_INIT) { + // Only allow 1 connection, so the new connection will be dropped: + GAPRole_TerminateLink(e->connectionHandle); + GAPRole_PeripheralConnParamUpdateReq(e->connectionHandle, + MIN_CONN_INTERVAL, + MAX_CONN_INTERVAL, + SLAVE_LATENCY, + CONN_TIMEOUT, + taskid); + return; + } + conn_list.connHandle = e->connectionHandle; + conn_list.connInterval = e->connInterval; + conn_list.connSlaveLatency = e->connLatency; + conn_list.connTimeout = e->connTimeout; + enable_advertising(FALSE); +} + +static void link_onTerminated(gapRoleEvent_t *pe) +{ + gapTerminateLinkEvent_t *event = (gapTerminateLinkEvent_t *)pe; + GAPRole_TerminateLink(pe->linkCmpl.connectionHandle); + enable_advertising(TRUE); + + if(event->connectionHandle == conn_list.connHandle) { + conn_list.connHandle = GAP_CONNHANDLE_INIT; + conn_list.connInterval = 0; + conn_list.connSlaveLatency = 0; + conn_list.connTimeout = 0; + } else { + // Requested connection is not existed in connection list + } +} + +static void gap_onParamUpdate(uint16 connHandle, uint16 connInterval, + uint16 connSlaveLatency, uint16 connTimeout) +{ + conn_list.connHandle = connHandle; + conn_list.connInterval = connInterval; + conn_list.connSlaveLatency = connSlaveLatency; + conn_list.connTimeout = connTimeout; +} + +static void gap_onStateChange(gapRole_States_t newState, gapRoleEvent_t *pEvent) +{ + switch(newState & GAPROLE_STATE_ADV_MASK) { + case GAPROLE_ADVERTISING: + if(pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT) { + link_onTerminated(pEvent); + } + break; + + case GAPROLE_CONNECTED: + if(pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT) { + link_onEstablished(pEvent); + } + break; + + case GAPROLE_WAITING: + if(pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT) { + link_onTerminated(pEvent); + } else { + // refer to pEvent->gap.opcode + } + break; + + default: + break; + } +} + +// GAP Role Callbacks +static gapRolesCBs_t gap_handlers = { + gap_onStateChange, + NULL, + gap_onParamUpdate +}; + +// Broadcast Callbacks +static gapRolesBroadcasterCBs_t broadcast_handlers = { + NULL, + NULL +}; + +// GAP Bond Manager Callbacks +static gapBondCBs_t bond_managers = { + NULL, + NULL +}; + + +static uint16 peripheral_task(uint8 task_id, uint16 events) +{ + if(events & SYS_EVENT_MSG) { // Handle BLE stack message + uint8 *pMsg = tmos_msg_receive(taskid); + if(pMsg) { + processTMOSMsg((tmos_event_hdr_t *)pMsg); + tmos_msg_deallocate(pMsg); + } + return (events ^ SYS_EVENT_MSG); + } + + return 0; +} + +static void gap_init() +{ + GAPRole_PeripheralInit(); + + static uint8 initial_advertising_enable = TRUE; + static uint16 desired_min_interval = 6; + static uint16 desired_max_interval = 500; + + // Set the GAP Role Parameters + GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8), &initial_advertising_enable); + GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData); + GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData); + GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16), &desired_min_interval); + GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16), &desired_max_interval); + + // Set the GAP Characteristics + GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, devName); + GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, MIN_ADV_INTERVAL); + GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, MAX_ADV_INTERVAL); + + static uint32 passkey = 0; // passkey "000000" + static uint8 pairMode = GAPBOND_PAIRING_MODE_NO_PAIRING; + static uint8 mitm = FALSE; + static uint8 bonding = FALSE; + static uint8 ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY; + GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32), &passkey); + GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8), &pairMode); + GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8), &mitm); + GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8), &ioCap); + GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8), &bonding); + + GAPRole_BroadcasterSetCB(&broadcast_handlers); + + GGS_AddService(GATT_ALL_SERVICES); + GATTServApp_AddService(GATT_ALL_SERVICES); +} + +void peripheral_init() +{ + taskid = TMOS_ProcessEventRegister(peripheral_task); + + initConn(&conn_list); + gap_init(); + + GAPRole_PeripheralStartDevice(taskid, &bond_managers, &gap_handlers); +} -- cgit v1.2.3