Feature/color objects color command (#302)

* added BACnetColorCommand and BACnetxyColor encoding and unit testing

* Added Color object and unit testing.

* Added Color Temperature object and Unit test

* Fix BVLC unit test warning.

* add port Makefile for extra types

* added RGB to and from CIE xy utility in sys folder, and add unit tests.

* added cmake-win32 target

* Change RP and RPM to use known property decoder.

Add color object RP and RPM decoding and printing
Fix RPM print for new reserved range above 4194303
Change default protocol-revision to 24 for Color object

* Integrate Color and Color Temperature objects into demo apps

Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Steve Karg
2022-07-13 09:54:36 -05:00
committed by GitHub
parent 085de3c385
commit 38d213b47c
80 changed files with 5369 additions and 347 deletions
+917
View File
@@ -0,0 +1,917 @@
/**
* @file
* @author Steve Karg
* @date June 2022
* @brief Color objects, customize for your use
*
* @section DESCRIPTION
*
* The Color object is an object with a present-value that
* uses an x,y color single precision floating point data type.
*
* @section LICENSE
*
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/config.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacerror.h"
#include "bacnet/bacapp.h"
#include "bacnet/bactext.h"
#include "bacnet/cov.h"
#include "bacnet/apdu.h"
#include "bacnet/npdu.h"
#include "bacnet/abort.h"
#include "bacnet/lighting.h"
#include "bacnet/reject.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/sys/keylist.h"
/* me! */
#include "color_object.h"
struct object_data {
bool Changed : 1;
bool Write_Enabled : 1;
BACNET_XY_COLOR Present_Value;
BACNET_XY_COLOR Tracking_Value;
BACNET_COLOR_COMMAND Color_Command;
BACNET_COLOR_OPERATION_IN_PROGRESS In_Progress;
BACNET_XY_COLOR Default_Color;
uint32_t Default_Fade_Time;
BACNET_COLOR_TRANSITION Transition;
const char *Object_Name;
const char *Description;
};
/* Key List for storing the object data sorted by instance number */
static OS_Keylist Object_List;
/* callback for present value writes */
static color_write_present_value_callback Color_Write_Present_Value_Callback;
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Color_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_TRACKING_VALUE,
PROP_COLOR_COMMAND, PROP_IN_PROGRESS, PROP_DEFAULT_COLOR,
PROP_DEFAULT_FADE_TIME, -1 };
static const int Color_Properties_Optional[] = { PROP_DESCRIPTION,
PROP_TRANSITION, -1 };
static const int Color_Properties_Proprietary[] = { -1 };
/**
* Returns the list of required, optional, and proprietary properties.
* Used by ReadPropertyMultiple service.
*
* @param pRequired - pointer to list of int terminated by -1, of
* BACnet required properties for this object.
* @param pOptional - pointer to list of int terminated by -1, of
* BACnet optkional properties for this object.
* @param pProprietary - pointer to list of int terminated by -1, of
* BACnet proprietary properties for this object.
*/
void Color_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Color_Properties_Required;
}
if (pOptional) {
*pOptional = Color_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Color_Properties_Proprietary;
}
return;
}
/**
* Determines if a given Color instance is valid
*
* @param object_instance - object-instance number of the object
*
* @return true if the instance is valid, and false if not
*/
bool Color_Valid_Instance(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
return true;
}
return false;
}
/**
* Determines the number of Color objects
*
* @return Number of Color objects
*/
unsigned Color_Count(void)
{
return Keylist_Count(Object_List);
}
/**
* Determines the object instance-number for a given 0..N index
* of Color objects where N is Color_Count().
*
* @param index - 0..N where N is Color_Count()
*
* @return object instance-number for the given index
*/
uint32_t Color_Index_To_Instance(unsigned index)
{
return Keylist_Key(Object_List, index);
}
/**
* For a given object instance-number, determines a 0..N index
* of Color objects where N is Color_Count().
*
* @param object_instance - object-instance number of the object
*
* @return index for the given instance-number, or Color_Count()
* if not valid.
*/
unsigned Color_Instance_To_Index(uint32_t object_instance)
{
return Keylist_Index(Object_List, object_instance);
}
/**
* For a given object instance-number, determines the present-value
*
* @param object_instance - object-instance number of the object
*
* @return present-value of the object
*/
bool Color_Present_Value(uint32_t object_instance, BACNET_XY_COLOR *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
xy_color_copy(value, &pObject->Present_Value);
status = true;
}
return status;
}
/**
* For a given object instance-number, sets the present-value
*
* @param object_instance - object-instance number of the object
* @param value - floating point Color
*
* @return true if values are within range and present-value is set.
*/
bool Color_Present_Value_Set(uint32_t object_instance, BACNET_XY_COLOR *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
xy_color_copy(&pObject->Present_Value, value);
status = true;
}
return status;
}
/**
* For a given object instance-number, sets the present-value
*
* @param object_instance - object-instance number of the object
* @param value - floating point Color
* @param priority - priority-array index value 1..16
* @param error_class - the BACnet error class
* @param error_code - BACnet Error code
*
* @return true if values are within range and present-value is set.
*/
static bool Color_Present_Value_Write(uint32_t object_instance,
BACNET_XY_COLOR *value,
uint8_t priority,
BACNET_ERROR_CLASS *error_class,
BACNET_ERROR_CODE *error_code)
{
bool status = false;
struct object_data *pObject;
BACNET_XY_COLOR old_value = { 0.0, 0.0 };
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
(void)priority;
if (pObject->Write_Enabled) {
xy_color_copy(&old_value, &pObject->Present_Value);
xy_color_copy(&pObject->Present_Value, value);
if (Color_Write_Present_Value_Callback) {
Color_Write_Present_Value_Callback(
object_instance, &old_value, value);
}
status = true;
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
} else {
*error_class = ERROR_CLASS_OBJECT;
*error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return status;
}
/**
* For a given object instance-number, determines the present-value
*
* @param object_instance - object-instance number of the object
*
* @return present-value of the object
*/
bool Color_Tracking_Value(uint32_t object_instance, BACNET_XY_COLOR *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
xy_color_copy(value, &pObject->Tracking_Value);
status = true;
}
return status;
}
/**
* For a given object instance-number, sets the present-value
*
* @param object_instance - object-instance number of the object
* @param value - floating point Color
*
* @return true if values are within range and present-value is set.
*/
bool Color_Tracking_Value_Set(uint32_t object_instance, BACNET_XY_COLOR *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
xy_color_copy(&pObject->Tracking_Value, value);
status = true;
}
return status;
}
/**
* For a given object instance-number, gets the property value
*
* @param object_instance - object-instance number of the object
* @param value - color command data
* @return true if the property value is copied
*/
bool Color_Command(uint32_t object_instance, BACNET_COLOR_COMMAND *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject && value) {
color_command_copy(value, &pObject->Color_Command);
status = true;
}
return status;
}
/**
* For a given object instance-number, sets the property value
*
* @param object_instance - object-instance number of the object
* @param value - color command data
* @return true if values are within range and value is set.
*/
bool Color_Command_Set(uint32_t object_instance, BACNET_COLOR_COMMAND *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject && value) {
color_command_copy(&pObject->Color_Command, value);
status = true;
}
return status;
}
/**
* For a given object instance-number, gets the property value
*
* @param object_instance - object-instance number of the object
* @return property value
*/
BACNET_COLOR_OPERATION_IN_PROGRESS Color_In_Progress(uint32_t object_instance)
{
BACNET_COLOR_OPERATION_IN_PROGRESS value =
BACNET_COLOR_OPERATION_IN_PROGRESS_MAX;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->In_Progress;
}
return value;
}
/**
* For a given object instance-number, sets the property value
*
* @param object_instance - object-instance number of the object
* @param value - BACNET_COLOR_OPERATION_IN_PROGRESS
* @return true if values are within range and value is set.
*/
bool Color_In_Progress_Set(
uint32_t object_instance, BACNET_COLOR_OPERATION_IN_PROGRESS value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (value < BACNET_COLOR_OPERATION_IN_PROGRESS_MAX) {
pObject->In_Progress = value;
status = true;
}
}
return status;
}
/**
* For a given object instance-number, determines the present-value
*
* @param object_instance - object-instance number of the object
*
* @return present-value of the object
*/
bool Color_Default_Color(uint32_t object_instance, BACNET_XY_COLOR *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
xy_color_copy(value, &pObject->Default_Color);
status = true;
}
return status;
}
/**
* For a given object instance-number, sets the present-value
*
* @param object_instance - object-instance number of the object
* @param value - floating point Color
*
* @return true if values are within range and present-value is set.
*/
bool Color_Default_Color_Set(uint32_t object_instance, BACNET_XY_COLOR *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
xy_color_copy(&pObject->Default_Color, value);
status = true;
}
return status;
}
/**
* For a given object instance-number, gets the property value
*
* @param object_instance - object-instance number of the object
* @return property value
*/
uint32_t Color_Default_Fade_Time(uint32_t object_instance)
{
uint32_t value = 0;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->Default_Fade_Time;
}
return value;
}
/**
* For a given object instance-number, sets the property value
*
* @param object_instance - object-instance number of the object
* @param value - BACNET_COLOR_OPERATION_IN_PROGRESS
* @return true if values are within range and value is set.
*/
bool Color_Default_Fade_Time_Set(uint32_t object_instance, uint32_t value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if ((value == 0) ||
((value >= BACNET_COLOR_FADE_TIME_MIN) &&
(value <= BACNET_COLOR_FADE_TIME_MAX))) {
pObject->Default_Fade_Time = value;
}
status = true;
}
return status;
}
/**
* For a given object instance-number, gets the property value
*
* @param object_instance - object-instance number of the object
* @return property value
*/
BACNET_COLOR_TRANSITION Color_Transition(uint32_t object_instance)
{
BACNET_COLOR_TRANSITION value = BACNET_COLOR_OPERATION_IN_PROGRESS_MAX;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->Transition;
}
return value;
}
/**
* For a given object instance-number, sets the property value
*
* @param object_instance - object-instance number of the object
* @param value - BACNET_COLOR_TRANSITION
* @return true if values are within range and value is set.
*/
bool Color_Transition_Set(
uint32_t object_instance, BACNET_COLOR_TRANSITION value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (value < BACNET_COLOR_TRANSITION_MAX) {
pObject->Transition = value;
status = true;
}
}
return status;
}
/**
* For a given object instance-number, loads the object-name into
* a characterstring. Note that the object name must be unique
* within this device.
*
* @param object_instance - object-instance number of the object
* @param object_name - holds the object-name retrieved
*
* @return true if object-name was retrieved
*/
bool Color_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
bool status = false;
struct object_data *pObject;
char name_text[16] = "COLOR-4194303";
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (pObject->Object_Name) {
status =
characterstring_init_ansi(object_name, pObject->Object_Name);
} else {
snprintf(name_text, sizeof(name_text), "COLOR-%u", object_instance);
status = characterstring_init_ansi(object_name, name_text);
}
}
return status;
}
/**
* For a given object instance-number, sets the object-name
* Note that the object name must be unique within this device.
*
* @param object_instance - object-instance number of the object
* @param new_name - holds the object-name to be set
*
* @return true if object-name was set
*/
bool Color_Name_Set(uint32_t object_instance, char *new_name)
{
bool status = false; /* return value */
BACNET_CHARACTER_STRING object_name;
BACNET_OBJECT_TYPE found_type = 0;
uint32_t found_instance = 0;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject && new_name) {
/* All the object names in a device must be unique */
characterstring_init_ansi(&object_name, new_name);
if (Device_Valid_Object_Name(
&object_name, &found_type, &found_instance)) {
if ((found_type == OBJECT_COLOR) &&
(found_instance == object_instance)) {
/* writing same name to same object */
status = true;
} else {
/* duplicate name! */
status = false;
}
} else {
status = true;
pObject->Object_Name = new_name;
Device_Inc_Database_Revision();
}
}
return status;
}
/**
* For a given object instance-number, returns the description
*
* @param object_instance - object-instance number of the object
*
* @return description text or NULL if not found
*/
char *Color_Description(uint32_t object_instance)
{
char *name = NULL;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (pObject->Description) {
name = (char *)pObject->Description;
} else {
name = "";
}
}
return name;
}
/**
* For a given object instance-number, sets the description
*
* @param object_instance - object-instance number of the object
* @param new_name - holds the description to be set
*
* @return true if object-name was set
*/
bool Color_Description_Set(uint32_t object_instance, char *new_name)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject && new_name) {
status = true;
pObject->Description = new_name;
}
return status;
}
/**
* ReadProperty handler for this object. For the given ReadProperty
* data, the application_data is loaded or the error flags are set.
*
* @param rpdata - BACNET_READ_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return number of APDU bytes in the response, or
* BACNET_STATUS_ERROR on error.
*/
int Color_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
BACNET_CHARACTER_STRING char_string;
uint8_t *apdu = NULL;
BACNET_XY_COLOR color_value = { 0.0, 0.0 };
BACNET_COLOR_COMMAND color_command = { 0 };
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], rpdata->object_type, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Color_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], rpdata->object_type);
break;
case PROP_PRESENT_VALUE:
if (Color_Present_Value(rpdata->object_instance, &color_value)) {
apdu_len = xy_color_encode(apdu, &color_value);
}
break;
case PROP_TRACKING_VALUE:
if (Color_Tracking_Value(rpdata->object_instance, &color_value)) {
apdu_len = xy_color_encode(apdu, &color_value);
}
break;
case PROP_COLOR_COMMAND:
if (Color_Command(rpdata->object_instance, &color_command)) {
apdu_len = color_command_encode(apdu, &color_command);
}
break;
case PROP_IN_PROGRESS:
apdu_len = encode_application_enumerated(
apdu, Color_In_Progress(rpdata->object_instance));
break;
case PROP_DEFAULT_COLOR:
if (Color_Default_Color(rpdata->object_instance, &color_value)) {
apdu_len = xy_color_encode(apdu, &color_value);
}
break;
case PROP_DEFAULT_FADE_TIME:
apdu_len = encode_application_unsigned(
apdu, Color_Default_Fade_Time(rpdata->object_instance));
break;
case PROP_TRANSITION:
apdu_len = encode_application_enumerated(
apdu, Color_Transition(rpdata->object_instance));
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(
&char_string, Color_Description(rpdata->object_instance));
apdu_len = encode_application_character_string(apdu, &char_string);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
(rpdata->object_property != PROP_EVENT_TIME_STAMPS) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/**
* WriteProperty handler for this object. For the given WriteProperty
* data, the application_data is loaded or the error flags are set.
*
* @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return false if an error is loaded, true if no errors
*/
bool Color_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->object_property != PROP_EVENT_TIME_STAMPS) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_REAL);
if (status) {
status = Color_Present_Value_Write(wp_data->object_instance,
&value.type.XY_Color, wp_data->priority,
&wp_data->error_class, &wp_data->error_code);
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_TYPE:
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
/**
* @brief Sets a callback used when present-value is written from BACnet
* @param cb - callback used to provide indications
*/
void Color_Write_Present_Value_Callback_Set(
color_write_present_value_callback cb)
{
Color_Write_Present_Value_Callback = cb;
}
/**
* @brief Determines a object write-enabled flag state
* @param object_instance - object-instance number of the object
* @return write-enabled status flag
*/
bool Color_Write_Enabled(uint32_t object_instance)
{
bool value = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->Write_Enabled;
}
return value;
}
/**
* @brief For a given object instance-number, sets the write-enabled flag
* @param object_instance - object-instance number of the object
*/
void Color_Write_Enable(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Write_Enabled = true;
}
}
/**
* @brief For a given object instance-number, clears the write-enabled flag
* @param object_instance - object-instance number of the object
*/
void Color_Write_Disable(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Write_Enabled = false;
}
}
/**
* Creates a Color object
* @param object_instance - object-instance number of the object
*/
bool Color_Create(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
int index = 0;
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
pObject = calloc(1, sizeof(struct object_data));
if (pObject) {
pObject->Object_Name = NULL;
pObject->Present_Value.x_coordinate = 0.0;
pObject->Present_Value.y_coordinate = 0.0;
pObject->Tracking_Value.x_coordinate = 0.0;
pObject->Tracking_Value.y_coordinate = 0.0;
pObject->Color_Command.operation = BACNET_COLOR_OPERATION_NONE;
pObject->In_Progress = BACNET_COLOR_OPERATION_IN_PROGRESS_IDLE;
pObject->Default_Color.x_coordinate = 1.0;
pObject->Default_Color.y_coordinate = 1.0;
pObject->Default_Fade_Time = 0;
pObject->Transition = BACNET_COLOR_TRANSITION_NONE;
pObject->Changed = false;
pObject->Write_Enabled = false;
/* add to list */
index = Keylist_Data_Add(Object_List, object_instance, pObject);
if (index >= 0) {
status = true;
Device_Inc_Database_Revision();
}
}
}
return status;
}
/**
* Deletes an Color object
* @param object_instance - object-instance number of the object
* @return true if the object is deleted
*/
bool Color_Delete(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
pObject = Keylist_Data_Delete(Object_List, object_instance);
if (pObject) {
free(pObject);
status = true;
Device_Inc_Database_Revision();
}
return status;
}
/**
* Deletes all the Colors and their data
*/
void Color_Cleanup(void)
{
struct object_data *pObject;
if (Object_List) {
do {
pObject = Keylist_Data_Pop(Object_List);
if (pObject) {
free(pObject);
Device_Inc_Database_Revision();
}
} while (pObject);
Keylist_Delete(Object_List);
Object_List = NULL;
}
}
/**
* Initializes the Color object data
*/
void Color_Init(void)
{
Object_List = Keylist_Create();
if (Object_List) {
atexit(Color_Cleanup);
}
}
+133
View File
@@ -0,0 +1,133 @@
/**
* @file
* @author Steve Karg
* @date June 2022
* @brief Color object, customize for your use
*
* @section DESCRIPTION
*
* The Color object is an object with a present-value that
* uses an x,y color single precision floating point data type.
*
* @section LICENSE
*
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
#ifndef BACNET_COLOR_OBJECT_H
#define BACNET_COLOR_OBJECT_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacnet_stack_exports.h"
#include "bacnet/config.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
/**
* @brief Callback for gateway write present value request
* @param object_instance - object-instance number of the object
* @param old_value - BACnetXYColor value prior to write
* @param value - BACnetXYColor value of the write
*/
typedef void (*color_write_present_value_callback)(uint32_t object_instance,
BACNET_XY_COLOR *old_value,
BACNET_XY_COLOR *value);
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
void Color_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary);
BACNET_STACK_EXPORT
bool Color_Valid_Instance(uint32_t object_instance);
BACNET_STACK_EXPORT
unsigned Color_Count(void);
BACNET_STACK_EXPORT
uint32_t Color_Index_To_Instance(unsigned index);
BACNET_STACK_EXPORT
unsigned Color_Instance_To_Index(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name);
BACNET_STACK_EXPORT
bool Color_Name_Set(uint32_t object_instance, char *new_name);
BACNET_STACK_EXPORT
int Color_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata);
BACNET_STACK_EXPORT
bool Color_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data);
BACNET_STACK_EXPORT
bool Color_Present_Value_Set(uint32_t object_instance, BACNET_XY_COLOR *value);
BACNET_STACK_EXPORT
bool Color_Present_Value(uint32_t object_instance, BACNET_XY_COLOR *value);
BACNET_STACK_EXPORT
void Color_Write_Present_Value_Callback_Set(
color_write_present_value_callback cb);
BACNET_STACK_EXPORT
bool Color_Tracking_Value_Set(uint32_t object_instance, BACNET_XY_COLOR *value);
BACNET_STACK_EXPORT
bool Color_Tracking_Value(uint32_t object_instance, BACNET_XY_COLOR *value);
BACNET_STACK_EXPORT
bool Color_Command(uint32_t object_instance, BACNET_COLOR_COMMAND *value);
BACNET_STACK_EXPORT
bool Color_Command_Set(uint32_t object_instance, BACNET_COLOR_COMMAND *value);
BACNET_STACK_EXPORT
bool Color_Default_Color_Set(uint32_t object_instance, BACNET_XY_COLOR *value);
BACNET_STACK_EXPORT
bool Color_Default_Color(uint32_t object_instance, BACNET_XY_COLOR *value);
BACNET_STACK_EXPORT
uint32_t Color_Default_Fade_Time(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Default_Fade_Time_Set(uint32_t object_instance, uint32_t value);
BACNET_STACK_EXPORT
BACNET_COLOR_OPERATION_IN_PROGRESS Color_In_Progress(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_In_Progress_Set(
uint32_t object_instance, BACNET_COLOR_OPERATION_IN_PROGRESS value);
BACNET_STACK_EXPORT
BACNET_COLOR_TRANSITION Color_Transition(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Transition_Set(
uint32_t object_instance, BACNET_COLOR_TRANSITION value);
BACNET_STACK_EXPORT
char *Color_Description(uint32_t instance);
BACNET_STACK_EXPORT
bool Color_Description_Set(uint32_t instance, char *new_name);
BACNET_STACK_EXPORT
bool Color_Write_Enabled(uint32_t instance);
BACNET_STACK_EXPORT
void Color_Write_Enable(uint32_t instance);
BACNET_STACK_EXPORT
void Color_Write_Disable(uint32_t instance);
BACNET_STACK_EXPORT
bool Color_Create(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Delete(uint32_t object_instance);
BACNET_STACK_EXPORT
void Color_Cleanup(void);
BACNET_STACK_EXPORT
void Color_Init(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
File diff suppressed because it is too large Load Diff
+165
View File
@@ -0,0 +1,165 @@
/**
* @file
* @author Steve Karg
* @date July 2022
* @brief Color Temperature object, customize for your use
*
* @section DESCRIPTION
*
* The Color Temperature object is an object with a present-value that
* uses an Color Temperature INTEGER type
*
* @section LICENSE
*
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
#ifndef BACNET_COLOR_TEMPERATURE_H
#define BACNET_COLOR_TEMPERATURE_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacnet_stack_exports.h"
#include "bacnet/config.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
/**
* @brief Callback for gateway write present value request
* @param object_instance - object-instance number of the object
* @param old_value - 32-bit value prior to write
* @param value - 32-bit value of the write
*/
typedef void (*color_temperature_write_present_value_callback)(
uint32_t object_instance, uint32_t old_value, uint32_t value);
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
void Color_Temperature_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary);
BACNET_STACK_EXPORT
bool Color_Temperature_Valid_Instance(uint32_t object_instance);
BACNET_STACK_EXPORT
unsigned Color_Temperature_Count(void);
BACNET_STACK_EXPORT
uint32_t Color_Temperature_Index_To_Instance(unsigned index);
BACNET_STACK_EXPORT
unsigned Color_Temperature_Instance_To_Index(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name);
BACNET_STACK_EXPORT
bool Color_Temperature_Name_Set(uint32_t object_instance, char *new_name);
BACNET_STACK_EXPORT
int Color_Temperature_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata);
BACNET_STACK_EXPORT
bool Color_Temperature_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data);
BACNET_STACK_EXPORT
bool Color_Temperature_Present_Value_Set(
uint32_t object_instance, uint32_t value);
BACNET_STACK_EXPORT
uint32_t Color_Temperature_Present_Value(uint32_t object_instance);
BACNET_STACK_EXPORT
void Color_Temperature_Write_Present_Value_Callback_Set(
color_temperature_write_present_value_callback cb);
BACNET_STACK_EXPORT
bool Color_Temperature_Tracking_Value_Set(
uint32_t object_instance, uint32_t value);
BACNET_STACK_EXPORT
uint32_t Color_Temperature_Tracking_Value(uint32_t object_instance);
BACNET_STACK_EXPORT
uint32_t Color_Temperature_Min_Pres_Value(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Min_Pres_Value_Set(
uint32_t object_instance, uint32_t value);
BACNET_STACK_EXPORT
uint32_t Color_Temperature_Max_Pres_Value(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Max_Pres_Value_Set(
uint32_t object_instance, uint32_t value);
BACNET_STACK_EXPORT
bool Color_Temperature_Command(
uint32_t object_instance, BACNET_COLOR_COMMAND *value);
BACNET_STACK_EXPORT
bool Color_Temperature_Command_Set(
uint32_t object_instance, BACNET_COLOR_COMMAND *value);
BACNET_STACK_EXPORT
bool Color_Temperature_Default_Color_Temperature_Set(
uint32_t object_instance, uint32_t value);
BACNET_STACK_EXPORT
uint32_t Color_Temperature_Default_Color_Temperature(uint32_t object_instance);
BACNET_STACK_EXPORT
uint32_t Color_Temperature_Default_Fade_Time(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Default_Fade_Time_Set(
uint32_t object_instance, uint32_t value);
BACNET_STACK_EXPORT
uint32_t Color_Temperature_Default_Ramp_Rate(
uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Default_Ramp_Rate_Set(
uint32_t object_instance, uint32_t value);
BACNET_STACK_EXPORT
uint32_t Color_Temperature_Default_Step_Increment(
uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Default_Step_Increment_Set(
uint32_t object_instance, uint32_t value);
BACNET_STACK_EXPORT
BACNET_COLOR_OPERATION_IN_PROGRESS Color_Temperature_In_Progress(
uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Temperature_In_Progress_Set(
uint32_t object_instance, BACNET_COLOR_OPERATION_IN_PROGRESS value);
BACNET_STACK_EXPORT
BACNET_COLOR_TRANSITION Color_Temperature_Transition(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Transition_Set(
uint32_t object_instance, BACNET_COLOR_TRANSITION value);
BACNET_STACK_EXPORT
char *Color_Temperature_Description(uint32_t instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Description_Set(uint32_t instance, char *new_name);
BACNET_STACK_EXPORT
bool Color_Temperature_Write_Enabled(uint32_t instance);
BACNET_STACK_EXPORT
void Color_Temperature_Write_Enable(uint32_t instance);
BACNET_STACK_EXPORT
void Color_Temperature_Write_Disable(uint32_t instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Create(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Delete(uint32_t object_instance);
BACNET_STACK_EXPORT
void Color_Temperature_Cleanup(void);
BACNET_STACK_EXPORT
void Color_Temperature_Init(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+113 -18
View File
@@ -75,6 +75,10 @@
#if defined(BACFILE)
#include "bacnet/basic/object/bacfile.h"
#endif /* defined(BACFILE) */
#if (BACNET_PROTOCOL_REVISION >= 24)
#include "bacnet/basic/object/color_object.h"
#include "bacnet/basic/object/color_temperature.h"
#endif
/* local forward (semi-private) and external prototypes */
int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata);
@@ -223,6 +227,20 @@ static object_functions_t My_Object_Table[] = {
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
#endif
#if (BACNET_PROTOCOL_REVISION >= 24)
{ OBJECT_COLOR, Color_Init, Color_Count,
Color_Index_To_Instance, Color_Valid_Instance,
Color_Object_Name, Color_Read_Property,
Color_Write_Property, Color_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
{ OBJECT_COLOR_TEMPERATURE, Color_Temperature_Init, Color_Temperature_Count,
Color_Temperature_Index_To_Instance, Color_Temperature_Valid_Instance,
Color_Temperature_Object_Name, Color_Temperature_Read_Property,
Color_Temperature_Write_Property, Color_Temperature_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
#endif
#if defined(BACFILE)
{ OBJECT_FILE, bacfile_init, bacfile_count, bacfile_index_to_instance,
bacfile_valid_instance, bacfile_object_name, bacfile_read_property,
@@ -1312,6 +1330,91 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
return apdu_len;
}
/** Looks up the common Object and Property, and encodes its Value in an
* APDU. Sets the error class and code if request is not appropriate.
* @param pObject - object table
* @param rpdata [in,out] Structure with the requested Object & Property info
* on entry, and APDU message on return.
* @return The length of the APDU on success, else BACNET_STATUS_ERROR
*/
static int Read_Property_Common(
struct object_functions *pObject, BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = BACNET_STATUS_ERROR;
BACNET_CHARACTER_STRING char_string;
uint8_t *apdu = NULL;
#if (BACNET_PROTOCOL_REVISION >= 14)
struct special_property_list_t property_list;
#endif
if ((rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
/* only array properties can have array options */
if (rpdata->array_index != BACNET_ARRAY_ALL) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
} else {
/* Device Object exception: requested instance
may not match our instance if a wildcard */
if (rpdata->object_type == OBJECT_DEVICE) {
rpdata->object_instance = Object_Instance_Number;
}
apdu_len = encode_application_object_id(
&apdu[0], rpdata->object_type, rpdata->object_instance);
}
break;
case PROP_OBJECT_NAME:
/* only array properties can have array options */
if (rpdata->array_index != BACNET_ARRAY_ALL) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
} else {
characterstring_init_ansi(&char_string, "");
if (pObject->Object_Name) {
(void)pObject->Object_Name(
rpdata->object_instance, &char_string);
}
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
}
break;
case PROP_OBJECT_TYPE:
/* only array properties can have array options */
if (rpdata->array_index != BACNET_ARRAY_ALL) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
} else {
apdu_len = encode_application_enumerated(
&apdu[0], rpdata->object_type);
}
break;
#if (BACNET_PROTOCOL_REVISION >= 14)
case PROP_PROPERTY_LIST:
Device_Objects_Property_List(
rpdata->object_type, rpdata->object_instance, &property_list);
apdu_len = property_list_encode(rpdata,
property_list.Required.pList, property_list.Optional.pList,
property_list.Proprietary.pList);
break;
#endif
default:
if (pObject->Object_Read_Property) {
apdu_len = pObject->Object_Read_Property(rpdata);
}
break;
}
return apdu_len;
}
/** Looks up the requested Object and Property, and encodes its Value in an
* APDU.
* @ingroup ObjIntf
@@ -1325,9 +1428,6 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = BACNET_STATUS_ERROR;
struct object_functions *pObject = NULL;
#if (BACNET_PROTOCOL_REVISION >= 14)
struct special_property_list_t property_list;
#endif
/* initialize the default return values */
rpdata->error_class = ERROR_CLASS_OBJECT;
@@ -1336,22 +1436,14 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(rpdata->object_instance)) {
if (pObject->Object_Read_Property) {
#if (BACNET_PROTOCOL_REVISION >= 14)
if ((int)rpdata->object_property == PROP_PROPERTY_LIST) {
Device_Objects_Property_List(rpdata->object_type,
rpdata->object_instance, &property_list);
apdu_len = property_list_encode(rpdata,
property_list.Required.pList,
property_list.Optional.pList,
property_list.Proprietary.pList);
} else
#endif
{
apdu_len = pObject->Object_Read_Property(rpdata);
}
}
apdu_len = Read_Property_Common(pObject, rpdata);
} else {
rpdata->error_class = ERROR_CLASS_OBJECT;
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
} else {
rpdata->error_class = ERROR_CLASS_OBJECT;
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return apdu_len;
@@ -1816,6 +1908,9 @@ void Device_Init(object_functions_t *object_table)
}
pObject++;
}
#if (BACNET_PROTOCOL_REVISION >= 24)
Color_Create(1);
#endif
}
bool DeviceGetRRInfo(BACNET_READ_RANGE_DATA *pRequest, /* Info on the request */
+3 -3
View File
@@ -313,7 +313,7 @@ static union {
* @param service_choice Service, see SERVICE_CONFIRMED_X enumeration.
* @return true if the service uses a Complex Error function
*/
bool apdu_complex_error(BACNET_CONFIRMED_SERVICE service_choice)
bool apdu_complex_error(uint8_t service_choice)
{
bool status = false;
@@ -547,8 +547,8 @@ void apdu_handler(BACNET_ADDRESS *src,
uint8_t *service_request = NULL;
uint16_t service_request_len = 0;
int len = 0; /* counts where we are in PDU */
uint32_t error_code = 0;
uint32_t error_class = 0;
BACNET_ERROR_CODE error_code = ERROR_CODE_SUCCESS;
BACNET_ERROR_CLASS error_class = ERROR_CLASS_SERVICES;
uint8_t reason = 0;
bool server = false;
+1 -1
View File
@@ -161,7 +161,7 @@ extern "C" {
BACNET_STACK_EXPORT
bool apdu_complex_error(
BACNET_CONFIRMED_SERVICE service_choice);
uint8_t service_choice);
BACNET_STACK_EXPORT
void apdu_set_error_handler(
+3 -2
View File
@@ -64,8 +64,9 @@ void rp_ack_print_data(BACNET_READ_PROPERTY_DATA *data)
/* FIXME: what if application_data_len is bigger than 255? */
/* value? need to loop until all of the len is gone... */
for (;;) {
len = bacapp_decode_application_data(
application_data, (unsigned)application_data_len, &value);
len = bacapp_decode_known_property(
application_data, (unsigned)application_data_len, &value,
data->object_type, data->object_property);
if (first_value && (len < application_data_len)) {
first_value = false;
#if PRINT_ENABLED
+19 -10
View File
@@ -115,17 +115,20 @@ int rpm_ack_decode_service_request(
value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE));
rpm_property->value = value;
while (value && (apdu_len > 0)) {
if (IS_CONTEXT_SPECIFIC(*apdu)) {
len = bacapp_decode_context_data(apdu, apdu_len, value,
rpm_property->propertyIdentifier);
} else {
len = bacapp_decode_application_data(
apdu, apdu_len, value);
}
len = bacapp_decode_known_property(
apdu, (unsigned)apdu_len, value,
rpm_object->object_type,
rpm_property->propertyIdentifier);
/* If len == 0 then it's an empty structure, which is OK. */
if (len < 0) {
/* problem decoding */
/* calling function will free the memory */
#if PRINT_ENABLED
fprintf(stderr, "RPM Ack: unable to decode! %s:%s\n",
bactext_object_type_name(rpm_object->object_type),
bactext_property_name(
rpm_property->propertyIdentifier));
#endif
/* note: caller will free the memory */
return BACNET_STATUS_ERROR;
}
decoded_len += len;
@@ -220,11 +223,17 @@ void rpm_ack_print_data(BACNET_READ_ACCESS_DATA *rpm_data)
listOfProperties = rpm_data->listOfProperties;
while (listOfProperties) {
#if PRINT_ENABLED
if (listOfProperties->propertyIdentifier < 512) {
if ((listOfProperties->propertyIdentifier < 512) ||
(listOfProperties->propertyIdentifier > 4194303)) {
/* Enumerated values 0-511 and 4194304+ are reserved
for definition by ASHRAE.*/
fprintf(stdout, " %s: ",
bactext_property_name(
listOfProperties->propertyIdentifier));
} else {
/* Enumerated values 512-4194303 may be used
by others subject to the procedures and
constraints described in Clause 23. */
fprintf(stdout, " proprietary %u: ",
(unsigned)listOfProperties->propertyIdentifier);
}
@@ -357,7 +366,7 @@ void handler_read_property_multiple_ack(uint8_t *service_request,
rpm_data = rpm_data_free(rpm_data);
}
} else {
#if 1
#if PRINT_ENABLED
fprintf(stderr, "RPM Ack Malformed! Freeing memory...\n");
#endif
while (rpm_data) {
+486
View File
@@ -0,0 +1,486 @@
/**
* @file
* @author Steve Karg <skarg@users.sourceforge.net>
* @date 2022
* @brief computes sRGB to and from from CIE xy and brightness
*
* @section LICENSE
*
* Public domain algorithms from Philips and W3C
*
*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include "bacnet/basic/sys/color_rgb.h"
/**
* @brief Clamp a double precision value between two limits
* @param d - value to be clamped
* @param min - minimum value to clamp within
* @param max - maximum value to clamp within
* @return value clamped between min and max inclusive
*/
static double clamp(double d, double min, double max)
{
if (isnan(d)) {
return min;
} else {
const double t = d < min ? min : d;
return t > max ? max : t;
}
}
/**
* @brief Convert sRGB to CIE xy
* @param r - R value of sRGB 0..255
* @param g - G value of sRGB 0..255
* @param b - B value of sRGB 0..255
* @param x_coordinate - return x of CIE xy 0.0..1.0
* @param y_coordinate - return y of CIE xy 0.0..1.0
* @param brightness - return brightness of the CIE xy color 0..255
* @note http://en.wikipedia.org/wiki/Srgb
*/
void color_rgb_to_xy(uint8_t r, uint8_t g, uint8_t b,
float *x_coordinate, float *y_coordinate, uint8_t *brightness)
{
/* Get the RGB values from your color object
and convert them to be between 0 and 1.
So the RGB color (255, 0, 100) becomes (1.0, 0.0, 0.39) */
float red = (float)r;
float green = (float)g;
float blue = (float)b;
red /= 255.0f;
green /= 255.0f;
blue /= 255.0f;
/* Apply a gamma correction to the RGB values,
which makes the color more vivid and more the
like the color displayed on the screen of your device.
This gamma correction is also applied to the screen
of your computer or phone, thus we need this to create
the same color on the light as on screen. */
red = (red > 0.04045f) ?
pow((red + 0.055f) / (1.0f + 0.055f), 2.4f) :
(red / 12.92f);
green = (green > 0.04045f) ?
pow((green + 0.055f) / (1.0f + 0.055f), 2.4f) :
(green / 12.92f);
blue = (blue > 0.04045f) ?
pow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) :
(blue / 12.92f);
/* Convert the RGB values to XYZ using the
Wide RGB D65 conversion formula */
float X = red * 0.649926f + green * 0.103455f + blue * 0.197109f;
float Y = red * 0.234327f + green * 0.743075f + blue * 0.022598f;
float Z = red * 0.0000000f + green * 0.053077f + blue * 1.035763f;
/* Calculate the xy values from the XYZ values */
float x = X / (X + Y + Z);
float y = Y / (X + Y + Z);
x = clamp(x, 0.0f, 1.0f);
y = clamp(y, 0.0f, 1.0f);
/* copy to return values if possible */
if (x_coordinate) {
*x_coordinate = x;
}
if (y_coordinate) {
*y_coordinate = y;
}
/* Use the Y value of XYZ as brightness
The Y value indicates the brightness
of the converted color. */
Y = Y*255.0f;
Y = clamp(Y, 0.0f, 255.0f);
if (brightness) {
*brightness = (uint8_t)Y;
}
}
/**
* @brief Convert sRGB from CIE xy and brightness
* @param red - return R value of sRGB
* @param green - return G value of sRGB
* @param blue - return B value of sRGB
* @param x_coordinate - x of CIE xy
* @param y_coordinate - y of CIE xy
* @param brightness - brightness of the CIE xy color
* @note http://en.wikipedia.org/wiki/Srgb
*/
void color_rgb_from_xy(uint8_t *red, uint8_t *green, uint8_t *blue,
float x_coordinate, float y_coordinate, uint8_t brightness)
{
/* Calculate XYZ values */
float x = x_coordinate;
float y = y_coordinate;
float z = 1.0f - x - y;
float Y = brightness;
Y /= 255.0f;
float X = (Y / y) * x;
float Z = (Y / y) * z;
/* Convert to RGB using Wide RGB D65 conversion
(THIS IS A D50 conversion currently) */
float r = X * 1.4628067f - Y * 0.1840623f - Z * 0.2743606f;
float g = -X * 0.5217933f + Y * 1.4472381f + Z * 0.0677227f;
float b = X * 0.0349342f - Y * 0.0968930f + Z * 1.2884099f;
/* Apply reverse gamma correction */
r = r <= 0.0031308f ? 12.92f * r :
(1.0f + 0.055f) * pow(r, (1.0f / 2.4f)) - 0.055f;
g = g <= 0.0031308f ? 12.92f * g :
(1.0f + 0.055f) * pow(g, (1.0f / 2.4f)) - 0.055f;
b = b <= 0.0031308f ? 12.92f * b :
(1.0f + 0.055f) * pow(b, (1.0f / 2.4f)) - 0.055f;
/* Convert the RGB values to your color object
The rgb values from the above formulas are
between 0.0 and 1.0. */
r = r*255.0f;
r = clamp(r, 0.0f, 255.0f);
g = g*255;
g = clamp(g, 0.0f, 255.0f);
b = b*255;
b = clamp(b, 0.0f, 255.0f);
/* copy to return value if possible */
if (red) {
*red = (uint8_t)r;
}
if (green) {
*green = (uint8_t)g;
}
if (blue) {
*blue = (uint8_t)b;
}
}
/* table for converting RGB to and from ASCII color names */
struct css_color_rgb {
const char *name;
uint8_t red;
uint8_t green;
uint8_t blue;
};
static struct css_color_rgb CSS_Color_RGB_Table[] = {
{"aliceblue", 240, 248, 255},
{"antiquewhite", 250, 235, 215},
{"aqua", 0, 255, 255},
{"aquamarine", 127, 255, 212},
{"azure", 240, 255, 255},
{"beige", 245, 245, 220},
{"bisque", 255, 228, 196},
{"black", 0, 0, 0},
{"blanchedalmond", 255, 235, 205},
{"blue", 0, 0, 255},
{"blueviolet", 138, 43, 226},
{"brown", 165, 42, 42},
{"burlywood", 222, 184, 135},
{"cadetblue", 95, 158, 160},
{"chartreuse", 127, 255, 0},
{"chocolate", 210, 105, 30},
{"coral", 255, 127, 80},
{"cornflowerblue", 100, 149, 237},
{"cornsilk", 255, 248, 220},
{"crimson", 220, 20, 60},
{"cyan", 0, 255, 255},
{"darkblue", 0, 0, 139},
{"darkcyan", 0, 139, 139},
{"darkgoldenrod", 184, 134, 11},
{"darkgray", 169, 169, 169},
{"darkgreen", 0, 100, 0},
{"darkgrey", 169, 169, 169},
{"darkkhaki", 189, 183, 107},
{"darkmagenta", 139, 0, 139},
{"darkolivegreen", 85, 107, 47},
{"darkorange", 255, 140, 0},
{"darkorchid", 153, 50, 204},
{"darkred", 139, 0, 0},
{"darksalmon", 233, 150, 122},
{"darkseagreen", 143, 188, 143},
{"darkslateblue", 72, 61, 139},
{"darkslategray", 47, 79, 79},
{"darkslategrey", 47, 79, 79},
{"darkturquoise", 0, 206, 209},
{"darkviolet", 148, 0, 211},
{"deeppink", 255, 20, 147},
{"deepskyblue", 0, 191, 255},
{"dimgray", 105, 105, 105},
{"dimgrey", 105, 105, 105},
{"dodgerblue", 30, 144, 255},
{"firebrick", 178, 34, 34},
{"floralwhite", 255, 250, 240},
{"forestgreen", 34, 139, 34},
{"fuchsia", 255, 0, 255},
{"gainsboro", 220, 220, 220},
{"ghostwhite", 248, 248, 255},
{"gold", 255, 215, 0},
{"goldenrod", 218, 165, 32},
{"gray", 128, 128, 128},
{"green", 0, 128, 0},
{"greenyellow", 173, 255, 47},
{"grey", 128, 128, 128},
{"honeydew", 240, 255, 240},
{"hotpink", 255, 105, 180},
{"indianred", 205, 92, 92},
{"indigo", 75, 0, 130},
{"ivory", 255, 255, 240},
{"khaki", 240, 230, 140},
{"lavender", 230, 230, 250},
{"lavenderblush", 255, 240, 245},
{"lawngreen", 124, 252, 0},
{"lemonchiffon", 255, 250, 205},
{"lightblue", 173, 216, 230},
{"lightcoral", 240, 128, 128},
{"lightcyan", 224, 255, 255},
{"lightgoldenrodyellow", 250, 250, 210},
{"lightgray", 211, 211, 211},
{"lightgreen", 144, 238, 144},
{"lightgrey", 211, 211, 211},
{"lightpink", 255, 182, 193},
{"lightsalmon", 255, 160, 122},
{"lightseagreen", 32, 178, 170},
{"lightskyblue", 135, 206, 250},
{"lightslategray", 119, 136, 153},
{"lightslategrey", 119, 136, 153},
{"lightsteelblue", 176, 196, 222},
{"lightyellow", 255, 255, 224},
{"lime", 0, 255, 0},
{"limegreen", 50, 205, 50},
{"linen", 250, 240, 230},
{"magenta", 255, 0, 255},
{"maroon", 128, 0, 0},
{"mediumaquamarine", 102, 205, 170},
{"mediumblue", 0, 0, 205},
{"mediumorchid", 186, 85, 211},
{"mediumpurple", 147, 112, 219},
{"mediumseagreen", 60, 179, 113},
{"mediumslateblue", 123, 104, 238},
{"mediumspringgreen", 0, 250, 154},
{"mediumturquoise", 72, 209, 204},
{"mediumvioletred", 199, 21, 133},
{"midnightblue", 25, 25, 112},
{"mintcream", 245, 255, 250},
{"mistyrose", 255, 228, 225},
{"moccasin", 255, 228, 181},
{"navajowhite", 255, 222, 173},
{"navy", 0, 0, 128},
{"navyblue", 0, 0, 128},
{"oldlace", 253, 245, 230},
{"olive", 128, 128, 0},
{"olivedrab", 107, 142, 35},
{"orange", 255, 165, 0},
{"orangered", 255, 69, 0},
{"orchid", 218, 112, 214},
{"palegoldenrod", 238, 232, 170},
{"palegreen", 152, 251, 152},
{"paleturquoise", 175, 238, 238},
{"palevioletred", 219, 112, 147},
{"papayawhip", 255, 239, 213},
{"peachpuff", 255, 218, 185},
{"peru", 205, 133, 63},
{"pink", 255, 192, 203},
{"plum", 221, 160, 221},
{"powderblue", 176, 224, 230},
{"purple", 128, 0, 128},
{"red", 255, 0, 0},
{"rosybrown", 188, 143, 143},
{"royalblue", 65, 105, 225},
{"saddlebrown", 139, 69, 19},
{"salmon", 250, 128, 114},
{"sandybrown", 244, 164, 96},
{"seagreen", 46, 139, 87},
{"seashell", 255, 245, 238},
{"sienna", 160, 82, 45},
{"silver", 192, 192, 192},
{"skyblue", 135, 206, 235},
{"slateblue", 106, 90, 205},
{"slategray", 112, 128, 144},
{"slategrey", 112, 128, 144},
{"snow", 255, 250, 250},
{"springgreen", 0, 255, 127},
{"steelblue", 70, 130, 180},
{"tan", 210, 180, 140},
{"teal", 0, 128, 128},
{"thistle", 216, 191, 216},
{"tomato", 255, 99, 71},
{"turquoise", 64, 224, 208},
{"violet", 238, 130, 238},
{"wheat", 245, 222, 179},
{"white", 255, 255, 255},
{"whitesmoke", 245, 245, 245},
{"yellow", 255, 255, 0},
{"yellowgreen", 154, 205, 50},
{NULL, 0, 0, 0}
};
/**
* @brief Convert sRGB from CIE xy and brightness
* @param red - return R value of sRGB
* @param green - return G value of sRGB
* @param blue - return B value of sRGB
* @return name - CSS color name from W3C or "" if not found
* @note Official CSS3 colors from w3.org:
* https://www.w3.org/TR/2010/PR-css3-color-20101028/#html4
* names do not have spaces
*/
const char * color_rgb_to_ascii(uint8_t red, uint8_t green, uint8_t blue)
{
const char * name = "";
unsigned index = 0;
while (CSS_Color_RGB_Table[index].name) {
if ((red == CSS_Color_RGB_Table[index].red) &&
(green == CSS_Color_RGB_Table[index].green) &&
(blue == CSS_Color_RGB_Table[index].blue)) {
return CSS_Color_RGB_Table[index].name;
}
index++;
};
return name;
}
/**
* @brief Convert sRGB from CIE xy and brightness
* @param red - return R value of sRGB
* @param green - return G value of sRGB
* @param blue - return B value of sRGB
* @param name - CSS color name from W3C
* @return index 0..color_rgb_count(), where color_rgb_count() is not found.
*/
unsigned color_rgb_from_ascii(uint8_t *red, uint8_t *green, uint8_t *blue,
const char *name)
{
unsigned index = 0;
while (CSS_Color_RGB_Table[index].name) {
if (strcmp(CSS_Color_RGB_Table[index].name, name) == 0) {
if (red) {
*red = CSS_Color_RGB_Table[index].red;
}
if (green) {
*green = CSS_Color_RGB_Table[index].green;
}
if (blue) {
*blue = CSS_Color_RGB_Table[index].blue;
}
break;
}
index++;
};
return index;
}
/**
* @brief Convert sRGB from CIE xy and brightness
* @param red - return R value of sRGB
* @param green - return G value of sRGB
* @param blue - return B value of sRGB
* @return CSS ASCII color name from W3C or NULL if invalid index
*/
const char *color_rgb_from_index(unsigned target_index, uint8_t *red, uint8_t *green, uint8_t *blue)
{
unsigned index = 0;
while (CSS_Color_RGB_Table[index].name) {
if (target_index == index) {
if (red) {
*red = CSS_Color_RGB_Table[index].red;
}
if (green) {
*green = CSS_Color_RGB_Table[index].green;
}
if (blue) {
*blue = CSS_Color_RGB_Table[index].blue;
}
return CSS_Color_RGB_Table[index].name;
}
index++;
};
return NULL;
}
/**
* @brief Gets the number of sRGB names from CSS3 defines in W3C
* @return the number of defined RGB names
*/
unsigned color_rgb_count(void)
{
unsigned count = 0;
while (CSS_Color_RGB_Table[count].name) {
count++;
}
return count;
}
/**
* @brief Return an RGB color from a color temperature in Kelvin.
* @note This is a rough approximation based on the formula
* provided by T. Helland
* http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
*/
void color_rgb_from_temperature(
uint16_t temperature_kelvin,
uint8_t *r, uint8_t *g, uint8_t *b)
{
float red = 0, green = 0, blue = 0;
if (temperature_kelvin < 1000) {
temperature_kelvin = 1000;
} else if (temperature_kelvin > 40000) {
temperature_kelvin = 40000;
}
temperature_kelvin /= 100;
/* Calculate Red */
if (temperature_kelvin <= 66) {
/* Red values below 6600 K are always 255 */
red = 255.0;
} else {
red = temperature_kelvin - 60;
red = 329.698727446 * pow(red, -0.1332047592);
red = clamp(red, 0.0, 255.0);
}
/* Calculate Green */
if (temperature_kelvin <= 66) {
/* Green values below 6600 K */
green = temperature_kelvin;
green = 99.4708025861 * log(green) - 161.1195681661;
} else {
green = temperature_kelvin - 60;
green = 288.1221695283 * pow(green, -0.0755148492);
}
green = clamp(green, 0.0, 255.0);
/* Calculate Blue */
if (temperature_kelvin >= 66) {
/* Blue values above 6600 K */
blue = 255.0;
} else if (temperature_kelvin <= 19) {
/* Blue values below 1900 K */
blue = 0.0;
} else {
blue = temperature_kelvin - 10;
blue = 138.5177312231 * log(blue) - 305.0447927307;
blue = clamp(blue, 0, 255);
}
if (r) {
*r = red;
}
if (g) {
*g = green;
}
if (b) {
*b = blue;
}
}
+44
View File
@@ -0,0 +1,44 @@
/**
* @file
* @author Steve Karg <skarg@users.sourceforge.net>
* @date 2022
* @brief API Color sRGB to CIE xy and brightness
*/
#ifndef COLOR_RGB_H
#define COLOR_RGB_H
#include <stdint.h>
#include <stdbool.h>
#include "bacnet/bacnet_stack_exports.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
void color_rgb_to_xy(uint8_t r, uint8_t g, uint8_t b,
float *x_coordinate, float *y_coordinate, uint8_t *brightness);
BACNET_STACK_EXPORT
void color_rgb_from_xy(uint8_t *red, uint8_t *green, uint8_t *blue,
float x_coordinate, float y_coordinate, uint8_t brightness);
BACNET_STACK_EXPORT
const char * color_rgb_to_ascii(uint8_t red, uint8_t green, uint8_t blue);
BACNET_STACK_EXPORT
unsigned color_rgb_from_ascii(uint8_t *red, uint8_t *green, uint8_t *blue,
const char *name);
BACNET_STACK_EXPORT
const char *color_rgb_from_index(unsigned target_index, uint8_t *red,
uint8_t *green, uint8_t *blue);
BACNET_STACK_EXPORT
unsigned color_rgb_count(void);
BACNET_STACK_EXPORT
void color_rgb_from_temperature(
uint16_t temperature_kelvin,
uint8_t *r, uint8_t *g, uint8_t *b);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif