Skip to content

Commit

Permalink
v5.0 beta - External BlueDisplay
Browse files Browse the repository at this point in the history
  • Loading branch information
ArminJo committed Dec 23, 2024
1 parent 6e2c723 commit 1516cf1
Show file tree
Hide file tree
Showing 5 changed files with 729 additions and 595 deletions.
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,26 @@ Battery packs up to 17.2 volt (4s) can be measured too. Voltages above 14.8 volt
Given the voltage measurement resistor network from the schematic, with Li-ion (3.7 V VCC) we can merely measure up to 14.8 V.<br/>
Since the build in load resistor is 12 &ohm;, **the current would go up to 1.4 ampere and the power to 24 watt**, leaving 2.8 watt at the 2 &ohm; shunt resistors.<br/>
This is too much for the resistors I used for shunt!<br/>
The solution is to **add an additional resistor of around 20 &ohm; in series to the 10 &ohm; already built in one**.
The solution is to **add an additional resistor of around 20 &ohm; in series to the 10 &ohm; already built in**.
This reduces the current to around 500 mA and power to 9 watt leaving 1 watt at the 2 &ohm; shunt resistors.<br/>
The voltage must still be measured at the battery terminal, so I use a distinct cable for voltage measurement, normally connected to the built in load resistors / battery + cable.<br/>
No other adaption has to be made.

<br/>

# Logger function
By connecting pin 10 to ground, logger mode is entered after startup. Voltage is still measured at the same pin as for Battery mode.
For current measurement in Logger mode, we use an separate external shunt connected at pin A4.<br/>
To avoid disturbing the circuit in which we are measuring the current, this shunt is typically smaller than the shunt used in battery mode.
Its value is defined by `LOGGER_SHUNT_RESISTOR_MILLIOHM`.<br/>
Cutoff / end condition is, when current drops below 50%, 25% or 12.5% of start current.
12.5% is default and useful for logging charging circuits wich reduce current at the end of charge.

<br/>

# Sample screenshots
This screenshots are from an 1200x800 tablet running the [BlueDisplay](https://github.com/ArminJo/Arduino-BlueDisplay) app.
The Arduino is connected to the tablet via **OSB OTG** or a **Bluetooth module** like HC-05, see [here](https://github.com/ArminJo/Arduino-BlueDisplay?tab=readme-ov-file#bluedisplayblink) and [here](https://github.com/ArminJo/Arduino-BlueDisplay?tab=readme-ov-file#connecting-arduino-rx).

![Li-ion_1100mAh](pictures/Li-ion_1100mAh.png)

Expand All @@ -95,7 +106,7 @@ This screenshots are from an 1200x800 tablet running the [BlueDisplay](https://g
# Sample plots
The plots are created with the Arduino 1.x Serial Plotter. The Arduino 2.x Serial Plotter is not as powerful and uses a different data format.

Plot for **2 parallel Li-Ion cells**.<br/>
Plot for **2 parallel 18660 Li-Ion cells**.<br/>
The first capacity value is the "standard" capacity measured from the first peak at standard full voltage 4100 mV to the second peak, the cutoff "high" voltage at 3400 mV.<br/>
The peaks, which are 50 mV added to the measured value, are artificially generated for easy recognition of the capacity range.<br/>
The second peak is always suppressed for graphs with discharge to "high", because the end of this graph is by definition the end of the standard capacity range.<br/>
Expand Down Expand Up @@ -223,8 +234,8 @@ A value of **99.999 &ohm; indicates overflow** over the maximum value of 65.535
- Every second, a sample is taken and displayed.
- Every 60 seconds the sample is stored.
- For the first 337 samples (5:37 hours) each 8 bit delta is stored to EEPROM.
- After the first 337 samples, all data are compressed, and every 120 seconds 2 compressed samples are stored to EEPROM.
- The number between the voltage and the current in the first row is the virtual EEPROM storage index and incremented at each storage.
- After 337 samples, all data are compressed by combining 2 samples and doubling the sample period This can be done multiple times.
- The number between the voltage and the current in the first row is the storage minute count and incremented at each storage.

![Storing](pictures/Storing.png)

Expand Down Expand Up @@ -256,6 +267,7 @@ and the according cutoff voltage is displayed in the first row for 2 seconds.
- Compression is now done by simply doubling the sampling period, which results in reducing the resolution from 336 of 168 samples directly after compression.
- Data and chart can be displayed on (old) tablets or mobile running the BlueDisplay app https://github.com/ArminJo/Arduino-BlueDisplay.
- Plotter pin logic does not depend any more on USB powering.
- Tested logger function with chart.

### Version 4.0.0
- Use capacity between NominalFullVoltageMillivolt and SwitchOffVoltageMillivoltHigh as standard capacity to enable better comparison.
Expand Down
3 changes: 3 additions & 0 deletions UltimateBatteryTester/ADCUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
#if defined(__AVR__) && defined(ADCSRA) && defined(ADATE) && (!defined(__AVR_ATmega4809__))
#define ADC_UTILS_ARE_AVAILABLE

// External Reference Current is 150 uA for 5 V and 100 uA for 3.5 V
#define READING_FOR_AREF 1024L // Datasheet 24.2: The minimum value represents GND and the maximum value represents the voltage on the AREF pin minus 1 LSB

// PRESCALE4 => 13 * 4 = 52 microseconds per ADC conversion at 1 MHz Clock => 19,2 kHz
#define ADC_PRESCALE2 1 // 26 microseconds per ADC conversion at 1 MHz
#define ADC_PRESCALE4 2 // 52 microseconds per ADC conversion at 1 MHz
Expand Down
24 changes: 12 additions & 12 deletions UltimateBatteryTester/ADCUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ uint16_t readUntil4ConsecutiveValuesAreEqual(uint8_t aADCChannelNumber, uint8_t
/*
* Get min and max of the last 4 values
*/
tMin = 1024;
tMin = READING_FOR_AREF;
tMax = 0;
for (uint_fast8_t i = 0; i < 4; ++i) {
if (tValues[i] < tMin) {
Expand Down Expand Up @@ -472,7 +472,7 @@ uint16_t readUntil4ConsecutiveValuesAreEqual(uint8_t aADCChannelNumber, uint8_t
float getVCCVoltageSimple(void) {
// use AVCC with (optional) external capacitor at AREF pin as reference
float tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
return ((1023 * 1.1 * 4) / tVCC);
return ((READING_FOR_AREF * 1.1 * 4) / tVCC);
}

/*
Expand All @@ -483,19 +483,19 @@ float getVCCVoltageSimple(void) {
uint16_t getVCCVoltageMillivoltSimple(void) {
// use AVCC with external capacitor at AREF pin as reference
uint16_t tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
return ((1023L * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCC);
return ((READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCC);
}

/*
* Gets the hypothetical 14 bit reading of VCC using 1.1 volt reference
* Similar to getVCCVoltageMillivolt() * 1023 / 1100
* Similar to getVCCVoltageMillivolt() * 1024 / 1100
*/
uint16_t getVCCVoltageReadingFor1_1VoltReference(void) {
uint16_t tVCC = waitAndReadADCChannelWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT);
/*
* Do not switch back ADMUX to enable checkAndWaitForReferenceAndChannelToSwitch() to work correctly for the next measurement
*/
return ((1023L * 1023L) / tVCC);
return ((READING_FOR_AREF * READING_FOR_AREF) / tVCC);
}

/*
Expand All @@ -519,7 +519,7 @@ uint16_t getVCCVoltageMillivolt(void) {
/*
* Do not switch back ADMUX to enable checkAndWaitForReferenceAndChannelToSwitch() to work correctly for the next measurement
*/
return ((1023L * ADC_INTERNAL_REFERENCE_MILLIVOLT) / tVCC);
return ((READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT) / tVCC);
}

/*
Expand Down Expand Up @@ -547,7 +547,7 @@ void readAndPrintVCCVoltageMillivolt(Print *aSerial) {
void readVCCVoltageSimple(void) {
// use AVCC with (optional) external capacitor at AREF pin as reference
float tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
sVCCVoltage = (1023 * (((float) ADC_INTERNAL_REFERENCE_MILLIVOLT) / 1000) * 4) / tVCC;
sVCCVoltage = (READING_FOR_AREF * (((float) ADC_INTERNAL_REFERENCE_MILLIVOLT) / 1000) * 4) / tVCC;
}

/*
Expand All @@ -558,7 +558,7 @@ void readVCCVoltageSimple(void) {
void readVCCVoltageMillivoltSimple(void) {
// use AVCC with external capacitor at AREF pin as reference
uint16_t tVCCVoltageMillivoltRaw = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
sVCCVoltageMillivolt = (1023L * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCCVoltageMillivoltRaw;
sVCCVoltageMillivolt = (READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCCVoltageMillivoltRaw;
}

/*
Expand All @@ -579,7 +579,7 @@ void readVCCVoltageMillivolt(void) {
/*
* Do not switch back ADMUX to enable checkAndWaitForReferenceAndChannelToSwitch() to work correctly for the next measurement
*/
sVCCVoltageMillivolt = (1023L * ADC_INTERNAL_REFERENCE_MILLIVOLT) / tVCCVoltageMillivoltRaw;
sVCCVoltageMillivolt = (READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT) / tVCCVoltageMillivoltRaw;
}

/*
Expand All @@ -588,7 +588,7 @@ void readVCCVoltageMillivolt(void) {
*/
uint16_t getVoltageMillivolt(uint16_t aVCCVoltageMillivolt, uint8_t aADCChannelForVoltageMeasurement) {
uint16_t tInputVoltageRaw = waitAndReadADCChannelWithReference(aADCChannelForVoltageMeasurement, DEFAULT);
return (aVCCVoltageMillivolt * (uint32_t) tInputVoltageRaw) / 1023;
return (aVCCVoltageMillivolt * (uint32_t) tInputVoltageRaw) / READING_FOR_AREF;
}

/*
Expand All @@ -597,12 +597,12 @@ uint16_t getVoltageMillivolt(uint16_t aVCCVoltageMillivolt, uint8_t aADCChannelF
*/
uint16_t getVoltageMillivolt(uint8_t aADCChannelForVoltageMeasurement) {
uint16_t tInputVoltageRaw = waitAndReadADCChannelWithReference(aADCChannelForVoltageMeasurement, DEFAULT);
return (getVCCVoltageMillivolt() * (uint32_t) tInputVoltageRaw) / 1023;
return (getVCCVoltageMillivolt() * (uint32_t) tInputVoltageRaw) / READING_FOR_AREF;
}

uint16_t getVoltageMillivoltWith_1_1VoltReference(uint8_t aADCChannelForVoltageMeasurement) {
uint16_t tInputVoltageRaw = waitAndReadADCChannelWithReference(aADCChannelForVoltageMeasurement, INTERNAL);
return (ADC_INTERNAL_REFERENCE_MILLIVOLT * (uint32_t) tInputVoltageRaw) / 1023;
return (ADC_INTERNAL_REFERENCE_MILLIVOLT * (uint32_t) tInputVoltageRaw) / READING_FOR_AREF;
}

/*
Expand Down
Loading

0 comments on commit 1516cf1

Please sign in to comment.