--- /dev/null
+#include <avr/io.h>
+
+#include "lights.h"
+
+#define BATTERY_ADC_SHIFT 2
+#define RESISTOR_HI 1500 // kOhm
+#define RESISTOR_LO 100 // kOhm
+/*
+ * The internal 1.1V reference has tolerance from 1.0 to 1.2V
+ * (datasheet, section 19.6). We have to measure the actual value
+ * of our part.
+ */
+#define AREF_1100MV 1060 // mV
+
+static volatile uint16_t battery_adcval;
+static unsigned char initial_readings = 0;
+
+void init_battery()
+{
+ battery_adcval = 0;
+ initial_readings = 5;
+}
+
+unsigned char battery_100mv()
+{
+ /*
+ * This is tricky: we need to maintain precision, so we first
+ * multiply adcval by as big number as possible to fit uint16_t,
+ * then divide to get the final value,
+ * and finally type-cast it to unsigned char.
+ * We don't do running average, as the required precision
+ * is coarse (0.1 V).
+ */
+ return (unsigned char)
+ ((uint16_t)(
+ (battery_adcval >> BATTERY_ADC_SHIFT)
+ * (11 // 1.1V
+ * (RESISTOR_HI+RESISTOR_LO)/RESISTOR_LO // resistor ratio
+ / 4)) >> 8); // divide by 1024
+}
+
+void battery_adc(uint16_t adcval)
+{
+ if (initial_readings) {
+ initial_readings--;
+ battery_adcval = adcval << BATTERY_ADC_SHIFT;
+ } else if (battery_adcval == 0) {
+ battery_adcval = adcval << BATTERY_ADC_SHIFT;
+ } else { // running average
+ battery_adcval += (adcval
+ - (battery_adcval >> BATTERY_ADC_SHIFT));
+ }
+#if 0
+ log_byte(battery_100mv());
+ log_flush();
+#endif
+}
+
+unsigned char battery_gauge()
+{
+ unsigned char b8 = battery_100mv();
+ unsigned char rv;
+
+ if (b8 < 70) {
+ rv = 1;
+ } else if (b8 < 75) {
+ rv = 2;
+ } else if (b8 < 80) {
+ rv = 3;
+ } else if (b8 < 85) {
+ rv = 4;
+ } else if (b8 < 90) {
+ rv = 5;
+ } else if (b8 < 95) {
+ rv = 6;
+ } else {
+ rv = 7;
+ }
+
+ if (rv == 1 && !initial_readings)
+ set_error(ERR_BATTERY);
+
+#if 0
+ log_byte(0xbb);
+ log_byte(rv);
+ log_flush();
+#endif
+ return rv;
+}