Sync while working on OT
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 12s
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 12s
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user