--- /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;
+}
--- /dev/null
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+#include <util/delay.h>
+#include <stdlib.h> // for NULL
+
+#include "lights.h"
+
+#define WAKEUP_LIMIT 5 // times 100 ms
+#define SHORT_PRESS_MIN 10 // in jiffies (100 Hz ticks)
+#define SHORT_PRESS_MAX 50
+#define LONG_PRESS_MIN 100
+static uint16_t button_start;
+static unsigned char prev_state;
+
+void status_led_on_off(unsigned char mode)
+{
+ if (mode)
+ PORTB |= _BV(PORTB0);
+ else
+ PORTB &= ~_BV(PORTB0);
+}
+
+void init_buttons()
+{
+ DDRB &= ~_BV(DDB1);
+ DDRB |= _BV(DDB0);
+ PORTB |= _BV(PORTB1); // enable internal pull-up
+ PORTB &= ~_BV(PORTB0); // status led off
+ GIMSK &= ~_BV(PCIE); // disable pin-change IRQs
+ PCMSK = 0; // disable pin-change IRQs on all pins of port B
+
+ button_start = 0;
+ prev_state = 0;
+}
+
+void susp_buttons()
+{
+ DDRB &= ~(_BV(DDB1)); // set as input
+ PORTB |= _BV(PORTB1); // enable internal pull-up
+ PORTB &= ~_BV(PORTB0); // set to zero
+
+ GIMSK |= _BV(PCIE);
+
+ PCMSK = _BV(PCINT1);
+ // disable pin-change IRQs on all pins except PB1
+}
+
+void timer_check_buttons()
+{
+ unsigned char cur = !(PINB & _BV(PINB1));
+ unsigned char prev = prev_state;
+
+ prev_state = cur;
+
+ if (cur && !prev) { // --- just pressed ---
+ button_start = jiffies;
+ // set_status_led(button, NULL);
+
+ } else if (cur && prev) { // --- is still pressed ---
+ uint16_t duration = jiffies - button_start;
+
+ if (duration > LONG_PRESS_MIN) {
+ long_press_start();
+ // acknowledge long press
+ }
+ } else if (!cur && prev) { // --- just released ---
+ uint16_t duration = jiffies - button_start;
+
+ if (duration > SHORT_PRESS_MIN && duration < SHORT_PRESS_MAX) {
+ short_press();
+ } else if (duration > LONG_PRESS_MIN) {
+ // set_status_led(button, NULL);
+ long_press();
+ }
+ // ignore other button-press durations
+ }
+}
+
+#if 0
+static void handle_brake(unsigned char cur, unsigned char prev)
+{
+ if (cur && !prev) { // --- just pressed ---
+ button_start[2] = jiffies;
+ } else if (!cur && prev) { // --- just released ---
+ button_start[2] = jiffies;
+ } else { // --- no change ---
+ uint16_t duration = jiffies - button_start[2];
+
+ if (duration > 6) {
+ if (cur) {
+ if (button_state.brake_working
+ && !button_state.brake_reported) {
+ button_state.brake_reported = 1;
+ brake_on();
+ }
+ } else {
+ button_state.brake_working = 1;
+ if (button_state.brake_reported) {
+ button_state.brake_reported = 0;
+ brake_off();
+ }
+ }
+ button_start[2] = jiffies - 7; // avoid overflow
+ }
+ }
+}
+#endif
+
+unsigned char buttons_wait_for_release()
+{
+ uint16_t wake_count = 0;
+ unsigned char pin;
+
+ do {
+ if (wake_count++ > WAKEUP_LIMIT)
+ status_led_on_off(1); // inform the user
+
+ _delay_ms(100);
+
+ pin = PINB & _BV(PINB1);
+ } while (!(pin & _BV(PINB1)));
+
+ status_led_on_off(0);
+
+ return wake_count > WAKEUP_LIMIT;
+}
+
+ISR(PCINT0_vect)
+{
+ // empty - let it wake us from sleep, but do nothing else
+}
+