Download

Digital Ammeter Program Includes and  Defines

/*

   ammeter.ino  self-contained ammeter 50 ua to 5A LCD display, 50 mv drop

   Works with bipolar DC and AC. Version with added transmissions.

   John Saunders 12/21/2023


   Power consumption:

   Li Battery normal running 18 MA. = 6 days

   Li Battery En to ground 75 uA - 4 years

   AA battery normal operation 6 mA - 10 days

   AA battery, En to ground 1 MA - 2 months - use auto switch

   Needs a capacitor to ground from En to start.

*/

#include <Wire.h>

#include <hd44780.h>                       // main hd44780 header

#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header

hd44780_I2Cexp lcd(0x26 );

#define SW_PORT A5                          //Wiper of 12-position switch and 1o resistor ladder

#define BATT_PORT A9                        //Half battery voltage

#define AA_PORT A2

#define OUT_PORT A0

#define SIGN_PORT A1

#define loopMax 30

#define acRMS 1.11

The value of acRMS adjusts the measured value of AC currents from the measured mean to the customary RMS. It is the fixed vale for a sine wave,

Digital Ammeter Program Global Variables

const int LCD_COLS = 8;

const int LCD_ROWS = 2;

const int STEP = 102;    //For measuring range switch position

const int INIT_COUNT = 52;

const float vref = 3.294;

const float gain = 55.5;

const float mult = 97.66 * vref / gain;   // 5000/(.05 * 1024), 15000 is 3 x that


struct range_t  {

  float errVal;             //Set to correct for shunt resistance errors

  int multVal;            //1 or 3

  int divVal;

  char units;

};


range_t range[10] = {

  //       0                  1                  2                    3                      4

  {.998, 1, 100, 'u'}, {1.011, 3, 100, 'u'}, {.964, 1, 10, 'u'}, {1.044, 3, 10000, 'M'}, {1.218, 1, 1000, 'M'},

  //       5                  6                     7                    8                   9

  {.993, 3, 1000, 'M'}, {1.206, 1, 100, 'M'}, {1.069, 3, 100, 'M'}, {.938, 1, 10, 'M'}, {.369, 3, 1000, 'M'},

};

const int overloadLimit[3] = {1000, 643, 1005};

The value of vref was measured. It is critical for all of the measurements. I may be checked by comparing the voltage of the lithium battery at its charging sockets to the value displayed.

the values of range[] resulted from DC measurements compared to a 4-digit ammeter to get agreement within 1%.

The overall gain calculated from resistor values is 59, but 55.5 was used to cluster the range values around 1.

5000 or 15000 are the nominal full-scale values, but the displayed values are divided by divVal to get engineering units.

Digital Ammeter Program Transmit Functions

void txBatt(void) {

  float battVolt = (analogRead(BATT_PORT) * vref) / 512;

  float AAVolt = vref * analogRead(AA_PORT) / 512 - vref;

  Serial.print("B,");

  Serial.print(battVolt, 2);

  Serial.print("V,");

  Serial.print(AAVolt, 2);

  Serial.println('V');

}


void txCurr(int sw, int sign, int rdn) {

  Serial.print(sw);

  Serial.print(',');

  Serial.print(signChar[sign]);

  Serial.print(rdn / range[sw].divVal);

  Serial.print('.');

  Serial.print(rdn %  range[sw].divVal);

  Serial.print(range[sw].units);

  Serial.print('A');

  if (sign == 1) {

    Serial.print(" AC");

  }

  Serial.println("  ");

}

Digital Ammeter Program Utility Functions

const char signChar[3] = {'+', '~', '-'};


int getSwitchPos(void) {

  int retVal = 0;

  int threshold = INIT_COUNT;

  while (threshold < analogRead(SW_PORT)) {

    threshold += STEP;

    retVal++;

  }

  return retVal;

}


int getSign(void) {

  int retval = '1';

  if (analogRead(SIGN_PORT) > 115) {

    retval = 2;

  }

  if (analogRead(SIGN_PORT) < 34) {

    retval = 0;

  }

  return retval;

}

Digital Ammeter Program Display Functions

void dispBatt(void) {

  float battVolt = (analogRead(BATT_PORT) * vref) / 512;

  float AAVolt = vref * analogRead(AA_PORT) / 512 - vref;

  lcd.print(battVolt, 2);

  lcd.print(" V ,");

  lcd.setCursor(0, 1);

  lcd.print(' ');

  lcd.print(AAVolt, 2);

  lcd.print(" V ");

}


void dispCurr(int sw, int sign, int rdn) {

  lcd.print(signChar[sign]);

  lcd.print(' ');

  lcd.print(rdn / range[sw].divVal);

  lcd.print('.');

  lcd.print(rdn %  range[sw].divVal);

  lcd.setCursor((range[sw].multVal - 1), 1);

  lcd.print(range[sw].units);

  lcd.print('A');

  if (sign == 1) {

    lcd.print(" AC   ");

  }

  else {

    lcd.print("      ");

  }

}

Digital Ammeter Program Setup

void setup() {

  int status;

  int i;

  pinMode(11, OUTPUT);

  digitalWrite(11, HIGH);

  Serial.begin(9600);

  /*

    while (!Serial) {

    ; // wait for serial port to connect. Needed for native USB port only

    }

  */

  status = lcd.begin(LCD_COLS, LCD_ROWS);

  if (status) // non zero status means it was unsuccesful

  {

    // hd44780 has a fatalError() routine that blinks an led if possible

    // begin() failed so blink error code using the onboard LED if possible

    //Serial.println("LCD init failure");

    hd44780::fatalError(status); // does not return

  }

  digitalWrite(11, LOW);

  delay(40);

  digitalWrite(11, HIGH);

  lcd.home();

  lcd.clear();

  lcd.print("Made in ");

  lcd.setCursor(0, 1);

  lcd.print("2023 by ");

  delay(2000);

  lcd.clear();

  lcd.print("John Sau");

  lcd.setCursor(0, 1);

  lcd.print("nders 89");

  delay(2000);

  Serial.println(mult);

}

Digital Ammeter Program  Loop 

void loop() {

  int sign = getSign();

  int switchPos = getSwitchPos();

  static int prevSwPos = 0;

  static int loopCount = 0;

  static int count;

  int outVal;

  lcd.clear();

  lcd.setCursor(0, 0);

  count = analogRead(OUT_PORT);

  if (sign == 1) {                  //AC

    outVal = (int)(count * mult * range[switchPos].errVal * range[switchPos].multVal * acRMS);

  }

  else {

    outVal = (int)(count * mult * range[switchPos].errVal * range[switchPos].multVal);

  }

  if (loopCount > 0) {

    loopCount--;

  }

  if (switchPos != prevSwPos) {

    prevSwPos = switchPos;

    loopCount = loopMax;

  }

  if ((switchPos != 10) ) {

    if (count >= overloadLimit[sign]) {

      lcd.print("OVERLOAD");

      lcd.setCursor(0, 1);

      lcd.print('!');

    }

    else {

      dispCurr(switchPos, sign, outVal);

      if (loopCount > 2) {

        lcd.setCursor(7, 1);

        lcd.print('*');

      }

      if (loopCount == 3) {

        txCurr(switchPos, sign, outVal);

      }

    }

  }

  if (switchPos == 10) {

    dispBatt();

  }

  delay(100);

}