-
-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for Arduino, Teensy 4 and 4.1
Added lots of changes to allow the stack to build without threading under the Arduino IDE. Added CAN plugin for Teensy hardware using the FlexCAN T4 api. Added single threaded CANHardwareInterface option. Changed dynamic casts to static casts to completely eliminate rtti, which shouldn't be an issue since the type can be inferred in those cases where we were using dynamic_pointer_cast. Added preprocessor checks to optinally compile out all threading. Added a script to automatically generate an arduino library from the content of the repo. Added Arduino example files.
- Loading branch information
Showing
40 changed files
with
17,581 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
#include <isobus.hpp> | ||
|
||
#include "ObjectPool.cpp" | ||
|
||
using namespace isobus; | ||
|
||
auto can0 = std::make_shared<FlexCANT4Plugin>(0); | ||
std::shared_ptr<InternalControlFunction> ISOBUSControlFunction = nullptr; | ||
std::shared_ptr<DiagnosticProtocol> ISOBUSDiagnostics = nullptr; | ||
std::shared_ptr<VirtualTerminalClient> ExampleVirtualTerminalClient = nullptr; | ||
std::shared_ptr<void> softKeyListener = nullptr; | ||
std::shared_ptr<void> buttonListener = nullptr; | ||
|
||
// A log sink for the CAN stack | ||
class CustomLogger : public isobus::CANStackLogger | ||
{ | ||
public: | ||
void sink_CAN_stack_log(CANStackLogger::LoggingLevel level, const std::string &text) override | ||
{ | ||
switch (level) | ||
{ | ||
case LoggingLevel::Debug: | ||
{ | ||
Serial.print("[Debug]: "); | ||
} | ||
break; | ||
|
||
case LoggingLevel::Info: | ||
{ | ||
Serial.print("[Info]: "); | ||
} | ||
break; | ||
|
||
case LoggingLevel::Warning: | ||
{ | ||
Serial.print("[Warning]: "); | ||
} | ||
break; | ||
|
||
case LoggingLevel::Error: | ||
{ | ||
Serial.print("[Error]: "); | ||
} | ||
break; | ||
|
||
case LoggingLevel::Critical: | ||
{ | ||
Serial.print("[Critical]: "); | ||
} | ||
break; | ||
} | ||
Serial.println(text.c_str()); | ||
} | ||
}; | ||
|
||
static CustomLogger logger; | ||
|
||
// This callback will provide us with event driven notifications of button presses from the stack | ||
void handleVTKeyEvents(const VirtualTerminalClient::VTKeyEvent &event) | ||
{ | ||
static std::uint32_t exampleNumberOutput = 214748364; // In the object pool the output number has an offset of -214748364 so we use this to represent 0. | ||
|
||
switch (event.keyEvent) | ||
{ | ||
case VirtualTerminalClient::KeyActivationCode::ButtonUnlatchedOrReleased: | ||
{ | ||
switch (event.objectID) | ||
{ | ||
case Plus_Button: | ||
{ | ||
ExampleVirtualTerminalClient->send_change_numeric_value(ButtonExampleNumber_VarNum, ++exampleNumberOutput); | ||
} | ||
break; | ||
|
||
case Minus_Button: | ||
{ | ||
ExampleVirtualTerminalClient->send_change_numeric_value(ButtonExampleNumber_VarNum, --exampleNumberOutput); | ||
} | ||
break; | ||
|
||
case alarm_SoftKey: | ||
{ | ||
ExampleVirtualTerminalClient->send_change_active_mask(example_WorkingSet, example_AlarmMask); | ||
} | ||
break; | ||
|
||
case acknowledgeAlarm_SoftKey: | ||
{ | ||
ExampleVirtualTerminalClient->send_change_active_mask(example_WorkingSet, mainRunscreen_DataMask); | ||
} | ||
break; | ||
|
||
default: | ||
break; | ||
} | ||
} | ||
break; | ||
|
||
default: | ||
break; | ||
} | ||
} | ||
|
||
void setup() { | ||
// put your setup code here, to run once: | ||
Serial.begin(115200); | ||
CANStackLogger::set_can_stack_logger_sink(&logger); | ||
CANStackLogger::set_log_level(isobus::CANStackLogger::LoggingLevel::Debug); | ||
// Optional, add delay() here to give you time to connect to the serial logger | ||
CANHardwareInterface::set_number_of_can_channels(1); | ||
CANHardwareInterface::assign_can_channel_frame_handler(0, can0); | ||
CANHardwareInterface::start(); | ||
CANHardwareInterface::update(); | ||
|
||
NAME deviceNAME(0); | ||
// Make sure you change these for your device | ||
// This is an example device that is using a manufacturer code that is currently unused at time of writing | ||
deviceNAME.set_arbitrary_address_capable(true); | ||
deviceNAME.set_industry_group(0); | ||
deviceNAME.set_device_class(0); | ||
deviceNAME.set_function_code(static_cast<std::uint8_t>(isobus::NAME::Function::SteeringControl)); | ||
deviceNAME.set_identity_number(2); | ||
deviceNAME.set_ecu_instance(0); | ||
deviceNAME.set_function_instance(0); | ||
deviceNAME.set_device_class_instance(0); | ||
deviceNAME.set_manufacturer_code(64); | ||
// Change 0x81 to be your preferred address, but 0x81 is the base arbitrary address | ||
ISOBUSControlFunction = InternalControlFunction::create(deviceNAME, 0x81, 0); | ||
ISOBUSDiagnostics = std::make_shared<DiagnosticProtocol>(ISOBUSControlFunction); | ||
ISOBUSDiagnostics->initialize(); | ||
|
||
// Change these to be specific to your device | ||
ISOBUSDiagnostics->set_product_identification_brand("Arduino"); | ||
ISOBUSDiagnostics->set_product_identification_code("123456789"); | ||
ISOBUSDiagnostics->set_product_identification_model("Example"); | ||
ISOBUSDiagnostics->set_software_id_field(0, "0.0.1"); | ||
ISOBUSDiagnostics->set_ecu_id_field(DiagnosticProtocol::ECUIdentificationFields::HardwareID, "Hardware ID"); | ||
ISOBUSDiagnostics->set_ecu_id_field(DiagnosticProtocol::ECUIdentificationFields::Location, "The Aether"); | ||
ISOBUSDiagnostics->set_ecu_id_field(DiagnosticProtocol::ECUIdentificationFields::ManufacturerName, "None"); | ||
ISOBUSDiagnostics->set_ecu_id_field(DiagnosticProtocol::ECUIdentificationFields::PartNumber, "1234"); | ||
ISOBUSDiagnostics->set_ecu_id_field(DiagnosticProtocol::ECUIdentificationFields::SerialNumber, "1"); | ||
ISOBUSDiagnostics->set_ecu_id_field(DiagnosticProtocol::ECUIdentificationFields::Type, "AgISOStack"); | ||
|
||
// Set up virtual terminal client | ||
const NAMEFilter filterVirtualTerminal(NAME::NAMEParameters::FunctionCode, static_cast<std::uint8_t>(NAME::Function::VirtualTerminal)); | ||
const std::vector<NAMEFilter> vtNameFilters = { filterVirtualTerminal }; | ||
auto TestPartnerVT = PartneredControlFunction::create(0, vtNameFilters); | ||
ExampleVirtualTerminalClient = std::make_shared<VirtualTerminalClient>(TestPartnerVT, ISOBUSControlFunction); | ||
ExampleVirtualTerminalClient->set_object_pool(0, VirtualTerminalClient::VTVersion::Version3, VT3TestPool, sizeof(VT3TestPool), "AIS1"); | ||
softKeyListener = ExampleVirtualTerminalClient->add_vt_soft_key_event_listener(handleVTKeyEvents); | ||
buttonListener = ExampleVirtualTerminalClient->add_vt_button_event_listener(handleVTKeyEvents); | ||
ExampleVirtualTerminalClient->initialize(false); | ||
} | ||
|
||
void loop() { | ||
// put your main code here, to run repeatedly: | ||
ISOBUSDiagnostics->update(); // Update diagnostics interface | ||
ExampleVirtualTerminalClient->update(); // Update VT client | ||
CANHardwareInterface::update(); // Update CAN stack | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import os, shutil, fnmatch, glob, fileinput | ||
from datetime import datetime | ||
|
||
def copyfiles(srcdir, dstdir, filepattern): | ||
def failed(exc): | ||
raise exc | ||
|
||
for dirpath, dirs, files in os.walk(srcdir, topdown=True, onerror=failed): | ||
for file in fnmatch.filter(files, filepattern): | ||
if "test" not in dirpath and "examples" not in dirpath and "CMakeFiles" not in dirpath: | ||
shutil.copy2(os.path.join(dirpath, file), dstdir) | ||
print("Copied ", file) | ||
|
||
def write_isobus_hpp(includefiles, f): | ||
f.write( | ||
f"""/******************************************************************************* | ||
** @file isobus.hpp | ||
** @author Automatic Code Generation | ||
** @date {datetime.today().strftime("%B %d, %Y")} at {datetime.now().strftime("%H:%M:%S")} | ||
** @brief Includes all important files in the AgIsoStack library. | ||
** | ||
** Copyright {datetime.today().strftime("%Y")} The AgIsoStack++ Developers | ||
*******************************************************************************/ | ||
#ifndef ISOBUS_HPP | ||
#define ISOBUS_HPP | ||
""") | ||
|
||
for includefile in includefiles: | ||
f.write(f"#include <{includefile}>\n") | ||
|
||
f.write(f"\n"); | ||
f.write(f"#endif // ISOBUS_HPP\n") | ||
|
||
def write_library_properties(f): | ||
f.write( | ||
f"""name=AgIsoStack-plus-plus | ||
version=0.1.0 | ||
license=MIT | ||
author=Adrian Del Grosso <[email protected]> | ||
maintainer=Adrian Del Grosso <[email protected]> | ||
sentence=A free ISOBUS (ISO11783) and J1939 CAN Stack for Teensy. | ||
paragraph=Includes ISOBUS virtual terminal client, task controller client, and transport layer functionality. Based on the CMake AgIsoStack++ at https://github.com/Open-Agriculture/AgIsoStack-plus-plus. | ||
category=Communication | ||
architectures=teensy | ||
includes=isobus.hpp | ||
url=https://github.com/Open-Agriculture/AgIsoStack-plus-plus | ||
""") | ||
|
||
def fixup_header_paths(fileName): | ||
f = open(fileName,'r') | ||
filedata = f.read() | ||
f.close() | ||
|
||
newdata = filedata.replace("isobus/isobus/","") | ||
newdata = newdata.replace("isobus/utility/","") | ||
newdata = newdata.replace("isobus/hardware_integration/","") | ||
|
||
f = open(fileName,'w') | ||
f.write(newdata) | ||
f.close() | ||
|
||
arduinoLibPath = "arduino_library/" | ||
sourceDir = "src" | ||
sourcePath = arduinoLibPath + sourceDir | ||
|
||
if os.path.exists(arduinoLibPath) and os.path.isdir(arduinoLibPath): | ||
shutil.rmtree(arduinoLibPath) | ||
print("Removed existing arduino library") | ||
|
||
os.mkdir(arduinoLibPath) | ||
print("Created directory ", arduinoLibPath) | ||
|
||
os.mkdir(sourcePath) | ||
print("Created directory ", sourcePath) | ||
|
||
print("Copying source files to library") | ||
|
||
copyfiles(".", sourcePath, "*.cpp") | ||
copyfiles(".", sourcePath, "*.hpp") | ||
copyfiles(".", sourcePath, "*.tpp") | ||
|
||
print("Pruning unneeded files for Arduino platform") | ||
|
||
filePruneList = [ | ||
"iop_file_interface.cpp", | ||
"iop_file_interface.hpp", | ||
"available_can_drivers.hpp", | ||
"canal.h", | ||
"canal_a.h", | ||
"innomaker_usb2can_windows_plugin.hpp", | ||
"InnoMakerUsb2CanLib.h", | ||
"libusb.h", | ||
"mac_can_pcan_plugin.hpp", | ||
"mcp2515_can_interface.hpp", | ||
"pcan_basic_windows_plugin.hpp", | ||
"PCANBasic.h", | ||
"PCBUSB.h", | ||
"socket_can_interface.hpp", | ||
"spi_hardware_plugin.hpp", | ||
"spi_interface_esp.hpp", | ||
"spi_transaction_frame.hpp", | ||
"toucan_vscp_canal.hpp", | ||
"twai_plugin.hpp", | ||
"virtual_can_plugin.hpp", | ||
"innomaker_usb2can_windows_plugin.cpp", | ||
"mac_can_pcan_plugin.cpp", | ||
"mcp2515_can_interface.cpp", | ||
"pcan_basic_windows_plugin.cpp", | ||
"socket_can_interface.cpp", | ||
"spi_interface_esp.cpp", | ||
"spi_transaction_frame.cpp", | ||
"toucan_vscp_canal.cpp", | ||
"twai_plugin.cpp", | ||
"virtual_can_plugin.cpp", | ||
"can_hardware_interface.hpp", | ||
"can_hardware_interface.cpp", | ||
"socketcand_windows_network_client.hpp", | ||
"socketcand_windows_network_client.cpp", | ||
"CMakeCXXCompilerId.cpp"] | ||
|
||
for punableFile in filePruneList: | ||
if os.path.exists(os.path.join(sourcePath, punableFile)) and os.path.isfile(os.path.join(sourcePath, punableFile)): | ||
os.remove(os.path.join(sourcePath, punableFile)) | ||
print("Pruning file ", punableFile) | ||
|
||
print("Generating isobus.hpp from files in " + "./" + sourcePath + "/*.hpp") | ||
headers = [os.path.normpath(i) for i in glob.glob("./" + sourcePath + "/*.hpp")] | ||
strippedHeaders = list(map(lambda x: x.replace('arduino_library\\src\\','').replace('arduino_library/src/',''),headers)) | ||
print("Headers ", strippedHeaders) | ||
f = open(os.path.join(sourcePath, "isobus.hpp"), "w") | ||
write_isobus_hpp(strippedHeaders, f) | ||
f.close() | ||
|
||
print("Generating library.properties") | ||
f = open(os.path.join(arduinoLibPath, "library.properties"), "w") | ||
write_library_properties(f) | ||
f.close() | ||
|
||
print("Patching header file paths") | ||
for header in headers: | ||
fixup_header_paths(header) | ||
print("Patched ", header) | ||
|
||
sources = [os.path.normpath(i) for i in glob.glob("./" + sourcePath + "/*.cpp")] | ||
for source in sources: | ||
fixup_header_paths(source) | ||
print("Patched ", source) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.