C2: Add LIN message support

This commit is contained in:
David Rebbe
2026-04-15 15:50:33 -04:00
committed by Kyle Schwarz
parent 6dc005fcea
commit f5f6d0828b
11 changed files with 1199 additions and 12 deletions
+6
View File
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-lin-example src/main.c)
target_link_libraries(libicsneoc2-lin-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-lin-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+311
View File
@@ -0,0 +1,311 @@
/* Note: This example requires LIN 1 and LIN 2 channels to be connected on the device */
#include <icsneo/icsneoc2.h>
#include <icsneo/icsneoc2messages.h>
#include <icsneo/icsneoc2settings.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <stdio.h>
#include <inttypes.h>
void sleep_ms(uint32_t ms) {
#ifdef _WIN32
Sleep(ms);
#else
sleep(ms / 1000);
#endif
}
int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64];
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
printf("%s: Failed to get string for error code %d with error code %d\n", message, error, res);
return res;
}
printf("%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
void print_events(const char* device_description) {
icsneoc2_event_t* events[1024] = {0};
size_t events_count = 1024;
for(size_t i = 0; i < events_count; ++i) {
icsneoc2_error_t res = icsneoc2_event_get(&events[i], NULL);
if(res != icsneoc2_error_success) {
for(size_t j = 0; j < i; ++j) {
icsneoc2_event_free(events[j]);
}
(void)print_error_code("\tFailed to get device events", res);
return;
}
if(events[i] == NULL) {
events_count = i;
break;
}
}
for(size_t i = 0; i < events_count; i++) {
char event_description[255] = {0};
size_t event_description_length = 255;
icsneoc2_error_t res = icsneoc2_event_description_get(events[i], event_description, &event_description_length);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to get event description", res);
continue;
}
printf("\t%s: Event %zu: %s\n", device_description, i, event_description);
}
for(size_t i = 0; i < events_count; i++) {
icsneoc2_event_free(events[i]);
}
printf("\t%s: Received %zu events\n", device_description, events_count);
}
void process_lin_messages(icsneoc2_message_t** messages, size_t count) {
for(size_t i = 0; i < count; i++) {
bool is_lin = false;
icsneoc2_error_t res = icsneoc2_message_is_lin(messages[i], &is_lin);
if(res != icsneoc2_error_success || !is_lin)
continue;
uint8_t id = 0;
uint8_t protected_id = 0;
uint8_t checksum = 0;
icsneoc2_lin_msg_type_t msg_type = 0;
bool is_enhanced_checksum = false;
res = icsneoc2_message_lin_props_get(messages[i], &id, &protected_id, &checksum, &msg_type, &is_enhanced_checksum);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to get LIN properties", res);
continue;
}
icsneoc2_netid_t netid = 0;
icsneoc2_message_netid_get(messages[i], &netid);
char netid_name[128] = {0};
size_t netid_name_length = 128;
icsneoc2_netid_name_get(netid, netid_name, &netid_name_length);
uint8_t data[64] = {0};
size_t data_length = 64;
icsneoc2_message_data_get(messages[i], data, &data_length);
icsneoc2_lin_err_flags_t err_flags = 0;
icsneoc2_message_lin_err_flags_get(messages[i], &err_flags);
printf("\t%s RX | ID: 0x%02x | Protected ID: 0x%02x\n", netid_name, id, protected_id);
printf("\tData: [");
for(size_t x = 0; x < data_length; x++) {
printf(" 0x%02x", data[x]);
}
printf(" ]\n");
printf("\tChecksum type: %s\n", is_enhanced_checksum ? "Enhanced" : "Classic");
printf("\tChecksum: 0x%02x\n", checksum);
printf("\tChecksum valid: %s\n\n", (!(err_flags & ICSNEOC2_LIN_ERR_CHECKSUM_MATCH)) ? "yes" : "no");
}
}
int main() {
/* Open the first available device */
printf("Opening first available device...\n");
icsneoc2_device_t* device = NULL;
icsneoc2_error_t res = icsneoc2_device_open_first(0, icsneoc2_open_options_default, &device);
if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to open first device", res);
}
char description[255] = {0};
size_t description_length = 255;
res = icsneoc2_device_description_get(device, description, &description_length);
if(res != icsneoc2_error_success) {
icsneoc2_device_close(device);
return print_error_code("\tFailed to get device description", res);
}
printf("\tOpened device: %s\n\n", description);
/* Configure LIN settings */
int64_t baud = 19200;
printf("Enable LIN 01 commander resistor... ");
res = icsneoc2_settings_commander_resistor_set(device, icsneoc2_netid_lin_01, true);
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
printf("Disable LIN 02 commander resistor... ");
res = icsneoc2_settings_commander_resistor_set(device, icsneoc2_netid_lin_02, false);
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
printf("Setting LIN 01 baudrate to %" PRId64 " bit/s... ", baud);
res = icsneoc2_settings_baudrate_set(device, icsneoc2_netid_lin_01, baud);
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
printf("Setting LIN 02 baudrate to %" PRId64 " bit/s... ", baud);
res = icsneoc2_settings_baudrate_set(device, icsneoc2_netid_lin_02, baud);
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
printf("Setting LIN 01 mode to NORMAL... ");
res = icsneoc2_settings_lin_mode_set(device, icsneoc2_netid_lin_01, icsneoc2_lin_mode_normal);
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
printf("Setting LIN 02 mode to NORMAL... ");
res = icsneoc2_settings_lin_mode_set(device, icsneoc2_netid_lin_02, icsneoc2_lin_mode_normal);
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
printf("Applying settings... ");
res = icsneoc2_settings_apply(device);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_close(device);
return print_error_code("\tFailed to apply settings", res);
}
printf("OK\n");
printf("Getting LIN 01 baudrate... ");
int64_t read_baud = 0;
res = icsneoc2_settings_baudrate_get(device, icsneoc2_netid_lin_01, &read_baud);
if(res == icsneoc2_error_success)
printf("OK, %" PRId64 " bit/s\n", read_baud);
else
printf("FAIL\n");
printf("Getting LIN 02 baudrate... ");
res = icsneoc2_settings_baudrate_get(device, icsneoc2_netid_lin_02, &read_baud);
if(res == icsneoc2_error_success)
printf("OK, %" PRId64 " bit/s\n\n", read_baud);
else
printf("FAIL\n\n");
/* Transmit a LIN responder data update on LIN 02 */
printf("Transmitting a LIN 02 responder data frame... ");
{
icsneoc2_message_t* msg = NULL;
res = icsneoc2_message_lin_create(&msg, 0x11);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_close(device);
return print_error_code("\tFailed to create LIN message", res);
}
icsneoc2_lin_msg_type_t msg_type = icsneoc2_lin_msg_type_update_responder;
res = icsneoc2_message_lin_props_set(msg, NULL, NULL, &msg_type, NULL);
uint8_t data[] = {0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22, 0x33, 0x44};
res += icsneoc2_message_data_set(msg, data, sizeof(data));
res += icsneoc2_message_netid_set(msg, icsneoc2_netid_lin_02);
res += icsneoc2_message_lin_calc_checksum(msg);
if(res != icsneoc2_error_success) {
icsneoc2_message_free(msg);
print_events(description);
icsneoc2_device_close(device);
return print_error_code("\tFailed to set LIN message properties", res);
}
res = icsneoc2_device_message_transmit(device, msg);
icsneoc2_message_free(msg);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_close(device);
return print_error_code("\tFailed to transmit LIN responder message", res);
}
printf("OK\n");
}
/* Transmit a LIN commander header on LIN 01 */
printf("Transmitting a LIN 01 commander header... ");
{
icsneoc2_message_t* msg = NULL;
res = icsneoc2_message_lin_create(&msg, 0x11);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_close(device);
return print_error_code("\tFailed to create LIN message", res);
}
icsneoc2_lin_msg_type_t msg_type = icsneoc2_lin_msg_type_header_only;
res = icsneoc2_message_lin_props_set(msg, NULL, NULL, &msg_type, NULL);
res += icsneoc2_message_netid_set(msg, icsneoc2_netid_lin_01);
if(res != icsneoc2_error_success) {
icsneoc2_message_free(msg);
print_events(description);
icsneoc2_device_close(device);
return print_error_code("\tFailed to set LIN message properties", res);
}
res = icsneoc2_device_message_transmit(device, msg);
icsneoc2_message_free(msg);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_close(device);
return print_error_code("\tFailed to transmit LIN header", res);
}
printf("OK\n\n");
}
sleep_ms(100);
/* Transmit a LIN commander message with data on LIN 01 */
printf("Transmitting a LIN 01 commander frame with data... ");
{
icsneoc2_message_t* msg = NULL;
res = icsneoc2_message_lin_create(&msg, 0x22);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_close(device);
return print_error_code("\tFailed to create LIN message", res);
}
icsneoc2_lin_msg_type_t msg_type = icsneoc2_lin_msg_type_commander_msg;
bool enhanced = true;
res = icsneoc2_message_lin_props_set(msg, NULL, NULL, &msg_type, &enhanced);
uint8_t data[] = {0x11, 0x22, 0x33, 0x44, 0xaa, 0xbb, 0xcc, 0xdd};
res += icsneoc2_message_data_set(msg, data, sizeof(data));
res += icsneoc2_message_netid_set(msg, icsneoc2_netid_lin_01);
res += icsneoc2_message_lin_calc_checksum(msg);
if(res != icsneoc2_error_success) {
icsneoc2_message_free(msg);
print_events(description);
icsneoc2_device_close(device);
return print_error_code("\tFailed to set LIN message properties", res);
}
res = icsneoc2_device_message_transmit(device, msg);
icsneoc2_message_free(msg);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_close(device);
return print_error_code("\tFailed to transmit LIN commander message", res);
}
printf("OK\n\n");
}
sleep_ms(100);
/* Read back any received messages and display LIN frames */
icsneoc2_message_t* messages[2048] = {0};
size_t message_count = 2048;
printf("Getting messages...\n");
for(size_t i = 0; i < message_count; ++i) {
res = icsneoc2_device_message_get(device, &messages[i], 0);
if(res != icsneoc2_error_success) {
for(size_t j = 0; j < i; ++j) {
icsneoc2_message_free(messages[j]);
}
print_events(description);
icsneoc2_device_close(device);
return print_error_code("\tFailed to get messages", res);
}
if(messages[i] == NULL) {
message_count = i;
break;
}
}
printf("\tReceived %zu messages\n", message_count);
process_lin_messages(messages, message_count);
for(size_t i = 0; i < message_count; ++i) {
icsneoc2_message_free(messages[i]);
}
/* Cleanup */
print_events(description);
printf("Closing device... ");
res = icsneoc2_device_close(device);
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-lin-transmit-example src/main.c)
target_link_libraries(libicsneoc2-lin-transmit-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-lin-transmit-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+276
View File
@@ -0,0 +1,276 @@
#include <icsneo/icsneoc2.h>
#include <icsneo/icsneoc2messages.h>
#include <icsneo/icsneoc2settings.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
void sleep_ms(uint32_t ms) {
#ifdef _WIN32
Sleep(ms);
#else
sleep(ms / 1000);
#endif
}
int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64];
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
printf("%s: Failed to get string for error code %d with error code %d\n", message, error, res);
return res;
}
printf("%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
int read_int(const char* prompt, int min_val, int max_val) {
int value;
while(1) {
printf("%s", prompt);
if(scanf("%d", &value) != 1) {
/* Clear invalid input */
int c;
while((c = getchar()) != '\n' && c != EOF) {}
printf("Invalid input, try again.\n");
continue;
}
if(value < min_val || value > max_val) {
printf("Please enter a value between %d and %d.\n", min_val, max_val);
continue;
}
return value;
}
}
int read_hex(const char* prompt, int min_val, int max_val) {
int value;
while(1) {
printf("%s", prompt);
if(scanf("%x", &value) != 1) {
/* Clear invalid input */
int c;
while((c = getchar()) != '\n' && c != EOF) {}
printf("Invalid input, try again.\n");
continue;
}
if(value < min_val || value > max_val) {
printf("Please enter a value between 0x%X and 0x%X.\n", min_val, max_val);
continue;
}
return value;
}
}
int main() {
icsneoc2_error_t res;
/* ===== Device Selection ===== */
printf("Searching for devices...\n");
icsneoc2_device_info_t* found_devices = NULL;
res = icsneoc2_device_enumerate(0, &found_devices);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to enumerate devices", res);
}
if(!found_devices) {
printf("No devices found.\n");
return 1;
}
/* Count and display devices */
int device_count = 0;
for(icsneoc2_device_info_t* cur = found_devices; cur; cur = icsneoc2_device_info_next(cur)) {
char desc[128] = {0};
size_t desc_len = sizeof(desc);
icsneoc2_device_info_description_get(cur, desc, &desc_len);
printf(" [%d] %s\n", device_count + 1, desc);
device_count++;
}
int device_choice = read_int("Select device: ", 1, device_count);
/* Find the selected device_info node */
icsneoc2_device_info_t* selected_info = found_devices;
for(int i = 1; i < device_choice; i++) {
selected_info = icsneoc2_device_info_next(selected_info);
}
/* Open the selected device */
icsneoc2_device_t* device = NULL;
res = icsneoc2_device_create(selected_info, &device);
if(res != icsneoc2_error_success) {
icsneoc2_enumeration_free(found_devices);
return print_error_code("Failed to create device from device info", res);
}
res = icsneoc2_device_open(device, icsneoc2_open_options_default);
icsneoc2_enumeration_free(found_devices);
if(res != icsneoc2_error_success) {
icsneoc2_device_free(device);
return print_error_code("Failed to open device", res);
}
char description[128] = {0};
size_t description_length = sizeof(description);
icsneoc2_device_description_get(device, description, &description_length);
printf("Opened device: %s\n\n", description);
/* ===== LIN Network Selection ===== */
/* Get all supported TX networks */
size_t tx_net_count = 0;
res = icsneoc2_device_supported_tx_networks_get(device, NULL, &tx_net_count);
if(res != icsneoc2_error_success || tx_net_count == 0) {
printf("No supported TX networks.\n");
icsneoc2_device_close(device);
return 1;
}
icsneoc2_netid_t* tx_networks = (icsneoc2_netid_t*)malloc(tx_net_count * sizeof(icsneoc2_netid_t));
if(!tx_networks) {
printf("Out of memory.\n");
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return 1;
}
res = icsneoc2_device_supported_tx_networks_get(device, tx_networks, &tx_net_count);
if(res != icsneoc2_error_success) {
free(tx_networks);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return print_error_code("Failed to get TX networks", res);
}
/* Filter for LIN networks */
icsneoc2_netid_t lin_networks[64] = {0};
char lin_names[64][128] = {{0}};
size_t lin_count = 0;
for(size_t i = 0; i < tx_net_count && lin_count < 64; i++) {
icsneoc2_message_t* tmp = NULL;
res = icsneoc2_message_lin_create(&tmp, 0);
if(res != icsneoc2_error_success) continue;
res = icsneoc2_message_netid_set(tmp, tx_networks[i]);
if(res != icsneoc2_error_success) { icsneoc2_message_free(tmp); continue; }
icsneoc2_network_type_t ntype = 0;
res = icsneoc2_message_network_type_get(tmp, &ntype);
icsneoc2_message_free(tmp);
if(res != icsneoc2_error_success) continue;
if(ntype == icsneoc2_network_type_lin) {
lin_networks[lin_count] = tx_networks[i];
size_t name_len = 128;
icsneoc2_netid_name_get(tx_networks[i], lin_names[lin_count], &name_len);
lin_count++;
}
}
free(tx_networks);
if(lin_count == 0) {
printf("No LIN networks available on this device.\n");
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return 1;
}
printf("Available LIN networks:\n");
for(size_t i = 0; i < lin_count; i++) {
printf(" [%zu] %s\n", i + 1, lin_names[i]);
}
int lin_choice = read_int("Select LIN network: ", 1, (int)lin_count);
icsneoc2_netid_t selected_netid = lin_networks[lin_choice - 1];
printf("Selected: %s\n\n", lin_names[lin_choice - 1]);
/* ===== Commander / Responder Selection ===== */
printf("Message type:\n");
printf(" [1] Commander frame\n");
printf(" [2] Responder frame (update responder + header only)\n");
int type_choice = read_int("Select message type: ", 1, 2);
bool is_commander = (type_choice == 1);
printf("Selected: %s\n\n", is_commander ? "Commander" : "Responder");
uint8_t id_choice = (uint8_t)read_hex("Select LIN ID (0x00-0x3F): ", 0, 0x3F);
printf("Selected: 0x%02X\n\n", id_choice);
/* ===== Configure LIN ===== */
printf("Configuring %s... ", lin_names[lin_choice - 1]);
res = icsneoc2_settings_commander_resistor_set(device, selected_netid, is_commander);
res += icsneoc2_settings_baudrate_set(device, selected_netid, 19200);
res += icsneoc2_settings_lin_mode_set(device, selected_netid, icsneoc2_lin_mode_normal);
res += icsneoc2_settings_apply(device);
if(res != icsneoc2_error_success) {
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return print_error_code("Failed to configure LIN", res);
}
printf("OK\n\n");
/* ===== Transmit Loop ===== */
printf("Transmitting on %s every second for 10 seconds...\n", lin_names[lin_choice - 1]);
uint8_t counter = 0;
for(int i = 0; i < 10; i++) {
icsneoc2_message_t* msg = NULL;
res = icsneoc2_message_lin_create(&msg, id_choice);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to create LIN message", res);
break;
}
uint8_t data[] = {counter, counter + 1, counter + 2, counter + 3, 0xAA, 0xBB, 0xCC, 0xDD};
bool enhanced = true;
if(is_commander) {
/* Commander: send header-only frame to poll the bus */
icsneoc2_lin_msg_type_t msg_type = icsneoc2_lin_msg_type_header_only;
res = icsneoc2_message_lin_props_set(msg, NULL, NULL, &msg_type, &enhanced);
res += icsneoc2_message_netid_set(msg, selected_netid);
if(res != icsneoc2_error_success) {
icsneoc2_message_free(msg);
print_error_code("\tFailed to set commander properties", res);
break;
}
res = icsneoc2_device_message_transmit(device, msg);
icsneoc2_message_free(msg);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to transmit header", res);
break;
}
} else {
/* Responder: update the responder table with new data */
icsneoc2_lin_msg_type_t msg_type = icsneoc2_lin_msg_type_update_responder;
res = icsneoc2_message_lin_props_set(msg, NULL, NULL, &msg_type, &enhanced);
res += icsneoc2_message_data_set(msg, data, sizeof(data));
res += icsneoc2_message_lin_calc_checksum(msg);
res += icsneoc2_message_netid_set(msg, selected_netid);
if(res != icsneoc2_error_success) {
icsneoc2_message_free(msg);
print_error_code("\tFailed to update responder", res);
break;
}
res = icsneoc2_device_message_transmit(device, msg);
icsneoc2_message_free(msg);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to transmit responder update", res);
break;
}
}
printf("[%2d/10] Transmitted %s msg ID=0x%02X, counter=%u\n",
i + 1, is_commander ? "commander" : "responder", id_choice, counter);
counter += 4;
sleep_ms(1000);
}
/* Cleanup */
printf("\nClosing device... ");
res = icsneoc2_device_close(device);
printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL");
icsneoc2_device_free(device);
return 0;
}