diff --git a/CHANGELOG.md b/CHANGELOG.md index a82f903ff..d71da9607 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,9 @@ the hub ([pybricks-micropython#250]). - Improved font for the digits ``0--9`` when displaying them with `hub.display.char(str(x))` ([pybricks-micropython#253]). +- On SPIKE Prime Hub and Robot Inventor Hub, moved Bluetooth indications to + the Bluetooth light. Only warning lights will be shown on the main button + light. See ([support#1716]) and ([pybricks-micropython#261]). ### Fixed - Fixed not able to connect to new Technic Move hub with `LWP3Device()`. @@ -44,11 +47,13 @@ [pybricks-micropython#250]: https://github.com/pybricks/pybricks-micropython/pull/250 [pybricks-micropython#253]: https://github.com/pybricks/pybricks-micropython/pull/253 [pybricks-micropython#254]: https://github.com/pybricks/pybricks-micropython/pull/254 +[pybricks-micropython#261]: https://github.com/pybricks/pybricks-micropython/pull/261 [support#1429]: https://github.com/pybricks/support/issues/1429 [support#1460]: https://github.com/pybricks/support/issues/1460 [support#1615]: https://github.com/pybricks/support/issues/1615 [support#1622]: https://github.com/pybricks/support/issues/1622 [support#1678]: https://github.com/pybricks/support/issues/1678 +[support#1716]: https://github.com/pybricks/support/issues/1716 ## [3.5.0] - 2024-04-11 diff --git a/lib/pbio/include/pbio/protocol.h b/lib/pbio/include/pbio/protocol.h index dcc104746..5932fc539 100644 --- a/lib/pbio/include/pbio/protocol.h +++ b/lib/pbio/include/pbio/protocol.h @@ -294,7 +294,7 @@ typedef enum { */ PBIO_PYBRICKS_STATUS_BLE_ADVERTISING = 3, /** - * Bluetooth Low Energy has low signal. + * Bluetooth Low Energy has low signal. Not implemented or used anywhere. * * @since Pybricks Profile v1.0.0 */ @@ -323,6 +323,12 @@ typedef enum { * @since Pybricks Profile v1.2.0 */ PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST = 8, + /** + * Hub is connected to a host (like Pybricks Code) via BLE. + * + * @since Pybricks Profile v1.4.0 + */ + PBIO_PYBRICKS_STATUS_BLE_HOST_CONNECTED = 9, /** Total number of indications. */ NUM_PBIO_PYBRICKS_STATUS, } pbio_pybricks_status_t; 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 72ba157de..982c8428f 100644 --- a/lib/pbio/platform/city_hub/pbsysconfig.h +++ b/lib/pbio/platform/city_hub/pbsysconfig.h @@ -20,5 +20,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 7726dd886..1a14284c8 100644 --- a/lib/pbio/platform/essential_hub/pbsysconfig.h +++ b/lib/pbio/platform/essential_hub/pbsysconfig.h @@ -18,5 +18,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 90a8bba36..705f10466 100644 --- a/lib/pbio/platform/ev3/pbsysconfig.h +++ b/lib/pbio/platform/ev3/pbsysconfig.h @@ -15,6 +15,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 78bb8d799..e970e4be8 100644 --- a/lib/pbio/platform/ev3rt/pbsysconfig.h +++ b/lib/pbio/platform/ev3rt/pbsysconfig.h @@ -14,5 +14,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 c26beba8e..be62295ca 100644 --- a/lib/pbio/platform/move_hub/pbsysconfig.h +++ b/lib/pbio/platform/move_hub/pbsysconfig.h @@ -20,5 +20,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 b5a11f5e3..5c10864e2 100644 --- a/lib/pbio/platform/nxt/pbsysconfig.h +++ b/lib/pbio/platform/nxt/pbsysconfig.h @@ -14,6 +14,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 d5f744ab9..561140b39 100644 --- a/lib/pbio/platform/prime_hub/pbsysconfig.h +++ b/lib/pbio/platform/prime_hub/pbsysconfig.h @@ -20,5 +20,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 0b77fb356..e44afeced 100644 --- a/lib/pbio/platform/technic_hub/pbsysconfig.h +++ b/lib/pbio/platform/technic_hub/pbsysconfig.h @@ -20,5 +20,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/bluetooth.c b/lib/pbio/sys/bluetooth.c index c5a48d57f..302057100 100644 --- a/lib/pbio/sys/bluetooth.c +++ b/lib/pbio/sys/bluetooth.c @@ -23,7 +23,6 @@ #include #include -#include "light.h" #include "storage.h" // REVISIT: this can be the negotiated MTU - 3 to allow for better throughput @@ -291,34 +290,23 @@ PROCESS_THREAD(pbsys_bluetooth_process, ev, data) { PROCESS_BEGIN(); - // Ensures Bluetooth preferences are loaded before we read them. - #if PBSYS_CONFIG_STORAGE && PBSYS_CONFIG_BLUETOOTH_TOGGLE - PROCESS_WAIT_EVENT_UNTIL(pbsys_storage_settings_get_settings() != NULL); - #endif // PBSYS_CONFIG_STORAGE && PBSYS_CONFIG_BLUETOOTH_TOGGLE - pbdrv_bluetooth_set_on_event(pbsys_bluetooth_process_poll); pbdrv_bluetooth_set_receive_handler(handle_receive); - while (!pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN)) { - - // Show inactive status only if user requested Bluetooth as disabled to - // avoid always flashing red in between program runs when disconnected. - if (!pbsys_storage_settings_bluetooth_enabled()) { - pbsys_status_light_bluetooth_set_color(PBIO_COLOR_RED); - } + while (!pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST)) { // make sure the Bluetooth chip is in reset long enough to actually reset etimer_set(&timer, 150); PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER && etimer_expired(&timer)); // Wait until Bluetooth enabled requested by user, but stop waiting on shutdown. - PROCESS_WAIT_UNTIL(pbsys_storage_settings_bluetooth_enabled() || pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN)); - if (pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN)) { + // If storage is not yet loaded, this will wait for that too. + PROCESS_WAIT_UNTIL(pbsys_storage_settings_bluetooth_enabled() || pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST)); + if (pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST)) { break; } - // Enable Bluetooth, and show on Bluetooth light if available. - pbsys_status_light_bluetooth_set_color(PBIO_COLOR_BLUE); + // Enable Bluetooth. pbdrv_bluetooth_power_on(true); PROCESS_WAIT_UNTIL(pbdrv_bluetooth_is_ready()); @@ -332,7 +320,7 @@ PROCESS_THREAD(pbsys_bluetooth_process, ev, data) { PROCESS_WAIT_UNTIL( pbdrv_bluetooth_is_connected(PBDRV_BLUETOOTH_CONNECTION_LE) || pbsys_status_test(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING) - || pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN) + || pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST) || !pbsys_storage_settings_bluetooth_enabled()); // Now change the state depending on which of the above was triggered. @@ -340,8 +328,11 @@ PROCESS_THREAD(pbsys_bluetooth_process, ev, data) { // If connected, advertising stops automatically. Otherwise manually // stop advertising (if the user code started using the button or we // are shutting down or or BLE became disabled). - if (!pbdrv_bluetooth_is_connected(PBDRV_BLUETOOTH_CONNECTION_LE)) { + if (pbdrv_bluetooth_is_connected(PBDRV_BLUETOOTH_CONNECTION_LE)) { + pbsys_status_set(PBIO_PYBRICKS_STATUS_BLE_HOST_CONNECTED); + } else { pbdrv_bluetooth_stop_advertising(); + pbsys_status_clear(PBIO_PYBRICKS_STATUS_BLE_HOST_CONNECTED); // REVISIT: also via state... } // In all cases, clear the advertising flag to stop blinking. @@ -354,7 +345,7 @@ PROCESS_THREAD(pbsys_bluetooth_process, ev, data) { // Bluetooth loop below and go directly to the disable step below it. while (pbsys_storage_settings_bluetooth_enabled() && pbdrv_bluetooth_is_connected(PBDRV_BLUETOOTH_CONNECTION_LE) - && !pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN)) { + && !pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST)) { if (pbdrv_bluetooth_is_connected(PBDRV_BLUETOOTH_CONNECTION_PYBRICKS)) { // Since pbsys status events are broadcast to all processes, this @@ -388,6 +379,9 @@ PROCESS_THREAD(pbsys_bluetooth_process, ev, data) { PROCESS_WAIT_EVENT(); } + pbsys_status_clear(PBIO_PYBRICKS_STATUS_BLE_HOST_CONNECTED); + pbsys_status_clear(PBIO_PYBRICKS_STATUS_BLE_ADVERTISING); + reset_all(); PROCESS_WAIT_WHILE(pbsys_status_test(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING)); 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 6bc15d7b4..f6a4e71f4 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_LOW_SIGNAL, - PBSYS_STATUS_LIGHT_INDICATION_BLE_LOW_SIGNAL_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,78 +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 = 2 }, + { .color = PBIO_COLOR_NONE, .duration = 2 }, + { .color = PBIO_COLOR_RED, .duration = 2 }, + { .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 = 2 }, + { .color = PBIO_COLOR_NONE, .duration = 2 }, + { .color = PBIO_COLOR_ORANGE, .duration = 2 }, + { .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_LOW_SIGNAL] = + // Black, so override to be off. Overrides lower priority signal. + [PBSYS_STATUS_LIGHT_INDICATION_WARNING_SHUTDOWN] = (const pbsys_status_light_indication_pattern_element_t[]) { - { .color = PBIO_COLOR_NONE, .duration = 8 }, - { .color = PBIO_COLOR_WHITE, .duration = 1 }, - PBSYS_STATUS_LIGHT_INDICATION_PATTERN_REPEAT + PBSYS_STATUS_LIGHT_INDICATION_PATTERN_FOREVER(PBIO_COLOR_BLACK), }, - [PBSYS_STATUS_LIGHT_INDICATION_BLE_LOW_SIGNAL_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_NONE, .duration = 8 }, - { .color = PBIO_COLOR_BLACK, .duration = 4 }, - { .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 = 8 }, - { .color = PBIO_COLOR_WHITE, .duration = 1 }, - 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_BLUE, .duration = 2 }, + { .color = PBIO_COLOR_BLACK, .duration = 2 }, + { .color = PBIO_COLOR_BLUE, .duration = 2 }, + { .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), }, }; @@ -138,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; @@ -173,72 +174,91 @@ 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_advertising = pbsys_status_test(PBIO_PYBRICKS_STATUS_BLE_ADVERTISING); - bool ble_low_signal = pbsys_status_test(PBIO_PYBRICKS_STATUS_BLE_LOW_SIGNAL); - 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_low_signal && low_voltage) { - new_indication = PBSYS_STATUS_LIGHT_INDICATION_BLE_LOW_SIGNAL_AND_LOW_VOLTAGE; - } else if (ble_low_signal) { - new_indication = PBSYS_STATUS_LIGHT_INDICATION_BLE_LOW_SIGNAL; - } 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; + #if PBSYS_CONFIG_STATUS_LIGHT_STATE_ANIMATIONS + } else if (pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST)) { + warning_indication = PBSYS_STATUS_LIGHT_INDICATION_WARNING_SHUTDOWN_REQUESTED; + #endif + } 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) { // The brightness pattern has the form /\ through which we cycle in N steps. - static uint8_t cycle = 0; - const uint8_t cycle_max = 200; + // It is reset back to the start when the user program starts. + const uint8_t animation_progress_max = 200; pbio_color_hsv_t hsv = { .h = PBIO_COLOR_HUE_BLUE, .s = 100, - .v = cycle < cycle_max / 2 ? cycle : cycle_max - cycle, + .v = animation_progress < animation_progress_max / 2 ? + animation_progress : + 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 - cycle = (cycle + 4) % cycle_max; + 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) { - pbio_light_animation_init(&pbsys_status_light->animation, default_user_program_light_animation_next); - pbio_light_animation_start(&pbsys_status_light->animation); + animation_progress = 0; + 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 } /** @@ -339,34 +359,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 { - if (new_color == PBIO_COLOR_NONE) { - // System ID color - new_color = PBIO_COLOR_BLUE; - } - 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 @@ -378,13 +410,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); } @@ -392,19 +425,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/lib/pbio/sys/storage_settings.c b/lib/pbio/sys/storage_settings.c index 04888d93e..a26039260 100644 --- a/lib/pbio/sys/storage_settings.c +++ b/lib/pbio/sys/storage_settings.c @@ -75,7 +75,7 @@ bool pbsys_storage_settings_bluetooth_enabled(void) { #if PBSYS_CONFIG_BLUETOOTH_TOGGLE pbsys_storage_settings_t *settings = pbsys_storage_settings_get_settings(); if (!settings) { - return true; + return false; } return settings->flags & PBSYS_STORAGE_SETTINGS_FLAGS_BLUETOOTH_ENABLED; #else 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); }