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 {