unsigned int batteryCapacity = 50000;
unsigned int batteryLevel = 0;
unsigned int lightCost = 20;
unsigned int nextSensorReading = 0;
unsigned int sensorSampleRate = 10;
unsigned int fastBlinkRate = 250;
unsigned int mediumBlinkRate = 1000;
unsigned int slowBlinkRate = 3000;
unsigned int nextEmergencyBlink = 0;
bool runningLightsOn = false;
bool emergencyLightOn = false;
const int dataPin = 2; /* DS */
const int latchPin = 3; /* STCP */
const int clockPin = 4; /* SHCP */
const int digitPins[] = { 10, 11, 12, 13 };
const int buttonPin = 9;
const int sensorPin = A0;
const int runningLightsPin = 8;
const int emergencyLightPin = 7;
const char segA = 0b00000001;
const char segB = 0b00000010;
const char segC = 0b00000100;
const char segD = 0b00001000;
const char segE = 0b00010000;
const char segF = 0b00100000;
const char segG = 0b01000000;
const char dot = 0b10000000;
const char digits[] = {
segA | segB | segC | segD | segE | segF, // 0
segB | segC, // 1
segA | segB | segD | segE | segG, // 2
segA | segB | segC | segD | segG, // 3
segB | segC | segF | segG, // 4
segA | segC | segD | segF | segG, // 5
segA | segC | segD | segE | segF | segG, // 6
segA | segB | segC, // 7
segA | segB | segC | segD | segE | segF | segG, // 8
segA | segB | segC | segF | segG, // 9
};
const char letter_F = segA | segE | segF | segG;
const char letter_U = segB | segC | segD | segE | segF;
const char letter_L = segD | segE | segF;
void setup() {
Serial.begin(9600);
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(latchPin, OUTPUT);
for (auto p : digitPins)
pinMode(p, OUTPUT);
pinMode(buttonPin, INPUT);
pinMode(sensorPin, INPUT);
pinMode(emergencyLightPin, OUTPUT);
pinMode(runningLightsPin, OUTPUT);
digitalWrite(clockPin, LOW);
}
void writeDigit(int i, byte pattern) {
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, ~pattern);
digitalWrite(latchPin, HIGH);
digitalWrite(digitPins[i], HIGH);
delay(10);
digitalWrite(digitPins[i], LOW);
}
//
// Write a 4-digit number as to the LCD panel but as if it were
// divided by ten (e.g. so the value 1234 will display as 123.4)
//
void writeNumber(int n) {
for (auto i = 0; i < 4; i++) {
auto digit = n % 10;
auto pattern = digits[digit];
// Add a decimal place on the ones digit (second from the right)
if (i == 1)
pattern |= dot;
// Hide the leading zeroes in the hundreds and tens columns
else if (i > 1 && n == 0)
pattern = 0;
writeDigit(3 - i, pattern);
n = (n - digit) / 10;
}
}
void noPower() {
writeDigit(0, 0);
writeDigit(1, 0);
writeDigit(2, 0);
writeDigit(3, 0);
digitalWrite(runningLightsPin, LOW);
digitalWrite(emergencyLightPin, LOW);
}
void loop() {
//
// Read from the light sensor and convert to a charge amount
// (1024 is darkest; 0 is brightest) and add the change to
// our pretend battery level.
//
if (millis() > nextSensorReading) {
auto reading = analogRead(sensorPin);
auto chargeAmount = (1024 - reading) / 10;
batteryLevel = min(batteryLevel + chargeAmount, batteryCapacity);
//
// Determine the state of the light switch.
// We do this inside the sensor reading so
// that the light state changes less frequently
// (i.e. so the lights flicker when almost dead battery
//
runningLightsOn = digitalRead(buttonPin) == HIGH;
if (runningLightsOn && batteryLevel > 3 * lightCost) {
digitalWrite(runningLightsPin, HIGH);
batteryLevel -= 3 * lightCost;
}
else {
digitalWrite(runningLightsPin, LOW);
}
nextSensorReading += sensorSampleRate;
}
//
// Display the state of the battery
//
int batteryPercentTenths = (long)1000 * batteryLevel / batteryCapacity;
if (batteryLevel == batteryCapacity) {
writeDigit(0, letter_F);
writeDigit(1, letter_U);
writeDigit(2, letter_L);
writeDigit(3, letter_L);
}
else {
writeNumber(batteryPercentTenths);
}
//
// Turn on the emergency light when the battery is getting low
//
if (batteryPercentTenths > 330) {
digitalWrite(emergencyLightPin, LOW);
}
else if (batteryPercentTenths > 250) {
if (millis() > nextEmergencyBlink) {
nextEmergencyBlink = millis() + slowBlinkRate;
emergencyLightOn = !emergencyLightOn;
}
digitalWrite(emergencyLightPin, emergencyLightOn ? HIGH : LOW);
}
else if (batteryPercentTenths > 100) {
if (millis() > nextEmergencyBlink) {
nextEmergencyBlink = millis() + mediumBlinkRate;
emergencyLightOn = !emergencyLightOn;
}
digitalWrite(emergencyLightPin, emergencyLightOn ? HIGH : LOW);
}
else if (batteryPercentTenths > 0) {
if (millis() > nextEmergencyBlink) {
nextEmergencyBlink = millis() + fastBlinkRate;
emergencyLightOn = !emergencyLightOn;
}
digitalWrite(emergencyLightPin, emergencyLightOn ? HIGH : LOW);
}
else {
// Battery is at 0... no power to show the emergency light
digitalWrite(emergencyLightPin, LOW);
}
}