diff --git a/firmware/apps/_samples/ir_recv_adc/.gitignore b/firmware/apps/_samples/ir_recv_adc/.gitignore new file mode 100644 index 0000000..a5309e6 --- /dev/null +++ b/firmware/apps/_samples/ir_recv_adc/.gitignore @@ -0,0 +1 @@ +build*/ diff --git a/firmware/apps/_samples/ir_recv_adc/CMakeLists.txt b/firmware/apps/_samples/ir_recv_adc/CMakeLists.txt new file mode 100644 index 0000000..19f6b90 --- /dev/null +++ b/firmware/apps/_samples/ir_recv_adc/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.20.0) + +# Tell Zephyr to look into our libs folder for extra modules +list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(_mcumgr) + +target_sources(app PRIVATE src/main.c) diff --git a/firmware/apps/_samples/ir_recv_adc/boards/nrf52840dk_nrf52840.overlay b/firmware/apps/_samples/ir_recv_adc/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 0000000..a74527e --- /dev/null +++ b/firmware/apps/_samples/ir_recv_adc/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,51 @@ +#include +#include + +/ { + zephyr,user { + /* Mappt die logischen Kanäle 0-3 auf die physischen ADC-Knoten */ + io-channels = <&adc 0>, <&adc 1>, <&adc 2>, <&adc 3>; + }; +}; + +&adc { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1_4"; + zephyr,reference = "ADC_REF_VDD_1_4"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; /* Pin P0.02 */ + zephyr,resolution = <12>; + }; + + channel@1 { + reg = <1>; + zephyr,gain = "ADC_GAIN_1_4"; + zephyr,reference = "ADC_REF_VDD_1_4"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; /* Pin P0.03 */ + zephyr,resolution = <12>; + }; + + channel@2 { + reg = <2>; + zephyr,gain = "ADC_GAIN_1_4"; + zephyr,reference = "ADC_REF_VDD_1_4"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; /* Pin P0.04 */ + zephyr,resolution = <12>; + }; + + channel@3 { + reg = <3>; + zephyr,gain = "ADC_GAIN_1_4"; + zephyr,reference = "ADC_REF_VDD_1_4"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; /* Pin P0.05 */ + zephyr,resolution = <12>; + }; +}; \ No newline at end of file diff --git a/firmware/apps/_samples/ir_recv_adc/nrf52840dk_nrf52840.overlay b/firmware/apps/_samples/ir_recv_adc/nrf52840dk_nrf52840.overlay new file mode 100644 index 0000000..4d8fb85 --- /dev/null +++ b/firmware/apps/_samples/ir_recv_adc/nrf52840dk_nrf52840.overlay @@ -0,0 +1,44 @@ +// To get started, press Ctrl+Space (or Option+Esc) to bring up the completion menu and view the available nodes. + +// You can also use the buttons in the sidebar to perform actions on nodes. +// Actions currently available include: + +// * Enabling / disabling the node +// * Adding the bus to a bus +// * Removing the node +// * Connecting ADC channels + +// For more help, browse the DeviceTree documentation at https://docs.zephyrproject.org/latest/guides/dts/index.html +// You can also visit the nRF DeviceTree extension documentation at https://docs.nordicsemi.com/bundle/nrf-connect-vscode/page/guides/ncs_configure_app.html#devicetree-support-in-the-extension + +/ { + chosen { + nordic,pm-ext-flash = &mx25r64; + }; +}; + +&pinctrl { + i2s0_default: i2s0_default { + group1 { + psels = , /* SCK Pin */ + , /* WS/LRCK Pin */ + ; /* SD Pin (DIN am MAX) */ + }; + }; + + i2s0_sleep: i2s0_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; + +&i2s0 { + status = "okay"; + pinctrl-0 = <&i2s0_default>; + pinctrl-1 = <&i2s0_sleep>; + pinctrl-names = "default", "sleep"; +}; \ No newline at end of file diff --git a/firmware/apps/_samples/ir_recv_adc/pm_static.yml b/firmware/apps/_samples/ir_recv_adc/pm_static.yml new file mode 100644 index 0000000..1f22f30 --- /dev/null +++ b/firmware/apps/_samples/ir_recv_adc/pm_static.yml @@ -0,0 +1,4 @@ +littlefs_storage: + address: 0x0 + size: 0x800000 + region: external_flash diff --git a/firmware/apps/_samples/ir_recv_adc/prj.conf b/firmware/apps/_samples/ir_recv_adc/prj.conf new file mode 100644 index 0000000..6dd22e8 --- /dev/null +++ b/firmware/apps/_samples/ir_recv_adc/prj.conf @@ -0,0 +1,25 @@ +CONFIG_LOG=y + +# UART-Grundlagen +CONFIG_SERIAL=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# Shell-Konfiguration +CONFIG_SHELL=y +CONFIG_SHELL_BACKEND_SERIAL=y + +# Lasertag-spezifische Konfiguration +CONFIG_LASERTAG_UTILS=y +CONFIG_IR_RECV=y +CONFIG_IR_RECV_LOG_LEVEL_DBG=y +CONFIG_IR_RECV_INVERT_SIGNAL=y + +# # Thread Analyzer aktivieren +# CONFIG_THREAD_ANALYZER=y +# CONFIG_THREAD_ANALYZER_AUTO=y +# CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=5 + +# # CPU-Laufzeit-Statistiken aktivieren +# CONFIG_THREAD_RUNTIME_STATS=y +# CONFIG_THREAD_RUNTIME_STATS_USE_TIMING_FUNCTIONS=y + diff --git a/firmware/apps/_samples/ir_recv_adc/src/main.c b/firmware/apps/_samples/ir_recv_adc/src/main.c new file mode 100644 index 0000000..fc8fe49 --- /dev/null +++ b/firmware/apps/_samples/ir_recv_adc/src/main.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +LOG_MODULE_REGISTER(ir_recv_sim, LOG_LEVEL_INF); + +int main(void) +{ + LOG_INF("Starting IR receive simulator application..."); + lasertag_utils_init(); + ir_recv_init(); + + return 0; +} \ No newline at end of file diff --git a/firmware/apps/_samples/ir_recv_adc/tool/requirements.txt b/firmware/apps/_samples/ir_recv_adc/tool/requirements.txt new file mode 100644 index 0000000..a1e37cd --- /dev/null +++ b/firmware/apps/_samples/ir_recv_adc/tool/requirements.txt @@ -0,0 +1,2 @@ +pyserial +cbor2 diff --git a/firmware/apps/_samples/ir_recv_adc/tool/tool.py b/firmware/apps/_samples/ir_recv_adc/tool/tool.py new file mode 100644 index 0000000..9cd5273 --- /dev/null +++ b/firmware/apps/_samples/ir_recv_adc/tool/tool.py @@ -0,0 +1,120 @@ +import serial +import base64 +import cbor2 +import struct +import time +import argparse +import sys + +# Icons (NerdFont / Emoji) +ICON_DIR = "📁" +ICON_FILE = "📄" + +class nRF_FS_Client: + def __init__(self, port, baud): + try: + self.ser = serial.Serial(port, baud, timeout=0.2) + self.seq = 0 + self.ser.reset_input_buffer() + except serial.SerialException as e: + print(f"Fehler: Konnte {port} nicht öffnen ({e})") + sys.exit(1) + + def crc16(self, data): + crc = 0x0000 + for byte in data: + crc ^= (byte << 8) + for _ in range(8): + if crc & 0x8000: + crc = (crc << 1) ^ 0x1021 + else: + crc = crc << 1 + crc &= 0xFFFF + return crc + + def build_packet(self, group, cmd, payload): + self.seq = (self.seq + 1) % 256 + cbor_payload = cbor2.dumps(payload) + header = struct.pack(">BBHHBB", 0x00, 0x08, len(cbor_payload), group, self.seq, cmd) + full_body = header + cbor_payload + checksum = self.crc16(full_body) + full_msg = full_body + struct.pack(">H", checksum) + return struct.pack(">H", len(full_msg)) + full_msg + + def request(self, group, cmd, payload): + packet = self.build_packet(group, cmd, payload) + b64_data = base64.b64encode(packet).decode() + self.ser.write(f"\x06\t{b64_data}\n".encode()) + + full_response_b64 = "" + expected_len = -1 + start_time = time.time() + + while (time.time() - start_time) < 3.0: + line = self.ser.readline().strip() + if not line: + continue + + is_smp = line.startswith(b'\x06\t') or line.startswith(b'\x06\n') + is_cont_special = line.startswith(b'\x04\x14') and expected_len > 0 + + if is_smp or is_cont_special: + full_response_b64 += line[2:].decode() + try: + raw_data = base64.b64decode(full_response_b64) + if expected_len == -1 and len(raw_data) >= 2: + expected_len = struct.unpack(">H", raw_data[:2])[0] + + if expected_len != -1 and len(raw_data) >= expected_len + 2: + if raw_data[8] == self.seq: + return cbor2.loads(raw_data[10:-2]) + except: + continue + return None + + def list_recursive(self, path="/", prefix=""): + res = self.request(64, 0, {"path": path}) + if res is None or 'files' not in res: + return + + # Sortierung: Verzeichnisse zuerst, dann Namen + entries = sorted(res['files'], key=lambda x: (x.get('t', 'f') != 'd', x['n'])) + count = len(entries) + + for i, entry in enumerate(entries): + is_last = (i == count - 1) + name = entry['n'] + is_dir = entry.get('t', 'f').startswith('d') + + # Line-Art Auswahl + # connector = "└── " if is_last else "├── " + connector = "└─ " if is_last else "├─ " + + + print(f"{prefix}{connector}{ICON_DIR if is_dir else ICON_FILE} {name}") + + if is_dir: + # Prefix für die nächste Ebene erweitern + extension = " " if is_last else "│ " + sub_path = f"{path}/{name}".replace("//", "/") + self.list_recursive(sub_path, prefix + extension) + + def close(self): + if hasattr(self, 'ser') and self.ser.is_open: + self.ser.close() + +def main(): + parser = argparse.ArgumentParser(description="nRF52840 LittleFS Tree Tool") + parser.add_argument("port", help="Serieller Port (z.B. /dev/cu.usbmodem...)") + args = parser.parse_args() + + client = nRF_FS_Client(args.port, 115200) + print(f"--- Dateistruktur auf nRF ({args.port}) ---") + try: + # Initialer Aufruf + client.list_recursive("/") + finally: + client.close() + +if __name__ == "__main__": + main() \ No newline at end of file