Zephyr TLM Eddystone Beacon

Hi There,

New to the Forum and I have started to use the Zephyr RTOS in Visual Studio Code:

I am trying to get an Eddystone Beacon based off the nRF528323 and I am transmitting the URL Frame and the TLM frame.

I have the URL Eddystone Beacon working sending Data to the nRF Connect AP but somehow I can't seem to see the TLM Data and i just need to display the battery data. I have tried this plenty of different ways but i simply cannot see the TLM data. Any help would be great appreciated!, below is the code I am using and the prj.conf file as well as the overlay file

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <drivers/adc.h>
#include <hal/nrf_saadc.h>
#include <drivers/gpio.h>
#include <zephyr.h>
#include <nrf.h>
#include <logging/log.h>
#include <sys/byteorder.h>


#define ADC_RESOLUTION 10
#define ADC_GAIN ADC_GAIN_1_4
#define BATTERY_CHANNEL_ID 0
#define BATTERY_INPUT NRF_SAADC_INPUT_AIN0
#define LED_PORT DT_LABEL(DT_NODELABEL(gpio0)) // Assuming LEDs are connected to GPIO0
#define LED_PIN 15 // Pin number for the first LED
#define LED_PIN_2 18 // Pin number for the second LED

static uint16_t adc_sample_buffer;
static const struct device *led_dev;
static const struct device *adc_dev;

static const struct adc_channel_cfg my_channel_cfg = {
    .gain = ADC_GAIN,
    .reference = ADC_REF_INTERNAL,
    .acquisition_time = ADC_ACQ_TIME_DEFAULT,
    .channel_id = BATTERY_CHANNEL_ID,
    .input_positive = BATTERY_INPUT,
};

static const struct bt_data ad[] = {
    BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0xAA, 0xFE), // Eddystone service UUID
    BT_DATA_BYTES(BT_DATA_SVC_DATA16,
    0xAA, 0xFE,   // Eddystone service UUID
    0x10,         // Eddystone-URL frame type
    0xF8,         // Calibrated Tx power at 0m
    0x01,         // URL Scheme 'http://www.'
    's', 't', 'r', 'a', 't', 'a', '.', 'c', 'o', 'm'), // URL

};

static int led_init(void) {
    led_dev = device_get_binding(LED_PORT);
    if (!led_dev) {
        printk("LED device not found\n");
        return -ENODEV;
    }

    int err = gpio_pin_configure(led_dev, LED_PIN, GPIO_OUTPUT_ACTIVE);
    if (err) {
        printk("Failed to configure LED pin (err %d)\n", err);
        return err;
    }

    err = gpio_pin_configure(led_dev, LED_PIN_2, GPIO_OUTPUT_ACTIVE);
    if (err) {
        printk("Failed to configure LED pin 2 (err %d)\n", err);
        return err;
    }

    return 0;
}

static int adc_init(void) {
    adc_dev = device_get_binding(DT_LABEL(DT_NODELABEL(adc)));
    if (!adc_dev) {
        printk("ADC device not found\n");
        return -ENODEV;
    }

    int err = adc_channel_setup(adc_dev, &my_channel_cfg);
    if (err) {
        printk("Failed to set up ADC channel (err %d)\n", err);
        return err;
    }

    return 0;
}

static uint16_t read_battery_voltage(void) {
    if (!adc_dev) {
        printk("ADC device not found\n");
        return 0;
    }

    const struct adc_sequence sequence = {
        .channels = BIT(my_channel_cfg.channel_id),
        .buffer = &adc_sample_buffer,
        .buffer_size = sizeof(adc_sample_buffer),
        .resolution = ADC_RESOLUTION,
    };

    int err = adc_read(adc_dev, &sequence);
    if (err) {
        printk("Failed to read ADC (err %d)\n", err);
        return 0;
    }

    return (adc_sample_buffer * 3300) / (1 << ADC_RESOLUTION)* (2.979 / 3.296);
   // return (adc_sample_buffer * 3300 * 4) / (1 << ADC_RESOLUTION); // Convert to millivolts

}

static void update_tlm_data(uint16_t battery_voltage) {
    uint8_t tlm_data[] = {
        // Eddystone TLM header
        0xAA, 0xFE,                   // Eddystone UUID
        0x20,                         // TLM frame type
        0x00,                         // Version
        0x00, 0x00,                   // Battery voltage (placeholder)
        0x00, 0x00,                   // Temperature (placeholder)
        0x00, 0x00, 0x00, 0x00,       // PDU count (placeholder)
        0x00, 0x00, 0x00, 0x00        // Time since boot (placeholder)
    };

    // Convert battery voltage to fixed point format and place in advertising packet
    sys_put_be16(battery_voltage, &tlm_data[4]);

    // Update the advertising data
    const struct bt_data ad_tlm[] = {
        BT_DATA(BT_DATA_MANUFACTURER_DATA, tlm_data, sizeof(tlm_data)),
    };

    int err = bt_le_adv_update_data(ad_tlm, ARRAY_SIZE(ad_tlm), NULL, 0);
    if (err) {
        printk("Failed to update advertising data (err %d)\n", err);
    }
}

void print_advertising_data(const struct bt_data *ad, size_t ad_len) {
    printk("Advertising Data:\n");
    for (size_t i = 0; i < ad_len; i++) {
        printk("Type: %d, Data: ", ad[i].type);
        for (size_t j = 0; j < ad[i].data_len; j++) {
            printk("%02X ", ad[i].data[j]);
        }
        printk("\n");
    }
}



void bt_ready(int err) {
    if (err) {
        printk("Bluetooth init failed (err %d)\n", err);
        return;
    }

    printk("Bluetooth initialized\n");

    if (led_init()) {
        printk("LED init failed\n");
        return;
    }

    if (adc_init()) {
        printk("ADC init failed\n");
        return;
    }

    const struct bt_le_adv_param adv_params = {
        .options = BT_LE_ADV_OPT_CONNECTABLE,
        .interval_min = BT_GAP_ADV_FAST_INT_MIN_2,
        .interval_max = BT_GAP_ADV_FAST_INT_MAX_2,
    };


    // Print advertising data for debugging
    print_advertising_data(ad, ARRAY_SIZE(ad));

    // Start advertising
    err = bt_le_adv_start(&adv_params, ad, ARRAY_SIZE(ad), NULL, 0);
    if (err) {
        printk("Failed to start advertising (err %d)\n", err);
        return;
    }

    printk("Advertising started\n");

    while (1) {
        // Read battery voltage and update TLM data
        uint16_t battery_voltage = read_battery_voltage();
        printk("Battery Voltage: %d mV\n", battery_voltage);
        update_tlm_data(battery_voltage);

        // Stop advertising
        bt_le_adv_stop();
        printk("Advertising stopped\n");
        
        // Wait a bit to ensure advertising has stopped
        k_sleep(K_SECONDS(1));

        // Set LED and start advertising Eddystone-URL
        gpio_pin_set(led_dev, LED_PIN, 1); // Turn on LED
        err = bt_le_adv_start(&adv_params, ad, ARRAY_SIZE(ad), NULL, 0);
        if (err) {
            printk("Advertising Eddystone-URL failed to start (err %d)\n", err);
            gpio_pin_set(led_dev, LED_PIN, 0); // Turn off LED if advertising fails
        } else {
            printk("Eddystone URL Advertising started\n");
            k_sleep(K_SECONDS(10)); // Adjust as needed
        }

        // Ensure LED is turned off
        gpio_pin_set(led_dev, LED_PIN, 0);
        
        // Wait a bit before next iteration
        k_sleep(K_SECONDS(10));
    }
}


void main(void) {
    int err = bt_enable(bt_ready);
    if (err) {
        printk("Bluetooth init failed (err %d)\n", err);
        return;
    }
}
  

Below is my prj.conf file:

# General Bluetooth options
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_BROADCASTER=y
CONFIG_BT_DEVICE_NAME_MAX=30
CONFIG_BT_DEVICE_NAME="GAVO"
CONFIG_ADC=y
CONFIG_SERIAL=y
CONFIG_UART_CONSOLE=y
CONFIG_LOG=y
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
CONFIG_BT_RX_STACK_SIZE=2048
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
CONFIG_BT_SETTINGS=y
CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_GATT_SERVICE_CHANGED=y

# Security Manager Protocol (SMP) for Bluetooth
CONFIG_BT_SMP=y  # Ensure this is set if BT_L2CAP_DYNAMIC_CHANNEL is used

# Logging options
CONFIG_LOG=y

# Bluetooth stack sizes
CONFIG_BT_RX_STACK_SIZE=2048
CONFIG_BT_HCI_TX_STACK_SIZE_WITH_PROMPT=y

and my overlay file is also below:

&uart0 {
    current-speed = <115200>;
    status = "okay";
    tx-pin = <6>;
    rx-pin = <8>;
};

Related