gobot.io/x/gobot/v2@v2.1.0/platforms/dji/tello/driver.go (about)

     1  package tello
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"math"
    10  	"net"
    11  	"strconv"
    12  	"sync"
    13  	"sync/atomic"
    14  	"time"
    15  
    16  	"gobot.io/x/gobot/v2"
    17  )
    18  
    19  const (
    20  	// BounceEvent event
    21  	BounceEvent = "bounce"
    22  
    23  	// ConnectedEvent event
    24  	ConnectedEvent = "connected"
    25  
    26  	// FlightDataEvent event
    27  	FlightDataEvent = "flightdata"
    28  
    29  	// TakeoffEvent event
    30  	TakeoffEvent = "takeoff"
    31  
    32  	// LandingEvent event
    33  	LandingEvent = "landing"
    34  
    35  	// PalmLandingEvent event
    36  	PalmLandingEvent = "palm-landing"
    37  
    38  	// FlipEvent event
    39  	FlipEvent = "flip"
    40  
    41  	// TimeEvent event
    42  	TimeEvent = "time"
    43  
    44  	// LogEvent event
    45  	LogEvent = "log"
    46  
    47  	// WifiDataEvent event
    48  	WifiDataEvent = "wifidata"
    49  
    50  	// LightStrengthEvent event
    51  	LightStrengthEvent = "lightstrength"
    52  
    53  	// SetExposureEvent event
    54  	SetExposureEvent = "setexposure"
    55  
    56  	// VideoFrameEvent event
    57  	VideoFrameEvent = "videoframe"
    58  
    59  	// SetVideoEncoderRateEvent event
    60  	SetVideoEncoderRateEvent = "setvideoencoder"
    61  )
    62  
    63  // the 16-bit messages and commands stored in bytes 6 & 5 of the packet
    64  const (
    65  	messageStart   = 0x00cc // 204
    66  	wifiMessage    = 0x001a // 26
    67  	videoRateQuery = 0x0028 // 40
    68  	lightMessage   = 0x0035 // 53
    69  	flightMessage  = 0x0056 // 86
    70  	logMessage     = 0x1050 // 4176
    71  
    72  	videoEncoderRateCommand = 0x0020 // 32
    73  	videoStartCommand       = 0x0025 // 37
    74  	exposureCommand         = 0x0034 // 52
    75  	timeCommand             = 0x0046 // 70
    76  	stickCommand            = 0x0050 // 80
    77  	takeoffCommand          = 0x0054 // 84
    78  	landCommand             = 0x0055 // 85
    79  	flipCommand             = 0x005c // 92
    80  	throwtakeoffCommand     = 0x005d // 93
    81  	palmLandCommand         = 0x005e // 94
    82  	bounceCommand           = 0x1053 // 4179
    83  )
    84  
    85  // FlipType is used for the various flips supported by the Tello.
    86  type FlipType int
    87  
    88  const (
    89  	// FlipFront flips forward.
    90  	FlipFront FlipType = 0
    91  
    92  	// FlipLeft flips left.
    93  	FlipLeft FlipType = 1
    94  
    95  	// FlipBack flips backwards.
    96  	FlipBack FlipType = 2
    97  
    98  	// FlipRight flips to the right.
    99  	FlipRight FlipType = 3
   100  
   101  	// FlipForwardLeft flips forwards and to the left.
   102  	FlipForwardLeft FlipType = 4
   103  
   104  	// FlipBackLeft flips backwards and to the left.
   105  	FlipBackLeft FlipType = 5
   106  
   107  	// FlipBackRight flips backwards and to the right.
   108  	FlipBackRight FlipType = 6
   109  
   110  	// FlipForwardRight flips forewards and to the right.
   111  	FlipForwardRight FlipType = 7
   112  )
   113  
   114  // VideoBitRate is used to set the bit rate for the streaming video returned by the Tello.
   115  type VideoBitRate int
   116  
   117  const (
   118  	// VideoBitRateAuto sets the bitrate for streaming video to auto-adjust.
   119  	VideoBitRateAuto VideoBitRate = 0
   120  
   121  	// VideoBitRate1M sets the bitrate for streaming video to 1 Mb/s.
   122  	VideoBitRate1M VideoBitRate = 1
   123  
   124  	// VideoBitRate15M sets the bitrate for streaming video to 1.5 Mb/s
   125  	VideoBitRate15M VideoBitRate = 2
   126  
   127  	// VideoBitRate2M sets the bitrate for streaming video to 2 Mb/s.
   128  	VideoBitRate2M VideoBitRate = 3
   129  
   130  	// VideoBitRate3M sets the bitrate for streaming video to 3 Mb/s.
   131  	VideoBitRate3M VideoBitRate = 4
   132  
   133  	// VideoBitRate4M sets the bitrate for streaming video to 4 Mb/s.
   134  	VideoBitRate4M VideoBitRate = 5
   135  )
   136  
   137  // FlightData packet returned by the Tello.
   138  //
   139  // The meaning of some fields is not documented. If you learned more, please, contribute.
   140  // See https://github.com/hybridgroup/gobot/issues/798.
   141  type FlightData struct {
   142  	BatteryLow               bool
   143  	BatteryLower             bool
   144  	BatteryPercentage        int8 // How much battery left [in %].
   145  	CameraState              int8
   146  	DroneBatteryLeft         int16
   147  	DroneFlyTimeLeft         int16
   148  	DroneHover               bool // If the drone is in the air and not moving.
   149  	EmOpen                   bool
   150  	Flying                   bool  // If the drone is currently in the air.
   151  	OnGround                 bool  // If the drone is currently on the ground.
   152  	EastSpeed                int16 // Movement speed towards East [in cm/s]. Negative if moving west.
   153  	ElectricalMachineryState int16
   154  	FactoryMode              bool
   155  	FlyMode                  int8
   156  	FlyTime                  int16 // How long since take off [in s/10].
   157  	FrontIn                  bool
   158  	FrontLSC                 bool
   159  	FrontOut                 bool
   160  	GravityState             bool
   161  	VerticalSpeed            int16 // Movement speed up [in cm/s].
   162  	Height                   int16 // The height [in decimeters].
   163  	ImuCalibrationState      int8  // The IMU calibration step (when doing IMU calibration).
   164  	NorthSpeed               int16 // Movement speed towards North [in cm/s]. Negative if moving South.
   165  	ThrowFlyTimer            int8
   166  
   167  	// Warnings:
   168  	DownVisualState bool // If the ground is visible by the down camera.
   169  	BatteryState    bool // If there is an issue with battery.
   170  	ImuState        bool // If drone needs IMU (Inertial Measurement Unit) calibration.
   171  	OutageRecording bool // If there is an issue with video recording.
   172  	PowerState      bool // If there is an issue with power supply.
   173  	PressureState   bool // If there is an issue with air pressure.
   174  	TemperatureHigh bool // If drone is overheating.
   175  	WindState       bool // If the wind is too strong.
   176  }
   177  
   178  // WifiData packet returned by the Tello
   179  type WifiData struct {
   180  	Disturb  int8
   181  	Strength int8
   182  }
   183  
   184  // Driver represents the DJI Tello drone
   185  type Driver struct {
   186  	name           string
   187  	reqAddr        string
   188  	cmdConn        io.WriteCloser // UDP connection to send/receive drone commands
   189  	videoConn      *net.UDPConn   // UDP connection for drone video
   190  	respPort       string
   191  	videoPort      string
   192  	cmdMutex       sync.Mutex
   193  	seq            int16
   194  	rx, ry, lx, ly float32
   195  	throttle       int
   196  	bouncing       bool
   197  	gobot.Eventer
   198  	doneCh            chan struct{}
   199  	doneChReaderCount int32
   200  }
   201  
   202  // NewDriver creates a driver for the Tello drone. Pass in the UDP port to use for the responses
   203  // from the drone.
   204  func NewDriver(port string) *Driver {
   205  	d := &Driver{name: gobot.DefaultName("Tello"),
   206  		reqAddr:   "192.168.10.1:8889",
   207  		respPort:  port,
   208  		videoPort: "11111",
   209  		Eventer:   gobot.NewEventer(),
   210  		doneCh:    make(chan struct{}, 1),
   211  	}
   212  
   213  	d.AddEvent(ConnectedEvent)
   214  	d.AddEvent(FlightDataEvent)
   215  	d.AddEvent(TakeoffEvent)
   216  	d.AddEvent(LandingEvent)
   217  	d.AddEvent(PalmLandingEvent)
   218  	d.AddEvent(BounceEvent)
   219  	d.AddEvent(FlipEvent)
   220  	d.AddEvent(TimeEvent)
   221  	d.AddEvent(LogEvent)
   222  	d.AddEvent(WifiDataEvent)
   223  	d.AddEvent(LightStrengthEvent)
   224  	d.AddEvent(SetExposureEvent)
   225  	d.AddEvent(VideoFrameEvent)
   226  	d.AddEvent(SetVideoEncoderRateEvent)
   227  
   228  	return d
   229  }
   230  
   231  // NewDriverWithIP creates a driver for the Tello EDU drone. Pass in the ip address and UDP port to use for the responses
   232  // from the drone.
   233  func NewDriverWithIP(ip string, port string) *Driver {
   234  	d := &Driver{name: gobot.DefaultName("Tello"),
   235  		reqAddr:   ip + ":8889",
   236  		respPort:  port,
   237  		videoPort: "11111",
   238  		Eventer:   gobot.NewEventer(),
   239  	}
   240  
   241  	d.AddEvent(ConnectedEvent)
   242  	d.AddEvent(FlightDataEvent)
   243  	d.AddEvent(TakeoffEvent)
   244  	d.AddEvent(LandingEvent)
   245  	d.AddEvent(PalmLandingEvent)
   246  	d.AddEvent(BounceEvent)
   247  	d.AddEvent(FlipEvent)
   248  	d.AddEvent(TimeEvent)
   249  	d.AddEvent(LogEvent)
   250  	d.AddEvent(WifiDataEvent)
   251  	d.AddEvent(LightStrengthEvent)
   252  	d.AddEvent(SetExposureEvent)
   253  	d.AddEvent(VideoFrameEvent)
   254  	d.AddEvent(SetVideoEncoderRateEvent)
   255  
   256  	return d
   257  }
   258  
   259  // Name returns the name of the device.
   260  func (d *Driver) Name() string { return d.name }
   261  
   262  // SetName sets the name of the device.
   263  func (d *Driver) SetName(n string) { d.name = n }
   264  
   265  // Connection returns the Connection of the device.
   266  func (d *Driver) Connection() gobot.Connection { return nil }
   267  
   268  // Start starts the driver.
   269  func (d *Driver) Start() error {
   270  	reqAddr, err := net.ResolveUDPAddr("udp", d.reqAddr)
   271  	if err != nil {
   272  		fmt.Println(err)
   273  		return err
   274  	}
   275  	respPort, err := net.ResolveUDPAddr("udp", ":"+d.respPort)
   276  	if err != nil {
   277  		fmt.Println(err)
   278  		return err
   279  	}
   280  	cmdConn, err := net.DialUDP("udp", respPort, reqAddr)
   281  	if err != nil {
   282  		fmt.Println(err)
   283  		return err
   284  	}
   285  	d.cmdConn = cmdConn
   286  
   287  	// handle responses
   288  	d.addDoneChReaderCount(1)
   289  	go func() {
   290  		defer d.addDoneChReaderCount(-1)
   291  
   292  		d.On(d.Event(ConnectedEvent), func(interface{}) {
   293  			d.SendDateTime()
   294  			d.processVideo()
   295  		})
   296  
   297  	cmdLoop:
   298  		for {
   299  			select {
   300  			case <-d.doneCh:
   301  				break cmdLoop
   302  			default:
   303  				err := d.handleResponse(cmdConn)
   304  				if err != nil {
   305  					fmt.Println("response parse error:", err)
   306  				}
   307  			}
   308  		}
   309  	}()
   310  
   311  	// starts notifications coming from drone to video port normally 11111
   312  	d.SendCommand(d.connectionString())
   313  
   314  	// send stick commands
   315  	d.addDoneChReaderCount(1)
   316  	go func() {
   317  		defer d.addDoneChReaderCount(-1)
   318  
   319  	stickCmdLoop:
   320  		for {
   321  			select {
   322  			case <-d.doneCh:
   323  				break stickCmdLoop
   324  			default:
   325  				err := d.SendStickCommand()
   326  				if err != nil {
   327  					fmt.Println("stick command error:", err)
   328  				}
   329  				time.Sleep(20 * time.Millisecond)
   330  			}
   331  		}
   332  	}()
   333  
   334  	return nil
   335  }
   336  
   337  // Halt stops the driver.
   338  func (d *Driver) Halt() (err error) {
   339  	// send a landing command when we disconnect, and give it 500ms to be received before we shutdown
   340  	if d.cmdConn != nil {
   341  		d.Land()
   342  	}
   343  	time.Sleep(500 * time.Millisecond)
   344  
   345  	if d.cmdConn != nil {
   346  		d.cmdConn.Close()
   347  	}
   348  
   349  	if d.videoConn != nil {
   350  		d.videoConn.Close()
   351  	}
   352  	readerCount := atomic.LoadInt32(&d.doneChReaderCount)
   353  	for i := 0; i < int(readerCount); i++ {
   354  		d.doneCh <- struct{}{}
   355  	}
   356  
   357  	return
   358  }
   359  
   360  // TakeOff tells drones to liftoff and start flying.
   361  func (d *Driver) TakeOff() (err error) {
   362  	buf, _ := d.createPacket(takeoffCommand, 0x68, 0)
   363  	d.seq++
   364  	binary.Write(buf, binary.LittleEndian, d.seq)
   365  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   366  
   367  	_, err = d.cmdConn.Write(buf.Bytes())
   368  	return
   369  }
   370  
   371  // Throw & Go support
   372  func (d *Driver) ThrowTakeOff() (err error) {
   373  	buf, _ := d.createPacket(throwtakeoffCommand, 0x48, 0)
   374  	d.seq++
   375  	binary.Write(buf, binary.LittleEndian, d.seq)
   376  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   377  
   378  	_, err = d.cmdConn.Write(buf.Bytes())
   379  	return
   380  }
   381  
   382  // Land tells drone to come in for landing.
   383  func (d *Driver) Land() (err error) {
   384  	buf, _ := d.createPacket(landCommand, 0x68, 1)
   385  	d.seq++
   386  	binary.Write(buf, binary.LittleEndian, d.seq)
   387  	binary.Write(buf, binary.LittleEndian, byte(0x00))
   388  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   389  
   390  	_, err = d.cmdConn.Write(buf.Bytes())
   391  	return
   392  }
   393  
   394  // StopLanding tells drone to stop landing.
   395  func (d *Driver) StopLanding() (err error) {
   396  	buf, _ := d.createPacket(landCommand, 0x68, 1)
   397  	d.seq++
   398  	binary.Write(buf, binary.LittleEndian, d.seq)
   399  	binary.Write(buf, binary.LittleEndian, byte(0x01))
   400  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   401  
   402  	_, err = d.cmdConn.Write(buf.Bytes())
   403  	return
   404  }
   405  
   406  // PalmLand tells drone to come in for a hand landing.
   407  func (d *Driver) PalmLand() (err error) {
   408  	buf, _ := d.createPacket(palmLandCommand, 0x68, 1)
   409  	d.seq++
   410  	binary.Write(buf, binary.LittleEndian, d.seq)
   411  	binary.Write(buf, binary.LittleEndian, byte(0x00))
   412  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   413  
   414  	_, err = d.cmdConn.Write(buf.Bytes())
   415  	return
   416  }
   417  
   418  // StartVideo tells Tello to send start info (SPS/PPS) for video stream.
   419  func (d *Driver) StartVideo() (err error) {
   420  	buf, _ := d.createPacket(videoStartCommand, 0x60, 0)
   421  	binary.Write(buf, binary.LittleEndian, int16(0x00)) // seq = 0
   422  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   423  
   424  	_, err = d.cmdConn.Write(buf.Bytes())
   425  	return
   426  }
   427  
   428  // SetExposure sets the drone camera exposure level. Valid levels are 0, 1, and 2.
   429  func (d *Driver) SetExposure(level int) (err error) {
   430  	if level < 0 || level > 2 {
   431  		return errors.New("Invalid exposure level")
   432  	}
   433  
   434  	buf, _ := d.createPacket(exposureCommand, 0x48, 1)
   435  	d.seq++
   436  	binary.Write(buf, binary.LittleEndian, d.seq)
   437  	binary.Write(buf, binary.LittleEndian, byte(level))
   438  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   439  
   440  	_, err = d.cmdConn.Write(buf.Bytes())
   441  	return
   442  }
   443  
   444  // SetVideoEncoderRate sets the drone video encoder rate.
   445  func (d *Driver) SetVideoEncoderRate(rate VideoBitRate) (err error) {
   446  	buf, _ := d.createPacket(videoEncoderRateCommand, 0x68, 1)
   447  	d.seq++
   448  	binary.Write(buf, binary.LittleEndian, d.seq)
   449  	binary.Write(buf, binary.LittleEndian, byte(rate))
   450  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   451  
   452  	_, err = d.cmdConn.Write(buf.Bytes())
   453  	return
   454  }
   455  
   456  // SetFastMode sets the drone throttle to 1.
   457  func (d *Driver) SetFastMode() error {
   458  	d.cmdMutex.Lock()
   459  	defer d.cmdMutex.Unlock()
   460  
   461  	d.throttle = 1
   462  	return nil
   463  }
   464  
   465  // SetSlowMode sets the drone throttle to 0.
   466  func (d *Driver) SetSlowMode() error {
   467  	d.cmdMutex.Lock()
   468  	defer d.cmdMutex.Unlock()
   469  
   470  	d.throttle = 0
   471  	return nil
   472  }
   473  
   474  // Rate queries the current video bit rate.
   475  func (d *Driver) Rate() (err error) {
   476  	buf, _ := d.createPacket(videoRateQuery, 0x48, 0)
   477  	d.seq++
   478  	binary.Write(buf, binary.LittleEndian, d.seq)
   479  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   480  
   481  	_, err = d.cmdConn.Write(buf.Bytes())
   482  	return
   483  }
   484  
   485  // bound is a naive implementation that returns the smaller of x or y.
   486  func bound(x, y float32) float32 {
   487  	if x < -y {
   488  		return -y
   489  	}
   490  	if x > y {
   491  		return y
   492  	}
   493  	return x
   494  }
   495  
   496  // Vector returns the current motion vector.
   497  // Values are from 0 to 1.
   498  // x, y, z denote forward, side and vertical translation,
   499  // and psi  yaw (rotation around the z-axis).
   500  func (d *Driver) Vector() (x, y, z, psi float32) {
   501  	return d.ry, d.rx, d.ly, d.lx
   502  }
   503  
   504  // AddVector adds to the current motion vector.
   505  // Pass values from 0 to 1.
   506  // See Vector() for the frame of reference.
   507  func (d *Driver) AddVector(x, y, z, psi float32) error {
   508  	d.cmdMutex.Lock()
   509  	defer d.cmdMutex.Unlock()
   510  
   511  	d.ry = bound(d.ry+x, 1)
   512  	d.rx = bound(d.rx+y, 1)
   513  	d.ly = bound(d.ly+z, 1)
   514  	d.lx = bound(d.lx+psi, 1)
   515  
   516  	return nil
   517  }
   518  
   519  // SetVector sets the current motion vector.
   520  // Pass values from 0 to 1.
   521  // See Vector() for the frame of reference.
   522  func (d *Driver) SetVector(x, y, z, psi float32) error {
   523  	d.cmdMutex.Lock()
   524  	defer d.cmdMutex.Unlock()
   525  
   526  	d.ry = x
   527  	d.rx = y
   528  	d.ly = z
   529  	d.lx = psi
   530  
   531  	return nil
   532  }
   533  
   534  // SetX sets the x component of the current motion vector
   535  // Pass values from 0 to 1.
   536  // See Vector() for the frame of reference.
   537  func (d *Driver) SetX(x float32) error {
   538  	d.cmdMutex.Lock()
   539  	defer d.cmdMutex.Unlock()
   540  
   541  	d.ry = x
   542  
   543  	return nil
   544  }
   545  
   546  // SetY sets the y component of the current motion vector
   547  // Pass values from 0 to 1.
   548  // See Vector() for the frame of reference.
   549  func (d *Driver) SetY(y float32) error {
   550  	d.cmdMutex.Lock()
   551  	defer d.cmdMutex.Unlock()
   552  
   553  	d.rx = y
   554  
   555  	return nil
   556  }
   557  
   558  // SetZ sets the z component of the current motion vector
   559  // Pass values from 0 to 1.
   560  // See Vector() for the frame of reference.
   561  func (d *Driver) SetZ(z float32) error {
   562  	d.cmdMutex.Lock()
   563  	defer d.cmdMutex.Unlock()
   564  
   565  	d.ly = z
   566  
   567  	return nil
   568  }
   569  
   570  // SetPsi sets the psi component (yaw) of the current motion vector
   571  // Pass values from 0 to 1.
   572  // See Vector() for the frame of reference.
   573  func (d *Driver) SetPsi(psi float32) error {
   574  	d.cmdMutex.Lock()
   575  	defer d.cmdMutex.Unlock()
   576  
   577  	d.lx = psi
   578  
   579  	return nil
   580  }
   581  
   582  // Up tells the drone to ascend. Pass in an int from 0-100.
   583  func (d *Driver) Up(val int) error {
   584  	d.cmdMutex.Lock()
   585  	defer d.cmdMutex.Unlock()
   586  
   587  	d.ly = float32(val) / 100.0
   588  	return nil
   589  }
   590  
   591  // Down tells the drone to descend. Pass in an int from 0-100.
   592  func (d *Driver) Down(val int) error {
   593  	d.cmdMutex.Lock()
   594  	defer d.cmdMutex.Unlock()
   595  
   596  	d.ly = float32(val) / 100.0 * -1
   597  	return nil
   598  }
   599  
   600  // Forward tells the drone to go forward. Pass in an int from 0-100.
   601  func (d *Driver) Forward(val int) error {
   602  	d.cmdMutex.Lock()
   603  	defer d.cmdMutex.Unlock()
   604  
   605  	d.ry = float32(val) / 100.0
   606  	return nil
   607  }
   608  
   609  // Backward tells drone to go in reverse. Pass in an int from 0-100.
   610  func (d *Driver) Backward(val int) error {
   611  	d.cmdMutex.Lock()
   612  	defer d.cmdMutex.Unlock()
   613  
   614  	d.ry = float32(val) / 100.0 * -1
   615  	return nil
   616  }
   617  
   618  // Right tells drone to go right. Pass in an int from 0-100.
   619  func (d *Driver) Right(val int) error {
   620  	d.cmdMutex.Lock()
   621  	defer d.cmdMutex.Unlock()
   622  
   623  	d.rx = float32(val) / 100.0
   624  	return nil
   625  }
   626  
   627  // Left tells drone to go left. Pass in an int from 0-100.
   628  func (d *Driver) Left(val int) error {
   629  	d.cmdMutex.Lock()
   630  	defer d.cmdMutex.Unlock()
   631  
   632  	d.rx = float32(val) / 100.0 * -1
   633  	return nil
   634  }
   635  
   636  // Clockwise tells drone to rotate in a clockwise direction. Pass in an int from 0-100.
   637  func (d *Driver) Clockwise(val int) error {
   638  	d.cmdMutex.Lock()
   639  	defer d.cmdMutex.Unlock()
   640  
   641  	d.lx = float32(val) / 100.0
   642  	return nil
   643  }
   644  
   645  // CounterClockwise tells drone to rotate in a counter-clockwise direction.
   646  // Pass in an int from 0-100.
   647  func (d *Driver) CounterClockwise(val int) error {
   648  	d.cmdMutex.Lock()
   649  	defer d.cmdMutex.Unlock()
   650  
   651  	d.lx = float32(val) / 100.0 * -1
   652  	return nil
   653  }
   654  
   655  // Hover tells the drone to stop moving on the X, Y, and Z axes and stay in place
   656  func (d *Driver) Hover() {
   657  	d.cmdMutex.Lock()
   658  	defer d.cmdMutex.Unlock()
   659  
   660  	d.rx = float32(0)
   661  	d.ry = float32(0)
   662  	d.lx = float32(0)
   663  	d.ly = float32(0)
   664  }
   665  
   666  // CeaseRotation stops any rotational motion
   667  func (d *Driver) CeaseRotation() {
   668  	d.cmdMutex.Lock()
   669  	defer d.cmdMutex.Unlock()
   670  
   671  	d.lx = float32(0)
   672  }
   673  
   674  // Bounce tells drone to start/stop performing the bouncing action
   675  func (d *Driver) Bounce() (err error) {
   676  	buf, _ := d.createPacket(bounceCommand, 0x68, 1)
   677  	d.seq++
   678  	binary.Write(buf, binary.LittleEndian, d.seq)
   679  	if d.bouncing {
   680  		binary.Write(buf, binary.LittleEndian, byte(0x31))
   681  	} else {
   682  		binary.Write(buf, binary.LittleEndian, byte(0x30))
   683  	}
   684  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   685  	_, err = d.cmdConn.Write(buf.Bytes())
   686  	d.bouncing = !d.bouncing
   687  	return
   688  }
   689  
   690  // Flip tells drone to flip
   691  func (d *Driver) Flip(direction FlipType) (err error) {
   692  	buf, _ := d.createPacket(flipCommand, 0x70, 1)
   693  	d.seq++
   694  	binary.Write(buf, binary.LittleEndian, d.seq)
   695  	binary.Write(buf, binary.LittleEndian, byte(direction))
   696  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   697  
   698  	_, err = d.cmdConn.Write(buf.Bytes())
   699  	return
   700  }
   701  
   702  // FrontFlip tells the drone to perform a front flip.
   703  func (d *Driver) FrontFlip() (err error) {
   704  	return d.Flip(FlipFront)
   705  }
   706  
   707  // BackFlip tells the drone to perform a back flip.
   708  func (d *Driver) BackFlip() (err error) {
   709  	return d.Flip(FlipBack)
   710  }
   711  
   712  // RightFlip tells the drone to perform a flip to the right.
   713  func (d *Driver) RightFlip() (err error) {
   714  	return d.Flip(FlipRight)
   715  }
   716  
   717  // LeftFlip tells the drone to perform a flip to the left.
   718  func (d *Driver) LeftFlip() (err error) {
   719  	return d.Flip(FlipLeft)
   720  }
   721  
   722  // ParseFlightData from drone
   723  func (d *Driver) ParseFlightData(b []byte) (fd *FlightData, err error) {
   724  	buf := bytes.NewReader(b)
   725  	fd = &FlightData{}
   726  	var data byte
   727  
   728  	if buf.Len() < 24 {
   729  		err = errors.New("Invalid buffer length for flight data packet")
   730  		fmt.Println(err)
   731  		return
   732  	}
   733  
   734  	err = binary.Read(buf, binary.LittleEndian, &fd.Height)
   735  	if err != nil {
   736  		return
   737  	}
   738  	err = binary.Read(buf, binary.LittleEndian, &fd.NorthSpeed)
   739  	if err != nil {
   740  		return
   741  	}
   742  	err = binary.Read(buf, binary.LittleEndian, &fd.EastSpeed)
   743  	if err != nil {
   744  		return
   745  	}
   746  	err = binary.Read(buf, binary.LittleEndian, &fd.VerticalSpeed)
   747  	if err != nil {
   748  		return
   749  	}
   750  	err = binary.Read(buf, binary.LittleEndian, &fd.FlyTime)
   751  	if err != nil {
   752  		return
   753  	}
   754  
   755  	err = binary.Read(buf, binary.LittleEndian, &data)
   756  	if err != nil {
   757  		return
   758  	}
   759  	fd.ImuState = (data >> 0 & 0x1) == 1
   760  	fd.PressureState = (data >> 1 & 0x1) == 1
   761  	fd.DownVisualState = (data >> 2 & 0x1) == 1
   762  	fd.PowerState = (data >> 3 & 0x1) == 1
   763  	fd.BatteryState = (data >> 4 & 0x1) == 1
   764  	fd.GravityState = (data >> 5 & 0x1) == 1
   765  	fd.WindState = (data >> 7 & 0x1) == 1
   766  
   767  	err = binary.Read(buf, binary.LittleEndian, &fd.ImuCalibrationState)
   768  	if err != nil {
   769  		return
   770  	}
   771  	err = binary.Read(buf, binary.LittleEndian, &fd.BatteryPercentage)
   772  	if err != nil {
   773  		return
   774  	}
   775  	err = binary.Read(buf, binary.LittleEndian, &fd.DroneFlyTimeLeft)
   776  	if err != nil {
   777  		return
   778  	}
   779  	err = binary.Read(buf, binary.LittleEndian, &fd.DroneBatteryLeft)
   780  	if err != nil {
   781  		return
   782  	}
   783  
   784  	err = binary.Read(buf, binary.LittleEndian, &data)
   785  	if err != nil {
   786  		return
   787  	}
   788  	fd.Flying = (data >> 0 & 0x1) == 1
   789  	fd.OnGround = (data >> 1 & 0x1) == 1
   790  	fd.EmOpen = (data >> 2 & 0x1) == 1
   791  	fd.DroneHover = (data >> 3 & 0x1) == 1
   792  	fd.OutageRecording = (data >> 4 & 0x1) == 1
   793  	fd.BatteryLow = (data >> 5 & 0x1) == 1
   794  	fd.BatteryLower = (data >> 6 & 0x1) == 1
   795  	fd.FactoryMode = (data >> 7 & 0x1) == 1
   796  
   797  	err = binary.Read(buf, binary.LittleEndian, &fd.FlyMode)
   798  	if err != nil {
   799  		return
   800  	}
   801  	err = binary.Read(buf, binary.LittleEndian, &fd.ThrowFlyTimer)
   802  	if err != nil {
   803  		return
   804  	}
   805  	err = binary.Read(buf, binary.LittleEndian, &fd.CameraState)
   806  	if err != nil {
   807  		return
   808  	}
   809  
   810  	err = binary.Read(buf, binary.LittleEndian, &data)
   811  	if err != nil {
   812  		return
   813  	}
   814  	fd.ElectricalMachineryState = int16(data & 0xff)
   815  
   816  	err = binary.Read(buf, binary.LittleEndian, &data)
   817  	if err != nil {
   818  		return
   819  	}
   820  	fd.FrontIn = (data >> 0 & 0x1) == 1
   821  	fd.FrontOut = (data >> 1 & 0x1) == 1
   822  	fd.FrontLSC = (data >> 2 & 0x1) == 1
   823  
   824  	err = binary.Read(buf, binary.LittleEndian, &data)
   825  	if err != nil {
   826  		return
   827  	}
   828  	fd.TemperatureHigh = (data >> 0 & 0x1) == 1
   829  
   830  	return
   831  }
   832  
   833  // SendStickCommand sends the joystick command packet to the drone.
   834  func (d *Driver) SendStickCommand() (err error) {
   835  	d.cmdMutex.Lock()
   836  	defer d.cmdMutex.Unlock()
   837  
   838  	buf, _ := d.createPacket(stickCommand, 0x60, 11)
   839  	binary.Write(buf, binary.LittleEndian, int16(0x00)) // seq = 0
   840  
   841  	// RightX center=1024 left =364 right =-364
   842  	axis1 := int16(660.0*d.rx + 1024.0)
   843  
   844  	// RightY down =364 up =-364
   845  	axis2 := int16(660.0*d.ry + 1024.0)
   846  
   847  	// LeftY down =364 up =-364
   848  	axis3 := int16(660.0*d.ly + 1024.0)
   849  
   850  	// LeftX left =364 right =-364
   851  	axis4 := int16(660.0*d.lx + 1024.0)
   852  
   853  	// speed control
   854  	axis5 := int16(d.throttle)
   855  
   856  	packedAxis := int64(axis1)&0x7FF | int64(axis2&0x7FF)<<11 | 0x7FF&int64(axis3)<<22 | 0x7FF&int64(axis4)<<33 | int64(axis5)<<44
   857  	binary.Write(buf, binary.LittleEndian, byte(0xFF&packedAxis))
   858  	binary.Write(buf, binary.LittleEndian, byte(packedAxis>>8&0xFF))
   859  	binary.Write(buf, binary.LittleEndian, byte(packedAxis>>16&0xFF))
   860  	binary.Write(buf, binary.LittleEndian, byte(packedAxis>>24&0xFF))
   861  	binary.Write(buf, binary.LittleEndian, byte(packedAxis>>32&0xFF))
   862  	binary.Write(buf, binary.LittleEndian, byte(packedAxis>>40&0xFF))
   863  
   864  	now := time.Now()
   865  	binary.Write(buf, binary.LittleEndian, byte(now.Hour()))
   866  	binary.Write(buf, binary.LittleEndian, byte(now.Minute()))
   867  	binary.Write(buf, binary.LittleEndian, byte(now.Second()))
   868  	binary.Write(buf, binary.LittleEndian, byte(now.UnixNano()/int64(time.Millisecond)&0xff))
   869  	binary.Write(buf, binary.LittleEndian, byte(now.UnixNano()/int64(time.Millisecond)>>8))
   870  
   871  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   872  
   873  	_, err = d.cmdConn.Write(buf.Bytes())
   874  
   875  	return
   876  }
   877  
   878  // SendDateTime sends the current date/time to the drone.
   879  func (d *Driver) SendDateTime() (err error) {
   880  	d.cmdMutex.Lock()
   881  	defer d.cmdMutex.Unlock()
   882  
   883  	buf, _ := d.createPacket(timeCommand, 0x50, 11)
   884  	d.seq++
   885  	binary.Write(buf, binary.LittleEndian, d.seq)
   886  
   887  	now := time.Now()
   888  	binary.Write(buf, binary.LittleEndian, byte(0x00))
   889  	binary.Write(buf, binary.LittleEndian, int16(now.Hour()))
   890  	binary.Write(buf, binary.LittleEndian, int16(now.Minute()))
   891  	binary.Write(buf, binary.LittleEndian, int16(now.Second()))
   892  	binary.Write(buf, binary.LittleEndian, int16(now.UnixNano()/int64(time.Millisecond)&0xff))
   893  	binary.Write(buf, binary.LittleEndian, int16(now.UnixNano()/int64(time.Millisecond)>>8))
   894  
   895  	binary.Write(buf, binary.LittleEndian, CalculateCRC16(buf.Bytes()))
   896  
   897  	_, err = d.cmdConn.Write(buf.Bytes())
   898  	return
   899  }
   900  
   901  // SendCommand is used to send a text command such as the initial connection request to the drone.
   902  func (d *Driver) SendCommand(cmd string) (err error) {
   903  	_, err = d.cmdConn.Write([]byte(cmd))
   904  	return
   905  }
   906  
   907  func (d *Driver) handleResponse(r io.Reader) error {
   908  	var buf [2048]byte
   909  	var msgType uint16
   910  	n, err := r.Read(buf[0:])
   911  	if err != nil {
   912  		return err
   913  	}
   914  
   915  	// parse binary packet
   916  	if buf[0] == messageStart {
   917  		msgType = (uint16(buf[6]) << 8) | uint16(buf[5])
   918  		switch msgType {
   919  		case wifiMessage:
   920  			buf := bytes.NewReader(buf[9:10])
   921  			wd := &WifiData{}
   922  			binary.Read(buf, binary.LittleEndian, &wd.Strength)
   923  			binary.Read(buf, binary.LittleEndian, &wd.Disturb)
   924  			d.Publish(d.Event(WifiDataEvent), wd)
   925  		case lightMessage:
   926  			buf := bytes.NewReader(buf[9:9])
   927  			var ld int8
   928  			binary.Read(buf, binary.LittleEndian, &ld)
   929  			d.Publish(d.Event(LightStrengthEvent), ld)
   930  		case logMessage:
   931  			d.Publish(d.Event(LogEvent), buf[9:])
   932  		case timeCommand:
   933  			d.Publish(d.Event(TimeEvent), buf[7:8])
   934  		case bounceCommand:
   935  			d.Publish(d.Event(BounceEvent), buf[7:8])
   936  		case takeoffCommand:
   937  			d.Publish(d.Event(TakeoffEvent), buf[7:8])
   938  		case landCommand:
   939  			d.Publish(d.Event(LandingEvent), buf[7:8])
   940  		case palmLandCommand:
   941  			d.Publish(d.Event(PalmLandingEvent), buf[7:8])
   942  		case flipCommand:
   943  			d.Publish(d.Event(FlipEvent), buf[7:8])
   944  		case flightMessage:
   945  			fd, _ := d.ParseFlightData(buf[9:])
   946  			d.Publish(d.Event(FlightDataEvent), fd)
   947  		case exposureCommand:
   948  			d.Publish(d.Event(SetExposureEvent), buf[7:8])
   949  		case videoEncoderRateCommand:
   950  			d.Publish(d.Event(SetVideoEncoderRateEvent), buf[7:8])
   951  		default:
   952  			fmt.Printf("Unknown message: %+v\n", buf[0:n])
   953  		}
   954  		return nil
   955  	}
   956  
   957  	// parse text packet
   958  	if buf[0] == 0x63 && buf[1] == 0x6f && buf[2] == 0x6e {
   959  		d.Publish(d.Event(ConnectedEvent), nil)
   960  	}
   961  
   962  	return nil
   963  }
   964  
   965  func (d *Driver) processVideo() error {
   966  	videoPort, err := net.ResolveUDPAddr("udp", ":11111")
   967  	if err != nil {
   968  		return err
   969  	}
   970  	d.videoConn, err = net.ListenUDP("udp", videoPort)
   971  	if err != nil {
   972  		return err
   973  	}
   974  
   975  	d.addDoneChReaderCount(1)
   976  	go func() {
   977  		defer d.addDoneChReaderCount(-1)
   978  
   979  	videoConnLoop:
   980  		for {
   981  			select {
   982  			case <-d.doneCh:
   983  				break videoConnLoop
   984  			default:
   985  				buf := make([]byte, 2048)
   986  				n, _, err := d.videoConn.ReadFromUDP(buf)
   987  				if err != nil {
   988  					fmt.Println("Error: ", err)
   989  					continue
   990  				}
   991  
   992  				d.Publish(d.Event(VideoFrameEvent), buf[2:n])
   993  			}
   994  		}
   995  	}()
   996  
   997  	return nil
   998  }
   999  
  1000  func (d *Driver) createPacket(cmd int16, pktType byte, len int16) (buf *bytes.Buffer, err error) {
  1001  	l := len + 11
  1002  	buf = &bytes.Buffer{}
  1003  
  1004  	binary.Write(buf, binary.LittleEndian, byte(messageStart))
  1005  	binary.Write(buf, binary.LittleEndian, l<<3)
  1006  	binary.Write(buf, binary.LittleEndian, CalculateCRC8(buf.Bytes()[0:3]))
  1007  	binary.Write(buf, binary.LittleEndian, pktType)
  1008  	binary.Write(buf, binary.LittleEndian, cmd)
  1009  
  1010  	return buf, nil
  1011  }
  1012  
  1013  func (d *Driver) connectionString() string {
  1014  	x, _ := strconv.Atoi(d.videoPort)
  1015  	b := [2]byte{}
  1016  	binary.LittleEndian.PutUint16(b[:], uint16(x))
  1017  	res := fmt.Sprintf("conn_req:%s", b)
  1018  	return res
  1019  }
  1020  
  1021  func (d *Driver) addDoneChReaderCount(delta int32) {
  1022  	atomic.AddInt32(&d.doneChReaderCount, delta)
  1023  }
  1024  
  1025  func (f *FlightData) AirSpeed() float64 {
  1026  	return math.Sqrt(
  1027  		math.Pow(float64(f.NorthSpeed), 2) +
  1028  			math.Pow(float64(f.EastSpeed), 2) +
  1029  			math.Pow(float64(f.VerticalSpeed), 2))
  1030  }
  1031  
  1032  func (f *FlightData) GroundSpeed() float64 {
  1033  	return math.Sqrt(
  1034  		math.Pow(float64(f.NorthSpeed), 2) +
  1035  			math.Pow(float64(f.EastSpeed), 2))
  1036  }