~aleteoryx/muditaos

c58bb1ad70cef0b9a37340f3840db0ba895eee27 — wojtekrzepecki 5 years ago a44ce56
[EGD 4654] spike light sensor to display (#1158)

* [EGD-4654] Light control first working

* [EGD-4654] Extended interface with custom brightness function

* [EGD-4654] Extended set of actions

* [EGD-4654] Fixed timer issue + cleanup

* [EGD-4654] Improving switch-case logic

* [EGD-4654] Moved keypad light control to previous state

* [EGD-4654] Refactor in screen light message

* [EGD-4654] Documentation work

* [EGD-4654] Extension of docu

* [EGD-4654] Typo fixing

* [EGD-4654] More improvements in doc

* [EGD-4654] Doxy generation fixes

* [EGD-4654] Pre pr cleanup

* [EGD-4654] PR comments pt 1

* [EGD-4654] Docu small change

* [EGD-4654] Doc updated with pumls

* [EGD-4654] added newlines at the end

Co-authored-by: Wojtek Rzepecki <wojtek.rzepecki@mudita.com>
M module-bsp/board/linux/eink_frontlight/eink_frontlight.cpp => module-bsp/board/linux/eink_frontlight/eink_frontlight.cpp +3 -0
@@ 20,4 20,7 @@ namespace bsp::eink_frontlight
    void turnOff()
    {}

    void setGammaFactor(float)
    {}

} // namespace bsp::eink_frontlight

M module-bsp/board/rt1051/bsp/eink_frontlight/eink_frontlight.cpp => module-bsp/board/rt1051/bsp/eink_frontlight/eink_frontlight.cpp +9 -4
@@ 13,12 13,12 @@ namespace bsp::eink_frontlight
    {
        std::shared_ptr<drivers::DriverPWM> pwm;
        constexpr inline auto PWM_FREQUENCY_HZ = 20000;
        constexpr inline float gamma           = 2.5f;
        float gammaFactor                      = 2.5f;

        constexpr inline std::uint8_t gammaCorrection(BrightnessPercentage brightness)
        std::uint8_t gammaCorrection(BrightnessPercentage brightness)
        {
            std::clamp(brightness, static_cast<std::uint8_t>(0), static_cast<std::uint8_t>(100));
            return static_cast<std::uint8_t>(100 * std::pow((brightness / 100.0f), gamma));
            std::clamp(brightness, 0.0f, 100.0f);
            return static_cast<std::uint8_t>(100 * std::pow((brightness / 100.0f), gammaFactor));
        }

    } // namespace


@@ 55,4 55,9 @@ namespace bsp::eink_frontlight
        pwm->Stop();
    }

    void setGammaFactor(float gamma)
    {
        gammaFactor = gamma;
    }

} // namespace bsp::eink_frontlight

M module-bsp/board/rt1051/bsp/light_sensor/LTR303ALS.hpp => module-bsp/board/rt1051/bsp/light_sensor/LTR303ALS.hpp +16 -6
@@ 28,22 28,32 @@ namespace bsp::light_sensor
        INTERRUPT_PERSIST = 0x9E
    };

    constexpr inline auto ACTIVE_MODE = 0b00000001;
    constexpr inline auto MEASUREMENT_GAIN_8 = 0b00001100;
    constexpr inline auto ACTIVE_MODE        = 0b00000001 | MEASUREMENT_GAIN_8;
    constexpr inline auto SW_RESET    = 0b00000010;

    constexpr inline auto INTEGRATION_TIME_50MS  = 0b00001000;
    constexpr inline auto ALS_INT_50MS           = 0.5f;
    constexpr inline auto INTEGRATION_TIME_100MS = 0b00000000;
    constexpr inline auto ALS_INT_100MS          = 1;
    constexpr inline auto INTEGRATION_TIME_200MS = 0b00010000;
    constexpr inline auto ALS_INT_200MS          = 2;
    constexpr inline auto INTEGRATION_TIME_400MS = 0b00011000;
    constexpr inline auto ALS_INT_400MS          = 4;

    constexpr inline auto ALS_GAIN = 1.0f;
    // Factor of light resistance of window above the sensor - roughly estimated value
    constexpr inline auto PFACTOR           = 0.8f;
    constexpr inline auto MEASUREMENT_COEFF = (ALS_INT_200MS * ALS_GAIN) / PFACTOR;

    constexpr inline auto MEASUREMENT_RATE_50MS   = 0b00000000;
    constexpr inline auto MEASUREMENT_RATE_100MS  = 0b00000001;
    constexpr inline auto MEASUREMENT_RATE_500MS  = 0b00000011;
    constexpr inline auto MEASUREMENT_RATE_1000MS = 0b00000100;
    constexpr inline auto MEASUREMENT_RATE_2000MS = 0b00000111;

    constexpr inline auto MEAS_SETUP = INTEGRATION_TIME_400MS | MEASUREMENT_RATE_500MS;

    constexpr inline auto ALS_GAIN = 8.0f;
    // Factor of light resistance of window above the sensor - roughly estimated value
    constexpr inline auto PFACTOR           = 0.8f;
    constexpr inline auto MEASUREMENT_COEFF = (ALS_INT_400MS * ALS_GAIN) / PFACTOR;

    constexpr inline auto ENABLE_IRQ = 0x0A;

    constexpr inline auto MANUFACTURER_ID = 0x05;

M module-bsp/board/rt1051/bsp/light_sensor/light_sensor.cpp => module-bsp/board/rt1051/bsp/light_sensor/light_sensor.cpp +1 -1
@@ 60,7 60,7 @@ namespace bsp::light_sensor

        void configureMeasurement()
        {
            std::uint8_t reg = MEASUREMENT_RATE_500MS | INTEGRATION_TIME_200MS;
            std::uint8_t reg = MEAS_SETUP;
            writeSingleRegister(static_cast<std::uint32_t>(LTR303ALS_Registers::ALS_MEAS_RATE), &reg);
        }


M module-bsp/bsp/eink_frontlight/eink_frontlight.hpp => module-bsp/bsp/eink_frontlight/eink_frontlight.hpp +3 -1
@@ 16,7 16,7 @@ namespace bsp::eink_frontlight
        setBrightness,
    };

    using BrightnessPercentage = uint8_t;
    using BrightnessPercentage = float;

    void init();



@@ 27,4 27,6 @@ namespace bsp::eink_frontlight
    void turnOn();
    
    void turnOff();

    void setGammaFactor(float gamma);
} // namespace bsp::eink_frontlight

M module-services/service-evtmgr/CMakeLists.txt => module-services/service-evtmgr/CMakeLists.txt +1 -0
@@ 7,6 7,7 @@ set(SOURCES
        alarm/EventManagerAlarm.cpp
        api/EventManagerServiceAPI.cpp
        messages/Message.cpp
        ScreenLightControl.cpp
)

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

M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +6 -26
@@ 7,6 7,7 @@
#include "service-evtmgr/EventManager.hpp"
#include "service-evtmgr/KbdMessage.hpp"
#include "service-evtmgr/WorkerEvent.hpp"
#include "service-evtmgr/ScreenLightControl.hpp"

#include <BaseInterface.hpp>
#include <MessageType.hpp>


@@ 19,7 20,6 @@
#include <bsp/magnetometer/magnetometer.hpp>
#include <bsp/rtc/rtc.hpp>
#include <bsp/torch/torch.hpp>
#include <bsp/keypad_backlight/keypad_backlight.hpp>
#include <common_data/RawKey.hpp>
#include <log/log.hpp>
#include <module-utils/time/time_conversion.hpp>


@@ 30,7 30,6 @@
#include <service-db/DBNotificationMessage.hpp>
#include <service-desktop/Constants.hpp>
#include <service-desktop/DesktopMessages.hpp>
#include <bsp/light_sensor/light_sensor.hpp>
#include <cassert>
#include <list>
#include <tuple>


@@ 280,17 279,12 @@ sys::ReturnCodes EventManager::InitHandler()
        return response;
    });

    connect(sevm::EinkFrontlightMessage(), [&](sys::Message *msgl) {
        auto msg = static_cast<sevm::EinkFrontlightMessage *>(msgl);
        processEinkFrontlightRequest(msg->action, msg->value);
    connect(sevm::ScreenLightControlMessage(sevm::screen_light_control::Action::turnOff), [&](sys::Message *msgl) {
        auto request = static_cast<sevm::ScreenLightControlMessage *>(msgl);
        sevm::screen_light_control::processRequest(request->action, request->parameters);
        return std::make_shared<sys::ResponseMessage>();
    });

    connect(sevm::LightSensorMessage(), [&](sys::Message *msgl) {
        auto message = std::make_shared<sevm::LightSensorReadoutMessage>(bsp::light_sensor::readout());
        return message;
    });

    // initialize keyboard worker
    EventWorker = std::make_unique<WorkerEvent>(this);



@@ 325,12 319,14 @@ sys::ReturnCodes EventManager::InitHandler()

    EventWorker->init(list);
    EventWorker->run();
    sevm::screen_light_control::init(this);

    return sys::ReturnCodes::Success;
}

sys::ReturnCodes EventManager::DeinitHandler()
{
    sevm::screen_light_control::deinit();
    EventWorker->close();
    EventWorker.reset();
    EventWorker = nullptr;


@@ 379,19 375,3 @@ bool EventManager::processKeypadBacklightRequest(bsp::keypad_backlight::Action a
    }
    return response;
}

void EventManager::processEinkFrontlightRequest(bsp::eink_frontlight::Action act,
                                                bsp::eink_frontlight::BrightnessPercentage val)
{
    switch (act) {
    case bsp::eink_frontlight::Action::turnOn:
        bsp::eink_frontlight::turnOn();
        break;
    case bsp::eink_frontlight::Action::turnOff:
        bsp::eink_frontlight::turnOff();
        break;
    case bsp::eink_frontlight::Action::setBrightness:
        bsp::eink_frontlight::setBrightness(val);
        break;
    }
}

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

#include "service-evtmgr/ScreenLightControl.hpp"

namespace sevm::screen_light_control
{
    namespace
    {
        constexpr inline auto CONTROL_TIMER_MS = 25;
        constexpr inline auto READOUT_TIMER_MS = 500;
        std::unique_ptr<sys::Timer> controlTimer;
        std::unique_ptr<sys::Timer> readoutTimer;

        bool automaticMode = false;
        bool lightOn       = false;
        struct FunctionSection
        {
            float xBound;
            float a;
            float b;
        };

        std::vector<FunctionSection> brightnessFunction;
        float rampStep;
        float brightnessHysteresis;

        bsp::eink_frontlight::BrightnessPercentage brightnessRampTarget;
        bsp::eink_frontlight::BrightnessPercentage brightnessRampState;
        bool rampTargetReached = false;

        float calculateBrightness(bsp::light_sensor::IlluminanceLux measurement)
        {
            for (const auto &section : brightnessFunction) {
                if (measurement < section.xBound) {
                    return section.a * measurement + section.b;
                }
            }

            if (brightnessFunction.empty()) {
                return 0.0f;
            }
            return brightnessFunction.back().xBound * brightnessFunction.back().a + brightnessFunction.back().b;
        }

        bsp::eink_frontlight::BrightnessPercentage brightnessRampOut()
        {
            if (rampTargetReached && std::abs(brightnessRampTarget - brightnessRampState) > brightnessHysteresis) {
                rampTargetReached = false;
            }

            if (!rampTargetReached) {
                if (brightnessRampState < brightnessRampTarget) {
                    brightnessRampState += rampStep;
                    if (brightnessRampState > brightnessRampTarget) {
                        rampTargetReached = true;
                    }
                }
                else if (brightnessRampState > brightnessRampTarget) {
                    brightnessRampState -= rampStep;
                    if (brightnessRampState < brightnessRampTarget) {
                        brightnessRampState = brightnessRampTarget;
                        rampTargetReached   = true;
                    }
                }
            }

            return brightnessRampState;
        }

        void controlTimerCallback()
        {
            auto out = brightnessRampOut();
            bsp::eink_frontlight::setBrightness(out);
        }

        void readoutTimerCallback()
        {
            float lightMeasurement = bsp::light_sensor::readout();
            brightnessRampTarget   = calculateBrightness(lightMeasurement);
        }

        void enableTimers()
        {
            controlTimer->connect([&](sys::Timer &) { controlTimerCallback(); });
            readoutTimer->connect([&](sys::Timer &) { readoutTimerCallback(); });
            controlTimer->start();
            readoutTimer->start();
        }

        void disableTimers()
        {
            controlTimer->stop();
            readoutTimer->stop();
        }

        void setAutomaticModeParameters(const Parameters &params)
        {
            if (lightOn && automaticMode) {
                disableTimers();
            }

            rampStep = 100.0f * (static_cast<float>(CONTROL_TIMER_MS) / static_cast<float>(params.rampTimeMS));
            brightnessHysteresis = params.brightnessHysteresis;

            if (!params.functionPoints.empty()) {
                brightnessFunction.clear();
                for (unsigned int i = 0; i < params.functionPoints.size(); ++i) {
                    FunctionSection section;
                    section.xBound = params.functionPoints[i].first;
                    if (i == 0) {
                        section.a = 0.0f;
                        section.b = params.functionPoints[i].second;
                    }
                    else {
                        section.a = (params.functionPoints[i - 1].second - params.functionPoints[i].second) /
                                    (params.functionPoints[i - 1].first - params.functionPoints[i].first);
                        section.b =
                            params.functionPoints[i - 1].second - section.a * params.functionPoints[i - 1].first;
                    }
                    brightnessFunction.push_back(section);
                }
            }

            if (lightOn && automaticMode) {
                enableTimers();
            }
        }

        void turnOff()
        {
            bsp::eink_frontlight::turnOff();
            bsp::light_sensor::standby();
            controlTimer->stop();
            readoutTimer->stop();
            lightOn = false;
        }

        void turnOn()
        {
            bsp::eink_frontlight::turnOn();
            bsp::light_sensor::wakeup();
            if (automaticMode) {
                enableTimers();
            }
            lightOn = true;
        }

        void enableAutomaticMode()
        {
            if (lightOn) {
                enableTimers();
            }
            automaticMode = true;
        }

        void disableAutomaticMode()
        {
            disableTimers();
            automaticMode = false;
        }

    } // namespace

    void init(sys::Service *parent)
    {
        controlTimer = std::make_unique<sys::Timer>("LightControlTimer", parent, CONTROL_TIMER_MS);
        readoutTimer = std::make_unique<sys::Timer>("LightSensorReadoutTimer", parent, READOUT_TIMER_MS);

        Parameters defaultParams;
        setAutomaticModeParameters(defaultParams);
    }

    void deinit()
    {
        disableTimers();
    }

    void processRequest(Action action, const Parameters &params)
    {
        switch (action) {
        case Action::turnOff:
            turnOff();
            break;
        case Action::turnOn:
            turnOn();
            break;
        case Action::enableAutomaticMode:
            enableAutomaticMode();
            break;
        case Action::disableAutomaticMode:
            disableAutomaticMode();
            break;
        case Action::setManualModeBrightness:
            bsp::eink_frontlight::setBrightness(params.manualModeBrightness);
            break;
        case Action::setGammaCorrectionFactor:
            bsp::eink_frontlight::setGammaFactor(params.gammaFactor);
            break;
        case Action::setAutomaticModeParameters:
            setAutomaticModeParameters(params);
            break;
        }
    }

} // namespace sevm::screen_light_control

A module-services/service-evtmgr/doc/gamma_correction.svg => module-services/service-evtmgr/doc/gamma_correction.svg +3 -0
@@ 0,0 1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(255, 255, 255);" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="293px" height="213px" viewBox="-0.5 -0.5 293 213" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2020-12-08T13:23:26.829Z&quot; agent=&quot;5.0 (X11)&quot; version=&quot;13.10.9&quot; etag=&quot;cIShF1NpU-w7JGPpKOUe&quot; type=&quot;google&quot;&gt;&lt;diagram id=&quot;yTwYNGOTziU7TQcxtC_X&quot;&gt;7VhNc5swEP01PjYjvvExttPm0M5kJoe2RxmtjaYycoXs4P76SiAMAkLs1Jl6JvHF6Gm1rN6+FQsTb74pvgi8Tb9xAmziIlJMvMXEdR0vctWfRg4VEiOvAtaCkgpCDfBI/4BZWaM7SiA3WAVJzpmkWxtMeJZBIi0MC8GfbLMVZ8QCtngNPeAxwayPfqdEpmYXbtTg90DXaX1nJ5xWMxtcGxsXeYoJf2pt2bubeHPBuayuNsUcmCbP5uXzM7PHwARk8pQFJhF7zHZmbyYueag3Cxm51ZypUcJwntNk4s1SuWEKcNRltQBIj7ImBue4MyUJ4BuQ4qBMnhruApPZtEVbjQlgWNK97R6bFK6P7o53eOBU3dhFRm6Bi25Q62d2aKQ3re9SO8z5TiRgfLSZO8ttFHfcSizWIHtu1UWLkgYq0zScMu8jZa9MWRSicUeXS5J/VpIynoGdoRXPpDn3VAV7s1wK/ut42LjGYs4ZF6U3T9M0n2ucMtbCCYZ4lRw9tGbCJIbl6h2rAdlqiJ03U0PwGjUQnKdAjB5a0hiWQlssHQms4gSSQQks48AP0IcEzpWAyhQ+tMy22iAfCT/qHDw+Go0r7MQ19V6w9zv26AX7TjxxaNmri2qHr9V72NP711ISyiKTkOV0T+WhVwISCmlLHTO6zvRDTKkQlGZnexCSqj7s1kxsKCF6+UxATv/gZelKC9pkRPkNZpNgoX3tJM+rIukcsE5TAdo/FOfWQK0mJ+jI0oxbNRIP1IjbEV27HKxDZ4TxaOCECZmmk9C9RXP4e6eby5KATxUht8rAcbdFSUM9r67W5p/V9v/kaFACqHa/FBeJUvFUBdqDSx5s9GqoYTQDrAlIUpypM/i8zVxLGbmXKaOg06VFU9QrIwcN1JF3gTqKx+uoovx+t8GZfl4e4ASBVdAW1BNpKynP2op/V4ntno+x30/s0Pl4ibxOz+rABl+a7L5riKPnWnIoqPyhib/xp74Z/9SLbhwnNuNFYTJTDg6twQMIqvar87sY6dWqnsd6GlxN+9btN6Jun3Vqw6Yao5E+MI7erIOvA/7vAnIdW0AoegsBxVcmoMi38+7Yee9+bDlZTsHoawW6lJzUsPmkV5k3H0a9u78=&lt;/diagram&gt;&lt;/mxfile&gt;"><defs><style xmlns="http://www.w3.org/1999/xhtml" type="text/css">.MathJax_Preview {color: #888; display: contents}&#xa;#MathJax_Message {position: fixed; left: 1px; bottom: 2px; background-color: #E6E6E6; border: 1px solid #959595; margin: 0px; padding: 2px 8px; z-index: 102; color: black; font-size: 80%; width: auto; white-space: nowrap}&#xa;#MathJax_MSIE_Frame {position: absolute; top: 0; left: 0; width: 0px; z-index: 101; border: 0px; margin: 0px; padding: 0px}&#xa;.MathJax_Error {color: #CC0000; font-style: italic}&#xa;</style></defs><g><path d="M 10 180 L 10 16.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 10 11.12 L 13.5 18.12 L 10 16.37 L 6.5 18.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 10 180 L 243.63 180" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 248.88 180 L 241.88 183.5 L 243.63 180 L 241.88 176.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 10 180 L 190 40" fill="none" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 10 180 L 50.19 171.96 Q 60 170 69.49 166.84 L 80.51 163.16 Q 90 160 98 154 L 122 136 Q 130 130 136 122 L 154 98 Q 160 90 165.14 81.43 L 190 40" fill="none" stroke="#b85450" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="6 6" pointer-events="stroke"/><rect x="205" y="185" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 195px; margin-left: 245px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; ">Light intensivity</div></div></div></foreignObject><text x="245" y="198" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">Light intensivity</text></switch></g><rect x="50" y="20" width="100" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 35px; margin-left: 100px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; "><div style="font-size: 12px"><font style="font-size: 12px">Light intensivity <br style="font-size: 12px" /></font></div><div style="font-size: 12px"><font style="font-size: 12px">linear change</font></div></div></div></div></foreignObject><text x="100" y="39" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Light intensivit...</text></switch></g><rect x="205" y="70" width="80" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 85px; margin-left: 245px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; "><div>Human eye</div><div>perception<br /></div></div></div></div></foreignObject><text x="245" y="89" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Human eye...</text></switch></g><path d="M 99.4 53.54 L 91.26 93.76" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 90.22 98.9 L 88.18 91.35 L 91.26 93.76 L 95.04 92.74 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 238.68 103.21 L 146.15 128.33" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 141.08 129.71 L 146.92 124.5 L 146.15 128.33 L 148.75 131.25 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file

A module-services/service-evtmgr/doc/light_control_algorithm.puml => module-services/service-evtmgr/doc/light_control_algorithm.puml +21 -0
@@ 0,0 1,21 @@
@startuml

hide footbox

box "500ms" #LightBlue
participant "Light Measurement" as lm
participant "Brightness Function" as bc
participant "Ramp target \nvalue update" as ru
end box

box "25ms" #LightGreen
participant "Current ramp value \ncalculation with hysteresis" as rc
participant "Light driver duty cycle \nupdate with Gamma Correction" as ld
end box

lm -> bc : measurement
bc -> ru : brightness \nvalue
ru -> rc : ramp target
rc -> ld : current ramp \nvalue

@enduml

A module-services/service-evtmgr/doc/light_control_algorithm.svg => module-services/service-evtmgr/doc/light_control_algorithm.svg +31 -0
@@ 0,0 1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="250px" preserveAspectRatio="none" style="width:900px;height:250px;" version="1.1" viewBox="0 0 900 250" width="900px" zoomAndPan="magnify"><defs><filter height="300%" id="f1mh8i95ojgk88" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><rect fill="#ADD8E6" height="231.5234" style="stroke:#A80036;stroke-width:1.0;" width="443" x="1" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="49" x="198" y="18.0669">500ms</text><rect fill="#90EE90" height="231.5234" style="stroke:#A80036;stroke-width:1.0;" width="448" x="446" y="6"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="40" x="650" y="18.0669">25ms</text><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="80" x2="80" y1="76.7266" y2="243.5234"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="244" x2="244" y1="76.7266" y2="243.5234"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="386" x2="386" y1="76.7266" y2="243.5234"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="549" x2="549" y1="76.7266" y2="243.5234"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="774" x2="774" y1="76.7266" y2="243.5234"/><rect fill="#FEFECE" filter="url(#f1mh8i95ojgk88)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="147" x="5" y="41.4297"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="133" x="12" y="61.4248">Light Measurement</text><rect fill="#FEFECE" filter="url(#f1mh8i95ojgk88)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="153" x="166" y="41.4297"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="139" x="173" y="61.4248">Brightness Function</text><rect fill="#FEFECE" filter="url(#f1mh8i95ojgk88)" height="46.5938" style="stroke:#A80036;stroke-width:1.5;" width="103" x="333" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="340" y="45.1279">Ramp target</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="89" x="340" y="61.4248">value update</text><rect fill="#FEFECE" filter="url(#f1mh8i95ojgk88)" height="46.5938" style="stroke:#A80036;stroke-width:1.5;" width="194" x="450" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="131" x="479.5" y="45.1279">Current ramp value</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="180" x="457" y="61.4248">calculation with hysteresis</text><rect fill="#FEFECE" filter="url(#f1mh8i95ojgk88)" height="46.5938" style="stroke:#A80036;stroke-width:1.5;" width="228" x="658" y="25.1328"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="148" x="696" y="45.1279">Light driver duty cycle</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="214" x="665" y="61.4248">update with Gamma Correction</text><polygon fill="#A80036" points="232.5,103.8594,242.5,107.8594,232.5,111.8594,236.5,107.8594" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="80.5" x2="238.5" y1="107.8594" y2="107.8594"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="91" x="87.5" y="102.7935">measurement</text><polygon fill="#A80036" points="374.5,148.125,384.5,152.125,374.5,156.125,378.5,152.125" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="244.5" x2="380.5" y1="152.125" y2="152.125"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="67" x="251.5" y="131.9263">brightness</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="34" x="251.5" y="147.0591">value</text><polygon fill="#A80036" points="537,177.2578,547,181.2578,537,185.2578,541,181.2578" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="386.5" x2="543" y1="181.2578" y2="181.2578"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="77" x="393.5" y="176.1919">ramp target</text><polygon fill="#A80036" points="762,221.5234,772,225.5234,762,229.5234,766,225.5234" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="549" x2="768" y1="225.5234" y2="225.5234"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="84" x="556" y="205.3247">current ramp</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="34" x="556" y="220.4575">value</text><!--MD5=[41ba1f1179bd1c783f736902b7001ce8]
@startuml

hide footbox

box "500ms" #LightBlue
participant "Light Measurement" as lm
participant "Brightness Function" as bc
participant "Ramp target \nvalue update" as ru
end box

box "25ms" #LightGreen
participant "Current ramp value \ncalculation with hysteresis" as rc
participant "Light driver duty cycle \nupdate with Gamma Correction" as ld
end box

lm -> bc : measurement
bc -> ru : brightness \nvalue
ru -> rc : ramp target
rc -> ld : current ramp \nvalue

@enduml

PlantUML version 1.2020.22(Sun Dec 06 10:36:27 CET 2020)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Default Encoding: UTF-8
Language: pl
Country: PL
--></g></svg>
\ No newline at end of file

A module-services/service-evtmgr/doc/light_control_function.svg => module-services/service-evtmgr/doc/light_control_function.svg +3 -0
@@ 0,0 1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(255, 255, 255);" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="472px" height="256px" viewBox="-0.5 -0.5 472 256" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2020-12-08T13:24:39.119Z&quot; agent=&quot;5.0 (X11)&quot; version=&quot;13.10.9&quot; etag=&quot;zb_maAve2Po3ZfvaQdIF&quot; type=&quot;google&quot;&gt;&lt;diagram id=&quot;bYA3SPjRXRI9Chxooi9G&quot;&gt;7VpLc9owEP41PjYjvzA+EkjbQzvTGQ5tjwIroIltUVkmpr++kiU/ZV7GpCEDF6zP8nq13+5qtWDY0yj7QuFm/Z0EKDQsEGSGPTMsy7Q9i38JZCeRMbAlsKI4kBCogDn+i9STBZriACUKkxAjJGR40wSXJI7RkjUwSCl5bU57JmHQADZwhTRgvoShjv7EAVurVVhehX9FeLUu3myOfHkngsVkJSJZw4C81pZsPxn2lBLC5FWUTVEojNe0y+c9d0vFKIrZKQ8oIrYwTNXalF5sVywWxcFE2IyPliFMErw07Mc1i0IOmPxSPoACzWSVDma5Mu4SiESI0R2f8lrZzlXMrmtmKzCKQsjwtikeKgpXpbjyDT8I5i+2QOluSo5yNtPxQVNGQlK6ROqxurGOSbLakhikK8Q0SfyitvAKysnoJsa+E3M6MY4LHkDtYx2ROxxNTgdNo5Db6THA2wZdoz+pCOrHZxKzT0me0iZ8ggk2Wc5VcZ9frcT3fEkRig0Rv1Lggl4krxDDV5Sr1kQv1XZBhXvEKEmExi7HXcOd3YTqV1ayFbQMZawZpjDEq1jEMI9KRDmwRZRhvtlM1I0IB4F4/JEirgdc5KIAH2+EA+cuzQ3Orc1lpYxIXXPRQne1e5qg1EbIR9m5OSFrxn6ZAscKqOWMcUfOcMD+9NCIvwPB5h4ONmnxSbTAKE8OYf72ZgCd7FASCtMIxzBeIunV39Ks5tUfnGnH1NKzq1Htd1BtD0D16KztLyYxapq62x7veC+0Wsa221bsXaRokvbsftyYcFebpnzufJUriqXIvnurp/mACcC7CTNvmCjz20a09CCzO/zOHCDIxgMGmRgnjJKX8kRkl8iUhITm8mwAfB/cREBqxWnvgPSOSRquHPXvjO5llO9nBw8Jbt/Dh32e3OHYLnLAne4OWvTM2pvgMx1nQILNO8H7CW7Xp70JfssMbVp3St+A0uPRPyCleq/uKYPRJpQtg/9ZrxqXF6iW1vTsOPF7HT5hDVChmkf6a/LoPYP0RTgfIZFhTU8+3C/QM6HiZJ+ECG1O7xLcOKFOO9n5Op9dHZwhjvXmSS2cAEcRCoyLOjew3Qb6kFxaoE2m+YZk6k2aeRorc9+4YZ2is1/uHx1RMrpW1tM7H4dqhJv88ae9PzvjvsWbls9s/2o7/Xkdk5skRuvwO6BvDaY1CEfXq8HO63x0MoMyzH6JHPPgeI4a/xb3HkzPV+NZppJQPtjVBj8QxVxhkcpmB2ptabxmAn033DujNvftdHbymdk5fGZu/xzU2xP4sPq3hJxe/efEfvoH&lt;/diagram&gt;&lt;/mxfile&gt;"><defs><style xmlns="http://www.w3.org/1999/xhtml" type="text/css">.MathJax_Preview {color: #888; display: contents}&#xa;#MathJax_Message {position: fixed; left: 1px; bottom: 2px; background-color: #E6E6E6; border: 1px solid #959595; margin: 0px; padding: 2px 8px; z-index: 102; color: black; font-size: 80%; width: auto; white-space: nowrap}&#xa;#MathJax_MSIE_Frame {position: absolute; top: 0; left: 0; width: 0px; z-index: 101; border: 0px; margin: 0px; padding: 0px}&#xa;.MathJax_Error {color: #CC0000; font-style: italic}&#xa;</style></defs><g><path d="M 93 213 L 93 19.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 93 14.12 L 96.5 21.12 L 93 19.37 L 89.5 21.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 93 213 L 406.63 213" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 411.88 213 L 404.88 216.5 L 406.63 213 L 404.88 209.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="13" y="3" width="80" height="40" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 23px; margin-left: 53px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; "><div style="font-size: 10px">Screen <br style="font-size: 10px" /></div><div style="font-size: 10px">brightness [%]<br style="font-size: 10px" /></div><div style="font-size: 10px"><br style="font-size: 10px" /></div></div></div></div></foreignObject><text x="53" y="26" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">Screen...</text></switch></g><rect x="373" y="218" width="90" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 233px; margin-left: 418px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; "><div>Ambient light <br /></div><div>luminance [Lux]</div></div></div></div></foreignObject><text x="418" y="236" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">Ambient light...</text></switch></g><path d="M 83 53 L 83 53 L 93 53" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><rect x="53" y="48" width="30" height="10" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 53px; margin-left: 68px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 7px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; ">100</div></div></div></foreignObject><text x="68" y="55" fill="#000000" font-family="Helvetica" font-size="7px" text-anchor="middle">100</text></switch></g><path d="M 93 153 L 133 153" fill="none" stroke="#009900" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 373 173 L 393 173" fill="none" stroke="#009900" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 253 73 L 373 173" fill="none" stroke="#009900" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 173 73 L 133 153" fill="none" stroke="#009900" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 173 73 L 253 73" fill="none" stroke="#009900" stroke-width="3" stroke-miterlimit="10" pointer-events="stroke"/><rect x="193" y="3" width="70" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 13px; margin-left: 228px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; ">Example:</div></div></div></foreignObject><text x="228" y="17" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Example:</text></switch></g><rect x="3" y="213" width="80" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 228px; margin-left: 43px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; "><div>Dark room,</div><div>before sleep<br /></div></div></div></div></foreignObject><text x="43" y="232" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Dark room,...</text></switch></g><rect x="163" y="133" width="80" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 148px; margin-left: 203px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; "><div>dimmed <br /></div><div>ambient light</div></div></div></div></foreignObject><text x="203" y="152" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">dimmed...</text></switch></g><rect x="387" y="113" width="60" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 123px; margin-left: 417px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; ">Sunlight</div></div></div></foreignObject><text x="417" y="127" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Sunlight</text></switch></g><path d="M 53 203 L 98.08 166.04" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 102.14 162.71 L 98.94 169.85 L 98.08 166.04 L 94.5 164.44 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 213 123 L 186.82 88.09" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 183.67 83.89 L 190.67 87.39 L 186.82 88.09 L 185.07 91.59 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 415.44 136.58 L 405.71 157.24" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 403.48 161.99 L 403.29 154.16 L 405.71 157.24 L 409.62 157.15 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file

A module-services/service-evtmgr/doc/light_control_hysteresis.puml => module-services/service-evtmgr/doc/light_control_hysteresis.puml +11 -0
@@ 0,0 1,11 @@
@startuml
(*) --> If "Ramp target reached?" then
    If "New target differs by \nhysteresis value from \nthe old one?" then
    -> [Yes] "update ramp" as ur
    else
    ---> [No] "RampValue = \nRampTarget"
    Endif
else
--> [No] ur
Endif
@enduml

A module-services/service-evtmgr/doc/light_control_hysteresis.svg => module-services/service-evtmgr/doc/light_control_hysteresis.svg +26 -0
@@ 0,0 1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="336px" preserveAspectRatio="none" style="width:324px;height:336px;" version="1.1" viewBox="0 0 324 336" width="324px" zoomAndPan="magnify"><defs><filter height="300%" id="f1t5fss58cooth" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><ellipse cx="207.363" cy="16" fill="#000000" filter="url(#f1t5fss58cooth)" rx="10" ry="10" style="stroke:none;stroke-width:1.0;"/><polygon fill="#FEFECE" filter="url(#f1t5fss58cooth)" points="207.363,67,219.363,79,207.363,91,195.363,79,207.363,67" style="stroke:#A80036;stroke-width:1.5;"/><polygon fill="#FEFECE" filter="url(#f1t5fss58cooth)" points="155.363,150,167.363,162,155.363,174,143.363,162,155.363,150" style="stroke:#A80036;stroke-width:1.5;"/><rect fill="#FEFECE" filter="url(#f1t5fss58cooth)" height="33.9688" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="101" x="209.863" y="145"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="81" x="219.863" y="166.1387">update ramp</text><rect fill="#FEFECE" filter="url(#f1t5fss58cooth)" height="47.9375" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="108" x="101.363" y="275"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="84" x="111.363" y="296.1387">RampValue =</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="76" x="117.363" y="310.1074">RampTarget</text><!--MD5=[9e7bccaab94f33706b7c2613f3cc0026]
link start to #3--><path d="M207.363,26 C207.363,35.34 207.363,50.16 207.363,61.59 " fill="none" id="start-to-#3" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="207.363,66.89,211.363,57.89,207.363,61.89,203.363,57.89,207.363,66.89" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="126" x="61.2817" y="59.4561">Ramp target reached?</text><!--MD5=[2773403863a502716c38047ac44135d7]
link #3 to #6--><path d="M202.923,86.91 C193.943,100.89 173.533,132.69 162.603,149.72 " fill="none" id="#3-to-#6" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="159.783,154.11,168.0145,148.7024,162.4868,149.9041,161.2851,144.3764,159.783,154.11" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="120" x="8.5" y="115.3137">New target differs by</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="125" x="6" y="128.1184">hysteresis value from</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="70" x="35.5" y="140.9231">the old one?</text><!--MD5=[7cfa565d5a7a904cbabcd798f1ced685]
link #6 to ur--><path d="M167.403,162 C176.713,162 190.323,162 204.273,162 " fill="none" id="#6-to-ur" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="209.423,162,200.423,158,204.423,162,200.423,166,209.423,162" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="21" x="178.113" y="156.2104">Yes</text><!--MD5=[52e4b7cb87ab58fb7e68b8a1b8520dc5]
link #6 to RampValue = \nRampTarget--><path d="M155.363,174.05 C155.363,194.74 155.363,239.91 155.363,269.63 " fill="none" id="#6-to-RampValue = \nRampTarget" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="155.363,274.99,159.363,265.99,155.363,269.99,151.363,265.99,155.363,274.99" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="15" x="156.363" y="231.2104">No</text><!--MD5=[1fc0cef09484a68efdf13ae0dd1e584d]
link #3 to ur--><path d="M211.883,86.91 C219.583,98.69 235.563,123.09 247.063,140.68 " fill="none" id="#3-to-ur" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="249.883,144.99,248.2958,135.2699,247.1425,140.8079,241.6045,139.6546,249.883,144.99" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="15" x="237.363" y="122.2104">No</text><!--MD5=[eb3de5f3e5b05e23159ab560e1b3b1b4]
@startuml
(*) - -> If "Ramp target reached?" then
    If "New target differs by \nhysteresis value from \nthe old one?" then
    -> [Yes] "update ramp" as ur
    else
    - - -> [No] "RampValue = \nRampTarget"
    Endif
else
- -> [No] ur
Endif
@enduml

PlantUML version 1.2020.22(Sun Dec 06 10:36:27 CET 2020)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Default Encoding: UTF-8
Language: pl
Country: PL
--></g></svg>
\ No newline at end of file

A module-services/service-evtmgr/doc/light_control_message_flow.puml => module-services/service-evtmgr/doc/light_control_message_flow.puml +6 -0
@@ 0,0 1,6 @@
@startuml
Application -> "Event Manager" as evm : ScreenLightControlMessage
evm -> "ScreenLightControl" as slc: processRequest()
slc -> "Eink Frontlight" : request
slc -> "Light Sensor" : request
@enduml

A module-services/service-evtmgr/doc/light_control_message_flow.svg => module-services/service-evtmgr/doc/light_control_message_flow.svg +16 -0
@@ 0,0 1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="220px" preserveAspectRatio="none" style="width:725px;height:220px;" version="1.1" viewBox="0 0 725 220" width="725px" zoomAndPan="magnify"><defs><filter height="300%" id="fywa74pnlo8ex" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="51" x2="51" y1="40.2969" y2="176.8281"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="254" x2="254" y1="40.2969" y2="176.8281"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="400" x2="400" y1="40.2969" y2="176.8281"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="543" x2="543" y1="40.2969" y2="176.8281"/><line style="stroke:#A80036;stroke-width:1.0;stroke-dasharray:5.0,5.0;" x1="663" x2="663" y1="40.2969" y2="176.8281"/><rect fill="#FEFECE" filter="url(#fywa74pnlo8ex)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="89" x="5" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="75" x="12" y="24.9951">Application</text><rect fill="#FEFECE" filter="url(#fywa74pnlo8ex)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="89" x="5" y="175.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="75" x="12" y="195.8232">Application</text><rect fill="#FEFECE" filter="url(#fywa74pnlo8ex)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="117" x="194" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="103" x="201" y="24.9951">Event Manager</text><rect fill="#FEFECE" filter="url(#fywa74pnlo8ex)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="117" x="194" y="175.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="103" x="201" y="195.8232">Event Manager</text><rect fill="#FEFECE" filter="url(#fywa74pnlo8ex)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="146" x="325" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="132" x="332" y="24.9951">ScreenLightControl</text><rect fill="#FEFECE" filter="url(#fywa74pnlo8ex)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="146" x="325" y="175.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="132" x="332" y="195.8232">ScreenLightControl</text><rect fill="#FEFECE" filter="url(#fywa74pnlo8ex)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="112" x="485" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="98" x="492" y="24.9951">Eink Frontlight</text><rect fill="#FEFECE" filter="url(#fywa74pnlo8ex)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="112" x="485" y="175.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="98" x="492" y="195.8232">Eink Frontlight</text><rect fill="#FEFECE" filter="url(#fywa74pnlo8ex)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="100" x="611" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="86" x="618" y="24.9951">Light Sensor</text><rect fill="#FEFECE" filter="url(#fywa74pnlo8ex)" height="30.2969" style="stroke:#A80036;stroke-width:1.5;" width="100" x="611" y="175.8281"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="86" x="618" y="195.8232">Light Sensor</text><polygon fill="#A80036" points="242.5,67.4297,252.5,71.4297,242.5,75.4297,246.5,71.4297" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="51.5" x2="248.5" y1="71.4297" y2="71.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="179" x="58.5" y="66.3638">ScreenLightControlMessage</text><polygon fill="#A80036" points="388,96.5625,398,100.5625,388,104.5625,392,100.5625" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="254.5" x2="394" y1="100.5625" y2="100.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="112" x="261.5" y="95.4966">processRequest()</text><polygon fill="#A80036" points="531,125.6953,541,129.6953,531,133.6953,535,129.6953" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="400" x2="537" y1="129.6953" y2="129.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="49" x="407" y="124.6294">request</text><polygon fill="#A80036" points="651,154.8281,661,158.8281,651,162.8281,655,158.8281" style="stroke:#A80036;stroke-width:1.0;"/><line style="stroke:#A80036;stroke-width:1.0;" x1="400" x2="657" y1="158.8281" y2="158.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="49" x="407" y="153.7622">request</text><!--MD5=[cffb76a7c8b7e9568ee4c32e1fb1e346]
@startuml
Application -> "Event Manager" as evm : ScreenLightControlMessage
evm -> "ScreenLightControl" as slc: processRequest()
slc -> "Eink Frontlight" : request
slc -> "Light Sensor" : request
@enduml

PlantUML version 1.2020.22(Sun Dec 06 10:36:27 CET 2020)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Default Encoding: UTF-8
Language: pl
Country: PL
--></g></svg>
\ No newline at end of file

A module-services/service-evtmgr/doc/screen_light_control.md => module-services/service-evtmgr/doc/screen_light_control.md +59 -0
@@ 0,0 1,59 @@
# Screen frontlight control

## Modes of operation

Screen frontlight can be controlled in Manual and Standalone mode.

### Manual mode

Manual mode refers to constant level of the screen brightness passed to
screen_light_control module via messages in field `sevm::screen_light_control::Parameters::manualModeBrightness` .

### Automatic mode

In the automatic mode screen light is controlled using data from the ambient light sensor. This data is 
then processed with brightness function mapping to obtain particular level of screen lighting. Control algoritm contains ramp, hysteresis and gamma correction calculation.

![](light_control_algorithm.svg "light control algorithm")

## Brightness level function
Brightness level [%] in relation to ambient light luminance level [Lux] is passed as 
a vector of points wchich are automatically interpolated with linear function. `sevm::screen_light_control::Parameters::functionPoints` field is used for parametrization. These
points have to be in ascending order of luminance values. Example:

```
sevm::screen_light_control::BrightnessFunction({{50.0f, 30.0f}, {150.0f, 80.0f}, {400.0f, 80.0f}, {700.0f, 40.0f}});
```
Which could be translated as follows:

![](light_control_function.svg "light control function example")

NOTE: Values before first point and after last point are set to constant level of brightness described by this points. Values in between are interpolated.

## Ramp
Values of screen brightness are udpated in faster control loop with ramping to acheive smooth change of light. Ramp value could be parametrized in `sevm::screen_light_control::Parameters::rampTimeMS` and refers to 0-100% change of brightness in given number of miliseconds.

## Hysteresis

Hysteresis value is adjustable via API using `sevm::screen_light_control::Parameters::brightnessHysteresis`.

![](light_control_hysteresis.svg "Hysteresis in ramp")

## Gamma correction
Gamma correction is an algorithm to adjust change of the light intesivity in the way that it is perceived as linear by human eye.

![](gamma_correction.svg "Human eye sensitivity")

Gamma correction is described by formula:

y(x) = scale*(x/scale) <sup>&gamma;</sup>

Where &gamma; is parametrizable correction factor in `sevm::screen_light_control::Parameters::gammaFactor`. Scale is used to normalize x value to 0-1 range.
Default value of &gamma; is 2.5 . For leds this factor could be in range 2.2-2.8.


## Message API

`sevm::ScreenLightControlMessage` is used to control the module. It takes action and data structure as parameters. Set of actions is described in `sevm::screen_light_control::Action`.

![](light_control_message_flow.svg "Message to light control")
\ No newline at end of file

M module-services/service-evtmgr/service-evtmgr/EVMessages.hpp => module-services/service-evtmgr/service-evtmgr/EVMessages.hpp +8 -23
@@ 5,6 5,7 @@

#include "KbdMessage.hpp"
#include "BatteryMessages.hpp"
#include "ScreenLightControl.hpp"

#include <MessageType.hpp>
#include <Service/Message.hpp>


@@ 15,8 16,6 @@
#include <bsp/keyboard/key_codes.hpp>
#include <bsp/torch/torch.hpp>
#include <bsp/keypad_backlight/keypad_backlight.hpp>
#include <bsp/eink_frontlight/eink_frontlight.hpp>
#include <bsp/light_sensor/light_sensor.hpp>

#include <string>



@@ 126,6 125,7 @@ namespace sevm
        {}
        bool success = false;
    };

    class KeypadBacklightMessage : public Message
    {
      public:


@@ 143,31 143,16 @@ namespace sevm
        bool success;
    };

    class EinkFrontlightMessage : public Message
    class ScreenLightControlMessage : public Message
    {
      public:
        explicit EinkFrontlightMessage(bsp::eink_frontlight::Action act = bsp::eink_frontlight::Action::turnOff,
                                       std::uint8_t val                 = 0)
            : Message(MessageType::EVMEinkFrontlightMessage), action(act), value(val)
        ScreenLightControlMessage(screen_light_control::Action act,
                                  screen_light_control::Parameters params = screen_light_control::Parameters())
            : Message(MessageType::EVMScreenLightControlMessage), action(act), parameters(params)
        {}

        const bsp::eink_frontlight::Action action;
        const bsp::eink_frontlight::BrightnessPercentage value;
    };

    class LightSensorMessage : public Message
    {
      public:
        LightSensorMessage() : Message(MessageType::EVMLightSensorMessage)
        {}
    };

    class LightSensorReadoutMessage : public LightSensorMessage
    {
      public:
        explicit LightSensorReadoutMessage(bsp::light_sensor::IlluminanceLux val) : LightSensorMessage(), value(val)
        {}
        const bsp::light_sensor::IlluminanceLux value;
        screen_light_control::Action action;
        screen_light_control::Parameters parameters;
    };

} /* namespace sevm*/

M module-services/service-evtmgr/service-evtmgr/EventManager.hpp => module-services/service-evtmgr/service-evtmgr/EventManager.hpp +1 -5
@@ 11,7 11,6 @@
#include <bsp/common.hpp>
#include <bsp/keyboard/key_codes.hpp>
#include <bsp/keypad_backlight/keypad_backlight.hpp>
#include <bsp/eink_frontlight/eink_frontlight.hpp>

#include <cstdint>
#include <memory>


@@ 25,6 24,7 @@ class EventManager : public sys::Service
  private:
    void HandleAlarmTrigger(sys::DataMessage *msgl);
    void GetNextAlarmTimestamp(time_t timestamp);
    bool processKeypadBacklightRequest(bsp::keypad_backlight::Action act);

  protected:
    std::unique_ptr<WorkerEvent> EventWorker;


@@ 60,8 60,4 @@ class EventManager : public sys::Service
     * with specified name .
     */
    static bool messageSetApplication(sys::Service *sender, const std::string &applicationName);

    bool processKeypadBacklightRequest(bsp::keypad_backlight::Action act);

    void processEinkFrontlightRequest(bsp::eink_frontlight::Action act, bsp::eink_frontlight::BrightnessPercentage val);
};

A module-services/service-evtmgr/service-evtmgr/ScreenLightControl.hpp => module-services/service-evtmgr/service-evtmgr/ScreenLightControl.hpp +54 -0
@@ 0,0 1,54 @@
// 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 <bsp/eink_frontlight/eink_frontlight.hpp>
#include <bsp/light_sensor/light_sensor.hpp>
#include <Service/Timer.hpp>
#include <memory>
#include <vector>

/// Screen light control algorithm. Automatic/Manual mode of operation.
/// Processing of ambient light sensor input to screen brightness output.
namespace sevm::screen_light_control
{
    /// Set of actions to control the screen light
    enum class Action
    {
        turnOff,                    ///< Turn off screen frontlight
        turnOn,                     ///< Turn on screen frontlight
        enableAutomaticMode,        ///< Enable automatic mode of screen frontlight
        disableAutomaticMode,       ///< Disable automatic mode of screen frontlight
        setManualModeBrightness,    ///< Set screen brightness in manual mode control
        setGammaCorrectionFactor,   ///< Set gamma factor for screen frontlight correction
        setAutomaticModeParameters, ///< Set parameters for automatic mode of screen frontlight
    };

    using BrightnessFunction =
        std::vector<std::pair<bsp::light_sensor::IlluminanceLux, bsp::eink_frontlight::BrightnessPercentage>>;

    struct Parameters
    {
        /// Screen brightness 0-100% in manual mode
        bsp::eink_frontlight::BrightnessPercentage manualModeBrightness = 50.0f;
        /// Vector of points for screen brightness [%] in relation to ambient light [Lux] function. Points have to be in
        /// ascending order of ambient light values.
        BrightnessFunction functionPoints = BrightnessFunction({{0.0f, 50.0f}});
        /// Ramp time of screen brightness in miliseconds per 0-100% change
        unsigned int rampTimeMS = 1500;
        /// Hysteresis value of screen brightness control
        float brightnessHysteresis = 10.0f;
        /// Gamma factor for screen brightness correction
        float gammaFactor = 2.5f;
    };

    /// Initialization of screen light control
    /// @param 'parent' - pointer to parent sys::Service class
    void init(sys::Service *parent);

    void deinit();

    void processRequest(Action action, const Parameters &params);

} // namespace sevm::screen_light_control

M source/MessageType.hpp => source/MessageType.hpp +3 -5
@@ 171,12 171,10 @@ enum class MessageType
    EVMTimeUpdated,   ///< This message is send on every time update.
    // Torch messages
    EVMTorchStateMessage,
    // Keypad backlight messages
    // Keypad backlight control messages
    EVMKeypadBacklightMessage,
    // Eink frontlight messages
    EVMEinkFrontlightMessage,
    // Light sensor messages
    EVMLightSensorMessage,
    // Screen frontlight control messages
    EVMScreenLightControlMessage,

    // cellular messages
    EVMGetBoard,