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

Parents Reply
  • Thanks. Initially, I verified it with the application(nRF Connect 4.26.1) for Android. Have you tried it?

    I just tested it on Apple phone and confirmed it's almost same as yours. I am internally checking why this is not parsed and will keep you updated soon.

    However, now that the example is already there, if the purpose is to see if it's TLM type and validify of the data on your beacon, you can verify it on Android. 

    If not, could you tell me what we can assist for you?

    //Brian

Children
Related