Best way to receive data sent over BLE

Hello, 

I've been working on a project that uses an nRF52832 to send data over bluetooth. I'm sending large chunks of data, and I'm wondering what the best way to view this data is. When I connect with the nRF connect mobile app on my iPhone, I am able to receive data but don't see an option to display all sent data in file format. Is there a software that I could get on my laptop that is capable of connecting to a Nordic device and also outputting all sent data to a file, or at least a form that I could copy and paste? 

Thank you!

Parents
  • Hi,

    As Tony asks: What format is the data you send in?
    Depending on the format, the answer will vary.

    my iPhone,
    my laptop

    Do you want a solution for a phone or a laptop?

    Regards,
    Sigurd Hellesvik

  • Hello,

    Yes, I am using NUS to send data. Specifically, I am filling a Ring Buffer (uint32_t) with a large amount of data, which I then unpack into signed 16 bit integers and send over bluetooth. Once the data is sent, I collect more and fill up the buffer again, then send again, and so on. I am also making a custom circuit to run this code that uses the nRF52832 to collect and send data, and won't be able to physically connect to a laptop or phone. Ideally, I would like an application on my laptop that functions similarly to the nRF connect mobile app, but is able to output all sent/received data in a file format. I've heard that Wireshark might be an option, but before I went too deep I thought I would check and see if there are other suggestions.

    Thank you,

    Connor

  • Yes, thank you. I will give AuTerm a shot, and if there are any issues I may reach back out in this thread.

  • Hello, I was able to get the bluetooth connection up and running using AuTerm, but I'm having some other difficulties. The main issue is that I do see data coming in and see it being outputted to a log file, but the format is unreadable and I'm not sure how to interpret the data. I'll attach a screenshot of both my terminal and the log file. I saw your comment about sending data in ASCII form, so I had assumed the log file would have it in that format. Do you have any advice here? Should I look into another software for this?

    Thank you for the help so far!

  • Another quick question: it seems like the terminal is still outputting data even when I'm not actually running the part of my code that sends data over bluetooth. Is AuTerm always trying to get data sent over bluetooth and outputting stuff to the terminal even when there is no data being sent from my chip/sensors?

  • Before I answer your question here:

    Can you try AuTerm with our Bluetooth Peripheral UART sample out of the box and see if that has the same behaviour?

  • You're correct in assuming that it works with the UART sample out of the box, which is interesting. That seems to indicate that my code is the issue, although I'm curious why it seems to work using the nrf connect mobile app (and by work I mean that it is capable of receiving integers that correspond to what I'm sending). I'll share my code here in case there is something obviously wrong with how I send data.

    //pseudocode 
    
    
    //necessary include statements here
    //--------------------------------------------------------------------------------------------------------------------
    #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 <stdint.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/drivers/sensor.h>
    #include <zephyr/sys/ring_buffer.h>
    #include <zephyr/drivers/adc.h>
    #include <zephyr/drivers/gpio.h>
    
    
    //and define statments
    #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
    #define RAD_TO_DEGREE 57.2958
    #define GRAVITY 9.80665
    #define DECIMAL_SHIFT 1000
    #define SAMPLING_FREQUENCY 104 //set sampling freq
    #define ACTIVE_MODE 1 //set mode
    #define BUFFER_SIZE 8000  // Number of elements for 32-bit integers (48 KB)
    #define RING_BUF_SIZE (BUFFER_SIZE * sizeof(int32_t))  // Total size in bytes
    RING_BUF_DECLARE(my_ring_buffer, RING_BUF_SIZE);
    #define ADC_BUFFER_SIZE 100  // Number of elements for 32-bit integers (48 KB)
    #define ADC_RING_BUF_SIZE (ADC_BUFFER_SIZE * sizeof(int32_t))  // Total size in bytes
    RING_BUF_DECLARE(adc_ring_buffer, ADC_RING_BUF_SIZE);
    #define RING_BUFFER_TEST 0
    uint16_t time_ms = 0; //define a global variable to track time
    #define TESTING_MODE 0
    #define ADC_MODE 1
    #define GPIO_NODE DT_NODELABEL(gpio0)
    #define TEST_PIN 19
    
    //--------------------------------------------------------------------------------------------------------------
    
    //all necessary random and initialization-type functions here (these functions should not need edits)
    
    static inline float out_ev(struct sensor_value *val)
    {
    	return (val->val1 + (float)val->val2 / 1000000);
    }
    
    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 = SAMPLING_FREQUENCY;
    	//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;
    }
    
    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)) {
    			printf("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);
    		printf("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),
    			       "Code written by Connor Shannon\r\n");
    
    		if ((pos < 0) || (pos >= sizeof(tx->data))) {
    			k_free(rx);
    			k_free(tx);
    			printf("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);
    		printf("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) {
    		printf("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) {
    		printf("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) {
    		printf("Cannot init buttons (err: %d)", err);
    	}
    #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    	err = dk_leds_init();
    	if (err) {
    		printf("Cannot init LEDs (err: %d)", err);
    	}
    }
    
    
    //____________________________________________________________________________________________________________________________________________________________
    
    //all functions above this should not need editing, while all functions below this are more important and may require editing
    
    //____________________________________________________________________________________________________________________________________________________________
    
    /**
     * Combines two 16-bit integers into a single 32-bit integer.
     * @param value1 The first 16-bit integer (to be placed in the upper 16 bits).
     * @param value2 The second 16-bit integer (to be placed in the lower 16 bits).
     * @return A 32-bit integer containing both input values.
     */
    uint32_t pack_16bit_to_32bit(int16_t value1, int16_t value2) {
        return ((uint32_t)(uint16_t)value1 << 16) | (uint16_t)value2;
    }
    
    /**
     * Extracts the first (upper) 16-bit integer from a 32-bit integer.
     * @param packed_value The 32-bit integer containing two 16-bit integers.
     * @return The first (upper) 16-bit integer.
     */
    int16_t extract_first_16bit(uint32_t packed_value) {
        return (int16_t)(packed_value >> 16);  // Ensure sign extension
    }
    
    /**
     * Extracts the second (lower) 16-bit integer from a 32-bit integer.
     * @param packed_value The 32-bit integer containing two 16-bit integers.
     * @return The second (lower) 16-bit integer.
     */
    int16_t extract_second_16bit(uint32_t packed_value) {
        return (int16_t)(packed_value & 0xFFFF);  // Implicitly sign-extended
    }
    
    // Add a 32-bit integer to the ring buffer
    int add_to_ring_buffer(struct ring_buf *rb, uint32_t data) {
        int ret = ring_buf_put(rb, (uint8_t *)&data, sizeof(data));
        return (ret == sizeof(data)) ? 1 : 0; // Return 1 if all bytes were written, 0 otherwise
    }
    
    // Check if the ring buffer is full
    int is_ring_buffer_full(struct ring_buf *rb) {
        return ring_buf_space_get(rb) == 0 ? 1 : 0; // Return 1 if full, 0 if not
    }
    
    
    // Get a 32-bit integer from the ring buffer
    int get_from_ring_buffer(struct ring_buf *rb, uint32_t *data) {
        // Attempt to retrieve the first 32-bit value (4 bytes) from the ring buffer
        int ret = ring_buf_get(rb, (uint8_t *)data, sizeof(uint32_t));
    
        if (ret == sizeof(uint32_t)) {
            return 1; // Successfully retrieved the first value
        } else {
            return 0; // Failed to retrieve (buffer might be empty)
        }
    }
    
    // Check if the ring buffer is empty
    int is_ring_buffer_empty(struct ring_buf *rb) {
        return ring_buf_is_empty(rb) ? 1 : 0; // Return 1 if empty, 0 if not
    }
    
    
    //check if bluetooth is connected
    bool is_bt_connected(void)
    {
        return (current_conn != NULL);
    }
    
    static int64_t time_reset_reference = 0;
    
    void reset_time_reference(void) { //resets time value every time data collection begins
        time_reset_reference = k_uptime_get();
    }
    
    int16_t get_elapsed_time_ms(void) { //uses the reference time to get elapsed time since start of data collection
        int64_t current_time = k_uptime_get();
        int64_t elapsed_time = current_time - time_reset_reference;
    
        // Safely cast to uint16_t
        return (int16_t)(elapsed_time & 0xFFFF); // Wrap around at 16 bits
    }
    void check_ring_buffer_progress(size_t total_elements) {
        static uint32_t call_count = 0;  // Tracks how many times the function is called
    	uint16_t time;
        // Increment the call count
        call_count++;
    
        // Only print every 100 calls
        if (call_count % 100 == 0) {
            // Get the current used space in bytes
            size_t used_space = ring_buf_size_get(&my_ring_buffer);  
    
            // Convert used space (in bytes) to number of elements
            size_t used_elements = used_space / sizeof(uint32_t);  // Assuming uint32_t (4 bytes each)
    
            // Calculate the percentage of the buffer that is full
            uint8_t current_percent = (used_elements * 100) / total_elements;
    
            // Print the buffer percentage
            printf("Ring buffer is %d%% full\n", current_percent);
    
    		//time = get_elapsed_time_ms();
    		//printf("Elapsed time: %d\n", time);
        }
    }
    
    
    void get_LSM6DSO_data(const struct device *dev){ //this function gets and stores 6 values from the sensor
    	struct sensor_value x, y, z;
    	int16_t pack1;
    	int16_t pack2;
    	uint32_t data_to_store;
    	int full;
    
    	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);
    	
    	if (TESTING_MODE == 1){
    		printf("Raw gyro X: %d, %d\n", x.val1, x.val2);
        	printf("Raw gyro Y: %d, %d\n", y.val1, y.val2);
        	printf("Raw gyro Z: %d, %d\n", z.val1, z.val2);
    		k_sleep(K_MSEC(500));
    	}
    
    	//package x and y gyro, store
    	pack1 = (int16_t)(out_ev(&x)*(float)DECIMAL_SHIFT*(float)RAD_TO_DEGREE);
    	pack2 = (int16_t)(out_ev(&y)*(float)DECIMAL_SHIFT*(float)RAD_TO_DEGREE);
    	data_to_store = pack_16bit_to_32bit(pack1, pack2);
    	full = add_to_ring_buffer(&my_ring_buffer, data_to_store); 
    
    	if (TESTING_MODE == 1){
    		printf("X gyro: %d\n", pack1);
    		printf("Y gyro: %d\n", pack2);
    		k_sleep(K_MSEC(500));
    	}
    
    
    	//package z gyro and x accel, store
    	pack1 = (int16_t)(out_ev(&z)*(float)DECIMAL_SHIFT*(float)RAD_TO_DEGREE);
    	
    	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);
    
    	if (TESTING_MODE == 1){
    		printf("Raw accel X: %d, %d\n", x.val1, x.val2);
        	printf("Raw accel Y: %d, %d\n", y.val1, y.val2);
        	printf("Raw accel Z: %d, %d\n", z.val1, z.val2);
    		k_sleep(K_MSEC(500));
    	}
    
    	pack2 = (int16_t)(out_ev(&x)*(float)DECIMAL_SHIFT/(float)GRAVITY);
    	data_to_store = pack_16bit_to_32bit(pack1, pack2);
    	full = add_to_ring_buffer(&my_ring_buffer, data_to_store);
    
    	if (TESTING_MODE == 1){
    		printf("Z gyro: %d\n", pack1);
    		printf("X accel: %d\n", pack2);
    		k_sleep(K_MSEC(500));
    	}
    
    	//package y accel and z accel, store
    	pack1 = (int16_t)(out_ev(&y)*(float)DECIMAL_SHIFT/(float)GRAVITY);
    	pack2 = (int16_t)(out_ev(&z)*(float)DECIMAL_SHIFT/(float)GRAVITY);
    	data_to_store = pack_16bit_to_32bit(pack1, pack2);
    	full = add_to_ring_buffer(&my_ring_buffer, data_to_store);
    
    	if (TESTING_MODE == 1){
    		printf("Y accel: %d\n", pack1);
    		printf("Z accel: %d\n", pack2);
    		k_sleep(K_MSEC(500));
    	}
    
    }
    
    
    int16_t get_adc(const struct adc_dt_spec *adc_channel, struct adc_sequence *sequence, int16_t *buf) {
        int32_t val_mv32;
        int err;
    
        /* STEP 5 - Read a sample from the ADC */
        err = adc_read(adc_channel->dev, sequence);
        if (err < 0) {
            printf("Could not read (%d)\n", err);
            return -1; // Return error indicator
        }
    
        val_mv32 = (int32_t)*buf;
    
        /* STEP 6 - Convert raw value to mV */
        err = adc_raw_to_millivolts_dt(adc_channel, &val_mv32);
        if (err < 0) {
            printf("Could not convert to millivolts (%d)\n", err);
            return -1; // Return error indicator
        }
    
        //printf("%d\n", (int16_t)val_mv32);
        return (int16_t)val_mv32;
    }
    
    
    
    int gather_data_no_adc(const struct device *dev){
    	int16_t pack1;
    	int16_t pack2;
    	uint32_t data_to_store;
    	int full;
    
    	pack1 = 0; //0 value tracks breaks between samples
    	pack2 = get_elapsed_time_ms(); 
    	
    	data_to_store = pack_16bit_to_32bit(pack1, pack2);
    	full = add_to_ring_buffer(&my_ring_buffer, data_to_store); 
    
    	get_LSM6DSO_data(dev); //note that this function packs and stores values automatically
        
        if (is_ring_buffer_full(&my_ring_buffer)){
            return 0;
        }
        return 1;
    }   
    
    void send_data(struct ring_buf *rb) {
        uint32_t buffer_value;
        int full;
        int16_t extract1;
        int16_t extract2;
        int send_success = 0; // Flag to track if both sends succeed
    
        // Get data from the buffer
    	if (rb == &adc_ring_buffer){
    		printf("ADC Buffer size before sending: %d\n", ring_buf_size_get(rb));
    	}
        full = get_from_ring_buffer(rb, &buffer_value);
        extract1 = extract_first_16bit(buffer_value);
        extract2 = extract_second_16bit(buffer_value);
    	if (rb == &adc_ring_buffer){
    		if (full == 0) {
    			printf("Ring buffer was empty, skipping send.\n");
    			return;
    		}
    		printf("ADC Buffer size after sending: %d\n", ring_buf_size_get(rb));
    		
    	}
    	
    
        while (1) {
            // Check if Bluetooth is connected
            if (!is_bt_connected()) {
                printf("Bluetooth not connected. Waiting for connection...\n");
                k_sleep(K_MSEC(3000)); // Wait for 1 second before checking again
                continue; // Skip the sending attempt until Bluetooth is connected
            }
    
            // Attempt to send the first part of the data
            if (bt_nus_send(NULL, (uint8_t *)&extract1, sizeof(extract1))) {
                //printf("Failed to send first part of data over BLE connection\n");
            } else {
                send_success |= 1; // Mark the first send as successful
            }
    
            // Attempt to send the second part of the data
            if (bt_nus_send(NULL, (uint8_t *)&extract2, sizeof(extract2))) {
                printf("Failed to send data.. Please make sure your device is ready to receive BLE.\n");
            } else {
                send_success |= 2; // Mark the second send as successful
            }
    
            // If both parts were successfully sent, break the loop
            if (send_success == 3) {
                break;
            }
    
            // If any part failed, retry after waiting for 1 second
            k_sleep(K_MSEC(3000));
        }
    }
    
    int store_adc_data(int16_t adc1, int16_t adc2, int16_t adc3){
    	int16_t time;
    	uint32_t packed;
    	int err;
    
    	time = get_elapsed_time_ms();
    	printf("Time: %d\n",time);
    
    	packed = pack_16bit_to_32bit(time, adc1);
    	err = add_to_ring_buffer(&adc_ring_buffer, packed);
    	if (err == 0){
    		return err;
    	}
    	packed = pack_16bit_to_32bit(adc2, adc3);
    	err = add_to_ring_buffer(&adc_ring_buffer, packed);
    	return err;
    }
    
    
    //main function here, this is what calls other functions and runs code.
    //***************************************************************************************************
    int main(void){
    	ring_buf_reset(&my_ring_buffer);
    	ring_buf_reset(&adc_ring_buffer);
    	if (RING_BUFFER_TEST == 1){
    		//lines below this test ring buffer
    		int32_t data = 0;  // Start with an arbitrary value for data
    		int stored_count = 0;
    
    		printf("Starting test to store 16-bit integers in the ring buffer...\n");
    
    		// Keep adding 16-bit integers until the buffer is full
    		for (;;) {
    			// Put the data into the ring buffer
    			if (ring_buf_put(&my_ring_buffer, (uint8_t *)&data, sizeof(data)) == 0) {
    				// If the put operation fails (buffer full), break the loop
    				printf("Ring buffer is full. Stored %d values.\n", stored_count);
    				break;
    			}
    
    			// Increment the data value (just for testing, you can store arbitrary data)
    			data++;
    
    			// Increment the stored count
    			stored_count++;
    		}
    		printf("exited loop");
    	}
    
        //all necessary initialization here
    	int err = 0;
    	uint8_t last_printed_percent;
    	//uint16_t adc_data;
    	int sensor_ready = 1;
    	int16_t adc1;
    	int16_t adc2;
    	int16_t adc3;
    	//int test = 0;
    	//static int counter = 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) {
    		printf("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) {
    		printf("Advertising failed to start (err %d)", err);
    		return 0;
    	}
    
    	//add sensor code below
    	while (sensor_ready == 1){
    		const struct device *const dev = DEVICE_DT_GET_ONE(st_lsm6dso);
    
    		if (!device_is_ready(dev)) {
    			printf("I2C device not ready. Waiting for it to initialize...\n");
    			k_sleep(K_MSEC(3000));
    		} else {
    			printf("I2C device is ready!\n");
    			sensor_ready = 0;
    		}
    
    		sensor_ready = set_sampling_freq(dev);
    	}
    	const struct device *const dev = DEVICE_DT_GET_ONE(st_lsm6dso);
    	//sensor code above
    
    	//ADC initialization below ===================================================================================================================================
    	
    	// static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET(DT_PATH(zephyr_user));
    
    	// uint32_t count = 0;
    
    	// /* STEP 4.1 - Define a variable of type adc_sequence and a buffer of type uint16_t */
    	// int16_t buf;
    	// struct adc_sequence sequence = {
    	// 	.buffer = &buf,
    	// 	/* buffer size in bytes, not number of samples */
    	// 	.buffer_size = sizeof(buf),
    	// 	// Optional
    	// 	//.calibrate = true,
    	// };
    
    	// /* STEP 3.3 - validate that the ADC peripheral (SAADC) is ready */
    	// if (!adc_is_ready_dt(&adc_channel)) {
    	// 	printf("ADC controller devivce %s not ready", adc_channel.dev->name);
    	// 	return 0;
    	// }
    	// /* STEP 3.4 - Setup the ADC channel */
    	// err = adc_channel_setup_dt(&adc_channel);
    	// if (err < 0) {
    	// 	printf("Could not setup channel #%d (%d)", 0, err);
    	// 	return 0;
    	// }
    	// /* STEP 4.2 - Initialize the ADC sequence */
    	// err = adc_sequence_init_dt(&adc_channel, &sequence);
    	// if (err < 0) {
    	// 	printf("Could not initalize sequnce");
    	// 	return 0;
    	// }
    
    
    	/* Define three ADC channels */
    	static const struct adc_dt_spec adc_channel1 = ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0);
    	static const struct adc_dt_spec adc_channel2 = ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 1);
    	static const struct adc_dt_spec adc_channel3 = ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 2);
    
    
    
    
    	/* Define three buffers */
    	int16_t buf1, buf2, buf3;
    
    	/* Define three ADC sequences */
    	struct adc_sequence sequence1 = { .buffer = &buf1, .buffer_size = sizeof(buf1) };
    	struct adc_sequence sequence2 = { .buffer = &buf2, .buffer_size = sizeof(buf2) };
    	struct adc_sequence sequence3 = { .buffer = &buf3, .buffer_size = sizeof(buf3) };
    
    	uint32_t count = 0;
    
    	/* Check which ADC devices are ready and initialize only those */
    	if (adc_is_ready_dt(&adc_channel1)) {
    		err = adc_channel_setup_dt(&adc_channel1);
    		if (err < 0) {
    			printf("Could not setup channel 1 (%d)\n", err);
    		} else {
    			err = adc_sequence_init_dt(&adc_channel1, &sequence1);
    			if (err < 0) {
    				printf("Could not initialize sequence 1\n");
    			}
    		}
    	} else {
    		printf("ADC1 not ready\n");
    	}
    
    	if (adc_is_ready_dt(&adc_channel2)) {
    		err = adc_channel_setup_dt(&adc_channel2);
    		if (err < 0) {
    			printf("Could not setup channel 2 (%d)\n", err);
    		} else {
    			err = adc_sequence_init_dt(&adc_channel2, &sequence2);
    			if (err < 0) {
    				printf("Could not initialize sequence 2\n");
    			}
    		}
    	} else {
    		printf("ADC2 not ready\n");
    	}
    
    	if (adc_is_ready_dt(&adc_channel3)) {
    		err = adc_channel_setup_dt(&adc_channel3);
    		if (err < 0) {
    			printf("Could not setup channel 3 (%d)\n", err);
    		} else {
    			err = adc_sequence_init_dt(&adc_channel3, &sequence3);
    			if (err < 0) {
    				printf("Could not initialize sequence 3\n");
    			}
    		}
    	} else {
    		printf("ADC3 not ready\n");
    	}
    
    
    	//ADC initialization above ===================================================================================================================================
    
    
        if (ACTIVE_MODE == 1){ //if the device is in its fully active mode, recording and sending data over bluetooth
            int BLUETOOTH_MODE = 0; //assume storage starts empty
            for (;;){ //infinite loop
                if (BLUETOOTH_MODE == 0){ //function occurs if storage has just been emptied (or starts empty)
    				while (gather_data_no_adc(dev)){
                    //while (gather_data(dev, &adc_channel, &sequence, &buf)){ //goes until buffer is filled
    					check_ring_buffer_progress(BUFFER_SIZE);
    					k_sleep(K_MSEC(10));
    					static int counter = 0;
    					if (ADC_MODE == 1){
    						if (++counter % 100 == 0) { // Code to run every 100th call
    							adc1 = get_adc(&adc_channel1, &sequence1, &buf1);
    							adc2 = get_adc(&adc_channel2, &sequence2, &buf2);
    							adc3 = get_adc(&adc_channel3, &sequence3, &buf3);
    							err = store_adc_data(adc1, adc2, adc3);
    							if (err == 0){
    								printf("ADC data could not be stored.\n");
    							}
    						}
    					}
                    }
    				printf("Ring buffer is full. Ready to send data over Bluetooth.\n");
                    BLUETOOTH_MODE = 1;
                }
                    
    
                if (BLUETOOTH_MODE == 1){ //function occurs once storage is full and ready to send data 
                    while (!is_ring_buffer_empty(&my_ring_buffer)){ //this line starts a loop that sends all imu data over bluetooth
                        send_data(&my_ring_buffer);
    					check_ring_buffer_progress(BUFFER_SIZE);
                    }
    				printf("Ring buffer is empty. Sending ADC data now.\n");
    				while (!is_ring_buffer_empty(&adc_ring_buffer)){  //this line sends ADC data over bluetooth
                        send_data(&adc_ring_buffer);
                    }
                    BLUETOOTH_MODE = 0; //change back to data collection mode once all data is sent
    				reset_time_reference(); //reset time so we are ready to collect more data
    				last_printed_percent = 0; //reset buffer storage tracker
    				printf("All data sent. Ready to collect more data.\n");
                }
                
    
            }
        }
    
        if (ACTIVE_MODE == 0){ //this situation would occur if we didn't want the device to be active. Likely, this mode would be to test something, perhaps to print the data to console
            int ret;
    		/* Get the GPIO device binding */
    		const struct device *dev = DEVICE_DT_GET(GPIO_NODE);
    		if (!device_is_ready(dev)) {
    			printk("GPIO device not ready\n");
    			return;
    		}
    
    		/* Configure the GPIO pin as an output */
    		ret = gpio_pin_configure(dev, TEST_PIN, GPIO_OUTPUT_ACTIVE);
    		if (ret < 0) {
    			printk("Failed to configure pin %d\n", TEST_PIN);
    			return;
    		}
    
    		/* Set pin HIGH (VCC) */
    		ret = gpio_pin_set(dev, TEST_PIN, 1);
    		if (ret < 0) {
    			printk("Failed to set pin %d\n", TEST_PIN);
    			return;
    		}
    
    		printk("Pin %d set HIGH. Measure voltage with a voltmeter.\n", TEST_PIN);
    
    		while (1) {
    			k_sleep(K_FOREVER);  // Keep the program running indefinitely
    		}
        }
    }
        
    
    
    
    
    

Reply
  • You're correct in assuming that it works with the UART sample out of the box, which is interesting. That seems to indicate that my code is the issue, although I'm curious why it seems to work using the nrf connect mobile app (and by work I mean that it is capable of receiving integers that correspond to what I'm sending). I'll share my code here in case there is something obviously wrong with how I send data.

    //pseudocode 
    
    
    //necessary include statements here
    //--------------------------------------------------------------------------------------------------------------------
    #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 <stdint.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/drivers/sensor.h>
    #include <zephyr/sys/ring_buffer.h>
    #include <zephyr/drivers/adc.h>
    #include <zephyr/drivers/gpio.h>
    
    
    //and define statments
    #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
    #define RAD_TO_DEGREE 57.2958
    #define GRAVITY 9.80665
    #define DECIMAL_SHIFT 1000
    #define SAMPLING_FREQUENCY 104 //set sampling freq
    #define ACTIVE_MODE 1 //set mode
    #define BUFFER_SIZE 8000  // Number of elements for 32-bit integers (48 KB)
    #define RING_BUF_SIZE (BUFFER_SIZE * sizeof(int32_t))  // Total size in bytes
    RING_BUF_DECLARE(my_ring_buffer, RING_BUF_SIZE);
    #define ADC_BUFFER_SIZE 100  // Number of elements for 32-bit integers (48 KB)
    #define ADC_RING_BUF_SIZE (ADC_BUFFER_SIZE * sizeof(int32_t))  // Total size in bytes
    RING_BUF_DECLARE(adc_ring_buffer, ADC_RING_BUF_SIZE);
    #define RING_BUFFER_TEST 0
    uint16_t time_ms = 0; //define a global variable to track time
    #define TESTING_MODE 0
    #define ADC_MODE 1
    #define GPIO_NODE DT_NODELABEL(gpio0)
    #define TEST_PIN 19
    
    //--------------------------------------------------------------------------------------------------------------
    
    //all necessary random and initialization-type functions here (these functions should not need edits)
    
    static inline float out_ev(struct sensor_value *val)
    {
    	return (val->val1 + (float)val->val2 / 1000000);
    }
    
    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 = SAMPLING_FREQUENCY;
    	//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;
    }
    
    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)) {
    			printf("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);
    		printf("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),
    			       "Code written by Connor Shannon\r\n");
    
    		if ((pos < 0) || (pos >= sizeof(tx->data))) {
    			k_free(rx);
    			k_free(tx);
    			printf("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);
    		printf("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) {
    		printf("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) {
    		printf("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) {
    		printf("Cannot init buttons (err: %d)", err);
    	}
    #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    	err = dk_leds_init();
    	if (err) {
    		printf("Cannot init LEDs (err: %d)", err);
    	}
    }
    
    
    //____________________________________________________________________________________________________________________________________________________________
    
    //all functions above this should not need editing, while all functions below this are more important and may require editing
    
    //____________________________________________________________________________________________________________________________________________________________
    
    /**
     * Combines two 16-bit integers into a single 32-bit integer.
     * @param value1 The first 16-bit integer (to be placed in the upper 16 bits).
     * @param value2 The second 16-bit integer (to be placed in the lower 16 bits).
     * @return A 32-bit integer containing both input values.
     */
    uint32_t pack_16bit_to_32bit(int16_t value1, int16_t value2) {
        return ((uint32_t)(uint16_t)value1 << 16) | (uint16_t)value2;
    }
    
    /**
     * Extracts the first (upper) 16-bit integer from a 32-bit integer.
     * @param packed_value The 32-bit integer containing two 16-bit integers.
     * @return The first (upper) 16-bit integer.
     */
    int16_t extract_first_16bit(uint32_t packed_value) {
        return (int16_t)(packed_value >> 16);  // Ensure sign extension
    }
    
    /**
     * Extracts the second (lower) 16-bit integer from a 32-bit integer.
     * @param packed_value The 32-bit integer containing two 16-bit integers.
     * @return The second (lower) 16-bit integer.
     */
    int16_t extract_second_16bit(uint32_t packed_value) {
        return (int16_t)(packed_value & 0xFFFF);  // Implicitly sign-extended
    }
    
    // Add a 32-bit integer to the ring buffer
    int add_to_ring_buffer(struct ring_buf *rb, uint32_t data) {
        int ret = ring_buf_put(rb, (uint8_t *)&data, sizeof(data));
        return (ret == sizeof(data)) ? 1 : 0; // Return 1 if all bytes were written, 0 otherwise
    }
    
    // Check if the ring buffer is full
    int is_ring_buffer_full(struct ring_buf *rb) {
        return ring_buf_space_get(rb) == 0 ? 1 : 0; // Return 1 if full, 0 if not
    }
    
    
    // Get a 32-bit integer from the ring buffer
    int get_from_ring_buffer(struct ring_buf *rb, uint32_t *data) {
        // Attempt to retrieve the first 32-bit value (4 bytes) from the ring buffer
        int ret = ring_buf_get(rb, (uint8_t *)data, sizeof(uint32_t));
    
        if (ret == sizeof(uint32_t)) {
            return 1; // Successfully retrieved the first value
        } else {
            return 0; // Failed to retrieve (buffer might be empty)
        }
    }
    
    // Check if the ring buffer is empty
    int is_ring_buffer_empty(struct ring_buf *rb) {
        return ring_buf_is_empty(rb) ? 1 : 0; // Return 1 if empty, 0 if not
    }
    
    
    //check if bluetooth is connected
    bool is_bt_connected(void)
    {
        return (current_conn != NULL);
    }
    
    static int64_t time_reset_reference = 0;
    
    void reset_time_reference(void) { //resets time value every time data collection begins
        time_reset_reference = k_uptime_get();
    }
    
    int16_t get_elapsed_time_ms(void) { //uses the reference time to get elapsed time since start of data collection
        int64_t current_time = k_uptime_get();
        int64_t elapsed_time = current_time - time_reset_reference;
    
        // Safely cast to uint16_t
        return (int16_t)(elapsed_time & 0xFFFF); // Wrap around at 16 bits
    }
    void check_ring_buffer_progress(size_t total_elements) {
        static uint32_t call_count = 0;  // Tracks how many times the function is called
    	uint16_t time;
        // Increment the call count
        call_count++;
    
        // Only print every 100 calls
        if (call_count % 100 == 0) {
            // Get the current used space in bytes
            size_t used_space = ring_buf_size_get(&my_ring_buffer);  
    
            // Convert used space (in bytes) to number of elements
            size_t used_elements = used_space / sizeof(uint32_t);  // Assuming uint32_t (4 bytes each)
    
            // Calculate the percentage of the buffer that is full
            uint8_t current_percent = (used_elements * 100) / total_elements;
    
            // Print the buffer percentage
            printf("Ring buffer is %d%% full\n", current_percent);
    
    		//time = get_elapsed_time_ms();
    		//printf("Elapsed time: %d\n", time);
        }
    }
    
    
    void get_LSM6DSO_data(const struct device *dev){ //this function gets and stores 6 values from the sensor
    	struct sensor_value x, y, z;
    	int16_t pack1;
    	int16_t pack2;
    	uint32_t data_to_store;
    	int full;
    
    	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);
    	
    	if (TESTING_MODE == 1){
    		printf("Raw gyro X: %d, %d\n", x.val1, x.val2);
        	printf("Raw gyro Y: %d, %d\n", y.val1, y.val2);
        	printf("Raw gyro Z: %d, %d\n", z.val1, z.val2);
    		k_sleep(K_MSEC(500));
    	}
    
    	//package x and y gyro, store
    	pack1 = (int16_t)(out_ev(&x)*(float)DECIMAL_SHIFT*(float)RAD_TO_DEGREE);
    	pack2 = (int16_t)(out_ev(&y)*(float)DECIMAL_SHIFT*(float)RAD_TO_DEGREE);
    	data_to_store = pack_16bit_to_32bit(pack1, pack2);
    	full = add_to_ring_buffer(&my_ring_buffer, data_to_store); 
    
    	if (TESTING_MODE == 1){
    		printf("X gyro: %d\n", pack1);
    		printf("Y gyro: %d\n", pack2);
    		k_sleep(K_MSEC(500));
    	}
    
    
    	//package z gyro and x accel, store
    	pack1 = (int16_t)(out_ev(&z)*(float)DECIMAL_SHIFT*(float)RAD_TO_DEGREE);
    	
    	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);
    
    	if (TESTING_MODE == 1){
    		printf("Raw accel X: %d, %d\n", x.val1, x.val2);
        	printf("Raw accel Y: %d, %d\n", y.val1, y.val2);
        	printf("Raw accel Z: %d, %d\n", z.val1, z.val2);
    		k_sleep(K_MSEC(500));
    	}
    
    	pack2 = (int16_t)(out_ev(&x)*(float)DECIMAL_SHIFT/(float)GRAVITY);
    	data_to_store = pack_16bit_to_32bit(pack1, pack2);
    	full = add_to_ring_buffer(&my_ring_buffer, data_to_store);
    
    	if (TESTING_MODE == 1){
    		printf("Z gyro: %d\n", pack1);
    		printf("X accel: %d\n", pack2);
    		k_sleep(K_MSEC(500));
    	}
    
    	//package y accel and z accel, store
    	pack1 = (int16_t)(out_ev(&y)*(float)DECIMAL_SHIFT/(float)GRAVITY);
    	pack2 = (int16_t)(out_ev(&z)*(float)DECIMAL_SHIFT/(float)GRAVITY);
    	data_to_store = pack_16bit_to_32bit(pack1, pack2);
    	full = add_to_ring_buffer(&my_ring_buffer, data_to_store);
    
    	if (TESTING_MODE == 1){
    		printf("Y accel: %d\n", pack1);
    		printf("Z accel: %d\n", pack2);
    		k_sleep(K_MSEC(500));
    	}
    
    }
    
    
    int16_t get_adc(const struct adc_dt_spec *adc_channel, struct adc_sequence *sequence, int16_t *buf) {
        int32_t val_mv32;
        int err;
    
        /* STEP 5 - Read a sample from the ADC */
        err = adc_read(adc_channel->dev, sequence);
        if (err < 0) {
            printf("Could not read (%d)\n", err);
            return -1; // Return error indicator
        }
    
        val_mv32 = (int32_t)*buf;
    
        /* STEP 6 - Convert raw value to mV */
        err = adc_raw_to_millivolts_dt(adc_channel, &val_mv32);
        if (err < 0) {
            printf("Could not convert to millivolts (%d)\n", err);
            return -1; // Return error indicator
        }
    
        //printf("%d\n", (int16_t)val_mv32);
        return (int16_t)val_mv32;
    }
    
    
    
    int gather_data_no_adc(const struct device *dev){
    	int16_t pack1;
    	int16_t pack2;
    	uint32_t data_to_store;
    	int full;
    
    	pack1 = 0; //0 value tracks breaks between samples
    	pack2 = get_elapsed_time_ms(); 
    	
    	data_to_store = pack_16bit_to_32bit(pack1, pack2);
    	full = add_to_ring_buffer(&my_ring_buffer, data_to_store); 
    
    	get_LSM6DSO_data(dev); //note that this function packs and stores values automatically
        
        if (is_ring_buffer_full(&my_ring_buffer)){
            return 0;
        }
        return 1;
    }   
    
    void send_data(struct ring_buf *rb) {
        uint32_t buffer_value;
        int full;
        int16_t extract1;
        int16_t extract2;
        int send_success = 0; // Flag to track if both sends succeed
    
        // Get data from the buffer
    	if (rb == &adc_ring_buffer){
    		printf("ADC Buffer size before sending: %d\n", ring_buf_size_get(rb));
    	}
        full = get_from_ring_buffer(rb, &buffer_value);
        extract1 = extract_first_16bit(buffer_value);
        extract2 = extract_second_16bit(buffer_value);
    	if (rb == &adc_ring_buffer){
    		if (full == 0) {
    			printf("Ring buffer was empty, skipping send.\n");
    			return;
    		}
    		printf("ADC Buffer size after sending: %d\n", ring_buf_size_get(rb));
    		
    	}
    	
    
        while (1) {
            // Check if Bluetooth is connected
            if (!is_bt_connected()) {
                printf("Bluetooth not connected. Waiting for connection...\n");
                k_sleep(K_MSEC(3000)); // Wait for 1 second before checking again
                continue; // Skip the sending attempt until Bluetooth is connected
            }
    
            // Attempt to send the first part of the data
            if (bt_nus_send(NULL, (uint8_t *)&extract1, sizeof(extract1))) {
                //printf("Failed to send first part of data over BLE connection\n");
            } else {
                send_success |= 1; // Mark the first send as successful
            }
    
            // Attempt to send the second part of the data
            if (bt_nus_send(NULL, (uint8_t *)&extract2, sizeof(extract2))) {
                printf("Failed to send data.. Please make sure your device is ready to receive BLE.\n");
            } else {
                send_success |= 2; // Mark the second send as successful
            }
    
            // If both parts were successfully sent, break the loop
            if (send_success == 3) {
                break;
            }
    
            // If any part failed, retry after waiting for 1 second
            k_sleep(K_MSEC(3000));
        }
    }
    
    int store_adc_data(int16_t adc1, int16_t adc2, int16_t adc3){
    	int16_t time;
    	uint32_t packed;
    	int err;
    
    	time = get_elapsed_time_ms();
    	printf("Time: %d\n",time);
    
    	packed = pack_16bit_to_32bit(time, adc1);
    	err = add_to_ring_buffer(&adc_ring_buffer, packed);
    	if (err == 0){
    		return err;
    	}
    	packed = pack_16bit_to_32bit(adc2, adc3);
    	err = add_to_ring_buffer(&adc_ring_buffer, packed);
    	return err;
    }
    
    
    //main function here, this is what calls other functions and runs code.
    //***************************************************************************************************
    int main(void){
    	ring_buf_reset(&my_ring_buffer);
    	ring_buf_reset(&adc_ring_buffer);
    	if (RING_BUFFER_TEST == 1){
    		//lines below this test ring buffer
    		int32_t data = 0;  // Start with an arbitrary value for data
    		int stored_count = 0;
    
    		printf("Starting test to store 16-bit integers in the ring buffer...\n");
    
    		// Keep adding 16-bit integers until the buffer is full
    		for (;;) {
    			// Put the data into the ring buffer
    			if (ring_buf_put(&my_ring_buffer, (uint8_t *)&data, sizeof(data)) == 0) {
    				// If the put operation fails (buffer full), break the loop
    				printf("Ring buffer is full. Stored %d values.\n", stored_count);
    				break;
    			}
    
    			// Increment the data value (just for testing, you can store arbitrary data)
    			data++;
    
    			// Increment the stored count
    			stored_count++;
    		}
    		printf("exited loop");
    	}
    
        //all necessary initialization here
    	int err = 0;
    	uint8_t last_printed_percent;
    	//uint16_t adc_data;
    	int sensor_ready = 1;
    	int16_t adc1;
    	int16_t adc2;
    	int16_t adc3;
    	//int test = 0;
    	//static int counter = 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) {
    		printf("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) {
    		printf("Advertising failed to start (err %d)", err);
    		return 0;
    	}
    
    	//add sensor code below
    	while (sensor_ready == 1){
    		const struct device *const dev = DEVICE_DT_GET_ONE(st_lsm6dso);
    
    		if (!device_is_ready(dev)) {
    			printf("I2C device not ready. Waiting for it to initialize...\n");
    			k_sleep(K_MSEC(3000));
    		} else {
    			printf("I2C device is ready!\n");
    			sensor_ready = 0;
    		}
    
    		sensor_ready = set_sampling_freq(dev);
    	}
    	const struct device *const dev = DEVICE_DT_GET_ONE(st_lsm6dso);
    	//sensor code above
    
    	//ADC initialization below ===================================================================================================================================
    	
    	// static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET(DT_PATH(zephyr_user));
    
    	// uint32_t count = 0;
    
    	// /* STEP 4.1 - Define a variable of type adc_sequence and a buffer of type uint16_t */
    	// int16_t buf;
    	// struct adc_sequence sequence = {
    	// 	.buffer = &buf,
    	// 	/* buffer size in bytes, not number of samples */
    	// 	.buffer_size = sizeof(buf),
    	// 	// Optional
    	// 	//.calibrate = true,
    	// };
    
    	// /* STEP 3.3 - validate that the ADC peripheral (SAADC) is ready */
    	// if (!adc_is_ready_dt(&adc_channel)) {
    	// 	printf("ADC controller devivce %s not ready", adc_channel.dev->name);
    	// 	return 0;
    	// }
    	// /* STEP 3.4 - Setup the ADC channel */
    	// err = adc_channel_setup_dt(&adc_channel);
    	// if (err < 0) {
    	// 	printf("Could not setup channel #%d (%d)", 0, err);
    	// 	return 0;
    	// }
    	// /* STEP 4.2 - Initialize the ADC sequence */
    	// err = adc_sequence_init_dt(&adc_channel, &sequence);
    	// if (err < 0) {
    	// 	printf("Could not initalize sequnce");
    	// 	return 0;
    	// }
    
    
    	/* Define three ADC channels */
    	static const struct adc_dt_spec adc_channel1 = ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0);
    	static const struct adc_dt_spec adc_channel2 = ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 1);
    	static const struct adc_dt_spec adc_channel3 = ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 2);
    
    
    
    
    	/* Define three buffers */
    	int16_t buf1, buf2, buf3;
    
    	/* Define three ADC sequences */
    	struct adc_sequence sequence1 = { .buffer = &buf1, .buffer_size = sizeof(buf1) };
    	struct adc_sequence sequence2 = { .buffer = &buf2, .buffer_size = sizeof(buf2) };
    	struct adc_sequence sequence3 = { .buffer = &buf3, .buffer_size = sizeof(buf3) };
    
    	uint32_t count = 0;
    
    	/* Check which ADC devices are ready and initialize only those */
    	if (adc_is_ready_dt(&adc_channel1)) {
    		err = adc_channel_setup_dt(&adc_channel1);
    		if (err < 0) {
    			printf("Could not setup channel 1 (%d)\n", err);
    		} else {
    			err = adc_sequence_init_dt(&adc_channel1, &sequence1);
    			if (err < 0) {
    				printf("Could not initialize sequence 1\n");
    			}
    		}
    	} else {
    		printf("ADC1 not ready\n");
    	}
    
    	if (adc_is_ready_dt(&adc_channel2)) {
    		err = adc_channel_setup_dt(&adc_channel2);
    		if (err < 0) {
    			printf("Could not setup channel 2 (%d)\n", err);
    		} else {
    			err = adc_sequence_init_dt(&adc_channel2, &sequence2);
    			if (err < 0) {
    				printf("Could not initialize sequence 2\n");
    			}
    		}
    	} else {
    		printf("ADC2 not ready\n");
    	}
    
    	if (adc_is_ready_dt(&adc_channel3)) {
    		err = adc_channel_setup_dt(&adc_channel3);
    		if (err < 0) {
    			printf("Could not setup channel 3 (%d)\n", err);
    		} else {
    			err = adc_sequence_init_dt(&adc_channel3, &sequence3);
    			if (err < 0) {
    				printf("Could not initialize sequence 3\n");
    			}
    		}
    	} else {
    		printf("ADC3 not ready\n");
    	}
    
    
    	//ADC initialization above ===================================================================================================================================
    
    
        if (ACTIVE_MODE == 1){ //if the device is in its fully active mode, recording and sending data over bluetooth
            int BLUETOOTH_MODE = 0; //assume storage starts empty
            for (;;){ //infinite loop
                if (BLUETOOTH_MODE == 0){ //function occurs if storage has just been emptied (or starts empty)
    				while (gather_data_no_adc(dev)){
                    //while (gather_data(dev, &adc_channel, &sequence, &buf)){ //goes until buffer is filled
    					check_ring_buffer_progress(BUFFER_SIZE);
    					k_sleep(K_MSEC(10));
    					static int counter = 0;
    					if (ADC_MODE == 1){
    						if (++counter % 100 == 0) { // Code to run every 100th call
    							adc1 = get_adc(&adc_channel1, &sequence1, &buf1);
    							adc2 = get_adc(&adc_channel2, &sequence2, &buf2);
    							adc3 = get_adc(&adc_channel3, &sequence3, &buf3);
    							err = store_adc_data(adc1, adc2, adc3);
    							if (err == 0){
    								printf("ADC data could not be stored.\n");
    							}
    						}
    					}
                    }
    				printf("Ring buffer is full. Ready to send data over Bluetooth.\n");
                    BLUETOOTH_MODE = 1;
                }
                    
    
                if (BLUETOOTH_MODE == 1){ //function occurs once storage is full and ready to send data 
                    while (!is_ring_buffer_empty(&my_ring_buffer)){ //this line starts a loop that sends all imu data over bluetooth
                        send_data(&my_ring_buffer);
    					check_ring_buffer_progress(BUFFER_SIZE);
                    }
    				printf("Ring buffer is empty. Sending ADC data now.\n");
    				while (!is_ring_buffer_empty(&adc_ring_buffer)){  //this line sends ADC data over bluetooth
                        send_data(&adc_ring_buffer);
                    }
                    BLUETOOTH_MODE = 0; //change back to data collection mode once all data is sent
    				reset_time_reference(); //reset time so we are ready to collect more data
    				last_printed_percent = 0; //reset buffer storage tracker
    				printf("All data sent. Ready to collect more data.\n");
                }
                
    
            }
        }
    
        if (ACTIVE_MODE == 0){ //this situation would occur if we didn't want the device to be active. Likely, this mode would be to test something, perhaps to print the data to console
            int ret;
    		/* Get the GPIO device binding */
    		const struct device *dev = DEVICE_DT_GET(GPIO_NODE);
    		if (!device_is_ready(dev)) {
    			printk("GPIO device not ready\n");
    			return;
    		}
    
    		/* Configure the GPIO pin as an output */
    		ret = gpio_pin_configure(dev, TEST_PIN, GPIO_OUTPUT_ACTIVE);
    		if (ret < 0) {
    			printk("Failed to configure pin %d\n", TEST_PIN);
    			return;
    		}
    
    		/* Set pin HIGH (VCC) */
    		ret = gpio_pin_set(dev, TEST_PIN, 1);
    		if (ret < 0) {
    			printk("Failed to set pin %d\n", TEST_PIN);
    			return;
    		}
    
    		printk("Pin %d set HIGH. Measure voltage with a voltmeter.\n", TEST_PIN);
    
    		while (1) {
    			k_sleep(K_FOREVER);  // Keep the program running indefinitely
    		}
        }
    }
        
    
    
    
    
    

Children
  • I think I've fixed it, or at least begun the process. I am now converting to strings before I send the data. Here my updates function that sends the data over bluetooth. 

    void send_data(struct ring_buf *rb) { 
        uint32_t buffer_value;
        int full;
        int16_t extract1;
        int16_t extract2;
        int send_success = 0; // Flag to track if both sends succeed
    
        // Get data from the buffer
        full = get_from_ring_buffer(rb, &buffer_value);
        extract1 = extract_first_16bit(buffer_value);
        extract2 = extract_second_16bit(buffer_value);
    
    	char buffer1[10];  // Enough space for "-32768" and null terminator
    	char buffer2[10];  // Enough space for "-32768" and null terminator
    	snprintf(buffer1, sizeof(buffer1), "%d\n", extract1);
    	snprintf(buffer2, sizeof(buffer2), "%d\n", extract2);
    
    
        while (1) {
            // Check if Bluetooth is connected
            if (!is_bt_connected()) {
                printf("Bluetooth not connected. Waiting for connection...\n");
                k_sleep(K_MSEC(3000)); // Wait for 1 second before checking again
                continue; // Skip the sending attempt until Bluetooth is connected
            }
    
            // Attempt to send the first part of the data
            //if (bt_nus_send(NULL, (uint8_t *)&extract1, sizeof(extract1))) {
    		if (bt_nus_send(NULL, (uint8_t *)buffer1, strlen(buffer1))){
                //printf("Failed to send first part of data over BLE connection\n");
            } else {
                send_success |= 1; // Mark the first send as successful
            }
    
            // Attempt to send the second part of the data
            //if (bt_nus_send(NULL, (uint8_t *)&extract2, sizeof(extract2))) {
    		if (bt_nus_send(NULL, (uint8_t *)buffer1, strlen(buffer1))){
                printf("Failed to send data.. Please make sure your device is ready to receive BLE.\n");
            } else {
                send_success |= 2; // Mark the second send as successful
            }
    
            // If both parts were successfully sent, break the loop
            if (send_success == 3) {
                break;
            }
    
            // If any part failed, retry after waiting for 1 second
            k_sleep(K_MSEC(3000));
        }
    }
    
    int store_adc_data(int16_t adc1, int16_t adc2, int16_t adc3){ //this function takes three inputs, which it stores as well as the time at which the function is called
    	int16_t time;
    	uint32_t packed;
    	int err;
    
    	time = get_elapsed_time_ms();
    	printf("Time: %d\n",time);
    
    	packed = pack_16bit_to_32bit(time, adc1);
    	err = add_to_ring_buffer(&adc_ring_buffer, packed);
    	if (err == 0){
    		return err;
    	}
    	packed = pack_16bit_to_32bit(adc2, adc3);
    	err = add_to_ring_buffer(&adc_ring_buffer, packed);
    	return err;
    }

  • I did have an error just relating to sending buffer1 twice instead of buffer 1 and then buffer2, but I've fixed that now.

  • From your answers it looks to me like you were able to fix the issue.
    Good job!

    Let me know if this is not the case, or if you got any questions

Related