Need help with my Arduino OS project

Hi everyone, i’ve been experimenting with my Arduino Mega 2560 and a 2.8” touchscreen + SD card ILI9341 shield that i bought from Robotistan.

I noticed that this shield was super different from the ones on the internet, i spent some time figuring it out.

I discovered that the XP XM YP YM touch pins overlap with the LCD control pins somehow? Here’s what i used:

#define YP A3
#define XM A2
#define YM 9
#define XP 8
TouchScreen ts(XP, YP, XM, YM, 300);

MCUFRIEND_kbv tft(A3, A2, A1, A0, A4);

Since i’m new to the touchscreen + TFT tech i asked AI to give me a simple PDA-like interface as if it was from Windows CE XP.

The code includes a menu, files, settings and an about screens.

Here’s the code i’m using right now:

#include <MCUFRIEND_kbv.h>

#include <TouchScreen.h>

#include <EEPROM.h>




#define TS_MINX 920

#define TS_MINY 120

#define TS_MAXX 150

#define TS_MAXY 940

#define YP A3

#define XM A2

#define YM 9

#define XP 8

#define MINPRESSURE 10

#define MAXPRESSURE 1000




TouchScreen ts(XP, YP, XM, YM, 300);

MCUFRIEND_kbv tft(A3, A2, A1, A0, A4);




// Colors

#define BLACK 0x0000

#define WHITE 0xFFFF

#define RED 0xF800

#define GREEN 0x07E0

#define BLUE 0x001F

#define CYAN 0x07FF

#define MAGENTA 0xF81F

#define GREY 0xC618

#define ORANGE 0xFD20




// Screen States

enum Screen { MENU, FILES, SETTINGS, ABOUT };

Screen currentScreen = MENU;




// Menu buttons

struct Button { int x, y, w, h; uint16_t color; const char* label; };

Button menuButtons[] = {

  {30,60,180,50,ORANGE,"Files"},

  {30,130,180,50,CYAN,"Settings"},

  {30,200,180,50,MAGENTA,"About"},

  {30,270,180,50,GREEN,"Exit"}

};

const int numButtons = sizeof(menuButtons)/sizeof(menuButtons[0]);




// Exit bar

Button exitBar = {200,5,40,20,RED,"X"};




// Files app

uint16_t currentColor = RED;

uint16_t colorPalette[5] = {RED, GREEN, BLUE, CYAN, MAGENTA};

Button colorButtons[5], eraserButton, clearButton, saveButton, loadButton;




// Layout

#define CANVAS_TOP 30

#define CANVAS_BOTTOM 250

#define TOOLBAR_TOP 250

#define TOOLBAR_BOTTOM 320

#define CANVAS_WIDTH 240

#define SCROLLBAR_WIDTH 10




int canvasOffsetY = 0;

#define EEPROM_WIDTH 16

#define EEPROM_HEIGHT 10

int fontSize = 2;




// Prototypes

void drawMenu(), drawFiles(), drawSettings(), drawAbout(), exitOS();

void handleMenuTouch(int,int), handleFilesTouch(int,int), handleSettingsTouch(int,int);

void drawButton(Button), saveCanvasEEPROM(), loadCanvasEEPROM(), drawScrollbar();




void setup(){

  Serial.begin(9600);

  uint16_t ID = tft.readID();

  if(ID==0xD3D3) ID=0x9341;

  tft.begin(ID);

  tft.setRotation(0);

  tft.fillScreen(GREY);




  tft.fillRect(0,0,CANVAS_WIDTH,30,BLUE);

  tft.setTextColor(WHITE); tft.setTextSize(fontSize);

  tft.setCursor(10,8); tft.print("PDA OS");




  drawMenu();




  for(int i=0;i<5;i++) colorButtons[i] = {10 + i*45, TOOLBAR_TOP+5, 40, 30, colorPalette[i], ""};

  eraserButton = {10 + 5*45, TOOLBAR_TOP+5, 40, 30, WHITE, "E"};

  clearButton  = {60 + 5*45, TOOLBAR_TOP+5, 50, 30, GREY, "C"};

  saveButton   = {120, TOOLBAR_TOP+5, 60, 30, ORANGE, "Save"};

  loadButton   = {190, TOOLBAR_TOP+5, 60, 30, CYAN, "Load"};

}




void loop(){

  TSPoint p = ts.getPoint();

  pinMode(XM, OUTPUT); pinMode(YP, OUTPUT);




  if(p.z > MINPRESSURE && p.z < MAXPRESSURE){

    int x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());

    int y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height()) + 10;

    if(y > tft.height()) y = tft.height();




    if(x>=exitBar.x && x<=exitBar.x+exitBar.w && y>=exitBar.y && y<=exitBar.y+exitBar.h){

      exitOS(); return;

    }




    switch(currentScreen){

      case MENU: handleMenuTouch(x,y); break;

      case FILES: handleFilesTouch(x,y); break;

      case SETTINGS: handleSettingsTouch(x,y); break;

      case ABOUT: currentScreen=MENU; drawMenu(); break;

    }

  }

}




void drawButton(Button b){

  tft.fillRoundRect(b.x,b.y,b.w,b.h,8,b.color);

  tft.drawRoundRect(b.x,b.y,b.w,b.h,8,WHITE);

  if(strlen(b.label)>0){

    tft.setTextColor(BLACK); tft.setTextSize(2);

    tft.setCursor(b.x+5,b.y+7); tft.print(b.label);

  }

}




void drawScrollbar(){

  int sbHeight = map(CANVAS_BOTTOM-CANVAS_TOP,0,(CANVAS_BOTTOM-CANVAS_TOP)*2,0,CANVAS_BOTTOM-CANVAS_TOP);

  int sbY = map(canvasOffsetY,0,(CANVAS_BOTTOM-CANVAS_TOP),0,CANVAS_BOTTOM-CANVAS_TOP-sbHeight);

  tft.fillRect(CANVAS_WIDTH-SCROLLBAR_WIDTH, CANVAS_TOP+sbY, SCROLLBAR_WIDTH, sbHeight, GREY);

}




void drawMenu(){

  tft.fillScreen(GREY);

  tft.fillRect(0,0,CANVAS_WIDTH,30,BLUE);

  tft.setTextColor(WHITE); tft.setTextSize(fontSize);

  tft.setCursor(10,8); tft.print("Ahmet OS");

  for(int i=0;i<numButtons;i++) drawButton(menuButtons[i]);

  drawButton(exitBar);

}




void drawFiles(){

  tft.fillRect(0,CANVAS_TOP,CANVAS_WIDTH,CANVAS_BOTTOM-CANVAS_TOP,WHITE);

  for(int i=0;i<5;i++) drawButton(colorButtons[i]);

  drawButton(eraserButton); drawButton(clearButton);

  drawButton(saveButton); drawButton(loadButton);

  drawButton(exitBar);

  drawScrollbar();

}




void drawSettings(){

  tft.fillScreen(GREY);

  tft.setTextColor(WHITE); tft.setTextSize(2);

  tft.setCursor(10,50); tft.print("Settings:");

  tft.setCursor(10,80); tft.print("Font size:");

  tft.fillRect(120,80,40,30,BLUE); tft.setTextColor(WHITE);

  tft.setCursor(125,85); tft.print(fontSize);

  tft.setCursor(10,120); tft.print("Touch to change font size");

  drawButton(exitBar);

}




void drawAbout(){

  tft.fillScreen(GREY); tft.setTextColor(WHITE); tft.setTextSize(2);

  tft.setCursor(10,50); tft.print("Ahmet's Mini OS");

  tft.setCursor(10,80); tft.print("Touch anywhere to go back");

  drawButton(exitBar);

}




void exitOS(){ asm volatile ("  jmp 0"); }




void handleMenuTouch(int x,int y){

  for(int i=0;i<numButtons;i++){

    Button b = menuButtons[i];

    if(x>=b.x && x<=b.x+b.w && y>=b.y && y<=b.y+b.h){

      switch(i){

        case 0: currentScreen=FILES; drawFiles(); break;

        case 1: currentScreen=SETTINGS; drawSettings(); break;

        case 2: currentScreen=ABOUT; drawAbout(); break;

        case 3: exitOS(); break;

      }

    }

  }

}




void handleFilesTouch(int x,int y){

  static int lastX=-1,lastY=-1;

  if(y>=TOOLBAR_TOP){

    for(int i=0;i<5;i++){ Button b=colorButtons[i];

      if(x>=b.x && x<=b.x+b.w && y>=b.y && y<=b.y+b.h){ currentColor=colorPalette[i]; return; }

    }

    if(x>=eraserButton.x && x<=eraserButton.x+eraserButton.w && y>=eraserButton.y && y<=eraserButton.y+eraserButton.h){

      currentColor=WHITE; return;

    }

    if(x>=clearButton.x && x<=clearButton.x+clearButton.w && y>=clearButton.y && y<=clearButton.y+clearButton.h){

      tft.fillRect(0,CANVAS_TOP,CANVAS_WIDTH,CANVAS_BOTTOM-CANVAS_TOP,WHITE); drawFiles(); return;

    }

    if(x>=saveButton.x && x<=saveButton.x+saveButton.w && y>=saveButton.y && y<=saveButton.y+saveButton.h){

      saveCanvasEEPROM(); tft.setCursor(10, TOOLBAR_TOP+40); tft.setTextColor(BLACK); tft.setTextSize(2); tft.print("Saved!"); return;

    }

    if(x>=loadButton.x && x<=loadButton.x+loadButton.w && y>=loadButton.y && y<=loadButton.y+loadButton.h){

      loadCanvasEEPROM(); tft.setCursor(10, TOOLBAR_TOP+40); tft.setTextColor(BLACK); tft.setTextSize(2); tft.print("Loaded!"); return;

    }

  }

  if(y>=CANVAS_TOP && y<CANVAS_BOTTOM){

    if(lastX!=-1 && lastY!=-1) tft.drawLine(lastX,lastY,x,y,currentColor);

    else tft.fillCircle(x,y,2,currentColor);

    lastX=x; lastY=y;

  } else { lastX=-1; lastY=-1; }

}




void handleSettingsTouch(int x,int y){ fontSize++; if(fontSize>5) fontSize=1; drawSettings(); }




void saveCanvasEEPROM(){

  for(int i=0;i<EEPROM_WIDTH;i++){

    for(int j=0;j<EEPROM_HEIGHT;j++){

      int sx=i*(CANVAS_WIDTH/EEPROM_WIDTH), sy=j*((CANVAS_BOTTOM-CANVAS_TOP)/EEPROM_HEIGHT)+CANVAS_TOP;

      uint16_t c=tft.readPixel(sx,sy);

      EEPROM.put((i*EEPROM_HEIGHT+j)*2,c);

    }

  }

}




void loadCanvasEEPROM(){

  for(int i=0;i<EEPROM_WIDTH;i++){

    for(int j=0;j<EEPROM_HEIGHT;j++){

      uint16_t c; EEPROM.get((i*EEPROM_HEIGHT+j)*2,c);

      int sx=i*(CANVAS_WIDTH/EEPROM_WIDTH), sy=j*((CANVAS_BOTTOM-CANVAS_TOP)/EEPROM_HEIGHT)+CANVAS_TOP;

      tft.fillRect(sx,sy,CANVAS_WIDTH/EEPROM_WIDTH,(CANVAS_BOTTOM-CANVAS_TOP)/EEPROM_HEIGHT,c);

    }

  }

  drawFiles();

}

  

What’s working well:

  • Navigation between menus work very well.
  • Paint works half-like (the colors and exit bars etc. are cramped and overlapped on the bottom of the screen)
  • Basic drawing works
    Issues:
  • Tab alignment is bad
  • About tab's text visibility isn't that great
  • EEPROM drawing storage, it has a 16x10 grid. Is there any better way to save and load drawings much faster and better?
  • Pin setup, it looks like i am using the correct pins for the touchscreen, i'm not going to include the screen since that's handled by MCUFRIEND, but i also want to try something else than MCUFRIEND.

I’m new to this type of project and haven’t been able to find tutorials that fit my shield and my goals. Any advice or examples on improving UI alignment, text visibility and drawing storage would be appreciated a lot!

Thanks in advance,
THE_COM3_PORT

Note: my timezone is GMT+3 so i might not see your responses. Excuse me for that:

That is the way the display board was designed, because of the number of pins available on an UNO. You will notice in your code that it is necessary to set the pinMode of two of the pins after reading the touchscreen.

  TSPoint p = ts.getPoint();

  pinMode(XM, OUTPUT); pinMode(YP, OUTPUT);

Step 1 is to use the IDE Auto Format command under the Tools menu. If that leaves the extra blank lines, manually delete them.

These screens may look alike but they seem to change every month or so which means to get any meaningful help you need to provide every link and datasheet you have for the screen and be EXTREMELY precise as to what you want the helpers to do. Keep in mind we are not a free design or coding swrvice. You are expected to make a realistic attempt and then we can help you debug/fine tune it. Good luck.

1 Like

Thank you a lot for the feedback. I meant to ask for advice, i didn't expect anyone to say "oh this part of the code is wrong". I'll do my best! :slight_smile:

You may recall from your first visit here when you read the pinned post re 'How to get the most from the Forum' that properly formatted code is one of the prerequisites to getting assistance.

Hmm, i'll take a look at that. I honestly don't remember much from it. Let me read again, thanks for reminding