Program Listing for File dptconvert.cpp

Return to documentation for file (src/knx/dptconvert.cpp)

#include "dptconvert.h"
#include "bits.h"
#include <cmath>
#include <cstdlib>
#include <cstring>

#define ASSERT_PAYLOAD(x)      \
    if (payload_length != (x)) \
        return false
#define ENSURE_PAYLOAD(x)


bool KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    if (payload_length > 0)
    {
        // DPT 1.* - Binary
        if (datatype.mainGroup == 1 && datatype.subGroup >= 1 && datatype.subGroup <= 23 && datatype.subGroup != 20 && !datatype.index)
            return busValueToBinary(payload, payload_length, datatype, value);

        // DPT 2.* - Binary Control
        if (datatype.mainGroup == 2 && datatype.subGroup >= 1 && datatype.subGroup <= 12 && datatype.index <= 1)
            return busValueToBinaryControl(payload, payload_length, datatype, value);

        // DPT 3.* - Step Control
        if (datatype.mainGroup == 3 && datatype.subGroup >= 7 && datatype.subGroup <= 8 && datatype.index <= 1)
            return busValueToStepControl(payload, payload_length, datatype, value);

        // DPT 4.* - Character
        if (datatype.mainGroup == 4 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && !datatype.index)
            return busValueToCharacter(payload, payload_length, datatype, value);

        // DPT 5.* - Unsigned 8 Bit Integer
        if (datatype.mainGroup == 5 && ((datatype.subGroup >= 1 && datatype.subGroup <= 6 && datatype.subGroup != 2) || datatype.subGroup == 10) && !datatype.index)
            return busValueToUnsigned8(payload, payload_length, datatype, value);

        // DPT 6.001/6.010 - Signed 8 Bit Integer
        if (datatype.mainGroup == 6 && (datatype.subGroup == 1 || datatype.subGroup == 10) && !datatype.index)
            return busValueToSigned8(payload, payload_length, datatype, value);

        // DPT 6.020 - Status with Mode
        if (datatype.mainGroup == 6 && datatype.subGroup == 20 && datatype.index <= 5)
            return busValueToStatusAndMode(payload, payload_length, datatype, value);

        // DPT 7.001/7.010/7.011/7.012/7.013/7.600 - Unsigned 16 Bit Integer
        if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13) || (datatype.subGroup == 600)) && !datatype.index)
            return busValueToUnsigned16(payload, payload_length, datatype, value);

        // DPT 7.002-DPT 7.007 - Time Period
        if (datatype.mainGroup == 7 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index)
            return busValueToTimePeriod(payload, payload_length, datatype, value);

        // DPT 8.001/8.010/8.011 - Signed 16 Bit Integer
        if (datatype.mainGroup == 8 && (datatype.subGroup == 1 || datatype.subGroup == 10 || datatype.subGroup == 11) && !datatype.index)
            return busValueToSigned16(payload, payload_length, datatype, value);

        // DPT 8.002-DPT 8.007 - Time Delta
        if (datatype.mainGroup == 8 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index)
            return busValueToTimeDelta(payload, payload_length, datatype, value);

        // DPT 9.* - 16 Bit Float
        if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11) || (datatype.subGroup >= 20 && datatype.subGroup <= 29)) && !datatype.index)
            return busValueToFloat16(payload, payload_length, datatype, value);

        // DPT 10.* - Time and Weekday
        if (datatype.mainGroup == 10 && datatype.subGroup == 1 && datatype.index <= 1)
            return busValueToTime(payload, payload_length, datatype, value);

        // DPT 11.* - Date
        if (datatype.mainGroup == 11 && datatype.subGroup == 1 && !datatype.index)
            return busValueToDate(payload, payload_length, datatype, value);

        // DPT 12.* - Unsigned 32 Bit Integer
        if (datatype.mainGroup == 12 && datatype.subGroup == 1 && !datatype.index)
            return busValueToUnsigned32(payload, payload_length, datatype, value);

        // DPT 13.001/13.002/13.010-13.015 - Signed 32 Bit Integer
        if (datatype.mainGroup == 13 && (datatype.subGroup == 1 || datatype.subGroup == 2 || (datatype.subGroup >= 10 && datatype.subGroup <= 15)) && !datatype.index)
            return busValueToSigned32(payload, payload_length, datatype, value);

        // DPT 13.100 - Long Time Period
        if (datatype.mainGroup == 13 && datatype.subGroup == 100 && !datatype.index)
            return busValueToLongTimePeriod(payload, payload_length, datatype, value);

        // DPT 14.* - 32 Bit Float
        if (datatype.mainGroup == 14 && datatype.subGroup <= 79 && !datatype.index)
            return busValueToFloat32(payload, payload_length, datatype, value);

        // DPT 15.* - Access Data
        if (datatype.mainGroup == 15 && !datatype.subGroup && datatype.index <= 5)
            return busValueToAccess(payload, payload_length, datatype, value);

        // DPT 16.* - String
        if (datatype.mainGroup == 16 && datatype.subGroup <= 1 && !datatype.index)
            return busValueToString(payload, payload_length, datatype, value);

        // DPT 17.* - Scene Number
        if (datatype.mainGroup == 17 && datatype.subGroup == 1 && !datatype.index)
            return busValueToScene(payload, payload_length, datatype, value);

        // DPT 18.* - Scene Control
        if (datatype.mainGroup == 18 && datatype.subGroup == 1 && datatype.index <= 1)
            return busValueToSceneControl(payload, payload_length, datatype, value);

        // DPT 19.* - Date and Time
        if (datatype.mainGroup == 19 && datatype.subGroup == 1 && (datatype.index <= 3 || datatype.index == 9 || datatype.index == 10))
            return busValueToDateTime(payload, payload_length, datatype, value);

        // DPT 20.* - HVAC Control mode Unsigned 8 Bit Integer
        if (datatype.mainGroup == 20 && !datatype.index)
            return busValueToUnsigned8(payload, payload_length, datatype, value);

        // DPT 26.* - Scene Info
        if (datatype.mainGroup == 26 && datatype.subGroup == 1 && datatype.index <= 1)
            return busValueToSceneInfo(payload, payload_length, datatype, value);

        // DPT 27.001 - 32 Bit field
        if (datatype.mainGroup == 27 && datatype.subGroup == 1 && !datatype.index)
            return busValueToSigned32(payload, payload_length, datatype, value);

        // DPT 28.* - Unicode String
        if (datatype.mainGroup == 28 && datatype.subGroup == 1 && !datatype.index)
            return busValueToUnicode(payload, payload_length, datatype, value);

        // DPT 29.* - Signed 64 Bit Integer
        if (datatype.mainGroup == 29 && datatype.subGroup >= 10 && datatype.subGroup <= 12 && !datatype.index)
            return busValueToSigned64(payload, payload_length, datatype, value);

        // DPT 219.* - Alarm Info
        if (datatype.mainGroup == 219 && datatype.subGroup == 1 && datatype.index <= 10)
            return busValueToAlarmInfo(payload, payload_length, datatype, value);

        // DPT 221.* - Serial Number
        if (datatype.mainGroup == 221 && datatype.subGroup == 1 && datatype.index <= 1)
            return busValueToSerialNumber(payload, payload_length, datatype, value);

        // DPT 217.* - Version
        if (datatype.mainGroup == 217 && datatype.subGroup == 1 && datatype.index <= 2)
            return busValueToVersion(payload, payload_length, datatype, value);

        // DPT 225.001/225.002 - Scaling Speed and Scaling Step Time
        if (datatype.mainGroup == 225 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && datatype.index <= 1)
            return busValueToScaling(payload, payload_length, datatype, value);

        // DPT 225.003 - Next Tariff
        if (datatype.mainGroup == 225 && datatype.subGroup == 3 && datatype.index <= 1)
            return busValueToTariff(payload, payload_length, datatype, value);

        // DPT 231.* - Locale
        if (datatype.mainGroup == 231 && datatype.subGroup == 1 && datatype.index <= 1)
            return busValueToLocale(payload, payload_length, datatype, value);

        // DPT 232.600 - RGB
        if (datatype.mainGroup == 232 && datatype.subGroup == 600 && !datatype.index)
            return busValueToRGB(payload, payload_length, datatype, value);

        // DPT 234.* - Language and Region
        if (datatype.mainGroup == 234 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && !datatype.index)
            return busValueToLocale(payload, payload_length, datatype, value);

        // DPT 235.* - Active Energy
        if (datatype.mainGroup == 235 && datatype.subGroup == 1 && datatype.index <= 3)
            return busValueToActiveEnergy(payload, payload_length, datatype, value);

        // DPT 238.* - Scene Config
        if (datatype.mainGroup == 238 && datatype.subGroup == 1 && datatype.index <= 2)
            return busValueToSceneConfig(payload, payload_length, datatype, value);

        // DPT 239.* - Flagged Scaling
        if (datatype.mainGroup == 239 && datatype.subGroup == 1 && datatype.index <= 1)
            return busValueToFlaggedScaling(payload, payload_length, datatype, value);

        // DPT 251.600 - RGBW
        if (datatype.mainGroup == 251 && datatype.subGroup == 600 && datatype.index <= 1)
            return busValueToRGBW(payload, payload_length, datatype, value);
    }

    return false;
}

bool KNX_Encode_Value(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if (datatype.mainGroup == 1 && datatype.subGroup >= 1 && datatype.subGroup <= 23 && datatype.subGroup != 20 && !datatype.index)
        return valueToBusValueBinary(value, payload, payload_length, datatype);

    // DPT 2.* - Binary Control
    if (datatype.mainGroup == 2 && datatype.subGroup >= 1 && datatype.subGroup <= 12 && datatype.index <= 1)
        return valueToBusValueBinaryControl(value, payload, payload_length, datatype);

    // DPT 3.* - Step Control
    if (datatype.mainGroup == 3 && datatype.subGroup >= 7 && datatype.subGroup <= 8 && datatype.index <= 1)
        return valueToBusValueStepControl(value, payload, payload_length, datatype);

    // DPT 4.* - Character
    if (datatype.mainGroup == 4 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && !datatype.index)
        return valueToBusValueCharacter(value, payload, payload_length, datatype);

    // DPT 5.* - Unsigned 8 Bit Integer
    if (datatype.mainGroup == 5 && ((datatype.subGroup >= 1 && datatype.subGroup <= 6 && datatype.subGroup != 2) || datatype.subGroup == 10) && !datatype.index)
        return valueToBusValueUnsigned8(value, payload, payload_length, datatype);

    // DPT 6.001/6.010 - Signed 8 Bit Integer
    if (datatype.mainGroup == 6 && (datatype.subGroup == 1 || datatype.subGroup == 10) && !datatype.index)
        return valueToBusValueSigned8(value, payload, payload_length, datatype);

    // DPT 6.020 - Status with Mode
    if (datatype.mainGroup == 6 && datatype.subGroup == 20 && datatype.index <= 5)
        return valueToBusValueStatusAndMode(value, payload, payload_length, datatype);

    // DPT 7.001/7.010/7.011/7.012/7.013/7.600 - Unsigned 16 Bit Integer
    if (datatype.mainGroup == 7 && (datatype.subGroup == 1 || (datatype.subGroup >= 10 && datatype.subGroup <= 13) || datatype.subGroup == 600) && !datatype.index)
        return valueToBusValueUnsigned16(value, payload, payload_length, datatype);

    // DPT 7.002-DPT 7.007 - Time Period
    if (datatype.mainGroup == 7 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index)
        return valueToBusValueTimePeriod(value, payload, payload_length, datatype);

    // DPT 8.001/8.010/8.011 - Signed 16 Bit Integer
    if (datatype.mainGroup == 8 && (datatype.subGroup == 1 || datatype.subGroup == 10 || datatype.subGroup == 11) && !datatype.index)
        return valueToBusValueSigned16(value, payload, payload_length, datatype);

    // DPT 8.002-DPT 8.007 - Time Delta
    if (datatype.mainGroup == 8 && datatype.subGroup >= 2 && datatype.subGroup <= 7 && !datatype.index)
        return valueToBusValueTimeDelta(value, payload, payload_length, datatype);

    // DPT 9.* - 16 Bit Float
    if (datatype.mainGroup == 9 && ((datatype.subGroup >= 1 && datatype.subGroup <= 11 ) || (datatype.subGroup >= 20 && datatype.subGroup <= 29)) && !datatype.index)
        return valueToBusValueFloat16(value, payload, payload_length, datatype);

    // DPT 10.* - Time and Weekday
    if (datatype.mainGroup == 10 && datatype.subGroup == 1 && datatype.index <= 1)
        return valueToBusValueTime(value, payload, payload_length, datatype);

    // DPT 11.* - Date
    if (datatype.mainGroup == 11 && datatype.subGroup == 1 && !datatype.index)
        return valueToBusValueDate(value, payload, payload_length, datatype);

    // DPT 12.* - Unsigned 32 Bit Integer
    if (datatype.mainGroup == 12 && datatype.subGroup == 1 && !datatype.index)
        return valueToBusValueUnsigned32(value, payload, payload_length, datatype);

    // DPT 13.001/13.002/13.010-13.015 - Signed 32 Bit Integer
    if (datatype.mainGroup == 13 && (datatype.subGroup == 1 || datatype.subGroup == 2 || (datatype.subGroup >= 10 && datatype.subGroup <= 15)) && !datatype.index)
        return valueToBusValueSigned32(value, payload, payload_length, datatype);

    // DPT 13.100 - Long Time Period
    if (datatype.mainGroup == 13 && datatype.subGroup == 100 && !datatype.index)
        return valueToBusValueLongTimePeriod(value, payload, payload_length, datatype);

    // DPT 14.* - 32 Bit Float
    if (datatype.mainGroup == 14 && datatype.subGroup <= 79 && !datatype.index)
        return valueToBusValueFloat32(value, payload, payload_length, datatype);

    // DPT 15.* - Access Data
    if (datatype.mainGroup == 15 && !datatype.subGroup && datatype.index <= 5)
        return valueToBusValueAccess(value, payload, payload_length, datatype);

    // DPT 16.* - String
    if (datatype.mainGroup == 16 && datatype.subGroup <= 1 && !datatype.index)
        return valueToBusValueString(value, payload, payload_length, datatype);

    // DPT 17.* - Scene Number
    if (datatype.mainGroup == 17 && datatype.subGroup == 1 && !datatype.index)
        return valueToBusValueScene(value, payload, payload_length, datatype);

    // DPT 18.* - Scene Control
    if (datatype.mainGroup == 18 && datatype.subGroup == 1 && datatype.index <= 1)
        return valueToBusValueSceneControl(value, payload, payload_length, datatype);

    // DPT 19.* - Date and Time
    if (datatype.mainGroup == 19 && datatype.subGroup == 1 && (datatype.index <= 3 || datatype.index == 9 || datatype.index == 10))
        return valueToBusValueDateTime(value, payload, payload_length, datatype);

    // DPT 20.* - HVAC Control mode Unsigned 8 Bit Integer
    if (datatype.mainGroup == 20 && !datatype.index)
        return valueToBusValueUnsigned8(value, payload, payload_length, datatype);

    // DPT 26.* - Scene Info
    if (datatype.mainGroup == 26 && datatype.subGroup == 1 && datatype.index <= 1)
        return valueToBusValueSceneInfo(value, payload, payload_length, datatype);

    // DPT 27.001 - 32 Bit Field
    if (datatype.mainGroup == 27 && datatype.subGroup == 1 && !datatype.index)
        return valueToBusValueUnsigned32(value, payload, payload_length, datatype);

    // DPT 28.* - Unicode String
    if (datatype.mainGroup == 28 && datatype.subGroup == 1 && !datatype.index)
        return valueToBusValueUnicode(value, payload, payload_length, datatype);

    // DPT 29.* - Signed 64 Bit Integer
    if (datatype.mainGroup == 29 && datatype.subGroup >= 10 && datatype.subGroup <= 12 && !datatype.index)
        return valueToBusValueSigned64(value, payload, payload_length, datatype);

    // DPT 219.* - Alarm Info
    if (datatype.mainGroup == 219 && datatype.subGroup == 1 && datatype.index <= 10)
        return valueToBusValueAlarmInfo(value, payload, payload_length, datatype);

    // DPT 221.* - Serial Number
    if (datatype.mainGroup == 221 && datatype.subGroup == 1 && datatype.index <= 1)
        return valueToBusValueSerialNumber(value, payload, payload_length, datatype);

    // DPT 217.* - Version
    if (datatype.mainGroup == 217 && datatype.subGroup == 1 && datatype.index <= 2)
        return valueToBusValueVersion(value, payload, payload_length, datatype);

    // DPT 225.001/225.002 - Scaling Speed and Scaling Step Time
    if (datatype.mainGroup == 225 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && datatype.index <= 1)
        return valueToBusValueScaling(value, payload, payload_length, datatype);

    // DPT 225.003 - Next Tariff
    if (datatype.mainGroup == 225 && datatype.subGroup == 3 && datatype.index <= 1)
        return valueToBusValueTariff(value, payload, payload_length, datatype);

    // DPT 231.* - Locale
    if (datatype.mainGroup == 231 && datatype.subGroup == 1 && datatype.index <= 1)
        return valueToBusValueLocale(value, payload, payload_length, datatype);

    // DPT 232.600 - RGB
    if (datatype.mainGroup == 232 && datatype.subGroup == 600 && !datatype.index)
        return valueToBusValueRGB(value, payload, payload_length, datatype);

    // DPT 234.* - Language and Region
    if (datatype.mainGroup == 234 && datatype.subGroup >= 1 && datatype.subGroup <= 2 && !datatype.index)
        return valueToBusValueLocale(value, payload, payload_length, datatype);

    // DPT 235.* - Active Energy
    if (datatype.mainGroup == 235 && datatype.subGroup == 1 && datatype.index <= 3)
        return valueToBusValueActiveEnergy(value, payload, payload_length, datatype);

    // DPT 238.* - Scene Config
    if (datatype.mainGroup == 238 && datatype.subGroup == 1 && datatype.index <= 2)
        return valueToBusValueSceneConfig(value, payload, payload_length, datatype);

    // DPT 239.* - Flagged Scaling
    if (datatype.mainGroup == 239 && datatype.subGroup == 1 && datatype.index <= 1)
        return valueToBusValueFlaggedScaling(value, payload, payload_length, datatype);

    // DPT 251.600 - RGBW
    if (datatype.mainGroup == 251 && datatype.subGroup == 600 && datatype.index <= 1)
        return valueToBusValueRGBW(value, payload, payload_length, datatype);

    return false;
}

bool busValueToBinary(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(1);
    value = bitFromPayload(payload, 7);
    return true;
}

bool busValueToBinaryControl(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(1);
    value = (uint8_t)(unsigned8FromPayload(payload, 0) & 0x03);
    return true;
}

bool busValueToStepControl(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(1);
    value = (uint8_t)(unsigned8FromPayload(payload, 0) & 0x0F);
    return true;
}
bool busValueToCharacter(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(1);
    int8_t charValue = signed8FromPayload(payload, 0);

    if (datatype.subGroup == 1 && (charValue & 0x80))
        return false;

    if (datatype.subGroup == 2)
    {
        value = (uint8_t)charValue;
        return true;
    }

    value = charValue;
    return true;
}

bool busValueToUnsigned8(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(1);

    switch (datatype.subGroup)
    {
        case 1:
            value = (uint8_t)round(unsigned8FromPayload(payload, 0) * 100.0 / 255.0);
            return true;

        case 3:
            value = (uint8_t)round(unsigned8FromPayload(payload, 0) * 360.0 / 255.0);
            return true;

        case 6:
        {
            uint8_t numValue = unsigned8FromPayload(payload, 0);

            if (numValue == 0xFF)
                return false;

            value = numValue;
            return true;
        }
    }

    value = unsigned8FromPayload(payload, 0);
    return true;
}

bool busValueToSigned8(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(1);
    value = (uint8_t)(unsigned8FromPayload(payload, 0));
    return true;
}

bool busValueToStatusAndMode(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(1);

    if (datatype.index < 5)
    {
        value = bitFromPayload(payload, datatype.index);
        return true;
    }
    else if (datatype.index == 5)
    {
        value = (uint8_t)(unsigned8FromPayload(payload, 0) & 0x07);
        return true;
    }

    return false;
}

bool busValueToUnsigned16(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(2);
    value = unsigned16FromPayload(payload, 0);
    return true;
}

bool busValueToTimePeriod(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(2);

    int64_t duration = unsigned16FromPayload(payload, 0);
    value = duration;
    return true;
}

bool busValueToSigned16(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(2);

    if (datatype.subGroup == 10)
    {
        value = signed16FromPayload(payload, 0) / 100.0;
        return true;
    }

    value = signed16FromPayload(payload, 0);
    return true;
}

bool busValueToTimeDelta(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(2);

    int64_t duration = signed16FromPayload(payload, 0);
    value = duration;
    return true;
}

bool busValueToFloat16(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(2);

    if (unsigned16FromPayload(payload, 0) == 0x7FFF)
        return false;

    value = float16FromPayload(payload, 0);
    return true;
}

bool busValueToTime(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(3);

    switch (datatype.index)
    {
        case 0:
            value = (uint8_t)((unsigned8FromPayload(payload, 0) >> 5) & 0x07);
            return true;

        case 1:
        {
            unsigned char hours = unsigned8FromPayload(payload, 0) & 0x1F;
            unsigned char weekDay = (unsigned8FromPayload(payload, 0) & 0xE0) >> 5;
            unsigned char minutes = unsigned8FromPayload(payload, 1) & 0x3F;
            unsigned char seconds = unsigned8FromPayload(payload, 2) & 0x3F;

            if (hours > 23 || minutes > 59 || seconds > 59)
                return false;

            struct tm tmp;
            memset(&tmp, 0, sizeof(tmp));
            tmp.tm_hour = hours;
            tmp.tm_wday = weekDay;
            tmp.tm_min = minutes;
            tmp.tm_sec = seconds;
            value = tmp;
            return true;
        }
    }

    return false;
}

bool busValueToDate(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(3);
    unsigned short year = unsigned8FromPayload(payload, 2) & 0x7F;
    unsigned char month = unsigned8FromPayload(payload, 1) & 0x0F;
    unsigned char day = unsigned8FromPayload(payload, 0) & 0x1F;

    if (year > 99 || month < 1 || month > 12 || day < 1)
        return false;

    struct tm tmp;
    memset(&tmp, 0, sizeof(tmp));
    year += year >= 90 ? 1900 : 2000;
    tmp.tm_mday = day;
    tmp.tm_year = year;
    tmp.tm_mon = month;
    value = tmp;
    return true;
}

bool busValueToUnsigned32(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(4);
    value = unsigned32FromPayload(payload, 0);
    return true;
}

bool busValueToSigned32(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(4);
    value = signed32FromPayload(payload, 0);
    return true;
}

bool busValueToLongTimePeriod(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(4);
    value = signed32FromPayload(payload, 0);
    return true;
}

bool busValueToFloat32(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(4);
    value = float32FromPayload(payload, 0);
    return true;
}

bool busValueToAccess(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(4);

    switch (datatype.index)
    {
        case 0:
        {
            int32_t digits = 0;

            for (int n = 0, factor = 100000; n < 6; ++n, factor /= 10)
            {
                unsigned char digit = bcdFromPayload(payload, n);

                if (digit > 9)
                    return false;

                digits += digit * factor;
            }

            value = digits;
            return true;
        }

        case 1:
        case 2:
        case 3:
        case 4:
            value = bitFromPayload(payload, 23 + datatype.index);
            return true;

        case 5:
            value = bcdFromPayload(payload, 7);
            return true;
    }

    return false;
}

bool busValueToString(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(14);

    for (int n = 0; n < 14; ++n)
    {
        auto value = signed8FromPayload(payload, n);

        if (!datatype.subGroup && (value & 0x80))
            return false;
    }

    value = (const char*) payload;
    return true;
}

bool busValueToScene(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(1);
    value = (uint8_t)(unsigned8FromPayload(payload, 0) & 0x3F);
    return true;
}

bool busValueToSceneControl(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(1);

    switch (datatype.index)
    {
        case 0:
        {
            value = bitFromPayload(payload, 0);
            return true;
        }

        case 1:
        {
            value = (uint8_t)(unsigned8FromPayload(payload, 0) & 0x3F);
            return true;
        }
    }

    return false;
}

bool busValueToSceneInfo(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(1);

    switch (datatype.index)
    {
        case 0:
        {
            value = bitFromPayload(payload, 1);
            return true;
        }

        case 1:
        {
            value = (uint8_t)(unsigned8FromPayload(payload, 0) & 0x3F);
            return true;
        }
    }

    return false;
}

bool busValueToSceneConfig(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(1);

    switch (datatype.index)
    {
        case 0:
        {
            value = (uint8_t)(unsigned8FromPayload(payload, 0) & 0x3F);
            return true;
        }

        case 1:
        case 2:
        {
            value = bitFromPayload(payload, 2 - datatype.index);
            return true;
        }
    }

    return false;
}

bool busValueToDateTime(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(8);

    if (datatype.index == 3)
    {
        value = bitFromPayload(payload, 48);
        return true;
    }

    if (!bitFromPayload(payload, 48))
    {
        switch (datatype.index)
        {
            case 0:
            {
                if (bitFromPayload(payload, 51) || bitFromPayload(payload, 52))
                    return false;

                unsigned short year = unsigned8FromPayload(payload, 0) + 1900;
                unsigned short month = unsigned8FromPayload(payload, 1) & 0x0F;
                unsigned short day = unsigned8FromPayload(payload, 2) & 0x1F;
                unsigned short hours = unsigned8FromPayload(payload, 3) & 0x1F;
                unsigned short minutes = unsigned8FromPayload(payload, 4) & 0x3F;
                unsigned short seconds = unsigned8FromPayload(payload, 5) & 0x3F;

                if ((month < 1 || month > 12 || day < 1))
                    return false;

                if ((hours > 24 || minutes > 59 || seconds > 59))
                    return false;

                struct tm tmp;
                memset(&tmp, 0, sizeof(tmp));
                tmp.tm_sec = seconds;
                tmp.tm_min = minutes;
                tmp.tm_hour = hours;
                tmp.tm_mday = day;
                tmp.tm_mon = month;
                tmp.tm_year = year;
                value = tmp;
                return true;
            }

            case 1:
            {
                if (bitFromPayload(payload, 53))
                    return false;

                value = (uint8_t)((unsigned8FromPayload(payload, 3) >> 5) & 0x07);
                return true;
            }

            case 2:
            {
                if (bitFromPayload(payload, 50))
                    return false;

                value = bitFromPayload(payload, 49);
                return true;
            }

            case 9:
            {
                value = bitFromPayload(payload, 55);
                return true;
            }

            case 10:
            {
                value = bitFromPayload(payload, 56);
                return true;
            }
        }
    }

    return false;
}

bool busValueToUnicode(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    //TODO
    return false;
}

bool busValueToSigned64(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(8);
    value = signed64FromPayload(payload, 0);
    return true;
}

bool busValueToAlarmInfo(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(6);

    switch (datatype.index)
    {
        case 1:
        {
            unsigned char prio = unsigned8FromPayload(payload, 1);

            if (prio > 3)
                return false;

            value = prio;
            return true;
        }

        case 0:
        case 2:
        case 3:
            value = unsigned8FromPayload(payload, datatype.index);
            return true;

        case 4:
        case 5:
        case 6:
        case 7:
            value = bitFromPayload(payload, 43 - datatype.index);
            return true;

        case 8:
        case 9:
        case 10:
            value = bitFromPayload(payload, 55 - datatype.index);
            return true;
    }

    return false;
}

bool busValueToSerialNumber(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(6);

    switch (datatype.index)
    {
        case 0:
            value = unsigned16FromPayload(payload, 0);
            return true;

        case 1:
            value = unsigned32FromPayload(payload, 2);
            return true;
    }

    return false;
}

bool busValueToVersion(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(2);

    switch (datatype.index)
    {
        case 0:
            value = (uint8_t)((unsigned8FromPayload(payload, 0) >> 3) & 0x1F);
            return true;

        case 1:
            value = (uint16_t)((unsigned16FromPayload(payload, 0) >> 6) & 0x1F);
            return true;

        case 2:
            value = (uint8_t)(unsigned8FromPayload(payload, 1) & 0x3F);
            return true;
    }

    return false;
}

bool busValueToScaling(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(3);

    switch (datatype.index)
    {
        case 0:
            value = unsigned16FromPayload(payload, 0);
            return true;

        case 1:
            value = (uint8_t)(unsigned8FromPayload(payload, 2) * 100.0 / 255.0);
            return true;
    }

    return false;
}

bool busValueToTariff(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(3);

    switch (datatype.index)
    {
        case 0:
            value = unsigned16FromPayload(payload, 0);
            return true;

        case 1:
        {
            uint8_t tariff = unsigned8FromPayload(payload, 2);

            if (tariff > 254)
                return false;

            value = tariff;
            return true;
        }
    }

    return false;
}

bool busValueToLocale(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(datatype.mainGroup == 231 ? 4 : 2);

    if (!datatype.index || (datatype.mainGroup == 231 && datatype.index == 1))
    {
        char code[2];
        code[0] = unsigned8FromPayload(payload, datatype.index * 2);
        code[1] = unsigned8FromPayload(payload, datatype.index * 2 + 1);
        value = code;
        return true;
    }

    return false;
}

bool busValueToRGB(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(3);
    uint32_t rgb = (unsigned16FromPayload(payload, 0) << 8) | unsigned8FromPayload(payload, 2);
    value = rgb;
    return true;
}

bool busValueToRGBW(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(6);

    switch (datatype.index)
    {
        case 0: // The RGBW value
        {
            uint32_t rgbw = unsigned32FromPayload(payload, 0);
            value = rgbw;
            return true;
        }
        case 1: // The mask bits only
        {
            value = unsigned8FromPayload(payload, 5);
            return true;
        }
    }

    return false;
}

bool busValueToFlaggedScaling(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(2);

    switch (datatype.index)
    {
        case 0:
            value = (uint8_t)(unsigned8FromPayload(payload, 0) * 100.0 / 255.0);
            return true;

        case 1:
            value = bitFromPayload(payload, 15);
            return true;
    }

    return false;
}

bool busValueToActiveEnergy(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
    ASSERT_PAYLOAD(6);

    switch (datatype.index)
    {
        case 0:
            value = signed32FromPayload(payload, 0);
            return true;

        case 1:
            value = unsigned8FromPayload(payload, 4);
            return true;

        case 2:
        case 3:
            value = bitFromPayload(payload, datatype.index + 44);
            return true;
    }

    return false;
}

//-------------------------------------------------------------------------------------------------------------------------------------

bool valueToBusValueBinary(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    bitToPayload(payload, payload_length, 7, value);
    return true;
}

bool valueToBusValueBinaryControl(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    unsigned8ToPayload(payload, payload_length, 0, (uint64_t)value, 0x03);
    return true;
}

bool valueToBusValueStepControl(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    unsigned8ToPayload(payload, payload_length, 0, (uint64_t)value, 0x0F);
    return true;
}

bool valueToBusValueCharacter(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if ((uint64_t)value > INT64_C(255) || (datatype.subGroup == 1 && (uint64_t)value > INT64_C(127)))
        return false;

    unsigned8ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFF);
    return true;
}

bool valueToBusValueUnsigned8(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if ((int64_t)value < INT64_C(0))
        return false;

    switch (datatype.subGroup)
    {
        case 1:
        {
            if ((double)value > 100.0)
                return false;

            unsigned8ToPayload(payload, payload_length, 0, round((double)value * 255.0 / 100.0), 0xFF);
            break;
        }

        case 3:
        {
            if ((double)value > 360.0)
                return false;

            unsigned8ToPayload(payload, payload_length, 0, round((double)value * 255.0 / 360.0), 0xFF);
            break;
        }

        case 6:
        {
            if ((int64_t)value > INT64_C(254))
                return false;

            unsigned8ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFF);
            break;
        }

        default:
        {
            if ((int64_t)value > INT64_C(255))
                return false;

            unsigned8ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFF);
        }
    }

    return true;
}

bool valueToBusValueSigned8(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if ((int64_t)value < INT64_C(-128) || (int64_t)value > INT64_C(127))
        return false;

    signed8ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFF);
    return true;
}

bool valueToBusValueStatusAndMode(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if (datatype.index < 5)
        bitToPayload(payload, payload_length, datatype.index, value);
    else if (datatype.index == 5)
    {
        if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(7))
            return false;

        unsigned8ToPayload(payload, payload_length, 0, (uint64_t)value, 0x07);
    }
    else
        return false;

    return true;
}

bool valueToBusValueUnsigned16(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(65535))
        return false;

    unsigned16ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFFFF);
    return true;
}

bool valueToBusValueTimePeriod(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    struct tm tmp = value;
    time_t timeSinceEpoch = mktime(&tmp);

    if (timeSinceEpoch < INT64_C(0) || timeSinceEpoch > INT64_C(65535))
        return false;

    unsigned16ToPayload(payload, payload_length, 0, timeSinceEpoch, 0xFFFF);
    return true;
}

bool valueToBusValueSigned16(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if ((int64_t)value < INT64_C(-32768) || (int64_t)value > INT64_C(32767))
        return false;

    if (datatype.subGroup == 10)
    {
        if ((double)value < -327.68 || (double)value > 327.67)
            return false;

        signed16ToPayload(payload, payload_length, 0, (int16_t)((double)value * 100.0), 0xFFFF);
    }
    else
        signed16ToPayload(payload, payload_length, 0, (uint64_t)value, 0xffff);

    return true;
}

bool valueToBusValueTimeDelta(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    struct tm tmp = value;
    time_t timeSinceEpoch = mktime(&tmp);

    if (timeSinceEpoch < INT64_C(-32768) || timeSinceEpoch > INT64_C(32767))
        return false;

    signed16ToPayload(payload, payload_length, 0, timeSinceEpoch, 0xFFFF);
    return true;
}

bool valueToBusValueFloat16(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    double numValue = value;

    // bigger values like 670760.0 result in 0x7FFF which denotes invalid data.
    // I'm not sure if the GO shouldn't be updated to this value instead
    if (numValue > 670433.28)
        return false;

    if (numValue < -671088.64)
        return false;

    switch (datatype.subGroup)
    {
        case 1:
            if (numValue < -273.0)
                return false;

            break;

        case 2:
        case 3:
        case 10:
        case 11:
        case 20:
        case 21:
        case 22:
        case 23:
        case 24:
        case 25:
            if (numValue < -670760.0)
                return false;

            break;

        case 4:
        case 5:
        case 6:
        case 7:
        case 8:
        case 28:
            if (numValue < 0.0)
                return false;

            break;

        case 27:
            if (numValue < -459.6)
                return false;

            break;
    }

    float16ToPayload(payload, payload_length, 0, numValue, 0xFFFF);
    return true;
}

bool valueToBusValueTime(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(7))
                return false;

            ENSURE_PAYLOAD(3);
            unsigned8ToPayload(payload, payload_length, 0, (uint64_t)value << 5, 0xE0);
            break;
        }

        case 1:
        {
            struct tm tmp = value;
            unsigned8ToPayload(payload, payload_length, 0, tmp.tm_hour, 0x1F);
            unsigned8ToPayload(payload, payload_length, 1, tmp.tm_min, 0x3F);
            unsigned8ToPayload(payload, payload_length, 2, tmp.tm_sec, 0x3F);
            break;
        }

        default:
            return false;
    }

    return true;
}

bool valueToBusValueDate(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    struct tm tmp = value;

    if (tmp.tm_year < 1990 || tmp.tm_year > 2089)
        return false;

    unsigned8ToPayload(payload, payload_length, 0, tmp.tm_mday, 0x1F);
    unsigned8ToPayload(payload, payload_length, 1, tmp.tm_mon, 0x0F);
    unsigned8ToPayload(payload, payload_length, 2, tmp.tm_year % 100, 0x7F);
    return true;
}

bool valueToBusValueUnsigned32(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(4294967295))
        return false;

    unsigned32ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFFFFFFFF);
    return true;
}

bool valueToBusValueSigned32(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if ((int64_t)value < INT64_C(-2147483648) || (int64_t)value > INT64_C(2147483647))
        return false;

    signed32ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFFFFFFFF);
    return true;
}

bool valueToBusValueLongTimePeriod(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if ((int64_t)value < INT64_C(-2147483648) || (int64_t)value > INT64_C(2147483647))
        return false;

    signed32ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFFFFFFFF);
    return true;
}

bool valueToBusValueFloat32(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    double numValue = value;

    if (numValue < (-8388608.0 * pow(2, 255)) || numValue > (8388607.0 * pow(2, 255)))
        return false;

    float32ToPayload(payload, payload_length, 0, numValue, 0xFFFFFFFF);
    return true;
}

bool valueToBusValueAccess(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(999999))
                return false;

            ENSURE_PAYLOAD(4);

            for (int n = 0, factor = 100000; n < 6; ++n, factor /= 10)
                bcdToPayload(payload, payload_length, n, ((uint64_t)value / factor) % 10);

            break;
        }

        case 1:
        case 2:
        case 3:
        case 4:
            bitToPayload(payload, payload_length, 23 + datatype.index, value);
            break;

        case 5:
        {
            if ((uint64_t)value > INT64_C(15))
                return false;

            bcdToPayload(payload, payload_length, 7, (uint64_t)value);
            break;
        }

        default:
            return false;
    }

    return true;
}

bool valueToBusValueString(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    const char* strValue = value;
    uint8_t val = strValue[0];

    for (int n = 0; n < 14; n++)
    {
        if (val)
            val = strValue[n]; //string terminator 0x00 will stop further assignments and init the remainig payload with zero

        unsigned8ToPayload(payload, payload_length, n, val, 0xff);
    }

    return true;
}

bool valueToBusValueScene(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(63))
        return false;

    unsigned8ToPayload(payload, payload_length, 0, (uint64_t)value, 0xFF);
    return true;
}

bool valueToBusValueSceneControl(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
            bitToPayload(payload, payload_length, 0, value);
            break;

        case 1:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(63))
                return false;

            unsigned8ToPayload(payload, payload_length, 0, (int64_t)value, 0x3F);
            break;
        }

        default:
            return false;
    }

    return true;
}

bool valueToBusValueSceneInfo(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
            bitToPayload(payload, payload_length, 1, value);
            break;

        case 1:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(63))
                return false;

            unsigned8ToPayload(payload, payload_length, 0, (int64_t)value, 0x3F);
            break;
        }

        default:
            return false;
    }

    return true;
}

bool valueToBusValueSceneConfig(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(63))
                return false;

            unsigned8ToPayload(payload, payload_length, 0, (int64_t)value, 0x3F);
            break;
        }

        case 1:
        case 2:
            bitToPayload(payload, payload_length, 2 - datatype.index, value);
            break;

        default:
            return false;
    }

    return true;
}

bool valueToBusValueDateTime(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
        {
            struct tm local = value;
            time_t time = mktime(&local);

            if (!time) //TODO add check if date or time is invalid
                return false;

            ENSURE_PAYLOAD(8);
            struct tm tmp = value;
            bitToPayload(payload, payload_length, 51, false);
            bitToPayload(payload, payload_length, 52, false);
            unsigned8ToPayload(payload, payload_length, 0, tmp.tm_year - 1900, 0xFF);
            unsigned8ToPayload(payload, payload_length, 1, tmp.tm_mon, 0x0F);
            unsigned8ToPayload(payload, payload_length, 2, tmp.tm_mday, 0x1F);

            bitToPayload(payload, payload_length, 54, false);
            unsigned8ToPayload(payload, payload_length, 3, tmp.tm_hour, 0x1F);
            unsigned8ToPayload(payload, payload_length, 4, tmp.tm_min, 0x3F);
            unsigned8ToPayload(payload, payload_length, 5, tmp.tm_sec, 0x3F);
            break;
        }

        case 1:
        {
            ENSURE_PAYLOAD(8);

            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(7))
                bitToPayload(payload, payload_length, 53, true);
            else
            {
                bitToPayload(payload, payload_length, 53, false);
                unsigned8ToPayload(payload, payload_length, 3, (int64_t)value << 5, 0xE0);
            }

            break;
        }

        case 2:
        {
            ENSURE_PAYLOAD(8);
            bitToPayload(payload, payload_length, 49, value);
            bitToPayload(payload, payload_length, 50, false);
            break;
        }

        case 3:
        {
            ENSURE_PAYLOAD(8);
            bitToPayload(payload, payload_length, 48, value);
            break;
        }

        case 9:
        {
            ENSURE_PAYLOAD(8);
            bitToPayload(payload, payload_length, 55, value);
            break;
        }

        case 10:
        {
            bitToPayload(payload, payload_length, 56, value);
            break;
        }

        default:
            return false;
    }

    return true;
}

bool valueToBusValueUnicode(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    //TODO
    return false;
}

bool valueToBusValueSigned64(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    signed64ToPayload(payload, payload_length, 0, (int64_t)value, UINT64_C(0xFFFFFFFFFFFFFFFF));
    return true;
}

bool valueToBusValueAlarmInfo(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 1:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(3))
                return false;

            ENSURE_PAYLOAD(6);
            unsigned8ToPayload(payload, payload_length, 1, (int64_t)value, 0xFF);
            break;
        }

        case 0:
        case 2:
        case 3:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(255))
                return false;

            ENSURE_PAYLOAD(6);
            unsigned8ToPayload(payload, payload_length, datatype.index, (int64_t)value, 0xFF);
            break;
        }

        case 4:
        case 5:
        case 6:
        case 7:
        {
            ENSURE_PAYLOAD(6);
            bitToPayload(payload, payload_length, 43 - datatype.index, value);
            break;
        }

        case 8:
        case 9:
        case 10:
        {
            ENSURE_PAYLOAD(6);
            bitToPayload(payload, payload_length, 55 - datatype.index, value);
            break;
        }

        default:
            return false;
    }

    return true;
}

bool valueToBusValueSerialNumber(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(65535))
                return false;

            ENSURE_PAYLOAD(6);
            unsigned16ToPayload(payload, payload_length, 0, (int64_t)value, 0xFFFF);
            break;
        }

        case 1:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(4294967295))
                return false;

            ENSURE_PAYLOAD(6);
            unsigned32ToPayload(payload, payload_length, 2, (int64_t)value, 0xFFFF);
            break;
        }

        default:
            return false;
    }

    return true;
}

bool valueToBusValueVersion(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(31))
                return false;

            ENSURE_PAYLOAD(2);
            unsigned8ToPayload(payload, payload_length, 0, (int64_t)value << 3, 0xF8);
            break;
        }

        case 1:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(31))
                return false;

            unsigned16ToPayload(payload, payload_length, 0, (int64_t)value << 6, 0x07C0);
            break;
        }

        case 2:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(63))
                return false;

            unsigned8ToPayload(payload, payload_length, 1, (int64_t)value, 0x3F);
            break;
        }

        default:
            return false;
    }

    return true;
}

bool valueToBusValueScaling(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
        {
            uint32_t duration = value;

            if (duration > INT64_C(65535))
                return false;

            ENSURE_PAYLOAD(3);
            unsigned16ToPayload(payload, payload_length, 0, duration, 0xFFFF);
            return true;
        }

        case 1:
        {
            if ((double)value < 0.0 || (double)value > 100.0)
                return false;

            unsigned8ToPayload(payload, payload_length, 2, round((double)value * 255.0 / 100.0), 0xff);
            break;
        }
    }

    return true;
}

bool valueToBusValueTariff(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
        {
            uint32_t duration = value;

            if (duration > INT64_C(65535))
                return false;

            ENSURE_PAYLOAD(3);
            unsigned16ToPayload(payload, payload_length, 0, duration, 0xFFFF);
            return true;
        }

        case 1:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(254))
                return false;

            unsigned8ToPayload(payload, payload_length, 2, (int64_t)value, 0xff);
            break;
        }
    }

    return true;
}

bool valueToBusValueLocale(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    int strl = strlen(value);

    if (strl != 2)
        return false;

    if (!datatype.index || (datatype.mainGroup == 231 && datatype.index == 1))
    {
        ENSURE_PAYLOAD(datatype.mainGroup == 231 ? 4 : 2);
        unsigned8ToPayload(payload, payload_length, datatype.index * 2, ((const char*)value)[0], 0xff);
        unsigned8ToPayload(payload, payload_length, datatype.index * 2 + 1, ((const char*)value)[1], 0xff);
        return true;
    }

    return false;
}

bool valueToBusValueRGB(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(0xffffff))
        return false;

    unsigned int rgb = (int64_t)value;

    unsigned16ToPayload(payload, payload_length, 0, rgb >> 8, 0xffff);
    unsigned8ToPayload(payload, payload_length, 2, rgb & 0xff, 0xff);
    return true;
}

bool valueToBusValueRGBW(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0: // RGBW
        {
            uint32_t rgbw = (uint32_t)value;
            unsigned32ToPayload(payload, payload_length, 0, rgbw, 0xffffffff); // RGBW
            break;
        }
        case 1: // Mask bits
        {
            unsigned8ToPayload(payload, payload_length, 5, (uint8_t)value, 0x0f);
            break;
        }
    }

    return true;
}

bool valueToBusValueFlaggedScaling(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
        {
            if ((double)value < 0.0 || (double)value > 100.0)
                return false;

            ENSURE_PAYLOAD(2);
            unsigned8ToPayload(payload, payload_length, 0, round((double)value * 255.0 / 100.0), 0xff);
            break;
        }

        case 1:
            bitToPayload(payload, payload_length, 15, value);
            break;

        default:
            return false;
    }

    return true;
}

bool valueToBusValueActiveEnergy(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype)
{
    switch (datatype.index)
    {
        case 0:
        {
            if ((int64_t)value < INT64_C(-2147483648) || (int64_t)value > INT64_C(2147483647))
                return false;

            ENSURE_PAYLOAD(6);
            signed32ToPayload(payload, payload_length, 0, (int64_t)value, 0xffffffff);
            break;
        }

        case 1:
        {
            if ((int64_t)value < INT64_C(0) || (int64_t)value > INT64_C(254))
                return false;

            ENSURE_PAYLOAD(6);
            unsigned8ToPayload(payload, payload_length, 4, (int64_t)value, 0xff);
            break;
        }

        case 2:
        case 3:
            bitToPayload(payload, payload_length, datatype.index + 44, value);
            break;

        default:
            return false;
    }

    return true;
}

// Helper functions
bool bitFromPayload(const uint8_t* payload, int index)
{
    int bit = payload[index / 8] & (1 << (7 - (index % 8)));
    return bit ? true : false;
}
uint8_t unsigned8FromPayload(const uint8_t* payload, int index)
{
    return (uint8_t)payload[index];
}
int8_t signed8FromPayload(const uint8_t* payload, int index)
{
    return (int8_t)payload[index];
}
uint16_t unsigned16FromPayload(const uint8_t* payload, int index)
{
    return ((((uint16_t)payload[index]) << 8) & 0xFF00) | (((uint16_t)payload[index + 1]) & 0x00FF);
}
int16_t signed16FromPayload(const uint8_t* payload, int index)
{
    return ((((uint16_t)payload[index]) << 8) & 0xFF00) | (((uint16_t)payload[index + 1]) & 0x00FF);
}
uint32_t unsigned32FromPayload(const uint8_t* payload, int index)
{
    return ((((uint32_t)payload[index]) << 24) & 0xFF000000) |
           ((((uint32_t)payload[index + 1]) << 16) & 0x00FF0000) |
           ((((uint32_t)payload[index + 2]) << 8) & 0x0000FF00) |
           (((uint32_t)payload[index + 3]) & 0x000000FF);
}
int32_t signed32FromPayload(const uint8_t* payload, int index)
{
    return (int32_t)unsigned32FromPayload(payload, index);
}
uint64_t unsigned64FromPayload(const uint8_t* payload, int index)
{
    return ((((uint64_t)payload[index]) << 56) & 0xFF00000000000000) |
           ((((uint64_t)payload[index + 1]) << 48) & 0x00FF000000000000) |
           ((((uint64_t)payload[index + 2]) << 40) & 0x0000FF0000000000) |
           ((((uint64_t)payload[index + 3]) << 32) & 0x000000FF00000000) |
           ((((uint64_t)payload[index + 4]) << 24) & 0x00000000FF000000) |
           ((((uint64_t)payload[index + 5]) << 16) & 0x0000000000FF0000) |
           ((((uint64_t)payload[index + 6]) << 8) & 0x000000000000FF00) |
           (((uint64_t)payload[index + 7]) & 0x00000000000000FF);
}
double float16FromPayload(const uint8_t* payload, int index)
{
    uint16_t mantissa = unsigned16FromPayload(payload, index) & 0x87FF;

    if (mantissa & 0x8000)
        return ((~mantissa & 0x07FF) + 1.0) * -0.01 * (1 << ((payload[index] >> 3) & 0x0F));

    return mantissa * 0.01 * (1 << ((payload[index] >> 3) & 0x0F));
}
float float32FromPayload(const uint8_t* payload, int index)
{
    union
    {
        float f;
        uint32_t i;
    } area;
    area.i = unsigned32FromPayload(payload, index);
    return area.f;
}
double float64FromPayload(const uint8_t* payload, int index)
{
    union
    {
        double f;
        uint64_t i;
    } area;
    area.i = unsigned64FromPayload(payload, index);
    return area.f;
}
int64_t signed64FromPayload(const uint8_t* payload, int index)
{
    return ((((uint64_t)payload[index]) << 56) & UINT64_C(0xFF00000000000000)) |
           ((((uint64_t)payload[index + 1]) << 48) & UINT64_C(0x00FF000000000000)) |
           ((((uint64_t)payload[index + 2]) << 40) & UINT64_C(0x0000FF0000000000)) |
           ((((uint64_t)payload[index + 3]) << 32) & UINT64_C(0x000000FF00000000)) |
           ((((uint64_t)payload[index + 4]) << 24) & UINT64_C(0x00000000FF000000)) |
           ((((uint64_t)payload[index + 5]) << 16) & UINT64_C(0x0000000000FF0000)) |
           ((((uint64_t)payload[index + 6]) << 8) & UINT64_C(0x000000000000FF00)) |
           (((uint64_t)payload[index + 7]) & UINT64_C(0x00000000000000FF));
}
uint8_t bcdFromPayload(const uint8_t* payload, int index)
{
    if (index % 2)
        return (uint8_t)(payload[index / 2] & 0x0F);

    return (uint8_t)((payload[index / 2] >> 4) & 0x0F);
}

void bitToPayload(uint8_t* payload, size_t payload_length, int index, bool value)
{
    ENSURE_PAYLOAD(index / 8 + 1);
    payload[index / 8] = (payload[index / 8] & ~(1 << (7 - (index % 8)))) | (value ? (1 << (7 - (index % 8))) : 0);
}
void unsigned8ToPayload(uint8_t* payload, size_t payload_length, int index, uint8_t value, uint8_t mask)
{
    ENSURE_PAYLOAD(index + 1);
    payload[index] = (payload[index] & ~mask) | (value & mask);
}
void signed8ToPayload(uint8_t* payload, size_t payload_length, int index, int8_t value, uint8_t mask)
{
    ENSURE_PAYLOAD(index + 1);
    payload[index] = (payload[index] & ~mask) | (value & mask);
}
void unsigned16ToPayload(uint8_t* payload, size_t payload_length, int index, uint16_t value, uint16_t mask)
{
    ENSURE_PAYLOAD(index + 2);
    payload[index] = (payload[index] & (~mask >> 8)) | ((value >> 8) & (mask >> 8));
    payload[index + 1] = (payload[index + 1] & ~mask) | (value & mask);
}
void signed16ToPayload(uint8_t* payload, size_t payload_length, int index, int16_t value, uint16_t mask)
{
    ENSURE_PAYLOAD(index + 2);
    payload[index] = (payload[index] & (~mask >> 8)) | ((value >> 8) & (mask >> 8));
    payload[index + 1] = (payload[index + 1] & ~mask) | (value & mask);
}
void unsigned32ToPayload(uint8_t* payload, size_t payload_length, int index, uint32_t value, uint32_t mask)
{
    ENSURE_PAYLOAD(index + 4);
    payload[index] = (payload[index] & (~mask >> 24)) | ((value >> 24) & (mask >> 24));
    payload[index + 1] = (payload[index + 1] & (~mask >> 16)) | ((value >> 16) & (mask >> 16));
    payload[index + 2] = (payload[index + 2] & (~mask >> 8)) | ((value >> 8) & (mask >> 8));
    payload[index + 3] = (payload[index + 3] & ~mask) | (value & mask);
}
void signed32ToPayload(uint8_t* payload, size_t payload_length, int index, int32_t value, uint32_t mask)
{
    ENSURE_PAYLOAD(index + 4);
    payload[index] = (payload[index] & (~mask >> 24)) | ((value >> 24) & (mask >> 24));
    payload[index + 1] = (payload[index + 1] & (~mask >> 16)) | ((value >> 16) & (mask >> 16));
    payload[index + 2] = (payload[index + 2] & (~mask >> 8)) | ((value >> 8) & (mask >> 8));
    payload[index + 3] = (payload[index + 3] & ~mask) | (value & mask);
}

void float16ToPayload(uint8_t* payload, size_t payload_length, int index, double value, uint16_t mask)
{
    bool wasNegative = false;

    if (value < 0)
    {
        wasNegative = true;
        value *= -1;
    }

    value *= 100.0;
    unsigned short exponent = 0;

    if (value > 2048)
        exponent = ceil(log2(value) - 11.0);

    short mantissa = roundf(value / (1 << exponent));

    // above calculation causes mantissa overflow for values of the form 2^n, where n>11
    if (mantissa >= 0x800)
    {
        exponent++;
        mantissa = roundf(value / (1 << exponent));
    }

    if (wasNegative)
        mantissa *= -1;

    // println(mantissa);

    signed16ToPayload(payload, payload_length, index, mantissa, mask);
    unsigned8ToPayload(payload, payload_length, index, exponent << 3, 0x78 & (mask >> 8));
}
void float32ToPayload(uint8_t* payload, size_t payload_length, int index, double value, uint32_t mask)
{
    union
    {
        float f;
        uint32_t i;
    } num;
    num.f = value;
    unsigned32ToPayload(payload, payload_length, index, num.i, mask);
}
void signed64ToPayload(uint8_t* payload, size_t payload_length, int index, int64_t value, uint64_t mask)
{
    ENSURE_PAYLOAD(index + 8);
    payload[index] = (payload[index] & (~mask >> 56)) | ((value >> 56) & (mask >> 56));
    payload[index + 1] = (payload[index + 1] & (~mask >> 48)) | ((value >> 48) & (mask >> 48));
    payload[index + 2] = (payload[index + 2] & (~mask >> 40)) | ((value >> 40) & (mask >> 40));
    payload[index + 3] = (payload[index + 3] & (~mask >> 32)) | ((value >> 32) & (mask >> 32));
    payload[index + 4] = (payload[index + 4] & (~mask >> 24)) | ((value >> 24) & (mask >> 24));
    payload[index + 5] = (payload[index + 5] & (~mask >> 16)) | ((value >> 16) & (mask >> 16));
    payload[index + 6] = (payload[index + 6] & (~mask >> 8)) | ((value >> 8) & (mask >> 8));
    payload[index + 7] = (payload[index + 7] & ~mask) | (value & mask);
}
void bcdToPayload(uint8_t* payload, size_t payload_length, int index, uint8_t value)
{
    ENSURE_PAYLOAD(index / 2 + 1);

    if (index % 2)
        payload[index / 2] = (payload[index / 2] & 0xF0) | (value & 0x0F);
    else
        payload[index / 2] = (payload[index / 2] & 0x0F) | ((value << 4) & 0xF0);
}