Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ble: add battery level characteristic #66

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

kienvo
Copy link
Member

@kienvo kienvo commented Jan 7, 2025

App might read battery level from the standard characteristic/service uuid: 0x2A19/0x180F

Summary by Sourcery

New Features:

  • Expose battery level as a readable Bluetooth characteristic using the standard Battery Service (0x180F) and Battery Level Characteristic (0x2A19).

App might read battery level from characteristic/service uuid: 0x2A19/0x180F
Copy link

sourcery-ai bot commented Jan 7, 2025

Reviewer's Guide by Sourcery

This pull request implements a new Battery Service profile in the Bluetooth Low Energy (BLE) stack, allowing a connected app to read the device's battery level.

Sequence diagram for battery level reading via BLE

sequenceDiagram
    participant App as BLE App
    participant Device as BLE Device
    participant ADC as ADC Hardware
    App->>Device: Read Battery Level (0x2A19)
    Device->>ADC: Read battery voltage
    ADC-->>Device: Return raw ADC value
    Note over Device: Convert ADC value to percentage
    Device-->>App: Return battery level percentage
Loading

Class diagram for battery service implementation

classDiagram
    class BatteryService {
        +ServiceUUID: 0x180F
        +battLv_CharUUID: 0x2A19
        +battLv_Val: uint8[]
        +read_handler()
        +batt_registerService()
    }
    class PowerManagement {
        +batt_raw()
        +charging_status()
        +batt_raw2percent()
        +poweroff()
    }
    BatteryService --> PowerManagement: uses
Loading

File-Level Changes

Change Details Files
Added Battery Service to BLE profiles
  • Registered the Battery Service during BLE setup.
  • Created a new Battery Service profile implementation.
  • Added the Battery Service files to the build system.
src/main.c
src/ble/profile.h
src/ble/profile/batt.c
Makefile
Implemented Battery Level Characteristic
  • Implemented the read handler for the Battery Level characteristic.
  • The handler reads the battery level, converts it to percentage, and sends it to the connected app.
src/ble/profile/batt.c
Moved power-related functions to a dedicated file
  • Moved power-related functions like poweroff, batt_raw, charging_status, and batt_raw2percent to power.c.
  • Added power.c to the build system.
  • Updated function calls in main.c to use the new location of the power-related functions.
src/power.c
src/power.h
src/main.c
Makefile
Removed unused code related to battery monitoring from main.c
  • Removed unused code related to battery monitoring, including ADC configuration, voltage conversion, and charging status checking from main.c.
src/main.c
Updated legacy service handlers
  • Made the service_handlers variable in legacy.c static to limit its scope.
src/ble/profile/legacy.c

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @kienvo - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider wrapping the ADC debug print statements in a debug macro/flag for production builds
Here's what I looked at during the review
  • 🟡 General issues: 3 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

LowPower_Shutdown(0);
}

int batt_raw()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Consider moving ADC calibration to an initialization function to avoid recalibrating on every battery reading

The ADC calibration value is unlikely to change during runtime. Caching it would improve performance and reduce power consumption.

Suggested implementation:

static int16_t adc_calib_value;

void power_init(void)
{
	GPIOA_ModeCfg(GPIO_Pin_5, GPIO_ModeIN_Floating);
	ADC_ExtSingleChSampInit(SampleFreq_3_2, ADC_PGA_0);

	adc_calib_value = ADC_DataCalib_Rough();
	PRINT("RoughCalib_Value = %d \n", adc_calib_value);
}

int batt_raw()
{
	int ret = 0;

	/* Use cached calibration value */
	ADC_ChannelCfg(1);

You'll also need to:

  1. Add a prototype for power_init() in the header file (likely power.h)
  2. Call power_init() during system initialization, before any battery readings are taken
  3. Consider adding error handling for the calibration process in power_init()


ADC_ChannelCfg(1);

PRINT("ADC reading: \n");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Debug print statements should be wrapped in conditional compilation

Consider using #ifdef DEBUG around PRINT statements to avoid unnecessary overhead in production builds

Suggested implementation:

	int16_t adc_calib = ADC_DataCalib_Rough();
#ifdef DEBUG
	PRINT("RoughCalib_Value = %d \n", adc_calib);
#endif

	ADC_ChannelCfg(1);

#ifdef DEBUG
	PRINT("ADC reading: \n");
#endif
	uint16_t buf[20];
	for(int i = 0; i < 20; i++) {
		uint16_t adc = ADC_ExcutSingleConver() + adc_calib;
		ret += adc;
#ifdef DEBUG
		PRINT("%d \n", adc);
#endif
	}

Make sure that:

  1. DEBUG is defined in your debug build configuration (typically in a header file or build system)
  2. All other source files using PRINT statements are similarly updated for consistency

}

*pLen = 1;
battLv_Val[0] = batt_raw2percent(batt_raw());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Battery level should be clamped to 0-100 range for BLE characteristic

batt_raw2percent can return negative values, but the Battery Level characteristic (0x2A19) must be between 0 and 100 per BLE spec

return GPIOA_ReadPortPin(CHARGE_STT_PIN) == 0;
}

#define ZERO_PERCENT_THRES (3.3)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider simplifying the voltage calculation by using functions instead of macros and combining the voltage divider and ADC conversion into a single step.

The voltage calculation chain can be simplified while maintaining precision. Consider replacing the macro definitions and multi-step calculations with these more direct functions:

// Constants
#define BATT_MIN_VOLT  3.3   // Zero percent threshold
#define BATT_MAX_VOLT  4.2   // 100 percent threshold
#define ADC_MAX_VAL    4096.0
#define ADC_REF_VOLT   2.1

// Convert ADC reading directly to battery voltage
static inline float adc_to_battery_voltage(int adc_raw) {
    // Combined voltage divider and ADC conversion
    return adc_raw * (ADC_REF_VOLT / ADC_MAX_VAL) * (282.0 / 100.0);
}

// Convert battery voltage to percentage
static inline int battery_voltage_to_percent(float voltage) {
    if (voltage < BATT_MIN_VOLT) return 0;
    if (voltage > BATT_MAX_VOLT) return 100;
    return (int)((voltage - BATT_MIN_VOLT) * 100.0 / (BATT_MAX_VOLT - BATT_MIN_VOLT));
}

This approach:

  1. Eliminates the macro chain while preserving the calculations
  2. Makes the conversion steps clear and debuggable
  3. Maintains floating-point precision
  4. Keeps the electrical engineering concepts visible

The main function becomes simpler:

int batt_raw2percent(int r) {
    return battery_voltage_to_percent(adc_to_battery_voltage(r));
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant