ruuvi.drivers.c  ${PROJECT_VERSION}
Drivers for external sensors and peripherals on embedded systems.
ruuvi_task_gatt.c
Go to the documentation of this file.
1 
8 #include "ruuvi_driver_error.h"
14 #include "ruuvi_interface_log.h"
15 #include "ruuvi_interface_rtc.h"
19 #include "ruuvi_task_gatt.h"
20 #if RT_GATT_ENABLED
21 
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 
26 #ifndef TASK_GATT_LOG_LEVEL
27 #define TASK_GATT_LOG_LEVEL RI_LOG_LEVEL_INFO
28 #endif
29 
30 static inline void LOGD (const char * const msg)
31 {
33 }
34 
35 static inline void LOGDHEX (const uint8_t * const msg, const size_t len)
36 {
37  ri_log_hex (RI_LOG_LEVEL_DEBUG, msg, len);
38 }
39 
40 static ri_comm_channel_t m_channel;
41 static bool m_is_init;
42 static bool m_nus_is_init;
43 static bool m_dis_is_init;
44 static bool m_dfu_is_init;
45 static bool m_nus_is_connected;
46 static char m_name[SCAN_RSP_NAME_MAX_LEN + 1] = { 0 };
47 
48 static ri_comm_cb_t m_on_connected;
49 static ri_comm_cb_t m_on_disconnected;
50 static ri_comm_cb_t m_on_received;
51 static ri_comm_cb_t m_on_sent;
52 
53 // https://github.com/arm-embedded/gcc-arm-none-eabi.debian/blob/master/src/libiberty/strnlen.c
54 // Not included when compiled with std=c99.
55 static inline size_t safe_strlen (const char * s, size_t maxlen)
56 {
57  size_t i;
58 
59  for (i = 0; (i < maxlen) && ('\0' != s[i]); ++i);
60 
61  return i;
62 }
63 
64 #ifdef CEEDLING
65 void rt_gatt_mock_state_reset()
66 {
67  m_on_connected = NULL;
68  m_on_disconnected = NULL;
69  m_on_received = NULL;
70  m_on_sent = NULL;
71  m_is_init = false;
72  m_nus_is_init = false;
73  m_dfu_is_init = false;
74  m_dis_is_init = false;
75  m_nus_is_connected = false;
76  memset (&m_channel, 0, sizeof (ri_comm_channel_t));
77  memset (m_name, 0, sizeof (m_name));
78 }
79 #endif
80 
99 #ifndef CEEDLING
100 static
101 #endif
102 rd_status_t rt_gatt_on_nus_isr (ri_comm_evt_t evt,
103  void * p_data, size_t data_len)
104 {
105  switch (evt)
106  {
107  // Note: This gets called only after the NUS notifications have been registered.
108  case RI_COMM_CONNECTED:
109  m_nus_is_connected = true;
110  (NULL != m_on_connected) ? m_on_connected (p_data, data_len) : false;
111  break;
112 
114  m_nus_is_connected = false;
115  (NULL != m_on_disconnected) ? m_on_disconnected (p_data, data_len) : false;
116  break;
117 
118  case RI_COMM_SENT:
119  (NULL != m_on_sent) ? m_on_sent (p_data, data_len) : false;
120  break;
121 
122  case RI_COMM_RECEIVED:
123  LOGD ("<<<;");
124  LOGDHEX (p_data, data_len);
125  LOGD (";\r\n");
126  (NULL != m_on_received) ? m_on_received (p_data, data_len) : false;
127  break;
128 
129  default:
130  break;
131  }
132 
133  return RD_SUCCESS;
134 }
135 
136 rd_status_t rt_gatt_dis_init (const ri_comm_dis_init_t * const p_dis)
137 {
138  rd_status_t err_code = RD_SUCCESS;
139 
140  if (NULL == p_dis)
141  {
142  err_code |= RD_ERROR_NULL;
143  }
144  else if (rt_gatt_is_init() && (!m_dis_is_init))
145  {
146  err_code |= ri_gatt_dis_init (p_dis);
147  m_dis_is_init = (RD_SUCCESS == err_code);
148  }
149  else
150  {
151  err_code |= RD_ERROR_INVALID_STATE;
152  }
153 
154  return err_code;
155 }
156 
158 {
159  rd_status_t err_code = RD_SUCCESS;
160 
161  if (rt_gatt_is_init() && (!m_nus_is_init))
162  {
163  err_code |= ri_gatt_nus_init (&m_channel);
164 
165  if (RD_SUCCESS == err_code)
166  {
167  m_channel.on_evt = rt_gatt_on_nus_isr;
168  m_nus_is_init = true;
169  }
170  }
171  else
172  {
173  err_code |= RD_ERROR_INVALID_STATE;
174  }
175 
176  return err_code;
177 }
178 
180 {
181  rd_status_t err_code = RD_SUCCESS;
182 
183  if (rt_gatt_is_init() && (!m_dfu_is_init))
184  {
185  err_code |= ri_gatt_dfu_init();
186  m_dfu_is_init = (RD_SUCCESS == err_code);
187  }
188  else
189  {
190  err_code |= RD_ERROR_INVALID_STATE;
191  }
192 
193  return err_code;
194 }
195 
196 rd_status_t rt_gatt_init (const char * const name)
197 {
198  rd_status_t err_code = RD_SUCCESS;
199 
200  if (NULL == name)
201  {
202  err_code |= RD_ERROR_NULL;
203  }
204  else if (rt_adv_is_init() && (!rt_gatt_is_init()))
205  {
206  const size_t name_length = safe_strlen (name, sizeof (m_name));
207 
208  if (sizeof (m_name) > name_length)
209  {
210  err_code |= ri_gatt_init();
211  memcpy (m_name, name, name_length);
212  m_name[name_length] = '\0';
213  }
214  else
215  {
216  err_code |= RD_ERROR_INVALID_LENGTH;
217  }
218  }
219  else
220  {
221  err_code |= RD_ERROR_INVALID_STATE;
222  }
223 
224  if (RD_SUCCESS == err_code)
225  {
226  m_is_init = true;
227  }
228 
229  return err_code;
230 }
231 
233 {
234  rd_status_t err_code = RD_SUCCESS;
235 
236  if (rt_adv_is_init())
237  {
238  err_code |= RD_ERROR_INVALID_STATE;
239  }
240  else
241  {
243  err_code |= ri_radio_get_modulation (&modulation);
248  err_code |= ri_radio_uninit();
249  err_code |= ri_gatt_uninit();
250  memset (&m_channel, 0, sizeof (m_channel));
251  err_code |= ri_radio_init (modulation);
252  m_is_init = false;
253  m_dis_is_init = false;
254  m_nus_is_init = false;
255  m_dfu_is_init = false;
256  }
257 
258  return err_code;
259 }
260 
262 {
263  rd_status_t err_code = RD_SUCCESS;
264 
265  if (rt_gatt_is_init())
266  {
267  err_code |= rt_adv_connectability_set (true, m_name);
268  }
269  else
270  {
271  err_code |= RD_ERROR_INVALID_STATE;
272  }
273 
274  return err_code;
275 }
276 
278 {
279  rd_status_t err_code = RD_SUCCESS;
280 
281  if (rt_gatt_is_init())
282  {
283  err_code |= rt_adv_connectability_set (false, NULL);
284  }
285  else
286  {
287  err_code |= RD_ERROR_INVALID_STATE;
288  }
289 
290  return err_code;
291 }
292 
293 bool rt_gatt_is_init (void)
294 {
295  return m_is_init;
296 }
297 
303 bool rt_gatt_nus_is_connected (void)
304 {
305  return m_nus_is_connected && (NULL != m_channel.send);
306 }
307 
309  * const p_msg)
310 {
311  rd_status_t err_code = RD_SUCCESS;
312 
313  // State, input check
314  if (NULL == p_msg)
315  {
316  err_code |= RD_ERROR_NULL;
317  }
318  else if (!rt_gatt_nus_is_connected())
319  {
320  err_code |= RD_ERROR_INVALID_STATE;
321  }
322  else
323  {
324  // Try to put data to SD
325  err_code |= m_channel.send (p_msg);
326 
327  // If success, return. Else put data to ringbuffer
328  if (RD_SUCCESS == err_code)
329  {
330  LOGD (">>>;");
331  LOGDHEX (p_msg->data, p_msg->data_length);
332  LOGD (";\r\n");
333  }
334  else if (RD_ERROR_RESOURCES == err_code)
335  {
336  err_code = RD_ERROR_NO_MEM;
337  }
338  // If the error code is something else than buffer full, return error.
339  else
340  {
341  RD_ERROR_CHECK (err_code, ~RD_ERROR_FATAL);
342  }
343  }
344 
345  return err_code;
346 }
347 
349 {
350  m_on_connected = cb;
351 }
352 
353 
355 {
356  m_on_disconnected = cb;
357 }
358 
360 {
361  m_on_received = cb;
362 }
363 
365 {
366  m_on_sent = cb;
367 }
368 
369 bool rt_gatt_is_nus_enabled (void)
370 {
371  return m_nus_is_init;
372 }
373 
374 #else
376  * const p_msg)
377 {
378  return RD_ERROR_NOT_ENABLED;
379 }
380 
382 {
383  return RD_ERROR_NOT_ENABLED;
384 }
385 
387 {
388  return RD_ERROR_NOT_ENABLED;
389 }
390 
392 {
393  return RD_ERROR_NOT_ENABLED;
394 }
395 
396 rd_status_t rt_gatt_init (const char * const name)
397 {
398  return RD_ERROR_NOT_ENABLED;
399 }
400 
402 {
403  return RD_ERROR_NOT_ENABLED;
404 }
405 
407 {
408  return RD_ERROR_NOT_ENABLED;
409 }
410 
412 {
413  return false;
414 }
415 
417 {
418  // No implementation needed
419 }
420 
421 
423 {
424  // No implementation needed
425 }
426 
428 {
429  // No implementation needed
430 }
431 
433 {
434  // No implementation needed
435 }
436 
438 {
439  return false;
440 }
441 
442 #endif
#define RD_ERROR_NULL
Null Pointer.
#define RD_ERROR_FATAL
Program should always reset after this.
uint32_t rd_status_t
bitfield for representing errors
#define RD_ERROR_INVALID_LENGTH
Invalid Length.
#define RD_ERROR_CHECK(error, mask)
Shorthand macro for calling the rd_error_check with current file & line.
#define RD_ERROR_NOT_ENABLED
Driver is not enabled.
#define RD_ERROR_RESOURCES
Not enough resources for operation.
#define RD_SUCCESS
Internal Error.
#define RD_ERROR_NO_MEM
No Memory for operation.
#define RD_ERROR_INVALID_STATE
Invalid state, operation disallowed in this state.
void ri_log_hex(const ri_log_severity_t severity, const uint8_t *const bytes, size_t byte_length)
Queues bytes to be logged out as a hex string.
void ri_log(const ri_log_severity_t severity, const char *const message)
Queues messages into log.
@ RI_LOG_LEVEL_DEBUG
rd_status_t ri_radio_get_modulation(ri_radio_modulation_t *const p_modulation)
Get the modulation used by application.
rd_status_t ri_radio_init(const ri_radio_modulation_t modulation)
Enable radio stack for an user. This function also starts radio activity callbacks internally.
rd_status_t ri_radio_uninit()
Release radio stack.
ri_radio_modulation_t
type of radio modulation to be used.
@ RI_RADIO_BLE_1MBPS
"Normal" BLE 4 modulation
rd_status_t rt_adv_connectability_set(const bool enable, const char *const device_name)
Start advertising BLE GATT connection.
bool rt_adv_is_init(void)
check if advertisement is initialized
#define SCAN_RSP_NAME_MAX_LEN
Longer name gets truncated when advertised with UUID.
Header to enable and disable module compilation.
Ruuvi error codes and error check function.
void(* ri_comm_cb_t)(void *p_data, size_t data_len)
ri_comm_evt_t
Communication event type.
@ RI_COMM_CONNECTED
Connection established, OK to send, may receive data.
@ RI_COMM_RECEIVED
New data received, available to read with read function.
@ RI_COMM_SENT
One queued message was sent with all repetitions.
@ RI_COMM_DISCONNECTED
Connection lost, cannot send, may not receive data.
rd_status_t ri_gatt_init(void)
Initializes GATT stack. Uses default values from sdk_config.h, these can be overridden in nrf5_sdk15_...
rd_status_t ri_gatt_dfu_init(void)
Initialize BLE4 Device firmware update service.
rd_status_t ri_gatt_uninit(void)
Uninitializes GATT stack.
rd_status_t ri_gatt_nus_init(ri_comm_channel_t *const channel)
Initialize Nordic UART Service as a communication channel. ri_communication_radio_init(RI_COMMUNICATI...
rd_status_t ri_gatt_dis_init(const ri_comm_dis_init_t *const dis)
Initialize BLE4 Device Information service.
#define LOGD(fmt,...)
Interface functions to scheduler.
Helper functions for communication.
rd_status_t rt_gatt_dfu_init(void)
Initialize Device Firmware Update service.
bool rt_gatt_is_nus_enabled(void)
Check if Nordic UART Service is enabled.
void rt_gatt_set_on_disconn_isr(const ri_comm_cb_t cb)
Setup disconnection event handler.
rd_status_t rt_gatt_adv_enable()
Start advertising GATT connection to devices.
rd_status_t rt_gatt_dis_init(const ri_comm_dis_init_t *const dis)
Initialize Device Information Update service.
rd_status_t rt_gatt_adv_disable()
Stop advertising GATT connection to devices.
rd_status_t rt_gatt_init(const char *const name)
Initialize GATT. Must be called as a first function in rt_gatt.
void rt_gatt_set_on_sent_isr(const ri_comm_cb_t cb)
Setup data sent event handler.
bool rt_gatt_is_init()
check if GATT task is initialized
void rt_gatt_set_on_connected_isr(const ri_comm_cb_t cb)
Setup connection event handler.
rd_status_t rt_gatt_nus_init()
Initialize Nordic UART Service.
rd_status_t rt_gatt_send_asynchronous(ri_comm_message_t *const p_msg)
Send given message via NUS.
void rt_gatt_set_on_received_isr(const ri_comm_cb_t cb)
Setup data received event handler.
bool rt_gatt_nus_is_connected()
check if NUS is connected, i.e. central has registered to TX notifications.
rd_status_t rt_gatt_uninit(void)
Uninitialize GATT.
control API for communication via outside world
ri_comm_evt_handler_fp_t on_evt
Callback to application-level event handler, must be set in application.
ri_comm_xfer_fp_t send
Asynchronous send function.
Application message structure used for communication.
uint8_t data[RI_COMM_MESSAGE_MAX_LENGTH]
Data payload.
uint8_t data_length
Length of data.