gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/adxl345_driver.go (about) 1 package i2c 2 3 import ( 4 "encoding/binary" 5 "log" 6 7 "github.com/pkg/errors" 8 ) 9 10 const adxl345Debug = false 11 12 // ADXL345 supports 2 addresses, which can be changed by the address pin, there is no internal pull-up/down resistor! 13 // pin to GND: 0x53, pin to VDD: 0x1D 14 const ( 15 ADXL345AddressPullUp = 0x1D // can be used by WithAddress() 16 adxl345DefaultAddress = 0x53 17 ) 18 19 type ADXL345RateConfig uint8 20 type ADXL345FsRangeConfig uint8 21 22 const ( 23 // registers are named according to the datasheet 24 adxl345Reg_DEVID = 0x00 // R, 11100101, Device ID 25 adxl345Reg_THRESH_TAP = 0x1D // R/W, 00000000, Tap threshold 26 adxl345Reg_OFSX = 0x1E // R/W, 00000000, X-axis offset 27 adxl345Reg_OFSY = 0x1F // R/W, 00000000, Y-axis offset 28 adxl345Reg_OFSZ = 0x20 // R/W, 00000000, Z-axis offset 29 adxl345Reg_DUR = 0x21 // R/W, 00000000, Tap duration 30 adxl345Reg_LATENT = 0x22 // R/W, 00000000, Tap latency 31 adxl345Reg_WINDOW = 0x23 // R/W, 00000000, Tap window 32 adxl345Reg_THRESH_ACT = 0x24 // R/W, 00000000, Activity threshold 33 adxl345Reg_THRESH_INACT = 0x25 // R/W, 00000000, Inactivity threshold 34 adxl345Reg_TIME_INACT = 0x26 // R/W, 00000000, Inactivity time 35 adxl345Reg_ACT_INACT_CTL = 0x27 // R/W, 00000000, Axis enable control for activity and inactivity detection 36 adxl345Reg_THRESH_FF = 0x28 // R/W, 00000000, Free-fall threshold 37 adxl345Reg_TIME_FF = 0x29 // R/W, 00000000, Free-fall time 38 adxl345Reg_TAP_AXES = 0x2A // R/W, 00000000, Axis control for single tap/double tap 39 adxl345Reg_ACT_TAP_STATUS = 0x2B // R, 00000000, Source of single tap/double tap 40 adxl345Reg_BW_RATE = 0x2C // R/W, 00001010, Data rate and power mode control 41 adxl345Reg_POWER_CTL = 0x2D // R/W, 00000000, Power-saving features control 42 adxl345Reg_INT_ENABLE = 0x2E // R/W, 00000000, Interrupt enable control 43 adxl345Reg_INT_MAP = 0x2F // R/W, 00000000, Interrupt mapping control 44 adxl345Reg_INT_SOUCE = 0x30 // R, 00000010, Source of interrupts 45 adxl345Reg_DATA_FORMAT = 0x31 // R/W, 00000000, Data format control (FS range, justify, full resolution) 46 adxl345Reg_DATAX0 = 0x32 // R, 00000000, X-Axis Data 0 (LSByte) 47 adxl345Reg_DATAX1 = 0x33 // R, 00000000, X-Axis Data 1 (MSByte) 48 adxl345Reg_DATAY0 = 0x34 // R, 00000000, Y-Axis Data 0 49 adxl345Reg_DATAY1 = 0x35 // R, 00000000, Y-Axis Data 1 50 adxl345Reg_DATAZ0 = 0x36 // R, 00000000, Z-Axis Data 0 51 adxl345Reg_DATAZ1 = 0x37 // R, 00000000, Z-Axis Data 1 52 adxl345Reg_FIFO_CTL = 0x38 // R/W, 00000000, FIFO control 53 adxl345Reg_FIFO_STATUS = 0x39 // R, 00000000, FIFO status 54 55 adxl345Rate_LowPowerBit = 0x10 // set the device to low power, but increase the noise by ~2.5x 56 57 ADXL345Rate_100mHZ ADXL345RateConfig = 0x00 // 0.10 Hz 58 ADXL345Rate_200mHZ ADXL345RateConfig = 0x01 // 0.20 Hz 59 ADXL345Rate_390mHZ ADXL345RateConfig = 0x02 // 0.39 Hz 60 ADXL345Rate_780mHZ ADXL345RateConfig = 0x03 // 0.78 Hz 61 ADXL345Rate_1560mHZ ADXL345RateConfig = 0x04 // 1.56 Hz 62 ADXL345Rate_3130mHZ ADXL345RateConfig = 0x05 // 3.13 Hz 63 ADXL345Rate_6250mHZ ADXL345RateConfig = 0x06 // 6.25 Hz 64 ADXL345Rate_12500mHZ ADXL345RateConfig = 0x07 // 12.5 Hz 65 ADXL345Rate_25HZ ADXL345RateConfig = 0x08 // 25 Hz 66 ADXL345Rate_50HZ ADXL345RateConfig = 0x09 // 50 Hz 67 ADXL345Rate_100HZ ADXL345RateConfig = 0x0A // 100 Hz 68 ADXL345Rate_200HZ ADXL345RateConfig = 0x0B // 200 Hz 69 ADXL345Rate_400HZ ADXL345RateConfig = 0x0C // 400 Hz 70 ADXL345Rate_800HZ ADXL345RateConfig = 0x0D // 800 Hz 71 ADXL345Rate_1600HZ ADXL345RateConfig = 0x0E // 1600 Hz 72 ADXL345Rate_3200HZ ADXL345RateConfig = 0x0F // 3200 Hz 73 74 ADXL345FsRange_2G ADXL345FsRangeConfig = 0x00 // +-2 g 75 ADXL345FsRange_4G ADXL345FsRangeConfig = 0x01 // +-4 g 76 ADXL345FsRange_8G ADXL345FsRangeConfig = 0x02 // +-8 g 77 ADXL345FsRange_16G ADXL345FsRangeConfig = 0x03 // +-16 g) 78 ) 79 80 // ADXL345Driver is the gobot driver for the digital accelerometer ADXL345 81 // 82 // Datasheet EN: http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf 83 // Datasheet JP: http://www.analog.com/media/jp/technical-documentation/data-sheets/ADXL345_jp.pdf 84 // 85 // Ported from the Arduino driver https://github.com/jakalada/Arduino-ADXL345 86 type ADXL345Driver struct { 87 *Driver 88 powerCtl adxl345PowerCtl 89 dataFormat adxl345DataFormat 90 bwRate adxl345BwRate 91 } 92 93 // Internal structure for the power configuration 94 type adxl345PowerCtl struct { 95 link uint8 96 autoSleep uint8 97 measure uint8 98 sleep uint8 99 wakeUp uint8 100 } 101 102 // Internal structure for the sensor's data format configuration 103 type adxl345DataFormat struct { 104 selfTest uint8 105 spi uint8 106 intInvert uint8 107 fullRes uint8 108 justify uint8 109 fullScaleRange ADXL345FsRangeConfig 110 } 111 112 // Internal structure for the sampling rate configuration 113 type adxl345BwRate struct { 114 lowPower bool 115 rate ADXL345RateConfig 116 } 117 118 // NewADXL345Driver creates a new driver with specified i2c interface 119 // Params: 120 // c Connector - the Adaptor to use with this Driver 121 // 122 // Optional params: 123 // i2c.WithBus(int): bus to use with this driver 124 // i2c.WithAddress(int): address to use with this driver 125 // 126 func NewADXL345Driver(c Connector, options ...func(Config)) *ADXL345Driver { 127 d := &ADXL345Driver{ 128 Driver: NewDriver(c, "ADXL345", adxl345DefaultAddress), 129 powerCtl: adxl345PowerCtl{ 130 measure: 1, 131 }, 132 dataFormat: adxl345DataFormat{ 133 fullScaleRange: ADXL345FsRange_2G, 134 }, 135 bwRate: adxl345BwRate{ 136 lowPower: true, 137 rate: ADXL345Rate_100HZ, 138 }, 139 } 140 d.afterStart = d.initialize 141 d.beforeHalt = d.shutdown 142 143 for _, option := range options { 144 option(d) 145 } 146 147 // TODO: add commands for API 148 return d 149 } 150 151 // WithADXL345LowPowerMode option modifies the low power mode. 152 func WithADXL345LowPowerMode(val bool) func(Config) { 153 return func(c Config) { 154 if d, ok := c.(*ADXL345Driver); ok { 155 d.bwRate.lowPower = val 156 } else if adxl345Debug { 157 log.Printf("Trying to modify low power mode for non-ADXL345Driver %v", c) 158 } 159 } 160 } 161 162 // WithADXL345DataOutputRate option sets the data output rate. 163 // Valid settings are of type "ADXL345RateConfig" 164 func WithADXL345DataOutputRate(val ADXL345RateConfig) func(Config) { 165 return func(c Config) { 166 if d, ok := c.(*ADXL345Driver); ok { 167 d.bwRate.rate = val 168 } else if adxl345Debug { 169 log.Printf("Trying to set data output rate for non-ADXL345Driver %v", c) 170 } 171 } 172 } 173 174 // WithADXL345FullScaleRange option sets the full scale range. 175 // Valid settings are of type "ADXL345FsRangeConfig" 176 func WithADXL345FullScaleRange(val ADXL345FsRangeConfig) func(Config) { 177 return func(c Config) { 178 if d, ok := c.(*ADXL345Driver); ok { 179 d.dataFormat.fullScaleRange = val 180 } else if adxl345Debug { 181 log.Printf("Trying to set full scale range for non-ADXL345Driver %v", c) 182 } 183 } 184 } 185 186 // UseLowPower change the current rate of the sensor 187 func (d *ADXL345Driver) UseLowPower(lowPower bool) (err error) { 188 d.mutex.Lock() 189 defer d.mutex.Unlock() 190 191 d.bwRate.lowPower = lowPower 192 if err := d.connection.WriteByteData(adxl345Reg_BW_RATE, d.bwRate.toByte()); err != nil { 193 return err 194 } 195 return 196 } 197 198 // SetRate change the current rate of the sensor immediately 199 func (d *ADXL345Driver) SetRate(rate ADXL345RateConfig) (err error) { 200 d.mutex.Lock() 201 defer d.mutex.Unlock() 202 203 d.bwRate.rate = rate 204 if err := d.connection.WriteByteData(adxl345Reg_BW_RATE, d.bwRate.toByte()); err != nil { 205 return err 206 } 207 return 208 } 209 210 // SetRange change the current range of the sensor immediately 211 func (d *ADXL345Driver) SetRange(fullScaleRange ADXL345FsRangeConfig) (err error) { 212 d.mutex.Lock() 213 defer d.mutex.Unlock() 214 215 d.dataFormat.fullScaleRange = fullScaleRange 216 if err := d.connection.WriteByteData(adxl345Reg_DATA_FORMAT, d.dataFormat.toByte()); err != nil { 217 return err 218 } 219 return 220 } 221 222 // XYZ returns the adjusted x, y and z axis, unit [g] 223 func (d *ADXL345Driver) XYZ() (float64, float64, float64, error) { 224 d.mutex.Lock() 225 defer d.mutex.Unlock() 226 227 xr, yr, zr, err := d.readRawData() 228 if err != nil { 229 return 0, 0, 0, err 230 } 231 232 return d.dataFormat.convertToG(xr), d.dataFormat.convertToG(yr), d.dataFormat.convertToG(zr), nil 233 } 234 235 // XYZ returns the raw x,y and z axis 236 func (d *ADXL345Driver) RawXYZ() (int16, int16, int16, error) { 237 d.mutex.Lock() 238 defer d.mutex.Unlock() 239 240 return d.readRawData() 241 } 242 243 func (d *ADXL345Driver) readRawData() (int16, int16, int16, error) { 244 buf := []byte{0, 0, 0, 0, 0, 0} 245 if err := d.connection.ReadBlockData(adxl345Reg_DATAX0, buf); err != nil { 246 return 0, 0, 0, err 247 } 248 249 rx := int16(binary.LittleEndian.Uint16(buf[0:2])) 250 ry := int16(binary.LittleEndian.Uint16(buf[2:4])) 251 rz := int16(binary.LittleEndian.Uint16(buf[4:6])) 252 return rx, ry, rz, nil 253 } 254 255 func (d *ADXL345Driver) initialize() error { 256 if err := d.connection.WriteByteData(adxl345Reg_BW_RATE, d.bwRate.toByte()); err != nil { 257 return err 258 } 259 if err := d.connection.WriteByteData(adxl345Reg_POWER_CTL, d.powerCtl.toByte()); err != nil { 260 return err 261 } 262 if err := d.connection.WriteByteData(adxl345Reg_DATA_FORMAT, d.dataFormat.toByte()); err != nil { 263 return err 264 } 265 266 return nil 267 } 268 269 func (d *ADXL345Driver) shutdown() error { 270 d.powerCtl.measure = 0 271 if d.connection == nil { 272 return errors.New("connection not available") 273 } 274 return d.connection.WriteByteData(adxl345Reg_POWER_CTL, d.powerCtl.toByte()) 275 } 276 277 // convertToG converts the given raw value by range configuration to the unit [g] 278 func (d *adxl345DataFormat) convertToG(rawValue int16) float64 { 279 switch d.fullScaleRange { 280 case ADXL345FsRange_2G: 281 return float64(rawValue) * 2 / 512 282 case ADXL345FsRange_4G: 283 return float64(rawValue) * 4 / 512 284 case ADXL345FsRange_8G: 285 return float64(rawValue) * 8 / 512 286 case ADXL345FsRange_16G: 287 return float64(rawValue) * 16 / 512 288 default: 289 return 0 290 } 291 } 292 293 // toByte returns a byte from the powerCtl configuration 294 func (p *adxl345PowerCtl) toByte() uint8 { 295 bits := p.wakeUp 296 bits = bits | (p.sleep << 2) 297 bits = bits | (p.measure << 3) 298 bits = bits | (p.autoSleep << 4) 299 return bits | (p.link << 5) 300 } 301 302 // toByte returns a byte from the dataFormat configuration 303 func (d *adxl345DataFormat) toByte() uint8 { 304 bits := uint8(d.fullScaleRange) 305 bits = bits | (d.justify << 2) 306 bits = bits | (d.fullRes << 3) 307 bits = bits | (d.intInvert << 5) 308 bits = bits | (d.spi << 6) 309 return bits | (d.selfTest << 7) 310 } 311 312 // toByte returns a byte from the bwRate configuration 313 func (b *adxl345BwRate) toByte() uint8 { 314 bits := uint8(b.rate) 315 if b.lowPower { 316 bits = bits | adxl345Rate_LowPowerBit 317 } 318 return bits 319 }