Couple of Issues including 'Low memory available, stability problems may occur'

Hi all,
This is my first post, and am a bit of a newbie when it comes to Arduino coding.

I've got a small project which I've progressed with, though have come to a bit of a dead end and would really appreciate some help.

My coding is probably not the quickest way to do things, though having done basic actionscript in the past I had a reasonable idea of some of the syntax etc.

So, my project is a basic graphical menu, which a user can navigate through (using a rotary switch), and then make a selection using a button.

On start up an OLED displays a few images in turn to simulate an 'initialising screen' These images are on the OLEDs built in SD Card. After that 10 images are displayed one after the other to play a small animation. The last frame is held...this is the 'menu'.

Using the rotary switch the user can then scroll backwards and forward through the menu. Basically a new image being shown each rotation.
When the user presses another button a sound will be played.

Using an Arduino Uno, and Adafruit 0.96 OLED (with SDCard), rotary switch and basic button. I got everything working (minus the sound).

This is the code:

/***************************************************
  This is a example sketch demonstrating the graphics
  capabilities of the SSD1331 library  for the 0.96"
  16-bit Color OLED with SSD1331 driver chip

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/684

  These displays use SPI to communicate, 4 or 5 pins are required to
  interface
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1331.h>

#include <SdFat.h>                // SD card & FAT filesystem library
#include <Adafruit_SPIFlash.h>    // SPI / QSPI flash library
#include <Adafruit_ImageReader.h> // Image-reading functions

// You can use any (4 or) 5 pins
#define sclk 13 //SCK (CK)
#define mosi 11 //SI
#define cs   10 //OCS (OC)
#define rst  9 //R
#define dc   8 //DC
#define SD_CS    4 // SD card select pin

// Rotary Encoder Inputs
#define CLK 6
#define DT 5
#define SW 3 //SWITCH BUTTON


SdFat                SD;         // SD card filesystem
Adafruit_ImageReader reader(SD); // Image-reader object, pass in SD filesys


// Option 1: use any pins but a little slower
//Adafruit_SSD1331 display = Adafruit_SSD1331(cs, dc, mosi, sclk, rst);

// Option 2: must use the hardware SPI pins
// (for UNO thats sclk = 13 and sid = 11) and pin 10 must be
// an output. This is much faster - also required if you want
// to use the microSD card (see the image drawing example)
Adafruit_SSD1331 display = Adafruit_SSD1331(&SPI, cs, dc, rst);

int button = 7;
int press = 0;
int toggle = 0;
int firstTime = true;

//ROTARY VARIABLES
int currentStateCLK;
int lastStateCLK;

void setup(void) {
  pinMode(button, INPUT);
  digitalWrite(7, HIGH);

  // Set encoder pins as inputs
  pinMode(CLK,INPUT);
  pinMode(DT,INPUT);
  pinMode(SW, INPUT_PULLUP);

  
  Serial.begin(9600);

  // Read the initial state of CLK
  lastStateCLK = digitalRead(CLK);

  // SD card is pretty straightforward, a single call...
  if(!SD.begin(SD_CS, SD_SCK_MHZ(10))) { // Breakouts require 10 MHz limit due to longer wires
    Serial.println(F("SD begin() failed"));
    for(;;); // Fatal error, do not continue
  }
  
  display.begin();
  display.fillScreen(0x0000);
  //SHOW INITIALISING SCREEN
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_01.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_02.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_03.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_01.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_02.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_03.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_01.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_02.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_03.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_01.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_02.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_03.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_01.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_02.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_03.bmp", display, 0, 0);

  //PLAY ZF-1 INTRO ANIMATION
  display.fillScreen(0x0000);
   reader.drawBMP("/Zf1_00.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_01.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_02.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_03.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_04.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_05.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_06.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_07.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_08.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_09.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_10.bmp", display, 0, 0);
   delay(1500);
   reader.drawBMP("/Zf1_11.bmp", display, 0, 0);
}

void loop() {
  //ButtonPressCode
    press = digitalRead(button);
    if (press == LOW){
      playSound();
    }
  
  // Read the current state of CLK
  currentStateCLK = digitalRead(CLK);

  // If last and current state of CLK are different, then pulse occurred
  // React to only 1 state change to avoid double count
  if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){

    // If the DT state is different than the CLK state then
    // the encoder is rotating CCW so decrement
    if (digitalRead(DT) != currentStateCLK) {
      if(firstTime == true){
        firstTime = false;
      }else{
        if(toggle != -5){
          toggle --;
        }else{
          toggle = 0;
        }
      }
      //RUN IMAGE FUNCTION
      showImage();
    } else {
      if(firstTime == true){
        firstTime = false;
      }else{
        if(toggle != 5){
      // Encoder is rotating CW so increment
        toggle ++;
        }else{
          toggle = 0;
        }
      }
      //RUN IMAGE FUNCTION
      showImage();
    }
  }

  // Remember last CLK state
  lastStateCLK = currentStateCLK;
}

//SHOW IMAGE FOR EACH MENU ITEM
void showImage(){

    if(toggle == 0){
          //SHOW MACHINE GUN IMAGE
          reader.drawBMP("/01_bullets.bmp", display, 0, 0);
    }
       
    if((toggle == 1) || (toggle == -5)){
          //SHOW ROCKET IMAGE
          reader.drawBMP("/02_rocket.bmp", display, 0, 0);
    }
       
    if((toggle == 2) || (toggle == -4)){
          //SHOW ARROW IMAGE
          reader.drawBMP("/03_arrow.bmp", display, 0, 0);
    }

    if((toggle == 3) || (toggle == -3)){
          //SHOW NET IMAGE
          reader.drawBMP("/04_net.bmp", display, 0, 0);
    }

    if((toggle == 4) || (toggle == -2)){
          //SHOW FLAME IMAGE
          reader.drawBMP("/05_flame.bmp", display, 0, 0);
    }

    if((toggle == 5) || (toggle == -1)){
          //SHOW ICE IMAGE
          reader.drawBMP("/06_ice.bmp", display, 0, 0);
    }
}

//CODE READY FOR PLAYING SOUNDS
void playSound(){
   if((toggle == 0) && (firstTime != true)){
      display.setCursor(0,10);
      display.print("Sound 0");
      delay(500);
   }

   if((toggle == 1) || (toggle == -5)){
    display.setCursor(0,10);
    display.print("Sound 1");
    delay(500);
   }

   if((toggle == 2) || (toggle == -4)){
    display.setCursor(0,10);
    display.print("Sound 2");
    delay(500);
   }

   if((toggle == 3) || (toggle == -3)){
    display.setCursor(0,10);
    display.print("Sound 3");
    delay(500);
   }

   if((toggle == 4) || (toggle == -2)){
    display.setCursor(0,10);
    display.print("Sound 4");
    delay(500);
   }
   
   if((toggle == 5) || (toggle == -1)){
    display.setCursor(0,10);
    display.print("Sound 5");
    delay(500);
  }
}

So far so good....so probably should be using arrays but it worked nicely.

I will continue the post with a new message...

The sound part of the project I decided to get a DFPlayer (with SDCard). Again I got this working alone using the example code.

The issue is when I combine the two codes. Sometimes it works, and sometimes it doesn't due to 'Low memory available, stability problems may occur'.
This only happens when I add SoftwareSerial.h library. There doesn't seem to be a memory issue until adding this.

Also at the moment I am using two separate SD Cards (one on the Oled and one on the DFPlayer), is there a way to share one SD Card between the two modules?

Thank you any help or advice is much appreciated.

The combined code is:

/***************************************************
  This is a example sketch demonstrating the graphics
  capabilities of the SSD1331 library  for the 0.96"
  16-bit Color OLED with SSD1331 driver chip

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/684

  These displays use SPI to communicate, 4 or 5 pins are required to
  interface
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1331.h>
#include <SPI.h>
#include <SdFat.h>                // SD card & FAT filesystem library
#include <Adafruit_SPIFlash.h>    // SPI / QSPI flash library
#include <Adafruit_ImageReader.h> // Image-reading functions

#include "SoftwareSerial.h" //DFPLAYER
#include "DFRobotDFPlayerMini.h"  //DFPLAYER


// You can use any (4 or) 5 pins
#define sclk 13 //SCK (CK) //12 SO
#define mosi 11 //SI
#define cs   10 //OCS (OC)
#define rst  9 //R
#define dc   8 //DC
#define SD_CS    4 // SD card select pin

// Rotary Encoder Inputs
#define CLK 6
#define DT 5
#define SW 3 //SWITCH BUTTON


// Color definitions
#define	BLACK           0x0000


SdFat                SD;         // SD card filesystem
Adafruit_ImageReader reader(SD); // Image-reader object, pass in SD filesys



// Option 1: use any pins but a little slower
//Adafruit_SSD1331 display = Adafruit_SSD1331(cs, dc, mosi, sclk, rst);

// Option 2: must use the hardware SPI pins
// (for UNO thats sclk = 13 and sid = 11) and pin 10 must be
// an output. This is much faster - also required if you want
// to use the microSD card (see the image drawing example)
Adafruit_SSD1331 display = Adafruit_SSD1331(&SPI, cs, dc, rst);


int button = 7;
int press = 0;
int toggle = 0;
int firstTime = true;

//ROTARY VARIABLES
int currentStateCLK;
int lastStateCLK;
String currentDir ="";

void setup(void) {
  pinMode(button, INPUT);
  digitalWrite(7, HIGH);

  // Set encoder pins as inputs
  pinMode(CLK,INPUT);
  pinMode(DT,INPUT);
  pinMode(SW, INPUT_PULLUP);

  
  // Init USB serial port for debugging
  Serial.begin(9600);
  
  // Init serial port for DFPlayer Mini
  mySoftwareSerial.begin(9600);

  if (player.begin(mySoftwareSerial)) {
    Serial.println("DFPLayer OK");
    // Set volume to maximum (0 to 30).
    player.volume(30);
    } else {
    Serial.println("Connecting to DFPlayer Mini failed!");
  }

  // Read the initial state of CLK
  lastStateCLK = digitalRead(CLK);

  // SD card is pretty straightforward, a single call...
  if(!SD.begin(SD_CS, SD_SCK_MHZ(10))) { // Breakouts require 10 MHz limit due to longer wires
    Serial.println(F("SD begin() failed"));
    for(;;); // Fatal error, do not continue
  }
  
  display.begin();
  display.fillScreen(BLACK);
  //SHOW INITIALISING SCREEN
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_01.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_02.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_03.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_01.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_02.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_03.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_01.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_02.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_03.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_01.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_02.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_03.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_00.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_01.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_02.bmp", display, 0, 0);
  delay(50);
  reader.drawBMP("/Intro_03.bmp", display, 0, 0);

  //PLAY ZF-1 ANIMATION
  display.fillScreen(BLACK);
   reader.drawBMP("/Zf1_00.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_01.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_02.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_03.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_04.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_05.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_06.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_07.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_08.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_09.bmp", display, 0, 0);
   delay(0);
   reader.drawBMP("/Zf1_10.bmp", display, 0, 0);
   delay(1500);
   reader.drawBMP("/Zf1_11.bmp", display, 0, 0);
}

void loop() {
  //ButtonPressCode
    press = digitalRead(button);
    if (press == LOW){
      playSound();
    }
  
  // Read the current state of CLK
  currentStateCLK = digitalRead(CLK);

  // If last and current state of CLK are different, then pulse occurred
  // React to only 1 state change to avoid double count
  if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){

    // If the DT state is different than the CLK state then
    // the encoder is rotating CCW so decrement
    if (digitalRead(DT) != currentStateCLK) {
      if(firstTime == true){
        firstTime = false;
      }else{
        if(toggle != -5){
          toggle --;
        }else{
          toggle = 0;
        }
      }
      currentDir ="CCW";
      //RUN IMAGE FUNCTION
      showImage();
    } else {
      if(firstTime == true){
        firstTime = false;
      }else{
        if(toggle != 5){
      // Encoder is rotating CW so increment
        toggle ++;
        }else{
          toggle = 0;
        }
      }
      currentDir ="CW";
      //RUN IMAGE FUNCTION
      showImage();
    }
  }

  // Remember last CLK state
  lastStateCLK = currentStateCLK;
}

//SHOW IMAGE FOR EACH MENU ITEM
void showImage(){

    if(toggle == 0){
          //SHOW MACHINE GUN IMAGE
          reader.drawBMP("/01_bullets.bmp", display, 0, 0);
    }
       
    if((toggle == 1) || (toggle == -5)){
          //SHOW ROCKET IMAGE
          reader.drawBMP("/02_rocket.bmp", display, 0, 0);
    }
       
    if((toggle == 2) || (toggle == -4)){
          //SHOW ARROW IMAGE
          reader.drawBMP("/03_arrow.bmp", display, 0, 0);
    }

    if((toggle == 3) || (toggle == -3)){
          //SHOW NET IMAGE
          reader.drawBMP("/04_net.bmp", display, 0, 0);
    }

    if((toggle == 4) || (toggle == -2)){
          //SHOW FLAME IMAGE
          reader.drawBMP("/05_flame.bmp", display, 0, 0);
    }

    if((toggle == 5) || (toggle == -1)){
          //SHOW ICE IMAGE
          reader.drawBMP("/06_ice.bmp", display, 0, 0);
    }
}

//CODE FOR PLAYING SOUNDS
void playSound(){
   if((toggle == 0) && (firstTime != true)){
      //Play the "0001.mp3" in the "mp3" folder on the SD card
      player.playMp3Folder(4);
      delay(500);
   }

   if((toggle == 1) || (toggle == -5)){
    display.setCursor(0,10);
    display.print("Sound 1");
    delay(500);
   }

   if((toggle == 2) || (toggle == -4)){
    display.setCursor(0,10);
    display.print("Sound 2");
    delay(500);
   }

   if((toggle == 3) || (toggle == -3)){
    display.setCursor(0,10);
    display.print("Sound 3");
    delay(500);
   }

   if((toggle == 4) || (toggle == -2)){
    display.setCursor(0,10);
    display.print("Sound 4");
    delay(500);
   }
   
   if((toggle == 5) || (toggle == -1)){
    display.setCursor(0,10);
    display.print("Sound 5");
    delay(500);
  }
}

If you use the print (F( “ whatever”) )

Then you will save a lot of program memory when printing text to the display

See : https://www.baldengineer.com/arduino-f-macro.html

Hammy, I only see one Serial.print() in the code, so there isn't a lot of savings there.
Lewis- you ALWAYS have potential memory issues, it's just a question of what pushes you over the edge.

The Uno has the least amount of available memory, so you might want to consider a board with more RAM, like the Mega.

Everything that follows is just a guess. If I am wrong there will be a dozen corrections very quickly.

I suspect that the String in lines like this one:

reader.drawBMP("/Intro_03.bmp", display, 0, 0);

is consuming your dynamic memory. Lots of it.

I am unfamiliar with this library, but the .h file shows the prototype:

ImageReturnCode drawBMP(char *filename, Adafruit_SPITFT &tft, int16_t x, int16_t y, boolean transact = true);

The first argument is a pointer to a character array (C string), but you are coding it as a String class (training wheels) using "/Intro_03.bmp".

I would try putting the filenames into static memory:

const char intro_03[15] = {"/Intro_03.bmp"};

Then in the call:

reader.drawBMP(&intro_03, display, 0, 0);

Now wait for the expected corrections to my reply.

SteveMann:
Hammy, I only see one Serial.print() in the code, so there isn't a lot of savings there.
Lewis- you ALWAYS have potential memory issues, it's just a question of what pushes you over the edge.

The Uno has the least amount of available memory, so you might want to consider a board with more RAM, like the Mega.

Everything that follows is just a guess. If I am wrong there will be a dozen corrections very quickly.

I suspect that the String in lines like this one:

reader.drawBMP("/Intro_03.bmp", display, 0, 0);

is consuming your dynamic memory. Lots of it.

I am unfamiliar with this library, but the .h file shows the prototype:

ImageReturnCode drawBMP(char *filename, Adafruit_SPITFT &tft, int16_t x, int16_t y, boolean transact = true);

The first argument is a pointer to a character array (C string), but you are coding it as a String class (training wheels) using "/Intro_03.bmp".

I would try putting the filenames into static memory:

const char intro_03[15] = {"/Intro_03.bmp"};

Then in the call:

reader.drawBMP(&intro_03, display, 0, 0);

Now wait for the expected corrections to my reply.

A string literal is not a String. It's basically the same as what you did but it's "anonymous" (it has no name like intro_03).

Aside: I know this isn't your code, but drawBMP really should take a const char *, not a char *, to tell users of the function that it won't modify the string (and to prevent compiler warnings about converting a string literal to char *). A lot of Arduino libraries seem to have this deficiency.

When using the Adafruit GFX with SSD1331 libraries and also the SDfat library, then your dynamic memory (sram) is almost used up.

Adding the SoftwareSerial and DFRobot player is just to much. On top of that, you also use the String object. The String object allocates memory from the heap. It can cause heap fragmentation.
You need an Arduino board with more memory or you have to simplify your project. Sorry, but you can't have all of that on a Arduino Uno.

You will save considerable dynamic memory (ram) by storing the filenames in program memory (PROGMEM), then copying the filename to a buffer before calling the drawBMP function. Unfortunately drawBMP cannot directly use a filename stored in PROGMEM.

  char bmpFile[14]; //buffer for file name
  strcpy_P(bmpFile, PSTR("/Zf1_00.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);

This is your original code without the DFplayer and SoftwareSerial:

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1331.h>

#include <SdFat.h>                // SD card & FAT filesystem library
#include <Adafruit_SPIFlash.h>    // SPI / QSPI flash library
#include <Adafruit_ImageReader.h> // Image-reading functions

// You can use any (4 or) 5 pins
#define sclk 13 //SCK (CK)
#define mosi 11 //SI
#define cs   10 //OCS (OC)
#define rst  9 //R
#define dc   8 //DC
#define SD_CS    4 // SD card select pin

// Rotary Encoder Inputs
#define CLK 6
#define DT 5
#define SW 3 //SWITCH BUTTON


SdFat                SD;         // SD card filesystem
Adafruit_ImageReader reader(SD); // Image-reader object, pass in SD filesys


// Option 1: use any pins but a little slower
//Adafruit_SSD1331 display = Adafruit_SSD1331(cs, dc, mosi, sclk, rst);

// Option 2: must use the hardware SPI pins
// (for UNO thats sclk = 13 and sid = 11) and pin 10 must be
// an output. This is much faster - also required if you want
// to use the microSD card (see the image drawing example)
Adafruit_SSD1331 display = Adafruit_SSD1331(&SPI, cs, dc, rst);

int button = 7;
int press = 0;
int toggle = 0;
int firstTime = true;

//ROTARY VARIABLES
int currentStateCLK;
int lastStateCLK;

void setup(void) {
  pinMode(button, INPUT);
  digitalWrite(7, HIGH);

  // Set encoder pins as inputs
  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);


  Serial.begin(9600);

  // Read the initial state of CLK
  lastStateCLK = digitalRead(CLK);

  // SD card is pretty straightforward, a single call...
  if (!SD.begin(SD_CS, SD_SCK_MHZ(10))) { // Breakouts require 10 MHz limit due to longer wires
    Serial.println(F("SD begin() failed"));
    for (;;); // Fatal error, do not continue
  }

  char bmpFile[14]; //buffer for file name
  //store repetitive file names in PROGMEM to prevent redundent copies
  static const char bmpIntro00[] PROGMEM = "/Intro_00.bmp";
  static const char bmpIntro01[] PROGMEM = "/Intro_01.bmp";
  static const char bmpIntro02[] PROGMEM = "/Intro_02.bmp";
  static const char bmpIntro03[] PROGMEM = "/Intro_03.bmp";
  display.begin();
  display.fillScreen(0x0000);
  //SHOW INITIALISING SCREEN
  strcpy_P(bmpFile, bmpIntro00);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro01);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro02);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro03);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro00);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro00);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro01);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro02);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro03);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro00);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro01);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro02);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro03);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro00);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro01);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro02);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro03);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro00);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro01);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro02);
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(50);
  strcpy_P(bmpFile, bmpIntro03);
  reader.drawBMP(bmpFile, display, 0, 0);

  //PLAY ZF-1 INTRO ANIMATION
  display.fillScreen(0x0000);
  strcpy_P(bmpFile, PSTR("/Zf1_00.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(0);
  strcpy_P(bmpFile, PSTR("/Zf1_01.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(0);
  strcpy_P(bmpFile, PSTR("/Zf1_02.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(0);
  strcpy_P(bmpFile, PSTR("/Zf1_03.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(0);
  strcpy_P(bmpFile, PSTR("/Zf1_04.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(0);
  strcpy_P(bmpFile, PSTR("/Zf1_05.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(0);
  strcpy_P(bmpFile, PSTR("/Zf1_06.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(0);
  strcpy_P(bmpFile, PSTR("/Zf1_07.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(0);
  strcpy_P(bmpFile, PSTR("/Zf1_08.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(0);
  strcpy_P(bmpFile, PSTR("/Zf1_09.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(0);
  strcpy_P(bmpFile, PSTR("/Zf1_10.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
  delay(1500);
  strcpy_P(bmpFile, PSTR("/Zf1_11.bmp"));
  reader.drawBMP(bmpFile, display, 0, 0);
}

void loop() {
  //ButtonPressCode
  press = digitalRead(button);
  if (press == LOW) {
    playSound();
  }

  // Read the current state of CLK
  currentStateCLK = digitalRead(CLK);

  // If last and current state of CLK are different, then pulse occurred
  // React to only 1 state change to avoid double count
  if (currentStateCLK != lastStateCLK  && currentStateCLK == 1) {

    // If the DT state is different than the CLK state then
    // the encoder is rotating CCW so decrement
    if (digitalRead(DT) != currentStateCLK) {
      if (firstTime == true) {
        firstTime = false;
      } else {
        if (toggle != -5) {
          toggle --;
        } else {
          toggle = 0;
        }
      }
      //RUN IMAGE FUNCTION
      showImage();
    } else {
      if (firstTime == true) {
        firstTime = false;
      } else {
        if (toggle != 5) {
          // Encoder is rotating CW so increment
          toggle ++;
        } else {
          toggle = 0;
        }
      }
      //RUN IMAGE FUNCTION
      showImage();
    }
  }

  // Remember last CLK state
  lastStateCLK = currentStateCLK;
}

//SHOW IMAGE FOR EACH MENU ITEM
void showImage() {

  char bmpFile[16];
  if (toggle == 0) {
    //SHOW MACHINE GUN IMAGE
    strcpy_P(bmpFile, PSTR("/01_bullets.bmp"));
    reader.drawBMP(bmpFile, display, 0, 0);
  }

  if ((toggle == 1) || (toggle == -5)) {
    //SHOW ROCKET IMAGE
    strcpy_P(bmpFile, PSTR("/02_rocket.bmp"));
    reader.drawBMP(bmpFile, display, 0, 0);
  }

  if ((toggle == 2) || (toggle == -4)) {
    //SHOW ARROW IMAGE
    strcpy_P(bmpFile, PSTR("/03_arrow.bmp"));
    reader.drawBMP(bmpFile, display, 0, 0);
  }

  if ((toggle == 3) || (toggle == -3)) {
    //SHOW NET IMAGE
    strcpy_P(bmpFile, PSTR("/04_net.bmp"));
    reader.drawBMP(bmpFile, display, 0, 0);
  }

  if ((toggle == 4) || (toggle == -2)) {
    //SHOW FLAME IMAGE
    strcpy_P(bmpFile, PSTR("/05_flame.bmp"));
    reader.drawBMP(bmpFile, display, 0, 0);
  }

  if ((toggle == 5) || (toggle == -1)) {
    //SHOW ICE IMAGE
    strcpy_P(bmpFile, PSTR("/06_ice.bmp"));
    reader.drawBMP(bmpFile, display, 0, 0);
  }
}

//CODE READY FOR PLAYING SOUNDS
void playSound() {
  if ((toggle == 0) && (firstTime != true)) {
    display.setCursor(0, 10);
    display.print(F("Sound 0"));
    delay(500);
  }

  if ((toggle == 1) || (toggle == -5)) {
    display.setCursor(0, 10);
    display.print(F("Sound 1"));
    delay(500);
  }

  if ((toggle == 2) || (toggle == -4)) {
    display.setCursor(0, 10);
    display.print(F("Sound 2"));
    delay(500);
  }

  if ((toggle == 3) || (toggle == -3)) {
    display.setCursor(0, 10);
    display.print(F("Sound 3"));
    delay(500);
  }

  if ((toggle == 4) || (toggle == -2)) {
    display.setCursor(0, 10);
    display.print(F("Sound 4"));
    delay(500);
  }

  if ((toggle == 5) || (toggle == -1)) {
    display.setCursor(0, 10);
    display.print(F("Sound 5"));
    delay(500);
  }
}

Wow, thank you for all the help guys. This is great!

Thanks for the suggestion Hammy, I removed all the Prints...but it didn't help much. Though something to make use of to tidy my code. Thanks also for the suggestions Christop.

I will update my code thanks David_2018.

Koepel, Looks like I'll need to buy a Mega then, hopefully that will sort the issues out. Will a Arduino Mega 2560 have enough memory?

Just on a side note, and not that it matters too much, but is there a way to share one SD Card reader between the two devices rather than having to have an SD Card in each?

Thanks again for all the help, and quick replies. Its much appreciated.

Lewis

I do not think there is any way to access the SD card on the DFPlayer from the Arduino. That would not save you any memory anyway, you would still need the SD library and the DFPlayer library in the code, the only savings would be in the expense of the second SD card.

If you are not going to be using Serial communications over the USB cable to a PC, you should be able to do without SoftwareSerial and use the hardware serial port, with the provision that you would need to disconnect the serial lines to the DFPlayer while programming the arduino.

Thanks David!

A Arduino Mega 2560 has more memory, and you have extra hardware serial ports.

Looking at the DFRobotDFPlayerMini, there are many functions (the code will be in Flash memory), but not large buffers. It does not need a lot of sram.

If you are able to drop the SoftwareSerial, maybe it will work.

It is possible to check how much sram there is in the sketch with the freeMemory() functions: Measuring Memory Usage | Memories of an Arduino | Adafruit Learning System. That function checks how many bytes there are between the bottom of the stack and the top of the heap.

Suppose it works, then after a few weeks (or a few days), you start thinking about adding other things :stuck_out_tongue: Adding sensors, buttons, leds, and so on. Then you still need to upgrade to a Arduino Mega 2560.
The Arduino Uno and Arduino Mega are 5V boards. However, most sensors are 3.3V. When you want to use a couple of sensor for a weather station, it might be better to use a 3.3V Arduino board.

Thanks Koepel, sounds like the easiest and safest route is to get a Mega.

Thanks everyone for the help.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.