Repository: TUDelft-SPC-Lab/midge-code
Pull request #7: nix setup
Author: login: harshilbhatt2001
Created: 2025-03-07T17:49:40Z | Merged: 2025-03-12T08:33:34Z

Fix #6 

You need to install `nrf-command-line-tools`, which provides `mergehex` 

Linked issue #6: Bluetooth Initialization Freeze
Based on message

>For 2 of them, I do not get the "external CPU reset" error but they get stuck on the Bluetooth module initialisation function 

>One the PhD students here was trying to help me debug the "external cpu reset issue" using a Segger JLink. In the process, it looks like we erased by accident the first 2.6 Kb of data from the memory that contain the manufacturer's Bluetooth protocol stack. This also happened with a different Batch 1 midge and it's also now stuck in the same function. 

>My Batch 2 midges got shuffled, so I cannot say for certain that those two are the ones that got the Bluetooth stack erased. 

>I think the solution here would be to re-flash the Bluetooth stack but I don't know how to do that. 

>Here is the link to the Bluetooth stack although maybe it is better to copy and paste it from one of the batch 1 midges. [https://www.nordicsemi.com/Products/Development-software/S132/Download](https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.nordicsemi.com%2FProducts%2FDevelopment-software%2FS132%2FDownload&data=05%7C02%7CE.Dorta%40tudelft.nl%7C8d0ec7a7466b4c768af008dd52768a8d%7C096e524d692940308cd38ab42de0887b%7C0%7C0%7C638757391253172244%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=H6eQ92F8TVFVfhBI1AvNAyAv5MRXnnVtsn49i4Q%2BlSE%3D&reserved=0)

Diff:
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..1587a60
--- /dev/null
+++ b/.envrc
@@ -0,0 +1,12 @@
+if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then
+  source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs="
+fi
+
+watch_file devenv.nix
+watch_file devenv.lock
+watch_file devenv.yaml
+if ! use flake . --impure
+then
+  echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2
+fi
+
diff --git a/.gitignore b/.gitignore
index 3446e37..f5ad9ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ _build/*
 __pycache/
 .vscode/c_cpp_properties.json
 .vscode/settings.json
+.devenv/
+.direnv/
\ No newline at end of file
diff --git a/README.md b/README.md
index 527f385..e161bc7 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,25 @@
 - You will probably have to use `conda` for setting up a python2 env. In that
   env, you will need to download the `bluepy` module
 
+### Using Nix:
+This project includes a `flake.nix` file that automatically sets up the development environment with all required dependencies.
+1. Install Nix package manager by following instructions at nixos.org
+2. Enable flakes by either:
+    * Adding `experimental-features = nix-command flakes` to your `~./config/nix/nix.conf`
+    * Or using the `-E` flash with each nix command
+3. Enter the development shell:
+    ```shell
+    nix develop
+    ```
+    This will setup an environment with:
+        * ARM GCC toolchain (gcc-arm-embedded-13)
+        * socat and picocom for debug logs
+        * Python with required packages
+        * C development tools
+4. Alternatively, you can use direnv to automatically enter the environment:
+    * Install direnv from direnv.net
+    * Run `direnv allow` in the project directory
+
 ## Board options
 
 There are several MCU boards that can be used for compiling and debuging the midge fimware.
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..28e5a1d
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,279 @@
+{
+  "nodes": {
+    "cachix": {
+      "inputs": {
+        "devenv": [
+          "devenv"
+        ],
+        "flake-compat": [
+          "devenv"
+        ],
+        "git-hooks": [
+          "devenv"
+        ],
+        "nixpkgs": "nixpkgs"
+      },
+      "locked": {
+        "lastModified": 1728672398,
+        "narHash": "sha256-KxuGSoVUFnQLB2ZcYODW7AVPAh9JqRlD5BrfsC/Q4qs=",
+        "owner": "cachix",
+        "repo": "cachix",
+        "rev": "aac51f698309fd0f381149214b7eee213c66ef0a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "ref": "latest",
+        "repo": "cachix",
+        "type": "github"
+      }
+    },
+    "devenv": {
+      "inputs": {
+        "cachix": "cachix",
+        "flake-compat": "flake-compat",
+        "git-hooks": "git-hooks",
+        "nix": "nix",
+        "nixpkgs": "nixpkgs_3"
+      },
+      "locked": {
+        "lastModified": 1737028622,
+        "narHash": "sha256-O1QDc/KWWVf9BgWDoa7WKZa2cDMh5mNMb6BMNMxq2Q4=",
+        "owner": "cachix",
+        "repo": "devenv",
+        "rev": "4e5b00134bf03f16af6b25b80abb38c598cfe239",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "devenv",
+        "type": "github"
+      }
+    },
+    "flake-compat": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1696426674,
+        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
+    "flake-parts": {
+      "inputs": {
+        "nixpkgs-lib": [
+          "devenv",
+          "nix",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1712014858,
+        "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "type": "github"
+      }
+    },
+    "git-hooks": {
+      "inputs": {
+        "flake-compat": [
+          "devenv"
+        ],
+        "gitignore": "gitignore",
+        "nixpkgs": [
+          "devenv",
+          "nixpkgs"
+        ],
+        "nixpkgs-stable": [
+          "devenv"
+        ]
+      },
+      "locked": {
+        "lastModified": 1730302582,
+        "narHash": "sha256-W1MIJpADXQCgosJZT8qBYLRuZls2KSiKdpnTVdKBuvU=",
+        "owner": "cachix",
+        "repo": "git-hooks.nix",
+        "rev": "af8a16fe5c264f5e9e18bcee2859b40a656876cf",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "git-hooks.nix",
+        "type": "github"
+      }
+    },
+    "gitignore": {
+      "inputs": {
+        "nixpkgs": [
+          "devenv",
+          "git-hooks",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1709087332,
+        "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "type": "github"
+      }
+    },
+    "libgit2": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1697646580,
+        "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
+        "owner": "libgit2",
+        "repo": "libgit2",
+        "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
+        "type": "github"
+      },
+      "original": {
+        "owner": "libgit2",
+        "repo": "libgit2",
+        "type": "github"
+      }
+    },
+    "nix": {
+      "inputs": {
+        "flake-compat": [
+          "devenv"
+        ],
+        "flake-parts": "flake-parts",
+        "libgit2": "libgit2",
+        "nixpkgs": "nixpkgs_2",
+        "nixpkgs-23-11": [
+          "devenv"
+        ],
+        "nixpkgs-regression": [
+          "devenv"
+        ],
+        "pre-commit-hooks": [
+          "devenv"
+        ]
+      },
+      "locked": {
+        "lastModified": 1727438425,
+        "narHash": "sha256-X8ES7I1cfNhR9oKp06F6ir4Np70WGZU5sfCOuNBEwMg=",
+        "owner": "domenkozar",
+        "repo": "nix",
+        "rev": "f6c5ae4c1b2e411e6b1e6a8181cc84363d6a7546",
+        "type": "github"
+      },
+      "original": {
+        "owner": "domenkozar",
+        "ref": "devenv-2.24",
+        "repo": "nix",
+        "type": "github"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1730531603,
+        "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_2": {
+      "locked": {
+        "lastModified": 1717432640,
+        "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "release-24.05",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_3": {
+      "locked": {
+        "lastModified": 1716977621,
+        "narHash": "sha256-Q1UQzYcMJH4RscmpTkjlgqQDX5yi1tZL0O345Ri6vXQ=",
+        "owner": "cachix",
+        "repo": "devenv-nixpkgs",
+        "rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "ref": "rolling",
+        "repo": "devenv-nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_4": {
+      "locked": {
+        "lastModified": 1737062831,
+        "narHash": "sha256-Tbk1MZbtV2s5aG+iM99U8FqwxU/YNArMcWAv6clcsBc=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "5df43628fdf08d642be8ba5b3625a6c70731c19c",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "devenv": "devenv",
+        "nixpkgs": "nixpkgs_4",
+        "systems": "systems"
+      }
+    },
+    "systems": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..5ab7762
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,57 @@
+{
+  inputs = {
+    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+    systems.url = "github:nix-systems/default";
+    devenv.url = "github:cachix/devenv";
+  };
+
+  outputs = { self, nixpkgs, devenv, systems, ... } @ inputs:
+    let
+      forEachSystem = nixpkgs.lib.genAttrs (import systems);
+    in
+    {
+      packages = forEachSystem (system: {
+        devenv-up = self.devShells.${system}.default.config.procfileScript;
+      });
+
+      devShells = forEachSystem
+        (system:
+          let
+            pkgs = nixpkgs.legacyPackages.${system};
+          in
+          {
+            default = devenv.lib.mkShell {
+              inherit inputs pkgs;
+              modules = [
+                ({ pkgs, lib, config, ... }: {
+                  dotenv.disableHint = true;
+
+                  env = {
+                  };
+
+                  languages.c = {
+                    enable = true;
+                  };
+
+                  languages.python = {
+                    enable = true;
+                    venv.enable = true;
+                  };
+
+                  packages = with pkgs; [
+                    gcc-arm-embedded-13
+                    socat
+                    picocom
+                    glib
+                    virtualenv
+                    python312Packages.tkinter
+                    python312Packages.ipython
+                    python312Packages.pandas
+                    pyocd
+                  ];
+                })
+              ];
+            };
+          });
+    };
+}


Reviews:
user: login: Josfemova
body: If you want to support nix perhaps adding a comment in the env setup in the readme would be a nice to have so it's explicit that at least atm this is supported 
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-10T01:01:32Z
user: login: Gara-Dorta
body: Nice work but I have a few questions.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T08:46:35Z
user: login: Gara-Dorta
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T08:49:15Z
user: login: harshilbhatt2001
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T10:17:18Z
user: login: Gara-Dorta
body: I thought a bit more about it and I didn't mention that for PR please keep it at one logical change per PR. This one is doing two changes:
1. Adding support for nix
2. Adding targets to flash the bluetooth stack

So this should have been 2 PRs. For this one, I'm fine to leave it as is but for future ones please keep it to one change. This is what I meant when I said in discord to please write small PRs.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T08:09:31Z
user: login: Gara-Dorta
body: Great, thanks for adding support for nix.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T08:33:09Z

Review comments:
diff_hunk: @@ -0,0 +1,57 @@
+{
path: flake.nix
user: login: Gara-Dorta
body: I know nix is very popular to create reproducible environments. I've only had nasty experiences with it though. I don't oppose having it but as Jose suggests, this needs to be documented.
created_at: 2025-03-10T08:41:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -326,11 +326,25 @@ sdk_config:
 openocd:
 	openocd -f interface/cmsis-dap.cfg -f target/nrf52.cfg
 load_gdb:
-	arm-none-eabi-gdb -se _build/nrf52832_xxaa_debug.out -x debug.gdb
+	arm-none-eabi-gdb -se $(OUTPUT_DIRECTORY)/nrf52832_xxaa_debug.out -x debug.gdb
 logs: SHELL:=/bin/bash   # Use the bash shell for the logs target, as sh does not have the disown command 
 logs:
 	socat pty,link=/tmp/ttyvnrf,waitslave tcp:127.0.0.1:8000 & disown
 	picocom /tmp/ttyvnrf -b 115200
 
+package_release_app:
path: Makefile
user: login: Gara-Dorta
body: Do I understand this well in that this is creating a combined binary of the bluetooth stack and the firmware and then the flash_with_gdb flashes the combined binary?
created_at: 2025-03-10T08:44:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -326,11 +326,25 @@ sdk_config:
 openocd:
 	openocd -f interface/cmsis-dap.cfg -f target/nrf52.cfg
 load_gdb:
-	arm-none-eabi-gdb -se _build/nrf52832_xxaa_debug.out -x debug.gdb
+	arm-none-eabi-gdb -se $(OUTPUT_DIRECTORY)/nrf52832_xxaa_debug.out -x debug.gdb
 logs: SHELL:=/bin/bash   # Use the bash shell for the logs target, as sh does not have the disown command 
 logs:
 	socat pty,link=/tmp/ttyvnrf,waitslave tcp:127.0.0.1:8000 & disown
 	picocom /tmp/ttyvnrf -b 115200
 
+package_release_app:
+	@echo Packaging: s132_nrf52_6.1.1_softdevice.hex + $(OUTPUT_DIRECTORY)/nrf52832_xxaa_release.out
+	cp $(SDK_ROOT)/components/softdevice/s132/hex/s132_nrf52_6.1.1_softdevice.hex $(OUTPUT_DIRECTORY)/s132_nrf52_6.1.1_softdevice.hex
+	mergehex --merge $(OUTPUT_DIRECTORY)/s132_nrf52_6.1.1_softdevice.hex $(OUTPUT_DIRECTORY)/nrf52832_xxaa_release.hex \
+		-o $(OUTPUT_DIRECTORY)/$(PROJECT_NAME).hex
+	@echo Package created at: $(OUTPUT_DIRECTORY)/$(PROJECT_NAME).hex
+
+package_debug_app:
+	@echo Packaging: s132_nrf52_6.1.1_softdevice.hex + $(OUTPUT_DIRECTORY)/nrf52832_xxaa_debug.out
+	cp $(SDK_ROOT)/components/softdevice/s132/hex/s132_nrf52_6.1.1_softdevice.hex $(OUTPUT_DIRECTORY)/s132_nrf52_6.1.1_softdevice.hex
+	mergehex --merge $(OUTPUT_DIRECTORY)/s132_nrf52_6.1.1_softdevice.hex $(OUTPUT_DIRECTORY)/nrf52832_xxaa_debug.hex \
+		-o $(OUTPUT_DIRECTORY)/$(PROJECT_NAME).hex
+	@echo Package created at: $(OUTPUT_DIRECTORY)/$(PROJECT_NAME).hex
+
 flash_with_gdb:
-	arm-none-eabi-gdb -e _build/nrf52832_xxaa_release.out -x flash.gdb
\ No newline at end of file
+	arm-none-eabi-gdb -x flash_package.gdb
path: Makefile
user: login: Gara-Dorta
body: I'd rather you make a new target, I think it's better to have two separate commands, one to flash only the release firmware and one to flash the bluetooth stack. Does that makes sense or am I missing something?
created_at: 2025-03-10T08:45:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -326,11 +326,25 @@ sdk_config:
 openocd:
 	openocd -f interface/cmsis-dap.cfg -f target/nrf52.cfg
 load_gdb:
-	arm-none-eabi-gdb -se _build/nrf52832_xxaa_debug.out -x debug.gdb
+	arm-none-eabi-gdb -se $(OUTPUT_DIRECTORY)/nrf52832_xxaa_debug.out -x debug.gdb
 logs: SHELL:=/bin/bash   # Use the bash shell for the logs target, as sh does not have the disown command 
 logs:
 	socat pty,link=/tmp/ttyvnrf,waitslave tcp:127.0.0.1:8000 & disown
 	picocom /tmp/ttyvnrf -b 115200
 
+package_release_app:
+	@echo Packaging: s132_nrf52_6.1.1_softdevice.hex + $(OUTPUT_DIRECTORY)/nrf52832_xxaa_release.out
+	cp $(SDK_ROOT)/components/softdevice/s132/hex/s132_nrf52_6.1.1_softdevice.hex $(OUTPUT_DIRECTORY)/s132_nrf52_6.1.1_softdevice.hex
+	mergehex --merge $(OUTPUT_DIRECTORY)/s132_nrf52_6.1.1_softdevice.hex $(OUTPUT_DIRECTORY)/nrf52832_xxaa_release.hex \
+		-o $(OUTPUT_DIRECTORY)/$(PROJECT_NAME).hex
+	@echo Package created at: $(OUTPUT_DIRECTORY)/$(PROJECT_NAME).hex
+
+package_debug_app:
path: Makefile
user: login: Gara-Dorta
body: Can you please add these two commands to the README too?
created_at: 2025-03-10T08:49:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -326,11 +326,25 @@ sdk_config:
 openocd:
 	openocd -f interface/cmsis-dap.cfg -f target/nrf52.cfg
 load_gdb:
-	arm-none-eabi-gdb -se _build/nrf52832_xxaa_debug.out -x debug.gdb
+	arm-none-eabi-gdb -se $(OUTPUT_DIRECTORY)/nrf52832_xxaa_debug.out -x debug.gdb
 logs: SHELL:=/bin/bash   # Use the bash shell for the logs target, as sh does not have the disown command 
 logs:
 	socat pty,link=/tmp/ttyvnrf,waitslave tcp:127.0.0.1:8000 & disown
 	picocom /tmp/ttyvnrf -b 115200
 
+package_release_app:
path: Makefile
user: login: harshilbhatt2001
body: Yes, that's correct. 
created_at: 2025-03-10T10:17:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -326,11 +326,25 @@ sdk_config:
 openocd:
 	openocd -f interface/cmsis-dap.cfg -f target/nrf52.cfg
 load_gdb:
-	arm-none-eabi-gdb -se _build/nrf52832_xxaa_debug.out -x debug.gdb
+	arm-none-eabi-gdb -se $(OUTPUT_DIRECTORY)/nrf52832_xxaa_debug.out -x debug.gdb
 logs: SHELL:=/bin/bash   # Use the bash shell for the logs target, as sh does not have the disown command 
 logs:
 	socat pty,link=/tmp/ttyvnrf,waitslave tcp:127.0.0.1:8000 & disown
 	picocom /tmp/ttyvnrf -b 115200
 
+package_release_app:
+	@echo Packaging: s132_nrf52_6.1.1_softdevice.hex + $(OUTPUT_DIRECTORY)/nrf52832_xxaa_release.out
path: Makefile
user: login: Gara-Dorta
body: Can you also add a link in the README of how (from where) to download the bluetooth stack file?
created_at: 2025-03-11T08:04:29Z
author_association: CONTRIBUTOR

PR comments:
user: login: harshilbhatt2001
created_at: 2025-03-11T17:49:47Z
body: > I thought a bit more about it and I didn't mention that for PR please keep it at one logical change per PR. This one is doing two changes:
> 
>     1. Adding support for nix
> 
>     2. Adding targets to flash the bluetooth stack
> 
> 
> So this should have been 2 PRs. For this one, I'm fine to leave it as is but for future ones please keep it to one change. This is what I meant when I said in discord to please write small PRs.

This should just include the nix setup now. 
author_association: CONTRIBUTOR
user: login: Gara-Dorta
created_at: 2025-03-12T08:33:59Z
body: As with the other PR, I did a squash merge here. Let me know if you hit any issues because of that.
author_association: CONTRIBUTOR