Files
WII5Firmware/WII5ModeSleep.cpp
scottp 295abb37ee Initial public release of WII5 Buoy firmware
Firmware for an autonomous wave-measurement buoy (ATmega2560-based
WII5 v2 board). Reads wave motion from a Sparton AHRS-M1/M2 IMU,
samples GPS and battery state, and reports back over Iridium SBD
satellite telemetry. Originally developed 2012-2024.

This is the first public release. Code, documentation, and field-tested
operating modes (Capture, Sleep, Position, ManualTest, SelfTest,
LowBattery) are licensed under Apache 2.0 — see LICENSE and NOTICE.

See README.md for an overview and build instructions, CONTRIBUTING.md
for how to contribute, and DEPLOYMENTS.md for the field-deployment log.
2026-05-07 16:27:18 +10:00

264 lines
8.3 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2012-2024 Scott Penrose <scottp@dd.com.au> and WII5 Buoy contributors
//
// This file is part of WII5 Buoy firmware.
// See LICENSE for full terms.
/**
* @file WII5ModeSleep.cpp
* @brief Sleep mode: long sleeps between wake-ups; powers down the Maths CPU.
*/
/*
Sleeping.... Not a short one, this is the full deal.
* Start
- Are we holding - jump to hold
- Or calculate next sleep time
* Wait
- Shut down Devices
* Sleep
- Sleep for n seconds
* Update
- Start doing an update
* Wait Gps
* Wait Iridium
* Wait Maths
*/
#include <Arduino.h>
#include <TimeLib.h>
#include <WII5.h>
void WII5ModeSleep::reset() {
step = WII5SLEEP_START; stepWait = 0;
lastStep = WII5SLEEP_SLEEPING;
}
void WII5ModeSleep::begin() {
step = WII5SLEEP_START; stepWait = 0;
lastStep = WII5SLEEP_SLEEPING;
}
void WII5ModeSleep::loop() {
first = (step != lastStep);
lastStep = step;
switch (step) {
case WII5SLEEP_START:
// TODO - Check we have valid time
// TODO - Check this is a timed mode - and change back to default
// - If default, stays here forever
// First time - LED on
if (first) {
console.log(LOG_INFO, F("Sleep: Waiting up to 60 seconds before power down"));
sh3dNodeIO.led1Set( LED_DOUBLE );
}
// Check maths on hold - it always wins
else if (wii5Maths.remainingHold() > 0) {
console.log(LOG_ERROR, F("Sleep: Maths holding, switching to wait"));
step = WII5SLEEP_BUTTONS; stepWait = 0;
}
// TODO 2024 - Removed time at boot start
// Check if GPS is old and been an hour since we checked it, force it now
// else if ((wii5Gps.sinceOnLast > (WII5TIME_1HOUR * 1000)) && (!wii5Gps.isTimeValid())) {
// console.log(LOG_ERROR, F("Sleep: Internal clock / time is not valid, turn on GPS"));
// step = WII5SLEEP_TIME; stepWait = 0;
// }
// Normal mode - calculate next period - Wait 60 seconds first (TODO 2024)
else if (stepWait > 60000) {
// Convert minutes to seconds - note, getSleepPeriod will not return over 24 hours.
sleepNextSeconds = (uint32_t)wii5Config.getSleepPeriod() * (uint32_t)60;
// Check if we have any thing we need to do
// e.g. Check are we allowed to sleep (rules)
// Log our intent
// What about auto switching to Capture - can we do that? Should we
#ifdef DEBUG_SLEEP
console.log(LOG_DEBUG, F("Sleep: step -> wait"));
#endif
step = WII5SLEEP_WAIT; stepWait = 0;
}
break;
case WII5SLEEP_WAIT:
// Has everything finished - e.g. Maths. Give it a bit of time. How long? Need to think about it.
if (first) {
#ifdef WII5_GPS
wii5Gps.off();
#endif
wii5Communications.stop();
wii5Iridium.stop();
wii5Sparton.stop();
// Maths off ?
// wii5Maths.off();
wii5Controller.setSDOff(); // Force off then power down SD
wii5Controller.shared5Off();
}
// TODO 2024 - Wait 20 seconds afterall off before sleeping
else if (stepWait > 20000) {
#ifdef DEBUG_SLEEP
console.log(LOG_DEBUG, F("Sleep: stop devices - step -> sleeping"));
#endif
step = WII5SLEEP_SLEEPING; stepWait = 0;
}
break;
case WII5SLEEP_SLEEPING:
// Something up - should never be here
if (sleepNextSeconds < 10) {
console.log(LOG_ERROR, F("Sleep: Time < 10 seconds, skipping to UPDATE mode"));
step = WII5SLEEP_UPDATE;
}
else {
if (sleepNextSeconds > 21600) {
console.log(LOG_ERROR, F("Sleep: ERROR > 6 hours (21600) seconds, changing to 1 hour"));
sleepNextSeconds = 3600;
}
// Actual sleep
console.log(LOG_INFO, F("Sleep: sleeping %d hours = %lu seconds"), int(sleepNextSeconds / 3600), sleepNextSeconds);
// Sleep, turning off Maths
wii5Setup.sleepBefore();
// TODO 2024 - Consider sleep loop - not actually WDT but rather just tight loop, therefore leaving WDT normal for now
sh3dNodeUtil.sleep(sleepNextSeconds);
wii5Setup.sleepAfter();
// }
console.log(LOG_INFO, F("Sleep: Woke up - requested=%lu, actual=%lu, reasons=%d"), sleepNextSeconds, sh3dNodeUtil.sleepLastSeconds, sh3dNodeUtil.sleepLastReason);
switch(sh3dNodeUtil.sleepLastReason) {
case 0:
console.log(LOG_FATAL, F("Unknwon error inside sleep function"));
// TODO What next? - not sure how to deal with this
// TODO put it in an error counter... if the counter gets too large or rescent
// Then switch back to default mode.
step = WII5SLEEP_START; stepWait = 0;
break;
// Normal exit
case 1:
// TODO 12 hours is up - turn on GPS etc and send/receive data
step = WII5SLEEP_UPDATE;
break;
// TODO Identify that this needs to stay on for some seconds for
// button Processing
// These are like a reboot - coming out of sleep. Beep like one
case 10:
case 11:
wii5Setup.bootbeep();
step = WII5SLEEP_BUTTONS;
break;
default:
console.log(LOG_ERROR, F("Sleep: Unknown mode = %d"), step);
step = WII5SLEEP_UPDATE;
break;
}
}
break;
case WII5SLEEP_BUTTONS:
if (first) {
sh3dNodeIO.led1Set( LED_DOUBLE );
console.log(LOG_ERROR, F("Sleep: BUTTONS process Waiting for Maths and another 10 seconds for buttons"));
wii5Maths.stop();
}
// Maths stopped and 30 seconds
else if (wii5Maths.isOff() && (stepWait > 30000)) {
console.log(LOG_ERROR, F("Sleep: BUTTONS process finished, Maths off, starting sleep process (30 seconds)"));
step = WII5SLEEP_START; stepWait = 0;
}
// 1 hour passed - shut it down buoys
else if (stepWait > ((uint32_t)WII5TIME_1HOUR * 1000)) {
console.log(LOG_FATAL, F("Sleep: Failed timeout of Buttons"));
step = WII5SLEEP_START; stepWait = 0;
}
break;
case WII5SLEEP_TIME:
if (first) {
wii5Gps.autoTime();
displayWait = 0;
}
else if (wii5Gps.ready()) {
step = WII5SLEEP_START; stepWait = 0;
}
else if (stepWait > 300000) {
step = WII5SLEEP_START; stepWait = 0;
}
else if (displayWait > 10000) {
console.log(LOG_INFO, F("Sleep: Waiting for GPS Time - %lu seconds"), (stepWait / 1000));
displayWait = 0;
}
break;
case WII5SLEEP_UPDATE:
if (first) {
sh3dNodeIO.led1Set( LED_DOUBLE );
#ifdef WII5_GPS
wii5Gps.autoAccurate();
#endif
wii5Battery.start();
wii5Weather_18B20.temperatureRead();
displayWait = 0;
wii5Maths.stop();
}
else if (wii5Gps.ready()) {
if (wii5Gps.isError()) {
console.log(LOG_ERROR, F("Sleep: GPS Finished with ERROR"));
}
wii5Gps.off();
step = WII5SLEEP_COMMS; stepWait = 0;
}
else if (stepWait > 300000) {
console.log(LOG_ERROR, F("Sleep: GPS Timed out after 300 seconds"));
step = WII5SLEEP_COMMS; stepWait = 0;
}
else if (displayWait > 10000) {
console.log(LOG_INFO, F("Sleep: Waiting for GPS Lock - %lu seconds"), (stepWait / 1000));
displayWait = 0;
}
break;
case WII5SLEEP_COMMS:
if (first) {
sh3dNodeIO.led1Set( LED_DOUBLE );
console.log(LOG_INFO, F("Sleep: Communications start"));
wii5Communications.setSimpleMode();
wii5Communications.sendBinModeType(wii5Config.getSleepBinaryType(), wii5Config.getRecordCount());
wii5Communications.start();
}
else if (!wii5Communications.isRunning()) {
console.log(LOG_INFO, F("Sleep: Communications end"));
step = WII5SLEEP_START; stepWait = 0;
}
// TODO 10 minutes ok
else if (stepWait > 600000) {
console.log(LOG_FATAL, F("Sleep: Communications timeout"));
step = WII5SLEEP_START; stepWait = 0;
}
break;
default:
console.log(LOG_FATAL, F("SLEEP: Default step... step=%d"), step);
step = WII5SLEEP_START; stepWait = 0;
break;
}
}
WII5ModeSleep wii5ModeSleep;