A Nerves-based robotic arm control system with real-time joystick input processing and Phoenix LiveView debug interface. This project provides the foundation for controlling a RoARM robot arm using gamepad/joystick input on Raspberry Pi 4.
Current Status: Joystick input processing and debug visualization complete Next Phase: Integration with RoARM robot arm hardware
- Standalone Joystick Handler - Hardware-independent input processing using
input_eventlibrary - Phoenix LiveView Debug Interface - Real-time event visualization and control monitoring
- Browser-side Visual Updates - JavaScript hooks for responsive gamepad visualization
- PubSub Event Broadcasting - Real-time communication between input handler and web interface
✅ Completed:
- Automatic joystick/gamepad detection and connection
- Real-time button press and analog stick processing
- Event normalization (-1.0 to 1.0 ranges for sticks, 0.0 to 1.0 for triggers)
- Live web interface with visual gamepad representation
- Comprehensive logging and debugging tools
- Unit tests for core functionality
🚧 In Progress:
- Robot arm command mapping (joystick → arm movements)
- Safety limits and emergency stops
- Multiple input device support
🎯 Planned:
- RoARM robot arm integration
- Servo position control
- Inverse kinematics calculations
- Position recording and playback
- Web-based arm configuration interface
- CRITICAL: Use Erlang 27 (NOT 28) for Nerves compatibility:
# Using mise (preferred)
mise use erlang@27.2
mise use elixir@1.16.3
# Verify versions
erl -eval 'erlang:display(erlang:system_info(otp_release)), halt().' -noshell
# Should output: "27"- Create
.envfile with your WiFi credentials:
# Copy from template and edit
NERVES_SSID=your-wifi-name
NERVES_PSK=your-wifi-password- Build and deploy to Raspberry Pi:
# Build assets and firmware
mix assets.deploy
mix firmware
# First time: burn to SD card
mix burn
# Subsequent updates: upload over SSH
mix upload roarm_nerves.local- Access the debug interface:
http://roarm_nerves.local
Start Phoenix server for web interface development:
iex -S mix phx.serverLocal Testing (Fast Feedback):
# Run core business logic tests (no hardware required)
mix test test/roarm_nerves/joystick_handler_test.exsWhat's tested locally:
- Button/axis name decoding (12 tests)
- Value normalization functions
- Pure functions without hardware dependencies
- ✅ All pass without requiring Pi hardware
Integration Testing (On Raspberry Pi):
# SSH into device (lands you in IEx)
ssh roarm_nerves.local# From IEx prompt - run interactive hardware tests
iex> RoarmNerves.JoystickHandlerHardwareTest.run_interactive_hardware_tests()
# Or test individual functions
iex> RoarmNerves.JoystickHandler.test_decode_button_name(:btn_a)
iex> RoarmNerves.JoystickHandler.get_state()
# Run full test suite on device (if MIX_ENV=test firmware built)
iex> ExUnit.start()
iex> ExUnit.run()Why not Docker? Available Nerves Docker images are outdated (2021). Our local unit tests provide fast feedback, and integration testing requires actual hardware anyway.
Using RoARM Custom Tasks (Recommended):
# Check environment (Erlang 27, WiFi credentials)
mix roarm.check
# Build firmware with full environment checks
mix roarm.firmware
# Build and upload to device (includes all checks)
mix roarm.upload
# Build and burn to SD card (includes all checks)
mix roarm.burn
# Run local unit tests only
mix roarm.test.local
# Show all available RoARM tasks
mix roarmManual Commands (if needed):
# Build firmware image manually
mise exec -- env MIX_TARGET=rpi4 NERVES_SSID="network" NERVES_PSK="password" mix firmware
# Deploy to device over SSH manually
mise exec -- env MIX_TARGET=rpi4 mix upload roarm_nerves.local
# Burn to SD card manually
mise exec -- env MIX_TARGET=rpi4 mix burn- Xbox controllers (wired/wireless)
- PlayStation controllers
- Generic USB gamepads
- Logitech F310: IMPORTANT - Set mode switch to "X" (XInput mode) on the bottom of the controller
- For Linux input_event system (this project): Use "X" mode
- For web browser gamepad API (roarm_demo): Use "D" mode
- Target hostname:
roarm_nerves.local - WiFi configuration via environment variables
- Automatic joystick device detection
- SSH access enabled for development
Access http://roarm_nerves.local for:
- Real-time joystick event logging
- Visual gamepad state representation
- Connection status monitoring
- Event history and debugging
# Connect to device
ssh roarm_nerves.local
# View joystick-related logs
RingLogger.grep(~r/JOYSTICK_HANDLER|joystick|gamepad/) |> Enum.take(20)
# Monitor all system logs
RingLogger.tail()Joystick Hardware → InputEvent → JoystickHandler → PubSub → LiveView → Browser
# WiFi Configuration
NERVES_SSID=your-network-name
NERVES_PSK=your-network-password
# Target Device
NERVES_TARGET=rpi4
NERVES_HOSTNAME=roarm_nerves.local
# Debug Settings
ENABLE_JOYSTICK_DEBUG=true
MAX_LOG_ENTRIES=100
# Future: Robot Arm Configuration
# ARM_SERIAL_PORT=/dev/ttyUSB0
# ARM_BAUD_RATE=115200
# SAFETY_TIMEOUT_MS=1000The application uses different configurations based on target:
config/host.exs- Development on local machineconfig/target.exs- Raspberry Pi deploymentconfig/prod.exs- Production settings (used on device)
Left Stick X/Y → Base rotation + Shoulder movement
Right Stick X/Y → Elbow + Wrist movement
Left/Right Bumpers → Gripper open/close
Triggers → Fine movement speed control
D-Pad → Preset positions
A/B/X/Y Buttons → Emergency stop, home, record, playback
- Emergency stop (immediate halt)
- Position limits (software constraints)
- Speed limiting
- Collision detection
- Timeout protection
lib/
├── roarm_nerves/
│ ├── application.ex # Main supervision tree
│ └── joystick_handler.ex # Core joystick processing
├── roarm_nerves_web/
│ ├── live/
│ │ └── joystick_control_live.ex # Debug interface
│ └── ...
assets/js/
└── app.js # Browser-side event handling
test/
├── roarm_nerves/
│ └── joystick_handler_test.exs # Unit tests
└── roarm_nerves_web/
└── live/joystick_control_live_test.exs
config/
├── config.exs # Base configuration
├── host.exs # Development config
├── target.exs # Raspberry Pi config
└── prod.exs # Production config
Joystick not detected:
- Check USB connection
- Verify device appears in
/dev/input/event* - Check logs for detection messages
Asset loading errors (404 for app.js/app.css):
- Always run
mix assets.deploybeforemix firmware
SSH connection issues:
- Remove old host keys:
ssh-keygen -R roarm_nerves.local - Verify device is connected to network
- Check WiFi credentials in
.env
Visual controls not updating:
- Check browser console for JavaScript errors
- Verify
push_eventcalls in server logs - Ensure EventLogger hook is mounted
# Check joystick detection
ssh roarm_nerves.local
RingLogger.grep(~r/Found joystick device/) |> Enum.take(5)
# Monitor event flow
RingLogger.grep(~r/JOYSTICK_HANDLER.*Button|Axis/) |> Enum.take(20)
# Check PubSub messages
RingLogger.grep(~r/PUBSUB_BROADCAST/) |> Enum.take(10)This project follows standard Phoenix/Nerves development practices:
- Test locally with
mix test test/roarm_nerves/joystick_handler_test.exs - Test integration on hardware
- Update documentation
- Follow existing code patterns and conventions
[Add your license here]