gobot.io/x/gobot@v1.16.0/drivers/i2c/wiichuck_driver.go (about)

     1  package i2c
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"gobot.io/x/gobot"
     8  )
     9  
    10  const (
    11  	// Joystick event when the Wiichuck joystick is moved
    12  	Joystick = "joystick"
    13  
    14  	// C event when the Wiichuck "C" button is pressed
    15  	C = "c"
    16  
    17  	// Z event when the Wiichuck "C" button is pressed
    18  	Z = "z"
    19  )
    20  
    21  const wiichuckAddress = 0x52
    22  
    23  type WiichuckDriver struct {
    24  	name       string
    25  	connector  Connector
    26  	connection Connection
    27  	Config
    28  	interval  time.Duration
    29  	pauseTime time.Duration
    30  	gobot.Eventer
    31  	mtx      sync.Mutex
    32  	joystick map[string]float64
    33  	data     map[string]float64
    34  }
    35  
    36  // NewWiichuckDriver creates a WiichuckDriver with specified i2c interface.
    37  //
    38  // Params:
    39  //		conn Connector - the Adaptor to use with this Driver
    40  //
    41  // Optional params:
    42  //		i2c.WithBus(int):	bus to use with this driver
    43  //		i2c.WithAddress(int):	address to use with this driver
    44  //
    45  func NewWiichuckDriver(a Connector, options ...func(Config)) *WiichuckDriver {
    46  	w := &WiichuckDriver{
    47  		name:      gobot.DefaultName("Wiichuck"),
    48  		connector: a,
    49  		Config:    NewConfig(),
    50  		interval:  10 * time.Millisecond,
    51  		pauseTime: 1 * time.Millisecond,
    52  		Eventer:   gobot.NewEventer(),
    53  		joystick: map[string]float64{
    54  			"sy_origin": -1,
    55  			"sx_origin": -1,
    56  		},
    57  		data: map[string]float64{
    58  			"sx": 0,
    59  			"sy": 0,
    60  			"z":  0,
    61  			"c":  0,
    62  		},
    63  	}
    64  
    65  	for _, option := range options {
    66  		option(w)
    67  	}
    68  
    69  	w.AddEvent(Z)
    70  	w.AddEvent(C)
    71  	w.AddEvent(Joystick)
    72  	w.AddEvent(Error)
    73  
    74  	return w
    75  }
    76  
    77  // Name returns the name of the device.
    78  func (w *WiichuckDriver) Name() string { return w.name }
    79  
    80  // SetName sets the name of the device.
    81  func (w *WiichuckDriver) SetName(n string) { w.name = n }
    82  
    83  // Connection returns the connection for the device.
    84  func (w *WiichuckDriver) Connection() gobot.Connection { return w.connector.(gobot.Connection) }
    85  
    86  // Start initilizes i2c and reads from adaptor
    87  // using specified interval to update with new value
    88  func (w *WiichuckDriver) Start() (err error) {
    89  	bus := w.GetBusOrDefault(w.connector.GetDefaultBus())
    90  	address := w.GetAddressOrDefault(wiichuckAddress)
    91  
    92  	w.connection, err = w.connector.GetConnection(address, bus)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	go func() {
    98  		for {
    99  			if _, err := w.connection.Write([]byte{0x40, 0x00}); err != nil {
   100  				w.Publish(w.Event(Error), err)
   101  				continue
   102  			}
   103  			time.Sleep(w.pauseTime)
   104  			if _, err := w.connection.Write([]byte{0x00}); err != nil {
   105  				w.Publish(w.Event(Error), err)
   106  				continue
   107  			}
   108  			time.Sleep(w.pauseTime)
   109  			newValue := make([]byte, 6)
   110  			bytesRead, err := w.connection.Read(newValue)
   111  			if err != nil {
   112  				w.Publish(w.Event(Error), err)
   113  				continue
   114  			}
   115  			if bytesRead == 6 {
   116  				if err = w.update(newValue); err != nil {
   117  					w.Publish(w.Event(Error), err)
   118  					continue
   119  				}
   120  			}
   121  			time.Sleep(w.interval)
   122  		}
   123  	}()
   124  	return
   125  }
   126  
   127  // Halt returns true if driver is halted successfully
   128  func (w *WiichuckDriver) Halt() (err error) { return }
   129  
   130  // Joystick returns the current value for the joystick
   131  func (w *WiichuckDriver) Joystick() map[string]float64 {
   132  	val := make(map[string]float64)
   133  	w.mtx.Lock()
   134  	defer w.mtx.Unlock()
   135  	val["sx_origin"] = w.joystick["sx_origin"]
   136  	val["sy_origin"] = w.joystick["sy_origin"]
   137  	return val
   138  }
   139  
   140  // update parses value to update buttons and joystick.
   141  // If value is encrypted, warning message is printed
   142  func (w *WiichuckDriver) update(value []byte) (err error) {
   143  	if w.isEncrypted(value) {
   144  		return ErrEncryptedBytes
   145  	} else {
   146  		w.parse(value)
   147  		w.adjustOrigins()
   148  		w.updateButtons()
   149  		w.updateJoystick()
   150  	}
   151  	return
   152  }
   153  
   154  // setJoystickDefaultValue sets default value if value is -1
   155  func (w *WiichuckDriver) setJoystickDefaultValue(joystickAxis string, defaultValue float64) {
   156  	w.mtx.Lock()
   157  	defer w.mtx.Unlock()
   158  	if w.joystick[joystickAxis] == -1 {
   159  		w.joystick[joystickAxis] = defaultValue
   160  	}
   161  }
   162  
   163  // calculateJoystickValue returns distance between axis and origin
   164  func (w *WiichuckDriver) calculateJoystickValue(axis float64, origin float64) float64 {
   165  	return float64(axis - origin)
   166  }
   167  
   168  // isEncrypted returns true if value is encrypted
   169  func (w *WiichuckDriver) isEncrypted(value []byte) bool {
   170  	if value[0] == value[1] && value[2] == value[3] && value[4] == value[5] {
   171  		return true
   172  	}
   173  	return false
   174  }
   175  
   176  // decode removes encoding from `x` byte
   177  func (w *WiichuckDriver) decode(x byte) float64 {
   178  	return float64((x ^ 0x17) + 0x17)
   179  }
   180  
   181  // adjustOrigins sets sy_origin and sx_origin with values from data
   182  func (w *WiichuckDriver) adjustOrigins() {
   183  	w.setJoystickDefaultValue("sy_origin", w.data["sy"])
   184  	w.setJoystickDefaultValue("sx_origin", w.data["sx"])
   185  }
   186  
   187  // updateButtons publishes "c" and "x" events if present in data
   188  func (w *WiichuckDriver) updateButtons() {
   189  	if w.data["c"] == 0 {
   190  		w.Publish(w.Event(C), true)
   191  	}
   192  	if w.data["z"] == 0 {
   193  		w.Publish(w.Event(Z), true)
   194  	}
   195  }
   196  
   197  // updateJoystick publishes event with current x and y values for joystick
   198  func (w *WiichuckDriver) updateJoystick() {
   199  	joy := w.Joystick()
   200  	w.Publish(w.Event(Joystick), map[string]float64{
   201  		"x": w.calculateJoystickValue(w.data["sx"], joy["sx_origin"]),
   202  		"y": w.calculateJoystickValue(w.data["sy"], joy["sy_origin"]),
   203  	})
   204  }
   205  
   206  // parse sets driver values based on parsed value
   207  func (w *WiichuckDriver) parse(value []byte) {
   208  	w.data["sx"] = w.decode(value[0])
   209  	w.data["sy"] = w.decode(value[1])
   210  	w.data["z"] = float64(uint8(w.decode(value[5])) & 0x01)
   211  	w.data["c"] = float64(uint8(w.decode(value[5])) & 0x02)
   212  }