using System; using Microsoft.SPOT; using Microsoft.SPOT.Hardware; using System.Text; namespace HWModules { public class EnergyMonitorLTC2946 : I2CPortBase { /// /// Input wiring pin for voltage source to measure. /// public enum VoltageSource { SensePlus, Adin } private const double minVoltage = 26.5; private readonly byte[] LTC2946_ADIN_MSB_REG = { 0x28 }; private readonly byte[] LTC2946_CONTROLA_REGISTER = { 0x00 }; private readonly byte[] LTC2946_CONTROLB_REGISTER = { 0x01 }; private readonly byte[] LTC2946_POWER_MSB2 = { 0x05 }; private readonly byte[] LTC2946_POWER_MSB1 = { 0x06 }; private readonly byte[] LTC2946_POWER_LSB = { 0x07 }; private readonly byte[] LTC2946_DELTA_SENSE_MSB = { 0x14 }; private readonly byte[] LTC2946_VIN_MSB_REG = { 0x1E }; private readonly byte[] LTC2946_TIMECOUNTER_MSB3 = { 0x34 }; private readonly byte[] LTC2946_TIMECOUNTER_MSB2 = { 0x35 }; private readonly byte[] LTC2946_TIMECOUNTER_MSB1 = { 0x36 }; private readonly byte[] LTC2946_TIMECOUNTER_LSB = { 0x37 }; private readonly byte[] LTC2946_CHARGE_MSB3 = { 0x38 }; private readonly byte[] LTC2946_CHARGE_MSB2 = { 0x39 }; private readonly byte[] LTC2946_CHARGE_MSB1 = { 0x3A }; private readonly byte[] LTC2946_CHARGE_LSB = { 0x3B }; private readonly byte[] LTC2946_ENERGY_MSB3 = { 0x3C }; private readonly byte[] LTC2946_ENERGY_MSB2 = { 0x3D }; private readonly byte[] LTC2946_ENERGY_MSB1 = { 0x3E }; private readonly byte[] LTC2946_ENERGY_LSB = { 0x3F }; private readonly byte[] registerByte = new byte[1]; private readonly byte[] registerBytes2 = new byte[2]; private readonly byte[] registerBytes3 = new byte[3]; private readonly byte[] registerBytes4 = new byte[4]; private EM_VandI emVI; private I2CDevice.I2CReadTransaction _rt; private I2CDevice.I2CWriteTransaction _wt; private int _xActionReturn; private I2CDevice.I2CTransaction[] _xActions2946 = new I2CDevice.I2CTransaction[2]; public EnergyMonitorLTC2946(I2CDevice.Configuration config, int slotnumber) : base(config, slotnumber) { Source = VoltageSource.Adin; milliAmpsPerLSB = 0.5; milliVoltsPerLSB = 0.009828; // let's set up the chip so that we are really sampling on adin if (Source == VoltageSource.Adin) { EngrLogger.Comment("Switching Energy Monitor to use ADIN"); SetCtlARegister(RegCTLA.CHCFG_ALTVI, RegCTLA.VOLT_ADIN, RegCTLA.OFFSETCAL_EVERY, RegCTLA.ADINCFG_GND); } } public EnergyMonitorLTC2946(I2CDevice.Configuration config, VoltageSource source, double millivoltspercount, double milliampspercount, int slotnumber = 0) : base(config, slotnumber) { Source = source; milliVoltsPerLSB = millivoltspercount; milliAmpsPerLSB = milliampspercount; microWattsPerLSB = milliAmpsPerLSB * milliVoltsPerLSB; milliSecondsPerLSB = 16.39543; uCoulombsPerLSB = milliAmpsPerLSB * milliSecondsPerLSB; milliJoulesPerLSB = microWattsPerLSB * 65536 * milliSecondsPerLSB; sbTemp.Clear(); sbTemp.Append("milliVolts/LSB: "); sbTemp.Append(milliVoltsPerLSB.ToString()); sbTemp.Append(", milliAmps/LSB: "); sbTemp.Append(milliAmpsPerLSB.ToString()); sbTemp.Append(", microWatts/LSB: "); sbTemp.Append(microWattsPerLSB.ToString()); sbTemp.Append(", milliSeconds/LSB: "); sbTemp.Append(milliSecondsPerLSB.ToString()); sbTemp.Append(", uCoulombs/LSB: "); sbTemp.Append(uCoulombsPerLSB.ToString()); sbTemp.Append(", milliJoules/LSB: "); sbTemp.Append(milliJoulesPerLSB.ToString()); EngrLogger.Comment(sbTemp.ToString()); // let's set up the chip so that we are really sampling on adin if (Source == VoltageSource.Adin) { EngrLogger.Comment("Switching Energy Monitor to use ADIN"); SetCtlARegister(RegCTLA.CHCFG_ALTVI, RegCTLA.VOLT_ADIN, RegCTLA.OFFSETCAL_EVERY, RegCTLA.ADINCFG_GND); } } //Properties public VoltageSource Source { get; private set; } public double milliAmpsPerLSB { get; private set; } public double milliVoltsPerLSB { get; private set; } public double microWattsPerLSB { get; private set; } public double milliSecondsPerLSB { get; private set; } public double uCoulombsPerLSB { get; private set; } public double milliJoulesPerLSB { get; private set; } public double getMilliAmps() { double milliAmps = double.NaN; // create write buffer to read current _wt = CreateWriteTransaction(LTC2946_DELTA_SENSE_MSB); // create read buffer to read the returned data _rt = CreateReadTransaction(registerBytes2); // execute transaction _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { EngrLogger.Comment("***ERROR getMilliAmps() executing 2946 Transaction = " + _xActionReturn.ToString()); } else { milliAmps = milliAmpsPerLSB * ((registerBytes2[0] << 4) | (registerBytes2[1] >> 4)); } return milliAmps; } public double getMilliVolts() { double milliVolts = double.NaN; // create write buffer to read voltage switch (Source) { case VoltageSource.SensePlus: _wt = CreateWriteTransaction(LTC2946_VIN_MSB_REG); break; case VoltageSource.Adin: _wt = CreateWriteTransaction(LTC2946_ADIN_MSB_REG); break; default: EngrLogger.Comment("***ERROR getVolts() invalid VoltageSource"); return milliVolts; } // create read buffer to read the returned data _rt = CreateReadTransaction(registerBytes2); // execute transaction _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { EngrLogger.Comment("***ERROR getVolts() executing 2946 Transaction = " + _xActionReturn.ToString()); } else { milliVolts = milliVoltsPerLSB * ((registerBytes2[0] << 4) | (registerBytes2[1] >> 4)); } return milliVolts; } public double getMilliWatts() { double milliWatts = double.NaN; // create write buffer to read current _wt = CreateWriteTransaction(LTC2946_POWER_MSB2); // create read buffer to read the returned data _rt = CreateReadTransaction(registerBytes3); // execute transaction _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { EngrLogger.Comment("***ERROR getMilliWatts() executing 2946 Transaction = " + _xActionReturn.ToString()); } else { milliWatts = microWattsPerLSB * ((double)(registerBytes3[0] * 65536) + (double)(registerBytes3[1] * 256) + (double)(registerBytes3[2])); } return milliWatts; } public double getMilliSeconds() { double milliSeconds = double.NaN; // create write buffer to read current _wt = CreateWriteTransaction(LTC2946_TIMECOUNTER_MSB3); // create read buffer to read the returned data _rt = CreateReadTransaction(registerBytes4); // execute transaction _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { EngrLogger.Comment("***ERROR getMilliSeconds() executing 2946 Transaction = " + _xActionReturn.ToString()); } else { milliSeconds = milliSecondsPerLSB * ((double)(registerBytes4[0] * 16777216) + (double)(registerBytes4[1] * 65536) + (double)(registerBytes4[2] * 256) + (double)(registerBytes4[3])); } return milliSeconds; } public double getUCoulombs() { double uCoulombs = double.NaN; // create write buffer to read current _wt = CreateWriteTransaction(LTC2946_CHARGE_MSB3); // create read buffer to read the returned data _rt = CreateReadTransaction(registerBytes4); // execute transaction _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { EngrLogger.Comment("***ERROR getUCoulombs() executing 2946 Transaction = " + _xActionReturn.ToString()); } else { uCoulombs = uCoulombsPerLSB * ((double)(registerBytes4[0] * 16777216) + (double)(registerBytes4[1] * 65536) + (double)(registerBytes4[2] * 256) + (double)(registerBytes4[3])); } return uCoulombs; } public double getJoules() { double Joules = double.NaN; // create write buffer to read current _wt = CreateWriteTransaction(LTC2946_ENERGY_MSB3); // create read buffer to read the returned data _rt = CreateReadTransaction(registerBytes4); // execute transaction _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { EngrLogger.Comment("***ERROR getJoules() executing 2946 Transaction = " + _xActionReturn.ToString()); } else { Joules = milliJoulesPerLSB * ((double)(registerBytes4[0] * 16777216) + (double)(registerBytes4[1] * 65536) + (double)(registerBytes4[2] * 256) + (double)(registerBytes4[3])); } return Joules; } private emIVWTCE ivwtce; public emIVWTCE getIVWTCE() { ivwtce.milliAmps = double.NaN; ivwtce.milliVolts = double.NaN; ivwtce.microWatts = double.NaN; ivwtce.milliSeconds = double.NaN; ivwtce.uCoulombs = double.NaN; ivwtce.milliJoules = double.NaN; ivwtce.milliAmps = getMilliAmps(); ivwtce.milliVolts = getMilliVolts(); ivwtce.microWatts = getMilliWatts(); ivwtce.milliSeconds = getMilliSeconds(); ivwtce.uCoulombs = getUCoulombs(); ivwtce.milliJoules = getJoules(); return ivwtce; } public EM_VandI getVandI() { emVI.milliAmps = double.NaN; emVI.milliVolts = double.NaN; // create write buffer to read voltage switch (Source) { case VoltageSource.SensePlus: _wt = CreateWriteTransaction(LTC2946_VIN_MSB_REG); break; case VoltageSource.Adin: _wt = CreateWriteTransaction(LTC2946_ADIN_MSB_REG); break; default: return emVI; } // create read buffer to read the returned data _rt = CreateReadTransaction(registerBytes2); _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { //EngrLogger.writeToColumns("[ERROR] executing 2946 Transaction = " + xActionReturn.ToString()); } else { emVI.milliVolts = milliVoltsPerLSB * ((registerBytes2[0] << 4) | (registerBytes2[1] >> 4)); } // create write buffer to read current _xActions2946[0] = CreateWriteTransaction(LTC2946_DELTA_SENSE_MSB); _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { //EngrLogger.writeToColumns("[ERROR] executing 2946 Transaction = " + xActionReturn.ToString()); } else { emVI.milliAmps = milliAmpsPerLSB * ((registerBytes2[0] << 4) | (registerBytes2[1] >> 4)); } return emVI; } private EM_VandI vi; private EM_VICE vice; private StringBuilder sbTemp = new StringBuilder(64); public EM_VICE getVICE() { vice.milliVolts = double.NaN; vice.milliAmps = double.NaN; vice.uCoulombs = double.NaN; vice.milliJoules = double.NaN; vi = getVandI(); vice.milliVolts = vi.milliVolts; vice.milliAmps = vi.milliAmps; // create write buffer to read 4 bytes of charge accumulator _wt = CreateWriteTransaction(LTC2946_CHARGE_MSB3); // create read buffer to read the returned data _rt = CreateReadTransaction(registerBytes4); registerBytes4[0] = 0; registerBytes4[1] = 0; registerBytes4[2] = 0; registerBytes4[3] = 0; _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { //EngrLogger.writeToColumns("Error executing 2946 Transaction = " + xActionReturn.ToString()); } else { vice.uCoulombs = (double)(registerBytes4[0] * 16777216) + (double)(registerBytes4[1] * 65536) + (double)(registerBytes4[2] * 256) + (double)(registerBytes4[3]); vice.uCoulombs = vice.uCoulombs * uCoulombsPerLSB; sbTemp.Clear(); sbTemp.Append("charge bytes 0-3:\t"); sbTemp.Append(registerBytes4[0]); sbTemp.Append("\t"); sbTemp.Append(registerBytes4[1]); sbTemp.Append("\t"); sbTemp.Append(registerBytes4[2]); sbTemp.Append("\t"); sbTemp.Append(registerBytes4[3]); //Debug.Print(sbTemp.ToString()); } // create write buffer to read 4 bytes of energy accumulator _wt = CreateWriteTransaction(LTC2946_ENERGY_MSB3); // create read buffer to read the returned data _rt = CreateReadTransaction(registerBytes4); registerBytes4[0] = 0; registerBytes4[1] = 0; registerBytes4[2] = 0; registerBytes4[3] = 0; _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { //EngrLogger.writeToColumns("Error executing 2946 Transaction = " + xActionReturn.ToString()); } else { vice.milliJoules = (double)(registerBytes4[0] * 16777216) + (double)(registerBytes4[1] * 65536) + (double)(registerBytes4[2] * 256) + (double)registerBytes4[3]; //For engineering units: joules = counts * mA/count * V/count * factor for reading upper 32 bits of 48 bit register * seconds per reading * 1/1000 amps per milliamp vice.milliJoules = vice.milliJoules * milliJoulesPerLSB; sbTemp.Clear(); sbTemp.Append("joule bytes 0-3:\t"); sbTemp.Append(registerBytes4[0]); sbTemp.Append("\t"); sbTemp.Append(registerBytes4[1]); sbTemp.Append("\t"); sbTemp.Append(registerBytes4[2]); sbTemp.Append("\t"); sbTemp.Append(registerBytes4[3]); //Debug.Print(sbTemp.ToString()); } return vice; } public bool ResetAccumulators() { var ctlRegister = (byte)3; registerBytes2[0] = LTC2946_CONTROLB_REGISTER[0]; registerBytes2[1] = ctlRegister; _wt = CreateWriteTransaction(registerBytes2); _rt = CreateReadTransaction(registerByte); _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { //Debug.Print("[ERROR] Failed to Set CtlA Register"); } else { if (registerByte[0] == ctlRegister) return true; //Debug.Print("GOT Control Register"); } return false; } public byte ReadCtlARegister() { _wt = CreateWriteTransaction(LTC2946_CONTROLA_REGISTER); _rt = CreateReadTransaction(registerByte); _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); return _xActionReturn == 0 ? byte.MaxValue : registerByte[0]; } public byte ReadCtlBRegister() { _wt = CreateWriteTransaction(LTC2946_CONTROLB_REGISTER); _rt = CreateReadTransaction(registerByte); _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); return _xActionReturn == 0 ? byte.MaxValue : registerByte[0]; } private bool SetCtlARegister(RegCTLA chcfgCtla, RegCTLA voltCtla, RegCTLA offsetCtla, RegCTLA adinCtla) { var ctlRegister = (byte)(chcfgCtla | voltCtla | offsetCtla | adinCtla); registerBytes2[0] = LTC2946_CONTROLA_REGISTER[0]; registerBytes2[1] = ctlRegister; _wt = CreateWriteTransaction(registerBytes2); _rt = CreateReadTransaction(registerByte); _xActions2946 = new I2CDevice.I2CTransaction[] { _wt, _rt }; _xActionReturn = Execute(_xActions2946, 500); if (_xActionReturn == 0) { //Debug.Print("[ERROR] Failed to Set CtlA Register"); } else { if (registerByte[0] == ctlRegister) return true; //Debug.Print("GOT Control Register"); } return false; } [Flags] private enum RegCTLA : byte { CHCFG_ALTVI = 0x00, CHCFG_VI16DC = 0x01, CHCFG_VI128DC = 0x02, CHCFG_ADINNODIV = 0x03, CHCFG_ADIN32DC = 0x04, CHCFG_ADIN256DC = 0x05, CHCFG_VONE_IINF = 0x06, CHCFG_SNAPSHOT = 0x07, VOLT_DELTA = 0x00 << 3, VOLT_VDD = 0x01 << 3, VOLT_ADIN = 0x02 << 3, VOLT_SENSEP = 0x03 << 3, OFFSETCAL_EVERY = 0x00 << 5, OFFSETCAL_EVERY16 = 0x01 << 5, OFFSETCAL_EVERY128 = 0x02 << 5, OFFSETCAL_POWERUP = 0x03 << 5, ADINCFG_GND = 0x00 << 7, ADINCFG_INTVCC = 0x01 << 7 } public struct EM_VandI { public double milliVolts; public double milliAmps; public override string ToString() { return "milliVolts: " + milliVolts.ToString() + " Milliamps: " + milliAmps.ToString(); } } public struct EM_VICE { public double milliVolts; public double milliAmps; public double uCoulombs; public double milliJoules; public override string ToString() { return "Volts: " + milliVolts.ToString() + " Milliamps: " + milliAmps.ToString() + " Charge: " + uCoulombs.ToString() + " Joules: " + milliJoules.ToString(); ; } } public struct emIVWTCE { public double milliAmps; public double milliVolts; public double microWatts; public double milliSeconds; public double uCoulombs; public double milliJoules; public override string ToString() { return "MilliSecond: " + milliSeconds.ToString("f1") + " milliVolts: " + milliVolts.ToString("f3") + " Milliamps: " + milliAmps.ToString("f1") + " microWatts: " + microWatts.ToString("f2") + " uCoulombs: " + uCoulombs.ToString("f0") + " milliJoules: " + milliJoules.ToString("f1"); } } public struct SystemVICE { public double pumpMilliVolts; public double pumpMilliamps; public double pumpUCoulombs; public double pumpMilliJoules; public double hotelMilliVolts; public double hotelMilliamps; public double hotelUCoulombs; public double hotelMilliJoules; } } }