diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8add50a..15b3d8e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,7 @@ add_library(jsd-lib STATIC jsd_el3318.c jsd_el3162.c jsd_el4102.c + jsd_el6001.c jsd_ild1900.c jsd_epd.c ) @@ -33,8 +34,8 @@ target_include_directories( ) find_package(Threads REQUIRED) -target_link_libraries(jsd-lib - PUBLIC soem +target_link_libraries(jsd-lib + PUBLIC soem m PRIVATE Threads::Threads PRIVATE rt ) diff --git a/src/jsd.c b/src/jsd.c index 6063c2b..25ba921 100644 --- a/src/jsd.c +++ b/src/jsd.c @@ -17,6 +17,7 @@ #include "jsd/jsd_el3356.h" #include "jsd/jsd_el3602.h" #include "jsd/jsd_el4102.h" +#include "jsd/jsd_el6001.h" #include "jsd/jsd_epd.h" #include "jsd/jsd_ild1900.h" #include "jsd/jsd_jed0101.h" @@ -475,6 +476,10 @@ bool jsd_init_single_device(jsd_t* self, uint16_t slave_id) { return jsd_el4102_init(self, slave_id); break; } + case JSD_EL6001_PRODUCT_CODE: { + return jsd_el6001_init(self, slave_id); + break; + } case JSD_ILD1900_PRODUCT_CODE: { return jsd_ild1900_init(self, slave_id); break; diff --git a/src/jsd_el6001.c b/src/jsd_el6001.c new file mode 100644 index 0000000..73f4f1b --- /dev/null +++ b/src/jsd_el6001.c @@ -0,0 +1,726 @@ +#include "jsd/jsd_el6001.h" + +#include +#include +#include + +#include "jsd/jsd.h" +#include "jsd/jsd_sdo.h" + +#define HEX_FORMAT "0x%02X" + +const char* jsd_el6001_baud_rate_strings[] = { + [JSD_EL6001_BAUD_RATE_2400] = "2400 bps", + [JSD_EL6001_BAUD_RATE_4800] = "4800 bps", + [JSD_EL6001_BAUD_RATE_9600] = "9600 bps", + [JSD_EL6001_BAUD_RATE_12000] = "12000 bps", + [JSD_EL6001_BAUD_RATE_14400] = "14400 bps", + [JSD_EL6001_BAUD_RATE_19200] = "19200 bps", + [JSD_EL6001_BAUD_RATE_38400] = "38400 bps", + [JSD_EL6001_BAUD_RATE_57600] = "57600 bps", + [JSD_EL6001_BAUD_RATE_115200] = "115200 bps", +}; + +// Print functions +static void jsd_el6001_print_controlword(const char* prefix, uint16_t controlword, uint16_t slave_id) { + uint8_t output_length = controlword >> JSD_EL6001_CONTROLWORD_OUTPUT_LENGTH_0; + + MSG( + "EL6001 ID %d: %s("HEX_FORMAT"), %s%s%s%s%d output bytes", + slave_id, + prefix, + controlword, + (controlword & (1 << JSD_EL6001_CONTROLWORD_TRANSMIT_REQUEST)) ? "Transmit Request | " : "", + (controlword & (1 << JSD_EL6001_CONTROLWORD_RECEIVE_ACCEPTED)) ? "Receive Accepted | " : "", + (controlword & (1 << JSD_EL6001_CONTROLWORD_INIT_REQUEST)) ? "Init Request | " : "", + (controlword & (1 << JSD_EL6001_CONTROLWORD_SEND_CONTINUOUS)) ? "Send Continuous | " : "", + output_length); +} + +static char* jsd_el6001_sms_to_string(jsd_el6001_sms_t sms) { + switch (sms) + { + case JSD_EL6001_SMS_INITING: return "INITING_SERIAL_COMMUNICATION"; + case JSD_EL6001_SMS_WAITING_FOR_TERMINAL_TO_INIT: return "WAITING_FOR_INIT_TO_COMPLETE"; + case JSD_EL6001_SMS_READY_FOR_SERIAL_COMMUNICATION: return "READY_FOR_SERIAL_COMMUNICATION"; + + default: assert(0); break; + } +} + +static char* jsd_el6001_transmit_sms_to_string(jsd_el6001_transmit_sms_t sms) { + switch (sms) + { + case JSD_EL6001_TRANSMIT_SMS_WAITING_FOR_TRANSMIT_REQUEST_FROM_USER: return "WAITING_FOR_TRANSMIT_REQUEST_FROM_USER"; + case JSD_EL6001_TRANSMIT_SMS_WAITING_FOR_TRANSMIT_CONFIRMATION: return "WAITING_FOR_TRANSMIT_CONFIRMATION_FROM_TERMINAL"; + + default: assert(0); break; + } +} + +// Controlwords +static void jsd_el6001_set_controlword(jsd_t* self, uint16_t slave_id, uint16_t control_word) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + + jsd_el6001_print_controlword("Setting controlword of: ", control_word, slave_id); + self->slave_states[slave_id].el6001.controlword_user = control_word; +} + +static uint16_t jsd_el6001_set_controlword_bit(const jsd_t* self, uint16_t slave_id, int bit) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + assert(bit >= 0); + assert(bit < JSD_EL6001_NUM_CONTROLWORD_BITS); + + return self->slave_states[slave_id].el6001.controlword_user | (1 << bit); +} + +static uint16_t jsd_el6001_clear_controlword_bit(const jsd_t* self, uint16_t slave_id, int bit) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + assert(bit >= 0); + assert(bit < JSD_EL6001_NUM_CONTROLWORD_BITS); + + return self->slave_states[slave_id].el6001.controlword_user & ~(1 << bit); +} + +static uint16_t jsd_el6001_toggle_controlword_bit(const jsd_t* self, uint16_t slave_id, int bit) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + assert(bit >= 0); + assert(bit < JSD_EL6001_NUM_CONTROLWORD_BITS); + + return self->slave_states[slave_id].el6001.controlword_user ^ (1 << bit); +} + +// Statuswords +static bool jsd_el6001_statusword_bit_is_set(const jsd_t* self, uint16_t slave_id, int bit) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + assert(bit >= 0); + assert(bit < JSD_EL6001_NUM_STATUSWORD_BITS); + + return self->slave_states[slave_id].el6001.statusword & (1 << bit); +} + +static bool jsd_el6001_statusword_bit_went_high(const jsd_t* self, uint16_t slave_id, int bit) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + assert(bit >= 0); + assert(bit < JSD_EL6001_NUM_STATUSWORD_BITS); + + return( !(self->slave_states[slave_id].el6001.statusword_last & (1 << bit)) + && (self->slave_states[slave_id].el6001.statusword & (1 << bit))); +} + +static bool jsd_el6001_statusword_bit_was_toggled(const jsd_t* self, uint16_t slave_id, int bit) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + assert(bit >= 0); + assert(bit < JSD_EL6001_NUM_STATUSWORD_BITS); + + return( (self->slave_states[slave_id].el6001.statusword_last & (1 << bit)) + != (self->slave_states[slave_id].el6001.statusword & (1 << bit)) ); +} + +// Fault check +static int jsd_el6001_check_for_faults(jsd_t* self, uint16_t slave_id) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + + jsd_el6001_private_state_t* state = &self->slave_states[slave_id].el6001; + + if (state->statusword != state->statusword_last) + { + if (jsd_el6001_statusword_bit_is_set(self, slave_id, JSD_EL6001_STATUSWORD_FIFO_BUFFER_FULL)) + { + ERROR("EL6001[%d]: FIFO buffer is full", slave_id); + } + + if (jsd_el6001_statusword_bit_is_set(self, slave_id, JSD_EL6001_STATUSWORD_PARITY_ERROR)) + { + ERROR("EL6001[%d]: Parity error", slave_id); + } + + if (jsd_el6001_statusword_bit_is_set(self, slave_id, JSD_EL6001_STATUSWORD_FRAMING_ERROR)) + { + ERROR("EL6001[%d]: Framing error", slave_id); + } + + if (jsd_el6001_statusword_bit_is_set(self, slave_id, JSD_EL6001_STATUSWORD_OVERRUN_ERROR)) + { + ERROR("EL6001[%d]: Overrun error", slave_id); + } + } + + return 0; +} + +// State Machine States +static int jsd_el6001_transition_sms(jsd_el6001_sms_t sms, jsd_el6001_sms_t* sms_ptr) { + assert(sms_ptr != NULL); + + *sms_ptr = sms; + MSG_DEBUG("Transitioning to %s state", jsd_el6001_sms_to_string(sms)); + + return 0; +} + +static int jsd_el6001_transition_transmit_sms(jsd_el6001_transmit_sms_t sms, jsd_el6001_transmit_sms_t* sms_ptr) { + assert(sms_ptr != NULL); + + *sms_ptr = sms; + MSG_DEBUG("Transitioning to %s state", jsd_el6001_transmit_sms_to_string(sms)); + + return 0; +} + +static int8_t jsd_el6001_compute_checksum(uint8_t data[], int num_bytes) { + int i; + int8_t checksum = 0; + + // sum each individual byte + for (i = 0; i < num_bytes; ++i) + { + checksum += data[i]; + } + + return checksum; +} + +//---------------------------------------------------------------------------// +// RECEIVE +//---------------------------------------------------------------------------// + +static int jsd_el6001_receive_data(jsd_t* self, uint16_t slave_id) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + + jsd_el6001_private_state_t* state = &self->slave_states[slave_id].el6001; + + int i; + int num_bytes_received = 0; + int8_t expected_checksum; + int8_t checksum; + bool ok = true; + + state->checksum_failed = false; + + // If terminal requests computer to receive data (ie. RECEIVE_REQUEST bit was toggled), indicating there's + // newly received data, read the data bytes + if (jsd_el6001_statusword_bit_was_toggled(self, slave_id, JSD_EL6001_STATUSWORD_RECEIVE_REQUEST)) + { + MSG("EL6001[%d]: New data arrived at the terminal", slave_id); + state->received_all_persistent_bytes = false; + + // Get number of data bytes received from the status word + num_bytes_received = (state->statusword & JSD_EL6001_STATUSWORD_INPUT_LENGTH_BIT_MASK) >> JSD_EL6001_STATUSWORD_INPUT_LENGTH_0; + MSG_DEBUG("EL6001[%d]: Terminal received %d bytes, waiting to accept reception from controller", slave_id, num_bytes_received); + + // assert number of bytes received is not above terminal capacity + if(num_bytes_received > JSD_EL6001_NUM_DATA_BYTES) + { + ERROR("Terminal received more data than can handle"); + assert(0); + } + + if (!state->received_first_byte_of_packet) + { + state->num_persistent_bytes_received = 0; + } + + // Need to prevent IOOB on the persistent received bytes + if(state->num_persistent_bytes_received + num_bytes_received > JSD_EL6001_MAX_NUM_DATA_BYTES) + { + ERROR("EL6001[%d]: Number of received bytes larger than persistence window. " + "num_persistent_bytes_received = %d. " + "num_bytes_received = %d. " + "JSD_EL6001_MAX_NUM_DATA_BYTES = %d. ", + slave_id, + state->num_persistent_bytes_received, + num_bytes_received, + JSD_EL6001_MAX_NUM_DATA_BYTES); + + state->num_persistent_bytes_received = 0; + state->received_first_byte_of_packet = false; + ++state->read_errors; + } + + // Store the received data bytes + for (i = 0; i < num_bytes_received; ++i) + { + //state->received_bytes[] : Data is already populated while read PDO + state->pub.persistent_received_bytes[state->num_persistent_bytes_received + i] = state->pub.received_bytes[i]; + } + + // increment number of persistent bytes by number received on this iteration + state->num_persistent_bytes_received += num_bytes_received; + + // if first byte of persistent receipt wasn't received yet, set number of + // bytes to receive from the first byte of the message + if (!state->received_first_byte_of_packet) + { + MSG_DEBUG("EL6001[%d]: Received the first byte of a data packet", slave_id); + if(state->use_first_byte_as_packet_length){ + /// Always set number of expected bytes based on the first byte of the packet + state->expected_num_bytes_to_receive = ( + 1 /*num_bytes byte*/ + + state->pub.received_bytes[0] + + 1 /*checksum byte*/); + } + else{ + state->expected_num_bytes_to_receive = num_bytes_received; + } + + MSG_DEBUG("EL6001[%d]: Expecting %d byte of data packet", slave_id, state->expected_num_bytes_to_receive); + state->received_first_byte_of_packet = true; + } + + // If there's a more than one whole expected packet, drop the earlier ones. Increment the error counter. + if (state->num_persistent_bytes_received > 2 * state->expected_num_bytes_to_receive) + { + int packets_to_drop = floor(state->num_persistent_bytes_received / state->expected_num_bytes_to_receive) - 1; + int bytes_to_drop = packets_to_drop * state->expected_num_bytes_to_receive; + + ERROR( + "EL6001 id %d: " + "Received %d persistent bytes, but expected_num_bytes_to_receive = %d. " + "Dropping %d stale packets (%d bytes).", + slave_id, + state->num_persistent_bytes_received, + state->expected_num_bytes_to_receive, + packets_to_drop, + bytes_to_drop); + + ++state->read_errors; + + // Dropping all packets before the latest whole packet + // Shift the persistent bytes over by bytes_to_drop + for (i = 0; i < (state->num_persistent_bytes_received - bytes_to_drop); ++i) + { + state->pub.persistent_received_bytes[i] = state->pub.persistent_received_bytes[i + bytes_to_drop]; + } + state->num_persistent_bytes_received -= bytes_to_drop; + } + + // If we received one whole packet, parse it. Checksum is last byte + if (state->num_persistent_bytes_received >= state->expected_num_bytes_to_receive) + { + MSG_DEBUG("EL6001 id %d: Received all expected %d bytes of data packet", slave_id, state->expected_num_bytes_to_receive); + + if(state->use_last_byte_as_checksum){ + expected_checksum = state->pub.persistent_received_bytes[state->expected_num_bytes_to_receive - 1]; + checksum = jsd_el6001_compute_checksum( + state->pub.persistent_received_bytes, + state->expected_num_bytes_to_receive - 1/*checksum*/); + if (expected_checksum != checksum) + { + ERROR("EL6001 id %d: Checksum invalid. Expected: %d, Computed: %d", slave_id, expected_checksum, checksum); + state->checksum_failed = true; + ++state->read_errors; + } + } + + // No Partial Packet + if (state->num_persistent_bytes_received == state->expected_num_bytes_to_receive) + { + // Received all expected bytes without errors. User must use this + // before the next loop + state->received_all_persistent_bytes = true; + + // reset first byte check + state->received_first_byte_of_packet = false; + } + else + { + // Copy partial packet to front of queue + ERROR( + "EL6001 id %d: " + "Full and partial packet received in one cycle. " + "Received %d persistent bytes, but expected num bytes to receive " + "is %d.", + slave_id, + state->num_persistent_bytes_received, + state->expected_num_bytes_to_receive); + + ++state->read_errors; + + state->num_persistent_bytes_received -= state->expected_num_bytes_to_receive; + for (i = 0; i < state->num_persistent_bytes_received; ++i) + { + state->pub.persistent_received_bytes[i] = + state->pub.persistent_received_bytes[i + state->expected_num_bytes_to_receive]; + } + } + } + + // Tell terminal that data was received, so that new data can be received from the terminal + jsd_el6001_set_controlword(self, slave_id, jsd_el6001_toggle_controlword_bit(self, slave_id, JSD_EL6001_CONTROLWORD_RECEIVE_ACCEPTED)); + + MSG_DEBUG("EL6001[%d]: Data transferred with num bytes %d", slave_id, num_bytes_received); + + // Printout received data + for(int i=0; i < num_bytes_received; i++){ + MSG_DEBUG("EL6001[%d]: Received data_in[%d]: %d", slave_id, i, state->pub.received_bytes[i]); + } + } + + // Set for user to know number of bytes received + state->pub.num_bytes_received = num_bytes_received; + + return ok ? 0 : -1; +} + +//---------------------------------------------------------------------------// +// TRANSMIT +//---------------------------------------------------------------------------// + +static int jsd_el6001_transmit_data(jsd_t *self, uint16_t slave_id) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + + jsd_el6001_private_state_t* state = &self->slave_states[slave_id].el6001; + + switch (state->transmit_state) + { + case JSD_EL6001_TRANSMIT_SMS_WAITING_FOR_TRANSMIT_REQUEST_FROM_USER: + if (state->user_requests_to_transmit_data) + { + MSG_DEBUG("Confirmed user request to transmit data, setting controlword to transmit request"); + // Tell terminal that data is available to transmit and then transition + jsd_el6001_set_controlword(self, slave_id, jsd_el6001_toggle_controlword_bit(self, slave_id, JSD_EL6001_CONTROLWORD_TRANSMIT_REQUEST)); + jsd_el6001_transition_transmit_sms(JSD_EL6001_TRANSMIT_SMS_WAITING_FOR_TRANSMIT_CONFIRMATION, &state->transmit_state); + + state->timer_start_sec = jsd_time_get_mono_time_sec(); + + // Reset user transmit variable + state->user_requests_to_transmit_data = false; + } + break; + + case JSD_EL6001_TRANSMIT_SMS_WAITING_FOR_TRANSMIT_CONFIRMATION: + { + double time_since_last_transmit = jsd_time_get_mono_time_sec() - state->timer_start_sec; + if (state->timeout_active && (time_since_last_transmit > state->timeout_sec)) + { + ERROR("EL6001 id %d: Transmit request timed out after %f seconds while waiting for transmit confirmation.", slave_id, time_since_last_transmit); + jsd_el6001_transition_transmit_sms(JSD_EL6001_TRANSMIT_SMS_WAITING_FOR_TRANSMIT_REQUEST_FROM_USER, &state->transmit_state); + } + else if (jsd_el6001_statusword_bit_was_toggled(self, slave_id, JSD_EL6001_STATUSWORD_TRANSMIT_ACCEPTED)) + { + // Beckhoff terminal transmitted data. + MSG_DEBUG("Transmit request accepted, data transmit confirmed"); + + if (state->pub.user_requests_to_transmit_data_persistently){ + // Start timeout timer + state->timer_start_sec = jsd_time_get_mono_time_sec(); + // Request another transmit by toggling transmit_request + jsd_el6001_set_controlword(self, slave_id, jsd_el6001_toggle_controlword_bit(self, slave_id, JSD_EL6001_CONTROLWORD_TRANSMIT_REQUEST)); + } + else if(!state->pub.user_requests_to_transmit_data_persistently){ + // Go back to waiting for another transmit request + jsd_el6001_set_controlword(self, slave_id, self->slave_states[slave_id].el6001.controlword_user & 0x00FF); //clear data_out_length + jsd_el6001_set_controlword(self, slave_id, jsd_el6001_clear_controlword_bit(self, slave_id, JSD_EL6001_CONTROLWORD_TRANSMIT_REQUEST)); + jsd_el6001_transition_transmit_sms(JSD_EL6001_TRANSMIT_SMS_WAITING_FOR_TRANSMIT_REQUEST_FROM_USER, &state->transmit_state); + } + + //Increment the successful transmit counter + if (state->autoincrement_byte >= 0) + { + jsd_el6001_set_transmit_data_8bits(self, slave_id, state->autoincrement_byte, state->pub.transmit_bytes[state->autoincrement_byte] + 1); + } + } + } + break; + + default: + assert(0); + break; + } + + return 0; +} + +/**************************************************** + * Public functions + ****************************************************/ + +const jsd_el6001_private_state_t* jsd_el6001_get_state(jsd_t* self, uint16_t slave_id) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == + JSD_EL6001_PRODUCT_CODE); + + return &self->slave_states[slave_id].el6001; +} + +void jsd_el6001_read(jsd_t* self, uint16_t slave_id) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + + jsd_el6001_private_state_t* state = &self->slave_states[slave_id].el6001; + + jsd_el6001_txpdo_data_t* txpdo = + (jsd_el6001_txpdo_data_t*)self->ecx_context.slavelist[slave_id].inputs; + + state->statusword_last = state->statusword; + state->statusword = txpdo->statusword; + + // TODO: check whether we can receive packed data as array + for(int i = 0; i < JSD_EL6001_NUM_DATA_BYTES; i ++){ + state->pub.received_bytes[i] = txpdo->data_in[i]; + } +} + +void jsd_el6001_write_PDO_data(jsd_t* self, uint16_t slave_id) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == + JSD_EL6001_PRODUCT_CODE); + + jsd_el6001_private_state_t* state = &self->slave_states[slave_id].el6001; + + jsd_el6001_rxpdo_t* rxpdo = + (jsd_el6001_rxpdo_t*)self->ecx_context.slavelist[slave_id].outputs; + + state->controlword_terminal_last = state->controlword_terminal; + + + if(state->controlword_user_last != state->controlword_user){ + MSG_DEBUG("Writing a controlword as it has been updated"); + rxpdo->controlword = state->controlword_user; + state->controlword_user_last = state->controlword_user; + } + + if(state->controlword_user & (1 << JSD_EL6001_CONTROLWORD_TRANSMIT_REQUEST)){ + MSG_DEBUG("User request to transmit, populating data_out stream"); + uint8_t output_length = rxpdo->controlword >> JSD_EL6001_CONTROLWORD_OUTPUT_LENGTH_0; + for(uint8_t i = 0; i < output_length; i++){ + rxpdo->data_out[i] = state->pub.transmit_bytes[i]; + } + } +} + +void jsd_el6001_process(jsd_t* self, uint16_t slave_id) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == + JSD_EL6001_PRODUCT_CODE); + + jsd_el6001_private_state_t* state = &self->slave_states[slave_id].el6001; + + // check for faults in statuswords + jsd_el6001_check_for_faults(self, slave_id); + + // Main state machine + switch (state->sms){ + case JSD_EL6001_SMS_INITING: + { + // Request Beckhoff terminal to initialize + jsd_el6001_set_controlword(self, slave_id, jsd_el6001_set_controlword_bit(self, slave_id, JSD_EL6001_CONTROLWORD_INIT_REQUEST)); + // jsd_el6001_write_controlword(self, slave_id); + jsd_el6001_transition_sms(JSD_EL6001_SMS_WAITING_FOR_TERMINAL_TO_INIT, &state->sms); + } + break; + + case JSD_EL6001_SMS_WAITING_FOR_TERMINAL_TO_INIT: // Waiting for Beckhoff terminal to initialize + { + if (jsd_el6001_statusword_bit_went_high(self, slave_id, JSD_EL6001_STATUSWORD_INIT_ACCEPTED)) + { + // Clear init request in order to make terminal ready for serial communication + jsd_el6001_set_controlword(self, slave_id, jsd_el6001_clear_controlword_bit(self, slave_id, JSD_EL6001_CONTROLWORD_INIT_REQUEST)); + // jsd_el6001_write_controlword(self, slave_id); + jsd_el6001_transition_sms(JSD_EL6001_SMS_READY_FOR_SERIAL_COMMUNICATION, &state->sms); + } + } + break; + + case JSD_EL6001_SMS_READY_FOR_SERIAL_COMMUNICATION: + { + // Normal operation state machine state + // receive serial data + if (jsd_el6001_receive_data(self, slave_id) != 0) + { + assert(0); + return; + } + + // transmit user serial data + jsd_el6001_transmit_data(self, slave_id); + } + break; + + default: + assert(0); + break; + } + + // Write slave state into RxPDO + jsd_el6001_write_PDO_data(self, slave_id); +} + +int jsd_el6001_set_transmit_data_8bits(jsd_t* self, uint16_t slave_id, int byte, uint8_t value) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + assert(byte < JSD_EL6001_NUM_DATA_BYTES); + assert(byte >= 0); + + self->slave_states[slave_id].el6001.pub.transmit_bytes[byte] = value; + + return 0; +} + +int jsd_el6001_set_transmit_data_payload(jsd_t* self, uint16_t slave_id, uint8_t* data_in, uint8_t data_len) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + assert(data_len < JSD_EL6001_NUM_DATA_BYTES); + + memcpy(self->slave_states[slave_id].el6001.pub.transmit_bytes, data_in, data_len*sizeof(uint8_t)); + + return 0; +} + +int jsd_el6001_set_baud_rate(jsd_t* self, uint16_t slave_id, jsd_el6001_baud_rate_t baud_rate) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + assert( + (baud_rate == JSD_EL6001_BAUD_RATE_2400) + || (baud_rate == JSD_EL6001_BAUD_RATE_4800) + || (baud_rate == JSD_EL6001_BAUD_RATE_9600) + || (baud_rate == JSD_EL6001_BAUD_RATE_19200) + || (baud_rate == JSD_EL6001_BAUD_RATE_38400) + || (baud_rate == JSD_EL6001_BAUD_RATE_57600) + || (baud_rate == JSD_EL6001_BAUD_RATE_115200) + || (baud_rate == JSD_EL6001_BAUD_RATE_12000) + || (baud_rate == JSD_EL6001_BAUD_RATE_14400)); + + self->slave_states[slave_id].el6001.pub.baud_rate = baud_rate; + + return 0; +} + +int jsd_el6001_request_transmit_data(jsd_t* self, uint16_t slave_id, int num_bytes_to_transmit) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + + // Set number of output bytes by setting bits 8-15 of the control word to that number + // Write the controlword so the terminal know number of bytes to send + MSG("Clearing out any previous data output length"); + jsd_el6001_set_controlword(self, slave_id, self->slave_states[slave_id].el6001.controlword_user & 0x00FF); + jsd_el6001_set_controlword(self, slave_id, + self->slave_states[slave_id].el6001.controlword_user | (num_bytes_to_transmit << JSD_EL6001_CONTROLWORD_OUTPUT_LENGTH_0)); + + // Set bool to advance transmit state machine + self->slave_states[slave_id].el6001.user_requests_to_transmit_data = true; + + return 0; +} + +int jsd_el6001_set_persistent_transmit_data(jsd_t* self, uint16_t slave_id, bool is_persistent) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + + // Set bool to advance transmit state machine + self->slave_states[slave_id].el6001.pub.user_requests_to_transmit_data_persistently = is_persistent; + MSG("Setting transmit data as persistent: %s", is_persistent? "yes" : "no"); + + return 0; +} + +int jsd_el6001_read_received_data(jsd_t* self, uint16_t slave_id, uint8_t* read_buffer){ + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + + if(self->slave_states[slave_id].el6001.pub.num_bytes_received > 0){ + memcpy(read_buffer, self->slave_states[slave_id].el6001.pub.received_bytes, self->slave_states[slave_id].el6001.pub.num_bytes_received); + } + else{ + MSG("There is no data received at the terminal"); + return 1; + } + + return 0; +} + +/**************************************************** + * Private functions + ****************************************************/ + +bool jsd_el6001_init(jsd_t* self, uint16_t slave_id) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == + JSD_EL6001_PRODUCT_CODE); + assert(self->ecx_context.slavelist[slave_id].eep_man == + JSD_BECKHOFF_VENDOR_ID); + + ec_slavet* slaves = self->ecx_context.slavelist; + ec_slavet* slave = &slaves[slave_id]; + + slave->PO2SOconfigx = jsd_el6001_PO2SO_config; + + // Initialize main state machine + self->slave_states[slave_id].el6001.sms = JSD_EL6001_SMS_INITING; // TODO: This used to be done in configure_sdos + + // Initialize transmit state machine + self->slave_states[slave_id].el6001.transmit_state = JSD_EL6001_TRANSMIT_SMS_WAITING_FOR_TRANSMIT_REQUEST_FROM_USER; + + // Initialize timeout timer + self->slave_states[slave_id].el6001.timer_start_sec = jsd_time_get_mono_time_sec(); + + // Initialize packet config TODO: Link with fastcat config + self->slave_states[slave_id].el6001.use_first_byte_as_packet_length = false; + self->slave_states[slave_id].el6001.use_last_byte_as_checksum = false; + + // Initialize the autoincrement byte to -1 (none, or unknown) + self->slave_states[slave_id].el6001.autoincrement_byte = -1; + + return true; +} + +int jsd_el6001_PO2SO_config(ecx_contextt* ecx_context, uint16_t slave_id) { + assert(ecx_context); + assert(ecx_context->slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + + // Since this function prototype is forced by SOEM, we have embedded a + // reference to jsd.slave_configs within th ecx_context and extract it here + jsd_slave_config_t* slave_configs = + (jsd_slave_config_t*)ecx_context->userdata; + + jsd_slave_config_t* config = &slave_configs[slave_id]; + + // Reset to factory default + uint32_t reset_word = JSD_BECKHOFF_RESET_WORD; + if (!jsd_sdo_set_param_blocking(ecx_context, slave_id, JSD_BECKHOFF_RESET_SDO, + JSD_BECKHOFF_RESET_SUBIND, JSD_SDO_DATA_U32, + &reset_word)) { + return 0; + } + + MSG("Configuring slave no: %u, SII inferred name: %s", slave_id, + ecx_context->slavelist[slave_id].name); + MSG("\t Configured name: %s", config->name); + + MSG("\t baud rate: %s", jsd_el6001_baud_rate_strings[config->el6001.baud_rate]); + + uint32_t sdo_channel_index = 0x8000; + + // Set Baud Rate + assert(config->el6001.baud_rate >= JSD_EL6001_BAUD_RATE_2400 && + config->el6001.baud_rate <= JSD_EL6001_BAUD_RATE_14400); + uint8_t baud_rate = config->el6001.baud_rate; + if (!jsd_sdo_set_param_blocking(ecx_context, slave_id, sdo_channel_index, + 0x11, JSD_SDO_DATA_U8, &baud_rate)) { + return 0; + } + + config->PO2SO_success = true; + return 1; +} + +void jsd_el6001_set_autoincrement_byte(jsd_t* self, uint16_t slave_id, int byte) { + assert(self); + assert(self->ecx_context.slavelist[slave_id].eep_id == JSD_EL6001_PRODUCT_CODE); + assert(byte >= 0); + assert(byte <= JSD_EL6001_MAX_NUM_DATA_BYTES); + + self->slave_states[slave_id].el6001.autoincrement_byte = byte; +} + diff --git a/src/jsd_el6001.h b/src/jsd_el6001.h new file mode 100644 index 0000000..4073872 --- /dev/null +++ b/src/jsd_el6001.h @@ -0,0 +1,40 @@ +#ifndef JSD_EL6001_H +#define JSD_EL6001_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "jsd/jsd_el6001_pub.h" + +/** + * @brief Initializes EL6001 + * + * @param self Pointer to JSD context + * @param slave_id Index of device on EtherCAT bus + * @return true on success, false on failure + */ +bool jsd_el6001_init(jsd_t* self, uint16_t slave_id); + +/** + * @brief Configuration function called by SOEM upon a PreOp to SafeOp state + * transition that (re)configures EL6001 device settings + * + * @param ecx_context SOEM context pointer + * @param slave_id index of device on EtherCAT bus + * @return 1 on success, 0 on failure + */ +int jsd_el6001_PO2SO_config(ecx_contextt* ecx_context, uint16_t slave_id); + +/** + * @brief Setting Baud Rate on the terminal + */ +int jsd_el6001_set_baud_rate(jsd_t *self, uint16_t slave_id, jsd_el6001_baud_rate_t baud_rate); + +void jsd_el6001_write_PDO_data(jsd_t* self, uint16_t slave_id); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/jsd_el6001_pub.h b/src/jsd_el6001_pub.h new file mode 100644 index 0000000..21cc5dd --- /dev/null +++ b/src/jsd_el6001_pub.h @@ -0,0 +1,83 @@ +#ifndef JSD_EL6001_PUB_H +#define JSD_EL6001_PUB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "jsd/jsd_pub.h" +#include "jsd/jsd_el6001_types.h" + +/** + * @brief Read the EL6001 device state + * + * Note: this device does not provide PDO feedback on state. This function reads + * back the command sent to the EL6001 device. + * + * @param self Pointer to JSD context + * @param slave_id Slave ID of EL6001 device + * @return Pointer to EL6001 device state + */ +const jsd_el6001_private_state_t* jsd_el6001_get_state(jsd_t* self, uint16_t slave_id); + +/** + * @brief Writes the set commands into the SOEM IOmap and processes asynchronous + * SDO responses + * Reads Process Data Objects + * Writes control word to transition through states + * Writes control word to transmit and receive data + * After this function (in between jsd_process calls: + * User can affect control word number of output bytes + * User can affect transmit request bit + * + * @param self Pointer to JSD context + * @param slave_id Slave ID of EL6001 device + */ +void jsd_el6001_process(jsd_t* self, uint16_t slave_id); + +/** + * @brief Get PDO data and update state data + * + * @param self pointer JSD context + * @param slave_id index of device on EtherCAT bus + */ +void jsd_el6001_read(jsd_t* self, uint16_t slave_id); + +/** + * @brief Set a transmission buffer at specific byte index + * + * @param self pointer JSD context + * @param slave_id index of device on EtherCAT bus + * @param byte index of transmit buffer + * @param value 1byte data to be populated + */ +int jsd_el6001_set_transmit_data_8bits(jsd_t* self, uint16_t slave_id, int byte, uint8_t value); + +/** + * @brief Set a transmission full buffer at specific byte index + * + * @param self pointer JSD context + * @param slave_id index of device on EtherCAT bus + * @param data_in pointer of array to be populated in transmit buffer + * @param data_len length of data buffer to be populated + */ +int jsd_el6001_set_transmit_data_payload(jsd_t* self, uint16_t slave_id, uint8_t* data_in, uint8_t data_len); + +int jsd_el6001_request_transmit_data(jsd_t* self, uint16_t slave_id, int num_bytes_to_transmit); + +int jsd_el6001_set_persistent_transmit_data(jsd_t* self, uint16_t slave_id, bool is_persistent); + +/** + * @brief Readout current data buffer + * + * @param self pointer JSD context + * @param slave_id index of device on EtherCAT bus + * @param read_buffer pointer of array of received data + */ +int jsd_el6001_read_received_data(jsd_t* self, uint16_t slave_id, uint8_t* read_buffer); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/jsd_el6001_types.h b/src/jsd_el6001_types.h new file mode 100644 index 0000000..241d9c2 --- /dev/null +++ b/src/jsd_el6001_types.h @@ -0,0 +1,201 @@ +#ifndef JSD_EL6001_TYPES_H +#define JSD_EL6001_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "jsd/jsd_common_device_types.h" + +#define JSD_EL6001_PRODUCT_CODE (uint32_t)0x17713052 + +#define JSD_EL6001_NUM_CHANNELS 1 + +#define JSD_EL6001_NUM_DATA_BYTES 22 + +#define JSD_EL6001_MAX_NUM_DATA_BYTES 100 + +#define JSD_EL6001_STATUSWORD_INPUT_LENGTH_BIT_MASK 0xFF00 ///< Bits 8-15 define the number of input bytes available for transfer from terminal to computer + +typedef enum +{ + JSD_EL6001_CONTROLWORD_TRANSMIT_REQUEST = 0, ///< Toggle to notify terminal that DataOut bytes contain number of bytes indicated via + ///< OL bits. Terminal acknowledges receipt of data via toggle of SW0 + JSD_EL6001_CONTROLWORD_RECEIVE_ACCEPTED, ///< Controller acknowledges receipt of data via toggle of this bit + JSD_EL6001_CONTROLWORD_INIT_REQUEST, ///< 1: Controller requests terminal to initialize. Terminal acknowledges completion by SW2 + ///< 0: Controller once again request terminal to prepare for serial data exchange + JSD_EL6001_CONTROLWORD_SEND_CONTINUOUS, ///< Rising edge (0->1): Continuous sending of data from the FIFO buffer. + ///< Send buffer is filled (<= 128 bytes) by controller. Buffer content is sent with + ///< rising edge of this bit. Terminal acknowledges the data transfer to controller + ///< through setting of bit SW2. SW2 is cancelled with CW3 + JSD_EL6001_CONTROLWORD_RESERVED_0, + JSD_EL6001_CONTROLWORD_RESERVED_1, + JSD_EL6001_CONTROLWORD_RESERVED_2, + JSD_EL6001_CONTROLWORD_RESERVED_3, + JSD_EL6001_CONTROLWORD_OUTPUT_LENGTH_0, + JSD_EL6001_CONTROLWORD_OUTPUT_LENGTH_1, + JSD_EL6001_CONTROLWORD_OUTPUT_LENGTH_2, + JSD_EL6001_CONTROLWORD_OUTPUT_LENGTH_3, + JSD_EL6001_CONTROLWORD_OUTPUT_LENGTH_4, + JSD_EL6001_CONTROLWORD_OUTPUT_LENGTH_5, + JSD_EL6001_CONTROLWORD_OUTPUT_LENGTH_6, + JSD_EL6001_CONTROLWORD_OUTPUT_LENGTH_7, + JSD_EL6001_NUM_CONTROLWORD_BITS + +} jsd_el6001_controlword_bit_t; + + +typedef enum +{ + JSD_EL6001_STATUSWORD_TRANSMIT_ACCEPTED = 0, ///< Toggle acknowledges receipt of data. Only now new data can be transmitted + ///< from controller to terminal. + JSD_EL6001_STATUSWORD_RECEIVE_REQUEST, ///< Toggle notifies the controller that DataIn bytes contain number of bytes in IL bits + ///< Controller must acknowledge receipt of data in control byte via toggling CW1. + ///< Only then new data can be transferred from terminal to controller. + JSD_EL6001_STATUSWORD_INIT_ACCEPTED, ///< 1: init completed by terminal. 0: terminal ready for serial data exchange. + JSD_EL6001_STATUSWORD_FIFO_BUFFER_FULL, ///< 1: all incoming data will be lost until buffer is not full + JSD_EL6001_STATUSWORD_PARITY_ERROR, ///< A parity error has occurred + JSD_EL6001_STATUSWORD_FRAMING_ERROR, ///< A framing error has occurred + JSD_EL6001_STATUSWORD_OVERRUN_ERROR, ///< An overrun error has occurred + JSD_EL6001_STATUSWORD_RESERVED, + JSD_EL6001_STATUSWORD_INPUT_LENGTH_0, ///< = 8 (BIT08) + JSD_EL6001_STATUSWORD_INPUT_LENGTH_1, + JSD_EL6001_STATUSWORD_INPUT_LENGTH_2, + JSD_EL6001_STATUSWORD_INPUT_LENGTH_3, + JSD_EL6001_STATUSWORD_INPUT_LENGTH_4, + JSD_EL6001_STATUSWORD_INPUT_LENGTH_5, + JSD_EL6001_STATUSWORD_INPUT_LENGTH_6, + JSD_EL6001_STATUSWORD_INPUT_LENGTH_7, ///< = 15 (BIT15) + JSD_EL6001_NUM_STATUSWORD_BITS ///< = 16 + +} jsd_el6001_statusword_bit_t; + +/** + * @brief Set using object 0x8000:11 to one of the following values + */ +typedef enum +{ + JSD_EL6001_BAUD_RATE_2400 = 4, + JSD_EL6001_BAUD_RATE_4800 = 5, + JSD_EL6001_BAUD_RATE_9600 = 6, + JSD_EL6001_BAUD_RATE_19200 = 7, + JSD_EL6001_BAUD_RATE_38400 = 8, + JSD_EL6001_BAUD_RATE_57600 = 9, + JSD_EL6001_BAUD_RATE_115200 = 10, + JSD_EL6001_BAUD_RATE_12000 = 14, + JSD_EL6001_BAUD_RATE_14400 = 15, + +} jsd_el6001_baud_rate_t; + +/** + * @brief Configuration struct for EL6001 device initialization + */ +typedef struct { + jsd_el6001_baud_rate_t baud_rate; + bool use_first_byte_as_packet_length; + bool use_last_byte_as_checksum; +} jsd_el6001_config_t; + +/** + * @brief RxPDO struct used to set device command data in SOEM IOmap + * + * Note: struct order matters and must be packed. + */ +typedef struct __attribute__((__packed__)) { + uint16_t controlword; //0x3003:0x01: 2byte + uint8_t data_out[JSD_EL6001_NUM_DATA_BYTES]; //0x3003:0x02 ~ 0x3003:0x17 +} jsd_el6001_rxpdo_t; + +/** + * @brief TxPDO struct used to read data in SOEM IOmap + * + * Note: struct order matters and must be packed. + */ +typedef struct __attribute__((__packed__)) { + uint16_t statusword; //0x3103:01 + uint8_t data_in[JSD_EL6001_NUM_DATA_BYTES]; //0x3103:0x02 ~ 0x3103:0x17 +} jsd_el6001_txpdo_data_t; + +/** + * @brief State Machine States needed for communication via serial RS232 interface + */ +typedef enum +{ + JSD_EL6001_SMS_INITING = 0, + JSD_EL6001_SMS_WAITING_FOR_TERMINAL_TO_INIT, + JSD_EL6001_SMS_READY_FOR_SERIAL_COMMUNICATION, + JSD_EL6001_SMS_NUM_STATES + +} jsd_el6001_sms_t; + +/** + * @brief Transmit State Machine States needed for communication via serial RS232 interface + */ +typedef enum +{ + JSD_EL6001_TRANSMIT_SMS_WAITING_FOR_TRANSMIT_REQUEST_FROM_USER = 0, + JSD_EL6001_TRANSMIT_SMS_WAITING_FOR_TRANSMIT_CONFIRMATION, + JSD_EL6001_TRANSMIT_SMS_NUM_STATES + +} jsd_el6001_transmit_sms_t; + +/** + * @brief EL6001 state data + */ +typedef struct +{ + int baud_rate; + + uint8_t received_bytes[JSD_EL6001_NUM_DATA_BYTES]; // Data payload buffer received + uint8_t persistent_received_bytes[JSD_EL6001_MAX_NUM_DATA_BYTES]; + int num_bytes_received; + + uint8_t transmit_bytes[JSD_EL6001_NUM_DATA_BYTES]; + + bool user_requests_to_transmit_data_persistently; + +} jsd_el6001_state_t; + +typedef struct +{ + jsd_el6001_state_t pub; + uint16_t statusword; + uint16_t statusword_last; + bool ready; + + uint16_t controlword_terminal; + uint16_t controlword_terminal_last; + uint16_t controlword_user; + uint16_t controlword_user_last; + + int read_errors; + + uint8_t transmit_bytes_prev[JSD_EL6001_NUM_DATA_BYTES]; + bool transmit_data; + bool user_requests_to_transmit_data; + int autoincrement_byte; + + bool timeout_active; + double timeout_sec; + double timer_start_sec; + bool timed_out; + bool checksum_failed; + + // Persistent receiving of bytes + int expected_num_bytes_to_receive; + int num_persistent_bytes_received; + bool received_all_persistent_bytes; + bool use_first_byte_as_packet_length; + bool received_first_byte_of_packet; + bool use_last_byte_as_checksum; + + jsd_el6001_sms_t sms; + jsd_el6001_transmit_sms_t transmit_state; + +} jsd_el6001_private_state_t; + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/jsd_types.h b/src/jsd_types.h index 58737ca..7864ad2 100644 --- a/src/jsd_types.h +++ b/src/jsd_types.h @@ -20,6 +20,7 @@ extern "C" { #include "jsd/jsd_el3356_types.h" #include "jsd/jsd_el3602_types.h" #include "jsd/jsd_el4102_types.h" +#include "jsd/jsd_el6001_types.h" #include "jsd/jsd_epd_types.h" #include "jsd/jsd_ild1900_types.h" #include "jsd/jsd_jed0101_types.h" @@ -45,6 +46,7 @@ typedef struct { jsd_el3318_config_t el3318; jsd_el3162_config_t el3162; jsd_el4102_config_t el4102; + jsd_el6001_config_t el6001; jsd_ild1900_config_t ild1900; jsd_epd_config_t epd; }; @@ -67,6 +69,7 @@ typedef struct { jsd_el3318_state_t el3318; jsd_el3162_state_t el3162; jsd_el4102_state_t el4102; + jsd_el6001_private_state_t el6001; jsd_ild1900_state_t ild1900; jsd_epd_private_state_t epd; }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 09dc4fe..7147bea 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -111,6 +111,21 @@ if(BUILD_JSD_TESTS) jsd_test_utils.c) target_link_libraries(jsd_el4102_test ${jsd_test_libs}) + add_executable(jsd_el6001_test + device/jsd_el6001_test.c + jsd_test_utils.c) + target_link_libraries(jsd_el6001_test ${jsd_test_libs}) + + add_executable(jsd_el6001_comm_test + device/jsd_el6001_comm_test.c + jsd_test_utils.c) + target_link_libraries(jsd_el6001_comm_test ${jsd_test_libs}) + + add_executable(jsd_el6001_loopback_test + device/jsd_el6001_loopback_test.c + jsd_test_utils.c) + target_link_libraries(jsd_el6001_loopback_test ${jsd_test_libs}) + add_executable(jsd_ild1900_test device/jsd_ild1900_test.c jsd_test_utils.c) diff --git a/test/device/jsd_el6001_comm_test.c b/test/device/jsd_el6001_comm_test.c new file mode 100644 index 0000000..f3dbacc --- /dev/null +++ b/test/device/jsd_el6001_comm_test.c @@ -0,0 +1,192 @@ +#include +#include + +// device includes as necessary by application +#include "jsd/jsd_el6001_pub.h" +#include "jsd/jsd_el6001_types.h" +#include "jsd_test_utils.h" + +#define HEX_FORMAT "0x%02X" +#define LOOP_FREQUENCY_HZ (500) // Rate to update ethercat devices +#define USER_LOOP_FREQUENCY_HZ (1) // Rate to send and receive user commands in test +#define NSEC_PER_SEC (1000000000L) +#define LOOP_PERIOD_NS (NSEC_PER_SEC / LOOP_FREQUENCY_HZ) // ns +#define USER_LOOP_RATE_SEC (1.0 / USER_LOOP_FREQUENCY_HZ) // sec +#define PRINT_RATE_HZ (1) // Hz +#define NUM_RESOLVERS (1) +#define MAX_NUM_RESOLVERS (6) +#define ID 0 +#define OP_CODE_SEND_DATA (1) +#define NUM_BYTES_BEFORE_RESOLVERS (2) +#define NUM_BYTES_PER_RESOLVER (3) +#define NUM_RESOLVER_BYTES (NUM_BYTES_PER_RESOLVER * MAX_NUM_RESOLVERS) +#define TOTAL_NUM_BYTES (NUM_BYTES_BEFORE_RESOLVERS + NUM_RESOLVER_BYTES + 1/*checksum*/) + +extern bool quit; +extern FILE* file; +uint8_t slave_id; + +void telemetry_header() { + if (!file) { + return; + } + fprintf(file, "EL6001_controlword_last, EL6001_controlword, "); + fprintf(file, "EL6001_data_out_0, EL6001_data_out_1, "); + fprintf(file, "EL6001_data_out_2, EL6001_data_out_3, "); + fprintf(file, "EL6001_data_out_4, EL6001_data_out_5, "); + fprintf(file, "EL6001_data_out_6, EL6001_data_out_7, "); + fprintf(file, "EL6001_data_out_8, EL6001_data_out_9, "); + fprintf(file, "EL6001_data_out_10, EL6001_data_out_11, "); + fprintf(file, "EL6001_data_out_12, EL6001_data_out_13, "); + fprintf(file, "EL6001_data_out_14, EL6001_data_out_15, "); + fprintf(file, "EL6001_data_out_16, EL6001_data_out_17, "); + fprintf(file, "EL6001_data_out_18, EL6001_data_out_19, "); + fprintf(file, "EL6001_data_out_20, EL6001_data_out_21, "); + + fprintf(file, "EL6001_statusword_last, EL6001_statusword, "); + fprintf(file, "EL6001_data_in_0, EL6001_data_in_1, "); + fprintf(file, "EL6001_data_in_2, EL6001_data_in_3, "); + fprintf(file, "EL6001_data_in_4, EL6001_data_in_5, "); + fprintf(file, "EL6001_data_in_6, EL6001_data_in_7, "); + fprintf(file, "EL6001_data_in_8, EL6001_data_in_9, "); + fprintf(file, "EL6001_data_in_10, EL6001_data_in_11, "); + fprintf(file, "EL6001_data_in_12, EL6001_data_in_13, "); + fprintf(file, "EL6001_data_in_14, EL6001_data_in_15, "); + fprintf(file, "EL6001_data_in_16, EL6001_data_in_17, "); + fprintf(file, "EL6001_data_in_18, EL6001_data_in_19, "); + fprintf(file, "EL6001_data_in_20, EL6001_data_in_21, "); + + fprintf(file, "\n"); +} + +void telemetry_data(void* self) { + assert(self); + + if (!file) { + return; + } + + single_device_server_t* sds = (single_device_server_t*)self; + const jsd_el6001_private_state_t* state = jsd_el6001_get_state(sds->jsd, slave_id); + + fprintf(file, "%u, %u,", state->controlword_user_last, state->controlword_user); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[0], state->pub.transmit_bytes[1]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[2], state->pub.transmit_bytes[3]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[4], state->pub.transmit_bytes[5]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[6], state->pub.transmit_bytes[7]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[8], state->pub.transmit_bytes[9]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[10], state->pub.transmit_bytes[11]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[12], state->pub.transmit_bytes[13]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[14], state->pub.transmit_bytes[15]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[16], state->pub.transmit_bytes[17]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[18], state->pub.transmit_bytes[19]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[20], state->pub.transmit_bytes[21]); + + fprintf(file, "%u, %u,", state->statusword_last, state->statusword); + fprintf(file, "%u, %u,", state->pub.received_bytes[0], state->pub.received_bytes[1]); + fprintf(file, "%u, %u,", state->pub.received_bytes[2], state->pub.received_bytes[3]); + fprintf(file, "%u, %u,", state->pub.received_bytes[4], state->pub.received_bytes[5]); + fprintf(file, "%u, %u,", state->pub.received_bytes[6], state->pub.received_bytes[7]); + fprintf(file, "%u, %u,", state->pub.received_bytes[8], state->pub.received_bytes[9]); + fprintf(file, "%u, %u,", state->pub.received_bytes[10], state->pub.received_bytes[11]); + fprintf(file, "%u, %u,", state->pub.received_bytes[12], state->pub.received_bytes[13]); + fprintf(file, "%u, %u,", state->pub.received_bytes[14], state->pub.received_bytes[15]); + fprintf(file, "%u, %u,", state->pub.received_bytes[16], state->pub.received_bytes[17]); + fprintf(file, "%u, %u,", state->pub.received_bytes[18], state->pub.received_bytes[19]); + fprintf(file, "%u, %u,", state->pub.received_bytes[20], state->pub.received_bytes[21]); + + fprintf(file, "\n"); + fflush(file); +} + +void print_info(void* self) { + assert(self); + + single_device_server_t* sds = (single_device_server_t*)self; + const jsd_el6001_private_state_t* state = jsd_el6001_get_state(sds->jsd, slave_id); + MSG("Controlword_user: %u ", state->controlword_user); + MSG("Statusword: %u ", state->statusword); +} + +void extract_data(void* self) { + single_device_server_t* sds = (single_device_server_t*)self; + jsd_el6001_read(sds->jsd, slave_id); +} + +void command(void* self) { (void)self; }; + + +int main(int argc, char* argv[]) { + if (argc != 4) { + ERROR("Expecting exactly 3 arguments"); + MSG("Usage: jsd_el6001_test "); + MSG("Example: $ jsd_el6001_test eth0 2 1000"); + return 0; + } + + char* ifname = strdup(argv[1]); + slave_id = atoi(argv[2]); + uint32_t loop_freq_hz = atoi(argv[3]); + + MSG("Configuring device %s, using slave %d", ifname, slave_id); + MSG("Using frequency of %i hz", loop_freq_hz); + + single_device_server_t sds; + + sds_set_telemetry_header_callback(&sds, telemetry_header); + sds_set_telemetry_data_callback(&sds, telemetry_data); + sds_set_print_info_callback(&sds, print_info); + sds_set_extract_data_callback(&sds, extract_data); + sds_set_command_callback(&sds, command); + + sds_setup(&sds, loop_freq_hz); + + // set device configuration here + jsd_slave_config_t my_config = {0}; + + my_config.el6001.baud_rate = JSD_EL6001_BAUD_RATE_115200; + + snprintf(my_config.name, JSD_NAME_LEN, "my_el6001_device"); + my_config.configuration_active = true; + my_config.product_code = JSD_EL6001_PRODUCT_CODE; + + jsd_set_slave_config(sds.jsd, slave_id, my_config); + + if (!jsd_init(sds.jsd, ifname, 1)) { + ERROR("Could not init jsd"); + return 0; + } + + jsd_read(sds.jsd, EC_TIMEOUTRET); + + int8_t counter = 2; + // bool quit = false; + const jsd_el6001_private_state_t* state = jsd_el6001_get_state(sds.jsd, slave_id); + + // initiate first transmit and for next time all data is received to prevent idle loop + // transmit data does not get deleted automatically + MSG("Transmitting opcode %d and counter %d", OP_CODE_SEND_DATA, counter); + jsd_el6001_set_transmit_data_8bits(sds.jsd, slave_id, 0/*byte_number*/, 1); + jsd_el6001_set_transmit_data_8bits(sds.jsd, slave_id, 1/*byte_number*/, 0); + jsd_el6001_set_transmit_data_8bits(sds.jsd, slave_id, 2/*byte_number*/, 2); + jsd_el6001_request_transmit_data(sds.jsd, slave_id, 3/*num_bytes*/); + + while(!quit){ + jsd_read(sds.jsd, EC_TIMEOUTRET); + + // read PDOs, updates statusword + jsd_el6001_read(sds.jsd, slave_id); + + MSG("Controlword_user: %u ", state->controlword_user); + MSG("Statusword: %u ", state->statusword); + + jsd_el6001_process(sds.jsd, slave_id); + + jsd_write(sds.jsd); + + // sleep(1.0); + jsd_timer_process(sds.jsd_timer); + } + + return 0; +} diff --git a/test/device/jsd_el6001_loopback_test.c b/test/device/jsd_el6001_loopback_test.c new file mode 100644 index 0000000..338939a --- /dev/null +++ b/test/device/jsd_el6001_loopback_test.c @@ -0,0 +1,207 @@ +#include +#include + +// device includes as necessary by application +#include "jsd/jsd_el6001_pub.h" +#include "jsd/jsd_el6001_types.h" +#include "jsd_test_utils.h" + +#define HEX_FORMAT "0x%02X" +#define LOOP_FREQUENCY_HZ (500) // Rate to update ethercat devices +#define USER_LOOP_FREQUENCY_HZ (1) // Rate to send and receive user commands in test +#define NSEC_PER_SEC (1000000000L) +#define LOOP_PERIOD_NS (NSEC_PER_SEC / LOOP_FREQUENCY_HZ) // ns +#define USER_LOOP_RATE_SEC (1.0 / USER_LOOP_FREQUENCY_HZ) // sec +#define PRINT_RATE_HZ (1) // Hz +#define NUM_RESOLVERS (1) +#define MAX_NUM_RESOLVERS (6) +#define ID 0 +#define OP_CODE_SEND_DATA (1) +#define NUM_BYTES_BEFORE_RESOLVERS (2) +#define NUM_BYTES_PER_RESOLVER (3) +#define NUM_RESOLVER_BYTES (NUM_BYTES_PER_RESOLVER * MAX_NUM_RESOLVERS) +#define TOTAL_NUM_BYTES (NUM_BYTES_BEFORE_RESOLVERS + NUM_RESOLVER_BYTES + 1/*checksum*/) + +extern bool quit; +extern FILE* file; +uint8_t slave_id; + +void telemetry_header() { + if (!file) { + return; + } + fprintf(file, "EL6001_controlword_last, EL6001_controlword, "); + fprintf(file, "EL6001_data_out_0, EL6001_data_out_1, "); + fprintf(file, "EL6001_data_out_2, EL6001_data_out_3, "); + fprintf(file, "EL6001_data_out_4, EL6001_data_out_5, "); + fprintf(file, "EL6001_data_out_6, EL6001_data_out_7, "); + fprintf(file, "EL6001_data_out_8, EL6001_data_out_9, "); + fprintf(file, "EL6001_data_out_10, EL6001_data_out_11, "); + fprintf(file, "EL6001_data_out_12, EL6001_data_out_13, "); + fprintf(file, "EL6001_data_out_14, EL6001_data_out_15, "); + fprintf(file, "EL6001_data_out_16, EL6001_data_out_17, "); + fprintf(file, "EL6001_data_out_18, EL6001_data_out_19, "); + fprintf(file, "EL6001_data_out_20, EL6001_data_out_21, "); + + fprintf(file, "EL6001_statusword_last, EL6001_statusword, "); + fprintf(file, "EL6001_data_in_0, EL6001_data_in_1, "); + fprintf(file, "EL6001_data_in_2, EL6001_data_in_3, "); + fprintf(file, "EL6001_data_in_4, EL6001_data_in_5, "); + fprintf(file, "EL6001_data_in_6, EL6001_data_in_7, "); + fprintf(file, "EL6001_data_in_8, EL6001_data_in_9, "); + fprintf(file, "EL6001_data_in_10, EL6001_data_in_11, "); + fprintf(file, "EL6001_data_in_12, EL6001_data_in_13, "); + fprintf(file, "EL6001_data_in_14, EL6001_data_in_15, "); + fprintf(file, "EL6001_data_in_16, EL6001_data_in_17, "); + fprintf(file, "EL6001_data_in_18, EL6001_data_in_19, "); + fprintf(file, "EL6001_data_in_20, EL6001_data_in_21, "); + + fprintf(file, "\n"); +} + +void telemetry_data(void* self) { + assert(self); + + if (!file) { + return; + } + + single_device_server_t* sds = (single_device_server_t*)self; + const jsd_el6001_private_state_t* state = jsd_el6001_get_state(sds->jsd, slave_id); + + fprintf(file, "%u, %u,", state->controlword_user_last, state->controlword_user); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[0], state->pub.transmit_bytes[1]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[2], state->pub.transmit_bytes[3]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[4], state->pub.transmit_bytes[5]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[6], state->pub.transmit_bytes[7]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[8], state->pub.transmit_bytes[9]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[10], state->pub.transmit_bytes[11]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[12], state->pub.transmit_bytes[13]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[14], state->pub.transmit_bytes[15]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[16], state->pub.transmit_bytes[17]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[18], state->pub.transmit_bytes[19]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[20], state->pub.transmit_bytes[21]); + + fprintf(file, "%u, %u,", state->statusword_last, state->statusword); + fprintf(file, "%u, %u,", state->pub.received_bytes[0], state->pub.received_bytes[1]); + fprintf(file, "%u, %u,", state->pub.received_bytes[2], state->pub.received_bytes[3]); + fprintf(file, "%u, %u,", state->pub.received_bytes[4], state->pub.received_bytes[5]); + fprintf(file, "%u, %u,", state->pub.received_bytes[6], state->pub.received_bytes[7]); + fprintf(file, "%u, %u,", state->pub.received_bytes[8], state->pub.received_bytes[9]); + fprintf(file, "%u, %u,", state->pub.received_bytes[10], state->pub.received_bytes[11]); + fprintf(file, "%u, %u,", state->pub.received_bytes[12], state->pub.received_bytes[13]); + fprintf(file, "%u, %u,", state->pub.received_bytes[14], state->pub.received_bytes[15]); + fprintf(file, "%u, %u,", state->pub.received_bytes[16], state->pub.received_bytes[17]); + fprintf(file, "%u, %u,", state->pub.received_bytes[18], state->pub.received_bytes[19]); + fprintf(file, "%u, %u,", state->pub.received_bytes[20], state->pub.received_bytes[21]); + + fprintf(file, "\n"); + fflush(file); +} + +void print_info(void* self) { + assert(self); + + single_device_server_t* sds = (single_device_server_t*)self; + const jsd_el6001_private_state_t* state = jsd_el6001_get_state(sds->jsd, slave_id); + MSG("Controlword_user: %u ", state->controlword_user); + MSG("Statusword: %u ", state->statusword); +} + +void extract_data(void* self) { + single_device_server_t* sds = (single_device_server_t*)self; + jsd_el6001_read(sds->jsd, slave_id); +} + +void command(void* self) { (void)self; }; + + +int main(int argc, char* argv[]) { + if (argc != 4) { + ERROR("Expecting exactly 3 arguments"); + MSG("Usage: jsd_el6001_test "); + MSG("Example: $ jsd_el6001_test eth0 2 1000"); + return 0; + } + + char* ifname = strdup(argv[1]); + slave_id = atoi(argv[2]); + uint32_t loop_freq_hz = atoi(argv[3]); + + MSG("Configuring device %s, using slave %d", ifname, slave_id); + MSG("Using frequency of %i hz", loop_freq_hz); + + single_device_server_t sds; + + sds_set_telemetry_header_callback(&sds, telemetry_header); + sds_set_telemetry_data_callback(&sds, telemetry_data); + sds_set_print_info_callback(&sds, print_info); + sds_set_extract_data_callback(&sds, extract_data); + sds_set_command_callback(&sds, command); + + sds_setup(&sds, loop_freq_hz); + + // set device configuration here + jsd_slave_config_t my_config = {0}; + + my_config.el6001.baud_rate = JSD_EL6001_BAUD_RATE_115200; + + snprintf(my_config.name, JSD_NAME_LEN, "my_el6001_device"); + my_config.configuration_active = true; + my_config.product_code = JSD_EL6001_PRODUCT_CODE; + + jsd_set_slave_config(sds.jsd, slave_id, my_config); + + if (!jsd_init(sds.jsd, ifname, 1)) { + ERROR("Could not init jsd"); + return 0; + } + + jsd_read(sds.jsd, EC_TIMEOUTRET); + + int8_t counter = 2; + const jsd_el6001_private_state_t* state = jsd_el6001_get_state(sds.jsd, slave_id); + + // initiate first transmit and for next time all data is received to prevent idle loop + // transmit data does not get deleted automatically + + jsd_el6001_set_persistent_transmit_data(sds.jsd, slave_id, 1); + MSG("Transmitting opcode %d and counter %d", OP_CODE_SEND_DATA, counter); + jsd_el6001_set_transmit_data_8bits(sds.jsd, slave_id, 0/*byte_number*/, 1); + jsd_el6001_set_transmit_data_8bits(sds.jsd, slave_id, 1/*byte_number*/, 0); + jsd_el6001_set_transmit_data_8bits(sds.jsd, slave_id, 2/*byte_number*/, 2); + jsd_el6001_request_transmit_data(sds.jsd, slave_id, 3/*num_bytes*/); + + while(!quit){ + jsd_read(sds.jsd, EC_TIMEOUTRET); + + // read PDOs, updates statusword + jsd_el6001_read(sds.jsd, slave_id); + + MSG("Controlword_user: %u ", state->controlword_user); + MSG("Statusword: %u ", state->statusword); + + if(state->pub.num_bytes_received > 0){ + MSG("Received data of size %d", state->pub.num_bytes_received); + for(int i=0; i < state->pub.num_bytes_received; i++){ + MSG("Received data byte: %d value: %d", i, state->pub.received_bytes[i]); + } + } + + jsd_el6001_process(sds.jsd, slave_id); + + jsd_write(sds.jsd); + + // sleep(1.0); + jsd_timer_process(sds.jsd_timer); + } + + // ecat_el6001_set_expected_num_bytes_to_receive(ecat->el6001, ID, TOTAL_NUM_BYTES); + // ecat_el6001_set_persistent_transmit_data(ecat->el6001, ID, 2/*num_bytes*/, true); + // ecat_el6001_set_timeout_sec(ecat->el6001, ID, 1.0, true); + // ecat_el6001_set_autoincrement_byte(ecat->el6001, ID, 1); + + // sds_run(&sds, ifname, "/tmp/jsd_el6001.csv"); + + return 0; +} diff --git a/test/device/jsd_el6001_test.c b/test/device/jsd_el6001_test.c new file mode 100644 index 0000000..1adc2e1 --- /dev/null +++ b/test/device/jsd_el6001_test.c @@ -0,0 +1,142 @@ +#include +#include + +// device includes as necessary by application +#include "jsd/jsd_el6001_pub.h" +#include "jsd/jsd_el6001_types.h" +#include "jsd_test_utils.h" + +extern bool quit; +extern FILE* file; +uint8_t slave_id; + +void telemetry_header() { + if (!file) { + return; + } + fprintf(file, "EL6001_controlword_last, EL6001_controlword, "); + fprintf(file, "EL6001_data_out_0, EL6001_data_out_1, "); + fprintf(file, "EL6001_data_out_2, EL6001_data_out_3, "); + fprintf(file, "EL6001_data_out_4, EL6001_data_out_5, "); + fprintf(file, "EL6001_data_out_6, EL6001_data_out_7, "); + fprintf(file, "EL6001_data_out_8, EL6001_data_out_9, "); + fprintf(file, "EL6001_data_out_10, EL6001_data_out_11, "); + fprintf(file, "EL6001_data_out_12, EL6001_data_out_13, "); + fprintf(file, "EL6001_data_out_14, EL6001_data_out_15, "); + fprintf(file, "EL6001_data_out_16, EL6001_data_out_17, "); + fprintf(file, "EL6001_data_out_18, EL6001_data_out_19, "); + fprintf(file, "EL6001_data_out_20, EL6001_data_out_21, "); + + fprintf(file, "EL6001_statusword_last, EL6001_statusword, "); + fprintf(file, "EL6001_data_in_0, EL6001_data_in_1, "); + fprintf(file, "EL6001_data_in_2, EL6001_data_in_3, "); + fprintf(file, "EL6001_data_in_4, EL6001_data_in_5, "); + fprintf(file, "EL6001_data_in_6, EL6001_data_in_7, "); + fprintf(file, "EL6001_data_in_8, EL6001_data_in_9, "); + fprintf(file, "EL6001_data_in_10, EL6001_data_in_11, "); + fprintf(file, "EL6001_data_in_12, EL6001_data_in_13, "); + fprintf(file, "EL6001_data_in_14, EL6001_data_in_15, "); + fprintf(file, "EL6001_data_in_16, EL6001_data_in_17, "); + fprintf(file, "EL6001_data_in_18, EL6001_data_in_19, "); + fprintf(file, "EL6001_data_in_20, EL6001_data_in_21, "); + + fprintf(file, "\n"); +} + +void telemetry_data(void* self) { + assert(self); + + if (!file) { + return; + } + + single_device_server_t* sds = (single_device_server_t*)self; + const jsd_el6001_private_state_t* state = jsd_el6001_get_state(sds->jsd, slave_id); + + fprintf(file, "%u, %u,", state->controlword_user_last, state->controlword_user); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[0], state->pub.transmit_bytes[1]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[2], state->pub.transmit_bytes[3]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[4], state->pub.transmit_bytes[5]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[6], state->pub.transmit_bytes[7]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[8], state->pub.transmit_bytes[9]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[10], state->pub.transmit_bytes[11]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[12], state->pub.transmit_bytes[13]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[14], state->pub.transmit_bytes[15]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[16], state->pub.transmit_bytes[17]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[18], state->pub.transmit_bytes[19]); + fprintf(file, "%u, %u,", state->pub.transmit_bytes[20], state->pub.transmit_bytes[21]); + + fprintf(file, "%u, %u,", state->statusword_last, state->statusword); + fprintf(file, "%u, %u,", state->pub.received_bytes[0], state->pub.received_bytes[1]); + fprintf(file, "%u, %u,", state->pub.received_bytes[2], state->pub.received_bytes[3]); + fprintf(file, "%u, %u,", state->pub.received_bytes[4], state->pub.received_bytes[5]); + fprintf(file, "%u, %u,", state->pub.received_bytes[6], state->pub.received_bytes[7]); + fprintf(file, "%u, %u,", state->pub.received_bytes[8], state->pub.received_bytes[9]); + fprintf(file, "%u, %u,", state->pub.received_bytes[10], state->pub.received_bytes[11]); + fprintf(file, "%u, %u,", state->pub.received_bytes[12], state->pub.received_bytes[13]); + fprintf(file, "%u, %u,", state->pub.received_bytes[14], state->pub.received_bytes[15]); + fprintf(file, "%u, %u,", state->pub.received_bytes[16], state->pub.received_bytes[17]); + fprintf(file, "%u, %u,", state->pub.received_bytes[18], state->pub.received_bytes[19]); + fprintf(file, "%u, %u,", state->pub.received_bytes[20], state->pub.received_bytes[21]); + + fprintf(file, "\n"); + fflush(file); +} + +void print_info(void* self) { + assert(self); + + single_device_server_t* sds = (single_device_server_t*)self; + const jsd_el6001_private_state_t* state = jsd_el6001_get_state(sds->jsd, slave_id); + MSG("Controlword_user: %u ", state->controlword_user); + MSG("Statusword: %u ", state->statusword); +} + +void extract_data(void* self) { + single_device_server_t* sds = (single_device_server_t*)self; + jsd_el6001_read(sds->jsd, slave_id); +} + +void command(void* self) { (void)self; }; + + +int main(int argc, char* argv[]) { + if (argc != 4) { + ERROR("Expecting exactly 3 arguments"); + MSG("Usage: jsd_el6001_test "); + MSG("Example: $ jsd_el6001_test eth0 2 1000"); + return 0; + } + + char* ifname = strdup(argv[1]); + slave_id = atoi(argv[2]); + uint32_t loop_freq_hz = atoi(argv[3]); + + MSG("Configuring device %s, using slave %d", ifname, slave_id); + MSG("Using frequency of %i hz", loop_freq_hz); + + single_device_server_t sds; + + sds_set_telemetry_header_callback(&sds, telemetry_header); + sds_set_telemetry_data_callback(&sds, telemetry_data); + sds_set_print_info_callback(&sds, print_info); + sds_set_extract_data_callback(&sds, extract_data); + sds_set_command_callback(&sds, command); + + sds_setup(&sds, loop_freq_hz); + + // set device configuration here + jsd_slave_config_t my_config = {0}; + + my_config.el6001.baud_rate = JSD_EL6001_BAUD_RATE_115200; + + snprintf(my_config.name, JSD_NAME_LEN, "my_el6001_device"); + my_config.configuration_active = true; + my_config.product_code = JSD_EL6001_PRODUCT_CODE; + + jsd_set_slave_config(sds.jsd, slave_id, my_config); + + sds_run(&sds, ifname, "/tmp/jsd_el6001.csv"); + + return 0; +}