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  }