From 4561c455b5ab9e701ef14b13e8ad96482c65ba7a Mon Sep 17 00:00:00 2001 From: Saqib Akram <144418717+sakrubx@users.noreply.github.com> Date: Thu, 3 Oct 2024 21:38:49 +0500 Subject: [PATCH] UART power saving support for LEXI-R10 (#1203) The UART power saving/32 kHz sleep is updated for LEXI-R10. Mode-1 of UART power saving is supported for LEXI-R10 in which the PWR_ON pin must be toggled to get the module out of sleep. The deep sleep limit can be adjusted by setting the `U_CELL_PWR_UART_POWER_SAVING_DEEP_SLEEP_MODE_R10` to the desired value from `uCellPwrSleepModeR10_t`. --- cell/api/u_cell_pwr.h | 26 ++++-- cell/src/u_cell_private.c | 73 ++++------------ cell/src/u_cell_private.h | 39 +-------- cell/src/u_cell_pwr.c | 125 ++++++++++++++++++++++------ cell/test/u_cell_pwr_test.c | 2 +- example/cell/power_saving/README.md | 4 +- 6 files changed, 138 insertions(+), 131 deletions(-) diff --git a/cell/api/u_cell_pwr.h b/cell/api/u_cell_pwr.h index 316df423..60692535 100644 --- a/cell/api/u_cell_pwr.h +++ b/cell/api/u_cell_pwr.h @@ -49,19 +49,20 @@ * * The sleep states are as follows: * - * "UART sleep"/"32 kHz sleep": in this sleep state the speed of the - * module's clocks are reduced to save a lot of power. Because of + * "UART sleep"/"32 kHz sleep"/"sleep-1": in this sleep state the speed + * of the module's clocks are reduced to save a lot of power. Because of * these reduced clock rates the module is not able to drive the * UART HW, hence this is often termed "UART sleep". However, all * of the module's RAM is still on, state is fully retained, the module * is still actually running, is still connected to the network, and - * it can be woken-up quickly by toggling lines of the UART AT interface. + * it can be woken-up quickly by toggling lines of the UART AT interface, + * except for LEXI-R10 where a configured GPIO line or PWR_ON must be toggled. * - * "deep sleep": in this sleep state the module is basically off, - * almost all state is lost, what is retained is only a basic notion - * of time and whether the module was attached to the cellular - * network when deep sleep began. The IP stack on the module, the - * MQTT client on the module, etc, are all reset by deep sleep. + * "deep sleep"/"sleep-2"/"hibernate": in this sleep state the module + * is basically off, almost all state is lost, what is retained is + * only a basic notion of time and whether the module was attached to + * the cellular network when deep sleep began. The IP stack on the + * module, the MQTT client on the module, etc, are all reset by deep sleep. * * The ways of entering these sleep states are as follows: * @@ -85,7 +86,7 @@ * go into 32 kHz sleep during the E-DRX sleep periods the application * never has to worry about state being lost. * - * "3GPP power saving made (PSM)": also a 3GPP-defined mechanism, this + * "3GPP power saving mode (PSM)": also a 3GPP-defined mechanism, this * forms an agreement with the network that the module will be out of * contact for long periods (think hours or days). The functions below * with "3gppPowerSaving" in the name allow you to initiate and manage @@ -179,6 +180,13 @@ extern "C" { # define U_CELL_PWR_UART_POWER_SAVING_DTR_HYSTERESIS_MS 20 #endif +#ifndef U_CELL_PWR_UART_POWER_SAVING_DEEP_SLEEP_MODE_R10 +/** The deep sleep limit of UART power saving that can be achieved for + * LEXI-R10 (see uCellPwrSleepModeR10_t). + */ +# define U_CELL_PWR_UART_POWER_SAVING_DEEP_SLEEP_MODE_R10 U_CELL_PWR_SLEEP_MODE_R10_1 +#endif + /* ---------------------------------------------------------------- * TYPES * -------------------------------------------------------------- */ diff --git a/cell/src/u_cell_private.c b/cell/src/u_cell_private.c index 0f6c4337..02959b27 100644 --- a/cell/src/u_cell_private.c +++ b/cell/src/u_cell_private.c @@ -520,7 +520,6 @@ const uCellPrivateModule_t gUCellPrivateModuleList[] = { (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_MQTT_KEEP_ALIVE) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_MQTT_SECURITY) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_SECURITY_ZTP) | - (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_DTR_POWER_SAVING) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_DEEP_SLEEP_URC) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_3GPP_POWER_SAVING) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_3GPP_POWER_SAVING_PAGING_WINDOW_SET) | @@ -529,6 +528,7 @@ const uCellPrivateModule_t gUCellPrivateModuleList[] = { (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_FOTA) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_SNR_REPORTED) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_AUTHENTICATION_MODE_AUTOMATIC) | + (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_UART_POWER_SAVING) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_UCGED) | (1ULL << (int32_t) U_CELL_PRIVATE_FEATURE_HTTP) /* features */ ), @@ -921,6 +921,21 @@ int32_t privateActivateProfile(const uCellPrivateInstance_t *pInstance, return errorCode; } +// Check whether the module is LEXI-R10 and +// we have the mode 1 for UPSV sleep. +static bool uCellPrivateIsR10SleepMode1(uCellPrivateInstance_t *pInstance) +{ + bool success = false; + + if (pInstance != NULL) { + if ((pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_LEXI_R10) && + (pInstance->uartSleepCache.mode == 1)) { + success = true; + } + } + return success; +} + /* ---------------------------------------------------------------- * PUBLIC FUNCTIONS THAT ARE PRIVATE TO CELLULAR * -------------------------------------------------------------- */ @@ -1374,7 +1389,7 @@ int32_t uCellPrivateWakeUpCallback(uAtClientHandle_t atHandle, void *pInstance) uPortUartCtsSuspend(stream.handle.int32); } - if (uCellPrivateIsDeepSleepActive(_pInstance)) { + if (uCellPrivateIsDeepSleepActive(_pInstance) || uCellPrivateIsR10SleepMode1(_pInstance)) { // We know that the module has gone into 3GPP sleep, wake it up. errorCode = deepSleepWakeUp(_pInstance); } else { @@ -1449,60 +1464,6 @@ void uCellPrivateSetDeepSleepState(uCellPrivateInstance_t *pInstance) } } -// Suspend "32 kHz" or UART/AT+UPSV sleep. -int32_t uCellPrivateSuspendUartPowerSaving(const uCellPrivateInstance_t *pInstance, - int32_t *pMode, int32_t *pTimeout) -{ - int32_t errorCode = (int32_t) U_ERROR_COMMON_INVALID_PARAMETER; - uAtClientHandle_t atHandle = pInstance->atHandle; - - if ((pMode != NULL) && (pTimeout != NULL)) { - // First, read the current AT+UPSV mode - uAtClientLock(atHandle); - uAtClientCommandStart(atHandle, "AT+UPSV?"); - uAtClientCommandStop(atHandle); - uAtClientResponseStart(atHandle, "+UPSV:"); - *pMode = uAtClientReadInt(atHandle); - *pTimeout = -1; - if (!U_CELL_PRIVATE_MODULE_IS_R4(pInstance->pModule->moduleType) && - ((*pMode == 1) || (*pMode == 4))) { - // Only non-SARA-R4 modules have a timeout value and - // only for AT+UPSV modes 1 and 4 - *pTimeout = uAtClientReadInt(atHandle); - } - uAtClientResponseStop(atHandle); - errorCode = uAtClientUnlock(atHandle); - if ((errorCode == 0) && (*pMode > 0)) { - // If that was successful and the current mode was - // not already zero then we now disable AT+UPSV - uAtClientLock(atHandle); - uAtClientCommandStart(atHandle, "AT+UPSV="); - uAtClientWriteInt(atHandle, 0); - uAtClientCommandStopReadResponse(atHandle); - errorCode = uAtClientUnlock(atHandle); - } - } - - return errorCode; -} - -// Resume "32 kHz" or UART/AT+UPSV sleep. -int32_t uCellPrivateResumeUartPowerSaving(const uCellPrivateInstance_t *pInstance, - int32_t mode, int32_t timeout) -{ - uAtClientHandle_t atHandle = pInstance->atHandle; - - uAtClientLock(atHandle); - uAtClientCommandStart(atHandle, "AT+UPSV="); - uAtClientWriteInt(atHandle, mode); - if (timeout >= 0) { - uAtClientWriteInt(atHandle, timeout); - } - uAtClientCommandStopReadResponse(atHandle); - - return uAtClientUnlock(atHandle); -} - // Delete file on file system. int32_t uCellPrivateFileDelete(const uCellPrivateInstance_t *pInstance, const char *pFileName) diff --git a/cell/src/u_cell_private.h b/cell/src/u_cell_private.h index c4d20c7f..eb7ba8a1 100644 --- a/cell/src/u_cell_private.h +++ b/cell/src/u_cell_private.h @@ -453,6 +453,7 @@ typedef struct { typedef struct { int32_t mode; int32_t sleepTime; + int32_t maxSleepMode; } uCellPrivateUartSleepCache_t; /** Track the state of the profile that is mapped to the @@ -895,44 +896,6 @@ int32_t uCellPrivateWakeUpCallback(uAtClientHandle_t atHandle, */ void uCellPrivateSetDeepSleepState(uCellPrivateInstance_t *pInstance); -/** Suspend "32 kHz" or UART/AT+UPSV sleep. This function reads the - * current AT+UPSV state, which it returns in pMode and pTimeout, then - * sets AT+UPSV=0. uCellPrivateResumeUartPowerSaving() should be used, - * with the values placed in pMode and pTimeout, to resume UART power - * saving. - * - * Note: gUCellPrivateMutex should be locked before this is called. - * - * @param[in] pInstance a pointer to the cellular instance. - * @param[out] pMode a pointer to a place to put the current AT+UPSV - * mode; cannot be NULL. - * @param[out] pTimeout a pointer to a place to put the current AT+UPSV - * timesout; cannot be NULL, if the AT+UPSV mode in - * pMode does not have a timeout then -1 will be - * returned. - * @return zero on successful wake-up, else negative error. - */ -int32_t uCellPrivateSuspendUartPowerSaving(const uCellPrivateInstance_t *pInstance, - int32_t *pMode, int32_t *pTimeout); - -/** Resume "32 kHz" or UART/AT+UPSV sleep, the counterpart to - * uCellPrivateSuspendUartPowerSaving(). - * - * Note: gUCellPrivateMutex should be locked before this is called. - * - * @param[in] pInstance a pointer to the cellular instance. - * @param mode the AT+UPSV mode to apply. - * @param timeout the timeout for the AT+UPSV mode; if the mode in - * question does not have a timeout value then - * a negative value should be used, in other words - * the value returned by - * uCellPrivateSuspendUartPowerSaving() can be used - * directly. - * @return zero on successful wake-up, else negative error. - */ -int32_t uCellPrivateResumeUartPowerSaving(const uCellPrivateInstance_t *pInstance, - int32_t mode, int32_t timeout); - /** Delete a file from the file system. If the file does not exist an * error will be returned. * diff --git a/cell/src/u_cell_pwr.c b/cell/src/u_cell_pwr.c index 8d4c910f..eafc2f73 100644 --- a/cell/src/u_cell_pwr.c +++ b/cell/src/u_cell_pwr.c @@ -107,15 +107,34 @@ /** The UART power-saving modes: note that these numbers are defined * by the AT interface and should NOT be changed. */ -//lint -esym(749, uCellPwrPsvMode_t::U_CELL_PWR_PSV_MODE_RTS) Suppress not referenced -//lint -esym(749, uCellPwrPsvMode_t::U_CELL_PWR_PSV_MODE_DTR) Suppress not referenced +//lint -esym(749, uCellPwrUpsvMode_t::U_CELL_PWR_UPSV_MODE_RTS) Suppress not referenced +//lint -esym(749, uCellPwrUpsvMode_t::U_CELL_PWR_UPSV_MODE_DTR) Suppress not referenced typedef enum { - U_CELL_PWR_PSV_MODE_DISABLED = 0, /**< No UART power saving. */ - U_CELL_PWR_PSV_MODE_DATA = 1, /**< Module wakes up on TXD line activity, SARA-U201/SARA-R5 version. */ - U_CELL_PWR_PSV_MODE_RTS = 2, /**< Module wakes up on RTS line being asserted (not used in this code). */ - U_CELL_PWR_PSV_MODE_DTR = 3, /**< Module wakes up on DTR line being asserted. */ - U_CELL_PWR_PSV_MODE_DATA_SARA_R4_LENA_R8 = 4 /**< Module wakes up on TXD line activity, SARA-R4/LENA-R8 version. */ -} uCellPwrPsvMode_t; + U_CELL_PWR_UPSV_MODE_DISABLED = 0, /**< No UART power saving. */ + U_CELL_PWR_UPSV_MODE_DATA = 1, /**< Module wakes up on TXD line activity, + SARA-U201/SARA-R5 version. */ + U_CELL_PWR_UPSV_MODE_RTS = 2, /**< Module wakes up on RTS line being + asserted (not used in this code). */ + U_CELL_PWR_UPSV_MODE_DTR = 3, /**< Module wakes up on DTR line being asserted. */ + U_CELL_PWR_UPSV_MODE_DATA_SARA_R4_LENA_R8 = 4, /**< Module wakes up on TXD line + activity, SARA-R4/LENA-R8 version. */ + U_CELL_PWR_UPSV_MODE_GPIO_LEXI_R10 = 5 /**< Module waking up is controlled by + the GPIO pin configured with GPIO + mode 34, LEXI-R10 version. */ +} uCellPwrUpsvMode_t; + +/** The UART power-saving max sleep modes for LEXI-R10. + * Note: these numbers are defined by the AT interface + * and should NOT be changed. + */ +typedef enum { + U_CELL_PWR_SLEEP_MODE_R10_1 = 2, /**< UART/32 kHz sleep, on-board + applications (IP/MQTT/HTTP) retained. */ + U_CELL_PWR_SLEEP_MODE_R10_2 = 3, /**< Deep sleep, all on-board applications + (IP/MQTT/HTTP) lost. */ + U_CELL_PWR_SLEEP_MODE_R10_HIBERNATE = 4 /**< Deepest sleep, all on-board + applications (IP/MQTT/HTTP) lost.*/ +} uCellPwrSleepModeR10_t; /** All the parameters for a wake-up-from-deep sleep callback. */ @@ -924,7 +943,10 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, bool success = true; uAtClientHandle_t atHandle = pInstance->atHandle; uAtClientStreamHandle_t stream = U_AT_CLIENT_STREAM_HANDLE_DEFAULTS; - uCellPwrPsvMode_t uartPowerSavingMode = U_CELL_PWR_PSV_MODE_DISABLED; // Assume no UART power saving + uCellPwrUpsvMode_t uartPowerSavingMode = + U_CELL_PWR_UPSV_MODE_DISABLED; // Assume no UART power saving + uCellPrivateUartSleepCache_t *pUartSleepCache = &(pInstance->uartSleepCache); + int32_t uartPowerSavingTimeout = U_CELL_PWR_UART_POWER_SAVING_GSM_FRAMES; char buffer[20]; // Enough room for AT+UPSV=2,1300 #if U_CELL_PWR_GNSS_PROFILE_BITS_EXTRA >= 0 char *pServerNameGnss; @@ -985,7 +1007,7 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, // It does: resume CTS and we can use the wake-up on // TX line feature for power saving uPortUartCtsResume(stream.handle.int32); - uartPowerSavingMode = U_CELL_PWR_PSV_MODE_DATA; + uartPowerSavingMode = U_CELL_PWR_UPSV_MODE_DATA; } } } else { @@ -996,7 +1018,7 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, if (uAtClientWakeUpHandlerIsSet(atHandle) && U_CELL_PRIVATE_HAS(pInstance->pModule, U_CELL_PRIVATE_FEATURE_UART_POWER_SAVING)) { - uartPowerSavingMode = U_CELL_PWR_PSV_MODE_DATA; + uartPowerSavingMode = U_CELL_PWR_UPSV_MODE_DATA; } } } @@ -1010,7 +1032,7 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, // on SARA-R5 and SARA-U201, can be used to get out of sleep. // This will already have been set by the user calling // uCellPwrSetDtrPowerSavingPin(). - uartPowerSavingMode = U_CELL_PWR_PSV_MODE_DTR; + uartPowerSavingMode = U_CELL_PWR_UPSV_MODE_DTR; } if (uAtClientWakeUpHandlerIsSet(atHandle) && @@ -1028,21 +1050,39 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, // Meanwhile, LENA-R8 supports all of the modes, including // the timing parameter, but renumbers 1 as 4, just to // be different - uartPowerSavingMode = U_CELL_PWR_PSV_MODE_DATA_SARA_R4_LENA_R8; + uartPowerSavingMode = U_CELL_PWR_UPSV_MODE_DATA_SARA_R4_LENA_R8; + } + + if (uAtClientWakeUpHandlerIsSet(atHandle) && + (pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_LEXI_R10)) { + // The mode 1 behaves differently for LEXI-R10, that requires a + // pulse on PWR_ON pin to wake the module up from sleep mode. + if (pInstance->pinPwrOn >= 0) { + uartPowerSavingMode = U_CELL_PWR_UPSV_MODE_DATA; + // In case of LEXI-R10 the timeout is in milliseconds + // rather than in GSM frames as in SARA-U201 and SARA-R5 cases. + uartPowerSavingTimeout = (int32_t) U_CELL_POWER_SAVING_UART_INACTIVITY_TIMEOUT_SECONDS * 1000; + } else { + uartPowerSavingMode = U_CELL_PWR_UPSV_MODE_DISABLED; + } } if (success) { // Assemble the UART power saving mode AT command - if ((uartPowerSavingMode == U_CELL_PWR_PSV_MODE_DATA) || - ((uartPowerSavingMode == U_CELL_PWR_PSV_MODE_DATA_SARA_R4_LENA_R8) && + if ((uartPowerSavingMode == U_CELL_PWR_UPSV_MODE_DATA) || + ((uartPowerSavingMode == U_CELL_PWR_UPSV_MODE_DATA_SARA_R4_LENA_R8) && (pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_LENA_R8))) { snprintf(buffer, sizeof(buffer), "AT+UPSV=%d,%d", (int) uartPowerSavingMode, - U_CELL_PWR_UART_POWER_SAVING_GSM_FRAMES); + (int) uartPowerSavingTimeout); + if (pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_LEXI_R10) { + snprintf(&buffer[strlen(buffer)], sizeof(buffer), ",%d", + (int) U_CELL_PWR_UART_POWER_SAVING_DEEP_SLEEP_MODE_R10); + } } else { snprintf(buffer, sizeof(buffer), "AT+UPSV=%d", (int) uartPowerSavingMode); if (!returningFromSleep && - (uartPowerSavingMode == U_CELL_PWR_PSV_MODE_DISABLED) && + (uartPowerSavingMode == U_CELL_PWR_UPSV_MODE_DISABLED) && uAtClientWakeUpHandlerIsSet(atHandle)) { // Remove the wake-up handler if it turns out that power // saving cannot be supported but leave well alone if @@ -1064,9 +1104,13 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, uAtClientSetWakeUpHandler(atHandle, NULL, NULL, 0); uPortLog("U_CELL_PWR: power saving not supported.\n"); } + // Update the UART sleep cache. + pUartSleepCache->mode = uartPowerSavingMode; + pUartSleepCache->sleepTime = uartPowerSavingTimeout; + pUartSleepCache->maxSleepMode = U_CELL_PWR_UART_POWER_SAVING_DEEP_SLEEP_MODE_R10; // Now tell the AT Client that it should control the // DTR pin, if relevant - if (!returningFromSleep && (uartPowerSavingMode == U_CELL_PWR_PSV_MODE_DTR)) { + if (!returningFromSleep && (uartPowerSavingMode == U_CELL_PWR_UPSV_MODE_DTR)) { uAtClientSetActivityPin(atHandle, pInstance->pinDtrPowerSaving, U_CELL_PWR_UART_POWER_SAVING_DTR_READY_MS, U_CELL_PWR_UART_POWER_SAVING_DTR_HYSTERESIS_MS, @@ -1223,6 +1267,8 @@ static int32_t powerOff(uCellPrivateInstance_t *pInstance, uAtClientCommandStart(atHandle, "AT+UPSV=0"); uAtClientCommandStopReadResponse(atHandle); uAtClientUnlock(atHandle); + // Updating the uart sleep cache. + pInstance->uartSleepCache.mode = U_CELL_PWR_UPSV_MODE_DISABLED; } // Send the power off command and then pull the power uAtClientLock(atHandle); @@ -1947,27 +1993,39 @@ int32_t uCellPwrPrivateDisableUartSleep(uCellPrivateInstance_t *pInstance) uAtClientCommandStop(atHandle); uAtClientResponseStart(atHandle, "+UPSV:"); pUartSleepCache->mode = uAtClientReadInt(atHandle); - if ((pUartSleepCache->mode == 1) || + if ((pUartSleepCache->mode == U_CELL_PWR_UPSV_MODE_DATA) || ((pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_LENA_R8) && - (pUartSleepCache->mode == 4))) { + (pUartSleepCache->mode == U_CELL_PWR_UPSV_MODE_DATA_SARA_R4_LENA_R8))) { // Mode 1 has a time attached, as does mode 4 but only if this // is LENA-R8 pUartSleepCache->sleepTime = uAtClientReadInt(atHandle); } + if (pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_LEXI_R10) { + pUartSleepCache->maxSleepMode = uAtClientReadInt(atHandle); + } uAtClientResponseStop(atHandle); errorCode = uAtClientUnlock(atHandle); - if (errorCode == 0) { + if (errorCode == U_ERROR_COMMON_SUCCESS) { // Now switch off sleep and remove the handler, // so that everyone knows sleep is gone uAtClientLock(atHandle); uAtClientCommandStart(atHandle, "AT+UPSV="); - uAtClientWriteInt(atHandle, 0); + uAtClientWriteInt(atHandle, U_CELL_PWR_UPSV_MODE_DISABLED); uAtClientCommandStopReadResponse(atHandle); errorCode = uAtClientUnlock(atHandle); - if (errorCode == 0) { + if (errorCode == U_ERROR_COMMON_SUCCESS) { uAtClientSetWakeUpHandler(atHandle, NULL, NULL, 0); } } + // In case of LEXI-R10 we need to enable the USB + // stack back if we have sleep 2 or hibernate mode. + if ((errorCode == 0) && (pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_LEXI_R10) && + (pUartSleepCache->maxSleepMode >= U_CELL_PWR_SLEEP_MODE_R10_2)) { + uAtClientLock(atHandle); + uAtClientCommandStart(atHandle, "AT+UUSBCONF=0"); + uAtClientCommandStopReadResponse(atHandle); + errorCode = uAtClientUnlock(atHandle); + } } } @@ -1993,17 +2051,28 @@ int32_t uCellPwrPrivateEnableUartSleep(uCellPrivateInstance_t *pInstance) // If no sleep handler is set then either sleep // is not supported or it has been disabled: // if it has been disabled then the cache - // will contain the previous mode so check it + // will contain the previous mode so check it. if (pUartSleepCache->mode > 0) { // There is a cached mode, put it back again #ifndef U_CFG_CELL_DISABLE_UART_POWER_SAVING + // In case of LEXI-R10 we need to disable the USB + // stack if we need to have sleep 2 or hibernate mode. + if (pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_LEXI_R10) { + if (pUartSleepCache->maxSleepMode >= U_CELL_PWR_SLEEP_MODE_R10_2) { + moduleConfigureOne(atHandle, "AT+UUSBCONF=99", 1); + } + } uAtClientLock(atHandle); uAtClientCommandStart(atHandle, "AT+UPSV="); uAtClientWriteInt(atHandle, pUartSleepCache->mode); - if (pUartSleepCache->mode == 1) { + if (pUartSleepCache->mode == U_CELL_PWR_UPSV_MODE_DATA) { // Mode 1 has a time uAtClientWriteInt(atHandle, pUartSleepCache->sleepTime); } + if (pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_LEXI_R10) { + // In case of LEXI-R10, specify the UART deep sleep mode. + uAtClientWriteInt(atHandle, pUartSleepCache->maxSleepMode); + } uAtClientCommandStopReadResponse(atHandle); errorCode = uAtClientUnlock(atHandle); if (errorCode == 0) { @@ -2011,6 +2080,7 @@ int32_t uCellPwrPrivateEnableUartSleep(uCellPrivateInstance_t *pInstance) // has been re-enabled pUartSleepCache->mode = 0; pUartSleepCache->sleepTime = 0; + pUartSleepCache->maxSleepMode = 0; uAtClientSetWakeUpHandler(atHandle, uCellPrivateWakeUpCallback, pInstance, (U_CELL_POWER_SAVING_UART_INACTIVITY_TIMEOUT_SECONDS * 1000) - U_CELL_POWER_SAVING_UART_WAKEUP_MARGIN_MILLISECONDS); @@ -2186,6 +2256,11 @@ int32_t uCellPwrOffHard(uDeviceHandle_t cellHandle, bool trulyHard, uAtClientCommandStart(atHandle, "AT+UPSV=0"); uAtClientCommandStopReadResponse(atHandle); uAtClientUnlock(atHandle); + // Update the uart sleep cache + pInstance->uartSleepCache.mode = U_CELL_PWR_UPSV_MODE_DISABLED; + // Some modules (e.g. LEXI-R10 in U_CELL_PWR_SLEEP_MODE_R10_1 mode) + // require a breather to let the AT+UPSV=0 effective. + uPortTaskBlock(500); } uPortLog("U_CELL_PWR: powering off using the PWR_ON pin.\n"); uPortGpioSet(pInstance->pinPwrOn, diff --git a/cell/test/u_cell_pwr_test.c b/cell/test/u_cell_pwr_test.c index b6c196bb..4e872123 100644 --- a/cell/test/u_cell_pwr_test.c +++ b/cell/test/u_cell_pwr_test.c @@ -789,7 +789,7 @@ U_PORT_TEST_FUNCTION("[cellPwr]", "cellPwr") /** Power on process testing for any module type. */ -U_PORT_TEST_FUNCTION("[cellPwr]", "uCellPwrAnyModule") +U_PORT_TEST_FUNCTION("[cellPwr]", "cellPwrAnyModule") { uAtClientStreamHandle_t stream; int32_t resourceCount; diff --git a/example/cell/power_saving/README.md b/example/cell/power_saving/README.md index bfb1f91d..5e8dff1b 100644 --- a/example/cell/power_saving/README.md +++ b/example/cell/power_saving/README.md @@ -8,9 +8,9 @@ A u-blox cellular module has two sleep states and three ways to get to them. A # Detailed Description The two sleep states of a u-blox cellular module are as follows: -- "UART sleep"/"32 kHz sleep": in this sleep state the speed of the module's clocks are reduced to save a lot of power. Because of these reduced clock rates the module is not able to drive the UART HW, hence this is often termed "UART sleep". However, all of the module's RAM is still on, state is fully retained, the module is still actually running, is still connected to the network, and it can be woken-up quickly by toggling lines of the UART AT interface. +- "UART sleep"/"32 kHz sleep"/"sleep-1": in this sleep state the speed of the module's clocks are reduced to save a lot of power. Because of these reduced clock rates the module is not able to drive the UART HW, hence this is often termed "UART sleep". However, all of the module's RAM is still on, state is fully retained, the module is still actually running, is still connected to the network, and it can be woken-up quickly by toggling lines of the UART AT interface, except for LEXI-R10 where a configured GPIO line or PWR_ON must be toggled. -- "deep sleep": in this sleep state the module is basically off, almost all state is lost, what is retained is only a basic notion of time and whether the module was attached to the cellular network when deep sleep began. The IP stack on the module, the MQTT client on the module, etc, are all reset by deep sleep. To exit from deep sleep the module `PWR_ON` pin must be toggled, hence it is a requirement that a pin of your MCU is connected to the `PWR_ON` pin of the module for deep sleep to be entered. It is also a requirement that this code is able to detect that the module has entered deep sleep, so for this purpose the `VINT` pin of the module must be connected to a pin of this MCU; note that while there is an unsolicited result code or URC, `+UUPSMR`, which can indicate the module sleep state, this does not actually indicate whether the module is about to enter deep asleep or not, only whether the cellular protocol stack inside the module has entered a deactivated state, hence it is not possible to use this as a true indication of deep sleep. +- "deep sleep"/"sleep-2"/"hibernate": in this sleep state the module is basically off, almost all state is lost, what is retained is only a basic notion of time and whether the module was attached to the cellular network when deep sleep began. The IP stack on the module, the MQTT client on the module, etc, are all reset by deep sleep. To exit from deep sleep the module `PWR_ON` pin must be toggled, hence it is a requirement that a pin of your MCU is connected to the `PWR_ON` pin of the module for deep sleep to be entered. It is also a requirement that this code is able to detect that the module has entered deep sleep, so for this purpose the `VINT` pin of the module must be connected to a pin of this MCU; note that while there is an unsolicited result code or URC, `+UUPSMR`, which can indicate the module sleep state, this does not actually indicate whether the module is about to enter deep asleep or not, only whether the cellular protocol stack inside the module has entered a deactivated state, hence it is not possible to use this as a true indication of deep sleep. The three ways of entering these sleep states are as follows: