295abb37ee
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.
264 lines
8.3 KiB
C++
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;
|