Skip to content

Commit 8dfff1e

Browse files
committed
samples: data_forwarder: sample sensors only when connected
As a way to optimize current consumption, this commit introduces gated sampling. It means that the device performs sensor sampling only when it can send sensor data. For the currently supported transports it means: - BLE NUS: sample when Bluetooth connection is active, - serial: sample always Ref. NCSDK-40322 Signed-off-by: Michał Grochala <michal.grochala@nordicsemi.no>
1 parent e4bdb99 commit 8dfff1e

10 files changed

Lines changed: 255 additions & 40 deletions

File tree

samples/data_forwarder/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ config DATA_FWD_EXTRA_SENSOR_BME688
104104
Extend streamed samples with temperature, humidity, and pressure from BME688 at its native
105105
rate by upsampling (repeating the latest reading between fetches).
106106

107+
config DATA_FWD_START_RETRY_MS
108+
int "Sensor/session start retry interval (ms)"
109+
range 10 10000
110+
default 100
111+
help
112+
Delay between retries when sensor or protocol session start fails while
113+
a transport link is up.
114+
107115
config DATA_FWD_PROTO_MAX_CHANNELS
108116
default 9 if DATA_FWD_SENSOR_BMI270 && DATA_FWD_EXTRA_SENSOR_BME688
109117
default 6 if DATA_FWD_SENSOR_BMI270

samples/data_forwarder/README.rst

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,18 @@ The default configuration on the nRF54L15 TAG reads a 6-axis BMI270 IMU and appe
2525
Overview
2626
********
2727

28-
The sample periodically reads data from an on-board sensor and forwards it to a host over :ref:`nrf:nus_service_readme` (default) or UART.
28+
The sample reads data from an on-board sensor and forwards it to a host over :ref:`nrf:nus_service_readme` (default) or UART.
2929
By default, the sample uses CBOR messages wrapped in COBS framing, as defined in :file:`cddl/data_forwarder.cddl`.
30-
Session metadata is sent periodically so a host can join an active stream.
30+
While a streaming session is active, session metadata is sent periodically so a host can join the stream.
3131

3232
The sample performs the following operations:
3333

34-
* Initializes the selected sensor driver and starts sampling at a fixed rate.
35-
* Encodes each sample and sends it through the configured transport.
34+
#. Initializes the sensor driver without starting periodic sampling.
35+
#. Waits until the configured transport is ready to carry data.
36+
#. Starts sensor sampling and a protocol session.
37+
#. Encodes each sample and sends it through the transport.
38+
39+
If the sensor or the session fails to start while the link is up, the sample retries after the period configured with the ``CONFIG_DATA_FWD_START_RETRY_MS`` Kconfig option.
3640

3741
Transport
3842
=========
@@ -42,9 +46,14 @@ Select the transport using the ``DATA_FWD_TRANSPORT`` Kconfig option.
4246

4347
Bluetooth LE NUS
4448
Sample uses :ref:`nrf:nus_service_readme` for sending the protocol frames.
49+
The sensors are sampled only while a central is connected.
50+
Before that, advertising is enabled and the sensors remain stopped.
51+
When the link drops, the sample stops the protocol session and the sensors, then waits for the next connection.
52+
This keeps the sensors off while no host is collecting data.
4553

4654
UART transport
4755
Sample uses UART for sending the protocol frames.
56+
The data link is always treated as connected, so sampling starts after initialization and continues until the device is reset.
4857

4958
.. note::
5059
UART transport is not available on the nRF54L15 TAG device without any external UART-to-USB converter.
@@ -185,8 +194,20 @@ Testing
185194
:class: highlight
186195
187196
transport: BLE NUS transport ready
188-
data_forwarder: Data forwarder started (sid 8779774)
197+
data_forwarder: Data forwarder started
189198
transport: BLE connected
199+
data_forwarder: Sampling session started (sid 8779774)
200+
201+
Sensor data is sent only after ``Sampling session started``.
202+
When the central disconnects, the log continues with:
203+
204+
.. parsed-literal::
205+
:class: highlight
206+
207+
transport: BLE disconnected
208+
data_forwarder: Connection terminated
209+
210+
The device then advertises again and waits for the next connection before sampling.
190211

191212
Collecting data on the host
192213
===========================

samples/data_forwarder/src/main.c

Lines changed: 87 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
55
*/
66

7+
#include <zephyr/kernel.h>
78
#include <zephyr/logging/log.h>
89

910
#include "protocol/protocol.h"
@@ -12,6 +13,87 @@
1213

1314
LOG_MODULE_REGISTER(data_forwarder);
1415

16+
/**
17+
* @brief Stop sensor sampling and log a warning if stop fails.
18+
*/
19+
static void sensor_stop(void)
20+
{
21+
int err = data_fwd_sensor_stop();
22+
23+
if (err) {
24+
LOG_WRN("Sensor stop failed (err %d)", err);
25+
}
26+
}
27+
28+
/**
29+
* @brief Start sensors and a protocol session, retrying on failure.
30+
*
31+
* Retries while the transport link remains up. Returns when both starts
32+
* succeed or the link drops.
33+
*
34+
* @param session Session configuration passed to @ref proto_start_session().
35+
*/
36+
static void session_start_retry(const struct proto_session_config *session)
37+
{
38+
int err;
39+
40+
while (transport_is_connected()) {
41+
err = data_fwd_sensor_start();
42+
if (err) {
43+
LOG_WRN("Sensor start failed (err %d), retrying in %d ms", err,
44+
CONFIG_DATA_FWD_START_RETRY_MS);
45+
k_sleep(K_MSEC(CONFIG_DATA_FWD_START_RETRY_MS));
46+
continue;
47+
}
48+
49+
err = proto_start_session(session);
50+
if (err) {
51+
LOG_WRN("Failed to start session (err %d), retrying in %d ms", err,
52+
CONFIG_DATA_FWD_START_RETRY_MS);
53+
sensor_stop();
54+
k_sleep(K_MSEC(CONFIG_DATA_FWD_START_RETRY_MS));
55+
continue;
56+
}
57+
58+
LOG_INF("Sampling session started (sid %u)", proto_get_session_id());
59+
break;
60+
}
61+
}
62+
63+
/**
64+
* @brief Fetch sensor samples and send them until the transport disconnects.
65+
*/
66+
static void stream_samples(void)
67+
{
68+
int err;
69+
70+
while (transport_is_connected()) {
71+
proto_value_t values[CONFIG_DATA_FWD_PROTO_MAX_CHANNELS];
72+
size_t count;
73+
74+
err = data_fwd_sensor_fetch(values, ARRAY_SIZE(values), &count);
75+
if (err) {
76+
LOG_WRN("Sample fetch failed (err %d)", err);
77+
continue;
78+
}
79+
80+
err = proto_send_samples(values, count);
81+
if (err) {
82+
LOG_WRN("Sample send failed (err %d)", err);
83+
}
84+
}
85+
}
86+
87+
/**
88+
* @brief Stop the protocol session and sensor sampling after a connection ends.
89+
*/
90+
static void session_stop(void)
91+
{
92+
proto_stop_session();
93+
sensor_stop();
94+
LOG_INF("Connection terminated");
95+
}
96+
1597
int main(void)
1698
{
1799
int err;
@@ -47,28 +129,13 @@ int main(void)
47129
return err;
48130
}
49131

50-
err = proto_start_session(&session);
51-
if (err) {
52-
LOG_ERR("Failed to start session (err %d)", err);
53-
return err;
54-
}
55-
56-
LOG_INF("Data forwarder started (sid %u)", proto_get_session_id());
132+
LOG_INF("Data forwarder started");
57133

58134
while (1) {
59-
proto_value_t values[CONFIG_DATA_FWD_PROTO_MAX_CHANNELS];
60-
size_t count;
61-
62-
err = data_fwd_sensor_fetch(values, ARRAY_SIZE(values), &count);
63-
if (err) {
64-
LOG_WRN("Sample fetch failed (err %d)", err);
65-
continue;
66-
}
67-
68-
err = proto_send_samples(values, count);
69-
if (err) {
70-
LOG_WRN("Sample send failed (err %d)", err);
71-
}
135+
transport_wait_connected();
136+
session_start_retry(&session);
137+
stream_samples();
138+
session_stop();
72139
}
73140

74141
return 0;

samples/data_forwarder/src/sensor/adxl367.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ int data_fwd_sensor_init(void)
5959
return 0;
6060
}
6161

62+
int data_fwd_sensor_start(void)
63+
{
64+
return 0;
65+
}
66+
67+
int data_fwd_sensor_stop(void)
68+
{
69+
return 0;
70+
}
71+
6272
int data_fwd_sensor_fetch(proto_value_t *values, const size_t values_size, size_t *count)
6373
{
6474
int err;

samples/data_forwarder/src/sensor/bme688.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,26 @@ int data_fwd_sensor_init(void)
3535
return -ENODEV;
3636
}
3737

38+
return 0;
39+
}
40+
41+
int data_fwd_sensor_start(void)
42+
{
3843
const uint32_t period_ns = Z_HZ_ns / FREQUENCY_HZ;
3944

4045
k_timer_start(&fetch_timer, K_NO_WAIT, K_NSEC(period_ns));
4146

4247
return 0;
4348
}
4449

50+
int data_fwd_sensor_stop(void)
51+
{
52+
k_timer_stop(&fetch_timer);
53+
k_sem_reset(&fetch_sem);
54+
55+
return 0;
56+
}
57+
4558
int data_fwd_sensor_fetch(proto_value_t *values, const size_t values_size, size_t *count)
4659
{
4760
int err;

samples/data_forwarder/src/sensor/bmi270.c

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static bool bmi_set_attr(enum sensor_channel chan, enum sensor_attribute attr,
6969

7070
int data_fwd_sensor_init(void)
7171
{
72-
struct sensor_value full_scale, sampling_freq, oversampling;
72+
struct sensor_value full_scale, oversampling;
7373

7474
if (!device_is_ready(dev_bmi)) {
7575
return -ENODEV;
@@ -84,46 +84,72 @@ int data_fwd_sensor_init(void)
8484
/* Setting scale in G to match the sensor scale */
8585
full_scale.val1 = 2; /* G */
8686
full_scale.val2 = 0;
87-
sampling_freq.val1 = FREQUENCY_HZ; /* Hz. Performance mode */
88-
sampling_freq.val2 = 0;
8987
oversampling.val1 = 1; /* Normal mode */
9088
oversampling.val2 = 0;
9189

9290
bool ok = true;
9391

9492
ok &= bmi_set_attr(SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_FULL_SCALE, &full_scale);
9593
ok &= bmi_set_attr(SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_OVERSAMPLING, &oversampling);
96-
/* Set sampling frequency last as this also sets the appropriate power mode. If already
97-
* sampling, change to 0.0Hz before changing other attributes
98-
*/
99-
ok &= bmi_set_attr(SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &sampling_freq);
10094

10195
/* Setting scale in degrees/s to match the sensor scale */
10296
full_scale.val1 = 500; /* dps */
10397
full_scale.val2 = 0;
104-
sampling_freq.val1 = FREQUENCY_HZ; /* Hz. Performance mode */
105-
sampling_freq.val2 = 0;
106-
oversampling.val1 = 1; /* Normal mode */
107-
oversampling.val2 = 0;
10898

10999
ok &= bmi_set_attr(SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_FULL_SCALE, &full_scale);
110100
ok &= bmi_set_attr(SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_OVERSAMPLING, &oversampling);
111-
/* Set sampling frequency last as this also sets the appropriate power mode. If already
112-
* sampling, change sampling frequency to 0.0Hz before changing other attributes
113-
*/
114-
ok &= bmi_set_attr(SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &sampling_freq);
115101

116102
if (!ok) {
117103
return -ENOTSUP;
118104
}
119105

106+
return 0;
107+
}
108+
109+
static int bmi270_set_sampling_frequency(uint16_t frequency_hz)
110+
{
111+
struct sensor_value sampling_freq = {
112+
.val1 = frequency_hz,
113+
.val2 = 0,
114+
};
115+
bool ok = true;
116+
117+
ok &= bmi_set_attr(SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &sampling_freq);
118+
ok &= bmi_set_attr(SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &sampling_freq);
119+
120+
return ok ? 0 : -ENOTSUP;
121+
}
122+
123+
int data_fwd_sensor_start(void)
124+
{
125+
int err;
120126
const uint32_t period_ns = Z_HZ_ns / FREQUENCY_HZ;
121127

128+
err = bmi270_set_sampling_frequency(FREQUENCY_HZ);
129+
if (err) {
130+
return err;
131+
}
132+
122133
k_timer_start(&fetch_timer, K_NO_WAIT, K_NSEC(period_ns));
123134

124135
return 0;
125136
}
126137

138+
int data_fwd_sensor_stop(void)
139+
{
140+
int err;
141+
142+
k_timer_stop(&fetch_timer);
143+
k_sem_reset(&fetch_sem);
144+
145+
err = bmi270_set_sampling_frequency(0);
146+
if (err) {
147+
return err;
148+
}
149+
150+
return 0;
151+
}
152+
127153
int data_fwd_sensor_fetch(proto_value_t *values, const size_t values_size, size_t *count)
128154
{
129155
int err;

samples/data_forwarder/src/sensor/data_fwd_sensor.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,38 @@ extern "C" {
3838
*/
3939
int data_fwd_sensor_init(void);
4040

41+
/**
42+
* @brief Start sensor sampling.
43+
*
44+
* Enables periodic sampling in the selected sensor driver.
45+
*
46+
* @retval 0 Success.
47+
* @retval -errno Negative error code on failure.
48+
*/
49+
int data_fwd_sensor_start(void);
50+
51+
/**
52+
* @brief Stop sensor sampling.
53+
*
54+
* Disables periodic sampling in the selected sensor driver. Implementations stop
55+
* the fetch timer and reset the internal sample-ready semaphore with
56+
* @c k_sem_reset(), clearing any pending tick signalled before stop.
57+
*
58+
* @note Do not call this while @ref data_fwd_sensor_fetch() is blocked in another
59+
* context: @c k_sem_reset() aborts outstanding takes with @c -EAGAIN.
60+
*
61+
* @retval 0 Success.
62+
* @retval -errno Negative error code on failure.
63+
*/
64+
int data_fwd_sensor_stop(void);
65+
4166
/**
4267
* @brief Read one sample from the sensor into @p values.
4368
*
69+
* Blocks until the next sample period elapses. Call @ref data_fwd_sensor_start()
70+
* before fetching and @ref data_fwd_sensor_stop() when sampling is no longer
71+
* needed.
72+
*
4473
* @note This function is blocking until sensor values are available.
4574
*
4675
* @param values Output buffer for channel values.

0 commit comments

Comments
 (0)