2.8" tft shield incompatible with SoftwareSerial?

For other supported Arduino products from Adafruit: Shields, accessories, etc.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
compengineer
 
Posts: 4
Joined: Mon Apr 22, 2013 3:42 pm

2.8" tft shield incompatible with SoftwareSerial?

Post by compengineer »

Hi, I am working on a project involving two Arduino boards. One board utilizes a DFRobot GPS shield and a Arduino WiFi shield. Due to the massive number of pins used by the Adafruit TFT shield, I chose to use a second Arduino board just to display information. I am attempting to use serial communication from one board to the other so that the display will update on one arduino with information received from the other. I was successfully loading bmp files from the SD card using the example provided. I also proved that I could transfer a bitmap from PC to Arduino through the serial port (USB), save it to the SD, then load it from the SD. Now I am attempting to use protoshields and SoftwareSerial to do the comms between the two Arduino boards instead of using the USB port, but I'm having an issue. I can have a sketch working perfectly to display a bmp file on the tft display and by only adding #include <SoftwareSerial.h>, the program will fail. The bmp will display without the include statement, but not with it. Is there a known compatibility issue with Adafruit libraries and SoftwareSerial?

User avatar
adafruit_support_bill
 
Posts: 88091
Joined: Sat Feb 07, 2009 10:11 am

Re: 2.8" tft shield incompatible with SoftwareSerial?

Post by adafruit_support_bill »

No incompatibility that I am am aware of, but it sounds like you have a lot of stuff going on there and you may be running out of SRAM. Display buffers, serial buffers, SD file buffers all consume a lot of SRAM and there is only 2K to begin with on the Atmega-328. You may be able to optimize enough to make it work. The Mega is another option (although display performance will be slower on the Mega).

http://itp.nyu.edu/~gpv206/2008/04/maki ... o_mem.html

compengineer
 
Posts: 4
Joined: Mon Apr 22, 2013 3:42 pm

Re: 2.8" tft shield incompatible with SoftwareSerial?

Post by compengineer »

There's not really that much going on with this device, though. It's just an uno with tft shield. The sketch loads a BMP from the SD card on the tft shield and it works fine until adding one simple include statement (#include <SoftwareSerial.h>. This is without even adding any code that uses SoftwareSerial.

User avatar
adafruit_support_bill
 
Posts: 88091
Joined: Sat Feb 07, 2009 10:11 am

Re: 2.8" tft shield incompatible with SoftwareSerial?

Post by adafruit_support_bill »

Post the code you are using.

compengineer
 
Posts: 4
Joined: Mon Apr 22, 2013 3:42 pm

Re: 2.8" tft shield incompatible with SoftwareSerial?

Post by compengineer »

Code: Select all

/**
 * Senior Design II Project
 * Display module for Backpack GPS Coordinate Transmitter
 *   This will confirm emergency transmission and display
 *   map to safe location when received from BaseStation
 * @author compengineer
 * @date   April 20, 2013
 */


#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_TFTLCD.h> // Hardware-specific library
#include <SD.h>              // SD library

boolean inDebug = false;

#define SD_CS 5 // Card select for shield use

// Assign human-readable names to some common 16-bit color values:
#define	BLACK   0x0000
#define	BLUE    0x001F
#define	RED     0xF800
#define	GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

Adafruit_TFTLCD tft;             // to control the display
uint8_t spi_save;                // hold value of pin settings in arduino
String inputString = "";         // a string to hold incoming data
char fileName[12];               // a string to hold bmp filename to be displayed
boolean stringComplete = false;  // whether the string is complete
boolean isTransmitted = false;   // whether the alert has just been transmitted
boolean isNewFile = false;       // whether a new file to load has been sent
File dataFile;

void setup()
{
  Serial.begin(4800);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
  
  // reset the display to make it ready for use
  tft.reset();
  
  // get the identifier of the display
  uint16_t identifier = tft.readID();
 
  // start the display and the sd card
  tft.begin(identifier);
  if (!SD.begin(SD_CS))
  {
    if(inDebug)Serial.println("SD Card Failed to initialize");
    return;
  }
  spi_save = SPCR; // save pin settings for switching between SD/TFT

  tft.fillScreen(BLACK); // black the screen
  bmpDraw("ghosts.bmp", 0, 0); // show the GhostAssembly Logo
  tft.setRotation(1); // turn the rotation for better display
  delay(1000); // wait a second
}

void loop()
{
  // if a new file has been received, display it
  if (isNewFile)
  {
    bmpDraw(fileName, 0, 0);
    // clear the string:
    inputString = "";
    stringComplete = false;
    isNewFile = false;
    isTransmitted = false;
  }
  
  // if alert is transmitted, confirm it to the user
  if(isTransmitted)
  {
    inputString = "";
    alerted();
    delay(3000);
  }
  
  // if enter button is pressed, clear it
  if(stringComplete)
  {
    inputString = "";
    stringComplete = false;
  }
}

/**
 * SerialEvent occurs whenever a new data comes in the
 * hardware serial RX.  This routine is run between each
 * time loop() runs, so using delay inside loop can delay
 * response.  Multiple bytes of data may be available.
 */
void serialEvent()
{
  
  while (Serial.available())
  {
    // get the new byte:
    char inChar = (char)Serial.read();
    
    // add the new char to the inputString:
    inputString += inChar;
    
    if (inChar == '\n')
    {
      stringComplete = true;
    }
    
    // if a new bitmap file is being sent, get the file name and save the next
    // datastream that comes in as that filename.
    if (inputString.endsWith(".BMP") || inputString.endsWith(".bmp"))
    {
      inputString.toCharArray(fileName,inputString.length()+1);
      inputString = "";
      saveFile();
    }
    
    // if an alert is sent, confirm it by displaying a message on the screen
    else if (inputString.equals("SENT!"))
    {
      isTransmitted = true;
      inputString = "";
    }
  }
}

void saveFile()
{
  int i = 0;  // used to count bytes to make sure data is written to the card every so often
  SPCR = spi_save; // sets pin mode of arduino to access SD card instead of display
  
  if((dataFile = SD.open(fileName, FILE_WRITE)) == NULL)
  {
    return; // if the file can't be opened, don't bother with the rest.
  }
  uint8_t inByte;  // stores the incoming byte
  uint8_t stuffed; // when an escape character is found this is used to capture the next 
                   // "stuffed" byte
                   
  while (true) {   // run until told to stop
    while (!(Serial.available())); // wait for data to be sent
    inByte = (uint8_t) Serial.read(); // get the next byte from the serial port
    
    i++; // counting bytes read to know when to save file
    
    // close file to save what has been written so far, then open it again.
    if(i%50000 == 0) {
      dataFile.close();
      delay(100);
      dataFile = SD.open(fileName, FILE_WRITE);
    }
    
    // if "space" character (one of the escape characters) is found, get the next byte and 
    // subtract 36 from it to get the real byte value that should be written to the file.
    if (inByte == 32) {
      while (!(Serial.available()));
      stuffed = Serial.read();
      inByte = stuffed - 36;
    }
    
    // if "!" character (another escape character) is found, get the next byte and add 128
    // to it to get the real byte value that should be written to the file.
    else if(inByte == 33) {
      while (!(Serial.available()));
      stuffed = Serial.read();
      inByte = stuffed + 128;
    }
    
    // if double-quote character is found, get the next byte and add 64 to it to get the
    // real byte value that should be written to the file.
    else if(inByte == 34) {
      while (!(Serial.available()));
      stuffed = Serial.read();
      inByte = stuffed + 64;
    }
    
    // finally, if the "#" character is found, we know that it is time to jump out of the
    // loop and close the file because we have reached the end of it.
    else if(inByte == 35) {
      break;
    }
    dataFile.write((char)inByte); // write the byte value to the file.
  }
  dataFile.close(); // close the file.
  isNewFile = true; // set flag to let main loop know that the new file is on the SD card.
}

/** 
 * This function opens a Windows Bitmap (BMP) file and
 * displays it at the given coordinates.  It's sped up
 * by reading many pixels worth of data at a time
 * (rather than pixel by pixel).  Increasing the buffer
 * size takes more of the Arduino's precious RAM but
 * makes loading a little faster.  20 pixels seems a
 * good balance.
 */
#define BUFFPIXEL 20

void bmpDraw(char *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  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;
  
  // Open requested file on SD card
  SPCR = spi_save;
  if ((bmpFile = SD.open(filename)) == NULL)
  {
    return;
  }

  // Parse BMP header
  if(read16(bmpFile) == 0x4D42) // BMP signature
  {
    (void)read32(bmpFile);
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    // Read DIB header
    (void)read32(bmpFile); // Read & ignore header size
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) // # planes -- must be '1'
    {
      bmpDepth = read16(bmpFile); // bits per pixel
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) // 0 = uncompressed
      {
        
        // 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 a law, but has been observed to be true sometimes.
        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
        SPCR = 0;
        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;
          SPCR = spi_save;
          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)) // Why yes, it is!
            {
              // Push LCD buffer to the display first
              if(lcdidx > 0)
              {
                SPCR   = 0;
                tft.pushColors(lcdbuffer, lcdidx, first);
                lcdidx = 0;
                first  = false;
              }
              SPCR = spi_save;
              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)
        {
          SPCR = 0;
          tft.pushColors(lcdbuffer, lcdidx, first);
        } 
      }
    }
  }
  bmpFile.close();
}

/**
 * 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();
}

/**
 * Displays message on screen to show that alert was transmitted
 */
void alerted()
{
  SPCR = 0;
  tft.fillScreen(BLACK);
  tft.setCursor(0, 0);
  tft.setTextColor(GREEN);
  tft.setTextSize(3);
  tft.println();
  tft.println(" Emergency Alert\n   Transmitted\n");
  tft.setTextColor(RED);
  tft.setTextSize(2);
  tft.println("         Awaiting\n   Map-To-Safe-Location\n           From\n       Base Station");
}
This sketch causes "ghosts.bmp" which is stored on the SD card to be displayed on the tft screen during setup. It does this properly as is, but if I simply add the line #include <SoftwareSerial.h> to the list of include statements, the image does not get displayed.

User avatar
adafruit_support_bill
 
Posts: 88091
Joined: Sat Feb 07, 2009 10:11 am

Re: 2.8" tft shield incompatible with SoftwareSerial?

Post by adafruit_support_bill »

That is quite a lot for a processor with a Harvard Architecture and only 2K of SRAM. 512 bytes for an SD buffer, 64 for a software serial buffer, 200 bytes of input string reserved, 150 or so bytes of literal strings, 80 bytes of pixel buffering etc. Remember that your stack, heap and BSS all come out of that 2K.

There is a possibility that the Software Serial interrupt handling could interfere with the TFT LCD code. But if all you are doing is including SoftwareSerial.h, then all SoftwareSerial is doing is allocating more SRAM for itself.

compengineer
 
Posts: 4
Joined: Mon Apr 22, 2013 3:42 pm

Re: 2.8" tft shield incompatible with SoftwareSerial?

Post by compengineer »

Yep, Bill, you were exactly right. Thank you so much for the help. I reduced the reserved number of bytes for inputString from 200 to 12 and the problem went away. I thought I had done that before, but evidently I reverted back to some old code that still had it and it was what was causing the problem. I didn't notice it until you pointed it out. Thanks again!

User avatar
adafruit_support_bill
 
Posts: 88091
Joined: Sat Feb 07, 2009 10:11 am

Re: 2.8" tft shield incompatible with SoftwareSerial?

Post by adafruit_support_bill »

Another easy way to reduce your SRAM footprint is with the "F()" macro. This prints literal strings directly from flash instead of moving them into SRAM at load time.

Instead of :

Code: Select all

  tft.println(" Emergency Alert\n   Transmitted\n");
use:

Code: Select all

  tft.println(F(" Emergency Alert\n   Transmitted\n"));

Locked
Please be positive and constructive with your questions and comments.

Return to “Other Arduino products from Adafruit”