Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Thumbs.db
__pycache__/
*.py[cod]
.Python
.venv-ble-ota/

# Optional local overrides (secrets, machine paths)
scripts/env.local.sh
19 changes: 16 additions & 3 deletions app.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
#include "opendisplay_ble.h"
#include <stdio.h>

// The advertising set handle allocated from Bluetooth stack.
// The advertising set handles allocated from Bluetooth stack.
static uint8_t advertising_set_handle = 0xff;
static uint8_t fmdn_advertising_set_handle = 0xff;
static uint8_t net_a_advertising_set_handle = 0xff;

// Application Init.
void app_init(void)
Expand Down Expand Up @@ -80,7 +82,19 @@ void sl_bt_on_event(sl_bt_msg_t *evt)
printf("[app] advertiser_create_set sc=0x%04lX\r\n", (unsigned long)sc);
}
app_assert_status(sc);
opendisplay_ble_on_boot(advertising_set_handle);
sc = sl_bt_advertiser_create_set(&fmdn_advertising_set_handle);
if (sc != SL_STATUS_OK) {
printf("[app] advertiser_create_set(fmdn) sc=0x%04lX\r\n", (unsigned long)sc);
}
app_assert_status(sc);
sc = sl_bt_advertiser_create_set(&net_a_advertising_set_handle);
if (sc != SL_STATUS_OK) {
printf("[app] advertiser_create_set(net_a) sc=0x%04lX\r\n", (unsigned long)sc);
}
app_assert_status(sc);
opendisplay_ble_on_boot(advertising_set_handle,
fmdn_advertising_set_handle,
net_a_advertising_set_handle);
break;

// -------------------------------
Expand All @@ -91,7 +105,6 @@ void sl_bt_on_event(sl_bt_msg_t *evt)

case sl_bt_evt_connection_closed_id:
opendisplay_ble_on_event(evt);
opendisplay_ble_restart_advertising(advertising_set_handle);
break;

///////////////////////////////////////////////////////////////////////////
Expand Down
100 changes: 88 additions & 12 deletions build-and-flash.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ NFC_WRITE_NDEF=0
NFC_WRITE_NDEF_CLI=0
NFC_NDEF_TEXT=""
NFC_NDEF_TEXT_CLI=0
DO_BLE_OTA=0
BLE_OTA_NAME=""
BLE_OTA_KEY=""
BLE_OTA_SLOW=0
BLE_OTA_ALREADY=0
BLE_OTA_SCAN_TIMEOUT="${BLE_OTA_SCAN_TIMEOUT:-12}"
BLE_OTA_APPLOADER_TIMEOUT="${BLE_OTA_APPLOADER_TIMEOUT:-45}"

usage() {
cat <<EOF
Expand All @@ -43,6 +50,12 @@ Usage: $0 [options]
--no-build Skip cmake build (flash existing artifact only)
--no-flash Build only
--gbl-only Only run Simplicity Commander gbl create (no build/flash; needs .s37)
--ble-ota NAME Build .gbl and OTA over BLE to GAP name matching NAME (no programmer).
Skips SWD flash. Uses silabs-ble-ota + scripts/ble_ota.py.
Example: $0 --ble-ota ODDEE7
--ble-ota-key HEX Optional 16-byte security master key if device encryption is on
--ble-ota-slow Proxy-safe OTA (disable silabs-ble-ota fast= mode)
--ble-ota-already-in-ota Device is already in AppLoader; skip CMD_ENTER_DFU
--rtt After flash: background J-Link RTT telnet + JLinkRTTClient
--rtt-only RTT only (no build/flash)
--no-rtt Build firmware with RTT disabled (OD_ENABLE_RTT=OFF)
Expand Down Expand Up @@ -70,13 +83,18 @@ Usage: $0 [options]
DO_MASS_ERASE (set to 1 for recover-before-flash, same as --mass-erase),
SWD_SPEED, JLINK_GDB_SERVER, JLINKEXE, JLINK_RTT_CLIENT,
RTT_BACKEND (gdbserver | exe, default gdbserver), RTT_GDBSERVER_ARGS,
RTT_TELNET_PORT (default 19021), RTT_ARGS (extra args for JLinkRTTClient).
RTT_TELNET_PORT (default 19021), RTT_ARGS (extra args for JLinkRTTClient),
BLE_OTA_SCAN_TIMEOUT, BLE_OTA_APPLOADER_TIMEOUT, PYTHON (python3 binary for BLE OTA).
Optional CMake (-D) when set: OPENDISPLAY_BUILD_ID, OD_APP_VERSION,
OD_FW_VERSION, OD_SL_APPLICATION_VERSION, OD_GENERATE_GBL_OTA,
OD_TNB132M_BOOT_PROBE, OD_TNB132M_WRITE_NDEF, OD_TNB132M_NDEF_TEXT

Default RTT_BACKEND=gdbserver: JLinkGDBServer stays connected (J-Link Commander often exits and breaks exe mode).
If Commander stays open on your setup, try RTT_BACKEND=exe (JLinkExe + sleep infinity) instead.

BLE OTA needs: pip install -r scripts/requirements-ble-ota.txt
(build-and-flash prefers Firmware_Silabs/.venv-ble-ota if present)
See https://github.com/OpenDisplay/silabs-ble-ota
EOF
exit 0
}
Expand All @@ -86,6 +104,16 @@ while [[ $# -gt 0 ]]; do
--no-build) DO_BUILD=0 ;;
--no-flash) DO_FLASH=0 ;;
--gbl-only) DO_BUILD=0; DO_FLASH=0; DO_GBL_ONLY=1 ;;
--ble-ota)
DO_BLE_OTA=1
DO_FLASH=0
DO_OTA_IMAGE=1
BLE_OTA_NAME="${2:?--ble-ota needs NAME (GAP name substring, e.g. ODDEE7)}"
shift
;;
--ble-ota-key) BLE_OTA_KEY="${2:?}"; shift ;;
--ble-ota-slow) BLE_OTA_SLOW=1 ;;
--ble-ota-already-in-ota) BLE_OTA_ALREADY=1 ;;
--rtt) DO_RTT=1 ;;
--rtt-only) RTT_ONLY=1; DO_BUILD=0; DO_FLASH=0; DO_RTT=1 ;;
--no-rtt) RTT_ENABLED=0 ;;
Expand Down Expand Up @@ -118,6 +146,7 @@ while [[ $# -gt 0 ]]; do
DO_RTT=0
RTT_ONLY=0
DO_GBL_ONLY=0
DO_BLE_OTA=0
;;
-h|--help) usage ;;
*) echo "Unknown option: $1" >&2; usage ;;
Expand Down Expand Up @@ -560,10 +589,10 @@ elif [[ "$DO_BUILD" -eq 1 ]] && [[ "$DO_FLASH" -eq 0 ]]; then
echo "==> Skipping flash (--no-flash)"
fi

if [[ "$DO_OTA_IMAGE" -eq 1 ]] && [[ "$DO_BUILD" -eq 1 || "$DO_FLASH" -eq 1 || "$DO_GBL_ONLY" -eq 1 ]]; then
if [[ "$DO_OTA_IMAGE" -eq 1 ]] && [[ "$DO_BUILD" -eq 1 || "$DO_FLASH" -eq 1 || "$DO_GBL_ONLY" -eq 1 || "$DO_BLE_OTA" -eq 1 ]]; then
APP_ART=$(find_artifact) || {
if [[ "$DO_GBL_ONLY" -eq 1 ]]; then
echo "ERROR: --gbl-only needs ${TARGET_NAME}.s37 or .hex under $CMAKE_DIR/build (build first)." >&2
if [[ "$DO_GBL_ONLY" -eq 1 || "$DO_BLE_OTA" -eq 1 ]]; then
echo "ERROR: need ${TARGET_NAME}.s37 or .hex under $CMAKE_DIR/build (build first)." >&2
exit 1
fi
APP_ART=""
Expand All @@ -574,26 +603,73 @@ if [[ "$DO_OTA_IMAGE" -eq 1 ]] && [[ "$DO_BUILD" -eq 1 || "$DO_FLASH" -eq 1 || "
APP_ART=""
}
if [[ -n "${APP_ART}" ]]; then
echo "==> OTA image: $CMD gbl create $OUT_GBL --app $APP_ART"
if "$CMD" gbl create "$OUT_GBL" --app "$APP_ART"; then
echo "==> OTA image generated: $OUT_GBL"
if [[ -f "$OUT_GBL" && "$DO_BUILD" -eq 0 && "$DO_BLE_OTA" -eq 1 ]]; then
echo "==> Reusing existing OTA image: $OUT_GBL"
else
echo "WARN: OTA .gbl generation failed; continuing." >&2
if [[ "$DO_GBL_ONLY" -eq 1 ]]; then
exit 1
echo "==> OTA image: $CMD gbl create $OUT_GBL --app $APP_ART"
if "$CMD" gbl create "$OUT_GBL" --app "$APP_ART"; then
echo "==> OTA image generated: $OUT_GBL"
else
echo "WARN: OTA .gbl generation failed; continuing." >&2
if [[ "$DO_GBL_ONLY" -eq 1 || "$DO_BLE_OTA" -eq 1 ]]; then
exit 1
fi
fi
fi
elif [[ "$DO_GBL_ONLY" -eq 1 ]]; then
elif [[ "$DO_GBL_ONLY" -eq 1 || "$DO_BLE_OTA" -eq 1 ]]; then
exit 1
fi
fi

if [[ "$DO_COLLECT_ARTIFACTS" -eq 1 ]] && [[ "$DO_BUILD" -eq 1 || "$DO_FLASH" -eq 1 || "$DO_GBL_ONLY" -eq 1 ]]; then
if [[ "$DO_COLLECT_ARTIFACTS" -eq 1 ]] && [[ "$DO_BUILD" -eq 1 || "$DO_FLASH" -eq 1 || "$DO_GBL_ONLY" -eq 1 || "$DO_BLE_OTA" -eq 1 ]]; then
echo "==> Staging artifacts + memory summary"
od_collect_artifacts
od_print_memory_summary
fi

run_ble_ota() {
local py gbl script
if [[ -n "${PYTHON:-}" ]]; then
py="$PYTHON"
elif [[ -x "$ROOT/.venv-ble-ota/bin/python" ]]; then
py="$ROOT/.venv-ble-ota/bin/python"
else
py="python3"
fi
gbl="${OTA_IMAGE_OUT:-$CMAKE_DIR/build/base/${TARGET_NAME}.gbl}"
if [[ ! -f "$gbl" && -f "$ARTIFACTS_DIR/${TARGET_NAME}.gbl" ]]; then
gbl="$ARTIFACTS_DIR/${TARGET_NAME}.gbl"
fi
if [[ ! -f "$gbl" ]]; then
echo "ERROR: no .gbl for BLE OTA at $gbl" >&2
return 1
fi
script="$ROOT/scripts/ble_ota.py"
if [[ ! -f "$script" ]]; then
echo "ERROR: missing $script" >&2
return 1
fi
if ! "$py" -c "import silabs_ble_ota" 2>/dev/null; then
echo "ERROR: silabs-ble-ota not installed for: $py" >&2
echo " python3 -m venv $ROOT/.venv-ble-ota" >&2
echo " $ROOT/.venv-ble-ota/bin/pip install -r $ROOT/scripts/requirements-ble-ota.txt" >&2
return 1
fi
echo "==> BLE OTA via silabs-ble-ota → name '$BLE_OTA_NAME' gbl=$gbl"
echo " python: $py"
local args=(--name "$BLE_OTA_NAME" --gbl "$gbl"
--scan-timeout "$BLE_OTA_SCAN_TIMEOUT"
--apploader-timeout "$BLE_OTA_APPLOADER_TIMEOUT")
[[ -n "$BLE_OTA_KEY" ]] && args+=(--key "$BLE_OTA_KEY")
[[ "$BLE_OTA_SLOW" -eq 1 ]] && args+=(--slow)
[[ "$BLE_OTA_ALREADY" -eq 1 ]] && args+=(--already-in-ota)
"$py" "$script" "${args[@]}"
}

if [[ "$DO_BLE_OTA" -eq 1 ]]; then
run_ble_ota || exit 1
fi

if [[ "$DO_RTT" -eq 1 ]]; then
run_rtt_session
fi
Expand Down
1 change: 1 addition & 0 deletions cmake_gcc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ target_compile_definitions(slc PUBLIC
OD_TNB132M_BOOT_PROBE=$<IF:$<BOOL:${OD_TNB132M_BOOT_PROBE}>,1,0>
OD_TNB132M_WRITE_NDEF=$<IF:$<BOOL:${OD_TNB132M_WRITE_NDEF}>,1,0>
"OD_TNB132M_NDEF_TEXT=\"${OD_TNB132M_NDEF_TEXT}\""
SL_CATALOG_BLUETOOTH_FEATURE_EXTENDED_ADVERTISER_PRESENT
SL_APP_PROPERTIES=1
"OD_APP_VERSION=${OD_APP_VERSION}"
"OPENDISPLAY_BUILD_ID=\"${OPENDISPLAY_BUILD_ID}\""
Expand Down
1 change: 1 addition & 0 deletions cmake_gcc/opendisplay-bg22.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ add_library(slc OBJECT
"../${COPIED_SDK_PATH}/security_se_manager/platform/security/sl_component/se_manager/src/sli_se_manager_mailbox.c"
"../app.c"
"../opendisplay_ble.c"
"../opendisplay_net_a_adv.c"
"../opendisplay_pipe.c"
"../opendisplay_config_storage.c"
"../opendisplay_config_parser.c"
Expand Down
2 changes: 1 addition & 1 deletion config/sl_bluetooth_advertiser_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
// <i> Specifically, if the component "bluetooth_feature_periodic_advertiser" is used, its configuration SL_BT_CONFIG_MAX_PERIODIC_ADVERTISERS specifies how many of the SL_BT_CONFIG_USER_ADVERTISERS advertising sets are capable of periodic advertising. Similarly, if the component bluetooth_feature_pawr_advertiser is used, its configuration SL_BT_CONFIG_MAX_PAWR_ADVERTISERS specifies how many of the periodic advertising sets are capable of Periodic Advertising with Responses.
// <i>
// <i> The configuration values must satisfy the condition SL_BT_CONFIG_USER_ADVERTISERS >= SL_BT_CONFIG_MAX_PERIODIC_ADVERTISERS >= SL_BT_CONFIG_MAX_PAWR_ADVERTISERS.
#define SL_BT_CONFIG_USER_ADVERTISERS (1)
#define SL_BT_CONFIG_USER_ADVERTISERS (3)
// <<< end of configuration section >>>

#endif
Expand Down
37 changes: 36 additions & 1 deletion include/opendisplay_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,8 @@ enum ConfigPacketType {
OD_PKT_BUZZER = 0x29, /**< @doc "buzzer (repeatable, max 4). Canonical name is BUZZER (matches Silabs), resolving the Silabs BUZZER vs NRF54/yaml PASSIVE_BUZZER split; config.yaml's packet name (passive_buzzer) is regenerated to buzzer." */
OD_PKT_NFC = 0x2A, /**< @doc "nfc_config (repeatable, max 2)" */
OD_PKT_FLASH = 0x2B, /**< @doc "flash_config (repeatable, max 2)" */
OD_PKT_DATA_EXTENDED = 0x2C /**< @doc "data_extended (singleton)" */
OD_PKT_DATA_EXTENDED = 0x2C, /**< @doc "data_extended (singleton)" */
OD_PKT_FINDMY = 0x2D /**< @doc "findmy_config (singleton)" */
};

/* ==========================================================================
Expand Down Expand Up @@ -1074,6 +1075,40 @@ struct DataExtended {
} __attribute__((packed));
OD_STATIC_ASSERT(sizeof(struct DataExtended) == 288, "DataExtended wire size");

/* -----------------------------------------------------------------------
* 0x2D findmy_config
* ----------------------------------------------------------------------- */

/** @enum FindMyCurve @width 1 @doc "FMDN EID curve selector (FindMyConfig.fmdn_curve)." */
enum FindMyCurve {
OD_FINDMY_CURVE_SECP160R1 = 0, /**< @doc "20-byte static EID (network G / legacy AD). Uses advertisement_key." */
OD_FINDMY_CURVE_SECP256R1 = 1 /**< @doc "32-byte rotating EID on SECP256R1 (extended AD)." */
};

/* FindMyConfig.flags @bits FindMyFlags (bits 2-7 reserved). */
#define OD_FINDMY_FLAG_FMDN_ENABLE (1u << 0) /* @doc "enable network G (FMDN) advertising." */
#define OD_FINDMY_FLAG_NET_A_ENABLE (1u << 1) /* @doc "enable network A (legacy offline-finding) advertising." */

/** @struct FindMyConfig @packet 0x2D
* @doc "Proximity-network provisioning blob. Singleton. 128 bytes. For network G use
* fmdn_curve=secp160r1 with advertisement_key=generate_eid(eik,0) (static beacon_counter=0).
* SECP256R1 rotates from eik+time_base on-device.
* For network A set net_a_enable and net_a_adv_key to the 28-byte P-224 advertisement public key X.
* net_a_time_base stays 0 (static mode, no on-device rotation). Reserved bytes must be zero." */
struct FindMyConfig {
uint8_t flags; /**< @bits FindMyFlags @doc "enable flags; bits 2-7 reserved." */
uint8_t fmdn_k; /**< @doc "FMDN rotation-period exponent K (typically 10)." */
uint8_t fmdn_curve; /**< @enum FindMyCurve @doc "FMDN EID curve selector." */
uint8_t reserved0[5]; /**< @reserved @doc "must be 0; pads the control header to 8 bytes." */
uint8_t eik[32]; /**< @doc "Network G ephemeral identity key (32 bytes)." */
uint8_t net_a_adv_key[28]; /**< @doc "Network A 28-byte advertisement public key X (not the private key)." */
uint32_t time_base_unix; /**< @endian le @unit s @doc "Unix epoch for SECP256R1 rotation; unused for static SECP160 (write 0)." */
uint32_t net_a_time_base; /**< @endian le @unit s @doc "unused in static network A mode; write 0." */
uint8_t advertisement_key[20]; /**< @doc "Static 20-byte SECP160 EID for network G (generate_eid(eik,0)); zero when unused." */
uint8_t reserved1[32]; /**< @reserved @doc "must be 0; reserved for future extensions." */
} __attribute__((packed));
OD_STATIC_ASSERT(sizeof(struct FindMyConfig) == 128, "FindMyConfig wire size");

/* ==========================================================================
* SECTION 5 -- MESSAGE PAYLOAD STRUCTS (fixed-size opcode payloads)
* ==========================================================================
Expand Down
1 change: 1 addition & 0 deletions opendisplay-bg22.slcp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ readme:
source:
- path: app.c
- path: opendisplay_ble.c
- path: opendisplay_net_a_adv.c
- path: opendisplay_pipe.c
- path: opendisplay_config_storage.c
- path: opendisplay_config_parser.c
Expand Down
Loading
Loading