/* ScreenToSDbmp
* Dump the ILI9341 TFT screen to an uSD .bmp file.
* Sideline features:
* - Fetch & display Teensy 3.6 Serial Number and MAC address.
* - Display pushbuttons
* - Display SD file name
* Author: Steven T. Stolen / K7CCR
* Version List:
* 0.9.0 Initial creation
*
* Hardware:
* - Teensy 3.6 running at 180mHz
* - ILI9341 Color 320x240 TFT Touchscreen Display
* Software:
* - Arduino IDE v3.6 Linux64 version
* - TeensyDuino v1.35
* Media:
* - MicroSD 4GB FAT16, 8GB FAT32
* Wanted:
* - SD routines that support larger than 8.3 dos type filenames
* - Thumbdrive type interface to xfer screen dump to PC rather
* than "sneakerdisk: method.
* - Implement push button graphics program application.
* Purpose:
* - Provide bitmap file for PC post processing, ie use for
* documenting the project, users manuals, etc.
* - Front-end package for further application developments, a
* "core" package
* Credits:
* -JoaoLopesF/SPFD5408 for _GFX_Button() usage giving me some working
* concepts for a foundation. See GitHub
* SPFD5408/examples/spfd5408_calibrate/
* spfd5408_calibrate.ino vers 0.9.0
* - FrankBoesing for the TeensyMAC library on Github.
* https://github.com/FrankBoesing/TeensyMAC
*
* What it does...
* ILI9341 setup and displays:
* - box frame
* - 3 push buttons
* - displays t3.6 serial nr & MAC Address
* Serial monitor window:
* - // - displays t3.6 serial nr & MAC Address
* 3-Color LED
* Rotate blinks - RED, GREEN, BLUE
*/
#include <SD.h>
#include <SD_t3.h>
#include <TeensyMAC.h>
#include "SPI.h"
#include "ILI9341_t3.h"
#include "font_NunitoBold.h"
#include "font_Arial.h"
#define DEBUG
#define DisplaySerial Serial
const boolean DO_DEBUG = true;
const uint32_t SERIAL_BAUD_RATE = 115200;
// TFT pins
const uint8_t TFT_DC = 9;
const uint8_t TFT_CS = 10;
const uint8_t MIC_PIN = 14;
const uint8_t BACKLIGHT_PIN = 23;
uint16_t colorPixel;
uint8_t r, g, b;
// LED Pins on 3-color LED
// Common Anode
// Selected these pins for default PWM
const uint8_t LED_RED = 34;
const uint8_t LED_GREEN = 35;
const uint8_t LED_BLUE = 36;
const char PROGRAM_NAME[] = "ScreenToSDbmp";
char SERIALNR[16];
char MACLINE[22];
// Dimensions
const uint16_t width = ILI9341_TFTWIDTH;
const uint16_t height = ILI9341_TFTHEIGHT;
uint16_t x = 0;
uint16_t y = 0;
const uint16_t linesize = 3 * width;
uint8_t linebuf[linesize];
const uint32_t filesize = 54 + 3 * width * height;
// note - 56 is to make the file size mod 4 (adds 2 bytes for pad)
// try pixel array for readRect()
uint16_t pixelColorArray[3 * width];
// Use hardware SPI (#13, #12, #11) and the above for CS/DC
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);
// Define the button Class objects (3 buttons)
#define BUTTONS 3
#define BUTTON_CLEAR 0
#define BUTTON_SHOW 1
#define BUTTON_RESET 2
Adafruit_GFX_Button buttons[BUTTONS];
uint16_t buttons_y = 0;
const int chipSelect = BUILTIN_SDCARD; // for Teensy 3.6
File bmpFile;
void setup() {
//Setup Serial Monitor with ArduinoIDE
Serial.begin(115200);
while (!Serial) ; //Wait for system monitor
Serial.println(PROGRAM_NAME);
// See if the card is present and can be initialized
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
return; //Byebye!
}
else Serial.println("uSD Card Initialized.");
// Fetch & display T3.6 serial nr and MAC address
sprintf(SERIALNR, "Serial: %lu\n",teensySerial());
Serial.printf(SERIALNR);
sprintf(MACLINE, "MAC: 0x%012llX\n", teensyMAC());
Serial.printf(MACLINE);
// Set up the TFT display
tft.begin();
tft.setRotation( 2 );
tft.fillScreen(ILI9341_LIGHTGREY);
tft.setFont(Nunito_9_Bold);
// Blink the LED a few times, rotating through the colors
initializeLED();
for (uint8_t i = 0; i < 2; i++) {
rotateLED();
}
initializeButtons();
// Lets draw the border rectangle
tft.drawRect(0,0,width-1,height-1,ILI9341_MAGENTA);
// Draw the serial nr
tft.setCursor(4,4);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(1);
tft.println(SERIALNR);
// Draw the MAC Address
tft.setCursor(4,16);
tft.println(MACLINE);
// Draw the program name
tft.setFont(Nunito_12_Bold); // Note: Font size change from 9
tft.setCursor(4,28);
tft.print("Program: ");
tft.println(PROGRAM_NAME);
tft.setFont(Nunito_9_Bold); // Note: Font size changed back
// Draw the buttons
for (uint8_t i=0; i<3; i++) {
buttons[i].drawButton();
}
// Let's flood a rectangle
Serial.print("Stuffing rectangle with ILI9341_ORANGE, 0xFD20: 0x");
Serial.println(ILI9341_ORANGE,HEX);
tft.fillRect(0,60,40,40,ILI9341_ORANGE);
delay(100);
for (int8_t i=0; i<10; i++) {
y = 60;
colorPixel = tft.readPixel(i,y);
Serial.print("colorPixel Read back = 0x");
Serial.println(colorPixel, HEX);
delay(200);
tft.color565toRGB(colorPixel,r,g,b);
Serial.print("Red = 0x");
Serial.print(r, HEX);
Serial.print(" in decimal: ");
Serial.println(r, DEC);
Serial.print("Grn = 0x");
Serial.print(g, HEX);
Serial.print(" in decimal: ");
Serial.println(g, DEC);
Serial.print("Blu = 0x");
Serial.print(b, HEX);
Serial.print(" in decimal: ");
Serial.println(b, DEC);
Serial.println(" ");
}
dumpScreenToSD();
}
void loop() {
// put your main code here, to run repeatedly:
}
void initializeButtons() {
uint16_t w = 70;
uint16_t h = 20;
uint8_t spacing_x = 5;
uint8_t textsize = 1;
x = 40;
y = height -20;
char buttonlabels[3][10] = { "DUMP ", "SHOW ", "RESET " };
uint16_t buttoncolors[15] = { ILI9341_RED, ILI9341_BLUE,
ILI9341_GREEN };
// button inits
for (uint8_t b=0; b<3; b++) {
buttons[b].initButton(&tft,
x+b*(w+spacing_x), y,
w, h, ILI9341_WHITE, buttoncolors[b], ILI9341_WHITE,
buttonlabels[b], textsize);
}
// Save the y position to avoid draws ---- ????
// I guess that since it is declared globally
// that it keeps these actions static and not continuously
// refreshed with redraws?
buttons_y = y;
delay(1000);
}
void initializeLED(void) {
// LED Setup
// 3-color common anode LED
// off when high, on when low
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
// Put the LED in off state
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_GREEN, HIGH);
digitalWrite(LED_BLUE, HIGH);
}
void rotateLED(void) {
// put your main code here, to run repeatedly:
digitalWrite(LED_RED, LOW); // on
delay(500);
digitalWrite(LED_RED, HIGH); // off
delay(500);
digitalWrite(LED_GREEN, LOW);
delay(500);
digitalWrite(LED_GREEN, HIGH);
delay(500);
digitalWrite(LED_BLUE, LOW);
delay(500);
digitalWrite(LED_BLUE, HIGH);
delay(1000);
}
void dumpScreenToSD(void) {
/*
* - Analyze TFT Display, collect size details
* - For DEBUG,
* - Format file ID and find next available name
* - Draw file name to display (relates screen dump
* to created file)
* - Open File
* - Format file records and write to SD file.
* - Don't forget to close the file.
*/
//-------------Local Declarations
char filename[] = "DMPTFT00.BMP";
unsigned char bmpfileheader[14] =
{'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0};
// uint8_t bmpinfoheader[40] =
// {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0};
#ifdef DEBUG
uint16_t linebufcount = 0; //count of linebuf records written
#endif
#define BMP_SIGNATURE_WORD 0x4D42
//-----xBITMAPFILEHEADER
// kept for reference
// struct xBITMAPFILEHEADER
// {
// uint16_t bfType; // signature - 'BM'
// uint32_t bfSize; // file size in bytes
// uint16_t bfReserved1; // 0
// uint16_t bfReserved2; // 0
// uint32_t bfOffBits; // offset to bitmap
// };
// union fh_data {
// struct xBITMAPFILEHEADER fh;
// uint8_t fhData[sizeof(xBITMAPFILEHEADER)];
// } fhFrame;
// union fhKludge {
// uint32_t bfSize;
// uint8_t kludge[4];
// } kludgeFrame;
//-----xBITMAPFILEHEADER--end of reference
//---------------xBITMAPINFOHEADER
struct xBITMAPINFOHEADER
{
uint32_t biSize; // size of this struct
int32_t biWidth; // bmap width in pixels
int32_t biHeight; // bmap height in pixels
uint16_t biPlanes; // numplanes - always 1
uint16_t biBitCount; // bits per pixel
uint32_t biCompression; // compression flag
uint32_t biSizeImage; // image size in bytes
int32_t biXPelsPerMeter; // horz resolution
int32_t biYPelsPerMeter; // vert resolution
uint32_t biClrUsed; // 0 -> color table size
uint32_t biClrImportant; // important color count
};
union ih_data {
struct xBITMAPINFOHEADER ih;
uint8_t ihData[sizeof(xBITMAPINFOHEADER)];
} ihFrame;
// Clear the line buffer, linebuf is a fixed array length.
memset(linebuf, 0, linesize);
// Format File Name - determine if unique (so we can open)
// Form: TFTDMP99.BMP then
// Open File
for (uint8_t i=0; i<100; i++) {
filename[6] = i / 10 + '0';
filename[7] = i % 10 + '0';
if (!SD.exists(filename)) {
// ony open a new file if it does not exist
bmpFile = SD.open (filename, FILE_WRITE);
break;
}
}
Serial.println("just passed SD.open");
if (! bmpFile) {
Serial.print ("Could not create file: ");
Serial.println (filename);
return;
}
Serial.print ("Opened new file: ");
Serial.println(filename);
// Format and draw file name to display
tft.setFont(Nunito_12_Bold); // Note: Font size change from 9
tft.setCursor(4,44);
tft.print("SDfile: ");
tft.println(filename);
tft.setFont(Nunito_9_Bold); // Note: Font size changed back
//********FORMAT FILE HEADER AND WRITE TO SD***********
#ifdef DEFINE
Serial.print("File size: ");
Serial.println(filesize);
Serial.print("Line size: ");
Serial.println(linesize);
Serial.print("Height: ");
Serial.println(height);
Serial.print("Width: ");
Serial.println(width);
#endif
//
bmpfileheader[2] = (filesize);
bmpfileheader[3] = (filesize>>8);
bmpfileheader[4] = (filesize>>16);
bmpfileheader[5] = (filesize>>24);
bmpFile.write(bmpfileheader, sizeof(bmpfileheader));
#ifdef DEBUG
// now, display the bmpfileheader
Serial.println("bmpfileheader (14 bytes):");
for (uint8_t i=0; i<14; i++) {
Serial.print(bmpfileheader[i],HEX);
Serial.print(" ");
}
Serial.println(" ");
#endif
// Here we put the write of the file header
// in linebuf to SD
// bmpFile.write(linebuf, 14);
//memset(linebuf, 0, 16); // Zero the used portion of the linebuf
//*******FORMAT INFO HEADER AND WRITE TO SD*******
ihFrame.ih.biSize = sizeof(xBITMAPINFOHEADER);
ihFrame.ih.biWidth = width;
ihFrame.ih.biHeight = height;
ihFrame.ih.biPlanes = (uint16_t) 1;
ihFrame.ih.biBitCount = (uint16_t) 24;
ihFrame.ih.biCompression = (uint32_t) 0;
ihFrame.ih.biSizeImage = width * height;
ihFrame.ih.biXPelsPerMeter = (uint32_t) 0;
ihFrame.ih.biYPelsPerMeter = (uint32_t) 0;
ihFrame.ih.biClrUsed = (uint32_t) 0;
ihFrame.ih.biClrImportant = (uint32_t) 0;
// Copy the INFO Buffer to the linebuf
for (uint8_t i=0; i<40; i++) {
//bitmap info header is 40 bytes
linebuf[i] = ihFrame.ihData[i];
}
//here we put the write of the info header
bmpFile.write(linebuf, 40);
#ifdef DEBUG
//Dump the infoheader to sys mon
Serial.println("xBITMAPINFOHEADER: ");
for (uint8_t i=0; i<40; i++) {
Serial.print(linebuf[i], HEX);
Serial.print(" ");
if((i+1)%8==0) Serial.println(" "); //After 8 byte dump,
//start a new line.
}
Serial.println(" ");
Serial.println("Done printing header dumps.");
#endif
// memset(linebuf,0,40); //clean up buffer
//************SCREEN DUMP TO SD FILE ***************
// Pixel dump to file here
delay(100);
// Build BMP file records from display & write to file
for(int16_t i = height-1; i >= 0; i--) {
// Here we write the pixels to the SD.
// We fetch the pixel, trnslate to rgb, stuff the
// line buf with the colors swapped to b,g,r order
// as we walk the linebuf.
// fetch whole line - rectangle height 1 pixel
tft.readRect(0, i, width, 1, pixelColorArray);
// do stuff & write a line from bottom of screen up to top
// as per normal BMP order
for(int16_t j = 0; j < width; j++) {
//Fetch pixel colors & stuff BGR color order to linebuf
tft.color565toRGB(pixelColorArray[j],r,g,b);
linebuf[j*3] = b;
linebuf[j*3 + 1] = g;
linebuf[j*3 + 2] = r;
#ifdef DEBUG
if(j==0 && i==60) {
Serial.print("colorPixel at 0,60 = 0x");
Serial.println(pixelColorArray[j], HEX);
//delay(200);
Serial.print("Red = 0x");
Serial.print(r, HEX);
Serial.print(" in decimal: ");
Serial.println(r, DEC);
Serial.print("Grn = 0x");
Serial.print(g, HEX);
Serial.print(" in decimal: ");
Serial.println(g, DEC);
Serial.print("Blu = 0x");
Serial.print(b, HEX);
Serial.print(" in decimal: ");
Serial.println(b, DEC);
Serial.println(" ");
}
#endif
}
// write the linebuf
bmpFile.write(linebuf, linesize);
#ifdef DEBUG
// do a partial dump of the first linebuf write to Serial
//linebufcount++; // bump the buffer line count
if ((i > 55) && (i < 66)) { // line nr range
Serial.print("Partial pixel dump of line ");
Serial.print(i, DEC);
Serial.println(" written:");
for (uint8_t nix = 0; nix<15; nix++) { //first 5 pixels
Serial.print(linebuf[nix], HEX);
Serial.print(" ");
}
Serial.println(" ");
}
#endif
}
// All records should be written now.
// Close File
bmpFile.close();
// Housekeeping
Serial.println(" ");
Serial.print("Closed File: ");
Serial.println(filename);
}