Sync while working on OT
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 12s

This commit is contained in:
2026-02-17 14:23:28 +01:00
parent 5406c9917f
commit 17cc33332d
14 changed files with 314 additions and 100 deletions

View File

@@ -1,10 +1,10 @@
CONFIG_LOG=y
# UART-Grundlagen
# UART basics
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
# Shell-Konfiguration
# Shell configuration
CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_SERIAL=y
@@ -16,13 +16,13 @@ CONFIG_SHELL_BACKEND_SERIAL=y
# CONFIG_CRC=y
# CONFIG_MCUMGR_TRANSPORT_SHELL=y
# # MCUMGR Gruppen
# # MCUMGR groups
# CONFIG_MCUMGR_GRP_OS=y
# CONFIG_MCUMGR_GRP_OS_ECHO=y
# CONFIG_MCUMGR_GRP_FS=y
# CONFIG_MCUMGR_GRP_FS_CHECKSUM_HASH=y
# Lasertag-spezifische Konfiguration
# Lasertag-specific configuration
CONFIG_LASERTAG_UTILS=y
CONFIG_FS_MGMT=y
CONFIG_FS_MGMT_LOG_LEVEL_DBG=n

View File

@@ -17,7 +17,7 @@ class nRF_FS_Client:
self.seq = 0
self.ser.reset_input_buffer()
except serial.SerialException as e:
print(f"Fehler: Konnte {port} nicht öffnen ({e})")
print(f"Error: Could not open {port} ({e})")
sys.exit(1)
def crc16(self, data):
@@ -77,7 +77,7 @@ class nRF_FS_Client:
if res is None or 'files' not in res:
return
# Sortierung: Verzeichnisse zuerst, dann Namen
# Sorting: directories first, then names
entries = sorted(res['files'], key=lambda x: (x.get('t', 'f') != 'd', x['n']))
count = len(entries)
@@ -86,7 +86,7 @@ class nRF_FS_Client:
name = entry['n']
is_dir = entry.get('t', 'f').startswith('d')
# Line-Art Auswahl
# Line style selection
# connector = "└── " if is_last else "├── "
connector = "└─ " if is_last else "├─ "
@@ -94,7 +94,7 @@ class nRF_FS_Client:
print(f"{prefix}{connector}{ICON_DIR if is_dir else ICON_FILE} {name}")
if is_dir:
# Prefix für die nächste Ebene erweitern
# Extend prefix for the next level
extension = " " if is_last else ""
sub_path = f"{path}/{name}".replace("//", "/")
self.list_recursive(sub_path, prefix + extension)
@@ -105,13 +105,13 @@ class nRF_FS_Client:
def main():
parser = argparse.ArgumentParser(description="nRF52840 LittleFS Tree Tool")
parser.add_argument("port", help="Serieller Port (z.B. /dev/cu.usbmodem...)")
parser.add_argument("port", help="Serial port (e.g. /dev/cu.usbmodem...)")
args = parser.parse_args()
client = nRF_FS_Client(args.port, 115200)
print(f"--- Dateistruktur auf nRF ({args.port}) ---")
print(f"--- Directory tree on nRF ({args.port}) ---")
try:
# Initialer Aufruf
# Initial call
client.list_recursive("/")
finally:
client.close()

View File

@@ -12,14 +12,14 @@ CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_LASERTAG_UTILS=y
CONFIG_IR_RECV=y
CONFIG_IR_RECV_LOG_LEVEL_DBG=y
CONFIG_IR_RECV_INVERT_SIGNAL=y
# UART basics
# Thread Analyzer aktivieren
CONFIG_THREAD_ANALYZER=y
CONFIG_THREAD_ANALYZER_AUTO=y
# Shell configuration
CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=5
# CPU-Laufzeit-Statistiken aktivieren
CONFIG_THREAD_RUNTIME_STATS=y
CONFIG_THREAD_RUNTIME_STATS_USE_TIMING_FUNCTIONS=y
# Lasertag-specific configuration

View File

@@ -17,7 +17,7 @@ class nRF_FS_Client:
self.seq = 0
self.ser.reset_input_buffer()
except serial.SerialException as e:
print(f"Fehler: Konnte {port} nicht öffnen ({e})")
print(f"Error: Could not open {port} ({e})")
sys.exit(1)
def crc16(self, data):
@@ -77,7 +77,7 @@ class nRF_FS_Client:
if res is None or 'files' not in res:
return
# Sortierung: Verzeichnisse zuerst, dann Namen
# Sorting: directories first, then names
entries = sorted(res['files'], key=lambda x: (x.get('t', 'f') != 'd', x['n']))
count = len(entries)
@@ -86,7 +86,7 @@ class nRF_FS_Client:
name = entry['n']
is_dir = entry.get('t', 'f').startswith('d')
# Line-Art Auswahl
# Line style selection
# connector = "└── " if is_last else "├── "
connector = "└─ " if is_last else "├─ "
@@ -94,7 +94,7 @@ class nRF_FS_Client:
print(f"{prefix}{connector}{ICON_DIR if is_dir else ICON_FILE} {name}")
if is_dir:
# Prefix für die nächste Ebene erweitern
# Extend prefix for the next level
extension = " " if is_last else ""
sub_path = f"{path}/{name}".replace("//", "/")
self.list_recursive(sub_path, prefix + extension)
@@ -105,13 +105,13 @@ class nRF_FS_Client:
def main():
parser = argparse.ArgumentParser(description="nRF52840 LittleFS Tree Tool")
parser.add_argument("port", help="Serieller Port (z.B. /dev/cu.usbmodem...)")
parser.add_argument("port", help="Serial port (e.g. /dev/cu.usbmodem...)")
args = parser.parse_args()
client = nRF_FS_Client(args.port, 115200)
print(f"--- Dateistruktur auf nRF ({args.port}) ---")
print(f"--- Directory tree on nRF ({args.port}) ---")
try:
# Initialer Aufruf
# Initial call
client.list_recursive("/")
finally:
client.close()

View File

@@ -1,25 +1,26 @@
CONFIG_LOG=y
# UART-Grundlagen
# UART basics
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
# Shell-Konfiguration
# Shell configuration
CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_CPLUSPLUS=y
# Lasertag-spezifische Konfiguration
# Lasertag-specific configuration
CONFIG_LASERTAG_UTILS=y
CONFIG_IR_RECV=y
CONFIG_IR_RECV_LOG_LEVEL_INF=y
CONFIG_IR_RECV_SIMULATOR=y
# Thread Analyzer aktivieren
# Enable Thread analyzer
CONFIG_THREAD_ANALYZER=y
CONFIG_THREAD_ANALYZER_AUTO=y
CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=5
# CPU-Laufzeit-Statistiken aktivieren
# Enable CPU runtime statistics
CONFIG_THREAD_RUNTIME_STATS=y
CONFIG_THREAD_RUNTIME_STATS_USE_TIMING_FUNCTIONS=y

View File

@@ -17,7 +17,7 @@ class nRF_FS_Client:
self.seq = 0
self.ser.reset_input_buffer()
except serial.SerialException as e:
print(f"Fehler: Konnte {port} nicht öffnen ({e})")
print(f"Error: Could not open {port} ({e})")
sys.exit(1)
def crc16(self, data):
@@ -77,7 +77,7 @@ class nRF_FS_Client:
if res is None or 'files' not in res:
return
# Sortierung: Verzeichnisse zuerst, dann Namen
# Sorting: directories first, then names
entries = sorted(res['files'], key=lambda x: (x.get('t', 'f') != 'd', x['n']))
count = len(entries)
@@ -86,7 +86,7 @@ class nRF_FS_Client:
name = entry['n']
is_dir = entry.get('t', 'f').startswith('d')
# Line-Art Auswahl
# Line style selection
# connector = "└── " if is_last else "├── "
connector = "└─ " if is_last else "├─ "
@@ -94,7 +94,7 @@ class nRF_FS_Client:
print(f"{prefix}{connector}{ICON_DIR if is_dir else ICON_FILE} {name}")
if is_dir:
# Prefix für die nächste Ebene erweitern
# Extend prefix for the next level
extension = " " if is_last else ""
sub_path = f"{path}/{name}".replace("//", "/")
self.list_recursive(sub_path, prefix + extension)
@@ -105,13 +105,13 @@ class nRF_FS_Client:
def main():
parser = argparse.ArgumentParser(description="nRF52840 LittleFS Tree Tool")
parser.add_argument("port", help="Serieller Port (z.B. /dev/cu.usbmodem...)")
parser.add_argument("port", help="Serial port (e.g. /dev/cu.usbmodem...)")
args = parser.parse_args()
client = nRF_FS_Client(args.port, 115200)
print(f"--- Dateistruktur auf nRF ({args.port}) ---")
print(f"--- Directory tree on nRF ({args.port}) ---")
try:
# Initialer Aufruf
# Initial call
client.list_recursive("/")
finally:
client.close()

View File

@@ -1,17 +1,17 @@
CONFIG_LOG=y
# UART-Grundlagen
# UART basics
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
# Shell-Konfiguration
# Shell configuration
CONFIG_SHELL_BACKEND_SERIAL=y
# Lasertag-spezifische Konfiguration
# Lasertag-specific configuration
CONFIG_BLE_MGMT=y
CONFIG_GAME_MGMT=y
CONFIG_GAME_MGMT_SHELL=y
CONFIG_GAME_MGMT_LOG_LEVEL_DBG=y
CONFIG_THREAD_MGMT=y
CONFIG_THREAD_MGMT_LOG_LEVEL_DBG=y
CONFIG_THREAD_MGMT_SHELL=y
CONFIG_THREAD_MGMT_SHELL=y

View File

@@ -1,6 +1,7 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <thread_mgmt.h>
#include <game_mgmt.h>
#include <lasertag_utils.h>
LOG_MODULE_REGISTER(OT_SAMPLE, LOG_LEVEL_INF);
@@ -15,5 +16,13 @@ int main(void)
return rc;
}
LOG_INF("Thread management initialized successfully.");
rc = game_mgmt_init();
if (rc < 0) {
LOG_ERR("Game management initialization failed: %d", rc);
return rc;
}
LOG_INF("Game management initialized successfully.");
return 0;
}

View File

@@ -17,7 +17,7 @@ class nRF_FS_Client:
self.seq = 0
self.ser.reset_input_buffer()
except serial.SerialException as e:
print(f"Fehler: Konnte {port} nicht öffnen ({e})")
print(f"Error: Could not open {port} ({e})")
sys.exit(1)
def crc16(self, data):
@@ -77,7 +77,7 @@ class nRF_FS_Client:
if res is None or 'files' not in res:
return
# Sortierung: Verzeichnisse zuerst, dann Namen
# Sorting: directories first, then names
entries = sorted(res['files'], key=lambda x: (x.get('t', 'f') != 'd', x['n']))
count = len(entries)
@@ -86,7 +86,7 @@ class nRF_FS_Client:
name = entry['n']
is_dir = entry.get('t', 'f').startswith('d')
# Line-Art Auswahl
# Line style selection
# connector = "└── " if is_last else "├── "
connector = "└─ " if is_last else "├─ "
@@ -94,7 +94,7 @@ class nRF_FS_Client:
print(f"{prefix}{connector}{ICON_DIR if is_dir else ICON_FILE} {name}")
if is_dir:
# Prefix für die nächste Ebene erweitern
# Extend prefix for the next level
extension = " " if is_last else ""
sub_path = f"{path}/{name}".replace("//", "/")
self.list_recursive(sub_path, prefix + extension)
@@ -105,13 +105,13 @@ class nRF_FS_Client:
def main():
parser = argparse.ArgumentParser(description="nRF52840 LittleFS Tree Tool")
parser.add_argument("port", help="Serieller Port (z.B. /dev/cu.usbmodem...)")
parser.add_argument("port", help="Serial port (e.g. /dev/cu.usbmodem...)")
args = parser.parse_args()
client = nRF_FS_Client(args.port, 115200)
print(f"--- Dateistruktur auf nRF ({args.port}) ---")
print(f"--- Directory tree on nRF ({args.port}) ---")
try:
# Initialer Aufruf
# Initial call
client.list_recursive("/")
finally:
client.close()

View File

@@ -1,14 +1,14 @@
CONFIG_LOG=y
# UART-Grundlagen
# UART basics
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
# Shell-Konfiguration
# Shell configuration
CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_SERIAL=y
# Lasertag-spezifische Konfiguration
# Lasertag-specific configuration
CONFIG_LASERTAG_UTILS=y
CONFIG_LASERTAG_UTILS_LOG_LEVEL_DBG=y

View File

@@ -17,7 +17,7 @@ class nRF_FS_Client:
self.seq = 0
self.ser.reset_input_buffer()
except serial.SerialException as e:
print(f"Fehler: Konnte {port} nicht öffnen ({e})")
print(f"Error: Could not open {port} ({e})")
sys.exit(1)
def crc16(self, data):
@@ -77,7 +77,7 @@ class nRF_FS_Client:
if res is None or 'files' not in res:
return
# Sortierung: Verzeichnisse zuerst, dann Namen
# Sorting: directories first, then names
entries = sorted(res['files'], key=lambda x: (x.get('t', 'f') != 'd', x['n']))
count = len(entries)
@@ -86,7 +86,7 @@ class nRF_FS_Client:
name = entry['n']
is_dir = entry.get('t', 'f').startswith('d')
# Line-Art Auswahl
# Line style selection
# connector = "└── " if is_last else "├── "
connector = "└─ " if is_last else "├─ "
@@ -94,7 +94,7 @@ class nRF_FS_Client:
print(f"{prefix}{connector}{ICON_DIR if is_dir else ICON_FILE} {name}")
if is_dir:
# Prefix für die nächste Ebene erweitern
# Extend prefix for the next level
extension = " " if is_last else ""
sub_path = f"{path}/{name}".replace("//", "/")
self.list_recursive(sub_path, prefix + extension)
@@ -105,13 +105,13 @@ class nRF_FS_Client:
def main():
parser = argparse.ArgumentParser(description="nRF52840 LittleFS Tree Tool")
parser.add_argument("port", help="Serieller Port (z.B. /dev/cu.usbmodem...)")
parser.add_argument("port", help="Serial port (e.g. /dev/cu.usbmodem...)")
args = parser.parse_args()
client = nRF_FS_Client(args.port, 115200)
print(f"--- Dateistruktur auf nRF ({args.port}) ---")
print(f"--- Directory tree on nRF ({args.port}) ---")
try:
# Initialer Aufruf
# Initial call
client.list_recursive("/")
finally:
client.close()

View File

@@ -2,6 +2,7 @@ menuconfig GAME_MGMT
bool "Game Management"
# select BT
select OPENTHREAD
select OPENTHREAD_COAP
help
Library for managing game states and logic in the lasertag device.

View File

@@ -66,6 +66,22 @@ typedef void (*game_mgmt_state_cb_t)(sys_state_t new_state);
*/
int game_mgmt_init(void);
/**
* @brief Send a game control payload as Thread multicast CoAP message.
* Uses realm-local all-nodes multicast address `ff03::1`.
* @param payload Payload to send.
* @return 0 on success, negative errno-style value on failure.
*/
int game_mgmt_send_control_multicast(const game_control_payload_t *payload);
/**
* @brief Send a game control payload as Thread unicast CoAP message.
* @param peer_addr_str IPv6 destination address string.
* @param payload Payload to send.
* @return 0 on success, negative errno-style value on failure.
*/
int game_mgmt_send_control_unicast(const char *peer_addr_str, const game_control_payload_t *payload);
/**
* @brief Set the system state and trigger corresponding actions.
* Leader: Starts/stops beacons. Nodes: Starts/stops heartbeats.

View File

@@ -1,6 +1,12 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/net/openthread.h>
#include <openthread/coap.h>
#include <openthread/ip6.h>
#include <openthread/instance.h>
#include <openthread/thread.h>
#include <errno.h>
#include <stdlib.h>
#include <thread_mgmt.h>
#include <game_mgmt.h>
@@ -9,12 +15,174 @@ LOG_MODULE_REGISTER(game_mgmt, CONFIG_GAME_MGMT_LOG_LEVEL);
static sys_state_t current_state = SYS_STATE_IDLE;
static uint64_t current_game_id = 0;
// Forward declaration for functions defined later in this file
void game_mgmt_init_coap(void);
int game_mgmt_init_coap(void);
otInstance *openthread_get_default_instance(void);
static otInstance *game_mgmt_get_instance(void)
{
struct openthread_context *context = openthread_get_default_context();
if (context)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
otInstance *instance = context->instance;
#pragma GCC diagnostic pop
if (instance)
{
return instance;
}
}
return openthread_get_default_instance();
}
static int ot_error_to_errno(otError err)
{
switch (err)
{
case OT_ERROR_NONE:
return 0;
case OT_ERROR_INVALID_ARGS:
return -EINVAL;
case OT_ERROR_NO_BUFS:
return -ENOMEM;
case OT_ERROR_INVALID_STATE:
return -EAGAIN;
case OT_ERROR_BUSY:
return -EBUSY;
case OT_ERROR_NO_ROUTE:
return -ENETUNREACH;
default:
return -EIO;
}
}
static int game_mgmt_send_control_to(const otIp6Address *peer_addr, const game_control_payload_t *payload)
{
otInstance *instance = game_mgmt_get_instance();
if (!instance || !peer_addr || !payload)
{
return -ENODEV;
}
if (!otIp6IsEnabled(instance) || (otThreadGetDeviceRole(instance) == OT_DEVICE_ROLE_DISABLED))
{
return -ENETDOWN;
}
otMessage *message = otCoapNewMessage(instance, NULL);
if (!message)
{
return -ENOMEM;
}
otCoapMessageInit(message, OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST);
otError ot_err = otCoapMessageAppendUriPathOptions(message, "g");
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("otCoapMessageAppendUriPathOptions failed: %d", ot_err);
otMessageFree(message);
return ot_error_to_errno(ot_err);
}
ot_err = otCoapMessageSetPayloadMarker(message);
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("otCoapMessageSetPayloadMarker failed: %d", ot_err);
otMessageFree(message);
return ot_error_to_errno(ot_err);
}
ot_err = otMessageAppend(message, payload, sizeof(*payload));
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("otMessageAppend failed: %d", ot_err);
otMessageFree(message);
return ot_error_to_errno(ot_err);
}
otMessageInfo messageInfo = {0};
const otIp6Address *mesh_local_eid = otThreadGetMeshLocalEid(instance);
if (!mesh_local_eid)
{
LOG_ERR("otThreadGetMeshLocalEid returned NULL");
otMessageFree(message);
return -ENETDOWN;
}
messageInfo.mSockAddr = *mesh_local_eid;
messageInfo.mPeerAddr = *peer_addr;
messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
ot_err = otCoapSendRequest(instance, message, &messageInfo, NULL, NULL);
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("otCoapSendRequest failed: %d", ot_err);
otMessageFree(message);
return ot_error_to_errno(ot_err);
}
return 0;
}
int game_mgmt_send_control_multicast(const game_control_payload_t *payload)
{
otIp6Address multicast_addr;
otError ot_err = otIp6AddressFromString("ff03::1", &multicast_addr);
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("Error parsing multicast address: %d", ot_err);
return ot_error_to_errno(ot_err);
}
int err = game_mgmt_send_control_to(&multicast_addr, payload);
if (err)
{
LOG_ERR("Error sending multicast control message: %d", err);
}
return err;
}
int game_mgmt_send_control_unicast(const char *peer_addr_str, const game_control_payload_t *payload)
{
if (!peer_addr_str)
{
LOG_ERR("Peer address string is NULL");
return -EINVAL;
}
otIp6Address peer_addr;
otError ot_err = otIp6AddressFromString(peer_addr_str, &peer_addr);
if (ot_err != OT_ERROR_NONE)
{
LOG_ERR("Error parsing peer address '%s': %d", peer_addr_str, ot_err);
return ot_error_to_errno(ot_err);
}
int err = game_mgmt_send_control_to(&peer_addr, payload);
if (err)
{
LOG_ERR("Error sending unicast control message to '%s': %d", peer_addr_str, err);
}
return err;
}
int game_mgmt_init(void)
{
game_mgmt_init_coap();
int err = game_mgmt_init_coap();
if (err)
{
LOG_ERR("Failed to initialize CoAP service: %d", err);
return err;
}
LOG_DBG("Game Management initialized (State: IDLE)");
return 0;
}
@@ -22,38 +190,55 @@ int game_mgmt_init(void)
void game_mgmt_set_state(sys_state_t state)
{
if (current_state == state)
{
return;
}
LOG_DBG("State Change: %d -> %d", current_state, state);
current_state = state;
/* Future: Trigger events based on state (e.g. start/stop beacon) */
}
sys_state_t game_mgmt_get_state(void) { return current_state; }
sys_state_t game_mgmt_get_state(void)
{
return current_state;
}
void game_mgmt_set_game_id(uint64_t id)
{
if (current_game_id == id)
{
return;
}
current_game_id = id;
LOG_DBG("Game ID updated: 0x%llx", id);
}
uint64_t game_mgmt_get_game_id(void) { return current_game_id; }
uint64_t game_mgmt_get_game_id(void)
{
return current_game_id;
}
static void handle_game_control(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
ARG_UNUSED(aContext);
ARG_UNUSED(aMessageInfo);
game_control_payload_t payload;
if (otCoapMessageGetCode(aMessage) != OT_COAP_CODE_POST) {
if (otCoapMessageGetCode(aMessage) != OT_COAP_CODE_POST)
{
return;
}
uint16_t offset = otMessageGetOffset(aMessage);
if (otMessageRead(aMessage, offset, &payload, sizeof(payload)) >= 1) {
if (payload.command == GAME_CTRL_CMD_START_GAME) {
LOG_DBG("Broadcast received: start time 0x%08X, duration %u s",
payload.data.start_game.start_time,
payload.data.start_game.duration);
if (otMessageRead(aMessage, offset, &payload, sizeof(payload)) >= 1)
{
if (payload.command == GAME_CTRL_CMD_START_GAME)
{
LOG_DBG("Broadcast received: start time 0x%08X, duration %u s",
payload.data.start_game.start_time,
payload.data.start_game.duration);
}
}
}
@@ -62,61 +247,63 @@ static otCoapResource game_ctrl_res = {
.mUriPath = "g",
.mHandler = handle_game_control,
.mContext = NULL,
.mNext = NULL
.mNext = NULL,
};
void game_mgmt_init_coap(void)
int game_mgmt_init_coap(void)
{
struct otInstance *instance = openthread_get_default_instance();
if (instance) {
otCoapAddResource(instance, &game_ctrl_res);
LOG_DBG("Registrated CoAP Ressource '/g'");
otInstance *instance = game_mgmt_get_instance();
if (!instance)
{
LOG_ERR("OpenThread instance unavailable");
return -ENODEV;
}
otError err = otCoapStart(instance, OT_DEFAULT_COAP_PORT);
if ((err != OT_ERROR_NONE) && (err != OT_ERROR_ALREADY))
{
LOG_ERR("Failed to start CoAP service: %d", err);
return ot_error_to_errno(err);
}
otCoapAddResource(instance, &game_ctrl_res);
LOG_DBG("Registered CoAP resource '/g'");
return 0;
}
#if IS_ENABLED(CONFIG_GAME_MGMT_SHELL)
#include <zephyr/shell/shell.h>
static int cmd_game_start(const struct shell *sh, size_t argc, char **argv)
{
struct otInstance *instance = openthread_get_default_instance();
if (!instance) return -ENODEV;
uint32_t delay_s = (argc > 1) ? strtoul(argv[1], NULL, 10) : 10;
game_control_payload_t payload = { .command = GAME_CTRL_CMD_START_GAME };
game_control_payload_t payload = {.command = GAME_CTRL_CMD_START_GAME};
uint64_t now = thread_mgmt_get_network_time();
if (now == 0) {
shell_error(sh, "Netzwerkzeit nicht synchronisiert!");
if (now == 0)
{
shell_error(sh, "Network time not synchronized, cannot start game.");
return -EAGAIN;
}
payload.data.start_game.start_time = (uint32_t)(now + (delay_s * 1000000ULL));
payload.data.start_game.duration = 600; // 10 Min Standard
payload.data.start_game.duration = 600;
otMessage *message = otCoapNewMessage(instance, NULL);
otCoapMessageInit(message, OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST);
otCoapMessageAppendUriPathOptions(message, "g");
otCoapMessageSetPayloadMarker(message);
otMessageAppend(message, &payload, sizeof(payload));
otMessageInfo messageInfo = {0};
otIp6AddressFromString("ff03::1", &messageInfo.mPeerAddr);
messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
otError err = otCoapSendRequest(instance, message, &messageInfo, NULL, NULL);
if (err == OT_ERROR_NONE) {
shell_print(sh, "Start-Broadcast für T+%u s gesendet.", delay_s);
} else {
shell_error(sh, "Fehler beim Senden: %d", err);
otMessageFree(message);
int err = game_mgmt_send_control_multicast(&payload);
if (err)
{
shell_error(sh, "game_mgmt_send_control_multicast failed: %d", err);
return err;
}
shell_print(sh, "Start broadcast sent for T+%u s.", delay_s);
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(game_sub,
SHELL_CMD_ARG(start, NULL, "<delay_s>", cmd_game_start, 2, 0),
SHELL_SUBCMD_SET_END
);
SHELL_SUBCMD_SET_END);
SHELL_CMD_REGISTER(game, &game_sub, "Game Management", NULL);
#endif // CONFIG_GAME_MGMT_SHELL
#endif