// Calculates the internal bandgap voltage value of an specific Arduino Nano/Uno/Mega board.
// The user must measure the voltage between Vcc and Ground with accuracy and precision (i.e.: using a DMM).
// Works for 168/328 and mega boards.
// Made by Juan 02/2025.
// Based in a "retrolefty" original code.
// https://forum.arduino.cc/t/measurement-of-bandgap-voltage/38215/17
long externally_measured_Vcc = 5000L;   // In mV. This value must be measured by the user.
long InternalReferenceVoltage;          // In mV. This value will be calculated by this program.
constexpr byte numChars = 64;
char readCstring[numChars];
boolean finished_reading_serial = false;
void setup(void)
{
  Serial.begin(9600);
  Serial.println("Welcome to the internal bandgap measurement tool.");
  Serial.println("Measure the Vcc voltage with a DMM.");
  Serial.println("End input with EOL");
  Serial.println( "\r\n" );
  Serial.print("What is the measured voltage at Vcc? (in mV): ");
  Serial.println();
  delay(100);
}
    
void loop(void)
{
  check_and_read_1_char_serial();
  if (finished_reading_serial == true) {
    externally_measured_Vcc = atol(readCstring);
    Serial.print("Recieved: ");
    Serial.println(externally_measured_Vcc);
    InternalReferenceVoltage = getBandgap(externally_measured_Vcc);
    Serial.print("The bandgap voltage is (in mV) = ");
    Serial.println(InternalReferenceVoltage);
    Serial.println();    
    delay(1000);
    Serial.print("What is the measured voltage at Vcc? (in mV): ");
    Serial.println();
    finished_reading_serial = false;
  }
}
int getBandgap(long externally_measured_Vcc)
// Returns the bandgap value (in mV).
{
    
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  // For mega boards
    // REFS1 REFS0          --> 0 1, AVcc internal ref. -Selects AVcc reference
    // MUX4 MUX3 MUX2 MUX1 MUX0  --> 11110 1.1V (VBG)         -Selects channel 30, bandgap voltage, to measure
  ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR)| (0<<MUX5) | (1<<MUX4) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
#else
  // For 168/328 boards
    // REFS1 REFS0          --> 0 1, AVcc internal ref. -Selects AVcc external reference
    // MUX3 MUX2 MUX1 MUX0  --> 1110 1.1V (VBG)         -Selects channel 14, bandgap voltage, to measure
  ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);    
#endif
  delay(50);  // Let mux settle a little to get a more stable A/D conversion
    // Start a conversion  
  ADCSRA |= _BV( ADSC );
    // Wait for it to complete
  while( ( (ADCSRA & (1<<ADSC)) != 0 ) );
  long result = (externally_measured_Vcc * ADC) / 1024L; // calculates for straight line value 
  return result;
}
void check_and_read_1_char_serial()
// Respeta lo que dice:
// https://forum.arduino.cc/t/serial-input-basics-updated/382007/2
// Made by Juan 02/2025.
{
  static byte i = 0;
  char endMarker = '\n';
  char recievedChar;
  while (Serial.available() > 0 && finished_reading_serial == false) {
        recievedChar = Serial.read();
        if (recievedChar != endMarker) {
            readCstring[i] = recievedChar;
            i++;
            if (i >= numChars) {
                i = numChars - 1;
                Serial.println("Error, buffer overflow.");
            }
        }
        else {
            readCstring[i] = '\0'; // terminate the string
            i = 0;
            finished_reading_serial = true;
        }
  }
}