|
| 1 | +// @ts-nocheck |
| 2 | +// deno run --allow-ffi --allow-read examples/serial_echo.ts |
| 3 | + |
| 4 | +interface CliOptions { |
| 5 | + lib: string; |
| 6 | + port?: string; |
| 7 | +} |
| 8 | + |
| 9 | +function parseArgs(): CliOptions { |
| 10 | + const opts: CliOptions = { lib: "./build/libCPP-Unix-Bindings.so" }; |
| 11 | + |
| 12 | + for (let i = 0; i < Deno.args.length; ++i) { |
| 13 | + const arg = Deno.args[i]; |
| 14 | + if (arg === "--lib" && i + 1 < Deno.args.length) { |
| 15 | + opts.lib = Deno.args[++i]; |
| 16 | + } else if (arg === "--port" && i + 1 < Deno.args.length) { |
| 17 | + opts.port = Deno.args[++i]; |
| 18 | + } else { |
| 19 | + console.warn(`Unknown argument '${arg}' ignored.`); |
| 20 | + } |
| 21 | + } |
| 22 | + return opts; |
| 23 | +} |
| 24 | + |
| 25 | +// ----------------------------------------------------------------------------- |
| 26 | +// Helper utilities for C interop |
| 27 | +// ----------------------------------------------------------------------------- |
| 28 | +const encoder = new TextEncoder(); |
| 29 | +const decoder = new TextDecoder(); |
| 30 | + |
| 31 | +function cString(str: string): Uint8Array { |
| 32 | + // Encodes JavaScript string as null-terminated UTF-8 byte array. |
| 33 | + const bytes = encoder.encode(str); |
| 34 | + const buf = new Uint8Array(bytes.length + 1); |
| 35 | + buf.set(bytes, 0); |
| 36 | + buf[bytes.length] = 0; |
| 37 | + return buf; |
| 38 | +} |
| 39 | + |
| 40 | +function pointer(view: Uint8Array): Deno.UnsafePointer { |
| 41 | + return Deno.UnsafePointer.of(view) as Deno.UnsafePointer; |
| 42 | +} |
| 43 | + |
| 44 | +// ----------------------------------------------------------------------------- |
| 45 | +// Load dynamic library |
| 46 | +// ----------------------------------------------------------------------------- |
| 47 | +const { lib, port: cliPort } = parseArgs(); |
| 48 | + |
| 49 | +const dylib = Deno.dlopen( |
| 50 | + lib, |
| 51 | + { |
| 52 | + serialOpen: { parameters: ["pointer", "i32", "i32", "i32", "i32"], result: "pointer" }, |
| 53 | + serialClose: { parameters: ["pointer"], result: "void" }, |
| 54 | + serialWrite: { parameters: ["pointer", "pointer", "i32", "i32", "i32"], result: "i32" }, |
| 55 | + serialReadUntil: { |
| 56 | + parameters: ["pointer", "pointer", "i32", "i32", "i32", "pointer"], |
| 57 | + result: "i32", |
| 58 | + }, |
| 59 | + serialGetPortsInfo: { parameters: ["pointer", "i32", "pointer"], result: "i32" }, |
| 60 | + } as const, |
| 61 | +); |
| 62 | + |
| 63 | +// ----------------------------------------------------------------------------- |
| 64 | +// 1. List available ports |
| 65 | +// ----------------------------------------------------------------------------- |
| 66 | +const sepBuf = cString(";"); |
| 67 | +const portsBuf = new Uint8Array(4096); |
| 68 | +dylib.symbols.serialGetPortsInfo( |
| 69 | + pointer(portsBuf), |
| 70 | + portsBuf.length, |
| 71 | + pointer(sepBuf), |
| 72 | +); |
| 73 | + |
| 74 | +const cPortsStr = decoder.decode(portsBuf.subarray(0, portsBuf.indexOf(0))); |
| 75 | +const ports = cPortsStr ? cPortsStr.split(";") : []; |
| 76 | +console.log("Available ports:"); |
| 77 | +for (const p of ports) { |
| 78 | + console.log(" •", p); |
| 79 | +} |
| 80 | +if (ports.length === 0) { |
| 81 | + console.error("No serial ports found (ttyUSB). Exiting."); |
| 82 | + dylib.close(); |
| 83 | + Deno.exit(1); |
| 84 | +} |
| 85 | + |
| 86 | +// ----------------------------------------------------------------------------- |
| 87 | +// 2. Echo test on selected port |
| 88 | +// ----------------------------------------------------------------------------- |
| 89 | +const portPath = cliPort ?? ports[0]; |
| 90 | +console.log(`\nUsing port: ${portPath}`); |
| 91 | + |
| 92 | +const portBuf = cString(portPath); |
| 93 | +const handle = dylib.symbols.serialOpen(pointer(portBuf), 115200, 8, 0, 0); |
| 94 | +if (handle === null) { |
| 95 | + console.error("Failed to open port!"); |
| 96 | + dylib.close(); |
| 97 | + Deno.exit(1); |
| 98 | +} |
| 99 | + |
| 100 | +// Give MCU a moment to reboot (similar to C++ tests) |
| 101 | +await new Promise((r) => setTimeout(r, 2000)); |
| 102 | + |
| 103 | +const msg = "HELLO\n"; |
| 104 | +const msgBuf = encoder.encode(msg); |
| 105 | +const written = dylib.symbols.serialWrite(handle, pointer(msgBuf), msgBuf.length, 100, 1); |
| 106 | +if (written !== msgBuf.length) { |
| 107 | + console.error(`Write failed (wrote ${written}/${msgBuf.length})`); |
| 108 | + dylib.symbols.serialClose(handle); |
| 109 | + dylib.close(); |
| 110 | + Deno.exit(1); |
| 111 | +} |
| 112 | + |
| 113 | +const readBuf = new Uint8Array(64); |
| 114 | +const untilBuf = new Uint8Array(["\n".charCodeAt(0)]); |
| 115 | + |
| 116 | +const read = dylib.symbols.serialReadUntil(handle, pointer(readBuf), readBuf.length, 500, 1, pointer(untilBuf)); |
| 117 | +if (read <= 0) { |
| 118 | + console.error("Read failed or timed out."); |
| 119 | + dylib.symbols.serialClose(handle); |
| 120 | + dylib.close(); |
| 121 | + Deno.exit(1); |
| 122 | +} |
| 123 | + |
| 124 | +const echo = decoder.decode(readBuf.subarray(0, read)); |
| 125 | +console.log(`Echo response (${read} bytes): '${echo}'`); |
| 126 | + |
| 127 | +if (echo === msg) { |
| 128 | + console.log("Echo test: ✅ success"); |
| 129 | +} else { |
| 130 | + console.error("Echo test: ❌ mismatch"); |
| 131 | +} |
| 132 | + |
| 133 | +dylib.symbols.serialClose(handle); |
| 134 | +dylib.close(); |
0 commit comments