Wake-up on timer or button interupt

Dear Arduino people,

I'm making a project that requires my Arduino to stay in low power mode for quit a long time. It wakes up every 24 hours and then draws a picture on an E-ink screen. I would however, like to be able to manually change the picture (and therefor wake up the Arduino) by pressing a button. Is there a way to create a hybrid, time/button interupt so that the Arduino will change the picture when the 24 hours are over OR when I press the button?

Thanks so much in advance!

Is there any point in putting the unknown controller in sleep while the screen draws current all the time?

An e-paper display does not draw current all the time. I have one e-paper display that my wife sues as a name tag. The image on the screen is held even when power is off.

For better wake sleep thingy you may need to look at another Microcontroller.

On an ESP32 there is a way to create a hybrid sleep interrupt event(s).

Are you sure? There must be a smart way to do this?
I wanna use the Arduino pro mini, since (with some modification) it has the lowest current draw and isn't over powered for the job.

Does it have enough storage for the images?

Wow. New technology to me. Do You have a data sheet link easily available?

A search on "waveshare e-paper" should get you started.

Thanks! I did look it up. Ultra low power consumption, sleeping current lower than 5mA. Low enough for the OP?

The E-paper might need a shield. What about shield power and sleep?

@ Idahowalker
Wow! I've learned something new!

I'm gonna use a transistor to disconnect the screen from the micro controller's power. System uses very little even when in operation.

But back to topic.
Is there a way to fix the button/timer code using an atmega 328P (Arduino)?

Yes. The images are about 322 KB each. I use a 32GB micro SD card ATM. Gonna switch to 2GB.

Check this forum. You can see the screen I use (and Google the datasheet on Goodisplay's website).

If in fact it requires less than 20 mA, you can generally simply power it directly from an Arduino pin.

Ok thank you, but do you have An answer on my original question?

How will you isolate the control lines so the e-paper display doesn't run on parasitic power from the Arduino?

The original question considered, I would first try periodic wakes and sample the input that changes the screen images. The duty cycle of a periodic wake can be very low, so the average power is close to deep sleep. So, for a button you might have to wake every 100ms or less, but your awake routine could be very short like 10-100us because it's so simple. Or possibly you could latch the switch closure with an input interrupt, but I haven't looked into whether that's possible... then wake every 500ms or so and handle the interrupt while woke.

You must already have the sleep() infrastructure in whatever code you have. Just wire a switch to INT0 or INT0 and set an interrupt on a low-level just before you go to sleep. In the ISR to service the INTx interrupt check the state of the switch input; if low, presume it was the source of the wake and set a flag so the rest of the code knows this. When you're done whatever you do on wakeup, clear this flag and go back to sleep again.

Thank you for your response.
I don't quit know how I could start. This is the code I am using ATM. It has the sleep function in it:

#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <avr/sleep.h>
#include <avr/wdt.h>


#if defined(ESP32)

// has support for FAT32 support with long filenames
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#define SdFile File
#define seekSet seek

#else

// include SdFat for FAT32 support with long filenames; available through Library Manager
#include <SdFat.h>
SdFat SD;

#endif

// select the display driver class (only one) for your  panel
//#define GxEPD2_DRIVER_CLASS GxEPD2_154     // GDEP015OC1  200x200, no longer available
//#define GxEPD2_DRIVER_CLASS GxEPD2_154_D67 // GDEH0154D67 200x200
//#define GxEPD2_DRIVER_CLASS GxEPD2_154_T8  // GDEW0154T8  152x152
//#define GxEPD2_DRIVER_CLASS GxEPD2_154_M09 // GDEW0154M09 200x200
//#define GxEPD2_DRIVER_CLASS GxEPD2_154_M10 // GDEW0154M10 152x152
//#define GxEPD2_DRIVER_CLASS GxEPD2_213     // GDE0213B1   128x250, phased out
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_B72 // GDEH0213B72 128x250
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_B73 // GDEH0213B73 128x250
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_flex // GDEW0213I5F 104x212
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_M21 // GDEW0213M21 104x212
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_T5D // GDEW0213T5D 104x212
//#define GxEPD2_DRIVER_CLASS GxEPD2_290     // GDEH029A1   128x296
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_T5  // GDEW029T5   128x296
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_T5D // GDEW029T5D  128x296
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_M06 // GDEW029M06  128x296
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_T94 // GDEM029T94  128x296
//#define GxEPD2_DRIVER_CLASS GxEPD2_260     // GDEW026T0   152x296
//#define GxEPD2_DRIVER_CLASS GxEPD2_270     // GDEW027W3   176x264
//#define GxEPD2_DRIVER_CLASS GxEPD2_371     // GDEW0371W7  240x416
//#define GxEPD2_DRIVER_CLASS GxEPD2_420     // GDEW042T2   400x300
//#define GxEPD2_DRIVER_CLASS GxEPD2_420_M01 // GDEW042M01  400x300
//#define GxEPD2_DRIVER_CLASS GxEPD2_583     // GDEW0583T7  600x448
//#define GxEPD2_DRIVER_CLASS GxEPD2_583_T8  // GDEW0583T8  648x480
//#define GxEPD2_DRIVER_CLASS GxEPD2_750     // GDEW075T8   640x384
//#define GxEPD2_DRIVER_CLASS GxEPD2_750_T7  // GDEW075T7   800x480

// 3-color e-papers
//#define GxEPD2_DRIVER_CLASS GxEPD2_154c     // GDEW0154Z04 200x200, no longer available
//#define GxEPD2_DRIVER_CLASS GxEPD2_154_Z90c // GDEH0154Z90 200x200
//#define GxEPD2_DRIVER_CLASS GxEPD2_213c     // GDEW0213Z16 104x212
//#define GxEPD2_DRIVER_CLASS GxEPD2_213_Z19c // GDEW0213Z19 104x212
//#define GxEPD2_DRIVER_CLASS GxEPD2_290c     // GDEW029Z10  128x296
//#define GxEPD2_DRIVER_CLASS GxEPD2_290_Z13c // GDEH029Z13  128x296
//#define GxEPD2_DRIVER_CLASS GxEPD2_270c     // GDEW027C44  176x264
#define GxEPD2_DRIVER_CLASS GxEPD2_420c     // GDEW042Z15  400x300    The 4.2 inch color screen I use.
//#define GxEPD2_DRIVER_CLASS GxEPD2_583c     // GDEW0583Z21 600x448
//#define GxEPD2_DRIVER_CLASS GxEPD2_750c     // GDEW075Z09  600x384
//#define GxEPD2_DRIVER_CLASS GxEPD2_750c_Z08 // GDEW075Z08  800x480
//#define GxEPD2_DRIVER_CLASS GxEPD2_750c_Z90 // GDEH075Z90  880x528

// 7-color e-paper
//#define GxEPD2_DRIVER_CLASS GxEPD2_565c // Waveshare 5.65" 7-color (3C graphics)

#if defined(__AVR)
#define SD_CS 6  // adapt to your wiring
#define EPD_CS SS // adapt to your wiring
GxEPD2_DRIVER_CLASS display(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7);
#endif
// SD card can be connected as:
//sck 13, miso 12, mosi 11, cs (6 in this case)
// non-AVR board can also be used with GxEPD2 base display classes, e.g. for SD bitmap drawing
// uncomment your display for your board in the following included header file and adapt to your mapping

#include "GxEPD2_SD_AVR_boards_added.h"
// function declaration with default parameter

void drawBitmapFromSD(const char *filename, int16_t x, int16_t y, bool with_color = true);
static const uint16_t input_buffer_pixels = 20; // may affect performance

static const uint16_t max_row_width = 640; // for up to 7.5" display
static const uint16_t max_palette_pixels = 256; // for depth <= 8

uint8_t input_buffer[3 * input_buffer_pixels]; // up to depth 24
uint8_t output_row_mono_buffer[max_row_width / 8]; // buffer for at least one row of b/w bits
uint8_t output_row_color_buffer[max_row_width / 8]; // buffer for at least one row of color bits
uint8_t mono_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 b/w
uint8_t color_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 c/w


void setup()
{
  Serial.begin(115200);
  Serial.println();
  delay(10);
  Serial.println(F("setup"));

  display.init(115200);

  Serial.print(F("Initializing SD card..."));
  if (!SD.begin(SD_CS))
  {
    Serial.println(F("SD failed!"));
    return;
  }
  Serial.println(F("SD OK!"));
  Serial.println(F("entering loop"));
}


void loop(void)
{
  //drawBitmaps_200x200(); //I don't want to draw such small pictures
  drawBitmaps_other();
}

void myWatchdogEnable(const byte interval) //code to enable watchdog (for low power)
{
  MCUSR = 0;                          // reset various flags
  WDTCSR |= 0b00011000;               // see docs, set WDCE, WDE
  WDTCSR =  0b01000000 | interval;    // set WDIE, and appropriate delay

  wdt_reset();
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  sleep_mode();            // now goes to Sleep and waits for the interrupt
}

ISR(WDT_vect)
{
  wdt_disable();  // disable watchdog
}

void sleepfunction(void) //the function that putes the arduino in deep sleep to save a lot of power
{
  delay(50);
  Serial.println("Putting Arduino in Low power mode");
  delay(10);
  for (int i = 0; i < 720; i++) //1 time loop is about 60 seconds. 1440 times this loop is about one day of sleep
  {
    myWatchdogEnable (0b100001);
    myWatchdogEnable (0b100001);
    myWatchdogEnable (0b100001);
    myWatchdogEnable (0b100001);
    myWatchdogEnable (0b100001);
    myWatchdogEnable (0b100001);
    myWatchdogEnable (0b100000);
    myWatchdogEnable (0b000111);
  }
  delay(100);
  Serial.println("Waking Arduino from low power mode");
  delay(10);
}
void drawBitmaps_200x200()
{
  int16_t x = (int16_t(display.WIDTH) - 200) / 2;
  int16_t y = (int16_t(display.HEIGHT) - 200) / 2;
  drawBitmapFromSD("photo(1).bmp", x, y);
  delay(2000);
}

void drawBitmaps_other() //this is where you put your pictures in BMP format.
{
  int16_t w2 = display.WIDTH / 2;
  int16_t h2 = display.HEIGHT / 2;
  drawBitmapFromSD("painttest.bmp", w2 - 200, h2 - 150);
  delay(1000);
  sleepfunction();
  drawBitmapFromSD("astro.bmp", w2 - 200, h2 - 150);
  delay(1000);
  sleepfunction();
  drawBitmapFromSD("snake.bmp", w2 - 200, h2 - 150);
  delay(1000);
  sleepfunction();
  drawBitmapFromSD("BPS.bmp", w2 - 200, h2 - 150);
  delay(1000);
  sleepfunction();
  drawBitmapFromSD("snoopy.bmp", w2 - 200, h2 - 150);
  delay(1000);
}

void drawBitmapFromSD(const char *filename, int16_t x, int16_t y, bool with_color) //function that draws the picture.
{
  SdFile file;
  bool valid = false; // valid format to be handled
  bool flip = true; // bitmap is stored bottom-to-top
  uint32_t startTime = millis();
  //Serial.println(); Serial.print(x); Serial.print(" "); Serial.print(y); Serial.print(" "); Serial.println(filename);
  if ((x >= int16_t(display.WIDTH)) || (y >= int16_t(display.HEIGHT))) return;
  Serial.println();
  Serial.println(F("--------PICTURE DATA--------"));
  Serial.print(F("Loading image '"));
  Serial.print(filename);
  Serial.println('\'');
#if defined(ESP32)
  file = SD.open(String("/") + filename, FILE_READ);
  if (!file)
  {
    Serial.print(F("File not found"));
    return;
  }
#else
  if (!file.open(filename, FILE_READ))
  {
    Serial.print(F("File not found"));
    return;
  }
#endif
  // Parse BMP header
  if (read16(file) == 0x4D42) // BMP signature
  {
    uint32_t fileSize = read32(file);
    uint32_t creatorBytes = read32(file);
    uint32_t imageOffset = read32(file); // Start of image data
    uint32_t headerSize = read32(file);
    uint32_t width  = read32(file);
    uint32_t height = read32(file);
    uint16_t planes = read16(file);
    uint16_t depth = read16(file); // bits per pixel
    uint32_t format = read32(file);
    if ((planes == 1) && ((format == 0) || (format == 3))) // uncompressed is handled, 565 also
    {
      Serial.print(F("File size: ")); Serial.println(fileSize);
      Serial.print(F("Image Offset: ")); Serial.println(imageOffset);
      Serial.print(F("Header size: ")); Serial.println(headerSize);
      Serial.print(F("Bit Depth: ")); Serial.println(depth);
      Serial.print(F("Image size: "));
      Serial.print(width);
      Serial.print('x');
      Serial.println(height);
      // BMP rows are padded (if needed) to 4-byte boundary
      uint32_t rowSize = (width * depth / 8 + 3) & ~3;
      if (height < 0)
      {
        height = -height;
        flip = false;
      }
      uint16_t w = width;
      uint16_t h = height;
      if ((x + w - 1) >= int16_t(display.WIDTH))  w = int16_t(display.WIDTH)  - x;
      if ((y + h - 1) >= int16_t(display.HEIGHT)) h = int16_t(display.HEIGHT) - y;
      if (w <= max_row_width) // handle with direct drawing
      {
        valid = true;
        uint8_t bitmask = 0xFF;
        uint8_t bitshift = 8 - depth;
        uint16_t red, green, blue;
        bool whitish, colored;
        if (depth == 1) with_color = false;
        if (depth <= 8)
        {
          if (depth < 8) bitmask >>= depth;
          //file.seekSet(54); //palette is always @ 54
          file.seekSet(imageOffset - (4 << depth)); // 54 for regular, diff for colorsimportant
          for (uint16_t pn = 0; pn < (1 << depth); pn++)
          {
            blue  = file.read();
            green = file.read();
            red   = file.read();
            file.read();
            whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
            colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
            if (0 == pn % 8) mono_palette_buffer[pn / 8] = 0;
            mono_palette_buffer[pn / 8] |= whitish << pn % 8;
            if (0 == pn % 8) color_palette_buffer[pn / 8] = 0;
            color_palette_buffer[pn / 8] |= colored << pn % 8;
          }
        }
        display.clearScreen();
        uint32_t rowPosition = flip ? imageOffset + (height - h) * rowSize : imageOffset;
        for (uint16_t row = 0; row < h; row++, rowPosition += rowSize) // for each line
        {
          uint32_t in_remain = rowSize;
          uint32_t in_idx = 0;
          uint32_t in_bytes = 0;
          uint8_t in_byte = 0; // for depth <= 8
          uint8_t in_bits = 0; // for depth <= 8
          uint8_t out_byte = 0xFF; // white (for w%8!=0 border)
          uint8_t out_color_byte = 0xFF; // white (for w%8!=0 border)
          uint32_t out_idx = 0;
          file.seekSet(rowPosition);
          for (uint16_t col = 0; col < w; col++) // for each pixel
          {
            // Time to read more pixel data?
            if (in_idx >= in_bytes) // ok, exact match for 24bit also (size IS multiple of 3)
            {
              in_bytes = file.read(input_buffer, in_remain > sizeof(input_buffer) ? sizeof(input_buffer) : in_remain);
              in_remain -= in_bytes;
              in_idx = 0;
            }
            switch (depth)
            {
              case 24:
                blue = input_buffer[in_idx++];
                green = input_buffer[in_idx++];
                red = input_buffer[in_idx++];
                whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
                colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
                break;
              case 16:
                {
                  uint8_t lsb = input_buffer[in_idx++];
                  uint8_t msb = input_buffer[in_idx++];
                  if (format == 0) // 555
                  {
                    blue  = (lsb & 0x1F) << 3;
                    green = ((msb & 0x03) << 6) | ((lsb & 0xE0) >> 2);
                    red   = (msb & 0x7C) << 1;
                  }
                  else // 565
                  {
                    blue  = (lsb & 0x1F) << 3;
                    green = ((msb & 0x07) << 5) | ((lsb & 0xE0) >> 3);
                    red   = (msb & 0xF8);
                  }
                  whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
                  colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
                }
                break;
              case 1:
              case 4:
              case 8:
                {
                  if (0 == in_bits)
                  {
                    in_byte = input_buffer[in_idx++];
                    in_bits = 8;
                  }
                  uint16_t pn = (in_byte >> bitshift) & bitmask;
                  whitish = mono_palette_buffer[pn / 8] & (0x1 << pn % 8);
                  colored = color_palette_buffer[pn / 8] & (0x1 << pn % 8);
                  in_byte <<= depth;
                  in_bits -= depth;
                }
                break;
            }
            if (whitish)
            {
              //out_byte |= 0x80 >> col % 8; // not black
              //out_color_byte |= 0x80 >> col % 8; // not colored
              // keep white
            }
            else if (colored && with_color)
            {
              //out_byte |= 0x80 >> col % 8; // not black
              out_color_byte &= ~(0x80 >> col % 8); // colored
            }
            else
            {
              //out_color_byte |= 0x80 >> col % 8; // not colored
              out_byte &= ~(0x80 >> col % 8); // black
            }
            if ((7 == col % 8) || (col == w - 1)) // write that last byte! (for w%8!=0 border)
            {
              output_row_color_buffer[out_idx] = out_color_byte;
              output_row_mono_buffer[out_idx++] = out_byte;
              out_byte = 0xFF; // white (for w%8!=0 border)
              out_color_byte = 0xFF; // white (for w%8!=0 border)
            }
          } // end pixel
          uint16_t yrow = y + (flip ? h - row - 1 : row);
          display.writeImage(output_row_mono_buffer, output_row_color_buffer, x, yrow, w, 1);
        } // end line
        Serial.print(F("loaded in ")); Serial.print(millis() - startTime); Serial.println(F(" ms"));
        display.refresh();
        Serial.println(F("---------------------------"));
        Serial.println();
      }
    }
  }
  //Serial.print(F("end curPosition  ")); Serial.println(file.curPosition());
  file.close();
  if (!valid)
  {
    Serial.println(F("bitmap format not handled."));
  }
}

uint16_t read16(SdFile& f)
{
  // BMP data is stored little-endian, same as Arduino.
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(SdFile& f)
{
  // BMP data is stored little-endian, same as Arduino.
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}

Also, could you tell me how to use int0 or int0 (or was this a typo?).

You can try this. It's a modification of your code with an attempt at INT0 added. It compiles but is untested. You might want to set the constant K_SLEEPCYCLES to something very small (say 5 or 10) to test the WDT function.

#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

#include <SdFat.h>
//#include "GxEPD2_SD_AVR_boards_added.h"

#define NUM_BITMAPS     5       //number of bitmaps to show
#define K_SLEEPCYCLES   9412u   //assumes ~9.18-sec 8-sec WDT timeout (86400/9.18)

#define GxEPD2_DRIVER_CLASS GxEPD2_420c     // GDEW042Z15  400x300    The 4.2 inch color screen I use.

#if defined(__AVR)
#define SD_CS 6  // adapt to your wiring
#define EPD_CS SS // adapt to your wiring
GxEPD2_DRIVER_CLASS display(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7);
#endif

// include SdFat for FAT32 support with long filenames; available through Library Manager

SdFat SD;

// SD card can be connected as:
//  sck 13, miso 12, mosi 11, cs (6 in this case)

//prototypes
void drawBitmapFromSD(const char *filename, int16_t x, int16_t y, bool with_color = true);
static const uint16_t input_buffer_pixels = 20; // may affect performance
static const uint16_t max_row_width = 640; // for up to 7.5" display
static const uint16_t max_palette_pixels = 256; // for depth <= 8

uint8_t input_buffer[3 * input_buffer_pixels]; // up to depth 24
uint8_t output_row_mono_buffer[max_row_width / 8]; // buffer for at least one row of b/w bits
uint8_t output_row_color_buffer[max_row_width / 8]; // buffer for at least one row of color bits
uint8_t mono_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 b/w
uint8_t color_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 c/w

const uint8_t pinAdvance = 2;

const char *pszBitmaps[NUM_BITMAPS] =
{
    "painttest.bmp",
    "astro.bmp",
    "snake.bmp",
    "BPS.bmp",
    "snoopy.bmp"    
};

volatile bool
    bWDT = false,
    bINT0 = false;
int16_t 
    w2,
    h2;

void setup()
{
    Serial.begin(115200);
    Serial.println();
    delay(10);
    Serial.println(F("setup"));
    
    display.init(115200);
    
    Serial.print(F("Initializing SD card..."));
    if (!SD.begin(SD_CS))
    {
        Serial.println(F("SD failed!"));
        return;
        
    }//if
    
    Serial.println(F("SD OK!"));

    w2 = display.WIDTH / 2;         //get this calculation out of the way
    h2 = display.HEIGHT / 2;

    pinMode( pinAdvance, INPUT_PULLUP );    //image 'advance' switch input (press must GND pin)

    //draw initial bitmap to screen
    drawBitmapFromSD( pszBitmaps[0], w2 - 200, h2 - 150);

    Serial.println(F("entering loop"));

}//setup

void loop(void)
{
    static uint16_t
        sleepCycles = K_SLEEPCYCLES;    //# of "8-sec" WDTs in a day; sleepCycles tracks day's worth
    static uint8_t
        idxBitmap = 0;

    //was cause of wake advance button press?
    if( bINT0 == true )
    {
        //yes; clear the flag and
        bINT0 = false;
        // immediately show the next image, bumping the index by one
        drawBitmapFromSD( pszBitmaps[idxBitmap++], w2 - 200, h2 - 150);
        //if we're showing the last, cycle back to the first
        if( idxBitmap == NUM_BITMAPS )
            idxBitmap = 0;
        
    }//if
    else if( bWDT == true )
    {
        //cause of wake was a WDT event
        bWDT = false;

        //are we still counting "daily" sleeps?
        if( sleepCycles > 0 )
        {
            //yep; tick it down by one
            sleepCycles--;
            
        }//if
        else
        {
            //done all sleeps for one day
            //show the next image
            drawBitmapFromSD( pszBitmaps[idxBitmap++], w2 - 200, h2 - 150);
            if( idxBitmap == NUM_BITMAPS )
                idxBitmap = 0;

            //reset count for another day of sleeps
            sleepCycles = K_SLEEPCYCLES;
            
        }//else

    }//else

    Serial.println("Putting Arduino in Low power mode");
    delay(100);
    
    //set up WDT for sleep
    MCUSR = 0;                          //reset various flags
    WDTCSR |= _BV(WDCE) | _BV(WDE);     //set WDT change enable and enable the WDCE, WDE    
    WDTCSR = _BV(WDIE) | _BV(WDP3) | _BV(WDP0); //Period is 0b10001 (8-sec), interrupt enabled
    wdt_reset();
    //set up INT0
    EICRA = 0x00;           //low-level on pin 2 (PD2, INT0) on Uno
    EIMSK |= _BV(INT0);     //enable INT0 interrupt    
    
    set_sleep_mode (SLEEP_MODE_PWR_DOWN);
    sleep_mode();            // now goes to Sleep and waits for the interrupt

    delay(100);
    Serial.println("Waking Arduino from low power mode");

}//loop

ISR(WDT_vect)
{
    wdt_disable();  // disable watchdog
    bWDT = true;    //indicate a WDT was source of wake
    
}//WDT handler

ISR( INT0_vect )
{
    EIMSK &= ~_BV( INT0 );  //disable INT0 interrupt
    bINT0 = true;           //indicate a press of the advance button was source of wake
        
}//INT0 interrupt handler

void drawBitmapFromSD(const char *filename, int16_t x, int16_t y, bool with_color) //function that draws the picture.
{
    SdFile file;
    bool valid = false; // valid format to be handled
    bool flip = true; // bitmap is stored bottom-to-top
    uint8_t lsb, msb;
    uint16_t pn, yrow;
        
    uint32_t startTime = millis();
    
    //Serial.println(); Serial.print(x); Serial.print(" "); Serial.print(y); Serial.print(" "); Serial.println(filename);
    if ((x >= int16_t(display.WIDTH)) || (y >= int16_t(display.HEIGHT))) 
        return;
        
    Serial.println();
    Serial.println(F("--------PICTURE DATA--------"));
    Serial.print(F("Loading image '"));
    Serial.print(filename);
    Serial.println('\'');
    
    if (!file.open(filename, FILE_READ))
    {
        Serial.print(F("File not found"));
        return;
        
    }//if

    // Parse BMP header
    if (read16(file) == 0x4D42) // BMP signature
    {
        uint32_t fileSize = read32(file);
        uint32_t creatorBytes = read32(file);
        uint32_t imageOffset = read32(file); // Start of image data
        uint32_t headerSize = read32(file);
        uint32_t width  = read32(file);
        uint32_t height = read32(file);
        uint16_t planes = read16(file);
        uint16_t depth = read16(file); // bits per pixel
        uint32_t format = read32(file);
        if ((planes == 1) && ((format == 0) || (format == 3))) // uncompressed is handled, 565 also
        {
            Serial.print(F("File size: ")); Serial.println(fileSize);
            Serial.print(F("Image Offset: ")); Serial.println(imageOffset);
            Serial.print(F("Header size: ")); Serial.println(headerSize);
            Serial.print(F("Bit Depth: ")); Serial.println(depth);
            Serial.print(F("Image size: "));
            Serial.print(width);
            Serial.print('x');
            Serial.println(height);
            // BMP rows are padded (if needed) to 4-byte boundary
            uint32_t rowSize = (width * depth / 8 + 3) & ~3;
            if (height < 0)
            {
                height = -height;
                flip = false;
                
            }//if
            
            uint16_t w = width;
            uint16_t h = height;
            if ((x + w - 1) >= int16_t(display.WIDTH))  
                w = int16_t(display.WIDTH)  - x;
            if ((y + h - 1) >= int16_t(display.HEIGHT)) 
                h = int16_t(display.HEIGHT) - y;
            if (w <= max_row_width) // handle with direct drawing
            {
                valid = true;
                uint8_t bitmask = 0xFF;
                uint8_t bitshift = 8 - depth;
                uint16_t red, green, blue;
                bool whitish, colored;
                if (depth == 1) 
                    with_color = false;
                if (depth <= 8)
                {
                    if (depth < 8) 
                        bitmask >>= depth;
                    //file.seekSet(54); //palette is always @ 54
                    file.seekSet(imageOffset - (4 << depth)); // 54 for regular, diff for colorsimportant
                    for (uint16_t pn = 0; pn < (1 << depth); pn++)
                    {
                        blue  = file.read();
                        green = file.read();
                        red   = file.read();
                        file.read();
                        whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
                        colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
                        if (0 == pn % 8) 
                            mono_palette_buffer[pn / 8] = 0;
                        mono_palette_buffer[pn / 8] |= whitish << pn % 8;
                        if (0 == pn % 8) 
                            color_palette_buffer[pn / 8] = 0;
                        color_palette_buffer[pn / 8] |= colored << pn % 8;
                        
                    }//for
                    
                }//if
                
                display.clearScreen();
                uint32_t rowPosition = flip ? imageOffset + (height - h) * rowSize : imageOffset;
                for (uint16_t row = 0; row < h; row++, rowPosition += rowSize) // for each line
                {
                    uint32_t in_remain = rowSize;
                    uint32_t in_idx = 0;
                    uint32_t in_bytes = 0;
                    uint8_t in_byte = 0; // for depth <= 8
                    uint8_t in_bits = 0; // for depth <= 8
                    uint8_t out_byte = 0xFF; // white (for w%8!=0 border)
                    uint8_t out_color_byte = 0xFF; // white (for w%8!=0 border)
                    uint32_t out_idx = 0;
                    file.seekSet(rowPosition);
                    for (uint16_t col = 0; col < w; col++) // for each pixel
                    {
                        // Time to read more pixel data?
                        if (in_idx >= in_bytes) // ok, exact match for 24bit also (size IS multiple of 3)
                        {
                            in_bytes = file.read(input_buffer, in_remain > sizeof(input_buffer) ? sizeof(input_buffer) : in_remain);
                            in_remain -= in_bytes;
                            in_idx = 0;
                            
                        }//if
                        
                        switch (depth)
                        {
                            case 24:
                                blue = input_buffer[in_idx++];
                                green = input_buffer[in_idx++];
                                red = input_buffer[in_idx++];
                                whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
                                colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
                                
                            break;
                            
                            case 16:
                                lsb = input_buffer[in_idx++];
                                msb = input_buffer[in_idx++];
                                if (format == 0) // 555
                                {
                                    blue  = (lsb & 0x1F) << 3;
                                    green = ((msb & 0x03) << 6) | ((lsb & 0xE0) >> 2);
                                    red   = (msb & 0x7C) << 1;
                                    
                                }//if
                                else // 565
                                {
                                    blue  = (lsb & 0x1F) << 3;
                                    green = ((msb & 0x07) << 5) | ((lsb & 0xE0) >> 3);
                                    red   = (msb & 0xF8);
                                    
                                }//else
                                
                                whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
                                colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
                                
                            break;
                            
                            case 1:
                            case 4:
                            case 8:
                                if (0 == in_bits)
                                {
                                    in_byte = input_buffer[in_idx++];
                                    in_bits = 8;
                                    
                                }//if
                                
                                pn = (in_byte >> bitshift) & bitmask;
                                whitish = mono_palette_buffer[pn / 8] & (0x1 << pn % 8);
                                colored = color_palette_buffer[pn / 8] & (0x1 << pn % 8);
                                in_byte <<= depth;
                                in_bits -= depth;
                                
                            break;
                            
                        }//switch
                        
                        if (whitish)
                        {
                            //out_byte |= 0x80 >> col % 8; // not black
                            //out_color_byte |= 0x80 >> col % 8; // not colored
                            // keep white
                            
                        }//if
                        else if (colored && with_color)
                        {
                            //out_byte |= 0x80 >> col % 8; // not black
                            out_color_byte &= ~(0x80 >> col % 8); // colored
                            
                        }//else if
                        else
                        {
                            //out_color_byte |= 0x80 >> col % 8; // not colored
                            out_byte &= ~(0x80 >> col % 8); // black
                        }//else
                        
                        if ((7 == col % 8) || (col == w - 1)) // write that last byte! (for w%8!=0 border)
                        {
                            output_row_color_buffer[out_idx] = out_color_byte;
                            output_row_mono_buffer[out_idx++] = out_byte;
                            out_byte = 0xFF; // white (for w%8!=0 border)
                            out_color_byte = 0xFF; // white (for w%8!=0 border)
                            
                        }//if
                        
                    }//for end pixel
                    
                    yrow = y + (flip ? h - row - 1 : row);
                    display.writeImage(output_row_mono_buffer, output_row_color_buffer, x, yrow, w, 1);
                    
                }//for end line
                
                Serial.print(F("loaded in ")); Serial.print(millis() - startTime); Serial.println(F(" ms"));
                display.refresh();
                Serial.println(F("---------------------------"));
                Serial.println();
                
            }//if
            
        }//if
        
    }//if
    
    //Serial.print(F("end curPosition  ")); Serial.println(file.curPosition());
    file.close();
    if (!valid)
    {
        Serial.println(F("bitmap format not handled."));
        
    }//if
    
}//drawBitmapFromSD

uint16_t read16(SdFile& f)
{
    // BMP data is stored little-endian, same as Arduino.
    uint16_t result;
    
    ((uint8_t *)&result)[0] = f.read(); // LSB
    ((uint8_t *)&result)[1] = f.read(); // MSB
    
    return result;
}

uint32_t read32(SdFile& f)
{
    // BMP data is stored little-endian, same as Arduino.
    uint32_t result;
    
    ((uint8_t *)&result)[0] = f.read(); // LSB
    ((uint8_t *)&result)[1] = f.read();
    ((uint8_t *)&result)[2] = f.read();
    ((uint8_t *)&result)[3] = f.read(); // MSB
    
    return result;
}
1 Like

First of all: wow. thank you so much for your effort! I really appreciate the time you have put into that! I have looked at your code and it seem to be working great except for the part that is waking up every few seconds as seen in the terminal:

This would be quit wastefull of energy since it jumps the current up every 8 seconds.. My previous code didn't have this problem. Apart from that, the code keeps ignoring the sleep function after I pressed the button once. I will fix that myself (hopefully)
Could you explain this line to me?

  sleepCycles = K_SLEEPCYCLES;    //# of "8-sec" WDTs in a day; sleepCycles tracks day's worth

Also, appart from this. How could I select the next picture without having to name every picture in the code and on the micro sd card? I found someone who did it, but my code seems to deviate from this principle.
I thought that this part of his code may be suitable for mine:

int index = 1;
int counter = 1;
String indexStr = String(counter);
String str = 'i' + indexStr + ".BMP ";

I would have to name the files on the sd card: i(0).bmp, i(1).bmp, i(2).bmp, obviously.
edit: fixed the button issue (I connected it wrong).