// SPDX-License-Identifier: Apache-2.0 // Copyright (c) 2012-2024 Scott Penrose 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 #include #include 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;