Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save sploders101/fef72bfa20e6ab176c390d6d67b942ea to your computer and use it in GitHub Desktop.

Select an option

Save sploders101/fef72bfa20e6ab176c390d6d67b942ea to your computer and use it in GitHub Desktop.
Roborock Gamepad Control
package main
import (
"bytes"
"encoding/json"
"fmt"
"math"
"net/http"
"os"
"time"
"github.com/veandco/go-sdl2/sdl"
)
const (
deadzone = 4000
emitInterval = 250 * time.Millisecond
maxAxis = 32767.0
endpoint = "http://192.168.1.26/api/v2/robot/capabilities/HighResolutionManualControlCapability"
)
type Payload struct {
Action string `json:"action"`
Vector struct {
Velocity float64 `json:"velocity"`
Angle float64 `json:"angle"`
} `json:"vector"`
}
func applyDeadzone(v int16) int16 {
if math.Abs(float64(v)) < deadzone {
return 0
}
return v
}
func normalize(v int16) float64 {
f := float64(v)
if f < -maxAxis {
f = -maxAxis
}
return f / maxAxis
}
func sendCommand(client *http.Client, velocity, angle float64) {
payload := Payload{
Action: "move",
}
payload.Vector.Velocity = velocity
payload.Vector.Angle = angle
data, err := json.Marshal(payload)
if err != nil {
fmt.Println("JSON error:", err)
return
}
req, err := http.NewRequest("PUT", endpoint, bytes.NewBuffer(data))
if err != nil {
fmt.Println("Request error:", err)
return
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
fmt.Println("HTTP error:", err)
return
}
defer resp.Body.Close()
}
func main() {
if err := sdl.Init(sdl.INIT_GAMECONTROLLER); err != nil {
fmt.Fprintf(os.Stderr, "SDL init failed: %s\n", err)
os.Exit(1)
}
defer sdl.Quit()
var controller *sdl.GameController
for i := 0; i < sdl.NumJoysticks(); i++ {
if sdl.IsGameController(i) {
c := sdl.GameControllerOpen(i)
if c != nil {
controller = c
fmt.Printf("Connected: %s\n", controller.Name())
break
}
}
}
if controller == nil {
fmt.Println("No controller found.")
return
}
defer controller.Close()
var currentX, currentY int16
client := &http.Client{
Timeout: 2 * time.Second,
}
ticker := time.NewTicker(emitInterval)
defer ticker.Stop()
fmt.Println("Sending continuous control commands...")
for {
select {
// Update state continuously
default:
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch e := event.(type) {
case *sdl.ControllerAxisEvent:
switch e.Axis {
case 0:
currentX = applyDeadzone(e.Value)
case 1:
currentY = applyDeadzone(e.Value)
}
case *sdl.QuitEvent:
return
}
}
// Always send at interval
case <-ticker.C:
normX := normalize(currentX)
normY := normalize(currentY)
velocity := -normY // up = positive
angle := normX * 90.0 // -90 to 90
go sendCommand(client, velocity, angle)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment