diff --git a/README.md b/README.md index f3fe89f3..6e20cbc9 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,9 @@ talking. This differs from a simple loopback via PulseAudio as you won't have an - SteelSeries Arctis (7 and Pro) - Sidetone, Battery, Inactive time, Chat-Mix level, LED on/off (allows to turn off the blinking LED on the base-station) - SteelSeries Arctis 7+ - - Sidetone, Battery, Inactive time, Equalizer Presets + - Sidetone, Battery, Chat-Mix level, Inactive time, Equalizer Presets, Equalizer - SteelSeries Arctis Nova 7 - - Sidetone, Battery, Chat-Mix level, Inactive time, Equalizer Presets + - Sidetone, Battery, Chat-Mix level, Inactive time, Equalizer Presets, Equalizer - SteelSeries Arctis 9 - Sidetone, Battery, Inactive time, Chat-Mix level - SteelSeries Arctis Pro Wireless @@ -167,6 +167,8 @@ The following options don't work on all devices yet: `headsetcontrol -p 0-3` sets equalizer preset, must be between 0 and 3, 0 is the default preset. +`headsetcontrol -e string` sets equalizer to specified curve, string must contain band values specific to the device (hex or decimal) delimited by spaces, or commas, or new-lines e.g "0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18". + ##### Modifiers `--timeout 5000` Specifies a timeout for read-operations in milliseconds. Default is 5 seconds, 0 disables timeout. diff --git a/src/dev.c b/src/dev.c index 6c4c8d59..487d1500 100644 --- a/src/dev.c +++ b/src/dev.c @@ -2,6 +2,8 @@ #include "hid_utility.h" +#include "utility.h" + #include #include @@ -39,42 +41,6 @@ static void print_devices(unsigned short vendorid, unsigned short productid) hid_free_enumeration(devs); } -/** - * @brief Accepts textual input and converts them to a sendable buffer - * - * Parses data like "0xff, 123, 0xb" and converts them to an array of len 3 - * - * @param input string - * @param dest destination array - * @param len max dest length - * @return int amount of data converted - */ -static int get_data_from_parameter(char* input, char* dest, size_t len) -{ - const char* delim = " ,{}\n\r"; - - size_t sz = strlen(input); - char* str = (char*)malloc(sz + 1); - strcpy(str, input); - - // For each token in the string, parse and store in buf[]. - char* token = strtok(input, delim); - int i = 0; - while (token) { - char* endptr; - long int val = strtol(token, &endptr, 0); - - if (i >= len) - return -1; - - dest[i++] = val; - token = strtok(NULL, delim); - } - - free(str); - return i; -} - /** * @brief check if number inside range * diff --git a/src/device.c b/src/device.c index 1b98b20c..91dad976 100644 --- a/src/device.c +++ b/src/device.c @@ -11,6 +11,7 @@ const char* const capabilities_str[NUM_CAPABILITIES] [CAP_VOICE_PROMPTS] = "voice prompts", [CAP_ROTATE_TO_MUTE] = "rotate to mute", [CAP_EQUALIZER_PRESET] = "equalizer preset", + [CAP_EQUALIZER] = "equalizer", }; const char capabilities_str_short[NUM_CAPABILITIES] @@ -24,4 +25,5 @@ const char capabilities_str_short[NUM_CAPABILITIES] [CAP_VOICE_PROMPTS] = 'v', [CAP_ROTATE_TO_MUTE] = 'r', [CAP_EQUALIZER_PRESET] = 'p', + [CAP_EQUALIZER] = 'e' }; \ No newline at end of file diff --git a/src/device.h b/src/device.h index faf70950..8243d65a 100644 --- a/src/device.h +++ b/src/device.h @@ -29,6 +29,7 @@ enum capabilities { CAP_VOICE_PROMPTS, CAP_ROTATE_TO_MUTE, CAP_EQUALIZER_PRESET, + CAP_EQUALIZER, NUM_CAPABILITIES }; @@ -59,6 +60,15 @@ enum headsetcontrol_errors { HSC_OUT_OF_BOUNDS = -102, }; +/** @brief Defines equalizer custom setings + */ +struct equalizer_settings { + /// The size of the bands array + int size; + /// The equalizer frequency bands values + char bands_values[]; +}; + /** @brief Defines the basic data of a device * * Also defines function pointers for using supported features @@ -204,4 +214,20 @@ struct device { * -1 HIDAPI error */ int (*send_equalizer_preset)(hid_device* hid_device, uint8_t num); + + /** @brief Function pointer for setting headset equalizer + * + * Forwards the request to the device specific implementation + * + * @param device_handle The hidapi handle. Must be the same + * device as defined here (same ids) + * @param settings The equalizer settings containing + * the frequency bands values + * + * @returns > 0 on success + * HSC_OUT_OF_BOUNDS on equalizer settings size out of range + * specific to this hardware + * -1 HIDAPI error + */ + int (*send_equalizer)(hid_device* hid_device, struct equalizer_settings* settings); }; diff --git a/src/devices/steelseries_arctis_7_plus.c b/src/devices/steelseries_arctis_7_plus.c index 109041a0..3e1c1ab8 100644 --- a/src/devices/steelseries_arctis_7_plus.c +++ b/src/devices/steelseries_arctis_7_plus.c @@ -19,11 +19,14 @@ static struct device device_arctis; #define HEADSET_OFFLINE 0x01 #define STATUS_BUF_SIZE 6 +#define EQUALIZER_BANDS_SIZE 10 + static const uint16_t PRODUCT_IDS[] = { ID_ARCTIS_7_PLUS, ID_ARCTIS_7_PLUS_PS5 }; static int arctis_7_plus_send_sidetone(hid_device* device_handle, uint8_t num); static int arctis_7_plus_send_inactive_time(hid_device* device_handle, uint8_t num); static int arctis_7_plus_send_equalizer_preset(hid_device* device_handle, uint8_t num); +static int arctis_7_plus_send_equalizer(hid_device* device_handle, struct equalizer_settings* settings); static int arctis_7_plus_request_battery(hid_device* device_handle); static int arctis_7_plus_request_chatmix(hid_device* device_handle); @@ -37,18 +40,20 @@ void arctis_7_plus_init(struct device** device) strncpy(device_arctis.device_name, "SteelSeries Arctis 7+", sizeof(device_arctis.device_name)); - device_arctis.capabilities = B(CAP_SIDETONE) | B(CAP_BATTERY_STATUS) | B(CAP_CHATMIX_STATUS) | B(CAP_INACTIVE_TIME) | B(CAP_EQUALIZER_PRESET); + device_arctis.capabilities = B(CAP_SIDETONE) | B(CAP_BATTERY_STATUS) | B(CAP_CHATMIX_STATUS) | B(CAP_INACTIVE_TIME) | B(CAP_EQUALIZER) | B(CAP_EQUALIZER_PRESET); device_arctis.capability_details[CAP_SIDETONE] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; device_arctis.capability_details[CAP_BATTERY_STATUS] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; device_arctis.capability_details[CAP_CHATMIX_STATUS] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; device_arctis.capability_details[CAP_INACTIVE_TIME] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; device_arctis.capability_details[CAP_EQUALIZER_PRESET] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; + device_arctis.capability_details[CAP_EQUALIZER] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; device_arctis.send_sidetone = &arctis_7_plus_send_sidetone; device_arctis.request_battery = &arctis_7_plus_request_battery; device_arctis.request_chatmix = &arctis_7_plus_request_chatmix; device_arctis.send_inactive_time = &arctis_7_plus_send_inactive_time; device_arctis.send_equalizer_preset = &arctis_7_plus_send_equalizer_preset; + device_arctis.send_equalizer = &arctis_7_plus_send_equalizer; *device = &device_arctis; } @@ -147,6 +152,7 @@ static int arctis_7_plus_send_equalizer_preset(hid_device* device_handle, uint8_ switch (num) { case 0: { + uint8_t flat[MSG_SIZE] = { 0x0, 0x33, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0 }; return hid_write(device_handle, flat, MSG_SIZE); } @@ -169,6 +175,22 @@ static int arctis_7_plus_send_equalizer_preset(hid_device* device_handle, uint8_ } } +static int arctis_7_plus_send_equalizer(hid_device* device_handle, struct equalizer_settings* settings) +{ + if (settings->size != EQUALIZER_BANDS_SIZE) { + printf("Device only supports %d bands.\n", EQUALIZER_BANDS_SIZE); + return HSC_OUT_OF_BOUNDS; + } + + uint8_t data[MSG_SIZE] = { 0x0, 0x33 }; + for (int i = 0; i < settings->size; i++) { + data[i + 2] = (uint8_t)settings->bands_values[i]; + } + data[settings->size + 3] = 0x0; + + return hid_write(device_handle, data, MSG_SIZE); +} + int arctis_7_plus_read_device_status(hid_device* device_handle, unsigned char* data_read) { unsigned char data_request[2] = { 0x00, 0xb0 }; diff --git a/src/devices/steelseries_arctis_nova_7.c b/src/devices/steelseries_arctis_nova_7.c index 7e35e60a..53f61602 100644 --- a/src/devices/steelseries_arctis_nova_7.c +++ b/src/devices/steelseries_arctis_nova_7.c @@ -20,11 +20,14 @@ static struct device device_arctis; #define HEADSET_OFFLINE 0x00 #define STATUS_BUF_SIZE 8 +#define EQUALIZER_BANDS_SIZE 10 + static const uint16_t PRODUCT_IDS[] = { ID_ARCTIS_NOVA_7, ID_ARCTIS_NOVA_7x, ID_ARCTIS_NOVA_7p }; static int arctis_nova_7_send_sidetone(hid_device* device_handle, uint8_t num); static int arctis_nova_7_send_inactive_time(hid_device* device_handle, uint8_t num); static int arctis_nova_7_send_equalizer_preset(hid_device* device_handle, uint8_t num); +static int arctis_nova_7_send_equalizer(hid_device* device_handle, struct equalizer_settings* settings); static int arctis_nova_7_request_battery(hid_device* device_handle); static int arctis_nova_7_request_chatmix(hid_device* device_handle); @@ -38,18 +41,20 @@ void arctis_nova_7_init(struct device** device) strncpy(device_arctis.device_name, "SteelSeries Arctis Nova 7", sizeof(device_arctis.device_name)); - device_arctis.capabilities = B(CAP_SIDETONE) | B(CAP_BATTERY_STATUS) | B(CAP_CHATMIX_STATUS) | B(CAP_INACTIVE_TIME) | B(CAP_EQUALIZER_PRESET); + device_arctis.capabilities = B(CAP_SIDETONE) | B(CAP_BATTERY_STATUS) | B(CAP_CHATMIX_STATUS) | B(CAP_INACTIVE_TIME) | B(CAP_EQUALIZER) | B(CAP_EQUALIZER_PRESET); device_arctis.capability_details[CAP_SIDETONE] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; device_arctis.capability_details[CAP_BATTERY_STATUS] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; device_arctis.capability_details[CAP_CHATMIX_STATUS] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; device_arctis.capability_details[CAP_INACTIVE_TIME] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; device_arctis.capability_details[CAP_EQUALIZER_PRESET] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; + device_arctis.capability_details[CAP_EQUALIZER] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 }; device_arctis.send_sidetone = &arctis_nova_7_send_sidetone; device_arctis.request_battery = &arctis_nova_7_request_battery; device_arctis.request_chatmix = &arctis_nova_7_request_chatmix; device_arctis.send_inactive_time = &arctis_nova_7_send_inactive_time; device_arctis.send_equalizer_preset = &arctis_nova_7_send_equalizer_preset; + device_arctis.send_equalizer = &arctis_nova_7_send_equalizer; *device = &device_arctis; } @@ -173,6 +178,22 @@ static int arctis_nova_7_send_equalizer_preset(hid_device* device_handle, uint8_ } } +static int arctis_nova_7_send_equalizer(hid_device* device_handle, struct equalizer_settings* settings) +{ + if (settings->size != EQUALIZER_BANDS_SIZE) { + printf("Device only supports %d bands.\n", EQUALIZER_BANDS_SIZE); + return HSC_OUT_OF_BOUNDS; + } + + uint8_t data[MSG_SIZE] = { 0x0, 0x33 }; + for (int i = 0; i < settings->size; i++) { + data[i + 2] = (uint8_t)settings->bands_values[i]; + } + data[settings->size + 3] = 0x0; + + return hid_write(device_handle, data, MSG_SIZE); +} + int arctis_nova_7_read_device_status(hid_device* device_handle, unsigned char* data_read) { unsigned char data_request[2] = { 0x00, 0xb0 }; diff --git a/src/main.c b/src/main.c index 75390c96..7d75f34f 100644 --- a/src/main.c +++ b/src/main.c @@ -161,7 +161,7 @@ static hid_device* dynamic_connect(char** existing_hid_path, hid_device* device_ * @param param first parameter of the feature * @return int 0 on success, some other number otherwise */ -static int handle_feature(struct device* device_found, hid_device** device_handle, char** hid_path, enum capabilities cap, int param) +static int handle_feature(struct device* device_found, hid_device** device_handle, char** hid_path, enum capabilities cap, void* param) { // Check if the headset implements the requested feature if ((device_found->capabilities & B(cap)) == 0) { @@ -179,7 +179,7 @@ static int handle_feature(struct device* device_found, hid_device** device_handl switch (cap) { case CAP_SIDETONE: - ret = device_found->send_sidetone(*device_handle, param); + ret = device_found->send_sidetone(*device_handle, *(int*)param); break; case CAP_BATTERY_STATUS: @@ -197,20 +197,20 @@ static int handle_feature(struct device* device_found, hid_device** device_handl break; case CAP_NOTIFICATION_SOUND: - ret = device_found->notifcation_sound(*device_handle, param); + ret = device_found->notifcation_sound(*device_handle, *(int*)param); break; case CAP_LIGHTS: - ret = device_found->switch_lights(*device_handle, param); + ret = device_found->switch_lights(*device_handle, *(int*)param); break; case CAP_INACTIVE_TIME: - ret = device_found->send_inactive_time(*device_handle, param); + ret = device_found->send_inactive_time(*device_handle, *(int*)param); if (ret < 0) break; - PRINT_INFO("Successfully set inactive time to %d minutes!\n", param); + PRINT_INFO("Successfully set inactive time to %d minutes!\n", *(int*)param); break; case CAP_CHATMIX_STATUS: @@ -223,20 +223,29 @@ static int handle_feature(struct device* device_found, hid_device** device_handl break; case CAP_VOICE_PROMPTS: - ret = device_found->switch_voice_prompts(*device_handle, param); + ret = device_found->switch_voice_prompts(*device_handle, *(int*)param); break; case CAP_ROTATE_TO_MUTE: - ret = device_found->switch_rotate_to_mute(*device_handle, param); + ret = device_found->switch_rotate_to_mute(*device_handle, *(int*)param); break; case CAP_EQUALIZER_PRESET: - ret = device_found->send_equalizer_preset(*device_handle, param); + ret = device_found->send_equalizer_preset(*device_handle, *(int*)param); if (ret < 0) break; - PRINT_INFO("Successfully set equalizer preset to %d!\n", param); + PRINT_INFO("Successfully set equalizer preset to %d!\n", *(int*)param); + break; + + case CAP_EQUALIZER: + ret = device_found->send_equalizer(*device_handle, (struct equalizer_settings*)param); + + if (ret < 0) + break; + + PRINT_INFO("Successfully set equalizer!\n"); break; case NUM_CAPABILITIES: @@ -275,19 +284,23 @@ int main(int argc, char* argv[]) { int c; - int sidetone_loudness = -1; - int request_battery = 0; - int request_chatmix = 0; - int notification_sound = -1; - int lights = -1; - int inactive_time = -1; - int voice_prompts = -1; - int rotate_to_mute = -1; - int print_capabilities = -1; - int equalizer_preset = -1; - int dev_mode = 0; - int follow = 0; - unsigned follow_sec = 2; + int sidetone_loudness = -1; + int request_battery = 0; + int request_chatmix = 0; + int notification_sound = -1; + int lights = -1; + int inactive_time = -1; + int voice_prompts = -1; + int rotate_to_mute = -1; + int print_capabilities = -1; + int equalizer_preset = -1; + int dev_mode = 0; + int follow = 0; + unsigned follow_sec = 2; + struct equalizer_settings* equalizer = NULL; + +#define BUFFERLENGTH 1024 + char* read_buffer = calloc(BUFFERLENGTH, sizeof(char)); struct option opts[] = { { "battery", no_argument, NULL, 'b' }, @@ -295,6 +308,7 @@ int main(int argc, char* argv[]) { "chatmix", no_argument, NULL, 'm' }, { "dev", no_argument, NULL, 0 }, { "help", no_argument, NULL, 'h' }, + { "equalizer", required_argument, NULL, 'e' }, { "equalizer-preset", required_argument, NULL, 'p' }, { "inactive-time", required_argument, NULL, 'i' }, { "light", required_argument, NULL, 'l' }, @@ -313,7 +327,7 @@ int main(int argc, char* argv[]) // Init all information of supported devices init_devices(); - while ((c = getopt_long(argc, argv, "bchi:l:f::mn:r:s:uv:p:?", opts, &option_index)) != -1) { + while ((c = getopt_long(argc, argv, "bchi:l:f::mn:r:s:uv:p:e:?", opts, &option_index)) != -1) { char* endptr = NULL; // for strtol switch (c) { @@ -323,6 +337,26 @@ int main(int argc, char* argv[]) case 'c': short_output = 1; break; + case 'e': { + int size = get_data_from_parameter(optarg, read_buffer, BUFFERLENGTH); + + if (size < 0) { + fprintf(stderr, "Equalizer bands values size larger than supported %d\n", BUFFERLENGTH); + return 1; + } + + if (size == 0) { + fprintf(stderr, "No bands values specified to --equalizer\n"); + return 1; + } + + equalizer = malloc(sizeof(struct equalizer_settings) + size * sizeof(char)); + equalizer->size = size; + for (int i = 0; i < size; i++) { + equalizer->bands_values[i] = read_buffer[i]; + } + break; + } case 'f': follow = 1; if (OPTIONAL_ARGUMENT_IS_PRESENT) { @@ -408,6 +442,7 @@ int main(int argc, char* argv[]) printf(" -m, --chatmix\t\t\tRetrieves the current chat-mix-dial level setting between 0 and 128. Below 64 is the game side and above is the chat side.\n"); printf(" -v, --voice-prompt 0|1\tTurn voice prompts on or off (0 = off, 1 = on)\n"); printf(" -r, --rotate-to-mute 0|1\tTurn rotate to mute feature on or off (0 = off, 1 = on)\n"); + printf(" -e, --equalizer string\tSets equalizer to specified curve, string must contain band values specific to the device (hex or decimal) delimited by spaces, or commas, or new-lines e.g \"0x18, 0x18, 0x18, 0x18, 0x18\".\n"); printf(" -p, --equalizer-preset number\tSets equalizer preset, number must be between 0 and 3, 0 sets the default\n"); printf(" -f, --follow [secs timeout]\tRe-run the commands after the specified seconds timeout or 2 by default\n"); printf("\n"); @@ -438,6 +473,8 @@ int main(int argc, char* argv[]) } } + free(read_buffer); + if (dev_mode) { // use +1 to make sure the first parameter is some previous argument (which normally would be the name of the program) return dev_main(argc - optind + 1, &argv[optind - 1]); @@ -465,47 +502,55 @@ int main(int argc, char* argv[]) loop_start: if (sidetone_loudness != -1) { - if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_SIDETONE, sidetone_loudness)) != 0) + if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_SIDETONE, &sidetone_loudness)) != 0) goto error; } if (lights != -1) { - if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_LIGHTS, lights)) != 0) + if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_LIGHTS, &lights)) != 0) goto error; } if (notification_sound != -1) { - if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_NOTIFICATION_SOUND, notification_sound)) != 0) + if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_NOTIFICATION_SOUND, ¬ification_sound)) != 0) goto error; } if (request_battery == 1) { - if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_BATTERY_STATUS, request_battery)) != 0) + if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_BATTERY_STATUS, &request_battery)) != 0) goto error; } if (inactive_time != -1) { - if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_INACTIVE_TIME, inactive_time)) != 0) + if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_INACTIVE_TIME, &inactive_time)) != 0) goto error; } if (request_chatmix == 1) { - if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_CHATMIX_STATUS, request_chatmix)) != 0) + if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_CHATMIX_STATUS, &request_chatmix)) != 0) goto error; } if (voice_prompts != -1) { - if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_VOICE_PROMPTS, voice_prompts)) != 0) + if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_VOICE_PROMPTS, &voice_prompts)) != 0) goto error; } if (rotate_to_mute != -1) { - if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_ROTATE_TO_MUTE, rotate_to_mute)) != 0) + if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_ROTATE_TO_MUTE, &rotate_to_mute)) != 0) goto error; } if (equalizer_preset != -1) { - if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_EQUALIZER_PRESET, equalizer_preset)) != 0) + if ((error = handle_feature(&device_found, &device_handle, &hid_path, CAP_EQUALIZER_PRESET, &equalizer_preset)) != 0) + goto error; + } + + if (equalizer != NULL) { + error = handle_feature(&device_found, &device_handle, &hid_path, CAP_EQUALIZER, equalizer); + free(equalizer); + + if ((error) != 0) goto error; } diff --git a/src/utility.c b/src/utility.c index c15f2c5c..a2bc4bbd 100644 --- a/src/utility.c +++ b/src/utility.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -51,3 +52,29 @@ size_t hexdump(char* out, size_t out_size, unsigned char* data, size_t data_size } return i; } + +int get_data_from_parameter(char* input, char* dest, size_t len) +{ + const char* delim = " ,{}\n\r"; + + size_t sz = strlen(input); + char* str = (char*)malloc(sz + 1); + strcpy(str, input); + + // For each token in the string, parse and store in buf[]. + char* token = strtok(input, delim); + int i = 0; + while (token) { + char* endptr; + long int val = strtol(token, &endptr, 0); + + if (i >= len) + return -1; + + dest[i++] = val; + token = strtok(NULL, delim); + } + + free(str); + return i; +} \ No newline at end of file diff --git a/src/utility.h b/src/utility.h index eb60003f..f6a25c0f 100644 --- a/src/utility.h +++ b/src/utility.h @@ -32,3 +32,15 @@ float poly_battery_level(const double terms[], const size_t numterms, uint16_t v * @return 0 on failure or filled size of out */ size_t hexdump(char* out, size_t out_size, unsigned char* data, size_t data_size); + +/** + * @brief Accepts textual input and converts them to a sendable buffer + * + * Parses data like "0xff, 123, 0xb" and converts them to an array of len 3 + * + * @param input string + * @param dest destination array + * @param len max dest length + * @return int amount of data converted + */ +int get_data_from_parameter(char* input, char* dest, size_t len); \ No newline at end of file