Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
0c9e4c9
saving
KrishKittur Nov 16, 2025
459d583
made some fixes
KrishKittur Dec 6, 2025
f2dc181
added message encoding and decoding
KrishKittur Dec 7, 2025
083ea06
pluh
KrishKittur Jan 13, 2026
4133554
saving
KrishKittur Jan 13, 2026
89860b6
save before merge
KrishKittur Jan 15, 2026
547d07c
merged
KrishKittur Jan 15, 2026
dfb1ce0
tested on hardware
KrishKittur Jan 18, 2026
39447b6
Live parameter handling (#10)
warrenyun Jan 23, 2026
939cc0c
test script
warrenyun Jan 23, 2026
74bbc0a
Merge branch 'main' into eth-test
warrenyun Jan 23, 2026
80ac976
Feature/better cmdline parsing (#12)
KrishKittur Jan 25, 2026
133e9d0
Initial Laptracker commit - KevinLuo
KevinLuo423 Jan 25, 2026
a9061a7
buh
warrenyun Jan 29, 2026
1833943
Implementing styling suggestions
KevinLuo423 Jan 30, 2026
8c114c6
goon
warrenyun Jan 31, 2026
9442286
cmake cleanup, safe singleton destruction
warrenyun Jan 31, 2026
ccc1202
Change all std::cout and std::cerr to spdlog
KevinLuo423 Jan 31, 2026
ff5b6d1
Eradicate std::cout and std:cerr
KevinLuo423 Feb 1, 2026
595f5ad
minor cleanups
warrenyun Feb 1, 2026
c9d9293
merge with main
warrenyun Feb 1, 2026
1c5afb9
Merge pull request #16 from hytech-racing/eth-test
warrenyun Feb 1, 2026
73fc8de
Change warnings to errors
KevinLuo423 Feb 1, 2026
a05e770
Fix laptime initialization issues
KevinLuo423 Feb 2, 2026
479fe31
Merge remote-tracking branch 'origin/main' into refactor/use-spdlog
KevinLuo423 Feb 2, 2026
af9c055
merge + cleanup
warrenyun Feb 5, 2026
7ee949f
Merge pull request #11 from hytech-racing/feature/can-communication
warrenyun Feb 5, 2026
d4ae240
readme
warrenyun Feb 7, 2026
9e325c5
Merge pull request #15 from hytech-racing/refactor/use-spdlog
warrenyun Feb 10, 2026
05beef3
includes fix for foxglove server and can driver (#17)
KrishKittur Feb 11, 2026
69fd021
Implement new metrics for lap tracking and refactor lap tracking logi…
KevinLuo423 Feb 20, 2026
66b5b52
Add two comments and a space
KevinLuo423 Feb 20, 2026
411daeb
Formatting and minor delta logic changes
KevinLuo423 Feb 20, 2026
52b6865
Merge remote-tracking branch 'origin/main' into kevin/lap-tracker
KevinLuo423 Feb 20, 2026
c562029
Merge remote-tracking branch 'origin/main' into kevin/lap-tracker
KevinLuo423 Feb 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Custom libs
#################################
add_subdirectory(drivebrain_core)
# add_subdirectory(drivebrain_comms)
add_subdirectory(drivebrain_comms)

#################################
# Find packages
Expand All @@ -20,15 +20,15 @@ find_package(Protobuf REQUIRED CONFIG)
find_package(Boost REQUIRED)
find_package(spdlog REQUIRED)
find_package(mcap REQUIRED)
find_package(GTest REQUIRED)
find_package(dbcppp REQUIRED)

#################################
# Upstreams (non-conan)
#################################
FetchContent_Declare(
HT_Proto
GIT_REPOSITORY https://github.com/hytech-racing/HT_proto.git
GIT_TAG f7967e90831271f9704a406a8f777d43c03d0d0b
GIT_TAG 7cdd552750e4c34d28512fbd291367a29be722d7
)
FetchContent_MakeAvailable(HT_Proto)

Expand Down Expand Up @@ -62,6 +62,7 @@ target_link_libraries(drivebrain PUBLIC
hytech_msgs_cpp_lib
hytech_can_msgs_cpp_lib
drivebrain_core
drivebrain_comms
)

#################################
Expand All @@ -71,4 +72,4 @@ target_link_libraries(drivebrain PUBLIC
if(NOT CMAKE_CROSSCOMPILING)
enable_testing()
add_subdirectory(tests)
endif()
endif()
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
# DriveBrain Software 2026
# DriveBrain Software

This repo contains the main executable that runs on the Drivebrain embedded computer on HyTech Racing's cars. This code is deployed as a systemd service onto the car within HyTech's Raspberry Pi [NixOS description](https://github.com/hytech-racing/hytech_nixos/releases). This service handles, among other things:

* high-performance control algorithm execution from both hand written c++ and MATLAB Simulink generated code c++
* car state estimation via both hand written c++ and MATLAB Simulink generated code c++
* Vectornav INS communication via Vectornav's driver
* live telemetry and data recording of all on-car inter-board communications (CAN and Ethernet mostly)
* car-level parameter servicing of all previously listed components via the integrated Foxglove websocket server

This is an active rewrite of an older iteration of Drivebrain, which you can find [here.](https://github.com/hytech-racing/drivebrain_software)


# Development

This guide explains how to build the DriveBrain Software 2026 using the provided Docker cross-compilation environment.

Expand Down
3 changes: 2 additions & 1 deletion build_script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ cd "$build_folder"
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=../cmake/conan_toolchain.cmake \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DCMAKE_EXE_LINKER_FLAGS="-static" \

make -j

Expand Down
3 changes: 2 additions & 1 deletion conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ def build(self):
def requirements(self):
self.requires("foxglove-websocket/1.4.0", transitive_headers=True)
self.requires("protobuf/5.29.3", transitive_headers=True)
self.requires("boost/1.88.0")
self.requires("boost/1.80.0")
self.requires("spdlog/1.15.3")
self.requires("mcap/2.0.2")
self.requires("dbcppp/3.2.6")

def build_requirements(self):
if not self.settings_build.get_safe("cross_build"):
Expand Down
1,637 changes: 1,637 additions & 0 deletions dbc/hytech.dbc

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions deploy_script.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
scp build-arm/drivebrain hytech@192.168.203.1:/home/hytech/drivebrain/
scp config/config.json hytech@192.168.203.1:/home/hytech/drivebrain/config

# scp build/drivebrain nixos@192.168.203.1:/home/nixos/fake_drivebrain
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:
working_dir: /app
tty: true
stdin_open: true
command: ["/bin/bash"]
command: sleep infinity
restart: "no"

volumes:
Expand Down
101 changes: 78 additions & 23 deletions drivebrain_app/src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include "ETHSendComms.hpp"
#include "ETHRecvComms.hpp"
#include <boost/asio/io_context.hpp>
#include <chrono>
#include <csignal>
#include <exception>
#include <google/protobuf/message.h>
#include <iostream>
#include <atomic>
Expand All @@ -8,47 +12,98 @@
#include <FoxgloveServer.hpp>
#include <MCAPLogger.hpp>
#include <mcap/writer.hpp>
#include <CANComms.hpp>
#include <memory>
#include <thread>
#include <spdlog/spdlog.h>
#include <optional>
#include <filesystem>

std::atomic<bool> running = true;
std::optional<std::string> json_file;
std::optional<std::string> dbc_file;

int parse_arguments(int &argc, char* argv[]) {
int opt;

while ((opt = getopt(argc, argv, "c:d:")) != -1) {
switch (opt) {
case 'c':
if (optarg != nullptr && strlen(optarg) > 0 && std::filesystem::exists(optarg)) {
json_file = optarg;
} else {
spdlog::error("Invalid json file provided: {}", optarg);
return 1;
}
break;
case 'd':
if (optarg != nullptr && strlen(optarg) > 0 && std::filesystem::exists(optarg)) {
dbc_file = optarg;
} else {
spdlog::error("Invalid dbc file provided: {}", optarg);
return 1;
}
break;
case '?':
spdlog::error("Unknown command line option: -{}", static_cast<char>(optopt));
return 1;
default:
spdlog::error("Could not parse command line arguments.");
return 1;
}
}

if (!json_file) {
spdlog::error("Did not receive the required json file argument.");
return 1;
}

if (!dbc_file) {
spdlog::error("Did not receive the required dbc file argument.");
return 1;
}

return 0;
}

void sig_handler(int signal) {
if(signal == SIGINT) {
std::cout << "halting\n";
spdlog::info("halting");
running = false;
}
}

void get_param_task(int wait_time, core::MsgType msg) {
while(running) {
core::MCAPLogger::instance().log_msg(static_cast<core::MsgType>(msg));
std::this_thread::sleep_for((std::chrono::milliseconds(wait_time)));
int main(int argc, char* argv[]) {
// Argument Handling
int return_code = parse_arguments(argc, argv);
if (return_code != 0) {
spdlog::error("Expected Usage: ./drivebrain -c path/to/config.json -d path/to/hytech.dbc");
return return_code;
}
}

int main(int argc, char* argv[]) {

comms::CANComms primary_can("can_secondary", dbc_file.value());

core::FoxgloveServer::create(argv[1]);
core::MCAPLogger::create("recordings/", mcap::McapWriterOptions(""));
// Singleton Creation
core::FoxgloveServer::create(json_file.value());
core::MCAPLogger::create("recordings/", mcap::McapWriterOptions(""), json_file.value());
core::MCAPLogger::instance().open_new_mcap("test_1.mcap");
core::MCAPLogger::instance().init_logging();

std::shared_ptr<hytech_msgs::ACUAllData> all_data_msg = std::make_shared<hytech_msgs::ACUAllData>();

std::signal(SIGINT, sig_handler);
while(running) {
std::shared_ptr<hytech::drivebrain_speed_set_input> speed_msg = std::make_shared<hytech::drivebrain_speed_set_input>();
speed_msg->set_drivebrain_set_rpm_fl(1.0);
speed_msg->set_drivebrain_set_rpm_fr(2.0);
speed_msg->set_drivebrain_set_rpm_rl(4.0);
speed_msg->set_drivebrain_set_rpm_rr(8.0);
primary_can.send_message(speed_msg);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}


auto vel_msg = std::make_shared<hytech::velocities>();
vel_msg->set_velocity_x(1000);
vel_msg->set_velocity_y(10000);

auto acu_data = std::make_shared<hytech_msgs::ACUAllData>();
acu_data->set_max_cell_temp_id(676767);

std::thread t1(get_param_task, 20, vel_msg);
std::thread t2(get_param_task, 40, acu_data);

if(t1.joinable()) t1.join();
if(t2.joinable()) t2.join();
core::MCAPLogger::instance().close_active_mcap();

core::MCAPLogger::destroy();
core::FoxgloveServer::destroy();
}
6 changes: 3 additions & 3 deletions drivebrain_comms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ find_package(Boost REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(foxglove-websocket REQUIRED)
find_package(Protobuf REQUIRED CONFIG)
find_package(dbcppp REQUIRED)

add_library(drivebrain_comms
src/CANComms.cpp
# src/DBServiceImpl.cpp
# src/foxglove_server.cpp
# src/VNComms.cpp
src/ETHSendComms.cpp
)

target_include_directories(drivebrain_comms PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
Expand All @@ -24,4 +23,5 @@ target_link_libraries(drivebrain_comms PUBLIC
protobuf::libprotobuf
hytech_msgs_cpp_lib
libvncxx
dbcppp::dbcppp
)
90 changes: 90 additions & 0 deletions drivebrain_comms/include/CANComms.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <chrono>
#include <thread>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <fstream>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <errno.h>

#include "hytech.pb.h"
#include "hytech_msgs.pb.h"

#include <dbcppp/Network.h>
#include <FoxgloveServer.hpp>
#include <MCAPLogger.hpp>


namespace comms {

class CANComms {

public:

/**
* Initializes a new can socket communications interface with the specified device name.
*
* @param device_name the name of the can device trying to be initialized
* @param dbc_file_path the file path for the dbc
*/
CANComms(const std::string &device_name, const std::string &dbc_file_path);

/**
* Sends a prootbuf message over CAN
*
* @param message the message to be sent
*/
void send_message(std::shared_ptr<google::protobuf::Message> message);

private:

/**
* Receives and unpacks a can frame.
*/
std::shared_ptr<google::protobuf::Message> _decode_can_frame(struct can_frame& frame);

/**
* Encodes a protobuf message to a CAN message
*/
int _encode_can_frame(std::shared_ptr<google::protobuf::Message> proto_message, can_frame* frame);


/**
* Receive handler that runs until the interface is destructed
*/
void _receive_handler();

/**
* Initializes the can interface, invoked by constructor.
*
* @return 0 if successful, negative error code on failure
*/
int _init(const std::string &device_name, const std::string &dbc_file_path);

/**
* Casts a can_frame struct to a protobuf message if it exists in the descriptor pool
*
* @param frame the can frame to be processed
* @return shared ptr to the protobuf message to return
*/
std::shared_ptr<google::protobuf::Message> _protobuf_message_receive(struct can_frame &frame);


public:

private:
int _socket;
can_frame _frame;
std::thread _reader_thread;
std::unordered_map<uint64_t, const dbcppp::IMessage*> _messages;
std::unordered_map<std::string, uint64_t> _names_to_can_id;
std::unique_ptr<dbcppp::INetwork> _net;

};

}
56 changes: 56 additions & 0 deletions drivebrain_comms/include/ETHRecvComms.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#pragma once

#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/bind.hpp>
#include <boost/system/detail/error_code.hpp>

#include <cstdint>
#include <memory>
#include <google/protobuf/message.h>

#include "MCAPLogger.hpp"

namespace comms {

/**
* @brief Ethernet driver class for receiving Protobuf messages
* @tparam MsgType The protobuf message type the initialized Ethernet class will receive.
*/
template<typename MsgType>
class ETHRecvComms {
public:
ETHRecvComms() = delete;
~ETHRecvComms();

/**
* Constructor for creating a new instance of the Ethernet receiving driver
*
* @param io_context The reference to the initialized io_context
* @param port The port to listen/receive messages from
* @param handler The handler function to be called on the received message. By default the message is logged to an MCAP.
*/
ETHRecvComms(boost::asio::io_context& io_context, uint16_t port,
std::function<void(std::shared_ptr<MsgType>)> handler = [](std::shared_ptr<MsgType> msg){ core::MCAPLogger::instance().log_msg(msg); });

private:
/* Deserializes the received ethernet message and logs to the MessagerLogger and state estimator */
void _handle_receive(const boost::system::error_code &error, std::size_t size);

/* Begins message receiving loop */
void _start_receive();

private:
std::array<uint8_t, 4096> _recv_buffer;
boost::asio::ip::udp::socket _socket;
boost::asio::ip::udp::endpoint _remote_endpoint;
std::shared_ptr<MsgType> _received_msg;
bool _running = false;
std::thread _output_thread;

std::function<void(std::shared_ptr<MsgType>)> _handler;
};
}

#include "ETHRecvComms.tpp"
Loading