Sending data from LSM6DSO over bluetooth using nRF52832

Hello,

My goal is to combine the Peripheral UART sample code and the LSM6DSO I2C sample code with the objective of sending data from the sensor over bluetooth. Based on my knowledge of C code, I should be able to copy and paste necessary #includes, functions, and everything that isn't in the main method directly from the LSM6DSO code to the UART code. However, I'm usure how to combine the main methods from each sample code. Currently, the UART code sends data over bluetooth when something is entered in the terminal, but I want to change it so that it sends data over bluetooth every time data is received from the connected LSM6DSO sensor. Some research I've done has pointed to using the k_fifo_put function to put whatever data output from the sensor into the transmission buffer, but I don't know exactly how or where in the code to implement this.

I'm using the current version of VScode, and I've connected the LSM6DSO sensor to p26 and p27 for the sda and scl lines. I've actually had some separate issues with the LSM6DSO code functionality that I'm working through in another thread, but I thought that while that was being sorted I could begin to figure out how to best combine these codes. 

Also, I'm very new to nordic devices and software, so my apologies my questions are ignorant. Please let me know if there's any more information I can share, and thank you for the help!

/*
 * Copyright (c) 2018 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

/** @file
 *  @brief Nordic UART Bridge Service (NUS) sample
 */

//anything that starts with include means this code can now access functions and values from the other file
#include <uart_async_adapter.h>

#include <zephyr/types.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/usb/usb_device.h>

#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <soc.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/hci.h>

#include <bluetooth/services/nus.h>

#include <dk_buttons_and_leds.h>

#include <zephyr/settings/settings.h>

#include <stdio.h>
#include <string.h>

#include <zephyr/logging/log.h>

//define is formatted as #define NAME value
// where you can make up a name and assign it a value
// and anytime you use that name in the code it gives it the assigned value
#define LOG_MODULE_NAME peripheral_uart
LOG_MODULE_REGISTER(LOG_MODULE_NAME);

#define STACKSIZE CONFIG_BT_NUS_THREAD_STACK_SIZE
#define PRIORITY 7

#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN	(sizeof(DEVICE_NAME) - 1)

#define RUN_STATUS_LED DK_LED1
#define RUN_LED_BLINK_INTERVAL 1000

#define CON_STATUS_LED DK_LED2

#define KEY_PASSKEY_ACCEPT DK_BTN1_MSK
#define KEY_PASSKEY_REJECT DK_BTN2_MSK

#define UART_BUF_SIZE CONFIG_BT_NUS_UART_BUFFER_SIZE
#define UART_WAIT_FOR_BUF_DELAY K_MSEC(50)
#define UART_WAIT_FOR_RX CONFIG_BT_NUS_UART_RX_WAIT_TIME

//these are all values that can be accessed in this code
static K_SEM_DEFINE(ble_init_ok, 0, 1);

//structures contain data
//these lines below are structure definitions, and the data will
// be filled in later
static struct bt_conn *current_conn;
static struct bt_conn *auth_conn;

static const struct device *uart = DEVICE_DT_GET(DT_CHOSEN(nordic_nus_uart));
static struct k_work_delayable uart_work;

//the structure uartdatat has three other data values stored inside
// including two integers data (which is the size of the buffer) and length
struct uart_data_t {
	void *fifo_reserved;
	uint8_t data[UART_BUF_SIZE];
	uint16_t len;
};

//i believe these functions are from the included files at the top
static K_FIFO_DEFINE(fifo_uart_tx_data);
static K_FIFO_DEFINE(fifo_uart_rx_data);

static const struct bt_data ad[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};

static const struct bt_data sd[] = {
	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
};

//the ifdef line means 'if defined', where if this variable exists,
// it runs the line below, otherwise, it doesn't
#ifdef CONFIG_UART_ASYNC_ADAPTER 
UART_ASYNC_ADAPTER_INST_DEFINE(async_adapter);
#else
#define async_adapter NULL
#endif

//here is the first larger function. This function is a static void,
//which means it doesn't return any values when called, it just does some stuff
//inside the function. This function takes three arguments: device, event, and data

//NOTE THAT ALL FUNCTIONS NOT IN MAIN DO NOT RUN AUTOMATICALLY WHEN THE CODE IS RUN.
// these functions will be called somewhere in the main method or called by another function
static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
	ARG_UNUSED(dev);

	static size_t aborted_len;
	struct uart_data_t *buf;
	static uint8_t *aborted_buf;
	static bool disable_req;
	//note that any time an arrow (->) is used, for example stuct->data,
	//the code goes to the struct structure and grabs what ever is stored
	// data, one of the values defined when the structure was created (or updated)

	//switch statements are fancy forms of if else statements,
	// where in this case the statements looks at the event type
	//If the event type is 'uarttxdone', it runs the code there, if the 
	// event is one of the other defined cases, it runs the code in that case
	// section. 

	//periods (.) are another way to get data from somewhere. Data.tx works 
	//similarly to the arrow, where it goes to data and then gets the tx
	//value from that location
	switch (evt->type) {
	case UART_TX_DONE:
		LOG_DBG("UART_TX_DONE"); //prints to log console? I think
		if ((evt->data.tx.len == 0) || //this checked if the transmit buffer length is zero
		    (!evt->data.tx.buf)) { // the || above is an OR statement. if either is true, the function returns (exits)
			return;
		}

		if (aborted_buf) { //If I dont comment a line, it means I don't immediately know what it does and don't think its important enough to spend time on immediately
			buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
					   data[0]);
			aborted_buf = NULL;
			aborted_len = 0;
		} else {
			buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t,
					   data[0]);
		}

		k_free(buf); //free functions are housekeeping needed in c code to clear up memory locations when they aren't being used anymore

		buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT); 
		if (!buf) {
			return;
		}

		if (uart_tx(uart, buf->data, buf->len, SYS_FOREVER_MS)) {
			LOG_WRN("Failed to send data over UART");
		}

		break;

	case UART_RX_RDY: //if the uart is ready to receive data
		LOG_DBG("UART_RX_RDY");
		buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data[0]); //i dont know what the container_of function does, need to look into that possibly
		buf->len += evt->data.rx.len; //+= adds something to a variable, so this adds the length of the rx buffer to the buf variable used here

		if (disable_req) {
			return;
		}

		if ((evt->data.rx.buf[buf->len - 1] == '\n') || //this or statement seems to check if the last character in a buffer is a newline character
		    (evt->data.rx.buf[buf->len - 1] == '\r')) {
			disable_req = true;
			uart_rx_disable(uart);
		}

		break; //break breaks you out of whatever loop you're in. In this case, I think it stops this case but doesnt exit the whole function yet (unlike return)

	case UART_RX_DISABLED: //if the uart receiving is disabled
		LOG_DBG("UART_RX_DISABLED");
		disable_req = false;

		buf = k_malloc(sizeof(*buf)); //malloc is short for memory allocation, more housekeeping to create a place to store the variable buf
		if (buf) {
			buf->len = 0;
		} else {
			LOG_WRN("Not able to allocate UART receive buffer");
			k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
			return;
		}

		uart_rx_enable(uart, buf->data, sizeof(buf->data), //this function seems to be enabled the receiving if it was already disabled
			       UART_WAIT_FOR_RX);

		break;

	case UART_RX_BUF_REQUEST: //this seems to allocate memory to make a buffer for receiving data
		LOG_DBG("UART_RX_BUF_REQUEST");
		buf = k_malloc(sizeof(*buf));
		if (buf) {
			buf->len = 0;
			uart_rx_buf_rsp(uart, buf->data, sizeof(buf->data));
		} else {
			LOG_WRN("Not able to allocate UART receive buffer");
		}

		break;

	case UART_RX_BUF_RELEASED: //not entirely sure about this one, but i think it puts whatever data was in the bugger into a new location (fifouartrxdata) and then frees the buffer
		LOG_DBG("UART_RX_BUF_RELEASED");
		buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t,
				   data[0]);

		if (buf->len > 0) {
			k_fifo_put(&fifo_uart_rx_data, buf);
		} else {
			k_free(buf);
		}

		break;

	case UART_TX_ABORTED: //unclear as to what this does, may just be there to catch errors 
		LOG_DBG("UART_TX_ABORTED");
		if (!aborted_buf) {
			aborted_buf = (uint8_t *)evt->data.tx.buf;
		}

		aborted_len += evt->data.tx.len;
		buf = CONTAINER_OF((void *)aborted_buf, struct uart_data_t,
				   data);

		uart_tx(uart, &buf->data[aborted_len],
			buf->len - aborted_len, SYS_FOREVER_MS);

		break;

	default: //default means that if no cases are met above, it just does this, which in this case is exiting the whole case switch statement
		break;
	}
}

static void uart_work_handler(struct k_work *item) //this takes an input of a structure
{
	struct uart_data_t *buf;

	buf = k_malloc(sizeof(*buf)); //creates a data buffer
	if (buf) {
		buf->len = 0;
	} else {
		LOG_WRN("Not able to allocate UART receive buffer");
		k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
		return;
	}

	uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_WAIT_FOR_RX); //seems to enable the receiving data 
}

static bool uart_test_async_api(const struct device *dev) //tests if api is up to date maybe?
{
	const struct uart_driver_api *api =
			(const struct uart_driver_api *)dev->api;

	return (api->callback_set != NULL);
}

//as this is just an initialize function, I don't think we will need to change anything within this function
static int uart_init(void) //init functions typpically initialize something. In this case, it looks like this function sets up the uart and is likely called early on in the main function
{
	int err;
	int pos;
	struct uart_data_t *rx;
	struct uart_data_t *tx;

	if (!device_is_ready(uart)) {
		return -ENODEV;
	}

	if (IS_ENABLED(CONFIG_USB_DEVICE_STACK)) {
		err = usb_enable(NULL);
		if (err && (err != -EALREADY)) {
			LOG_ERR("Failed to enable USB");
			return err;
		}
	}

	rx = k_malloc(sizeof(*rx)); //creates the rx buffer
	if (rx) {
		rx->len = 0;
	} else {
		return -ENOMEM;
	}

	k_work_init_delayable(&uart_work, uart_work_handler);


	if (IS_ENABLED(CONFIG_UART_ASYNC_ADAPTER) && !uart_test_async_api(uart)) {
		/* Implement API adapter */
		uart_async_adapter_init(async_adapter, uart);
		uart = async_adapter;
	}

	err = uart_callback_set(uart, uart_cb, NULL);
	if (err) {
		k_free(rx);
		LOG_ERR("Cannot initialize UART callback");
		return err;
	}

	if (IS_ENABLED(CONFIG_UART_LINE_CTRL)) {
		LOG_INF("Wait for DTR");
		while (true) {
			uint32_t dtr = 0;

			uart_line_ctrl_get(uart, UART_LINE_CTRL_DTR, &dtr);
			if (dtr) {
				break;
			}
			/* Give CPU resources to low priority threads. */
			k_sleep(K_MSEC(100));
		}
		LOG_INF("DTR set");
		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DCD, 1);
		if (err) {
			LOG_WRN("Failed to set DCD, ret code %d", err);
		}
		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DSR, 1);
		if (err) {
			LOG_WRN("Failed to set DSR, ret code %d", err);
		}
	}

	tx = k_malloc(sizeof(*tx));

	if (tx) {
		pos = snprintf(tx->data, sizeof(tx->data),
			       "Starting Nordic UART service example\r\n");

		if ((pos < 0) || (pos >= sizeof(tx->data))) {
			k_free(rx);
			k_free(tx);
			LOG_ERR("snprintf returned %d", pos);
			return -ENOMEM;
		}

		tx->len = pos;
	} else {
		k_free(rx);
		return -ENOMEM;
	}

	err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS); //this is the line that sends data over uart
	if (err) {
		k_free(rx);
		k_free(tx);
		LOG_ERR("Cannot display welcome message (err: %d)", err);
		return err;
	}

	err = uart_rx_enable(uart, rx->data, sizeof(rx->data), UART_WAIT_FOR_RX);
	if (err) {
		LOG_ERR("Cannot enable uart reception (err: %d)", err);
		/* Free the rx buffer only because the tx buffer will be handled in the callback */
		k_free(rx);
	}

	return err;
}

static void connected(struct bt_conn *conn, uint8_t err) //appears to check the connection status and if connected start flashing LED 1 indicate that
{
	char addr[BT_ADDR_LE_STR_LEN];

	if (err) {
		LOG_ERR("Connection failed (err %u)", err);
		return;
	}

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
	LOG_INF("Connected %s", addr);

	current_conn = bt_conn_ref(conn);

	dk_set_led_on(CON_STATUS_LED);
}

static void disconnected(struct bt_conn *conn, uint8_t reason) //turns the led off if not connected and gives reason for disconnection
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	LOG_INF("Disconnected: %s (reason %u)", addr, reason);

	if (auth_conn) {
		bt_conn_unref(auth_conn);
		auth_conn = NULL;
	}

	if (current_conn) {
		bt_conn_unref(current_conn);
		current_conn = NULL;
		dk_set_led_off(CON_STATUS_LED);
	}
}

#ifdef CONFIG_BT_NUS_SECURITY_ENABLED
static void security_changed(struct bt_conn *conn, bt_security_t level,
			     enum bt_security_err err)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	if (!err) {
		LOG_INF("Security changed: %s level %u", addr, level);
	} else {
		LOG_WRN("Security failed: %s level %u err %d", addr,
			level, err);
	}
}
#endif

BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected    = connected,
	.disconnected = disconnected,
#ifdef CONFIG_BT_NUS_SECURITY_ENABLED
	.security_changed = security_changed,
#endif
};

#if defined(CONFIG_BT_NUS_SECURITY_ENABLED)
static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	LOG_INF("Passkey for %s: %06u", addr, passkey); 
}

static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey)
{
	char addr[BT_ADDR_LE_STR_LEN];

	auth_conn = bt_conn_ref(conn);

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	LOG_INF("Passkey for %s: %06u", addr, passkey);
	LOG_INF("Press Button 1 to confirm, Button 2 to reject.");
}


static void auth_cancel(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	LOG_INF("Pairing cancelled: %s", addr);
}


static void pairing_complete(struct bt_conn *conn, bool bonded)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	LOG_INF("Pairing completed: %s, bonded: %d", addr, bonded);
}


static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	LOG_INF("Pairing failed conn: %s, reason %d", addr, reason);
}


static struct bt_conn_auth_cb conn_auth_callbacks = {
	.passkey_display = auth_passkey_display,
	.passkey_confirm = auth_passkey_confirm,
	.cancel = auth_cancel,
};

static struct bt_conn_auth_info_cb conn_auth_info_callbacks = {
	.pairing_complete = pairing_complete,
	.pairing_failed = pairing_failed
};
#else
static struct bt_conn_auth_cb conn_auth_callbacks;
static struct bt_conn_auth_info_cb conn_auth_info_callbacks;
#endif

static void bt_receive_cb(struct bt_conn *conn, const uint8_t *const data,
			  uint16_t len)
{
	int err;
	char addr[BT_ADDR_LE_STR_LEN] = {0};

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, ARRAY_SIZE(addr));

	LOG_INF("Received data from: %s", addr);

	for (uint16_t pos = 0; pos != len;) {
		struct uart_data_t *tx = k_malloc(sizeof(*tx));

		if (!tx) {
			LOG_WRN("Not able to allocate UART send data buffer");
			return;
		}

		/* Keep the last byte of TX buffer for potential LF char. */
		size_t tx_data_size = sizeof(tx->data) - 1;

		if ((len - pos) > tx_data_size) {
			tx->len = tx_data_size;
		} else {
			tx->len = (len - pos);
		}

		memcpy(tx->data, &data[pos], tx->len);

		pos += tx->len;

		/* Append the LF character when the CR character triggered
		 * transmission from the peer.
		 */
		if ((pos == len) && (data[len - 1] == '\r')) {
			tx->data[tx->len] = '\n';
			tx->len++;
		}

		err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
		if (err) {
			k_fifo_put(&fifo_uart_tx_data, tx);
		}
	}
}

static struct bt_nus_cb nus_cb = {
	.received = bt_receive_cb,
};

void error(void)
{
	dk_set_leds_state(DK_ALL_LEDS_MSK, DK_NO_LEDS_MSK);

	while (true) {
		/* Spin for ever */
		k_sleep(K_MSEC(1000));
	}
}

#ifdef CONFIG_BT_NUS_SECURITY_ENABLED
static void num_comp_reply(bool accept)
{
	if (accept) {
		bt_conn_auth_passkey_confirm(auth_conn);
		LOG_INF("Numeric Match, conn %p", (void *)auth_conn);
	} else {
		bt_conn_auth_cancel(auth_conn);
		LOG_INF("Numeric Reject, conn %p", (void *)auth_conn);
	}

	bt_conn_unref(auth_conn);
	auth_conn = NULL;
}

void button_changed(uint32_t button_state, uint32_t has_changed)
{
	uint32_t buttons = button_state & has_changed;

	if (auth_conn) {
		if (buttons & KEY_PASSKEY_ACCEPT) {
			num_comp_reply(true);
		}

		if (buttons & KEY_PASSKEY_REJECT) {
			num_comp_reply(false);
		}
	}
}
#endif /* CONFIG_BT_NUS_SECURITY_ENABLED */

static void configure_gpio(void)
{
	int err;

#ifdef CONFIG_BT_NUS_SECURITY_ENABLED
	err = dk_buttons_init(button_changed);
	if (err) {
		LOG_ERR("Cannot init buttons (err: %d)", err);
	}
#endif /* CONFIG_BT_NUS_SECURITY_ENABLED */

	err = dk_leds_init();
	if (err) {
		LOG_ERR("Cannot init LEDs (err: %d)", err);
	}
}

//here is the  main method, where the code is actually run and functions are called to
int main(void)
{
	int blink_status = 0;
	int err = 0;

	configure_gpio(); //this calls the gpio configuration function

	err = uart_init(); //calls the uart init function, if there is an error it will display
	if (err) {
		error();
	}

	if (IS_ENABLED(CONFIG_BT_NUS_SECURITY_ENABLED)) {
		err = bt_conn_auth_cb_register(&conn_auth_callbacks);
		if (err) {
			printk("Failed to register authorization callbacks.\n");
			return 0;
		}

		err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
		if (err) {
			printk("Failed to register authorization info callbacks.\n");
			return 0;
		}
	}

	err = bt_enable(NULL);
	if (err) {
		error();
	}

	LOG_INF("Bluetooth initialized");

	k_sem_give(&ble_init_ok);

	if (IS_ENABLED(CONFIG_SETTINGS)) {
		settings_load();
	}

	err = bt_nus_init(&nus_cb); //calls the uart initialization function
	if (err) {
		LOG_ERR("Failed to initialize UART service (err: %d)", err);
		return 0;
	}

	err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd,
			      ARRAY_SIZE(sd));
	if (err) {
		LOG_ERR("Advertising failed to start (err %d)", err);
		return 0;
	}

	for (;;) { //keeps the LED blinking forever
		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
	}
}

void ble_write_thread(void) //this function has something to do with the k thread define function all the way at the bottom. I think it runs continuously? I'm not sure since it isn't in the main method
{
	/* Don't go any further until BLE is initialized */
	k_sem_take(&ble_init_ok, K_FOREVER);
	struct uart_data_t nus_data = {
		.len = 0,
	};

	for (;;) {
		/* Wait indefinitely for data to be sent over bluetooth */
		struct uart_data_t *buf = k_fifo_get(&fifo_uart_rx_data,
						     K_FOREVER);

		int plen = MIN(sizeof(nus_data.data) - nus_data.len, buf->len);
		int loc = 0;

		while (plen > 0) { //loop that will stay while the buffer isn't empty
			memcpy(&nus_data.data[nus_data.len], &buf->data[loc], plen); //I think this copies data from one buffer into another 
			nus_data.len += plen;
			loc += plen;

			if (nus_data.len >= sizeof(nus_data.data) ||
			   (nus_data.data[nus_data.len - 1] == '\n') ||
			   (nus_data.data[nus_data.len - 1] == '\r')) {
				if (bt_nus_send(NULL, nus_data.data, nus_data.len)) {
					LOG_WRN("Failed to send data over BLE connection");
				}
				nus_data.len = 0;
			}

			plen = MIN(sizeof(nus_data.data), buf->len - loc);
		}

		k_free(buf);
	}
}

K_THREAD_DEFINE(ble_write_thread_id, STACKSIZE, ble_write_thread, NULL, NULL,
		NULL, PRIORITY, 0, 0);

/*
 * Copyright (c) 2020 Yestin Sun
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>

static inline float out_ev(struct sensor_value *val)
{
	return (val->val1 + (float)val->val2 / 1000000);
}

static void fetch_and_display(const struct device *dev)
{
	struct sensor_value x, y, z;
	static int trig_cnt;

	trig_cnt++;

	/* lsm6dso accel */
	sensor_sample_fetch_chan(dev, SENSOR_CHAN_ACCEL_XYZ);
	sensor_channel_get(dev, SENSOR_CHAN_ACCEL_X, &x);
	sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Y, &y);
	sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Z, &z);

	printf("accel x:%f ms/2 y:%f ms/2 z:%f ms/2\n",
			(double)out_ev(&x), (double)out_ev(&y), (double)out_ev(&z));

	/* lsm6dso gyro */
	sensor_sample_fetch_chan(dev, SENSOR_CHAN_GYRO_XYZ);
	sensor_channel_get(dev, SENSOR_CHAN_GYRO_X, &x);
	sensor_channel_get(dev, SENSOR_CHAN_GYRO_Y, &y);
	sensor_channel_get(dev, SENSOR_CHAN_GYRO_Z, &z);

	printf("gyro x:%f rad/s y:%f rad/s z:%f rad/s\n",
			(double)out_ev(&x), (double)out_ev(&y), (double)out_ev(&z));

	printf("trig_cnt:%d\n\n", trig_cnt);
}

static int set_sampling_freq(const struct device *dev)
{
	int ret = 0;
	struct sensor_value odr_attr;

	/* set accel/gyro sampling frequency to 12.5 Hz */
	odr_attr.val1 = 12.5;
	odr_attr.val2 = 0;

	ret = sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ,
			SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr);
	if (ret != 0) {
		printf("Cannot set sampling frequency for accelerometer.\n");
		return ret;
	}

	ret = sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ,
			SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr);
	if (ret != 0) {
		printf("Cannot set sampling frequency for gyro.\n");
		return ret;
	}

	return 0;
}

#ifdef CONFIG_LSM6DSO_TRIGGER
static void trigger_handler(const struct device *dev,
			    const struct sensor_trigger *trig)
{
	fetch_and_display(dev);
}

static void test_trigger_mode(const struct device *dev)
{
	struct sensor_trigger trig;

	if (set_sampling_freq(dev) != 0)
		return;

	trig.type = SENSOR_TRIG_DATA_READY;
	trig.chan = SENSOR_CHAN_ACCEL_XYZ;

	if (sensor_trigger_set(dev, &trig, trigger_handler) != 0) {
		printf("Could not set sensor type and channel\n");
		return;
	}
}

#else
static void test_polling_mode(const struct device *dev)
{
	if (set_sampling_freq(dev) != 0) {
		return;
	}

	while (1) {
		fetch_and_display(dev);
		k_sleep(K_MSEC(1000));
	}
}
#endif

int main(void)
{
	printf("We are in main\n");
	const struct device *const dev = DEVICE_DT_GET_ONE(st_lsm6dso);
	if (!device_is_ready(dev)) {
		printk("%s: device not ready.\n", dev->name);
		return 0;
	}

#ifdef CONFIG_LSM6DSO_TRIGGER
	printf("Testing LSM6DSO sensor in trigger mode.\n\n");
	test_trigger_mode(dev);
#else
	printf("Testing LSM6DSO sensor in polling mode.\n\n");
	test_polling_mode(dev);
#endif
	return 0;
}

Parents
  • Hello,

    Also, I'm very new to nordic devices and software, so my apologies my questions are ignorant. Please let me know if there's any more information I can share, and thank you for the help!

    As you are new to our devices, we recommend starting with our Developer Academy. From here you can start with the fundamentals of nRF Connect SDK, then move on to either nRF Connect SDK intermediate or the Bluetooth Low Energy fundamentals course.

    This will help getting to know the most important aspects of your development and how to add custom sensors and such.

    Kind regards,
    Øyvind

  • Hello Øyvind,

    Thank you for the quick response. I actually had already gone through all of those. I'll spend some more time reviewing, but I didn't feel ready to fully solve this problem based on what I learned.

  • Okay, I'll spend some time playing around with the code and trying to combine the peripheral uart sample with the lsm6dso sample. I'm sure I'll have some issues, so if I get stuck I'll share my code here again. If I happen to get it working, I'll also try to share my code in case anyone needs to do something similar.

  • Alright, I've combined the UART and LSM6DSO code as well as updated the overlay and configuration files, and the code builds with no issues. Right now, I've inserted the code from the sensor sample into the main of the uart sample such that the infinite loop of the sensor code (test_polling_mode(dev)) occurs right before the infinite loop of the uart code. As such, the code works exactly how the sensor code would normally work, with it outputting the sensor data to the terminal on repeat. My goal is to now send this data over bluetooth each time it is collected from the sensor. I assume that I will need to add code into the loop that is continuously printing sensor data to the console, but I don't know exactly what to add. I'm hoping that there are people out there who understand the bluetooth code and process more than I do that can help. Any comments or advice would be appreciated, thank you!

    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    /** @file
     *  @brief Nordic UART Bridge Service (NUS) sample
     */
    #include <uart_async_adapter.h>
    
    #include <zephyr/types.h>
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/uart.h>
    #include <zephyr/usb/usb_device.h>
    
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <soc.h>
    
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/bluetooth/hci.h>
    
    #include <bluetooth/services/nus.h>
    
    #include <dk_buttons_and_leds.h>
    
    #include <zephyr/settings/settings.h>
    
    #include <stdio.h>
    #include <string.h>
    
    #include <zephyr/logging/log.h>
    
    #define LOG_MODULE_NAME peripheral_uart
    LOG_MODULE_REGISTER(LOG_MODULE_NAME);
    
    #define STACKSIZE CONFIG_BT_NUS_THREAD_STACK_SIZE
    #define PRIORITY 7
    
    #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN	(sizeof(DEVICE_NAME) - 1)
    
    #define RUN_STATUS_LED DK_LED1
    #define RUN_LED_BLINK_INTERVAL 1000
    
    #define CON_STATUS_LED DK_LED2
    
    #define KEY_PASSKEY_ACCEPT DK_BTN1_MSK
    #define KEY_PASSKEY_REJECT DK_BTN2_MSK
    
    #define UART_BUF_SIZE CONFIG_BT_NUS_UART_BUFFER_SIZE
    #define UART_WAIT_FOR_BUF_DELAY K_MSEC(50)
    #define UART_WAIT_FOR_RX CONFIG_BT_NUS_UART_RX_WAIT_TIME
    
    //add sensor code below
    #include <stdio.h>
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/sensor.h>
    
    static inline float out_ev(struct sensor_value *val)
    {
    	return (val->val1 + (float)val->val2 / 1000000);
    }
    
    static void fetch_and_display(const struct device *dev)
    {
    	struct sensor_value x, y, z;
    	static int trig_cnt;
    
    	trig_cnt++;
    
    	/* lsm6dso accel */
    	sensor_sample_fetch_chan(dev, SENSOR_CHAN_ACCEL_XYZ);
    	sensor_channel_get(dev, SENSOR_CHAN_ACCEL_X, &x);
    	sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Y, &y);
    	sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Z, &z);
    
    	printf("accel x:%f ms/2 y:%f ms/2 z:%f ms/2\n",
    			(double)out_ev(&x), (double)out_ev(&y), (double)out_ev(&z));
    
    	/* lsm6dso gyro */
    	sensor_sample_fetch_chan(dev, SENSOR_CHAN_GYRO_XYZ);
    	sensor_channel_get(dev, SENSOR_CHAN_GYRO_X, &x);
    	sensor_channel_get(dev, SENSOR_CHAN_GYRO_Y, &y);
    	sensor_channel_get(dev, SENSOR_CHAN_GYRO_Z, &z);
    
    	printf("gyro x:%f rad/s y:%f rad/s z:%f rad/s\n",
    			(double)out_ev(&x), (double)out_ev(&y), (double)out_ev(&z));
    
    	printf("trig_cnt:%d\n\n", trig_cnt);
    }
    
    static int set_sampling_freq(const struct device *dev)
    {
    	int ret = 0;
    	struct sensor_value odr_attr;
    
    	/* set accel/gyro sampling frequency to 12.5 Hz */
    	odr_attr.val1 = 12.5;
    	odr_attr.val2 = 0;
    
    	ret = sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ,
    			SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr);
    	if (ret != 0) {
    		printf("Cannot set sampling frequency for accelerometer.\n");
    		return ret;
    	}
    
    	ret = sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ,
    			SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr);
    	if (ret != 0) {
    		printf("Cannot set sampling frequency for gyro.\n");
    		return ret;
    	}
    
    	return 0;
    }
    
    #ifdef CONFIG_LSM6DSO_TRIGGER
    static void trigger_handler(const struct device *dev,
    			    const struct sensor_trigger *trig)
    {
    	fetch_and_display(dev);
    }
    
    static void test_trigger_mode(const struct device *dev)
    {
    	struct sensor_trigger trig;
    
    	if (set_sampling_freq(dev) != 0)
    		return;
    
    	trig.type = SENSOR_TRIG_DATA_READY;
    	trig.chan = SENSOR_CHAN_ACCEL_XYZ;
    
    	if (sensor_trigger_set(dev, &trig, trigger_handler) != 0) {
    		printf("Could not set sensor type and channel\n");
    		return;
    	}
    }
    
    #else
    static void test_polling_mode(const struct device *dev)
    {
    	if (set_sampling_freq(dev) != 0) {
    		return;
    	}
    
    	while (1) {
    		fetch_and_display(dev);
    		k_sleep(K_MSEC(10000));
    	}
    }
    #endif
    
    
    //add sensor code above
    
    
    
    static K_SEM_DEFINE(ble_init_ok, 0, 1);
    
    static struct bt_conn *current_conn;
    static struct bt_conn *auth_conn;
    
    static const struct device *uart = DEVICE_DT_GET(DT_CHOSEN(nordic_nus_uart));
    static struct k_work_delayable uart_work;
    
    struct uart_data_t {
    	void *fifo_reserved;
    	uint8_t data[UART_BUF_SIZE];
    	uint16_t len;
    };
    
    static K_FIFO_DEFINE(fifo_uart_tx_data);
    static K_FIFO_DEFINE(fifo_uart_rx_data);
    
    static const struct bt_data ad[] = {
    	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
    };
    
    static const struct bt_data sd[] = {
    	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
    };
    
    #ifdef CONFIG_UART_ASYNC_ADAPTER
    UART_ASYNC_ADAPTER_INST_DEFINE(async_adapter);
    #else
    #define async_adapter NULL
    #endif
    
    static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
    {
    	ARG_UNUSED(dev);
    
    	static size_t aborted_len;
    	struct uart_data_t *buf;
    	static uint8_t *aborted_buf;
    	static bool disable_req;
    
    	switch (evt->type) {
    	case UART_TX_DONE:
    		LOG_DBG("UART_TX_DONE");
    		if ((evt->data.tx.len == 0) ||
    		    (!evt->data.tx.buf)) {
    			return;
    		}
    
    		if (aborted_buf) {
    			buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
    					   data[0]);
    			aborted_buf = NULL;
    			aborted_len = 0;
    		} else {
    			buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t,
    					   data[0]);
    		}
    
    		k_free(buf);
    
    		buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT);
    		if (!buf) {
    			return;
    		}
    
    		if (uart_tx(uart, buf->data, buf->len, SYS_FOREVER_MS)) {
    			LOG_WRN("Failed to send data over UART");
    		}
    
    		break;
    
    	case UART_RX_RDY:
    		LOG_DBG("UART_RX_RDY");
    		buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data[0]);
    		buf->len += evt->data.rx.len;
    
    		if (disable_req) {
    			return;
    		}
    
    		if ((evt->data.rx.buf[buf->len - 1] == '\n') ||
    		    (evt->data.rx.buf[buf->len - 1] == '\r')) {
    			disable_req = true;
    			uart_rx_disable(uart);
    		}
    
    		break;
    
    	case UART_RX_DISABLED:
    		LOG_DBG("UART_RX_DISABLED");
    		disable_req = false;
    
    		buf = k_malloc(sizeof(*buf));
    		if (buf) {
    			buf->len = 0;
    		} else {
    			LOG_WRN("Not able to allocate UART receive buffer");
    			k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    			return;
    		}
    
    		uart_rx_enable(uart, buf->data, sizeof(buf->data),
    			       UART_WAIT_FOR_RX);
    
    		break;
    
    	case UART_RX_BUF_REQUEST:
    		LOG_DBG("UART_RX_BUF_REQUEST");
    		buf = k_malloc(sizeof(*buf));
    		if (buf) {
    			buf->len = 0;
    			uart_rx_buf_rsp(uart, buf->data, sizeof(buf->data));
    		} else {
    			LOG_WRN("Not able to allocate UART receive buffer");
    		}
    
    		break;
    
    	case UART_RX_BUF_RELEASED:
    		LOG_DBG("UART_RX_BUF_RELEASED");
    		buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t,
    				   data[0]);
    
    		if (buf->len > 0) {
    			k_fifo_put(&fifo_uart_rx_data, buf);
    		} else {
    			k_free(buf);
    		}
    
    		break;
    
    	case UART_TX_ABORTED:
    		LOG_DBG("UART_TX_ABORTED");
    		if (!aborted_buf) {
    			aborted_buf = (uint8_t *)evt->data.tx.buf;
    		}
    
    		aborted_len += evt->data.tx.len;
    		buf = CONTAINER_OF((void *)aborted_buf, struct uart_data_t,
    				   data);
    
    		uart_tx(uart, &buf->data[aborted_len],
    			buf->len - aborted_len, SYS_FOREVER_MS);
    
    		break;
    
    	default:
    		break;
    	}
    }
    
    static void uart_work_handler(struct k_work *item)
    {
    	struct uart_data_t *buf;
    
    	buf = k_malloc(sizeof(*buf));
    	if (buf) {
    		buf->len = 0;
    	} else {
    		LOG_WRN("Not able to allocate UART receive buffer");
    		k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    		return;
    	}
    
    	uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_WAIT_FOR_RX);
    }
    
    static bool uart_test_async_api(const struct device *dev)
    {
    	const struct uart_driver_api *api =
    			(const struct uart_driver_api *)dev->api;
    
    	return (api->callback_set != NULL);
    }
    
    static int uart_init(void)
    {
    	int err;
    	int pos;
    	struct uart_data_t *rx;
    	struct uart_data_t *tx;
    
    	if (!device_is_ready(uart)) {
    		return -ENODEV;
    	}
    
    	if (IS_ENABLED(CONFIG_USB_DEVICE_STACK)) {
    		err = usb_enable(NULL);
    		if (err && (err != -EALREADY)) {
    			LOG_ERR("Failed to enable USB");
    			return err;
    		}
    	}
    
    	rx = k_malloc(sizeof(*rx));
    	if (rx) {
    		rx->len = 0;
    	} else {
    		return -ENOMEM;
    	}
    
    	k_work_init_delayable(&uart_work, uart_work_handler);
    
    
    	if (IS_ENABLED(CONFIG_UART_ASYNC_ADAPTER) && !uart_test_async_api(uart)) {
    		/* Implement API adapter */
    		uart_async_adapter_init(async_adapter, uart);
    		uart = async_adapter;
    	}
    
    	err = uart_callback_set(uart, uart_cb, NULL);
    	if (err) {
    		k_free(rx);
    		LOG_ERR("Cannot initialize UART callback");
    		return err;
    	}
    
    	if (IS_ENABLED(CONFIG_UART_LINE_CTRL)) {
    		LOG_INF("Wait for DTR");
    		while (true) {
    			uint32_t dtr = 0;
    
    			uart_line_ctrl_get(uart, UART_LINE_CTRL_DTR, &dtr);
    			if (dtr) {
    				break;
    			}
    			/* Give CPU resources to low priority threads. */
    			k_sleep(K_MSEC(100));
    		}
    		LOG_INF("DTR set");
    		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DCD, 1);
    		if (err) {
    			LOG_WRN("Failed to set DCD, ret code %d", err);
    		}
    		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DSR, 1);
    		if (err) {
    			LOG_WRN("Failed to set DSR, ret code %d", err);
    		}
    	}
    
    	tx = k_malloc(sizeof(*tx));
    
    	if (tx) {
    		pos = snprintf(tx->data, sizeof(tx->data),
    			       "Starting Nordic UART service example\r\n");
    
    		if ((pos < 0) || (pos >= sizeof(tx->data))) {
    			k_free(rx);
    			k_free(tx);
    			LOG_ERR("snprintf returned %d", pos);
    			return -ENOMEM;
    		}
    
    		tx->len = pos;
    	} else {
    		k_free(rx);
    		return -ENOMEM;
    	}
    
    	err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    	if (err) {
    		k_free(rx);
    		k_free(tx);
    		LOG_ERR("Cannot display welcome message (err: %d)", err);
    		return err;
    	}
    
    	err = uart_rx_enable(uart, rx->data, sizeof(rx->data), UART_WAIT_FOR_RX);
    	if (err) {
    		LOG_ERR("Cannot enable uart reception (err: %d)", err);
    		/* Free the rx buffer only because the tx buffer will be handled in the callback */
    		k_free(rx);
    	}
    
    	return err;
    }
    
    static void connected(struct bt_conn *conn, uint8_t err)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	if (err) {
    		LOG_ERR("Connection failed (err %u)", err);
    		return;
    	}
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    	LOG_INF("Connected %s", addr);
    
    	current_conn = bt_conn_ref(conn);
    
    	dk_set_led_on(CON_STATUS_LED);
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Disconnected: %s (reason %u)", addr, reason);
    
    	if (auth_conn) {
    		bt_conn_unref(auth_conn);
    		auth_conn = NULL;
    	}
    
    	if (current_conn) {
    		bt_conn_unref(current_conn);
    		current_conn = NULL;
    		dk_set_led_off(CON_STATUS_LED);
    	}
    }
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    static void security_changed(struct bt_conn *conn, bt_security_t level,
    			     enum bt_security_err err)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	if (!err) {
    		LOG_INF("Security changed: %s level %u", addr, level);
    	} else {
    		LOG_WRN("Security failed: %s level %u err %d", addr,
    			level, err);
    	}
    }
    #endif
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected    = connected,
    	.disconnected = disconnected,
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    	.security_changed = security_changed,
    #endif
    };
    
    #if defined(CONFIG_BT_NUS_SECURITY_ENABLED)
    static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Passkey for %s: %06u", addr, passkey);
    }
    
    static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	auth_conn = bt_conn_ref(conn);
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Passkey for %s: %06u", addr, passkey);
    	LOG_INF("Press Button 1 to confirm, Button 2 to reject.");
    }
    
    
    static void auth_cancel(struct bt_conn *conn)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing cancelled: %s", addr);
    }
    
    
    static void pairing_complete(struct bt_conn *conn, bool bonded)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing completed: %s, bonded: %d", addr, bonded);
    }
    
    
    static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing failed conn: %s, reason %d", addr, reason);
    }
    
    
    static struct bt_conn_auth_cb conn_auth_callbacks = {
    	.passkey_display = auth_passkey_display,
    	.passkey_confirm = auth_passkey_confirm,
    	.cancel = auth_cancel,
    };
    
    static struct bt_conn_auth_info_cb conn_auth_info_callbacks = {
    	.pairing_complete = pairing_complete,
    	.pairing_failed = pairing_failed
    };
    #else
    static struct bt_conn_auth_cb conn_auth_callbacks;
    static struct bt_conn_auth_info_cb conn_auth_info_callbacks;
    #endif
    
    static void bt_receive_cb(struct bt_conn *conn, const uint8_t *const data,
    			  uint16_t len)
    {
    	int err;
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, ARRAY_SIZE(addr));
    
    	LOG_INF("Received data from: %s", addr);
    
    	for (uint16_t pos = 0; pos != len;) {
    		struct uart_data_t *tx = k_malloc(sizeof(*tx));
    
    		if (!tx) {
    			LOG_WRN("Not able to allocate UART send data buffer");
    			return;
    		}
    
    		/* Keep the last byte of TX buffer for potential LF char. */
    		size_t tx_data_size = sizeof(tx->data) - 1;
    
    		if ((len - pos) > tx_data_size) {
    			tx->len = tx_data_size;
    		} else {
    			tx->len = (len - pos);
    		}
    
    		memcpy(tx->data, &data[pos], tx->len);
    
    		pos += tx->len;
    
    		/* Append the LF character when the CR character triggered
    		 * transmission from the peer.
    		 */
    		if ((pos == len) && (data[len - 1] == '\r')) {
    			tx->data[tx->len] = '\n';
    			tx->len++;
    		}
    
    		err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    		if (err) {
    			k_fifo_put(&fifo_uart_tx_data, tx);
    		}
    	}
    }
    
    static struct bt_nus_cb nus_cb = {
    	.received = bt_receive_cb,
    };
    
    void error(void)
    {
    	dk_set_leds_state(DK_ALL_LEDS_MSK, DK_NO_LEDS_MSK);
    
    	while (true) {
    		/* Spin for ever */
    		k_sleep(K_MSEC(1000));
    	}
    }
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    static void num_comp_reply(bool accept)
    {
    	if (accept) {
    		bt_conn_auth_passkey_confirm(auth_conn);
    		LOG_INF("Numeric Match, conn %p", (void *)auth_conn);
    	} else {
    		bt_conn_auth_cancel(auth_conn);
    		LOG_INF("Numeric Reject, conn %p", (void *)auth_conn);
    	}
    
    	bt_conn_unref(auth_conn);
    	auth_conn = NULL;
    }
    
    void button_changed(uint32_t button_state, uint32_t has_changed)
    {
    	uint32_t buttons = button_state & has_changed;
    
    	if (auth_conn) {
    		if (buttons & KEY_PASSKEY_ACCEPT) {
    			num_comp_reply(true);
    		}
    
    		if (buttons & KEY_PASSKEY_REJECT) {
    			num_comp_reply(false);
    		}
    	}
    }
    #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    static void configure_gpio(void)
    {
    	int err;
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    	err = dk_buttons_init(button_changed);
    	if (err) {
    		LOG_ERR("Cannot init buttons (err: %d)", err);
    	}
    #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    	err = dk_leds_init();
    	if (err) {
    		LOG_ERR("Cannot init LEDs (err: %d)", err);
    	}
    }
    
    int main(void)
    {
    	int blink_status = 0;
    	int err = 0;
    
    	configure_gpio();
    
    	err = uart_init();
    	if (err) {
    		error();
    	}
    
    	if (IS_ENABLED(CONFIG_BT_NUS_SECURITY_ENABLED)) {
    		err = bt_conn_auth_cb_register(&conn_auth_callbacks);
    		if (err) {
    			printk("Failed to register authorization callbacks.\n");
    			return 0;
    		}
    
    		err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
    		if (err) {
    			printk("Failed to register authorization info callbacks.\n");
    			return 0;
    		}
    	}
    
    	err = bt_enable(NULL);
    	if (err) {
    		error();
    	}
    
    	LOG_INF("Bluetooth initialized");
    
    	k_sem_give(&ble_init_ok);
    
    	if (IS_ENABLED(CONFIG_SETTINGS)) {
    		settings_load();
    	}
    
    	err = bt_nus_init(&nus_cb);
    	if (err) {
    		LOG_ERR("Failed to initialize UART service (err: %d)", err);
    		return 0;
    	}
    
    	err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd,
    			      ARRAY_SIZE(sd));
    	if (err) {
    		LOG_ERR("Advertising failed to start (err %d)", err);
    		return 0;
    	}
    
    	//add sensor code below
    	const struct device *const dev = DEVICE_DT_GET_ONE(st_lsm6dso);
    	if (!device_is_ready(dev)) {
    		printk("%s: device not ready.\n", dev->name);
    		return 0;
    	}
    
    	#ifdef CONFIG_LSM6DSO_TRIGGER
    		printf("Testing LSM6DSO sensor in trigger mode.\n\n");
    		test_trigger_mode(dev);
    	#else
    		printf("Testing LSM6DSO sensor in polling mode.\n\n");
    		test_polling_mode(dev);
    	#endif
    		return 0;
    	//add sensor code above
    
    	for (;;) {
    		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
    		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
    	}
    }
    
    void ble_write_thread(void)
    {
    	/* Don't go any further until BLE is initialized */
    	k_sem_take(&ble_init_ok, K_FOREVER);
    	struct uart_data_t nus_data = {
    		.len = 0,
    	};
    
    	for (;;) {
    		/* Wait indefinitely for data to be sent over bluetooth */
    		struct uart_data_t *buf = k_fifo_get(&fifo_uart_rx_data,
    						     K_FOREVER);
    
    		int plen = MIN(sizeof(nus_data.data) - nus_data.len, buf->len);
    		int loc = 0;
    
    		while (plen > 0) {
    			memcpy(&nus_data.data[nus_data.len], &buf->data[loc], plen);
    			nus_data.len += plen;
    			loc += plen;
    
    			if (nus_data.len >= sizeof(nus_data.data) ||
    			   (nus_data.data[nus_data.len - 1] == '\n') ||
    			   (nus_data.data[nus_data.len - 1] == '\r')) {
    				if (bt_nus_send(NULL, nus_data.data, nus_data.len)) {
    					LOG_WRN("Failed to send data over BLE connection");
    				}
    				nus_data.len = 0;
    			}
    
    			plen = MIN(sizeof(nus_data.data), buf->len - loc);
    		}
    
    		k_free(buf);
    	}
    }
    
    K_THREAD_DEFINE(ble_write_thread_id, STACKSIZE, ble_write_thread, NULL, NULL,
    		NULL, PRIORITY, 0, 0);
    

  • Hi,

    It is good you got the sensor working in the NUS sample code. To procede, you first need to remove one of the loops, as obviously nothing happens after the first eternal loop. So all continious work in one loop (you can also use separate threads, but in this case that does not seem appropriate).

    When it comes to sending the data over BLE via NUS you can do it the same way as in the example Ther eyou can see that there is a separate thread ble_write_thread, which get data from a FIFO - fifo_uart_rx_data, and send it via bt_nus_send(). You could use this approach, and populate fifo_uart_rx_data with your sensor data instead using k_fifo_put() as is done for UART (remove the UART related code as you don't need that). A simpler approach would be to simply call bt_nus_send() from the main loop whear you poll the sensor.

  • Hello, this was very helpful, thank you! I am now able to send data over bluetooth using the sensor. I ended up adding the bt_nus_send code to the fetch_and_display loop, which worked.

    A follow up question I have is this: does anyone know what data types can be sent over bluetooth? Obviously, the accelerometer data isn't best represented as an 8 bit integer, but it appears that when the bt_nus_send function is defined in the code its argument is of this data type. Is that something I can change?

    Also, the sensor that I am using has both acceleration and gyroscope readings on all 3 axes. My plan was to send this data by just calling the bt_nus_send six times in a row, but it feels like there could be a better way to do this. Let me know if anyone out there has a better grasp on the bluetooth capabilities than I do and can help.

    Thanks!

  • Hi,

    If you make your own service, then you can decide which data type each characteristics are. If you use the NUS service, it is up to you to know how to intepret the data. With nus, you can send a number of unsigned 8 bit integers (uint8_t). The data is not processed in any way, so this can be used to send any data, as long as both sides know how to interpret the data (you could for instance use 4 bytes to send a float, or a signed integer, or anything else. Of 8 bytes to senda double, or anything else you need.

Reply
  • Hi,

    If you make your own service, then you can decide which data type each characteristics are. If you use the NUS service, it is up to you to know how to intepret the data. With nus, you can send a number of unsigned 8 bit integers (uint8_t). The data is not processed in any way, so this can be used to send any data, as long as both sides know how to interpret the data (you could for instance use 4 bytes to send a float, or a signed integer, or anything else. Of 8 bytes to senda double, or anything else you need.

Children
No Data
Related