A web-based control system for a robot tank running on Raspberry Pi Zero 2W. Features real-time camera streaming, GPIO motor control via DRV8833 drivers, and a mobile-responsive control panel.
- Features
- Hardware Requirements
- GPIO Pin Configuration
- Installation
- Quick Reference: System Dependencies
- Verification
- Usage
- API Documentation
- Configuration
- Development
- Architecture
- Project Structure
- Troubleshooting
- Safety
- Future Enhancements
- Contributing
- Author
- License
- Credits
- Support
- Web Control Panel: Responsive UI that works on desktop and mobile devices
- Real-time Camera Streaming: MJPEG streaming from CSI camera module
- JSON API: RESTful API for mobile app integration (iOS/Android)
- GPIO Motor Control: DRV8833 dual H-bridge motor driver for wheels, SG90 servo for turret
- Safety Mechanisms: Auto-stop, emergency stop, and graceful shutdown
- Development Mode: Mock controller for testing without GPIO hardware
- PWM Soft-Start: Linear motor ramp-up prevents circuit overloads and Pi resets
- Future-Ready: Abstracted control interface for GamePad support
- Raspberry Pi Zero 2W
- 1x DRV8833 dual H-bridge motor driver board
- 2x 3V brush motors class 130 (wheels)
- SG90 micro servo (turret rotation)
- CSI camera module (ribbon cable)
- Power supply and battery for motors
- Robot tank chassis
| Motor | Function | GPIO Pin (BCM) | DRV8833 Pin | Output |
|---|---|---|---|---|
| Left Wheel | Forward | GPIO 17 | IN1 | OUT1 |
| Left Wheel | Backward | GPIO 18 | IN2 | OUT2 |
| Left Wheel | PWM Enable | GPIO 12 | EEP | - |
| Right Wheel | Forward | GPIO 22 | IN3 | OUT3 |
| Right Wheel | Backward | GPIO 23 | IN4 | OUT4 |
| Right Wheel | PWM Enable | GPIO 13 | EEP | - |
The turret uses an SG90 micro servo connected directly to the Raspberry Pi (no motor driver needed).
| Wire Color | Function | Connect To |
|---|---|---|
| Brown | Ground (GND) | Raspberry Pi GND |
| Red | Power (VCC) | Raspberry Pi 5V |
| Yellow | Signal (PWM) | GPIO 19 (BCM) |
Wiring Notes:
- The SG90 servo requires 5V power (not 3.3V)
- GPIO 19 is a hardware PWM capable pin for precise servo control
- Keep servo wires away from motor power lines to reduce interference
| DRV8833 Pin | Connect To |
|---|---|
| VCC | Raspberry Pi 3.3V |
| GND | Raspberry Pi GND |
| ULT | Motor battery + (3-6V) |
Note: PWM pins provide soft-start to prevent inrush current spikes. The system works without PWM pins connected (backward compatible).
There are two ways to install and run the robot tank controller:
- Docker (Recommended) - Containerized, isolated, easy deployment
- Native - Direct installation on Raspberry Pi OS
| Method | Best For | Pros | Cons |
|---|---|---|---|
| Docker | Multi-service setups, reproducible deployments | Isolated, portable, easy updates | Slightly more resources, initial setup |
| Native | Dedicated robot controller, maximum performance | Direct GPIO, less overhead, simpler debugging | System-wide installation |
- Docker and Docker Compose installed on Raspberry Pi
- Raspberry Pi Zero 2W with Raspberry Pi OS (ARM64)
# 1. Clone repository
cd ~
git clone https://github.com/matiyas/robot.git
cd robot
# 2. Run deployment script
./scripts/docker-deploy-pi.sh
# 3. Access the robot
# Open browser to http://<RASPBERRY_PI_IP># Start (development mode - no GPIO)
make dev
# Start (production mode - with GPIO)
make prod
# View logs
make logs
# Stop
make down
# Access shell
make shellSee DOCKER.md for comprehensive Docker documentation including:
- Complete setup guide
- Configuration options
- Troubleshooting
- Performance tuning
- CI/CD integration
- Raspberry Pi Zero 2W with Raspberry Pi OS (ARM64)
- Internet connection
# 1. Clone repository
cd ~
git clone https://github.com/matiyas/robot.git
cd robot
# 2. Run installation script
chmod +x scripts/native-install.sh
./scripts/native-install.sh
# 3. Reboot (required)
sudo reboot
# 4. Start the service
sudo systemctl start robotIf you prefer step-by-step installation:
sudo apt-get update
sudo apt-get upgrade -ysudo apt-get install -y \
build-essential \
git \
curl \
libssl-dev \
libreadline-dev \
zlib1g-dev \
libffi-dev \
libyaml-dev \
pkg-config \
motion \
v4l-utils# Install rbenv
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
# Install ruby-build
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
# Reload shell
source ~/.bashrc
# Install Ruby from .ruby-version
cd ~/robot
rbenv install $(cat .ruby-version)
rbenv global $(cat .ruby-version)gem install bundler
rbenv rehash
cd ~/robot
bundle config set --local without 'development test'
bundle install# Copy motion config
sudo cp scripts/motion.conf /etc/motion/motion.conf
# Install systemd service
ROBOT_DIR=$(pwd)
sed "s|/home/pi/robot|$ROBOT_DIR|g" scripts/robot.service | \
sudo tee /etc/systemd/system/robot.service > /dev/null
# Enable services
sudo systemctl daemon-reload
sudo systemctl enable robot motionsudo usermod -a -G video $USER
sudo usermod -a -G gpio $USERsudo reboot# Test installation
./scripts/test-native-setup.sh
# Check service status
sudo systemctl status robot
# View logs
sudo journalctl -u robot -fSee NATIVE_INSTALLATION.md for comprehensive guide including:
- Detailed installation steps
- Configuration options
- Performance optimization
- Troubleshooting
- Uninstallation
Based on tested Docker configuration, here's what gets installed:
# Core packages (required)
build-essential # GCC, G++, make (for Ruby compilation)
git # Version control
curl # HTTP client
libssl-dev # OpenSSL headers (required for Ruby and gems)
libreadline-dev # Readline library (required for Ruby)
zlib1g-dev # Compression library (required for Ruby)
libffi-dev # FFI library (required for Ruby fiddle extension)
libyaml-dev # YAML library (required for Ruby psych extension)
pkg-config # Package configuration
# Ruby version management
rbenv # Ruby version manager (installed via git)
ruby-build # rbenv plugin for installing Ruby
# Application packages
motion # Camera streaming
v4l-utils # Camera utilities
# Ruby gems (installed via bundler)
puma # Web server
sinatra # Web framework
pigpio # GPIO controlNote: Ruby is installed via rbenv using the version specified in .ruby-version (currently 3.2.2)
After installation (Docker or Native), verify it's working:
# Check health
curl http://<PI_IP_ADDRESS>/health
# Check status
curl http://<PI_IP_ADDRESS>/api/v1/status
# Test movement
curl -X POST http://<PI_IP_ADDRESS>/api/v1/move \
-H "Content-Type: application/json" \
-d '{"direction":"forward","duration":1000}'
# Access web interface
# Open browser to http://<PI_IP_ADDRESS>- Connect to the same WiFi network as your Raspberry Pi
- Open a browser and navigate to
http://<PI_IP_ADDRESS> - Use the on-screen controls to drive the robot
Controls:
- D-pad: Forward, backward, left, right
- Turret buttons: Rotate turret left/right
- Emergency Stop: Immediately stops all motors
Use the JSON API to build custom mobile applications. Example using fetch API:
// Move forward for 1 second
fetch('http://PI_IP/api/v1/move', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({direction: 'forward', duration: 1000})
})
.then(response => response.json())
.then(data => console.log(data));Production: http://<PI_IP_ADDRESS>
Development: http://localhost:4567
Swagger UI is available at /docs for interactive API exploration:
- Development:
http://localhost:4567/docs - Production:
http://<PI_IP_ADDRESS>/docs
POST /api/v1/move
Content-Type: application/json
{
"direction": "forward|backward|left|right",
"duration": 1000 // milliseconds (optional)
}Response:
{
"success": true,
"action": "forward",
"duration": 1000
}POST /api/v1/turret
Content-Type: application/json
{
"direction": "left|right",
"duration": 500 // milliseconds (optional)
}Response:
{
"success": true,
"action": "turret_left",
"duration": 500
}POST /api/v1/stopResponse:
{
"success": true,
"action": "stop_all"
}GET /api/v1/statusResponse:
{
"success": true,
"connected": true,
"gpio_enabled": true,
"camera_url": "http://192.168.1.100:8081"
}GET /api/v1/cameraResponse:
{
"success": true,
"stream_url": "http://192.168.1.100:8081"
}All endpoints return error responses in this format:
{
"success": false,
"error": "Error message"
}HTTP status codes:
200- Success400- Bad request (invalid parameters)401- Unauthorized (when authentication is enabled)500- Internal server error
Edit config/settings.yml:
production:
host: '0.0.0.0'
port: 4567
gpio_enabled: true
camera_url: 'http://localhost:8081'
movement_timeout: 5000 # Max movement duration (ms)
turret_timeout: 2000 # Max turret rotation duration (ms)
log_level: 'info'
auth_enabled: false # Enable authentication (future)
pwm_enabled: true # Enable PWM soft-start
pwm_frequency: 1000 # PWM frequency in Hz (1kHz default)
pwm_ramp_duration: 500 # Time to reach full power (ms)PWM soft-start prevents sudden current spikes when motors start, which can cause the Raspberry Pi to reset. When enabled, motor power ramps linearly from 0 to 100% over the configured duration.
- pwm_enabled: Set to
trueto enable soft-start (default:true) - pwm_frequency: PWM frequency in Hz, 1000 Hz is quiet and provides good resolution
- pwm_ramp_duration: Time in milliseconds to reach full power (default: 500ms)
The system is backward compatible - it works without PWM pins connected by falling back to instant-on behavior.
Edit config/gpio_pins.yml if your wiring differs:
motor_left:
in1: 17 # Forward
in2: 18 # Backward
enable: 12 # PWM soft-start (optional)Edit scripts/motion.conf to adjust camera settings:
width 640
height 480
framerate 15
quality 85
stream_port 8081
You can develop and test the application on your computer without GPIO hardware:
bundle install
bundle exec ruby app/robot_app.rbThe app will run in development mode using MockController. Access at http://localhost:4567
bundle exec rerun ruby app/robot_app.rb# Check status
curl http://localhost:4567/api/v1/status
# Move forward for 1 second
curl -X POST http://localhost:4567/api/v1/move \
-H "Content-Type: application/json" \
-d '{"direction":"forward","duration":1000}'
# Emergency stop
curl -X POST http://localhost:4567/api/v1/stopThe project uses a clean abstraction layer that separates hardware control from business logic:
- ControlInterface: Abstract base class defining control methods
- GpioController: Hardware implementation using pigpio library
- MockController: Development implementation for testing
- Robot Model: High-level orchestration and business logic
- Sinatra App: HTTP API and web interface
This architecture makes it easy to:
- Test without hardware
- Swap control implementations (GPIO → GamePad)
- Add authentication
- Extend with new features
robot/
├── app/
│ ├── robot_app.rb # Main Sinatra application
│ ├── models/
│ │ └── robot.rb # Robot control model
│ ├── services/
│ │ ├── control_interface.rb # Abstract controller
│ │ ├── gpio_controller.rb # GPIO implementation
│ │ ├── mock_controller.rb # Development mock
│ │ └── pwm_ramper.rb # PWM soft-start implementation
│ └── helpers/
│ └── api_helpers.rb # API utilities
├── lib/
│ ├── gpio_manager.rb # GPIO lifecycle
│ └── safety_handler.rb # Safety mechanisms
├── public/
│ ├── css/style.css # Responsive styles
│ ├── images/ # Static images
│ ├── openapi.yaml # OpenAPI specification
│ └── js/
│ ├── api-client.js # API wrapper
│ └── robot-controller.js # UI controller
├── views/
│ ├── layout.erb # HTML layout
│ └── index.erb # Control panel
├── config/
│ ├── settings.yml # App configuration
│ └── gpio_pins.yml # GPIO mappings
└── scripts/
├── setup.sh # Installation script
├── motion.conf # Camera config
└── robot.service # Systemd service
# Check camera is detected
vcgencmd get_camera
# Test camera
raspistill -o test.jpg
# Check Motion logs
sudo journalctl -u motion -f
# Restart Motion
sudo systemctl restart motion# Check GPIO permissions
groups # Should include 'gpio'
# Test GPIO pins
gpio readall # If wiringPi installed
# Check app logs
sudo journalctl -u robot -f# Check service status
sudo systemctl status robot
# View detailed logs
sudo journalctl -u robot -n 50
# Restart service
sudo systemctl restart robot# Check resource usage
top -b -n 1 | grep -E "ruby|motion"
# Reduce camera quality in motion.conf:
# width 640 -> 480
# framerate 15 -> 10
# quality 85 -> 75- Always test in a safe environment
- Keep the emergency stop button accessible
- Monitor battery levels
- Use appropriate voltage regulators
- Ensure proper motor driver heat dissipation
- PWM soft-start for motor protection
- GamePad controller support (USB or Bluetooth)
- Authentication (HTTP Basic Auth or JWT)
- Battery voltage monitoring
- Ultrasonic distance sensors
- IMU for orientation tracking
- Recording and playback of movement sequences
- Autonomous navigation modes
Pull requests are welcome! Please follow these guidelines:
- Test in development mode first
- Ensure backward compatibility
- Update documentation
- Follow existing code style
matiyas - GitHub
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Sinatra
- GPIO control via pigpio
- Camera streaming via rpicam-apps
- Runs on Raspberry Pi
For issues, questions, or suggestions:
- Open an issue on GitHub
- Check the troubleshooting section above
- Review system logs:
sudo journalctl -u robot -f