Long Pressing button should go into Deep Sleep and waking simultaneously.

Hello guys,
I’m working on my homemade project, I’m using GNSS (quectel L86) and light sensor (Analog) and successfully reading their values on Serial monitor, I’m running this setup on a 7.4V Lipo battery and it’s draining quickly,But i only wanted my setup to work on certain time (typically when i pressed a button, connected to one of the pin on my Arduino), i will attach a tactile button to it, and when i long press it should wake up and when i long pressed that button it should do into deep sleep. (seems i should work on interrupts)

Please help me in to this

Here is my code…

#include <TinyGPS++.h>


// The TinyGPS++ object
TinyGPSPlus gps;

const int analogPin = A0;
int sensorValue = 0;
void setup()
{
  Serial.begin(9600);
  Serial2.begin(9600);

  Serial.println(F("FullExample.ino"));
  Serial.println(F("An extensive example of many interesting TinyGPS++ features"));
  Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion());
  Serial.println(F("by Mikal Hart"));
  Serial.println();
  Serial.println(F("Sats HDOP Latitude   Longitude   Fix  Date       Time     Date Alt    Course Speed Card  Distance Course Card  Chars Sentences Checksum"));
  Serial.println(F("          (deg)      (deg)       Age                      Age  (m)    --- from GPS ----  ---- to London  ----  RX    RX        Fail"));
  Serial.println(F("---------------------------------------------------------------------------------------------------------------------------------------"));
}

void loop()
{
  static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002;

  printInt(gps.satellites.value(), gps.satellites.isValid(), 5);
  printInt(gps.hdop.value(), gps.hdop.isValid(), 5);
  printFloat(gps.location.lat(), gps.location.isValid(), 11, 6);
  printFloat(gps.location.lng(), gps.location.isValid(), 12, 6);
  printInt(gps.location.age(), gps.location.isValid(), 5);
  printDateTime(gps.date, gps.time);
  printFloat(gps.altitude.meters(), gps.altitude.isValid(), 7, 2);
  printFloat(gps.course.deg(), gps.course.isValid(), 7, 2);
  printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2);
  printStr(gps.course.isValid() ? TinyGPSPlus::cardinal(gps.course.value()) : "*** ", 6);

  unsigned long distanceKmToLondon =
    (unsigned long)TinyGPSPlus::distanceBetween(
      gps.location.lat(),
      gps.location.lng(),
      LONDON_LAT,
      LONDON_LON) / 1000;
  printInt(distanceKmToLondon, gps.location.isValid(), 9);

  double courseToLondon =
    TinyGPSPlus::courseTo(
      gps.location.lat(),
      gps.location.lng(),
      LONDON_LAT,
      LONDON_LON);

  printFloat(courseToLondon, gps.location.isValid(), 7, 2);

  const char *cardinalToLondon = TinyGPSPlus::cardinal(courseToLondon);

  printStr(gps.location.isValid() ? cardinalToLondon : "*** ", 6);

  printInt(gps.charsProcessed(), true, 6);
  printInt(gps.sentencesWithFix(), true, 10);
  printInt(gps.failedChecksum(), true, 9);


  //Printing Sensor Value
  sensorValue = analogRead(analogPin);
  Serial.print("Sensor");
  Serial.print(sensorValue);

  Serial.println();

  smartDelay(1000);

  if (millis() > 5000 && gps.charsProcessed() < 10)
    Serial.println(F("No GPS data received: check wiring"));
}

// This custom version of delay() ensures that the gps object
// is being "fed".
static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do
  {
    while (Serial2.available())
      gps.encode(Serial2.read());
  } while (millis() - start < ms);
}

static void printFloat(float val, bool valid, int len, int prec)
{
  if (!valid)
  {
    while (len-- > 1)
      Serial.print('*');
    Serial.print(' ');
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i = flen; i < len; ++i)
      Serial.print(' ');
  }
  smartDelay(0);
}

static void printInt(unsigned long val, bool valid, int len)
{
  char sz[32] = "*****************";
  if (valid)
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i = strlen(sz); i < len; ++i)
    sz[i] = ' ';
  if (len > 0)
    sz[len - 1] = ' ';
  Serial.print(sz);
  smartDelay(0);
}

static void printDateTime(TinyGPSDate &d, TinyGPSTime &t)
{
  if (!d.isValid())
  {
    Serial.print(F("********** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
    Serial.print(sz);
  }

  if (!t.isValid())
  {
    Serial.print(F("******** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
    Serial.print(sz);
  }

  printInt(d.age(), d.isValid(), 5);
  smartDelay(0);
}

static void printStr(const char *str, int len)
{
  int slen = strlen(str);
  for (int i = 0; i < len; ++i)
    Serial.print(i < slen ? str[i] : ' ');
  smartDelay(0);
}

I believe that you can set up so that any button press causes an interrupt that wakes up the Arduino. If it is not a long press, then the Arduino may go back to sleep. The Arduino is going to have to be awake to decide between short press and long press, or you are going to have to use additional hardware.

You may get more "bang for the buck" by turning off things that you don't need (such as some timers) and/or lowering the clock frequency.

I don't see how sleeping, turning off unused peripherals, or lowering the clock frequency is going to have any affect on your GPS module. Do you have a way to switch power to the GPS module?

vaj4088: The Arduino is going to have to be awake to decide between short press and long press, or you are going to have to use additional hardware.

not necessarily

you don't need the interrupt to go to sleep - deal with the button in the main loop - and use the interrupt only for waking up. Program it so that the sleep happens only after the user releases the button after the long press

have 3 variables, one to record the time in startMillis when button gets pressed and two boolean to remember that the button has been pressed buttonPressed (initialized to false) and a needToSleep also initialized to false.

at the beginning of the loop() do something like this.

if the button is down (through digitalRead) if buttonPressed is true it means you were already having the button down, so compare millis() with startMillis if it's larger than "a long press" then set needToSleep = true; and do a beep or something else it is a new press, so set buttonPressed to true and set startMillis to millis(); else set buttonPressed to false

when you press the button for long enough, that will at some point set the needToSleep to true. You want to wait until the user actually releases the button to perform the sleep, so in the loop() check for this

if needToSleep is true AND buttonPressed is false set needToSleep to false prepare the micropossor for sleep attach the interrupt sleep your Arduino detach the Interrupt

this way the button has been released when you actually put your Arduino to sleep and only a new press will wake it up (and then you continue the code and detach the interrupt and are ready to go again)

Note that there is a common problem if the interrupt occurs after attachInterrupt but before sleep_cpu() so read carefully this page.

hope this helps

Among other things, mudassir9999 wrote:

when i long press it should wake up

I guess I missed how this is done with the Arduino asleep. :(

vaj4088: Among other things, mudassir9999 wrote:

---- when i long press it should wake up

I guess I missed how this is done with the Arduino asleep. :(

right I misread that indeed. I read it as long press to sleep and any press to wake up.... with my approach any press long enough to establish the ISR call will be enough.. so not long in user clock time

(that being said in my approach a long press will also wake it up. And as the OP did not explain what to do in case of short press while asleep... ;) )

Agreed.