001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2008-2018 FIRST. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj;
009
010import java.nio.ByteBuffer;
011import java.nio.ByteOrder;
012
013import edu.wpi.first.networktables.NetworkTableEntry;
014import edu.wpi.first.wpilibj.hal.FRCNetComm.tInstances;
015import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
016import edu.wpi.first.wpilibj.hal.HAL;
017import edu.wpi.first.wpilibj.interfaces.Accelerometer;
018import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
019
020/**
021 * ADXL345 I2C Accelerometer.
022 */
023@SuppressWarnings({"TypeName", "PMD.UnusedPrivateField"})
024public class ADXL345_I2C extends SensorBase implements Accelerometer, Sendable {
025  private static final byte kAddress = 0x1D;
026  private static final byte kPowerCtlRegister = 0x2D;
027  private static final byte kDataFormatRegister = 0x31;
028  private static final byte kDataRegister = 0x32;
029  private static final double kGsPerLSB = 0.00390625;
030  private static final byte kPowerCtl_Link = 0x20;
031  private static final byte kPowerCtl_AutoSleep = 0x10;
032  private static final byte kPowerCtl_Measure = 0x08;
033  private static final byte kPowerCtl_Sleep = 0x04;
034
035  private static final byte kDataFormat_SelfTest = (byte) 0x80;
036  private static final byte kDataFormat_SPI = 0x40;
037  private static final byte kDataFormat_IntInvert = 0x20;
038  private static final byte kDataFormat_FullRes = 0x08;
039  private static final byte kDataFormat_Justify = 0x04;
040
041  public enum Axes {
042    kX((byte) 0x00),
043    kY((byte) 0x02),
044    kZ((byte) 0x04);
045
046    /**
047     * The integer value representing this enumeration.
048     */
049    @SuppressWarnings("MemberName")
050    public final byte value;
051
052    Axes(byte value) {
053      this.value = value;
054    }
055  }
056
057  @SuppressWarnings("MemberName")
058  public static class AllAxes {
059    public double XAxis;
060    public double YAxis;
061    public double ZAxis;
062  }
063
064  protected I2C m_i2c;
065
066  /**
067   * Constructs the ADXL345 Accelerometer with I2C address 0x1D.
068   *
069   * @param port  The I2C port the accelerometer is attached to
070   * @param range The range (+ or -) that the accelerometer will measure.
071   */
072  public ADXL345_I2C(I2C.Port port, Range range) {
073    this(port, range, kAddress);
074  }
075
076  /**
077   * Constructs the ADXL345 Accelerometer over I2C.
078   *
079   * @param port          The I2C port the accelerometer is attached to
080   * @param range         The range (+ or -) that the accelerometer will measure.
081   * @param deviceAddress I2C address of the accelerometer (0x1D or 0x53)
082   */
083  public ADXL345_I2C(I2C.Port port, Range range, int deviceAddress) {
084    m_i2c = new I2C(port, deviceAddress);
085
086    // Turn on the measurements
087    m_i2c.write(kPowerCtlRegister, kPowerCtl_Measure);
088
089    setRange(range);
090
091    HAL.report(tResourceType.kResourceType_ADXL345, tInstances.kADXL345_I2C);
092    setName("ADXL345_I2C", port.value);
093  }
094
095  @Override
096  public void free() {
097    super.free();
098    m_i2c.free();
099  }
100
101  @Override
102  public void setRange(Range range) {
103    final byte value;
104
105    switch (range) {
106      case k2G:
107        value = 0;
108        break;
109      case k4G:
110        value = 1;
111        break;
112      case k8G:
113        value = 2;
114        break;
115      case k16G:
116        value = 3;
117        break;
118      default:
119        throw new IllegalArgumentException(range + " unsupported range type");
120    }
121
122    // Specify the data format to read
123    m_i2c.write(kDataFormatRegister, kDataFormat_FullRes | value);
124  }
125
126  @Override
127  public double getX() {
128    return getAcceleration(Axes.kX);
129  }
130
131  @Override
132  public double getY() {
133    return getAcceleration(Axes.kY);
134  }
135
136  @Override
137  public double getZ() {
138    return getAcceleration(Axes.kZ);
139  }
140
141  /**
142   * Get the acceleration of one axis in Gs.
143   *
144   * @param axis The axis to read from.
145   * @return Acceleration of the ADXL345 in Gs.
146   */
147  public double getAcceleration(Axes axis) {
148    ByteBuffer rawAccel = ByteBuffer.allocate(2);
149    m_i2c.read(kDataRegister + axis.value, 2, rawAccel);
150
151    // Sensor is little endian... swap bytes
152    rawAccel.order(ByteOrder.LITTLE_ENDIAN);
153    return rawAccel.getShort(0) * kGsPerLSB;
154  }
155
156  /**
157   * Get the acceleration of all axes in Gs.
158   *
159   * @return An object containing the acceleration measured on each axis of the ADXL345 in Gs.
160   */
161  public AllAxes getAccelerations() {
162    AllAxes data = new AllAxes();
163    ByteBuffer rawData = ByteBuffer.allocate(6);
164    m_i2c.read(kDataRegister, 6, rawData);
165
166    // Sensor is little endian... swap bytes
167    rawData.order(ByteOrder.LITTLE_ENDIAN);
168    data.XAxis = rawData.getShort(0) * kGsPerLSB;
169    data.YAxis = rawData.getShort(2) * kGsPerLSB;
170    data.ZAxis = rawData.getShort(4) * kGsPerLSB;
171    return data;
172  }
173
174  @Override
175  public void initSendable(SendableBuilder builder) {
176    builder.setSmartDashboardType("3AxisAccelerometer");
177    NetworkTableEntry entryX = builder.getEntry("X");
178    NetworkTableEntry entryY = builder.getEntry("Y");
179    NetworkTableEntry entryZ = builder.getEntry("Z");
180    builder.setUpdateTable(() -> {
181      AllAxes data = getAccelerations();
182      entryX.setDouble(data.XAxis);
183      entryY.setDouble(data.YAxis);
184      entryZ.setDouble(data.ZAxis);
185    });
186  }
187}