This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SystemStatus.h> // Get VCC | |
#include <avr/sleep.h> // Sleep Modes | |
#include <avr/wdt.h> // Watchdog timer | |
// PIN Definition - Remember to change pinMode(PIN) in setup when changing pin definitions | |
#define LED_PIN 0 | |
#define SOLAR_PIN 7 | |
#define RX_PIN 3 | |
#define TX_PIN 4 | |
// Max solar cell volt reading output to be counted as night | |
#define NIGHT_THRESHOLD_MILLIVOLT 280 | |
#define MIN_LED_MILLI_VOLTAGE 2600 | |
// Enable serial logging - only works with >= 8Mhz clock | |
// #define DEBUG 1 | |
#ifdef DEBUG | |
#include <SoftwareSerial.h> | |
SoftwareSerial serial(RX_PIN, TX_PIN); | |
#define DEBUG_PRINT(x) serial.print(x) | |
#define DEBUG_PRINTLN(x) serial.println(x) | |
#define SLEEP_TIME_IN_SECONDS 3 | |
#define LED_LIGHT_UP_CYCLE 10 | |
#else | |
#define DEBUG_PRINT(x) | |
#define DEBUG_PRINTLN(x) | |
#define SLEEP_TIME_IN_SECONDS 16 // 2x8 - watchdog max sleep is 8 seconds. 15sec would mean 4 weakups (8+4+2+1) | |
#define LED_LIGHT_UP_CYCLE 40 | |
#endif | |
bool isNightStatusSet = false; | |
int nightCycleCount = 0; | |
int systemMilliVolt = 0; | |
void setup() | |
{ | |
#ifdef DEBUG | |
serial.begin(9600); | |
#else | |
// disable unused pins | |
pinMode(RX_PIN, INPUT_PULLUP); | |
pinMode(TX_PIN, INPUT_PULLUP); | |
#endif | |
// disable unused pins | |
pinMode(1, INPUT_PULLUP); | |
pinMode(5, INPUT_PULLUP); | |
pinMode(SOLAR_PIN, INPUT); | |
pinMode(LED_PIN, OUTPUT); | |
ADCSRA &= ~(1 << ADEN); //Disable ADC, saves ~230uA | |
//Power down various bits of hardware to lower power usage | |
set_sleep_mode(SLEEP_MODE_PWR_DOWN); //Power down everything, wake up from WDT | |
sleep_enable(); | |
} | |
void loop() | |
{ | |
sleep(SLEEP_TIME_IN_SECONDS); | |
if (isNight()) { | |
processNightTime(); | |
} else { | |
processDayTime(); | |
} | |
} | |
void processDayTime() { | |
if (isNightStatusSet) { | |
isNightStatusSet = false; | |
} | |
DEBUG_PRINTLN("Day"); | |
} | |
void processNightTime() { | |
if(!canLedLightUp()){ | |
DEBUG_PRINTLN("System voltage below LED threshold!"); | |
return; | |
} | |
initNightTime(); | |
nightCycleCount++; | |
DEBUG_PRINT("Night Cycle: "); | |
DEBUG_PRINTLN(nightCycleCount); | |
if (nightCycleCount == 1) { | |
lightUpLED(); | |
} else if (nightCycleCount > LED_LIGHT_UP_CYCLE) { | |
nightCycleCount = 0; | |
} | |
} | |
bool canLedLightUp() { | |
return systemMilliVolt >= MIN_LED_MILLI_VOLTAGE; | |
} | |
void lightUpLED() { | |
float trimBrightnessMax = 0.854; | |
float sinRange = 127.5; | |
float sinOffset = 127.5; | |
float stepLength, start, end, out; | |
// rising | |
start = 4.712; //pi*1.5 | |
end = 7.854; //pi*2.5 | |
end -= trimBrightnessMax; | |
stepLength = .004; | |
ledFade(start, end, stepLength, sinRange, sinOffset); | |
delay(500); // pause at peak brightness | |
// falling | |
start = 1.570; //pi*0.5 | |
start += trimBrightnessMax; | |
end = 4.712; //pi*1.5 | |
stepLength = .004; | |
ledFade(start, end, stepLength, sinRange, sinOffset); | |
} | |
// Source: https://www.sparkfun.com/tutorials/329 | |
void ledFade(float start, float end, float stepLength, float sinRange, float sinOffset) { | |
float out; | |
while (start < end) | |
{ | |
start = start + stepLength; | |
out = sin(start) * sinRange + sinOffset; | |
analogWrite(LED_PIN, out); | |
delay(1); | |
} | |
} | |
void initNightTime() { | |
if (isNightStatusSet == false) { | |
isNightStatusSet = true; | |
nightCycleCount = 0; | |
} | |
} | |
bool isNight() { | |
ADCSRA |= (1 << ADEN); //Enable ADC | |
systemMilliVolt = SystemStatus ().getVCC(); | |
int solarCellRead = analogRead(SOLAR_PIN); | |
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V): | |
int solarCellMilliVolt = solarCellRead / 1024.0 * systemMilliVolt; | |
DEBUG_PRINT("Sensor Value: "); | |
DEBUG_PRINT(solarCellRead); | |
DEBUG_PRINT(" - Sensor Milli Volt: "); | |
DEBUG_PRINT(solarCellMilliVolt); | |
DEBUG_PRINT(" - System Milli Volt: "); | |
DEBUG_PRINTLN(systemMilliVolt); | |
ADCSRA &= ~(1 << ADEN); //Disable ADC, saves ~230uA | |
return solarCellMilliVolt < NIGHT_THRESHOLD_MILLIVOLT; | |
} | |
void sleep(int seconds) { | |
if (seconds == 0) | |
{ | |
return; | |
} | |
else if (seconds % 2 != 0) | |
{ | |
seconds--; | |
setup_watchdog(6); | |
} | |
else if (seconds >= 8) | |
{ | |
seconds = seconds - 8; | |
setup_watchdog(9); | |
} | |
else if (seconds >= 4) | |
{ | |
seconds = seconds - 4; | |
setup_watchdog(8); | |
} | |
else if (seconds >= 2) | |
{ | |
seconds = seconds - 2; | |
setup_watchdog(7); | |
} | |
sleep_mode(); | |
sleep(seconds); | |
} | |
//Sets the watchdog timer to wake us up, but not reset | |
//0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms | |
//6=1sec, 7=2sec, 8=4sec, 9=8sec | |
//From: http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/ | |
void setup_watchdog(int timerPrescaler) { | |
if (timerPrescaler > 9 ) timerPrescaler = 9; //Limit incoming amount to legal settings | |
byte bb = timerPrescaler & 7; | |
if (timerPrescaler > 7) bb |= (1 << 5); //Set the special 5th bit if necessary | |
//This order of commands is important and cannot be combined | |
MCUSR &= ~(1 << WDRF); //Clear the watch dog reset | |
WDTCR |= (1 << WDCE) | (1 << WDE); //Set WD_change enable, set WD enable | |
WDTCR = bb; //Set new watchdog timeout value | |
WDTCR |= _BV(WDIE); //Set the interrupt enable, this will keep unit from resetting after each int | |
} | |
// WDT Interrupt Need some code for coming out of sleep. | |
// but is does not need to do anything! (just exist). | |
ISR(WDT_vect) {} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment