Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ In order to install this application,

Please see the file winbuild.bat in this directory, or use the MSI file build by github actions on any commit

### USB2CAN (8devices Korlan) Support on Windows

The DroneCAN GUI Tool now includes support for 8devices USB2CAN (Korlan) adapters on Windows.

**Setup:**
1. Connect your 8devices USB2CAN adapter
2. Run the setup script to install the required DLL:
```
python setup_usb2can.py
```
3. The USB2CAN adapter will now appear in the interface selection dropdown as "8devices USB2CAN (channel_id)"

**Features:**
- Automatic detection of USB2CAN adapters
- Native integration with DroneCAN protocol
- Full support for all GUI tool features

**Requirements:**
- 8devices USB2CAN adapter (Korlan)
- Windows 10/11 (x64 or x86)
- DLL files included in `bin/usb2can_canal_v2.0.0/`

## Installing on macOS

OSX support is a bit lacking in the way that installation doesn't create an entry in the applications menu,
Expand Down
Binary file added bin/usb2can_canal_v2.0.0/x64/Release/usb2can.dll
Binary file not shown.
Binary file added bin/usb2can_canal_v2.0.0/x64/Release/usb2can.lib
Binary file not shown.
Binary file added bin/usb2can_canal_v2.0.0/x86/Release/usb2can.dll
Binary file not shown.
Binary file added bin/usb2can_canal_v2.0.0/x86/Release/usb2can.lib
Binary file not shown.
31 changes: 30 additions & 1 deletion dronecan_gui_tool/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,36 @@ def main():
node_info.software_version.major = __version__[0]
node_info.software_version.minor = __version__[1]

node = dronecan.make_node(iface,
# Handle USB2CAN interface specification (format: "usb2can:channel")
actual_iface = iface
if iface and ':' in iface:
bustype, channel = iface.split(':', 1)
if bustype == 'usb2can':
# For USB2CAN interfaces, specify the bustype and DLL path
import platform
import os

actual_iface = channel
iface_kwargs['bustype'] = 'usb2can'

# Determine the correct DLL path based on architecture
current_dir = os.path.dirname(os.path.abspath(__file__))
gui_tool_dir = os.path.dirname(current_dir)

if platform.machine().lower() in ['amd64', 'x86_64', 'x64']:
dll_path = os.path.join(gui_tool_dir, 'bin', 'usb2can_canal_v2.0.0', 'x64', 'Release', 'usb2can.dll')
else:
dll_path = os.path.join(gui_tool_dir, 'bin', 'usb2can_canal_v2.0.0', 'x86', 'Release', 'usb2can.dll')

iface_kwargs['dll'] = dll_path
logger.info('Using USB2CAN interface: channel=%s, dll=%s', channel, dll_path)
elif bustype == 'pcan':
# PCAN interfaces should also specify bustype
actual_iface = channel
iface_kwargs['bustype'] = 'pcan'
logger.info('Using PCAN interface: channel=%s', channel)

node = dronecan.make_node(actual_iface,
node_info=node_info,
mode=dronecan.uavcan.protocol.NodeStatus().MODE_OPERATIONAL,
**iface_kwargs)
Expand Down
31 changes: 27 additions & 4 deletions dronecan_gui_tool/setup_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,34 @@ def list_ifaces():
# Windows, Mac, whatever
from PyQt5 import QtSerialPort

out = OrderedDict()
# Collect ports with priority handling for USB2CAN adapters
priority_ports = [] # USB2CAN adapters go first
regular_ports = []

for port in QtSerialPort.QSerialPortInfo.availablePorts():
if sys.platform == 'darwin':
if 'tty' in port.systemLocation():
if port.systemLocation() not in MACOS_SERIAL_PORTS_FILTER:
out[port.systemLocation()] = port.systemLocation()
regular_ports.append((port.systemLocation(), port.systemLocation()))
else:
sys_name = port.systemLocation()
sys_alpha = re.sub(r'[^a-zA-Z0-9]', '', sys_name)
description = port.description()
# show the COM port in parentheses to make it clearer which port it is
out["%s (%s)" % (description, sys_alpha)] = sys_name

# Special handling for 8devices Korlan USB2CAN adapter
# The Korlan appears in Windows as "USB2CAN converter"
if "USB2CAN converter" in description:
display_name = "8devices Korlan USB2CAN (%s)" % sys_alpha
priority_ports.append((display_name, sys_name))
else:
# show the COM port in parentheses to make it clearer which port it is
display_name = "%s (%s)" % (description, sys_alpha)
regular_ports.append((display_name, sys_name))

# Build output with priority ports first
out = OrderedDict()
for display_name, sys_name in priority_ports + regular_ports:
out[display_name] = sys_name

mifaces = _mavcan_interfaces()
mifaces += ["mcast:0", "mcast:1"]
Expand All @@ -125,6 +141,13 @@ def list_ifaces():
for interface in detect_available_configs():
if interface['interface'] == "pcan":
out[interface['channel']] = interface['channel']
elif interface['interface'] == "usb2can":
# Add USB2CAN (8devices Korlan) interfaces with descriptive name
# Store as "usb2can:channel" to specify the bustype
display_name = "8devices USB2CAN (%s)" % interface['channel']
interface_spec = "usb2can:%s" % interface['channel']
out[display_name] = interface_spec
logger.info('Added USB2CAN interface: %s -> %s', display_name, interface_spec)
except Exception as ex:
logger.warning('Could not load can interfaces: %s', ex, exc_info=True)

Expand Down
40 changes: 39 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
# Windows-specific options and hacks
#
if os.name == 'nt':
args.setdefault('install_requires', []).append('cx_Freeze ~= 6.1')
args.setdefault('install_requires', []).append('cx_Freeze >= 6.1')

if ('bdist_msi' in sys.argv) or ('build_exe' in sys.argv):
import cx_Freeze
Expand All @@ -122,6 +122,7 @@
'qtpy',
'qtconsole',
'easywebdav',
'python-can', # Add python-can to unpacked eggs (package name is python-can, import name is can)
]
unpacked_eggs_dir = os.path.join('build', 'hatched_eggs')
sys.path.insert(0, unpacked_eggs_dir)
Expand All @@ -144,6 +145,7 @@
import jupyter_client
import traitlets
import numpy
import can # Add python-can import

# Oh, Windows, never change.
missing_dlls = glob.glob(os.path.join(os.path.dirname(numpy.core.__file__), '*.dll'))
Expand All @@ -157,6 +159,41 @@
'zmq',
'pygments',
'jupyter_client',
# Add python-can and related packages
'can',
'can.interfaces',
'can.interfaces.usb2can',
# Add only the most critical missing modules
'traceback',
'token',
'threading',
'queue',
'logging',
'json',
'urllib',
'socket',
'ssl',
'datetime',
'multiprocessing',
'subprocess',
'tempfile',
'shutil',
'glob',
'pathlib',
'io',
'platform',
'importlib',
'encodings',
'enum',
'contextlib',
'configparser',
'winreg',
'msvcrt',
'email',
'csv',
'xml',
'zlib',
'zipfile',
],
'include_msvcr': True,
'include_files': [
Expand All @@ -172,6 +209,7 @@
os.path.join(unpacked_eggs_dir, os.path.dirname(jupyter_client.__file__)),
os.path.join(unpacked_eggs_dir, os.path.dirname(traitlets.__file__)),
os.path.join(unpacked_eggs_dir, os.path.dirname(numpy.__file__)),
os.path.join(unpacked_eggs_dir, os.path.dirname(can.__file__)), # Include python-can package files
] + missing_dlls,
},
'bdist_msi': {
Expand Down
51 changes: 51 additions & 0 deletions setup_usb2can.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""
USB2CAN setup script for DroneCAN GUI Tool

This script sets up the USB2CAN DLL for use with the DroneCAN GUI Tool.
It copies the appropriate DLL to a location where it can be found by python-can.
"""

import os
import shutil
import platform
import sys

def setup_usb2can_dll():
"""Copy the USB2CAN DLL to an accessible location"""
print("Setting up USB2CAN DLL for DroneCAN GUI Tool...")

# Get the current directory (should be the gui_tool root)
current_dir = os.path.dirname(os.path.abspath(__file__))

# Determine the correct DLL based on architecture
if platform.machine().lower() in ['amd64', 'x86_64', 'x64']:
dll_source = os.path.join(current_dir, 'bin', 'usb2can_canal_v2.0.0', 'x64', 'Release', 'usb2can.dll')
arch = 'x64'
else:
dll_source = os.path.join(current_dir, 'bin', 'usb2can_canal_v2.0.0', 'x86', 'Release', 'usb2can.dll')
arch = 'x86'

if not os.path.exists(dll_source):
print(f"Error: USB2CAN DLL not found at {dll_source}")
return False

# Copy to the current directory (where the script is run from)
dll_dest = os.path.join(current_dir, 'usb2can.dll')

try:
shutil.copy2(dll_source, dll_dest)
print(f"Successfully copied {arch} USB2CAN DLL to {dll_dest}")
return True
except Exception as e:
print(f"Error copying DLL: {e}")
return False

if __name__ == "__main__":
success = setup_usb2can_dll()
if success:
print("\n✅ USB2CAN setup completed successfully!")
print("You can now use 8devices USB2CAN adapters with the DroneCAN GUI Tool.")
else:
print("\n❌ USB2CAN setup failed!")
sys.exit(1)