### Build a single example with installed library Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/examples/README.md Build a specific example, like 'diagnostic_protocol', after installing the library. Ensure to set CMAKE_PREFIX_PATH to the installation directory. ```bash cd examples/diagnostic_protocol cmake -S . -B build -DCMAKE_PREFIX_PATH=../../install cmake --build build ``` -------------------------------- ### Build and Run Examples Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Developer Guide.rst Commands to enable example compilation and execute a specific example binary. ```bash cmake -S . -B build -DBUILD_EXAMPLES=ON cmake --build build cd build ./examples/ ``` -------------------------------- ### Install AgIsoStack++ library Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/examples/README.md Install the built AgIsoStack++ library to a specified prefix, for example, an 'install' folder. ```bash cmake --install build --prefix install ``` -------------------------------- ### Build all AgIsoStack++ examples Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/examples/README.md Use this command to build all examples from the top-level repository directory. ```bash cmake -S . -B build -DBUILD_EXAMPLES=ON cmake --build build ``` -------------------------------- ### Run a built example Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/examples/README.md Execute a compiled example. The executable name will vary based on the example built. ```bash ./build/DiagnosticProtocolExample ``` -------------------------------- ### Initialize and Start CANHardwareInterface Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/api/hardware/index.rst Create a CAN plugin, set the number of channels, assign the driver, and start the interface. Check return values for success. ```c++ std::shared_ptr canDriver = std::make_shared("can0"); isobus::CANHardwareInterface::set_number_of_can_channels(1); isobus::CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver); isobus::CANHardwareInterface::start(); ``` -------------------------------- ### Define Installation Rules Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/isobus/CMakeLists.txt Specifies the installation paths for the library binaries and header files. ```cmake install( TARGETS Isobus EXPORT isobusTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin) install( DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*.hpp") ``` -------------------------------- ### Complete AGISOstack++ Application Setup Source: https://context7.com/open-agriculture/agisostack-plus-plus/llms.txt This C++ code provides a comprehensive example of setting up and running an agricultural implement application with the AGISOstack++ library. It includes CAN hardware initialization, device naming, control function creation, and the setup of diagnostic, virtual terminal, and task controller protocols. Ensure appropriate CAN drivers are available and configured for your hardware. ```cpp #include "isobus/hardware_integration/available_can_drivers.hpp" #include "isobus/hardware_integration/can_hardware_interface.hpp" #include "isobus/isobus/can_network_manager.hpp" #include "isobus/isobus/isobus_virtual_terminal_client.hpp" #include "isobus/isobus/isobus_task_controller_client.hpp" #include "isobus/isobus/isobus_diagnostic_protocol.hpp" #include #include #include std::atomic_bool running = { true }; void signal_handler(int) { running = false; } int main() { std::signal(SIGINT, signal_handler); // 1. Setup CAN hardware driver std::shared_ptr canDriver; #if defined(ISOBUS_SOCKETCAN_AVAILABLE) canDriver = std::make_shared("can0"); #elif defined(ISOBUS_WINDOWSPCANBASIC_AVAILABLE) canDriver = std::make_shared(PCAN_USBBUS1); #endif if (!canDriver) { std::cerr << "No CAN driver available" << std::endl; return -1; } // 2. Configure and start CAN hardware interface isobus::CANHardwareInterface::set_number_of_can_channels(1); isobus::CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver); if (!isobus::CANHardwareInterface::start() || !canDriver->get_is_valid()) { std::cerr << "Failed to start CAN interface" << std::endl; return -2; } std::this_thread::sleep_for(std::chrono::milliseconds(250)); // 3. Configure device NAME isobus::NAME myNAME(0); myNAME.set_arbitrary_address_capable(true); myNAME.set_industry_group(2); // Agriculture myNAME.set_device_class(4); // Planter/Seeder myNAME.set_function_code(static_cast(isobus::NAME::Function::RateControl)); myNAME.set_identity_number(1); myNAME.set_manufacturer_code(1407); // 4. Create control functions auto myECU = isobus::CANNetworkManager::CANNetwork.create_internal_control_function(myNAME, 0); std::vector vtFilters = { isobus::NAMEFilter(isobus::NAME::NAMEParameters::FunctionCode, static_cast(isobus::NAME::Function::VirtualTerminal)) }; auto partnerVT = isobus::CANNetworkManager::CANNetwork.create_partnered_control_function(0, vtFilters); std::vector tcFilters = { isobus::NAMEFilter(isobus::NAME::NAMEParameters::FunctionCode, static_cast(isobus::NAME::Function::TaskController)) }; auto partnerTC = isobus::CANNetworkManager::CANNetwork.create_partnered_control_function(0, tcFilters); // 5. Setup diagnostic protocol auto diagnostics = std::make_unique(myECU); diagnostics->initialize(); diagnostics->set_product_identification_brand("MyImplement"); diagnostics->set_product_identification_model("Model 100"); // 6. Setup VT client (with object pool) auto vtClient = std::make_shared(partnerVT, myECU); // vtClient->set_object_pool(0, &objectPoolData, "v1.0"); // vtClient->initialize(false); // Don't spawn thread, we'll update manually // 7. Setup TC client (with DDOP) auto tcClient = std::make_shared(partnerTC, myECU, partnerVT); // auto ddop = std::make_shared(); // ... build DDOP ... // tcClient->configure(ddop, 1, 16, 1, true, false, true, false, true); // tcClient->initialize(false); // 8. Main application loop while (running) { // Update all protocols isobus::CANNetworkManager::CANNetwork.update(); diagnostics->update(); // vtClient->update(); // tcClient->update(); // Your application logic here std::this_thread::sleep_for(std::chrono::milliseconds(50)); } // 9. Cleanup // vtClient->terminate(); // tcClient->terminate(); diagnostics->terminate(); isobus::CANHardwareInterface::stop(); return 0; } ``` -------------------------------- ### Build and Run ISOBUS VT Example Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/Virtual Terminal Basics.rst These bash commands demonstrate how to configure, build, and run the ISOBUS VT example program using CMake. Ensure you are in the project's root directory. ```bash cmake -S . -B build cmake --build build cd build ./vt_example ``` -------------------------------- ### Setup CAN Hardware Interface Source: https://context7.com/open-agriculture/agisostack-plus-plus/llms.txt Configures the CAN hardware interface using SocketCAN for Linux. Ensure the CAN driver is valid before starting the CAN stack. ```cpp #include "isobus/hardware_integration/can_hardware_interface.hpp" #include "isobus/hardware_integration/socket_can_interface.hpp" // Create a CAN driver (SocketCAN for Linux example) auto canDriver = std::make_shared("can0"); // Configure the CAN hardware interface isobus::CANHardwareInterface::set_number_of_can_channels(1); isobus::CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver); // Start the CAN stack if (isobus::CANHardwareInterface::start() && canDriver->get_is_valid()) { // CAN stack is running std::cout << "CAN stack started successfully" << std::endl; } // When done, stop the interface isobus::CANHardwareInterface::stop(); ``` -------------------------------- ### Configure CMake for Transport Layer Example Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/examples/transport_layer/CMakeLists.txt Sets up the project, finds required packages, and links the necessary libraries for the transport layer example target. ```cmake cmake_minimum_required(VERSION 3.16) project(transport_layer_example) if(NOT BUILD_EXAMPLES) find_package(isobus REQUIRED) endif() find_package(Threads REQUIRED) add_executable(TransportLayerExampleTarget main.cpp) target_compile_features(TransportLayerExampleTarget PUBLIC cxx_std_11) set_target_properties(TransportLayerExampleTarget PROPERTIES CXX_EXTENSIONS OFF) target_link_libraries( TransportLayerExampleTarget PRIVATE isobus::Isobus isobus::HardwareIntegration Threads::Threads isobus::Utility) ``` -------------------------------- ### Complete PlatformIO Project Configuration Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/ESP32 PlatformIO.rst An example of a full platformio.ini file configured for an ESP32 board using the AgIsoStack++ library. ```ini ; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:denky32] platform = espressif32 board = denky32 framework = espidf lib_deps = https://github.com/Open-Agriculture/AgIsoStack-plus-plus.git build_type = debug upload_protocol = esptool monitor_speed = 115200 monitor_rts = 0 monitor_dtr = 0 monitor_filters = esp32_exception_decoder ``` -------------------------------- ### Install Include Directory Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/hardware_integration/CMakeLists.txt Installs the include directory for the HardwareIntegration module. Only files matching the '*.hpp' pattern are installed. ```cmake install( DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*.hpp") ``` -------------------------------- ### CMakeLists.txt for PGN Request Example Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/examples/pgn_requests/CMakeLists.txt Defines the project structure, finds required ISOBUS and thread dependencies, and links the necessary libraries to the executable. ```cmake cmake_minimum_required(VERSION 3.16) project(pgn_requests_example) if(NOT BUILD_EXAMPLES) find_package(isobus REQUIRED) endif() find_package(Threads REQUIRED) add_executable(PGNRequestExampleTarget main.cpp) target_compile_features(PGNRequestExampleTarget PUBLIC cxx_std_11) set_target_properties(PGNRequestExampleTarget PROPERTIES CXX_EXTENSIONS OFF) target_link_libraries( PGNRequestExampleTarget PRIVATE isobus::Isobus isobus::HardwareIntegration Threads::Threads isobus::Utility) ``` -------------------------------- ### Install Python Dependencies Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/README.md Installs the necessary Python modules required for the documentation build process. ```bash pip install -r requirements.txt ``` -------------------------------- ### CMakeLists.txt for ISOBUS VT Example Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/Virtual Terminal Basics.rst This CMake configuration sets up the project, finds necessary packages like Threads, adds the AgIsoStack-plus-plus subdirectory, and links the required libraries including Isobus, HardwareIntegration, and Utility. ```cmake cmake_minimum_required(VERSION 3.16) project( isobus_vt_tutorial VERSION 1.0 LANGUAGES CXX DESCRIPTION "An example VT client program" ) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) add_subdirectory("AgIsoStack-plus-plus") add_executable(vt_example main.cpp) target_link_libraries(isobus_hello_world PRIVATE isobus::Isobus isobus::HardwareIntegration isobus::Utility Threads::Threads) add_custom_command( TARGET vt_example POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/VT3TestPool.iop $/VT3TestPool.iop) ``` -------------------------------- ### Build AgIsoStack++ library Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/examples/README.md Build the AgIsoStack++ library from the top-level directory before building individual examples. ```bash cmake -S . -B build cmake --build build ``` -------------------------------- ### Exporting Targets and Installing CMake Configuration Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/CMakeLists.txt Installs CMake export files and configuration files for the isobus library. This allows other CMake projects to find and use the installed library. ```cmake install( EXPORT isobusTargets FILE isobusTargets.cmake NAMESPACE isobus:: DESTINATION lib/cmake/isobus) configure_file(cmake/isobusConfig.cmake.in isobusConfig.cmake @ONLY) ``` ```cmake write_basic_package_version_file(isobusConfigVersion.cmake COMPATIBILITY SameMajorVersion) ``` ```cmake install(FILES "${CMAKE_CURRENT_BINARY_DIR}/isobusConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/isobusConfigVersion.cmake" DESTINATION lib/cmake/isobus) ``` -------------------------------- ### Conditional Example Building in CMake Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/CMakeLists.txt Configures whether to build example projects. It checks the BUILD_EXAMPLES option and also verifies thread safety if the isobus library is configured for single-threaded operation. ```cmake option(BUILD_EXAMPLES "Set to ON to enable building of examples from top level" OFF) ``` ```cmake if(BUILD_EXAMPLES AND CAN_STACK_DISABLE_THREADS) message( WARNING "You are trying to build examples while the isobus library is configured in single-threaded mode, examples cannot be built in this mode. The current build configuration is (BUILD_EXAMPLES == ON AND CAN_STACK_DISABLE_THREADS == ON), please change the configuration." ) endif() ``` ```cmake if(BUILD_EXAMPLES) add_subdirectory("examples/transport_layer") add_subdirectory("examples/diagnostic_protocol") add_subdirectory("examples/pgn_requests") add_subdirectory("examples/nmea2000/fast_packet_protocol") add_subdirectory("examples/nmea2000/nmea2000_generator") add_subdirectory("examples/nmea2000/nmea2000_parser") add_subdirectory("examples/virtual_terminal/version3_object_pool") add_subdirectory("examples/virtual_terminal/aux_functions") add_subdirectory("examples/virtual_terminal/aux_inputs") if(NOT CAN_STACK_DISABLE_THREADS) # Cannot build without threading as vt server is multi-threaded add_subdirectory("examples/virtual_terminal/iop_parser_tester") add_subdirectory("examples/virtual_terminal/iop_load_tester") endif() add_subdirectory("examples/task_controller_client") add_subdirectory("examples/task_controller_server") add_subdirectory("examples/guidance") add_subdirectory("examples/seeder_example") endif() ``` -------------------------------- ### Full Example: Initialize CAN Stack and Receive Messages in C++ Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/Receiving Messages.rst A complete C++ program demonstrating how to initialize the CAN hardware interface, set up a signal handler for graceful termination, define a message callback, and enter a loop to continuously receive messages. ```c++ #include "isobus/isobus/can_network_manager.hpp" #include "isobus/hardware_integration/socket_can_interface.hpp" #include "isobus/hardware_integration/can_hardware_interface.hpp" #include "isobus/isobus/can_partnered_control_function.hpp" #include #include #include #include // This helps us handle control+c and other requests to terminate the program static std::atomic_bool running = { true }; void signal_handler(int) { running = false; } void propa_callback(const isobus::CANMessage &CANMessage, void *) { std::cout << CANMessage.get_data_length() << std::endl; } int main() { isobus::NAME myNAME(0); // Create an empty NAME std::shared_ptr myECU = nullptr; // A pointer to hold our InternalControlFunction std::shared_ptr myPartner = nullptr; // A pointer to hold a partner // Set up the hardware layer to use SocketCAN interface on channel "can0" std::shared_ptr canDriver = std::make_shared("can0"); isobus::CANHardwareInterface::set_number_of_can_channels(1); isobus::CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver); if ((!isobus::CANHardwareInterface::start()) || (!canDriver->get_is_valid())) { std::cout << "Failed to start hardware interface. The CAN driver might be invalid." << std::endl; return -1; } // Handle control+c std::signal(SIGINT, signal_handler); //! Consider customizing some of these fields, like the function code, to be representative of your device myNAME.set_arbitrary_address_capable(true); myNAME.set_industry_group(1); myNAME.set_device_class(0); myNAME.set_function_code(static_cast(isobus::NAME::Function::SteeringControl)); myNAME.set_identity_number(2); myNAME.set_ecu_instance(0); myNAME.set_function_instance(0); myNAME.set_device_class_instance(0); myNAME.set_manufacturer_code(1407); // Create our InternalControlFunction ``` -------------------------------- ### Install Build Dependencies on Linux Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Installation.rst Installs essential build tools and CMake for compiling AgIsoStack++ on Linux systems. ```bash sudo apt install build-essential cmake git ``` -------------------------------- ### Generate Doxygen Documentation Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Developer Guide.rst Commands to install dependencies and generate local API documentation. ```bash cd AgIsoStack-plus-plus ``` ```bash sudo apt install doxygen graphviz ``` ```bash sudo subscription-manager repos --enable codeready-builder-for-rhel-9-$(arch)-rpms sudo dnf install doxygen graphviz ``` ```bash doxygen doxyfile ``` -------------------------------- ### Initialize and Start CAN Hardware Interface Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/Virtual Terminal Basics.rst Initializes the CAN hardware interface with a selected driver and starts the communication. Ensure a compatible CAN driver is available and correctly configured. ```c++ #include "isobus/hardware_integration/available_can_drivers.hpp" #include "isobus/hardware_integration/can_hardware_interface.hpp" #include "isobus/isobus/can_network_manager.hpp" #include "isobus/isobus/can_partnered_control_function.hpp" #include "isobus/isobus/isobus_virtual_terminal_client.hpp" #include "isobus/isobus/isobus_virtual_terminal_client_update_helper.hpp" #include "isobus/utility/iop_file_interface.hpp" #include "objectPoolObjects.h" #include #include #include //! It is discouraged to use global variables, but it is done here for simplicity. static std::shared_ptr virtualTerminalClient = nullptr; static std::shared_ptr virtualTerminalUpdateHelper = nullptr; static std::atomic_bool running = { true }; void signal_handler(int) { running = false; } // This callback will provide us with event driven notifications of softkey presses from the stack void handle_softkey_event(const isobus::VirtualTerminalClient::VTKeyEvent &event) { if (event.keyNumber == 0) { // We have the alarm ACK code, so if we have an active alarm, acknowledge it by going back to the main runscreen virtualTerminalUpdateHelper->set_active_data_or_alarm_mask(example_WorkingSet, mainRunscreen_DataMask); } switch (event.keyEvent) { case isobus::VirtualTerminalClient::KeyActivationCode::ButtonUnlatchedOrReleased: { switch (event.objectID) { case alarm_SoftKey: { virtualTerminalUpdateHelper->set_active_data_or_alarm_mask(example_WorkingSet, example_AlarmMask); } break; case acknowledgeAlarm_SoftKey: { virtualTerminalUpdateHelper->set_active_data_or_alarm_mask(example_WorkingSet, mainRunscreen_DataMask); } break; default: break; } } break; default: break; } } // This callback will provide us with event driven notifications of button presses from the stack void handle_button_event(const isobus::VirtualTerminalClient::VTKeyEvent &event) { switch (event.keyEvent) { case isobus::VirtualTerminalClient::KeyActivationCode::ButtonUnlatchedOrReleased: case isobus::VirtualTerminalClient::KeyActivationCode::ButtonStillHeld: { switch (event.objectID) { case Plus_Button: { virtualTerminalUpdateHelper->increase_numeric_value(ButtonExampleNumber_VarNum); } break; case Minus_Button: { virtualTerminalUpdateHelper->decrease_numeric_value(ButtonExampleNumber_VarNum); } break; default: break; } } break; default: break; } } int main() { std::signal(SIGINT, signal_handler); // Automatically load the desired CAN driver based on the available drivers std::shared_ptr canDriver = nullptr; #if defined(ISOBUS_SOCKETCAN_AVAILABLE) canDriver = std::make_shared("can0"); #elif defined(ISOBUS_WINDOWSPCANBASIC_AVAILABLE) canDriver = std::make_shared(PCAN_USBBUS1); #elif defined(ISOBUS_WINDOWSINNOMAKERUSB2CAN_AVAILABLE) canDriver = std::make_shared(0); // CAN0 #elif defined(ISOBUS_MACCANPCAN_AVAILABLE) canDriver = std::make_shared(PCAN_USBBUS1); #elif defined(ISOBUS_SYS_TEC_AVAILABLE) canDriver = std::make_shared(); #endif if (nullptr == canDriver) { std::cout << "Unable to find a CAN driver. Please make sure you have one of the above drivers installed with the library." << std::endl; std::cout << "If you want to use a different driver, please add it to the list above." << std::endl; return -1; } isobus::CANHardwareInterface::set_number_of_can_channels(1); isobus::CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver); if ((!isobus::CANHardwareInterface::start()) || (!canDriver->get_is_valid())) { std::cout << "Failed to start hardware interface. The CAN driver might be invalid." << std::endl; return -2; } std::this_thread::sleep_for(std::chrono::milliseconds(250)); isobus::NAME TestDeviceNAME(0); //! Consider customizing some of these fields, like the function code, to be representative of your device TestDeviceNAME.set_arbitrary_address_capable(true); TestDeviceNAME.set_industry_group(1); TestDeviceNAME.set_device_class(0); ``` -------------------------------- ### Example of Build Error Message Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/FAQ.rst This is an example of the 'undefined reference to `pthread_create`' error that may occur during the build process. It indicates a missing link to the pthreads library. ```text /usr/bin/ld: AgIsoStack-plus-plus/socket_can/libSocketCANInterface.so: undefined reference to `pthread_create' collect2: error: ld returned 1 exit status make[2]: *** [CMakeFiles/isobus_hello_world.dir/build.make:86: isobus_hello_world] Error 1 make[1]: *** [CMakeFiles/Makefile2:285: CMakeFiles/isobus_hello_world.dir/all] Error 2 make: *** [Makefile:130: all] Error 2 ``` -------------------------------- ### CMake Build Script for Diagnostic Protocol Example Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/examples/diagnostic_protocol/CMakeLists.txt This CMake script configures the build for the Diagnostic Protocol Example. It specifies the minimum CMake version, project name, finds necessary packages like isobus and Threads, defines the executable, sets C++ standard and extensions, and links required libraries. ```cmake cmake_minimum_required(VERSION 3.16) project(diagnostic_protocol_example) if(NOT BUILD_EXAMPLES) find_package(isobus REQUIRED) endif() find_package(Threads REQUIRED) add_executable(DiagnosticProtocolExample main.cpp) target_compile_features(DiagnosticProtocolExample PUBLIC cxx_std_11) set_target_properties(DiagnosticProtocolExample PROPERTIES CXX_EXTENSIONS OFF) target_link_libraries( DiagnosticProtocolExample PRIVATE isobus::Isobus isobus::HardwareIntegration Threads::Threads isobus::Utility) ``` -------------------------------- ### Configure VT3 Example Target with CMake Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/examples/virtual_terminal/version3_object_pool/CMakeLists.txt This CMakeLists.txt file sets up the build for the VT3ExampleTarget. It finds required packages like isobus and Threads, compiles using C++11 standards, and links against necessary libraries. A post-build command is included to copy a VT3TestPool.iop file. ```cmake cmake_minimum_required(VERSION 3.16) project(vt3_version_3_object_pool_example) if(NOT BUILD_EXAMPLES) find_package(isobus REQUIRED) endif() find_package(Threads REQUIRED) add_executable(VT3ExampleTarget main.cpp ../../common/console_logger.cpp objectPoolObjects.h) target_compile_features(VT3ExampleTarget PUBLIC cxx_std_11) set_target_properties(VT3ExampleTarget PROPERTIES CXX_EXTENSIONS OFF) target_link_libraries( VT3ExampleTarget PRIVATE isobus::Isobus isobus::HardwareIntegration Threads::Threads isobus::Utility) add_custom_command( TARGET VT3ExampleTarget POST_BUILD COMMENT "Copying VT3TestPool.iop to build directory" COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/VT3TestPool.iop $/VT3TestPool.iop) ``` -------------------------------- ### Initialize Task Controller Client Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/Task Controller Client.rst Call `initialize` on the TaskControllerClient to start it. Passing `true` runs the client in a separate thread, while `false` runs it in the main thread, requiring periodic calls to `update`. ```c++ OurTaskControllerClient.initialize(true); ``` -------------------------------- ### Initialize ISOBUS Hardware and Control Function Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/The ISOBUS Hello World.rst Basic setup for a SocketCAN interface and creation of an internal control function. ```c++ int main() { isobus::NAME myNAME(0); // Create an empty NAME std::shared_ptr myECU = nullptr; // A pointer to hold our InternalControlFunction // Set up the hardware layer to use SocketCAN interface on channel "can0" std::shared_ptr canDriver = std::make_shared("can0"); isobus::CANHardwareInterface::set_number_of_can_channels(1); isobus::CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver); if ((!isobus::CANHardwareInterface::start()) || (!canDriver->get_is_valid())) { std::cout << "Failed to start hardware interface. The CAN driver might be invalid." << std::endl; return -2; } //! Consider customizing some of these fields, like the function code, to be representative of your device myNAME.set_arbitrary_address_capable(true); myNAME.set_industry_group(1); myNAME.set_device_class(0); myNAME.set_function_code(static_cast(isobus::NAME::Function::SteeringControl)); myNAME.set_identity_number(2); myNAME.set_ecu_instance(0); myNAME.set_function_instance(0); myNAME.set_device_class_instance(0); myNAME.set_manufacturer_code(1407); // Create our InternalControlFunction myECU = isobus::CANNetworkManager::CANNetwork.create_internal_control_function(myNAME, 0); return 0; } ``` -------------------------------- ### Install Git Pre-commit Hook Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/CONTRIBUTING.md Command to link the project's pre-commit hook script to the local git repository. ```bash ln -s $PWD/docs/pre-commit-hook.sh $PWD/.git/hooks/pre-commit; chmod +x docs/pre-commit-hook.sh ``` -------------------------------- ### Initialize CAN Stack and Device NAME Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/ESP32 PlatformIO.rst Boilerplate for starting the CAN hardware interface and defining the device's NAME for address claiming. ```c++ #include "esp_log.h" #include "freertos/task.h" extern "C" void app_main() { twai_general_config_t twaiConfig = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_21, GPIO_NUM_22, TWAI_MODE_NORMAL); twai_timing_config_t twaiTiming = TWAI_TIMING_CONFIG_250KBITS(); twai_filter_config_t twaiFilter = TWAI_FILTER_CONFIG_ACCEPT_ALL(); std::shared_ptr canDriver = std::make_shared(&twaiConfig, &twaiTiming, &twaiFilter); isobus::CANHardwareInterface::set_number_of_can_channels(1); isobus::CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver); isobus::CANHardwareInterface::set_periodic_update_interval(10); // Default is 4ms, but we need to adjust this for default ESP32 tick rate of 100Hz if (!isobus::CANHardwareInterface::start() || !canDriver->get_is_valid()) { ESP_LOGE("AgIsoStack", "Failed to start hardware interface, the CAN driver might be invalid"); } isobus::NAME TestDeviceNAME(0); //! Consider customizing some of these fields, like the function code, to be representative of your device TestDeviceNAME.set_arbitrary_address_capable(true); TestDeviceNAME.set_industry_group(1); TestDeviceNAME.set_device_class(0); TestDeviceNAME.set_function_code(static_cast(isobus::NAME::Function::RateControl)); TestDeviceNAME.set_identity_number(2); TestDeviceNAME.set_ecu_instance(0); TestDeviceNAME.set_function_instance(0); TestDeviceNAME.set_device_class_instance(0); TestDeviceNAME.set_manufacturer_code(1407); auto TestInternalECU = isobus::CANNetworkManager::CANNetwork.create_internal_control_function(TestDeviceNAME, 0); while (true) { // CAN stack runs in other threads. Do nothing forever. vTaskDelay(10); } isobus::CANHardwareInterface::stop(); } ``` -------------------------------- ### Utility Library CMake Configuration Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/utility/CMakeLists.txt Defines the Utility library, sets source files, manages include directories, and configures installation targets. ```cmake cmake_minimum_required(VERSION 3.16) # Set source and include directories set(UTILITY_SRC_DIR "src") set(UTILITY_INCLUDE_DIR "include/isobus/utility") # Set source files set(UTILITY_SRC "system_timing.cpp" "processing_flags.cpp" "iop_file_interface.cpp" "platform_endianness.cpp") # Prepend the source directory path to all the source files prepend(UTILITY_SRC ${UTILITY_SRC_DIR} ${UTILITY_SRC}) # Set the include files set(UTILITY_INCLUDE "system_timing.hpp" "processing_flags.hpp" "iop_file_interface.hpp" "to_string.hpp" "platform_endianness.hpp" "event_dispatcher.hpp" "thread_synchronization.hpp") # Prepend the include directory path to all the include files prepend(UTILITY_INCLUDE ${UTILITY_INCLUDE_DIR} ${UTILITY_INCLUDE}) # Create the library from the source and include files add_library(Utility ${UTILITY_SRC} ${UTILITY_INCLUDE}) add_library(${PROJECT_NAME}::Utility ALIAS Utility) target_compile_features(Utility PUBLIC cxx_std_11) set_target_properties(Utility PROPERTIES CXX_EXTENSIONS OFF) # Specify the include directory to be exported for other moduels to use. The # PUBLIC keyword here allows other libraries or exectuables to link to this # library and use its functionality. target_include_directories( Utility PUBLIC $ $) if(CAN_STACK_DISABLE_THREADS) target_include_directories( Utility PRIVATE BEFORE $) endif() install( TARGETS Utility EXPORT isobusTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin) install( DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*.hpp") ``` -------------------------------- ### CMake Build Configuration for Seeder Example Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/examples/seeder_example/CMakeLists.txt This CMakeLists.txt file configures the build for the SeederExample executable. It specifies the C++ standard, finds required packages, defines the executable and its sources, links necessary libraries, and includes a post-build command to copy a configuration file. ```cmake cmake_minimum_required(VERSION 3.16) project(seeder_example) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT BUILD_EXAMPLES) find_package(isobus REQUIRED) endif() find_package(Threads REQUIRED) add_executable( SeederExample main.cpp ../common/console_logger.cpp vt_application.cpp vt_application.hpp seeder.cpp seeder.hpp object_pool.hpp section_control_implement_sim.hpp section_control_implement_sim.cpp) target_link_libraries( SeederExample PRIVATE isobus::Isobus isobus::HardwareIntegration Threads::Threads isobus::Utility) add_custom_command( TARGET SeederExample POST_BUILD COMMENT "Copying BasePool.iop to build directory" COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/BasePool.iop $/BasePool.iop) ``` -------------------------------- ### Build Tutorial Documentation Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/README.md Commands to build the HTML documentation on Windows and Linux platforms. ```bash ./make.bat html ``` ```bash sphinx-build -M html source build ``` -------------------------------- ### Initialize Project Environment Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/Virtual Terminal Basics.rst Commands to create a project directory and clone the AgIsoStack-plus-plus repository. ```bash mkdir vt_example cd vt_example git clone https://github.com/Open-Agriculture/AgIsoStack-plus-plus.git ``` -------------------------------- ### Initialize and Use Virtual Terminal Client Source: https://context7.com/open-agriculture/agisostack-plus-plus/llms.txt Demonstrates creating a VirtualTerminalClient, loading an object pool, registering event listeners for button presses and value changes, initializing the client, and sending various commands to the VT. Ensure the object pool data is loaded correctly and object IDs are valid for your VT implementation. ```cpp #include "isobus/isobus/isobus_virtual_terminal_client.hpp" // Create VT client with partner VT and your internal ECU auto vtClient = std::make_shared(partnerVT, myECU); // Load object pool from binary data (IOP file contents) std::vector objectPoolData; // Load your .iop file contents here vtClient->set_object_pool(0, &objectPoolData, "MyPool1"); // Version label for caching // Register event callbacks vtClient->get_vt_button_event_dispatcher().add_listener( [](const isobus::VirtualTerminalClient::VTKeyEvent &event) { std::cout << "Button " << event.objectID << " "; if (event.keyEvent == isobus::VirtualTerminalClient::KeyActivationCode::ButtonPressedOrLatched) std::cout << "pressed" << std::endl; else std::cout << "released" << std::endl; } ); vtClient->get_vt_change_numeric_value_event_dispatcher().add_listener( [](const isobus::VirtualTerminalClient::VTChangeNumericValueEvent &event) { std::cout << "Object " << event.objectID << " value changed to: " << event.value << std::endl; } ); // Initialize the VT client (starts state machine, optionally spawns worker thread) vtClient->initialize(true); // true = spawn worker thread // Wait for connection while (!vtClient->get_is_connected()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } // Send commands to the VT vtClient->send_change_numeric_value(outputNumberObjectID, 12345); vtClient->send_change_string_value(stringObjectID, "Hello VT!"); vtClient->send_change_active_mask(workingSetObjectID, newDataMaskObjectID); vtClient->send_hide_show_object(containerObjectID, isobus::VirtualTerminalClient::HideShowObjectCommand::ShowObject); // Cleanup vtClient->terminate(); ``` -------------------------------- ### Build the Project Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Developer Guide.rst Standard commands to configure and compile the project using CMake. ```bash cd agisostack-plus-plus ``` ```bash cmake -S . -B build ``` ```bash cmake --build build ``` -------------------------------- ### Install Hardware Integration Target Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/hardware_integration/CMakeLists.txt Installs the HardwareIntegration target, including libraries, archives, and runtime binaries. It also exports targets for use by other CMake projects. ```cmake install( TARGETS HardwareIntegration EXPORT isobusTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin) ``` -------------------------------- ### Building a Task Controller DDOP Source: https://context7.com/open-agriculture/agisostack-plus-plus/llms.txt Demonstrates the initialization of a DDOP, adding device hierarchy, section elements, process data, and value presentation, followed by binary generation. ```cpp #include "isobus/isobus/isobus_device_descriptor_object_pool.hpp" auto ddop = std::make_shared(4); // TC version 4 // Localization label (language, units, etc.) std::array locLabel = { 'e', 'n', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // Add root device object (Object ID 0) ddop->add_device( "16-Section Sprayer", // Designator "2.0.0", // Software version "SER-001", // Serial number "DDOP001", // Structure label (max 7 chars) locLabel, // Localization label {}, // Extended structure label myECU->get_NAME().get_full_name() ); // Add main device element (Object ID 1) ddop->add_device_element( "Main Device", 1, // Element number 0, // Parent: Device Object isobus::task_controller_object::DeviceElementObject::Type::Device, 1 // Object ID ); // Add boom element (Object ID 2) ddop->add_device_element( "Spray Boom", 2, // Element number 1, // Parent: Main Device Element isobus::task_controller_object::DeviceElementObject::Type::Function, 2 // Object ID ); // Add section elements (Object IDs 3-18 for 16 sections) for (std::uint16_t i = 0; i < 16; i++) { ddop->add_device_element( "Section " + std::to_string(i + 1), 100 + i, // Element number (100-115) 2, // Parent: Boom Element isobus::task_controller_object::DeviceElementObject::Type::Section, 3 + i // Object ID ); // Add section state process data for each section ddop->add_device_process_data( "Section State", 0x0024, // DDI: Actual Work State 0xFFFF, // No value presentation 0x02, // Settable 0x01, // On-change trigger 100 + i // Object ID ); } // Add value presentation for rate display ddop->add_device_value_presentation( "L/ha", // Unit designator 0, // Offset 0.001f, // Scale factor 1, // Number of decimals 200 // Object ID ); // Generate binary DDOP for upload std::vector binaryDDOP; if (ddop->generate_binary_object_pool(binaryDDOP)) { std::cout << "DDOP generated: " << binaryDDOP.size() << " bytes" << std::endl; } ``` -------------------------------- ### Project Initialization and Options Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/CMakeLists.txt Defines the project version, language, and initial build options including testing and bus monitoring. ```cmake cmake_minimum_required(VERSION 3.16) project( isobus VERSION 0.1 LANGUAGES CXX DESCRIPTION "A control function focused implementation of the major ISOBUS and J1939 transport layers" ) include(GNUInstallDirs) include(CMakePackageConfigHelpers) include(cmake/CheckHasStdThreads.cmake) # Make CTest available and the BUILD_TESTING option (OFF by default) option(BUILD_TESTING "Set to ON to enable building of tests from top level" OFF) include(CTest) if(BUILD_TESTING AND NOT MSVC AND NOT APPLE) # Set --coverage flag for gcovr (SonarCloud) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") endif() if(ESP_PLATFORM) add_compile_options(-Wall -Wextra) elseif(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() option(DISABLE_BUSLOAD_MONITORING "Disable CAN bus load monitoring" OFF) option( CAN_STACK_DISABLE_THREADS "Set to ON to disable multi-threading, which removes the need for std::thread, std::mutex, and related libraries." OFF) ``` -------------------------------- ### Full Program Implementation Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/Adding a Destination.rst The complete source code including necessary includes for the destination-specific messaging tutorial. ```c++ #include "isobus/isobus/can_network_manager.hpp" #include "isobus/hardware_integration/socket_can_interface.hpp" #include "isobus/hardware_integration/can_hardware_interface.hpp" #include "isobus/isobus/can_partnered_control_function.hpp" #include #include #include #include // This helps us handle control+c and other requests to terminate the program ``` -------------------------------- ### Yoda Condition Style Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/CONTRIBUTING.md Example of the required constant-on-the-left comparison style to prevent assignment bugs. ```cpp if (5 == value) ``` -------------------------------- ### Initialize and Configure TaskControllerClient Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/Task Controller Client.rst Instantiates the client with control functions and configures it with implement capabilities via a DDOP. ```c++ #include "isobus/isobus/isobus_task_controller_client.hpp" // Create a TaskControllerClient // The parameters are, in order: // The control function for the TC // The control function used to send messages to the TC // The control function for a virtual terminal you're connected to with the VT client (optional - helps synchronize language and units in some cases) isobus::TaskControllerClient OurTaskControllerClient(PartnerTC, InternalECU, nullptr); // Configure the TaskControllerClient with our DDOP. // The parameters are, in order: // Includes support for 1 boom, 10 sections, 1 rate // Supports documentation, not supporting TC-GEO without position based control // Supports TC-GEO with position based control, not supporting peer control // Supports TC-SC section control OurTaskControllerClient.configure(myDDOP, 1, 10, 1, true, false, true, false, true); ``` -------------------------------- ### Run PlatformIO menuconfig Source: https://github.com/open-agriculture/agisostack-plus-plus/blob/main/sphinx/source/Tutorials/ESP32 PlatformIO.rst Command to launch the configuration menu for PlatformIO projects. ```bash pio run -t menuconfig ```