You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

224 lines
6.1 KiB

// System includes for libraries
#include "Arduino.h"
#include "Wire.h"
#include "MAX1704.h"
#include "Sleepy.h"
#include "Adafruit_NeoPixel.h"
#ifdef __AVR__
#include <avr/power.h>
#endif
// Various defines / pins / etc
#define DEBUG true
#define INTERACTIVE_DEBUG false
#define USE_SLEEP true
#define SLEEP_INTERVAL 1000 // miliseconds
#define ALERT_LEVEL 15 // battery alert level (%)
#define POT_BRIGHT A0
#define POT_COLOR A1
#define PIN_ALWAYS_ON 9 // Switch that enables always on/off of pixels
#define PIN_WHITE 12 // Short Vout to this pin to change color to white
#define PIN_DOOR 11 // Door sensor pin
#define PIN_NEO 6 // Pin with the pixel comm line
#define NEO_PIX_NUM 2 // FIXME: this should be 5 for final build for safe lighting
// Objects / values / etc
MAX1704 fuelGauge;
float charge_percent;
uint8_t brightness;
uint16_t raw_color;
uint8_t color;
bool always_on = false;
Adafruit_NeoPixel neopix = Adafruit_NeoPixel(NEO_PIX_NUM, PIN_NEO, NEO_GRB + NEO_KHZ800);
uint8_t rgb[3]; // index 0 = red / index 1 = green / index 2 = blue
bool white = false;
bool max_found = false;
bool door_open = false;
// Various function definitions
void hsvToRgb(int h, double s, double v); // See end of file for implementation
ISR(WDT_vect) { Sleepy::watchdogEvent(); } // Setup the watchdog -- For sleepy
void setup() {
Serial.begin(9600);
Wire.begin();
// Setup serial and wait for console
if (DEBUG) {
if (INTERACTIVE_DEBUG) {
while(!Serial);
}
Serial.println("Starting up...");
}
// Setup various digital/analog inputs
pinMode(PIN_ALWAYS_ON, INPUT);
pinMode(PIN_WHITE, INPUT);
pinMode(PIN_DOOR, INPUT_PULLUP);
pinMode(POT_BRIGHT, INPUT);
pinMode(POT_COLOR, INPUT);
// Setup / config MAX1704
Wire.beginTransmission(MAX1704_ADDR);
max_found = (Wire.endTransmission() == 0);
if (max_found) {
fuelGauge.reset();
fuelGauge.quickStart();
fuelGauge.awake(); // In case it was turned off when sleep was enabled
delay(250); // Wait for it to settle before writing config value
fuelGauge.setAlertThreshold(ALERT_LEVEL);
}
// AdaFruit NeoPixel init
// Begin trinket special code (per NeoPixel examples)
#if defined (__AVR_ATtiny85__)
if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
// End of trinket special code
neopix.begin();
neopix.show(); // Init to "off"
// Various startup info (debugging)
if (DEBUG) {
fuelGauge.showConfig();
Serial.println("----------");
}
if (INTERACTIVE_DEBUG) {
while(!Serial.available());
}
}
void loop() {
// Read various values needed
if (max_found) {
charge_percent = fuelGauge.stateOfCharge(); // Battery level
}
brightness = map(analogRead(POT_BRIGHT), 0, 1024, 0, 255); // Brightness (0-1023) mapped to 0-255
raw_color = analogRead(POT_COLOR); // Color (0-1024)
always_on = (digitalRead(PIN_ALWAYS_ON) == HIGH); // Toggle button pressed == ALWAYS ON
white = (digitalRead(PIN_WHITE) == HIGH); // Shorted pin to enable white color for LEDs
door_open = (digitalRead(PIN_DOOR == LOW)); // SparkFun door sensor is indicating door is open (NO for door closed state, set as input_pullup so the logic is inverted)
// Set RGB array to values as appropriate
if (white) {
rgb[0] = rgb[1] = rgb[2] = 255;
}
else {
// Convert raw color value to position on a circle and then use the circle position to figure out color on a color wheel
color = map(raw_color, 0, 1024, 0, 360);
hsvToRgb(color, 1, 1); // set rgb byte array
}
// NeoPixel related
for (uint8_t i=0; i<NEO_PIX_NUM; i++) {
neopix.setPixelColor(i, neopix.Color(rgb[0], rgb[1], rgb[2]));
}
neopix.show();
neopix.setBrightness(brightness);
// Debugging code
if (DEBUG) {
if (max_found) {
Serial.print("Battery %: ");
Serial.println(charge_percent);
Serial.print("Alert level: ");
Serial.println(fuelGauge.alertThreshold());
Serial.print("Alert: ");
Serial.println(fuelGauge.isAlerting());
}
else {
Serial.println("MAX1704 not found!");
}
Serial.print("Brightness: ");
Serial.println(brightness);
Serial.print("White: ");
Serial.println(white);
Serial.print("Color: ");
Serial.println(color);
Serial.print("Red: ");
Serial.println(rgb[0]);
Serial.print("Green: ");
Serial.println(rgb[1]);
Serial.print("Blue: ");
Serial.println(rgb[2]);
Serial.print("Always on: ");
Serial.println(always_on);
Serial.print("Door sensor: ");
Serial.println(door_open);
Serial.println("----------");
delay(250);
}
// Sleep mode enabled
if (USE_SLEEP && !always_on && !door_open) { // Don't enable sleep if always on toggle is set
if (DEBUG) {
Serial.println("SLEEPING!");
}
if (max_found) {
fuelGauge.sleep();
}
neopix.setBrightness(0);
neopix.show();
delay(250); // Be sure that everything is settled before going to sleep
Sleepy::loseSomeTime(SLEEP_INTERVAL);
if (max_found) {
fuelGauge.awake();
}
neopix.setBrightness(brightness);
}
}
// Convert degrees on a circle to color values
// http://www.skylark-software.com/2011/01/arduino-notebook-rgb-led-and-color_21.html
void hsvToRgb(int h, double s, double v) {
// Make sure our arguments stay in-range
h = max(0, min(360, h));
s = max(0, min(1.0, s));
v = max(0, min(1.0, v));
if(s == 0) {
// Achromatic (grey)
rgb[0] = rgb[1] = rgb[2] = round(v * 255);
}
double hs = h / 60.0; // sector 0 to 5
int i = floor(hs);
double f = hs - i; // factorial part of h
double p = v * (1 - s);
double q = v * (1 - s * f);
double t = v * (1 - s * (1 - f));
double r, g, b;
switch(i) {
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default: // case 5:
r = v;
g = p;
b = q;
}
rgb[0] = round(r * 255.0);
rgb[1] = round(g * 255.0);
rgb[2] = round(b * 255.0);
}