Help to stop Arduino Uno Rev3 from looping without the screen being touched

Hello! I am fairly new to Arduino, and have been playing around with the Arduino Uno Rev3 and a 3.5 inch TFT LCD Display compatible with Arduino Uno Rev3, and have a question about coding loops. As I have the code now, the goal was to use the touchscreen to move from one screen the next, until you get to the end the 5 pages, then the loop starts at the first page, and only moves to the next when the screen is touched.

My issue is that once I upload the code to the Arduino, the 5 pages loop continuously, and do not stop. I do not know what I did wrong or what I am missing, and am posting the entirety of my code below.

Thank you very much for your help with this!

#include <Adafruit_GFX.h>    // Core graphics library
#include <MCUFRIEND_kbv.h>
#include <TouchScreen.h>
#include <SPI.h>
#include <SD.h>
int nextMenu = 0;
#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0

#define YP A2
#define XM A3
#define YM 8
#define XP 9

int X, Y;
#define SD_CS 10  //SD card pin on your shield
#define TS_LEFT 126
#define TS_RT   876
#define TS_TOP  88
#define TS_BOT  875
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF
#define YP A3  // must be an analog pin, use "An" notation!
#define XM A2  // must be an analog pin, use "An" notation!
#define YM 9   // can be a digital pin
#define XP 8 

#define TS_MINX 100
#define TS_MINY 120
#define TS_MAXX 940
#define TS_MAXY 920

#define TS_LEFT 126
#define TS_RT   907
#define TS_TOP  78
#define TS_BOT  889
#define MINPRESSURE 10
#define MAXPRESSURE 1000
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); 
MCUFRIEND_kbv tft;

TSPoint waitTouch() {
  TSPoint p;
  do {
    p = ts.getPoint();
    pinMode(XM, OUTPUT);
    pinMode(YP, OUTPUT);
  } while ((p.z < MINPRESSURE ) || (p.z > MAXPRESSURE));
  
  p.x = map(p.x, TS_LEFT, TS_RT, 0, 240);
  p.y = map(p.y,TS_TOP, TS_BOT, 0, 320);
  
  return p;
}
void setup(){

 
  Serial.begin(9600);
  
  uint16_t identifier = tft.readID();
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  tft.begin(identifier);
  if (!SD.begin(SD_CS)) {
  progmemPrintln(PSTR("failed!"));
  return;
  }

  tft.reset();
  
  tft.begin(identifier);
  tft.setRotation(3);
  tft.invertDisplay(0);
  
  tft.fillScreen(BLACK);  
  tft.setTextSize(2);    
  tft.setTextColor(GREEN); 
  tft.setCursor(150, 145);  
  tft.print("Initializing...");
  delay(500); 

  tft.fillScreen(BLACK);  
  tft.setTextSize(2);     
  tft.setTextColor(GREEN); 
  tft.setCursor(150, 145);  
  tft.print("Initializing...");
  delay(500); 

  tft.fillScreen(BLACK);  
  tft.setTextSize(2);    
  tft.setTextColor(GREEN); 
  tft.setCursor(150, 145);  
  tft.print("Initializing...");
  delay(500); 

  
  tft.fillScreen(BLACK); 
  
    tft.setTextSize(1);
  for (int i = 0; i < 15; i += 7) {
    tft.setCursor(0, i * 16); 
    tft.println("DLP Vault Pip Boy initializing… \n\nCode:01100100000_101110_1110100011_001001100 _ Code complete \n\nStarting the main screen_DLP's Vault \n\nWelcome to DLP's Vault \n\nThe date is 05/07/2289, 12:22, Have a LOVELY day!");
    delay(500);
  }

  tft.fillScreen(BLACK); // Clear the screen before drawing image

  bmpDraw("stat.bmp", 0, 0);
     //Calling the bmpDraw function ("Name_of_your_image.bmp",x,y) (x,y) is the starting position of the picture drawing
}

void loop(){
   
  //bmpDraw("mone.bmp", 0, 0);      //Calling the bmpDraw function ("Name_of_your_image.bmp",x,y) (x,y) is the starting position of the picture drawing
 
TSPoint p = waitTouch();

  X = p.x; Y = p.y;
  Serial.println(X);
  Serial.println(Y);
  if(X <= 240 || Y <= 320){
    nextMenu++;
  }
  if(nextMenu == 0){
    bmpDraw("stat.bmp", 0, 0);
  }
  if(nextMenu == 1){
    bmpDraw("inv.bmp", 0, 0);
  }
  if(nextMenu == 2){
    bmpDraw("data.bmp", 0, 0);
  }
   if(nextMenu == 3){
    bmpDraw("map.bmp", 0, 0);
  }
   if(nextMenu == 4){
    bmpDraw("radio.bmp", 0, 0);
  }
  if(nextMenu == 5){
    nextMenu = -1;
  }
}

#define BUFFPIXEL 20           //Drawing speed, 20 is meant to be the best but you can use 60 altough it takes a lot of uno's RAM         

//Drawing function, reads the file from the SD card and do the 
//conversion and drawing, also it shows messages on the Serial monitor in case of a problem
//No touchy to this function :D

void bmpDraw(String filename, int x, int y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H in pixels
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset;        // Start of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel in buffer (R+G+B per pixel)
  uint16_t lcdbuffer[BUFFPIXEL];  // pixel out buffer (16-bit per pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();
  uint8_t  lcdidx = 0;
  boolean  first = true;

  if((x >= tft.width()) || (y >= tft.height())) return;

  Serial.println();
  progmemPrint(PSTR("Loading image '"));
  Serial.print(filename);
  Serial.println('\'');
  // Open requested file on SD card
  if ((bmpFile = SD.open(filename)) == NULL) {
    progmemPrintln(PSTR("File not found"));
    return;
  }

  // Parse BMP header
  if(read16(bmpFile) == 0x4D42) { // BMP signature
    progmemPrint(PSTR("File size: ")); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    progmemPrint(PSTR("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
    // Read DIB header
    progmemPrint(PSTR("Header size: ")); Serial.println(read32(bmpFile));
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel
      progmemPrint(PSTR("Bit Depth: ")); //Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

        goodBmp = true; // Supported BMP format -- proceed!
        progmemPrint(PSTR("Image size: "));
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);

        // BMP rows are padded (if needed) to 4-byte boundary
        rowSize = (bmpWidth * 3 + 3) & ~3;

        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        // Set TFT address window to clipped image bounds
        tft.setAddrWindow(x, y, x+w-1, y+h-1);

        for (row=0; row<h; row++) { // For each scanline...
          // Seek to start of scan line.  It might seem labor-
          // intensive to be doing this on every line, but this
          // method covers a lot of gritty details like cropping
          // and scanline padding.  Also, the seek only takes
          // place if the file position actually needs to change
          // (avoids a lot of cluster math in SD library).
          if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // Bitmap is stored top-to-bottom
            pos = bmpImageoffset + row * rowSize;
          if(bmpFile.position() != pos) { // Need seek?
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
          }

          for (col=0; col<w; col++) { // For each column...
            // Time to read more pixel data?
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              // Push LCD buffer to the display first
              if(lcdidx > 0) {
                tft.pushColors(lcdbuffer, lcdidx, first);
                lcdidx = 0;
                first  = false;
              }
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
            }

            // Convert pixel from BMP to TFT format
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            lcdbuffer[lcdidx++] = tft.color565(r,g,b);
          } // end pixel
        } // end scanline
        // Write any remaining data to LCD
        if(lcdidx > 0) {
          tft.pushColors(lcdbuffer, lcdidx, first);
        } 
        progmemPrint(PSTR("Loaded in "));
        Serial.print(millis() - startTime);
        Serial.println(" ms");
      } // end goodBmp
    }
  }

  bmpFile.close();
  if(!goodBmp) progmemPrintln(PSTR("BMP format not recognized."));
}

// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

uint16_t read16(File f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(File f) {
  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;
}

// Copy string from flash to serial port
// Source string MUST be inside a PSTR() declaration!
void progmemPrint(const char *str) {
  char c;
  while(c = pgm_read_byte(str++)) Serial.print(c);
}

// Same as above, with trailing newline
void progmemPrintln(const char *str) {
  progmemPrint(str);
  Serial.println();
}

When you touch the screen (or not) what do these two lines print out, and what did you learn from that information?

  X = p.x; Y = p.y;
  Serial.println(X);
  Serial.println(Y);

Looking carefully at this code,

  p.x = map(p.x, TS_LEFT, TS_RT, 0, 240);
  p.y = map(p.y,TS_TOP, TS_BOT, 0, 320);

can you imagine a case in which this statement is not true?

  if(X <= 240 || Y <= 320){
1 Like

For your first question, the code below begins the looping sequence of the 5 .bmp images right below it without stopping, regardless of whether i touch the screen or not.

X = p.x; Y = p.y;
Serial.println(X);
Serial.println(Y);

For your second question, I am truly not sure what the answer should be... I do not know lol, I'm sorry!

  p.x = map(p.x, TS_LEFT, TS_RT, 0, 240);
  p.y = map(p.y,TS_TOP, TS_BOT, 0, 320);
  if(X <= 240 || Y <= 320){

I'll repeat the question:

Please post examples of the printout. If the printout happens when you are not touching the screen, then there is a fundamental problem (wiring, hardware, etc.).

I am truly not sure what the answer should be

At some point, you will need to learn what lines like that do, and one place to start is to look up the map function in the Arduino reference.

1 Like

Welcome to the forum.
The Arduino Uno R3 has 2kbyte of SRAM. Looking at your code, you might need about 10kbyte of SRAM.

Thank you! Is this something as simple as getting a new SD card with at least 10kbytes of SRAM?

SRAM is part of the processor and cannot be changed. @Koepel has a good point, although it is not clear how much SRAM is actually required.

  1. Post a link to the actual display.

  2. Try running an unmodified touchscreen example from the display library.

  3. Answer the question of what is printed on the serial monitor.

1 Like

Whoa, a fellow vault dweller, eh? Not so fast, sport!
So it's a Robco Personal Information Processor you're building.
Ok, here's what I need from you to give me the sight like Mama Murphy: I need a bulleted list

  • that tells me what features you're covering
  • what happens inside each feature
  • what is the exit condition to break out of each feature

I'm going to go out on a limb here and assume you're just picking some of the Pip-Boy menus and submenus. Fulfill with this request and I can help keep you safely away from the rads...or the Reds.

coop

I've pasted this into my own IDE and right off the bat, this section in void loop()

  if (X <= 240 || Y <= 320) {
    nextMenu++;
  }
  if (nextMenu == 0) {
    bmpDraw("stat.bmp", 0, 0);
  }
  if (nextMenu == 1) {
    bmpDraw("inv.bmp", 0, 0);
  }
  if (nextMenu == 2) {
    bmpDraw("data.bmp", 0, 0);
  }
  if (nextMenu == 3) {
    bmpDraw("map.bmp", 0, 0);
  }
  if (nextMenu == 4) {
    bmpDraw("radio.bmp", 0, 0);
  }
  if (nextMenu == 5) {
    nextMenu = -1;
  }

You don't want to use if/else if/else in a scenario like this. The trick to gamifying Arduino code is to create a finite state machine replacing if (whatever) with a program driving variable that you might declare globally

byte pipMode;

Then create a function such as

void pipboyReset(){
 pipMode = 0;
// whatever other variables you initialized to their starting value, probably
// in void setup()
}

Then call pipboyReset(); at the end of void setup() and again after whatever condition loops back to the start of the "game" Think of it like Super Mario Bros: there's levels 1-1, 1-2, 1-3 and so forth, at the end of each level you jump on the flagpole, it performs a score adding function, runs a little animation and sets a variable (pipMode in this example), to tell the program what level should come next.
There's also another variable at play: one that tells the program if you're just starting or restarting the program, either because you pressed the RESET button on the NES, or because you ran out of lives, or you defeated Bowser and saved the Princess.

So instead of if(//stuff){} if (other stuff){} // etc, use

// outside of switch/case keep checking whatever causes variable pipMode to change
// either increment, decrement or flat out assign it as needed when you turn the dial, // // press a button, whatever
switch(pipMode){
case 0:
// do stuff
break;
case 1:
// do other stuff
break;
case 2:
// a byte variable of course gives you 256 levels or as few as needed
break;
default: 
// default's optional, you might make your default simply pipMode = 0; instead
// (the ready and waiting state after you power up/reset as described above)
break;
}

You can even nest these switch/case structures, as I did in my Fallout Terminal. Take a look, this should seem familiar and give you a better idea of everything I just recommended. This is the bulk of void loop() in my Terminal sketch (except for checking some buttons and flashing some LEDs):

void loop(){
// blinky leds
//checking buttons which, like Serial inputs, also drive terminal screens and game //progress
 switch (gameControl) {
    case 0:
      if (Serial.available()) {
        char input = Serial.read();
        switch (input) {
          case '+': //run robcoAddOneProtocol to add one chance
            password.reset();
            currentLength = 0;
            numberChances += 1;
            if (numberChances > 4) {
              numberChances = 4;
            }
            robcoAddOneProtocol();
            break;
          case '!': //reset password
            password.reset();
            currentLength = 0;
            Serial.println(F("             > PASSWORD RESET < "));
            Serial.println(F("          Password Required "));
            printGameState();
            break;
          case '?': //run robcoInquiryProtocol in case we need the instructions
            password.reset();
            currentLength = 0;
            robcoInquiryProtocol();
            break;
          case '@': //Eden's journal
            edensHacksMenu();
            break;
          case '\r': //evaluate password
            if ((password.evaluate()) && (numberChances > 0)) {
              rightAnswer();
            }

            /* if you have no chances left (numberChances), straight to terminalLocked()
              with no prettyprinting */
            else if (numberChances == 0) {
              terminalLocked();
            }

            /* first answer was wrong, still have 3 chances remaining */
            else if ((!password.evaluate()) && (numberChances > lastAttempt) && (numberChances == 4)) {
              numberChances -= 1;
              switch (gameDifficulty) {
                case 1:
                  firstWrongEasyAnswer();
                  break;
                case 2:
                  firstWrongHardAnswer();
                  break;
                default:
                  firstWrongEasyAnswer();
                  break;
              }
            }

            /* second answer was wrong, still have 2 chances remaining */
            else if ((!password.evaluate()) && (numberChances > lastAttempt) && (numberChances == 3)) {
              numberChances -= 1;
              switch (gameDifficulty) {
                case 1:
                  secondWrongEasyAnswer();
                  break;
                case 2:
                  secondWrongHardAnswer();
                  break;
                default:
                  secondWrongEasyAnswer();
                  break;
              }
            }

            /* third answer was wrong, still have 1 chance remaining */
            else if ((!password.evaluate()) && (numberChances > lastAttempt) && (numberChances == 2)) {
              numberChances -= 1;
              lockoutImminent();
              //thirdWrongAnswer() gives the easyPeasy game list
              switch (gameDifficulty) {
                case 1:
                  thirdWrongEasyAnswer();
                  break;
                case 2:
                  thirdWrongHardAnswer();
                  break;
                default:
                  thirdWrongEasyAnswer();
                  break;

              }
            }

            /* last chance! */
            else if ((!password.evaluate()) && (numberChances > lastAttempt) && (numberChances == 1)) {
              terminalLocked();
              numberChances -= 1;
            }

            /* if numberChances is zero, straight to locked out */
            else if ((password.evaluate()) && (numberChances == 0)) {
              terminalLocked();
            }

            /* if you tried to enter a response when already at zero chances... */
            else if ((!password.evaluate()) && (numberChances == 0)) {
              terminalLocked();
            }
            break;
          default: //append any keypress that is not a '!' nor a '?' to the currently guessed password.
            password << input;
            Serial.print('*'); // TO SHOW WE'RE TYPING SOMETHING
            currentLength++;
            if (currentLength > 9) {
              password.reset(); // to avoid button mashing
              currentLength = 0;
              Serial.println();
              Serial.println(F("             >> Error"));
              Serial.println();
              Serial.println(F("         >> Input exceeds RX buffer"));
              Serial.println();
              Serial.println(F("         >> Key in valid password entry"));
              Serial.println();
              delay(1500);
              //wait for all the garbage
              discardIncomingSerial();
              buttonMashProtocol(); // reprint the word list/logo
              break; // now get outta here and try again
            }
        }
      }
      break;
    case 1:
      edensHacksMenu();
      break;
  }
}

You, my friend, are wonderful. I will be giving your suggestions a shot tonight! Thank you!!!

Are you use A2 and A3 pins simultaneously for LCD and touch?

1 Like