ruuvi.drivers.c  ${PROJECT_VERSION}
Drivers for external sensors and peripherals on embedded systems.
ruuvi_interface_communication_ble_gatt_test.c
Go to the documentation of this file.
2 #if RUUVI_RUN_TESTS
3 #include "ruuvi_driver_error.h"
4 #include "ruuvi_driver_test.h"
10 #include "ruuvi_interface_flash.h"
11 #include "ruuvi_interface_rtc.h"
12 #include "ruuvi_interface_timer.h"
13 #include "ruuvi_interface_yield.h"
14 #include <stdbool.h>
15 #include <string.h>
16 #include <stdio.h>
17 
30 #define GATT_TX_TEST_TIMEOUT_MS (10000U)
31 
32 static ri_comm_channel_t m_channel;
33 static ri_comm_channel_t m_adv;
34 static volatile bool m_has_connected;
35 static volatile bool m_has_disconnected;
36 static volatile bool m_has_sent;
37 static volatile bool m_has_received;
38 static ri_comm_message_t rx_data;
39 
40 static rd_status_t ble_isr (ri_comm_evt_t evt,
41  void * p_data, size_t data_len)
42 {
43  switch (evt)
44  {
45  // Note: This gets called only after the NFC notifications have been registered.
46  case RI_COMM_CONNECTED:
47  m_has_connected = true;
48  break;
49 
51  m_has_disconnected = true;
52  break;
53 
54  case RI_COMM_SENT:
55  m_has_sent = true;
56  break;
57 
58  case RI_COMM_RECEIVED:
59  m_has_received = true;
61  m_channel.read (&rx_data);
62  break;
63 
64  default:
65  break;
66  }
67 
68  return RD_SUCCESS;
69 }
70 
80 static bool ri_gatt_init_null_test (void)
81 {
82  rd_status_t err_code = RD_SUCCESS;
83  err_code |= ri_gatt_init ();
84  err_code |= ri_gatt_nus_init (NULL);
85  return (RD_ERROR_NULL != err_code);
86 }
87 
88 static bool ri_gatt_init_twice_test (void)
89 {
90  rd_status_t err_code = RD_SUCCESS;
91  err_code |= ri_gatt_init ();
92  err_code |= ri_gatt_init ();
93  return (RD_ERROR_INVALID_STATE != err_code);
94 }
95 
96 static bool ri_gatt_init_norad_test (void)
97 {
98  rd_status_t err_code = RD_SUCCESS;
99  err_code |= ri_gatt_init ();
100  return (RD_ERROR_INVALID_STATE != err_code);
101 }
102 
103 static bool ri_gatt_init_test (const rd_test_print_fp printfp,
104  const ri_radio_modulation_t modulation)
105 {
106  bool status = false;
107  rd_status_t err_code = RD_SUCCESS;
108  printfp ("\"init\":");
109  status |= ri_gatt_init_norad_test();
110  err_code |= ri_timer_init();
111  err_code |= ri_flash_init();
112  err_code |= ri_radio_init (modulation);
113  err_code |= ri_gatt_init ();
114  // nRF SDK requires complete radio re-init.
115  err_code |= ri_radio_uninit ();
116  err_code |= ri_gatt_uninit ();
117  err_code |= ri_radio_init (modulation);
118 
119  if (RD_SUCCESS == err_code)
120  {
121  status |= ri_gatt_init_null_test();
122  status |= ri_gatt_init_twice_test();
123  }
124  else
125  {
126  status |= true;
127  }
128 
129  if (status)
130  {
131  printfp ("\"fail\",\r\n");
132  }
133  else
134  {
135  printfp ("\"pass\",\r\n");
136  }
137 
138  ri_gatt_nus_uninit (&m_channel);
139  ri_radio_uninit();
140  ri_gatt_uninit ();
141  ri_flash_uninit();
142  ri_timer_uninit();
143  return status;
144 }
145 
146 /* Send data slowfly for 10 s to verify power consumption when little data is sent */
147 static bool tx_slow_test (void)
148 {
149  rd_status_t err_code = RD_SUCCESS;
150  const char test_data[] = "Test slow tx: ";
151  ri_comm_message_t msg = {0};
152  uint16_t msg_index = 0U;
153 
154  for (uint8_t ii = 0U; ii < 10U; ii++)
155  {
156  snprintf ( (char *) & msg.data, RI_COMM_MESSAGE_MAX_LENGTH, "%s%d", test_data,
157  msg_index++);
158  msg.data_length = strlen (test_data);
159  msg.repeat_count = 1U;
160  err_code |= m_channel.send (&msg);
161  ri_delay_ms (1000U);
162  }
163 
164  return (RD_SUCCESS != err_code);
165 }
166 
167 /* Check throughput, return throughput- */
168 static float tx_throughput_test (void)
169 {
170  rd_status_t err_code = RD_SUCCESS;
171  const char test_data[] = "msg: "; // 5 chars
172  ri_comm_message_t msg = {0};
173  uint64_t test_start = ri_rtc_millis();
174 
175  for (uint16_t ii = 0U; ii < TEST_GATT_PACKET_NUM;)
176  {
177  err_code = RD_SUCCESS;
178 
179  while (RD_SUCCESS == err_code)
180  {
181  // 5 + 5 + 3 + 5 + 1 + NULL = 20 bytes
182  snprintf ( (char *) & msg.data, RI_COMM_MESSAGE_MAX_LENGTH, "%s%05d / %05d.", test_data,
185  msg.repeat_count = 1U;
186  err_code |= m_channel.send (&msg);
187 
188  if (RD_SUCCESS == err_code)
189  {
190  ii++;
191  }
192  }
193 
194  ri_yield();
195  }
196 
197  uint64_t test_end = ri_rtc_millis();
198  return ( (float) (TEST_GATT_PACKET_NUM * TEST_GATT_PACKET_LEN)) / ( ( (float) (
199  test_end - test_start)) / 1000.0F);
200 }
201 
202 bool ri_gatt_tx_test (const rd_test_print_fp printfp,
203  const ri_radio_modulation_t modulation)
204 {
205  bool status = false;
206  m_has_connected = false;
207  uint64_t start_time = 0;
208  bool timeout = false;
209  rd_status_t err_code = RD_SUCCESS;
210  float throughput = 0;
211  char throughput_string[100];
212  printfp ("\"tx\":");
213  // RTC + timer required for low-power yield.
214  err_code |= ri_rtc_init();
215  err_code |= ri_timer_init();
216  err_code |= ri_yield_low_power_enable (true);
217  err_code |= ri_flash_init();
218  err_code |= ri_radio_init (modulation);
219  // Advertise NUS to tester.
220  err_code |= ri_adv_init (&m_adv);
221  err_code |= ri_adv_scan_response_setup ("RuuviTest", true);
223  err_code |= ri_adv_tx_interval_set (100U);
224  err_code |= ri_gatt_init ();
225  err_code |= ri_gatt_nus_init (&m_channel);
226  const char test_data[] = "Lorem Ipsum";
227  ri_comm_message_t msg = {0};
228  snprintf ( (char *) & msg.data, RI_COMM_MESSAGE_MAX_LENGTH, "%s", test_data);
229  msg.data_length = strlen (test_data);
230  msg.repeat_count = 200U; // 200 * 100ms => 20 s to connect.
231  err_code |= m_adv.send (&msg);
232 
233  if (RD_SUCCESS == err_code)
234  {
235  m_channel.on_evt = ble_isr;
236  start_time = ri_rtc_millis();
237 
238  while (! (m_has_connected))
239  {
240  if ( (start_time + GATT_TX_TEST_TIMEOUT_MS) < ri_rtc_millis())
241  {
242  timeout = true;
243  break;
244  }
245  }
246 
247  if (false == timeout)
248  {
249  status |= tx_slow_test ();
250  throughput = tx_throughput_test();
251 
252  if (0 == throughput)
253  {
254  status = true;
255  }
256  }
257  }
258  else
259  {
260  status = true;
261  }
262 
263  if (timeout)
264  {
265  printfp ("\"timeout\",\r\n");
266  }
267  else
268  {
269  if (status)
270  {
271  printfp ("\"fail\",\r\n");
272  }
273  else
274  {
275  printfp ("\"pass\",\r\n");
276  }
277  }
278 
279  snprintf (throughput_string, sizeof (throughput_string),
280  "\"throughput\":\"%.3f bps\"\r\n",
281  throughput);
282  printfp (throughput_string);
283  ri_gatt_nus_uninit (&m_channel);
284  ri_adv_uninit (&m_adv);
285  ri_delay_ms (2000);
286  ri_radio_uninit();
287  ri_gatt_uninit (); // GATT can only be uninit after radio.
288  err_code |= ri_flash_uninit();
290  ri_timer_uninit();
291  ri_rtc_uninit();
292  return status;
293 }
294 
296  const ri_radio_modulation_t modulation)
297 {
298  rd_status_t status = false;
299  printfp ("\"ble_gatt_");
300  print_modulation (printfp, modulation);
301  printfp ("\":{\r\n");
302  status |= ri_gatt_init_test (printfp, modulation);
303  status |= ri_gatt_tx_test (printfp, modulation);
304  printfp ("},\r\n");
305  return status;
306 }
307 #endif
#define TEST_GATT_PACKET_NUM
Number of packets to send in throughput test.
bool ri_communication_ble_gatt_run_integration_test(const rd_test_print_fp printfp, const ri_radio_modulation_t modulation)
#define TEST_GATT_PACKET_LEN
Bytes of GATT test packet.
#define RD_ERROR_NULL
Null Pointer.
uint32_t rd_status_t
bitfield for representing errors
#define RD_SUCCESS
Internal Error.
#define RD_ERROR_INVALID_STATE
Invalid state, operation disallowed in this state.
rd_status_t ri_flash_uninit(void)
Unintialize flash. After uninitialization only initialization can be used.
rd_status_t ri_flash_init(void)
void print_modulation(const rd_test_print_fp printfp, const ri_radio_modulation_t modulation)
Print used modulation to printfp.
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.
void(* rd_test_print_fp)(const char *const msg)
function pointer to print test information
rd_status_t ri_timer_uninit(void)
rd_status_t ri_timer_init(void)
Header to enable and disable module compilation.
Ruuvi error codes and error check function.
Functions for testing drivers.
#define RI_COMM_MESSAGE_MAX_LENGTH
The maximum length for the application message for sending over BLE, which depends on whether extende...
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.
@ CONNECTABLE_SCANNABLE
Connectable, scannable.
rd_status_t ri_adv_init(ri_comm_channel_t *const channel)
Initialize Advertising module and scanning module.
rd_status_t ri_adv_scan_response_setup(const char *const name, const bool advertise_nus)
Configure advertising data with a scan response.
rd_status_t ri_adv_uninit(ri_comm_channel_t *const channel)
rd_status_t ri_adv_type_set(ri_adv_type_t type)
Configure the type of advertisement.
rd_status_t ri_adv_tx_interval_set(const uint32_t ms)
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_nus_uninit(ri_comm_channel_t *const _channel)
Uninitialize Nordic UART Service as a communication channel.
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...
Interface functions to persistent flash storage.
rd_status_t ri_rtc_init(void)
Initializes RTC at 0 ms.
rd_status_t ri_rtc_uninit(void)
Stop RTC if applicable.
uint64_t ri_rtc_millis(void)
Get milliseconds since init.
Interface functions to timer.
rd_status_t ri_yield(void)
Function which will release execution.
rd_status_t ri_delay_ms(uint32_t time)
Delay a given number of milliseconds.
rd_status_t ri_yield_low_power_enable(const bool enable)
Initializes yielding functions.
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.
ri_comm_xfer_fp_t read
Asynchronous read function.
Application message structure used for communication.
uint8_t data[RI_COMM_MESSAGE_MAX_LENGTH]
Data payload.
uint8_t repeat_count
Number of times to repeat the message,.
uint8_t data_length
Length of data.