Notify battery level every 10 minutes when connected to a BLE host.

Refactor battery percent : only use uint8_t to store the battery % remaining.
This commit is contained in:
Jean-François Milants 2021-07-11 16:55:06 +02:00
parent 6a91b83b12
commit e21f6a7f41
17 changed files with 71 additions and 68 deletions

View file

@ -1,9 +1,7 @@
#include "BatteryController.h" #include "BatteryController.h"
#include <hal/nrf_gpio.h> #include <hal/nrf_gpio.h>
#include <nrfx_saadc.h> #include <nrfx_saadc.h>
#include <libraries/log/nrf_log.h>
#include <algorithm> #include <algorithm>
#include <math.h>
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
@ -18,7 +16,6 @@ void Battery::Init() {
} }
void Battery::Update() { void Battery::Update() {
isCharging = !nrf_gpio_pin_read(chargingPin); isCharging = !nrf_gpio_pin_read(chargingPin);
isPowerPresent = !nrf_gpio_pin_read(powerPresentPin); isPowerPresent = !nrf_gpio_pin_read(powerPresentPin);
@ -33,13 +30,13 @@ void Battery::Update() {
nrfx_saadc_sample(); nrfx_saadc_sample();
} }
void Battery::adcCallbackStatic(nrfx_saadc_evt_t const* event) { void Battery::AdcCallbackStatic(nrfx_saadc_evt_t const* event) {
instance->SaadcEventHandler(event); instance->SaadcEventHandler(event);
} }
void Battery::SaadcInit() { void Battery::SaadcInit() {
nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG; nrfx_saadc_config_t adcConfig = NRFX_SAADC_DEFAULT_CONFIG;
APP_ERROR_CHECK(nrfx_saadc_init(&adcConfig, adcCallbackStatic)); APP_ERROR_CHECK(nrfx_saadc_init(&adcConfig, AdcCallbackStatic));
nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = NRF_SAADC_RESISTOR_DISABLED, nrf_saadc_channel_config_t adcChannelConfig = {.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
.resistor_n = NRF_SAADC_RESISTOR_DISABLED, .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
@ -55,7 +52,6 @@ void Battery::SaadcInit() {
} }
void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) { void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) {
const uint16_t battery_max = 4180; // maximum voltage of battery ( max charging voltage is 4.21 ) const uint16_t battery_max = 4180; // maximum voltage of battery ( max charging voltage is 4.21 )
const uint16_t battery_min = 3200; // minimum voltage of battery before shutdown ( depends on the battery ) const uint16_t battery_min = 3200; // minimum voltage of battery before shutdown ( depends on the battery )
@ -69,13 +65,10 @@ void Battery::SaadcEventHandler(nrfx_saadc_evt_t const* p_event) {
// reference_voltage is 0.6V // reference_voltage is 0.6V
// p_event->data.done.p_buffer[0] = (adc_voltage / reference_voltage) * 1024 // p_event->data.done.p_buffer[0] = (adc_voltage / reference_voltage) * 1024
voltage = p_event->data.done.p_buffer[0] * 6000 / 1024; voltage = p_event->data.done.p_buffer[0] * 6000 / 1024;
percentRemaining = (voltage - battery_min) * 100 / (battery_max - battery_min); percentRemaining = (voltage - battery_min) * 100 / (battery_max - battery_min);
percentRemaining = std::max(percentRemaining, 0); percentRemaining = std::max(percentRemaining, 0);
percentRemaining = std::min(percentRemaining, 100); percentRemaining = std::min(percentRemaining, 100);
percentRemainingBuffer.Insert(percentRemaining);
percentRemainingBuffer.insert(percentRemaining);
samples++; samples++;
if (samples > percentRemainingSamples) { if (samples > percentRemainingSamples) {

View file

@ -19,7 +19,7 @@ namespace Pinetime {
insert member function overwrites the next data to the current insert member function overwrites the next data to the current
HEAD and moves the HEAD to the newly inserted value. HEAD and moves the HEAD to the newly inserted value.
*/ */
void insert(const int num) { void Insert(const uint8_t num) {
head %= cap; head %= cap;
arr[head++] = num; arr[head++] = num;
if (sz != cap) { if (sz != cap) {
@ -27,13 +27,13 @@ namespace Pinetime {
} }
} }
int GetAverage() const { uint8_t GetAverage() const {
int sum = std::accumulate(arr.begin(), arr.end(), 0); int sum = std::accumulate(arr.begin(), arr.end(), 0);
return (sum / sz); return static_cast<uint8_t>(sum / sz);
} }
private: private:
std::array<int, N> arr; /**< internal array used to store the values*/ std::array<uint8_t, N> arr; /**< internal array used to store the values*/
uint8_t sz; /**< The current size of the array.*/ uint8_t sz; /**< The current size of the array.*/
uint8_t cap; /**< Total capacity of the CircBuffer.*/ uint8_t cap; /**< Total capacity of the CircBuffer.*/
uint8_t head; /**< The current head of the CircBuffer*/ uint8_t head; /**< The current head of the CircBuffer*/
@ -46,8 +46,11 @@ namespace Pinetime {
void Init(); void Init();
void Update(); void Update();
int PercentRemaining() const { uint8_t PercentRemaining() const {
return percentRemainingBuffer.GetAverage(); auto avg = percentRemainingBuffer.GetAverage();
avg = std::min(avg, static_cast<uint8_t>(100));
avg = std::max(avg, static_cast<uint8_t>(0));
return avg;
} }
uint16_t Voltage() const { uint16_t Voltage() const {
@ -57,6 +60,7 @@ namespace Pinetime {
bool IsCharging() const { bool IsCharging() const {
return isCharging; return isCharging;
} }
bool IsPowerPresent() const { bool IsPowerPresent() const {
return isPowerPresent; return isPowerPresent;
} }
@ -80,7 +84,7 @@ namespace Pinetime {
void SaadcInit(); void SaadcInit();
void SaadcEventHandler(nrfx_saadc_evt_t const* p_event); void SaadcEventHandler(nrfx_saadc_evt_t const* p_event);
static void adcCallbackStatic(nrfx_saadc_evt_t const* event); static void AdcCallbackStatic(nrfx_saadc_evt_t const* event);
bool isReading = false; bool isReading = false;
uint8_t samples = 0; uint8_t samples = 0;

View file

@ -17,7 +17,7 @@ BatteryInformationService::BatteryInformationService(Controllers::Battery& batte
characteristicDefinition {{.uuid = (ble_uuid_t*) &batteryLevelUuid, characteristicDefinition {{.uuid = (ble_uuid_t*) &batteryLevelUuid,
.access_cb = BatteryInformationServiceCallback, .access_cb = BatteryInformationServiceCallback,
.arg = this, .arg = this,
.flags = BLE_GATT_CHR_F_READ, .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.val_handle = &batteryLevelHandle}, .val_handle = &batteryLevelHandle},
{0}}, {0}},
serviceDefinition { serviceDefinition {
@ -49,3 +49,7 @@ int BatteryInformationService::OnBatteryServiceRequested(uint16_t connectionHand
} }
return 0; return 0;
} }
void BatteryInformationService::NotifyBatteryLevel(uint16_t connectionHandle, uint8_t level) {
auto* om = ble_hs_mbuf_from_flat(&level, 1);
ble_gattc_notify_custom(connectionHandle, batteryLevelHandle, om);
}

View file

@ -17,7 +17,7 @@ namespace Pinetime {
void Init(); void Init();
int OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context); int OnBatteryServiceRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context);
void NotifyBatteryLevel(uint16_t connectionHandle, uint8_t level);
private: private:
Controllers::Battery& batteryController; Controllers::Battery& batteryController;
static constexpr uint16_t batteryInformationServiceId {0x180F}; static constexpr uint16_t batteryInformationServiceId {0x180F};

View file

@ -235,3 +235,9 @@ void NimbleController::StartDiscovery() {
uint16_t NimbleController::connHandle() { uint16_t NimbleController::connHandle() {
return connectionHandle; return connectionHandle;
} }
void NimbleController::NotifyBatteryLevel(uint8_t level) {
if(connectionHandle != BLE_HS_CONN_HANDLE_NONE) {
batteryInformationService.NotifyBatteryLevel(connectionHandle, level);
}
}

View file

@ -70,6 +70,7 @@ namespace Pinetime {
}; };
uint16_t connHandle(); uint16_t connHandle();
void NotifyBatteryLevel(uint8_t level);
private: private:
static constexpr const char* deviceName = "InfiniTime"; static constexpr const char* deviceName = "InfiniTime";
@ -92,7 +93,7 @@ namespace Pinetime {
HeartRateService heartRateService; HeartRateService heartRateService;
uint8_t addrType; // 1 = Random, 0 = PUBLIC uint8_t addrType; // 1 = Random, 0 = PUBLIC
uint16_t connectionHandle = 0; uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
ble_uuid128_t dfuServiceUuid { ble_uuid128_t dfuServiceUuid {
.u {.type = BLE_UUID_TYPE_128}, .u {.type = BLE_UUID_TYPE_128},

View file

@ -1,9 +1,10 @@
#include <cstdint>
#include "BatteryIcon.h" #include "BatteryIcon.h"
#include "Symbols.h" #include "Symbols.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
const char* BatteryIcon::GetBatteryIcon(int batteryPercent) { const char* BatteryIcon::GetBatteryIcon(uint8_t batteryPercent) {
if (batteryPercent > 90) if (batteryPercent > 90)
return Symbols::batteryFull; return Symbols::batteryFull;
if (batteryPercent > 75) if (batteryPercent > 75)

View file

@ -6,7 +6,7 @@ namespace Pinetime {
class BatteryIcon { class BatteryIcon {
public: public:
static const char* GetUnknownIcon(); static const char* GetUnknownIcon();
static const char* GetBatteryIcon(int batteryPercent); static const char* GetBatteryIcon(uint8_t batteryPercent);
static const char* GetPlugIcon(bool isCharging); static const char* GetPlugIcon(bool isCharging);
}; };
} }

View file

@ -38,11 +38,7 @@ BatteryInfo::BatteryInfo(Pinetime::Applications::DisplayApp* app, Pinetime::Cont
percent = lv_label_create(lv_scr_act(), nullptr); percent = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(percent, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); lv_obj_set_style_local_text_font(percent, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
if (batteryPercent >= 0) {
lv_label_set_text_fmt(percent, "%02i%%", batteryPercent); lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
} else {
lv_label_set_text(percent, "--%");
}
lv_label_set_align(percent, LV_LABEL_ALIGN_LEFT); lv_label_set_align(percent, LV_LABEL_ALIGN_LEFT);
lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60); lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60);
@ -72,7 +68,6 @@ BatteryInfo::~BatteryInfo() {
void BatteryInfo::UpdateAnim() { void BatteryInfo::UpdateAnim() {
batteryPercent = batteryController.PercentRemaining(); batteryPercent = batteryController.PercentRemaining();
if (batteryPercent >= 0) {
if (batteryController.IsCharging() and batteryPercent < 100) { if (batteryController.IsCharging() and batteryPercent < 100) {
animation += 1; animation += 1;
if (animation >= 100) { if (animation >= 100) {
@ -89,7 +84,6 @@ void BatteryInfo::UpdateAnim() {
} }
lv_bar_set_value(charging_bar, animation, LV_ANIM_OFF); lv_bar_set_value(charging_bar, animation, LV_ANIM_OFF);
}
} }
void BatteryInfo::UpdateScreen() { void BatteryInfo::UpdateScreen() {
@ -99,7 +93,6 @@ void BatteryInfo::UpdateScreen() {
batteryPercent = batteryController.PercentRemaining(); batteryPercent = batteryController.PercentRemaining();
batteryVoltage = batteryController.Voltage(); batteryVoltage = batteryController.Voltage();
if (batteryPercent >= 0) {
if (batteryController.IsCharging() and batteryPercent < 100) { if (batteryController.IsCharging() and batteryPercent < 100) {
lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED); lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_label_set_text_static(status, "Battery charging"); lv_label_set_text_static(status, "Battery charging");
@ -116,11 +109,6 @@ void BatteryInfo::UpdateScreen() {
lv_label_set_text_fmt(percent, "%02i%%", batteryPercent); lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
} else {
lv_label_set_text_static(status, "Reading Battery status");
lv_label_set_text(percent, "--%");
}
lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20); lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10); lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10);
} }

View file

@ -36,7 +36,7 @@ namespace Pinetime {
lv_task_t* taskAnim; lv_task_t* taskAnim;
int8_t animation = 0; int8_t animation = 0;
int8_t batteryPercent = -1; uint8_t batteryPercent = 0;
uint16_t batteryVoltage = 0; uint16_t batteryVoltage = 0;
}; };
} }

View file

@ -42,7 +42,7 @@ namespace Pinetime {
Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown; Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown;
uint8_t currentDay = 0; uint8_t currentDay = 0;
DirtyValue<int> batteryPercentRemaining {}; DirtyValue<uint8_t> batteryPercentRemaining {};
DirtyValue<bool> bleState {}; DirtyValue<bool> bleState {};
DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {}; DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {};
DirtyValue<bool> motionSensorOk {}; DirtyValue<bool> motionSensorOk {};

View file

@ -103,7 +103,7 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen1() {
} }
std::unique_ptr<Screen> SystemInfo::CreateScreen2() { std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
auto batteryPercent = static_cast<uint8_t>(batteryController.PercentRemaining()); auto batteryPercent = batteryController.PercentRemaining();
auto resetReason = [this]() { auto resetReason = [this]() {
switch (watchdog.ResetReason()) { switch (watchdog.ResetReason()) {
case Drivers::Watchdog::ResetReasons::Watchdog: case Drivers::Watchdog::ResetReasons::Watchdog:

View file

@ -165,7 +165,6 @@ void WatchFaceAnalog::UpdateClock() {
} }
bool WatchFaceAnalog::Refresh() { bool WatchFaceAnalog::Refresh() {
batteryPercentRemaining = batteryController.PercentRemaining(); batteryPercentRemaining = batteryController.PercentRemaining();
if (batteryPercentRemaining.IsUpdated()) { if (batteryPercentRemaining.IsUpdated()) {
auto batteryPercent = batteryPercentRemaining.Get(); auto batteryPercent = batteryPercentRemaining.Get();

View file

@ -48,7 +48,7 @@ namespace Pinetime {
Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown; Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown;
uint8_t currentDay = 0; uint8_t currentDay = 0;
DirtyValue<float> batteryPercentRemaining {0}; DirtyValue<uint8_t> batteryPercentRemaining {0};
DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime; DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime;
DirtyValue<bool> notificationState {false}; DirtyValue<bool> notificationState {false};

View file

@ -45,7 +45,7 @@ namespace Pinetime {
Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown; Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown;
uint8_t currentDay = 0; uint8_t currentDay = 0;
DirtyValue<int> batteryPercentRemaining {}; DirtyValue<uint8_t> batteryPercentRemaining {};
DirtyValue<bool> bleState {}; DirtyValue<bool> bleState {};
DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {}; DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {};
DirtyValue<bool> motionSensorOk {}; DirtyValue<bool> motionSensorOk {};

View file

@ -330,6 +330,11 @@ void SystemTask::Work() {
} }
} }
if (xTaskGetTickCount() - batteryNotificationTick > batteryNotificationPeriod) {
nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining());
batteryNotificationTick = xTaskGetTickCount();
}
monitor.Process(); monitor.Process();
uint32_t systick_counter = nrf_rtc_counter_get(portNRF_RTC_REG); uint32_t systick_counter = nrf_rtc_counter_get(portNRF_RTC_REG);
dateTimeController.UpdateTime(systick_counter); dateTimeController.UpdateTime(systick_counter);

View file

@ -135,6 +135,8 @@ namespace Pinetime {
void GoToRunning(); void GoToRunning();
void UpdateMotion(); void UpdateMotion();
bool stepCounterMustBeReset = false; bool stepCounterMustBeReset = false;
static constexpr TickType_t batteryNotificationPeriod = 1000 * 60 * 10; // 1 tick ~= 1ms. 1ms * 60 * 10 = 10 minutes
TickType_t batteryNotificationTick = 0;
#if configUSE_TRACE_FACILITY == 1 #if configUSE_TRACE_FACILITY == 1
SystemMonitor<FreeRtosMonitor> monitor; SystemMonitor<FreeRtosMonitor> monitor;