skyhub_rover and skyhub_ugv. These are the edge of the fleet — the pieces you flash onto a microcontroller or bolt to a landing pad, not services that run in AWS.
None of the repos on this page are wired into the Gateway control plane in-repo. Nexus is standalone USB-tethered hardware. Rover and UGV are self-contained robot firmware that today speak their own HTTP/ESP-NOW protocol. Where they would attach to the platform is through the WS Proxy gamepad channel — described under Control-plane integration below. For the wider satellite map see the Ecosystem Overview.
Nexus AirHub — battery swap & charging
skyhub_nexus controls the airhub: the ground station a drone lands on to have its depleted LiPo swapped for a charged one. It is two loosely-coupled subsystems, both driven from a host PC over USB:
Swap actuation
A Python host talks to an Arduino Nano Every over USB serial (
/dev/ttyACM0, 9600 bps) to switch which battery is bonded to the charger via relays. Sketch: battery_swapping/Battery_Swapping_Station.ino.Charge cycling
A separate USB link drives an IMAX B6 Mini hobby charger (
pyusb) to run LiPo charge/discharge/storage cycles. Logic in imax_usb.py, defaults in imaxconfig.ini.Swap station serial protocol
The Arduino firmware (battery_swapping/Battery_Swapping_Station.ino) is a single-character command loop over serial at 9600 bps. It manages four relay-bonded battery slots — two 3-cell and two 4-cell — plus a voltage sensor and an error LED. Commands are sent as a bare ASCII digit (no quotes, line-ending optional), and every command echoes a status string back.
| Cmd | Action | Relay / pin | Success reply |
|---|---|---|---|
1 | Connect 3S battery #1 | battery_3s_1 (pin 4) | 1_OK_battery_3s_1 |
2 | Connect 4S battery #1 | battery_4s_1 (pin 5) | 2_OK_battery_4s_1 |
3 | Connect 4S battery #2 | battery_4s_2 (pin 6) | 3_OK_battery_4s_2 |
4 | Connect 3S battery #2 | battery_3s_2 (pin 7) | 4_OK_battery_3s_2 |
5 | Disconnect all batteries | all relays LOW | 5_OK_OFF_All_Batteries__Voltage_NOT_present |
6 | Report current status | (read-only) | 6_Connected_battery_3s_1 … / 6_All_Batteries_are_Disconnected |
main.py demonstrates the intended sequence — query status, then bond a slot:
skyhub_nexus/main.py
IMAX B6 charger control
imax_usb.py speaks the IMAX B6 Mini’s raw USB protocol via pyusb (device idVendor=0x0000, idProduct=0x0001). It builds a 64-byte settings packet (get_settings_packet()) encoding battery chemistry, cell count, charge mode, and current limits, then streams periodic reads back (energy, timer, voltage, current, temperatures, per-cell voltages). Battery chemistry and cell count come from imaxconfig.ini:
skyhub_nexus/imaxconfig.ini
imax_usb.py (btype, LiXX_CD_Modes, NiXX_CD_Modes, sensitivity, limits). detach.py is a one-shot helper that detaches the kernel driver and claims the USB device — run it if pyusb reports the interface is busy.
The IMAX driver builds on the community
imax_charger library (from imax_charger import imax) documented at GaryDyr/imaxcharger. It expects a USB-serial IMAX B6 clone that exposes the raw HID-style protocol; genuine units with only a balance port will not enumerate at 0x0000:0x0001.How Nexus fits the docking workflow
Autonomous docking is a choreography of the two subsystems: land the drone,6 to read station state, 5 to isolate, bond the target slot with 1–4, then run an IMAX cycle. There is no ROS or rosbridge involvement — a higher-level fleet automation layer (not present in this repo) would sequence Nexus alongside the drone’s landing. On-drone precision landing that gets a drone onto the pad is covered in YOLO Detection, ArUco Landing & Docking.
Ground vehicle firmware — Rover & UGV
skyhub_rover and skyhub_ugv are ESP32 firmware for WaveShare-class differential-drive ground robots. They are siblings with the same hardware DNA (dual DC motors + encoders, IMU, INA219 power monitor, SSD1306 OLED, SCServo bus servos) but different build systems and feature depth.
skyhub_rover | skyhub_ugv | |
|---|---|---|
| Build system | Arduino IDE sketch (OUR_ROVER/OUR_ROVER.ino) | PlatformIO (UGV/platformio.ini) |
| Board | ESP32 (esp32dev) | ESP32 (esp32dev), 4 MB flash, 240 MHz |
| Toolchain constraint | ESP32 Arduino core 2.0.17 — 3.x breaks the build | PlatformIO espressif32 platform |
| Web UI / control port | HTTP WebServer on :80 | HTTP WebServer on :80 |
| Command protocol | JSON {"T":<cmd>,...} via /cmd & /js | JSON {"T":<cmd>,...} via /js |
| Peer link | ESP-NOW (struct_message) | ESP-NOW (leader/follower modes) |
| IMU | QMI8658 + AK09918 mag | ICM-20948 (SparkFun) |
| Extras | RC PWM passthrough (RMT reader) | RoArm-M2 arm + gimbal module, named missions |
| Default AP SSID | UGV01_BASE / 12345678 | UGV (or RoArm) / 12345678 |
Rover firmware (skyhub_rover)
The Rover is an Arduino IDE single-sketch project — OUR_ROVER.ino pulls in a dozen .h modules (config.h, connectionFuncs.h, motorCtrl.h, IMU.h, busServoCtrl.h, …). On boot it initializes the IMU, servos, OLED and WiFi, then spins two FreeRTOS tasks (serialCtrl, motorSpeedGet) and starts the web server.
Networking (connectionFuncs.h, config.h): WiFi defaults to AP mode (DEFAULT_WIFI_MODE 1) advertising SSID UGV01_BASE (password 12345678); STA credentials for JSBZY-2.4G are compiled in, and a setTrySTA() fallback can try STA first and drop back to AP. The WebServer on port 80 exposes:
| Route | Purpose |
|---|---|
GET / | Serves the control web UI (WebPage.h) |
GET /deviceInfo | JSON telemetry: voltage, roll/pitch/yaw, magnetometer, IP/MAC/RSSI, speed |
GET /jsfb | Returns buffered JSON feedback |
GET /cmd?... | Simple motor control (cmd 1 = L/R speed, cmd 2 = speed tier) |
GET /js?... | Raw JSON command (deserialized and dispatched to cmdHandler()) |
"T" type (enumerated in config.h):
esp32-rmt-pwm-reader). A dead-man heartbeat zeroes both setpoints if no command arrives within HEART_BEAT (3000 ms) — preserve this when editing loop().
UGV firmware (skyhub_ugv)
The UGV is the more capable, PlatformIO-built sibling and the lower-level base controller. platformio.ini targets board = esp32dev, framework = arduino, 4 MB QIO flash at 80 MHz, f_cpu = 240 MHz, with all libraries vendored under UGV/lib/ (Adafruit SSD1306/GFX/BusIO, ArduinoJson, ESP32Encoder, ESP32Servo, INA219_WE, PID_v2, SCServo, SimpleKalmanFilter, SparkFun ICM-20948).
ugv_config.h selects the platform variant at compile time:
mainType—1RaspRover,2UGV Rover (default),3UGV BeastmoduleType—0base only (default),1RoArm-M2 robotic arm,2gimbalespNowMode—3follower (default),1/2leader modes; broadcast control on by default
UGV/data/ — wifiConfig.json / devConfig.json default to {"wifi_mode_on_boot":3, "sta_ssid":"JSBZY-2.4G", "ap_ssid":"RoArm", ...}. The HTTP server (http_server.h) again binds :80 with / (web UI) and /js (JSON command dispatch through jsonCmdReceiveHandler()).
The UGV’s "T" command vocabulary (src/json_cmd.h) is a superset of the Rover’s, adding differential-drive and arm/gimbal control:
UGV/src/main.cpp in the repo is a stripped RC-only build (reads RC PWM on GPIO 32/33 and drives the motors, nothing else). The full firmware — web server, ESP-NOW, arm/gimbal, JSON dispatch — lives in the module headers and main.cpp.bak. If you pio run the repo as-is you get the minimal RC bring-up, not the networked robot. Restore the full main.cpp before expecting the HTTP/JSON interface.Control-plane integration (WS Proxy)
Today both robots are driven standalone: connect to the robot’s AP (UGV01_BASE / RoArm), open its web UI on http://<ap-ip>/, and drive it with /cmd and /js. ESP-NOW lets one board relay commands to another as leader/follower.
To bring a ground robot onto the SkyHub control plane, the intended attach point is the WS Proxy gamepad relay, whose direct mode bridges a Dashboard client to ws://{drone_ip}:5001/gamepad (skyhub_ws_proxy/main.py:100). That :5001 gamepad-WebSocket convention is the one SkyCore’s onboard gamepad service exposes on a full drone.
Flashing quickstart
- Rover (Arduino IDE)
- UGV (PlatformIO)
Install ESP32 core 2.0.17
In Arduino IDE Boards Manager install esp32 by Espressif Systems, version
2.0.17 — not 3.x.Related repos & pages
- Carrier (
skyhub_carrier) is a stub repo — a README only (“carrier for the prob-e ground vehicle”), no code. It, along with Probe and Observer, is catalogued in Planned / Stub Repos. Prob-E is the hybrid air+ground vehicle the Carrier is meant to transport. - Manual control transport: WebSocket Gamepad Proxy and the Redis Channels & MAVLink Port Map.
- On-drone landing/docking that pairs with Nexus: YOLO Detection, ArUco Landing & Docking.
- The full satellite map and port reference: Ecosystem Overview.

