# SPDX-License-Identifier: MIT

source "$(ZEPHYR_BASE)/Kconfig.zephyr"

# DT-driven PSRAM auto-enable for ESP32 boards (see Kconfig.psram).
rsource "Kconfig.psram"

module = ZEPHCORE_MAIN
module-str = zephcore_main
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"

module = ZEPHCORE_BLE
module-str = zephcore_ble
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"

module = ZEPHCORE_LORA
module-str = zephcore_lora
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"

module = ZEPHCORE_BOARD
module-str = zephcore_board
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"

module = ZEPHCORE_DATASTORE
module-str = zephcore_datastore
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"

module = ZEPHCORE_GPS
module-str = zephcore_gps
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"

module = ZEPHCORE_SENSORS
module-str = zephcore_sensors
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"

module = ZEPHCORE_USB
module-str = zephcore_usb
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"

module = ZEPHCORE_UI_ACTIONS
module-str = zephcore_ui_actions
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"

module = ZEPHCORE_WIFI_OTA
module-str = zephcore_wifi_ota
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"

module = ZEPHCORE_OBSERVER
module-str = zephcore_observer
source "$(ZEPHYR_BASE)/subsys/logging/Kconfig.template.log_config"

menu "ZephCore"

config ZEPHCORE_QSPI_RDID_PROBE
	bool "QSPI flash bring-up probe"
	help
	  Five seconds after boot, report whether the QSPI flash driver came
	  up. If it did not, print the init errno the driver discards and
	  re-read the JEDEC id by bit-banging the pins as single-lane SPI,
	  which distinguishes a driver-side fault from a part that is not
	  answering. Skipped entirely when the flash is working, so it is safe
	  to leave enabled. Bring-up diagnostic.

config ZEPHCORE_RESET_ON_FATAL_ERROR
	bool "Reboot on fatal error"
	default y
	help
	  Cold-reboot instead of halting when a fatal error occurs (CPU
	  exception, stack overflow, k_panic, kernel oops).  Production default;
	  turn it off (boards/common/debug.conf does) so a debugger can inspect
	  the halted core.  Implemented via a k_sys_fatal_error_handler()
	  override in helpers/fatal_reboot.c — Zephyr has no built-in Kconfig
	  for this.

menu "Device Role"

choice ZEPHCORE_ROLE
	prompt "Device Role"
	default ZEPHCORE_ROLE_COMPANION
	help
	  Select the device role. Companion devices connect to mobile apps via BLE.
	  Repeaters forward packets and are configured via USB serial CLI.

config ZEPHCORE_ROLE_COMPANION
	bool "Companion Device"
	help
	  BLE companion device that connects to ZephCore mobile app.
	  Includes BLE stack, contact management, offline queue, channels.

config ZEPHCORE_ROLE_REPEATER
	bool "Repeater"
	help
	  LoRa mesh repeater with USB serial CLI interface.
	  No BLE stack. Has ACL authentication, region filtering,
	  neighbor tracking, and full CLI command set.

config ZEPHCORE_ROLE_OBSERVER
	bool "Observer (listen-only, WiFi+MQTT)"
	help
	  ESP32-only listen-only node. Receives LoRa packets without
	  forwarding. Connects to WiFi (STA mode) and publishes received
	  packets to an MQTT broker in meshcoretomqtt-compatible format.
	  All parameters (WiFi SSID/PSK, MQTT host/port/TLS/user/pass,
	  IATA location code) are configured at runtime via serial CLI
	  and stored in LittleFS. No BLE, no advertising, no routing.

config ZEPHCORE_ROLE_ROOM_SERVER
	bool "Room Server (shared BBS, USB serial CLI)"
	help
	  Store-and-forward shared message room (a "BBS").  Clients log in
	  with an admin or guest password and post messages; the server
	  pushes each new post to all other logged-in clients and tracks a
	  per-client sync cursor.  No BLE — configured via USB serial CLI.
	  Reuses the repeater's ACL, region filtering and CLI command set.

endchoice

config ZEPHCORE_COMPANION_USB
	bool "Enable USB CDC companion transport"
	depends on USB_DEVICE_STACK_NEXT
	default y if ZEPHCORE_ROLE_COMPANION
	help
	  Compile and init the USB CDC-ACM companion protocol stack
	  (ZephyrCompanionUSB / ZephyrUSBCDC) independently of CONFIG_LOG.
	  The full ~60-opcode binary protocol is available over USB even in
	  production builds (CONFIG_LOG=n).  USB and BLE arbitrate the active
	  interface first-come-first-served: CMD_APP_START claims it for USB only
	  if no BLE session is already active (and is ignored while BLE is live);
	  on USB unplug the interface is handed back to BLE or released to idle.

	  Enabled by default for all companion builds on platforms that already
	  have CONFIG_USB_DEVICE_STACK_NEXT=y (all nRF52840 boards).  On
	  platforms without USB hardware (MG24, ESP32 classic, C3/C6) the
	  depends forces this to n automatically — no conf change needed.

	  For ESP32-S3 boards, USB_DEVICE_STACK_NEXT must be enabled first via
	  boards/common/esp32s3_usb.conf before this becomes active.

config ZEPHCORE_COMPANION_SERIAL
	bool "Enable plain-UART companion transport"
	depends on ZEPHCORE_ROLE_COMPANION && !ZEPHCORE_COMPANION_USB
	help
	  Run the same wired-companion stack (ZephyrCompanionUSB: frame parser,
	  TX ring, text CLI, BLE-arbitrated interface) over a plain UART instead
	  of a native-USB CDC-ACM endpoint.  For boards whose USB-C is wired to a
	  USB-UART bridge rather than the SoC's native USB (e.g. Heltec V3 /
	  CP2102), or that have no USB device controller at all.

	  The board points the transport at its UART with the
	  `zephcore,companion-uart` chosen node (e.g. = &uart0); that UART must be
	  dedicated to the companion (console/shell moved off or disabled), since
	  the protocol stream and a text console cannot share one line.  Coexists
	  with BLE exactly like the USB backend — same first-come arbitration.

	  Distinct from the legacy SerialCompanionTransport (no-BLE STM32WL path):
	  this one keeps BLE and adds the text CLI.

if ZEPHCORE_ROLE_REPEATER || ZEPHCORE_ROLE_ROOM_SERVER

config ZEPHCORE_REPEATER_UPLINK
	bool "Enable repeater WiFi+MQTT uplink"
	depends on ZEPHCORE_ROLE_REPEATER && SOC_FAMILY_ESPRESSIF_ESP32
	default n
	help
	  Adds observer-style WiFi station and MQTT packet publishing to
	  repeater builds. Configuration is stored in LittleFS and applied
	  on reboot.

config ZEPHCORE_MAX_CLIENTS
	int "Maximum ACL clients"
	default 32
	help
	  Maximum clients in the Access Control List.
	  Each client stores pubkey, permissions, and shared secret.

config ZEPHCORE_MAX_NEIGHBOURS
	int "Maximum tracked neighbors"
	default 50
	help
	  Maximum number of neighboring repeaters to track.
	  Set to 0 to disable neighbor tracking.
	  Default 50 matches upstream Arduino MeshCore repeater variants.

config ZEPHCORE_MAX_REGION_ENTRIES
	int "Maximum region entries"
	default 32
	help
	  Maximum entries in the region map for flood filtering.
	  Regions control which transport codes are allowed to flood.

config ZEPHCORE_ADMIN_PASSWORD
	string "Default admin password"
	default "password"
	help
	  Default password for admin access to the repeater.
	  Should be changed on first configuration!

config ZEPHCORE_GUEST_PASSWORD
	string "Default guest password"
	default ""
	help
	  Default password for guest access.

	  An empty string means different things per role, matching Arduino
	  MeshCore:

	    REPEATER     - open guest access.  A blank submitted password logs
	                   in as PERM_ACL_GUEST, which cannot run CLI commands
	                   or read the access list; it gets login plus
	                   status/telemetry.
	    ROOM SERVER  - guest access disabled, so a room is never
	                   accidentally left open.  Arduino reaches the same
	                   place by defaulting the room server's guest password
	                   to ROOM_PASSWORD instead of leaving it empty.

	  Set a non-empty value to require a password on either role.

config ZEPHCORE_MAX_UNSYNCED_POSTS
	int "Room server: max buffered posts"
	default 32
	depends on ZEPHCORE_ROLE_ROOM_SERVER
	help
	  Size of the room server's circular post buffer.  When full, the
	  oldest unsynced post is overwritten.  Matches upstream MeshCore (32).

endif # ZEPHCORE_ROLE_REPEATER || ZEPHCORE_ROLE_ROOM_SERVER

config ZEPHCORE_TIMESYNC_QUORUM
	int "Mesh time sync: consensus quorum"
	default 6
	range 3 32
	help
	  Minimum eligible advert senders required before a mesh time-sync
	  consensus may step the clock (a strict majority of them must also
	  agree). Each quorum unit costs a local attacker one more physical
	  radio sustained for tenure-hours, so lowering this trades eclipse
	  resistance for coverage on small meshes (e.g. 7 repeaters = exactly
	  6 potential voters). Floor 3 matches the bootstrap quorum — below
	  that the interval intersection degenerates. Deliberately a build-time
	  knob, not a runtime pref: this is a security parameter.

config ZEPHCORE_TIMESYNC_TABLE_SIZE
	int "Mesh time sync: sample table slots"
	default 16 if ZEPHCORE_ROLE_COMPANION
	default 32
	range 8 64
	help
	  Per-sender advert sample slots (24 bytes each) for the mesh
	  time-sync consensus. 32 fits dense neighborhoods; companions
	  default to 16 (RAM-bound role). Must be at least the quorum for
	  normal-mode stepping to be reachable.

endmenu # Device Role

if ZEPHCORE_ROLE_COMPANION

config ZEPHCORE_MAX_CONTACTS
	int "Maximum number of contacts"
	default 350
	help
	  Maximum number of contacts that can be stored.

config ZEPHCORE_MAX_CHANNELS
	int "Maximum number of channels"
	default 40
	help
	  Maximum number of group channels that can be stored.

config ZEPHCORE_OFFLINE_QUEUE_SIZE
	int "Offline message queue size"
	default 256
	help
	  Size of the offline message queue for incoming messages.
	  Arduino MeshCore uses 256 for both T1000-E and Wio Tracker.

config ZEPHCORE_ACK_TABLE_SIZE
	int "ACK tracking table size"
	default 16
	help
	  Number of pending ACKs that can be tracked.

	  Sizing this below the number of messages that can be in flight at
	  once costs delivery confirmations: a peer working through a backlog
	  can answer seconds later, and an entry evicted before its ACK
	  arrives leaves the message unconfirmed, prompting a retry that adds
	  further load. Each entry costs 16 bytes.

config ZEPHCORE_MAX_CONNECTIONS
	int "Maximum server connections"
	default 16
	help
	  Maximum number of simultaneous room server connections with keep-alive.

config ZEPHCORE_ADVERT_PATH_TABLE_SIZE
	int "Advert path table size"
	default 16
	help
	  Size of recently-heard advert path table.

menu "BLE Configuration"

config ZEPHCORE_BLE_PASSKEY
	int "BLE pairing passkey (6 digits)"
	default 123456
	range 0 999999
	help
	  Fixed 6-digit passkey for BLE MITM pairing.

config ZEPHCORE_BLE_QUEUE_SIZE
	int "BLE TX/RX message queue depth"
	default 24
	range 4 64
	help
	  Number of frames that can be queued for BLE TX/RX.
	  Larger = better buffering for bursty traffic, more RAM.
	  24 absorbs the app's bursty CMD_GET_CHANNEL pipelining during
	  channel sync (it fires up to MAX_CHANNELS requests back-to-back);
	  at 12 the recv queue overran and dropped requests, stalling sync.

config ZEPHCORE_BLE_CONN_MIN_INTERVAL
	int "BLE minimum connection interval (units: 1.25ms)"
	default 12
	range 6 3200
	help
	  Minimum BLE connection interval in 1.25ms units.
	  12 = 15ms. Matches Arduino default. Lower = more responsive, higher power.

config ZEPHCORE_BLE_CONN_MAX_INTERVAL
	int "BLE maximum connection interval (units: 1.25ms)"
	default 36
	range 6 3200
	help
	  Maximum BLE connection interval in 1.25ms units.
	  36 = 45ms. Matches Arduino default. Higher = lower power when idle.

config ZEPHCORE_BLE_CONN_LATENCY
	int "BLE connection latency (skip events)"
	default 4
	range 0 500
	help
	  Skip up to N connection events when idle to save power.
	  4 = skip 4 events (effective interval up to 375ms idle).

config ZEPHCORE_BLE_CONN_TIMEOUT
	int "BLE supervision timeout (units: 10ms)"
	default 500
	range 10 3200
	help
	  Supervision timeout in 10ms units.
	  500 = 5000ms. Connection drops if no data for this duration.

config ZEPHCORE_BLE_ADV_SLOW_INTERVAL
	int "BLE advertising interval (units: 0.625ms)"
	default 338
	range 244 3200
	help
	  Advertising interval in 0.625ms units.
	  338 = 211.25ms (Apple-compliant, used after 60s fast window).

config ZEPHCORE_BLE_DFU
	bool "Buttonless BLE DFU (legacy Nordic/Adafruit OTA jump)"
	depends on SOC_SERIES_NRF52
	default y
	help
	  Register the legacy Nordic DFU service (00001530/00001531) in the
	  running companion app so a paired phone can trigger the jump into the
	  Adafruit bootloader's BLE OTA mode without pressing a button. On a write
	  of 0x01 to the control point the device sets the bootloader GPREGRET
	  magic (0xA8) and resets; the bootloader then advertises for DFU and the
	  phone's DFU tool reconnects to flash. nRF52 only.

endmenu # BLE Configuration

menu "Linux Companion Transport (native_sim)"

config ZEPHCORE_TRANSPORT_TCP
	bool "Use TCP socket transport instead of BLE NUS"
	default n
	help
	  Replaces the ZephyrBLE adapter with LinuxTCPTransport, exposing
	  the companion NUS protocol over a TCP socket on Linux SBC builds
	  (BOARD=native_sim). Auto-enabled by boards/linux_native/linux_common.conf.

	  The wire format is MeshCore's SerialWifiInterface framing:
	  ['<'][len_LSB][len_MSB][NUS payload] app->node and ['>']... node->app.

config ZEPHCORE_LINUX_TCP_PORT
	int "TCP listen port"
	depends on ZEPHCORE_TRANSPORT_TCP
	default 5000
	range 1 65535
	help
	  Port the companion TCP transport binds to. Default 5000 matches
	  the upstream MeshCore Linux companion service so a single mobile
	  app TCP configuration works against both backends.

endmenu # Linux Companion Transport

endif # ZEPHCORE_ROLE_COMPANION

config ZEPHCORE_HOUSEKEEPING_INTERVAL_MS
	int "Periodic housekeeping interval (ms)"
	depends on ZEPHCORE_ROLE_COMPANION || ZEPHCORE_ROLE_ROOM_SERVER
	default 5000
	range 1000 60000
	help
	  Wake interval for noise floor calibration, battery reads, and UI refresh.
	  Longer = lower power, less responsive monitoring.

	  Companion and room server only.  Both still run a fixed periodic tick:
	  the companion's block contains genuine pollers (contact-dump progress,
	  BLE advertising watchdog) that have no deadline to arm, and the room
	  server is paced by its own 500 ms push timer regardless.  Repeaters are
	  deadline-driven instead — see ZEPHCORE_MAINTENANCE_BACKSTOP_MS.

config ZEPHCORE_MAINTENANCE_BACKSTOP_MS
	int "Repeater maintenance backstop (ms)"
	depends on ZEPHCORE_ROLE_REPEATER
	default 60000
	range 15000 600000
	help
	  Hard ceiling on how long a repeater will go without a maintenance pass.

	  main_repeater.cpp arms a one-shot wake at the soonest deadline reported
	  by msUntilNextMaintenance(), then clamps it to this value.  The clamp is
	  UNCONDITIONAL, so this is a safety net against a deadline that is missed
	  or mis-reported — not a value that should ever bind in normal operation.

	  MUST stay above the radio measurement interval ("set probe.interval",
	  default 15 s; ZEPHCORE_NOISE_FLOOR_INTERVAL_MS when probing is off) —
	  the shortest recurring deadline on an idle repeater.  Set at or below it
	  and this silently becomes the wake period, reproducing the old fixed 5 s
	  tick and making the deadline scheduling a no-op.  That is exactly the bug
	  the original 5000 default shipped with.

	  Lowering it costs wakes without improving responsiveness — real work is
	  already scheduled at its own deadline.  Raising it widens the window in
	  which a scheduling bug goes unnoticed.

config ZEPHCORE_NOISE_FLOOR_INTERVAL_MS
	int "Default radio measurement interval (ms)"
	default 15000
	range 1000 120000
	help
	  Fallback cadence for periodic radio measurements — the noise-floor RSSI
	  sample and the CAD probe that consumes it.

	  This is only the DEFAULT.  At runtime the "probe.interval" pref governs
	  both (see set probe.interval); this value applies when that pref is 0,
	  i.e. CAD probing is switched off but the floor sampler still has to run.

	  This used to be implicit: the sampler ran once per housekeeping tick, so
	  it inherited that 5 s period.  The sampler lives in shared radio code, so
	  roles that still tick periodically (companion, room server) keep 5000 —
	  raising it there would change their RF tracking for no power gain, since
	  their housekeeping timer wakes them at 5 s regardless.

	  Repeaters use 15 s, matching ZEPHCORE_CAD_PROBE_INTERVAL — the only other
	  recurring radio deadline — so the two do not interleave into separate
	  wakes any more often than they have to.

	  On a repeater this is one of the two shortest recurring deadlines, so it
	  bounds how long the SoC can sleep between wakes.  Raising it stretches
	  EMA warmup (8 samples) and the every-16th-sample unguarded bypass by the
	  same factor: at 15000 warmup is ~2 min and the bypass ~4 min apart; at
	  30000, ~4 min and ~8 min.  Past 15000 the CAD probe becomes the binding
	  deadline instead, so raise both or neither.

config ZEPHCORE_PM_BOOT_AWAKE_MS
	int "Block light sleep for this long after boot (ms)"
	depends on PM && SOC_FAMILY_ESPRESSIF_ESP32
	default 600000
	range 0 3600000
	help
	  How long after boot the SoC refuses to light-sleep, so the USB console
	  is guaranteed reachable for a configuration session.

	  This exists because nothing arms a UART wake source on ESP32 — see
	  helpers/pm_esp32_console.c.  Once the window closes, characters typed at
	  a sleeping node are DROPPED and the CLI stops answering over USB until
	  the board resets.  In practice connecting a terminal usually does reset
	  it (the USB bridge drives EN from DTR), which re-arms the window.

	  0 disables the window entirely — the node may sleep immediately after
	  boot, and the USB console becomes unusable except in the instants it
	  happens to be awake.  Do not set 0 on a board you still need to
	  configure.

	  Raising it costs only the extra awake time at the start of each boot.

config ZEPHCORE_PM_CONSOLE_UART_NUM
	int "Console UART index flushed before light sleep"
	depends on PM && SOC_FAMILY_ESPRESSIF_ESP32
	default 0
	range 0 2
	help
	  Hardware UART index (not a DT node) passed to
	  esp_rom_uart_tx_wait_idle() before entering light sleep, so a line in
	  the TX FIFO is not cut off mid-character by the clock stopping.

	  0 is correct for every board this is currently enabled on: their
	  console is uart0, whether via a USB-serial bridge (Heltec V3/V4/V4.3)
	  or the ROM-default pads.  Change it only if a board's console is moved
	  to another hardware UART — a wrong index does not fail the build, it
	  just silently flushes the wrong peripheral and lets the console
	  truncate again.

menu "Radio Configuration"

choice ZEPHCORE_RADIO_TYPE
	prompt "LoRa Radio Type"
	default ZEPHCORE_RADIO_NATIVE
	help
	  Select the LoRa radio driver.

config ZEPHCORE_RADIO_NATIVE
	bool "Zephyr native LoRa driver (SX126x, SX127x, LLCC68, STM32WL)"
	help
	  Uses Zephyr's built-in LoRa driver. Supports all radios that Zephyr
	  has device tree bindings for:
	    SX126x:  SX1261, SX1262, SX1268
	    SX127x:  SX1272, SX1276, SX1278
	    LLCC68:  Semtech LLCC68
	    STM32WL: STM32WL SUBGHZ radio (SX126x-based)
	  Used by: RAK4631, Wio Tracker L1, all XIAO + Wio-SX1262 boards

config ZEPHCORE_RADIO_LR1110
	bool "LR1110/LR1120/LR1121 (custom driver)"
	select LORA
	select SPI
	help
	  Semtech LR11xx via custom ZephCore driver (not upstream Zephyr).
	  Uses SPI mutex for thread safety + BUSY stuck recovery.
	  TCXO, RF switch, and PA config are in the device tree.
	  For any board with an LR1110, LR1120, or LR1121 radio.

config ZEPHCORE_RADIO_LR2021
	bool "LR2021 (custom driver)"
	select LORA
	select SPI
	help
	  Semtech LR2021 (LoRa Plus, 4th-gen) via custom ZephCore driver.
	  Supports sub-GHz + 2.4 GHz ISM + NTN/SATCOM.
	  TCXO, RF switch, and PA config are in the device tree.

config ZEPHCORE_RADIO_SX127X
	bool "SX127x (SX1272/SX1276/SX1278) via Zephyr loramac-node driver"
	help
	  Uses Zephyr's loramac-node-based LoRa driver for SX127x radios.
	  Supports SX1272, SX1276, and SX1278 chips via the standard Zephyr
	  LoRa API (lora_config, lora_send_async, lora_recv_async).

	  Chip-specific features not available via standard API are stubbed:
	    - Instantaneous RSSI (hwGetCurrentRSSI returns -80 dBm sentinel)
	    - Preamble detection (always false — TX won't abort mid-preamble)
	    - RX boost (no-op — SX127x has no dedicated boost register)
	    - AGC reset (no-op — loramac-node manages AGC internally)
	    - BUSY pin (false — SX127x has no BUSY signal)

	  Used by: TTGO LoRa32, Heltec LoRa32 V1/V2, and any board with
	  an SX1272/SX1276/SX1278 radio.

endchoice

config ZEPHCORE_DEFAULT_TX_POWER_DBM
	int "Default TX power (dBm)"
	default 22
	range -9 22
	help
	  Default SX1262 TX power in dBm for fresh devices (no saved prefs).
	  Boards with external PA should set this lower to avoid overdriving.
	  Example: Station G2 with 20dB PA gain uses 15 dBm (→ 35 dBm output).

config ZEPHCORE_MAX_TX_POWER_DBM
	int "Maximum TX power (dBm)"
	default 22
	range -9 22
	help
	  Hard cap on SX1262 TX power. The radio adapter clamps any value
	  above this, protecting external PAs from damage.
	  Example: Station G2 caps at 19 dBm to stay below PA P1dB (35 dBm).

config ZEPHCORE_SX126X_HELTEC_REG_PATCH
	bool "Apply undocumented SX126x register 0x8B5 RX improvement (external PA/FEM)"
	depends on ZEPHCORE_RADIO_NATIVE
	default n
	help
	  Sets the LSB of undocumented register 0x8B5 after the first lora_config().
	  Described by Heltec engineer @Quency-D in MeshCore PR#1398 — consistently
	  improves RX reception on boards with GC1109 or KCT8103L PA (Heltec V4/V4.3).
	  Upstream MeshCore also enables it for the RAK3401's SKY66122 FEM, so it is
	  not Heltec-specific despite the symbol name. Enable in board.conf for boards
	  with an external PA/FEM in the RX path.

endmenu # Radio Configuration

menu "Board Configuration"

config ZEPHCORE_BOARD_NAME
	string "Board name for identification"
	default "Zephyr Device"
	help
	  Human-readable board name returned by the 'board' CLI command
	  and reported in device info. Should match Arduino variant names
	  for consistency (e.g., "RAK 4631", "Wio Tracker L1", "T1000-E").

config ZEPHCORE_SD_FWID
	hex "SoftDevice firmware ID for DFU packages"
	default 0x00B6
	depends on SOC_SERIES_NRF52
	help
	  SoftDevice firmware ID used by adafruit-nrfutil when generating
	  DFU zip packages (--sd-req parameter). Must match the SoftDevice
	  version flashed with the Adafruit bootloader on the target device.
	  Only applicable to nRF52 boards with Adafruit bootloader.

	  Common values:
	    0x00B6 - S140 v6.1.1 (RAK4631, Wio Tracker, T1000-E)
	    0x0123 - S140 v7.3.0 (XIAO nRF52840, Ikoka Nano)

config ZEPHCORE_VBAT_MV_MULTIPLIER
	int "Battery voltage ADC multiplier (fallback)"
	default 7200
	help
	  Fallback multiplier if vbat-mv-multiplier is not defined in
	  devicetree zephyr,user node.

	  Prefer defining in devicetree alongside ADC channel:
	    zephyr,user {
	        io-channels = <&adc 7>;
	        vbat-mv-multiplier = <7200>;
	    };

	  Formula: mv = (raw * multiplier) / 4096

endmenu

menu "Debug & Logging"

config ZEPHCORE_PACKET_LOGGING
	bool "Enable mesh packet logging"
	default n
	depends on LOG
	help
	  Log all RX and TX mesh packets in Arduino-compatible format.
	  Output format matches Arduino MESH_PACKET_LOGGING exactly:
	  "HH:MM:SS - D/M/YYYY U: RX, len=N (type=N, route=D/F, payload_len=N) SNR=N RSSI=N score=N time=N hash=XXXX [XX -> XX]"

config ZEPHCORE_PACKET_LOGGING_ONLY
	bool "Quiet mode - only packet logs, no debug spam"
	default n
	depends on ZEPHCORE_PACKET_LOGGING
	help
	  When enabled, suppresses all other log messages and only outputs
	  packet logging (RAW, RX, TX lines). Uses printk() to bypass
	  log level filtering.

endmenu

menu "LoRa Power Saving"

config ZEPHCORE_LORA_RX_DUTY_CYCLE
	bool "Enable LoRa RX duty cycle power saving"
	default n
	help
	  Enable RX duty cycling (chip-autonomous sniff mode) for power
	  saving on battery-powered devices.  This sets the boot default;
	  it can be toggled at runtime via CLI "set rxduty on/off".

	  Window timing is computed per SF/BW/preamble from primary-source
	  constraints (SX1261/2 datasheet sec 13.1.7, AN1200.36): the deaf
	  time per cycle (sleep + TCXO/PLL wake transition) never exceeds
	  the budget needed to guarantee one full detection window inside
	  the sender's preamble, and the post-detect timer budget
	  (2*rxPeriod + sleepPeriod) always covers preamble + header.
	  With a 32-symbol sender preamble (current MeshCore firmware at
	  SF<=8) this yields ~40-55% radio-off time with zero loss against
	  updated senders.  Presets whose preamble is too short for a
	  guaranteed catch (16 symbols at SF>=9) automatically fall back to
	  continuous RX — no fake duty cycling.

	  Caveat for mixed meshes: nodes still on pre-preamble-32 firmware
	  (16 symbols at SF<=8) are only caught ~50% worst-phase.  Keep
	  this off on infrastructure until the local mesh has converted.
	  Floods are usually re-heard via updated repeaters; direct
	  messages from legacy nodes are the real exposure.

	  Duty cycle mode also resets AGC state on entry via
	  Calibrate(ALL), pins the boosted RX gain into the warm-start
	  retention list (datasheet sec 9.6, mandatory for SetRxDutyCycle),
	  and runs a parked-RX watchdog that re-arms the cycle after a
	  false preamble detect (inspect via CLI "get dc.restarts").

	  LR1110/LR20xx use the same chip-level SetRxDutyCycle MODE_RX
	  (identical preamble-detect-and-extend mechanism) sized by the same
	  adapter math; the LR1110's earlier "broken" status was a window-
	  sizing bug, not a chip defect. Both are wired but HW-unverified —
	  confirm no packet loss on a live board before trusting in
	  production. LR1110 has no infinite-park failure mode (its DC uses a
	  bounded 2*rx+sleep timeout), so it re-arms via the normal RX-timeout
	  path rather than the SX126x watchdog.

config ZEPHCORE_LORA_DC_MIN_SYMBOLS
	int "RX duty cycle: preamble symbols required for detection"
	range 4 16
	default 8
	help
	  Minimum LoRa preamble symbols that must land inside one open RX
	  window for detection to be considered guaranteed.  8 is Semtech's
	  own figure for sniff mode (AN1200.36 sec 4); their time-synced
	  LoRaWAN stacks budget 6.  Raise to 10-12 to trade radio-off time
	  for extra detection margin on very noisy sites.  SF5/6 adds +4
	  internally.  Lowering below 8 risks missed packets at low SNR.

config ZEPHCORE_LORA_DC_MARGIN_PCT
	int "RX duty cycle: sleep-budget safety margin (percent)"
	range 0 40
	default 5
	help
	  Percentage the per-cycle deaf-time budget is derated before the
	  sleep period is programmed.  The theoretical catch budget assumes
	  the sleep clock and wake transition are exact, but the chip's sleep
	  timer runs on an internal RC oscillator (RC64k on SX126x, RTC on
	  LR11xx) that drifts several percent over temperature, and the wake-
	  transition estimate is a datasheet figure the datasheet itself calls
	  "not accurate and may vary".  Either overshoot silently pushes real
	  deaf time past the budget and drops the fraction of packets whose
	  preamble phase aligns with the window edge — signal-strength-
	  independent loss that presents as random DC packet drops.  This
	  margin trades a little radio-off time for that robustness and is
	  shared by every radio (SX126x / LR11xx / LR20xx).  Set to 0 to
	  restore the old zero-margin behaviour.

	  Lowered from 15% to 5% on 2026-08-21.  What justifies it: the clock
	  half of the error is bounded, not open-ended.  RC64k is recalibrated
	  by Calibrate(ALL) (SX126X_CALIBRATE_RC64K is bit 0 of 0x7F) whenever
	  agcMaintenance() sees the chip temperature move AGC_RECAL_TEMP_DELTA_C
	  (5 C), so the drift this must absorb is what accrues inside a 5 C
	  window, not across the whole operating range since boot.

	  What it costs, at SF7/BW62.5/P=32 on a 5 ms-TCXO board: absolute slack
	  against the catch budget falls from 4608 us (15%) to 1536 us (5%), and
	  the tolerated sleep-timer error falls from 23.5% to 6.8%.  Off-time
	  goes 30.9% -> 34.9%.  Note the tolerance always exceeds the margin
	  percentage, because the derate applies to the whole budget while the
	  clock error acts only on the sleep portion.

	  The part NOT bounded by the temperature recalibration is the
	  wake-transition estimate in hwWakeupTimeUs() -- a programmed TCXO delay
	  (deterministic) plus ~1 ms of context restore and PLL lock that the
	  datasheet itself calls "not accurate and may vary".  At 5% that term
	  has 1536 us of headroom minus whatever RC64k drift consumes.  If a
	  board shows unexplained duty-cycle packet loss, raise this first --
	  the loss is signal-strength-independent, so RSSI will look fine and
	  only a PDR comparison against rxduty 0 will show it.

config ZEPHCORE_LORA_RETENTION_DEBUG
	bool "SX126x: verify the warm-start retention list on air"
	depends on ZEPHCORE_RADIO_NATIVE
	help
	  Read back the SX126x register retention list (0x029F) and the Rx gain
	  register (0x08AC) and report whether they survived, at chip init and
	  after every duty-cycle packet.

	  This exists to answer one question: does installing the retention list
	  once at init actually hold?  The list is what restores Rx gain across
	  the chip-internal sleep->RX wakes of SetRxDutyCycle, which the host
	  never sees.  If it does not hold, every window after the first sleep
	  listens at power-saving gain -- a silent 3 dB sensitivity loss with
	  nothing in the log, which is precisely the failure this check makes
	  visible.

	  The check runs after RX_DONE and before sx126x_restart_rx() rewrites
	  the gain, so it observes the value the packet was actually received
	  with rather than the one we are about to reapply.

	  Costs two SPI register reads per received packet.  Debug builds only;
	  enabled by boards/common/debug.conf.

endmenu

config ZEPHCORE_RTC_AUTODISCOVER
	bool "Auto-discover a hardware I2C RTC at boot"
	default y
	depends on I2C
	help
	  Probe I2C RTC chips declared with the "zephcore,rtc-i2c" binding
	  (DS3231 / PCF8563 / RV3028 / RX8130CE). Boards with a battery/cap-
	  backed RTC opt in by including boards/common/rtc-i2c.dtsi; if a chip
	  holds a valid time it is restored at boot — shown tagged "L" until the
	  next external sync. On every GPS/app/CLI sync the time is written back
	  so it survives power-off. Compact raw-I2C reader (no Zephyr RTC
	  subsystem). Safe to leave on: a board with no rtc-i2c.dtsi include has
	  no RTC nodes and compiles to nothing.

menu "GPS Configuration"

config ZEPHCORE_GPS_POLL_INTERVAL_SEC
	int "GPS poll interval in seconds"
	default 300
	range 10 86400
	help
	  How often to wake GPS and acquire a fix.
	  Default 300 seconds (5 minutes).

config ZEPHCORE_GPS_FIX_TIMEOUT_SEC
	int "GPS fix acquisition timeout in seconds"
	default 120
	range 10 300
	help
	  Maximum time to wait for GPS fix before giving up.
	  GPS will be powered off after this timeout.
	  Default 120s — 30s was too aggressive for Air530Z / MAX-7Q warm
	  starts in marginal sky conditions; the acquire loop would time out
	  before the 3-consecutive-good-fix gate could promote. Only applies
	  to wake cycles after the first successful fix since enable.

config ZEPHCORE_GPS_FIRST_FIX_TIMEOUT_SEC
	int "GPS first-acquisition timeout in seconds"
	default 300
	range 30 1800
	help
	  Bounded window for the FIRST position fix after GPS is enabled
	  (cold start). Longer than the normal fix timeout because a cold
	  start may need to download almanac data. If no fix is acquired in
	  this window the node drops into the normal duty cycle anyway, so
	  the GPS can never stay powered indefinitely with no sky view.
	  Default 300s (5 minutes). Companion mode only; repeaters use a
	  fixed 5-minute time-sync window.

config ZEPHCORE_REPEATER_GPS_INTERVAL_SEC
	int "Repeater GPS duty interval in seconds (boot default)"
	default 172800
	range 0 604800
	help
	  Default GPS standby interval for repeaters/room servers: how often
	  the GPS wakes for a time-sync fix. Default 172800s (48 hours).
	  0 = always-on (never sleeps). Applied at boot until the operator
	  overrides it with "set gps duty <sec>" (persisted to flash).
	  Companions default to ZEPHCORE_GPS_POLL_INTERVAL_SEC (300s) instead.

config ZEPHCORE_GPS_NMEA_DUMP
	bool "Log every NMEA sentence received from the GNSS module"
	depends on GNSS
	help
	  Print each sentence the GNSS driver parses, plus everything else the
	  module emits, to the console. For diagnosing a receiver that reports
	  no satellites: it distinguishes a module that is talking normally
	  while it searches the sky from one that is silent, at the wrong baud,
	  or has had its NMEA output disabled — cases that are otherwise
	  indistinguishable from the application.

	  Verbose: at 1 Hz with multi-GNSS this is a continuous stream, and on
	  a repeater it shares the console with the CLI. Diagnostic builds only.

config ZEPHCORE_GPS_NAV_MODE
	int "GNSS navigation dynamic model (CASIC $PCAS11)"
	range -1 7
	default 1 if ZEPHCORE_ROLE_REPEATER || ZEPHCORE_ROLE_ROOM_SERVER
	default 3
	help
	  Dynamic model sent to CASIC-family GNSS modules (Quectel L76K/L76KB,
	  Air530Z) on boards using the generic-NMEA driver: 0 = portable,
	  1 = stationary, 2 = pedestrian, 3 = automotive, 4 = sea,
	  5-7 = airborne. -1 sends nothing.

	  Defaults to stationary for fixed roles and automotive otherwise.
	  Worth setting rather than leaving alone: the model is stored IN THE
	  MODULE and survives reflashing the host, so a slot module that
	  previously lived in another device can arrive stuck in an airborne
	  model that quietly degrades fixes on a fixed site.

	  Boards that always carry a CASIC part use the air530z driver instead
	  — see CONFIG_GNSS_LUATOS_AIR530Z_NAV_MODE for those.

config ZEPHCORE_GPS_SAT_DIAG
	bool "GPS per-constellation satellite diagnostics"
	# Must depend on GNSS, not just select GNSS_SATELLITES: on a board with
	# no GNSS at all (native Linux, xiao_nrf52840) GNSS is n, and y-selecting
	# a symbol whose dependencies are unmet is a Kconfig error that aborts the
	# build — not a warning. The repeater default below would otherwise turn
	# this on for every GNSS-less repeater target.
	depends on GNSS
	select GNSS_SATELLITES
	default y if ZEPHCORE_ROLE_REPEATER
	help
	  Tally tracked satellites per constellation from GSV sentences and
	  report them in "get gps" as sys=G<n>/R<n>/E<n>/B<n>/?<n>
	  (GPS / GLONASS / Galileo / BeiDou / other).

	  This is the ground truth for whether the boot-time multi-constellation
	  configuration (PMTK353 / UBX-CFG-GNSS) actually took: a module still in
	  its GPS-only default emits only $GPGSV, so every counter but G stays 0.
	  Satellite count alone cannot prove this — only the talker IDs can.

	  Costs RAM: CONFIG_GNSS_SATELLITES adds 512 bytes to the driver's UART
	  RX buffer plus a per-satellite array (~770 bytes total, measured on
	  RAK3401). Default on for repeaters, which have the headroom; off for
	  companions, which are RAM-bound. Enable it explicitly for a companion
	  diagnostic build.

	  Independent of "set gps diag", which is a runtime toggle and always
	  available — this option only adds the per-constellation evidence to
	  its report.

endmenu

menu "WiFi OTA Update"

config ZEPHCORE_WIFI_OTA
	bool "WiFi OTA firmware update"
	depends on ZEPHCORE_ROLE_REPEATER
	help
	  Enable WiFi AP + HTTP server for OTA firmware updates.
	  Auto-enabled for ESP32 repeater builds via wifi_ota.conf.

	  When "start ota" is issued, starts WiFi AP "ZephCore-OTA"
	  and HTTP server. User uploads firmware via browser at
	  http://192.168.100.1/update. Requires MCUboot (--sysbuild).

if ZEPHCORE_WIFI_OTA

config ZEPHCORE_OTA_AP_SSID
	string "WiFi AP SSID for OTA"
	default "ZephCore-OTA"
	help
	  SSID of the WiFi access point started for OTA updates.
	  Open network (no password), matching Arduino behavior.

config ZEPHCORE_OTA_AP_IP
	string "WiFi AP static IP address"
	default "192.168.100.1"
	help
	  Static IP address assigned to the WiFi AP interface.
	  DHCP server hands out addresses starting at .10.

endif # ZEPHCORE_WIFI_OTA

endmenu

menu "UI Configuration"

config ZEPHCORE_UI_BUTTONS
	bool "Enable button input"
	default y
	depends on INPUT
	help
	  Enable button/joystick input with long-press, double-click,
	  and triple-click detection using Zephyr's native input subsystem.

config ZEPHCORE_UI_MULTI_TAP
	bool "Enable multi-tap input filter"
	default y
	depends on ZEPHCORE_UI_BUTTONS
	help
	  Enable the multi-tap input filter that counts rapid taps
	  within a configurable window and emits different key codes
	  based on tap count. Supports 1-4 tap levels via devicetree.
	  Single-tap has ~400ms latency (waits for window to expire).

config ZEPHCORE_UI_BUZZER
	bool "Enable buzzer"
	default y if $(dt_nodelabel_enabled,buzzer)
	select PWM
	help
	  Enable PWM buzzer with RTTTL melody support.
	  Auto-enabled when a "buzzer" nodelabel exists in devicetree.
	  Pulls in CONFIG_PWM automatically.

DT_CHOSEN_ZEPHCORE_DISPLAY := zephyr,display

config ZEPHCORE_UI_DISPLAY
	bool "Enable display (OLED, LCD, e-ink)"
	default y if $(dt_chosen_enabled,$(DT_CHOSEN_ZEPHCORE_DISPLAY))
	select DISPLAY
	select CHARACTER_FRAMEBUFFER
	help
	  Enable page-based UI on any Zephyr-supported display.
	  Auto-enabled when "zephyr,display" chosen node exists in DT.
	  Auto-detects display via chosen node or legacy nodelabels
	  (sh1106, ssd1306). Resolution and font are queried at runtime.
	  Supports OLED (SSD1306, SH1106), LCD, e-ink, etc.

config ZEPHCORE_UI_DISPLAY_AUTO_OFF_MS
	int "Display auto-off timeout (ms)"
	default 10000
	depends on ZEPHCORE_UI_DISPLAY
	help
	  Time in milliseconds before the display turns off
	  after the last user interaction. Set to 0 to disable.

config ZEPHCORE_UI_TIME_SOURCE_FRESH_HOURS
	int "Clock source tag freshness window (hours)"
	default 12
	help
	  The top-bar clock shows a one-character source tag: G (GPS),
	  A (app), N (network/SNTP). If the most recent sync from one of
	  those sources is older than this many hours, the tag falls back
	  to L (local) — the RTC is free-running on its own oscillator.
	  Set to 0 to keep the last external source tag indefinitely
	  (no freshness expiry). Drift on a 50 ppm crystal is ~4 s/day,
	  so this is a trust/freshness signal, not an accuracy limit.

config ZEPHCORE_AUTO_SHUTDOWN_MILLIVOLTS
	int "Low-battery auto-shutdown threshold (mV, 0 = disabled)"
	default 3300 if SOC_SERIES_NRF52
	default 0
	help
	  Companion only. When the battery falls below this voltage the device
	  shows a brief "Low Battery / Shutting Down" notice (3 s on OLED;
	  e-paper persists without delay) and enters System OFF to protect the
	  cell from over-discharge. The check piggybacks the existing periodic
	  housekeeping tick (no extra polling) and is skipped while externally
	  powered (USB/charger) via MainBoard::isExternalPowered().

	  3300 mV is ~10% SoC on the default LiPo curve (battery_curve.c) — a
	  protective floor that still leaves headroom for the under-load voltage
	  sag during a TX burst. Requires CONFIG_POWEROFF to actually power down.

	  This is only the boot DEFAULT: the value is copied into prefs and is
	  tunable at runtime over the USB CLI ("set autoshutdown <mv>" /
	  "get autoshutdown", 0 = off or 1-5000 mV), persisted to flash.

	  Defaults ON (3300) for nRF52 only: that is where isExternalPowered()
	  reads VBUS, so the "plugged in" gate actually works. ESP32 / MG24 /
	  nRF54L stay at 0 (off) — isExternalPowered() is a stub there, so a
	  USB/mains-powered node could otherwise self-shutdown on a low ADC
	  reading. Set explicitly per board to enable once VBUS detect exists.

config ZEPHCORE_UI_CONFIRM_WINDOW_MS
	int "Double-press confirmation window (ms)"
	default 500
	depends on ZEPHCORE_UI_DISPLAY
	help
	  Maximum time between the two KEY_ENTER presses that confirm
	  destructive actions (shutdown, DFU, offgrid toggle).  The default
	  500 ms suits boards where ENTER is a direct short tap.  Boards
	  with only one button that emit ENTER via a zephyr,input-longpress
	  filter (T-Echo, RAK4631 Pocket) need a longer window — two
	  1 s holds plus the release gap do not fit in 500 ms.  Set to
	  3000 on such boards.

config ZEPHCORE_DISPLAY_INSET
	int "Display inset margin (pixels)"
	default 0
	depends on ZEPHCORE_UI_DISPLAY
	help
	  Shrink the usable drawing area by this many pixels on each side.
	  mc_display_width/height() report the reduced size and all draw
	  primitives are offset by this amount.  Useful on panels (e.g.
	  LilyGo T-Echo SSD1681) where the outermost rows/columns show
	  hardware artefacts — a small inset hides them behind a clean
	  background margin.  Set to 0 on boards without edge artefacts.

config ZEPHCORE_DISPLAY_EPD_FULL_REFRESH_INTERVAL
	int "E-paper full-refresh interval (partial refreshes)"
	default 16
	depends on ZEPHCORE_UI_DISPLAY
	help
	  On e-paper panels, partial refreshes never fully clear the previous
	  image, so ghosting accumulates where text changes most (status bar
	  name/time/battery).  After this many partial refreshes, perform one
	  full refresh to clear the panel.  A full refresh is the ~2 s
	  black/white flash, so keep this large enough that it isn't constant
	  but small enough that ghosting never becomes unreadable.  Set to 0 to
	  disable periodic full refreshes (partial-only — accumulates ghosting).
	  Ignored on non-EPD displays (OLED/TFT never ghost).

config ZEPHCORE_DISPLAY_LARGE_FONT
	bool "Use a larger built-in CFB font"
	default n
	depends on ZEPHCORE_UI_DISPLAY && CHARACTER_FRAMEBUFFER_USE_DEFAULT_FONTS
	help
	  Select the smallest Zephyr built-in CFB font whose height is
	  >= 16 pixels (typically the 10x16 font).  Intended for larger
	  e-paper panels (T-Echo, ThinkNode M1) where the default 6x8
	  glyph is too small to read comfortably.  The ui_pages.c layout
	  auto-centers its content rows using the active font height, so
	  no per-page changes are needed.  Requires Zephyr's default CFB
	  fonts to be compiled in (CONFIG_CHARACTER_FRAMEBUFFER_USE_DEFAULT_FONTS).

config ZEPHCORE_DISPLAY_MONO_TFT
	bool "Monochrome TFT wrapper (MONO01 => RGB565)"
	default y if DT_HAS_ZEPHCORE_MONO_TFT_ENABLED
	depends on DT_HAS_ZEPHCORE_MONO_TFT_ENABLED
	help
	  Wraps an RGB TFT (e.g. ST7789V) as a PIXEL_FORMAT_MONO01 display.
	  CFB allocates x*y/8 bytes (~4 KB for 240×135) instead of ~64 KB RGB565,
	  making a colour TFT viable on RAM-constrained devices like the nRF52840.
	  On display_write(), converts 1bpp row-major MONO01 to RGB565 row by row.
	  Auto-enabled when a "zephcore,mono-tft" device exists in the devicetree.

config ZEPHCORE_EASTER_EGG_DOOM
	bool "Enable Doom raycaster easter egg"
	default n
	depends on ZEPHCORE_UI_DISPLAY && ZEPHCORE_UI_BUTTONS
	help
	  Hidden easter egg: Wolf3D-style raycaster on the OLED.
	  Button UI: triple-press ENTER on the Messages page.
	  Joystick UI: open the Tools menu and select "Doom".
	  Press BACK/ESC to exit.
	  Adds ~5KB flash and ~1.7KB RAM when enabled.

config ZEPHCORE_UI_JOYSTICK
	bool
	help
	  Set by board-level Kconfig when the hardware has a 5-way joystick or
	  D-pad. Not user-selectable; set by the board configuration.
	  Follows the same pattern as ZEPHCORE_UI_BUTTONS and ZEPHCORE_UI_DISPLAY.

config ZEPHCORE_UI_KEYBOARD
	bool
	help
	  Set by board-level Kconfig when the hardware has a full keyboard
	  (e.g. ThinkNode M9's STC8H matrix MCU on I2C). Not user-selectable.

	  A keyboard is a superset of a joystick — it supplies arrows, enter
	  and escape — so it drives the same full companion UI rather than a
	  separate one. Selecting this makes ZEPHCORE_UI_DESIGN_JOYSTICK
	  available on a board that has no 5-way stick.

	  Selected by ThinkNode M9. The STC8H driver is helpers/input/
	  stc8h_keypad.c; the keypad's arrow-key codes are still unknown, so
	  the driver logs any code it does not recognise (see its header).
	  The key-space reservation in helpers/ui-joystick/joystick_defs.h is
	  the groundwork that makes them droppable in later.

config ZEPHCORE_UI_DESIGN_BUTTON
	bool "Button-based page UI"
	default y if (ZEPHCORE_UI_BUTTONS || ZEPHCORE_UI_DISPLAY || ZEPHCORE_UI_BUZZER) && !ZEPHCORE_UI_DESIGN_JOYSTICK
	depends on (ZEPHCORE_UI_BUTTONS || ZEPHCORE_UI_DISPLAY || ZEPHCORE_UI_BUZZER) && !ZEPHCORE_UI_DESIGN_JOYSTICK
	help
	  Compile the default page-based button UI (helpers/ui-button/).
	  Auto-selected when any UI hardware is present and the joystick UI
	  design is not active. Disable to supply a custom ui_task.h
	  implementation without touching this Kconfig.

config ZEPHCORE_UI_DESIGN_JOYSTICK
	bool "Full companion UI (joystick or keyboard driven)"
	default y if ZEPHCORE_ROLE_COMPANION && ZEPHCORE_UI_DISPLAY && ZEPHCORE_UI_BUTTONS && (ZEPHCORE_UI_JOYSTICK || ZEPHCORE_UI_KEYBOARD)
	depends on ZEPHCORE_ROLE_COMPANION && ZEPHCORE_UI_DISPLAY && ZEPHCORE_UI_BUTTONS && (ZEPHCORE_UI_JOYSTICK || ZEPHCORE_UI_KEYBOARD)
	help
	  Full companion UI (helpers/ui-joystick/). Provides GPS, contacts,
	  channels, snake game, repeater admin, and all other companion screens.
	  Replaces ZEPHCORE_UI_DESIGN_BUTTON for companion builds on boards with
	  joystick or keyboard hardware.

	  Named "joystick" for the hardware that first drove it; the screens
	  themselves only consume the abstract key codes in joystick_defs.h, so
	  any input device supplying arrows/enter/escape can drive them.

endmenu

endmenu
