{"id":13710719,"url":"https://github.com/lupyuen/zig-bl602-nuttx","last_synced_at":"2025-04-11T17:35:43.993Z","repository":{"id":37326673,"uuid":"495749363","full_name":"lupyuen/zig-bl602-nuttx","owner":"lupyuen","description":"Zig on RISC-V BL602 with Apache NuttX RTOS and LoRaWAN","archived":false,"fork":false,"pushed_at":"2022-10-18T03:14:16.000Z","size":814,"stargazers_count":35,"open_issues_count":0,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-25T13:39:37.951Z","etag":null,"topics":["bl602","bl604","iot","lora","lorawan","nuttx","pinecone","pinedio","riscv32","zig"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lupyuen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["lupyuen"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":["paypal.me/lupyuen"]}},"created_at":"2022-05-24T09:12:29.000Z","updated_at":"2024-11-09T02:36:24.000Z","dependencies_parsed_at":"2023-01-19T11:30:59.866Z","dependency_job_id":null,"html_url":"https://github.com/lupyuen/zig-bl602-nuttx","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fzig-bl602-nuttx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fzig-bl602-nuttx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fzig-bl602-nuttx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fzig-bl602-nuttx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lupyuen","download_url":"https://codeload.github.com/lupyuen/zig-bl602-nuttx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248450459,"owners_count":21105683,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bl602","bl604","iot","lora","lorawan","nuttx","pinecone","pinedio","riscv32","zig"],"created_at":"2024-08-02T23:01:00.166Z","updated_at":"2025-04-11T17:35:43.958Z","avatar_url":"https://github.com/lupyuen.png","language":"Zig","funding_links":["https://github.com/sponsors/lupyuen","paypal.me/lupyuen"],"categories":["Systems Programming"],"sub_categories":["Embedded Development"],"readme":"# Zig on RISC-V BL602 with Apache NuttX RTOS and LoRaWAN\n\nRead the articles...\n\n-   [\"Zig on RISC-V BL602: Quick Peek with Apache NuttX RTOS\"](https://lupyuen.github.io/articles/zig)\n\n-   [\"Build an IoT App with Zig and LoRaWAN\"](https://lupyuen.github.io/articles/iot)\n\nTo build the Hello Zig App for NuttX on BL602...\n\n```bash\n##  Enable Zig App in NuttX menuconfig\nmake menuconfig\n\n##  TODO: Select \"Application Configuration \u003e Examples \u003e Hello Zig Example\"\n##  Save the configuration and exit menuconfig.\n\n##  Build Nuttx\nmake\n\n##  NuttX Build fails with Undefined Reference to `hello_zig_main`\n##  That's OK, here's the fix...\n\n##  Download our modified Zig App for NuttX\ngit clone --recursive https://github.com/lupyuen/zig-bl602-nuttx\ncd zig-bl602-nuttx\n\n##  Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)\nzig build-obj \\\n    -target riscv32-freestanding-none \\\n    -mcpu sifive_e76 \\\n    hello_zig_main.zig\n\n##  Dump the ABI for the compiled app\nriscv64-unknown-elf-readelf -h -A hello_zig_main.o\n##  Shows \"Flags: 0x1, RVC, soft-float ABI\"\n##  Which is Software Floating-Point.\n##  This won't link with NuttX because NuttX is compiled with Hardware Floating-Point\n\n##  We change Software Floating-Point to Hardware Floating-Point...\n##  Edit hello_zig_main.o in a Hex Editor, change byte 0x24 from 0x01 to 0x03\n##  (See https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header)\n\n##  Dump the ABI for the compiled app\nriscv64-unknown-elf-readelf -h -A hello_zig_main.o\n##  Shows \"Flags: 0x3, RVC, single-float ABI\"\n##  Which is Hardware Floating-Point and will link with NuttX\n\n##  Copy the compiled app to NuttX and overwrite `hello.o`\n##  TODO: Change \"$HOME/nuttx\" to your NuttX Project Directory\ncp hello_zig_main.o $HOME/nuttx/apps/examples/hello/*hello.o\n\n##  Build NuttX to link the Zig Object from `hello.o`\n##  TODO: Change \"$HOME/nuttx\" to your NuttX Project Directory\ncd $HOME/nuttx/nuttx\nmake\n\n##  NuttX build should now succeed\n```\n\nBoot NuttX and enter this at the NuttX Shell...\n\n```text\nNuttShell (NSH) NuttX-10.3.0-RC2\n\nnsh\u003e hello_zig\nHello, Zig!\n```\n\nFor the LoRaWAN Zig App, see this...\n\n-   [\"Convert LoRaWAN App to Zig\"](#convert-lorawan-app-to-zig)\n\nTo compile the LoRa SX1262 Library in C with Zig Compiler, see this...\n\n-   [\"Zig Compiler as Drop-In Replacement for GCC\"](#zig-compiler-as-drop-in-replacement-for-gcc)\n\nHere's how we made Zig and LoRaWAN run on BL602 NuttX...\n\n# Zig App for NuttX\n\nApache NuttX RTOS is bundled with a simple Zig App ... Let's run this on BL602: [hello_zig_main.zig](hello_zig_main.zig)\n\n```zig\n//  Import the Zig Standard Library\nconst std = @import(\"std\");\n\n//  Import printf() from C\npub extern fn printf(\n    _format: [*:0]const u8\n) c_int;\n\n//  Main Function\npub export fn hello_zig_main(\n    _argc: c_int, \n    _argv: [*]const [*]const u8\n) c_int {\n    _ = _argc;\n    _ = _argv;\n    _ = printf(\"Hello, Zig!\\n\");\n    return 0;\n}\n```\n\nWe fixed the last 2 lines to make the Zig compiler happy...\n\n```zig\n//  Previously: printf(\"Hello, Zig!\\n\");\n//  Zig needs us to use the returned value from printf()...\n_ = printf(\"Hello, Zig!\\n\");\n\n//  Previously this was missing.\n//  Zig needs us to return a value...\nreturn 0;\n```\n\nOriginal version is here: [hello_zig_main.zig](https://github.com/apache/incubator-nuttx-apps/blob/master/examples/hello_zig/hello_zig_main.zig)\n\n# Enable Zig App\n\nTo enable the Zig App in NuttX...\n\n```bash\nmake menuconfig\n```\n\nSelect \"Application Configuration \u003e Examples \u003e Hello Zig Example\"\n\nSave the configuration and exit menuconfig.\n\n# Build Fails on NuttX\n\nWhen we build NuttX...\n\n```bash\nmake\n```\n\nWe see this error...\n\n```text\nLD: nuttx\nriscv64-unknown-elf-ld: nuttx/staging/libapps.a(builtin_list.c.home.user.nuttx.apps.builtin.o):(.rodata.g_builtins+0xbc): \nundefined reference to `hello_zig_main'\n```\n\n[(Source)](https://gist.github.com/lupyuen/497c90b862aef48b57ff3124f2ea94d8)\n\nWhich looks similar to this issue...\n\nhttps://github.com/apache/incubator-nuttx/issues/6219\n\nThis seems to be caused by the NuttX Build not calling the Zig Compiler.\n\nBut no worries! Let's compile the Zig App ourselves and link into NuttX.\n\n# Compile Zig App\n\nHere's how we compile our Zig App for RISC-V BL602 and link it with NuttX...\n\n```bash\n##  Download our modified Zig App for NuttX\ngit clone --recursive https://github.com/lupyuen/zig-bl602-nuttx\ncd zig-bl602-nuttx\n\n##  Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)\nzig build-obj \\\n    -target riscv32-freestanding-none \\\n    -mcpu sifive_e76 \\\n    hello_zig_main.zig\n\n##  Copy the compiled app to NuttX and overwrite `hello.o`\n##  TODO: Change \"$HOME/nuttx\" to your NuttX Project Directory\ncp hello_zig_main.o $HOME/nuttx/apps/examples/hello/*hello.o\n\n##  Build NuttX to link the Zig Object from `hello.o`\n##  TODO: Change \"$HOME/nuttx\" to your NuttX Project Directory\ncd $HOME/nuttx/nuttx\nmake\n```\n\n# Zig Target\n\n_Why is the target `riscv32-freestanding-none`?_\n\nZig Targets have the form `\u003carch\u003e\u003csub\u003e-\u003cos\u003e-\u003cabi\u003e`...\n\n`riscv32`: Because BL602 is a 32-bit RISC-V processor\n\n`freestanding`: Because embedded targets don't need an OS\n\n`none`: Because embedded targets don't specify the ABI\n\n_Why is the target CPU `sifive_e76`?_\n\nBL602 is designated as RV32IMACF...\n\n| Designation | Meaning |\n|:---:|:---|\n| __`RV32I`__ | 32-bit RISC-V with Base Integer Instructions\n| __`M`__ | Integer Multiplication + Division\n| __`A`__ | Atomic Instructions\n| __`C`__ | Compressed Instructions\n| __`F`__ | Single-Precision Floating-Point\n\n[(Source)](https://en.wikipedia.org/wiki/RISC-V#ISA_base_and_extensions)\n\nAmong all Zig Targets, only `sifive_e76` has the same designation...\n\n```bash\n$ zig targets\n...\n\"sifive_e76\": [ \"a\", \"c\", \"f\", \"m\" ],\n```\n\n[(Source)](https://gist.github.com/lupyuen/09d64c79e12b30e5eebc7d0a9c3b20a4)\n\nThus we use `sifive_e76` as our CPU Target.\n\nAlternatively we may use `baseline_rv32-d` as our CPU Target...\n\n```bash\n##  Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)\nzig build-obj \\\n    -target riscv32-freestanding-none \\\n    -mcpu=baseline_rv32-d \\\n    hello_zig_main.zig\n```\n\nBecause...\n\n-   `baseline_rv32` means RV32IMACFD \n\n    (D for Double-Precision Floating-Point)\n\n-   `-d` means remove the Double-Precision Floating-Point (D)\n\n    (But keep the Single-Precision Floating-Point)\n\n[(More about RISC-V Feature Flags. Thanks Matheus!)](https://github.com/lupyuen/zig-bl602-nuttx/issues/1)\n\n# Floating-Point ABI\n\nWhen linking the Compiled Zig App with NuttX, we see this error...\n\n```bash\n$ make\n...\nriscv64-unknown-elf-ld: nuttx/staging/libapps.a(hello_main.c.home.user.nuttx.apps.examples.hello.o): \ncan't link soft-float modules with single-float modules\n```\n\nThat's because NuttX was compiled for (Single-Precision) __Hardware Floating-Point__ ABI (Application Binary Interface)...\n\n```bash\n##  Do this BEFORE overwriting hello.o by hello_zig_main.o.\n##  \"*hello.o\" expands to something like \"hello_main.c.home.user.nuttx.apps.examples.hello.o\"\n$ riscv64-unknown-elf-readelf -h -A $HOME/nuttx/apps/examples/hello/*hello.o\nELF Header:\n  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 \n  Class:                             ELF32\n  Data:                              2's complement, little endian\n  Version:                           1 (current)\n  OS/ABI:                            UNIX - System V\n  ABI Version:                       0\n  Type:                              REL (Relocatable file)\n  Machine:                           RISC-V\n  Version:                           0x1\n  Entry point address:               0x0\n  Start of program headers:          0 (bytes into file)\n  Start of section headers:          4528 (bytes into file)\n  Flags:                             0x3, RVC, single-float ABI\n  Size of this header:               52 (bytes)\n  Size of program headers:           0 (bytes)\n  Number of program headers:         0\n  Size of section headers:           40 (bytes)\n  Number of section headers:         26\n  Section header string table index: 25\nAttribute Section: riscv\nFile Attributes\n  Tag_RISCV_stack_align: 16-bytes\n  Tag_RISCV_arch: \"rv32i2p0_m2p0_a2p0_f2p0_c2p0\"\n```\n\n[(Source)](https://gist.github.com/lupyuen/5c090dead49eb50751578f28c15cecd5)\n\n[(NuttX was compiled with the GCC Flags `-march=rv32imafc -mabi=ilp32f`)](https://gist.github.com/lupyuen/288c980fdef75c334d32e669a921e623)\n\nWhereas Zig Compiler produces an Object File with __Software Floating-Point__ ABI...\n\n```bash\n$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o\nELF Header:\n  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 \n  Class:                             ELF32\n  Data:                              2's complement, little endian\n  Version:                           1 (current)\n  OS/ABI:                            UNIX - System V\n  ABI Version:                       0\n  Type:                              REL (Relocatable file)\n  Machine:                           RISC-V\n  Version:                           0x1\n  Entry point address:               0x0\n  Start of program headers:          0 (bytes into file)\n  Start of section headers:          11968 (bytes into file)\n  Flags:                             0x1, RVC, soft-float ABI\n  Size of this header:               52 (bytes)\n  Size of program headers:           0 (bytes)\n  Number of program headers:         0\n  Size of section headers:           40 (bytes)\n  Number of section headers:         24\n  Section header string table index: 22\nAttribute Section: riscv\nFile Attributes\n  Tag_RISCV_stack_align: 16-bytes\n  Tag_RISCV_arch: \"rv32i2p0_m2p0_a2p0_f2p0_c2p0\"\n```\n\n[(Source)](https://gist.github.com/lupyuen/f04386a0b94ed1fb42a94d671edb1ba7)\n\nGCC won't allow us to link object files with Software Floating-Point and Hardware Floating-Point ABIs!\n\n(Why did the Zig Compiler produce an Object File with Software Floating-Point ABI, when `sifive_e76` supports Hardware Floating-Point? [See this](https://www.reddit.com/r/Zig/comments/v2zgvh/comment/iavw5xp/?utm_source=share\u0026utm_medium=web2x\u0026context=3))\n\n# Patch ELF Header\n\nZig Compiler generates an Object File with __Software Floating-Point__ ABI (Application Binary Interface)...\n\n```bash\n##  Dump the ABI for the compiled app\n$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o\n...\nFlags: 0x1, RVC, soft-float ABI\n```\n\nThis won't link with NuttX because NuttX is compiled with Hardware Floating-Point ABI.\n\nWe fix this by modifying the ELF Header...\n\n-   Edit `hello_zig_main.o` in a Hex Editor\n\n    [(Like VSCode Hex Editor)](https://marketplace.visualstudio.com/items?itemName=ms-vscode.hexeditor)\n\n-   Change byte `0x24` (Flags) from `0x01` (Soft Float) to `0x03` (Hard Float)\n\n    [(See this)](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header)\n\nWe verify that the Object File has been changed to __Hardware Floating-Point__ ABI...\n\n```bash\n##  Dump the ABI for the compiled app\n$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o\n...\nFlags: 0x3, RVC, single-float ABI\n```\n\nThis is now Hardware Floating-Point ABI and will link with NuttX.\n\nNow we link the modified Object File with NuttX...\n\n```bash\n##  Copy the compiled app to NuttX and overwrite `hello.o`\n##  TODO: Change \"$HOME/nuttx\" to your NuttX Project Directory\ncp hello_zig_main.o $HOME/nuttx/apps/examples/hello/*hello.o\n\n##  Build NuttX to link the Zig Object from `hello.o`\n##  TODO: Change \"$HOME/nuttx\" to your NuttX Project Directory\ncd $HOME/nuttx/nuttx\nmake\n```\n\nThe NuttX Build should now succeed.\n\n_Is it really OK to change the ABI like this?_\n\nWell technically the __ABI is correctly generated__ by the Zig Compiler...\n\n```bash\n##  Dump the ABI for the compiled Zig app\n$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o\n...\nFlags: 0x1, RVC, soft-float ABI\nTag_RISCV_arch: \"rv32i2p0_m2p0_a2p0_f2p0_c2p0\"\n```\n\nThe last line translates to __RV32IMACF__, which means that the RISC-V Instruction Set is indeed targeted for __Hardware Floating-Point__. \n\nWe're only editing the __ELF Header__, because it didn't seem to reflect the correct ABI for the Object File.\n\n_Is there a proper fix for this?_\n\nIn future the Zig Compiler might allow us to specify the __Floating-Point ABI__ as the target...\n\n```bash\n##  Compile the Zig App for BL602\n##  (\"ilp32f\" means Hardware Floating-Point ABI)\nzig build-obj \\\n  -target riscv32-freestanding-ilp32f \\\n  ...\n```\n\n[(See this)](https://github.com/ziglang/zig/issues/9760#issuecomment-991738757)\n\nStay Tuned!\n\n_Can we patch the ELF Header via Command Line?_\n\nYep we may patch the ELF Header via __Command Line__...\n\n```bash\nxxd -c 1 hello_zig_main.o \\\n  | sed 's/00000024: 01/00000024: 03/' \\\n  | xxd -r -c 1 - hello_zig_main2.o\n```\n\n# Zig Runs OK!\n\nThe NuttX Build succeeds. Zig runs OK on NuttX BL602!\n\n```text\nNuttShell (NSH) NuttX-10.3.0-RC2\n\nnsh\u003e hello_zig\nHello, Zig!\n```\n\n# Hello App\n\nRemember that we overwrote `hello.o` with our Zig Compiled Object File.\n\nNuttX Build will fail unless we provide the `hello_main` function...\n\n```text\nriscv64-unknown-elf-ld: nuttx/staging/libapps.a(builtin_list.c.home.user.nuttx.apps.builtin.o):(.rodata.g_builtins+0xcc): \nundefined reference to `hello_main'\n```\n\nThat's why we define `hello_main` in our Zig App...\n\n```zig\npub export fn hello_main(\n    _argc: c_int, \n    _argv: [*]const [*]const u8\n) c_int {\n    _ = _argc;\n    _ = _argv;\n    _ = printf(\"Hello, Zig!\\n\");\n    return 0;\n}\n```\n\n[(Source)](hello_zig_main.zig)\n\nWhich means that the `hello` app will call our Zig Code too...\n\n```text\nNuttShell (NSH) NuttX-10.3.0-RC2\n\nnsh\u003e hello\nHello, Zig!\n```\n\n![Pine64 PineCone BL602 Board (right) connected to Semtech SX1262 LoRa Transceiver (left) over SPI](https://lupyuen.github.io/images/spi2-title.jpg)\n\n[_Pine64 PineCone BL602 Board (right) connected to Semtech SX1262 LoRa Transceiver (left) over SPI_](https://lupyuen.github.io/articles/spi2)\n\n# Zig Compiler as Drop-In Replacement for GCC\n\n_Will Zig Compiler work as [Drop-In Replacement for GCC](https://lupyuen.github.io/articles/zig#why-zig) for compiling NuttX Libraries?_\n\nLet's test it on the [LoRa SX1262 Library](https://lupyuen.github.io/articles/sx1262) for Apache NuttX RTOS!\n\nHere's how NuttX compiles the [LoRa SX1262 Library](https://lupyuen.github.io/articles/sx1262) with GCC...\n\n```bash\n##  LoRa SX1262 Source Directory\ncd $HOME/nuttx/nuttx/libs/libsx1262\n\n##  Compile radio.c with GCC\nriscv64-unknown-elf-gcc \\\n  -c \\\n  -fno-common \\\n  -Wall \\\n  -Wstrict-prototypes \\\n  -Wshadow \\\n  -Wundef \\\n  -Os \\\n  -fno-strict-aliasing \\\n  -fomit-frame-pointer \\\n  -fstack-protector-all \\\n  -ffunction-sections \\\n  -fdata-sections \\\n  -g \\\n  -march=rv32imafc \\\n  -mabi=ilp32f \\\n  -mno-relax \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -D__NuttX__ \\\n  -DNDEBUG \\\n  -DARCH_RISCV  \\\n  -pipe   src/radio.c \\\n  -o  src/radio.o\n\n##  Compile sx126x.c with GCC\nriscv64-unknown-elf-gcc \\\n  -c \\\n  -fno-common \\\n  -Wall \\\n  -Wstrict-prototypes \\\n  -Wshadow \\\n  -Wundef \\\n  -Os \\\n  -fno-strict-aliasing \\\n  -fomit-frame-pointer \\\n  -fstack-protector-all \\\n  -ffunction-sections \\\n  -fdata-sections \\\n  -g \\\n  -march=rv32imafc \\\n  -mabi=ilp32f \\\n  -mno-relax \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -D__NuttX__ \\\n  -DNDEBUG \\\n  -DARCH_RISCV  \\\n  -pipe   src/sx126x.c \\\n  -o  src/sx126x.o\n\n##  Compile sx126x-nuttx.c with GCC\nriscv64-unknown-elf-gcc \\\n  -c \\\n  -fno-common \\\n  -Wall \\\n  -Wstrict-prototypes \\\n  -Wshadow \\\n  -Wundef \\\n  -Os \\\n  -fno-strict-aliasing \\\n  -fomit-frame-pointer \\\n  -fstack-protector-all \\\n  -ffunction-sections \\\n  -fdata-sections \\\n  -g \\\n  -march=rv32imafc \\\n  -mabi=ilp32f \\\n  -mno-relax \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -D__NuttX__ \\\n  -DNDEBUG \\\n  -DARCH_RISCV  \\\n  -pipe   src/sx126x-nuttx.c \\\n  -o  src/sx126x-nuttx.o\n```\n\nWe make these changes...\n\n-   Change `riscv64-unknown-elf-gcc` to `zig cc`\n\n-   Add the target `-target riscv32-freestanding-none -mcpu=baseline_rv32-d`\n\n-   Remove `-march=rv32imafc`\n\nAnd we run this...\n\n```bash\n##  LoRa SX1262 Source Directory\ncd $HOME/nuttx/nuttx/libs/libsx1262\n\n##  Compile radio.c with zig cc\nzig cc \\\n  -target riscv32-freestanding-none \\\n  -mcpu=baseline_rv32-d \\\n  -c \\\n  -fno-common \\\n  -Wall \\\n  -Wstrict-prototypes \\\n  -Wshadow \\\n  -Wundef \\\n  -Os \\\n  -fno-strict-aliasing \\\n  -fomit-frame-pointer \\\n  -fstack-protector-all \\\n  -ffunction-sections \\\n  -fdata-sections \\\n  -g \\\n  -mabi=ilp32f \\\n  -mno-relax \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -D__NuttX__ \\\n  -DNDEBUG \\\n  -DARCH_RISCV  \\\n  -pipe   src/radio.c \\\n  -o  src/radio.o\n\n##  Compile sx126x.c with zig cc\nzig cc \\\n  -target riscv32-freestanding-none \\\n  -mcpu=baseline_rv32-d \\\n  -c \\\n  -fno-common \\\n  -Wall \\\n  -Wstrict-prototypes \\\n  -Wshadow \\\n  -Wundef \\\n  -Os \\\n  -fno-strict-aliasing \\\n  -fomit-frame-pointer \\\n  -fstack-protector-all \\\n  -ffunction-sections \\\n  -fdata-sections \\\n  -g \\\n  -mabi=ilp32f \\\n  -mno-relax \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -D__NuttX__ \\\n  -DNDEBUG \\\n  -DARCH_RISCV  \\\n  -pipe   src/sx126x.c \\\n  -o  src/sx126x.o\n\n##  Compile sx126x-nuttx.c with zig cc\nzig cc \\\n  -target riscv32-freestanding-none \\\n  -mcpu=baseline_rv32-d \\\n  -c \\\n  -fno-common \\\n  -Wall \\\n  -Wstrict-prototypes \\\n  -Wshadow \\\n  -Wundef \\\n  -Os \\\n  -fno-strict-aliasing \\\n  -fomit-frame-pointer \\\n  -fstack-protector-all \\\n  -ffunction-sections \\\n  -fdata-sections \\\n  -g \\\n  -mabi=ilp32f \\\n  -mno-relax \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -D__NuttX__ \\\n  -DNDEBUG \\\n  -DARCH_RISCV  \\\n  -pipe   src/sx126x-nuttx.c \\\n  -o  src/sx126x-nuttx.o\n\n##  Link Zig Object Files with NuttX after compiling with `zig cc`\n##  TODO: Change \"$HOME/nuttx\" to your NuttX Project Directory\ncd $HOME/nuttx/nuttx\nmake\n```\n\nZig Compiler shows these errors...\n\n```text\nIn file included from src/sx126x-nuttx.c:3:\nIn file included from nuttx/include/debug.h:39:\nIn file included from nuttx/include/sys/uio.h:45:\nnuttx/include/sys/types.h:119:9: error: unknown type name '_size_t'\ntypedef _size_t      size_t;\n        ^\nnuttx/include/sys/types.h:120:9: error: unknown type name '_ssize_t'\ntypedef _ssize_t     ssize_t;\n        ^\nnuttx/include/sys/types.h:121:9: error: unknown type name '_size_t'\ntypedef _size_t      rsize_t;\n        ^\nnuttx/include/sys/types.h:174:9: error: unknown type name '_wchar_t'\ntypedef _wchar_t     wchar_t;\n        ^\nIn file included from src/sx126x-nuttx.c:4:\nIn file included from nuttx/include/stdio.h:34:\nnuttx/include/nuttx/fs/fs.h:238:20: error: use of undeclared identifier 'NAME_MAX'\n  char      parent[NAME_MAX + 1];\n                   ^\n```\n\nWe fix this by including the right header files...\n\n```c\n#if defined(__NuttX__) \u0026\u0026 defined(__clang__)  //  Workaround for NuttX with zig cc\n#include \u003carch/types.h\u003e\n#include \"../../nuttx/include/limits.h\"\n#endif  //  defined(__NuttX__) \u0026\u0026 defined(__clang__)\n```\n\nInto these source files...\n\n-   [radio.c](https://github.com/lupyuen/lora-sx1262/blob/lorawan/src/radio.c#L23-L26)\n-   [sx126x-nuttx.c](https://github.com/lupyuen/lora-sx1262/blob/lorawan/src/sx126x-nuttx.c#L4-L7)\n-   [sx126x.c](https://github.com/lupyuen/lora-sx1262/blob/lorawan/src/sx126x.c#L23-L26)\n\n[(See the changes)](https://github.com/lupyuen/lora-sx1262/commit/8da7e4d7cc8f1455d750bc51d75c640eea221f41)\n\nWe insert this code to tell us (at runtime) whether it was compiled with Zig Compiler or GCC...\n\n```c\nvoid SX126xIoInit( void ) {\n#ifdef __clang__\n#warning Compiled with zig cc\n    puts(\"SX126xIoInit: Compiled with zig cc\");\n#else\n#warning Compiled with gcc\n    puts(\"SX126xIoInit: Compiled with gcc\");\n#endif  //  __clang__\n```\n\n[(Source)](https://github.com/lupyuen/lora-sx1262/blob/lorawan/src/sx126x-nuttx.c#L119-L127)\n\nCompiled with `zig cc`, the LoRa SX1262 Library runs OK on NuttX yay!\n\n```text\nnsh\u003e lorawan_test\nSX126xIoInit: Compiled with zig cc\n...\n###### =========== MLME-Confirm ============ ######\nSTATUS      : OK\n###### ===========   JOINED     ============ ######\nOTAA\nDevAddr     :  000E268C\nDATA RATE   : DR_2\n...\n###### =========== MCPS-Confirm ============ ######\nSTATUS      : OK\n###### =====   UPLINK FRAME        1   ===== ######\nCLASS       : A\nTX PORT     : 1\nTX DATA     : UNCONFIRMED\n48 69 20 4E 75 74 74 58 00\nDATA RATE   : DR_3\nU/L FREQ    : 923400000\nTX POWER    : 0\nCHANNEL MASK: 0003\n```\n\n[(See the complete log)](https://gist.github.com/lupyuen/ada7f83a96eb36ad1b9fe09da4527003)\n\n# LoRaWAN Library for NuttX\n\nLet's compile the huge [LoRaWAN Library](https://lupyuen.github.io/articles/lorawan3) with Zig Compiler.\n\nNuttX compiles the LoRaWAN Library like this...\n\n```bash\n##  LoRaWAN Source Directory\ncd $HOME/nuttx/nuttx/libs/liblorawan\n\n##  Compile mac/LoRaMac.c with GCC\nriscv64-unknown-elf-gcc \\\n  -c \\\n  -fno-common \\\n  -Wall \\\n  -Wstrict-prototypes \\\n  -Wshadow \\\n  -Wundef \\\n  -Os \\\n  -fno-strict-aliasing \\\n  -fomit-frame-pointer \\\n  -fstack-protector-all \\\n  -ffunction-sections \\\n  -fdata-sections \\\n  -g \\\n  -march=rv32imafc \\\n  -mabi=ilp32f \\\n  -mno-relax \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -D__NuttX__ \\\n  -DNDEBUG \\\n  -DARCH_RISCV  \\\n  -pipe   src/mac/LoRaMac.c \\\n  -o  src/mac/LoRaMac.o\n```\n\nWe switch to the Zig Compiler...\n\n```bash\n##  LoRaWAN Source Directory\ncd $HOME/nuttx/nuttx/libs/liblorawan\n\n##  Compile mac/LoRaMac.c with zig cc\nzig cc \\\n  -target riscv32-freestanding-none \\\n  -mcpu=baseline_rv32-d \\\n  -c \\\n  -fno-common \\\n  -Wall \\\n  -Wstrict-prototypes \\\n  -Wshadow \\\n  -Wundef \\\n  -Os \\\n  -fno-strict-aliasing \\\n  -fomit-frame-pointer \\\n  -fstack-protector-all \\\n  -ffunction-sections \\\n  -fdata-sections \\\n  -g \\\n  -mabi=ilp32f \\\n  -mno-relax \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -D__NuttX__ \\\n  -DNDEBUG \\\n  -DARCH_RISCV  \\\n  -pipe   src/mac/LoRaMac.c \\\n  -o  src/mac/LoRaMac.o\n\n##  Link Zig Object Files with NuttX after compiling with `zig cc`\n##  TODO: Change \"$HOME/nuttx\" to your NuttX Project Directory\ncd $HOME/nuttx/nuttx\nmake\n```\n\nWe include the right header files into [LoRaMac.c](https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/mac/LoRaMac.c#L33-L36)...\n\n```c\n#if defined(__NuttX__) \u0026\u0026 defined(__clang__)  //  Workaround for NuttX with zig cc\n#include \u003carch/types.h\u003e\n#include \"../../nuttx/include/limits.h\"\n#endif  //  defined(__NuttX__) \u0026\u0026 defined(__clang__)\n```\n\n[(See the changes)](https://github.com/lupyuen/LoRaMac-node-nuttx/commit/e36b54ea3351fc80f03d13a131527bf6733410ab)\n\n[LoRaMac.c](https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/mac/LoRaMac.c) compiles OK with Zig Compiler.\n\nTODO: Compile the other files in the LoRaWAN Library with `build.zig`\n\nhttps://ziglang.org/documentation/master/#Zig-Build-System\n\nTODO: Test the LoRaWAN Library\n\n# LoRaWAN App for NuttX\n\nNow we compile the LoRaWAN App [lorawan_test_main.c](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c) with Zig Compiler.\n\nNuttX compiles the LoRaWAN App [lorawan_test_main.c](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c) like this...\n\n```bash\n##  App Source Directory\ncd $HOME/nuttx/apps/examples/lorawan_test/lorawan_test_main.c\n\n##  Compile lorawan_test_main.c with GCC\nriscv64-unknown-elf-gcc \\\n  -c \\\n  -fno-common \\\n  -Wall \\\n  -Wstrict-prototypes \\\n  -Wshadow \\\n  -Wundef \\\n  -Os \\\n  -fno-strict-aliasing \\\n  -fomit-frame-pointer \\\n  -fstack-protector-all \\\n  -ffunction-sections \\\n  -fdata-sections \\\n  -g \\\n  -march=rv32imafc \\\n  -mabi=ilp32f \\\n  -mno-relax \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -D__NuttX__ \\\n  -DNDEBUG \\\n  -DARCH_RISCV  \\\n  -pipe \\\n  -I \"$HOME/nuttx/apps/graphics/lvgl\" \\\n  -I \"$HOME/nuttx/apps/graphics/lvgl/lvgl\" \\\n  -I \"$HOME/nuttx/apps/include\" \\\n  -Dmain=lorawan_test_main  lorawan_test_main.c \\\n  -o  lorawan_test_main.c.home.user.nuttx.apps.examples.lorawan_test.o\n```\n\nWe switch to Zig Compiler...\n\n```bash\n##  App Source Directory\ncd $HOME/nuttx/apps/examples/lorawan_test\n\n##  Compile lorawan_test_main.c with zig cc\nzig cc \\\n  -target riscv32-freestanding-none \\\n  -mcpu=baseline_rv32-d \\\n  -c \\\n  -fno-common \\\n  -Wall \\\n  -Wstrict-prototypes \\\n  -Wshadow \\\n  -Wundef \\\n  -Os \\\n  -fno-strict-aliasing \\\n  -fomit-frame-pointer \\\n  -fstack-protector-all \\\n  -ffunction-sections \\\n  -fdata-sections \\\n  -g \\\n  -mabi=ilp32f \\\n  -mno-relax \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -D__NuttX__ \\\n  -DNDEBUG \\\n  -DARCH_RISCV  \\\n  -pipe \\\n  -I \"$HOME/nuttx/apps/graphics/lvgl\" \\\n  -I \"$HOME/nuttx/apps/graphics/lvgl/lvgl\" \\\n  -I \"$HOME/nuttx/apps/include\" \\\n  -Dmain=lorawan_test_main  lorawan_test_main.c \\\n  -o  *lorawan_test.o\n\n##  Link Zig Object Files with NuttX after compiling with `zig cc`\n##  TODO: Change \"$HOME/nuttx\" to your NuttX Project Directory\ncd $HOME/nuttx/nuttx\nmake\n```\n\nWe include the right header files into [lorawan_test_main.c](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L20-L23)...\n\n```c\n#if defined(__NuttX__) \u0026\u0026 defined(__clang__)  //  Workaround for NuttX with zig cc\n#include \u003carch/types.h\u003e\n#include \"../../nuttx/include/limits.h\"\n#endif  //  defined(__NuttX__) \u0026\u0026 defined(__clang__)\n```\n\n[(See the changes)](https://github.com/lupyuen/lorawan_test/commit/3d4a451d44cf36b19ef8d900281a2f8f9590de62)\n\nCompiled with `zig cc`, the LoRaWAN App runs OK on NuttX yay!\n\n```text\nnsh\u003e lorawan_test\nlorawan_test_main: Compiled with zig cc\n...\n###### =========== MLME-Confirm ============ ######\nSTATUS      : OK\n###### ===========   JOINED     ============ ######\nOTAA\nDevAddr     :  00DC5ED5\nDATA RATE   : DR_2\n...\n###### =========== MCPS-Confirm ============ ######\nSTATUS      : OK\n###### =====   UPLINK FRAME        1   ===== ######\nCLASS       : A\nTX PORT     : 1\nTX DATA     : UNCONFIRMED\n48 69 20 4E 75 74 74 58 00\nDATA RATE   : DR_3\nU/L FREQ    : 923400000\nTX POWER    : 0\nCHANNEL MASK: 0003\n```\n\n[(See the complete log)](https://gist.github.com/lupyuen/477982242d897771d7a5780c8a9b0910)\n\n# Auto-Translate LoRaWAN App to Zig\n\nThe Zig Compiler can auto-translate C code to Zig. [(See this)](https://ziglang.org/documentation/master/#C-Translation-CLI)\n\nHere's how we auto-translate our LoRaWAN App [lorawan_test_main.c](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c) from C to Zig...\n\n-   Change `zig cc` to `zig translate-c`\n\n-   Surround the C Flags by `-cflags` ... `--`\n\nLike this...\n\n```bash\n##  App Source Directory\ncd $HOME/nuttx/apps/examples/lorawan_test\n\n##  Auto-translate lorawan_test_main.c from C to Zig\nzig translate-c \\\n  -target riscv32-freestanding-none \\\n  -mcpu=baseline_rv32-d \\\n  -cflags \\\n    -fno-common \\\n    -Wall \\\n    -Wstrict-prototypes \\\n    -Wshadow \\\n    -Wundef \\\n    -Os \\\n    -fno-strict-aliasing \\\n    -fomit-frame-pointer \\\n    -fstack-protector-all \\\n    -ffunction-sections \\\n    -fdata-sections \\\n    -g \\\n    -mabi=ilp32f \\\n    -mno-relax \\\n  -- \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -D__NuttX__ \\\n  -DNDEBUG \\\n  -DARCH_RISCV  \\\n  -I \"$HOME/nuttx/apps/graphics/lvgl\" \\\n  -I \"$HOME/nuttx/apps/graphics/lvgl/lvgl\" \\\n  -I \"$HOME/nuttx/apps/include\" \\\n  -Dmain=lorawan_test_main  \\\n  lorawan_test_main.c \\\n  \u003elorawan_test_main.zig\n```\n\nHere's the original C code: [lorawan_test_main.c](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c)\n\nAnd the auto-translation from C to Zig: [translated/lorawan_test_main.zig](translated/lorawan_test_main.zig)\n\nHere's a snippet from the original C code...\n\n```c\nint main(int argc, FAR char *argv[]) {\n#ifdef __clang__\n    puts(\"lorawan_test_main: Compiled with zig cc\");\n#else\n    puts(\"lorawan_test_main: Compiled with gcc\");\n#endif  //  __clang__\n\n    //  If we are using Entropy Pool and the BL602 ADC is available,\n    //  add the Internal Temperature Sensor data to the Entropy Pool\n    init_entropy_pool();\n\n    //  Compute the interval between transmissions based on Duty Cycle\n    TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );\n\n    const Version_t appVersion    = { .Value = FIRMWARE_VERSION };\n    const Version_t gitHubVersion = { .Value = GITHUB_VERSION };\n    DisplayAppInfo( \"lorawan_test\", \n                    \u0026appVersion,\n                    \u0026gitHubVersion );\n\n    //  Init LoRaWAN\n    if ( LmHandlerInit( \u0026LmHandlerCallbacks, \u0026LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )\n    {\n        printf( \"LoRaMac wasn't properly initialized\\n\" );\n        //  Fatal error, endless loop.\n        while ( 1 ) {}\n    }\n\n    // Set system maximum tolerated rx error in milliseconds\n    LmHandlerSetSystemMaxRxError( 20 );\n\n    // The LoRa-Alliance Compliance protocol package should always be initialized and activated.\n    LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, \u0026LmhpComplianceParams );\n    LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL );\n    LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL );\n    LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, \u0026FragmentationParams );\n\n    IsClockSynched     = false;\n    IsFileTransferDone = false;\n\n    //  Join the LoRaWAN Network\n    LmHandlerJoin( );\n\n    //  Set the Transmit Timer\n    StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER );\n\n    //  Handle LoRaWAN Events\n    handle_event_queue(NULL);  //  Never returns\n\n    return 0;\n}\n```\n\n[(Source)](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L271-L323)\n\nAnd the auto-translated Zig code...\n\n```zig\npub export fn lorawan_test_main(arg_argc: c_int, arg_argv: [*c][*c]u8) c_int {\n    var argc = arg_argc;\n    _ = argc;\n    var argv = arg_argv;\n    _ = argv;\n    _ = puts(\"lorawan_test_main: Compiled with zig cc\");\n    init_entropy_pool();\n    TxPeriodicity = @bitCast(u32, @as(c_int, 40000) + randr(-@as(c_int, 5000), @as(c_int, 5000)));\n    const appVersion: Version_t = Version_t{\n        .Value = @bitCast(u32, @as(c_int, 16908288)),\n    };\n    const gitHubVersion: Version_t = Version_t{\n        .Value = @bitCast(u32, @as(c_int, 83886080)),\n    };\n    DisplayAppInfo(\"lorawan_test\", \u0026appVersion, \u0026gitHubVersion);\n    if (LmHandlerInit(\u0026LmHandlerCallbacks, \u0026LmHandlerParams) != LORAMAC_HANDLER_SUCCESS) {\n        _ = printf(\"LoRaMac wasn't properly initialized\\n\");\n        while (true) {}\n    }\n    _ = LmHandlerSetSystemMaxRxError(@bitCast(u32, @as(c_int, 20)));\n    _ = LmHandlerPackageRegister(@bitCast(u8, @truncate(i8, @as(c_int, 0))), @ptrCast(?*anyopaque, \u0026LmhpComplianceParams));\n    _ = LmHandlerPackageRegister(@bitCast(u8, @truncate(i8, @as(c_int, 1))), @intToPtr(?*anyopaque, @as(c_int, 0)));\n    _ = LmHandlerPackageRegister(@bitCast(u8, @truncate(i8, @as(c_int, 2))), @intToPtr(?*anyopaque, @as(c_int, 0)));\n    _ = LmHandlerPackageRegister(@bitCast(u8, @truncate(i8, @as(c_int, 3))), @ptrCast(?*anyopaque, \u0026FragmentationParams));\n    IsClockSynched = @as(c_int, 0) != 0;\n    IsFileTransferDone = @as(c_int, 0) != 0;\n    LmHandlerJoin();\n    StartTxProcess(@bitCast(c_uint, LORAMAC_HANDLER_TX_ON_TIMER));\n    handle_event_queue(@intToPtr(?*anyopaque, @as(c_int, 0)));\n    return 0;\n}\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L4535-L4565)\n\nWe'll refer to this auto-translated Zig Code when we manually convert our LoRaWAN App [lorawan_test_main.c](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c) from C to Zig in the next section...\n\n![Pine64 PineDio Stack BL604 (left) talking LoRaWAN to RAKwireless WisGate (right)](https://lupyuen.github.io/images/lorawan3-title.jpg)\n\n[_Pine64 PineDio Stack BL604 (left) talking LoRaWAN to RAKwireless WisGate (right)_](https://lupyuen.github.io/articles/lorawan3)\n\n# Convert LoRaWAN App to Zig\n\nFinally we convert the LoRaWAN App [lorawan_test_main.c](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c) from C to Zig, to show that we can build Complex IoT Apps in Zig.\n\nThe LoRaWAN App runs on PineDio Stack BL604 (RISC-V). The app connects to a LoRaWAN Gateway (like ChirpStack or The Things Network) and sends a Data Packet at regular intervals.\n\nHere's the original C code: [lorawan_test_main.c](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c)\n\n(700 lines of C code)\n\nAnd our converted LoRaWAN Zig App: [lorawan_test.zig](lorawan_test.zig)\n\n(673 lines of Zig code)\n\n```zig\n/// Import the LoRaWAN Library from C\nconst c = @cImport({\n    // NuttX Defines\n    @cDefine(\"__NuttX__\",  \"\");\n    @cDefine(\"NDEBUG\",     \"\");\n    @cDefine(\"ARCH_RISCV\", \"\");\n\n    // Workaround for \"Unable to translate macro: undefined identifier `LL`\"\n    @cDefine(\"LL\", \"\");\n    @cDefine(\"__int_c_join(a, b)\", \"a\");  //  Bypass zig/lib/include/stdint.h\n\n    // NuttX Header Files\n    @cInclude(\"arch/types.h\");\n    @cInclude(\"../../nuttx/include/limits.h\");\n    @cInclude(\"stdio.h\");\n\n    // LoRaWAN Header Files\n    @cInclude(\"firmwareVersion.h\");\n    @cInclude(\"../libs/liblorawan/src/apps/LoRaMac/common/githubVersion.h\");\n    @cInclude(\"../libs/liblorawan/src/boards/utilities.h\");\n    @cInclude(\"../libs/liblorawan/src/mac/region/RegionCommon.h\");\n    @cInclude(\"../libs/liblorawan/src/apps/LoRaMac/common/Commissioning.h\");\n    @cInclude(\"../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/LmHandler.h\");\n    @cInclude(\"../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpCompliance.h\");\n    @cInclude(\"../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpClockSync.h\");\n    @cInclude(\"../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpRemoteMcastSetup.h\");\n    @cInclude(\"../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpFragmentation.h\");\n    @cInclude(\"../libs/liblorawan/src/apps/LoRaMac/common/LmHandlerMsgDisplay.h\");\n});\n\n//  Main Function that will be called by NuttX\npub export fn lorawan_test_main(\n    _argc: c_int, \n    _argv: [*]const [*]const u8\n) c_int {\n    //  Call the LoRaWAN Library to set system maximum tolerated rx error in milliseconds\n    _ = c.LmHandlerSetSystemMaxRxError(20);\n\n    //  TODO: Call the LoRaWAN Library to Join LoRaWAN Network\n    //  and send a Data Packet\n```\n\nTo compile the LoRaWAN Zig App [lorawan_test.zig](lorawan_test.zig)...\n\n```bash\n##  Download our LoRaWAN Zig App for NuttX\ngit clone --recursive https://github.com/lupyuen/zig-bl602-nuttx\ncd zig-bl602-nuttx\n\n##  Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)\nzig build-obj \\\n  --verbose-cimport \\\n  -target riscv32-freestanding-none \\\n  -mcpu=baseline_rv32-d \\\n  -isystem \"$HOME/nuttx/nuttx/include\" \\\n  -I \"$HOME/nuttx/apps/examples/lorawan_test\" \\\n  lorawan_test.zig\n\n##  Patch the ELF Header of `lorawan_test.o` from Soft-Float ABI to Hard-Float ABI\nxxd -c 1 lorawan_test.o \\\n  | sed 's/00000024: 01/00000024: 03/' \\\n  | xxd -r -c 1 - lorawan_test2.o\ncp lorawan_test2.o lorawan_test.o\n\n##  Copy the compiled app to NuttX and overwrite `lorawan_test.o`\n##  TODO: Change \"$HOME/nuttx\" to your NuttX Project Directory\ncp lorawan_test.o $HOME/nuttx/apps/examples/lorawan_test/*lorawan_test.o\n\n##  Build NuttX to link the Zig Object from `lorawan_test.o`\n##  TODO: Change \"$HOME/nuttx\" to your NuttX Project Directory\ncd $HOME/nuttx/nuttx\nmake\n```\n\nOur LoRaWAN Zig App [lorawan_test.zig](lorawan_test.zig) compiles OK with Zig Compiler after making the following fixes...\n\n# Refer to Auto-Translated Zig Code\n\nSome parts of the LoRaWAN Zig App [lorawan_test.zig](lorawan_test.zig) can get tricky to convert from C to Zig, like this C code...\n\n```c\n//  Original C code...\n#define APP_TX_DUTYCYCLE     40000\n#define APP_TX_DUTYCYCLE_RND  5000\n\nuint32_t TxPeriodicity = \n    APP_TX_DUTYCYCLE +\n    randr( \n        -APP_TX_DUTYCYCLE_RND, \n        APP_TX_DUTYCYCLE_RND\n    );\n```\n\n[(Source)](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L283-L286)\n\nWhich has conflicting signed (`randr`) and unsigned (`APP_TX_DUTYCYCLE`) types.\n\nWe get help by referring to the auto-translated Zig Code: [translated/lorawan_test_main.zig](translated/lorawan_test_main.zig)\n\n```zig\n//  Converted from C to Zig...\nconst APP_TX_DUTYCYCLE:     c_int = 40000;\nconst APP_TX_DUTYCYCLE_RND: c_int = 5000;\n\n//  Cast to u32 because randr() can be negative\nvar TxPeriodicity: u32 = @bitCast(u32,\n    APP_TX_DUTYCYCLE +\n    c.randr(\n        -APP_TX_DUTYCYCLE_RND,\n        APP_TX_DUTYCYCLE_RND\n    )\n);\n```\n\nWhich resolves the conflicting types by casting the signed result to become unsigned.\n\n# Opaque Type Error\n\nWhen we reference `LmHandlerCallbacks` in our LoRaWAN Zig App [lorawan_test.zig](lorawan_test.zig)...\n\n```zig\n    _ = \u0026LmHandlerCallbacks;\n```\n\nZig Compiler will show this Opaque Type Error...\n\n```text\nzig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1353:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions\n    Fields: struct_sInfoFields,\n    ^\nzig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1563:5: note: while checking this field\n    PingSlot: PingSlotInfo_t,\n    ^\nzig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1579:5: note: while checking this field\n    PingSlotInfo: MlmeReqPingSlotInfo_t,\n    ^\nzig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1585:5: note: while checking this field\n    Req: union_uMlmeParam,\n    ^\nzig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:2277:5: note: while checking this field\n    OnMacMlmeRequest: ?fn (LoRaMacStatus_t, [*c]MlmeReq_t, TimerTime_t) callconv(.C) void,\n    ^\n```\n\nOpaque Type Error is explained here...\n\n-   [\"Extend a C/C++ Project with Zig\"](https://zig.news/kristoff/extend-a-c-c-project-with-zig-55di)\n\n-   [\"Translation failures\"](https://ziglang.org/documentation/master/#Translation-failures)\n\nLet's trace through our Opaque Type Error...\n\n```zig\nexport fn OnMacMlmeRequest(\n    status: c.LoRaMacStatus_t,\n    mlmeReq: [*c]c.MlmeReq_t, \n    nextTxIn: c.TimerTime_t\n) void {\n    c.DisplayMacMlmeRequestUpdate(status, mlmeReq, nextTxIn);\n}\n```\n\nOur function `OnMacMlmeRequest` has a parameter of type `MlmeReq_t`, auto-imported by Zig Compiler as...\n\n```zig\npub const MlmeReq_t = struct_sMlmeReq;\n\npub const struct_sMlmeReq = extern struct {\n    Type: Mlme_t,\n    Req: union_uMlmeParam,\n    ReqReturn: RequestReturnParam_t,\n};\n```\n\nWhich contains another auto-imported type `union_uMlmeParam`...\n\n```zig\npub const union_uMlmeParam = extern union {\n    Join: MlmeReqJoin_t,\n    TxCw: MlmeReqTxCw_t,\n    PingSlotInfo: MlmeReqPingSlotInfo_t,\n    DeriveMcKEKey: MlmeReqDeriveMcKEKey_t,\n    DeriveMcSessionKeyPair: MlmeReqDeriveMcSessionKeyPair_t,\n};\n```\n\nWhich contains an `MlmeReqPingSlotInfo_t`...\n\n```zig\npub const MlmeReqPingSlotInfo_t = struct_sMlmeReqPingSlotInfo;\n\npub const struct_sMlmeReqPingSlotInfo = extern struct {\n    PingSlot: PingSlotInfo_t,\n};\n```\n\nWhich contains a `PingSlotInfo_t`...\n\n```zig\npub const PingSlotInfo_t = union_uPingSlotInfo;\n\npub const union_uPingSlotInfo = extern union {\n    Value: u8,\n    Fields: struct_sInfoFields,\n};\n```\n\nWhich contains a `struct_sInfoFields`...\n\n```zig\npub const struct_sInfoFields = opaque {};\n```\n\nBut the fields of `struct_sInfoFields` are not known by the Zig Compiler!\n\nIf we refer to the original C code...\n\n```c\ntypedef union uPingSlotInfo\n{\n    /*!\n     * Parameter for byte access\n     */\n    uint8_t Value;\n    /*!\n     * Structure containing the parameters for the PingSlotInfoReq\n     */\n    struct sInfoFields\n    {\n        /*!\n         * Periodicity = 0: ping slot every second\n         * Periodicity = 7: ping slot every 128 seconds\n         */\n        uint8_t Periodicity     : 3;\n        /*!\n         * RFU\n         */\n        uint8_t RFU             : 5;\n    }Fields;\n}PingSlotInfo_t;\n```\n\n[(Source)](https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/mac/LoRaMac.h#L312-L333)\n\nWe see that `sInfoFields` contains Bit Fields, that the Zig Compiler is unable to translate.\n\n# Fix Opaque Type\n\nEarlier we saw that this fails to compile in our LoRaWAN Zig App [lorawan_test.zig](lorawan_test.zig)...\n\n```zig\n    _ = \u0026LmHandlerCallbacks;\n```\n\nThat's because `LmHandlerCallbacks` references the auto-imported type `MlmeReq_t`, which contains Bit Fields and can't be translated by the Zig Compiler.\n\nLet's convert `MlmeReq_t` to an Opaque Type, since we won't be accessing the fields anyway...\n\n```zig\n/// We use an Opaque Type to represent MLME Request, because it contains Bit Fields that can't be converted by Zig\nconst MlmeReq_t = opaque {};\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L695-L696)\n\nWe convert `LmHandlerCallbacks` to use our Opaque Type `MlmeReq_t`...\n\n```zig\n/// Handler Callbacks. Adapted from \n/// https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L2818-L2833\npub const LmHandlerCallbacks_t = extern struct {\n    GetBatteryLevel: ?fn () callconv(.C) u8,\n    GetTemperature: ?fn () callconv(.C) f32,\n    GetRandomSeed: ?fn () callconv(.C) u32,\n    OnMacProcess: ?fn () callconv(.C) void,\n    OnNvmDataChange: ?fn (c.LmHandlerNvmContextStates_t, u16) callconv(.C) void,\n    OnNetworkParametersChange: ?fn ([*c]c.CommissioningParams_t) callconv(.C) void,\n    OnMacMcpsRequest: ?fn (c.LoRaMacStatus_t, [*c]c.McpsReq_t, c.TimerTime_t) callconv(.C) void,\n    /// Changed `[*c]c.MlmeReq_t` to `*MlmeReq_t`\n    OnMacMlmeRequest: ?fn (c.LoRaMacStatus_t, *MlmeReq_t, c.TimerTime_t) callconv(.C) void,\n    OnJoinRequest: ?fn ([*c]c.LmHandlerJoinParams_t) callconv(.C) void,\n    OnTxData: ?fn ([*c]c.LmHandlerTxParams_t) callconv(.C) void,\n    OnRxData: ?fn ([*c]c.LmHandlerAppData_t, [*c]c.LmHandlerRxParams_t) callconv(.C) void,\n    OnClassChange: ?fn (c.DeviceClass_t) callconv(.C) void,\n    OnBeaconStatusChange: ?fn ([*c]c.LoRaMacHandlerBeaconParams_t) callconv(.C) void,\n    OnSysTimeUpdate: ?fn (bool, i32) callconv(.C) void,\n};\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L675-L693)\n\nWe change all auto-imported `MlmeReq_t` references from...\n\n```zig\n[*c]c.MlmeReq_t\n```\n\nTo our Opaque Type...\n\n```zig\n*MlmeReq_t\n```\n\nWe also change all auto-imported `LmHandlerCallbacks_t` references from...\n\n```zig\n[*c]c.LmHandlerCallbacks_t\n```\n\nTo our converted `LmHandlerCallbacks_t`...\n\n```zig\n*LmHandlerCallbacks_t\n```\n\nWhich means we need to import the affected LoRaWAN Functions ourselves...\n\n```zig\n/// Changed `[*c]c.MlmeReq_t` to `*MlmeReq_t`. Adapted from\n/// https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L2905\nextern fn DisplayMacMlmeRequestUpdate(\n    status: c.LoRaMacStatus_t, \n    mlmeReq: *MlmeReq_t, \n    nextTxIn: c.TimerTime_t\n) void;\n\n/// Changed `[*c]c.LmHandlerCallbacks_t` to `*LmHandlerCallbacks_t`. Adapted from\n/// https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L2835\nextern fn LmHandlerInit(\n    callbacks: *LmHandlerCallbacks_t, \n    handlerParams: [*c]c.LmHandlerParams_t\n) c.LmHandlerErrorStatus_t;\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L707-L720)\n\nAfter fixing the Opaque Type, Zig Compiler successfully compiles our LoRaWAN Test App [lorawan_test.zig](lorawan_test.zig) yay!\n\n# Macro Error\n\nWhile compiling our LoRaWAN Test App [lorawan_test.zig](lorawan_test.zig), we see this Macro Error...\n\n```text\nzig-cache/o/23409ceec9a6e6769c416fde1695882f/cimport.zig:2904:32: \nerror: unable to translate macro: undefined identifier `LL`\npub const __INT64_C_SUFFIX__ = @compileError(\"unable to translate macro: undefined identifier `LL`\"); \n// (no file):178:9\n```\n\nAccording to the Zig Docs, this means that the Zig Compiler failed to translate a C Macro...\n\n-   [\"C Macros\"](https://ziglang.org/documentation/master/#C-Macros)\n\nSo we define `LL` ourselves...\n\n```zig\n/// Import the LoRaWAN Library from C\nconst c = @cImport({\n    // Workaround for \"Unable to translate macro: undefined identifier `LL`\"\n    @cDefine(\"LL\", \"\");\n```\n\n(`LL` is the \"long long\" suffix for C Constants, which is probably not needed when we import C Types and Functions into Zig)\n\nThen Zig Compiler emits this error...\n\n```text\nzig-cache/o/83fc6cf7a78f5781f258f156f891554b/cimport.zig:2940:26: \nerror: unable to translate C expr: unexpected token '##'\npub const __int_c_join = @compileError(\"unable to translate C expr: unexpected token '##'\"); \n// /home/user/zig-linux-x86_64-0.10.0-dev.2351+b64a1d5ab/lib/include/stdint.h:282:9\n```\n\nWhich refers to this line in `stdint.h`...\n\n```c\n#define __int_c_join(a, b) a ## b\n```\n\nThe `__int_c_join` Macro fails because the `LL` suffix is now blank and the `##` Concatenation Operator fails.\n\nWe redefine the `__int_c_join` Macro without the `##` Concatenation Operator...\n\n```zig\n/// Import the LoRaWAN Library from C\nconst c = @cImport({\n    // Workaround for \"Unable to translate macro: undefined identifier `LL`\"\n    @cDefine(\"LL\", \"\");\n    @cDefine(\"__int_c_join(a, b)\", \"a\");  //  Bypass zig/lib/include/stdint.h\n```\n\nNow Zig Compiler successfully compiles our LoRaWAN Test App [lorawan_test.zig](lorawan_test.zig)\n\n# Struct Initialisation Error\n\nZig Compiler crashes when it tries to initialise the Timer Struct at startup...\n\n```zig\n/// Timer to handle the application data transmission duty cycle\nvar TxTimer: c.TimerEvent_t = \n    std.mem.zeroes(c.TimerEvent_t);\n\n// Zig Compiler crashes with...\n// TODO buf_write_value_bytes maybe typethread 11512 panic:\n// Unable to dump stack trace: debug info stripped\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L679-L684)\n\nSo we initialise the Timer Struct in the Main Function instead...\n\n```zig\n/// Timer to handle the application data transmission duty cycle.\n/// Init the timer in Main Function.\nvar TxTimer: c.TimerEvent_t = undefined;\n\n/// Main Function\npub export fn lorawan_test_main(\n    _argc: c_int, \n    _argv: [*]const [*]const u8\n) c_int {\n    // Init the Timer Struct at startup\n    TxTimer = std.mem.zeroes(c.TimerEvent_t);\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L90-L101)\n\n# LoRaWAN Zig App Runs OK!\n\nAfter fixing the above issues, we test the LoRaWAN Zig App on NuttX: [lorawan_test.zig](lorawan_test.zig)\n\n```text\nnsh\u003e lorawan_test\nApplication name   : Zig LoRaWAN Test\n...\n###### =========== MLME-Confirm ============ ######\nSTATUS      : OK\n###### ===========   JOINED     ============ ######\nOTAA\nDevAddr     :  00D803AB\nDATA RATE   : DR_2\n...\nPrepareTxFrame: Transmit to LoRaWAN (9 bytes): Hi NuttX\n###### =========== MCPS-Confirm ============ ######\nSTATUS      : OK\n###### =====   UPLINK FRAME        1   ===== ######\nCLASS       : A\nTX PORT     : 1\nTX DATA     : UNCONFIRMED\n48 69 20 4E 75 74 74 58 00\nDATA RATE   : DR_3\nU/L FREQ    : 923200000\nTX POWER    : 0\nCHANNEL MASK: 0003\n```\n\n[(See the complete log)](https://gist.github.com/lupyuen/0871ac515b18d9d68d3aacf831fd0f5b)\n\nLoRaWAN Zig App [lorawan_test.zig](lorawan_test.zig) successfully joins the LoRaWAN Network (ChirpStack on RAKwireless WisGate) and sends a Data Packet to the LoRaWAN Gateway yay!\n\n# Safety Checks\n\nThe Zig Compiler reveals interesting insights when auto-translating our C code to Zig.\n\nThis C code copies an array, byte by byte...\n\n```c\nstatic int8_t FragDecoderWrite( uint32_t addr, uint8_t *data, uint32_t size ) {\n    ...\n    for(uint32_t i = 0; i \u003c size; i++ ) {\n        UnfragmentedData[addr + i] = data[i];\n    }\n    return 0; // Success\n}\n```\n\n[(Source)](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L539-L550)\n\nHere's the auto-translated Zig code...\n\n```zig\npub fn FragDecoderWrite(arg_addr: u32, arg_data: [*c]u8, arg_size: u32) callconv(.C) i8 {\n    ...\n    var size = arg_size;\n    var i: u32 = 0;\n    while (i \u003c size) : (i +%= 1) {\n        UnfragmentedData[addr +% i] = data[i];\n    }\n    return 0;\n}\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L4335-L4349)\n\nNote that the Array Indexing in C...\n\n```c\n//  Array Indexing in C...\nUnfragmentedData[addr + i]\n```\n\nGets translated to this in Zig...\n\n```zig\n//  Array Indexing in Zig...\nUnfragmentedData[addr +% i]\n```\n\n`+` in C becomes `+%` in Zig!\n\n_What's `+%` in Zig?_\n\nThat's the Zig Operator for [__Wraparound Addition__](https://ziglang.org/documentation/master/#Wrapping-Operations).\n\nWhich means that the result wraps back to 0 (and beyond) if the addition overflows the integer.\n\nBut this isn't what we intended, since we don't expect the addition to overflow. That's why in our final converted Zig code, we revert `+%` back to `+`...\n\n```zig\nexport fn FragDecoderWrite(addr: u32, data: [*c]u8, size: u32) i8 {\n    ...\n    var i: u32 = 0;\n    while (i \u003c size) : (i += 1) {\n        UnfragmentedData[addr + i] = data[i];\n    }\n    return 0; // Success\n}\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L407-L416)\n\n_What happens if the addition overflows?_\n\nWe'll see a Runtime Error...\n\n```text\npanic: integer overflow\n```\n\n[(Source)](https://ziglang.org/documentation/master/#Integer-Overflow)\n\nWhich is probably a good thing, to ensure that our values are sensible.\n\n_What if our Array Index goes out of bounds?_\n\nWe'll get this Runtime Error...\n\n```text\npanic: index out of bounds\n```\n\n[(Source)](https://ziglang.org/documentation/master/#Index-out-of-Bounds)\n\nHere's the list of __Safety Checks__ done by Zig at runtime...\n\n-   [\"Undefined Behavior\"](https://ziglang.org/documentation/master/#Undefined-Behavior)\n\nIf we prefer to live recklessly, this is how we disable the Safety Checks...\n\n-   [\"@setRuntimeSafety\"](https://ziglang.org/documentation/master/#setRuntimeSafety)\n\n# Panic Handler\n\n_Some debug features don't seem to be working? Like `unreachable`, `std.debug.assert` and `std.debug.panic`?_\n\nThat's because for Embedded Platforms we need to implement our own Panic Handler...\n\n-   [\"Using Zig to Provide Stack Traces on Kernel Panic for a Bare Bones Operating System\"](https://andrewkelley.me/post/zig-stack-traces-kernel-panic-bare-bones-os.html)\n\n-   [Default Panic Handler: `std.debug.default_panic`](https://github.com/ziglang/zig/blob/master/lib/std/builtin.zig#L763-L847)\n\nWith our own Panic Handler, this Assertion Failure...\n\n```zig\n//  Create a short alias named `assert`\nconst assert = std.debug.assert;\n\n//  Assertion Failure\nassert(TxPeriodicity != 0);\n```\n\nWill show this Stack Trace...\n\n```text\n!ZIG PANIC!\nreached unreachable code\nStack Trace:\n0x23016394\n0x23016ce0\n```\n\nAccording to our RISC-V Disassembly, the first address `23016394` doesn't look interesting, because it's inside the `assert` function...\n\n```text\n/home/user/zig-linux-x86_64-0.10.0-dev.2351+b64a1d5ab/lib/std/debug.zig:259\npub fn assert(ok: bool) void {\n2301637c:\t00b51c63          \tbne\ta0,a1,23016394 \u003cstd.debug.assert+0x2c\u003e\n23016380:\ta009                j\t23016382 \u003cstd.debug.assert+0x1a\u003e\n23016382:\t2307e537          \tlui\ta0,0x2307e\n23016386:\tf9850513          \taddi\ta0,a0,-104 # 2307df98 \u003c__unnamed_4\u003e\n2301638a:\t4581                li\ta1,0\n2301638c:\t00000097          \tauipc\tra,0x0\n23016390:\tf3c080e7          \tjalr\t-196(ra) # 230162c8 \u003cpanic\u003e\n    if (!ok) unreachable; // assertion failure\n23016394:\ta009                j\t23016396 \u003cstd.debug.assert+0x2e\u003e\n```\n\nBut the second address `23016ce0` reveals the assertion that failed...\n\n```text\n/home/user/nuttx/zig-bl602-nuttx/lorawan_test.zig:95\n    assert(TxPeriodicity != 0);\n23016ccc:\t42013537          \tlui\ta0,0x42013\n23016cd0:\tfbc52503          \tlw\ta0,-68(a0) # 42012fbc \u003cTxPeriodicity\u003e\n23016cd4:\t00a03533          \tsnez\ta0,a0\n23016cd8:\tfffff097          \tauipc\tra,0xfffff\n23016cdc:\t690080e7          \tjalr\t1680(ra) # 23016368 \u003cstd.debug.assert\u003e\n/home/user/nuttx/zig-bl602-nuttx/lorawan_test.zig:100\n    TxTimer = std.mem.zeroes(c.TimerEvent_t);\n23016ce0:\t42016537          \tlui\ta0,0x42016\n```\n\nThis is our implementation of the Zig Panic Handler...\n\n```zig\n/// Called by Zig when it hits a Panic. We print the Panic Message, Stack Trace and halt. See \n/// https://andrewkelley.me/post/zig-stack-traces-kernel-panic-bare-bones-os.html\n/// https://github.com/ziglang/zig/blob/master/lib/std/builtin.zig#L763-L847\npub fn panic(\n    message: []const u8, \n    _stack_trace: ?*std.builtin.StackTrace\n) noreturn {\n    // Print the Panic Message\n    _ = _stack_trace;\n    _ = puts(\"\\n!ZIG PANIC!\");\n    _ = puts(@ptrCast([*c]const u8, message));\n\n    // Print the Stack Trace\n    _ = puts(\"Stack Trace:\");\n    var it = std.debug.StackIterator.init(@returnAddress(), null);\n    while (it.next()) |return_address| {\n        _ = printf(\"%p\\n\", return_address);\n    }\n\n    // Halt\n    while(true) {}\n}\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L501-L522)\n\n# Logging\n\nWe have implemented Debug Logging `std.log.debug` that's described here...\n\n-   [\"A simple overview of Zig's std.log\"](https://gist.github.com/leecannon/d6f5d7e5af5881c466161270347ce84d)\n\nHere's how we call `std.log.debug` to print a log message...\n\n```zig\n//  Create a short alias named `debug`\nconst debug  = std.log.debug;\n\n//  Message with 8 bytes\nconst msg: []const u8 = \"Hi NuttX\";\n\n//  Print the message\ndebug(\"Transmit to LoRaWAN ({} bytes): {s}\", .{ \n    msg.len, msg \n});\n\n// Prints: Transmit to LoRaWAN (8 bytes): Hi NuttX\n```\n\n`.{ ... }` creates an [__Anonymous Struct__](https://ziglearn.org/chapter-1/#anonymous-structs) with a variable number of arguments that will be passed to `std.log.debug` for printing.\n\nBelow is our implementation of `std.log.debug`...\n\n```zig\n/// Called by Zig for `std.log.debug`, `std.log.info`, `std.log.err`, ...\n/// https://gist.github.com/leecannon/d6f5d7e5af5881c466161270347ce84d\npub fn log(\n    comptime _message_level: std.log.Level,\n    comptime _scope: @Type(.EnumLiteral),\n    comptime format: []const u8,\n    args: anytype,\n) void {\n    _ = _message_level;\n    _ = _scope;\n\n    // Format the message\n    var buf: [100]u8 = undefined;  // Limit to 100 chars\n    var slice = std.fmt.bufPrint(\u0026buf, format, args)\n        catch { _ = puts(\"*** log error: buf too small\"); return; };\n    \n    // Terminate the formatted message with a null\n    var buf2: [buf.len + 1 : 0]u8 = undefined;\n    std.mem.copy(\n        u8, \n        buf2[0..slice.len], \n        slice[0..slice.len]\n    );\n    buf2[slice.len] = 0;\n\n    // Print the formatted message\n    _ = puts(\u0026buf2);\n}\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L519-L546)\n\nThis implementation calls `puts()`, which is supported by Apache NuttX RTOS since it's [__POSIX-Compliant__](https://nuttx.apache.org/docs/latest/introduction/inviolables.html#strict-posix-compliance).\n\n# Compare C and Zig\n\nThe Original C Code and the Converted Zig Code for our LoRaWAN App look highly similar.\n\nHere's the Main Function from our Original C Code...\n\n```c\n/// Main Function that will be called by NuttX. We call the LoRaWAN Library \n/// to Join a LoRaWAN Network and send a Data Packet.\nint main(int argc, FAR char *argv[]) {\n    //  If we are using Entropy Pool and the BL602 ADC is available,\n    //  add the Internal Temperature Sensor data to the Entropy Pool\n    init_entropy_pool();\n\n    //  Compute the interval between transmissions based on Duty Cycle\n    TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );\n\n    const Version_t appVersion    = { .Value = FIRMWARE_VERSION };\n    const Version_t gitHubVersion = { .Value = GITHUB_VERSION };\n    DisplayAppInfo( \"lorawan_test\", \n                    \u0026appVersion,\n                    \u0026gitHubVersion );\n\n    //  Init LoRaWAN\n    if ( LmHandlerInit( \u0026LmHandlerCallbacks, \u0026LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )\n    {\n        printf( \"LoRaMac wasn't properly initialized\\n\" );\n        //  Fatal error, endless loop.\n        while ( 1 ) {}\n    }\n\n    // Set system maximum tolerated rx error in milliseconds\n    LmHandlerSetSystemMaxRxError( 20 );\n\n    // The LoRa-Alliance Compliance protocol package should always be initialized and activated.\n    LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, \u0026LmhpComplianceParams );\n    LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL );\n    LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL );\n    LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, \u0026FragmentationParams );\n\n    IsClockSynched     = false;\n    IsFileTransferDone = false;\n\n    //  Join the LoRaWAN Network\n    LmHandlerJoin( );\n\n    //  Set the Transmit Timer\n    StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER );\n\n    //  Handle LoRaWAN Events\n    handle_event_queue(NULL);  //  Never returns\n\n    return 0;\n}\n```\n\n[(Source)](https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L271-L323)\n\nAnd the Main Function from our Converted Zig Code (after some scrubbing)...\n\n```zig\n/// Main Function that will be called by NuttX. We call the LoRaWAN Library \n/// to Join a LoRaWAN Network and send a Data Packet.\npub export fn lorawan_test_main(\n    _argc: c_int, \n    _argv: [*]const [*]const u8\n) c_int {\n    _ = _argc;\n    _ = _argv;\n\n    // Init the Timer Struct at startup\n    TxTimer = std.mem.zeroes(c.TimerEvent_t);\n\n    // If we are using Entropy Pool and the BL602 ADC is available,\n    // add the Internal Temperature Sensor data to the Entropy Pool\n    // TODO: init_entropy_pool();\n\n    // Compute the interval between transmissions based on Duty Cycle\n    TxPeriodicity = @bitCast(u32,  // Cast to u32 because randr() can be negative\n        APP_TX_DUTYCYCLE +\n        c.randr(\n            -APP_TX_DUTYCYCLE_RND,\n            APP_TX_DUTYCYCLE_RND\n        )\n    );\n\n    // Show the Firmware and GitHub Versions\n    const appVersion = c.Version_t {\n        .Value = c.FIRMWARE_VERSION,\n    };\n    const gitHubVersion = c.Version_t {\n        .Value = c.GITHUB_VERSION,\n    };\n    c.DisplayAppInfo(\"Zig LoRaWAN Test\", \u0026appVersion, \u0026gitHubVersion);\n\n    // Init LoRaWAN\n    if (LmHandlerInit(\u0026LmHandlerCallbacks, \u0026LmHandlerParams)\n        != c.LORAMAC_HANDLER_SUCCESS) {\n        std.log.err(\"LoRaMac wasn't properly initialized\", .{});\n        // Fatal error, endless loop.\n        while (true) {}\n    }\n\n    // Set system maximum tolerated rx error in milliseconds\n    _ = c.LmHandlerSetSystemMaxRxError(20);\n\n    // The LoRa-Alliance Compliance protocol package should always be initialized and activated.\n    _ = c.LmHandlerPackageRegister(c.PACKAGE_ID_COMPLIANCE,         \u0026LmhpComplianceParams);\n    _ = c.LmHandlerPackageRegister(c.PACKAGE_ID_CLOCK_SYNC,         null);\n    _ = c.LmHandlerPackageRegister(c.PACKAGE_ID_REMOTE_MCAST_SETUP, null);\n    _ = c.LmHandlerPackageRegister(c.PACKAGE_ID_FRAGMENTATION,      \u0026FragmentationParams);\n\n    // Init the Clock Sync and File Transfer status\n    IsClockSynched     = false;\n    IsFileTransferDone = false;\n\n    // Join the LoRaWAN Network\n    c.LmHandlerJoin();\n\n    // Set the Transmit Timer\n    StartTxProcess(LmHandlerTxEvents_t.LORAMAC_HANDLER_TX_ON_TIMER);\n\n    // Handle LoRaWAN Events\n    handle_event_queue();  //  Never returns\n\n    return 0;\n}\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/main/lorawan_test.zig#L90-L158)\n\nTODO: Clean up names of Types, Functions and Variables\n\nTODO: Read the Internal Temperature Sensor\n\nTODO: Encode the Temperature Sensor Data with TinyCBOR and transmit to The Things Network\n\nhttps://lupyuen.github.io/articles/cbor2\n\nTODO: Monitor sensor data with Prometheus and Grafana\n\nhttps://lupyuen.github.io/articles/prometheus\n\nTODO: Add new code with `@import()`\n\nhttps://zig.news/mattnite/import-and-packages-23mb\n\nTODO: Do we need to align buffers to 32 bits when exporting to C?\n\n```zig\n/// User application data\n/// (Aligned to 32-bit because it's exported to C)\nvar AppDataBuffer: [LORAWAN_APP_DATA_BUFFER_MAX_SIZE]u8 align(4) = \n    std.mem.zeroes([LORAWAN_APP_DATA_BUFFER_MAX_SIZE]u8);\n```\n\n# Zig Type Reflection\n\nZig Type Reflection ... Can we use it to generate a Structured Call Graph for C Libraries ... Like for the LoRaWAN Library? 🤔\n\nThis Zig Program imports the LoRaWAN Library from C and dumps out the LoRaWAN Types and Functions...\n\n```zig\n// Do Type Reflection on the imported C functions\nfn reflect() void {\n    // We run this at Compile-Time (instead of Runtime)...\n    comptime {\n        // Allow Zig Compiler to loop up to 100,000 times (Default is 1,000)\n        @setEvalBranchQuota(100_000);\n\n        // Get the Type Info of the C Namespace\n        const T = @typeInfo(c);\n\n        // Show the Type Info of the C Namespace (Struct)\n        @compileLog(\"@typeInfo(c): \", T);\n        // Shows | *\"@typeInfo(c): \", std.builtin.Type { .Struct = (struct std.builtin.Type.Struct constant)}\n\n        // Show the number of Fields in the C Namespace (0)\n        @compileLog(\"T.Struct.fields.len: \", T.Struct.fields.len);\n        // Shows | *\"T.Struct.fields.len: \", 0\n\n        // Show the number of Declarations in the C Namespace (4743)\n        @compileLog(\"T.Struct.decls.len: \", T.Struct.decls.len);\n        // Shows | *\"T.Struct.decls.len: \", 4743\n\n        // Show the first Declaration in the C Namespace (__builtin_bswap16)\n        @compileLog(\"T.Struct.decls[0].name: \", T.Struct.decls[0].name);\n        // Shows | *\"T.Struct.decls[0].name: \", \"__builtin_bswap16\"\n\n        // For every C Declaration...\n        for (T.Struct.decls) |decl, i| {\n            // If the C Declaration starts with \"Lm\" (LoRaMAC)...\n            if (std.mem.startsWith(u8, decl.name, \"Lm\")) {\n                // Dump the C Declaration\n                var T2 = @typeInfo(c);\n                @compileLog(\"decl.name: \", T2.Struct.decls[i].name);\n\n                // Strangely we can't do this...\n                //   @compileLog(\"decl.name: \", decl.name);\n                // Because it shows...\n                //   *\"decl.name: \", []const u8{76,109,110,83,116,97,116,117,115,95,116}\n            }\n        }\n\n    }   // End of Compile-Time Code\n}\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/d30cc1e8714f053ad5e417087f5685a62f9df10e/reflect.zig#L825-L867)\n\n[(`@typeInfo` is explained here)](https://ziglang.org/documentation/master/#typeInfo)\n\nWhen Zig Compiler compiles the code above, we see this at Compile-Time...\n\n```text\n$ zig build-obj --verbose-cimport -target riscv32-freestanding-none -mcpu=baseline_rv32-d -isystem /Users/Luppy/pinecone/nuttx/nuttx/include -I /Users/Luppy/pinecone/nuttx/apps/examples/lorawan_test reflect.zig\ninfo(compilation): C import output: zig-cache/o/e979b806463a36dcecc2ef773bd2d2ad/cimport.zig\n| *\"@typeInfo(c): \", std.builtin.Type { .Struct = (struct std.builtin.Type.Struct constant)}\n| *\"T.Struct.fields.len: \", 0\n| *\"T.Struct.decls.len: \", 4744\n| *\"T.Struct.decls[0].name: \", \"__builtin_bswap16\"\n| *\"decl.name: \", \"LmnStatus_t\"\n| *\"decl.name: \", \"LmHandlerAdrStates_t\"\n| *\"decl.name: \", \"LmHandlerFlagStatus_t\"\n...\n| *\"decl.name: \", \"LmHandlerInit\"\n| *\"decl.name: \", \"LmHandlerIsBusy\"\n| *\"decl.name: \", \"LmHandlerProcess\"\n| *\"decl.name: \", \"LmHandlerGetDutyCycleWaitTime\"\n| *\"decl.name: \", \"LmHandlerSend\"\n| *\"decl.name: \", \"LmHandlerJoin\"\n...\n./reflect.zig:836:9: error: found compile log statement\n        @compileLog(\"@typeInfo(c): \", T);\n        ^\n./reflect.zig:840:9: error: found compile log statement\n        @compileLog(\"T.Struct.fields.len: \", T.Struct.fields.len);\n        ^\n./reflect.zig:844:9: error: found compile log statement\n        @compileLog(\"T.Struct.decls.len: \", T.Struct.decls.len);\n        ^\n./reflect.zig:848:9: error: found compile log statement\n        @compileLog(\"T.Struct.decls[0].name: \", T.Struct.decls[0].name);\n        ^\n./reflect.zig:857:17: error: found compile log statement\n                @compileLog(\"decl.name: \", T2.Struct.decls[i].name);\n                ^\n```\n\n[(Source)](https://gist.github.com/lupyuen/ff225b4208f03ac012f48b130fd3ddd1)\n\nWhich is a list of C Types and Functions from the LoRaWAN Library.\n\nLet's use this to visualise the Call Graph (by module) for the LoRaWAN Library.\n\nWe'll render the Call Graph with Mermaid.js...\n\nhttps://mermaid-js.github.io/mermaid/#/./flowchart?id=flowcharts\n\nAnd we'll group LoRaWAN Functions in Call Graph by LoRaWAN Module (Subgraph), so we can see the calls across LoRaWAN Modules.\n\n# Heisenbug\n\n_In the code above, why did we use `T2.Struct.decls[i].name` instead of `decl.name`?_\n\n```zig\n// For every C Declaration...\nfor (T.Struct.decls) |decl, i| {\n    // If the C Declaration starts with \"Lm\" (LoRaMAC)...\n    if (std.mem.startsWith(u8, decl.name, \"Lm\")) {\n        // Dump the C Declaration\n        var T2 = @typeInfo(c);\n\n        // Can't use decl.name here...\n        @compileLog(\"decl.name: \", T2.Struct.decls[i].name);\n    }\n}\n```\n\nWe expect this code to print the name of the declaration...\n\n```zig\n@compileLog(\"decl.name: \", decl.name);\n```\n\nLike so...\n\n```text\n\"decl.name: \", \"LmnStatus_t\"\n```\n\nBut strangely it prints the bytes...\n\n```text\n\"decl.name: \", []const u8{76,109,110,83,116,97,116,117,115,95,116}\n```\n\nZig Compiler seems to interpret the name differently after we have referenced the name earlier...\n\n```zig\n// If the C Declaration starts with \"Lm\" (LoRaMAC)...\nif (std.mem.startsWith(u8, decl.name, \"Lm\")) { ...\n```\n\nSo we use this workaround instead...\n\n```zig\n// Get a fresh reference to the Type Info\nvar T2 = @typeInfo(c);\n\n// This works OK\n@compileLog(\"decl.name: \", T2.Struct.decls[i].name);\n```\n\nWhich produces the result we need...\n\n```text\n\"decl.name: \", \"LmnStatus_t\"\n```\n\n# Group by C Header Files\n\nCan we automatically identify the LoRaWAN Module for every LoRaWAN Function, by analysing the `HEADER_NAME_H__` C Header Macros?\n\nLet's try this to dump out all C Declarations and Macros imported from C...\n\n```zig\n// Get the Type Info of the C Namespace\nconst T = @typeInfo(c);\n\n// Remember the C Header\nvar header: []const u8 = \"\";\n\n// For every C Declaration...\nfor (T.Struct.decls) |decl, i| {\n    var T2 = @typeInfo(c);\n\n    // If the C Declaration ends with \"_H\"...\n    if (\n        std.mem.endsWith(u8, decl.name, \"_H\") or\n        std.mem.endsWith(u8, decl.name, \"_H_\") or\n        std.mem.endsWith(u8, decl.name, \"_H__\")\n    ) {\n        // Dump the C Header and remember it\n        var name = T2.Struct.decls[i].name;\n        @compileLog(\"-----\", name);\n        header = name;\n\n    } else {\n        // Dump the C Declaration\n        var name = T2.Struct.decls[i].name;\n        @compileLog(\"decl.name:\", name);\n    }\n}   // End of C Declaration\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/293ccb37ec6fdd6b4ac40da5410bdf4c97f12eea/reflect.zig#L825-L885)\n\nWe get this list of LoRaWAN Functions and LoRaWAN Macros...\n\n```text\n| *\"decl.name:\", \"LmHandlerInit\"\n| *\"decl.name:\", \"LmHandlerIsBusy\"\n| *\"decl.name:\", \"LmHandlerProcess\"\n| *\"decl.name:\", \"LmHandlerGetDutyCycleWaitTime\"\n| *\"decl.name:\", \"LmHandlerSend\"\n| *\"decl.name:\", \"LmHandlerJoin\"\n...\n| *\"-----\", \"__LORAMAC_HANDLER_H__\"\n| *\"-----\", \"__LORAMAC_HANDLER_TYPES_H__\"\n| *\"decl.name:\", \"LMH_SYS_TIME_UPDATE_NEW_API\"\n| *\"decl.name:\", \"__LMHP_COMPLIANCE__\"\n```\n\n[(Source)](https://gist.github.com/lupyuen/7f0058c982b958a245123714fccd2289)\n\nWhich isn't useful. `LmHandlerInit` is actually declared inside the C Header File for `__LORAMAC_HANDLER_H__`. But somehow the Zig Type Reflection moves `LmHandlerInit` up to the top, before `__LORAMAC_HANDLER_H__` appears.\n\nSo it seems we need to manually group the LoRaWAN Functions into LoRaWAN Modules.\n\nThe LoRaWAN Functions appear to be sequenced according to the C Header File, so we only need to manually tag the first LoRaWAN Function for each LoRaWAN Module. Like this...\n\n```text\nLmHandlerInit → __LORAMAC_HANDLER_H__\nLoRaMacInitialization → __LORAMAC_H__\nRegionCommonValueInRange → __REGIONCOMMON_H__\nSX126xInit → __SX126x_H__\nSX126xIoInit → __SX126x_BOARD_H__\n```\n\n# Self Type Reflection\n\nOur Zig App can do Type Reflection on itself to discover its Types, Constants, Variables and Functions...\n\n```zig\n// Show the Type Info for our Zig Namespace\nconst ThisType = @typeInfo(@This());\n@compileLog(\"ThisType: \", ThisType);\n@compileLog(\"ThisType.Struct.decls.len: \", ThisType.Struct.decls.len);\n@compileLog(\"ThisType.Struct.decls[0].name: \", ThisType.Struct.decls[0].name);\n@compileLog(\"ThisType.Struct.decls[1].name: \", ThisType.Struct.decls[1].name);\n@compileLog(\"ThisType.Struct.decls[2].name: \", ThisType.Struct.decls[2].name);\n\n// Shows...\n// | *\"ThisType: \", std.builtin.Type { .Struct = (struct std.builtin.Type.Struct constant)}\n// | *\"ThisType.Struct.decls.len: \", 66\n// | *\"ThisType.Struct.decls[0].name: \", \"std\"\n// | *\"ThisType.Struct.decls[1].name: \", \"c\"\n// | *\"ThisType.Struct.decls[2].name: \", \"ACTIVE_REGION\"    \n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/293ccb37ec6fdd6b4ac40da5410bdf4c97f12eea/reflect.zig#L886-L898)\n\nWe'll use this to plot the Function Calls from our Zig Functions to the C Functions in the LoRaWAN Library.\n\n# Import Call Log\n\nEarlier we have captured this Call Log: A log of calls to the C Functions in the LoRaWAN Library...\n\n```text\ninit_event_queue\nTimerInit:     0x4201c76c\nSX126xIoInit: Compiled with gcc\ninit_gpio\n...\nRadioSetChannel: freq=923200000\nRadioSetTxConfig: modem=1, power=13, fdev=0, bandwidth=0, datarate=10, coderate=1, preambleLen=8, fixLen=0, crcOn=1, freqHopOn=0, hopPeriod=0, iqInverted=0, timeout=4000\nRadioSetTxConfig: SpreadingFactor=10, Bandwidth=4, CodingRate=1, LowDatarateOptimize=0, PreambleLength=8, HeaderType=0, PayloadLength=255, CrcMode=1, InvertIQ=0\nRadioStandby\nRadioSetModem\nSX126xSetTxParams: power=13, rampTime=7\nSX126xSetPaConfig: paDutyCycle=4, hpMax=7, deviceSel=0, paLut=1\n```\n\n[(Source)](https://gist.github.com/lupyuen/0871ac515b18d9d68d3aacf831fd0f5b)\n\nEach line of the Call Log contains the LoRaWAN Function Name. We'll match this with the info from Zig Type Reflection to plot the Call Graph.\n\nWe import the Call Log into our Zig App like so...\n\n```zig\n/// Call Log captured for this app. From\n/// https://gist.github.com/lupyuen/0871ac515b18d9d68d3aacf831fd0f5b\nconst call_log =\n    \\\\init_event_queue\n    \\\\TimerInit:     0x4201c76c\n    \\\\SX126xIoInit: Compiled with gcc\n    \\\\init_gpio\n    ...\n    \\\\RadioSetChannel: freq=923200000\n    \\\\RadioSetTxConfig: modem=1, power=13, fdev=0, bandwidth=0, datarate=10, coderate=1, preambleLen=8, fixLen=0, crcOn=1, freqHopOn=0, hopPeriod=0, iqInverted=0, timeout=4000\n    \\\\RadioSetTxConfig: SpreadingFactor=10, Bandwidth=4, CodingRate=1, LowDatarateOptimize=0, PreambleLength=8, HeaderType=0, PayloadLength=255, CrcMode=1, InvertIQ=0\n    \\\\RadioStandby\n    \\\\RadioSetModem\n    \\\\SX126xSetTxParams: power=13, rampTime=7\n    \\\\SX126xSetPaConfig: paDutyCycle=4, hpMax=7, deviceSel=0, paLut=1\n    \\\\SecureElementRandomNumber: 0xbc9f21c2\n    \\\\RadioSend: size=23\n    \\\\00 00 00 00 00 00 00 00 00 5b b1 7b 37 e7 5e c1 4b c2 21 9c 04 48 1b\n    \\\\RadioSend: PreambleLength=8, HeaderType=0, PayloadLength=23, CrcMode=1, InvertIQ=0\n    \\\\TimerStop:     0x4201b86c\n    \\\\TimerStart2:   0x4201b86c, 4000 ms\n    \\\\callout_reset: evq=0x420131a8, ev=0x4201b86c\n    \\\\\n    \\\\###### =========== MLME-Request ============ ######\n    \\\\######               MLME_JOIN               ######\n    \\\\###### ===================================== ######\n    \\\\STATUS      : OK\n    \\\\StartTxProcess\n    \\\\handle_event_queue\n    \\\\DIO1 add event\n    \\\\handle_event_queue: ev=0x4201b894\n    \\\\RadioOnDioIrq\n    \\\\RadioIrqProcess\n    \\\\IRQ_TX_DONE\n    \\\\TimerStop:     0x4201b86c\n    \\\\TODO: RtcGetCalendarTime\n    \\\\TODO: RtcBkupRead\n    \\\\RadioOnDioIrq\n    \\\\RadioIrqProcess\n    \\\\ProcessRadioTxDone: RxWindow1Delay=4988\n    \\\\RadioSleep\n    ...\n;\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/293ccb37ec6fdd6b4ac40da5410bdf4c97f12eea/reflect.zig#L916-L2409)\n\nZig Compiler can process the Call Log line by line like so...\n\n```zig\n// Show the first line of the Call Log\nvar call_log_split = std.mem.split(u8, call_log, \"\\n\");\nconst line = call_log_split.next();\n@compileLog(\"line:\", line);\n\n// Shows | *\"line:\", []const u8{103,112,108,104,95,101,110,97,98,108,101,58,32,87,65,82,78,73,78,71,58,32,112,105,110,57,58,32,65,108,114,101,97,100,121,32,100,101,116,97,99,104,101,100}\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/293ccb37ec6fdd6b4ac40da5410bdf4c97f12eea/reflect.zig#L900-L904)\n\nTo iterate through all lines of the Call Log we do this...\n\n```zig\n// For every line in the Call Log...\nvar call_log_split = std.mem.split(u8, call_log, \"\\n\");\nwhile (call_log_split.next()) |line| {\n\n    // TODO: Process the line\n    @compileLog(\"line:\", line);\n    ...\n}  // End of Call Log\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/0c480450bcffca353948fc24f41258316c95b8e1/reflect.zig#L906-L928)\n\n# Match Call Log\n\nLet's match the Call Log with the Function Names from our LoRaWAN Library...\n\n```zig\n// For every line in the Call Log...\nvar call_log_split = std.mem.split(u8, call_log, \"\\n\");\nwhile (call_log_split.next()) |line| {\n\n    // For every C Declaration...\n    for (T.Struct.decls) |decl, i| {\n        if (std.mem.eql(u8, decl.name, \"Radio\")) { continue; }  // Skip Radio\n        var T2 = @typeInfo(c);\n\n        // If the C Declaration matches the Call Log...\n        if (std.mem.startsWith(u8, line, decl.name)) {\n            // Dump the C Declaration\n            var name = T2.Struct.decls[i].name;\n            @compileLog(\"Found call log\", name);\n            break;\n        }\n    }   // End of C Declaration\n\n}  // End of Call Log\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/0c480450bcffca353948fc24f41258316c95b8e1/reflect.zig#L906-L928)\n\nThe code above produces this result...\n\n```text\n| *\"Found call log\", \"LoRaMacInitialization\"\n| *\"Found call log\", \"TimerInit\"\n| *\"Found call log\", \"SX126xIoInit\"\n| *\"Found call log\", \"SX126xSetTx\"\n| *\"Found call log\", \"SX126xSetPaConfig\"\n| *\"Found call log\", \"TimerInit\"\n| *\"Found call log\", \"TimerInit\"\n| *\"Found call log\", \"RadioSetModem\"\n| *\"Found call log\", \"RadioSetModem\"\n| *\"Found call log\", \"RadioSetPublicNetwork\"\n| *\"Found call log\", \"RadioSleep\"\n| *\"Found call log\", \"RadioSetModem\"\n| *\"Found call log\", \"RadioSetPublicNetwork\"\n...\n```\n\n[(Source)](https://gist.github.com/lupyuen/e423de95105708223cf5f1b6455e07d7)\n\nWhich is a list of LoRaWAN Functions and the sequence they were called.\n\nOne step closer to rendering our Structured Call Graph!\n\n# Draw The Graph\n\nLet's extend the code above and render a Naive Call Graph...\n\n-   For every LoRaWAN Function that we identify in the Call Log...\n\n-   We plot a line between the LoRaWAN Function and the next LoRaWAN Function in the Call Log\n\n-   So we're just plotting the connected sequence of Function Calls\n\n(Yep it's super naive!)\n\nWe do it like this...\n\n```zig\n// Draw the graph for all functions in the Call Log\nvar call_log_split = std.mem.split(u8, call_log, \"\\n\");\nvar prev_name: []const u8 = \"Start\";\n@compileLog(\"flowchart TD;\");  // Top-Down Flowchart\n\n// For every line in the Call Log...\nwhile (call_log_split.next()) |line| {\n\n    // For every C Declaration...\n    for (T.Struct.decls) |decl, i| {\n        if (std.mem.eql(u8, decl.name, \"Radio\")) { continue; }  // Skip Radio\n        var T2 = @typeInfo(c);\n\n        // If the C Declaration matches the Call Log...\n        if (std.mem.startsWith(u8, line, decl.name)) {\n            // Draw the graph: [previous function]--\u003e[current function]\n            var name = T2.Struct.decls[i].name;\n            @compileLog(\"    \", prev_name, \"--\u003e\", name, \";\");\n            prev_name = name;\n            break;\n        }\n    }   // End of C Declaration\n\n}  // End of Call Log\n@compileLog(\"    \", prev_name, \"--\u003e\", \"End\", \";\");\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/5c7989894566cdc0d1852789bc68621c531e459e/reflect.zig#L906-L932)\n\nZig Compiler produces this result at Compile-Time...\n\n```text\n| *\"flowchart TD;\"\n| *\"    \", \"Start\", *\"--\u003e\", \"LoRaMacInitialization\", *\";\"\n| *\"    \", \"LoRaMacInitialization\", *\"--\u003e\", \"TimerInit\", *\";\"\n| *\"    \", \"TimerInit\", *\"--\u003e\", \"TimerInit\", *\";\"\n| *\"    \", \"TimerInit\", *\"--\u003e\", \"TimerInit\", *\";\"\n| *\"    \", \"TimerInit\", *\"--\u003e\", \"TimerInit\", *\";\"\n| *\"    \", \"TimerInit\", *\"--\u003e\", \"TimerInit\", *\";\"\n| *\"    \", \"TimerInit\", *\"--\u003e\", \"TimerInit\", *\";\"\n| *\"    \", \"TimerInit\", *\"--\u003e\", \"TimerInit\", *\";\"\n| *\"    \", \"TimerInit\", *\"--\u003e\", \"TimerInit\", *\";\"\n| *\"    \", \"TimerInit\", *\"--\u003e\", \"SX126xIoInit\", *\";\"\n| *\"    \", \"SX126xIoInit\", *\"--\u003e\", \"SX126xSetTx\", *\";\"\n| *\"    \", \"SX126xSetTx\", *\"--\u003e\", \"SX126xSetPaConfig\", *\";\"\n| *\"    \", \"SX126xSetPaConfig\", *\"--\u003e\", \"TimerInit\", *\";\"\n| *\"    \", \"TimerInit\", *\"--\u003e\", \"TimerInit\", *\";\"\n| *\"    \", \"TimerInit\", *\"--\u003e\", \"RadioSetModem\", *\";\"\n...\n```\n\n[(Source)](https://gist.github.com/lupyuen/056340b299495682c0c2fbc61b30f203)\n\nWe manually remove the delimiters from the Zig Compiler Log above like this...\n\n```text\nflowchart TD;\n    Start--\u003eLoRaMacInitialization;\n    LoRaMacInitialization--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eSX126xIoInit;\n    SX126xIoInit--\u003eSX126xSetTx;\n    SX126xSetTx--\u003eSX126xSetPaConfig;\n    SX126xSetPaConfig--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eRadioSetModem;\n    ...\n```\n\nTo get this [Mermaid.js Flowchart](https://mermaid-js.github.io/mermaid/#/./flowchart?id=flowcharts)...\n\n```mermaid\nflowchart TD;\n    Start--\u003eLoRaMacInitialization;\n    LoRaMacInitialization--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eSX126xIoInit;\n    SX126xIoInit--\u003eSX126xSetTx;\n    SX126xSetTx--\u003eSX126xSetPaConfig;\n    SX126xSetPaConfig--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eRadioSetModem;\n    RadioSetModem--\u003eRadioSetModem;\n    RadioSetModem--\u003eRadioSetPublicNetwork;\n    RadioSetPublicNetwork--\u003eRadioSleep;\n    RadioSleep--\u003eRadioSetModem;\n    RadioSetModem--\u003eRadioSetPublicNetwork;\n    RadioSetPublicNetwork--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eRadioSetChannel;\n    RadioSetChannel--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetModem;\n    RadioSetModem--\u003eSX126xSetTx;\n    SX126xSetTx--\u003eSX126xSetPaConfig;\n    SX126xSetPaConfig--\u003eRadioSend;\n    RadioSend--\u003eRadioSend;\n    RadioSend--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerInit;\n    TimerInit--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStop;\n    TimerStop--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_TX_DONE;\n    IRQ_TX_DONE--\u003eTimerStop;\n    TimerStop--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioSleep;\n    RadioSleep--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetChannel;\n    RadioSetChannel--\u003eRadioSetRxConfig;\n    RadioSetRxConfig--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetModem;\n    RadioSetModem--\u003eRadioSetRxConfig;\n    RadioSetRxConfig--\u003eRadioRx;\n    RadioRx--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_HEADER_VALID;\n    IRQ_HEADER_VALID--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_RX_DONE;\n    IRQ_RX_DONE--\u003eTimerStop;\n    TimerStop--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioSleep;\n    RadioSleep--\u003eTimerStop;\n    TimerStop--\u003eLmHandlerSend;\n    LmHandlerSend--\u003eRadioSetChannel;\n    RadioSetChannel--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetModem;\n    RadioSetModem--\u003eSX126xSetTx;\n    SX126xSetTx--\u003eSX126xSetPaConfig;\n    SX126xSetPaConfig--\u003eRadioSend;\n    RadioSend--\u003eRadioSend;\n    RadioSend--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_TX_DONE;\n    IRQ_TX_DONE--\u003eTimerStop;\n    TimerStop--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioSleep;\n    RadioSleep--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetChannel;\n    RadioSetChannel--\u003eRadioSetRxConfig;\n    RadioSetRxConfig--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetModem;\n    RadioSetModem--\u003eRadioSetRxConfig;\n    RadioSetRxConfig--\u003eRadioRx;\n    RadioRx--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_RX_TX_TIMEOUT;\n    IRQ_RX_TX_TIMEOUT--\u003eTimerStop;\n    TimerStop--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioSleep;\n    RadioSleep--\u003eTimerStop;\n    TimerStop--\u003eTimerStop;\n    TimerStop--\u003eTimerStop;\n    TimerStop--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eLmHandlerSend;\n    LmHandlerSend--\u003eRadioSetChannel;\n    RadioSetChannel--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetModem;\n    RadioSetModem--\u003eSX126xSetTx;\n    SX126xSetTx--\u003eSX126xSetPaConfig;\n    SX126xSetPaConfig--\u003eRadioSend;\n    RadioSend--\u003eRadioSend;\n    RadioSend--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_TX_DONE;\n    IRQ_TX_DONE--\u003eTimerStop;\n    TimerStop--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioSleep;\n    RadioSleep--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetChannel;\n    RadioSetChannel--\u003eRadioSetRxConfig;\n    RadioSetRxConfig--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetModem;\n    RadioSetModem--\u003eRadioSetRxConfig;\n    RadioSetRxConfig--\u003eRadioRx;\n    RadioRx--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_RX_TX_TIMEOUT;\n    IRQ_RX_TX_TIMEOUT--\u003eTimerStop;\n    TimerStop--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioSleep;\n    RadioSleep--\u003eTimerStop;\n    TimerStop--\u003eTimerStop;\n    TimerStop--\u003eTimerStop;\n    TimerStop--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eLmHandlerSend;\n    LmHandlerSend--\u003eRadioSetChannel;\n    RadioSetChannel--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetModem;\n    RadioSetModem--\u003eSX126xSetTx;\n    SX126xSetTx--\u003eSX126xSetPaConfig;\n    SX126xSetPaConfig--\u003eRadioSend;\n    RadioSend--\u003eRadioSend;\n    RadioSend--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_TX_DONE;\n    IRQ_TX_DONE--\u003eTimerStop;\n    TimerStop--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioSleep;\n    RadioSleep--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetChannel;\n    RadioSetChannel--\u003eRadioSetRxConfig;\n    RadioSetRxConfig--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetModem;\n    RadioSetModem--\u003eRadioSetRxConfig;\n    RadioSetRxConfig--\u003eRadioRx;\n    RadioRx--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_RX_TX_TIMEOUT;\n    IRQ_RX_TX_TIMEOUT--\u003eTimerStop;\n    TimerStop--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioSleep;\n    RadioSleep--\u003eTimerStop;\n    TimerStop--\u003eTimerStop;\n    TimerStop--\u003eTimerStop;\n    TimerStop--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eLmHandlerSend;\n    LmHandlerSend--\u003eRadioSetChannel;\n    RadioSetChannel--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetModem;\n    RadioSetModem--\u003eSX126xSetTx;\n    SX126xSetTx--\u003eSX126xSetPaConfig;\n    SX126xSetPaConfig--\u003eRadioSend;\n    RadioSend--\u003eRadioSend;\n    RadioSend--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_TX_DONE;\n    IRQ_TX_DONE--\u003eTimerStop;\n    TimerStop--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioSleep;\n    RadioSleep--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetChannel;\n    RadioSetChannel--\u003eRadioSetRxConfig;\n    RadioSetRxConfig--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetModem;\n    RadioSetModem--\u003eRadioSetRxConfig;\n    RadioSetRxConfig--\u003eRadioRx;\n    RadioRx--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_RX_TX_TIMEOUT;\n    IRQ_RX_TX_TIMEOUT--\u003eTimerStop;\n    TimerStop--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioSleep;\n    RadioSleep--\u003eTimerStop;\n    TimerStop--\u003eTimerStop;\n    TimerStop--\u003eTimerStop;\n    TimerStop--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eLmHandlerSend;\n    LmHandlerSend--\u003eRadioSetChannel;\n    RadioSetChannel--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioSetTxConfig;\n    RadioSetTxConfig--\u003eRadioStandby;\n    RadioStandby--\u003eRadioSetModem;\n    RadioSetModem--\u003eSX126xSetTx;\n    SX126xSetTx--\u003eSX126xSetPaConfig;\n    SX126xSetPaConfig--\u003eRadioSend;\n    RadioSend--\u003eRadioSend;\n    RadioSend--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eIRQ_TX_DONE;\n    IRQ_TX_DONE--\u003eTimerStop;\n    TimerStop--\u003eRadioOnDioIrq;\n    RadioOnDioIrq--\u003eRadioIrqProcess;\n    RadioIrqProcess--\u003eRadioSleep;\n    RadioSleep--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n    TimerStart--\u003eTimerSetValue;\n    TimerSetValue--\u003eTimerStart;\n    TimerStart--\u003eTimerStop;\n    TimerStop--\u003eTimerStart;\n```\n\n# Super Naive Call Graph\n\n_What's wrong with the super-naive Call Graph that we have produced?_\n\n```text\nflowchart TD;\n    Start--\u003eLoRaMacInitialization;\n    LoRaMacInitialization--\u003eTimerInit;\n    TimerInit--\u003eTimerInit;\n    TimerInit--\u003eSX126xIoInit;\n    SX126xIoInit--\u003eSX126xSetTx;\n    SX126xSetTx--\u003eSX126xSetPaConfig;\n    SX126xSetPaConfig--\u003eTimerInit;\n    TimerInit--\u003eRadioSetModem;\n    ...\n```\n\n[(Source)](https://gist.github.com/lupyuen/056340b299495682c0c2fbc61b30f203)\n\nOur Call Graph lacks __Structure__. Remember we're only plotting the sequence of Function Calls...\n\n-   `TimerInit → TimerInit` doesn't make sense (because we don't have recursive functions)\n\n-   `TimerInit → RadioSetModem` is totally wrong because `TimerInit` is a Low-Level Function (NimBLE Multithreading Library), whereas `RadioSetModem` is an High-Level Function (SX1262 Library)\n\n_Can we add some Structure to improve the Call Graph?_\n\nTo produce a Structured Call Graph we'll group the functions into Modules, from High-Level to Low-Level...\n\n1.  Zig App (Highest Level)\n\n1.  LoRaWAN Library\n\n1.  SX1262 Library\n\n1.  NimBLE Multithreading Library (Lowest Level)\n\nAnd we'll ensure that we never draw a line from a Low-Level Function to a High-Level Function.\n\nThis is a snippet of what we want to achieve...\n\n```mermaid\nflowchart TD;\n    subgraph LoRaWAN;\n        Start;\n        LoRaMacInitialization;\n    end;\n    subgraph SX1262;\n        SX126xIoInit;\n        SX126xSetTx;\n        SX126xSetPaConfig;\n    end;\n    subgraph NimBLE;\n        TimerInit;\n        TimerInit;\n    end;\n    Start--\u003eLoRaMacInitialization;\n    LoRaMacInitialization--\u003eTimerInit;\n    SX126xIoInit--\u003eSX126xSetTx;\n    SX126xSetTx--\u003eSX126xSetPaConfig;\n    SX126xSetPaConfig--\u003eTimerInit;\n```\n\n# Group Functions by Modules\n\nLet's add some Structure to our Call Graph by grouping the C Functions into Modules.\n\nThis is how we define the Modules, from High Level to Low Level...\n\n```zig\n// Define the Modules and the First / Last Functions in each Module.\n// We order the Modules from High-Level to Low-Level.\nvar all_modules = [_]Module {\n    // LoRaMAC Handler is the Top Level Module that drives the LoRaWAN Stack\n    Module {\n        .name           = \"LMHandler\",\n        .first_function = \"LmHandlerInit\",\n        .last_function  = \"DisplayAppInfo\",\n        .first_index    = undefined,\n        .last_index     = undefined,\n    },\n    // LoRaMAC Module is the implementation of the LoRaWAN Driver\n    Module {\n        .name           = \"LoRaMAC\",\n        .first_function = \"LoRaMacInitialization\",\n        .last_function  = \"LoRaMacDeInitialization\",\n        .first_index    = undefined,\n        .last_index     = undefined,\n    },\n    // Radio Module is the abstract interface for LoRa Radio Transceivers\n    Module {\n        .name           = \"Radio\",\n        .first_function = \"RadioInit\",\n        .last_function  = \"RadioAddRegisterToRetentionList\",\n        .first_index    = undefined,\n        .last_index     = undefined,\n    },\n    // SX1262 Module is the LoRa Driver for Semtech SX1262 Radio Transceiver\n    Module {\n        .name           = \"SX1262\",\n        .first_function = \"SX126xInit\",\n        .last_function  = \"SX126xSetOperatingMode\",\n        .first_index    = undefined,\n        .last_index     = undefined,\n    },\n    // NimBLE is the Bottom Level Module that contains Multithreading Functions like Timers and Event Queues\n    Module {\n        .name           = \"NimBLE\",\n        .first_function = \"TimerInit\",\n        .last_function  = \"TimerGetElapsedTime\",\n        .first_index    = undefined,\n        .last_index     = undefined,\n    },\n};\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/69565443d4b9afb5a2153473b831d7188ff266ef/reflect.zig#L838-L876)\n\nNote that we specify only the First and Last Functions for each Module.\n\nThat's because functions from the same Module are clustered together in the Zig Type Reflection.\n\nThen we render each Module as a Mermaid.js Subgraph...\n\n```zig\n/// Render all Modules and their Functions as Subgraphs\nfn render_modules(all_modules: []Module) void {\n    comptime {\n        // Render every Module\n        for (all_modules) |module, m| {\n            @compileLog(\"    subgraph \", module.name, \";\");\n\n            // For every line in the Call Log...\n            var call_log_split = std.mem.split(u8, call_log, \"\\n\");\n            while (call_log_split.next()) |line| {\n                var T = @typeInfo(c);\n\n                // If the Call Log matches a C Declaration...\n                if (get_decl_by_name_filtered(all_modules, line)) |decl_index| {\n\n                    // Get the Module Index for the C Declaration\n                    if (get_module_by_decl(all_modules, decl_index)) |m2| {\n\n                        // If the C Declaration matches our Module Index...\n                        if (m == m2) {\n\n                            // Print the Function Name\n                            var name = T.Struct.decls[decl_index].name;\n                            @compileLog(\"        \", name, \";\");\n                        }\n                    } else {\n                        // Missing Declaration\n                        var name = T.Struct.decls[decl_index].name;\n                        @compileLog(\"Missing Decl:\", name);\n                    }\n                }\n            }  // End of Call Log            \n\n            @compileLog(\"    end;\");\n        }  // End of Module\n    }\n}\n```\n\n[(Source)](https://github.com/lupyuen/zig-bl602-nuttx/blob/69565443d4b9afb5a2153473b831d7188ff266ef/reflect.zig#L914-L952)\n\nEach Subgraph contains a list of Functions that belong to the Module...\n\n```text\nflowchart TD;\n    subgraph LMHandler;\n        LmHandlerSend;\n    end;\n    subgraph LoRaMAC;\n        LoRaMacInitialization;\n    end;\n    subgraph Radio;\n        RadioSetModem;\n        RadioSetPublicNetwork;\n        RadioSleep;\n        RadioSetChannel;\n        RadioSetTxConfig;\n        RadioStandby;\n        RadioSend;\n        RadioIrqProcess;\n        ...\n    end;\n    subgraph SX1262;\n        SX126xIoInit;\n        SX126xSetTx;\n        SX126xSetPaConfig;\n    end;\n    subgraph NimBLE;\n        TimerInit;\n        TimerStop;\n        TimerStart;\n        TimerSetValue;\n    end;\n    Start--\u003eLoRaMacInitialization;\n    LoRaMacInitialization--\u003eTimerInit;\n    TimerInit--\u003eSX126xIoInit;\n    ...\n```\n\n[(Source)](https://gist.github.com/lupyuen/a7fa7b9973ade3c4565f21e20ef7f88b)\n\nWhen we render the output with Mermaid.js, we get a Structured Call Graph that looks more meaningful...\n\n```mermaid\nflowchart TD;\n    subgraph LMHandler;\n        LmHandlerSend;\n        LmHandlerSend;\n        LmHandlerSend;\n        LmHandlerSend;\n        LmHandlerSend;\n        LmHandlerSend;\n        LmHandlerSend;\n        LmHandlerSend;\n        LmHandlerSend;\n        LmHandlerSend;\n        LmHandlerSend;\n        LmHandlerSend;\n    end;\n    subgraph LoRaMAC;\n        LoRaMacInitialization;\n    end;\n    subgraph Radio;\n        RadioSetModem;\n        RadioSetModem;\n        RadioSetPublicNetwork;\n        RadioSleep;\n        RadioSetModem;\n        RadioSetPublicNetwork;\n        RadioSetChannel;\n        RadioSetTxConfig;\n        RadioSetTxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSend;\n        RadioSend;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioStandby;\n        RadioSetChannel;\n        RadioSetRxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSetRxConfig;\n        RadioRx;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioSetChannel;\n        RadioSetTxConfig;\n        RadioSetTxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSend;\n        RadioSend;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioStandby;\n        RadioSetChannel;\n        RadioSetRxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSetRxConfig;\n        RadioRx;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioIrqProcess;\n        RadioSetChannel;\n        RadioSetTxConfig;\n        RadioSetTxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSend;\n        RadioSend;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioStandby;\n        RadioSetChannel;\n        RadioSetRxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSetRxConfig;\n        RadioRx;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioIrqProcess;\n        RadioSetChannel;\n        RadioSetTxConfig;\n        RadioSetTxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSend;\n        RadioSend;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioStandby;\n        RadioSetChannel;\n        RadioSetRxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSetRxConfig;\n        RadioRx;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioIrqProcess;\n        RadioSetChannel;\n        RadioSetTxConfig;\n        RadioSetTxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSend;\n        RadioSend;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioStandby;\n        RadioSetChannel;\n        RadioSetRxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSetRxConfig;\n        RadioRx;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioIrqProcess;\n        RadioSetChannel;\n        RadioSetTxConfig;\n        RadioSetTxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSend;\n        RadioSend;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioStandby;\n        RadioSetChannel;\n        RadioSetRxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSetRxConfig;\n        RadioRx;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioIrqProcess;\n        RadioSetChannel;\n        RadioSetTxConfig;\n        RadioSetTxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSend;\n        RadioSend;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioStandby;\n        RadioSetChannel;\n        RadioSetRxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSetRxConfig;\n        RadioRx;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioIrqProcess;\n        RadioSetChannel;\n        RadioSetTxConfig;\n        RadioSetTxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSend;\n        RadioSend;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioStandby;\n        RadioSetChannel;\n        RadioSetRxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSetRxConfig;\n        RadioRx;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioIrqProcess;\n        RadioSetChannel;\n        RadioSetTxConfig;\n        RadioSetTxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSend;\n        RadioSend;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioStandby;\n        RadioSetChannel;\n        RadioSetRxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSetRxConfig;\n        RadioRx;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioIrqProcess;\n        RadioSetChannel;\n        RadioSetTxConfig;\n        RadioSetTxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSend;\n        RadioSend;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioStandby;\n        RadioSetChannel;\n        RadioSetRxConfig;\n        RadioStandby;\n        RadioSetModem;\n        RadioSetRxConfig;\n        RadioRx;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioIrqProcess;\n        RadioSleep;\n        RadioIrqProcess;\n        RadioSet","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flupyuen%2Fzig-bl602-nuttx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flupyuen%2Fzig-bl602-nuttx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flupyuen%2Fzig-bl602-nuttx/lists"}