Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RDM Discovery Request and Request Updates #33

Merged
merged 7 commits into from
Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 113 additions & 100 deletions src/esp_rdm.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,39 +90,35 @@ size_t rdm_send_disc_response(dmx_port_t dmx_num, size_t preamble_len,
return written;
}

size_t rdm_send_disc_unique_branch(dmx_port_t dmx_num,
rdm_disc_unique_branch_t *params,
rdm_response_t *response, rdm_uid_t *uid) {
rdm_uid_t rdm_send_disc_unique_branch(dmx_port_t dmx_num,
rdm_disc_unique_branch_t *params,
rdm_response_t *response) {
RDM_CHECK(dmx_num < DMX_NUM_MAX, 0, "dmx_num error");
RDM_CHECK(dmx_driver_is_installed(dmx_num), 0, "driver is not installed");
RDM_CHECK(params != NULL, 0, "params is null");
RDM_CHECK(uid != NULL, 0, "uid is null");

// Take mutex so driver values may be accessed
dmx_driver_t *const driver = dmx_driver[dmx_num];
xSemaphoreTakeRecursive(driver->mux, portMAX_DELAY);
dmx_wait_sent(dmx_num, portMAX_DELAY);

// Prepare the RDM message
const uint8_t tn = driver->rdm.tn;
rdm_data_t *rdm = (rdm_data_t *)driver->data.buffer;
rdm_data_t *const restrict rdm = (rdm_data_t *)driver->data.buffer;
size_t written = rdm_encode_uids(&rdm->pd, (rdm_uid_t *)params, 2);
rdm_header_t header = {
.destination_uid = RDM_BROADCAST_ALL_UID,
.source_uid = rdm_get_uid(dmx_num),
.tn = tn,
.port_id = dmx_num + 1,
.message_count = 0,
.sub_device = 0,
.cc = RDM_CC_DISC_COMMAND,
.pid = RDM_PID_DISC_UNIQUE_BRANCH,
.pdl = written,
};
const rdm_header_t header = {.destination_uid = RDM_BROADCAST_ALL_UID,
.source_uid = rdm_get_uid(dmx_num),
.tn = driver->rdm.tn,
.port_id = dmx_num + 1,
.message_count = 0,
.sub_device = RDM_ROOT_DEVICE,
.cc = RDM_CC_DISC_COMMAND,
.pid = RDM_PID_DISC_UNIQUE_BRANCH,
.pdl = written};
written += rdm_encode_header(rdm, &header);
dmx_send(dmx_num, written);

// Wait for a response
size_t num_params = 0;
rdm_uid_t uid = 0;
dmx_packet_t event;
const size_t read = dmx_receive(dmx_num, &event, DMX_TIMEOUT_TICK);
if (!read) {
Expand All @@ -135,10 +131,11 @@ size_t rdm_send_disc_unique_branch(dmx_port_t dmx_num,
// Check the packet for errors
esp_err_t err;
rdm_response_type_t response_type;
if (!rdm_decode_disc_response(driver->data.buffer, uid)) {
size_t num_params;
if (!rdm_decode_disc_response((uint8_t *)rdm, &uid)) {
err = ESP_ERR_INVALID_CRC;
response_type = RDM_RESPONSE_TYPE_NONE;
*uid = 0;
num_params = 0;
} else {
err = ESP_OK;
response_type = RDM_RESPONSE_TYPE_ACK;
Expand All @@ -154,11 +151,11 @@ size_t rdm_send_disc_unique_branch(dmx_port_t dmx_num,
}

xSemaphoreGiveRecursive(driver->mux);
return num_params;
return uid;
}

size_t rdm_send_disc_mute(dmx_port_t dmx_num, rdm_uid_t uid, bool mute,
rdm_response_t *response, rdm_disc_mute_t *params) {
bool rdm_send_disc_mute(dmx_port_t dmx_num, rdm_uid_t uid, bool mute,
rdm_response_t *response, rdm_disc_mute_t *params) {
RDM_CHECK(dmx_num < DMX_NUM_MAX, 0, "dmx_num error");
RDM_CHECK(dmx_driver_is_installed(dmx_num), 0, "driver is not installed");

Expand All @@ -172,17 +169,17 @@ size_t rdm_send_disc_mute(dmx_port_t dmx_num, rdm_uid_t uid, bool mute,

// Write and send the RDM message
const uint8_t tn = driver->rdm.tn;
rdm_data_t *const rdm = (rdm_data_t *)driver->data.buffer;
rdm_header_t header = {.destination_uid = uid,
.source_uid = rdm_get_uid(dmx_num),
.tn = tn,
.port_id = dmx_num + 1,
.message_count = 0,
.sub_device = 0,
.cc = RDM_CC_DISC_COMMAND,
.pid = pid,
.pdl = 0};
size_t written = rdm_encode_header(rdm, &header);
rdm_data_t *const restrict rdm = (rdm_data_t *)driver->data.buffer;
const rdm_header_t req_header = {.destination_uid = uid,
.source_uid = rdm_get_uid(dmx_num),
.tn = tn,
.port_id = dmx_num + 1,
.message_count = 0,
.sub_device = RDM_ROOT_DEVICE,
.cc = RDM_CC_DISC_COMMAND,
.pid = pid,
.pdl = 0};
size_t written = rdm_encode_header(rdm, &req_header);
dmx_send(dmx_num, written);

// Determine if a response is expected
Expand All @@ -200,30 +197,38 @@ size_t rdm_send_disc_mute(dmx_port_t dmx_num, rdm_uid_t uid, bool mute,
} else {
// Check the packet for errors
esp_err_t err;
if (!rdm_decode_header(driver->data.buffer, &header)) {
rdm_header_t resp_header;
if (!rdm_decode_header(rdm, &resp_header)) {
err = ESP_ERR_INVALID_RESPONSE;
} else if (!header.checksum_is_valid) {
} else if (!resp_header.checksum_is_valid) {
err = ESP_ERR_INVALID_CRC;
} else if (resp_header.cc != RDM_CC_DISC_COMMAND_RESPONSE ||
resp_header.pid != pid ||
resp_header.destination_uid != req_header.source_uid ||
resp_header.source_uid != req_header.destination_uid ||
resp_header.tn != tn ||
resp_header.sub_device != RDM_ROOT_DEVICE) {
err = ESP_ERR_INVALID_RESPONSE;
} else {
err = ESP_OK;
}
// TODO: error checking of packet -- check pid, cc

// Decode the response
if (header.response_type == RDM_RESPONSE_TYPE_ACK) {
if (params != NULL) {
rdm_decode_mute(&rdm->pd, params, 1, header.pdl);
// Decode the response
if (resp_header.response_type == RDM_RESPONSE_TYPE_ACK &&
resp_header.pdl >= 2) {
if (params != NULL) {
rdm_decode_mute(&rdm->pd, params, 1, resp_header.pdl);
}
num_params = 1;
} else {
// Discovery commands do not accept any other response type
err = ESP_ERR_INVALID_RESPONSE;
}
num_params = 1;
} else {
// Discovery commands do not accept any other response type
err = ESP_ERR_INVALID_RESPONSE;
}

// Report response back to user
if (response != NULL) {
response->err = err;
response->type = header.response_type;
response->type = resp_header.response_type;
response->num_params = num_params;
}
}
Expand All @@ -237,7 +242,7 @@ size_t rdm_send_disc_mute(dmx_port_t dmx_num, rdm_uid_t uid, bool mute,
}

xSemaphoreGiveRecursive(driver->mux);
return num_params;
return num_params > 0;
}

size_t rdm_discover_with_callback(dmx_port_t dmx_num, rdm_discovery_cb_t cb,
Expand Down Expand Up @@ -268,38 +273,40 @@ size_t rdm_discover_with_callback(dmx_port_t dmx_num, rdm_discovery_cb_t cb,
stack[0].lower_bound = 0;
stack[0].upper_bound = RDM_MAX_UID;

rdm_disc_mute_t mute; // Mute parameters returned from devices.
rdm_response_t response; // Request response information.
bool dev_muted; // Is true if the responding device was muted.
rdm_uid_t uid; // The UID of the responding device.

size_t num_found = 0;
while (stack_size > 0) {
rdm_disc_unique_branch_t *branch = &stack[--stack_size];
size_t attempts = 0;
rdm_response_t response;
rdm_uid_t uid;

if (branch->lower_bound == branch->upper_bound) {
// Can't branch further so attempt to mute the device
uid = branch->lower_bound;
rdm_disc_mute_t mute;
do {
rdm_send_disc_mute(dmx_num, uid, true, &response, &mute);
} while (response.num_params == 0 && ++attempts < 3);
dev_muted = rdm_send_disc_mute(dmx_num, uid, true, &response, &mute);
} while (!dev_muted && ++attempts < 3);

// Attempt to fix possible error where responder is flipping its own UID
if (response.num_params == 0) {
if (!dev_muted) {
uid = bswap64(uid) >> 16; // Flip UID
rdm_send_disc_mute(dmx_num, uid, true, &response, &mute);
dev_muted = rdm_send_disc_mute(dmx_num, uid, true, NULL, &mute);
}

// Call the callback function and report a device has been found
if (response.num_params > 0 && !response.err) {
if (dev_muted && !response.err) {
cb(dmx_num, uid, num_found, &mute, context);
++num_found;
}
} else {
// Search the current branch in the RDM address space
do {
rdm_send_disc_unique_branch(dmx_num, branch, &response, &uid);
} while (response.num_params == 0 && ++attempts < 3);
if (response.num_params > 0) {
uid = rdm_send_disc_unique_branch(dmx_num, branch, &response);
} while (uid == 0 && ++attempts < 3);
if (uid != 0) {
bool devices_remaining = true;

#ifndef CONFIG_RDM_DEBUG_DEVICE_DISCOVERY
Expand All @@ -313,23 +320,22 @@ size_t rdm_discover_with_callback(dmx_port_t dmx_num, rdm_discovery_cb_t cb,
for (int quick_finds = 0; quick_finds < 3; ++quick_finds) {
// Attempt to mute the device
attempts = 0;
rdm_disc_mute_t mute;
do {
rdm_send_disc_mute(dmx_num, uid, true, &response, &mute);
} while (response.num_params == 0 && ++attempts < 3);
dev_muted = rdm_send_disc_mute(dmx_num, uid, true, NULL, &mute);
} while (!dev_muted && ++attempts < 3);

// Call the callback function and report a device has been found
if (response.num_params > 0) {
if (dev_muted) {
cb(dmx_num, uid, num_found, &mute, context);
++num_found;
}

// Check if there are more devices in this branch
attempts = 0;
do {
rdm_send_disc_unique_branch(dmx_num, branch, &response, &uid);
} while (response.num_params == 0 && ++attempts < 3);
if (response.num_params > 0 && response.err) {
uid = rdm_send_disc_unique_branch(dmx_num, branch, &response);
} while (uid == 0 && ++attempts < 3);
if (uid != 0 && response.err) {
// There are more devices in this branch - branch further
devices_remaining = true;
break;
Expand Down Expand Up @@ -414,16 +420,16 @@ static size_t rdm_send_generic_request(
} else {
written = 0;
}
rdm_header_t header = {.destination_uid = uid,
.source_uid = rdm_get_uid(dmx_num),
.tn = tn,
.port_id = dmx_num + 1,
.message_count = 0,
.sub_device = sub_device,
.cc = cc,
.pid = pid,
.pdl = written};
written += rdm_encode_header(rdm, &header);
rdm_header_t req_header = {.destination_uid = uid,
.source_uid = rdm_get_uid(dmx_num),
.tn = tn,
.port_id = dmx_num + 1,
.message_count = 0,
.sub_device = sub_device,
.cc = cc,
.pid = pid,
.pdl = written};
written += rdm_encode_header(rdm, &req_header);
dmx_send(dmx_num, written);

// Receive and decode the RDM response
Expand All @@ -440,55 +446,59 @@ static size_t rdm_send_generic_request(
} else {
// Parse the response to ensure it is valid
esp_err_t err;
if (!rdm_decode_header(driver->data.buffer, &header)) {
rdm_header_t resp_header;
if (!rdm_decode_header(driver->data.buffer, &resp_header)) {
err = ESP_ERR_INVALID_RESPONSE;
} else if (!header.checksum_is_valid) {
} else if (!resp_header.checksum_is_valid) {
err = ESP_ERR_INVALID_CRC;
} else if (header.destination_uid != rdm_get_uid(dmx_num)) {
} else if (resp_header.cc != req_header.cc + 1 ||
resp_header.pid != req_header.pid ||
resp_header.destination_uid != req_header.source_uid ||
resp_header.source_uid != req_header.destination_uid ||
resp_header.sub_device != req_header.sub_device ||
resp_header.tn != req_header.tn) {
err = ESP_ERR_INVALID_RESPONSE;
} else {
err = ESP_OK;
}

// Handle the parameter data
uint32_t response_val;
if (header.cc == cc + 1 && header.pid == pid) {
if (header.response_type == RDM_RESPONSE_TYPE_ACK) {
// Handle the parameter data
uint32_t response_val;
if (resp_header.response_type == RDM_RESPONSE_TYPE_ACK) {
// Decode the parameter data
if (decode) {
// Return the number of params available when response is received
return_val =
decode(&rdm->pd, decode_params, num_decode_params, header.pdl);
return_val = decode(&rdm->pd, decode_params, num_decode_params,
resp_header.pdl);
response_val = return_val;
} else {
// Return true when no response parameters are expected
return_val = true;
response_val = 0;
}
} else if (header.response_type == RDM_RESPONSE_TYPE_ACK_TIMER) {
} else if (resp_header.response_type == RDM_RESPONSE_TYPE_ACK_TIMER) {
// Get the estimated response time and convert it to FreeRTOS ticks
rdm_decode_16bit(&rdm->pd, &response_val, 1, header.pdl);
rdm_decode_16bit(&rdm->pd, &response_val, 1, resp_header.pdl);
response_val = pdMS_TO_TICKS(response_val * 10);
} else if (header.response_type == RDM_RESPONSE_TYPE_NACK_REASON) {
} else if (resp_header.response_type == RDM_RESPONSE_TYPE_NACK_REASON) {
// Report the NACK reason
rdm_decode_16bit(&rdm->pd, &response_val, 1, header.pdl);
} else if (header.response_type == RDM_RESPONSE_TYPE_ACK_OVERFLOW) {
rdm_decode_16bit(&rdm->pd, &response_val, 1, resp_header.pdl);
} else if (resp_header.response_type ==
RDM_RESPONSE_TYPE_ACK_OVERFLOW) {
// TODO: implement overflow support
err = ESP_ERR_NOT_SUPPORTED;
response_val = 0;
} else {
// An unknown response type was received
err = ESP_ERR_INVALID_RESPONSE;
response_val = 0;
}
} else {
// The received CC and PID are invalid
err = ESP_ERR_INVALID_RESPONSE;
}

// Report response back to user
if (response != NULL) {
response->err = err;
response->type = header.response_type;
response->num_params = response_val;
// Report response back to user
if (response != NULL) {
response->err = err;
response->type = resp_header.response_type;
response->num_params = response_val;
}
}
}
} else {
Expand All @@ -509,8 +519,11 @@ size_t rdm_get_supported_parameters(dmx_port_t dmx_num, rdm_uid_t uid,
rdm_response_t *response, rdm_pid_t *pids,
size_t size) {
RDM_CHECK(dmx_num < DMX_NUM_MAX, 0, "dmx_num error");
// TODO: rdm check for valid uid
// TODO: rdm check for valid sub_device
RDM_CHECK(dmx_driver_is_installed(dmx_num), 0, "driver is not installed");
RDM_CHECK(!RDM_UID_IS_BROADCAST(uid), 0, "uid cannot be broadcast");
// TODO: sub_device cannot be broadcast
return rdm_send_generic_request(dmx_num, uid, sub_device, RDM_CC_GET_COMMAND,
RDM_PID_SUPPORTED_PARAMETERS, NULL, NULL, 0,
rdm_decode_16bit, pids, size, response);
Expand Down
Loading