diff --git a/src/dmx/hal.c b/src/dmx/hal.c index 51ab4353f..82338dcc3 100644 --- a/src/dmx/hal.c +++ b/src/dmx/hal.c @@ -459,10 +459,12 @@ bool dmx_driver_install(dmx_port_t dmx_num, const dmx_config_t *config, rdm_register_software_version_label(dmx_num, config->software_version_label, NULL, NULL); rdm_register_identify_device(dmx_num, rdm_default_identify_cb, NULL); + rdm_register_dmx_personality(dmx_num, NULL, NULL); + rdm_register_dmx_personality_description(dmx_num, NULL, NULL); + if (device_info.dmx_start_address != DMX_START_ADDRESS_NONE) { rdm_register_dmx_start_address(dmx_num, NULL, NULL); } - // TODO: rdm_register_supported_parameters() } else { dmx_driver_personality_t *dmx = rdm_pd_alloc(dmx_num, pd_size); assert(dmx != NULL); diff --git a/src/rdm/responder.c b/src/rdm/responder.c index 3e8ef3c4e..5700bc785 100644 --- a/src/rdm/responder.c +++ b/src/rdm/responder.c @@ -216,6 +216,109 @@ static int rdm_supported_params_response_cb(dmx_port_t dmx_num, return RDM_RESPONSE_TYPE_ACK; } +static int rdm_personality_description_response_cb(dmx_port_t dmx_num, + const rdm_header_t *header, void *pd, + uint8_t *pdl_out, void *param, + const rdm_pid_description_t *desc, + const char *param_str) +{ + if (header->sub_device != RDM_SUB_DEVICE_ROOT) { + *pdl_out = rdm_pd_emplace_word(pd, RDM_NR_SUB_DEVICE_OUT_OF_RANGE); + return RDM_RESPONSE_TYPE_NACK_REASON; + } + + rdm_device_info_t *di = rdm_pd_find(dmx_num, RDM_PID_DEVICE_INFO); + if(di == NULL) + { + //none of the error codes really fit, thus we just go with unknown pid + *pdl_out = rdm_pd_emplace_word(pd, RDM_NR_UNKNOWN_PID); + return RDM_RESPONSE_TYPE_NACK_REASON; + } + + if(header->cc == RDM_CC_SET_COMMAND) + { + *pdl_out = rdm_pd_emplace_word(pd, RDM_NR_WRITE_PROTECT); + return RDM_RESPONSE_TYPE_NACK_REASON; + } + + if(header->pdl != 1) + { + *pdl_out = rdm_pd_emplace_word(pd, RDM_NR_FORMAT_ERROR); + return RDM_RESPONSE_TYPE_NACK_REASON; + } + + const uint8_t requestedPersonality = *((uint8_t*)pd); + const uint16_t footprint = (uint16_t)dmx_get_footprint(dmx_num, requestedPersonality); + const char* personalityDesc = dmx_get_personality_description(dmx_num, requestedPersonality); + + if(personalityDesc == NULL) + { + *pdl_out = rdm_pd_emplace_word(pd, RDM_NR_DATA_OUT_OF_RANGE); + return RDM_RESPONSE_TYPE_NACK_REASON; + } + + memcpy(pd, &requestedPersonality, 1); + pd++; + rdm_pd_emplace_word(pd, footprint); + pd += 2; + const size_t emplacedBytes = rdm_pd_emplace(pd, "a$", personalityDesc, 32, false); + *pdl_out = 3 + emplacedBytes; + + return RDM_RESPONSE_TYPE_ACK; +} +static int rdm_personality_response_cb(dmx_port_t dmx_num, + const rdm_header_t *header, void *pd, + uint8_t *pdl_out, void *param, + const rdm_pid_description_t *desc, + const char *param_str) +{ + // Return early if the sub-device is out of range + if (header->sub_device != RDM_SUB_DEVICE_ROOT) { + *pdl_out = rdm_pd_emplace_word(pd, RDM_NR_SUB_DEVICE_OUT_OF_RANGE); + return RDM_RESPONSE_TYPE_NACK_REASON; + } + + rdm_device_info_t *di = rdm_pd_find(dmx_num, RDM_PID_DEVICE_INFO); + if(di == NULL) + { + //none of the error codes really fit, thus we just go with unknown pid + *pdl_out = rdm_pd_emplace_word(pd, RDM_NR_UNKNOWN_PID); + return RDM_RESPONSE_TYPE_NACK_REASON; + } + + if(header->cc == RDM_CC_GET_COMMAND) + { + const uint8_t data[] = {di->current_personality, di->personality_count}; + memcpy(pd, data, 2); + *pdl_out = 2; + return RDM_RESPONSE_TYPE_ACK; + } + else if(header->cc == RDM_CC_SET_COMMAND) + { + if(header->pdl != 1) + { + *pdl_out = rdm_pd_emplace_word(pd, RDM_NR_FORMAT_ERROR); + return RDM_RESPONSE_TYPE_NACK_REASON; + } + + const uint8_t requestedPersonality = *((uint8_t*)pd); + if(requestedPersonality >= di->personality_count) + { + *pdl_out = rdm_pd_emplace_word(pd, RDM_NR_DATA_OUT_OF_RANGE); + return RDM_RESPONSE_TYPE_NACK_REASON; + } + + dmx_set_current_personality(dmx_num, requestedPersonality); + //note: we do not need to set it in nvs because that is done in hal.c:dmx_receive() + return RDM_RESPONSE_TYPE_ACK; + } + else + { + *pdl_out = rdm_pd_emplace_word(pd, RDM_NR_DATA_OUT_OF_RANGE); + return RDM_RESPONSE_TYPE_NACK_REASON; + } +} + bool rdm_register_device_info(dmx_port_t dmx_num, rdm_device_info_t *device_info, @@ -388,6 +491,71 @@ bool rdm_register_supported_parameters(dmx_port_t dmx_num, rdm_responder_cb_t cb rdm_supported_params_response_cb, param, NULL, NULL); } + +bool rdm_register_dmx_personality(dmx_port_t dmx_num, rdm_responder_cb_t cb, + void *context){ + DMX_CHECK(dmx_num < DMX_NUM_MAX, false, "dmx_num error"); + DMX_CHECK(dmx_driver_is_installed(dmx_num), false, "driver is not installed"); + + // Current personality is stored within device info + rdm_device_info_t *di = rdm_pd_find(dmx_num, RDM_PID_DEVICE_INFO); + DMX_CHECK(di != NULL, false, "RDM_PID_DEVICE_INFO must be registered first"); + + // Note: The personality is a strange parameter that needs a custom callback + // because in the get case it behaves like two parameters. + // The pd of get is a 2 byte array consisting of the current personality + // and maximum number of personalities. + // The pd of set is byte personality. + // Thus we cannot use the rdm_simple_response_cb. + // + + uint8_t* param = &di->current_personality; + + const rdm_pid_description_t desc = {.pid = RDM_PID_DMX_PERSONALITY, + .pdl_size = 1, + .data_type = RDM_DS_UNSIGNED_BYTE, + .cc = RDM_CC_GET_SET, + .unit = RDM_UNITS_NONE, + .prefix = RDM_PREFIX_NONE, + .min_value = 1, + .max_value = 255, + .default_value = 1, + .description = "DMX Personality"}; + + return rdm_register_parameter(dmx_num, RDM_SUB_DEVICE_ROOT, &desc, "b$", + rdm_personality_response_cb, param, cb, context); +} + + +bool rdm_register_dmx_personality_description(dmx_port_t dmx_num, rdm_responder_cb_t cb, + void *context) +{ + DMX_CHECK(dmx_num < DMX_NUM_MAX, false, "dmx_num error"); + DMX_CHECK(dmx_driver_is_installed(dmx_num), false, "driver is not installed"); + + // Personality is stored within device info + rdm_device_info_t *di = rdm_pd_find(dmx_num, RDM_PID_DEVICE_INFO); + DMX_CHECK(di != NULL, false, "RDM_PID_DEVICE_INFO must be registered first"); + + const rdm_pid_description_t desc = {.pid = RDM_PID_DMX_PERSONALITY_DESCRIPTION, + .pdl_size = 35, // this is the max size, not necessarily the one we send + .data_type = RDM_DS_BIT_FIELD, + .cc = RDM_CC_GET, + .unit = RDM_UNITS_NONE, + .prefix = RDM_PREFIX_NONE, + .min_value = 0, // has no meaning for RDM_DS_BIT_FIELD + .max_value = 0, // has no meaning for RDM_DS_BIT_FIELD + .default_value = 0, // has no meaning for RDM_DS_BIT_FIELD + .description = "DMX Personality Description"}; + + + return rdm_register_parameter(dmx_num, RDM_SUB_DEVICE_ROOT, &desc, NULL, + rdm_personality_description_response_cb, NULL, cb, context); + + +} + + bool rdm_register_dmx_start_address(dmx_port_t dmx_num, rdm_responder_cb_t cb, void *context) { DMX_CHECK(dmx_num < DMX_NUM_MAX, false, "dmx_num error"); diff --git a/src/rdm/responder.h b/src/rdm/responder.h index f324fa230..616f83c58 100644 --- a/src/rdm/responder.h +++ b/src/rdm/responder.h @@ -122,6 +122,11 @@ bool rdm_register_software_version_label(dmx_port_t dmx_num, bool rdm_register_identify_device(dmx_port_t dmx_num, rdm_responder_cb_t cb, void *context); +bool rdm_register_dmx_personality(dmx_port_t dmx_num, rdm_responder_cb_t cb, + void *context); + +bool rdm_register_dmx_personality_description(dmx_port_t dmx_num, rdm_responder_cb_t cb, + void *context); bool rdm_register_supported_parameters(dmx_port_t dmx_num, rdm_responder_cb_t cb, void *context);