diff --git a/examples/Example22_PowerOff/Example22_PowerOff.ino b/examples/Example22_PowerOff/Example22_PowerOff.ino new file mode 100644 index 0000000..90ae355 --- /dev/null +++ b/examples/Example22_PowerOff/Example22_PowerOff.ino @@ -0,0 +1,88 @@ +/* + Powering off a ublox GPS module + By: bjorn + unsurv.org + Date: July 20th, 2020 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows you how to turn off the ublox module to lower the power consumption. + There are two functions: one just specifies a duration in milliseconds the other also specifies a pin on the GPS device to wake it up with. + By driving a voltage from LOW to HIGH or HIGH to LOW on the chosen module pin you wake the device back up. + Note: Doing so on the INT0 pin when using the regular powerOff(durationInMs) function will wake the device anyway. (tested on SAM-M8Q) + Note: While powered off, you should not query the device for data or it might wake up. This can be used to wake the device but is not recommended. + Works best when also putting your microcontroller to sleep. + + Feel like supporting open source hardware? + Buy a board from SparkFun! + ZED-F9P RTK2: https://www.sparkfun.com/products/15136 + NEO-M8P RTK: https://www.sparkfun.com/products/15005 + SAM-M8Q: https://www.sparkfun.com/products/15106 + + Hardware Connections: + Plug a Qwiic cable into the GPS and a BlackBoard. + To force the device to wake up you need to connect to a pin (for example INT0) seperately on the module. + If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425) + Open the serial monitor at 115200 baud to see the output +*/ + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +// define a digital pin capable of driving HIGH and LOW +#define WAKEUP_PIN 5 + +// Possible GNSS interrupt pins for powerOffWithInterrupt are: +// VAL_RXM_PMREQ_WAKEUPSOURCE_UARTRX = uartrx +// VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT0 = extint0 (default) +// VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT1 = extint1 +// VAL_RXM_PMREQ_WAKEUPSOURCE_SPICS = spics +// These values can be or'd (|) together to enable interrupts on multiple pins + +void wakeUp() { + + Serial.print("-- waking up module via pin " + String(WAKEUP_PIN)); + Serial.println(" on your microcontroller --"); + + digitalWrite(WAKEUP_PIN, LOW); + delay(1000); + digitalWrite(WAKEUP_PIN, HIGH); + delay(1000); + digitalWrite(WAKEUP_PIN, LOW); +} + + +void setup() { + + pinMode(WAKEUP_PIN, OUTPUT); + digitalWrite(WAKEUP_PIN, LOW); + + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + Wire.begin(); + + //myGPS.enableDebugging(); // Enable debug messages + + if (myGPS.begin() == false) //Connect to the Ublox module using Wire port + { + Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing.")); + while (1); + } + + // Powering off for 20s, you should see the power consumption drop. + Serial.println("-- Powering off module for 20s --"); + + myGPS.powerOff(20000); + //myGPS.powerOffWithInterrupt(20000, VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT0); + + delay(10000); + + // After 10 seconds wake the device via the specified pin on your microcontroller and module. + wakeUp(); +} + +void loop() { + //Do nothing +} diff --git a/keywords.txt b/keywords.txt index 9a9d8af..0b5d1d5 100644 --- a/keywords.txt +++ b/keywords.txt @@ -139,6 +139,8 @@ setDynamicModel KEYWORD2 getDynamicModel KEYWORD2 powerSaveMode KEYWORD2 getPowerSaveMode KEYWORD2 +powerOff KEYWORD2 +powerOffWithInterrupt KEYWORD2 configureMessage KEYWORD2 enableMessage KEYWORD2 diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp index a1f47a3..4a5857e 100644 --- a/src/SparkFun_Ublox_Arduino_Library.cpp +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -2569,6 +2569,129 @@ uint8_t SFE_UBLOX_GPS::getPowerSaveMode(uint16_t maxWait) return (payloadCfg[1]); // Return the low power mode } +// Powers off the GPS device for a given duration to reduce power consumption. +// NOTE: Querying the device before the duration is complete, for example by "getLatitude()" will wake it up! +// Returns true if command has not been not acknowledged. +// Returns false if command has not been acknowledged or maxWait = 0. +boolean SFE_UBLOX_GPS::powerOff(uint32_t durationInMs, uint16_t maxWait) +{ + // use durationInMs = 0 for infinite duration + if (_printDebug == true) + { + _debugSerial->print(F("Powering off for ")); + _debugSerial->print(durationInMs); + _debugSerial->println(" ms"); + } + + // Power off device using UBX-RXM-PMREQ + packetCfg.cls = UBX_CLASS_RXM; // 0x02 + packetCfg.id = UBX_RXM_PMREQ; // 0x41 + packetCfg.len = 8; + packetCfg.startingSpot = 0; + + // duration + // big endian to little endian, switch byte order + payloadCfg[0] = (durationInMs >> (8*0)) & 0xff; + payloadCfg[1] = (durationInMs >> (8*1)) & 0xff; + payloadCfg[2] = (durationInMs >> (8*2)) & 0xff; + payloadCfg[3] = (durationInMs >> (8*3)) & 0xff; + + payloadCfg[4] = 0x02; //Flags : set the backup bit + payloadCfg[5] = 0x00; //Flags + payloadCfg[6] = 0x00; //Flags + payloadCfg[7] = 0x00; //Flags + + if (maxWait != 0) + { + // check for "not acknowledged" command + return (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_COMMAND_NACK); + } + else + { + sendCommand(&packetCfg, maxWait); + return false; // can't tell if command not acknowledged if maxWait = 0 + } +} + +// Powers off the GPS device for a given duration to reduce power consumption. +// While powered off it can be woken up by creating a falling or rising voltage edge on the specified pin. +// NOTE: The GPS seems to be sensitve to signals on the pins while powered off. Works best when Microcontroller is in deepsleep. +// NOTE: Querying the device before the duration is complete, for example by "getLatitude()" will wake it up! +// Returns true if command has not been not acknowledged. +// Returns false if command has not been acknowledged or maxWait = 0. +boolean SFE_UBLOX_GPS::powerOffWithInterrupt(uint32_t durationInMs, uint32_t wakeupSources, boolean forceWhileUsb, uint16_t maxWait) +{ + // use durationInMs = 0 for infinite duration + if (_printDebug == true) + { + _debugSerial->print(F("Powering off for ")); + _debugSerial->print(durationInMs); + _debugSerial->println(" ms"); + } + + // Power off device using UBX-RXM-PMREQ + packetCfg.cls = UBX_CLASS_RXM; // 0x02 + packetCfg.id = UBX_RXM_PMREQ; // 0x41 + packetCfg.len = 16; + packetCfg.startingSpot = 0; + + payloadCfg[0] = 0x00; // message version + + // bytes 1-3 are reserved - and must be set to zero + payloadCfg[1] = 0x00; + payloadCfg[2] = 0x00; + payloadCfg[3] = 0x00; + + // duration + // big endian to little endian, switch byte order + payloadCfg[4] = (durationInMs >> (8*0)) & 0xff; + payloadCfg[5] = (durationInMs >> (8*1)) & 0xff; + payloadCfg[6] = (durationInMs >> (8*2)) & 0xff; + payloadCfg[7] = (durationInMs >> (8*3)) & 0xff; + + // flags + + // disables USB interface when powering off, defaults to true + if (forceWhileUsb) + { + payloadCfg[8] = 0x06; // force | backup + } + else + { + payloadCfg[8] = 0x02; // backup only (leave the force bit clear - module will stay on if USB is connected) + } + + payloadCfg[9] = 0x00; + payloadCfg[10] = 0x00; + payloadCfg[11] = 0x00; + + // wakeUpSources + + // wakeupPin mapping, defaults to VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT0 + + // Possible values are: + // VAL_RXM_PMREQ_WAKEUPSOURCE_UARTRX + // VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT0 + // VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT1 + // VAL_RXM_PMREQ_WAKEUPSOURCE_SPICS + + payloadCfg[12] = (wakeupSources >> (8*0)) & 0xff; + payloadCfg[13] = (wakeupSources >> (8*1)) & 0xff; + payloadCfg[14] = (wakeupSources >> (8*2)) & 0xff; + payloadCfg[15] = (wakeupSources >> (8*3)) & 0xff; + + if (maxWait != 0) + { + // check for "not acknowledged" command + return (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_COMMAND_NACK); + } + else + { + sendCommand(&packetCfg, maxWait); + return false; // can't tell if command not acknowledged if maxWait = 0 + } +} + //Change the dynamic platform model using UBX-CFG-NAV5 //Possible values are: //PORTABLE,STATIONARY,PEDESTRIAN,AUTOMOTIVE,SEA, diff --git a/src/SparkFun_Ublox_Arduino_Library.h b/src/SparkFun_Ublox_Arduino_Library.h index ab28e22..96c0e0a 100644 --- a/src/SparkFun_Ublox_Arduino_Library.h +++ b/src/SparkFun_Ublox_Arduino_Library.h @@ -407,6 +407,12 @@ const uint32_t VAL_CFG_SUBSEC_ANTCONF = 0x00000400; // antConf - antenna config const uint32_t VAL_CFG_SUBSEC_LOGCONF = 0x00000800; // logConf - logging configuration const uint32_t VAL_CFG_SUBSEC_FTSCONF = 0x00001000; // ftsConf - FTS configuration (FTS products only) +// Bitfield wakeupSources for UBX_RXM_PMREQ +const uint32_t VAL_RXM_PMREQ_WAKEUPSOURCE_UARTRX = 0x00000008; // uartrx +const uint32_t VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT0 = 0x00000020; // extint0 +const uint32_t VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT1 = 0x00000040; // extint1 +const uint32_t VAL_RXM_PMREQ_WAKEUPSOURCE_SPICS = 0x00000080; // spics + enum dynModel // Possible values for the dynamic platform model, which provide more accuract position output for the situation. Description extracted from ZED-F9P Integration Manual { DYN_MODEL_PORTABLE = 0, //Applications with low acceleration, e.g. portable devices. Suitable for most situations. @@ -641,6 +647,8 @@ class SFE_UBLOX_GPS boolean powerSaveMode(bool power_save = true, uint16_t maxWait = 1100); uint8_t getPowerSaveMode(uint16_t maxWait = 1100); // Returns 255 if the sendCommand fails + boolean powerOff(uint32_t durationInMs, uint16_t maxWait = 1100); + boolean powerOffWithInterrupt(uint32_t durationInMs, uint32_t wakeupSources = VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT0, boolean forceWhileUsb = true, uint16_t maxWait = 1100); //Change the dynamic platform model using UBX-CFG-NAV5 boolean setDynamicModel(dynModel newDynamicModel = DYN_MODEL_PORTABLE, uint16_t maxWait = 1100);