gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/hmc5883l_driver.go (about)

     1  package i2c
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"math"
     7  	"sort"
     8  )
     9  
    10  const (
    11  	hmc5883lDebug          = false
    12  	hmc5883lDefaultAddress = 0x1E // default I2C Address
    13  )
    14  
    15  const (
    16  	hmc5883lRegA      = 0x00 // Address of Configuration register A
    17  	hmc5883lRegB      = 0x01 // Address of Configuration register B
    18  	hmc5883lRegMode   = 0x02 // Address of node register
    19  	hmc5883lAxisX     = 0x03 // Address of X-axis MSB data register
    20  	hmc5883lAxisZ     = 0x05 // Address of Z-axis MSB data register
    21  	hmc5883lAxisY     = 0x07 // Address of Y-axis MSB data register
    22  	hmc5883lRegStatus = 0x09 // Address of status register
    23  	hmc5883lRegIdA    = 0x0A // Address of identification register A
    24  	hmc5883lRegIdB    = 0x0B // Address of identification register B
    25  	hmc5883lRegIdC    = 0x0C // Address of identification register C
    26  
    27  	hmc5883lRegA_SamplesAvg1      = 0x00 // no samples averaged
    28  	hmc5883lRegA_SamplesAvg2      = 0x01 // 2 samples averaged
    29  	hmc5883lRegA_SamplesAvg4      = 0x02 // 4 samples averaged
    30  	hmc5883lRegA_SamplesAvg8      = 0x03 // 8 samples averaged
    31  	hmc5883lRegA_OutputRate750    = 0x00 // data output rate 0.75 Hz
    32  	hmc5883lRegA_OutputRate1500   = 0x01 // data output rate 1.5 Hz
    33  	hmc5883lRegA_OutputRate3000   = 0x02 // data output rate 3.0 Hz
    34  	hmc5883lRegA_OutputRate7500   = 0x03 // data output rate 7.5 Hz
    35  	hmc5883lRegA_OutputRate15000  = 0x04 // data output rate 15.0 Hz
    36  	hmc5883lRegA_OutputRate30000  = 0x05 // data output rate 30.0 Hz
    37  	hmc5883lRegA_OutputRate75000  = 0x06 // data output rate 75.0 Hz
    38  	hmc5883lRegA_MeasNormal       = 0x00 // normal measurement configuration
    39  	hmc5883lRegA_MeasPositiveBias = 0x01 // positive bias for X, Y, Z
    40  	hmc5883lRegA_MeasNegativeBias = 0x02 // negative bias for X, Y, Z
    41  
    42  	hmc5883lRegB_Gain1370 = 0x00 // gain is 1370 Gauss
    43  	hmc5883lRegB_Gain1090 = 0x01 // gain is 1090 Gauss
    44  	hmc5883lRegB_Gain820  = 0x02 // gain is 820 Gauss
    45  	hmc5883lRegB_Gain660  = 0x03 // gain is 660 Gauss
    46  	hmc5883lRegB_Gain440  = 0x04 // gain is 440 Gauss
    47  	hmc5883lRegB_Gain390  = 0x05 // gain is 390 Gauss
    48  	hmc5883lRegB_Gain330  = 0x06 // gain is 330 Gauss
    49  	hmc5883lRegB_Gain230  = 0x07 // gain is 230 Gauss
    50  
    51  	hmc5883lRegM_Continuous = 0x00 // continuous measurement mode
    52  	hmc5883lRegM_Single     = 0x01 // return to idle after a single measurement
    53  	hmc5883lRegM_Idle       = 0x10 // idle mode
    54  )
    55  
    56  // HMC5883LDriver is a Gobot Driver for a HMC5883 I2C 3 axis digital compass.
    57  //
    58  // This driver was tested with Tinkerboard & Digispark adaptor and a HMC5883L breakout board GY-273,
    59  // available from various distributors.
    60  //
    61  // datasheet:
    62  // http://www.adafruit.com/datasheets/HMC5883L_3-Axis_Digital_Compass_IC.pdf
    63  //
    64  // reference implementations:
    65  // * https://github.com/gvalkov/micropython-esp8266-hmc5883l
    66  // * https://github.com/adafruit/Adafruit_HMC5883_Unified
    67  type HMC5883LDriver struct {
    68  	*Driver
    69  	samplesAvg      uint8
    70  	outputRate      uint32 // in mHz
    71  	applyBias       int8
    72  	measurementMode int
    73  	gain            float64 // in 1/Gauss
    74  }
    75  
    76  var hmc5883lSamplesAvgBits = map[uint8]int{
    77  	1: hmc5883lRegA_SamplesAvg1,
    78  	2: hmc5883lRegA_SamplesAvg2,
    79  	4: hmc5883lRegA_SamplesAvg4,
    80  	8: hmc5883lRegA_SamplesAvg8,
    81  }
    82  
    83  var hmc5883lOutputRateBits = map[uint32]int{
    84  	750:   hmc5883lRegA_OutputRate750,
    85  	1500:  hmc5883lRegA_OutputRate1500,
    86  	3000:  hmc5883lRegA_OutputRate3000,
    87  	7500:  hmc5883lRegA_OutputRate7500,
    88  	15000: hmc5883lRegA_OutputRate15000,
    89  	30000: hmc5883lRegA_OutputRate30000,
    90  	75000: hmc5883lRegA_OutputRate75000,
    91  }
    92  
    93  var hmc5883lMeasurementFlowBits = map[int8]int{
    94  	0:  hmc5883lRegA_MeasNormal,
    95  	1:  hmc5883lRegA_MeasPositiveBias,
    96  	-1: hmc5883lRegA_MeasNegativeBias,
    97  }
    98  
    99  var hmc5883lGainBits = map[float64]int{
   100  	1370.0: hmc5883lRegB_Gain1370,
   101  	1090.0: hmc5883lRegB_Gain1090,
   102  	820.0:  hmc5883lRegB_Gain820,
   103  	660.0:  hmc5883lRegB_Gain660,
   104  	440.0:  hmc5883lRegB_Gain440,
   105  	390.0:  hmc5883lRegB_Gain390,
   106  	330.0:  hmc5883lRegB_Gain330,
   107  	230.0:  hmc5883lRegB_Gain230,
   108  }
   109  
   110  // NewHMC5883LDriver creates a new driver with specified i2c interface
   111  // Params:
   112  //		c Connector - the Adaptor to use with this Driver
   113  //
   114  // Optional params:
   115  //		i2c.WithBus(int):	bus to use with this driver
   116  //		i2c.WithAddress(int):	address to use with this driver
   117  //    i2c.WithHMC5883LSamplesAveraged(int)
   118  //    i2c.WithHMC5883LDataOutputRate(int)
   119  //    i2c.WithHMC5883LMeasurementFlow(int)
   120  //    i2c.WithHMC5883LGain(int)
   121  //
   122  func NewHMC5883LDriver(c Connector, options ...func(Config)) *HMC5883LDriver {
   123  	h := &HMC5883LDriver{
   124  		Driver:          NewDriver(c, "HMC5883L", hmc5883lDefaultAddress),
   125  		samplesAvg:      8,
   126  		outputRate:      15000,
   127  		applyBias:       0,
   128  		measurementMode: hmc5883lRegM_Continuous,
   129  		gain:            390,
   130  	}
   131  	h.afterStart = h.initialize
   132  
   133  	for _, option := range options {
   134  		option(h)
   135  	}
   136  
   137  	return h
   138  }
   139  
   140  // WithHMC5883LSamplesAveraged option sets the number of samples averaged per measurement.
   141  // Valid settings are 1, 2, 4, 8.
   142  func WithHMC5883LSamplesAveraged(val int) func(Config) {
   143  	return func(c Config) {
   144  		d, ok := c.(*HMC5883LDriver)
   145  		if ok {
   146  			if err := hmc5883lValidateSamplesAveraged(val); err != nil {
   147  				panic(err)
   148  			}
   149  			d.samplesAvg = uint8(val)
   150  		} else if hmc5883lDebug {
   151  			log.Printf("Trying to set samples averaged for non-HMC5883LDriver %v", c)
   152  		}
   153  	}
   154  }
   155  
   156  // WithHMC5883LDataOutputRate option sets the data output rate in mHz.
   157  // Valid settings are 750, 1500, 3000, 7500, 15000, 30000, 75000.
   158  func WithHMC5883LDataOutputRate(val int) func(Config) {
   159  	return func(c Config) {
   160  		d, ok := c.(*HMC5883LDriver)
   161  		if ok {
   162  			if err := hmc5883lValidateOutputRate(val); err != nil {
   163  				panic(err)
   164  			}
   165  			d.outputRate = uint32(val)
   166  		} else if hmc5883lDebug {
   167  			log.Printf("Trying to set data output rate for non-HMC5883LDriver %v", c)
   168  		}
   169  	}
   170  }
   171  
   172  // WithHMC5883LApplyBias option sets to apply a measurement bias.
   173  // Valid settings are -1 (negative bias), 0 (normal), 1 (positive bias).
   174  func WithHMC5883LApplyBias(val int) func(Config) {
   175  	return func(c Config) {
   176  		d, ok := c.(*HMC5883LDriver)
   177  		if ok {
   178  			if err := hmc5883lValidateApplyBias(val); err != nil {
   179  				panic(err)
   180  			}
   181  			d.applyBias = int8(val)
   182  		} else if hmc5883lDebug {
   183  			log.Printf("Trying to set measurement flow for non-HMC5883LDriver %v", c)
   184  		}
   185  	}
   186  }
   187  
   188  // WithHMC5883LGain option sets the gain.
   189  // Valid settings are 1370, 1090, 820, 660, 440, 390, 330 230 in 1/Gauss.
   190  func WithHMC5883LGain(val int) func(Config) {
   191  	return func(c Config) {
   192  		d, ok := c.(*HMC5883LDriver)
   193  		if ok {
   194  			if err := hmc5883lValidateGain(val); err != nil {
   195  				panic(err)
   196  			}
   197  			d.gain = float64(val)
   198  		} else if hmc5883lDebug {
   199  			log.Printf("Trying to set gain for non-HMC5883LDriver %v", c)
   200  		}
   201  	}
   202  }
   203  
   204  // Read reads the values X, Y, Z in Gauss
   205  func (h *HMC5883LDriver) Read() (x float64, y float64, z float64, err error) {
   206  	h.mutex.Lock()
   207  	defer h.mutex.Unlock()
   208  
   209  	xr, yr, zr, err := h.readRawData()
   210  	if err != nil {
   211  		return
   212  	}
   213  	return float64(xr) / h.gain, float64(yr) / h.gain, float64(zr) / h.gain, nil
   214  }
   215  
   216  // Heading returns the current heading in radians
   217  func (h *HMC5883LDriver) Heading() (heading float64, err error) {
   218  	h.mutex.Lock()
   219  	defer h.mutex.Unlock()
   220  
   221  	var x, y int16
   222  	x, y, _, err = h.readRawData()
   223  	if err != nil {
   224  		return
   225  	}
   226  	heading = math.Atan2(float64(y), float64(x))
   227  	if heading > 2*math.Pi {
   228  		heading -= 2 * math.Pi
   229  	}
   230  	if heading < 0 {
   231  		heading += 2 * math.Pi
   232  	}
   233  	return
   234  }
   235  
   236  // readRawData reads the raw values from the X, Y, and Z registers
   237  func (h *HMC5883LDriver) readRawData() (x int16, y int16, z int16, err error) {
   238  	// read the data, starting from the initial register
   239  	data := make([]byte, 6)
   240  	if err = h.connection.ReadBlockData(hmc5883lAxisX, data); err != nil {
   241  		return
   242  	}
   243  
   244  	unsignedX := (uint16(data[0]) << 8) | uint16(data[1])
   245  	unsignedZ := (uint16(data[2]) << 8) | uint16(data[3])
   246  	unsignedY := (uint16(data[4]) << 8) | uint16(data[5])
   247  
   248  	return twosComplement16Bit(unsignedX), twosComplement16Bit(unsignedY), twosComplement16Bit(unsignedZ), nil
   249  }
   250  
   251  func (h *HMC5883LDriver) initialize() (err error) {
   252  	regA := hmc5883lMeasurementFlowBits[h.applyBias]
   253  	regA |= hmc5883lOutputRateBits[h.outputRate] << 2
   254  	regA |= hmc5883lSamplesAvgBits[h.samplesAvg] << 5
   255  	if err := h.connection.WriteByteData(hmc5883lRegA, uint8(regA)); err != nil {
   256  		return err
   257  	}
   258  	regB := hmc5883lGainBits[h.gain] << 5
   259  	if err := h.connection.WriteByteData(hmc5883lRegB, uint8(regB)); err != nil {
   260  		return err
   261  	}
   262  	if err := h.connection.WriteByteData(hmc5883lRegMode, uint8(h.measurementMode)); err != nil {
   263  		return err
   264  	}
   265  	return
   266  }
   267  
   268  func hmc5883lValidateSamplesAveraged(samplesAvg int) (err error) {
   269  	if _, ok := hmc5883lSamplesAvgBits[uint8(samplesAvg)]; ok {
   270  		return
   271  	}
   272  
   273  	keys := []int{}
   274  	for k := range hmc5883lSamplesAvgBits {
   275  		keys = append(keys, int(k))
   276  	}
   277  	sort.Ints(keys)
   278  	err = fmt.Errorf("Samples averaged must be one of: %d", keys)
   279  	return
   280  }
   281  
   282  func hmc5883lValidateOutputRate(outputRate int) (err error) {
   283  	if _, ok := hmc5883lOutputRateBits[uint32(outputRate)]; ok {
   284  		return
   285  	}
   286  
   287  	keys := []int{}
   288  	for k := range hmc5883lOutputRateBits {
   289  		keys = append(keys, int(k))
   290  	}
   291  	sort.Ints(keys)
   292  	err = fmt.Errorf("Data output rate must be one of: %d", keys)
   293  	return
   294  }
   295  
   296  func hmc5883lValidateApplyBias(applyBias int) (err error) {
   297  	if _, ok := hmc5883lMeasurementFlowBits[int8(applyBias)]; ok {
   298  		return
   299  	}
   300  
   301  	keys := []int{}
   302  	for k := range hmc5883lMeasurementFlowBits {
   303  		keys = append(keys, int(k))
   304  	}
   305  	sort.Ints(keys)
   306  	err = fmt.Errorf("Apply measurement bias must be one of: %d", keys)
   307  	return
   308  }
   309  
   310  func hmc5883lValidateGain(gain int) (err error) {
   311  	if _, ok := hmc5883lGainBits[float64(gain)]; ok {
   312  		return
   313  	}
   314  
   315  	keys := []int{}
   316  	for k := range hmc5883lGainBits {
   317  		keys = append(keys, int(k))
   318  	}
   319  	sort.Ints(keys)
   320  	err = fmt.Errorf("Gain must be one of: %d", keys)
   321  	return
   322  }