Skip to content

Instantly share code, notes, and snippets.

@jwillmer
Created June 17, 2022 13:28
Show Gist options
  • Save jwillmer/5c22c1071e47aff0efba87d8b5bec1ef to your computer and use it in GitHub Desktop.
Save jwillmer/5c22c1071e47aff0efba87d8b5bec1ef to your computer and use it in GitHub Desktop.
#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