Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
263 lines (239 sloc) 12.2 KB
// *************************************************************************************************************
// MightyBoost control sample sketch
// *************************************************************************************************************
// Copyright (2015) Felix Rusu, http://lowpowerlab.com
// http://lowpowerlab.com/mightyboost
// MightyBoost is a smart backup PSU controllable by Moteino, and this sketch is a sample control sketch to run
// MightyBoost in this mode.
// Be sure to check back for code updates and patches
// *************************************************************************************************************
// This sketch will provide control over the essential features of MightyBoost:
// - provide switched 5V power to a sensitive load like RaspberryPi which should not lose power instantly
// - Control the "5V*" output via Moteino+PowerButton (momentary tactile)
// - Monitor input supply and switch to battery backup when external power is lost
// - Monitor battery voltage and issue a shutdown/reboot signal when battery runs low
// This sketch may be extended to include integration with other LowPowerLab automation products
// *************************************************************************************************************
// License
// *************************************************************************************************************
// This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU General
// Public License as published by the Free Software
// Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will
// be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General
// Public License along with this program; if not, write
// to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Licence can be viewed at
// http://www.fsf.org/licenses/gpl.txt
//
// Please maintain this license information along with authorship
// and copyright notices in any redistribution of this code
// *************************************************************************************************************
#include <LowPower.h> //get library from: https://github.com/lowpowerlab/lowpower
#define LED 5 // LED pin, should be analog for fading effect (PWM)
#define BUTTON 3 // Power button pin
#define SIG_SHUTOFF 6 // Signal to Pi to ask for a shutdown
#define SIG_BOOTOK A0 // Signal from Pi that it's OK to cutoff power
// !!NOTE!! Originally this was D7 but it was moved to A0 at least temporarily.
// On MightyBoost R1 you need to connect D7 and A0 with a jumper wire.
// The explanation for this is given here: http://lowpowerlab.com/mightyboost/#source
#define OUTPUT_5V 4 // HIGH on this pin will switch the "5V*" output ON
#define BATTERYSENSE A7 // Sense VBAT_COND signal (when powered externally should read ~3.25v/3.3v (1000-1023), when external power is cutoff it should start reading around 2.85v/3.3v * 1023 ~= 880 (ratio given by 10k+4.7K divider from VBAT_COND = 1.47 multiplier)
// hence the actual input voltage = analogRead(A7) * 0.00322 (3.3v/1024) * 1.47 (10k+4.7k voltage divider ratio)
// when plugged in this should be 4.80v, nothing to worry about
// when on battery power this should decrease from 4.15v (fully charged Lipoly) to 3.3v (discharged Lipoly)
// trigger a shutdown to the target device once voltage is around 3.4v to allow 30sec safe shutdown
#define LOWBATTERYTHRESHOLD 3.5 // a shutdown will be triggered to the target device when battery voltage drops below this (Volts)
#define RESETHOLDTIME 500 // Button must be hold this many mseconds before a reset is issued (should be much less than SHUTDOWNHOLDTIME)
#define SHUTDOWNHOLDTIME 2000 // Button must be hold this many mseconds before a shutdown sequence is started (should be much less than ForcedShutoffDelay)
#define ShutoffTriggerDelay 6000 // will start checking the SIG_BOOTOK line after this long
#define RecycleTime 50000 // window of time in which SIG_BOOTOK is expected to go HIGH
// should be at least 3000 more than Min
// if nothing happens after this window, if button is
// still pressed, force cutoff power, otherwise switch back to normal ON state
#define RESETPULSETIME 500 // When reset is issued, the SHUTOFF signal is held HIGH this many ms
#define ForcedShutoffDelay 7500 // when SIG_BOOTOK==0 (PI in unknown state): if button is held
// for this long, force shutdown (this should be less than RecycleTime)
#define ShutdownFinalDelay 4500 // after shutdown signal is received, delay for this long
// to allow all PI LEDs to stop activity (pulse LED faster)
#define PRINTPERIOD 10000
int lastValidReading = 1;
unsigned long lastValidReadingTime = 0;
unsigned long NOW=0;
int PowerState = 0;
long lastPeriod = -1;
float systemVoltage = 5;
void setup() {
Serial.begin(115200);
pinMode(BUTTON, INPUT_PULLUP);
pinMode(SIG_BOOTOK, INPUT);
pinMode(SIG_SHUTOFF, OUTPUT);
pinMode(LED, OUTPUT);
pinMode(OUTPUT_5V, OUTPUT);
pinMode(A7, INPUT);
digitalWrite(SIG_SHUTOFF, LOW);//added after sudden shutdown quirks, DO NOT REMOVE!
digitalWrite(OUTPUT_5V, LOW);//added after sudden shutdown quirks, DO NOT REMOVE!
}
void loop() {
//reduce MCU ON current by ~10mA
LowPower.idle(SLEEP_30MS, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_ON, SPI_OFF, USART0_OFF, TWI_OFF);
int reading = digitalRead(BUTTON);
NOW = millis();
digitalWrite(SIG_SHUTOFF, LOW);//added after sudden shutdown quirks, DO NOT REMOVE!
boolean batteryLow = systemVoltage < LOWBATTERYTHRESHOLD;
if (batteryLow || reading != lastValidReading && NOW - lastValidReadingTime > 200)
{
lastValidReading = reading;
lastValidReadingTime = NOW;
if (batteryLow || reading == 0)
{
//make sure the button is held down for at least 'RESETHOLDTIME' before taking action (this is to avoid accidental button presses and consequently Pi shutdowns)
NOW = millis();
while (!batteryLow && (PowerState == 1 && millis()-NOW < RESETHOLDTIME)) { delay(10); if (digitalRead(BUTTON) != 0) return; }
//RESETHOLDTIME is satisfied, now check if button still held until SHUTDOWNHOLDTIME is satisfied
analogWrite(LED, 128); //dim the LED to show something's going on
while (!batteryLow && (PowerState == 1 && millis()-NOW < SHUTDOWNHOLDTIME))
{
if (digitalRead(BUTTON) != 0)
{
if (BOOTOK()) //SIG_BOOTOK is HIGH so Pi is running the shutdowncheck.sh script, ready to intercept the RESET PULSE
{
digitalWrite(SIG_SHUTOFF, HIGH);
delay(RESETPULSETIME);
digitalWrite(SIG_SHUTOFF, LOW);
NOW = millis();
boolean recycleDetected=false;
while (millis()-NOW < RecycleTime) //blink LED while waiting for BOOTOK to go high
{
//blink 3 times and pause
digitalWrite(LED, LOW);
delay(100);
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
delay(100);
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
delay(100);
digitalWrite(LED, HIGH);
delay(500);
if (!BOOTOK()) recycleDetected = true;
else if (BOOTOK() && recycleDetected)
return;
}
return; //reboot pulse sent but it appears a reboot failed; exit all checks
}
else return; //ignore everything else (button was held for RESETHOLDTIME, but SIG_BOOTOK was LOW)
}
}
//SIG_BOOTOK must be HIGH when Pi is ON. During boot, this will take a while to happen (till it executes the "shutdowncheck" script)
//so I dont want to cutoff power before it had a chance to fully boot up
if (batteryLow || (PowerState == 1 && BOOTOK()))
{
// signal Pi to shutdown
digitalWrite(SIG_SHUTOFF, HIGH);
//now wait for the Pi to signal back
NOW = millis();
float in, out;
boolean forceShutdown = true;
while (millis()-NOW < RecycleTime)
{
if (in > 6.283) in = 0;
in += .00628;
out = sin(in) * 127.5 + 127.5;
analogWrite(LED,out);
delayMicroseconds(1500);
//account for force-shutdown action (if button held for ForcedShutoffDelay, then force shutdown regardless)
if (millis()-NOW <= (ForcedShutoffDelay-SHUTDOWNHOLDTIME) && digitalRead(BUTTON) != 0)
forceShutdown = false;
if (millis()-NOW >= (ForcedShutoffDelay-SHUTDOWNHOLDTIME) && forceShutdown)
{
PowerState = 0;
digitalWrite(LED, PowerState); //turn off LED to indicate power is being cutoff
digitalWrite(OUTPUT_5V, PowerState);
break;
}
if (millis() - NOW > ShutoffTriggerDelay)
{
// Pi signaling OK to turn off
if (!BOOTOK())
{
PowerState = 0;
digitalWrite(LED, PowerState); //turn off LED to indicate power is being cutoff
NOW = millis();
while (millis()-NOW < ShutdownFinalDelay)
{
if (in > 6.283) in = 0;
in += .00628;
out = sin(in) * 127.5 + 127.5;
analogWrite(LED,out);
delayMicroseconds(300);
}
digitalWrite(OUTPUT_5V, PowerState);
break;
}
}
}
// last chance: if power still on but button still pressed, force cutoff power
if (PowerState == 1 && digitalRead(BUTTON) == 0)
{
PowerState = 0;
digitalWrite(OUTPUT_5V, PowerState);
}
digitalWrite(SIG_SHUTOFF, LOW);
}
else if (PowerState == 1 && !BOOTOK())
{
NOW = millis();
unsigned long NOW2 = millis();
int analogstep = 255 / ((ForcedShutoffDelay-SHUTDOWNHOLDTIME)/100); //every 500ms decrease LED intensity
while (digitalRead(BUTTON) == 0)
{
if (millis()-NOW2 > 100)
{
analogWrite(LED, 255 - ((millis()-NOW)/100)*analogstep);
NOW2 = millis();
}
if (millis()-NOW > ForcedShutoffDelay-SHUTDOWNHOLDTIME)
{
//TODO: add blinking here to signal final shutdown delay
PowerState = 0;
digitalWrite(OUTPUT_5V, PowerState);
break;
}
}
}
else if (PowerState == 0)
{
PowerState = 1;
digitalWrite(OUTPUT_5V, PowerState); //digitalWrite(LED, PowerState);
}
}
digitalWrite(LED, PowerState);
}
int currPeriod = millis()/PRINTPERIOD;
if (currPeriod != lastPeriod)
{
lastPeriod=currPeriod;
Serial.print("VIN: ");
systemVoltage = analogRead(BATTERYSENSE) * 0.00322 * 1.47;
Serial.print(systemVoltage);
if (systemVoltage > 4.3)
Serial.println(" (plugged in)");
else Serial.println(" (running from battery!)");
}
}
boolean BOOTOK() {
return analogRead(SIG_BOOTOK) > 800;
}
You can’t perform that action at this time.