Seems this code doesn't loop?

Hi,
I am testing this Arduino Based Autoranging AC-DC Voltmeter with TRMS, The code seems just run one time and doesn't loop as the serial monitor picture.

Question:

  1. the input A3 not appear in the code, how to get the readings?
  2. I didn't installed a LCD yet, added serial.print lines, any matter?
/**************************************************************************
 * 
 * Arduino autoranging AC/DC voltmeter.
 * Voltage and frequency are printed on 1602 LCD screen.
 * This is a free software with NO WARRANTY - Use it at your own risk!
 * https://simple-circuit.com/
 *
 *************************************************************************/

#include <LiquidCrystal.h>   // include Arduino LCD library
// LCD module connections (RS, E, D4, D5, D6, D7)
LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

// define autoranging channel pins
#define CH0  2
#define CH1  3
#define CH2  4
#define CH3  5

const uint16_t Time_Out = 50000,  // time out in microseconds
               Periods  = 10;     // number of periods of measurement (for AC voltage only)

// variables
byte ch_number;
const uint16_t res_table[4] = {2444, 244, 94, 47},  // voltage divider resistances in tenths kOhms
               total_res = 22444;                   // total resistance in tenths kOhms
uint16_t current_res;
volatile byte per;

void setup(void)
{

Serial.begin(9600); //// edited 

  pinMode(CH0, OUTPUT);
  pinMode(CH1, OUTPUT);
  pinMode(CH2, OUTPUT);
  pinMode(CH3, OUTPUT);

  lcd.begin(16, 2);     // set up the LCD's number of columns and rows
  lcd.setCursor(1, 0);
  lcd.print("Voltage:");

  ch_number = 0;
  ch_select(ch_number);

  // ADC and analog comparator configuration
  ADMUX  = 0x03;
  ADCSRA = 0x87;
  ADCSRB = (0 << ACME);  // select AIN1 as comparator negative input
  ACSR   = 0x13;         // turn on analog comparator

}

// analog comparator ISR
ISR (ANALOG_COMP_vect)
{
  byte count = 0;
  for(byte i = 0; i < 50; i++) {
    if ( ACSR & 0x20 )
      count++;
  }

  if(count > 48)
    per++;
}

// main loop
void loop()
{
  bool dc_flag = 0; // DC voltage flag bit
  int32_t sum = 0;  // sum of all readings
  uint16_t n = 0;   // number of readings (samples)

  ACSR = (1 << ACI);   // clear analog comparator interrupt flag
  ACSR = (1 << ACIE);  // enable analog comparator interrupt

  uint32_t current_m = micros();  // save current millis
  byte current_per = per;         // save current period number
  while ( (current_per == per) && (micros() - current_m < Time_Out) ) ;

  if( micros() - current_m >= Time_Out ) {  // if there's time out event ==> voltage signal is DC
    dc_flag = 1;
    for (byte i = 0; i < 200; i++) {
      ADCSRA |= 1 << ADSC;   // start conversion
      while(ADCSRA & 0x40);  // wait for conversion complete
      int16_t an = (int16_t)(ADCL | (uint16_t)ADCH << 8) - 511;
      sum += an;
      n++;         // increment number of readings
      delay(1);
    }
  }

  else {   // here, voltage signal is AC
    current_m = micros();  // save current millis()
    per = 0;
    while ( (per < Periods) && (micros() - current_m < (uint32_t)Time_Out * Periods) ) {
      ADCSRA |= 1 << ADSC;   // start conversion
      while(ADCSRA & 0x40);  // wait for conversion complete
      int32_t an = (int16_t)(ADCL | (uint16_t)ADCH << 8) - 511;
      sum += sq(an);  // sq: square
      n++;            // increment number of readings
    }
  }

  ACSR = (0 << ACIE);  // disable analog comparator interrupt
  uint32_t total_time = micros() - current_m;  // used to claculate frequency

  // voltage calculation
  float v;
  if(dc_flag)   // if voltage signal is DC
    v = (4 * sum)/n;   // calculate Arduino analog channel DC voltage in milli-Volts

  else  // here voltage signal is AC
    v = 4 * sqrt(sum/n);   // calculate Arduino analog channel RMS voltage in milli-Volts

  // claculate actual (input) voltage in milli-Volts (apply voltage divider equation)
  v = v * (float)total_res/current_res;
  v /= 1000;  // get voltage in Volts

  uint16_t v_abs = abs(int16_t(v));
  if( (v_abs >= 10 && ch_number == 0) || (v_abs >= 100 && ch_number == 1) || (v_abs >= 250 && ch_number == 2) ) {
    ch_number++;
    ch_select(ch_number);
    delay(10);
    return;
  }

  if( (v_abs < 220 && ch_number == 3) || (v_abs < 80 && ch_number == 2) || (v_abs < 8 && ch_number == 1) ) {
    ch_number--;
    ch_select(ch_number);
    delay(10);
    return;
  }

  char _buffer[8];
  lcd.setCursor(0, 1);
  if( v < 0)
    lcd.print('-');
  else
    lcd.print(' ');
  if(v_abs < 10)
    sprintf( _buffer, "%01u.%02u", v_abs, abs((int16_t)(v * 100)) % 100 );
  else if( v_abs < 100)
    sprintf( _buffer, "%02u.%01u", v_abs, abs((int16_t)(v * 10)) % 10 );
  else
    sprintf( _buffer, "%03u ", v_abs );

  lcd.print(_buffer);
  if(dc_flag)
    lcd.print("VDC        ");
  else {
    lcd.print("VAC ");
    // calculate signal frequency in Hz
    uint32_t period_time = total_time/Periods;
    float freq = 1000000.0/period_time;
    sprintf( _buffer, "%02u.%02uHz", (uint16_t)freq % 100, (uint16_t)(freq * 100) % 100 );
    lcd.print(_buffer);
  }

  delay(500);    // wait half a second

}

void ch_select(byte n) {
  switch(n) {
    case 0:
      digitalWrite(CH0, HIGH);
      digitalWrite(CH1, LOW);
      digitalWrite(CH2, LOW);
      digitalWrite(CH3, LOW);

      Serial.println("CH0 = ");
      Serial.println(CH0);

      break;
    case 1:
      digitalWrite(CH0, LOW);
      digitalWrite(CH1, HIGH);
      digitalWrite(CH2, LOW);
      digitalWrite(CH3, LOW);

      Serial.println("CH1 = ");
      Serial.println(CH1);

      break;
    case 2:
      digitalWrite(CH0, LOW);
      digitalWrite(CH1, LOW);
      digitalWrite(CH2, HIGH);
      digitalWrite(CH3, LOW);

      Serial.println("CH2 = ");
      Serial.println(CH2);

      break;
    case 3:
      digitalWrite(CH0, LOW);
      digitalWrite(CH1, LOW);
      digitalWrite(CH2, LOW);
      digitalWrite(CH3, HIGH);

      Serial.println("CH3 = ");
      Serial.println(CH3);
  }
  current_res = res_table[n];
}

// end of code.

pp09

How do you know that it doesn't loop? Explain what happens.

the input A3 not appear in the code, how to get the readings?

The program reads the ADC registers directly as follows. It does not use analogRead().

      while(ADCSRA & 0x40);  // wait for conversion complete
      int32_t an = (int16_t)(ADCL | (uint16_t)ADCH << 8) - 511;  //read the ADC

I didn't installed a LCD yet, added serial.print lines, any matter?

Perhaps. You tell us.

1 Like

Small clarification. The next line

ADMUX  = 0x03;

selects which analog inputs are connected to the ADC.

1 Like

Thanks.
The Serial monitor print just one time as picture shown, should it keep print?

Thanks.

The code you posted contains not Serial.print() calls so it can not possible produce the output you claim. Did you post your modified sketch?

1 Like

Thanks.
Sorry I didn't post the modified code. It's edited now still in the first post.

I am surprised you see anything since you do not have a Serial.begin(9600) call in setup() to begin serial communication.

The only place your print is inside ch_select() and it only gets called inside loop() if either of the two if() statements are true. Maybe they are not true?

1 Like

Thanks.
I just added it.
Do you know how to Serial.print the ADC result?

Three questions:

  1. need help on how to change the free run model into analogread() type please;

  2. should remove one 1 MOhm from the circuit?

  3. with the modified code, got serial monitor as below, why the code keep on ADMUX,BIN = 11?

Thanks

#include <LiquidCrystal.h>   // include Arduino LCD library
// LCD module connections (RS, E, D4, D5, D6, D7)
LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

// define autoranging channel pins
#define CH0  2
#define CH1  3
#define CH2  4
#define CH3  5

const uint16_t Time_Out = 50000,  // time out in microseconds
               Periods  = 10;     // number of periods of measurement (for AC voltage only)

// variables
byte ch_number;
const uint16_t res_table[4] = {2444, 244, 94, 47},  // voltage divider resistances in tenths kOhms
                              total_res = 22444;                   // total resistance in tenths kOhms
uint16_t current_res;
volatile byte per;

void setup(void)
{
  Serial.begin(9600);

  pinMode(CH0, OUTPUT);
  pinMode(CH1, OUTPUT);
  pinMode(CH2, OUTPUT);
  pinMode(CH3, OUTPUT);

  lcd.begin(16, 2);     // set up the LCD's number of columns and rows
  lcd.setCursor(1, 0);
  lcd.print("Voltage:");

  ch_number = 0;
  ch_select(ch_number);

  // ADC and analog comparator configuration
  ADMUX  = 0x03;
  ADCSRA = 0x87;
  ADCSRB = (0 << ACME);  // select AIN1 as comparator negative input
  ACSR   = 0x13;         // turn on analog comparator

  ///////////////////////
  Serial.println("ch_number.1 = ");
  Serial.println(ch_number);
  ////////////////////////////

}

// analog comparator ISR
ISR (ANALOG_COMP_vect)
{
  byte count = 0;
  for (byte i = 0; i < 50; i++) {
    if ( ACSR & 0x20 )
      count++;
  }

  if (count > 48)
    per++;
}

// main loop
void loop()
{
  bool dc_flag = 0; // DC voltage flag bit
  int32_t sum = 0;  // sum of all readings
  uint16_t n = 0;   // number of readings (samples)

  ACSR = (1 << ACI);   // clear analog comparator interrupt flag
  ACSR = (1 << ACIE);  // enable analog comparator interrupt

  uint32_t current_m = micros();  // save current millis
  byte current_per = per;         // save current period number
  while ( (current_per == per) && (micros() - current_m < Time_Out) ) ;

  if ( micros() - current_m >= Time_Out ) { // if there's time out event ==> voltage signal is DC
    dc_flag = 1;
    for (byte i = 0; i < 200; i++) {
      ADCSRA |= 1 << ADSC;   // start conversion
      while (ADCSRA & 0x40); // wait for conversion complete
      int16_t an = (int16_t)(ADCL | (uint16_t)ADCH << 8) - 511;
      sum += an;
      n++;         // increment number of readings
      delay(1);
    }
  }

  else {   // here, voltage signal is AC
    current_m = micros();  // save current millis()
    per = 0;
    while ( (per < Periods) && (micros() - current_m < (uint32_t)Time_Out * Periods) ) {
      ADCSRA |= 1 << ADSC;   // start conversion
      while (ADCSRA & 0x40); // wait for conversion complete
      int32_t an = (int16_t)(ADCL | (uint16_t)ADCH << 8) - 511;
      sum += sq(an);  // sq: square
      n++;            // increment number of readings
    }
  }

  ACSR = (0 << ACIE);  // disable analog comparator interrupt
  uint32_t total_time = micros() - current_m;  // used to claculate frequency

  ///////////////////////
  Serial.println("ADMUX,BIN = ");
  Serial.println(ADMUX, BIN);
  ////////////////////////////

  // voltage calculation
  float v;
  if (dc_flag)  // if voltage signal is DC
    v = (4 * sum) / n; // calculate Arduino analog channel DC voltage in milli-Volts

  else  // here voltage signal is AC
    v = 4 * sqrt(sum / n); // calculate Arduino analog channel RMS voltage in milli-Volts

  // claculate actual (input) voltage in milli-Volts (apply voltage divider equation)
  v = v * (float)total_res / current_res;
  v /= 1000;  // get voltage in Volts

  uint16_t v_abs = abs(int16_t(v));
  if ( (v_abs >= 10 && ch_number == 0) || (v_abs >= 100 && ch_number == 1) || (v_abs >= 250 && ch_number == 2) ) {
    ch_number++;
    ch_select(ch_number);
    delay(10);
    return;
    ///////////////////////
    Serial.println("ch_number.2 = ");
    Serial.println(ch_number);
    ////////////////////////////

  }

  if ( (v_abs < 220 && ch_number == 3) || (v_abs < 80 && ch_number == 2) || (v_abs < 8 && ch_number == 1) ) {
    ch_number--;
    ch_select(ch_number);
    delay(10);
    return;

    ///////////////////////
    Serial.println("ch_number.3 = ");
    Serial.println(ch_number);
    ////////////////////////////

  }

  char _buffer[8];
  lcd.setCursor(0, 1);
  if ( v < 0)
    lcd.print('-');
  else
    lcd.print(' ');
  if (v_abs < 10)
    sprintf( _buffer, "%01u.%02u", v_abs, abs((int16_t)(v * 100)) % 100 );
  else if ( v_abs < 100)
    sprintf( _buffer, "%02u.%01u", v_abs, abs((int16_t)(v * 10)) % 10 );
  else
    sprintf( _buffer, "%03u ", v_abs );

  lcd.print(_buffer);
  if (dc_flag)
    lcd.print("VDC        ");
  else {
    lcd.print("VAC ");
    // calculate signal frequency in Hz
    uint32_t period_time = total_time / Periods;
    float freq = 1000000.0 / period_time;
    sprintf( _buffer, "%02u.%02uHz", (uint16_t)freq % 100, (uint16_t)(freq * 100) % 100 );
    lcd.print(_buffer);
  }

  delay(500);    // wait half a second

}

void ch_select(byte n) {
  switch (n) {
    case 0:
      digitalWrite(CH0, HIGH);
      digitalWrite(CH1, LOW);
      digitalWrite(CH2, LOW);
      digitalWrite(CH3, LOW);

      Serial.println("CH0 = ");
      Serial.println(CH0);

      break;
    case 1:
      digitalWrite(CH0, LOW);
      digitalWrite(CH1, HIGH);
      digitalWrite(CH2, LOW);
      digitalWrite(CH3, LOW);

      Serial.println("CH1 = ");
      Serial.println(CH1);

      break;
    case 2:
      digitalWrite(CH0, LOW);
      digitalWrite(CH1, LOW);
      digitalWrite(CH2, HIGH);
      digitalWrite(CH3, LOW);

      Serial.println("CH2 = ");
      Serial.println(CH2);

      break;
    case 3:
      digitalWrite(CH0, LOW);
      digitalWrite(CH1, LOW);
      digitalWrite(CH2, LOW);
      digitalWrite(CH3, HIGH);

      Serial.println("CH3 = ");
      Serial.println(CH3);
  }
  current_res = res_table[n];
  delay(1000);
}

Serial Monitor:

CH0 = 
2
ch_number.1 = 
0
ADMUX,BIN = 
11
CH1 = 
3
ADMUX,BIN = 
11
CH2 = 
4
ADMUX,BIN = 
11
CH3 = 
5
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11
ADMUX,BIN = 
11

Why do you think that value should change?

If you want to modify a program, you need to understand what it does. Go through it line by line and make sure you understand the function of each line.

1 Like

Thanks.

Because I manually changing the input measuring voltage.

and why 11, where this number come from?

Go back and read replies #2 and #3.

The "channel" selected by the program is a resistor in the voltage divider. The ADC channel 3 is always used to measure the voltage.

You will need to understand the circuit as well as the code.

Thanks.
I read that, and in my understanding the 'ADMUX,BIN' value is relative to the input measuring voltage, right?

Binary value 11 is 3 decimal, ADC channel 3.

Thanks.
Is this mean the input pin is A3?

Please read reply #3 and look at the circuit diagram.

Thanks.
Well, I insert the input into A1, A0, all got the same number 11.

Sorry, I have no idea what you mean by that.

Why do you want to change the analog input channel? The circuit you are using is connected only to channel 3 (A3).

I suggest that you study some of the many tutorials on using the Arduino analogRead() function.

You have, I hope, understood that that circuit provides no isolation between the Arduino and the voltage source that is being measured?
There is a warning in the description about it, especially about connecting a PC to it , as you clearly are to view the serial monitor.

1 Like