feat: MageDok 7in display setup for Orin (Issue #369) #373
@ -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
|
||||
33
jetson/ros2_ws/src/saltybot_bringup/config/xorg-magedok.conf
Normal file
33
jetson/ros2_ws/src/saltybot_bringup/config/xorg-magedok.conf
Normal file
@ -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
|
||||
@ -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 <hdmi-sink-name>
|
||||
|
||||
# Verify routing
|
||||
pactl get-default-sink
|
||||
|
||||
# Optional: Set volume
|
||||
pactl set-sink-volume <sink-name> 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
|
||||
@ -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',
|
||||
),
|
||||
])
|
||||
97
jetson/ros2_ws/src/saltybot_bringup/scripts/audio_router.py
Normal file
97
jetson/ros2_ws/src/saltybot_bringup/scripts/audio_router.py
Normal file
@ -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()
|
||||
88
jetson/ros2_ws/src/saltybot_bringup/scripts/touch_monitor.py
Normal file
88
jetson/ros2_ws/src/saltybot_bringup/scripts/touch_monitor.py
Normal file
@ -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()
|
||||
@ -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()
|
||||
@ -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
|
||||
@ -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"
|
||||
Loading…
x
Reference in New Issue
Block a user