001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package org.wpilib.hardware.accelerometer; 006 007import java.nio.ByteBuffer; 008import java.nio.ByteOrder; 009import org.wpilib.hardware.bus.I2C; 010import org.wpilib.hardware.hal.HAL; 011import org.wpilib.hardware.hal.SimDevice; 012import org.wpilib.hardware.hal.SimDouble; 013import org.wpilib.hardware.hal.SimEnum; 014import org.wpilib.networktables.DoublePublisher; 015import org.wpilib.networktables.DoubleTopic; 016import org.wpilib.networktables.NTSendable; 017import org.wpilib.networktables.NTSendableBuilder; 018import org.wpilib.util.sendable.SendableRegistry; 019 020/** ADXL345 I2C Accelerometer. */ 021@SuppressWarnings("TypeName") 022public class ADXL345_I2C implements NTSendable, AutoCloseable { 023 /** Default I2C device address. */ 024 public static final byte DEFAULT_ADDRESS = 0x1D; 025 026 private static final byte POWER_CTL_REGISTER = 0x2D; 027 private static final byte DATA_FORMAT_REGISTER = 0x31; 028 private static final byte DATA_REGISTER = 0x32; 029 private static final double GS_PER_LSB = 0.00390625; 030 // private static final byte POWER_CTL_LINK = 0x20; 031 // private static final byte POWER_CTL_AUTO_SLEEP = 0x10; 032 private static final byte POWER_CTL_MEASURE = 0x08; 033 // private static final byte POWER_CTL_SLEEP = 0x04; 034 035 // private static final byte DATA_FORMAT_SELF_TEST = (byte) 0x80; 036 // private static final byte DATA_FORMAT_SPI = 0x40; 037 // private static final byte DATA_FORMAT_INT_INVERT = 0x20; 038 private static final byte DATA_FORMAT_FULL_RES = 0x08; 039 040 // private static final byte DATA_FORMAT_JUSTIFY = 0x04; 041 042 /** Accelerometer axes. */ 043 public enum Axis { 044 /** X axis. */ 045 X((byte) 0x00), 046 /** Y axis. */ 047 Y((byte) 0x02), 048 /** Z axis. */ 049 Z((byte) 0x04); 050 051 /** The integer value representing this enumeration. */ 052 public final byte value; 053 054 Axis(byte value) { 055 this.value = value; 056 } 057 } 058 059 /** Container type for accelerations from all axes. */ 060 public static class AllAxes { 061 /** Acceleration along the X axis in g-forces. */ 062 public double x; 063 064 /** Acceleration along the Y axis in g-forces. */ 065 public double y; 066 067 /** Acceleration along the Z axis in g-forces. */ 068 public double z; 069 070 /** Default constructor. */ 071 public AllAxes() {} 072 } 073 074 private I2C m_i2c; 075 076 private SimDevice m_simDevice; 077 private SimEnum m_simRange; 078 private SimDouble m_simX; 079 private SimDouble m_simY; 080 private SimDouble m_simZ; 081 082 /** 083 * Constructs the ADXL345 Accelerometer with I2C address 0x1D. 084 * 085 * @param port The I2C port the accelerometer is attached to 086 * @param range The range (+ or -) that the accelerometer will measure. Valid values are 2, 4, 8, 087 * or 16 Gs. 088 */ 089 public ADXL345_I2C(I2C.Port port, int range) { 090 this(port, range, DEFAULT_ADDRESS); 091 } 092 093 /** 094 * Constructs the ADXL345 Accelerometer over I2C. 095 * 096 * @param port The I2C port the accelerometer is attached to 097 * @param range The range (+ or -) that the accelerometer will measure. Valid values are 2, 4, 8, 098 * or 16 Gs. 099 * @param deviceAddress I2C address of the accelerometer (0x1D or 0x53) 100 */ 101 @SuppressWarnings("this-escape") 102 public ADXL345_I2C(I2C.Port port, int range, int deviceAddress) { 103 m_i2c = new I2C(port, deviceAddress); 104 105 // simulation 106 m_simDevice = SimDevice.create("Accel:ADXL345_I2C", port.value, deviceAddress); 107 if (m_simDevice != null) { 108 m_simRange = 109 m_simDevice.createEnumDouble( 110 "range", 111 SimDevice.Direction.OUTPUT, 112 new String[] {"2G", "4G", "8G", "16G"}, 113 new double[] {2.0, 4.0, 8.0, 16.0}, 114 0); 115 m_simX = m_simDevice.createDouble("x", SimDevice.Direction.INPUT, 0.0); 116 m_simY = m_simDevice.createDouble("y", SimDevice.Direction.INPUT, 0.0); 117 m_simZ = m_simDevice.createDouble("z", SimDevice.Direction.INPUT, 0.0); 118 } 119 120 // Turn on the measurements 121 m_i2c.write(POWER_CTL_REGISTER, POWER_CTL_MEASURE); 122 123 setRange(range); 124 125 HAL.reportUsage("I2C[" + port.value + "][" + deviceAddress + "]", "ADXL345"); 126 SendableRegistry.add(this, "ADXL345_I2C", port.value); 127 } 128 129 /** 130 * Returns the I2C port. 131 * 132 * @return The I2C port. 133 */ 134 public I2C.Port getPort() { 135 return m_i2c.getPort(); 136 } 137 138 /** 139 * Returns the I2C device address. 140 * 141 * @return The I2C device address. 142 */ 143 public int getDeviceAddress() { 144 return m_i2c.getDeviceAddress(); 145 } 146 147 @Override 148 public void close() { 149 SendableRegistry.remove(this); 150 if (m_i2c != null) { 151 m_i2c.close(); 152 m_i2c = null; 153 } 154 if (m_simDevice != null) { 155 m_simDevice.close(); 156 m_simDevice = null; 157 } 158 } 159 160 /** 161 * Set the measuring range of the accelerometer. 162 * 163 * @param range The maximum acceleration, positive or negative, that the accelerometer will 164 * measure. Valid values are 2, 4, 8, or 16 Gs. 165 */ 166 public final void setRange(int range) { 167 final byte value = 168 switch (range) { 169 case 2 -> 0; 170 case 4 -> 1; 171 case 8 -> 2; 172 case 16 -> 3; 173 default -> 0; // default to 2G if invalid value is passed in 174 }; 175 176 // Specify the data format to read 177 m_i2c.write(DATA_FORMAT_REGISTER, DATA_FORMAT_FULL_RES | value); 178 179 if (m_simRange != null) { 180 m_simRange.set(value); 181 } 182 } 183 184 /** 185 * Returns the acceleration along the X axis in g-forces. 186 * 187 * @return The acceleration along the X axis in g-forces. 188 */ 189 public double getX() { 190 return getAcceleration(Axis.X); 191 } 192 193 /** 194 * Returns the acceleration along the Y axis in g-forces. 195 * 196 * @return The acceleration along the Y axis in g-forces. 197 */ 198 public double getY() { 199 return getAcceleration(Axis.Y); 200 } 201 202 /** 203 * Returns the acceleration along the Z axis in g-forces. 204 * 205 * @return The acceleration along the Z axis in g-forces. 206 */ 207 public double getZ() { 208 return getAcceleration(Axis.Z); 209 } 210 211 /** 212 * Get the acceleration of one axis in Gs. 213 * 214 * @param axis The axis to read from. 215 * @return Acceleration of the ADXL345 in Gs. 216 */ 217 public double getAcceleration(Axis axis) { 218 if (axis == Axis.X && m_simX != null) { 219 return m_simX.get(); 220 } 221 if (axis == Axis.Y && m_simY != null) { 222 return m_simY.get(); 223 } 224 if (axis == Axis.Z && m_simZ != null) { 225 return m_simZ.get(); 226 } 227 ByteBuffer rawAccel = ByteBuffer.allocate(2); 228 m_i2c.read(DATA_REGISTER + axis.value, 2, rawAccel); 229 230 // Sensor is little endian... swap bytes 231 rawAccel.order(ByteOrder.LITTLE_ENDIAN); 232 return rawAccel.getShort(0) * GS_PER_LSB; 233 } 234 235 /** 236 * Get the acceleration of all axes in Gs. 237 * 238 * @return An object containing the acceleration measured on each axis of the ADXL345 in Gs. 239 */ 240 public AllAxes getAccelerations() { 241 AllAxes data = new AllAxes(); 242 if (m_simX != null && m_simY != null && m_simZ != null) { 243 data.x = m_simX.get(); 244 data.y = m_simY.get(); 245 data.z = m_simZ.get(); 246 return data; 247 } 248 ByteBuffer rawData = ByteBuffer.allocate(6); 249 m_i2c.read(DATA_REGISTER, 6, rawData); 250 251 // Sensor is little endian... swap bytes 252 rawData.order(ByteOrder.LITTLE_ENDIAN); 253 data.x = rawData.getShort(0) * GS_PER_LSB; 254 data.y = rawData.getShort(2) * GS_PER_LSB; 255 data.z = rawData.getShort(4) * GS_PER_LSB; 256 return data; 257 } 258 259 @Override 260 public void initSendable(NTSendableBuilder builder) { 261 builder.setSmartDashboardType("3AxisAccelerometer"); 262 DoublePublisher pubX = new DoubleTopic(builder.getTopic("X")).publish(); 263 DoublePublisher pubY = new DoubleTopic(builder.getTopic("Y")).publish(); 264 DoublePublisher pubZ = new DoubleTopic(builder.getTopic("Z")).publish(); 265 builder.addCloseable(pubX); 266 builder.addCloseable(pubY); 267 builder.addCloseable(pubZ); 268 builder.setUpdateTable( 269 () -> { 270 AllAxes data = getAccelerations(); 271 pubX.set(data.x); 272 pubY.set(data.y); 273 pubZ.set(data.z); 274 }); 275 } 276}