From 90dc3729a92bbdca7be11a4906c23dda6152a2a6 Mon Sep 17 00:00:00 2001
From: Andy Sorge <124544413+asorge29@users.noreply.github.com>
Date: Sat, 28 Feb 2026 02:24:22 -0800
Subject: [PATCH 1/9] feat: service to setup can on boot
---
util/guppy_can/install_can_service.sh | 16 +++++++++++++++
util/guppy_can/setup_can.service | 11 +++++++++++
util/guppy_can/setup_can.sh | 28 +++++++++++++++++++++++++++
3 files changed, 55 insertions(+)
create mode 100755 util/guppy_can/install_can_service.sh
create mode 100755 util/guppy_can/setup_can.service
create mode 100755 util/guppy_can/setup_can.sh
diff --git a/util/guppy_can/install_can_service.sh b/util/guppy_can/install_can_service.sh
new file mode 100755
index 0000000..a27111c
--- /dev/null
+++ b/util/guppy_can/install_can_service.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Check if script is run as root
+if [[ "$EUID" -ne 0 ]]; then
+ echo "Error: This script must be run as sudo or as root."
+ echo "Usage: sudo $0"
+ exit 1
+fi
+
+set -e
+
+cp setup_can.sh /etc/setup_can.sh
+
+cp setup_can.service /etc/systemd/system/guppy_can.service
+
+systemctl enable --now guppy_can.service
\ No newline at end of file
diff --git a/util/guppy_can/setup_can.service b/util/guppy_can/setup_can.service
new file mode 100755
index 0000000..4b8d5b6
--- /dev/null
+++ b/util/guppy_can/setup_can.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Sets up can interface can0
+After=network.target
+
+[Service]
+Type=oneshot
+ExecStart=/etc/setup_can.sh
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/util/guppy_can/setup_can.sh b/util/guppy_can/setup_can.sh
new file mode 100755
index 0000000..5b73986
--- /dev/null
+++ b/util/guppy_can/setup_can.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# Check if script is run as root
+if [[ "$EUID" -ne 0 ]]; then
+ echo "Error: This script must be run as sudo or as root."
+ echo "Usage: sudo $0"
+ exit 1
+fi
+
+set -e
+
+adapter=""
+for dev in /dev/ttyACM*; do
+ [ -e "$dev" ] || continue
+ if udevadm info -a -n "$dev" | grep -qi 'canable'; then
+ adapter="$dev"
+ echo "Found CAN adapter at $adapter"
+ break
+ fi
+done
+
+if [ -z "$adapter" ]; then
+ echo "No CAN adapter found" >&2
+ exit 1
+fi
+
+slcand -o -c -s6 "$adapter"
+ip link set can0 up
\ No newline at end of file
From beb9896e520960b3f479331e6e304327924f6574 Mon Sep 17 00:00:00 2001
From: Andy Sorge <124544413+asorge29@users.noreply.github.com>
Date: Sun, 1 Mar 2026 21:53:13 -0800
Subject: [PATCH 2/9] fix: change subnet to match dvl
---
util/guppy_network/99-guppy-netplan.yaml | 4 ++--
util/guppy_network/dnsmasq.conf | 6 +++---
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/util/guppy_network/99-guppy-netplan.yaml b/util/guppy_network/99-guppy-netplan.yaml
index 820918c..e3bb4e6 100755
--- a/util/guppy_network/99-guppy-netplan.yaml
+++ b/util/guppy_network/99-guppy-netplan.yaml
@@ -8,8 +8,8 @@ network:
enp2s0:
dhcp4: no
addresses:
- - 192.168.3.1/24
+ - 192.168.194.1/24
enp1s0:
dhcp4: no
addresses:
- - 192.168.3.1/24
\ No newline at end of file
+ - 192.168.194.1/24
\ No newline at end of file
diff --git a/util/guppy_network/dnsmasq.conf b/util/guppy_network/dnsmasq.conf
index 5a1a59f..7cb468c 100755
--- a/util/guppy_network/dnsmasq.conf
+++ b/util/guppy_network/dnsmasq.conf
@@ -3,8 +3,8 @@ interface=enp2s0
bind-dynamic # dnsmasq will watch for the interfaces to come up and down, and will bind to them only when they are up
# 12 hour lease time for DHCP clients
-dhcp-range=interface:enp1s0,192.168.3.100,192.168.3.150,255.255.255.0,12h
-dhcp-range=interface:enp2s0,192.168.3.150,192.168.3.200,255.255.255.0,12h
+dhcp-range=interface:enp1s0,192.168.194.100,192.168.194.150,255.255.255.0,12h
+dhcp-range=interface:enp2s0,192.168.194.150,192.168.194.200,255.255.255.0,12h
-dhcp-option=option:router,192.168.3.1
+dhcp-option=option:router,192.168.194.1
dhcp-option=option:netmask,255.255.255.0
\ No newline at end of file
From 2b01eaadb808c6e225dcebfe4c427d35675a5c2c Mon Sep 17 00:00:00 2001
From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com>
Date: Tue, 24 Mar 2026 15:09:04 -0700
Subject: [PATCH 3/9] feat: nix flake to develop on other systems
---
.clangd | 1 +
.gitignore | 5 +++-
flake.lock | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
flake.nix | 77 +++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 166 insertions(+), 1 deletion(-)
create mode 100644 flake.lock
create mode 100644 flake.nix
diff --git a/.clangd b/.clangd
index c6f6b4f..401942d 100644
--- a/.clangd
+++ b/.clangd
@@ -1,4 +1,5 @@
CompileFlags:
+ CompilationDatabase: build/
Add:
- -std=gnu++17
- -I/opt/ros/jazzy/include
diff --git a/.gitignore b/.gitignore
index 3533592..55cb6cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -322,4 +322,7 @@ __marimo__/
# debug information files
*.dwo
-compile_commands.json
\ No newline at end of file
+compile_commands.json
+
+# NixOS stuff
+.direnv
\ No newline at end of file
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..f2052f2
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,84 @@
+{
+ "nodes": {
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nix-ros-overlay": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ },
+ "locked": {
+ "lastModified": 1774001258,
+ "narHash": "sha256-DoT1Dyg/8O1P980AqP5KbzVQu5iC5BarvgrUspS17VM=",
+ "owner": "lopsided98",
+ "repo": "nix-ros-overlay",
+ "rev": "9816ceba7fc2e340308768fdecfd7c07e8a70730",
+ "type": "github"
+ },
+ "original": {
+ "owner": "lopsided98",
+ "ref": "master",
+ "repo": "nix-ros-overlay",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1759381078,
+ "narHash": "sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "7df7ff7d8e00218376575f0acdcc5d66741351ee",
+ "type": "github"
+ },
+ "original": {
+ "owner": "lopsided98",
+ "ref": "nix-ros",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nix-ros-overlay": "nix-ros-overlay",
+ "nixpkgs": [
+ "nix-ros-overlay",
+ "nixpkgs"
+ ]
+ }
+ },
+ "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..b0799d9
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,77 @@
+{
+ inputs = {
+ nix-ros-overlay.url = "github:lopsided98/nix-ros-overlay/master";
+ nixpkgs.follows = "nix-ros-overlay/nixpkgs"; # IMPORTANT!!!
+ };
+ outputs = { self, nix-ros-overlay, nixpkgs }:
+ nix-ros-overlay.inputs.flake-utils.lib.eachDefaultSystem (system:
+ let
+ pkgs = import nixpkgs {
+ inherit system;
+ overlays = [ nix-ros-overlay.overlays.default ];
+ };
+ in {
+ devShells.default = pkgs.mkShell {
+ name = "guppy_ros";
+ packages = [
+ # non ros
+
+ # build
+ pkgs.colcon
+ pkgs.cmake
+ pkgs.clang-tools
+
+ # deps
+ pkgs.proxsuite
+ pkgs.python3Packages.pygame
+ pkgs.python3Packages.pip
+
+ # extra
+ pkgs.fastfetch
+
+ (with pkgs.rosPackages.jazzy; buildEnv {
+ # ros packages
+ paths = [
+ # ros base
+ ros-core
+ ros-base
+ rclcpp
+ rclpy
+
+ # ros msgs
+ std-msgs
+ geometry-msgs
+ sensor-msgs
+ nav-msgs
+
+ # rqt
+ rqt
+ rqt-common-plugins
+
+ # build
+ ament-cmake
+ ament-cmake-python
+ ament-lint-auto
+
+ # launch
+ launch
+ launch-ros
+ launch-xml
+
+ # deps
+ ros2-control
+ control-toolbox
+ ];
+ })
+ ];
+ shellHook = ''
+ export ROS_DOMAIN_ID=0
+ fastfetch -l ./.github/guppy.txt
+ '';
+ };
+ });
+ nixConfig = {
+ extra-substituters = [ "https://ros.cachix.org" ];
+ extra-trusted-public-keys = [ "ros.cachix.org-1:dSyZxI8geDCJrwgvCOHDoAfOm5sV1wCPjBkKL+38Rvo=" ];
+ };
+}
\ No newline at end of file
From 888d6ebdc2f003d52001b7e78fd34bead7fe2f30 Mon Sep 17 00:00:00 2001
From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com>
Date: Tue, 24 Mar 2026 16:06:39 -0700
Subject: [PATCH 4/9] fix: add deps for vendors and sim
---
flake.nix | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/flake.nix b/flake.nix
index b0799d9..6eb2dc7 100644
--- a/flake.nix
+++ b/flake.nix
@@ -28,6 +28,7 @@
# extra
pkgs.fastfetch
+ pkgs.can-utils
(with pkgs.rosPackages.jazzy; buildEnv {
# ros packages
@@ -50,6 +51,7 @@
# build
ament-cmake
+ ament-cmake-core # vectornav_msgs
ament-cmake-python
ament-lint-auto
@@ -61,6 +63,12 @@
# deps
ros2-control
control-toolbox
+ ros-gz-interfaces # gncea_autonomy
+ python-cmake-module # vecornav_msgs
+ marine-acoustic-msgs # waterlinked_dvl_driver
+ ros-gz-sim # gncea_description
+ ros-gz-bridge # sim
+ joint-state-publisher # sim
];
})
];
From 833690d6dfab7a8e460041e6e45e585f9e4cfcab Mon Sep 17 00:00:00 2001
From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com>
Date: Tue, 24 Mar 2026 16:11:26 -0700
Subject: [PATCH 5/9] chore: update readme with nix info
---
README.md | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/README.md b/README.md
index 6f0fbcb..ed5876d 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,19 @@ The included `Dockerfile` and `devcontainer.json` allow the guppy repo to be ope
To interact with the Docker contaner graphically, you can visit [localhost:6080/vnc.html](https://localhost:6080/vnc.html), or connect with a VCN client to [localhost:5901](vnc://localhost:5091)
+### Nix & NixOS
+
+The included `flake.nix` allows for development, sim, and operation to run on any x86 machine with Nix or NixOS installed.
+
+After cloning, simply run:
+```bash
+nix develop
+```
+Or if using `direnv`, run:
+```bash
+direnv allow
+```
+
## Organization
The code is broken up into several ROS 2 packages, in the [`src/`](./src/) directory:
- [**`guppy`**](./src/guppy/#readme): A metapackage that contains dependencies of all other packages, as well as bringup and launch scripts.
From 6e77fba9789c224d8676a20ba47f458dda7aea3e Mon Sep 17 00:00:00 2001
From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com>
Date: Tue, 24 Mar 2026 16:12:51 -0700
Subject: [PATCH 6/9] chore: credit nix-ros-overlay
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index ed5876d..db9439e 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,8 @@ Or if using `direnv`, run:
direnv allow
```
+This is made possible using the [nix-ros-overlay](https://github.com/lopsided98/nix-ros-overlay)
+
## Organization
The code is broken up into several ROS 2 packages, in the [`src/`](./src/) directory:
- [**`guppy`**](./src/guppy/#readme): A metapackage that contains dependencies of all other packages, as well as bringup and launch scripts.
From 3463217d7ed1bbbc19176502c7bf05e19f7d2588 Mon Sep 17 00:00:00 2001
From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com>
Date: Tue, 24 Mar 2026 21:08:54 -0700
Subject: [PATCH 7/9] fix: networking setup
---
README.md | 13 +++++++++++++
flake.nix | 1 -
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index db9439e..a1b8540 100644
--- a/README.md
+++ b/README.md
@@ -63,6 +63,19 @@ direnv allow
This is made possible using the [nix-ros-overlay](https://github.com/lopsided98/nix-ros-overlay)
+#### Nix Networking
+
+If you encounter issues, make sure to allow the UDP ports required by ROS in your `configuration.nix`:
+```nix
+networking.firewall = {
+ allowedUDPPorts = [ 7400 7401 ];
+ allowedUDPPortRanges = [
+ { from = 7410; to = 7500; }
+ ];
+};
+```
+
+
## Organization
The code is broken up into several ROS 2 packages, in the [`src/`](./src/) directory:
- [**`guppy`**](./src/guppy/#readme): A metapackage that contains dependencies of all other packages, as well as bringup and launch scripts.
diff --git a/flake.nix b/flake.nix
index 6eb2dc7..f826b99 100644
--- a/flake.nix
+++ b/flake.nix
@@ -73,7 +73,6 @@
})
];
shellHook = ''
- export ROS_DOMAIN_ID=0
fastfetch -l ./.github/guppy.txt
'';
};
From 187cddf12b7c179f888bea0ddc0d63c806fb3348 Mon Sep 17 00:00:00 2001
From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com>
Date: Tue, 24 Mar 2026 22:18:31 -0700
Subject: [PATCH 8/9] feat: send 0 to all thrusters when in disabled state
---
src/guppy_control/CMakeLists.txt | 3 ++-
.../include/guppy_control/t200_interface.hpp | 8 ++++++++
src/guppy_control/package.xml | 1 +
src/guppy_control/src/control_chassis_node.cpp | 14 +++++++++++++-
src/guppy_control/src/t200_interface.cpp | 9 ++++++++-
5 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/src/guppy_control/CMakeLists.txt b/src/guppy_control/CMakeLists.txt
index 0c93b8e..319ed5e 100644
--- a/src/guppy_control/CMakeLists.txt
+++ b/src/guppy_control/CMakeLists.txt
@@ -16,13 +16,14 @@ if(NOT CMAKE_CXX_STANDARD)
find_package(control_toolbox REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(nav_msgs REQUIRED)
+ find_package(guppy_msgs REQUIRED)
find_package(eigen3_cmake_module REQUIRED)
find_package(Eigen3)
include_directories(include)
add_executable(control_chassis src/control_chassis_node.cpp src/chassis_controller.cpp src/t200_interface.cpp)
- ament_target_dependencies(control_chassis control_toolbox geometry_msgs nav_msgs Eigen3 proxsuite)
+ ament_target_dependencies(control_chassis control_toolbox geometry_msgs nav_msgs guppy_msgs Eigen3 proxsuite)
set_source_files_properties(src/chassis_controller.cpp PROPERTIES
COMPILE_OPTIONS "-O3;-DNDEBUG;-DPROXSUITE_VECTORIZE"
)
diff --git a/src/guppy_control/include/guppy_control/t200_interface.hpp b/src/guppy_control/include/guppy_control/t200_interface.hpp
index 97b44a9..ab2f7f7 100644
--- a/src/guppy_control/include/guppy_control/t200_interface.hpp
+++ b/src/guppy_control/include/guppy_control/t200_interface.hpp
@@ -27,6 +27,9 @@ class T200Interface {
/* the can interface (like can0 or vcan0) */
std::string can_interface_;
+
+ /* are thrusters enabled */
+ bool enabled_;
/*
@brief sets up the CAN interface and socket
@@ -76,6 +79,11 @@ class T200Interface {
@return whether or not the operation was sucessful for all values
*/
bool shutdown();
+
+ /*
+ @brief sets thrusters to be enabled/disabled
+ */
+ void set_enabled(bool enabled);
};
}
diff --git a/src/guppy_control/package.xml b/src/guppy_control/package.xml
index 29a2c07..171fcfb 100644
--- a/src/guppy_control/package.xml
+++ b/src/guppy_control/package.xml
@@ -12,6 +12,7 @@
ros2_control
proxsuite
control_toolbox
+ guppy_msgs
ament_lint_auto
ament_lint_common
diff --git a/src/guppy_control/src/control_chassis_node.cpp b/src/guppy_control/src/control_chassis_node.cpp
index 0c66ce5..bb2b2a8 100644
--- a/src/guppy_control/src/control_chassis_node.cpp
+++ b/src/guppy_control/src/control_chassis_node.cpp
@@ -1,9 +1,11 @@
+#include
#include "rclcpp/rclcpp.hpp"
#include "guppy_control/chassis_controller.hpp"
#include "guppy_control/t200_interface.hpp"
#include "std_msgs/msg/float32.hpp"
#include "std_msgs/msg/float64.hpp"
+#include "guppy_msgs/msg/state.hpp"
using namespace std::chrono_literals;
using namespace t200_interface;
@@ -38,7 +40,7 @@ class ControlChassis : public rclcpp::Node {
load_params_(¶meters); // load parameters from configuration file
this->param_subscriber_ = std::make_shared(this); // subscribe to parameter change event
-
+
// parameter callback to update parameters on event
auto param_callback = [this](const rcl_interfaces::msg::ParameterEvent & parameter_event) {
@@ -105,6 +107,7 @@ class ControlChassis : public rclcpp::Node {
// setup subscriptions
odom_subscription_ = this->create_subscription("/odom",10,std::bind(&ControlChassis::odom_callback, this, std::placeholders::_1));
cmd_vel_subscription_ = this->create_subscription("/cmd_vel",10,std::bind(&ControlChassis::cmdvel_callback, this, std::placeholders::_1));
+ state_subscription_ = this->create_subscription("/state", 10, std::bind(&ControlChassis::state_callback, this, std::placeholders::_1));
auto timer_callback = [this]() -> void {
auto thrusts = controller->get_motor_thrusts();
@@ -139,11 +142,20 @@ class ControlChassis : public rclcpp::Node {
void cmdvel_callback(geometry_msgs::msg::Twist::SharedPtr msg) {
controller->update_desired_state(msg);
}
+
+ void state_callback(guppy_msgs::msg::State::SharedPtr msg) {
+ if (msg->state == guppy_msgs::msg::State::DISABLED) {
+ this->thruster_interface->set_enabled(false);
+ } else {
+ this->thruster_interface->set_enabled(true);
+ }
+ }
private:
rclcpp::Publisher::SharedPtr sim_motor_publishers_[8];
rclcpp::Subscription::SharedPtr odom_subscription_;
rclcpp::Subscription::SharedPtr cmd_vel_subscription_;
+ rclcpp::Subscription::SharedPtr state_subscription_;
rclcpp::TimerBase::SharedPtr timer_;
std::shared_ptr param_subscriber_;
diff --git a/src/guppy_control/src/t200_interface.cpp b/src/guppy_control/src/t200_interface.cpp
index ab55b55..1de0304 100644
--- a/src/guppy_control/src/t200_interface.cpp
+++ b/src/guppy_control/src/t200_interface.cpp
@@ -36,7 +36,10 @@ bool T200Interface::write(Eigen::VectorXd throttles) {
bool okay = true;
for (size_t i=0; i < n; i++) {
- okay = okay && send_to_can(can_ids[i], throttles[i]);
+ if (this->enabled_)
+ okay = okay && send_to_can(can_ids[i], throttles[i]);
+ else
+ okay = okay && send_to_can(can_ids[i], 0);
// std::cout << can_ids[i] << "\t" << throttles[i] << "\t" << okay << std::endl;
}
@@ -63,4 +66,8 @@ bool T200Interface::shutdown() {
return okay;
}
+void T200Interface::set_enabled(bool enabled) {
+ this->enabled_ = enabled;
+}
+
}
\ No newline at end of file
From 449fd1e91f6ffa4eaea7d0575515913a8be10972 Mon Sep 17 00:00:00 2001
From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com>
Date: Wed, 25 Mar 2026 01:28:38 -0700
Subject: [PATCH 9/9] feat: add rviz, include .envrc for direnv
---
.envrc | 2 ++
.gitignore | 1 -
flake.nix | 3 +++
3 files changed, 5 insertions(+), 1 deletion(-)
create mode 100644 .envrc
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..2b33721
--- /dev/null
+++ b/.envrc
@@ -0,0 +1,2 @@
+use flake
+source install/setup.bash
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 55cb6cd..0538423 100644
--- a/.gitignore
+++ b/.gitignore
@@ -216,7 +216,6 @@ activemq-data/
# Environments
.env
-.envrc
.venv
env/
venv/
diff --git a/flake.nix b/flake.nix
index f826b99..befc9a9 100644
--- a/flake.nix
+++ b/flake.nix
@@ -60,6 +60,9 @@
launch-ros
launch-xml
+ # rviz
+ rviz2
+
# deps
ros2-control
control-toolbox