Repository navigation
Expand file tree
/
Copy pathble_interface.c
More file actions
193 lines (158 loc) · 6.65 KB
/
Copy pathble_interface.c
File metadata and controls
193 lines (158 loc) · 6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/**
* @file ble_interface.c
* @brief BLE abstraction: send samples, history batches, and EOF via indications.
* @author Ihar Hlukhau
* @date 26/04/2026
*/
#include "ble_interface.h"
#include "ble_event_handler.h"
#include "ble_gatt_server.h"
#include "ble_setup.h"
#include "host/ble_hs.h"
#include "host/ble_gatt.h"
#include "freertos/semphr.h"
#include "esp_log.h"
/** @brief First byte of the history EOF sentinel. */
#define HISTORY_EOF_SEQ_BYTE0 0xFF
/** @brief Second byte of the history EOF sentinel. */
#define HISTORY_EOF_SEQ_BYTE1 0xEF
/** @brief Third byte of the history EOF sentinel. */
#define HISTORY_EOF_SEQ_BYTE2 0x00
/** @brief Fourth byte of the history EOF sentinel. */
#define HISTORY_EOF_SEQ_BYTE3 0x00
/** @brief Length of the EOF sequence (bytes). */
#define HISTORY_EOF_SEQ_LEN 4
/** @brief Timeout for waiting on an indication ACK (ms). */
#define INDICATION_TIMEOUT_MS 80
/** @brief Max retries for history batch/EOF sends. */
#define HISTORY_BATCH_MAX_RETRIES 3
/** @brief Delay between retries for history sends (ms). */
#define HISTORY_RETRY_DELAY_MS 20
static const char *TAG = "BLE_INTF";
/** @brief Binary semaphore for indication flow control. */
static SemaphoreHandle_t s_indication_sem = NULL;
/** @brief Current history transfer state. */
static history_state_e s_history_state = HISTORY_STATE_IDLE;
/** @brief Map a NimBLE return code to an esp_err_t for fault-compatible propagation. */
static esp_err_t nimble_to_esp_err(const int nimble_rc) {
if (nimble_rc == 0) return ESP_OK;
if (nimble_rc == BLE_HS_ENOTCONN || nimble_rc == BLE_HS_EBUSY) return ESP_ERR_INVALID_STATE;
if (nimble_rc == BLE_HS_ENOMEM) return ESP_ERR_NO_MEM;
if (nimble_rc == BLE_HS_ETIMEOUT) return ESP_ERR_TIMEOUT;
return ESP_FAIL;
}
void ble_signal_indication_done(void) {
if (s_indication_sem != NULL) {
xSemaphoreGive(s_indication_sem);
}
}
bool ble_wait_for_indication_done(const uint32_t timeout_ms) {
if (s_indication_sem == NULL) return false;
return xSemaphoreTake(s_indication_sem, pdMS_TO_TICKS(timeout_ms)) == pdTRUE;
}
history_state_e ble_get_history_state(void) {
return s_history_state;
}
void ble_set_history_state(history_state_e state) {
s_history_state = state;
}
esp_err_t ble_send_sample(const sen66_sample_s *sample) {
const uint16_t live_data_attr_handle = ble_gatt_get_attr_handles()->live_data;
if (!ble_is_ready_for_live_data() || live_data_attr_handle == 0) {
return nimble_to_esp_err(BLE_HS_ENOTCONN);
}
if (!ble_wait_for_indication_done(0)) {
return nimble_to_esp_err(BLE_HS_EBUSY);
}
struct os_mbuf *om = ble_hs_mbuf_from_flat(sample, sizeof(*sample));
if (om == NULL) {
ble_signal_indication_done();
return nimble_to_esp_err(BLE_HS_ENOMEM);
}
const int rc = ble_gatts_indicate_custom(ble_get_conn_handle(), live_data_attr_handle, om);
if (rc != 0) {
ble_signal_indication_done();
}
return nimble_to_esp_err(rc);
}
esp_err_t ble_send_history_batch(const sen66_sample_s *samples, const uint8_t count) {
const uint16_t history_data_attr_handle = ble_gatt_get_attr_handles()->history_data;
if (!ble_is_ready_for_history_data() || history_data_attr_handle == 0) {
return nimble_to_esp_err(BLE_HS_ENOTCONN);
}
if (count == 0) return ESP_OK;
for (int retry = 0; retry < HISTORY_BATCH_MAX_RETRIES; retry++) {
if (!ble_wait_for_indication_done(INDICATION_TIMEOUT_MS)) {
ESP_LOGW(TAG, "History batch pre-send timeout (retry %d/%d)", retry + 1, HISTORY_BATCH_MAX_RETRIES);
ble_signal_indication_done();
vTaskDelay(pdMS_TO_TICKS(HISTORY_RETRY_DELAY_MS));
continue;
}
struct os_mbuf *om = ble_hs_mbuf_from_flat(samples, count * sizeof(sen66_sample_s));
if (om == NULL) {
ble_signal_indication_done();
return nimble_to_esp_err(BLE_HS_ENOMEM);
}
const int rc = ble_gatts_indicate_custom(ble_get_conn_handle(), history_data_attr_handle, om);
if (rc != 0) {
ble_signal_indication_done();
return nimble_to_esp_err(rc);
}
if (!ble_wait_for_indication_done(INDICATION_TIMEOUT_MS)) {
ESP_LOGW(TAG, "History indication ACK timed out (retry %d/%d)", retry + 1, HISTORY_BATCH_MAX_RETRIES);
ble_signal_indication_done();
vTaskDelay(pdMS_TO_TICKS(HISTORY_RETRY_DELAY_MS));
continue;
}
ble_signal_indication_done();
return ESP_OK;
}
ESP_LOGE(TAG, "History batch send failed after %d retries", HISTORY_BATCH_MAX_RETRIES);
return nimble_to_esp_err(BLE_HS_ETIMEOUT);
}
esp_err_t ble_send_history_eof(void) {
const uint16_t history_data_attr_handle = ble_gatt_get_attr_handles()->history_data;
if (!ble_is_ready_for_history_data() || history_data_attr_handle == 0) {
return nimble_to_esp_err(BLE_HS_ENOTCONN);
}
const uint8_t eof_seq[] = { HISTORY_EOF_SEQ_BYTE0, HISTORY_EOF_SEQ_BYTE1,
HISTORY_EOF_SEQ_BYTE2, HISTORY_EOF_SEQ_BYTE3 };
for (int retry = 0; retry < HISTORY_BATCH_MAX_RETRIES; retry++) {
if (!ble_wait_for_indication_done(INDICATION_TIMEOUT_MS)) {
ESP_LOGW(TAG, "EOF pre-send timeout (retry %d/%d)", retry + 1, HISTORY_BATCH_MAX_RETRIES);
ble_signal_indication_done();
vTaskDelay(pdMS_TO_TICKS(HISTORY_RETRY_DELAY_MS));
continue;
}
struct os_mbuf *om = ble_hs_mbuf_from_flat(eof_seq, HISTORY_EOF_SEQ_LEN);
if (om == NULL) {
ble_signal_indication_done();
return nimble_to_esp_err(BLE_HS_ENOMEM);
}
const int rc = ble_gatts_indicate_custom(ble_get_conn_handle(), history_data_attr_handle, om);
if (rc != 0) {
ble_signal_indication_done();
return nimble_to_esp_err(rc);
}
if (!ble_wait_for_indication_done(INDICATION_TIMEOUT_MS)) {
ESP_LOGW(TAG, "EOF indication ACK timed out (retry %d/%d)", retry + 1, HISTORY_BATCH_MAX_RETRIES);
ble_signal_indication_done();
vTaskDelay(pdMS_TO_TICKS(HISTORY_RETRY_DELAY_MS));
continue;
}
ble_signal_indication_done();
return ESP_OK;
}
ESP_LOGE(TAG, "EOF send failed after %d retries", HISTORY_BATCH_MAX_RETRIES);
return nimble_to_esp_err(BLE_HS_ETIMEOUT);
}
esp_err_t ble_init(void) {
esp_log_level_set(TAG, CONFIG_LOG_MAXIMUM_LEVEL);
s_indication_sem = xSemaphoreCreateBinary();
if (s_indication_sem == NULL) {
ESP_LOGE(TAG, "Failed to create indication semaphore");
return ESP_ERR_NO_MEM;
}
xSemaphoreGive(s_indication_sem);
return ble_init_nimble_stack();
}