~aleteoryx/muditaos

7a8e35016dc0509858bfc3d3d67830734cc1b97a — Lefucjusz 2 years ago 93b11aa
[MOS-1026] Add extended logging to Pure's charger driver

* Added extended logging to Pure's charger driver.
* Removed redundant handling of INOKB pin
interrupt - charger is configured to provide
the same interrupt via INTB pin.
* Minor code cleanup.
M module-apps/apps-common/windows/AppWindow.cpp => module-apps/apps-common/windows/AppWindow.cpp +7 -5
@@ 65,12 65,14 @@ namespace gui
        return appConfiguration;
    }

    void AppWindow::applyToStatusBar(StatusBarConfigurationChangeFunction configChange)
    void AppWindow::applyToStatusBar(const StatusBarConfigurationChangeFunction &configChange)
    {
        if (configChange) {
            auto newConfiguration = configChange(statusBar->getConfiguration());
            statusBar->configure(std::move(newConfiguration));
        if (configChange == nullptr) {
            return;
        }

        auto newConfiguration = configChange(statusBar->getConfiguration());
        statusBar->configure(std::move(newConfiguration));
    }

    bool AppWindow::updateBluetooth(sys::bluetooth::BluetoothMode mode)


@@ 105,7 107,7 @@ namespace gui
        }
        return statusBar->updateBattery();
    }
    // updates battery level in the window

    bool AppWindow::updateSignalStrength()
    {
        if (statusBar == nullptr) {

M module-apps/apps-common/windows/AppWindow.hpp => module-apps/apps-common/windows/AppWindow.hpp +1 -1
@@ 97,7 97,7 @@ namespace gui
         * Applies configuration change on the current status bar configuration.
         * @param configChange  The function that contains the status bar configuration changes.
         */
        void applyToStatusBar(StatusBarConfigurationChangeFunction configChange);
        void applyToStatusBar(const StatusBarConfigurationChangeFunction &configChange);

        void setTitle(const UTF8 &text);
        [[nodiscard]] UTF8 getTitle();

M module-bsp/WorkerQueue.hpp => module-bsp/WorkerQueue.hpp +4 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 44,7 44,7 @@ class WorkerQueue
        bool kill{false};
    };

    static constexpr auto queueLength = 2;
    static constexpr auto queueLength = 4;

    xQueueHandle queueHandle{};
    xTaskHandle taskHandle{};


@@ 91,6 91,7 @@ WorkerQueue<Message>::~WorkerQueue()
        vQueueDelete(queueHandle);
    }
}

template <typename Message>
void WorkerQueue<Message>::worker()
{


@@ 107,6 108,7 @@ void WorkerQueue<Message>::worker()
        }
    }
}

template <typename Message>
BaseType_t WorkerQueue<Message>::post(const Message &msg)
{

M module-bsp/board/rt1051/puretx/bsp/battery_charger/MAX77818.hpp => module-bsp/board/rt1051/puretx/bsp/battery_charger/MAX77818.hpp +58 -39
@@ 1,13 1,12 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once

namespace bsp::battery_charger
{

    constexpr inline auto BATTERY_CHARGER_I2C_ADDR = 0xD2 >> 1;
    constexpr inline auto FUEL_GAUGE_I2C_ADDR      = 0x6C >> 1;
    constexpr inline auto TOP_CONTROLLER_I2C_ADDR  = 0xCC >> 1;
    constexpr inline auto BATTERY_CHARGER_I2C_ADDR = (0xD2 >> 1);
    constexpr inline auto FUEL_GAUGE_I2C_ADDR      = (0x6C >> 1);
    constexpr inline auto TOP_CONTROLLER_I2C_ADDR  = (0xCC >> 1);

    enum class Registers
    {


@@ 16,6 15,7 @@ namespace bsp::battery_charger
        TOP_CONTROLL_IRQ_SRC_REG  = 0x22,
        TOP_CONTROLL_IRQ_MASK_REG = 0x23,
        SYSTEM_IRQ_REG            = 0x24,
        SYSTEM_IRQ_MASK_REG       = 0x26,

        STATUS_REG     = 0x00,
        VALRT_Th_REG   = 0x01,


@@ 104,7 104,6 @@ namespace bsp::battery_charger
        CHG_CNFG_10    = 0xC1,
        CHG_CNFG_11    = 0xC2,
        CHG_CNFG_12    = 0xC3

    };

    // STATUS register bits


@@ 125,10 124,17 @@ namespace bsp::battery_charger
        Vmx    = (1 << 12),
        Tmx    = (1 << 13),
        Smx    = (1 << 14),
        Br     = (1 << 15),
        Br     = (1 << 15)
    };

    enum class TOP_MASK
    {
        CHGR_INT_MASK = (1 << 0),
        FG_INT_MASK   = (1 << 1),
        SYS_INT_MASK  = (1 << 2)
    };

    /// CHG_INT register
    // CHG_INT register
    enum class CHG_INT
    {
        BYP_I   = (1 << 0),


@@ 153,7 159,7 @@ namespace bsp::battery_charger
        AICL_M  = (1 << 7)
    };

    /// CHG_DETAILS_01 register
    // CHG_DETAILS_01 register
    enum class CHG_DETAILS_01
    {
        CHARGER_PREQUALIFICATION = 0x00,


@@ 166,11 172,11 @@ namespace bsp::battery_charger
        CHARGER_OFF              = 0x08,
    };

    // CHG_CNFG_09 register
    enum class USBCurrentLimit
    // CHG_CNFG_00 register
    enum class ChargerMode
    {
        lim500mA  = 0x0F,
        lim1000mA = 0x1E,
        ChargerOffBuckOn = 0x04,
        ChargerOnBuckOn  = 0x05
    };

    // CHG_CNFG_02 register


@@ 181,6 187,20 @@ namespace bsp::battery_charger
        lim1600mA = 0x20, /// 1C of battery
    };

    // CHG_CNFG_06
    enum class ChargerSettingsProtection
    {
        Enable  = 0,
        Disable = (0x03 << 2)
    };

    // CHG_CNFG_09 register
    enum class USBCurrentLimit
    {
        lim500mA  = 0x0F,
        lim1000mA = 0x1E,
    };

    enum class ChargeTerminationVoltage
    {
        mV4100 = 0x12, /// 4.1V


@@ 190,36 210,35 @@ namespace bsp::battery_charger
    // CONFIG register bits
    enum class CONFIG
    {
        Ber    = 1 << 0,
        Bei    = 1 << 1,
        Aen    = 1 << 2,
        FTHRM  = 1 << 3,
        ETHRM  = 1 << 4,
        SPR_5  = 1 << 5,
        I2CSH  = 1 << 6,
        SHDN   = 1 << 7,
        Tex    = 1 << 8,
        Ten    = 1 << 9,
        AINSH  = 1 << 10,
        SPR_11 = 1 << 11,
        Vs     = 1 << 12,
        Ts     = 1 << 13,
        Ss     = 1 << 14,
        SPR_15 = 1 << 15
        Ber    = (1 << 0),
        Bei    = (1 << 1),
        Aen    = (1 << 2),
        FTHRM  = (1 << 3),
        ETHRM  = (1 << 4),
        SPR_5  = (1 << 5),
        I2CSH  = (1 << 6),
        SHDN   = (1 << 7),
        Tex    = (1 << 8),
        Ten    = (1 << 9),
        AINSH  = (1 << 10),
        SPR_11 = (1 << 11),
        Vs     = (1 << 12),
        Ts     = (1 << 13),
        Ss     = (1 << 14),
        SPR_15 = (1 << 15)
    };

    // CONFIG2 register bits
    enum class CONFIG2
    {
        ISysNCurr    = 1 << 0,
        OCVQen       = 1 << 4,
        LdMdl        = 1 << 5,
        TAlrtEn      = 1 << 6,
        dSOCen       = 1 << 7,
        ThmHotAlrtEn = 1 << 8,
        ThmHotEn     = 1 << 9,
        FCThmHot     = 1 << 10,
        SPR          = 1 << 11
        ISysNCurr    = (1 << 0),
        OCVQen       = (1 << 4),
        LdMdl        = (1 << 5),
        TAlrtEn      = (1 << 6),
        dSOCen       = (1 << 7),
        ThmHotAlrtEn = (1 << 8),
        ThmHotEn     = (1 << 9),
        FCThmHot     = (1 << 10),
        SPR          = (1 << 11)
    };

} // namespace bsp::battery_charger

M module-bsp/board/rt1051/puretx/bsp/battery_charger/battery_charger.cpp => module-bsp/board/rt1051/puretx/bsp/battery_charger/battery_charger.cpp +297 -246
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "battery_charger.hpp"


@@ 26,7 26,7 @@ namespace bsp::battery_charger
    {
        using Crc32 = std::uint32_t;

        constexpr std::uint32_t i2cSubaddresSize = 1;
        constexpr std::uint32_t i2cSubaddressSize = 1;

        const auto cfgFile = purefs::dir::getSystemVarDirPath() / "batteryFuelGaugeConfig.cfg";



@@ 34,16 34,8 @@ namespace bsp::battery_charger
        constexpr auto configFileSizeWithoutChecksum = registersToStore * sizeof(Register);
        constexpr auto configFileSizeWithChecksum    = configFileSizeWithoutChecksum + sizeof(Crc32);

        constexpr std::uint16_t ENABLE_CHG_FG_IRQ_MASK = 0xFC;
        constexpr std::uint8_t UNLOCK_CHARGER          = 0x3 << 2;

        constexpr std::uint8_t CHG_ON_OTG_OFF_BUCK_ON  = 0b00000101;
        constexpr std::uint8_t CHG_OFF_OTG_OFF_BUCK_ON = 0b00000100;

        constexpr std::uint8_t VSYS_MIN = 0x80; // 3.6V

        constexpr std::uint16_t nominalCapacitymAh = 1600;

        constexpr units::SOC fullyChargedSOC       = 100;
        constexpr units::SOC dischargingSocDelta   = 5;
        constexpr units::SOC chargingSocDelta      = 1;


@@ 52,8 44,8 @@ namespace bsp::battery_charger
        constexpr std::uint16_t maxVoltagemV = 4400;
        constexpr std::uint16_t minVoltagemV = 3600;

        constexpr auto currentSenseGain = 0.15625;  // mA
        constexpr auto voltageSenseGain = 0.078125; // mV
        constexpr auto currentSenseGain = 0.15625f;  // mA
        constexpr auto voltageSenseGain = 0.078125f; // mV

        constexpr std::uint16_t maxMinMilliVoltGain = 20;



@@ 65,7 57,6 @@ namespace bsp::battery_charger
        {
            /// Parameters calculated in in MAXIM EVKIT software
            /// Initial parameters for fuel gauge battery model

            constexpr std::uint16_t LearnCFG    = 0x2602;
            constexpr std::uint16_t FilterCFG   = 0xCEA0; // Current filter Tc = 0.7s
            constexpr std::uint16_t MiscCFG     = 0x01D0;


@@ 88,10 79,9 @@ namespace bsp::battery_charger
            constexpr std::uint16_t V_empty = 0x965A;
            /// Fully charged threshold = 90%
            constexpr std::uint16_t FullSOCthr = 0x5A00;

        } // namespace fuel_gauge_params

        enum class fileConfigRetval
        enum class FileConfigRetval
        {
            OK,
            MissingFileError,


@@ 112,7 102,7 @@ namespace bsp::battery_charger
            Hot
        };

        struct TemparatureThresholds
        struct TemperatureThresholds
        {
            int lowTemperatureThreshold;
            int highTemperatureThreshold;


@@ 139,11 129,11 @@ namespace bsp::battery_charger
        constexpr std::uint8_t maxAlertDisabled{0x7F};
        constexpr std::uint8_t minAlertDisabled{0x80};

        // INT is triggered if T>=maxThreshold || T<minThreshold
        // INT is triggered if T >= maxThreshold || T < minThreshold
        constexpr std::uint8_t lowHysteresis{1};
        constexpr std::uint8_t highHysteresis{2};

        const std::map<TemperatureRanges, TemparatureThresholds> temperatureRanges{
        const std::map<TemperatureRanges, TemperatureThresholds> temperatureRanges{
            {TemperatureRanges::Cold, {std::numeric_limits<std::int8_t>::min(), 1, minAlertDisabled, 1}},
            {TemperatureRanges::Cdeg1to15, {1, 15, 0, 15 + highHysteresis}},
            {TemperatureRanges::Cdeg15to35, {15, 35, 15 - lowHysteresis, 35 + highHysteresis}},


@@ 154,9 144,9 @@ namespace bsp::battery_charger
        std::shared_ptr<drivers::DriverI2C> i2c;
        std::shared_ptr<drivers::DriverGPIO> gpio;

        drivers::I2CAddress fuelGaugeAddress      = {FUEL_GAUGE_I2C_ADDR, 0, i2cSubaddresSize};
        drivers::I2CAddress batteryChargerAddress = {BATTERY_CHARGER_I2C_ADDR, 0, i2cSubaddresSize};
        drivers::I2CAddress topControllerAddress  = {TOP_CONTROLLER_I2C_ADDR, 0, i2cSubaddresSize};
        drivers::I2CAddress fuelGaugeAddress      = {FUEL_GAUGE_I2C_ADDR, 0, i2cSubaddressSize};
        drivers::I2CAddress batteryChargerAddress = {BATTERY_CHARGER_I2C_ADDR, 0, i2cSubaddressSize};
        drivers::I2CAddress topControllerAddress  = {TOP_CONTROLLER_I2C_ADDR, 0, i2cSubaddressSize};

        void addChecksumToConfigFile()
        {


@@ 168,8 158,8 @@ namespace bsp::battery_charger
            CRC32 checksum;
            std::uint16_t regVal;
            for (auto i = 0; i < registersToStore; ++i) {
                file.read(reinterpret_cast<char *>(&regVal), sizeof(std::uint16_t));
                checksum.add(&regVal, sizeof(std::uint16_t));
                file.read(reinterpret_cast<char *>(&regVal), sizeof(regVal));
                checksum.add(&regVal, sizeof(regVal));
            }
            const auto result = checksum.getHashValue();
            file.write(reinterpret_cast<const char *>(&result), sizeof(result));


@@ 189,21 179,24 @@ namespace bsp::battery_charger
            case configFileSizeWithoutChecksum:
                ret = true;
                break;

            case configFileSizeWithChecksum: {
                CRC32 checksum;
                std::uint16_t regVal;
                for (auto i = 0; i < registersToStore; ++i) {
                    file.read(reinterpret_cast<char *>(&regVal), sizeof(std::uint16_t));
                    checksum.add(&regVal, sizeof(std::uint16_t));
                    file.read(reinterpret_cast<char *>(&regVal), sizeof(regVal));
                    checksum.add(&regVal, sizeof(regVal));
                }
                std::uint32_t fileChecksum;
                file.read(reinterpret_cast<char *>(&fileChecksum), sizeof(std::uint32_t));
                ret = fileChecksum == checksum.getHashValue();
                file.read(reinterpret_cast<char *>(&fileChecksum), sizeof(fileChecksum));
                ret = (fileChecksum == checksum.getHashValue());
            } break;

            default:
                ret = false;
                break;
            }

            file.close();
            return ret;
        }


@@ 212,15 205,15 @@ namespace bsp::battery_charger
        {
            /// 16 bit result. The high byte indicates 1% per LSB. The low byte reports fractional percent. We don't
            /// care about fractional part.
            return (registerValue & 0xff00) >> 8;
            return (registerValue & 0xFF00) >> 8;
        }

        int fuelGaugeWrite(Registers registerAddress, std::uint16_t value)
        {
            fuelGaugeAddress.subAddress = static_cast<std::uint32_t>(registerAddress);
            auto ret = i2c->Write(fuelGaugeAddress, reinterpret_cast<std::uint8_t *>(&value), sizeof(std::uint16_t));
            auto ret = i2c->Write(fuelGaugeAddress, reinterpret_cast<std::uint8_t *>(&value), sizeof(value));

            if (ret != sizeof(std::uint16_t)) {
            if (ret != sizeof(value)) {
                return kStatus_Fail;
            }
            return kStatus_Success;


@@ 231,9 224,9 @@ namespace bsp::battery_charger
            std::uint16_t value;
            int status                  = kStatus_Success;
            fuelGaugeAddress.subAddress = static_cast<std::uint32_t>(registerAddress);
            auto ret = i2c->Read(fuelGaugeAddress, reinterpret_cast<std::uint8_t *>(&value), sizeof(std::uint16_t));
            auto ret = i2c->Read(fuelGaugeAddress, reinterpret_cast<std::uint8_t *>(&value), sizeof(value));

            if (ret != sizeof(std::uint16_t)) {
            if (ret != sizeof(value)) {
                status = kStatus_Fail;
            }
            return std::make_pair(status, value);


@@ 242,10 235,9 @@ namespace bsp::battery_charger
        int chargerWrite(Registers registerAddress, std::uint8_t value)
        {
            batteryChargerAddress.subAddress = static_cast<std::uint32_t>(registerAddress);
            auto ret =
                i2c->Write(batteryChargerAddress, reinterpret_cast<std::uint8_t *>(&value), sizeof(std::uint8_t));
            auto ret = i2c->Write(batteryChargerAddress, reinterpret_cast<std::uint8_t *>(&value), sizeof(value));

            if (ret != sizeof(std::uint8_t)) {
            if (ret != sizeof(value)) {
                return kStatus_Fail;
            }
            return kStatus_Success;


@@ 256,9 248,9 @@ namespace bsp::battery_charger
            std::uint8_t value;
            int status                       = kStatus_Success;
            batteryChargerAddress.subAddress = static_cast<std::uint32_t>(registerAddress);
            auto ret                         = i2c->Read(batteryChargerAddress, &value, sizeof(std::uint8_t));
            auto ret                         = i2c->Read(batteryChargerAddress, &value, sizeof(value));

            if (ret != sizeof(std::uint8_t)) {
            if (ret != sizeof(value)) {
                status = kStatus_Fail;
            }
            return std::make_pair(status, value);


@@ 267,9 259,9 @@ namespace bsp::battery_charger
        int chargerTopControllerWrite(Registers registerAddress, std::uint8_t value)
        {
            topControllerAddress.subAddress = static_cast<std::uint32_t>(registerAddress);
            auto ret = i2c->Write(topControllerAddress, reinterpret_cast<std::uint8_t *>(&value), sizeof(std::uint8_t));
            auto ret = i2c->Write(topControllerAddress, reinterpret_cast<std::uint8_t *>(&value), sizeof(value));

            if (ret != sizeof(std::uint8_t)) {
            if (ret != sizeof(value)) {
                return kStatus_Fail;
            }
            return kStatus_Success;


@@ 280,42 272,46 @@ namespace bsp::battery_charger
            std::uint8_t value;
            int status                      = kStatus_Success;
            topControllerAddress.subAddress = static_cast<std::uint32_t>(registerAddress);
            auto ret                        = i2c->Read(topControllerAddress, &value, sizeof(std::uint8_t));
            if (ret != sizeof(std::uint8_t)) {
            const auto ret                  = i2c->Read(topControllerAddress, &value, sizeof(value));
            if (ret != sizeof(value)) {
                status = kStatus_Fail;
            }
            return std::make_pair(status, value);
        }

        batteryRetval unlockProtectedChargerRegisters()
        BatteryRetval unlockProtectedChargerRegisters()
        {
            if (chargerWrite(Registers::CHG_CNFG_06, UNLOCK_CHARGER) != kStatus_Success) {
                LOG_ERROR("Charger registers unlock failed!");
                return batteryRetval::ChargerError;
            if (chargerWrite(Registers::CHG_CNFG_06, static_cast<std::uint8_t>(ChargerSettingsProtection::Disable)) !=
                kStatus_Success) {
                LOG_ERROR("Failed to unlock charger registers");
                return BatteryRetval::ChargerError;
            }
            return batteryRetval::OK;
            return BatteryRetval::OK;
        }

        batteryRetval lockProtectedChargerRegisters()
        BatteryRetval lockProtectedChargerRegisters()
        {
            if (chargerWrite(Registers::CHG_CNFG_06, 0) != kStatus_Success) {
                LOG_ERROR("Charger registers lock failed!");
                return batteryRetval::ChargerError;
            if (chargerWrite(Registers::CHG_CNFG_06, static_cast<std::uint8_t>(ChargerSettingsProtection::Enable)) !=
                kStatus_Success) {
                LOG_ERROR("Failed to lock charger registers");
                return BatteryRetval::ChargerError;
            }
            return batteryRetval::OK;
            return BatteryRetval::OK;
        }

        void enableCharging()
        {
            if (chargerWrite(Registers::CHG_CNFG_00, CHG_ON_OTG_OFF_BUCK_ON) != kStatus_Success) {
                LOG_ERROR("Charge enable fail");
            if (chargerWrite(Registers::CHG_CNFG_00, static_cast<std::uint8_t>(ChargerMode::ChargerOnBuckOn)) !=
                kStatus_Success) {
                LOG_ERROR("Failed to enable charger");
            }
        }

        void disableCharging()
        {
            if (chargerWrite(Registers::CHG_CNFG_00, CHG_OFF_OTG_OFF_BUCK_ON) != kStatus_Success) {
                LOG_ERROR("Charge disable fail");
            if (chargerWrite(Registers::CHG_CNFG_00, static_cast<std::uint8_t>(ChargerMode::ChargerOffBuckOn)) !=
                kStatus_Success) {
                LOG_ERROR("Failed to disable charger");
            }
        }



@@ 324,7 320,7 @@ namespace bsp::battery_charger
            unlockProtectedChargerRegisters();
            const auto value = static_cast<std::uint8_t>(limit);
            if (chargerWrite(Registers::CHG_CNFG_02, value) != kStatus_Success) {
                LOG_ERROR("Fast charge current write fail");
                LOG_ERROR("Failed to set fast charge current");
            }
            lockProtectedChargerRegisters();
        }


@@ 332,33 328,37 @@ namespace bsp::battery_charger
        void setChargeTargetVoltage(ChargeTerminationVoltage voltage)
        {
            unlockProtectedChargerRegisters();
            std::uint8_t value = static_cast<std::uint8_t>(voltage) | VSYS_MIN;
            const auto value = static_cast<std::uint8_t>(voltage) | VSYS_MIN;
            if (chargerWrite(Registers::CHG_CNFG_04, value) != kStatus_Success) {
                LOG_ERROR("Charge target voltage write fail");
                LOG_ERROR("Failed to set charge target voltage");
            }
            lockProtectedChargerRegisters();
        }

        void resetUSBCurrrentLimit()
        void resetUSBCurrentLimit()
        {
            LOG_INFO("USB current limit set to 500mA");
            LOG_INFO("USB current limit reset to 500mA");
            setMaxBusCurrent(USBCurrentLimit::lim500mA);
        }

        void configureBatteryCharger()
        {
            setChargeTargetVoltage(ChargeTerminationVoltage::mV4350);
            resetUSBCurrrentLimit();
            resetUSBCurrentLimit();
            setMaxChargeCurrent(ChargeCurrentLimit::lim1600mA);
        }

        std::uint8_t getChargerDetails()
        std::optional<std::uint8_t> getChargerDetails()
        {
            auto status = chargerRead(Registers::CHG_DETAILS_01);
            return status.second & 0x0F;
            const auto [status, value] = chargerRead(Registers::CHG_DETAILS_01);
            if (status != kStatus_Success) {
                LOG_ERROR("Failed to get charger details");
                return std::nullopt;
            }
            return value & 0x0F;
        }

        batteryRetval configureFuelGaugeBatteryModel()
        BatteryRetval configureFuelGaugeBatteryModel()
        {
            int status = fuelGaugeWrite(Registers::LearnCFG_REG, fuel_gauge_params::LearnCFG);
            status |= fuelGaugeWrite(Registers::FilterCFG_REG, fuel_gauge_params::FilterCFG);


@@ 380,77 380,82 @@ namespace bsp::battery_charger
            status |= fuelGaugeWrite(Registers::FullSOCthr_REG, fuel_gauge_params::FullSOCthr);

            if (status != kStatus_Success) {
                LOG_ERROR("configureFuelGaugeBatteryModel failed.");
                return batteryRetval::ChargerError;
                LOG_ERROR("Failed to configure fuel gauge battery model");
                return BatteryRetval::ChargerError;
            }
            return batteryRetval::OK;
            return BatteryRetval::OK;
        }

        batteryRetval resetFuelGaugeModel()
        BatteryRetval resetFuelGaugeModel()
        {
            auto regVal = fuelGaugeRead(Registers::CONFIG2_REG);
            const auto [status, value] = fuelGaugeRead(Registers::CONFIG2_REG);
            if (status != kStatus_Success) {
                LOG_ERROR("Failed to read CONFIG2 register");
            }

            std::uint16_t toWrite = regVal.second | static_cast<std::uint16_t>(CONFIG2::LdMdl);
            const std::uint16_t toWrite = value | static_cast<std::uint16_t>(CONFIG2::LdMdl);

            if (fuelGaugeWrite(Registers::CONFIG2_REG, toWrite) != kStatus_Success) {
                LOG_ERROR("resetFuelGaugeModel failed.");
                return batteryRetval::ChargerError;
                LOG_ERROR("Failed to reset fuel gauge model");
                return BatteryRetval::ChargerError;
            }
            return batteryRetval::OK;
            return BatteryRetval::OK;
        }

        fileConfigRetval saveConfigurationFile(std::vector<Register> &savedData)
        FileConfigRetval saveConfigurationFile(std::vector<Register> &savedData)
        {
            std::ofstream file(cfgFile.c_str(), std::ios::binary | std::ios::out);
            if (!file.is_open()) {
                return fileConfigRetval::MissingFileError;
                return FileConfigRetval::MissingFileError;
            }
            if (savedData.size() != registersToStore) {
                return fileConfigRetval::DataSizeError;
                return FileConfigRetval::DataSizeError;
            }
            for (unsigned int i = 0; i < registersToStore; ++i) {

            for (auto i = 0; i < registersToStore; ++i) {
                auto regVal = fuelGaugeRead(static_cast<Registers>(i));
                if (regVal.first != kStatus_Success) {
                    file.close();
                    return fileConfigRetval::ReadingRegisterError;
                    return FileConfigRetval::ReadingRegisterError;
                }
                file.write(reinterpret_cast<const char *>(&regVal.second), sizeof(Register));
                file.write(reinterpret_cast<const char *>(&regVal.second), sizeof(regVal.second));
                savedData[i] = regVal.second;
            }
            file.close();
            addChecksumToConfigFile();
            return fileConfigRetval::OK;
            return FileConfigRetval::OK;
        }

        fileConfigRetval verifySavedConfigurationFile(const std::vector<Register> &dataForVerification)
        FileConfigRetval verifySavedConfigurationFile(const std::vector<Register> &dataForVerification)
        {
            std::ifstream readFile(cfgFile.c_str(), std::ios::binary | std::ios::in);
            if (!readFile.is_open()) {
                return fileConfigRetval::MissingFileError;
                return FileConfigRetval::MissingFileError;
            }
            if (dataForVerification.size() != registersToStore) {
                return fileConfigRetval::DataSizeError;
                return FileConfigRetval::DataSizeError;
            }

            std::uint16_t savedRegValue;
            for (unsigned int i = 0; i < registersToStore; ++i) {
            for (auto i = 0; i < registersToStore; ++i) {
                readFile.read(reinterpret_cast<char *>(&savedRegValue), sizeof(savedRegValue));
                if (savedRegValue != dataForVerification[i]) {
                    readFile.close();
                    return fileConfigRetval::StoreVerificationError;
                    return FileConfigRetval::StoreVerificationError;
                }
            }
            readFile.close();
            return fileConfigRetval::OK;
            return FileConfigRetval::OK;
        }

        fileConfigRetval readConfigurationFile(std::vector<Register> &readData)
        FileConfigRetval readConfigurationFile(std::vector<Register> &readData)
        {
            std::ifstream file(cfgFile.c_str(), std::ios::binary | std::ios::in);
            if (!file.is_open()) {
                return fileConfigRetval::MissingFileError;
                return FileConfigRetval::MissingFileError;
            }
            if (readData.size() != registersToStore) {
                return fileConfigRetval::DataSizeError;
                return FileConfigRetval::DataSizeError;
            }
            std::uint16_t regVal;
            for (auto i = 0; i < registersToStore; ++i) {


@@ 459,22 464,23 @@ namespace bsp::battery_charger
            }
            file.close();
            if (!isCorrectChecksumFromConfigFile()) {
                return fileConfigRetval::FileCrcError;
                return FileConfigRetval::FileCrcError;
            }
            return fileConfigRetval::OK;
            return FileConfigRetval::OK;
        }

        fileConfigRetval storeFuelGaugeRegisters(const std::vector<Register> &writeData)
        FileConfigRetval storeFuelGaugeRegisters(const std::vector<Register> &writeData)
        {
            if (writeData.size() != registersToStore) {
                return fileConfigRetval::DataSizeError;
                return FileConfigRetval::DataSizeError;
            }

            for (auto i = 0; i < registersToStore; ++i) {
                if (fuelGaugeWrite(static_cast<Registers>(i), writeData[i]) != kStatus_Success) {
                    return fileConfigRetval::WritingRegisterError;
                    return FileConfigRetval::WritingRegisterError;
                }
            }
            return fileConfigRetval::OK;
            return FileConfigRetval::OK;
        }

        bool isSocDataDiscrepancy(const units::SOC SoC1, const units::SOC SoC2)


@@ 482,48 488,48 @@ namespace bsp::battery_charger
            return (std::abs(SoC1 - SoC2) > maxSocPercentageError);
        }

        fileConfigRetval storeConfiguration()
        FileConfigRetval storeConfiguration()
        {
            std::vector<Register> storedData(registersToStore);

            if (auto retVal = saveConfigurationFile(storedData); retVal != fileConfigRetval::OK) {
            if (auto retVal = saveConfigurationFile(storedData); retVal != FileConfigRetval::OK) {
                LOG_ERROR("Save configuration file error: %s", magic_enum::enum_name(retVal).data());
                return retVal;
            }
            if (auto retVal = verifySavedConfigurationFile(storedData); retVal != fileConfigRetval::OK) {
            if (auto retVal = verifySavedConfigurationFile(storedData); retVal != FileConfigRetval::OK) {
                LOG_ERROR("Verify configuration file error: %s", magic_enum::enum_name(retVal).data());
                return retVal;
            }

            const auto soc = getBatteryLevelFromRegisterValue(storedData[static_cast<Register>(Registers::RepSOC_REG)]);
            LOG_INFO("Storing fuel gauge configuration (RawSoC: %d)", soc);
            LOG_INFO("Saved fuel gauge configuration (RawSoC: %d)", soc);

            return fileConfigRetval::OK;
            return FileConfigRetval::OK;
        }

        fileConfigRetval loadConfiguration()
        FileConfigRetval loadConfiguration()
        {
            // First, we load the default battery configuration
            if (configureFuelGaugeBatteryModel() == batteryRetval::OK) {
            if (configureFuelGaugeBatteryModel() == BatteryRetval::OK) {
                resetFuelGaugeModel();
            }
            else {
                LOG_ERROR("Configure fuel gauge battery model failed.");
                return fileConfigRetval::ConfigFuelGaugeModelError;
                LOG_ERROR("Failed to configure fuel gauge battery model");
                return FileConfigRetval::ConfigFuelGaugeModelError;
            }

            const auto batteryLevelAfterDefaultConfiguration = getBatteryLevel();
            if (batteryLevelAfterDefaultConfiguration != std::nullopt) {
            if (batteryLevelAfterDefaultConfiguration.has_value()) {
                rawSoc.update(batteryLevelAfterDefaultConfiguration.value());
            }
            std::vector<Register> readData(registersToStore);

            // then we read the battery configuration from the saved file
            if (auto retVal = readConfigurationFile(readData); retVal != fileConfigRetval::OK) {
            if (auto retVal = readConfigurationFile(readData); retVal != FileConfigRetval::OK) {
                // if there is a problem with reading the data, leave the default configuration and exit
                LOG_ERROR("Read configuration file error: %s", magic_enum::enum_name(retVal).data());
                storeConfiguration();
                return fileConfigRetval::OK;
                return FileConfigRetval::OK;
            }

            const auto savedBatteryLevel =


@@ 531,9 537,9 @@ namespace bsp::battery_charger

            // if the difference in SOC readings is less than 'maxSocPercentageError' then load the configuration read
            // from the file
            if (batteryLevelAfterDefaultConfiguration &&
                not isSocDataDiscrepancy(batteryLevelAfterDefaultConfiguration.value(), savedBatteryLevel)) {
                if (auto retVal = storeFuelGaugeRegisters(readData); retVal != fileConfigRetval::OK) {
            if (batteryLevelAfterDefaultConfiguration.has_value() &&
                !isSocDataDiscrepancy(batteryLevelAfterDefaultConfiguration.value(), savedBatteryLevel)) {
                if (auto retVal = storeFuelGaugeRegisters(readData); retVal != FileConfigRetval::OK) {
                    LOG_ERROR("Store configuration file error: %s", magic_enum::enum_name(retVal).data());
                    return retVal;
                }


@@ 547,93 553,90 @@ namespace bsp::battery_charger
                storeConfiguration();
            }

            return fileConfigRetval::OK;
            return FileConfigRetval::OK;
        }

        batteryRetval setTemperatureThresholds(std::uint8_t minTemperatureDegrees, std::uint8_t maxTemperatureDegrees)
        BatteryRetval setTemperatureThresholds(std::uint8_t minTemperatureDegrees, std::uint8_t maxTemperatureDegrees)
        {
            std::uint16_t regVal = (static_cast<uint16_t>(maxTemperatureDegrees) << 8) | minTemperatureDegrees;
            const std::uint16_t regVal =
                (static_cast<std::uint16_t>(maxTemperatureDegrees) << 8) | minTemperatureDegrees;
            if (fuelGaugeWrite(Registers::TALRT_Th_REG, regVal) != kStatus_Success) {
                LOG_ERROR("setTemperatureThresholds failed.");
                return batteryRetval::ChargerError;
                LOG_ERROR("Failed to set temperature thresholds");
                return BatteryRetval::ChargerError;
            }
            return batteryRetval::OK;
            return BatteryRetval::OK;
        }

        batteryRetval setServiceVoltageThresholds(std::uint16_t maxVoltage_mV, std::uint16_t minVoltage_mV)
        BatteryRetval setServiceVoltageThresholds(std::uint16_t minVoltage_mV, std::uint16_t maxVoltage_mV)
        {
            std::uint16_t regVal = ((maxVoltage_mV / 20) << 8) | (minVoltage_mV / 20);
            const std::uint16_t regVal = ((maxVoltage_mV / 20) << 8) | (minVoltage_mV / 20);

            if (fuelGaugeWrite(Registers::VALRT_Th_REG, regVal) != kStatus_Success) {

                LOG_ERROR("setServiceVoltageThresholds failed.");
                return batteryRetval::ChargerError;
                LOG_ERROR("Failed to set service voltage thresholds");
                return BatteryRetval::ChargerError;
            }
            return batteryRetval::OK;
            return BatteryRetval::OK;
        }

        batteryRetval fillConfig2RegisterValue()
        BatteryRetval fillConfigRegisterValue()
        {
            std::uint16_t regVal = static_cast<std::uint16_t>(CONFIG2::dSOCen) |  // SOC 1% change alert
                                   static_cast<std::uint16_t>(CONFIG2::TAlrtEn) | // Temperature alerts
                                   static_cast<std::uint16_t>(CONFIG2::OCVQen); // Enable  automatic empty compensation
            const std::uint16_t regVal = static_cast<std::uint16_t>(CONFIG::Aen)      // Enable alerts
                                         | static_cast<std::uint16_t>(CONFIG::Ten)    // Enable temperature conversion
                                         | static_cast<std::uint16_t>(CONFIG::ETHRM); // External thermistor

            if (fuelGaugeWrite(Registers::CONFIG2_REG, regVal) != kStatus_Success) {
                LOG_ERROR("fillConfig2RegisterValue failed.");
                return batteryRetval::ChargerError;
            if (fuelGaugeWrite(Registers::CONFIG_REG, regVal) != kStatus_Success) {
                LOG_ERROR("Failed to fill CONFIG register");
                return BatteryRetval::ChargerError;
            }

            return batteryRetval::OK;
            return BatteryRetval::OK;
        }

        batteryRetval fillConfigRegisterValue()
        BatteryRetval fillConfig2RegisterValue()
        {
            std::uint16_t regVal = static_cast<std::uint16_t>(CONFIG::Aen)      // Enable alerts
                                   | static_cast<std::uint16_t>(CONFIG::Ten)    // Enable tepreature conversion
                                   | static_cast<std::uint16_t>(CONFIG::ETHRM); // External thermistor
            const std::uint16_t regVal =
                static_cast<std::uint16_t>(CONFIG2::dSOCen) |  // SOC 1% change alert
                static_cast<std::uint16_t>(CONFIG2::TAlrtEn) | // Temperature alerts
                static_cast<std::uint16_t>(CONFIG2::OCVQen);   // Enable automatic empty compensation

            if (fuelGaugeWrite(Registers::CONFIG_REG, regVal) != kStatus_Success) {
                LOG_ERROR("fillConfigRegisterValue failed.");
                return batteryRetval::ChargerError;
            if (fuelGaugeWrite(Registers::CONFIG2_REG, regVal) != kStatus_Success) {
                LOG_ERROR("Failed to fill CONFIG2 register");
                return BatteryRetval::ChargerError;
            }

            return batteryRetval::OK;
            return BatteryRetval::OK;
        }

        batteryRetval configureTemperatureMeasurement()
        BatteryRetval configureTemperatureMeasurement()
        {
            if ((fuelGaugeWrite(Registers::TGAIN_REG, temperatureConversionGain) &
                 fuelGaugeWrite(Registers::TOFF_REG, temperatureConversionOffset)) != kStatus_Success) {
                LOG_ERROR("configureTemperatureMeasurement failed.");
                return batteryRetval::ChargerError;
                LOG_ERROR("Failed to configure temperature measurement");
                return BatteryRetval::ChargerError;
            }

            return batteryRetval::OK;
            return BatteryRetval::OK;
        }

        batteryRetval enableTopIRQs()
        BatteryRetval enableTopIRQs()
        {
            std::uint8_t val = ENABLE_CHG_FG_IRQ_MASK;
            const std::uint8_t mask = ~(static_cast<std::uint8_t>(TOP_MASK::CHGR_INT_MASK) |
                                        static_cast<std::uint8_t>(TOP_MASK::FG_INT_MASK));

            if (chargerTopControllerWrite(Registers::TOP_CONTROLL_IRQ_MASK_REG, val) != kStatus_Success) {
                LOG_ERROR("enableIRQs failed.");
                return batteryRetval::ChargerError;
            if (chargerTopControllerWrite(Registers::TOP_CONTROLL_IRQ_MASK_REG, mask) != kStatus_Success) {
                LOG_ERROR("Failed to enable top-level controller IRQs");
                return BatteryRetval::ChargerError;
            }

            return batteryRetval::OK;
            return BatteryRetval::OK;
        }

        batteryRetval enableChargerIRQs()
        BatteryRetval enableChargerIRQs()
        {
            std::uint8_t mask = ~(static_cast<std::uint8_t>(CHG_MASK::CHG_M) |
                                  static_cast<std::uint8_t>(CHG_MASK::CHGIN_M)); // unmask IRQs
            const std::uint8_t mask =
                ~(static_cast<std::uint8_t>(CHG_MASK::CHG_M) | static_cast<std::uint8_t>(CHG_MASK::CHGIN_M));

            if (chargerWrite(Registers::CHG_INT_MASK, mask) != kStatus_Success) {
                LOG_ERROR("enableChargerIRQs failed.");
                return batteryRetval::ChargerError;
                LOG_ERROR("Failed to enable charger IRQs");
                return BatteryRetval::ChargerError;
            }

            return batteryRetval::OK;
            return BatteryRetval::OK;
        }

        void IRQPinsInit()


@@ 649,28 652,24 @@ namespace bsp::battery_charger
            INTBPinConfig.pin      = static_cast<std::uint32_t>(BoardDefinitions::BATTERY_CHARGER_INTB_PIN);
            gpio->ConfPin(INTBPinConfig);

            drivers::DriverGPIOPinParams INOKBPinConfig{};
            INOKBPinConfig.dir      = drivers::DriverGPIOPinParams::Direction::Input;
            INOKBPinConfig.irqMode  = drivers::DriverGPIOPinParams::InterruptMode::IntRisingOrFallingEdge;
            INOKBPinConfig.defLogic = 0;
            INOKBPinConfig.pin      = static_cast<std::uint32_t>(BoardDefinitions::BATTERY_CHARGER_INOKB_PIN);
            gpio->ConfPin(INOKBPinConfig);

            gpio->EnableInterrupt(1 << static_cast<std::uint32_t>(BoardDefinitions::BATTERY_CHARGER_INTB_PIN));
            gpio->EnableInterrupt(1 << static_cast<std::uint32_t>(BoardDefinitions::BATTERY_CHARGER_INOKB_PIN));
        }

        int getCellTemperature()
        std::optional<int> getCellTemperature()
        {
            auto value = fuelGaugeRead(Registers::TEMP_REG);
            const auto [status, value] = fuelGaugeRead(Registers::TEMP_REG);
            if (status != kStatus_Success) {
                LOG_ERROR("Failed to read cell temperature");
                return std::nullopt;
            }
            // Round to integer by stripping fractions
            std::uint8_t temperatureInt = value.second >> 8;
            const std::uint8_t temperatureInt = value >> 8;
            return utils::twosComplimentToInt(temperatureInt);
        }

        void chargingFinishedAction()
        {
            LOG_DEBUG("Charging finished.");
            LOG_DEBUG("Charging finished");
        }

        void processTemperatureRange(TemperatureRanges temperatureRange)


@@ 695,11 694,11 @@ namespace bsp::battery_charger
                setMaxChargeCurrent(ChargeCurrentLimit::lim1600mA);
                break;
            case TemperatureRanges::Cold:
                LOG_DEBUG("Cell temperature too low, charging disabled.");
                LOG_DEBUG("Cell temperature too low, charging disabled");
                disableCharging();
                break;
            case TemperatureRanges::Hot:
                LOG_DEBUG("Cell temperature too high, charging disabled.");
                LOG_DEBUG("Cell temperature too high, charging disabled");
                disableCharging();
                break;
            }


@@ 707,7 706,6 @@ namespace bsp::battery_charger
            setTemperatureThresholds(temperatureRanges.at(temperatureRange).alertLow,
                                     temperatureRanges.at(temperatureRange).alertHigh);
        }

    } // namespace

    int init()


@@ 719,15 717,18 @@ namespace bsp::battery_charger

        configureBatteryCharger();

        // check Power-On reset bit
        std::uint16_t status = fuelGaugeRead(Registers::STATUS_REG).second;
        // Check power-on reset bit
        const auto [status, registerValue] = fuelGaugeRead(Registers::STATUS_REG);
        if (status != kStatus_Success) {
            LOG_INFO("Failed to check POR bit!");
        }

        if (status & static_cast<std::uint16_t>(STATUS::POR)) {
        if (static_cast<bool>(registerValue & static_cast<std::uint16_t>(STATUS::POR))) {
            LOG_INFO("Initializing battery fuel gauge model.");
            loadConfiguration();
        }

        setServiceVoltageThresholds(maxVoltagemV, minVoltagemV);
        setServiceVoltageThresholds(minVoltagemV, maxVoltagemV);

        fillConfigRegisterValue();
        fillConfig2RegisterValue();


@@ 739,21 740,20 @@ namespace bsp::battery_charger
        vTaskDelay(pdMS_TO_TICKS(100));

        clearAllChargerIRQs();
        clearFuelGuageIRQ(static_cast<std::uint16_t>(batteryINTBSource::all));
        clearFuelGaugeIRQ(static_cast<std::uint16_t>(BatteryINTBSource::all));
        enableTopIRQs();
        enableChargerIRQs();
        IRQPinsInit();

        return 0;
        return kStatus_Success;
    }

    void deinit()
    {
        resetUSBCurrrentLimit();
        resetUSBCurrentLimit();
        storeConfiguration();

        gpio->DisableInterrupt(1 << static_cast<uint32_t>(BoardDefinitions::BATTERY_CHARGER_INTB_PIN));
        gpio->DisableInterrupt(1 << static_cast<std::uint32_t>(BoardDefinitions::BATTERY_CHARGER_INOKB_PIN));
        gpio->DisableInterrupt(1 << static_cast<std::uint32_t>(BoardDefinitions::BATTERY_CHARGER_INTB_PIN));

        i2c.reset();
        gpio.reset();


@@ 770,12 770,14 @@ namespace bsp::battery_charger
            return;
        }

        const auto status = getChargeStatus();
        units::SOC percentLevelDelta;

        const auto status = getChargeStatus();
        switch (status) {
        case batteryRetval::ChargerCharging:
        case BatteryRetval::ChargerCharging:
            percentLevelDelta = chargingSocDelta;
            break;

        default:
            percentLevelDelta = dischargingSocDelta;
            break;


@@ 790,60 792,76 @@ namespace bsp::battery_charger
    {
        auto readout = fuelGaugeRead(Registers::RepSOC_REG);
        if (readout.first != kStatus_Success) {
            LOG_ERROR("failed to get battery percent");
            LOG_ERROR("Failed to get battery level");
            return std::nullopt;
        }

        auto soc = getBatteryLevelFromRegisterValue(readout.second);
        if (soc > fullyChargedSOC) {
            // Sometimes MAX77818 can return soc > 100% when config file is missing
            // or Soc is based on voltage, so we need to truncate value.
            LOG_WARN("Raw SOC value has been truncated (read value: %d)", soc);
            // Sometimes MAX77818 can return SOC > 100% when config file is missing
            // or SOC is based on voltage, so we need to truncate value.
            LOG_WARN("Raw SOC value has been truncated (value read: %d)", soc);
            soc = fullyChargedSOC;
        }
        return soc;
    }

    batteryRetval getChargeStatus()
    BatteryRetval getChargeStatus()
    {
        batteryRetval retVal{};
        // read clears state
        auto IRQSource = chargerRead(Registers::CHG_INT_REG);
        BatteryRetval retVal{};

        // Read register to clear pending interrupts
        const auto IRQSource = chargerRead(Registers::CHG_INT_REG);
        if (IRQSource.first != kStatus_Success) {
            LOG_ERROR("failed to read charge INT source");
            LOG_ERROR("Failed to read charger INT source");
        }
        auto summary = chargerRead(Registers::CHG_INT_OK);

        const auto summary = chargerRead(Registers::CHG_INT_OK);
        if (summary.first != kStatus_Success) {
            LOG_ERROR("failed to read charge summary");
            LOG_ERROR("Failed to read charger summary");
            return BatteryRetval::ChargerError;
        }

        const auto chargerDetails = getChargerDetails();
        if (!chargerDetails.has_value()) {
            return BatteryRetval::ChargerError;
        }
        auto chargerDetails = getChargerDetails();
        auto chargingSetup  = chargerRead(Registers::CHG_CNFG_00);

        if (summary.second & static_cast<std::uint8_t>(CHG_INT::CHGIN_I)) {
            retVal = batteryRetval::ChargerCharging;
        const auto chargingSetup = chargerRead(Registers::CHG_CNFG_00);
        if (chargingSetup.first != kStatus_Success) {
            LOG_ERROR("Failed to read charging setup!");
            return BatteryRetval::ChargerError;
        }

        if (static_cast<bool>(summary.second & static_cast<std::uint8_t>(CHG_INT::CHGIN_I))) {
            retVal = BatteryRetval::ChargerCharging;
        }
        else {
            retVal = batteryRetval::ChargerNotCharging;
            retVal = BatteryRetval::ChargerNotCharging;
        }

        switch (static_cast<CHG_DETAILS_01>(chargerDetails)) {
        switch (static_cast<CHG_DETAILS_01>(chargerDetails.value())) {
        case CHG_DETAILS_01::CHARGER_DONE:
            retVal = batteryRetval::ChargingDone;
            retVal = BatteryRetval::ChargingDone;
            chargingFinishedAction();
            break;

        case CHG_DETAILS_01::CHARGER_OFF:
            if (chargingSetup.second != CHG_ON_OTG_OFF_BUCK_ON) {
                retVal = batteryRetval::ChargerNotCharging;
            if (chargingSetup.second != static_cast<std::uint8_t>(ChargerMode::ChargerOnBuckOn)) {
                retVal = BatteryRetval::ChargerNotCharging;
            }
            break;

        case CHG_DETAILS_01::CHARGER_PREQUALIFICATION:
        case CHG_DETAILS_01::CHARGER_CC:
        case CHG_DETAILS_01::CHARGER_CV:
        case CHG_DETAILS_01::CHARGER_TOPOFF:
            retVal = batteryRetval::ChargerCharging;
            retVal = BatteryRetval::ChargerCharging;
            break;

        case CHG_DETAILS_01::CHARGER_TIMER_FAULT:
        case CHG_DETAILS_01::CHARGER_BATTERY_DETECT:
            retVal = batteryRetval::ChargerError;
            retVal = BatteryRetval::ChargerError;
            break;
        }



@@ 852,30 870,45 @@ namespace bsp::battery_charger

    Register getStatusRegister()
    {
        auto status = fuelGaugeRead(Registers::STATUS_REG);
        return status.second;
        const auto [status, value] = fuelGaugeRead(Registers::STATUS_REG);
        if (status != kStatus_Success) {
            LOG_ERROR("Failed to read STATUS register");
        }
        return value;
    }

    void clearAllChargerIRQs()
    {
        auto value = chargerRead(Registers::CHG_INT_REG);
        if (value.second != 0) {
            // write zero to clear irq source
            chargerWrite(Registers::CHG_INT_REG, 0);
        const auto [status, value] = chargerRead(Registers::CHG_INT_REG);
        if (status != kStatus_Success) {
            LOG_ERROR("Failed to read charger IRQs register");
        }

        if (value != 0) {
            // Write zero to clear IRQ sources
            if (chargerWrite(Registers::CHG_INT_REG, 0) != kStatus_Success) {
                LOG_ERROR("Failed to clear charger IRQs");
            }
        }
    }

    void clearFuelGuageIRQ(std::uint16_t intToClear)
    void clearFuelGaugeIRQ(std::uint16_t intToClear)
    {
        auto readout          = fuelGaugeRead(Registers::STATUS_REG);
        std::uint16_t toWrite = readout.second & (~intToClear);
        fuelGaugeWrite(Registers::STATUS_REG, toWrite);
        const auto readout          = getStatusRegister();
        const std::uint16_t toWrite = readout & (~intToClear);
        if (fuelGaugeWrite(Registers::STATUS_REG, toWrite) != kStatus_Success) {
            LOG_ERROR("Failed to clear fuel gauge IRQ");
        }
    }

    void checkTemperatureRange()
    {
        int temperature = getCellTemperature();
        LOG_DEBUG("Cell temperature: %d", temperature);
        const auto temperature = getCellTemperature();
        if (!temperature.has_value()) {
            return;
        }

        LOG_DEBUG("Cell temperature: %dC", temperature.value());

        for (const auto &[range, thresholds] : temperatureRanges) {
            if ((temperature > thresholds.lowTemperatureThreshold) &&


@@ 886,69 919,87 @@ namespace bsp::battery_charger
        }
    }

    std::uint8_t getTopControllerINTSource()
    std::optional<std::uint8_t> getTopControllerINTSource()
    {
        auto value = chargerTopControllerRead(Registers::TOP_CONTROLL_IRQ_SRC_REG);
        return value.second;
        const auto [status, value] = chargerTopControllerRead(Registers::TOP_CONTROLL_IRQ_SRC_REG);
        if (status != kStatus_Success) {
            LOG_ERROR("Failed to read interrupt source");
            return std::nullopt;
        }
        return value;
    }

    void setMaxBusCurrent(USBCurrentLimit limit)
    {
        const auto value = static_cast<std::uint8_t>(limit);
        if (chargerWrite(Registers::CHG_CNFG_09, value) != kStatus_Success) {
            LOG_ERROR("Maximum usb current write fail");
            LOG_ERROR("Failed to set maximum USB current");
        }
    }

    int getVoltageFilteredMeasurement()
    std::optional<int> getVoltageFilteredMeasurement()
    {
        const auto [_, value] = fuelGaugeRead(Registers::AvgVCELL_REG);
        int voltage           = value * voltageSenseGain;
        return voltage;
        const auto [status, value] = fuelGaugeRead(Registers::AvgVCELL_REG);
        if (status != kStatus_Success) {
            LOG_ERROR("Failed to get filtered voltage measurement");
            return std::nullopt;
        }
        return static_cast<int>(value * voltageSenseGain);
    }

    int getAvgCurrent()
    std::optional<int> getAvgCurrent()
    {
        const auto [_, value] = fuelGaugeRead(Registers::AvgCurrent_REG);
        const auto [status, value] = fuelGaugeRead(Registers::AvgCurrent_REG);
        if (status != kStatus_Success) {
            LOG_ERROR("Failed to get average current measurement");
            return std::nullopt;
        }
        return static_cast<int>(utils::twosComplimentToInt(value) * currentSenseGain);
    }

    int getCurrentMeasurement()
    std::optional<int> getCurrentMeasurement()
    {
        auto [_, value] = fuelGaugeRead(Registers::Current_REG);
        const auto [status, value] = fuelGaugeRead(Registers::Current_REG);
        if (status != kStatus_Success) {
            LOG_ERROR("Failed to get current measurement");
            return std::nullopt;
        }
        return static_cast<int>(utils::twosComplimentToInt(value) * currentSenseGain);
    }

    MaxMinVolt getMaxMinVolt()
    std::optional<MaxMinVolt> getMaxMinVolt()
    {
        MaxMinVolt ret;

        const auto [_, value] = fuelGaugeRead(Registers::MaxMinVolt_REG);
        ret.maxMilliVolt      = ((value & 0xFF00) >> 8) * maxMinMilliVoltGain;
        ret.minMilliVolt      = (value & 0xFF) * maxMinMilliVoltGain;
        const auto [status, value] = fuelGaugeRead(Registers::MaxMinVolt_REG);
        if (status != kStatus_Success) {
            LOG_ERROR("Failed to get MaxMin voltage measurement");
            return std::nullopt;
        }

        MaxMinVolt ret;
        ret.maxMilliVolt = ((value & 0xFF00) >> 8) * maxMinMilliVoltGain;
        ret.minMilliVolt = (value & 0xFF) * maxMinMilliVoltGain;
        return ret;
    }

    void printFuelGaugeInfo()
    {
        const auto maxMinVolt = getMaxMinVolt();
        const auto maxMinVoltage = getMaxMinVolt().value_or(MaxMinVolt{0, 0});

        LOG_INFO("Fuel Gauge info:");
        LOG_INFO("\tAvgVCell: %dmV", getVoltageFilteredMeasurement());
        LOG_INFO("\tMaxVolt: %dmV", maxMinVolt.maxMilliVolt);
        LOG_INFO("\tMinVolt: %dmV", maxMinVolt.minMilliVolt);
        LOG_INFO("\tAvgCurrent: %dmA", getAvgCurrent());
        LOG_INFO("\tRawSoC: %d%%", getBatteryLevel().value());
        LOG_INFO("\tAvgVCell: %dmV", getVoltageFilteredMeasurement().value_or(0));
        LOG_INFO("\tMaxVolt: %dmV", maxMinVoltage.maxMilliVolt);
        LOG_INFO("\tMinVolt: %dmV", maxMinVoltage.minMilliVolt);
        LOG_INFO("\tAvgCurrent: %dmA", getAvgCurrent().value_or(0));
        LOG_INFO("\tRawSoC: %d%%", getBatteryLevel().value_or(0));
    }

    bool isChargerPlugged()
    {
        const auto chargerStatus = chargerRead(Registers::CHG_INT_OK);
        if (chargerStatus.first != kStatus_Success) {
            LOG_ERROR("failed to read charger status");
            LOG_ERROR("Failed to read charger status");
            return false;
        }
        return chargerStatus.second & static_cast<std::uint8_t>(CHG_INT::CHGIN_I);
        return static_cast<bool>(chargerStatus.second & static_cast<std::uint8_t>(CHG_INT::CHGIN_I));
    }
} // namespace bsp::battery_charger

M module-bsp/board/rt1051/puretx/bsp/battery_charger/battery_charger.hpp => module-bsp/board/rt1051/puretx/bsp/battery_charger/battery_charger.hpp +17 -29
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 13,17 13,17 @@ namespace bsp::battery_charger
{
    using Register = std::uint16_t;

    enum class batteryChargerType
    enum class BatteryChargerType
    {
        DcdTimeOut = 0x00U, /*!< Dcd detect result is timeout */
        DcdUnknownType,     /*!< Dcd detect result is unknown type */
        DcdError,           /*!< Dcd detect result is error*/
        DcdError,           /*!< Dcd detect result is error */
        DcdSDP,             /*!< The SDP facility is detected */
        DcdCDP,             /*!< The CDP facility is detected */
        DcdDCP,             /*!< The DCP facility is detected */
    };

    enum class batteryRetval
    enum class BatteryRetval
    {
        OK,
        ChargerError,


@@ 32,26 32,20 @@ namespace bsp::battery_charger
        ChargingDone
    };

    enum class batteryIRQSource
    {
        INTB  = 0x01,
        INOKB = 0x02
    };

    enum class topControllerIRQsource
    enum class TopControllerIRQsource
    {
        CHGR_INT = (1 << 0),
        FG_INT   = (1 << 1),
        SYS_INT  = (1 << 2)
    };

    enum class batteryINTBSource
    enum class BatteryINTBSource
    {
        maxTemp             = 1 << 13,
        minSOCAlert         = 1 << 10,
        minTemp             = 1 << 9,
        minVAlert           = 1 << 8,
        SOCOnePercentChange = 1 << 7,
        SOCOnePercentChange = (1 << 7),
        minVAlert           = (1 << 8),
        minTemp             = (1 << 9),
        minSOCAlert         = (1 << 10),
        maxTemp             = (1 << 13),
        all                 = 0xFFFF
    };



@@ 62,35 56,29 @@ namespace bsp::battery_charger
    };

    int init();

    void deinit();

    [[nodiscard]] std::optional<units::SOC> getBatteryLevel();

    void storeBatteryLevelChange(const units::SOC newSocValue);
    void storeBatteryLevelChange(units::SOC newSocValue);

    batteryRetval getChargeStatus();
    BatteryRetval getChargeStatus();

    void clearAllChargerIRQs();

    void clearFuelGuageIRQ(std::uint16_t intToClear);
    void clearFuelGaugeIRQ(std::uint16_t intToClear);

    Register getStatusRegister();

    void checkTemperatureRange();

    std::uint8_t getTopControllerINTSource();
    std::optional<std::uint8_t> getTopControllerINTSource();

    void setMaxBusCurrent(USBCurrentLimit limit);

    int getVoltageFilteredMeasurement();

    MaxMinVolt getMaxMinVolt();
    std::optional<int> getVoltageFilteredMeasurement();
    std::optional<MaxMinVolt> getMaxMinVolt();

    void printFuelGaugeInfo();

    bool checkConfigurationFile(std::ifstream &file);

    bool isChargerPlugged();

} // namespace bsp::battery_charger

M module-bsp/board/rt1051/puretx/bsp/battery_charger/battery_charger_utils.hpp => module-bsp/board/rt1051/puretx/bsp/battery_charger/battery_charger_utils.hpp +9 -6
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once



@@ 18,18 18,21 @@ namespace bsp::battery_charger::utils
            bitset.flip();
            return static_cast<int>(bitset.to_ulong() * -1);
        }
        else {
            return static_cast<int>(toConvert);
        }
        return static_cast<int>(toConvert);
    }

    std::size_t getFileSize(std::ifstream &file)
    {
        const auto currentPosition = file.tellg();

        file.seekg(0, std::ios::beg);
        const auto begin = file.tellg();

        file.seekg(0, std::ios::end);
        const auto end = file.tellg();
        file.seekg(0, std::ios::beg);

        file.seekg(currentPosition, std::ios::beg);

        return end - begin;
    }

} // namespace bsp::battery_charger::utils

M module-bsp/board/rt1051/puretx/hal/battery_charger/BatteryCharger.cpp => module-bsp/board/rt1051/puretx/hal/battery_charger/BatteryCharger.cpp +84 -70
@@ 21,9 21,9 @@
namespace
{
    hal::battery::AbstractBatteryCharger::ChargingStatus transformChargingState(
        bsp::battery_charger::batteryRetval status)
        bsp::battery_charger::BatteryRetval status)
    {
        using Status   = bsp::battery_charger::batteryRetval;
        using Status   = bsp::battery_charger::BatteryRetval;
        using NewState = hal::battery::AbstractBatteryCharger::ChargingStatus;
        switch (status) {
        case Status::ChargerCharging:


@@ 39,25 39,26 @@ namespace

    /// A few constants to make code readability better
    constexpr auto int_b_soc_change =
        static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::SOCOnePercentChange);
    constexpr auto int_b_max_temp = static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::maxTemp);
    constexpr auto int_b_min_temp = static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::minTemp);
    constexpr auto int_b_all      = static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::all);
    constexpr auto int_b_miv_v    = static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::minVAlert);
        static_cast<std::uint16_t>(bsp::battery_charger::BatteryINTBSource::SOCOnePercentChange);
    constexpr auto int_b_max_temp = static_cast<std::uint16_t>(bsp::battery_charger::BatteryINTBSource::maxTemp);
    constexpr auto int_b_min_temp = static_cast<std::uint16_t>(bsp::battery_charger::BatteryINTBSource::minTemp);
    constexpr auto int_b_all      = static_cast<std::uint16_t>(bsp::battery_charger::BatteryINTBSource::all);
    constexpr auto int_b_miv_v    = static_cast<std::uint16_t>(bsp::battery_charger::BatteryINTBSource::minVAlert);

    constexpr auto int_source_charger =
        static_cast<std::uint8_t>(bsp::battery_charger::topControllerIRQsource::CHGR_INT);
        static_cast<std::uint8_t>(bsp::battery_charger::TopControllerIRQsource::CHGR_INT);

    constexpr auto int_source_fuel_gauge =
        static_cast<std::uint8_t>(bsp::battery_charger::topControllerIRQsource::FG_INT);
        static_cast<std::uint8_t>(bsp::battery_charger::TopControllerIRQsource::FG_INT);

    constexpr uint16_t clearStatusTickTime_MS = 10000;
    constexpr auto irqClearTimerName   = "BatteryIrqClearTimer";
    constexpr auto irqClearTimerTimeMs = 10000;

    void clearIrqStatusHandler(TimerHandle_t xTimer)
    void clearIrqStatusHandler([[maybe_unused]] TimerHandle_t xTimer)
    {
        if (bsp::battery_charger::getStatusRegister()) {
            bsp::battery_charger::clearFuelGuageIRQ(
                static_cast<std::uint16_t>(bsp::battery_charger::batteryINTBSource::all));
        if (bsp::battery_charger::getStatusRegister() != 0) {
            bsp::battery_charger::clearFuelGaugeIRQ(
                static_cast<std::uint16_t>(bsp::battery_charger::BatteryINTBSource::all));
        }
    }
} // namespace


@@ 71,8 72,8 @@ namespace hal::battery
        {
            enum class Type
            {
                Controller,
                USBChargerAttached
                ControllerINTB,
                USBChargerAttached,
            };
            Type type;
            std::uint8_t chargerType{};


@@ 90,37 91,41 @@ namespace hal::battery
        static BatteryWorkerQueue &getWorkerQueueHandle();

      private:
        static constexpr auto workerStackSize = 2048;
        static constexpr auto irqWorkerStackSize = 1024 * 2;

        void setChargingCurrentLimit(std::uint8_t usbType);
        void sendNotification(AbstractBatteryCharger::Events event);
        void checkControllerInterrupts();

        QueueHandle_t notificationChannel;
        TimerHandle_t timerHandle;
        TimerHandle_t irqClearTimerHandle;
        inline static std::unique_ptr<BatteryWorkerQueue> workerQueue;
    };

    PureBatteryCharger::PureBatteryCharger(QueueHandle_t irqQueueHandle)
        : notificationChannel{irqQueueHandle},
          timerHandle{xTimerCreate(
              "clearIrqStatusTimer", pdMS_TO_TICKS(clearStatusTickTime_MS), false, nullptr, clearIrqStatusHandler)}
          irqClearTimerHandle{xTimerCreate(
              irqClearTimerName, pdMS_TO_TICKS(irqClearTimerTimeMs), pdFALSE, nullptr, clearIrqStatusHandler)}
    {

        workerQueue = std::make_unique<BatteryWorkerQueue>(
            "battery_charger",
            [this](const auto &msg) {
                switch (msg.type) {
                case IrqEvents::Type::Controller: {
                case IrqEvents::Type::ControllerINTB:
                    checkControllerInterrupts();
                } break;
                    break;

                case IrqEvents::Type::USBChargerAttached: {
                case IrqEvents::Type::USBChargerAttached:
                    setChargingCurrentLimit(msg.chargerType);
                } break;
                    break;

                default:
                    LOG_ERROR("Unhandled IrqEvent %d", static_cast<int>(msg.type));
                    break;
                }
            },
            workerStackSize);
            irqWorkerStackSize);

        bsp::battery_charger::init();
        bsp::battery_charger::printFuelGaugeInfo();


@@ 129,109 134,124 @@ namespace hal::battery
        CurrentMeasurementScope::start();
#endif
    }

    PureBatteryCharger::~PureBatteryCharger()
    {
        bsp::battery_charger::deinit();
    }

    void PureBatteryCharger::setChargingCurrentLimit(std::uint8_t usbType)
    {
        using namespace bsp::battery_charger;
        const auto event = static_cast<batteryChargerType>(usbType);
        LOG_INFO("USB charging port discovery result: %s", std::string{magic_enum::enum_name(event)}.c_str());

        const auto event = static_cast<BatteryChargerType>(usbType);
        LOG_INFO("USB charging port discovery result: %s", magic_enum::enum_name(event).data());

        switch (event) {
        case batteryChargerType::DcdTimeOut:
        case batteryChargerType::DcdUnknownType:
        case batteryChargerType::DcdError:
        case batteryChargerType::DcdSDP:
        case BatteryChargerType::DcdTimeOut:
        case BatteryChargerType::DcdUnknownType:
        case BatteryChargerType::DcdError:
        case BatteryChargerType::DcdSDP:
            LOG_INFO("USB current limit set to 500mA");
            setMaxBusCurrent(USBCurrentLimit::lim500mA);
            break;
        case batteryChargerType::DcdCDP:
        case batteryChargerType::DcdDCP:

        case BatteryChargerType::DcdCDP:
        case BatteryChargerType::DcdDCP:
            LOG_INFO("USB current limit set to 1000mA");
            setMaxBusCurrent(USBCurrentLimit::lim1000mA);
            break;
        }
    }

    std::optional<AbstractBatteryCharger::Voltage> PureBatteryCharger::getBatteryVoltage() const
    {
        return bsp::battery_charger::getVoltageFilteredMeasurement();
    }

    std::optional<AbstractBatteryCharger::SOC> PureBatteryCharger::getSOC() const
    {
        const auto soc = bsp::battery_charger::getBatteryLevel();
        if (not soc) {
        if (!soc.has_value()) {
            LOG_ERROR("Cannot read SOC (I2C issue)");
            return std::nullopt;
        }
        bsp::battery_charger::storeBatteryLevelChange(soc.value());
        const auto scaled_soc = scale_soc(soc.value());
        if (not scaled_soc) {
        const auto scaledSoc = scale_soc(soc.value());
        if (!scaledSoc.has_value()) {
            LOG_ERROR("SOC is out of valid range. SOC: %d", soc.value());
            return std::nullopt;
        }
        return scaled_soc;
        return scaledSoc;
    }

    AbstractBatteryCharger::ChargingStatus PureBatteryCharger::getChargingStatus() const
    {
        return transformChargingState(bsp::battery_charger::getChargeStatus());
    }

    AbstractBatteryCharger::ChargerPresence PureBatteryCharger::getChargerPresence() const
    {
        return bsp::battery_charger::isChargerPlugged() ? AbstractBatteryCharger::ChargerPresence::PluggedIn
                                                        : AbstractBatteryCharger::ChargerPresence::Unplugged;
    }

    PureBatteryCharger::BatteryWorkerQueue &PureBatteryCharger::getWorkerQueueHandle()
    {
        return *workerQueue;
    }

    void PureBatteryCharger::checkControllerInterrupts()
    {
        std::array<std::optional<Events>, 4> events;
        std::vector<Events> eventsToProcess;

        const auto topINT = bsp::battery_charger::getTopControllerINTSource();
        if (topINT & int_source_charger) {
        const auto IRQSource = bsp::battery_charger::getTopControllerINTSource();
        if (!IRQSource.has_value()) {
            return;
        }

        if (static_cast<bool>(IRQSource.value() & int_source_charger)) {
            bsp::battery_charger::clearAllChargerIRQs();
            events[magic_enum::enum_integer(Events::Charger)] = Events::Charger;
            eventsToProcess.push_back(Events::Charger);
        }
        if (topINT & int_source_fuel_gauge) {
        if (static_cast<bool>(IRQSource.value() & int_source_fuel_gauge)) {
            const auto status = bsp::battery_charger::getStatusRegister();
            if (status & int_b_miv_v) {
                bsp::battery_charger::clearFuelGuageIRQ(int_b_miv_v);

                if (bsp::battery_charger::getChargeStatus() == bsp::battery_charger::batteryRetval::ChargerCharging) {
                    if (xTimerIsTimerActive(timerHandle) == pdFALSE) {
                        xTimerStart(timerHandle, 0);
                        LOG_DEBUG("Battery Brownout detected while charging");
            if (static_cast<bool>(status & int_b_miv_v)) {
                bsp::battery_charger::clearFuelGaugeIRQ(int_b_miv_v);
                if (bsp::battery_charger::getChargeStatus() == bsp::battery_charger::BatteryRetval::ChargerCharging) {
                    if (xTimerIsTimerActive(irqClearTimerHandle) == pdFALSE) {
                        xTimerStart(irqClearTimerHandle, 0);
                        LOG_INFO("Battery brownout detected while charging");
                    }
                }
                else {
                    events[magic_enum::enum_integer(Events::Brownout)] = Events::Brownout;
                    eventsToProcess.push_back(Events::Brownout);
                }
            }
            if (status & int_b_soc_change) {
                bsp::battery_charger::clearFuelGuageIRQ(int_b_soc_change);
                events[magic_enum::enum_integer(Events::SOC)] = Events::SOC;
            if (static_cast<bool>(status & int_b_soc_change)) {
                bsp::battery_charger::clearFuelGaugeIRQ(int_b_soc_change);
                eventsToProcess.push_back(Events::SOC);
            }
            if (status & int_b_min_temp || status & int_b_max_temp) {
                bsp::battery_charger::clearFuelGuageIRQ(int_b_min_temp | int_b_max_temp);
            if (static_cast<bool>(status & int_b_min_temp) || static_cast<bool>(status & int_b_max_temp)) {
                bsp::battery_charger::clearFuelGaugeIRQ(int_b_min_temp | int_b_max_temp);
                bsp::battery_charger::checkTemperatureRange();
            }
            /// Clear other unsupported IRQ sources just in case
            bsp::battery_charger::clearFuelGuageIRQ(int_b_all);
            // Clear other unsupported IRQ sources just in case
            bsp::battery_charger::clearFuelGaugeIRQ(int_b_all);
        }

        /// Send notifications
        for (const auto &event : events) {
            if (event) {
                sendNotification(*event);
            }
        // Send notifications
        for (const auto &event : eventsToProcess) {
            sendNotification(event);
        }
    }

    void PureBatteryCharger::sendNotification(AbstractBatteryCharger::Events event)
    {
        xQueueSend(notificationChannel, &event, pdMS_TO_TICKS(100));
        const auto status = xQueueSend(notificationChannel, &event, pdMS_TO_TICKS(100));
        if (status != pdTRUE) {
            LOG_ERROR("Failed to push event to battery charger queue, status %" PRIi32, status);
        }
    }

    std::unique_ptr<AbstractBatteryCharger> AbstractBatteryCharger::Factory::create(xQueueHandle irqQueueHandle)


@@ 247,12 267,6 @@ namespace hal::battery

    BaseType_t INTBHandlerIRQ()
    {
        return PureBatteryCharger::getWorkerQueueHandle().post({PureBatteryCharger::IrqEvents::Type::Controller});
        return PureBatteryCharger::getWorkerQueueHandle().post({PureBatteryCharger::IrqEvents::Type::ControllerINTB});
    }

    BaseType_t INOKBHandlerIRQ()
    {
        return PureBatteryCharger::getWorkerQueueHandle().post({PureBatteryCharger::IrqEvents::Type::Controller});
    }

} // namespace hal::battery

M module-bsp/board/rt1051/puretx/hal/battery_charger/BatteryChargerIRQ.hpp => module-bsp/board/rt1051/puretx/hal/battery_charger/BatteryChargerIRQ.hpp +1 -3
@@ 1,13 1,11 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <cstdint>
#include "FreeRTOS.h"

namespace hal::battery
{
    BaseType_t INTBHandlerIRQ();
    BaseType_t INOKBHandlerIRQ();
} // namespace hal::battery

M module-bsp/board/rt1051/puretx/hal/battery_charger/CurrentMeasurementScope.cpp => module-bsp/board/rt1051/puretx/hal/battery_charger/CurrentMeasurementScope.cpp +4 -5
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CurrentMeasurementScope.hpp"


@@ 19,7 19,7 @@ namespace hal::battery::CurrentMeasurementScope
        constexpr auto samplingTime = 100ms;
        TimerHandle_t samplingTimerHandle;

        static void getSample(TimerHandle_t xTimer)
        void getSample([[maybe_unused]] TimerHandle_t xTimer)
        {
            LOG_DEBUG("[scope]: { \"timestamp\" : %ld, \"current\" : %d, \"current_filtered\" : %d  }",
                      cpp_freertos::Ticks::TicksToMs(cpp_freertos::Ticks::GetTicks()),


@@ 31,10 31,9 @@ namespace hal::battery::CurrentMeasurementScope
    void start()
    {
        if (samplingTimerHandle == nullptr) {
            samplingTimerHandle =
                xTimerCreate("samplingTimer", pdMS_TO_TICKS(samplingTime.count()), true, nullptr, getSample);
            samplingTimerHandle = xTimerCreate(
                "CurrentMeasurementSamplingTimer", pdMS_TO_TICKS(samplingTime.count()), pdTRUE, nullptr, getSample);
        }
        xTimerStart(samplingTimerHandle, 0);
    }

} // namespace hal::battery::CurrentMeasurementScope

M module-bsp/board/rt1051/puretx/irq_gpio.cpp => module-bsp/board/rt1051/puretx/irq_gpio.cpp +0 -6
@@ 97,12 97,6 @@ namespace bsp
                xHigherPriorityTaskWoken |= hal::key_input::rightFunctionalIRQHandler();
            }

            if (irq_mask & (1 << BOARD_BATTERY_CHARGER_INOKB_PIN)) {
                xHigherPriorityTaskWoken |= hal::battery::INOKBHandlerIRQ();
            }

            if (irq_mask & (1 << BOARD_BATTERY_CHARGER_WCINOKB_PIN)) {}

            if (irq_mask & (1 << BOARD_BATTERY_CHARGER_INTB_PIN)) {
                xHigherPriorityTaskWoken |= hal::battery::INTBHandlerIRQ();
            }

M module-gui/gui/widgets/StatusBar.cpp => module-gui/gui/widgets/StatusBar.cpp +7 -3
@@ 283,11 283,15 @@ namespace gui::status_bar

    bool StatusBar::showBattery(bool enabled)
    {
        const auto visibilityChanged = battery->isVisible() == enabled ? false : true;
        enabled ? battery->show() : battery->hide();
        const auto visibilityChanged = (battery->isVisible() != enabled);
        if (enabled) {
            battery->show();
        }
        else {
            battery->hide();
        }
        const auto stateChanged    = battery->update(Store::Battery::get());
        const auto refreshRequired = stateChanged || visibilityChanged;

        return refreshRequired;
    }


M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +6 -8
@@ 331,13 331,11 @@ auto ServiceDesktop::handle(sdesktop::usb::USBConfigured *msg) -> std::shared_pt
        LOG_INFO("Endpoint security enabled, requesting passcode");
        bus.sendUnicast(std::make_shared<locks::UnlockPhoneForMTP>(), service::name::appmgr);
    }
    else {
        if (isPlugEventUnhandled) {
            bus.sendUnicast(std::make_shared<sys::TetheringStateRequest>(sys::phone_modes::Tethering::On),
                            service::name::system_manager);
            isPlugEventUnhandled = false;
            desktopWorker->notify(WorkerDesktop::Signal::unlockMTP);
        }
    else if (isPlugEventUnhandled) {
        bus.sendUnicast(std::make_shared<sys::TetheringStateRequest>(sys::phone_modes::Tethering::On),
                        service::name::system_manager);
        isPlugEventUnhandled = false;
        desktopWorker->notify(WorkerDesktop::Signal::unlockMTP);
    }

    return sys::MessageNone{};


@@ 357,7 355,7 @@ auto ServiceDesktop::handle(sdesktop::usb::USBDisconnected * /*msg*/) -> std::sh

auto ServiceDesktop::handle(sevm::USBPlugEvent *msg) -> std::shared_ptr<sys::Message>
{
    auto message = static_cast<sevm::USBPlugEvent *>(msg);
    const auto message = static_cast<sevm::USBPlugEvent *>(msg);
    if (message->event == sevm::USBPlugEvent::Event::CablePlugged) {
        usbWorkerInit();
        isPlugEventUnhandled = true;

M module-services/service-evtmgr/WorkerEventCommon.cpp => module-services/service-evtmgr/WorkerEventCommon.cpp +20 -18
@@ 10,7 10,6 @@
#include <MessageType.hpp>
#include <Service/Worker.hpp>
#include <bsp/rtc/rtc.hpp>
#include <bsp/keypad_backlight/keypad_backlight.hpp>
#include <bsp/vibrator/vibrator.hpp>
#include <bsp/eink_frontlight/eink_frontlight.hpp>



@@ 20,7 19,6 @@

#include "task.h"

#include <sys/types.h>
#include <memory>
#include <optional>



@@ 32,7 30,7 @@ WorkerEventCommon::WorkerEventCommon(sys::Service *service)
      service(service), keyInput{hal::key_input::AbstractKeyInput::Factory::create()}
{}

bool WorkerEventCommon::handleMessage(uint32_t queueID)
bool WorkerEventCommon::handleMessage(std::uint32_t queueID)
{
#if DEBUG_HEAP_ALLOCATIONS == 1
    LOG_INFO("Free space: %zu", usermemGetFreeHeapSize());


@@ 41,7 39,7 @@ bool WorkerEventCommon::handleMessage(uint32_t queueID)
    auto &queue = queues[queueID];

    // service queue
    if (queueID == static_cast<uint32_t>(WorkerEventQueues::queueService)) {
    if (queueID == static_cast<std::uint32_t>(WorkerEventQueues::queueService)) {
        sys::WorkerCommand wcmd;
        if (!queue->Dequeue(&wcmd, 0)) {
            return false;


@@ 50,8 48,8 @@ bool WorkerEventCommon::handleMessage(uint32_t queueID)
        // place some code here to handle messages from service
    }

    if (queueID == static_cast<uint32_t>(WorkerEventQueues::queueKeyboardIRQ)) {
        uint8_t notification;
    if (queueID == static_cast<std::uint32_t>(WorkerEventQueues::queueKeyboardIRQ)) {
        std::uint8_t notification;
        if (!queue->Dequeue(&notification, 0)) {
            return false;
        }


@@ 65,25 63,27 @@ bool WorkerEventCommon::handleMessage(uint32_t queueID)
        if (!queue->Dequeue(&event, 0)) {
            return false;
        }

        batteryController->handleNotification(event);
    }

    if (queueID == static_cast<uint32_t>(WorkerEventQueues::queueRTC)) {
        uint8_t notification;
    if (queueID == static_cast<std::uint32_t>(WorkerEventQueues::queueRTC)) {
        std::uint8_t notification;
        if (!queue->Dequeue(&notification, 0)) {
            return false;
        }

        time_t timestamp;
        std::time_t timestamp;
        bsp::rtc::getCurrentTimestamp(&timestamp);
        bsp::rtc::setMinuteAlarm(timestamp);

        /// Poll battery controller to recalculate state and possibly send update requests to
        /// appmgr/sysmgr
        batteryController->poll();

        auto message       = std::make_shared<sevm::RtcMinuteAlarmMessage>(MessageType::EVMMinuteUpdated);
        message->timestamp = timestamp;
        service->bus.sendUnicast(message, service::name::evt_manager);
        service->bus.sendUnicast(std::move(message), service::name::evt_manager);
    }

    return true;


@@ 114,13 114,14 @@ bool WorkerEventCommon::initEventQueues()

bool WorkerEventCommon::initCommonHardwareComponents(EventManagerParams params)
{
    keyInput->init(queues[static_cast<int32_t>(WorkerEventQueues::queueKeyboardIRQ)]->GetQueueHandle());
    auto queueBatteryHandle = queues[static_cast<int32_t>(WorkerEventQueues::queueBatteryController)]->GetQueueHandle();
    keyInput->init(queues[static_cast<std::int32_t>(WorkerEventQueues::queueKeyboardIRQ)]->GetQueueHandle());

    const auto queueBatteryHandle =
        queues[static_cast<std::int32_t>(WorkerEventQueues::queueBatteryController)]->GetQueueHandle();
    batteryController = std::make_shared<sevm::battery::BatteryController>(service, queueBatteryHandle, params);
    bsp::rtc::init(queues[static_cast<int32_t>(WorkerEventQueues::queueRTC)]->GetQueueHandle());

    time_t timestamp;
    std::time_t timestamp;
    bsp::rtc::init(queues[static_cast<std::int32_t>(WorkerEventQueues::queueRTC)]->GetQueueHandle());
    bsp::rtc::getCurrentTimestamp(&timestamp);
    bsp::rtc::setMinuteAlarm(timestamp);



@@ 142,10 143,9 @@ void WorkerEventCommon::init(std::shared_ptr<settings::Settings> settings, Event
    initCommonHardwareComponents(params);
}

bool WorkerEventCommon::deinit(void)
bool WorkerEventCommon::deinit()
{
    Worker::deinit();

    keyInput->deinit();
    return true;
}


@@ 154,7 154,7 @@ void WorkerEventCommon::sendKeyUnicast(RawKey const &key)
{
    auto message = std::make_shared<sevm::KbdMessage>();
    message->key = key;
    service->bus.sendUnicast(message, service::name::evt_manager);
    service->bus.sendUnicast(std::move(message), service::name::evt_manager);
}

void WorkerEventCommon::processKeyEvent(bsp::KeyEvents event, bsp::KeyCodes code)


@@ 163,7 163,7 @@ void WorkerEventCommon::processKeyEvent(bsp::KeyEvents event, bsp::KeyCodes code
    case bsp::KeyEvents::Pressed: {
        auto const tick = xTaskGetTickCount();
        if (lastState == bsp::KeyEvents::Pressed) {
            LOG_WARN("Generating Release %s", c_str(lastPressed));
            LOG_WARN("Generating release %s", c_str(lastPressed));
            sendKeyUnicast({RawKey::State::Released, lastPressed, 0, tick});
        }
        sendKeyUnicast({RawKey::State::Pressed, code, tick, 0});


@@ 171,6 171,7 @@ void WorkerEventCommon::processKeyEvent(bsp::KeyEvents event, bsp::KeyCodes code
        lastPressed = code;
        break;
    }

    case bsp::KeyEvents::Released:
        if (lastState != bsp::KeyEvents::Pressed || lastPressed != code) {
            return;


@@ 179,6 180,7 @@ void WorkerEventCommon::processKeyEvent(bsp::KeyEvents event, bsp::KeyCodes code
        lastState   = bsp::KeyEvents::Released;
        lastPressed = code;
        break;

    case bsp::KeyEvents::Moved:
        sendKeyUnicast({RawKey::State::Moved, code});
        break;

M module-services/service-evtmgr/battery/BatteryController.cpp => module-services/service-evtmgr/battery/BatteryController.cpp +23 -18
@@ 30,9 30,9 @@ namespace
        case Status::Charging:
            return NewState::Charging;
        case Status::ChargingDone:
            return NewState ::ChargingDone;
            return NewState::ChargingDone;
        case Status::PluggedNotCharging:
            return NewState ::PluggedNotCharging;
            return NewState::PluggedNotCharging;
        default:
            return NewState::Discharging;
        }


@@ 48,9 48,9 @@ namespace
        case Status::Charging:
            return NewState::Charging;
        case Status::ChargingDone:
            return NewState ::ChargingDone;
            return NewState::ChargingDone;
        case Status::PluggedNotCharging:
            return NewState ::PluggedNotCharging;
            return NewState::PluggedNotCharging;
        default:
            return NewState::Discharging;
        }


@@ 66,9 66,9 @@ namespace
        case Status::Shutdown:
            return NewState::Shutdown;
        case Status::CriticalCharging:
            return NewState ::CriticalCharging;
            return NewState::CriticalCharging;
        case Status::CriticalNotCharging:
            return NewState ::CriticalNotCharging;
            return NewState::CriticalNotCharging;
        default:
            return NewState::Normal;
        }


@@ 108,15 108,15 @@ BatteryController::BatteryController(sys::Service *service, xQueueHandle notific
    Store::Battery::modify().state = transformChargingState(charger->getChargingStatus());
    batteryState.check(transformChargingState(Store::Battery::modify().state), Store::Battery::modify().level);

    LOG_INFO("Initial charger state:%s", magic_enum::enum_name(Store::Battery::get().state).data());
    LOG_INFO("Initial battery SOC:%d", Store::Battery::get().level);
    LOG_INFO("Initial battery voltage:%" PRIu32 "mV", getVoltage());
    LOG_INFO("Initial battery state:%s", magic_enum::enum_name(Store::Battery::get().levelState).data());
    LOG_INFO("Initial charger state: %s", magic_enum::enum_name(Store::Battery::get().state).data());
    LOG_INFO("Initial battery SOC: %d", Store::Battery::get().level);
    LOG_INFO("Initial battery voltage: %" PRIu32 "mV", getVoltage());
    LOG_INFO("Initial battery state: %s", magic_enum::enum_name(Store::Battery::get().levelState).data());
}

void sevm::battery::BatteryController::handleNotification(Events evt)
{
    LOG_INFO("Incoming event: %s", std::string{magic_enum::enum_name(evt)}.c_str());
    LOG_INFO("Incoming event: %s", magic_enum::enum_name(evt).data());
    switch (evt) {
    case Events::Charger:
        checkChargerPresence();


@@ 136,14 136,16 @@ void sevm::battery::BatteryController::poll()
    update();
    brownoutDetector.poll();
}

void sevm::battery::BatteryController::printCurrentState()
{
    LOG_INFO("Charger state:%s Battery SOC %d voltage: %" PRIu32 "mV state: %s",
    LOG_INFO("Charger state: %s | Battery SOC: %d%% | Voltage: %" PRIu32 "mV | Level state: %s",
             magic_enum::enum_name(Store::Battery::get().state).data(),
             Store::Battery::get().level,
             getVoltage(),
             magic_enum::enum_name(Store::Battery::get().levelState).data());
}

void sevm::battery::BatteryController::update()
{
    const auto lastSoc   = Store::Battery::get().level;


@@ 186,12 188,15 @@ units::Voltage sevm::battery::BatteryController::getVoltage()

void sevm::battery::BatteryController::checkChargerPresence()
{
    using PlugEvent = sevm::USBPlugEvent::Event;

    const auto newChargerPresence = charger->getChargerPresence();
    if (chargerPresence != newChargerPresence) {
        service->bus.sendUnicast(std::make_shared<sevm::USBPlugEvent>(newChargerPresence == ChargerPresence::PluggedIn
                                                                          ? sevm::USBPlugEvent::Event::CablePlugged
                                                                          : sevm::USBPlugEvent::Event::CableUnplugged),
                                 service::name::service_desktop);
        chargerPresence = newChargerPresence;
    if (chargerPresence == newChargerPresence) {
        return;
    }

    const auto plugEvent =
        (newChargerPresence == ChargerPresence::PluggedIn) ? PlugEvent::CablePlugged : PlugEvent::CableUnplugged;
    service->bus.sendUnicast(std::make_shared<sevm::USBPlugEvent>(plugEvent), service::name::service_desktop);
    chargerPresence = newChargerPresence;
}

M module-services/service-evtmgr/service-evtmgr/WorkerEventCommon.hpp => module-services/service-evtmgr/service-evtmgr/WorkerEventCommon.hpp +2 -8
@@ 24,12 24,6 @@ namespace sys
    class Service;
} // namespace sys

struct KeyState
{
    uint8_t event;
    uint8_t code;
};

enum class WorkerEventQueues
{
    queueService = 0,


@@ 73,7 67,7 @@ class WorkerEventCommon : public sys::Worker
    /**
     * @brief list of keys with long press enabled. First item is key code, second is long press time.
     */
    std::map<uint32_t, uint32_t> longPressParamsList;
    std::map<std::uint32_t, std::uint32_t> longPressParamsList;
    bsp::KeyEvents lastState  = bsp::KeyEvents::Released;
    bsp::KeyCodes lastPressed = static_cast<bsp::KeyCodes>(0);
    std::shared_ptr<sys::CpuSentinel> cpuSentinel;


@@ 91,5 85,5 @@ class WorkerEventCommon : public sys::Worker
     * This method is called from thread when new message arrives in queue.
     * @param queueID Index of the queue in the queues vector.
     */
    bool handleMessage(uint32_t queueID) override;
    bool handleMessage(std::uint32_t queueID) override;
};