~aleteoryx/muditaos

402a7416378afb1864120c3aef3854bee610d457 — Piotr Tański 5 years ago d240072
[EGD-5026] Change Eink service code structure

Refactor.
M module-gui/gui/Common.hpp => module-gui/gui/Common.hpp +6 -3
@@ 30,13 30,16 @@ namespace gui
    struct Point
    {
        Position x = 0, y = 0;
        Point(Position x = 0, Position y = 0) : x(x), y(y)

        constexpr Point(Position x = 0, Position y = 0) : x(x), y(y)
        {}
        [[nodiscard]] auto get(Axis axis) const -> Length

        [[nodiscard]] constexpr auto get(Axis axis) -> Length
        {
            return Axis::X == axis ? x : y;
        }
        [[nodiscard]] auto isZero() const -> bool

        [[nodiscard]] constexpr auto isZero() -> bool
        {
            return 0 == x && 0 == y;
        }

M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +2 -2
@@ 139,8 139,8 @@ namespace app::manager
            !ret) {
            LOG_ERROR("Failed to initialize GUI service");
        }
        if (bool ret =
                sys::SystemManager::CreateService(std::make_shared<ServiceEink>(service::name::eink, GetName()), this);
        if (bool ret = sys::SystemManager::CreateService(
                std::make_shared<eink::ServiceEink>(service::name::eink, GetName()), this);
            !ret) {
            LOG_ERROR("Failed to initialize EInk service");
        }

M module-services/service-eink/CMakeLists.txt => module-services/service-eink/CMakeLists.txt +1 -0
@@ 14,6 14,7 @@ message( "EINK BOARD PATH: ${CMAKE_CURRENT_LIST_DIR}/${EINK_BOARD_PATH}" )

set(SOURCES
    ServiceEink.cpp
    EinkScreen.cpp
    messages/ImageMessage.cpp
)


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

#include "EinkScreen.hpp"

#include <gui/core/Color.hpp>
#include <gsl/gsl_util>

#include <cstdio>
#include <cstring>

namespace eink
{
    namespace
    {
        constexpr auto DefaultSurroundingTemperature = -1000;
        constexpr auto LutsFileName                  = "Luts.bin";
        constexpr auto LUTDSize                      = 16385;
        constexpr auto LUTCSize                      = 64;
        constexpr auto LUTRSize                      = 256; ///< Needed due to \ref EINK_LUTS_FILE_PATH structure
        constexpr auto LUTSTotalSize                 = LUTDSize + LUTCSize + LUTRSize;

        EinkWaveFormSettings_t createDefaultWaveFormSettings(EinkWaveforms_e waveformMode)
        {
            EinkWaveFormSettings_t settings{};
            settings.mode        = waveformMode;
            settings.temperature = DefaultSurroundingTemperature;
            settings.LUTCData    = nullptr;
            settings.LUTCSize    = 0;
            settings.LUTDData    = nullptr;
            settings.LUTDSize    = 0;
            return settings;
        }

        std::unique_ptr<std::uint8_t[]> allocateScreenBuffer(gui::Size screenSize)
        {
            return std::make_unique<std::uint8_t[]>(screenSize.width * screenSize.height);
        }
    } // namespace

    EinkScreen::EinkScreen(gui::Size screenSize)
        : size{screenSize}, screenBuffer{allocateScreenBuffer(screenSize)},
          waveformSettings{createDefaultWaveFormSettings(EinkWaveformGC16)},
          displayMode{EinkDisplayColorMode_e::EinkDisplayColorModeStandard}
    {}

    EinkScreen::~EinkScreen() noexcept
    {
        delete[] waveformSettings.LUTCData;
        delete[] waveformSettings.LUTDData;
    }

    EinkStatus_e EinkScreen::resetAndInit()
    {
        return EinkResetAndInitialize();
    }

    void EinkScreen::dither()
    {
        EinkDitherDisplay();
    }

    void EinkScreen::powerOn()
    {
        EinkPowerOn();
    }

    void EinkScreen::powerOff()
    {
        EinkPowerOff();
    }

    void EinkScreen::shutdown()
    {
        EinkPowerDown();
    }

    void EinkScreen::setScreenBuffer(const std::uint8_t *buffer, std::uint32_t bufferSize)
    {
        std::memcpy(screenBuffer.get(), buffer, bufferSize);
    }

    void EinkScreen::setScreenBuffer(std::uint8_t value, std::uint32_t bufferSize)
    {
        std::memset(screenBuffer.get(), value, bufferSize);
    }

    EinkStatus_e EinkScreen::update()
    {
        return EinkUpdateFrame(
            pointTopLeft.x, pointTopLeft.y, size.width, size.height, screenBuffer.get(), Eink4Bpp, displayMode);
    }

    EinkStatus_e EinkScreen::refresh(EinkDisplayTimingsMode_e refreshMode)
    {
        return EinkRefreshImage(pointTopLeft.x, pointTopLeft.y, size.width, size.height, refreshMode);
    }

    bool EinkScreen::deepClear(std::int32_t temperature)
    {
        const auto waveformMode = waveformSettings.mode;

        powerOn();
        changeWaveform(EinkWaveforms_e::EinkWaveformA2, temperature);

        fillScreen(gui::Color::White);
        for (auto i = 0; i < 2; ++i) {
            fillScreen(gui::Color::Black);
            fillScreen(gui::Color::White);
        }

        changeWaveform(waveformMode, temperature);
        powerOff();
        return true;
    }

    void EinkScreen::fillScreen(std::uint8_t colorIntensity)
    {
        const auto screenBufferSize = size.width * size.height;
        setScreenBuffer(colorIntensity, screenBufferSize);
        if (const auto status = update(); status != EinkOK) {
            LOG_FATAL("Failed to update frame");
        }
        if (const auto status = refresh(EinkDisplayTimingsFastRefreshMode); status != EinkOK) {
            LOG_FATAL("Failed to refresh frame");
        }
    }

    bool EinkScreen::changeWaveform(EinkWaveforms_e mode, std::int32_t temperature)
    {
        if (temperature == waveformSettings.temperature && mode == waveformSettings.mode) {
            return EinkOK;
        }
        waveformSettings.temperature = temperature;
        waveformSettings.mode        = mode;

        const auto segment = calculateWaveFormSegment(temperature);
        auto offset        = calculateWaveFormOffset(mode, segment);

        auto file = std::fopen(LutsFileName, "rb");
        if (file == nullptr) {
            LOG_FATAL("Could not find the LUTS.bin file. Returning");
            return false;
        }
        auto fileHandlerCleanup = gsl::finally([&file]() { std::fclose(file); });

        resetWaveFormSettings();
        std::fseek(file, offset, SEEK_SET);
        std::fread(&waveformSettings.LUTDData[1], 1, LUTDSize, file);

        // 0x00 - 1 frame, ... , 0x0F - 16 frames
        const uint8_t frameCount = waveformSettings.LUTDData[1] + 1;
        // (frameCount * 64) - size of actual LUT; (+1) - the byte containing frameCount; (+1) - EinkLUTD command
        waveformSettings.LUTDSize = (frameCount * 64) + 1 + 1;

        offset += LUTDSize;
        std::fseek(file, offset, SEEK_SET);
        std::fread(&waveformSettings.LUTCData[1], 1, LUTCSize, file);

        EinkUpdateWaveform(&waveformSettings);
        return true;
    }

    unsigned int EinkScreen::calculateWaveFormSegment(std::int32_t temperature) const
    {
        if (temperature < 38) {
            return temperature / 3;
        }
        if (temperature < 43) {
            return 12;
        }
        return 13;
    }

    unsigned int EinkScreen::calculateWaveFormOffset(EinkWaveforms_e mode, unsigned int segment) const
    {
        switch (mode) {
        case EinkWaveformINIT:
            return LUTSTotalSize * segment;
        case EinkWaveformA2:
            return LUTSTotalSize * (14 + segment);
        case EinkWaveformDU2:
            return LUTSTotalSize * (28 + segment);
        case EinkWaveformGLD16:
            return LUTSTotalSize * (42 + segment);
        case EinkWaveformGC16:
            [[fallthrough]];
        default:
            return LUTSTotalSize * (56 + segment);
        }
    }

    void EinkScreen::resetWaveFormSettings()
    {
        delete[] waveformSettings.LUTDData;
        waveformSettings.LUTDSize    = 0;
        waveformSettings.LUTDData    = new uint8_t[LUTDSize + 1];
        waveformSettings.LUTDData[0] = EinkLUTD;

        delete[] waveformSettings.LUTCData;
        waveformSettings.LUTCSize    = LUTCSize;
        waveformSettings.LUTCData    = new uint8_t[LUTCSize + 1];
        waveformSettings.LUTCData[0] = EinkLUTC;
    }

    void EinkScreen::setDisplayMode(EinkDisplayColorMode_e mode) noexcept
    {
        displayMode = mode;
    }
} // namespace eink

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

#pragma once

#include <gui/Common.hpp>

#include <EinkIncludes.hpp>

#include <cstdint>
#include <memory>

namespace eink
{
    class EinkScreen
    {
      public:
        explicit EinkScreen(gui::Size screenSize = {480, 600});
        ~EinkScreen() noexcept;

        EinkStatus_e resetAndInit();
        EinkStatus_e update();
        EinkStatus_e refresh(EinkDisplayTimingsMode_e refreshMode);
        void dither();
        void powerOn();
        void powerOff();
        void shutdown();

        bool deepClear(std::int32_t temperature);
        bool changeWaveform(EinkWaveforms_e mode, std::int32_t temperature);

        void setScreenBuffer(const std::uint8_t *buffer, std::uint32_t bufferSize);
        void setDisplayMode(EinkDisplayColorMode_e mode) noexcept;

      private:
        void fillScreen(std::uint8_t colorIntensity);
        void setScreenBuffer(std::uint8_t value, std::uint32_t bufferSize);
        unsigned int calculateWaveFormSegment(std::int32_t temperature) const;
        unsigned int calculateWaveFormOffset(EinkWaveforms_e mode, unsigned int segment) const;
        void resetWaveFormSettings();

        static constexpr gui::Point pointTopLeft{0, 0};

        const gui::Size size;
        std::unique_ptr<std::uint8_t[]> screenBuffer;
        EinkWaveFormSettings_t waveformSettings;
        EinkDisplayColorMode_e displayMode;
    };
} // namespace eink

M module-services/service-eink/ServiceEink.cpp => module-services/service-eink/ServiceEink.cpp +126 -299
@@ 13,316 13,140 @@
#include <log/log.hpp>
#include <messages/EinkMessage.hpp>
#include <messages/ImageMessage.hpp>
#include <MessageType.hpp>
#include <Service/Bus.hpp>
#include <service-gui/messages/GUIMessage.hpp>

#include <cstdio>
#include <cstring>
#include <memory>

enum class EinkWorkerCommands
namespace eink
{
    Initialize,
    Initialized,
    Destroy,
    CopyImage,
    CopyCompleteCallback,
    CopyComplete
};

ServiceEink::ServiceEink(const std::string &name, std::string parent)
    : sys::Service(name, parent, 4096 + 1024),
      einkRenderBuffer(std::make_unique<uint8_t[]>(screen.height * screen.width)), selfRefereshTriggerCount{0},
      temperatureMeasurementTriggerCount{0}, powerOffTriggerCount{0},
      powerOffTimer("PwrOffTimer", this, 3000, sys::Timer::Type::SingleShot)
{
    memset(&waveformSettings, 0, sizeof(EinkWaveFormSettings_t));
    waveformSettings.mode        = EinkWaveformGC16;
    waveformSettings.temperature = -1000;

    connect(typeid(service::eink::EinkModeMessage), [this](sys::Message *message) -> sys::MessagePointer {
        auto msg          = static_cast<service::eink::EinkModeMessage *>(message);
        this->displayMode = msg->getMode() == service::eink::EinkModeMessage::Mode::Normal
                                ? EinkDisplayColorMode_e::EinkDisplayColorModeStandard
                                : EinkDisplayColorMode_e::EinkDisplayColorModeInverted;
        return sys::MessageNone{};
    });

    connect(typeid(service::eink::EinkDMATransfer),
            [&](sys::Message *request) -> sys::MessagePointer { return handleEinkDMATransfer(request); });

    connect(typeid(service::eink::ImageMessage),
            [&](sys::Message *request) -> sys::MessagePointer { return handleImageMessage(request); });

    connect(typeid(service::eink::StateRequest),
            [&](sys::Message *request) -> sys::MessagePointer { return handleStateRequest(request); });

    connect(typeid(service::eink::TemperatureUpdate),
            [&](sys::Message *request) -> sys::MessagePointer { return handleTemperatureUpdate(request); });
}

ServiceEink::~ServiceEink()
{
    if (waveformSettings.LUTCData != nullptr)
        delete[] waveformSettings.LUTCData;
    if (waveformSettings.LUTDData != nullptr)
        delete[] waveformSettings.LUTDData;
    waveformSettings.temperature = -1000;
}

sys::MessagePointer ServiceEink::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
{
    return std::make_shared<sys::ResponseMessage>();
}

sys::ReturnCodes ServiceEink::InitHandler()
{

    LOG_INFO("[ServiceEink] Initializing");

    EinkStatus_e einkStatus = EinkResetAndInitialize();

    if (einkStatus != EinkOK) {
        LOG_FATAL("Error: Could not initialize Eink display!\n");
    namespace
    {
        constexpr auto ServceEinkStackDepth = 4096 + 1024;
    } // namespace

    ServiceEink::ServiceEink(const std::string &name, std::string parent)
        : sys::Service(name, parent, ServceEinkStackDepth), selfRefereshTriggerCount{0},
          temperatureMeasurementTriggerCount{0}, powerOffTriggerCount{0},
          powerOffTimer("PwrOffTimer", this, 3000, sys::Timer::Type::SingleShot)
    {
        connect(typeid(service::eink::EinkModeMessage),
                [this](sys::Message *message) -> sys::MessagePointer { return handleEinkModeChangedMessage(message); });

        connect(typeid(service::eink::EinkDMATransfer),
                [this](sys::Message *request) -> sys::MessagePointer { return handleEinkDMATransfer(request); });

        connect(typeid(service::eink::ImageMessage),
                [this](sys::Message *request) -> sys::MessagePointer { return handleImageMessage(request); });

        connect(typeid(service::eink::StateRequest),
                [this](sys::Message *request) -> sys::MessagePointer { return handleStateRequest(request); });

        connect(typeid(service::eink::TemperatureUpdate),
                [this](sys::Message *request) -> sys::MessagePointer { return handleTemperatureUpdate(request); });
    }

    EinkPowerOn();

    auto msg = std::make_shared<service::gui::GUIDisplayReady>(suspendInProgress, shutdownInProgress);
    sys::Bus::SendUnicast(msg, service::name::gui, this);

    return sys::ReturnCodes::Success;
}

sys::ReturnCodes ServiceEink::DeinitHandler()
{
    EinkPowerDown();
    return sys::ReturnCodes::Success;
}

sys::ReturnCodes ServiceEink::SwitchPowerModeHandler(const sys::ServicePowerMode mode)
{
    LOG_FATAL("[ServiceEink] PowerModeHandler: %s", c_str(mode));

    switch (mode) {
    case sys::ServicePowerMode ::Active: {
        suspended               = false;
        EinkStatus_e einkStatus = EinkResetAndInitialize();
    sys::MessagePointer ServiceEink::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
    {
        return std::make_shared<sys::ResponseMessage>();
    }

        if (einkStatus != EinkOK) {
    sys::ReturnCodes ServiceEink::InitHandler()
    {
        LOG_INFO("[ServiceEink] Initializing");
        if (const auto status = screen.resetAndInit(); status != EinkOK) {
            LOG_FATAL("Error: Could not initialize Eink display!\n");
            return sys::ReturnCodes::Failure;
        }
        screen.powerOn();

        EinkPowerOn();
        EinkPowerOff();
    } break;
    case sys::ServicePowerMode ::SuspendToRAM:
    case sys::ServicePowerMode ::SuspendToNVM:
        suspended = true;
        powerOffTimer.stop();
        EinkPowerDown();
        break;
        auto msg = std::make_shared<service::gui::GUIDisplayReady>(suspendInProgress, shutdownInProgress);
        sys::Bus::SendUnicast(msg, service::name::gui, this);
        return sys::ReturnCodes::Success;
    }

    return sys::ReturnCodes::Success;
}

bool ServiceEink::changeWaveform(EinkWaveforms_e mode, const int32_t temperature)
{

    if ((temperature == waveformSettings.temperature) && (mode == waveformSettings.mode)) {
        return EinkOK;
    sys::ReturnCodes ServiceEink::DeinitHandler()
    {
        screen.shutdown();
        return sys::ReturnCodes::Success;
    }

    const uint32_t LUTD_SIZE = 16385;
    const uint32_t LUTC_SIZE = 64;
    const uint32_t LUTR_SIZE = 256; ///< Needed due to \ref EINK_LUTS_FILE_PATH structure

    const uint32_t LUTS_TOTAL_SIZE = LUTD_SIZE + LUTC_SIZE + LUTR_SIZE;

    waveformSettings.temperature = temperature;
    waveformSettings.mode        = mode;

    unsigned int segment = 0;

    if (temperature < 38) {
        segment = temperature / 3;
    }
    else {
        if (temperature < 43) {
            segment = 12;
        }
        else {
            segment = 13;
    sys::ReturnCodes ServiceEink::SwitchPowerModeHandler(const sys::ServicePowerMode mode)
    {
        LOG_FATAL("[ServiceEink] PowerModeHandler: %s", c_str(mode));

        switch (mode) {
        case sys::ServicePowerMode::Active:
            enterActiveMode();
            break;
        case sys::ServicePowerMode::SuspendToRAM:
            [[fallthrough]];
        case sys::ServicePowerMode::SuspendToNVM:
            suspend();
            break;
        }
        return sys::ReturnCodes::Success;
    }

    uint32_t offset = 0;

    switch (mode) {
    case EinkWaveformINIT:
        offset = LUTS_TOTAL_SIZE * segment;
        break;

    case EinkWaveformA2:
        offset = LUTS_TOTAL_SIZE * (14 + segment);
        break;

    case EinkWaveformDU2:
        offset = LUTS_TOTAL_SIZE * (28 + segment);
        break;

    case EinkWaveformGLD16:
        offset = LUTS_TOTAL_SIZE * (42 + segment);
        break;

    case EinkWaveformGC16:
    default:
        offset = LUTS_TOTAL_SIZE * (56 + segment);
        break;
    }

    auto file = std::fopen("Luts.bin", "rb");
    if (file == nullptr) {
        LOG_FATAL("Could not find the LUTS.bin file. Returning");
        return false;
    }

    if (waveformSettings.LUTDData != nullptr)
        delete[] waveformSettings.LUTDData;

    waveformSettings.LUTDSize = 0;
    waveformSettings.LUTDData = new uint8_t[LUTD_SIZE + 1];
    void ServiceEink::enterActiveMode()
    {
        suspended = false;

    if (waveformSettings.LUTDData == nullptr) {
        LOG_ERROR("Could not allocate memory for the LUTD array");
        std::fclose(file);
        return false;
        if (const auto status = screen.resetAndInit(); status != EinkOK) {
            LOG_FATAL("Error: Could not initialize Eink display!\n");
        }
        screen.powerOn();
        screen.powerOff();
    }

    if (waveformSettings.LUTCData != nullptr)
        delete[] waveformSettings.LUTCData;
    void ServiceEink::suspend()
    {
        suspended = true;

    waveformSettings.LUTCSize = LUTC_SIZE;
    waveformSettings.LUTCData = new uint8_t[LUTC_SIZE + 1];
    if (waveformSettings.LUTCData == nullptr) {
        LOG_ERROR("Could not allocate memory for the LUTC array");
        std::fclose(file);
        return false;
        powerOffTimer.stop();
        screen.shutdown();
    }

    waveformSettings.LUTDData[0] = EinkLUTD;
    waveformSettings.LUTCData[0] = EinkLUTC;

    std::fseek(file, offset, SEEK_SET);
    std::fread(&waveformSettings.LUTDData[1], 1, LUTD_SIZE, file);

    uint8_t frameCount = waveformSettings.LUTDData[1] + 1; // 0x00 - 1 frame, ... , 0x0F - 16 frames
    waveformSettings.LUTDSize =
        frameCount * 64 + 1 +
        1; // (frameCount * 64) - size of actual LUT; (+1) - the byte containing frameCount; (+1) - EinkLUTD command

    offset += LUTD_SIZE;
    std::fseek(file, offset, SEEK_SET);
    std::fread(&waveformSettings.LUTCData[1], 1, LUTC_SIZE, file);

    std::fclose(file);

    EinkUpdateWaveform(&waveformSettings);

    return true;
}

bool ServiceEink::deepClearScreen(int8_t temperature)
{
    EinkWaveforms_e wv = waveformSettings.mode;

    EinkPowerOn();
    changeWaveform(EinkWaveforms_e::EinkWaveformA2, temperature);

    EinkStatus_e ret;
    memset(einkRenderBuffer.get(), 15, screen.width * screen.height);
    ret = EinkUpdateFrame(
        pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, einkRenderBuffer.get(), Eink4Bpp, displayMode);
    if (ret != EinkOK)
        LOG_FATAL("Failed to update frame");
    ret = EinkRefreshImage(
        pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, EinkDisplayTimingsFastRefreshMode);
    if (ret != EinkOK)
        LOG_FATAL("Failed to refresh frame");

    for (uint32_t i = 0; i < 2; i++) {
        memset(einkRenderBuffer.get(), 0, screen.width * screen.height);
        ret = EinkUpdateFrame(
            pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, einkRenderBuffer.get(), Eink4Bpp, displayMode);
        if (ret != EinkOK)
            LOG_FATAL("Failed to update frame");
        ret = EinkRefreshImage(
            pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, EinkDisplayTimingsFastRefreshMode);
        if (ret != EinkOK)
            LOG_FATAL("Failed to refresh frame");

        memset(einkRenderBuffer.get(), 15, screen.width * screen.height);
        ret = EinkUpdateFrame(
            pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, einkRenderBuffer.get(), Eink4Bpp, displayMode);
        if (ret != EinkOK)
            LOG_FATAL("Failed to update frame");
        ret = EinkRefreshImage(
            pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, EinkDisplayTimingsFastRefreshMode);
        if (ret != EinkOK)
            LOG_FATAL("Failed to refresh frame");
    sys::MessagePointer ServiceEink::handleEinkModeChangedMessage(sys::Message *message)
    {
        const auto msg         = static_cast<service::eink::EinkModeMessage *>(message);
        const auto displayMode = msg->getMode() == service::eink::EinkModeMessage::Mode::Normal
                                     ? EinkDisplayColorMode_e::EinkDisplayColorModeStandard
                                     : EinkDisplayColorMode_e::EinkDisplayColorModeInverted;
        screen.setDisplayMode(displayMode);
        return sys::MessageNone{};
    }

    changeWaveform(wv, temperature);

    EinkPowerOff();

    return true;
}

sys::MessagePointer ServiceEink::handleEinkDMATransfer(sys::Message *message)
{
    utils::time::Scoped scopedtimming("EinkDMATransfer");

    if (suspended) {
        if (suspendInProgress) {
            LOG_ERROR("drawing before suspend failed");
            suspendInProgress = false;
    sys::MessagePointer ServiceEink::handleEinkDMATransfer(sys::Message *message)
    {
        utils::time::Scoped measurement("EinkDMATransfer");

        if (suspended) {
            if (suspendInProgress) {
                LOG_ERROR("drawing before suspend failed");
                suspendInProgress = false;
            }
            LOG_INFO("[ServiceEink] Received image while suspended, ignoring");
            return sys::MessageNone{};
        }

        LOG_INFO("[ServiceEink] Received image while suspended, ignoring");
    }
    else {
        EinkPowerOn();

        int32_t temperature = EinkGetTemperatureInternal();

        EinkStatus_e ret;
        if (deepRefresh) {
            changeWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature);
            EinkDitherDisplay();
        screen.powerOn();
        if (const auto temperature = EinkGetTemperatureInternal(); deepRefresh) {
            screen.changeWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature);
            screen.dither();
        }
        else {
            changeWaveform(EinkWaveforms_e::EinkWaveformDU2, temperature);
            screen.changeWaveform(EinkWaveforms_e::EinkWaveformDU2, temperature);
        }

        ret = EinkUpdateFrame(
            pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, einkRenderBuffer.get(), Eink4Bpp, displayMode);
        if (ret != EinkOK)
        if (const auto status = screen.update(); status != EinkOK) {
            LOG_FATAL("Failed to update frame");

        if (deepRefresh) {
            ret = EinkRefreshImage(
                pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, EinkDisplayTimingsDeepCleanMode);
        }
        else {
            ret = EinkRefreshImage(
                pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, EinkDisplayTimingsFastRefreshMode);
        }

        if (ret != EinkOK)
        if (const auto status =
                screen.refresh(deepRefresh ? EinkDisplayTimingsDeepCleanMode : EinkDisplayTimingsFastRefreshMode);
            status != EinkOK) {
            LOG_FATAL("Failed to refresh frame");
        }

        powerOffTimer.reload();



@@ 330,35 154,38 @@ sys::MessagePointer ServiceEink::handleEinkDMATransfer(sys::Message *message)
        suspendInProgress  = false;
        shutdownInProgress = false;
        sys::Bus::SendUnicast(msg, service::name::gui, this);

        return sys::MessageNone{};
    }
    return nullptr;
}

sys::MessagePointer ServiceEink::handleImageMessage(sys::Message *request)
{
    auto message = static_cast<service::eink::ImageMessage *>(request);
    sys::MessagePointer ServiceEink::handleImageMessage(sys::Message *request)
    {
        auto message = static_cast<service::eink::ImageMessage *>(request);

    powerOffTimer.stop();
    memcpy(einkRenderBuffer.get(), message->getData(), message->getSize());
    deepRefresh = message->getDeepRefresh();
        powerOffTimer.stop();
        screen.setScreenBuffer(message->getData(), message->getSize());
        deepRefresh = message->getDeepRefresh();

    shutdownInProgress = message->getShutdown();
    if (shutdownInProgress)
        LOG_DEBUG("Shutdown In Progress");
        shutdownInProgress = message->getShutdown();
        if (shutdownInProgress) {
            LOG_DEBUG("Shutdown In Progress");
        }
        suspendInProgress = message->getSuspend();
        if (suspendInProgress) {
            LOG_DEBUG("Suspend In Progress");
        }

    suspendInProgress = message->getSuspend();
    if (suspendInProgress)
        LOG_DEBUG("Suspend In Progress");
    sys::Bus::SendUnicast(std::make_shared<service::eink::EinkDMATransfer>(), GetName(), this);
    return std::make_shared<sys::ResponseMessage>();
}
        sys::Bus::SendUnicast(std::make_shared<service::eink::EinkDMATransfer>(), GetName(), this);
        return std::make_shared<sys::ResponseMessage>();
    }

sys::MessagePointer ServiceEink::handleStateRequest(sys::Message *)
{
    return std::make_shared<service::gui::GUIDisplayReady>(suspendInProgress, shutdownInProgress);
}
    sys::MessagePointer ServiceEink::handleStateRequest(sys::Message *)
    {
        return std::make_shared<service::gui::GUIDisplayReady>(suspendInProgress, shutdownInProgress);
    }

sys::MessagePointer ServiceEink::handleTemperatureUpdate(sys::Message *)
{
    return nullptr;
}
    sys::MessagePointer ServiceEink::handleTemperatureUpdate(sys::Message *)
    {
        return nullptr;
    }
} // namespace eink

M module-services/service-eink/ServiceEink.hpp => module-services/service-eink/ServiceEink.hpp +50 -54
@@ 13,58 13,54 @@
#include <cstdint>
#include <string>

class ServiceEink : public sys::Service
{
    gui::Size screen        = {480, 600};
    gui::Point pointTopLeft = {0, 0};
    std::unique_ptr<uint8_t[]> einkRenderBuffer;

  protected:
    // counts timer triggers from last self refresh
    uint32_t selfRefereshTriggerCount;
    // counts timer events from last temperature measurement
    uint32_t temperatureMeasurementTriggerCount;
    // counts trigger counts from last action that required eink to be powered on
    uint32_t powerOffTriggerCount;

    // number of timer triggers required to execute self refresh handler
    const uint32_t selfRefereshTriggerValue = 60;
    // number of timer triggers required to execute temperature measurement handler
    const uint32_t temperatureMeasurementTriggerValue = 5 * 60;
    // number of timer triggers from last action requiring power on eink to power down eink.
    const uint32_t powerOffTriggerValue = 3;

    // structure with recently loaded waveformdata
    EinkWaveFormSettings_t waveformSettings;

    EinkDisplayColorMode_e displayMode = EinkDisplayColorMode_e::EinkDisplayColorModeStandard;

    bool suspended = false;

    bool suspendInProgress  = false;
    bool shutdownInProgress = false;
#include "EinkScreen.hpp"

    bool changeWaveform(EinkWaveforms_e Mode, const int32_t temperature);

    bool deepClearScreen(int8_t temperature);

    bool deepRefresh         = false;

    sys::ms powerOffTime = 3000;
    sys::Timer powerOffTimer;

  public:
    ServiceEink(const std::string &name, std::string parent = "");
    ~ServiceEink() override;

    sys::MessagePointer DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) override;
    sys::ReturnCodes InitHandler() override;
    sys::ReturnCodes DeinitHandler() override;
    sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override final;

  private:
    sys::MessagePointer handleEinkDMATransfer(sys::Message *message);
    sys::MessagePointer handleImageMessage(sys::Message *message);
    sys::MessagePointer handleStateRequest(sys::Message *messge);
    sys::MessagePointer handleTemperatureUpdate(sys::Message *);
};
namespace eink
{
    class ServiceEink : public sys::Service
    {
      protected:
        EinkScreen screen;

        // counts timer triggers from last self refresh
        uint32_t selfRefereshTriggerCount;
        // counts timer events from last temperature measurement
        uint32_t temperatureMeasurementTriggerCount;
        // counts trigger counts from last action that required eink to be powered on
        uint32_t powerOffTriggerCount;

        // number of timer triggers required to execute self refresh handler
        const uint32_t selfRefereshTriggerValue = 60;
        // number of timer triggers required to execute temperature measurement handler
        const uint32_t temperatureMeasurementTriggerValue = 5 * 60;
        // number of timer triggers from last action requiring power on eink to power down eink.
        const uint32_t powerOffTriggerValue = 3;

        bool suspended = false;

        bool suspendInProgress  = false;
        bool shutdownInProgress = false;
        bool deepRefresh        = false;

        sys::ms powerOffTime = 3000;
        sys::Timer powerOffTimer;

      public:
        explicit ServiceEink(const std::string &name, std::string parent = {});

        sys::MessagePointer DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) override;
        sys::ReturnCodes InitHandler() override;
        sys::ReturnCodes DeinitHandler() override;
        sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override final;

      private:
        void enterActiveMode();
        void suspend();

        sys::MessagePointer handleEinkModeChangedMessage(sys::Message *message);
        sys::MessagePointer handleEinkDMATransfer(sys::Message *message);
        sys::MessagePointer handleImageMessage(sys::Message *message);
        sys::MessagePointer handleStateRequest(sys::Message *messge);
        sys::MessagePointer handleTemperatureUpdate(sys::Message *);
    };
} // namespace eink