Hello and welcome to our community! Is this your first visit?
Register

BatteryMonitor h v2

Battery Monitor

Battery Monitor History

Original release with AeroQuad 2.1 written by Honk, modified by Mikro.
Discussion for how this code was developed can be found here.

General Information

Battery Monitor support provides a way for the AeroQuad copter to sense its current power level by measuring the battery supply voltage, and when it gets below a pre-determined point, to signal a warning status to the pilot via LED indications, as well as an alternating speedup and slowdown of the copter's vertical movement, simulating a “heartbeat” that is much more visible to the pilot. If it gets below a second pre-determined point, it signals an alarm status and begins a slow descent to land the copter. If AltitudeHold is enabled, the barometer is used for determining descent rate, if AltitudeHold is not enabled, a fixed decrease rate of throttle PWM is applied. Also, if you have a platform with available digital pins, a pin is turned on to sound a 12v buzzer.

The way it works is to connect raw battery voltage to a voltage divider circuit that scales the voltage down to a range that can be read by the analog to digital converter, allowing the software to compare it to the two user defined levels described above. This module simply determines the batteryStatus – the behavior of the motors based on batteryStatus is described in FlightControl.pde.

To include support for the Battery Monitor, you will need an appropriately wired voltage divider on the hardware of your copter as described in the hardware section below, and you will need to make the appropriate changes to the configuration section of Aeroquad.pde, as well as edit any custom parameters in BatteryMonitor.h as described below.

Configuration

In AeroQuad.pde, find the following lines:

Code:
// *******************************************************************************************************************************
// Optional Sensors
// Warning: If you enable HeadingHold or AltitudeHold and do not have the correct sensors connected, the flight software may hang
// *******************************************************************************************************************************
// You must define one of the next 3 attitude stabilization modes or the software will not build
// *******************************************************************************************************************************
#define HeadingMagHold // Enables HMC5843 Magnetometer, gets automatically selected if CHR6DM is defined
#define AltitudeHold // Enables BMP085 Barometer (experimental, use at your own risk)
#define BattMonitor //define your personal specs in BatteryMonitor.h! Full documentation with schematic there

And ensure that the following line is uncommented (remove any leading //) and looks like this:

Code:
#define BattMonitor //define your personal specs in BatteryMonitor.h! Full documentation with schematic there
Note the comment that indicates further configuration is possible in BatteryMonitor.h.

Further in AeroQuad.pde for the hardware platform you've chosen, an extended class is instanced for that platform.
For instance, for the AeroQuad Mega 2.0 platform:

Code:
#ifdef BattMonitor
#include "BatteryMonitor.h"
BatteryMonitor_AeroQuad batteryMonitor;
#endif
the extended class BatteryMonitor_AeroQuad is instanced.

Further, in the setup section of this file:

Code:
// Battery Monitor
#ifdef BattMonitor
batteryMonitor.initialize();
#endif
the initialize function for that class is executed.

Then, in Battery.h, we find the parent class BatteryMonitor declared:

Code:
class BatteryMonitor {
public:
#define BATTERYPIN 0 // Ain 0 (universal to every Arduino), pin 55 on Mega (1280)
#define OK 0
#define WARNING 1
#define ALARM 2
byte batteryStatus;
float lowVoltageWarning; // Pack voltage at which to trigger alarm (first alarm)
float lowVoltageAlarm; // Pack voltage at which to trigger alarm (critical alarm)
float batteryVoltage;

BatteryMonitor(void) {

lowVoltageWarning = 10.2; //10.8;
lowVoltageAlarm = 9.5; //10.2;
batteryVoltage = lowVoltageWarning+2;
batteryStatus = OK;
}

virtual void initialize(void);
virtual const float readBatteryVoltage(byte); // defined as virtual in case future hardware has custom way to read battery voltage
virtual void lowBatteryEvent(byte);

void measure(byte armed) {
batteryVoltage = smooth(readBatteryVoltage(BATTERYPIN), batteryVoltage, 0.1);
if (armed == ON) {
if (batteryVoltage < lowVoltageWarning) batteryStatus = WARNING;
if (batteryVoltage < lowVoltageAlarm) batteryStatus = ALARM;
}
else
batteryStatus = OK;
lowBatteryEvent(batteryStatus);
}

const float getData(void) {
return batteryVoltage;
}
};
In this class declaration we see the two important variables being set, lowVoltageWarning and lowVoltageAlarm. The function measure( ) reads the battery voltage from this divider and compares it to these variables, and sets the batteryStatus to WARNING if the battery voltage is less than lowVoltageWarning. Also, if it is less than lowVoltageAlarm, then the batteryStatus is reset to ALARM. Otherwise the batteryStatus is set to OK. Then the function lowBatteyEvent( ) is called with this status. These warning and alarm variables (10.2 and 9.5 shown above) can be used as a starting point, but you may have to experiment to see what you are comfortable with in order to have sufficient reserve to land safely.

For example, one user reported in the forum:

"I have the warning set for 10.5 volts, and the alarm set to 10.2. When the alarm goes off, I usually land the quad within 15 seconds or so. Using the above voltage for alarm is using about 2700mah of my 3000mah pack. Just about perfect." - Scotth

In any event, care must be taken with the settings in your ESC's, as many ESC's provide for a degraded mode of operation when the voltage drops to a settable level. Make sure that these ESC cutoff points are set lower than the lowVoltage numbers used here, or disable this feature in your ESCs.

After the BatteryMonitor class declaration, there follows extensions to that class for the platform specific support.
For instance,

Code:
class BatteryMonitor_AeroQuad : public BatteryMonitor {
private:
#if defined (__AVR_ATmega328P__)
#define BUZZERPIN 12
#else
#define BUZZERPIN 49
#endif

byte state, firstAlarm;
float diode; // raw voltage goes through diode on Arduino
float batteryScaleFactor;
long currentBatteryTime, previousBatteryTime;

public:
BatteryMonitor_AeroQuad() : BatteryMonitor(){}

void initialize(void) {
float R1 = 15000;
float R2 = 7500;
float Aref = 5.0;
batteryScaleFactor = ((Aref / 1024.0) * ((R1 + R2) / R2));
#ifdef AeroQuad_Mini
diode = 0.53; // measured with DMM
#else
diode = 0.9; // measured with DMM
#endif
analogReference(DEFAULT);
pinMode(BUZZERPIN, OUTPUT); // connect a 12V buzzer to buzzer pin
digitalWrite(BUZZERPIN, LOW);
previousBatteryTime = millis();
state = LOW;
firstAlarm = OFF;
}
Is the section for support of the Mega platform. In the initialize section, nominal values are used here for the resistance of R1, R2, and the forward voltage drop of the diode. You can significantly increase the accuracy of the monitor by substituting measured values for your components here, as described in the Hardware section below.

For users of the Aeroquad shields: There is a small bug in the code leading to illuminated LEDs although battery voltage is OK.
You should add the following two lines (marked with // *****) to the code within BatteryMonitor.h within the class BatteryMonitor_AeroQuad in order to initialize the digital LED pin correctly:
Code:
class BatteryMonitor_AeroQuad : public BatteryMonitor {
private:
#if defined (__AVR_ATmega328P__)
#define BUZZERPIN 12
#else
#define BUZZERPIN 49
#endif

byte state, firstAlarm;
float diode; // raw voltage goes through diode on Arduino
float batteryScaleFactor;
long currentBatteryTime, previousBatteryTime;

public:
BatteryMonitor_AeroQuad() : BatteryMonitor(){}

void initialize(void) {
float R1 = 15020.;
float R2 = 7490.;
float Aref = 4.94;
batteryScaleFactor = ((Aref / 1024.0) * ((R1 + R2) / R2));
#ifdef AeroQuad_Mini
diode = 0.53; // measured with DMM
#else
diode = 0.9; // measured with DMM
#endif
analogReference(DEFAULT);
pinMode(BUZZERPIN, OUTPUT); // connect a 12V buzzer to buzzer pin
digitalWrite(BUZZERPIN, LOW);
digitalWrite(LED3PIN, LOW); // *****
previousBatteryTime = millis();
state = LOW;
firstAlarm = OFF;
}

void lowBatteryEvent(byte level) {
long currentBatteryTime = millis() - previousBatteryTime;
if (level == OK) {
digitalWrite(BUZZERPIN, LOW);
digitalWrite(LED3PIN, LOW); // *****
autoDescent = 0;
}
if (level == WARNING) {
//if ((autoDescent == 0) && (currentBatteryTime > 1000)) {
// autoDescent = -50;
//}
if (currentBatteryTime > 1100) {
//autoDescent = 50;
digitalWrite(LED3PIN, HIGH);
digitalWrite(BUZZERPIN, HIGH);
}
if (currentBatteryTime > 1200) {
previousBatteryTime = millis();
//autoDescent = 0;
digitalWrite(LED3PIN, LOW);
digitalWrite(BUZZERPIN, LOW);
}
}
if (level == ALARM) {
if (firstAlarm == OFF) autoDescent = 0; // intialize autoDescent to zero if first time in ALARM state
firstAlarm = ON;
digitalWrite(BUZZERPIN, HIGH); // enable buzzer
digitalWrite(LED3PIN, HIGH);
if ((currentBatteryTime > 500) && (throttle > 1400)) {
autoDescent -= 1; // auto descend quad
holdAltitude -= 0.2; // descend if in attitude hold mode
previousBatteryTime = millis();
//if (state == LOW) state = HIGH;
//else state = LOW;
//digitalWrite(LEDPIN, state);
//digitalWrite(LED2PIN, state);
//digitalWrite(LED3PIN, state);
}
}
}

Hardware

On the AeroQuad Shields v1.9 and v2.x, the voltage divider is already present in the form of the resistors R1 and R2.

Schematic of voltage divider circuitSchematic of voltage divider circuit


The schematic above is showing how the diode (if required) and resistors are to be wired. Vout should be connected to pin A0 that is present on any Arduino. The diode may be a 1N914 or equivalent. The resistors may be quarter watt. You can affect the accuracy of this monitor in two ways: One of them is to use 1% tolerance resistors, which guarantee the resistance will be within 1% of its nominal value. (Generally, resistors are available in 10%, 5% and 1% tolerance.) These are a bit more expensive, but still not much.
The other way, which is the more accurate way is to measure the actual resistance of both of them (regardless of their tolerance) and declare that value for them in the code. This should be done before they are placed in the circuit. Also, if the diode shown in Fig. 1 is used, you must measure its forward voltage, and declare that in the code as well.
To do this, when the circuit is installed and power applied, measure the voltage across the diode (place the probes on either side of the diode's leads) and declare that value for it in the code. If there is no diode used on your platform (e.g on Mini Shield v1.0), this value must be set to zero. It is referred to as voltage “drop” in the code. If Aref on your platform is well regulated (as is the case on the Mega) then using its nominal value is good; if your platform is not regulated, then measuring this voltage and declaring that value is also a good idea.

For the APM/Oilpan, there is already a 10K resistor on board, so it is only necessary to add a 3.3K resistor to complete the circuit and a jumper for the 3.3v Aref.

Future Development

  • Make user settable parameters available from AeroQuad.h and placed in the EEPROM.
  • Make LED blinking, "heartbeat" behavior, buzzer operation, and auto descend user configurable combinations from setup.

Additional information

Tags:

This page has been seen 3,994 times.

    • Created by on
      Last updated by on