Serial communication between an Arduino MEGA 2560 & UNO (w/ TFT shield screen)

Hi everybody,

We're making a miniature vending machine, driven by an Arduino MEGA. Basically consists of a TFT screen, a select & a down button to navigate the menu, 4 stepper motors that drive the spiral vending mechanism and an IR sensor to check whether the product has been vended.

However, the screen is a shield which takes up almost all of the pins on an Arduino UNO board, although digital pins 1 and 0 are free. So we decided to try and make our Arduinos communicate by linking the TX and RX pins and using Serial.

We want to send instructions to the UNO to display a certain bitmap file on the screen when a button is pressed. The code we've got so far hasn't been very successful, and we're wondering whether anybody has tried to do something similar before? Or failing that, just take a look at our code so far and tell us if there are any glaring errors we're missing.

This code has just got one button coded to try and change the screen a few times. It uploads fine but the button doesn't have any effect on the bitmap being displayed.

Master code:

int sbutton = 6; // select button

int outgoingByte; // variable for serial communication

int sbuttonState = 0;

void setup() {
 
 Serial.begin(9600);   // start serial between MEGA and UNO
 
 pinMode(sbutton, INPUT);   // define select button
 
}

void loop() {
  
  sbuttonState = digitalRead(sbutton); // resets buttons to 0
  while(sbuttonState ==LOW){
    sbuttonState = digitalRead(sbutton);
  }
  
  if (sbuttonState==HIGH)     // select button pressed
  {outgoingByte==1;            // How can I help? SUPPLIES comes up
     Serial.write(outgoingByte);
    
      sbuttonState = digitalRead(sbutton); //button reset
  while(sbuttonState ==LOW){
    sbuttonState = digitalRead(sbutton);}
}
  
  if (sbuttonState==HIGH) // if supplies chosen
  {
    outgoingByte ==7; //bring up supplies menu : chocolate
    
   sbuttonState = digitalRead(sbutton); //button reset
  while(sbuttonState ==LOW){
    sbuttonState = digitalRead(sbutton);
    }
  }
}

Screen code:

#include <SD.h>
#include "TFTLCD.h"


#define LCD_CS A3    
#define LCD_CD A2    
#define LCD_WR A1    
#define LCD_RD A0   
#define LCD_RESET A4
//Duemilanove/Diecimila/UNO/etc ('168 and '328 chips) microcontoller:

#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 SD_CS 10     // Set the chip select line to whatever you use (10 doesnt conflict with the library)

// our TFT wiring
TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

// the file itself
File bmpFile;

// information we extract about the bitmap file
int bmpWidth, bmpHeight;
uint8_t bmpDepth, bmpImageoffset;

//int loads = 0;

#define ROTATION 3
#define BUFFPIXEL 60
#define BYTES_PER_PIXEL 3

uint8_t picBuffer[BYTES_PER_PIXEL * BUFFPIXEL];  // 3 * pixels to buffer
int bufferIndex = BYTES_PER_PIXEL * BUFFPIXEL;

int incomingByte;

void setup()
{
  Serial.begin(9600);

  tft.reset();
  tft.initDisplay(); 

  tft.setRotation(ROTATION);
  

  Serial.print("Initializing SD card...");
  pinMode(10, OUTPUT);

  tft.setRotation(ROTATION);
  
  if (!SD.begin(10)) {
    Serial.println("failed!");
    while(1);
  }
  Serial.println("SD OK!");
 
}


/*
  Callback for displayBitmap function to load data into buffer
*/
unsigned int loadBitmapData(void *data)
{
  File *sdFile = (File *) data;
  int bytesRead = sdFile->read(picBuffer, 2 * BUFFPIXEL);
  return bytesRead / 2;
}


/*
  Loads a raw 16-bit bitmap with the format
  
  4-byte width
  4-byte height
  raw 16-bit pixel data in R5G6B5 format
  
*/

void displayBitmap(const char *name, int x, int y)
{
  File sdFile = SD.open(name);
  
  if( ! sdFile )
  {
    Serial.println("Error opening file.");
    return; 
  }
  
  uint32_t width = read32( sdFile );
  uint32_t height = read32( sdFile );
  
  Serial.print("Width: ");
  Serial.println(width, DEC);
  
  uint32_t time = millis();
  uint16_t bx, ex, by, ey;
  
  tft.setViewport(x, y, x + width - 1, y + height - 1);
  tft.goTo(x, y);
 
  int bytesRead = ( loadBitmapData(&sdFile) );
  tft.bulkWrite( (uint16_t *) picBuffer, bytesRead, loadBitmapData, &sdFile);
  
 // tft.setViewport(bx, ex, by, ey);
  tft.setDefaultViewport();
  
  Serial.print(millis() - time, DEC);
  Serial.println(" ms");
  
  sdFile.close();
}


/*
 Slightly modified routine for drawing bitmaps (From original samples).
 
 Watch out, as this one uses some global variables
*/
void bmpdraw(const char *name, int x, int y) {

  File sdFile = SD.open(name);
  if( ! sdFile )
  {
    Serial.println("Error opening file.");
    return; 
  }  
  
  if( ! bmpReadHeader(sdFile) )
  {
    Serial.println("Error reading header.");
    return; 
  }
  
  sdFile.seek(bmpImageoffset);
  
  uint32_t diskTime = 0;
  uint32_t tempTime;
  
  uint32_t time = millis();
  uint16_t p;
  uint8_t g, b;
  int i, j;
 
  Serial.print("rotation = "); Serial.println(tft.getRotation(), DEC);
  
  tft.goTo(0,0);
  for (i=0; i< bmpHeight; i++) {
    // bitmaps are stored with the BOTTOM line first so we have to move 'up'
    tft.goTo(0, bmpHeight - i - 1);

    for (j=0; j<bmpWidth; j++) {
      // read more pixels

      if (bufferIndex >= BYTES_PER_PIXEL * BUFFPIXEL) {
        tempTime =  millis();
        sdFile.read(picBuffer, BYTES_PER_PIXEL * BUFFPIXEL);
        diskTime += millis() - tempTime;
        bufferIndex= 0;
      }
      
      p = tft.Color565( picBuffer[bufferIndex+2], picBuffer[bufferIndex+1], picBuffer[bufferIndex]);
      bufferIndex += 3;

      tft.writeData(p);
    }
  }
 
 uint32_t totalTime = millis();
 Serial.print("Total time: ");
 Serial.println(totalTime - time, DEC);
 Serial.print("Disk Time: ");
 Serial.println(diskTime, DEC);
 
 sdFile.close();
}


/*
  From example code.  Reads bitmap header.  Uses global variables.

*/
boolean bmpReadHeader(File f) {
   // read header
  uint32_t tmp;
  
  if (read16(f) != 0x4D42) {
    // magic bytes missing
    return false;
  }
 
  // read file size
  tmp = read32(f);  
  Serial.print("size 0x"); Serial.println(tmp, HEX);
  
  // read and ignore creator bytes
  read32(f);
  
  bmpImageoffset = read32(f);  
  Serial.print("offset "); Serial.println(bmpImageoffset, DEC);
  
  // read DIB header
  tmp = read32(f);
  Serial.print("header size "); Serial.println(tmp, DEC);
  bmpWidth = read32(f);
  bmpHeight = read32(f);

  
  if (read16(f) != 1)
    return false;
    
  bmpDepth = read16(f);
  Serial.print("bitdepth "); Serial.println(bmpDepth, DEC);


  Serial.print("compression "); Serial.println(tmp, DEC);

  return true;
}


/*********************************************/

// These read data from the SD card file and convert them to big endian 
// (the data is stored in little endian format!)


// LITTLE ENDIAN!

uint16_t read16(File &f) {
  uint16_t d;
  uint8_t b;
  b = f.read();
  d = f.read();
  d <<= 8;
  d |= b;
  return d;
}


// LITTLE ENDIAN!
uint32_t read32(File &f) {
  uint32_t d;
  uint16_t b;
 
  b = read16(f);
  d = read16(f);
  d <<= 16;
  d |= b;
  return d;
}




void loop()
{
  
   bmpdraw("pdt.bmp", 0, 0); 
   
     if (Serial.available() > 0) {
                 // read the incoming byte:
                 incomingByte = Serial.read();
                 
                  if (incomingByte==1)
                 {
                  tft.fillScreen(WHITE);
                  bmpdraw("ssup.bmp", 0, 0);                 // 24-bit bottom-up display
                  delay(3000);
                 }
}
                 
  if (Serial.available() > 0) {
                read the incoming byte:
                incomingByte = Serial.read();
                  if (incomingByte==7)
                 {
                   tft.fillScreen(WHITE);
                  bmpdraw("choc.bmp", 0, 0);     // 24-bit bottom-up display
                  delay(3000);
                 }  
}
}

There is a lot of code in the screen code that was provided by a blogger who had used our particular ebay bought tft screen before. The screen works fine when it is programmed directly.

Apologies for this being quite long and confusing. Please do ask if I've omitted any details you'd need to know to start offering suggestions.

Thanks!

Just a technicality but could be important. Your statement that the shield takes up almost all the pins appears to be your way of saying you cannot access the pins not used by the shield because the shield is after all , a display and has no headers but if you ignore that , the fact of the matter is that the shield is NOT USING most of the other pins , only blocking access to them. So if you were to rephrase question correctly , would it be:
Is there a way to access pins from the side instead of from the top to deal with a scenario where the header pins are occupied by a shield ? For example is there a shield that has screw terminals (like the green phoenix screw terminals) coming out the side so I can
still plug my display shield in the top but still use the pins not actually used by the display ?
Would that be your question ?

Well not exactly, we've realised the screen isn't using all of the available pins on the Arduino but it is using too many for us to have all of our components plugged into one board (4 motor driver boards & buttons to plug in too).

We have soldered wires to the header pins in the TX and RX pins, so they can be utilised. However we can't seem to get the UNO to receive anything from the MEGA despite this.

So our question is more to do with why that might be :slight_smile:

(screen in question):

http://www.ebay.co.uk/itm/321326402157?ssPageName=STRK:MEWNX:IT&_trksid=p3984.m1439.l2649

Have you considered using I/O expanders to get more I/O ?

raschemmel:
Have you considered using I/O expanders to get more I/O ?

Not entirely sure what they are, if they need I2C then we don't have the correct pins available (we tried to communicate with I2C before and had problems).

There is absolutely no reason for that. What do you know about using I2C ? Did you research it before you tried it ? How did you connect it ? Did you use any pullup resistors ? (what value ?)

raschemmel:
There is absolutely no reason for that. What do you know about using I2C ? Did you research it before you tried it ? How did you connect it ? Did you use any pullup resistors ? (what value ?)

Not a lot, we have only just started using Arduino in a undergraduate course electronics module. We looked up online tutorials detailing how to get the Arduino's to communicate and it recommended using I2C. We only got as far as coding it, didn't actually connect it because we were told by our tutor that we wouldn't be able to, with the pins we had available.

You believe everything he tells you ?
post a schematic.
tell us how you WOULD HAVE tried to connect IF YOU HAD DONE IT. You have not given one shred of proof to justify not being able to use I2C . Give us a list of all the pins you are using and for what.

we were told by our tutor that we wouldn't be able to, with the pins we had available.

I seriously doubt your tutor can hold a candle to the massive database of knowledge constituted by a global network of arduino users who range from hobbyist to scientist with every possible occupation in between, running , I might add 24/7/365 worldwide.
I just helped a guy in Italy using google translate. If you want to show up your tutor, give us the info we need and we'll tell you if it is possible or not. So far, I don't see any reason why not, but I don't have all the data.