From af3e8687a48dae1d8a5807df3da35f53cf3a61c9 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 28 May 2026 15:06:09 +0200 Subject: [PATCH] advertising fix --- autogen/gatt_db.c | 4 +- config/btconf/gatt_configuration.btconf | 2 +- opendisplay_ble.c | 28 +- opendisplay_display.cpp | 2 +- opendisplay_led.c | 377 ++++++++++++++++++++---- opendisplay_led.h | 8 + opendisplay_pipe.c | 17 ++ opendisplay_protocol.h | 2 + opendisplay_structs.h | 6 +- 9 files changed, 354 insertions(+), 92 deletions(-) diff --git a/autogen/gatt_db.c b/autogen/gatt_db.c index 4a494fd..9275841 100644 --- a/autogen/gatt_db.c +++ b/autogen/gatt_db.c @@ -22,8 +22,8 @@ GATT_DATA(const uint8_t gattdb_uuidtable_128_map[]) = }; GATT_DATA(sli_bt_gattdb_attribute_chrvalue_t gattdb_attribute_field_2) = { .properties = 0x0a, - .max_len = 13, - .data = { 0x45, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, }, + .max_len = 16, + .data = { 0x4f, 0x44, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, }, }; GATT_DATA(const sli_bt_gattdb_value_t gattdb_attribute_field_0) = { .len = 2, diff --git a/config/btconf/gatt_configuration.btconf b/config/btconf/gatt_configuration.btconf index 93ff493..6b554b1 100644 --- a/config/btconf/gatt_configuration.btconf +++ b/config/btconf/gatt_configuration.btconf @@ -5,7 +5,7 @@ Abstract: The generic_access service contains generic information about the device. All available Characteristics are readonly. - Empty Example + OD000000 diff --git a/opendisplay_ble.c b/opendisplay_ble.c index a630be6..2294c09 100644 --- a/opendisplay_ble.c +++ b/opendisplay_ble.c @@ -22,7 +22,7 @@ #define MSD_PAYLOAD_LEN 16u #define OD_NAME_PREFIX "OD" #ifndef OD_APP_VERSION -#define OD_APP_VERSION 0x0019u +#define OD_APP_VERSION 0x0100u #endif #ifndef OPENDISPLAY_MAX_PIPE_LEN @@ -74,7 +74,6 @@ static uint8_t reboot_flag = 1u; static uint8_t connection_requested = 0u; static uint16_t g_od_pipe_char; -static uint16_t g_od_appver_char; static uint8_t g_connection; static uint8_t s_adv_handle = 0xFFu; static char s_dev_name[16]; @@ -1618,11 +1617,8 @@ static sl_status_t install_opendisplay_gatt(void) uint16_t session; uint16_t svc; uint16_t ch_pipe; - uint16_t ch_ver; sl_bt_uuid_16_t uuid_svc = { .data = { 0x46, 0x24 } }; sl_bt_uuid_16_t uuid_pipe = { .data = { 0x46, 0x24 } }; - sl_bt_uuid_16_t uuid_ver = { .data = { 0x03, 0x00 } }; - uint8_t ver_init[2] = { (uint8_t)(OD_APP_VERSION & 0xFFu), (uint8_t)((OD_APP_VERSION >> 8) & 0xFFu) }; uint8_t pipe_init = 0; sl_status_t sc; @@ -1661,22 +1657,6 @@ static sl_status_t install_opendisplay_gatt(void) return sc; } - sc = sl_bt_gattdb_add_uuid16_characteristic(session, - svc, - SL_BT_GATTDB_CHARACTERISTIC_READ, - 0, - 0, - uuid_ver, - sl_bt_gattdb_fixed_length_value, - 2, - sizeof(ver_init), - ver_init, - &ch_ver); - if (sc != SL_STATUS_OK) { - (void)sl_bt_gattdb_abort(session); - return sc; - } - sc = sl_bt_gattdb_start_service(session, svc); if (sc != SL_STATUS_OK) { (void)sl_bt_gattdb_abort(session); @@ -1689,8 +1669,6 @@ static sl_status_t install_opendisplay_gatt(void) } g_od_pipe_char = ch_pipe; - g_od_appver_char = ch_ver; - (void)g_od_appver_char; return SL_STATUS_OK; } @@ -1792,8 +1770,7 @@ void opendisplay_ble_on_boot(uint8_t advertising_set_handle) } } app_assert_status(sc); - printf("[OD] GATT 0x2446 ok, pipe_char=%u appver=%u\r\n", - (unsigned)g_od_pipe_char, (unsigned)g_od_appver_char); + printf("[OD] GATT 0x2446 ok, pipe_char=%u\r\n", (unsigned)g_od_pipe_char); opendisplay_pipe_set_characteristic(g_od_pipe_char); od_buttons_init_from_config(); @@ -1872,6 +1849,7 @@ void opendisplay_ble_schedule_deep_sleep(void) void opendisplay_ble_process(void) { + opendisplay_led_process(); uint32_t now_ms = sl_sleeptimer_tick_to_ms(sl_sleeptimer_get_tick_count()); if (od_process_button_event(now_ms) && s_adv_handle != 0xFFu) { build_and_apply_adv(s_adv_handle, s_dev_name); diff --git a/opendisplay_display.cpp b/opendisplay_display.cpp index acac622..f33958f 100644 --- a/opendisplay_display.cpp +++ b/opendisplay_display.cpp @@ -407,7 +407,7 @@ static bool render_boot_screen(BBEPAPER &epd, const struct GlobalConfig *cfg) (void)snprintf(name_line, sizeof(name_line), "OD%06lX", (unsigned long)last3); { uint16_t ver = opendisplay_ble_get_app_version(); - (void)snprintf(fw_line, sizeof(fw_line), "FW: %u.%u", + (void)snprintf(fw_line, sizeof(fw_line), "FW:S %u.%u", (unsigned)((ver >> 8) & 0xFFu), (unsigned)(ver & 0xFFu)); } bytes_to_hex(key, sizeof(key), key_hex, sizeof(key_hex)); diff --git a/opendisplay_led.c b/opendisplay_led.c index 43ea7e2..03c76e1 100644 --- a/opendisplay_led.c +++ b/opendisplay_led.c @@ -17,9 +17,48 @@ #define LED_DELAY_FACTOR_MS 100u #define LED_PWM_DELAY_US 100u -static uint8_t s_active_instance = 0xFFu; -static bool s_flash_active; -static uint8_t s_flash_position; +typedef enum { + LED_PHASE_IDLE = 0, + LED_PHASE_GROUP, + LED_PHASE_LOOP1, + LED_PHASE_LOOP1_DELAY, + LED_PHASE_INTER1_DELAY, + LED_PHASE_LOOP2, + LED_PHASE_LOOP2_DELAY, + LED_PHASE_INTER2_DELAY, + LED_PHASE_LOOP3, + LED_PHASE_LOOP3_DELAY, + LED_PHASE_INTER3_DELAY, +} led_phase_t; + +static struct { + bool active; + uint8_t instance; + struct LedConfig *led; + uint8_t brightness; + uint8_t c1; + uint8_t c2; + uint8_t c3; + uint8_t loop1delay; + uint8_t loop2delay; + uint8_t loop3delay; + uint8_t loopcnt1; + uint8_t loopcnt2; + uint8_t loopcnt3; + uint8_t ildelay1; + uint8_t ildelay2; + uint8_t ildelay3; + uint8_t grouprepeats; + uint8_t group_pos; + uint8_t i1; + uint8_t i2; + uint8_t i3; + led_phase_t phase; + bool waiting_delay; +} s_run; + +static sl_sleeptimer_timer_handle_t s_led_timer; +static volatile bool s_timer_due; static bool od_led_pin_decode(uint8_t v, GPIO_Port_TypeDef *port_out, uint8_t *pin_out) { @@ -63,6 +102,23 @@ static void od_gpio_write(uint8_t cfg, bool level_high) } } +static void od_led_all_off(const struct LedConfig *led) +{ + bool inv_r = (led->led_flags & LED_FLAG_INVERT_RED) != 0u; + bool inv_g = (led->led_flags & LED_FLAG_INVERT_GREEN) != 0u; + bool inv_b = (led->led_flags & LED_FLAG_INVERT_BLUE) != 0u; + + if (led->led_1_r != GPIO_PIN_UNUSED) { + od_gpio_write(led->led_1_r, inv_r); + } + if (led->led_2_g != GPIO_PIN_UNUSED) { + od_gpio_write(led->led_2_g, inv_g); + } + if (led->led_3_b != GPIO_PIN_UNUSED) { + od_gpio_write(led->led_3_b, inv_b); + } +} + static void od_flash_led(const struct LedConfig *led, uint8_t color, uint8_t brightness) { uint8_t led_red = led->led_1_r; @@ -106,68 +162,234 @@ static void od_flash_led(const struct LedConfig *led, uint8_t color, uint8_t bri } } -static void od_led_flash_logic(struct LedConfig *led) +static void led_timer_cb(sl_sleeptimer_timer_handle_t *handle, void *data) +{ + (void)handle; + (void)data; + s_timer_due = true; +} + +static void led_timer_stop(void) +{ + (void)sl_sleeptimer_stop_timer(&s_led_timer); + s_timer_due = false; + s_run.waiting_delay = false; +} + +static void led_schedule_delay_ms(uint16_t ms) +{ + if (ms == 0u) { + s_timer_due = true; + s_run.waiting_delay = true; + return; + } + led_timer_stop(); + s_timer_due = false; + s_run.waiting_delay = true; + (void)sl_sleeptimer_start_timer_ms(&s_led_timer, ms, led_timer_cb, NULL, 0u, 0u); +} + +static void led_run_finish(void) +{ + if (s_run.led != NULL) { + od_led_all_off(s_run.led); + } + led_timer_stop(); + memset(&s_run, 0, sizeof(s_run)); +} + +static void led_load_config(struct LedConfig *led) { uint8_t *ledcfg = led->reserved; - uint8_t brightness = (uint8_t)(((ledcfg[0] >> 4) & 0x0Fu) + 1u); - uint8_t mode = (uint8_t)(ledcfg[0] & 0x0Fu); + s_run.led = led; + s_run.brightness = (uint8_t)(((ledcfg[0] >> 4) & 0x0Fu) + 1u); + s_run.c1 = ledcfg[1]; + s_run.c2 = ledcfg[4]; + s_run.c3 = ledcfg[7]; + s_run.loop1delay = (uint8_t)((ledcfg[2] >> 4) & 0x0Fu); + s_run.loop2delay = (uint8_t)((ledcfg[5] >> 4) & 0x0Fu); + s_run.loop3delay = (uint8_t)((ledcfg[8] >> 4) & 0x0Fu); + s_run.loopcnt1 = (uint8_t)(ledcfg[2] & 0x0Fu); + s_run.loopcnt2 = (uint8_t)(ledcfg[5] & 0x0Fu); + s_run.loopcnt3 = (uint8_t)(ledcfg[8] & 0x0Fu); + s_run.ildelay1 = ledcfg[3]; + s_run.ildelay2 = ledcfg[6]; + s_run.ildelay3 = ledcfg[9]; + s_run.grouprepeats = (uint8_t)(ledcfg[10] + 1u); + s_run.group_pos = 0; + s_run.i1 = 0; + s_run.i2 = 0; + s_run.i3 = 0; + s_run.phase = LED_PHASE_GROUP; + s_run.waiting_delay = false; +} + +static void led_run_stop_internal(bool clear_mode) +{ + struct LedConfig *led = s_run.led; + + led_timer_stop(); + if (led != NULL) { + od_led_all_off(led); + if (clear_mode) { + led->reserved[0] = 0x00u; + } + } + memset(&s_run, 0, sizeof(s_run)); +} + +static bool led_delay_ready(void) +{ + if (!s_run.waiting_delay) { + return true; + } + if (!s_timer_due) { + return false; + } + s_timer_due = false; + s_run.waiting_delay = false; + return true; +} + +static void led_run_step(void) +{ + struct LedConfig *led; + uint8_t mode; + + if (!s_run.active || s_run.led == NULL) { + return; + } + led = s_run.led; + mode = (uint8_t)(led->reserved[0] & 0x0Fu); if (mode != 1u) { + led_run_finish(); return; } - uint8_t c1 = ledcfg[1]; - uint8_t c2 = ledcfg[4]; - uint8_t c3 = ledcfg[7]; - uint8_t loop1delay = (uint8_t)((ledcfg[2] >> 4) & 0x0Fu); - uint8_t loop2delay = (uint8_t)((ledcfg[5] >> 4) & 0x0Fu); - uint8_t loop3delay = (uint8_t)((ledcfg[8] >> 4) & 0x0Fu); - uint8_t loopcnt1 = (uint8_t)(ledcfg[2] & 0x0Fu); - uint8_t loopcnt2 = (uint8_t)(ledcfg[5] & 0x0Fu); - uint8_t loopcnt3 = (uint8_t)(ledcfg[8] & 0x0Fu); - uint8_t ildelay1 = ledcfg[3]; - uint8_t ildelay2 = ledcfg[6]; - uint8_t ildelay3 = ledcfg[9]; - uint8_t grouprepeats = (uint8_t)(ledcfg[10] + 1u); - - while (s_flash_active) { - if (s_flash_position >= grouprepeats && grouprepeats != 255u) { - ledcfg[0] = 0x00u; - s_flash_position = 0; - break; + for (;;) { + if (!s_run.active) { + return; } - for (uint8_t i = 0; i < loopcnt1; i++) { - od_flash_led(led, c1, brightness); - if (loop1delay > 0u) { - sl_sleeptimer_delay_millisecond((uint16_t)(loop1delay * LED_DELAY_FACTOR_MS)); - } - } - if (ildelay1 > 0u) { - sl_sleeptimer_delay_millisecond((uint16_t)(ildelay1 * LED_DELAY_FACTOR_MS)); - } + switch (s_run.phase) { + case LED_PHASE_GROUP: + if (s_run.group_pos >= s_run.grouprepeats && s_run.grouprepeats != 255u) { + led->reserved[0] = 0x00u; + led_run_finish(); + return; + } + s_run.i1 = 0; + s_run.i2 = 0; + s_run.i3 = 0; + s_run.phase = LED_PHASE_LOOP1; + break; - for (uint8_t i = 0; i < loopcnt2; i++) { - od_flash_led(led, c2, brightness); - if (loop2delay > 0u) { - sl_sleeptimer_delay_millisecond((uint16_t)(loop2delay * LED_DELAY_FACTOR_MS)); - } - } - if (ildelay2 > 0u) { - sl_sleeptimer_delay_millisecond((uint16_t)(ildelay2 * LED_DELAY_FACTOR_MS)); - } + case LED_PHASE_LOOP1: + if (s_run.i1 >= s_run.loopcnt1) { + if (s_run.ildelay1 > 0u) { + s_run.phase = LED_PHASE_INTER1_DELAY; + led_schedule_delay_ms((uint16_t)(s_run.ildelay1 * LED_DELAY_FACTOR_MS)); + return; + } + s_run.phase = LED_PHASE_LOOP2; + break; + } + od_flash_led(led, s_run.c1, s_run.brightness); + s_run.i1++; + if (s_run.loop1delay > 0u) { + s_run.phase = LED_PHASE_LOOP1_DELAY; + led_schedule_delay_ms((uint16_t)(s_run.loop1delay * LED_DELAY_FACTOR_MS)); + return; + } + break; - for (uint8_t i = 0; i < loopcnt3; i++) { - od_flash_led(led, c3, brightness); - if (loop3delay > 0u) { - sl_sleeptimer_delay_millisecond((uint16_t)(loop3delay * LED_DELAY_FACTOR_MS)); - } - } - if (ildelay3 > 0u) { - sl_sleeptimer_delay_millisecond((uint16_t)(ildelay3 * LED_DELAY_FACTOR_MS)); - } + case LED_PHASE_LOOP1_DELAY: + if (!led_delay_ready()) { + return; + } + s_run.phase = LED_PHASE_LOOP1; + break; + + case LED_PHASE_INTER1_DELAY: + if (!led_delay_ready()) { + return; + } + s_run.phase = LED_PHASE_LOOP2; + break; + + case LED_PHASE_LOOP2: + if (s_run.i2 >= s_run.loopcnt2) { + if (s_run.ildelay2 > 0u) { + s_run.phase = LED_PHASE_INTER2_DELAY; + led_schedule_delay_ms((uint16_t)(s_run.ildelay2 * LED_DELAY_FACTOR_MS)); + return; + } + s_run.phase = LED_PHASE_LOOP3; + break; + } + od_flash_led(led, s_run.c2, s_run.brightness); + s_run.i2++; + if (s_run.loop2delay > 0u) { + s_run.phase = LED_PHASE_LOOP2_DELAY; + led_schedule_delay_ms((uint16_t)(s_run.loop2delay * LED_DELAY_FACTOR_MS)); + return; + } + break; + + case LED_PHASE_LOOP2_DELAY: + if (!led_delay_ready()) { + return; + } + s_run.phase = LED_PHASE_LOOP2; + break; - s_flash_position++; + case LED_PHASE_INTER2_DELAY: + if (!led_delay_ready()) { + return; + } + s_run.phase = LED_PHASE_LOOP3; + break; + + case LED_PHASE_LOOP3: + if (s_run.i3 >= s_run.loopcnt3) { + if (s_run.ildelay3 > 0u) { + s_run.phase = LED_PHASE_INTER3_DELAY; + led_schedule_delay_ms((uint16_t)(s_run.ildelay3 * LED_DELAY_FACTOR_MS)); + return; + } + s_run.group_pos++; + s_run.phase = LED_PHASE_GROUP; + break; + } + od_flash_led(led, s_run.c3, s_run.brightness); + s_run.i3++; + if (s_run.loop3delay > 0u) { + s_run.phase = LED_PHASE_LOOP3_DELAY; + led_schedule_delay_ms((uint16_t)(s_run.loop3delay * LED_DELAY_FACTOR_MS)); + return; + } + break; + + case LED_PHASE_LOOP3_DELAY: + if (!led_delay_ready()) { + return; + } + s_run.phase = LED_PHASE_LOOP3; + break; + + case LED_PHASE_INTER3_DELAY: + if (!led_delay_ready()) { + return; + } + s_run.group_pos++; + s_run.phase = LED_PHASE_GROUP; + break; + + default: + led_run_finish(); + return; + } } } @@ -210,23 +432,54 @@ void opendisplay_led_init(void) int opendisplay_led_activate(uint8_t instance, const uint8_t *rest, uint16_t rest_len) { struct GlobalConfig *gc = (struct GlobalConfig *)opendisplay_get_global_config(); + struct LedConfig *led; + uint8_t mode; if (gc == NULL || !gc->loaded || instance >= gc->led_count) { return 2; } - struct LedConfig *led = &gc->leds[instance]; - uint8_t *ledcfg = led->reserved; - + led = &gc->leds[instance]; if (rest_len >= 12u) { - memcpy(ledcfg, rest, 12); + memcpy(led->reserved, rest, 12); + } + + mode = (uint8_t)(led->reserved[0] & 0x0Fu); + if (mode != 1u) { + opendisplay_led_stop(0, false); + return 0; } - s_active_instance = instance; - s_flash_position = 0; - s_flash_active = true; - od_led_flash_logic(led); - s_flash_active = false; + opendisplay_led_stop(0, false); + s_run.active = true; + s_run.instance = instance; + led_load_config(led); + led_run_step(); + return 0; +} + +int opendisplay_led_stop(uint8_t instance, bool instance_given) +{ + if (!s_run.active) { + return 0; + } + if (instance_given && instance != s_run.instance) { + return 2; + } + led_run_stop_internal(true); return 0; } + +void opendisplay_led_process(void) +{ + if (!s_run.active) { + return; + } + led_run_step(); +} + +bool opendisplay_led_is_active(void) +{ + return s_run.active; +} diff --git a/opendisplay_led.h b/opendisplay_led.h index 3664d72..9c3b86c 100644 --- a/opendisplay_led.h +++ b/opendisplay_led.h @@ -1,6 +1,7 @@ #ifndef OPENDISPLAY_LED_H #define OPENDISPLAY_LED_H +#include #include void opendisplay_led_init(void); @@ -8,4 +9,11 @@ void opendisplay_led_init(void); /* 0 = success, 2 = instance/config invalid (pipe error code 0x02) */ int opendisplay_led_activate(uint8_t instance, const uint8_t *payload_after_instance, uint16_t rest_len); +/* 0 = stopped or idle, 2 = wrong instance while another sequence is active */ +int opendisplay_led_stop(uint8_t instance, bool instance_given); + +void opendisplay_led_process(void); + +bool opendisplay_led_is_active(void); + #endif diff --git a/opendisplay_pipe.c b/opendisplay_pipe.c index 95a23ca..720d15c 100644 --- a/opendisplay_pipe.c +++ b/opendisplay_pipe.c @@ -1061,6 +1061,23 @@ static void dispatch(uint8_t connection, uint16_t cmd, const uint8_t *payload, u pipe_send(connection, ok, sizeof(ok)); break; } + case CMD_LED_STOP: { + uint8_t ok[] = { 0x00u, RESP_LED_STOP_ACK, 0x00u, 0x00u }; + uint8_t e2[] = { 0xFFu, RESP_LED_STOP_ACK, 0x02u, 0x00u }; + int rc; + + if (payload_len >= 1u) { + rc = opendisplay_led_stop(payload[0], true); + } else { + rc = opendisplay_led_stop(0, false); + } + if (rc != 0) { + pipe_send(connection, e2, sizeof(e2)); + break; + } + pipe_send(connection, ok, sizeof(ok)); + break; + } case CMD_NFC_ENDPOINT: handle_nfc_endpoint(connection, payload, payload_len); break; diff --git a/opendisplay_protocol.h b/opendisplay_protocol.h index 2cce2a9..a0eb1ad 100644 --- a/opendisplay_protocol.h +++ b/opendisplay_protocol.h @@ -12,6 +12,7 @@ #define CMD_DIRECT_WRITE_DATA 0x0071u #define CMD_DIRECT_WRITE_END 0x0072u #define CMD_LED_ACTIVATE 0x0073u +#define CMD_LED_STOP 0x0075u /* NFC: sub byte0 0=read, 1=write (+rec_type,len16,payload), 0x10/0x11/0x12=chunked write; rec_type OD_NFC_REC_* */ #define CMD_NFC_ENDPOINT 0x0082u #define CMD_REBOOT 0x000Fu @@ -36,6 +37,7 @@ #define RESP_MSD_READ 0x44u #define RESP_AUTHENTICATE 0x50u #define RESP_LED_ACTIVATE_ACK 0x73u +#define RESP_LED_STOP_ACK 0x75u #define RESP_NFC_ENDPOINT 0x82u #define RESP_ENTER_DFU 0x51u #define RESP_DEEP_SLEEP 0x52u diff --git a/opendisplay_structs.h b/opendisplay_structs.h index 5853c26..e675560 100644 --- a/opendisplay_structs.h +++ b/opendisplay_structs.h @@ -16,7 +16,11 @@ struct ManufacturerData { uint16_t manufacturer_id; uint8_t board_type; uint8_t board_revision; - uint8_t reserved[18]; + uint16_t simple_config_driver_index; + uint16_t simple_config_display_index; + uint16_t simple_config_power_index; + uint8_t simple_config_configured_at[6]; + uint8_t reserved[6]; } __attribute__((packed)); struct PowerOption {