Orange Pi RV2
The Orange Pi RV2 is a quad-core RISC-V single board computer with up to 8 GB of RAM and full Linux support. It fills the role in the Rovari ecosystem that the Raspberry Pi fills in the ARM world: a general purpose Linux box with GPIO headers, camera and display connectors, USB, Ethernet, WiFi, and HDMI output.
Where the CH32V003 teaches you bare metal embedded programming, the Orange Pi RV2 is for applications that need a full operating system: edge computing, IoT gateways, home automation, web servers, and containerized services. The 40-pin GPIO header is electrically compatible with Raspberry Pi HATs, so most existing add-on boards work without modification.
Specifications
| Component | Detail |
|---|---|
| Processor | Quad-core RISC-V @ 1.5 GHz |
| RAM | 2 GB / 4 GB / 8 GB LPDDR4X |
| Storage | MicroSD slot, optional eMMC socket |
| Video | HDMI 2.0 (4K @ 60 Hz) |
| USB | 2x USB 3.0, 1x USB 2.0 OTG |
| Network | Gigabit Ethernet, WiFi 5, Bluetooth 5.0 |
| GPIO | 40-pin header (Raspberry Pi compatible) |
| Display | DSI connector for LCD panels |
| Camera | CSI connector for camera modules |
| Power | 5V / 3A via USB-C or GPIO header |
| Temperature | 0°C to 70°C |
| Dimensions | 85 mm x 56 mm |
Setup
What you need
- Orange Pi RV2 board
- MicroSD card (16 GB or larger, Class 10)
- 5V / 3A USB-C power supply
- HDMI cable and monitor (optional for headless setup)
- USB keyboard and mouse (for initial configuration)
- Ethernet cable or WiFi credentials
Flash the image
Download the latest Ubuntu or Debian image for Orange Pi RV2 from the official Orange Pi website. Write it to your SD card using Balena Etcher or dd:
# Balena Etcher: select image, select SD card, click Flash # Or using dd on Linux sudo dd if=ubuntu-orangepi-rv2.img of=/dev/sdX bs=4M status=progress sudo sync
First boot
- Insert the SD card into the Orange Pi RV2
- Connect HDMI, keyboard, and mouse
- Connect power (the board boots automatically)
- Wait for initial setup to complete (two to three minutes on first boot)
Default credentials: Username orangepi, password orangepi. Change these immediately after first login.
Linux Configuration
System update
After first boot, update everything and install build tools:
sudo apt update sudo apt upgrade -y sudo apt install build-essential git python3-pip -y
System settings
The built-in configuration tool handles filesystem expansion, hostname, timezone, and service management:
sudo orangepi-config
Use this to expand the filesystem to fill the entire SD card, set your timezone, and configure the keyboard layout.
Networking
Wired Ethernet
Ethernet works out of the box with DHCP. Verify your connection:
ip addr show ping -c 4 google.com hostname -I
Static IP
Edit the netplan configuration to assign a fixed address:
network:
version: 2
renderer: networkd
ethernets:
eth0:
addresses:
- 192.168.1.100/24
gateway4: 192.168.1.1
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4sudo netplan apply
WiFi
Connect using NetworkManager from the command line:
nmcli dev wifi list nmcli dev wifi connect "YourSSID" password "YourPassword" nmcli connection show
Hidden networks: nmcli dev wifi connect "SSID" password "pass" hidden yes
SSH
Enable SSH for headless access:
sudo apt install openssh-server -y sudo systemctl enable ssh sudo systemctl start ssh
Connect from another machine:
ssh orangepi@192.168.1.100
Firewall
sudo apt install ufw -y sudo ufw allow ssh sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable sudo ufw status verbose
Allow SSH first. Always add the SSH rule before enabling the firewall, or you will lock yourself out of remote access.
GPIO
The Orange Pi RV2 has a 40-pin GPIO header compatible with the Raspberry Pi pinout. Install the GPIO library:
sudo pip3 install OPi.GPIO
Blink an LED
import OPi.GPIO as GPIO import time GPIO.setmode(GPIO.BOARD) GPIO.setup(11, GPIO.OUT) try: while True: GPIO.output(11, GPIO.HIGH) time.sleep(1) GPIO.output(11, GPIO.LOW) time.sleep(1) except KeyboardInterrupt: GPIO.cleanup()
Read a button
import OPi.GPIO as GPIO import time GPIO.setmode(GPIO.BOARD) GPIO.setup(11, GPIO.OUT) GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP) try: while True: if GPIO.input(13) == GPIO.LOW: GPIO.output(11, GPIO.HIGH) else: GPIO.output(11, GPIO.LOW) time.sleep(0.1) except KeyboardInterrupt: GPIO.cleanup()
Python
Environment setup
python3 --version python3 -m pip install --upgrade pip sudo apt install python3-dev python3-venv -y
Virtual environments
python3 -m venv ~/myproject/venv source ~/myproject/venv/bin/activate pip install flask requests numpy deactivate
Hardware libraries
pip install OPi.GPIO # GPIO control pip install smbus2 # I2C pip install spidev # SPI pip install pyserial # UART
I2C temperature sensor
import smbus2 import time BMP280_ADDR = 0x76 bus = smbus2.SMBus(1) def read_temperature(): data = bus.read_i2c_block_data(BMP280_ADDR, 0xFA, 3) raw_temp = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4) return raw_temp / 5120.0 try: while True: temp = read_temperature() print(f"Temperature: {temp:.2f}°C") time.sleep(2) except KeyboardInterrupt: bus.close()
Flask web monitor
from flask import Flask, jsonify import subprocess, os app = Flask(__name__) def get_cpu_temp(): try: temp = subprocess.check_output(["cat", "/sys/class/thermal/thermal_zone0/temp"]) return float(temp) / 1000 except: return 0 def get_memory_usage(): with open('/proc/meminfo') as f: lines = f.readlines() total = int(lines[0].split()[1]) available = int(lines[2].split()[1]) return round(((total - available) / total) * 100, 1) @app.route('/api/status') def status(): return jsonify({'cpu_temp': get_cpu_temp(), 'memory_pct': get_memory_usage()}) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)
pip install flask python3 web_monitor.py
Run at startup with systemd
[Unit] Description=My Python Application After=network.target [Service] Type=simple User=orangepi WorkingDirectory=/home/orangepi/myproject ExecStart=/home/orangepi/myproject/venv/bin/python app.py Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
sudo systemctl daemon-reload sudo systemctl enable myapp.service sudo systemctl start myapp.service
Docker
Docker runs containerized services on the Orange Pi RV2 with isolated environments and managed dependencies. Install it using the official script:
Installation
curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker $USER
Log out and back in after adding yourself to the docker group for it to take effect.
sudo apt install docker-compose-plugin -y docker compose version
Portainer (container management UI)
docker volume create portainer_data docker run -d \ --name portainer \ --restart=always \ -p 9000:9000 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/data \ portainer/portainer-ce:latest
Access at http://your-ip:9000
Home Assistant
version: '3.8'
services:
homeassistant:
container_name: homeassistant
image: ghcr.io/home-assistant/home-assistant:stable
volumes:
- ./homeassistant:/config
- /etc/localtime:/etc/localtime:ro
restart: unless-stopped
privileged: true
network_mode: hostmkdir -p ~/homeassistant && cd ~/homeassistant docker compose up -d
MQTT + Node-RED + InfluxDB stack
version: '3.8'
services:
mosquitto:
container_name: mosquitto
image: eclipse-mosquitto:latest
ports:
- "1883:1883"
- "9001:9001"
volumes:
- ./mosquitto/config:/mosquitto/config
- ./mosquitto/data:/mosquitto/data
restart: unless-stopped
nodered:
container_name: nodered
image: nodered/node-red:latest
ports:
- "1880:1880"
volumes:
- ./nodered:/data
environment:
- TZ=America/Port_of_Spain
depends_on:
- mosquitto
restart: unless-stopped
influxdb:
container_name: influxdb
image: influxdb:2.7
ports:
- "8086:8086"
volumes:
- ./influxdb:/var/lib/influxdb2
environment:
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=admin
- DOCKER_INFLUXDB_INIT_PASSWORD=adminpassword
- DOCKER_INFLUXDB_INIT_ORG=myorg
- DOCKER_INFLUXDB_INIT_BUCKET=iot
restart: unless-stoppedUseful commands
docker ps # running containers docker ps -a # all containers docker logs -f container_name # follow logs docker stats # resource usage docker system prune -a # clean up unused data docker system df # disk usage
SD card wear. Docker writes heavily. If you are running containers long term, move the Docker data directory to an external SSD or eMMC to avoid wearing out the SD card.
RISC-V image compatibility
Not all Docker images support RISC-V yet. Look for images tagged riscv64 or multi-architecture builds. Portainer, Home Assistant, Node-RED, Mosquitto, InfluxDB, Grafana, Nginx, and PostgreSQL all have confirmed RISC-V support. Check Docker Hub before deploying anything else.
Web Server
Turn the Orange Pi RV2 into a web server running Nginx.
Install Nginx
sudo apt install nginx -y sudo systemctl start nginx sudo systemctl enable nginx
Your board is now serving the default Nginx page. Open a browser and navigate to the board's IP address to verify.
Deploy your site
Replace the default page with your own HTML:
sudo nano /var/www/html/index.html
HTTPS with Let's Encrypt
If the board has a public domain name, add a free SSL certificate:
sudo apt install certbot python3-certbot-nginx -y sudo certbot --nginx -d yourdomain.com sudo certbot renew --dry-run
IoT Gateway
The Orange Pi RV2 makes a capable IoT gateway. The Docker section on this page includes a ready-to-deploy stack with Mosquitto (MQTT broker), Node-RED (data processing and flow automation), and InfluxDB (time series storage). Together, they give you a complete pipeline: sensors publish data over MQTT, Node-RED transforms and routes it, and InfluxDB stores it for dashboards and analysis.
Add Grafana on top for visualization, or forward data upstream to AWS IoT, Azure IoT Hub, or Google Cloud IoT Core. The Orange Pi handles the local aggregation, buffering, and preprocessing so your cloud bill stays low and your sensors keep logging even when the internet connection drops.
Downloads
OS images, GPIO libraries, and documentation for the Orange Pi RV2.
External Resources
- Orange Pi official site
- CH32V003 platform page (bare metal RISC-V microcontroller)
- Rovari Studio (IDE for CH32V003 development)