Bluetooth & TFT Screen



Arduino with Bluetooth and a TFT Screen

ABSTRACT 

In this project, my intentions are to get data from the sensors, show them in a TFT screen and send them via Bluetooth to other device like as an Android phone. Like others exercises that I’ve done before, I’ve also used temperature and humidity sensors. The TFT screen is a 2.8" TFT Touch Shield bought in “Adafruit.com”, it costs 59$.  The Bluetooth was bought in “dealextreme.com”, this device is very economic and can be acquired for around 6$, depends on the web offers, in addition, the shipping is free.

The goals of this exercise are to learn how to use the TFT screen and the Bluetooth with Arduino. The Bluetooth works with AT commands sent by the serial ports. With Arduino Mega is very straightforward because has four serial ports and as a consequence, you don’t need to use other libraries like “SoftwareSerial.h” to simulate another serial port. Moreover, this Bluetooth basically uses just four AT command for setup the configuration that are: AT, AT+NAME, AT+BAUD and AT+PIN [2].

In the case of the TFT SCREEN, everything you need for this shield is described in [1].
  
Figure 1: Arduino Mega with the TFT screen, the Bluetooth and the sockets for the sensors.
  
MATERIALS

1 x Arduino Mega board [3]
1 x USB cable
1 x Humidity sensor
1 x Temperature sensor
1 x 2.8" TFT Touch Shield for Arduino
1 x  Arduino Bluetooth Wireless Serial Port Module (from DealExtreme.com)
2 x 3 pins socket
1 x proto-board shield
Wires, pins, solder lead, etc…
   
Figure 2: Components
   
DEVELOPMENT

For making this I used a small proto shield in which I’ve soldered pins for the connection with the Arduino Mega, two sockets of three pins for the sensors, pins for the Bluetooth and finally, two wires that are connected to GND and VCC of the ICSP pins for distributing the signals for the rest components on the board. You can see how in the Figure 2. Each socket has a pin for VCC, GND and the analog input.

The temperature sensor uses the analog pin 14 and the humidity sensor uses the analog pin 15.

The Bluetooth need four pins: TXD, RXD, VCC and GND. TXD is attached to the RX2 (digital pin 16) and RXD is connected to the TX2 (digital pin 17).

After the assembling, I wrapped all in a box and plugged to the electricity. For seeing the data transmitted by the Bluetooth it is necessary a device for visualizing it. I used an application for Android called Blueterm [4].

The Arduino codes are in the appendix 1, 2 and 3.

Figure 3: The circuit inside the box.



Figure 4: Blueterm App  receives the data transmitted by the bluetooth.
In the Figure 4, you can also see the temperature sensor ( looks like a transistor) and the humidity sensor ( plated color) .

RESULTS

The results can be watched in the next video:
 


The temperature measures are not very accurate because the voltage levels measured are not constant.

PROPOSALS OF IMPROVEMENT

Develop my own Android app for communicate with the Bluetooth.

Improve the graphics of the TFT screen.

REFERENCE

  
APPENDIX 1

Arduino main code:

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

#define SD_CS     5     // The chip select pin for the SD card on the shield

// 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;
uint8_t         spi_save;

int humidity=0,temperature=0;

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

  uint16_t identifier = tft.readID();

  if(identifier == 0x9325)      progmemPrintln(PSTR("Found ILI9325 LCD driver"));
  else if(identifier == 0x9328) progmemPrintln(PSTR("Found ILI9328 LCD driver"));
  else if(identifier == 0x7575) progmemPrintln(PSTR("Found HX8347G LCD driver"));
  else
  {
    progmemPrint(PSTR("Unknown LCD driver chip: "));
    Serial.println(identifier, HEX);
    progmemPrintln(PSTR("If using the Adafruit 2.8\" TFT Arduino shield, the line:"));
    progmemPrintln(PSTR("  #define USE_ADAFRUIT_SHIELD_PINOUT"));
    progmemPrintln(PSTR("should appear in the library header (Adafruit_TFT.h)."));
    progmemPrintln(PSTR("If using the breakout board, it should NOT be #defined!"));
    progmemPrintln(PSTR("Also if using the breakout, double-check that all wiring"));
    progmemPrintln(PSTR("matches the tutorial."));
    return;
  }

  tft.begin(identifier);

  progmemPrint(PSTR("Initializing SD card..."));
  if (!SD.begin(SD_CS))
  {
    progmemPrintln(PSTR("failed!"));
    return;
  }
  progmemPrintln(PSTR("OK!"));
  spi_save = SPCR;

  tft.setRotation(3);
  bmpDraw("Arduino.bmp", 0, 0);
 
  setupBlueToothConnection();
 
  progmemPrint(PSTR("Screen fill:"));
  Serial.println(FillScreen(RED));
  delay(500);
}

void loop()
{
  //if (Serial.available()  > 0 )    Serial2.write((char)Serial.read());
  //if (Serial2.available()  > 0 )   Serial.write((char)Serial3.read());
  temperature=Temperatura();
  humidity=Humedad();
  Serial2.println();
  Serial.println();
  Text();
  delay(2000);
}

// 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  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();
  Serial.print("Loading image '");
  Serial.print(filename);
  Serial.println('\'');
  // Open requested file on SD card
  SPCR = spi_save;
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print("File not found");
    return;
  }
  //The bitmap Header use to be 54 Bytes
  // Read BMP header(14 Bytes)
  if(read16(bmpFile) == 0x4D42) { // "BM" Magic number (unsigned 66,77),2 Bytes
    progmemPrint(PSTR("File size: ")); Serial.println(read32(bmpFile));//Size of the BMP file 4 Bytes
    (void)read32(bmpFile); // Read & ignore creator  4 Bytes
    bmpImageoffset = read32(bmpFile); // Offset where the pixel array (bitmap data) can be found.
    progmemPrint(PSTR("Image Offset: "));
    Serial.println(bmpImageoffset, DEC);
    //Normally 36h, the byte number 54. 4 Bytes
    // Read DIB header (40 Bytes).(Device Independent Bitmap)
    progmemPrint(PSTR("Header size: ")); Serial.println(read32(bmpFile));
    //Number of bytes in the DIB header(from this point).4 Bytes
    bmpWidth  = read32(bmpFile);//Width of the bitmap in pixels.4 Bytes
    bmpHeight = read32(bmpFile);//Height of the bitmap in pixels.4 Bytes
    if(read16(bmpFile) == 1) { // # planes -- must be '1'. 2 Bytes
      bmpDepth = read16(bmpFile); //Number of bits per pixel. It must be: 0x18 0x00 = 24 bits. 2 Bytes
      progmemPrint(PSTR("Bit Depth: ")); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed. 4 Bytes
        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. 4 Bytes
        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
        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)) { // Indeed
              // 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);
        }
        progmemPrint(PSTR("Loaded in "));
        Serial.print(millis() - startTime);
        Serial.println(" ms");
      } // end goodBmp
    }
  }

  bmpFile.close();
  if(!goodBmp) Serial.println("BMP format not recognized.");
  Serial.println();
}

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

unsigned long FillScreen(int color)
{
  unsigned long start = micros();
  tft.fillScreen(color);
  return micros() - start;
}

unsigned long Text()
{
  tft.fillScreen(BLUE);
  unsigned long start = micros();
  tft.setCursor(0, 0);
  tft.setTextColor(YELLOW);  tft.setTextSize(3);
  tft.println("\n\n");
  tft.print("TEMPERATURA: ");tft.print(temperature,DEC);tft.println("\11C");
  tft.println();
  tft.print("HUMEDAD: ");tft.print(humidity,DEC);tft.println("%");
  return micros() - start;
}

APPENDIX 2

Sensors.h code:

static int temperaturePin = 14;//Analog input 0
static int humidityPin=15;//Analog input 1static int moisturePin=2;//Analog input 2


int Temperatura(void)
{
 //the analog pin the TMP36's Vout (sense) pin is connected to the resolution is 10 mV / degree centigrade.
 //(500 mV offset) to make negative temperatures an option
 float temperature=analogRead(temperaturePin) * .004882814;//converting from a 0 to 1023 digital range
 // to 0 to 5 volts (each 1 reading equals ~ 5 millivolts.Example: 5:1024=0.004882814
 temperature = (temperature - .5) * 100;//0 degree begins in 500mV
 Serial.print("Temperatura: ");
 Serial.print(temperature,0);
 Serial.println("\260C");
 delay(100);
 Serial2.print("Temperatura: ");
 Serial2.print(temperature,0);
 Serial2.println(" C");
 return(int(temperature));                                               
}

int Humedad (void)
{
 //5V=1024;5000mV/1024=4.882814mv por unidad
 float humidity=analogRead(humidityPin)* 4.882814;
 humidity=(humidity-800)/31;//Conversion of milivolts to humidity
 Serial.print("Air humidity: ");
 Serial.print(humidity,0);
 Serial.println("%");
 delay(100);
 Serial2.print("Humedad del aire: ");
 Serial2.print(humidity,0);
 Serial2.println("%");
 delay(100);
 return(int(humidity));
}

APPENDIX 3

Bluetooth.h code:

void setupBlueToothConnection()
{  
  Serial2.begin(9600);
  Serial.print("Initializing bluetooth->");
  delay(5000);
  Serial2.print("AT");
  delay(1000);//Wait 1 second according to the datasheet of AT commands
  while(Serial2.available()  > 0 )   Serial.write((char)Serial2.read());
  Serial.println();//Write the response of the bluetooth for debugging
  Serial2.print("AT+NAMETFTBlue"); Serial.print("Name: TFTBlue->");
  delay(1000);//Wait 1 second according to the datasheet of AT commands
  while (Serial2.available()  > 0 )   Serial.write((char)Serial2.read());
  Serial.println();//Write the response of the bluetooth for debugging
  /*Cambio de la velocidad del modulo en baudios
  Se envia AT+BAUD y seguido el numero correspondiente:
  1 -- 1200 bauds
  2 -- 2400 bauds
  3 -- 4800 bauds
  4 -- 9600 bauds (by defect)
  5 -- 19200 bauds
  6 -- 38400 bauds
  7 -- 57600 bauds
  8 -- 115200 bauds
  */
  Serial2.print("AT+BAUD4");Serial.print("9600 bauds->");
  delay(1000);//Wait 1 second according to the datasheet of AT commands
  while (Serial2.available()  > 0 )   Serial.write((char)Serial2.read());
  Serial.println();//Write the response of the bluetooth for debugging
  //Configuracion Password, se envia AT+PIN y seguido password que queremos
  Serial2.print("AT+PIN1234"); Serial.print("Pin: 1234->");
  delay(1000);//Wait 1 second according to the datasheet of AT commands
  while (Serial2.available()  > 0 )   Serial.write((char)Serial2.read());
  Serial.println();//Write the response of the bluetooth for debugging
  Serial.println("Bluetooth Setup complete");
  Serial.println();//Write the response of the bluetooth for debugging
}