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;
}

  • 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.

  • Actually as I spent some time reviewing I found the code used in lesson 4, exercise 2 of the BLE fundamentals course. This code seems like it might be a better place to start than the other two codes, so I'm planning on looking into that a little bit. Do you have any feedback on which of these samples to start from?

    /*
     * Copyright (c) 2023 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/gap.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/conn.h>
    #include <dk_buttons_and_leds.h>
    #include "my_lbs.h"
    
    static struct bt_le_adv_param *adv_param = BT_LE_ADV_PARAM(
    	(BT_LE_ADV_OPT_CONNECTABLE |
    	 BT_LE_ADV_OPT_USE_IDENTITY), /* Connectable advertising and use identity address */
    	800, /* Min Advertising Interval 500ms (800*0.625ms) */
    	801, /* Max Advertising Interval 500.625ms (801*0.625ms) */
    	NULL); /* Set to NULL for undirected advertising */
    
    LOG_MODULE_REGISTER(Lesson4_Exercise2, LOG_LEVEL_INF);
    
    #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
    
    #define RUN_STATUS_LED DK_LED1
    #define CON_STATUS_LED DK_LED2
    #define USER_LED DK_LED3
    #define USER_BUTTON DK_BTN1_MSK
    
    #define STACKSIZE 1024
    #define PRIORITY 7
    
    #define RUN_LED_BLINK_INTERVAL 1000
    /* STEP 17 - Define the interval at which you want to send data at */
    #define NOTIFY_INTERVAL 500
    static bool app_button_state;
    /* STEP 15 - Define the data you want to stream over Bluetooth LE */
    static uint32_t app_sensor_value = 100;
    
    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_LBS_VAL),
    };
    
    /* STEP 16 - Define a function to simulate the data */
    static void simulate_data(void)
    {
    	app_sensor_value++;
    	if (app_sensor_value == 200) {
    		app_sensor_value = 100;
    	}
    }
    static void app_led_cb(bool led_state)
    {
    	dk_set_led(USER_LED, led_state);
    }
    
    static bool app_button_cb(void)
    {
    	return app_button_state;
    }
    
    /* STEP 18.1 - Define the thread function  */
    void send_data_thread(void)
    {
    	while (1) {
    		/* Simulate data */
    		simulate_data();
    		/* Send notification, the function sends notifications only if a client is subscribed */
    		my_lbs_send_sensor_notify(app_sensor_value);
    
    		k_sleep(K_MSEC(NOTIFY_INTERVAL));
    	}
    }
    
    static struct my_lbs_cb app_callbacks = {
    	.led_cb = app_led_cb,
    	.button_cb = app_button_cb,
    };
    
    static void button_changed(uint32_t button_state, uint32_t has_changed)
    {
    	if (has_changed & USER_BUTTON) {
    		uint32_t user_button_state = button_state & USER_BUTTON;
    		/* STEP 6 - Send indication on a button press */
    		my_lbs_send_button_state_indicate(user_button_state);
    		app_button_state = user_button_state ? true : false;
    	}
    }
    static void on_connected(struct bt_conn *conn, uint8_t err)
    {
    	if (err) {
    		printk("Connection failed (err %u)\n", err);
    		return;
    	}
    
    	printk("Connected\n");
    
    	dk_set_led_on(CON_STATUS_LED);
    }
    
    static void on_disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	printk("Disconnected (reason %u)\n", reason);
    
    	dk_set_led_off(CON_STATUS_LED);
    }
    
    struct bt_conn_cb connection_callbacks = {
    	.connected = on_connected,
    	.disconnected = on_disconnected,
    };
    
    static int init_button(void)
    {
    	int err;
    
    	err = dk_buttons_init(button_changed);
    	if (err) {
    		printk("Cannot init buttons (err: %d)\n", err);
    	}
    
    	return err;
    }
    
    int main(void)
    {
    	int blink_status = 0;
    	int err;
    
    	LOG_INF("Starting Lesson 4 - Exercise 2 \n");
    
    	err = dk_leds_init();
    	if (err) {
    		LOG_ERR("LEDs init failed (err %d)\n", err);
    		return -1;
    	}
    
    	err = init_button();
    	if (err) {
    		printk("Button init failed (err %d)\n", err);
    		return -1;
    	}
    
    	err = bt_enable(NULL);
    	if (err) {
    		LOG_ERR("Bluetooth init failed (err %d)\n", err);
    		return -1;
    	}
    	bt_conn_cb_register(&connection_callbacks);
    
    	err = my_lbs_init(&app_callbacks);
    	if (err) {
    		printk("Failed to init LBS (err:%d)\n", err);
    		return -1;
    	}
    	LOG_INF("Bluetooth initialized\n");
    	err = bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
    	if (err) {
    		LOG_ERR("Advertising failed to start (err %d)\n", err);
    		return -1;
    	}
    
    	LOG_INF("Advertising successfully started\n");
    	for (;;) {
    		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
    		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
    	}
    }
    /* STEP 18.2 - Define and initialize a thread to send data periodically */
    K_THREAD_DEFINE(send_data_thread_id, STACKSIZE, send_data_thread, NULL, NULL, NULL, PRIORITY, 0, 0);

  • Hi, 

    Øyvind is out of office. Did you make any progress on this? Regarding starting point, it depends on what you need. An alternative would be to start with the Bluetooht peripheral_uart sample that you have seen before. Using the LSM6DSO is discussed in this thread, which you may find usefull.

  • Thank you for the response. I haven't made any progress yet, as I only recently got the LSM6DSO working. In fact, the thread you linked is actually mine. My next step is to transmit data from the LSM6DSO over bluetooth, which is what I was hoping to get figured out in this thread. To give you more information, I will eventually be manufacturing a custom circuit board using the NRF52832 chip that I will connect to the sensor and use to transmit data over bluetooth.

Related