~aleteoryx/muditaos

9e3cc7a69242942458eabf0b05f3c2fa6b12247d — Kuba 4 years ago c4ce446
[EGD-8203] Add URC counter and csq poll mode

Added CSQ notifications counter and CSQ poll mode.
It will help to reduce current cosnuption in case of
variable signal.
M module-cellular/CMakeLists.txt => module-cellular/CMakeLists.txt +1 -0
@@ 40,6 40,7 @@ set(SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/QNWINFO.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/QSIMSTAT.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/QCFGUsbnet.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/CSQ.cpp
        )

add_library(${PROJECT_NAME} STATIC ${SOURCES})

A module-cellular/at/cmd/CSQ.hpp => module-cellular/at/cmd/CSQ.hpp +43 -0
@@ 0,0 1,43 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Result.hpp"
#include <at/Cmd.hpp>
#include <functional>

namespace at
{
    namespace csq
    {
        constexpr auto tokensCount = 2;

        enum class Tokens
        {
            Csq,
            Ber
        };
    } // namespace csq
    namespace result
    {
        struct CSQ : public Result
        {
            uint32_t csq;
            uint32_t ber;
            explicit CSQ(const Result &);
        };
    } // namespace result

    namespace cmd
    {
        class CSQ : public Cmd
        {
          public:
            CSQ() noexcept;
            explicit CSQ(at::cmd::Modifier mod) noexcept;
            [[nodiscard]] auto parseCSQ(const Result &base_result) -> result::CSQ;
        };
    } // namespace cmd

} // namespace at

A module-cellular/at/cmd/src/CSQ.cpp => module-cellular/at/cmd/src/CSQ.cpp +77 -0
@@ 0,0 1,77 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <log/log.hpp>
#include <memory>
#include <string>
#include <type_traits>
#include <at/cmd/CSQ.hpp>
#include <chrono>

namespace at
{
    namespace cmd
    {
        using namespace std::chrono_literals;
        CSQ::CSQ(at::cmd::Modifier mod) noexcept : Cmd("AT+CSQ", mod, 300ms)
        {}

        CSQ::CSQ() noexcept : CSQ(at::cmd::Modifier::None)
        {}

        result::CSQ CSQ::parseCSQ(const Result &base_result)
        {
            auto constexpr responseHeader = "+CSQ: ";

            result::CSQ p{base_result};
            // use parent operator bool
            if (p.Result::operator bool()) {
                if (p.response.empty()) {
                    LOG_ERROR("Can't parse - empty response");
                    p.code = result::CSQ::Code::PARSING_ERROR;

                    return p;
                }

                std::string str = p.response[0];
                if (str.find(responseHeader) == std::string::npos) {
                    LOG_ERROR("Can't parse - bad header");
                    p.code = result::CSQ::Code::PARSING_ERROR;
                    return p;
                }

                utils::findAndReplaceAll(str, responseHeader, "");
                utils::trim(str);

                std::vector<std::string> tokens = utils::split(str, ',');
                if (tokens.size() != csq::tokensCount) {
                    LOG_ERROR("Can't parse - wrong token count");
                    p.code = result::CSQ::Code::PARSING_ERROR;
                    return p;
                }

                auto csq = 0;
                auto ber = 0;
                if (utils::toNumeric(tokens.at(magic_enum::enum_integer(csq::Tokens::Csq)), csq) &&
                    utils::toNumeric(tokens.at(magic_enum::enum_integer(csq::Tokens::Ber)), ber)) {
                    p.csq = csq;
                    p.ber = ber;
                }
                else {
                    LOG_ERROR("Can't parse - bad value");
                    p.code = result::CSQ::Code::PARSING_ERROR;
                }
            }

            return p;
        }

    } // namespace cmd

    namespace result
    {
        CSQ::CSQ(const Result &that) : Result(that)
        {}

    } // namespace result
} // namespace at

M module-cellular/test/mock/AtCommon_channel.hpp => module-cellular/test/mock/AtCommon_channel.hpp +51 -0
@@ 566,4 566,55 @@ namespace at
            return result;
        }
    };

    /// provides invalid QSIMSTAT response
    class QSIMSTAT_tooFewTokens : public ChannelMock
    {
      public:
        auto ResultMock() -> Result final
        {
            auto result     = Result();
            result.code     = Result::Code::OK;
            result.response = {"+QCFG: \"usbnet\",", "OK"};
            return result;
        }
    };

    /// provides proper CSQ response
    class CSQ_successChannel : public ChannelMock
    {
      public:
        auto ResultMock() -> Result final
        {
            auto result     = Result();
            result.code     = Result::Code::OK;
            result.response = {"+CSQ: 28,99", "OK"};
            return result;
        }
    };

    /// provides invalid CSQ response
    class CSQ_tooFewTokens : public ChannelMock
    {
      public:
        auto ResultMock() -> Result final
        {
            auto result     = Result();
            result.code     = Result::Code::OK;
            result.response = {"+CSQ: 28", "OK"};
            return result;
        }
    };
    /// provides invalid CSQ response
    class CSQ_toManyTokens : public ChannelMock
    {
      public:
        auto ResultMock() -> Result final
        {
            auto result     = Result();
            result.code     = Result::Code::OK;
            result.response = {"+CSQ: 28,99,43", "OK"};
            return result;
        }
    };
} // namespace at

M module-cellular/test/unittest_parse_result.cpp => module-cellular/test/unittest_parse_result.cpp +56 -1
@@ 15,7 15,7 @@
#include <at/cmd/QNWINFO.hpp>
#include <at/cmd/QSIMSTAT.hpp>
#include <at/cmd/QCFGUsbnet.hpp>

#include <at/cmd/CSQ.hpp>
#include "mock/AtCommon_channel.hpp"
#include "PhoneNumber.hpp"
#include "Result.hpp"


@@ 815,3 815,58 @@ TEST_CASE("QCFGUsbnet parser")
        REQUIRE(response.code == at::Result::Code::PARSING_ERROR);
    }
}

TEST_CASE("CSQ parser")
{
    SECTION("Empty data")
    {
        at::cmd::CSQ cmd;
        at::Result result;
        auto response = cmd.parseCSQ(result);
        REQUIRE(!response);
    }

    SECTION("Failing channel")
    {
        at::cmd::CSQ cmd;
        at::FailingChannel channel;
        auto base     = channel.cmd(cmd);
        auto response = cmd.parseCSQ(base);
        REQUIRE(!response);
        REQUIRE(response.code == at::Result::Code::ERROR);
    }

    SECTION("success")
    {
        constexpr uint32_t csq = 28;
        constexpr uint32_t ber = 99;

        at::cmd::CSQ cmd;
        at::CSQ_successChannel channel;
        auto base     = channel.cmd(cmd);
        auto response = cmd.parseCSQ(base);
        REQUIRE(response);
        REQUIRE(response.csq == csq);
        REQUIRE(response.ber == ber);
    }

    SECTION("too few tokens")
    {
        at::cmd::CSQ cmd;
        at::CSQ_tooFewTokens channel;
        auto base     = channel.cmd(cmd);
        auto response = cmd.parseCSQ(base);
        REQUIRE(!response);
        REQUIRE(response.code == at::Result::Code::PARSING_ERROR);
    }

    SECTION("Failed - too many tokens")
    {
        at::cmd::CSQ cmd;
        at::CSQ_toManyTokens channel;
        auto base     = channel.cmd(cmd);
        auto response = cmd.parseCSQ(base);
        REQUIRE(!response);
        REQUIRE(response.code == at::Result::Code::PARSING_ERROR);
    }
}
\ No newline at end of file

M module-services/service-cellular/CMakeLists.txt => module-services/service-cellular/CMakeLists.txt +2 -0
@@ 13,6 13,8 @@ set(SOURCES
    src/ImeiGetHandler.cpp
    src/TerhetingHandler.cpp
    src/ModemResetHandler.cpp
    src/URCCounter.cpp
    src/CSQHandler.cpp

    CellularCall.cpp
    CellularServiceAPI.cpp

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +12 -0
@@ 148,6 148,14 @@ ServiceCellular::ServiceCellular()
    simTimer = sys::TimerFactory::createSingleShotTimer(
        this, "simTimer", std::chrono::milliseconds{6000}, [this](sys::Timer &) { priv->simCard->handleSimTimer(); });

    csqTimer = sys::TimerFactory::createPeriodicTimer(this, "csqTimer", std::chrono::seconds{60}, [this](sys::Timer &) {
        auto message = std::make_shared<cellular::URCCounterMessage>(csqCounter.getCounter());
        csqCounter.clearCounter();
        bus.sendUnicast(std::move(message), serviceName);

        priv->csqHandler->handleTimerTick();
    });

    ongoingCall.setStartCallAction([=](const CalllogRecord &rec) {
        auto call = DBServiceAPI::CalllogAdd(this, rec);
        if (call.ID == DB_ID_NONE) {


@@ 307,6 315,7 @@ void ServiceCellular::registerMessageHandlers()
    priv->connectNetworkTime();
    priv->connectSimContacts();
    priv->connectImeiGetHandler();
    priv->connectCSQHandler();

    connect(typeid(CellularStartOperatorsScanMessage), [&](sys::Message *request) -> sys::MessagePointer {
        auto msg = static_cast<CellularStartOperatorsScanMessage *>(request);


@@ 555,6 564,7 @@ void ServiceCellular::registerMessageHandlers()
            [&](sys::Message *request) -> sys::MessagePointer { return handleSmsDoneNotification(request); });

    connect(typeid(CellularSignalStrengthUpdateNotification), [&](sys::Message *request) -> sys::MessagePointer {
        csqCounter.count();
        return handleSignalStrengthUpdateNotification(request);
    });



@@ 867,6 877,8 @@ bool ServiceCellular::handle_cellular_priv_init()
                         interval)) {
        connectionManager->setInterval(std::chrono::minutes{interval});
    }

    priv->initCSQHandler();
    priv->state->set(State::ST::APNConfProcedure);
    return true;
}

M module-services/service-cellular/service-cellular/CellularMessage.hpp => module-services/service-cellular/service-cellular/CellularMessage.hpp +34 -0
@@ 1018,4 1018,38 @@ namespace cellular
      public:
        RetryPhoneModeChangeRequest() : sys::DataMessage(MessageType::MessageTypeUninitialized){};
    };

    class URCCounterMessage : public sys::DataMessage
    {
      public:
        explicit URCCounterMessage(const uint32_t urcCounter)
            : sys::DataMessage(MessageType::MessageTypeUninitialized), urcCounter(urcCounter){};
        auto getCounter()
        {
            return urcCounter;
        }

      private:
        uint32_t urcCounter;
    };

    class RetrySwitchCSQMode : public sys::DataMessage
    {
      public:
        explicit RetrySwitchCSQMode(bool switchToPollMode)
            : sys::DataMessage(MessageType::MessageTypeUninitialized), switchToPollMode(switchToPollMode){};
        auto isSwitchToPollMode()
        {
            return switchToPollMode;
        };

      private:
        bool switchToPollMode = false;
    };

    class RetryGetCSQ : public sys::DataMessage
    {
      public:
        RetryGetCSQ() : sys::DataMessage(MessageType::MessageTypeUninitialized){};
    };
} // namespace cellular

M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +4 -0
@@ 10,6 10,7 @@
#include "PacketDataCellularMessage.hpp"
#include "src/CallManager.hpp"
#include <service-cellular/connection-manager/ConnectionManager.hpp>
#include "src/URCCounter.hpp"

#include <modem/ATURCStream.hpp>
#include <modem/mux/DLCChannel.h>


@@ 113,6 114,7 @@ class ServiceCellular : public sys::Service
    sys::TimerHandle stateTimer;
    sys::TimerHandle ussdTimer;
    sys::TimerHandle simTimer;
    sys::TimerHandle csqTimer;

    // used to enter modem sleep mode
    sys::TimerHandle sleepTimer;


@@ 141,6 143,8 @@ class ServiceCellular : public sys::Service

    ussd::State ussdState = ussd::State::none;

    cellular::service::URCCounter csqCounter;

    bool nextPowerStateChangeAwaiting = false;

    /// one point of state change handling

A module-services/service-cellular/src/CSQHandler.cpp => module-services/service-cellular/src/CSQHandler.cpp +106 -0
@@ 0,0 1,106 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CSQHandler.hpp"

#include <log/log.hpp>

namespace cellular::service
{

    void CSQHandler::handleTimerTick()
    {
        if (isInPollMode()) {
            timeSpentInPollMode++;
            if (isPollModeTimeElapsed()) {
                switchToReportMode();
                return;
            }

            getCSQ();
        }
    }

    void CSQHandler::handleURCCounterMessage(const uint32_t counter)
    {
        urcCounter = counter;
        if (isTooManyURC() && not isInPollMode()) {
            switchToPollMode();
        }
    }

    auto CSQHandler::isInPollMode() -> bool
    {
        return currentMode == CSQMode::Polling;
    }

    auto CSQHandler::isTooManyURC() -> bool
    {
        return urcCounter > urcThreshold;
    }

    auto CSQHandler::isPollModeTimeElapsed() -> bool
    {
        return timeSpentInPollMode > pollTime;
    }

    bool CSQHandler::switchToReportMode()
    {
        LOG_INFO("CSQ poll mode time elapsed, switch to report mode.");
        if (onEnableCsqReporting != nullptr && onEnableCsqReporting()) {
            timeSpentInPollMode = std::chrono::minutes{0};
            currentMode         = CSQMode::Reporting;
            return true;
        }

        LOG_ERROR("Failed to switch to CSQ report mode! Retry!");
        if (onRetrySwitchMode != nullptr) {
            onRetrySwitchMode(false);
        }
        return false;
    }

    bool CSQHandler::switchToPollMode()
    {
        LOG_INFO("Too many signal strength updates, switch to poll mode.");
        if (onDisableCsqReporting != nullptr && onDisableCsqReporting()) {
            currentMode = CSQMode::Polling;
            return true;
        }

        LOG_ERROR("Failed to switch to CSQ poll mode! Retry!");
        if (onRetrySwitchMode != nullptr) {
            onRetrySwitchMode(false);
        }
        return false;
    }

    bool CSQHandler::getCSQ()
    {
        if (onGetCsq != nullptr) {
            if (auto result = onGetCsq(); result.has_value()) {
                auto csq = result.value();
                if (csq.csq != invalid_rssi_low && csq.csq != invalid_rssi_high) {
                    LOG_INFO("Propagate valid CSQ");
                    if (onPropagateCSQ != nullptr) {
                        onPropagateCSQ(csq.csq);
                        return true;
                    }
                }
                else {
                    LOG_INFO("Invalid CSQ, notify service antenna");
                    if (onInvalidCSQ != nullptr) {
                        onInvalidCSQ();
                        return true;
                    }
                }
            }
        }

        LOG_ERROR("Failed to get CSQ! Retry!");
        if (onRetryGetCSQ != nullptr) {
            onRetryGetCSQ();
        }
        return false;
    }
} // namespace cellular::service

A module-services/service-cellular/src/CSQHandler.hpp => module-services/service-cellular/src/CSQHandler.hpp +55 -0
@@ 0,0 1,55 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <at/cmd/CSQ.hpp>

#include <cstdint>
#include <chrono>
#include <functional>

namespace cellular::service
{

    constexpr auto urcThreshold = 4;
    constexpr auto pollTime     = std::chrono::minutes{15};

    static const auto invalid_rssi_low  = 99;
    static const auto invalid_rssi_high = 199;

    enum class CSQMode
    {
        Reporting,
        Polling
    };

    class CSQHandler
    {
      public:
        void handleTimerTick();
        void handleURCCounterMessage(const uint32_t counter);

        std::function<bool()> onEnableCsqReporting;
        std::function<bool()> onDisableCsqReporting;
        std::function<std::optional<at::result::CSQ>()> onGetCsq;
        std::function<void(uint32_t)> onPropagateCSQ;
        std::function<void()> onInvalidCSQ;

        std::function<void(bool)> onRetrySwitchMode;
        std::function<void()> onRetryGetCSQ;

        bool switchToReportMode();
        bool switchToPollMode();
        bool getCSQ();

      private:
        uint32_t urcCounter = 0;
        CSQMode currentMode = CSQMode::Reporting;
        std::chrono::minutes timeSpentInPollMode{};

        auto isInPollMode() -> bool;
        auto isPollModeTimeElapsed() -> bool;
        auto isTooManyURC() -> bool;
    };
} // namespace cellular::service

M module-services/service-cellular/src/ServiceCellularPriv.cpp => module-services/service-cellular/src/ServiceCellularPriv.cpp +104 -2
@@ 6,6 6,7 @@
#include "SMSPartsHandler.hpp"

#include <service-cellular-api>
#include <service-cellular/Constans.hpp>

#include <service-evtmgr/EVMessages.hpp>
#include <service-evtmgr/Constants.hpp>


@@ 14,8 15,11 @@
#include <service-time/Constants.hpp>
#include <queries/messages/sms/QuerySMSUpdate.hpp>

#include <service-antenna/AntennaServiceAPI.hpp>

#include <at/ATFactory.hpp>
#include <at/cmd/QCFGUsbnet.hpp>
#include <at/cmd/CSQ.hpp>
#include <ucs2/UCS2.hpp>
#include <service-appmgr/Constants.hpp>



@@ 30,8 34,8 @@ namespace cellular::internal
        : owner{owner}, simCard{std::make_unique<SimCard>()}, state{std::make_unique<State>(owner)},
          networkTime{std::make_unique<NetworkTime>()}, simContacts{std::make_unique<SimContacts>()},
          imeiGetHandler{std::make_unique<service::ImeiGetHandler>()},
          tetheringHandler{std::make_unique<TetheringHandler>()}, modemResetHandler{
                                                                      std::make_unique<ModemResetHandler>()}
          tetheringHandler{std::make_unique<TetheringHandler>()},
          modemResetHandler{std::make_unique<ModemResetHandler>()}, csqHandler{std::make_unique<CSQHandler>()}
    {
        initSimCard();
        initSMSSendHandler();


@@ 390,4 394,102 @@ namespace cellular::internal
            return false;
        };
    }

    void ServiceCellularPriv::initCSQHandler()
    {
        csqHandler->onEnableCsqReporting = [this]() {
            constexpr auto cpuSentinelTimeout = 2000;
            auto handle                       = owner->getTaskHandle();
            if (owner->cpuSentinel) {
                owner->cpuSentinel->HoldMinimumFrequencyAndWait(
                    bsp::CpuFrequencyMHz::Level_4, handle, cpuSentinelTimeout);
            }

            auto channel = owner->cmux->get(CellularMux::Channel::Commands);
            if (channel) {
                auto result = channel->cmd(at::AT::CSQ_URC_ON);
                if (result) {
                    return true;
                }
            }
            return false;
        };

        csqHandler->onDisableCsqReporting = [this]() {
            constexpr auto cpuSentinelTimeout = 2000;
            auto handle                       = owner->getTaskHandle();
            if (owner->cpuSentinel) {
                owner->cpuSentinel->HoldMinimumFrequencyAndWait(
                    bsp::CpuFrequencyMHz::Level_4, handle, cpuSentinelTimeout);
            }

            auto channel = owner->cmux->get(CellularMux::Channel::Commands);
            if (channel) {
                auto result = channel->cmd(at::AT::CSQ_URC_OFF);
                if (result) {
                    return true;
                }
            }
            return false;
        };

        csqHandler->onGetCsq = [this]() -> std::optional<at::result::CSQ> {
            auto channel = owner->cmux->get(CellularMux::Channel::Commands);
            if (!channel) {
                return std::nullopt;
            }
            auto command  = at::cmd::CSQ();
            auto response = channel->cmd(command);
            if (response.code == at::Result::Code::OK) {
                auto result = command.parseCSQ(response);
                if (result) {
                    return result;
                }
            }
            return std::nullopt;
        };

        csqHandler->onPropagateCSQ = [this](uint32_t csq) {
            SignalStrength signalStrength(static_cast<int>(csq));
            Store::GSM::get()->setSignalStrength(signalStrength.data);
            auto message = std::make_shared<CellularSignalStrengthUpdateNotification>("");
            owner->bus.sendMulticast(message, sys::BusChannel::ServiceCellularNotifications);
        };

        csqHandler->onInvalidCSQ = [this]() { AntennaServiceAPI::InvalidCSQNotification(owner); };

        csqHandler->onRetrySwitchMode = [this](bool isSwitchToPollMode) {
            owner->bus.sendUnicast(std::make_shared<RetrySwitchCSQMode>(isSwitchToPollMode), ::service::name::cellular);
        };

        csqHandler->onRetryGetCSQ = [this]() {
            owner->bus.sendUnicast(std::make_shared<RetryGetCSQ>(), ::service::name::cellular);
        };

        owner->csqTimer.start();
    }

    void ServiceCellularPriv::connectCSQHandler()
    {
        owner->connect(typeid(URCCounterMessage), [&](sys::Message *request) -> sys::MessagePointer {
            auto message = dynamic_cast<cellular::URCCounterMessage *>(request);
            csqHandler->handleURCCounterMessage(message->getCounter());
            return sys::MessageNone{};
        });

        owner->connect(typeid(RetrySwitchCSQMode), [&](sys::Message *request) -> sys::MessagePointer {
            auto message = dynamic_cast<cellular::RetrySwitchCSQMode *>(request);
            if (message->isSwitchToPollMode()) {
                csqHandler->switchToPollMode();
                return sys::MessageNone{};
            }
            csqHandler->switchToReportMode();
            return sys::MessageNone{};
        });

        owner->connect(typeid(RetryGetCSQ), [&](sys::Message *request) -> sys::MessagePointer {
            csqHandler->getCSQ();
            return sys::MessageNone{};
        });
    }
} // namespace cellular::internal

M module-services/service-cellular/src/ServiceCellularPriv.hpp => module-services/service-cellular/src/ServiceCellularPriv.hpp +7 -0
@@ 13,9 13,11 @@
#include "ImeiGetHandler.hpp"
#include "TetheringHandler.hpp"
#include "ModemResetHandler.hpp"
#include "CSQHandler.hpp"

namespace cellular::internal
{
    using service::CSQHandler;
    using service::ModemResetHandler;
    using service::NetworkTime;
    using service::SimCard;


@@ 35,6 37,8 @@ namespace cellular::internal
        std::unique_ptr<service::ImeiGetHandler> imeiGetHandler;
        std::unique_ptr<TetheringHandler> tetheringHandler;
        std::unique_ptr<ModemResetHandler> modemResetHandler;
        std::unique_ptr<CSQHandler> csqHandler;

        State::PowerState nextPowerState = State::PowerState::Off;
        std::uint8_t multiPartSMSUID     = 0;



@@ 45,6 49,7 @@ namespace cellular::internal
        void connectNetworkTime();
        void connectSimContacts();
        void connectImeiGetHandler();
        void connectCSQHandler();

        void requestNetworkTimeSettings();
        void setInitialMultiPartSMSUID(std::uint8_t uid);


@@ 55,6 60,8 @@ namespace cellular::internal
        void initSMSSendHandler();
        void initTetheringHandler();
        void initModemResetHandler();
        void initCSQHandler();

        /** Send SMS action used by the SMSSendHandler
         * \param record SMS record to send
         */

A module-services/service-cellular/src/URCCounter.cpp => module-services/service-cellular/src/URCCounter.cpp +21 -0
@@ 0,0 1,21 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "URCCounter.hpp"

namespace cellular::service
{

    void URCCounter::count()
    {
        urcOccurences++;
    }
    void URCCounter::clearCounter()
    {
        urcOccurences = 0;
    }
    auto URCCounter::getCounter() -> uint32_t
    {
        return urcOccurences;
    }
} // namespace cellular::service

A module-services/service-cellular/src/URCCounter.hpp => module-services/service-cellular/src/URCCounter.hpp +20 -0
@@ 0,0 1,20 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <cstdint>

namespace cellular::service
{

    class URCCounter
    {
      public:
        void count();
        void clearCounter();
        auto getCounter() -> uint32_t;

      private:
        uint32_t urcOccurences = 0;
    };
} // namespace cellular::service