This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Hal ADC support for CC2541ST ( SensorTag ) (for use as battery monitor with the battery service )

Other Parts Discussed in Thread: CC2540

Hi.

The reason behind this post is that I want to measure the battery level similar to this post:

http://e2e.ti.com/support/low_power_rf/f/538/t/234130.aspx

There you state that it might be available in the (upcomming) 1.3 release, which was in december 2012. Now half a year later, I sit with the same problem.

Now I tried adding the battery service, yet I can't get it to link due to these errors:

  • Error[e46]: Undefined external "HalAdcSetReference::?relay" referred in battservice
  • Error[e46]: Undefined external "HalAdcRead::?relay" referred in battservice

I discovered that there is no hal_adc.c in 

\Components\hal\target\CC2541ST

of the ble1.3.x stack. (neither in 1.3, 1.3.1 or 1.3.2). Only adding the header in the \hal\include folder, doesn't solve the problem.

Is there any way I can measure the battery level on the SensorTag ? I need it for a business application where it is set in a hermetically (airtight) container, so its critical to know its battery level without taking the battery out.

Thanks in advance :)

  • Hi Jesper,

    Unfortunately ADC readings has not been implemented in the current 1.3.2 version of the SensorTag.

    I would suggest to re-use and/or modify the hal_adc.c used for the CC2540 (Components\hal\target\CC2540EB) and create your own battery service in a similar way that the other sensors are implemented (minus the SPI functionality).

    Regards,
    Svend 

  • Has anyone made this modification to the firmware that can share?


    I'm very surprised this feature isn't implemented by default. For any proximity use, knowing the battery level is a must for accurate readings. I have several SensorTags with different battery levels (known by measuring the batteries externally) and I get vastly different RSSI readings between them. Accurate battery level readings are required to calibrate the values on the fly.

  • Hi, Chris:

    I've figured out how to add sample service/profile into current project (ex: SensorTag), most of them were implemented in battservice.c from BLE-CC254x-1.3.2. The following steps is my modification for reference, you can also discover more details in project HIDAdvRemote or HeartRate.

    Step by step as following instruction:

    0. Add include folder in project Options

    $PROJ_DIR$\..\..\Profiles\Batt
    $PROJ_DIR$\..\..\Profiles\HIDDev


    1. Header

    #include "battservice.h"


    2. CONSTANTS

    // Battery level is critical when it is less than this %
    #define DEFAULT_BATT_CRITICAL_LEVEL 6

    // Battery measurement period in ms
    #define DEFAULT_BATT_PERIOD 15000


    3. Add service UUID in advertData[] 

    0x03,
    GAP_ADTYPE_16BIT_MORE,
    LO_UINT16(BATT_SERVICE_UUID),
    HI_UINT16(BATT_SERVICE_UUID)


    4. Add definition in LOCAL FUNCTIONS

    static void stBattPeriodicTask( void );
    static void stBattCB(uint8 event);


    5. Initialize GATT attributes and setup Characteristic Values in init()

    // Setup Battery Characteristic Values
    {
         uint8 critical = DEFAULT_BATT_CRITICAL_LEVEL;
         Batt_SetParameter( BATT_PARAM_CRITICAL_LEVEL, sizeof (uint8 ), &critical );
    }

    // Simple GATT Profile
    Batt_AddService( ); 

    // Register for Battery service callback
    Batt_Register ( stBattCB );


    6. Register callback with Batt_Register (in battservice.c)

    Batt_Register ( stBattCB );


    7. Add event handler in SensorTag_ProcessEvent()

    if ( events & BATT_PERIODIC_EVT )
    {
         // Perform periodic battery task
         stBattPeriodicTask();

         return (events ^ BATT_PERIODIC_EVT);
    }


    8. Create task of CallBack function

    //Callback function for battery service.

    static void stBattCB(uint8 event)
    {
         if (event == BATT_LEVEL_NOTI_ENABLED)
         {
              // if connected start periodic measurement
              if (gapProfileState == GAPROLE_CONNECTED)
              {
                   osal_start_timerEx( sensorTag_TaskID, BATT_PERIODIC_EVT, DEFAULT_BATT_PERIOD );
              } 
         }
         else if (event == BATT_LEVEL_NOTI_DISABLED)
         {
              // stop periodic measurement
              osal_stop_timerEx( sensorTag_TaskID, BATT_PERIODIC_EVT );
         }
    }

    // Perform a periodic task for battery measurement.
    static void stBattPeriodicTask( void )
    {
         if (gapProfileState == GAPROLE_CONNECTED)
         {
              // perform battery level check
              Batt_MeasLevel( );

              // Restart timer
              osal_start_timerEx( sensorTag_TaskID, BATT_PERIODIC_EVT, DEFAULT_BATT_PERIOD );
         }
    }

  • I have done something similar but at a battery level > 3.1V it is only returning a % of about 20% which is what you are showing above.  Have you been able to get it to display the full 100% when the battery is fully charged?

  • Hi,

    Something misses in ADC configuration. As said above by Tim, battery percent ~20%.

    I've powered sensortag throw a variable voltage =>  0x0129 <= ADC value <= 0x012b with a voltage from 2.7 to 3.3v

    I will do further investigations, If someone has solutions before, don't hesitate to share!

    I've tried two different codes to read ADC

    HalAdcSetReference( HAL_ADC_REF_125V );
    adc = HalAdcRead( battServiceAdcCh, HAL_ADC_RESOLUTION_10 );

    Anf :

    HalAdcSetReference(HAL_ADC_REF_125V);
    adc = HalAdcRead(HAL_ADC_CHN_VDD3, HAL_ADC_RESOLUTION_12);
    HalAdcSetReference(HAL_ADC_REF_AVDD);

    Lahorde.

  • Hi,

    It may be that the DC/DC converter is turned on so that AVDD measures in at 2.1V.

    Try to disable this, measure, then re-enable. You can do this via direct pin-manipulation DCDC_SBIT = 0; and = 1. This bit-macro is really P0_7, and is not available in other projects.

    You may need to disable the DC/DC some ~100µs in advance to allow the voltage to settle. I'd suggest perhaps an OSAL event with 1ms timeout before the actual measurement just to make it easier.

    BR,
    Aslak

  • Thanks for the suggesting.  Tried it.  No joy.

    One thing I do notice is that it looks like it comes up with a reasonable number for the first couple of battery reads (i.e. something close to 100 for a fresh battery), this is regardless of the DCDC_SBIT,  before it drops down to something that looks like 1/4 of what would be a valid reading.

    I don't really care if it is at 1/4 just don't know if I believe the value.  

    Tim

  • Many thanks @Aslak

    With this code :

    // Configure ADC and perform a read
    DCDC_SBIT = 0; 
    HalAdcSetReference( HAL_ADC_REF_125V );
    adc = HalAdcRead( battServiceAdcCh, HAL_ADC_RESOLUTION_10 );
    DCDC_SBIT = 1;

    I just tried without adding wait... and I see battery changes when modifying voltage. 

    @Tim how do you get handle value? To be sure about your code get handle value with linuxBT 4.0 tools, each time a read handle is asked, an adc read is done :

    ~ $ sudo hcitool lecc 90:59:AF:0B:87:5D
    Connection handle 64
    $ sudo gatttool -I -b 90:59:AF:0B:87:5D
    [ ][90:59:AF:0B:87:5D][LE]> connect
    [CON][90:59:AF:0B:87:5D][LE]> char-read-hnd 0x82
    [CON][90:59:AF:0B:87:5D][LE]> 
    Characteristic value/descriptor: 06 
    [CON][90:59:AF:0B:87:5D][LE]>

    For Info voltage for handle value @6% is 2.02V

    Lahorde

  • Hello,

       A friend of mine have made it work for iOS as shown in the picture, able to display battery level of the sensor tag device in his iPhone.  Now, I want to display the battery level in my Android device.

    I have very little experience in BLE, however, I've started to add the few lines in SensorTag.java android source code( from TI ).

         SensorTag.java

          // I added this 

          UUID_BAT_SERV = fromString("f000180f-0451-4000-b000-000000000000"),
          UUID_BAT_DATA = fromString("f0002a19-0451-4000-b000-000000000000"),
          UUID_BAT_CONF = fromString("f0000030-0451-4000-b000-000000000000");

    The red texts are defined in projects/ble/include/gap_profile_uuid.h

    #define BATT_LEVEL_UUID                 0x2A19  // Battery Level

    #define BATT_SERV_UUID                  0x180F  // Battery Service

    I am not sure if these UUID are CORRECT, just by guessing.

    I then inserted a log message in DeviceView.java

      public void onCharacteristicChanged(String uuidStr, byte[] rawValue) {

       if (uuidStr.equals(UUID_BAT_DATA.toString())) {
            Log.d(TAG, "Battery Data: " + rawValue);
          }
      }

    and compiled and ran the app and it didn't work. No message in LogCat showing Battery Data.

    Please help? how do I get this to work in Android?

  • Hello,

    I have implemented the changes as described in the response of Stanley. However, when I query the UUID = 0x2A19, it always returns a 0. Any hints how I get there a reasonable value? Can someone help me with the problem?

    I would be very thankful for any advice!
  • Hey Lahorde,

    I got your oad-image from https://github.com/Lahorde/sensor_tag_firmware/tree/master/gyro_period

    As I see your battery-service works fine. Could you perhaps post what you changed in your project/files that the service works? Maybe you can share your sensortag.h and battservice.h files with your changes to help me understand?

    (Which value have "BATT_DATA_LEN" and "BATT_PERIODIC_EVT"?

    Thanks for your help in advance!

  • Hi,

    Sorry for the delay.
    Here are battery service files

    6431.battservice.c
    /**************************************************************************************************
      Filename:       battservice.c
      Revised:        $Date $
      Revision:       $Revision $
    
      Description:    This file contains the Battery service.
    
      Copyright 2012-2013 Texas Instruments Incorporated. All rights reserved.
    
      IMPORTANT: Your use of this Software is limited to those specific rights
      granted under the terms of a software license agreement between the user
      who downloaded the software, his/her employer (which must be your employer)
      and Texas Instruments Incorporated (the "License").  You may not use this
      Software unless you agree to abide by the terms of the License. The License
      limits your use, and you acknowledge, that the Software may not be modified,
      copied or distributed unless embedded on a Texas Instruments microcontroller
      or used solely and exclusively in conjunction with a Texas Instruments radio
      frequency transceiver, which is integrated into your product.  Other than for
      the foregoing purpose, you may not use, reproduce, copy, prepare derivative
      works of, modify, distribute, perform, display or sell this Software and/or
      its documentation for any purpose.
    
      YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
      PROVIDED �AS IS� WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
      INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
      NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
      TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
      NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
      LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
      INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
      OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
      OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
      (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
    
      Should you have any questions regarding your right to use this Software,
      contact Texas Instruments Incorporated at www.TI.com.
    **************************************************************************************************/
    
    /*********************************************************************
     * INCLUDES
     */
    #include "bcomdef.h"
    #include "OSAL.h"
    #include "hal_adc.h"
    #include "linkdb.h"
    #include "att.h"
    #include "gatt.h"
    #include "gatt_uuid.h"
    #include "gatt_profile_uuid.h"
    #include "gattservapp.h"
    #include "hiddev.h"
    
    #include "battservice.h"
    #include "st_util.h"
    /*********************************************************************
     * MACROS
     */
    
    /*********************************************************************
     * CONSTANTS
     */
    
    // ADC voltage levels
    #define BATT_ADC_LEVEL_3V           409
    #define BATT_ADC_LEVEL_2V           273
    
    #define BATT_LEVEL_VALUE_IDX        2 // Position of battery level in attribute array
    #define BATT_LEVEL_VALUE_CCCD_IDX   3 // Position of battery level CCCD in attribute array
    
    /*********************************************************************
     * TYPEDEFS
     */
    
    /*********************************************************************
     * GLOBAL VARIABLES
     */
    // Battery service
    CONST uint8 battServUUID[ATT_BT_UUID_SIZE] =
    {
      LO_UINT16(BATT_SERV_UUID), HI_UINT16(BATT_SERV_UUID)
    };
    
    // Battery level characteristic
    CONST uint8 battLevelUUID[ATT_BT_UUID_SIZE] =
    {
      LO_UINT16(BATT_LEVEL_UUID), HI_UINT16(BATT_LEVEL_UUID)
    };
    
    /*********************************************************************
     * EXTERNAL VARIABLES
     */
    
    /*********************************************************************
     * EXTERNAL FUNCTIONS
     */
    
    /*********************************************************************
     * LOCAL VARIABLES
     */
    
    // Application callback
    static battServiceCB_t battServiceCB;
    
    // Measurement setup callback
    static battServiceSetupCB_t battServiceSetupCB = NULL;
    
    // Measurement teardown callback
    static battServiceTeardownCB_t battServiceTeardownCB = NULL;
    
    // Measurement calculation callback
    static battServiceCalcCB_t battServiceCalcCB = NULL;
    
    static uint16 battMinLevel = BATT_ADC_LEVEL_2V; // For VDD/3 measurements
    static uint16 battMaxLevel = BATT_ADC_LEVEL_3V; // For VDD/3 measurements
    
    // Critical battery level setting
    static uint8 battCriticalLevel;
    
    // ADC channel to be used for reading
    static uint8 battServiceAdcCh = HAL_ADC_CHANNEL_VDD;
    
    /*********************************************************************
     * Profile Attributes - variables
     */
    
    // Battery Service attribute
    static CONST gattAttrType_t battService = { ATT_BT_UUID_SIZE, battServUUID };
    
    // Battery level characteristic
    static uint8 battLevelProps = GATT_PROP_READ | GATT_PROP_NOTIFY;
    static uint8 battLevel = 100;
    static gattCharCfg_t battLevelClientCharCfg[GATT_MAX_NUM_CONN];
    
    // HID Report Reference characteristic descriptor, battery level
    static uint8 hidReportRefBattLevel[HID_REPORT_REF_LEN] =
                 { HID_RPT_ID_BATT_LEVEL_IN, HID_REPORT_TYPE_INPUT };
    
    /*********************************************************************
     * Profile Attributes - Table
     */
    
    static gattAttribute_t battAttrTbl[] =
    {
      // Battery Service
      {
        { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
        GATT_PERMIT_READ,                         /* permissions */
        0,                                        /* handle */
        (uint8 *)&battService                     /* pValue */
      },
    
        // Battery Level Declaration
        {
          { ATT_BT_UUID_SIZE, characterUUID },
          GATT_PERMIT_READ,
          0,
          &battLevelProps
        },
    
          // Battery Level Value
          {
            { ATT_BT_UUID_SIZE, battLevelUUID },
            GATT_PERMIT_READ,
            0,
            &battLevel
          },
    
          // Battery Level Client Characteristic Configuration
          {
            { ATT_BT_UUID_SIZE, clientCharCfgUUID },
            GATT_PERMIT_READ | GATT_PERMIT_WRITE,
            0,
            (uint8 *) &battLevelClientCharCfg
          },
    
          // HID Report Reference characteristic descriptor, batter level input
          {
            { ATT_BT_UUID_SIZE, reportRefUUID },
            GATT_PERMIT_READ,
            0,
            hidReportRefBattLevel
          }
    };
    
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    static uint8 battReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen );
    static bStatus_t battWriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                      uint8 *pValue, uint8 len, uint16 offset );
    static void battNotifyCB( linkDBItem_t *pLinkItem );
    static uint8 battMeasure( void );
    static void battNotifyLevel( void );
    
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    // Battery Service Callbacks
    CONST gattServiceCBs_t battCBs =
    {
      battReadAttrCB,  // Read callback function pointer
      battWriteAttrCB, // Write callback function pointer
      NULL             // Authorization callback function pointer
    };
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    
    /*********************************************************************
     * @fn      Batt_AddService
     *
     * @brief   Initializes the Battery Service by registering
     *          GATT attributes with the GATT server.
     *
     * @return  Success or Failure
     */
    bStatus_t Batt_AddService( void )
    {
      uint8 status = SUCCESS;
    
      // Initialize Client Characteristic Configuration attributes
      GATTServApp_InitCharCfg( INVALID_CONNHANDLE, battLevelClientCharCfg );
    
      // Register GATT attribute list and CBs with GATT Server App
      status = GATTServApp_RegisterService( battAttrTbl,
                                            GATT_NUM_ATTRS( battAttrTbl ),
                                            &battCBs );
    
      return ( status );
    }
    
    /*********************************************************************
     * @fn      Batt_Register
     *
     * @brief   Register a callback function with the Battery Service.
     *
     * @param   pfnServiceCB - Callback function.
     *
     * @return  None.
     */
    extern void Batt_Register( battServiceCB_t pfnServiceCB )
    {
      battServiceCB = pfnServiceCB;
    }
    
    /*********************************************************************
     * @fn      Batt_SetParameter
     *
     * @brief   Set a Battery Service parameter.
     *
     * @param   param - Profile parameter ID
     * @param   len - length of data to right
     * @param   value - pointer to data to write.  This is dependent on
     *          the parameter ID and WILL be cast to the appropriate
     *          data type (example: data type of uint16 will be cast to
     *          uint16 pointer).
     *
     * @return  bStatus_t
     */
    bStatus_t Batt_SetParameter( uint8 param, uint8 len, void *value )
    {
      bStatus_t ret = SUCCESS;
    
      switch ( param )
      {
        case BATT_PARAM_LEVEL:
          if ( len == BATT_DATA_LEN )
          {
            VOID osal_memcpy( &battLevel, value, BATT_DATA_LEN );
            // See if Notification has been enabled
            //RP do not notify when externally setting param eg RESET
            /*GATTServApp_ProcessCharCfg( battLevelClientCharCfg, &battLevel, FALSE,
                                     battAttrTbl, GATT_NUM_ATTRS( battAttrTbl ),
                                     INVALID_TASK_ID );*/
          }
          else
          {
            ret = bleInvalidRange;
          }
          break;
          
        case BATT_PARAM_CRITICAL_LEVEL:
          battCriticalLevel = *((uint8*)value);
    
          // If below the critical level and critical state not set, notify it
          if ( battLevel < battCriticalLevel )
          {
            battNotifyLevel();
          }
          break;
    
        default:
          ret = INVALIDPARAMETER;
          break;
      }
    
      return ( ret );
    }
    
    /*********************************************************************
     * @fn      Batt_GetParameter
     *
     * @brief   Get a Battery Service parameter.
     *
     * @param   param - Profile parameter ID
     * @param   value - pointer to data to get.  This is dependent on
     *          the parameter ID and WILL be cast to the appropriate
     *          data type (example: data type of uint16 will be cast to
     *          uint16 pointer).
     *
     * @return  bStatus_t
     */
    bStatus_t Batt_GetParameter( uint8 param, void *value )
    {
      bStatus_t ret = SUCCESS;
      switch ( param )
      {
        case BATT_PARAM_LEVEL:
          *((uint8*)value) = battLevel;
          break;
    
        case BATT_PARAM_CRITICAL_LEVEL:
          *((uint8*)value) = battCriticalLevel;
          break;
    
        case BATT_PARAM_SERVICE_HANDLE:
          *((uint16*)value) = GATT_SERVICE_HANDLE( battAttrTbl );
          break;
    
        case BATT_PARAM_BATT_LEVEL_IN_REPORT:
          {
            hidRptMap_t *pRpt = (hidRptMap_t *)value;
    
            pRpt->id = hidReportRefBattLevel[0];
            pRpt->type = hidReportRefBattLevel[1];
            pRpt->handle = battAttrTbl[BATT_LEVEL_VALUE_IDX].handle;
            pRpt->cccdHandle = battAttrTbl[BATT_LEVEL_VALUE_CCCD_IDX].handle;
            pRpt->mode = HID_PROTOCOL_MODE_REPORT;
          }
          break;
    
        default:
          ret = INVALIDPARAMETER;
          break;
      }
    
      return ( ret );
    }
    
    /*********************************************************************
     * @fn          Batt_MeasLevel
     *
     * @brief       Measure the battery level and update the battery
     *              level value in the service characteristics.  If
     *              the battery level-state characteristic is configured
     *              for notification and the battery level has changed
     *              since the last measurement, then a notification
     *              will be sent.
     *
     * @return      Success
     */
    bStatus_t Batt_MeasLevel( void )
    {
      uint8 level;
    
      level = battMeasure();
    
      // If level has gone down
      if (level < battLevel)
      {
        // Update level
        battLevel = level;
    
        // Send a notification
        battNotifyLevel();
      }
      return SUCCESS;
    }
    
    /*********************************************************************
     * @fn      Batt_Setup
     *
     * @brief   Set up which ADC source is to be used. Defaults to VDD/3.
     *
     * @param   adc_ch - ADC Channel, e.g. HAL_ADC_CHN_AIN6
     * @param   minVal - max battery level
     * @param   maxVal - min battery level
     * @param   sCB - HW setup callback
     * @param   tCB - HW tear down callback
     * @param   cCB - percentage calculation callback
     *
     * @return  none.
     */
    void Batt_Setup( uint8 adc_ch, uint16 minVal, uint16 maxVal,
                     battServiceSetupCB_t sCB, battServiceTeardownCB_t tCB,
                     battServiceCalcCB_t cCB )
    {
      battServiceAdcCh = adc_ch;
      battMinLevel = minVal;
      battMaxLevel = maxVal;
    
      battServiceSetupCB = sCB;
      battServiceTeardownCB = tCB;
      battServiceCalcCB = cCB;
    }
    
    /*********************************************************************
     * @fn          battReadAttrCB
     *
     * @brief       Read an attribute.
     *
     * @param       connHandle - connection message was received on
     * @param       pAttr - pointer to attribute
     * @param       pValue - pointer to data to be read
     * @param       pLen - length of data to be read
     * @param       offset - offset of the first octet to be read
     * @param       maxLen - maximum length of data to be read
     *
     * @return      Success or Failure
     */
    static uint8 battReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen )
    {
      bStatus_t status = SUCCESS;
    
      // Make sure it's not a blob operation (no attributes in the profile are long)
      if ( offset > 0 )
      {
        return ( ATT_ERR_ATTR_NOT_LONG );
      }
    
      uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1] );
    
      // Measure battery level if reading level
      if ( uuid == BATT_LEVEL_UUID )
      {
        uint8 level;
    
        level = battMeasure();
    
        // If level has gone down
        if (level < battLevel)
        {
          // Update level
          battLevel = level;
        }
    
        *pLen = 1;
        pValue[0] = battLevel;
      }
      else if ( uuid == GATT_REPORT_REF_UUID )
      {
        *pLen = HID_REPORT_REF_LEN;
        osal_memcpy( pValue, pAttr->pValue, HID_REPORT_REF_LEN );
      }
      else
      {
        status = ATT_ERR_ATTR_NOT_FOUND;
      }
    
      return ( status );
    }
    
    /*********************************************************************
     * @fn      battWriteAttrCB
     *
     * @brief   Validate attribute data prior to a write operation
     *
     * @param   connHandle - connection message was received on
     * @param   pAttr - pointer to attribute
     * @param   pValue - pointer to data to be written
     * @param   len - length of data
     * @param   offset - offset of the first octet to be written
     *
     * @return  Success or Failure
     */
    static bStatus_t battWriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                      uint8 *pValue, uint8 len, uint16 offset )
    {
      bStatus_t status = SUCCESS;
    
      uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
      switch ( uuid )
      {
        case GATT_CLIENT_CHAR_CFG_UUID:
          status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                                   offset, GATT_CLIENT_CFG_NOTIFY );
          if ( status == SUCCESS )
          {
            uint16 charCfg = BUILD_UINT16( pValue[0], pValue[1] );
    
            if ( battServiceCB )
            {
              (*battServiceCB)( (charCfg == GATT_CFG_NO_OPERATION) ?
                                BATT_LEVEL_NOTI_DISABLED :
                                BATT_LEVEL_NOTI_ENABLED);
            }
          }
          break;
    
        default:
          status = ATT_ERR_ATTR_NOT_FOUND;
          break;
      }
    
      return ( status );
    }
    
    /*********************************************************************
     * @fn          battNotifyCB
     *
     * @brief       Send a notification of the level state characteristic.
     *
     * @param       connHandle - linkDB item
     *
     * @return      None.
     */
    static void battNotifyCB( linkDBItem_t *pLinkItem )
    {
      if ( pLinkItem->stateFlags & LINK_CONNECTED )
      {
        uint16 value = GATTServApp_ReadCharCfg( pLinkItem->connectionHandle,
                                                battLevelClientCharCfg );
        if ( value & GATT_CLIENT_CFG_NOTIFY )
        {
          attHandleValueNoti_t noti;
    
          noti.handle = battAttrTbl[BATT_LEVEL_VALUE_IDX].handle;
          noti.len = 1;
          noti.value[0] = battLevel;
    
          GATT_Notification( pLinkItem->connectionHandle, &noti, FALSE );
        }
      }
    }
    
    /*********************************************************************
     * @fn      battMeasure
     *
     * @brief   Measure the battery level with the ADC and return
     *          it as a percentage 0-100%.
     *
     * @return  Battery level.
     */
    static uint8 battMeasure( void )
    {
      uint16 adc;
      uint8 percent;
    
      /**
       * Battery level conversion from ADC to a percentage:
       *
       * The maximum ADC value for the battery voltage level is 511 for a
       * 10-bit conversion.  The ADC value is references vs. 1.25v and
       * this maximum value corresponds to a voltage of 3.75v.
       *
       * For a coin cell battery 3.0v = 100%.  The minimum operating
       * voltage of the CC2540 is 2.0v so 2.0v = 0%.
       *
       * To convert a voltage to an ADC value use:
       *
       *   (v/3)/1.25 * 511 = adc
       *
       * 3.0v = 409 ADC
       * 2.0v = 273 ADC
       *
       * We need to map ADC values from 409-273 to 100%-0%.
       *
       * Normalize the ADC values to zero:
       *
       * 409 - 273 = 136
       *
       * And convert ADC range to percentage range:
       *
       * percent/adc = 100/136 = 25/34
       *
       * Resulting in the final equation, with round:
       *
       * percent = ((adc - 273) * 25) + 33 / 34
       */
    
      // Call measurement setup callback
      if (battServiceSetupCB != NULL)
      {
        battServiceSetupCB();
      }
    
      // Configure ADC and perform a read
      // Turn off DC/DC converter to measure AVDD
      DCDC_SBIT = 0; 
      HalAdcSetReference( HAL_ADC_REF_125V );
      adc = HalAdcRead( battServiceAdcCh, HAL_ADC_RESOLUTION_10 );
      // Turn on DC/DC converter AVDD now at at 2.1V.
      DCDC_SBIT = 1; 
    
      // Call measurement teardown callback
      if (battServiceTeardownCB != NULL)
      {
        battServiceTeardownCB();
      }
    
      if (adc >= battMaxLevel)
      {
        percent = 100;
      }
      else if (adc <= battMinLevel)
      {
        percent = 0;
      }
      else
      {
        if (battServiceCalcCB != NULL)
        {
          percent = battServiceCalcCB(adc);
        }
        else
        {
          uint16 range =  battMaxLevel - battMinLevel + 1;
    
          // optional if you want to keep it even, otherwise just take floor of divide
          // range += (range & 1);
          range >>= 2; // divide by 4
    
          percent = (uint8) ((((adc - battMinLevel) * 25) + (range - 1)) / range);
        }
      }
    
      return percent;
    }
    
    /*********************************************************************
     * @fn      battNotifyLevelState
     *
     * @brief   Send a notification of the battery level state
     *          characteristic if a connection is established.
     *
     * @return  None.
     */
    static void battNotifyLevel( void )
    {
      // Execute linkDB callback to send notification
      linkDB_PerformFunc( battNotifyCB );
    }
    
    /*********************************************************************
     * @fn          Batt_HandleConnStatusCB
     *
     * @brief       Battery Service link status change handler function.
     *
     * @param       connHandle - connection handle
     * @param       changeType - type of change
     *
     * @return      none
     */
    void Batt_HandleConnStatusCB( uint16 connHandle, uint8 changeType )
    {
      // Make sure this is not loopback connection
      if ( connHandle != LOOPBACK_CONNHANDLE )
      {
        // Reset Client Char Config if connection has dropped
        if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||
             ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) &&
               ( !linkDB_Up( connHandle ) ) ) )
        {
          GATTServApp_InitCharCfg( connHandle, battLevelClientCharCfg );
        }
      }
    }
    
    
    /*********************************************************************
    *********************************************************************/
    

    5141.battservice.h

    You will find sensortag.s in this thread :

    http://e2e.ti.com/support/wireless_connectivity/f/538/p/311302/1294648.aspx#1294648

    Lahorde

  • Hi Lahorde,
    first thanks for your answer.

    So that I can try out the battery service I have added your files in my project. When I search in BTool by the services the battery service is now offered to me, but the value is still '00' (seen in the picture below) .


    Have you also made ​​adjustments in other files? Maybe in hal_adc.c / .h? Or do you have any ideas what is the problem?

    Thanks for your time and patience

    bator

  • Hi bator. 

    Please check the answer in this topic:

    http://e2e.ti.com/support/wireless_connectivity/f/538/p/379430/1336578.aspx#1336578

  • Hey Osman,


    many thanks for your answer, that was the solution of the problem.

  • HI,

    Will this work in simplBLEPeriperhal? I have tried but cant get it to work.

    Thanks.

  • I'm stuck on the same issue, getting a reading of 19-20% on a fresh battery.
    Did you ever get this working?

    Edit: found the issue... dcdc wasn't correctly disabled..