diff --git a/cell/src/u_cell_pwr.c b/cell/src/u_cell_pwr.c index b7f6bd32..cbdeb3a0 100644 --- a/cell/src/u_cell_pwr.c +++ b/cell/src/u_cell_pwr.c @@ -100,6 +100,15 @@ */ #define U_CELL_INITIAL_CONFIG_RETRIES 2 +#ifndef U_CELL_PWR_SARA_U201_SHORT_AT_TIMEOUT_MS +/** SARA-U201 sometimes switches off the UART to power save mode + * before it has sent the "OK" response, so use a short-timeout + * for that case to avoid hanging about when we're meant to be + * saving power. + */ +# define U_CELL_PWR_SARA_U201_SHORT_AT_TIMEOUT_MS 2000 +#endif + /* ---------------------------------------------------------------- * TYPES * -------------------------------------------------------------- */ @@ -899,11 +908,15 @@ static void UUPSMR_urc(uAtClientHandle_t atHandle, void *pParameter) // Configure one item in the cellular module. static bool moduleConfigureOne(uAtClientHandle_t atHandle, const char *pAtString, - int32_t configurationTries) + int32_t configurationTries, + int32_t atTimeoutMs) { bool success = false; for (size_t x = configurationTries; (x > 0) && !success; x--) { uAtClientLock(atHandle); + if (atTimeoutMs >= 0) { + uAtClientTimeoutSet(atHandle, atTimeoutMs); + } uAtClientCommandStart(atHandle, pAtString); uAtClientCommandStopReadResponse(atHandle); success = (uAtClientUnlock(atHandle) == 0); @@ -922,6 +935,7 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, uAtClientStreamHandle_t stream = U_AT_CLIENT_STREAM_HANDLE_DEFAULTS; uCellPwrPsvMode_t uartPowerSavingMode = U_CELL_PWR_PSV_MODE_DISABLED; // Assume no UART power saving char buffer[20]; // Enough room for AT+UPSV=2,1300 + int32_t atTimeoutMs = -1; #if U_CELL_PWR_GNSS_PROFILE_BITS_EXTRA >= 0 char *pServerNameGnss; int32_t y; @@ -932,7 +946,7 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, (x < sizeof(gpConfigCommand) / sizeof(gpConfigCommand[0])) && success; x++) { success = moduleConfigureOne(atHandle, gpConfigCommand[x], - U_CELL_PWR_CONFIGURATION_COMMAND_TRIES); + U_CELL_PWR_CONFIGURATION_COMMAND_TRIES, -1); } if (success && @@ -943,10 +957,10 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, // (SARA-R5 and SARA-U201 have a single mode and require no setting) if (U_CELL_PRIVATE_HAS(pInstance->pModule, U_CELL_PRIVATE_FEATURE_UCGED5)) { success = moduleConfigureOne(atHandle, "AT+UCGED=5", - U_CELL_PWR_CONFIGURATION_COMMAND_TRIES); + U_CELL_PWR_CONFIGURATION_COMMAND_TRIES, -1); } else { success = moduleConfigureOne(atHandle, "AT+UCGED=2", - U_CELL_PWR_CONFIGURATION_COMMAND_TRIES); + U_CELL_PWR_CONFIGURATION_COMMAND_TRIES, -1); } } @@ -961,7 +975,7 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, // of a cellular module if (uPortUartIsRtsFlowControlEnabled(stream.handle.int32) && uPortUartIsCtsFlowControlEnabled(stream.handle.int32)) { - moduleConfigureOne(atHandle, "AT&K3", 1); + moduleConfigureOne(atHandle, "AT&K3", 1, -1); if (uAtClientWakeUpHandlerIsSet(atHandle)) { // The RTS/CTS handshaking lines are being used // for flow control by the UART HW. This complicates @@ -984,7 +998,7 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, } } } else { - moduleConfigureOne(atHandle, "AT&K0", 1); + moduleConfigureOne(atHandle, "AT&K0", 1, -1); // RTS/CTS handshaking is not used by the UART HW, we // can use the wake-up on TX line feature without any // complications @@ -1048,7 +1062,18 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, } // Use the UART power saving mode AT command to set the mode // in the module - if (!moduleConfigureOne(atHandle, buffer, 1) && + // Note: SARA-U201 does not always respond to the AT+UPSV=1 command, + // it disables the UART immediately, hence here we set a short + // AT timeoutand ignore a failure to configure power saving if + // the module type is SARA-U201 and the power saving state is enabled + if ((pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_SARA_U201) && + (uartPowerSavingMode != U_CELL_PWR_PSV_MODE_DISABLED)) { + atTimeoutMs = U_CELL_PWR_SARA_U201_SHORT_AT_TIMEOUT_MS; + } + + if ((!moduleConfigureOne(atHandle, buffer, 1, atTimeoutMs) && + !((pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_SARA_U201) && + (uartPowerSavingMode != U_CELL_PWR_PSV_MODE_DISABLED))) && uAtClientWakeUpHandlerIsSet(atHandle) && !returningFromSleep) { // If AT+UPSV returns error and we're not already returning @@ -1059,6 +1084,14 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, uAtClientSetWakeUpHandler(atHandle, NULL, NULL, 0); uPortLog("U_CELL_PWR: power saving not supported.\n"); } + // If we have successfully enabled UART power saving on SARA-U201 + // it is _immediately_ in UART power-saving mode, it doesn't wait + // for 6 seconds, so we have to tell the AT client that + if ((pInstance->pModule->moduleType == U_CELL_MODULE_TYPE_SARA_U201) && + (uartPowerSavingMode != U_CELL_PWR_PSV_MODE_DISABLED)) { + uAtClientWakeUpHandlerForce(atHandle); + } + // Now tell the AT Client that it should control the // DTR pin, if relevant if (!returningFromSleep && (uartPowerSavingMode == U_CELL_PWR_PSV_MODE_DTR)) { @@ -1074,7 +1107,7 @@ static int32_t moduleConfigure(uCellPrivateInstance_t *pInstance, if (U_CELL_PRIVATE_HAS(pInstance->pModule, U_CELL_PRIVATE_FEATURE_DEEP_SLEEP_URC)) { success = moduleConfigureOne(atHandle, "AT+UPSMR=1", - U_CELL_PWR_CONFIGURATION_COMMAND_TRIES); + U_CELL_PWR_CONFIGURATION_COMMAND_TRIES, -1); if (success && !returningFromSleep) { // Add the URC handler if it wasn't there before uAtClientSetUrcHandler(pInstance->atHandle, "+UUPSMR:", diff --git a/common/at_client/api/u_at_client.h b/common/at_client/api/u_at_client.h index b649e604..40c3306e 100644 --- a/common/at_client/api/u_at_client.h +++ b/common/at_client/api/u_at_client.h @@ -1759,6 +1759,14 @@ int32_t uAtClientSetWakeUpHandler(uAtClientHandle_t atHandle, */ bool uAtClientWakeUpHandlerIsSet(const uAtClientHandle_t atHandle); +/** Force the timeout of an AT wake-up handler set using + * uAtClientSetWakeUpHandler(). + * + * @param atHandle the handle of the AT client. + * @return zero on success else negative error code. + */ +int32_t uAtClientWakeUpHandlerForce(const uAtClientHandle_t atHandle); + /** Get the current wake-up handler function and parameters. * * @param atHandle the handle of the AT client. diff --git a/common/at_client/src/u_at_client.c b/common/at_client/src/u_at_client.c index 4db6e2f9..ba2427f8 100644 --- a/common/at_client/src/u_at_client.c +++ b/common/at_client/src/u_at_client.c @@ -448,6 +448,7 @@ typedef struct uAtClientInstance_t { uTimeoutStart_t lastResponseStop; /** The time the last response ended in milliseconds. */ int32_t lockTimeMs; /** The time when the stream was locked. */ uTimeoutStart_t lastTxTime; /** The time when the last transmit activity was carried out. */ + bool forceNextWakeUp; /** True if the application requires the AT client to force a module wake-up next time. */ size_t urcMaxStringLength; /** The longest URC string to monitor for. */ size_t maxRespLength; /** The max length of OK, (CME) (CMS) ERROR and URCs. */ bool delimiterRequired; /** Is a delimiter to be inserted before the next parameter or not. */ @@ -2264,9 +2265,12 @@ static size_t write(uAtClientInstance_t *pClient, (pClient->error == U_ERROR_COMMON_SUCCESS)) { lengthToWrite = length - (pData - pDataStart); if ((pClient->pWakeUp != NULL) && - uTimeoutExpiredMs(pClient->lastTxTime, - pClient->pWakeUp->inactivityTimeoutMs) && + (uTimeoutExpiredMs(pClient->lastTxTime, + pClient->pWakeUp->inactivityTimeoutMs) || + (pClient->forceNextWakeUp)) && (uPortMutexTryLock(pClient->pWakeUp->inWakeUpHandlerMutex, 0) == 0)) { + // Reset the force next flag + pClient->forceNextWakeUp = false; // We have a wake-up handler, the inactivity timeout // has expired and we've managed to lock the wake-up // handler mutex (if we aren't able to lock the wake-up @@ -4516,6 +4520,23 @@ bool uAtClientWakeUpHandlerIsSet(const uAtClientHandle_t atHandle) return ((const uAtClientInstance_t *) atHandle)->pWakeUp != NULL; } +// Force the next timeout of the AT wake-up handler. +int32_t uAtClientWakeUpHandlerForce(const uAtClientHandle_t atHandle) +{ + int32_t errorCode = (int32_t) U_ERROR_COMMON_NOT_SUPPORTED; + uAtClientInstance_t *pClient = (uAtClientInstance_t *) atHandle; + + U_AT_CLIENT_LOCK_CLIENT_MUTEX(pClient); + + if (pClient->pWakeUp != NULL) { + pClient->forceNextWakeUp = true; + } + + U_AT_CLIENT_UNLOCK_CLIENT_MUTEX(pClient); + + return errorCode; +} + // Get the current wake-up handler function and parameters. void uAtClientGetWakeUpHandler(uAtClientHandle_t atHandle, int32_t (**ppHandler) (uAtClientHandle_t, diff --git a/port/platform/common/automation/DATABASE.md b/port/platform/common/automation/DATABASE.md index 49fdd2ff..0f175e78 100644 --- a/port/platform/common/automation/DATABASE.md +++ b/port/platform/common/automation/DATABASE.md @@ -71,7 +71,7 @@ The table below defines the instances of test hardware available on the `ubxlib` | 29 | HPG C214 board (NINA-W1), live network | ESP32 | | ESP-IDF | | LENA_R8 M9 | port device network sock cell security mqtt_client gnss location || U_CFG_PPP_ENABLE U_HTTP_CLIENT_DISABLE_TEST U_CELL_GPIO_DISABLE_TEST U_MQTT_CLIENT_TEST_NO_NULL_SEND U_CFG_TEST_GNSS_POWER_SAVING_NOT_SUPPORTED U_GNSS_MGA_TEST_ASSIST_NOW_AUTONOMOUS_NOT_SUPPORTED U_CELL_CFG_TEST_USE_FIXED_TIME_SECONDS U_CFG_MONITOR_DTR_RTS_OFF U_CELL_TEST_NO_INVALID_APN U_CELL_TEST_CFG_BANDMASK1=0x0000000000080084ULL U_CELL_NET_TEST_RAT=U_CELL_NET_RAT_LTE U_CFG_APP_PIN_CELL_ENABLE_POWER=-1 U_CFG_APP_PIN_CELL_PWR_ON=0x801a U_CFG_APP_PIN_CELL_RESET=33 U_CELL_RESET_PIN_DRIVE_MODE=U_PORT_GPIO_DRIVE_MODE_NORMAL U_CFG_APP_PIN_CELL_VINT=0x8025 U_CFG_APP_PIN_CELL_DTR=15 U_CFG_APP_PIN_CELL_TXD=25 U_CFG_APP_PIN_CELL_RXD=34 U_CFG_APP_PIN_CELL_RTS=27 U_CFG_APP_PIN_CELL_CTS=36 U_CFG_APP_GNSS_I2C=0 U_GNSS_TEST_I2C_ADDRESS_EXTRA=0x43 U_CFG_APP_CELL_PIN_GNSS_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_DATA_READY=-1 U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_DEBUG_UTILS_DUMP_THREADS | | 30 | STM32F407 Discovery, NORA-W3, SARA-R520 EVK| STM32F4 | | STM32Cube | | SARA_R52 M10 NORA_W36 | port device network sock cell ble wifi short_range gnss security mqtt_client http_client location | cell gnss short_range short_range_gen2 | CMSIS_V2 HSE_VALUE=8000000U U_CFG_TEST_GNSS_POWER_SAVING_NOT_SUPPORTED U_CFG_APP_GNSS_UART=-1 U_CFG_LOC_TEST_CHANGE_SYSTEM_TYPES_DISABLE U_CFG_APP_PIN_C030_ENABLE_3V3=-1 U_CFG_APP_PIN_CELL_RESET=-1 U_CFG_APP_CELL_UART=2 U_CFG_APP_PIN_CELL_TXD=0x03 U_CFG_APP_PIN_CELL_RXD=0x02 U_CFG_APP_PIN_CELL_RTS=-1 U_CFG_APP_PIN_CELL_CTS=-1 U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_TEST_UART_A=-1 U_BLE_TEST_CFG_REMOTE_SPS_CENTRAL=2462ABB6CC42p U_BLE_TEST_CFG_REMOTE_SPS_PERIPHERAL=2462ABB6EAC6p U_CFG_APP_SHORT_RANGE_ROLE=3 U_CFG_APP_SHORT_RANGE_UART2=6 U_CFG_APP_PIN_SHORT_RANGE_TXD2=0x26 U_CFG_APP_PIN_SHORT_RANGE_RXD2=0x27 U_DEBUG_UTILS_DUMP_THREADS U_CFG_TEST_GNSS_TRANSPORT_AT_DISABLE | | 31 | STM32F7, Nucleo-F767ZI, LARA-R6, live net | STM32 | nucleo_f767zi | Zephyr | | LARA_R6 | port device network sock cell security mqtt_client http_client location || U_ZEPHYR_PORT_UART_ASYNC U_CFG_TEST_DISABLE_MUX U_CFG_TEST_CELL_PWR_DISABLE U_CELL_TEST_CFG_APN=iot.1nce.net U_CELL_CFG_TEST_USE_FIXED_TIME_SECONDS U_CELL_TEST_NO_INVALID_APN U_CELL_TEST_CFG_BANDMASK1=0x0000000000080084ULL U_CELL_NET_TEST_RAT=U_CELL_NET_RAT_LTE U_CELL_TEST_CFG_MNO_PROFILE=90 U_DEBUG_UTILS_DUMP_THREADS | -| 32 | STM32U5, Nucleo-U575ZI-q | STM32 | nucleo_u575zi_q | Zephyr | | SARA_U201 M9 ODIN_W2 | port device network sock cell ble wifi short_range gnss security http_client location ubx_protocol spartn || U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CELL_NET_TEST_RAT=U_CELL_NET_RAT_GSM_GPRS_EGPRS U_CELL_TEST_CFG_APN=iot.1nce.net U_CELL_CFG_APN_DEFAULT=iot.1nce.net U_CFG_TEST_TRANSPORT_SECURITY_DISABLE U_CELL_CFG_TEST_USE_FIXED_TIME_SECONDS U_CELL_TEST_NO_INVALID_APN U_CFG_CELL_DISABLE_UART_POWER_SAVING U_CFG_APP_GNSS_I2C=1 U_CFG_APP_I2C_MAX_SEGMENT_SIZE=255 U_GNSS_MGA_TEST_HAS_FLASH U_CFG_TEST_GNSS_POWER_SAVING_NOT_SUPPORTED U_GNSS_MGA_TEST_ASSIST_NOW_AUTONOMOUS_NOT_SUPPORTED U_CFG_TEST_PIN_GNSS_RESET_N=0x5D U_CFG_APP_SHORT_RANGE_UART=3 U_CFG_TEST_BLE_DISABLE_SPS U_CFG_TEST_UART_A=-1 U_DEBUG_UTILS_DUMP_THREADS | +| 32 | STM32U5, Nucleo-U575ZI-q | STM32 | nucleo_u575zi_q | Zephyr | | SARA_U201 M9 ODIN_W2 | port device network sock cell ble wifi short_range gnss security http_client location ubx_protocol spartn || U_CELL_NET_TEST_RAT=U_CELL_NET_RAT_GSM_GPRS_EGPRS U_CELL_TEST_CFG_APN=iot.1nce.net U_CELL_CFG_APN_DEFAULT=iot.1nce.net U_CFG_TEST_TRANSPORT_SECURITY_DISABLE U_CELL_CFG_TEST_USE_FIXED_TIME_SECONDS U_CELL_TEST_NO_INVALID_APN U_CFG_APP_GNSS_I2C=1 U_CFG_APP_I2C_MAX_SEGMENT_SIZE=255 U_GNSS_MGA_TEST_HAS_FLASH U_CFG_TEST_GNSS_POWER_SAVING_NOT_SUPPORTED U_GNSS_MGA_TEST_ASSIST_NOW_AUTONOMOUS_NOT_SUPPORTED U_CFG_TEST_PIN_GNSS_RESET_N=0x5D U_CFG_APP_SHORT_RANGE_UART=3 U_CFG_TEST_BLE_DISABLE_SPS U_CFG_TEST_UART_A=-1 U_DEBUG_UTILS_DUMP_THREADS | | 33 | ESP32-DevKitC + EVK, live network | ESP32 | | ESP-IDF | | | port device network sock cell security mqtt_client http_client || U_CFG_TEST_UART_A=-1 U_CFG_TEST_PIN_A=-1 U_CFG_TEST_PIN_B=-1 U_CFG_TEST_PIN_C=-1 U_CFG_APP_PIN_CELL_VINT=-1 U_CFG_APP_PIN_CELL_ENABLE_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_POWER=-1 U_CFG_APP_CELL_PIN_GNSS_DATA_READY=-1 U_DEBUG_UTILS_DUMP_THREADS | Notes: