ACwing FC firmware
STM32WB55: attitude IEKF, altitude Kalman filter, flight core, protocol v2
Loading...
Searching...
No Matches
ble_tlm_svc.c
Go to the documentation of this file.
1
26#include "ble_tlm_svc.h"
27#include "app_config.h"
28
29#if APP_TLM_BLE
30
31#if defined(__has_include)
32#if !__has_include("common_blesvc.h")
33#error "APP_TLM_BLE = 1 requires STM32_WPAN (BLE) enabled in CubeMX: see 03 - Docs/firmware/ble.md, CubeMX configuration"
34#endif
35#endif
36
37#include "common_blesvc.h"
38#include "cmsis_os2.h"
39#include "tlm_ble.h"
40#include "app_link.h"
41#include "main.h"
42#include <string.h>
43
44#ifndef CFG_BLE_MAX_ATT_MTU
45#define CFG_BLE_MAX_ATT_MTU 23
46#endif
47#define TLM_CHAR_MAX_LEN (CFG_BLE_MAX_ATT_MTU - 3) /* one notification */
48#define CMD_CHAR_MAX_LEN 20
49
50#define FLAG_KICK 0x0001u /* pool available / subscription changed / connected */
51
52/* UUIDs written MSB first, as printed (7E4A0001-2C6B-...); the stack wants them LSB first */
53static const uint8_t UUID_SVC[16] = { 0x7E,0x4A,0x00,0x01, 0x2C,0x6B, 0x4F,0x8E,
54 0x9A,0x5D, 0x3B,0x1C,0x0E,0x1D,0x2F,0x30 };
55static const uint8_t UUID_TLM[16] = { 0x7E,0x4A,0x00,0x02, 0x2C,0x6B, 0x4F,0x8E,
56 0x9A,0x5D, 0x3B,0x1C,0x0E,0x1D,0x2F,0x30 };
57static const uint8_t UUID_CMD[16] = { 0x7E,0x4A,0x00,0x03, 0x2C,0x6B, 0x4F,0x8E,
58 0x9A,0x5D, 0x3B,0x1C,0x0E,0x1D,0x2F,0x30 };
59
60static uint16_t s_svcHdl, s_tlmHdl, s_cmdHdl;
61static volatile uint16_t s_conn = 0xFFFFu;
62static volatile uint8_t s_notify; /* CCCD of TLM: notifications enabled */
63static volatile uint8_t s_paramsPending; /* request connection parameters/DLE */
64static osThreadId_t s_txThread;
65static uint8_t s_chunk[TLM_CHAR_MAX_LEN];
66
67static void uuid_lsb_first(uint8_t *dst, const uint8_t *msb_first)
68{
69 for (int i = 0; i < 16; ++i) dst[i] = msb_first[15 - i];
70}
71
72static void kick(void)
73{
74 if (s_txThread) (void)osThreadFlagsSet(s_txThread, FLAG_KICK);
75}
76
77/* ------------------------------------------------------------------ events
78 * Runs in the stack's HCI user-event thread: flags only. */
79static SVCCTL_EvtAckStatus_t TlmSvc_EventHandler(void *Event)
80{
81 SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
82 hci_event_pckt *ev = (hci_event_pckt *)(((hci_uart_pckt *)Event)->data);
83 if (ev->evt != HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) return ret;
84
85 evt_blecore_aci *ble = (evt_blecore_aci *)ev->data;
86 switch (ble->ecode) {
87 case ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE: {
88 aci_gatt_attribute_modified_event_rp0 *am =
89 (aci_gatt_attribute_modified_event_rp0 *)ble->data;
90 if (am->Attr_Handle == (uint16_t)(s_tlmHdl + 2u)) { /* CCCD of TLM */
91 ret = SVCCTL_EvtAckFlowEnable;
92 s_conn = am->Connection_Handle;
93 if (am->Attr_Data_Length > 0u && (am->Attr_Data[0] & COMSVC_Notification)) {
95 s_notify = 1u;
96 s_paramsPending = 1u;
97 } else {
98 s_notify = 0u;
100 }
101 kick();
102 } else if (am->Attr_Handle == (uint16_t)(s_cmdHdl + 1u)) { /* CMD value */
103 ret = SVCCTL_EvtAckFlowEnable;
104 /* Only the arrival is recorded here (app_link.h): time from the 1 kHz
105 * HAL tick, RC values, or a copy into the command queue drained by
106 * the AppTask. No command is executed in the BLE context. */
107 if (!g_dbg_ble_stall) {
108 appl_on_cmd_write(am->Attr_Data, am->Attr_Data_Length, HAL_GetTick());
109 tlm_ble_on_command(am->Attr_Data, am->Attr_Data_Length);
110 }
111 }
112 break;
113 }
114 case ACI_ATT_EXCHANGE_MTU_RESP_VSEVT_CODE: {
115 aci_att_exchange_mtu_resp_event_rp0 *mr =
116 (aci_att_exchange_mtu_resp_event_rp0 *)ble->data;
117 /* effective ATT_MTU = min(ours, peer's) */
118 uint16_t mtu = mr->Server_RX_MTU;
119 if (mtu > CFG_BLE_MAX_ATT_MTU) mtu = CFG_BLE_MAX_ATT_MTU;
120 tlm_ble_set_mtu(mtu);
121 break; /* not ours only: let app_ble.c see it too */
122 }
123 case ACI_GATT_TX_POOL_AVAILABLE_VSEVT_CODE:
124 kick();
125 break;
126 default:
127 break;
128 }
129 return ret;
130}
131
132/* ------------------------------------------------------------------ TX thread */
133static void request_link_params(uint16_t conn)
134{
135 /* 15-30 ms, no latency, 4 s supervision: accepted by iOS (min 15 ms,
136 * max >= min + 15 ms) and Windows/macOS. The central has the last word. */
137 tBleStatus st = aci_l2cap_connection_parameter_update_req(conn,
140 if (st != BLE_STATUS_SUCCESS) tlm_ble_set_error(st);
141 /* MTU exchange started by us as well: iOS and Windows usually start it
142 * themselves, but if the central does not, we would stay at 23 bytes.
143 * The result arrives as ACI_ATT_EXCHANGE_MTU_RESP (handled above). */
144 (void)aci_gatt_exchange_config(conn);
145 /* Data Length Extension: 251-byte link-layer packets -> one notification
146 * per packet with large MTUs. Not fatal if the central refuses. */
147 (void)hci_le_set_data_length(conn, 251u, 2120u);
148}
149
150static void BleTxThread(void *arg)
151{
152 (void)arg;
153 for (;;) {
154 (void)osThreadFlagsWait(FLAG_KICK, osFlagsWaitAny, APP_BLE_FLUSH_MS);
155 if (g_dbg_ble_stall) continue; /* bench test: BLE path stalled on purpose */
156
157 if (s_paramsPending && s_conn != 0xFFFFu) {
158 s_paramsPending = 0u;
159 request_link_params(s_conn);
160 }
161 if (!s_notify) { tlm_ble_discard(); continue; }
162
163 uint16_t maxc = tlm_ble_chunk_max();
164 if (maxc > TLM_CHAR_MAX_LEN) maxc = TLM_CHAR_MAX_LEN;
165 for (;;) {
166 const uint16_t n = tlm_ble_peek(s_chunk, maxc);
167 if (n == 0u) break;
168 const tBleStatus st = aci_gatt_update_char_value(s_svcHdl, s_tlmHdl, 0u,
169 (uint8_t)n, s_chunk);
170 if (st == BLE_STATUS_SUCCESS) {
172 } else if (st == BLE_STATUS_INSUFFICIENT_RESOURCES || st == BLE_STATUS_BUSY) {
173 break; /* link full: retry on TX_POOL_AVAILABLE */
174 } else {
175 tlm_ble_set_error(st); /* e.g. disconnected meanwhile */
177 break;
178 }
179 }
180 }
181}
182
183/* ------------------------------------------------------------------ public */
184extern volatile uint8_t g_cpu2_ready; /* bsp/cfg_flash_wb.c */
185
186void ble_tlm_svc_init(void)
187{
188 Char_UUID_t uuid;
189 tBleStatus st;
190
191 g_cpu2_ready = 1u; /* called from APP_BLE_Init(): the wireless stack is running */
192
194 SVCCTL_RegisterSvcHandler(TlmSvc_EventHandler);
195
196 /* records: 1 service + TLM (decl, value, CCCD) + CMD (decl, value) = 6 -> 8 */
197 uuid_lsb_first(uuid.Char_UUID_128, UUID_SVC);
198 st = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)&uuid, PRIMARY_SERVICE,
199 8u, &s_svcHdl);
200 if (st != BLE_STATUS_SUCCESS) { tlm_ble_set_error(st); return; }
201
202 uuid_lsb_first(uuid.Char_UUID_128, UUID_TLM);
203 st = aci_gatt_add_char(s_svcHdl, UUID_TYPE_128, &uuid, TLM_CHAR_MAX_LEN,
204 CHAR_PROP_NOTIFY, ATTR_PERMISSION_NONE,
205 GATT_DONT_NOTIFY_EVENTS, 0x10u, CHAR_VALUE_LEN_VARIABLE,
206 &s_tlmHdl);
207 if (st != BLE_STATUS_SUCCESS) { tlm_ble_set_error(st); return; }
208
209 uuid_lsb_first(uuid.Char_UUID_128, UUID_CMD);
210 st = aci_gatt_add_char(s_svcHdl, UUID_TYPE_128, &uuid, CMD_CHAR_MAX_LEN,
211 CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE,
212 ATTR_PERMISSION_NONE, GATT_NOTIFY_ATTRIBUTE_WRITE,
213 0x10u, CHAR_VALUE_LEN_VARIABLE, &s_cmdHdl);
214 if (st != BLE_STATUS_SUCCESS) { tlm_ble_set_error(st); return; }
215
216 static const osThreadAttr_t a = { .name = "bletx", .priority = osPriorityBelowNormal,
217 .stack_size = 1024 };
218 s_txThread = osThreadNew(BleTxThread, NULL, &a);
220}
221
222void ble_tlm_on_connect(uint16_t conn_handle)
223{
225 s_conn = conn_handle;
226 s_notify = 0u;
229}
230
231void ble_tlm_on_disconnect(void)
232{
233 appl_on_disconnect(); /* may only make the app-link failsafe start earlier */
234 s_conn = 0xFFFFu;
235 s_notify = 0u;
236 s_paramsPending = 0u;
237 tlm_ble_set_state(TLM_BLE_READY); /* app_ble.c restarts advertising */
238 kick(); /* the TX thread empties the queue */
239}
240
241#else /* APP_TLM_BLE == 0: hooks compile to nothing */
242
243void ble_tlm_svc_init(void) {}
244void ble_tlm_on_connect(uint16_t conn_handle) { (void)conn_handle; }
246
247#endif /* APP_TLM_BLE */
Phase-1 configuration: P-NUCLEO-WB55 (NUCLEO-WB55RG) + X-NUCLEO-IKS4A1 in Mode 3 (LSM6DSV16X is the s...
#define APP_BLE_CONN_INT_MIN
x1.25 ms = 15 ms (iOS minimum)
Definition app_config.h:124
#define APP_BLE_FLUSH_MS
Definition app_config.h:123
#define APP_BLE_CONN_INT_MAX
x1.25 ms = 30 ms (iOS: max >= min + 15 ms)
Definition app_config.h:125
#define APP_BLE_SUPERV_TIMEOUT
x10 ms = 4 s
Definition app_config.h:126
void ble_tlm_svc_init(void)
After SVCCTL_Init() and before advertising starts (APP_BLE_Init_3)
void ble_tlm_on_disconnect(void)
void ble_tlm_on_connect(uint16_t conn_handle)
"ACwing telemetry" GATT service on the STM32WB BLE stack (STM32_WPAN).
uint32_t n
Definition calib_mgr.c:35
volatile uint8_t g_cpu2_ready
void tlm_ble_set_state(tlm_ble_state_t st)
Definition tlm_ble.c:198
uint16_t tlm_ble_peek(uint8_t *dst, uint16_t max)
Copies up to max bytes of the next notification into dst without removing them; tlm_ble_consume(n) wi...
Definition tlm_ble.c:152
volatile uint8_t g_dbg_ble_stall
Bench test of HANDOVER_FW.md §6 / §16.12 (debugger Live Expression): 1 = the BLE path stops on purpos...
Definition tlm_ble.c:43
void tlm_ble_consume(uint16_t n)
Definition tlm_ble.c:180
void tlm_ble_on_command(const uint8_t *data, uint16_t len)
legacy 0x01/0x02
Definition tlm_ble.c:219
void tlm_ble_reset(void)
Definition tlm_ble.c:53
uint16_t tlm_ble_chunk_max(void)
ATT_MTU - 3.
Definition tlm_ble.c:237
void tlm_ble_discard(void)
drop everything queued (consumer side)
Definition tlm_ble.c:190
void tlm_ble_set_mtu(uint16_t att_mtu)
Definition tlm_ble.c:211
void tlm_ble_set_error(uint8_t ble_status)
last non-recoverable stack error
Definition tlm_ble.c:217
BLE telemetry, transport-independent part (phase 2 + protocol v2).
@ TLM_BLE_STREAMING
Definition tlm_ble.h:48
@ TLM_BLE_OFF
BLE not compiled in or stack not started
Definition tlm_ble.h:45
@ TLM_BLE_CONNECTED
central connected, notifications not enabled
Definition tlm_ble.h:47
@ TLM_BLE_READY
service registered, advertising
Definition tlm_ble.h:46
#define TLM_BLE_MTU_DEFAULT
ATT_MTU before any exchange
Definition tlm_ble.h:43
static USBD_StatusTypeDef st(HAL_StatusTypeDef h)
Definition usbd_conf.c:86