From 79325fe5ad46a23f7badd859add886eb12df43cd Mon Sep 17 00:00:00 2001 From: Ran Bao Date: Thu, 5 Oct 2023 16:37:35 +1300 Subject: [PATCH] Add draft implementation --- src/charge_mode.cpp | 103 +++++++++++++++++++++++++---- src/charge_mode.h | 8 ++- src/html/dashboard.html | 42 +++++++----- src/neopixel_led.c | 139 ++++++++++++++++++---------------------- src/neopixel_led.h | 28 ++++---- 5 files changed, 202 insertions(+), 118 deletions(-) diff --git a/src/charge_mode.cpp b/src/charge_mode.cpp index f3935d0..a5031ba 100644 --- a/src/charge_mode.cpp +++ b/src/charge_mode.cpp @@ -40,6 +40,12 @@ const eeprom_charge_mode_data_t default_charge_mode_data = { .set_point_sd_margin = 0.02, .set_point_mean_margin = 0.02, + + // LED related + .neopixel_normal_charge_colour = urgb_u32(0, 0xFF, 0), // green + .neopixel_under_charge_colour = urgb_u32(0xFF, 0xFF, 0), // yellow + .neopixel_over_charge_colour = urgb_u32(0xFF, 0, 0), // red + .neopixel_not_ready_colour = urgb_u32(0, 0, 0xFF), // blue }; // Configures @@ -103,6 +109,14 @@ void scale_measurement_render_task(void *p) { ChargeModeState_t charge_mode_wait_for_zero(ChargeModeState_t prev_state) { + // Set colour to not ready + neopixel_led_set_colour( + NEOPIXEL_LED_DEFAULT_COLOUR, + charge_mode_config.eeprom_charge_mode_data.neopixel_not_ready_colour, + charge_mode_config.eeprom_charge_mode_data.neopixel_not_ready_colour, + true + ); + // Wait for 5 measurements and wait for stable FloatRingBuffer data_buffer(10); @@ -143,6 +157,14 @@ ChargeModeState_t charge_mode_wait_for_zero(ChargeModeState_t prev_state) { } ChargeModeState_t charge_mode_wait_for_complete(ChargeModeState_t prev_state) { + // Set colour to under charge + neopixel_led_set_colour( + NEOPIXEL_LED_DEFAULT_COLOUR, + charge_mode_config.eeprom_charge_mode_data.neopixel_under_charge_colour, + charge_mode_config.eeprom_charge_mode_data.neopixel_under_charge_colour, + true + ); + // Update current status snprintf(title_string, sizeof(title_string), "Target: %.02f %s", @@ -219,6 +241,7 @@ ChargeModeState_t charge_mode_wait_for_complete(ChargeModeState_t prev_state) { last_error = error; } + vTaskDelay(pdMS_TO_TICKS(20)); // Wait for other tasks to complete return CHARGE_MODE_WAIT_FOR_CUP_REMOVAL; } @@ -229,8 +252,38 @@ ChargeModeState_t charge_mode_wait_for_cup_removal(ChargeModeState_t prev_state) FloatRingBuffer data_buffer(5); - // Update LED colour - neopixel_led_set_colour(NEOPIXEL_LED_COLOUR_2, NEOPIXEL_LED_COLOUR_2, true); + // Post charge analysis (while waiting for removal of the cup) + vTaskDelay(pdMS_TO_TICKS(1000)); // Wait for other tasks to complete + float error = charge_mode_config.target_charge_weight - scale_block_wait_for_next_measurement(); + + // Update LED colour before moving to the next stage + // Over charged + if (error <= -charge_mode_config.eeprom_charge_mode_data.fine_stop_threshold) { + neopixel_led_set_colour( + NEOPIXEL_LED_DEFAULT_COLOUR, + charge_mode_config.eeprom_charge_mode_data.neopixel_over_charge_colour, + charge_mode_config.eeprom_charge_mode_data.neopixel_over_charge_colour, + true + ); + } + // Under charged + else if (error >= charge_mode_config.eeprom_charge_mode_data.fine_stop_threshold) { + neopixel_led_set_colour( + NEOPIXEL_LED_DEFAULT_COLOUR, + charge_mode_config.eeprom_charge_mode_data.neopixel_under_charge_colour, + charge_mode_config.eeprom_charge_mode_data.neopixel_under_charge_colour, + true + ); + } + // Normal + else { + neopixel_led_set_colour( + NEOPIXEL_LED_DEFAULT_COLOUR, + charge_mode_config.eeprom_charge_mode_data.neopixel_normal_charge_colour, + charge_mode_config.eeprom_charge_mode_data.neopixel_normal_charge_colour, + true + ); + } // Stop condition: 5 stable measurements in 300ms apart (1.5 seconds minimum) while (true) { @@ -258,13 +311,21 @@ ChargeModeState_t charge_mode_wait_for_cup_removal(ChargeModeState_t prev_state) vTaskDelayUntil(&last_sample_tick, pdMS_TO_TICKS(300)); } - // Update LED colour - neopixel_led_set_colour(NEOPIXEL_LED_COLOUR_1, NEOPIXEL_LED_COLOUR_1, true); + // Reset LED to default colour + neopixel_led_set_colour(NEOPIXEL_LED_DEFAULT_COLOUR, NEOPIXEL_LED_DEFAULT_COLOUR, NEOPIXEL_LED_DEFAULT_COLOUR, true); return CHARGE_MODE_WAIT_FOR_CUP_RETURN; } ChargeModeState_t charge_mode_wait_for_cup_return(ChargeModeState_t prev_state) { + // Set colour to not ready + neopixel_led_set_colour( + NEOPIXEL_LED_DEFAULT_COLOUR, + charge_mode_config.eeprom_charge_mode_data.neopixel_not_ready_colour, + charge_mode_config.eeprom_charge_mode_data.neopixel_not_ready_colour, + true + ); + snprintf(title_string, sizeof(title_string), "Return Cup", charge_mode_config.target_charge_weight); FloatRingBuffer data_buffer(5); @@ -296,8 +357,8 @@ ChargeModeState_t charge_mode_wait_for_cup_return(ChargeModeState_t prev_state) uint8_t charge_mode_menu() { - // Update LED colour - neopixel_led_set_colour(NEOPIXEL_LED_COLOUR_1, NEOPIXEL_LED_COLOUR_1, true); + // Reset LED to default colour + neopixel_led_set_colour(NEOPIXEL_LED_DEFAULT_COLOUR, NEOPIXEL_LED_DEFAULT_COLOUR, NEOPIXEL_LED_DEFAULT_COLOUR, true); // Create target weight charge_mode_config.target_charge_weight = charge_weight_digits[4] * 100 + \ @@ -354,9 +415,9 @@ uint8_t charge_mode_menu() { // } // } - // Update LED colour - neopixel_led_set_colour(NEOPIXEL_LED_COLOUR_1, NEOPIXEL_LED_COLOUR_1, true); - + // Reset LED to default colour + neopixel_led_set_colour(NEOPIXEL_LED_DEFAULT_COLOUR, NEOPIXEL_LED_DEFAULT_COLOUR, NEOPIXEL_LED_DEFAULT_COLOUR, true); + // vTaskDelete(scale_measurement_render_handler); vTaskSuspend(scale_measurement_render_task_handler); @@ -438,12 +499,27 @@ bool http_rest_charge_mode_config(struct fs_file *file, int num_params, char *pa else if (strcmp(params[idx], "sp_avg") == 0) { charge_mode_config.eeprom_charge_mode_data.set_point_mean_margin = strtof(values[idx], NULL); } + + // LED related settings + else if (strcmp(params[idx], "c1") == 0) { + charge_mode_config.eeprom_charge_mode_data.neopixel_normal_charge_colour = hex_string_to_decimal(values[idx]); + } + else if (strcmp(params[idx], "c2") == 0) { + charge_mode_config.eeprom_charge_mode_data.neopixel_under_charge_colour = hex_string_to_decimal(values[idx]); + } + else if (strcmp(params[idx], "c3") == 0) { + charge_mode_config.eeprom_charge_mode_data.neopixel_over_charge_colour = hex_string_to_decimal(values[idx]); + } + else if (strcmp(params[idx], "c4") == 0) { + charge_mode_config.eeprom_charge_mode_data.neopixel_not_ready_colour = hex_string_to_decimal(values[idx]); + } } // Response snprintf(charge_mode_json_buffer, sizeof(charge_mode_json_buffer), - "{\"c_kp\":%.3f,\"c_ki\":%.3f,\"c_kd\":%.3f,\"f_kp\":%.3f,\"f_ki\":%.3f,\"f_kd\":%.3f,\"c_stop\":%.3f,\"f_stop\":%.3f,\"sp_sd\":%.3f,\"sp_avg\":%.3f}", + "{\"c_kp\":%.3f,\"c_ki\":%.3f,\"c_kd\":%.3f,\"f_kp\":%.3f,\"f_ki\":%.3f,\"f_kd\":%.3f,\"c_stop\":%.3f,\"f_stop\":%.3f,\"sp_sd\":%.3f,\"sp_avg\":%.3f," + "\"c1\":\"#%06x\",\"c2\":\"#%06x\",\"c3\":\"#%06x\",\"c4\":\"#%06x\"}", charge_mode_config.eeprom_charge_mode_data.coarse_kp, charge_mode_config.eeprom_charge_mode_data.coarse_ki, charge_mode_config.eeprom_charge_mode_data.coarse_kd, @@ -453,7 +529,12 @@ bool http_rest_charge_mode_config(struct fs_file *file, int num_params, char *pa charge_mode_config.eeprom_charge_mode_data.coarse_stop_threshold, charge_mode_config.eeprom_charge_mode_data.fine_stop_threshold, charge_mode_config.eeprom_charge_mode_data.set_point_sd_margin, - charge_mode_config.eeprom_charge_mode_data.set_point_mean_margin); + charge_mode_config.eeprom_charge_mode_data.set_point_mean_margin, + + charge_mode_config.eeprom_charge_mode_data.neopixel_normal_charge_colour, + charge_mode_config.eeprom_charge_mode_data.neopixel_under_charge_colour, + charge_mode_config.eeprom_charge_mode_data.neopixel_over_charge_colour, + charge_mode_config.eeprom_charge_mode_data.neopixel_not_ready_colour); size_t data_length = strlen(charge_mode_json_buffer); file->data = charge_mode_json_buffer; diff --git a/src/charge_mode.h b/src/charge_mode.h index 050171c..ed3b708 100644 --- a/src/charge_mode.h +++ b/src/charge_mode.h @@ -4,7 +4,7 @@ #include #include "http_rest.h" -#define EEPROM_CHARGE_MODE_DATA_REV 4 // 16 byte +#define EEPROM_CHARGE_MODE_DATA_REV 5 // 16 byte typedef struct { @@ -23,6 +23,12 @@ typedef struct { float set_point_sd_margin; float set_point_mean_margin; + // LED related settings + uint32_t neopixel_normal_charge_colour; + uint32_t neopixel_under_charge_colour; + uint32_t neopixel_over_charge_colour; + uint32_t neopixel_not_ready_colour; + } eeprom_charge_mode_data_t; typedef struct { diff --git a/src/html/dashboard.html b/src/html/dashboard.html index 559e6bf..1b49b95 100644 --- a/src/html/dashboard.html +++ b/src/html/dashboard.html @@ -242,6 +242,26 @@

Control

+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ @@ -442,28 +462,18 @@

Control

- - -
- -
- - -
- -
- - + +
- - + +
- - + +
diff --git a/src/neopixel_led.c b/src/neopixel_led.c index 46e67b8..3f4f3c1 100644 --- a/src/neopixel_led.c +++ b/src/neopixel_led.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "neopixel_led.h" #include "generated/ws2812.pio.h" @@ -30,19 +31,11 @@ #include "eeprom.h" -typedef struct { - neopixel_led_colour_t led1_colour; - neopixel_led_colour_t led2_colour; -} _new_led_colour_t; - typedef struct { eeprom_neopixel_led_metadata_t eeprom_neopixel_led_metadata; - uint32_t current_mini12864_backlight_colour; - uint32_t current_led1_colour; - uint32_t current_led2_colour; - + neopixel_led_colours_t current_led_colours; xQueueHandle colour_update_queue; TaskHandle_t neopixel_control_task_handler; } neopixel_led_config_t; @@ -63,20 +56,49 @@ static inline void put_pixel(uint32_t pixel_grb) { } -void neopixel_led_set_colour(neopixel_led_colour_t led1_colour, neopixel_led_colour_t led2_colour, bool block_wait) { - _new_led_colour_t new_led_colour = { - .led1_colour = led1_colour, - .led2_colour = led2_colour - }; +void neopixel_led_set_colour(uint32_t mini12864_backlight_colour, uint32_t led1_colour, uint32_t led2_colour, bool block_wait) { + neopixel_led_colours_t new_colours; + + if (mini12864_backlight_colour == NEOPIXEL_LED_NO_CHANGE) { + new_colours.mini12864_backlight_colour = neopixel_led_config.current_led_colours.mini12864_backlight_colour; + } + else if (mini12864_backlight_colour == NEOPIXEL_LED_DEFAULT_COLOUR) { + new_colours.mini12864_backlight_colour = neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.mini12864_backlight_colour; + } + else { + new_colours.mini12864_backlight_colour = mini12864_backlight_colour; + } + + if (led1_colour == NEOPIXEL_LED_NO_CHANGE) { + new_colours.led1_colour = neopixel_led_config.current_led_colours.led1_colour; + + } + else if (led1_colour == NEOPIXEL_LED_DEFAULT_COLOUR) { + new_colours.led1_colour = neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.led1_colour; + } + else { + new_colours.led1_colour = led1_colour; + } + + if (led2_colour == NEOPIXEL_LED_NO_CHANGE) { + new_colours.led2_colour = neopixel_led_config.current_led_colours.led2_colour; + } + else if (led1_colour == NEOPIXEL_LED_DEFAULT_COLOUR) { + new_colours.led2_colour = neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.led2_colour; + } + else { + new_colours.led2_colour = led2_colour; + } TickType_t ticks_to_wait = 0; if (block_wait) { ticks_to_wait = portMAX_DELAY; } - xQueueSend(neopixel_led_config.colour_update_queue, &new_led_colour, ticks_to_wait); + xQueueSend(neopixel_led_config.colour_update_queue, &new_colours, ticks_to_wait); } + static void _neopixel_led_set_colour(uint32_t led1_colour, uint32_t led2_colour, uint32_t mini12864_backlight_colour) { put_pixel(led1_colour); // Encoder RGB1 put_pixel(led2_colour); // Encoder RGB2 @@ -86,37 +108,15 @@ static void _neopixel_led_set_colour(uint32_t led1_colour, uint32_t led2_colour, void neopixel_control_task(void *p) { while (true) { - _new_led_colour_t new_led_colour; - xQueueReceive(neopixel_led_config.colour_update_queue, &new_led_colour, portMAX_DELAY); - - uint32_t led1_colour_rgb, led2_colour_rgb; - - switch (new_led_colour.led1_colour) { - case NEOPIXEL_LED_COLOUR_1: - led1_colour_rgb = neopixel_led_config.eeprom_neopixel_led_metadata.led1_colour1; - break; - case NEOPIXEL_LED_COLOUR_2: - led1_colour_rgb = neopixel_led_config.eeprom_neopixel_led_metadata.led1_colour2; - break; - default: - led1_colour_rgb = neopixel_led_config.current_led1_colour; - break; - } - - switch (new_led_colour.led2_colour) { - case NEOPIXEL_LED_COLOUR_1: - led2_colour_rgb = neopixel_led_config.eeprom_neopixel_led_metadata.led2_colour1; - break; - case NEOPIXEL_LED_COLOUR_2: - led2_colour_rgb = neopixel_led_config.eeprom_neopixel_led_metadata.led2_colour2; - break; - default: - led2_colour_rgb = neopixel_led_config.current_led2_colour; - break; - } + neopixel_led_colours_t new_colours; + xQueueReceive(neopixel_led_config.colour_update_queue, &new_colours, portMAX_DELAY); // Apply - _neopixel_led_set_colour(led1_colour_rgb, led2_colour_rgb, neopixel_led_config.current_mini12864_backlight_colour); + _neopixel_led_set_colour( + new_colours.led1_colour, + new_colours.led2_colour, + new_colours.mini12864_backlight_colour + ); } } @@ -136,12 +136,11 @@ bool neopixel_led_init(void) { // If the revision doesn't match then re-initialize the config if (neopixel_led_config.eeprom_neopixel_led_metadata.neopixel_data_rev != EEPROM_NEOPIXEL_LED_METADATA_REV) { neopixel_led_config.eeprom_neopixel_led_metadata.neopixel_data_rev = EEPROM_NEOPIXEL_LED_METADATA_REV; - neopixel_led_config.eeprom_neopixel_led_metadata.default_mini12864_backlight_colour = urgb_u32(0xFF, 0xFF, 0xFF); - neopixel_led_config.eeprom_neopixel_led_metadata.led1_colour1 = urgb_u32(0x0F, 0x0F, 0x0F); - neopixel_led_config.eeprom_neopixel_led_metadata.led1_colour2 = urgb_u32(0xFF, 0xFF, 0x00); - neopixel_led_config.eeprom_neopixel_led_metadata.led2_colour1 = urgb_u32(0x0F, 0x0F, 0x0F); - neopixel_led_config.eeprom_neopixel_led_metadata.led2_colour2 = urgb_u32(0x00, 0xFF, 0xFF); + // Default to white + neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.led1_colour = urgb_u32(0x0F, 0x0F, 0x0F); + neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.led2_colour = urgb_u32(0x0F, 0x0F, 0x0F); + neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.mini12864_backlight_colour = urgb_u32(0xFF, 0xFF, 0xFF); // Write data back is_ok = neopixel_led_config_save(); @@ -152,12 +151,10 @@ bool neopixel_led_init(void) { } // Initialise current values - neopixel_led_config.current_mini12864_backlight_colour = neopixel_led_config.eeprom_neopixel_led_metadata.default_mini12864_backlight_colour; - neopixel_led_config.current_led1_colour = neopixel_led_config.eeprom_neopixel_led_metadata.led1_colour1; - neopixel_led_config.current_led2_colour = neopixel_led_config.eeprom_neopixel_led_metadata.led2_colour1; + memcpy(&neopixel_led_config.current_led_colours, &neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours, sizeof(neopixel_led_colours_t)); // Initialize the queue - neopixel_led_config.colour_update_queue = xQueueCreate(2, sizeof(_new_led_colour_t)); + neopixel_led_config.colour_update_queue = xQueueCreate(2, sizeof(neopixel_led_colours_t)); assert(neopixel_led_config.colour_update_queue); // Configure Neopixel (WS2812) with PIO @@ -167,9 +164,9 @@ bool neopixel_led_init(void) { // Set default colour _neopixel_led_set_colour( - neopixel_led_config.current_led1_colour, - neopixel_led_config.current_led2_colour, - neopixel_led_config.current_mini12864_backlight_colour + neopixel_led_config.current_led_colours.led1_colour, + neopixel_led_config.current_led_colours.led2_colour, + neopixel_led_config.current_led_colours.mini12864_backlight_colour ); // Initialize the task @@ -190,7 +187,7 @@ bool neopixel_led_config_save() { } -uint32_t _to_hex_colour(char * string) { +uint32_t hex_string_to_decimal(char * string) { uint32_t value = 0; // Valid hex decimal starts with # @@ -208,33 +205,25 @@ bool http_rest_neopixel_led_config(struct fs_file *file, int num_params, char *p // Control for (int idx = 0; idx < num_params; idx += 1) { - if (strcmp(params[idx], "12864bl") == 0) { + if (strcmp(params[idx], "bl") == 0) { // Remove %23 (#) from the request - neopixel_led_config.eeprom_neopixel_led_metadata.default_mini12864_backlight_colour = _to_hex_colour(values[idx]); - } - else if (strcmp(params[idx], "led1_c1") == 0) { - neopixel_led_config.eeprom_neopixel_led_metadata.led1_colour1 = _to_hex_colour(values[idx]); - } - else if (strcmp(params[idx], "led1_c2") == 0) { - neopixel_led_config.eeprom_neopixel_led_metadata.led1_colour2 = _to_hex_colour(values[idx]); + neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.mini12864_backlight_colour= hex_string_to_decimal(values[idx]); } - else if (strcmp(params[idx], "led2_c1") == 0) { - neopixel_led_config.eeprom_neopixel_led_metadata.led2_colour1 = _to_hex_colour(values[idx]); + else if (strcmp(params[idx], "l1") == 0) { + neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.led1_colour = hex_string_to_decimal(values[idx]); } - else if (strcmp(params[idx], "led2_c2") == 0) { - neopixel_led_config.eeprom_neopixel_led_metadata.led2_colour2 = _to_hex_colour(values[idx]); + else if (strcmp(params[idx], "l2") == 0) { + neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.led2_colour = hex_string_to_decimal(values[idx]); } } // Response snprintf(neopixel_config_json_buffer, sizeof(neopixel_config_json_buffer), - "{\"12864bl\":\"#%06x\",\"led1_c1\":\"#%06x\",\"led1_c2\":\"#%06x\",\"led2_c1\":\"#%06x\",\"led2_c2\":\"#%06x\"}", - neopixel_led_config.eeprom_neopixel_led_metadata.default_mini12864_backlight_colour, - neopixel_led_config.eeprom_neopixel_led_metadata.led1_colour1, - neopixel_led_config.eeprom_neopixel_led_metadata.led1_colour2, - neopixel_led_config.eeprom_neopixel_led_metadata.led2_colour1, - neopixel_led_config.eeprom_neopixel_led_metadata.led2_colour2); + "{\"bl\":\"#%06x\",\"l1\":\"#%06x\",\"l2\":\"#%06x\"}", + neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.mini12864_backlight_colour, + neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.led1_colour, + neopixel_led_config.eeprom_neopixel_led_metadata.default_led_colours.led2_colour); size_t data_length = strlen(neopixel_config_json_buffer); file->data = neopixel_config_json_buffer; diff --git a/src/neopixel_led.h b/src/neopixel_led.h index cc2d3a1..0df1782 100644 --- a/src/neopixel_led.h +++ b/src/neopixel_led.h @@ -5,28 +5,25 @@ #include "http_rest.h" -#define EEPROM_NEOPIXEL_LED_METADATA_REV 1 // 16 byte +#define EEPROM_NEOPIXEL_LED_METADATA_REV 2 // 16 byte + +#define NEOPIXEL_LED_NO_CHANGE (uint32_t)(1 << 24) // High bit is not used for Neopixel RGB +#define NEOPIXEL_LED_DEFAULT_COLOUR (uint32_t)(1 << 25) typedef struct { - uint16_t neopixel_data_rev; - uint32_t default_mini12864_backlight_colour; + uint32_t led1_colour; + uint32_t led2_colour; + uint32_t mini12864_backlight_colour; +} neopixel_led_colours_t; - uint32_t led1_colour1; - uint32_t led1_colour2; - uint32_t led2_colour1; - uint32_t led2_colour2; +typedef struct { + uint16_t neopixel_data_rev; + neopixel_led_colours_t default_led_colours; } eeprom_neopixel_led_metadata_t; -typedef enum { - NEOPIXEL_LED_COLOUR_1, - NEOPIXEL_LED_COLOUR_2, - NEOPIXEL_LED_NO_CHANGE, -} neopixel_led_colour_t; - - #ifdef __cplusplus extern "C" { #endif @@ -34,10 +31,11 @@ extern "C" { bool neopixel_led_init(void); bool neopixel_led_config_save(); -void neopixel_led_set_colour(neopixel_led_colour_t led1_colour, neopixel_led_colour_t led2_colour, bool block_wait); +void neopixel_led_set_colour(uint32_t mini12864_backlight_colour, uint32_t led1_colour, uint32_t led2_colour, bool block_wait); bool http_rest_neopixel_led_config(struct fs_file *file, int num_params, char *params[], char *values[]); uint32_t urgb_u32(uint8_t r, uint8_t g, uint8_t b); +uint32_t hex_string_to_decimal(char * string); #ifdef __cplusplus