/* Copyright 2025 by COG9 Enterprises LLC
 * All rights reserved.
 * Title: ina219.ino
 * Author: Jed Marti KI7NNP
 * Description: INA219 non-object code.
 * Created: Sun Dec 28 10:18:26 2025
 * Revision History:
 */

#include <Wire.h>

#include "ina219.h"

// Error messages in case you need them.
static char *inaErrorStr[] = {"OK", "bad device", "bad I2C address",
                              "no answer", "bad range", "bad PGA", "bad BADC", "bad SADC",
                              "bad mode"};


static void writeINAReg(INA219 *dev, uint8_t reg, uint16_t val)
/*     Write a 16 bit value to a register for this device.
 * Parameters:
 *   dev: Device descriptor.
 *   reg: One of the INA219's register number.
 *   val: 16 bit value to write.
 * External References:
 *   Wire: I2C I/O controller.
 * Returned values: none
 * Errors detected: none
 */
{
  Wire.begin();
  Wire.beginTransmission(dev->i2caddr);
  Wire.write(reg);
  Wire.write(val >> 8);
  Wire.write(val & 0xFF);
  Wire.endTransmission();
}



/*
static uint32_t readINAReg(INA219 *dev, uint8_t reg)
{
  uint32_t rval;
  Wire.begin();
  Wire.beginTransmission(dev->i2caddr);
  Wire.write(reg);
  if (Wire.endTransmission() != 0) return 0xFFFFFFFF;
  Wire.requestFrom(dev->i2caddr, 2);
  rval = (uint32_t) Wire.read();
  rval <<= 8;
  rval |= Wire.read();
  Wire.endTransmission();
  return rval;
}
*/



static uint16_t readINAReg16(INA219 *dev, uint8_t reg)
/*     Read  the contents of a a register. Returns 0xFFFF on failure to read but you should have
 *   checked that it's OK before calling this.
 * Parameters:
 *   dev: The INA219 structure. We need the device address.
 *   reg: One of the registers (0 - 5) only, not checked.
 * External References:
 *   Wire: Arduino I2C control object.
 * Returned: The value of the register or 0xFFFF if the write register number fails.
 * Errors detected: Failure to write the register number, probably the wrong i2c address.
 */
{
  uint16_t rval;
  Wire.begin();
  Wire.beginTransmission(dev->i2caddr);
  Wire.write(reg);
  if (Wire.endTransmission() != 0) return 0xFFFF;
  Wire.requestFrom(dev->i2caddr, 2);
  rval = (uint16_t) Wire.read();
  rval <<= 8;
  rval |= Wire.read();
  Wire.endTransmission();
  return rval;
}


void ina219Reset(INA219 *dev)
/*     Reset the selected INA219.
 * Parameters:
 *   dev: The INA219 structure. We need the device address.
 * External References: constants from ina219.h
 * Returned: Nothing.
 * Errors detected: None, but bad dev ignored.
 */
{
  if (dev != NULL && dev->i2caddr != 0) writeINAReg(dev, INA219_Configuration, 0x8000);
}


float ina219GetVoltage(INA219 *dev)
/*     Get the bus voltage and return.
 */
{
  if (dev == NULL || dev->i2caddr == 0) return -999.0f;
  return (readINAReg16(dev, INA219_BusVoltage) >> 1) * 0.001f;
}



float ina219GetShuntVoltage(INA219 *dev)
/*     Get the voltage across the shunt, value in millivolts.
 */
{
  if (dev->i2caddr == 0) return -999999.0f;
  return (float) readINAReg16(dev, INA219_ShuntVoltage) * 0.01f;
}


uint16_t ina219GetCurrent(INA219 *dev)
/*     Get the current by dividing the shunt voltage by our known resistance (.05 ohms).
 *   Forget their complex calibration which doesn't seem to work too well. Value returned is
 *   milliamps.
 */
{
  uint16_t shv;
  
  if (dev->i2caddr == 0) return 0xFFFFFFFF;

  // The shunt voltage in millivolts.
  shv = readINAReg16(dev, INA219_ShuntVoltage) / 100;
  return shv * 20;
}


float ina219GetCurrentf(INA219 *dev)
  
{
  float shv_mv;

  if (dev->i2caddr == 0) return -999999.0f;

  // Voltage in millivolts.
  shv_mv = (float) readINAReg16(dev, INA219_ShuntVoltage) * 0.01f;
  return (shv_mv * 20.0f);
}


void ina219Begin(INA219 *dev)
/*     Initialize one of the sensors. See record.ino initialization information. Calibration
 *   values are read from EEPROM at startup.
 * Parameters:
 *   dev: Address of the control block (see record.ino).
 *
 */
{
  uint16_t config;
  
  // See if the device descriptor has a good address and answers.
  if (dev == NULL) return;
  if (dev->i2caddr == 0 || dev->i2caddr > 127)
  {
    dev->errno = INA219_BADI2CADDR;
    return;
  }
  dev->errno = INA219_OK;

  // Fire up I2C on this device and punt if not there.
  Wire.beginTransmission(dev->i2caddr);
  if (Wire.endTransmission() != 0)
  {
    dev->errno = INA219_NODEVICE;
    return;
  }

  // Set voltage range. This is the BRNG bit in configuration register 0.
  if (dev->range == 16) config = 0;
  else if (dev->range == 32) config = 0x2000;
  else
  {
    dev->errno = INA219_BADRANGE;
    return;
  }    
    
  // PGA bits. 
  if (dev->pg > 3)
  {
     dev->errno = INA219_BADPGA;
     return;
  }
  config |= ((uint16_t) (dev->pg)) << 11;

  // First set the number of bits, we'll set the samples on the second round.
  if (dev->badc > 0x0F)
  {
    dev->errno = INA219_BADC;
    return;
  }
  if (dev->sadc > 0x0F)
  {
    dev->errno = INA219_SADC;
    return;
  }
  if (dev->mode > 0x07)
  {
    dev->errno = INA219_BADMODE;
    return;
  }
  
  config |= (((uint16_t) (dev->badc)) << 7) |
    (((uint16_t) (dev->sadc)) << 3) |
    dev->mode;

  // First write the configuration.
  //Serial.print("config = 0x"); Serial.println(config, HEX);
  writeINAReg(dev, INA219_Configuration, config);
  
  // Good to go.
  dev->errno = INA219_OK;
}


char *ina219Emsg(INA219 *dev)
{
  return inaErrorStr[dev->errno];
}

void testsh(INA219 *m)
{
  float sv;
  float v;
  float i;
  sv = ina219GetShuntVoltage(m) / 100.0;
  v = ina219GetVoltage(m);
  Serial.println(sv, 4);
  Serial.println(v, 4);
  i = sv / 0.05;
  Serial.print(i * 100.0f, 1); Serial.println(" ma");
}



/* Debug code to test.
uint16_t c;

// Supply A.
INA219 a5 = {
             .i2caddr = 0x40,
             .range = 16,
             .pg = 0x03,
             .badc = 0x0B,
             .sadc = 0x0C,
             .mode = 0x07,
            .calibration = 4096 };

void setup(void)
{
  uint8_t rc;
  
  Serial.begin(115200);
  Wire.begin();
  delay(40);

  Serial.println("Starting");

  rc = ina219Init(&a5);
  Serial.print("current = "); Serial.print(ina219GetCurrent(&a5)); Serial.print("\n");
  Serial.print("voltage = "); Serial.print(ina219GetVoltage(&a5)); Serial.print("\n");
}

void loop(void)
{
  //  Serial.print(ina219GetVoltage(&a5), 2); Serial.print(" ");
  //  Serial.print(ina219GetCurrent(&a5), 1); Serial.print("\n");
  delay(500);
}
*/

// EOF
