gobot.io/x/gobot/v2@v2.1.0/platforms/holystone/hs200/hs200_driver.go (about) 1 package hs200 2 3 import ( 4 "net" 5 "sync" 6 "time" 7 8 "gobot.io/x/gobot/v2" 9 ) 10 11 // Driver represents the control information for the hs200 drone 12 type Driver struct { 13 name string 14 mutex *sync.RWMutex // Protect the command from concurrent access 15 stopc chan struct{} // Stop the flight loop goroutine 16 cmd []byte // the UDP command packet we keep sending the drone 17 enabled bool // Are we in an enabled state 18 tcpaddress string 19 udpaddress string 20 udpconn net.Conn // UDP connection to the drone 21 tcpconn net.Conn // TCP connection to the drone 22 } 23 24 // NewDriver creates a driver for the HolyStone hs200 25 func NewDriver(tcpaddress string, udpaddress string) *Driver { 26 command := []byte{ 27 0xff, // 2 byte header 28 0x04, 29 30 // Left joystick 31 0x7e, // throttle 0x00 - 0xff(?) 32 0x3f, // rotate left/right 33 34 // Right joystick 35 0xc0, // forward / backward 0x80 - 0xfe(?) 36 0x3f, // left / right 0x00 - 0x7e(?) 37 38 // Trim 39 0x90, // ? yaw (used as a setting to trim the yaw of the uav) 40 0x10, // ? pitch (used as a setting to trim the pitch of the uav) 41 0x10, // ? roll (used as a setting to trim the roll of the uav) 42 43 0x00, // flags/buttons 44 0x00, // checksum; 255 - ((sum of flight controls from index 1 to 9) % 256) 45 } 46 command[10] = checksum(command) 47 48 return &Driver{name: gobot.DefaultName("HS200"), stopc: make(chan struct{}), 49 tcpaddress: tcpaddress, udpaddress: udpaddress, cmd: command, mutex: &sync.RWMutex{}} 50 } 51 52 // Name returns the name of the device. 53 func (d *Driver) Name() string { return d.name } 54 55 // SetName sets the name of the device. 56 func (d *Driver) SetName(n string) { d.name = n } 57 58 // Connection returns the Connection of the device. 59 func (d *Driver) Connection() gobot.Connection { return nil } 60 61 // Start starts the driver. 62 func (d *Driver) Start() (err error) { 63 tc, terr := net.Dial("tcp", d.tcpaddress) 64 if terr != nil { 65 return terr 66 } 67 uc, uerr := net.Dial("udp4", d.udpaddress) 68 if uerr != nil { 69 return uerr 70 } 71 72 d.udpconn = uc 73 d.tcpconn = tc 74 75 return 76 } 77 78 // Halt stops the driver. 79 func (d *Driver) Halt() (err error) { 80 d.stop() 81 return 82 } 83 84 func (d *Driver) stop() { 85 d.mutex.Lock() 86 defer d.mutex.Unlock() 87 d.enabled = false 88 } 89 90 func (d *Driver) flightLoop(stopc chan struct{}) { 91 udpTick := time.NewTicker(50 * time.Millisecond) 92 defer udpTick.Stop() 93 tcpTick := time.NewTicker(1000 * time.Millisecond) 94 defer tcpTick.Stop() 95 for { 96 select { 97 case <-udpTick.C: 98 d.sendUDP() 99 case <-tcpTick.C: 100 // Send TCP commands from here once we figure out what they do... 101 case <-stopc: 102 d.stop() 103 return 104 } 105 } 106 } 107 108 func checksum(c []byte) byte { 109 var sum byte 110 for i := 1; i < 10; i++ { 111 sum += c[i] 112 } 113 return 255 - sum 114 } 115 116 func (d *Driver) sendUDP() { 117 d.mutex.RLock() 118 defer d.mutex.RUnlock() 119 d.udpconn.Write(d.cmd) 120 } 121 122 // Enable enables the drone to start flying. 123 func (d Driver) Enable() { 124 d.mutex.Lock() 125 defer d.mutex.Unlock() 126 if !d.enabled { 127 go d.flightLoop(d.stopc) 128 d.enabled = true 129 } 130 } 131 132 // Disable disables the drone from flying. 133 func (d Driver) Disable() { 134 d.mutex.Lock() 135 defer d.mutex.Unlock() 136 if d.enabled { 137 d.stopc <- struct{}{} 138 } 139 } 140 141 // TakeOff tells drones to liftoff and start flying. 142 func (d Driver) TakeOff() { 143 d.mutex.Lock() 144 d.cmd[9] = 0x40 145 d.cmd[10] = checksum(d.cmd) 146 d.mutex.Unlock() 147 time.Sleep(500 * time.Millisecond) 148 d.mutex.Lock() 149 d.cmd[9] = 0x04 150 d.cmd[10] = checksum(d.cmd) 151 d.mutex.Unlock() 152 } 153 154 // Land tells drone to come in for landing. 155 func (d Driver) Land() { 156 d.mutex.Lock() 157 d.cmd[9] = 0x80 158 d.cmd[10] = checksum(d.cmd) 159 d.mutex.Unlock() 160 time.Sleep(500 * time.Millisecond) 161 d.mutex.Lock() 162 d.cmd[9] = 0x04 163 d.cmd[10] = checksum(d.cmd) 164 d.mutex.Unlock() 165 } 166 167 // floatToCmdByte converts a float in the range of -1 to +1 to an integer command 168 func floatToCmdByte(cmd float32, mid byte, maxv byte) byte { 169 if cmd > 1.0 { 170 cmd = 1.0 171 } 172 if cmd < -1.0 { 173 cmd = -1.0 174 } 175 cmd = cmd * float32(maxv) 176 bval := byte(cmd + float32(mid) + 0.5) 177 return bval 178 } 179 180 // Throttle sends the drone up from a hover (or down if speed is negative) 181 func (d *Driver) Throttle(speed float32) { 182 d.mutex.Lock() 183 defer d.mutex.Unlock() 184 d.cmd[2] = floatToCmdByte(speed, 0x7e, 0x7e) 185 d.cmd[10] = checksum(d.cmd) 186 } 187 188 // Rotate rotates the drone (yaw) 189 func (d *Driver) Rotate(speed float32) { 190 d.mutex.Lock() 191 defer d.mutex.Unlock() 192 d.cmd[3] = floatToCmdByte(speed, 0x3f, 0x3f) 193 d.cmd[10] = checksum(d.cmd) 194 } 195 196 // Forward sends the drone forward (or backwards if speed is negative, pitch the drone) 197 func (d *Driver) Forward(speed float32) { 198 speed = -speed 199 d.mutex.Lock() 200 defer d.mutex.Unlock() 201 d.cmd[4] = floatToCmdByte(speed, 0xc0, 0x3f) 202 d.cmd[10] = checksum(d.cmd) 203 } 204 205 // Right moves the drone to the right (or left if speed is negative, rolls the drone) 206 func (d *Driver) Right(speed float32) { 207 d.mutex.Lock() 208 defer d.mutex.Unlock() 209 d.cmd[5] = floatToCmdByte(speed, 0x3f, 0x3f) 210 d.cmd[10] = checksum(d.cmd) 211 }