GATT characteristic value not maintained if initial value is 0

I am writing a custom BLE profile library and when I add new characteristics that are read write parameters and initialize the value with 0 the code seems to lose context and whenever there is a BLE update the value goes to 0xff.  When i initialize it with any other value it maintains its value.  I have tried the volatile keyword and that has no effect.  If it is read only then the value is also maintained.  Also if I initialize it to 0 and use a function to updated it at boot the value is updated with the read but if any other BLE event happens the value goes to 0xff. This seems very strange like some inappropriate compiler optimization.

I am using 3.0.2 version of the sdk. I have also tried using the _user_data field with a pointer to my variable and that does not work either.  It does not matter if the variable is marked static.

static uint8_t gSenseVal = 0;

BT_GATT_CHARACTERISTIC(&BT_UUID_SENSE.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE ,
                                              BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, read_sense, write_sense, NULL));
Parents Reply Children
  • /*
     *   Copyright (c) 2024 Rogue Solutions LLC
     *   All rights reserved.
     */
    
    #include <errno.h>
    #include <zephyr/init.h>
    #include <zephyr/sys/__assert.h>
    #include <stdbool.h>
    #include <zephyr/types.h>
    
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/bluetooth/uuid.h>
    
    #include "bleLTECwldm.h"
    #include "btLTEC.h"
    
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(bt_wldm, LOG_LEVEL_INF);
    
    /*******************************************************************************
    * Defines 
    *******************************************************************************/
    
    
    
    /*******************************************************************************
    * Private Declerations 
    *******************************************************************************/
    static int wldm_init(void);
    
    static void hit_ccc_cfg_changed(const struct bt_gatt_attr* attr, uint16_t value);
    
    static void indicate_cb(struct bt_conn* conn, struct bt_gatt_indicate_params* params, unsigned char err);
    
    static ssize_t read_hit(struct bt_conn* conn, const struct bt_gatt_attr* attr, void* buf, uint16_t len, uint16_t offset);
    
    static ssize_t read_sense(struct bt_conn* conn, const struct bt_gatt_attr* attr, void* buf, uint16_t len, uint16_t offset);
    
    static ssize_t write_sense(struct bt_conn* conn, const struct bt_gatt_attr* attr, const void* buf, uint16_t len, uint16_t offset,
                               uint8_t flags);
    
    /*******************************************************************************
    * Private Variables 
    *******************************************************************************/
    
    /* Custom Service Variables */
    static struct bt_uuid_128 BT_UUID_MILES       = LTEC_UUID_INIT(MILES_UUID_SERVICE);
    static struct bt_uuid_128 BT_UUID_MILES_HIT   = LTEC_UUID_INIT(MILES_UUID_HIT_CHAR);
    static struct bt_uuid_128 BT_UUID_MILES_SENSE   = LTEC_UUID_INIT(MILES_UUID_SENSITIVITY_CHAR);
    
    /* Indication parameters for MILES HIT char */
    static struct bt_gatt_indicate_params g_hit_char_ind_params = {0};
    static volatile bool g_b_is_hit_ind_enabled                 = false;
    
    static struct ble_wldm_event g_miles_event = {
        .u16Pid         = 0,
        .u8BasicCode    = 0,
        .u8AmmoCode     = 0,
        .u8NumWords     = 0,
        .u8DetectorType = 0,
    };
    
    static struct wldmSenseWork gSenseWork = {0};
    
    static uint8_t gSenseVal = 0; // Default to 127, which is the max sensitivity.
    /***************************************************************************************************************************************
    Profile Definition
    ***************************************************************************************************************************************/
    
    BT_GATT_SERVICE_DEFINE(
        wldm, BT_GATT_PRIMARY_SERVICE(&BT_UUID_MILES.uuid),
        BT_GATT_CHARACTERISTIC(&BT_UUID_MILES_HIT.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_INDICATE,
                               BT_GATT_PERM_READ, read_hit, NULL, &g_miles_event), 
        BT_GATT_CCC(hit_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
        BT_GATT_CHARACTERISTIC(&BT_UUID_MILES_SENSE.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE ,
                                                  BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, read_sense, write_sense, NULL));
    
    /*******************************************************************************
    * Public Functions 
    *******************************************************************************/
    
    int bt_wldm_report_hit(uint16_t uPid, uint8_t u8BasicCode, uint8_t u8AmmoCode, uint8_t u8NumWords, uint8_t u8DetectorType)
    {
        int rc;
        g_miles_event.u16Pid         = uPid;
        g_miles_event.u8BasicCode    = u8BasicCode;
        g_miles_event.u8AmmoCode     = u8AmmoCode;
        g_miles_event.u8NumWords     = u8NumWords;
        g_miles_event.u8DetectorType = u8DetectorType;
    
    
        // Don't try to indicate if indications haven't been enabled.
        if (!g_b_is_hit_ind_enabled)
        {
            LOG_DBG("Not indicating because indications are not enabled");
            return 0;
        }
    
        // ind_params.attr MUST be set to the GATT service attribute if the characteristic attribute
        // being indicated has a shared UUID (e.g. LTEC_PID_CHAR_UUID). This tells the BT stack where to
        // start looking for the characteristic attribute with the uuid ind_params.uuid value.
        g_hit_char_ind_params.attr = attr_wldm;
        g_hit_char_ind_params.uuid = &BT_UUID_MILES_HIT;
        g_hit_char_ind_params.func = indicate_cb;
        g_hit_char_ind_params.data = &g_miles_event;
        g_hit_char_ind_params.len  = sizeof(g_miles_event);
    
        
        rc = bt_gatt_indicate(NULL, &g_hit_char_ind_params);
        if (rc)
        {
            LOG_WRN("Hit Indicate failed: %d", rc);
        }
    
        return rc == -ENOTCONN ? 0 : rc;
    
    	// return rc == -ENOTCONN ? 0 : rc;
    }
    
    int bt_wldm_SetSensitivtyHandler(k_work_handler_t handler)
    {
        k_work_init(&gSenseWork.work, handler);
    }
    
    
    int bt_wldm_SetSensitivty(uint8_t uLvl)
    {
        gSenseVal = uLvl;
    }
    
    /*******************************************************************************
    * Private Functions  
    *******************************************************************************/
    
    static int wldm_init(void)
    {
        LOG_DBG("BT WLDM Init");
        gSenseVal =0;
    	return 0;
    }
    
    static void hit_ccc_cfg_changed(const struct bt_gatt_attr* attr, uint16_t value)
    {
        ARG_UNUSED(attr);
    
        bool ind_enabled = (value == BT_GATT_CCC_INDICATE);
    
        g_b_is_hit_ind_enabled = ind_enabled;
    
        LOG_INF("MILES Hit Indication %s", ind_enabled ? "enabled" : "disabled");
    }
    
    static void indicate_cb(struct bt_conn* conn, struct bt_gatt_indicate_params* params, unsigned char err)
    {
        LOG_INF("MILES Hit Indication %s", err != 0U ? "fail" : "success");
        // todo handle
    }
    
    /* BLE Read Functions */
    
    static ssize_t read_hit(struct bt_conn* conn, const struct bt_gatt_attr* attr, void* buf, uint16_t len, uint16_t offset)
    {
        return bt_gatt_attr_read(conn, attr, buf, len, offset, &g_miles_event, sizeof(g_miles_event));
    }
    
    static ssize_t read_sense(struct bt_conn* conn, const struct bt_gatt_attr* attr, void* buf, uint16_t len, uint16_t offset)
    {
        // uint8_t status = gSenseVal;
    
        return bt_gatt_attr_read(conn, attr, buf, len, offset, &gSenseVal, sizeof(gSenseVal));
    }
    
    /* BLE Write Functions */
    
    static ssize_t write_sense(struct bt_conn* conn, const struct bt_gatt_attr* attr, const void* buf, uint16_t len, uint16_t offset,
                               uint8_t flags)
    {
        struct net_buf_simple data;
    
        if (flags & BT_GATT_WRITE_FLAG_PREPARE)
        {
            return 0;
        }
    
        if (offset + len > sizeof(gSenseVal))
        {
            return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
        }
    
        net_buf_simple_init_with_data(&data, (void *)buf, len);
    	gSenseVal = net_buf_simple_pull_u8(&data);
    
        LOG_INF("Sense Write %x", gSenseVal);
        /* Submit Reset Work */
        if(gSenseWork.work.handler != 0)
        {
            gSenseWork.uLevel = gSenseVal;
            k_work_submit(&gSenseWork);
        }
        return len;
    }
    
    
    SYS_INIT(wldm_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);

  • Read Before Indicate

    Read After

    If gSenseVal is 0 in the definition this happens if it is anything else the value is maintained i.e. 

    static uint8_t gSenseVal = 0xff;
Related