mirror of
https://github.com/IS1DI/x0gp.git
synced 2026-07-16 20:47:55 +00:00
feat: implement WebRTC signaling and UDP-based video streaming and control service
This commit is contained in:
@@ -2,8 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"math"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -269,6 +271,10 @@ func (s *WebRTCService) handleDataChannelMessage(session *driverSession, data []
|
||||
in.Gear = int(v)
|
||||
hasInput = true
|
||||
}
|
||||
if v, ok := payload["buttons"].(float64); ok {
|
||||
in.Buttons = uint32(v)
|
||||
hasInput = true
|
||||
}
|
||||
|
||||
if hasInput {
|
||||
s.logger.Debug("received inputs on webrtc data channel", "driver_id", session.driverID, "throttle", in.Throttle, "steering", in.Steering)
|
||||
@@ -277,19 +283,6 @@ func (s *WebRTCService) handleDataChannelMessage(session *driverSession, data []
|
||||
car.ApplyInput(in, 1.0/60.0)
|
||||
}
|
||||
|
||||
// Send commands to physical car
|
||||
var cmd string
|
||||
if in.Throttle > 0.1 {
|
||||
cmd = "start"
|
||||
} else {
|
||||
cmd = "stop"
|
||||
}
|
||||
if in.Steering < -0.2 {
|
||||
cmd = "left"
|
||||
} else if in.Steering > 0.2 {
|
||||
cmd = "right"
|
||||
}
|
||||
|
||||
// Resolve destination device
|
||||
var destDeviceID *uint8
|
||||
if car != nil && car.DeviceID != nil {
|
||||
@@ -300,14 +293,49 @@ func (s *WebRTCService) handleDataChannelMessage(session *driverSession, data []
|
||||
destDeviceID = &session.deviceID
|
||||
}
|
||||
|
||||
if destDeviceID != nil && cmd != "" && cmd != session.lastCommand {
|
||||
s.logger.Info("webrtc control state transition", "driver_id", session.driverID, "old_cmd", session.lastCommand, "new_cmd", cmd)
|
||||
session.lastCommand = cmd
|
||||
err := s.videoSvc.SendCommand(*destDeviceID, cmd)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to forward command to ESP32", "device_id", *destDeviceID, "cmd", cmd, "err", err)
|
||||
} else {
|
||||
s.logger.Info("forwarded command to ESP32 via UDP", "device_id", *destDeviceID, "cmd", cmd)
|
||||
if destDeviceID != nil {
|
||||
var packet [5]byte
|
||||
|
||||
// 1. Steer: mapping float64 [-1.0, 1.0] to [0..200] (100 is center)
|
||||
steerVal := int(math.Round((in.Steering + 1.0) * 100.0))
|
||||
if steerVal < 0 { steerVal = 0 }
|
||||
if steerVal > 200 { steerVal = 200 }
|
||||
packet[0] = uint8(steerVal)
|
||||
|
||||
// 2. Throttle: mapping float64 [0.0, 1.0] to [0..100]
|
||||
throttleVal := int(math.Round(in.Throttle * 100.0))
|
||||
if throttleVal < 0 { throttleVal = 0 }
|
||||
if throttleVal > 100 { throttleVal = 100 }
|
||||
packet[1] = uint8(throttleVal)
|
||||
|
||||
// 3. Brake: mapping float64 [0.0, 1.0] to [0..100]
|
||||
brakeVal := int(math.Round(in.Brake * 100.0))
|
||||
if brakeVal < 0 { brakeVal = 0 }
|
||||
if brakeVal > 100 { brakeVal = 100 }
|
||||
packet[2] = uint8(brakeVal)
|
||||
|
||||
// 4. Gear: map -1 to 0 (R), 0 to 1 (N), >=1 to 2 (D)
|
||||
gearVal := 1 // Neutral by default
|
||||
if in.Gear < 0 {
|
||||
gearVal = 0 // R
|
||||
} else if in.Gear > 0 {
|
||||
gearVal = 2 // D
|
||||
}
|
||||
packet[3] = uint8(gearVal)
|
||||
|
||||
// 5. Flags: bit 0 (DRS), bit 1 (Pit Limiter)
|
||||
packet[4] = uint8(in.Buttons & 0x03)
|
||||
|
||||
hexCmd := hex.EncodeToString(packet[:])
|
||||
if hexCmd != session.lastCommand {
|
||||
s.logger.Info("webrtc control state transition", "driver_id", session.driverID, "old_hex", session.lastCommand, "new_hex", hexCmd)
|
||||
session.lastCommand = hexCmd
|
||||
err := s.videoSvc.SendControlPacket(*destDeviceID, packet)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to forward control packet to ESP32", "device_id", *destDeviceID, "packet", packet, "err", err)
|
||||
} else {
|
||||
s.logger.Info("forwarded control packet to ESP32 via UDP", "device_id", *destDeviceID, "packet", packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user