A complete Arduino-based 6DOF robot arm control system with Python Qt6 GUI interface and ESP32 WiFi bridge REST API for network-based control.
Try the Interactive Demo - Experience it online first!
- Interactive Demo
- Features
- Hardware Requirements
- Software Requirements
- Installation
- Project Structure
- Usage
- Serial Protocol
- Safety Features
- Sequence Management
- Customization
- Keyboard Shortcuts
- ESP32 WiFi Bridge
- Troubleshooting
- Development
- License
- Contributing
- Support
- Serial Communication: Text-based protocol at 115200 baud
- Real-time Control: Live joint position control with debounced sliders
- Joint Limit Validation: All commands checked against mechanical limits before execution
- Safety Features: Emergency stop (keyboard shortcut), movement validation
- Preset Positions: Home, Min, Max, Wave
- Sequence Management: Record, play, delete, save/load to file
- Auto-reconnect: GUI detects USB disconnection and reconnects automatically
- Keyboard Shortcuts: Esc for emergency stop, Ctrl+H for home, Ctrl+R for recording
- ESP32 WiFi Bridge: REST API over HTTP for network-based control, mDNS discovery, optional API key authentication, CORS-enabled
- Optimized Codebase: Servo array architecture, char buffer serial input, compatible with Arduino Uno and Mega
Complete system architecture showing all components and data flows
Complete 6DOF robot arm assembly showing servo motor connections and mechanical structure
Detailed view of robot arm components and joint configurations
- Arduino Uno or Arduino Mega (recommended)
- 6DOF Robot Arm with servo motors
- USB cable for serial communication
- Servo motors connected to pins 4, 5, 6, 7, 8, 9
- Arduino IDE (for uploading code to Arduino)
- Python 3.8+
- PyQt6
- pyserial
-
Arduino Setup:
# For Arduino Uno: arduino-cli compile --fqbn arduino:avr:uno --build-path ../build_uno . arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:avr:uno . # For Arduino Mega: arduino-cli compile --fqbn arduino:avr:mega --build-path ../build_mega . arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:avr:mega .
-
Python Dependencies:
pip install -r requirements.txt
Try it online first! - No hardware required
Experience the 6DOF Robot Arm in your browser:
- Wokwi Online Simulator
- Test all commands in real-time
- See servo movements visually
- Suitable for learning and testing
The system consists of layered architecture for reliable control:
User Interface Layer -> Communication Layer -> Control Layer -> Hardware Layer
For detailed system flow and component interactions, see the complete system flowchart.
6DoF_arm/
├── .git/
├── .gitignore
├── code/ # Arduino controller
│ ├── code.ino # Main Arduino sketch
│ └── config.h # Configuration (pins, limits, commands)
├── bridge/ # ESP32 WiFi bridge
│ ├── bridge.ino # REST API gateway
│ ├── config.h # Bridge configuration
│ └── secrets.h.example # WiFi credentials template
├── arm_control_gui.py # Python Qt6 GUI
├── requirements.txt # Python dependencies
├── assets/
│ ├── flowchart.mermaid
│ ├── render.png
│ ├── gui.png
│ ├── arm_assambler.jpg
│ └── arm_component.jpg
├── LICENSE
└── README.md
The Arduino code (code/code.ino and code/config.h) provides:
- Serial communication at 115200 baud with char buffer input (no heap fragmentation)
- Joint control with limit validation against
JOINT_MIN[]/JOINT_MAX[] - Servo array architecture (no duplicated switch-case blocks)
- Emergency stop functionality
- Real-time position feedback
- Portable serial reading in
loop()(works on all boards)
Joint Pin Mapping:
- Joint 1 (Base): Pin 9, range 0-180 degrees
- Joint 2 (Shoulder): Pin 8, range 30-150 degrees
- Joint 3 (Elbow): Pin 7, range 0-180 degrees
- Joint 4 (Wrist Rotation): Pin 6, range 0-180 degrees
- Joint 5 (Wrist Bend): Pin 5, range 0-180 degrees
- Joint 6 (Gripper): Pin 4, range 90-180 degrees
Run the GUI application:
python arm_control_gui.py
Python Qt6 GUI interface showing joint controls, preset positions, and status monitoring
GUI Features:
-
Connection Panel:
- Select serial port from dropdown
- Connect/Disconnect button
- Scan button to refresh available ports
- Auto-reconnect on USB disconnection
-
Joint Control:
- 6 sliders for individual joint control (J1-J6)
- Real-time position display with min/max labels
- Joint limits enforced automatically
- Debounced slider commands (80ms) to prevent serial flooding
-
Speed Control:
- Movement Speed: Configurable 5-200ms delay between steps
- Real-time Adjustment: Change speed without restarting Arduino
- Default: 15ms (optimized for smooth and fast movement)
-
Preset Positions:
- Home: Default safe position
- Min: Move all joints to minimum positions
- Max: Move all joints to maximum positions
- Wave: Friendly wave gesture (raises arm and waves left-right)
-
Safety Controls:
- Emergency Stop: Immediately halts all movement (also via Esc key)
- Get Status: Requests current position from Arduino
-
Status Display:
- Real-time communication log (capped at 200 lines, oldest removed first)
- Error messages and feedback
J1:90 - Set joint 1 to 90 degrees (validated against limits)
J2:45 - Set joint 2 to 45 degrees
HOME - Move to home position
MIN - Move all joints to minimum positions
MAX - Move all joints to maximum positions
WAVE - Perform friendly wave gesture
SET_SPEED:15 - Set movement speed to 15ms (5-200ms range)
STOP - Emergency stop
STATUS - Request current positions
OK:J1:90,J2:45,J3:0,J4:108,J5:80,J6:152 - Current positions
ERROR:Joint 2 range is 30-150 - Limit violation
ERROR:Unknown command: XYZ - Command error
SEQUENCE: - Sequence list header
0:MySequence - Sequence entry
Individual Joint Control:
J1:90 # Move base to 90 degrees
J2:45 # Move shoulder to 45 degrees
J3:60 # Move elbow to 60 degrees
J4:120 # Move wrist rotation to 120 degrees
J5:80 # Move wrist bend to 80 degrees
J6:150 # Move gripper to 150 degrees
STATUS # Check current positionsPosition Commands:
HOME # Move to home position (92,85,45,108,80,152)
STATUS # Verify home position
MIN # Move to minimum positions (0,30,0,0,0,90)
STATUS # Verify minimum positions
MAX # Move to maximum positions (180,150,180,180,180,180)
STATUS # Verify maximum positions
WAVE # Perform friendly wave gesture
STATUS # Verify position after wave
STOP # Emergency stop (if needed)Speed Control:
SET_SPEED:15 # Set fast/smooth movement (15ms delay)
SET_SPEED:50 # Set normal movement (50ms delay)
SET_SPEED:100 # Set slow/precise movement (100ms delay)
SET_SPEED:3 # Error: below minimum (5ms)
SET_SPEED:250 # Error: above maximum (200ms)Valid Ranges:
J1:0 # Base minimum
J1:180 # Base maximum
J2:30 # Shoulder minimum (mechanical limit)
J2:150 # Shoulder maximum (mechanical limit)
J3:0 # Elbow minimum
J3:180 # Elbow maximum
J6:90 # Gripper minimum
J6:180 # Gripper maximumError Testing:
J1:200 # Error: over 180 degrees
J2:10 # Error: under 30 degrees
J2:160 # Error: over 150 degrees
J6:50 # Error: under 90 degrees
J7:90 # Error: invalid joint numberRecording Sequences:
RECORD_START:0:PickSequence # Start recording sequence 0
J1:45 # Record base movement
J2:90 # Record shoulder movement
J3:135 # Record elbow movement
J4:60 # Record wrist rotation
RECORD_STOP # Stop recording
LIST_SEQUENCES # Verify sequence savedPlaying Sequences:
PLAY_SEQUENCE:0 # Play recorded sequence
STATUS # Check final positionSequence Management:
LIST_SEQUENCES # Show all sequences
DELETE_SEQUENCE:0 # Delete sequence 0
LIST_SEQUENCES # Verify deletion- Upload code to Arduino Uno/Mega
- Open Serial Monitor (Tools -> Serial Monitor)
- Set baud rate to
115200 - Line ending to
NewlineorBoth NL & CR - Send commands one by one and observe responses
# Initial status
STATUS
# Test individual joints
J1:90
STATUS
J2:45
STATUS
J3:60
STATUS
# Test home command
HOME
STATUS
# Test wave command
WAVE
STATUS
# Test emergency stop
J1:180
STOP
STATUS
# Test sequence recording
RECORD_START:0:TestMove
J1:30
J2:80
J3:120
RECORD_STOP
# Test sequence playback
LIST_SEQUENCES
PLAY_SEQUENCE:0
STATUS
# Clean up
DELETE_SEQUENCE:0
LIST_SEQUENCES- Flash Usage: ~29% (9500/32256 bytes)
- RAM Usage: ~81% (1675/2048 bytes)
- Status: Reliable, all features functional
- Movement Speed: Configurable 5-200ms (default 15ms)
- Sequences: 2 sequences x 15 waypoints each
- Commands: HOME, MIN, MAX, WAVE, SET_SPEED, full sequence control
- Flash Usage: ~4% (10660/253952 bytes)
- RAM Usage: ~21% (1786/8192 bytes)
- Status: Full headroom, recommended for expansion
- Movement Speed: Configurable 5-200ms (default 15ms)
- Sequences: 2 sequences x 15 waypoints each
- Commands: HOME, MIN, MAX, WAVE, SET_SPEED, full sequence control
- Joint Limit Validation: Every angle command is checked against
JOINT_MIN[]andJOINT_MAX[]before servo movement. Out-of-range values return an error with the valid range. - Angle Clamping: Internal movement functions clamp angles to valid range as a secondary safeguard.
- Emergency Stop: Immediate halt of all movements via button or Esc key.
- Command Validation: All commands are validated for format and parameters before execution.
- Movement Interruption: Emergency stop can interrupt ongoing movements including sequence playback.
The system supports creating and managing movement sequences for automated operations.
- Enter a sequence name in the text field
- Click "Start Recording" or press Ctrl+R (button turns red)
- Move joints using sliders - each movement is recorded as a waypoint
- Click "Stop Recording" or press Ctrl+R again when finished
- Play: Execute the selected sequence from the list
- Delete: Remove the selected sequence
- Refresh: Update the sequence list from Arduino
- Save to File: Export current positions and sequence list to JSON
- Load from File: Import positions and speed from JSON, send to Arduino
RECORD_START:sequence_index:name - Start recording sequence
RECORD_STOP - Stop recording
PLAY_SEQUENCE:sequence_index - Play recorded sequence
LIST_SEQUENCES - Get list of sequences
DELETE_SEQUENCE:sequence_index - Delete sequence
- Capacity: Up to 2 sequences in Arduino memory
- Length: Each sequence can have up to 15 waypoints
- Persistence: Sequences are stored in RAM (lost on power cycle)
| Shortcut | Action |
|---|---|
| Esc | Emergency Stop |
| Ctrl+H | Move to Home position |
| Ctrl+R | Start/Stop recording |
| Ctrl+Shift+C | Connect/Disconnect serial |
Modify JOINT_MIN and JOINT_MAX in code/config.h:
const int JOINT_MIN[NUM_JOINTS] = {0, 30, 0, 0, 0, 90};
const int JOINT_MAX[NUM_JOINTS] = {180, 150, 180, 180, 180, 180};Also update JOINT_LIMITS in arm_control_gui.py to match.
Via Serial Commands:
SET_SPEED:15 # Fast and smooth (default)
SET_SPEED:50 # Normal speed
SET_SPEED:100 # Slow and preciseVia GUI:
- Use the speed control spinbox (5-200ms range)
- Click "Set" to apply changes immediately
Arduino Code Default (in config.h):
#define DEFAULT_MOVE_SPEED 15
#define MIN_MOVE_SPEED 5
#define MAX_MOVE_SPEED 200Modify HOME_POSITIONS in code/config.h and HOME_POSITIONS in arm_control_gui.py.
The ESP32 bridge turns the robot arm into a network-accessible device with a REST API, similar to how industrial robot controllers operate. The Arduino firmware requires no modifications -- the ESP32 speaks the same serial protocol.
[HTTP Client] <--WiFi--> [ESP32 Bridge] <--UART--> [Arduino Uno/Mega] <--PWM--> [Servos]
-
Install dependencies in Arduino IDE:
- Board: "ESP32 Dev Module" (via Boards Manager)
- Library: ArduinoJson (via Library Manager)
-
Configure WiFi credentials:
cd bridge/ cp secrets.h.example secrets.h # Edit secrets.h with your WiFi SSID, password, and optional API key
-
Upload to ESP32:
arduino-cli compile --fqbn esp32:esp32:esp32 --build-path ../build_bridge . arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:esp32 .
-
Access the API at
http://robot-arm.local(mDNS) or the IP printed on serial monitor.
| ESP32 Pin | Arduino Pin | Function |
|---|---|---|
| GPIO 16 (RX2) | TX (pin 1) | Arduino transmit to ESP32 |
| GPIO 17 (TX2) | RX (pin 0) | ESP32 transmit to Arduino |
| GND | GND | Common ground |
Disconnect the Arduino USB cable when using the ESP32 bridge, since both share the same UART on Uno. Arduino Mega users can modify the bridge to use Serial1 to keep USB available for debugging.
All endpoints return JSON. Set X-API-Key header if authentication is configured in secrets.h. CORS is enabled for browser clients.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/health |
Health check: Arduino status, WiFi RSSI, uptime |
| GET | /api/info |
System info: firmware version, IP, hostname, free heap |
| GET | /api/status |
Current joint positions |
| Method | Endpoint | Body | Description |
|---|---|---|---|
| POST | /api/joint |
{"joint": 1, "angle": 90} |
Move single joint |
| POST | /api/joints |
{"joints": {"J1": 90, "J2": 45}} |
Move multiple joints |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/home |
Move to home position |
| POST | /api/stop |
Emergency stop (no auth required) |
| POST | /api/preset/min |
All joints to minimum |
| POST | /api/preset/max |
All joints to maximum |
| POST | /api/preset/wave |
Wave gesture |
| POST | /api/speed |
Set speed: {"speed_ms": 15} |
| Method | Endpoint | Body | Description |
|---|---|---|---|
| GET | /api/program/list |
-- | List stored programs |
| POST | /api/program/record |
{"slot": 0, "name": "pick"} |
Start recording |
| POST | /api/program/stop |
-- | Stop recording |
| POST | /api/program/run |
{"slot": 0} |
Execute program |
| POST | /api/program/delete |
{"slot": 0} |
Delete program |
# Check health
curl http://robot-arm.local/api/health
# Get current positions
curl http://robot-arm.local/api/status
# Move joint 1 to 90 degrees
curl -X POST http://robot-arm.local/api/joint \
-H "Content-Type: application/json" \
-d '{"joint": 1, "angle": 90}'
# Move multiple joints
curl -X POST http://robot-arm.local/api/joints \
-H "Content-Type: application/json" \
-d '{"joints": {"J1": 90, "J2": 45, "J3": 60}}'
# Go home
curl -X POST http://robot-arm.local/api/home
# Emergency stop
curl -X POST http://robot-arm.local/api/stop
# Set speed
curl -X POST http://robot-arm.local/api/speed \
-H "Content-Type: application/json" \
-d '{"speed_ms": 50}'
# Record a program
curl -X POST http://robot-arm.local/api/program/record \
-H "Content-Type: application/json" \
-d '{"slot": 0, "name": "pick_and_place"}'
# (move joints via /api/joint calls while recording)
# Stop recording
curl -X POST http://robot-arm.local/api/program/stop
# Run the program
curl -X POST http://robot-arm.local/api/program/run \
-H "Content-Type: application/json" \
-d '{"slot": 0}'
# List programs
curl http://robot-arm.local/api/program/list
# With API key authentication
curl -H "X-API-Key: your-key" http://robot-arm.local/api/statusSuccess:
{
"status": "ok",
"positions": {"J1": 92, "J2": 85, "J3": 45, "J4": 108, "J5": 80, "J6": 152}
}Error:
{
"error": "Joint 2 range is 30-150"
}Program list:
{
"status": "ok",
"programs": [
{"slot": 0, "name": "pick_and_place"},
{"slot": 1, "name": "wave_demo"}
]
}- Check that Arduino Uno/Mega is connected (
arduino-cli board list) - Verify correct port selection (typically
/dev/ttyACM0or/dev/ttyUSB0) - Ensure Arduino code is uploaded and running
- Check serial baud rate (115200)
- If the GUI shows "Connection lost", it will auto-reconnect when the port reappears
- Verify servo connections: pins 4, 5, 6, 7, 8, 9 on both Uno and Mega
- Check servo power supply (adequate current for 6 servos)
- Confirm joint limits are appropriate for your arm
- Test individual joints with manual commands
- Check for "ERROR:Joint N range is X-Y" messages indicating limit violations
- Ensure PyQt6 and pyserial are installed
- Check Python version (3.8+ required)
- Run GUI with proper display (not headless)
- If sliders feel unresponsive, the 80ms debounce is working as intended
-
Complete System Flowchart: View detailed Mermaid flowchart
- User interface interactions
- Serial communication protocol
- Command processing flows
- Hardware control sequences
- Safety and validation systems
- Sequence management operations
-
Rendered Flowchart: View rendered flowchart diagram
- Visual representation of system architecture
- Component relationships and data flows
- Hierarchical organization of system layers
- New Commands: Add to
processCommand()incode/code.ino - New GUI Elements: Extend
_build_right_panel()inarm_control_gui.py - Additional Safety: Add validation in
clampAngle()orprocessJointCommand()
- Arduino: Servo array, char buffer input, modular command processing
- Python: Object-oriented Qt6 application with threaded serial I/O
- Communication: Text protocol with debounced slider commands
- Compatibility: Tested and optimized for Arduino Uno and Mega
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Submit a pull request
For issues and questions:
- Check the troubleshooting section
- Review the serial protocol documentation
- Open an issue on GitHub
Note: Always test safety features before operating with real hardware. Ensure proper power supplies and mechanical constraints to prevent damage to equipment or injury.