gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/mpu6050_driver.go (about) 1 package i2c 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "log" 8 "time" 9 ) 10 11 const ( 12 mpu6050Debug = false 13 mpu6050DefaultAddress = 0x68 14 mpu6050EarthStandardGravity = 9.80665 // [m/s²] standard gravity (pole: 9.834, equator: 9.764) 15 ) 16 17 type MPU6050DlpfConfig uint8 18 type MPU6050FrameSyncConfig uint8 19 type MPU6050GyroFsConfig uint8 20 type MPU6050AccelFsConfig uint8 21 type MPU6050Pwr1ClockConfig uint8 22 23 const ( 24 mpu6050Reg_GeneralConfig = 0x1A // external frame synchronization and digital low pass filter 25 mpu6050Reg_GyroConfig = 0x1B // self test and full scale range 26 mpu6050Reg_AccelConfig = 0x1C // self test and full scale range 27 mpu6050Reg_AccelXoutH = 0x3B // first data register 28 mpu6050Reg_SignalPathReset = 0x68 29 mpu6050Reg_PwrMgmt1 = 0x6B 30 31 MPU6050General_Dlpf260Hz MPU6050DlpfConfig = 0x00 32 MPU6050General_Dlpf184Hz MPU6050DlpfConfig = 0x01 33 MPU6050General_Dlpf94Hz MPU6050DlpfConfig = 0x02 34 MPU6050General_Dlpf44Hz MPU6050DlpfConfig = 0x03 35 MPU6050General_Dlpf21Hz MPU6050DlpfConfig = 0x04 36 MPU6050General_Dlpf10Hz MPU6050DlpfConfig = 0x05 37 MPU6050General_Dlpf5Hz MPU6050DlpfConfig = 0x06 38 39 MPU6050General_FrameSyncDisabled MPU6050FrameSyncConfig = 0x00 40 MPU6050General_FrameSyncTemp MPU6050FrameSyncConfig = 0x01 41 MPU6050General_FrameSyncGyroX MPU6050FrameSyncConfig = 0x02 42 MPU6050General_FrameSyncGyroY MPU6050FrameSyncConfig = 0x03 43 MPU6050General_FrameSyncGyroZ MPU6050FrameSyncConfig = 0x04 44 MPU6050General_FrameSyncAccelX MPU6050FrameSyncConfig = 0x05 45 MPU6050General_FrameSyncAccelY MPU6050FrameSyncConfig = 0x06 46 MPU6050General_FrameSyncAccelZ MPU6050FrameSyncConfig = 0x07 47 48 MPU6050Gyro_FsSel250dps MPU6050GyroFsConfig = 0x00 // +/- 250 °/s 49 MPU6050Gyro_FsSel500dps MPU6050GyroFsConfig = 0x01 // +/- 500 °/s 50 MPU6050Gyro_FsSel1000dps MPU6050GyroFsConfig = 0x02 // +/- 1000 °/s 51 MPU6050Gyro_FsSel2000dps MPU6050GyroFsConfig = 0x03 // +/- 2000 °/s 52 53 MPU6050Accel_AFsSel2g MPU6050AccelFsConfig = 0x00 // +/- 2 g 54 MPU6050Accel_AFsSel4g MPU6050AccelFsConfig = 0x01 // +/- 4 g 55 MPU6050Accel_AFsSel8g MPU6050AccelFsConfig = 0x02 // +/- 8 g 56 MPU6050Accel_AFsSel16g MPU6050AccelFsConfig = 0x03 // +/- 16 g 57 58 mpu6050SignalReset_TempBit = 0x01 59 mpu6050SignalReset_AccelBit = 0x02 60 mpu6050SignalReset_GyroBit = 0x04 61 62 MPU6050Pwr1_ClockIntern8G MPU6050Pwr1ClockConfig = 0x00 // internal 8GHz 63 MPU6050Pwr1_ClockPllXGyro MPU6050Pwr1ClockConfig = 0x01 // PLL with X axis gyroscope reference 64 MPU6050Pwr1_ClockPllYGyro MPU6050Pwr1ClockConfig = 0x02 // PLL with Y axis gyroscope reference 65 MPU6050Pwr1_ClockPllZGyro MPU6050Pwr1ClockConfig = 0x03 // PLL with Z axis gyroscope reference 66 MPU6050Pwr1_ClockPllExt32K MPU6050Pwr1ClockConfig = 0x04 // PLL with external 32.768kHz reference 67 MPU6050Pwr1_ClockPllExt19M MPU6050Pwr1ClockConfig = 0x05 // PLL with external 19.2MHz reference 68 MPU6050Pwr1_ClockStop MPU6050Pwr1ClockConfig = 0x07 // Stops the clock and keeps the timing generator in reset 69 70 mpu6050Pwr1_SleepOnBit = 0x40 // put into low power sleep mode 71 mpu6050Pwr1_DeviceResetBit = 0x80 72 ) 73 74 type MPU6050ThreeDData struct { 75 X float64 76 Y float64 77 Z float64 78 } 79 80 // MPU6050Driver is a Gobot Driver for an MPU6050 I2C Accelerometer/Gyroscope/Temperature sensor. 81 // 82 // This driver was tested with Tinkerboard & Digispark adaptor and a MPU6050 breakout board GY-521, 83 // available from various distributors. 84 // 85 // datasheet: 86 // https://product.tdk.com/system/files/dam/doc/product/sensor/mortion-inertial/imu/data_sheet/mpu-6000-datasheet1.pdf 87 // 88 // reference implementations: 89 // * https://github.com/adafruit/Adafruit_CircuitPython_MPU6050 90 // * https://github.com/ElectronicCats/mpu6050 91 type MPU6050Driver struct { 92 *Driver 93 Accelerometer MPU6050ThreeDData 94 Gyroscope MPU6050ThreeDData 95 Temperature float64 96 dlpf MPU6050DlpfConfig 97 frameSync MPU6050FrameSyncConfig 98 accelFs MPU6050AccelFsConfig 99 gyroFs MPU6050GyroFsConfig 100 clock MPU6050Pwr1ClockConfig 101 gravity float64 // set to 1.0 leads to [g] 102 } 103 104 // mpu6050AccelGain in 1/g 105 var mpu6050AccelGain = map[MPU6050AccelFsConfig]uint16{ 106 MPU6050Accel_AFsSel2g: 16384, 107 MPU6050Accel_AFsSel4g: 8192, 108 MPU6050Accel_AFsSel8g: 4096, 109 MPU6050Accel_AFsSel16g: 2028, 110 } 111 112 // mpu6050GyroGain in s/° 113 var mpu6050GyroGain = map[MPU6050GyroFsConfig]float64{ 114 MPU6050Gyro_FsSel250dps: 131.0, 115 MPU6050Gyro_FsSel500dps: 65.5, 116 MPU6050Gyro_FsSel1000dps: 32.8, 117 MPU6050Gyro_FsSel2000dps: 16.4, 118 } 119 120 // NewMPU6050Driver creates a new Gobot Driver for an MPU6050 I2C Accelerometer/Gyroscope/Temperature sensor. 121 // 122 // Params: 123 // conn Connector - the Adaptor to use with this Driver 124 // 125 // Optional params: 126 // i2c.WithBus(int): bus to use with this driver 127 // i2c.WithAddress(int): address to use with this driver 128 // 129 func NewMPU6050Driver(a Connector, options ...func(Config)) *MPU6050Driver { 130 m := &MPU6050Driver{ 131 Driver: NewDriver(a, "MPU6050", mpu6050DefaultAddress), 132 dlpf: MPU6050General_Dlpf260Hz, 133 frameSync: MPU6050General_FrameSyncDisabled, 134 accelFs: MPU6050Accel_AFsSel2g, 135 gyroFs: MPU6050Gyro_FsSel250dps, 136 clock: MPU6050Pwr1_ClockPllXGyro, 137 gravity: mpu6050EarthStandardGravity, 138 } 139 m.afterStart = m.initialize 140 141 for _, option := range options { 142 option(m) 143 } 144 145 // TODO: add commands to API 146 return m 147 } 148 149 // WithMPU6050DigitalFilter option sets the digital low pass filter bandwidth frequency. 150 // Valid settings are of type "MPU6050DlpfConfig" 151 func WithMPU6050DigitalFilter(val MPU6050DlpfConfig) func(Config) { 152 return func(c Config) { 153 if d, ok := c.(*MPU6050Driver); ok { 154 d.dlpf = val 155 } else if mpu6050Debug { 156 log.Printf("Trying to set digital low pass filter for non-MPU6050Driver %v", c) 157 } 158 } 159 } 160 161 // WithMPU6050FrameSync option sets the external frame synchronization. 162 // Valid settings are of type "MPU6050FrameSyncConfig" 163 func WithMPU6050FrameSync(val MPU6050FrameSyncConfig) func(Config) { 164 return func(c Config) { 165 if d, ok := c.(*MPU6050Driver); ok { 166 d.frameSync = val 167 } else if mpu6050Debug { 168 log.Printf("Trying to set external frame synchronization for non-MPU6050Driver %v", c) 169 } 170 } 171 } 172 173 // WithMPU6050AccelFullScaleRange option sets the full scale range for the accelerometer. 174 // Valid settings are of type "MPU6050AccelFsConfig" 175 func WithMPU6050AccelFullScaleRange(val MPU6050AccelFsConfig) func(Config) { 176 return func(c Config) { 177 if d, ok := c.(*MPU6050Driver); ok { 178 d.accelFs = val 179 } else if mpu6050Debug { 180 log.Printf("Trying to set full scale range of accelerometer for non-MPU6050Driver %v", c) 181 } 182 } 183 } 184 185 // WithMPU6050GyroFullScaleRange option sets the full scale range for the gyroscope. 186 // Valid settings are of type "MPU6050GyroFsConfig" 187 func WithMPU6050GyroFullScaleRange(val MPU6050GyroFsConfig) func(Config) { 188 return func(c Config) { 189 if d, ok := c.(*MPU6050Driver); ok { 190 d.gyroFs = val 191 } else if mpu6050Debug { 192 log.Printf("Trying to set full scale range of gyroscope for non-MPU6050Driver %v", c) 193 } 194 } 195 } 196 197 // WithMPU6050ClockSource option sets the clock source. 198 // Valid settings are of type "MPU6050Pwr1ClockConfig" 199 func WithMPU6050ClockSource(val MPU6050Pwr1ClockConfig) func(Config) { 200 return func(c Config) { 201 if d, ok := c.(*MPU6050Driver); ok { 202 d.clock = val 203 } else if mpu6050Debug { 204 log.Printf("Trying to set clock source for non-MPU6050Driver %v", c) 205 } 206 } 207 } 208 209 // WithMPU6050Gravity option sets the gravity in [m/s²/g]. 210 // Useful settings are "1.0" (to use unit "g") or a value between 9.834 (pole) and 9.764 (equator) 211 func WithMPU6050Gravity(val float64) func(Config) { 212 return func(c Config) { 213 if d, ok := c.(*MPU6050Driver); ok { 214 d.gravity = val 215 } else if mpu6050Debug { 216 log.Printf("Trying to set gravity for non-MPU6050Driver %v", c) 217 } 218 } 219 } 220 221 // GetData fetches the latest data from the MPU6050 222 func (m *MPU6050Driver) GetData() (err error) { 223 m.mutex.Lock() 224 defer m.mutex.Unlock() 225 226 data := make([]byte, 14) 227 err = m.connection.ReadBlockData(mpu6050Reg_AccelXoutH, data) 228 if err != nil { 229 return 230 } 231 232 var accel struct { 233 X int16 234 Y int16 235 Z int16 236 } 237 var temp int16 238 var gyro struct { 239 X int16 240 Y int16 241 Z int16 242 } 243 244 buf := bytes.NewBuffer(data) 245 binary.Read(buf, binary.BigEndian, &accel) 246 binary.Read(buf, binary.BigEndian, &temp) 247 binary.Read(buf, binary.BigEndian, &gyro) 248 249 ag := float64(mpu6050AccelGain[m.accelFs]) / m.gravity 250 m.Accelerometer.X = float64(accel.X) / ag 251 m.Accelerometer.Y = float64(accel.Y) / ag 252 m.Accelerometer.Z = float64(accel.Z) / ag 253 254 m.Temperature = float64(temp)/340 + 36.53 255 256 gg := mpu6050GyroGain[m.gyroFs] 257 m.Gyroscope.X = float64(gyro.X) / gg 258 m.Gyroscope.Y = float64(gyro.Y) / gg 259 m.Gyroscope.Z = float64(gyro.Z) / gg 260 261 return 262 } 263 264 func (m *MPU6050Driver) waitForReset() error { 265 wait := 100 * time.Millisecond 266 start := time.Now() 267 for { 268 if time.Since(start) > wait { 269 return fmt.Errorf("timeout on wait for reset is done") 270 } 271 if val, err := m.connection.ReadByteData(mpu6050Reg_PwrMgmt1); (val&mpu6050Pwr1_DeviceResetBit == 0) && (err == nil) { 272 return nil 273 } 274 time.Sleep(wait / 10) 275 } 276 } 277 278 func (m *MPU6050Driver) initialize() (err error) { 279 // reset device and wait for reset is finished 280 if err = m.connection.WriteByteData(mpu6050Reg_PwrMgmt1, mpu6050Pwr1_DeviceResetBit); err != nil { 281 return 282 } 283 if err = m.waitForReset(); err != nil { 284 return 285 } 286 287 // reset signal path register 288 reset := uint8(mpu6050SignalReset_TempBit | mpu6050SignalReset_AccelBit | mpu6050SignalReset_GyroBit) 289 if err = m.connection.WriteByteData(mpu6050Reg_SignalPathReset, reset); err != nil { 290 return 291 } 292 time.Sleep(100 * time.Millisecond) 293 294 // configure digital filter bandwidth and external frame synchronization (bits 3...5 are used) 295 generalConf := uint8(m.dlpf) | uint8(m.frameSync)<<3 296 if err = m.connection.WriteByteData(mpu6050Reg_GeneralConfig, generalConf); err != nil { 297 return 298 } 299 300 // set full scale range of gyroscope (bits 3 and 4 are used) 301 if err = m.connection.WriteByteData(mpu6050Reg_GyroConfig, uint8(m.gyroFs)<<3); err != nil { 302 return 303 } 304 305 // set full scale range of accelerometer (bits 3 and 4 are used) 306 if err = m.connection.WriteByteData(mpu6050Reg_AccelConfig, uint8(m.accelFs)<<3); err != nil { 307 return 308 } 309 310 // set clock source and reset sleep 311 pwr1 := uint8(m.clock) & ^uint8(mpu6050Pwr1_SleepOnBit) 312 if err = m.connection.WriteByteData(mpu6050Reg_PwrMgmt1, pwr1); err != nil { 313 return 314 } 315 316 return 317 }