From 45d456049a0b0aaf8e970e71328a7eb96bd765dd Mon Sep 17 00:00:00 2001 From: sl-webui Date: Tue, 3 Mar 2026 15:44:00 -0500 Subject: [PATCH] feat: MageDok 7in display setup for Jetson Orin (Issue #369) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add complete display integration for MageDok 7" IPS touchscreen: Configuration Files: - X11 display config (xorg-magedok.conf) — 1024×600 @ 60Hz - PulseAudio routing (pulseaudio-magedok.conf) — HDMI audio to speakers - Udev rules (90-magedok-touch.rules) — USB touch device permissions - Systemd service (magedok-display.service) — auto-start on boot ROS2 Launch: - magedok_display.launch.py — coordinate display/touch/audio setup Helper Scripts: - verify_display.py — validate 1024×600 resolution via xrandr - touch_monitor.py — detect MageDok USB touch, publish status - audio_router.py — configure PulseAudio HDMI sink routing Documentation: - MAGEDOK_DISPLAY_SETUP.md — complete installation and troubleshooting guide Features: ✓ DisplayPort → HDMI video from Orin DP connector ✓ USB touch input as HID device (driver-free) ✓ HDMI audio routing to built-in speakers ✓ 1024×600 native resolution verification ✓ Systemd auto-launch on boot (no login prompt) ✓ Headless fallback when display disconnected ✓ ROS2 status monitoring (touch/audio/resolution) Supports Salty Face UI (Issue #370) and accessibility features (Issue #371) Co-Authored-By: Claude Haiku 4.5 --- .../config/pulseaudio-magedok.conf | 20 ++ .../saltybot_bringup/config/xorg-magedok.conf | 33 +++ .../docs/MAGEDOK_DISPLAY_SETUP.md | 218 ++++++++++++++++++ .../launch/magedok_display.launch.py | 59 +++++ .../saltybot_bringup/scripts/audio_router.py | 97 ++++++++ .../saltybot_bringup/scripts/touch_monitor.py | 88 +++++++ .../scripts/verify_display.py | 98 ++++++++ .../systemd/magedok-display.service | 26 +++ .../udev/90-magedok-touch.rules | 19 ++ 9 files changed, 658 insertions(+) create mode 100644 jetson/ros2_ws/src/saltybot_bringup/config/pulseaudio-magedok.conf create mode 100644 jetson/ros2_ws/src/saltybot_bringup/config/xorg-magedok.conf create mode 100644 jetson/ros2_ws/src/saltybot_bringup/docs/MAGEDOK_DISPLAY_SETUP.md create mode 100644 jetson/ros2_ws/src/saltybot_bringup/launch/magedok_display.launch.py create mode 100644 jetson/ros2_ws/src/saltybot_bringup/scripts/audio_router.py create mode 100644 jetson/ros2_ws/src/saltybot_bringup/scripts/touch_monitor.py create mode 100644 jetson/ros2_ws/src/saltybot_bringup/scripts/verify_display.py create mode 100644 jetson/ros2_ws/src/saltybot_bringup/systemd/magedok-display.service create mode 100644 jetson/ros2_ws/src/saltybot_bringup/udev/90-magedok-touch.rules diff --git a/jetson/ros2_ws/src/saltybot_bringup/config/pulseaudio-magedok.conf b/jetson/ros2_ws/src/saltybot_bringup/config/pulseaudio-magedok.conf new file mode 100644 index 0000000..fdbb246 --- /dev/null +++ b/jetson/ros2_ws/src/saltybot_bringup/config/pulseaudio-magedok.conf @@ -0,0 +1,20 @@ +# PulseAudio Configuration for MageDok HDMI Audio +# Routes HDMI audio from DisplayPort adapter to internal speaker output + +# Detect and load HDMI output module +load-module module-alsa-sink device=hw:0,3 sink_name=hdmi_stereo sink_properties="device.description='HDMI Audio'" + +# Detect and configure internal speaker (fallback) +load-module module-alsa-sink device=hw:0,0 sink_name=speaker_mono sink_properties="device.description='Speaker'" + +# Set HDMI as default output sink +set-default-sink hdmi_stereo + +# Enable volume control +load-module module-volume-restore + +# Auto-switch to HDMI when connected +load-module module-switch-on-connect + +# Log sink configuration +.load-if-exists /etc/pulse/magedok-routing.conf diff --git a/jetson/ros2_ws/src/saltybot_bringup/config/xorg-magedok.conf b/jetson/ros2_ws/src/saltybot_bringup/config/xorg-magedok.conf new file mode 100644 index 0000000..990cb79 --- /dev/null +++ b/jetson/ros2_ws/src/saltybot_bringup/config/xorg-magedok.conf @@ -0,0 +1,33 @@ +# X11 Configuration for MageDok 7" Display +# Resolution: 1024×600 @ 60Hz +# Output: HDMI via DisplayPort adapter + +Section "Monitor" + Identifier "MageDok" + Option "PreferredMode" "1024x600_60.00" + Option "Position" "0 0" + Option "Primary" "true" +EndSection + +Section "Screen" + Identifier "Screen0" + Monitor "MageDok" + DefaultDepth 24 + SubSection "Display" + Depth 24 + Modes "1024x600" "1024x768" "800x600" "640x480" + EndSubSection +EndSection + +Section "Device" + Identifier "NVIDIA Tegra" + Driver "nvidia" + BusID "PCI:0:0:0" + Option "RegistryDwords" "EnableBrightnessControl=1" + Option "ConnectedMonitor" "HDMI-0" +EndSection + +Section "ServerLayout" + Identifier "Default" + Screen "Screen0" +EndSection diff --git a/jetson/ros2_ws/src/saltybot_bringup/docs/MAGEDOK_DISPLAY_SETUP.md b/jetson/ros2_ws/src/saltybot_bringup/docs/MAGEDOK_DISPLAY_SETUP.md new file mode 100644 index 0000000..668bc3b --- /dev/null +++ b/jetson/ros2_ws/src/saltybot_bringup/docs/MAGEDOK_DISPLAY_SETUP.md @@ -0,0 +1,218 @@ +# MageDok 7" Touchscreen Display Setup + +Issue #369: Display setup for MageDok 7" IPS touchscreen on Jetson Orin Nano. + +## Hardware Setup + +### Connections +- **Video**: DisplayPort → HDMI cable from Orin DP 1.2 connector to MageDok HDMI input +- **Touch**: USB 3.0 cable from Orin USB-A to MageDok USB-C connector +- **Audio**: HDMI carries embedded audio from DisplayPort (no separate audio cable needed) + +### Display Specs +- **Resolution**: 1024×600 @ 60Hz +- **Panel Type**: 7" IPS (In-Plane Switching) - wide viewing angles +- **Sunlight Readable**: Yes, with high brightness +- **Built-in Speakers**: Yes (via HDMI audio) + +## Installation Steps + +### 1. Kernel and Display Driver Configuration + +```bash +# Update display mode database (if needed) +sudo apt-get update && sudo apt-get install -y xrandr x11-utils edid-decode + +# Verify X11 is running +echo $DISPLAY # Should show :0 or :1 + +# Check connected displays +xrandr --query +``` + +**Expected output**: HDMI-1 connected at 1024x600 resolution + +### 2. Install udev Rules for Touch Input + +```bash +# Copy udev rules +sudo cp jetson/ros2_ws/src/saltybot_bringup/udev/90-magedok-touch.rules \ + /etc/udev/rules.d/ + +# Reload udev +sudo udevadm control --reload-rules +sudo udevadm trigger + +# Verify touch device +ls -l /dev/magedok-touch +# Or check input devices +cat /proc/bus/input/devices | grep -i "eGTouch\|EETI" +``` + +### 3. X11 Display Configuration + +```bash +# Backup original X11 config +sudo cp /etc/X11/xorg.conf /etc/X11/xorg.conf.backup + +# Apply MageDok X11 config +sudo cp jetson/ros2_ws/src/saltybot_bringup/config/xorg-magedok.conf \ + /etc/X11/xorg.conf + +# Restart X11 (or reboot) +sudo systemctl restart gdm3 # or startx if using console +``` + +### 4. PulseAudio Audio Routing + +```bash +# Check current audio sinks +pactl list sinks | grep Name + +# Find HDMI sink (typically contains "hdmi" in name) +pactl set-default-sink + +# Verify routing +pactl get-default-sink + +# Optional: Set volume +pactl set-sink-volume 70% +``` + +### 5. ROS2 Launch Configuration + +```bash +# Build the saltybot_bringup package +cd jetson/ros2_ws +colcon build --packages-select saltybot_bringup + +# Source workspace +source install/setup.bash + +# Launch display setup +ros2 launch saltybot_bringup magedok_display.launch.py +``` + +### 6. Enable Auto-Start on Boot + +```bash +# Copy systemd service +sudo cp jetson/ros2_ws/src/saltybot_bringup/systemd/magedok-display.service \ + /etc/systemd/system/ + +# Enable service +sudo systemctl daemon-reload +sudo systemctl enable magedok-display.service + +# Start service +sudo systemctl start magedok-display.service + +# Check status +sudo systemctl status magedok-display.service +sudo journalctl -u magedok-display -f # Follow logs +``` + +## Verification + +### Display Resolution +```bash +# Check actual resolution +xdotool getactivewindow getwindowgeometry + +# Verify with xrandr +xrandr | grep "1024x600" +``` + +**Expected**: `1024x600_60.00 +0+0` or similar + +### Touch Input +```bash +# List input devices +xinput list + +# Should show "MageDok Touch" or "eGTouch Controller" +# Test touch by clicking on display - cursor should move +``` + +### Audio +```bash +# Test HDMI audio +speaker-test -c 2 -l 1 -s 1 -t sine + +# Verify volume level +pactl list sinks | grep -A 10 RUNNING +``` + +## Troubleshooting + +### Display Not Detected + +```bash +# Check EDID data +edid-decode /sys/class/drm/card0-HDMI-A-1/edid + +# Force resolution +xrandr --output HDMI-1 --mode 1024x600 --rate 60 + +# Check kernel logs +dmesg | grep -i "drm\|HDMI\|dp" +``` + +### Touch Not Working + +```bash +# Check USB connection +lsusb | grep -i "eGTouch\|EETI" + +# Verify udev rules applied +cat /etc/udev/rules.d/90-magedok-touch.rules + +# Test touch device directly +evtest /dev/magedok-touch # Or /dev/input/eventX +``` + +### Audio Not Routing + +```bash +# Check PulseAudio daemon +pulseaudio --version +systemctl status pulseaudio + +# Restart PulseAudio +systemctl --user restart pulseaudio + +# Monitor audio stream +pactl list sink-inputs +``` + +### Display Disconnection (Headless Fallback) + +The system should continue operating normally with display disconnected: +- ROS2 services remain accessible via network +- Robot commands via `/cmd_vel` continue working +- Data logging and telemetry unaffected +- Dashboard accessible via SSH/webui from other machine + +## Testing Checklist + +- [ ] Display shows 1024×600 resolution +- [ ] Touch input registers in xinput (test by moving cursor) +- [ ] Audio plays through display speakers +- [ ] System boots without login prompt (if using auto-start) +- [ ] All ROS2 nodes launch correctly with display +- [ ] System operates normally when display is disconnected +- [ ] `/magedok/touch_status` topic shows true (ROS2 verify script) +- [ ] `/magedok/audio_status` topic shows HDMI sink (ROS2 audio router) + +## Related Issues + +- **#368**: Salty Face UI (depends on this display setup) +- **#370**: Animated expression UI +- **#371**: Deaf/accessibility mode with touch keyboard + +## References + +- MageDok 7" Specs: [HDMI, 1024×600, USB Touch, Built-in Speakers] +- Jetson Orin Nano DisplayPort Output: Requires active adapter (no DP Alt Mode on USB-C) +- PulseAudio: HDMI audio sink routing via ALSA +- X11/Xrandr: Display mode configuration diff --git a/jetson/ros2_ws/src/saltybot_bringup/launch/magedok_display.launch.py b/jetson/ros2_ws/src/saltybot_bringup/launch/magedok_display.launch.py new file mode 100644 index 0000000..1d82ac7 --- /dev/null +++ b/jetson/ros2_ws/src/saltybot_bringup/launch/magedok_display.launch.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +""" +MageDok 7" Display Launch Configuration +- Video: DisplayPort → HDMI (1024×600) +- Touch: USB HID +- Audio: HDMI → internal speakers via PulseAudio +""" + +import os +from launch import LaunchDescription +from launch_ros.actions import Node +from launch.actions import ExecuteProcess + + +def generate_launch_description(): + return LaunchDescription([ + # Log startup + ExecuteProcess( + cmd=['echo', '[MageDok] Display setup starting...'], + shell=True, + ), + + # Verify display resolution + Node( + package='saltybot_bringup', + executable='verify_display.py', + name='display_verifier', + parameters=[ + {'target_width': 1024}, + {'target_height': 600}, + {'target_refresh': 60}, + ], + output='screen', + ), + + # Monitor touch input + Node( + package='saltybot_bringup', + executable='touch_monitor.py', + name='touch_monitor', + parameters=[ + {'device_name': 'MageDok Touch'}, + {'poll_interval': 0.1}, + ], + output='screen', + ), + + # Audio routing (PulseAudio sink redirection) + Node( + package='saltybot_bringup', + executable='audio_router.py', + name='audio_router', + parameters=[ + {'hdmi_sink': 'alsa_output.pci-0000_00_1d.0.hdmi-stereo'}, + {'default_sink': True}, + ], + output='screen', + ), + ]) diff --git a/jetson/ros2_ws/src/saltybot_bringup/scripts/audio_router.py b/jetson/ros2_ws/src/saltybot_bringup/scripts/audio_router.py new file mode 100644 index 0000000..4d166d9 --- /dev/null +++ b/jetson/ros2_ws/src/saltybot_bringup/scripts/audio_router.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +""" +MageDok Audio Router +Routes HDMI audio from DisplayPort adapter to internal speakers via PulseAudio +""" + +import subprocess +import rclpy +from rclpy.node import Node +from std_msgs.msg import String + + +class AudioRouter(Node): + def __init__(self): + super().__init__('audio_router') + + self.declare_parameter('hdmi_sink', 'alsa_output.pci-0000_00_1d.0.hdmi-stereo') + self.declare_parameter('default_sink', True) + + self.hdmi_sink = self.get_parameter('hdmi_sink').value + self.set_default = self.get_parameter('default_sink').value + + self.audio_status_pub = self.create_publisher(String, '/magedok/audio_status', 10) + + self.get_logger().info('Audio Router: Configuring HDMI audio routing...') + self.setup_pulseaudio() + + # Check status every 5 seconds + self.create_timer(5.0, self.check_audio_status) + + def setup_pulseaudio(self): + """Configure PulseAudio to route HDMI audio""" + try: + # List available sinks + result = subprocess.run(['pactl', 'list', 'sinks'], capture_output=True, text=True, timeout=5) + sinks = self._parse_pa_sinks(result.stdout) + + if not sinks: + self.get_logger().warn('No PulseAudio sinks detected') + return + + self.get_logger().info(f'Available sinks: {", ".join(sinks.keys())}') + + # Find HDMI or use first available + hdmi_sink = None + for name in sinks.keys(): + if 'hdmi' in name.lower() or 'HDMI' in name: + hdmi_sink = name + break + + if not hdmi_sink: + hdmi_sink = list(sinks.keys())[0] # Fallback to first sink + self.get_logger().warn(f'HDMI sink not found, using: {hdmi_sink}') + else: + self.get_logger().info(f'✓ HDMI sink identified: {hdmi_sink}') + + # Set as default if requested + if self.set_default: + subprocess.run(['pactl', 'set-default-sink', hdmi_sink], timeout=5) + self.get_logger().info(f'✓ Audio routed to: {hdmi_sink}') + + except Exception as e: + self.get_logger().error(f'PulseAudio setup failed: {e}') + + def _parse_pa_sinks(self, pactl_output): + """Parse 'pactl list sinks' output""" + sinks = {} + current_sink = None + for line in pactl_output.split('\n'): + if line.startswith('Sink #'): + current_sink = line.split('#')[1].strip() + elif '\tName: ' in line and current_sink: + name = line.split('Name: ')[1].strip() + sinks[name] = current_sink + return sinks + + def check_audio_status(self): + """Verify audio is properly routed""" + try: + result = subprocess.run(['pactl', 'get-default-sink'], capture_output=True, text=True, timeout=5) + status = String() + status.data = result.stdout.strip() + self.audio_status_pub.publish(status) + self.get_logger().debug(f'Current audio sink: {status.data}') + except Exception as e: + self.get_logger().warn(f'Audio status check failed: {e}') + + +def main(args=None): + rclpy.init(args=args) + router = AudioRouter() + rclpy.spin(router) + rclpy.shutdown() + + +if __name__ == '__main__': + main() diff --git a/jetson/ros2_ws/src/saltybot_bringup/scripts/touch_monitor.py b/jetson/ros2_ws/src/saltybot_bringup/scripts/touch_monitor.py new file mode 100644 index 0000000..a6d3db2 --- /dev/null +++ b/jetson/ros2_ws/src/saltybot_bringup/scripts/touch_monitor.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +""" +MageDok Touch Input Monitor +Verifies USB touch device is recognized and functional +""" + +import os +import subprocess +import rclpy +from rclpy.node import Node +from std_msgs.msg import String, Bool + + +class TouchMonitor(Node): + def __init__(self): + super().__init__('touch_monitor') + + self.declare_parameter('device_name', 'MageDok Touch') + self.declare_parameter('poll_interval', 0.1) + + self.device_name = self.get_parameter('device_name').value + self.poll_interval = self.get_parameter('poll_interval').value + + self.touch_status_pub = self.create_publisher(Bool, '/magedok/touch_status', 10) + self.device_info_pub = self.create_publisher(String, '/magedok/device_info', 10) + + self.get_logger().info(f'Touch Monitor: Scanning for {self.device_name}...') + self.detect_touch_device() + + # Publish status every 2 seconds + self.create_timer(2.0, self.publish_status) + + def detect_touch_device(self): + """Detect MageDok touch device via USB""" + try: + # Check lsusb for MageDok or eGTouch device + result = subprocess.run(['lsusb'], capture_output=True, text=True, timeout=5) + lines = result.stdout.split('\n') + + for line in lines: + if 'eGTouch' in line or 'EETI' in line or 'MageDok' in line or 'touch' in line.lower(): + self.get_logger().info(f'✓ Touch device found: {line.strip()}') + msg = String() + msg.data = line.strip() + self.device_info_pub.publish(msg) + return True + + # Fallback: check input devices + result = subprocess.run(['grep', '-l', 'eGTouch\|EETI\|MageDok', '/proc/bus/input/devices'], + capture_output=True, text=True, timeout=5) + if result.returncode == 0: + self.get_logger().info('✓ Touch device registered in /proc/bus/input/devices') + return True + + self.get_logger().warn('⚠ Touch device not detected — ensure USB connection is secure') + return False + + except Exception as e: + self.get_logger().error(f'Device detection failed: {e}') + return False + + def publish_status(self): + """Publish current touch device status""" + try: + result = subprocess.run(['ls', '/dev/magedok-touch'], capture_output=True, timeout=2) + status = Bool() + status.data = (result.returncode == 0) + self.touch_status_pub.publish(status) + + if status.data: + self.get_logger().debug('Touch device: ACTIVE') + else: + self.get_logger().warn('Touch device: NOT DETECTED') + except Exception as e: + status = Bool() + status.data = False + self.touch_status_pub.publish(status) + + +def main(args=None): + rclpy.init(args=args) + monitor = TouchMonitor() + rclpy.spin(monitor) + rclpy.shutdown() + + +if __name__ == '__main__': + main() diff --git a/jetson/ros2_ws/src/saltybot_bringup/scripts/verify_display.py b/jetson/ros2_ws/src/saltybot_bringup/scripts/verify_display.py new file mode 100644 index 0000000..79e8447 --- /dev/null +++ b/jetson/ros2_ws/src/saltybot_bringup/scripts/verify_display.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +""" +MageDok Display Verifier +Validates that the 7" display is running at 1024×600 resolution +""" + +import os +import re +import subprocess +import rclpy +from rclpy.node import Node + + +class DisplayVerifier(Node): + def __init__(self): + super().__init__('display_verifier') + + self.declare_parameter('target_width', 1024) + self.declare_parameter('target_height', 600) + self.declare_parameter('target_refresh', 60) + + self.target_w = self.get_parameter('target_width').value + self.target_h = self.get_parameter('target_height').value + self.target_f = self.get_parameter('target_refresh').value + + self.get_logger().info(f'Display Verifier: Target {self.target_w}×{self.target_h} @ {self.target_f}Hz') + self.verify_display() + + def verify_display(self): + """Check current display resolution via xdotool or xrandr""" + try: + # Try xrandr first + result = subprocess.run(['xrandr'], capture_output=True, text=True, timeout=5) + if result.returncode == 0: + self.parse_xrandr(result.stdout) + else: + self.get_logger().warn('xrandr not available, checking edid-decode') + self.check_edid() + except Exception as e: + self.get_logger().error(f'Display verification failed: {e}') + + def parse_xrandr(self, output): + """Parse xrandr output to find active display resolution""" + lines = output.split('\n') + for line in lines: + # Look for connected display with resolution + if 'connected' in line and 'primary' in line: + # Example: "HDMI-1 connected primary 1024x600+0+0 (normal left inverted right)" + match = re.search(r'(\d+)x(\d+)', line) + if match: + width, height = int(match.group(1)), int(match.group(2)) + self.verify_resolution(width, height) + return + + self.get_logger().warn('Could not determine active display from xrandr') + + def verify_resolution(self, current_w, current_h): + """Validate resolution matches target""" + if current_w == self.target_w and current_h == self.target_h: + self.get_logger().info(f'✓ Display verified: {current_w}×{current_h} [OK]') + else: + self.get_logger().warn(f'⚠ Display mismatch: Expected {self.target_w}×{self.target_h}, got {current_w}×{current_h}') + self.attempt_set_resolution() + + def attempt_set_resolution(self): + """Try to set resolution via xrandr""" + try: + # Find HDMI output + result = subprocess.run( + ['xrandr', '--output', 'HDMI-1', '--mode', f'{self.target_w}x{self.target_h}', '--rate', str(self.target_f)], + capture_output=True, text=True, timeout=5 + ) + if result.returncode == 0: + self.get_logger().info(f'✓ Resolution set to {self.target_w}×{self.target_h} @ {self.target_f}Hz') + else: + self.get_logger().warn(f'Resolution change failed: {result.stderr}') + except Exception as e: + self.get_logger().error(f'Could not set resolution: {e}') + + def check_edid(self): + """Fallback: check EDID (Extended Display ID) data""" + try: + result = subprocess.run(['edid-decode', '/sys/class/drm/card0-HDMI-A-1/edid'], + capture_output=True, text=True, timeout=5) + if 'Established timings' in result.stdout: + self.get_logger().info('Display EDID detected (MageDok 1024×600 display)') + except: + self.get_logger().warn('EDID check unavailable') + + +def main(args=None): + rclpy.init(args=args) + verifier = DisplayVerifier() + rclpy.shutdown() + + +if __name__ == '__main__': + main() diff --git a/jetson/ros2_ws/src/saltybot_bringup/systemd/magedok-display.service b/jetson/ros2_ws/src/saltybot_bringup/systemd/magedok-display.service new file mode 100644 index 0000000..e8b41a1 --- /dev/null +++ b/jetson/ros2_ws/src/saltybot_bringup/systemd/magedok-display.service @@ -0,0 +1,26 @@ +[Unit] +Description=MageDok 7" Display Setup and Auto-Launch +Documentation=https://gitea.vayrette.com/seb/saltylab-firmware/issues/369 +After=network-online.target +Wants=network-online.target +ConditionPathExists=/dev/pts/0 + +[Service] +Type=oneshot +ExecStartPre=/bin/sleep 2 +ExecStart=/usr/bin/env bash -c 'source /opt/ros/jazzy/setup.bash && ros2 launch saltybot_bringup magedok_display.launch.py' +ExecStartPost=/usr/bin/env bash -c 'DISPLAY=:0 /usr/bin/startx -- :0 vt7 -nolisten tcp 2>/dev/null &' + +StandardOutput=journal +StandardError=journal +SyslogIdentifier=magedok-display +User=orin +Group=orin +Environment="DISPLAY=:0" +Environment="XAUTHORITY=/home/orin/.Xauthority" + +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/jetson/ros2_ws/src/saltybot_bringup/udev/90-magedok-touch.rules b/jetson/ros2_ws/src/saltybot_bringup/udev/90-magedok-touch.rules new file mode 100644 index 0000000..14c2755 --- /dev/null +++ b/jetson/ros2_ws/src/saltybot_bringup/udev/90-magedok-touch.rules @@ -0,0 +1,19 @@ +# MageDok 7" Touchscreen USB Device Rules +# Ensure touch device is recognized and accessible + +# Generic USB touch input device (MageDok) +# Manufacturer typically reports as: EETI eGTouch Controller +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="*eGTouch*", TAG="uaccess" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="*EETI*", TAG="uaccess" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="*MageDok*", TAG="uaccess" + +# Fallback: Any USB device with touch capability (VID/PID may vary by batch) +SUBSYSTEM=="usb", ATTRS{bInterfaceClass}=="03", ATTRS{bInterfaceSubClass}=="01", TAG="uaccess" + +# Create /dev/magedok-touch symlink for consistent reference +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="*eGTouch*", SYMLINK="magedok-touch" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="*EETI*", SYMLINK="magedok-touch" + +# Permissions: 0666 (rw for all users) +SUBSYSTEM=="input", KERNEL=="event*", MODE="0666" +SUBSYSTEM=="input", KERNEL=="mouse*", MODE="0666" -- 2.47.2