From 8fc935bd3360c7649900117ec5fa3cc29f1a9ee9 Mon Sep 17 00:00:00 2001 From: Laurens Valk Date: Fri, 30 Aug 2024 17:26:38 +0200 Subject: [PATCH] pbio/sys/light: Split Bluetooth light patterns. This separates the combined status light patterns into Bluetooth patterns with warning pattern overlays. On hubs with a separate Bluetooth light, these can be displayed separately. This also simplifies the warning light. Instead of having three orange patterns with different meanings, this introduces a single orange pattern for low battery and a red patterm for high current. --- lib/pbio/include/pbsys/light.h | 2 +- lib/pbio/platform/city_hub/pbsysconfig.h | 1 + lib/pbio/platform/debug/pbsysconfig.h | 1 + lib/pbio/platform/essential_hub/pbsysconfig.h | 1 + lib/pbio/platform/ev3/pbsysconfig.h | 1 + lib/pbio/platform/ev3rt/pbsysconfig.h | 1 + lib/pbio/platform/move_hub/pbsysconfig.h | 1 + lib/pbio/platform/nxt/pbsysconfig.h | 1 + lib/pbio/platform/prime_hub/pbsysconfig.h | 1 + lib/pbio/platform/technic_hub/pbsysconfig.h | 1 + lib/pbio/platform/virtual_hub/pbsysconfig.h | 1 + lib/pbio/sys/core.c | 1 - lib/pbio/sys/light.c | 291 +++++++++--------- lib/pbio/sys/light.h | 10 - pybricks/hubs/pb_type_cityhub.c | 2 +- pybricks/hubs/pb_type_essentialhub.c | 2 +- pybricks/hubs/pb_type_movehub.c | 2 +- pybricks/hubs/pb_type_primehub.c | 2 +- pybricks/hubs/pb_type_technichub.c | 2 +- pybricks/hubs/pb_type_virtualhub.c | 2 +- 20 files changed, 171 insertions(+), 155 deletions(-) diff --git a/lib/pbio/include/pbsys/light.h b/lib/pbio/include/pbsys/light.h index 47cae0884..44c491568 100644 --- a/lib/pbio/include/pbsys/light.h +++ b/lib/pbio/include/pbsys/light.h @@ -13,7 +13,7 @@ #if PBSYS_CONFIG_STATUS_LIGHT #include -extern pbio_color_light_t *pbsys_status_light; +extern pbio_color_light_t *pbsys_status_light_main; #endif #if PBSYS_CONFIG_HUB_LIGHT_MATRIX diff --git a/lib/pbio/platform/city_hub/pbsysconfig.h b/lib/pbio/platform/city_hub/pbsysconfig.h index 7482995d5..ab5eba622 100644 --- a/lib/pbio/platform/city_hub/pbsysconfig.h +++ b/lib/pbio/platform/city_hub/pbsysconfig.h @@ -15,5 +15,6 @@ #define PBSYS_CONFIG_STATUS_LIGHT (1) #define PBSYS_CONFIG_STATUS_LIGHT_BATTERY (0) #define PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH (0) +#define PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS (1) #define PBSYS_CONFIG_USER_PROGRAM (1) #define PBSYS_CONFIG_PROGRAM_STOP (1) diff --git a/lib/pbio/platform/debug/pbsysconfig.h b/lib/pbio/platform/debug/pbsysconfig.h index e896006a2..1ec115fc6 100644 --- a/lib/pbio/platform/debug/pbsysconfig.h +++ b/lib/pbio/platform/debug/pbsysconfig.h @@ -13,5 +13,6 @@ #define PBSYS_CONFIG_STATUS_LIGHT (1) #define PBSYS_CONFIG_STATUS_LIGHT_BATTERY (0) #define PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH (0) +#define PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS (1) #define PBSYS_CONFIG_USER_PROGRAM (1) #define PBSYS_CONFIG_PROGRAM_STOP (1) diff --git a/lib/pbio/platform/essential_hub/pbsysconfig.h b/lib/pbio/platform/essential_hub/pbsysconfig.h index 3b7007ab8..2bb8cb795 100644 --- a/lib/pbio/platform/essential_hub/pbsysconfig.h +++ b/lib/pbio/platform/essential_hub/pbsysconfig.h @@ -13,5 +13,6 @@ #define PBSYS_CONFIG_STATUS_LIGHT (1) #define PBSYS_CONFIG_STATUS_LIGHT_BATTERY (1) #define PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH (0) +#define PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS (1) #define PBSYS_CONFIG_USER_PROGRAM (1) #define PBSYS_CONFIG_PROGRAM_STOP (1) diff --git a/lib/pbio/platform/ev3/pbsysconfig.h b/lib/pbio/platform/ev3/pbsysconfig.h index 0b4e6405f..4ea06e594 100644 --- a/lib/pbio/platform/ev3/pbsysconfig.h +++ b/lib/pbio/platform/ev3/pbsysconfig.h @@ -10,6 +10,7 @@ #define PBSYS_CONFIG_STATUS_LIGHT (0) #define PBSYS_CONFIG_STATUS_LIGHT_BATTERY (0) #define PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH (0) +#define PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS (1) #define PBSYS_CONFIG_USER_PROGRAM (1) #define PBSYS_CONFIG_USER_PROGRAM_AUTO_START (1) #define PBSYS_CONFIG_PROGRAM_STOP (0) diff --git a/lib/pbio/platform/ev3rt/pbsysconfig.h b/lib/pbio/platform/ev3rt/pbsysconfig.h index b5f8450fa..c0c7f17d6 100644 --- a/lib/pbio/platform/ev3rt/pbsysconfig.h +++ b/lib/pbio/platform/ev3rt/pbsysconfig.h @@ -9,5 +9,6 @@ #define PBSYS_CONFIG_STATUS_LIGHT (0) #define PBSYS_CONFIG_STATUS_LIGHT_BATTERY (0) #define PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH (0) +#define PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS (1) #define PBSYS_CONFIG_USER_PROGRAM (0) #define PBSYS_CONFIG_PROGRAM_STOP (1) diff --git a/lib/pbio/platform/move_hub/pbsysconfig.h b/lib/pbio/platform/move_hub/pbsysconfig.h index 8f47bd691..9d7e7c5bc 100644 --- a/lib/pbio/platform/move_hub/pbsysconfig.h +++ b/lib/pbio/platform/move_hub/pbsysconfig.h @@ -15,5 +15,6 @@ #define PBSYS_CONFIG_STATUS_LIGHT (1) #define PBSYS_CONFIG_STATUS_LIGHT_BATTERY (0) #define PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH (0) +#define PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS (1) #define PBSYS_CONFIG_USER_PROGRAM (1) #define PBSYS_CONFIG_PROGRAM_STOP (1) diff --git a/lib/pbio/platform/nxt/pbsysconfig.h b/lib/pbio/platform/nxt/pbsysconfig.h index 381dc993e..b88054013 100644 --- a/lib/pbio/platform/nxt/pbsysconfig.h +++ b/lib/pbio/platform/nxt/pbsysconfig.h @@ -9,6 +9,7 @@ #define PBSYS_CONFIG_STATUS_LIGHT (0) #define PBSYS_CONFIG_STATUS_LIGHT_BATTERY (0) #define PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH (0) +#define PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS (1) #define PBSYS_CONFIG_USER_PROGRAM (1) #define PBSYS_CONFIG_USER_PROGRAM_AUTO_START (1) #define PBSYS_CONFIG_PROGRAM_STOP (1) diff --git a/lib/pbio/platform/prime_hub/pbsysconfig.h b/lib/pbio/platform/prime_hub/pbsysconfig.h index 47fe8eec7..17a699910 100644 --- a/lib/pbio/platform/prime_hub/pbsysconfig.h +++ b/lib/pbio/platform/prime_hub/pbsysconfig.h @@ -15,5 +15,6 @@ #define PBSYS_CONFIG_STATUS_LIGHT (1) #define PBSYS_CONFIG_STATUS_LIGHT_BATTERY (1) #define PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH (1) +#define PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS (0) #define PBSYS_CONFIG_USER_PROGRAM (1) #define PBSYS_CONFIG_PROGRAM_STOP (1) diff --git a/lib/pbio/platform/technic_hub/pbsysconfig.h b/lib/pbio/platform/technic_hub/pbsysconfig.h index dd1b7b0ff..24a3efc93 100644 --- a/lib/pbio/platform/technic_hub/pbsysconfig.h +++ b/lib/pbio/platform/technic_hub/pbsysconfig.h @@ -15,5 +15,6 @@ #define PBSYS_CONFIG_STATUS_LIGHT (1) #define PBSYS_CONFIG_STATUS_LIGHT_BATTERY (0) #define PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH (0) +#define PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS (1) #define PBSYS_CONFIG_USER_PROGRAM (1) #define PBSYS_CONFIG_PROGRAM_STOP (1) diff --git a/lib/pbio/platform/virtual_hub/pbsysconfig.h b/lib/pbio/platform/virtual_hub/pbsysconfig.h index e84cfb400..4de115df8 100644 --- a/lib/pbio/platform/virtual_hub/pbsysconfig.h +++ b/lib/pbio/platform/virtual_hub/pbsysconfig.h @@ -9,5 +9,6 @@ #define PBSYS_CONFIG_STATUS_LIGHT (0) #define PBSYS_CONFIG_STATUS_LIGHT_BATTERY (0) #define PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH (0) +#define PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS (1) #define PBSYS_CONFIG_USER_PROGRAM (0) #define PBSYS_CONFIG_PROGRAM_STOP (0) diff --git a/lib/pbio/sys/core.c b/lib/pbio/sys/core.c index a9709d3a0..22c3c4a1f 100644 --- a/lib/pbio/sys/core.c +++ b/lib/pbio/sys/core.c @@ -59,7 +59,6 @@ void pbsys_init(void) { void pbsys_deinit(void) { - pbsys_status_light_bluetooth_deinit(); pbsys_storage_deinit(); uint32_t start = pbdrv_clock_get_ms(); diff --git a/lib/pbio/sys/light.c b/lib/pbio/sys/light.c index 3a3e7371a..2099da22c 100644 --- a/lib/pbio/sys/light.c +++ b/lib/pbio/sys/light.c @@ -22,16 +22,18 @@ #if PBSYS_CONFIG_STATUS_LIGHT typedef enum { - PBSYS_STATUS_LIGHT_INDICATION_NONE, - PBSYS_STATUS_LIGHT_INDICATION_HIGH_CURRENT, - PBSYS_STATUS_LIGHT_INDICATION_LOW_VOLTAGE, - PBSYS_STATUS_LIGHT_INDICATION_BLE_ADVERTISING, - PBSYS_STATUS_LIGHT_INDICATION_BLE_ADVERTISING_AND_LOW_VOLTAGE, - PBSYS_STATUS_LIGHT_INDICATION_BLE_CONNECTED_IDLE, - PBSYS_STATUS_LIGHT_INDICATION_BLE_CONNECTED_IDLE_AND_LOW_VOLTAGE, - PBSYS_STATUS_LIGHT_INDICATION_SHUTDOWN_REQUESTED, - PBSYS_STATUS_LIGHT_INDICATION_SHUTDOWN, -} pbsys_status_light_indication_t; + PBSYS_STATUS_LIGHT_INDICATION_WARNING_NONE, + PBSYS_STATUS_LIGHT_INDICATION_WARNING_HIGH_CURRENT, + PBSYS_STATUS_LIGHT_INDICATION_WARNING_LOW_VOLTAGE, + PBSYS_STATUS_LIGHT_INDICATION_WARNING_SHUTDOWN_REQUESTED, + PBSYS_STATUS_LIGHT_INDICATION_WARNING_SHUTDOWN, +} pbsys_status_light_indication_warning_t; + +typedef enum { + PBSYS_STATUS_LIGHT_INDICATION_BLUETOOTH_BLE_NONE, + PBSYS_STATUS_LIGHT_INDICATION_BLUETOOTH_BLE_ADVERTISING, + PBSYS_STATUS_LIGHT_INDICATION_BLUETOOTH_BLE_CONNECTED_IDLE, +} pbsys_status_light_indication_ble_t; /** A single element of a status light indication pattern. */ typedef struct { @@ -53,74 +55,64 @@ typedef struct { #define PBSYS_STATUS_LIGHT_INDICATION_PATTERN_FOREVER(color_num) \ { .color = color_num, .duration = PBSYS_STATUS_LIGHT_DURATION_FOREVER, } -// Most indications patterns are selected to match the official LEGO firmware -// with the exception that the BLE advertising color is blue in Pybricks instead -// of white so that users can known which firmware is loaded when the hub is -// powered on. Timing is slightly different in some cases since we use 50 ms -// interval instead of 10 ms. Patterns are rotated compared to LEGO firmware -// so that we don't have the light off at the beginning of the pattern. static const pbsys_status_light_indication_pattern_element_t *const -pbsys_status_light_indication_pattern[] = { - [PBSYS_STATUS_LIGHT_INDICATION_HIGH_CURRENT] = +pbsys_status_light_indication_pattern_warning[] = { + // Transparent, i.e. no warning overlay. + [PBSYS_STATUS_LIGHT_INDICATION_WARNING_NONE] = (const pbsys_status_light_indication_pattern_element_t[]) { - { .color = PBIO_COLOR_ORANGE, .duration = 1 }, - { .color = PBIO_COLOR_BLACK, .duration = 1 }, - { .color = PBIO_COLOR_ORANGE, .duration = 1 }, - { .color = PBIO_COLOR_BLACK, .duration = 1 }, - { .color = PBIO_COLOR_ORANGE, .duration = 1 }, - { .color = PBIO_COLOR_NONE, .duration = 22 }, - { .color = PBIO_COLOR_BLACK, .duration = 1 }, - PBSYS_STATUS_LIGHT_INDICATION_PATTERN_REPEAT + PBSYS_STATUS_LIGHT_INDICATION_PATTERN_FOREVER(PBIO_COLOR_NONE), }, - [PBSYS_STATUS_LIGHT_INDICATION_LOW_VOLTAGE] = + // Two red blinks, pause, then repeat. Overlays on lower priority signal. + [PBSYS_STATUS_LIGHT_INDICATION_WARNING_HIGH_CURRENT] = (const pbsys_status_light_indication_pattern_element_t[]) { - { .color = PBIO_COLOR_ORANGE, .duration = 6 }, - { .color = PBIO_COLOR_BLACK, .duration = 8 }, - { .color = PBIO_COLOR_ORANGE, .duration = 6 }, - { .color = PBIO_COLOR_BLACK, .duration = 4 }, - { .color = PBIO_COLOR_NONE, .duration = 16 }, - { .color = PBIO_COLOR_BLACK, .duration = 4 }, + { .color = PBIO_COLOR_RED, .duration = 1 }, + { .color = PBIO_COLOR_NONE, .duration = 2 }, + { .color = PBIO_COLOR_RED, .duration = 1 }, + { .color = PBIO_COLOR_NONE, .duration = 22 }, PBSYS_STATUS_LIGHT_INDICATION_PATTERN_REPEAT }, - [PBSYS_STATUS_LIGHT_INDICATION_BLE_ADVERTISING] = + // Two orange blinks, pause, then repeat. Overlays on lower priority signal. + [PBSYS_STATUS_LIGHT_INDICATION_WARNING_LOW_VOLTAGE] = (const pbsys_status_light_indication_pattern_element_t[]) { - { .color = PBIO_COLOR_BLUE, .duration = 1 }, - { .color = PBIO_COLOR_BLACK, .duration = 2 }, - { .color = PBIO_COLOR_BLUE, .duration = 1 }, - { .color = PBIO_COLOR_BLACK, .duration = 22 }, + { .color = PBIO_COLOR_ORANGE, .duration = 1 }, + { .color = PBIO_COLOR_NONE, .duration = 2 }, + { .color = PBIO_COLOR_ORANGE, .duration = 1 }, + { .color = PBIO_COLOR_NONE, .duration = 22 }, PBSYS_STATUS_LIGHT_INDICATION_PATTERN_REPEAT }, - [PBSYS_STATUS_LIGHT_INDICATION_BLE_ADVERTISING_AND_LOW_VOLTAGE] = + // Rapidly repeating blue blink. Overrides lower priority signal. + [PBSYS_STATUS_LIGHT_INDICATION_WARNING_SHUTDOWN_REQUESTED] = (const pbsys_status_light_indication_pattern_element_t[]) { - { .color = PBIO_COLOR_ORANGE, .duration = 1 }, - { .color = PBIO_COLOR_BLACK, .duration = 2 }, - { .color = PBIO_COLOR_ORANGE, .duration = 1 }, - { .color = PBIO_COLOR_BLACK, .duration = 22 }, + { .color = PBIO_COLOR_BLACK, .duration = 1 }, + { .color = PBIO_COLOR_BLUE, .duration = 1 }, PBSYS_STATUS_LIGHT_INDICATION_PATTERN_REPEAT }, - [PBSYS_STATUS_LIGHT_INDICATION_BLE_CONNECTED_IDLE] = + // Black, so override to be off. Overrides lower priority signal. + [PBSYS_STATUS_LIGHT_INDICATION_WARNING_SHUTDOWN] = (const pbsys_status_light_indication_pattern_element_t[]) { - PBSYS_STATUS_LIGHT_INDICATION_PATTERN_FOREVER(PBIO_COLOR_BLUE), + PBSYS_STATUS_LIGHT_INDICATION_PATTERN_FOREVER(PBIO_COLOR_BLACK), }, - [PBSYS_STATUS_LIGHT_INDICATION_BLE_CONNECTED_IDLE_AND_LOW_VOLTAGE] = +}; + +static const pbsys_status_light_indication_pattern_element_t *const +pbsys_status_light_indication_pattern_ble[] = { + [PBSYS_STATUS_LIGHT_INDICATION_BLUETOOTH_BLE_NONE] = (const pbsys_status_light_indication_pattern_element_t[]) { - { .color = PBIO_COLOR_ORANGE, .duration = 6 }, - { .color = PBIO_COLOR_BLUE, .duration = 8 }, - { .color = PBIO_COLOR_ORANGE, .duration = 6 }, - { .color = PBIO_COLOR_BLUE, .duration = 4 }, - { .color = PBIO_COLOR_NONE, .duration = 16 }, - { .color = PBIO_COLOR_BLUE, .duration = 4 }, - PBSYS_STATUS_LIGHT_INDICATION_PATTERN_REPEAT + PBSYS_STATUS_LIGHT_INDICATION_PATTERN_FOREVER(PBIO_COLOR_NONE), }, - [PBSYS_STATUS_LIGHT_INDICATION_SHUTDOWN_REQUESTED] = + // Two blue blinks, pause, then repeat. + [PBSYS_STATUS_LIGHT_INDICATION_BLUETOOTH_BLE_ADVERTISING] = (const pbsys_status_light_indication_pattern_element_t[]) { - { .color = PBIO_COLOR_BLACK, .duration = 1 }, { .color = PBIO_COLOR_BLUE, .duration = 1 }, + { .color = PBIO_COLOR_BLACK, .duration = 2 }, + { .color = PBIO_COLOR_BLUE, .duration = 1 }, + { .color = PBIO_COLOR_BLACK, .duration = 22 }, PBSYS_STATUS_LIGHT_INDICATION_PATTERN_REPEAT }, - [PBSYS_STATUS_LIGHT_INDICATION_SHUTDOWN] = + // Blue, always on. + [PBSYS_STATUS_LIGHT_INDICATION_BLUETOOTH_BLE_CONNECTED_IDLE] = (const pbsys_status_light_indication_pattern_element_t[]) { - PBSYS_STATUS_LIGHT_INDICATION_PATTERN_FOREVER(PBIO_COLOR_BLACK), + PBSYS_STATUS_LIGHT_INDICATION_PATTERN_FOREVER(PBIO_COLOR_BLUE), }, }; @@ -134,31 +126,44 @@ typedef struct { } pbsys_status_light_pattern_state_t; typedef struct { + /** The color LED device */ + pbdrv_led_dev_t *led; /** The most recent user color value. */ pbio_color_hsv_t user_color; /** The user program is currently allowed to control the status light. */ bool allow_user_update; /** The current pattern state. */ pbsys_status_light_pattern_state_t pattern_state; + /** The current pattern overlay state. */ + pbsys_status_light_pattern_state_t pattern_overlay_state; /** Color light struct for PBIO light implementation. */ pbio_color_light_t color_light; } pbsys_status_light_t; -static pbsys_status_light_t pbsys_status_light_instance; - /** The system status light instance. */ -pbio_color_light_t *pbsys_status_light = &pbsys_status_light_instance.color_light; +static pbsys_status_light_t pbsys_status_light_instance_main; +pbio_color_light_t *pbsys_status_light_main = &pbsys_status_light_instance_main.color_light; + +#if PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH +static pbsys_status_light_t pbsys_status_light_instance_ble; +static pbsys_status_light_pattern_state_t *ble_pattern_state = &pbsys_status_light_instance_ble.pattern_state; +static pbsys_status_light_pattern_state_t *warning_pattern_state = &pbsys_status_light_instance_main.pattern_state; +#else +static pbsys_status_light_pattern_state_t *ble_pattern_state = &pbsys_status_light_instance_main.pattern_state; +static pbsys_status_light_pattern_state_t *warning_pattern_state = &pbsys_status_light_instance_main.pattern_overlay_state; +#endif + static pbio_error_t pbsys_status_light_set_hsv(pbio_color_light_t *light, const pbio_color_hsv_t *hsv) { pbsys_status_light_t *instance = PBIO_CONTAINER_OF(light, pbsys_status_light_t, color_light); instance->user_color = *hsv; + + if (!instance->led) { + return PBIO_ERROR_NO_DEV; + } + if (instance->allow_user_update) { - // FIXME: currently system status light is hard-coded as LED at index 0 - // on all platforms - pbdrv_led_dev_t *led; - if (pbdrv_led_get_dev(0, &led) == PBIO_SUCCESS) { - return pbdrv_led_set_hsv(led, hsv); - } + return pbdrv_led_set_hsv(instance->led, hsv); } return PBIO_SUCCESS; @@ -169,46 +174,54 @@ static const pbio_color_light_funcs_t pbsys_status_light_funcs = { }; void pbsys_status_light_init(void) { - pbio_color_light_init(pbsys_status_light, &pbsys_status_light_funcs); + // REVISIT: Light ids currently hard-coded. + pbdrv_led_get_dev(0, &pbsys_status_light_instance_main.led); + pbio_color_light_init(pbsys_status_light_main, &pbsys_status_light_funcs); + #if PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH + pbdrv_led_get_dev(2, &pbsys_status_light_instance_ble.led); + pbio_color_light_init(&pbsys_status_light_instance_ble.color_light, &pbsys_status_light_funcs); + #endif } static void pbsys_status_light_handle_status_change(void) { - pbsys_status_light_pattern_state_t *state = &pbsys_status_light_instance.pattern_state; - pbsys_status_light_indication_t new_indication = PBSYS_STATUS_LIGHT_INDICATION_NONE; - bool ble_connected_idle = pbsys_status_test(PBIO_PYBRICKS_STATUS_BLE_HOST_CONNECTED) && - !pbsys_status_test(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING); - bool ble_advertising = pbsys_status_test(PBIO_PYBRICKS_STATUS_BLE_ADVERTISING); - bool low_voltage = pbsys_status_test(PBIO_PYBRICKS_STATUS_BATTERY_LOW_VOLTAGE_WARNING); - bool high_current = pbsys_status_test(PBIO_PYBRICKS_STATUS_BATTERY_HIGH_CURRENT); - bool shutdown_requested = pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST); - bool shutdown = pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN); - - // This determines which indication has the highest precedence. - if (shutdown) { - new_indication = PBSYS_STATUS_LIGHT_INDICATION_SHUTDOWN; - } else if (shutdown_requested) { - new_indication = PBSYS_STATUS_LIGHT_INDICATION_SHUTDOWN_REQUESTED; - } else if (ble_advertising && low_voltage) { - new_indication = PBSYS_STATUS_LIGHT_INDICATION_BLE_ADVERTISING_AND_LOW_VOLTAGE; - } else if (ble_advertising) { - new_indication = PBSYS_STATUS_LIGHT_INDICATION_BLE_ADVERTISING; - } else if (high_current) { - new_indication = PBSYS_STATUS_LIGHT_INDICATION_HIGH_CURRENT; - } else if (ble_connected_idle && low_voltage) { - new_indication = PBSYS_STATUS_LIGHT_INDICATION_BLE_CONNECTED_IDLE_AND_LOW_VOLTAGE; - } else if (ble_connected_idle) { - new_indication = PBSYS_STATUS_LIGHT_INDICATION_BLE_CONNECTED_IDLE; - } else if (low_voltage) { - new_indication = PBSYS_STATUS_LIGHT_INDICATION_LOW_VOLTAGE; + + // Warning pattern precedence. + pbsys_status_light_indication_warning_t warning_indication = PBSYS_STATUS_LIGHT_INDICATION_WARNING_NONE; + if (pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN)) { + warning_indication = PBSYS_STATUS_LIGHT_INDICATION_WARNING_SHUTDOWN; + } else if (pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST)) { + warning_indication = PBSYS_STATUS_LIGHT_INDICATION_WARNING_SHUTDOWN_REQUESTED; + } else if (pbsys_status_test(PBIO_PYBRICKS_STATUS_BATTERY_HIGH_CURRENT)) { + warning_indication = PBSYS_STATUS_LIGHT_INDICATION_WARNING_HIGH_CURRENT; + } else if (pbsys_status_test(PBIO_PYBRICKS_STATUS_BATTERY_LOW_VOLTAGE_WARNING)) { + warning_indication = PBSYS_STATUS_LIGHT_INDICATION_WARNING_LOW_VOLTAGE; } - // if the indication changed, then reset the indication pattern to the beginning - if (state->indication != new_indication) { - state->indication = new_indication; - state->pattern_index = state->pattern_count = 0; + // BLE pattern precedence. + pbsys_status_light_indication_ble_t ble_indication = PBSYS_STATUS_LIGHT_INDICATION_BLUETOOTH_BLE_NONE; + if (pbsys_status_test(PBIO_PYBRICKS_STATUS_BLE_ADVERTISING)) { + ble_indication = PBSYS_STATUS_LIGHT_INDICATION_BLUETOOTH_BLE_ADVERTISING; + } else if (pbsys_status_test(PBIO_PYBRICKS_STATUS_BLE_HOST_CONNECTED) + #if !PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH + // Hubs without Bluetooth light show idle state only when program not running. + && !pbsys_status_test(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING) + #endif + ) { + ble_indication = PBSYS_STATUS_LIGHT_INDICATION_BLUETOOTH_BLE_CONNECTED_IDLE; + } + + // If the indication changed, then reset the indication pattern to the + // beginning. Reset both so that patterns with the same length stay in sync. + if (ble_pattern_state->indication != ble_indication || warning_pattern_state->indication != warning_indication) { + ble_pattern_state->indication = ble_indication; + ble_pattern_state->pattern_index = ble_pattern_state->pattern_count = 0; + + warning_pattern_state->indication = warning_indication; + warning_pattern_state->pattern_index = warning_pattern_state->pattern_count = 0; } } +#if PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS static uint8_t animation_progress; static uint32_t default_user_program_light_animation_next(pbio_light_animation_t *animation) { @@ -224,23 +237,26 @@ static uint32_t default_user_program_light_animation_next(pbio_light_animation_t animation_progress_max - animation_progress, }; - pbsys_status_light->funcs->set_hsv(pbsys_status_light, &hsv); + pbsys_status_light_main->funcs->set_hsv(pbsys_status_light_main, &hsv); // This increment controls the speed of the pattern and wraps on completion animation_progress = (animation_progress + 4) % animation_progress_max; return 40; } +#endif // PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS void pbsys_status_light_handle_event(process_event_t event, process_data_t data) { if (event == PBIO_EVENT_STATUS_SET || event == PBIO_EVENT_STATUS_CLEARED) { pbsys_status_light_handle_status_change(); } + #if PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS if (event == PBIO_EVENT_STATUS_SET && (pbio_pybricks_status_t)(intptr_t)data == PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING) { animation_progress = 0; - pbio_light_animation_init(&pbsys_status_light->animation, default_user_program_light_animation_next); - pbio_light_animation_start(&pbsys_status_light->animation); + pbio_light_animation_init(&pbsys_status_light_main->animation, default_user_program_light_animation_next); + pbio_light_animation_start(&pbsys_status_light_main->animation); } + #endif // PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS } /** @@ -341,30 +357,46 @@ pbsys_battery_light_state_t pbsys_battery_light_get_state(void) { #endif // PBSYS_CONFIG_STATUS_LIGHT_BATTERY +static void pbsys_status_light_set_pattern_or_user_color(pbsys_status_light_t *instance, pbio_color_t new_color) { + if (!instance->led) { + return; + } + if (instance->allow_user_update) { + pbdrv_led_set_hsv(instance->led, &instance->user_color); + } else { + pbio_color_hsv_t hsv; + pbio_color_to_hsv(new_color, &hsv); + pbdrv_led_set_hsv(instance->led, &hsv); + } +} + void pbsys_status_light_poll(void) { - pbsys_status_light_t *instance = &pbsys_status_light_instance; - pbio_color_t new_color = pbsys_status_light_pattern_next( - &instance->pattern_state, pbsys_status_light_indication_pattern); + pbio_color_t new_warning_color = pbsys_status_light_pattern_next( + warning_pattern_state, pbsys_status_light_indication_pattern_warning); + + pbio_color_t new_ble_color = pbsys_status_light_pattern_next( + ble_pattern_state, pbsys_status_light_indication_pattern_ble); + + + pbio_color_t new_main_color = new_warning_color; + #if !PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH + // Overlay warnings on ble state on hubs with just one light. + if (new_warning_color == PBIO_COLOR_NONE) { + new_main_color = new_ble_color; + } + #endif // If the new system indication is not overriding the status light and a user // program is running, then we can allow the user program to directly change // the status light. - instance->allow_user_update = - new_color == PBIO_COLOR_NONE && pbsys_status_test(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING); + pbsys_status_light_instance_main.allow_user_update = + new_main_color == PBIO_COLOR_NONE && pbsys_status_test(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING); - // FIXME: currently system status light is hard-coded as LED at index 0 on - // all platforms - pbdrv_led_dev_t *led; - if (pbdrv_led_get_dev(0, &led) == PBIO_SUCCESS) { - if (instance->allow_user_update) { - pbdrv_led_set_hsv(led, &instance->user_color); - } else { - pbio_color_hsv_t hsv; - pbio_color_to_hsv(new_color, &hsv); - pbdrv_led_set_hsv(led, &hsv); - } - } + pbsys_status_light_set_pattern_or_user_color(&pbsys_status_light_instance_main, new_main_color); + #if PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH + pbsys_status_light_set_pattern_or_user_color(&pbsys_status_light_instance_ble, new_ble_color); + #endif // REVISIT: We should be able to make updating the state event driven instead of polled. #if PBSYS_CONFIG_STATUS_LIGHT_BATTERY @@ -376,13 +408,14 @@ void pbsys_status_light_poll(void) { pbsys_battery_light_pattern_state.pattern_count = pbsys_battery_light_pattern_state.pattern_index = 0; } - new_color = pbsys_status_light_pattern_next( + pbio_color_t new_battery_color = pbsys_status_light_pattern_next( &pbsys_battery_light_pattern_state, pbsys_battery_light_patterns); - // FIXME: battery light is currently hard-coded to id 1 on all platforms + // FIXME: Use sys light instance like the other lights. + pbdrv_led_dev_t *led; if (pbdrv_led_get_dev(1, &led) == PBIO_SUCCESS) { pbio_color_hsv_t hsv; - pbio_color_to_hsv(new_color, &hsv); + pbio_color_to_hsv(new_battery_color, &hsv); pbdrv_led_set_hsv(led, &hsv); } @@ -390,19 +423,3 @@ void pbsys_status_light_poll(void) { } #endif // PBSYS_CONFIG_STATUS_LIGHT - -#if PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH -void pbsys_status_light_bluetooth_set_color(pbio_color_t color) { - pbdrv_led_dev_t *led; - // FIXME: Bluetooth light is currently hard-coded to id 2 on all platforms - if (pbdrv_led_get_dev(2, &led) == PBIO_SUCCESS) { - pbio_color_hsv_t hsv; - pbio_color_to_hsv(color, &hsv); - pbdrv_led_set_hsv(led, &hsv); - } -} - -void pbsys_status_light_bluetooth_deinit(void) { - pbsys_status_light_bluetooth_set_color(PBIO_COLOR_NONE); -} -#endif diff --git a/lib/pbio/sys/light.h b/lib/pbio/sys/light.h index cd0cde733..00c0c41cb 100644 --- a/lib/pbio/sys/light.h +++ b/lib/pbio/sys/light.h @@ -19,14 +19,4 @@ void pbsys_status_light_poll(void); #define pbsys_status_light_poll() #endif -#if PBSYS_CONFIG_STATUS_LIGHT_BLUETOOTH -void pbsys_status_light_bluetooth_set_color(pbio_color_t color); -void pbsys_status_light_bluetooth_deinit(void); -#else -static inline void pbsys_status_light_bluetooth_set_color(pbio_color_t color) { -} -static inline void pbsys_status_light_bluetooth_deinit(void) { -} -#endif - #endif // _PBSYS_SYS_LIGHT_H_ diff --git a/pybricks/hubs/pb_type_cityhub.c b/pybricks/hubs/pb_type_cityhub.c index ff020c1ec..64e3521cc 100644 --- a/pybricks/hubs/pb_type_cityhub.c +++ b/pybricks/hubs/pb_type_cityhub.c @@ -38,7 +38,7 @@ static mp_obj_t hubs_CityHub_make_new(const mp_obj_type_t *type, size_t n_args, self->ble = pb_type_BLE_new(broadcast_channel_in, observe_channels_in); #endif self->button = pb_type_Keypad_obj_new(pb_type_button_pressed_hub_single_button); - self->light = common_ColorLight_internal_obj_new(pbsys_status_light); + self->light = common_ColorLight_internal_obj_new(pbsys_status_light_main); self->system = MP_OBJ_FROM_PTR(&pb_type_System); return MP_OBJ_FROM_PTR(self); } diff --git a/pybricks/hubs/pb_type_essentialhub.c b/pybricks/hubs/pb_type_essentialhub.c index ffe3bc774..6066eb689 100644 --- a/pybricks/hubs/pb_type_essentialhub.c +++ b/pybricks/hubs/pb_type_essentialhub.c @@ -55,7 +55,7 @@ static mp_obj_t hubs_EssentialHub_make_new(const mp_obj_type_t *type, size_t n_a self->buttons = pb_type_Keypad_obj_new(pb_type_button_pressed_hub_single_button); self->charger = pb_type_Charger_obj_new(); self->imu = pb_type_IMU_obj_new(MP_OBJ_FROM_PTR(self), top_side_in, front_side_in); - self->light = common_ColorLight_internal_obj_new(pbsys_status_light); + self->light = common_ColorLight_internal_obj_new(pbsys_status_light_main); self->system = MP_OBJ_FROM_PTR(&pb_type_System); return MP_OBJ_FROM_PTR(self); } diff --git a/pybricks/hubs/pb_type_movehub.c b/pybricks/hubs/pb_type_movehub.c index f5641baf9..4e6537078 100644 --- a/pybricks/hubs/pb_type_movehub.c +++ b/pybricks/hubs/pb_type_movehub.c @@ -331,7 +331,7 @@ static mp_obj_t hubs_MoveHub_make_new(const mp_obj_type_t *type, size_t n_args, #endif self->button = pb_type_Keypad_obj_new(pb_type_button_pressed_hub_single_button); self->imu = hubs_MoveHub_IMU_make_new(top_side_in, front_side_in); - self->light = common_ColorLight_internal_obj_new(pbsys_status_light); + self->light = common_ColorLight_internal_obj_new(pbsys_status_light_main); self->system = MP_OBJ_FROM_PTR(&pb_type_System); return MP_OBJ_FROM_PTR(self); } diff --git a/pybricks/hubs/pb_type_primehub.c b/pybricks/hubs/pb_type_primehub.c index 0e3452d8c..cffe79b66 100644 --- a/pybricks/hubs/pb_type_primehub.c +++ b/pybricks/hubs/pb_type_primehub.c @@ -80,7 +80,7 @@ static mp_obj_t hubs_PrimeHub_make_new(const mp_obj_type_t *type, size_t n_args, self->charger = pb_type_Charger_obj_new(); self->display = pb_type_LightMatrix_obj_new(pbsys_hub_light_matrix); self->imu = pb_type_IMU_obj_new(MP_OBJ_FROM_PTR(self), top_side_in, front_side_in); - self->light = common_ColorLight_internal_obj_new(pbsys_status_light); + self->light = common_ColorLight_internal_obj_new(pbsys_status_light_main); self->speaker = mp_call_function_0(MP_OBJ_FROM_PTR(&pb_type_Speaker)); self->system = MP_OBJ_FROM_PTR(&pb_type_System); return MP_OBJ_FROM_PTR(self); diff --git a/pybricks/hubs/pb_type_technichub.c b/pybricks/hubs/pb_type_technichub.c index bb1225127..10b0960ec 100644 --- a/pybricks/hubs/pb_type_technichub.c +++ b/pybricks/hubs/pb_type_technichub.c @@ -44,7 +44,7 @@ static mp_obj_t hubs_TechnicHub_make_new(const mp_obj_type_t *type, size_t n_arg #endif self->button = pb_type_Keypad_obj_new(pb_type_button_pressed_hub_single_button); self->imu = pb_type_IMU_obj_new(MP_OBJ_FROM_PTR(self), top_side_in, front_side_in); - self->light = common_ColorLight_internal_obj_new(pbsys_status_light); + self->light = common_ColorLight_internal_obj_new(pbsys_status_light_main); self->system = MP_OBJ_FROM_PTR(&pb_type_System); return MP_OBJ_FROM_PTR(self); } diff --git a/pybricks/hubs/pb_type_virtualhub.c b/pybricks/hubs/pb_type_virtualhub.c index b040e6f3e..d4a490edc 100644 --- a/pybricks/hubs/pb_type_virtualhub.c +++ b/pybricks/hubs/pb_type_virtualhub.c @@ -31,7 +31,7 @@ static mp_obj_t hubs_VirtualHub_make_new(const mp_obj_type_t *type, size_t n_arg self->battery = MP_OBJ_FROM_PTR(&pb_module_battery); self->buttons = pb_type_Keypad_obj_new(pb_type_button_pressed_hub_single_button); // FIXME: Implement lights. - // self->light = common_ColorLight_internal_obj_new(pbsys_status_light); + // self->light = common_ColorLight_internal_obj_new(pbsys_status_light_main); self->system = MP_OBJ_FROM_PTR(&pb_type_System); return MP_OBJ_FROM_PTR(self); }