September 2021
This Event Datalogger is based on the Adafruit 3.5" 480/320 Touchscreen with SD Card FeatherWing coupled with the Feather M0 Proto Basic (ATSAMD21 Cortex M0). Using the touchscreen, you start and stop the datalogging of an event to the SD card at 1 kHz, and then that data is plotted to the display.
This display is fantastic looking, and has great performance. The HX8357, and GFX libraries developed by Adafruit, along with the standard Arduino SD card library make it easy to create a fully functional event data logger. Button (areas) are drawn on the screen and when touched by the user, this starts and stops the recording of data. After the data is X-Y plotted to the screen, the user clicks in the center of the screen to begin the recording of another event. Although only one signal is plotted to the screen, multiple signals could be recorded to the SD card.
The 1 kHz (1 ms sample period) data sampling rate is possible when using a fast microcontroller and writing to the SD card without a flush() until the end of the event. Nearly all mechanical systems can be monitored effectively by a sensor with a sample rate of 1 kHz. For example, this event data logger was developed to support an application where an impact event was to be measured with an accelerometer.
The plotting of the data to the screen is accomplished with a modified version of the plot function created by Kris Kasprzak and posted on the arduino forum and on github at KrisKasprzak/GraphingFunction. I modified Kris's plotting function to format the x & y axis numerical labels, and to fit them to the x/y chart plotted to the screen. My code also demonstrates how to get the proper values for the arguments that are passed to his charting function from any type of data.
Reading the data back from the card and then plotting it to the screen does take some time, depending on the duration of the event. The benefit is that you can record and then plot a lot of data, without using memory on the microcontroller. Sampling at 1 kHz records a lot of data! You can easily adapt the code to record data at a slower rate to match your specific application.
A function GetSdCardWriteSpeed() measures the SD card write speed and sets a global variable SdSampleIntervalMs that is used by the data recording function SdRecordByMaxSampleInterval (a timer) to record the data. So if you use the display with a different microcontroller or slower SD card, then the appropriate sample rate will be chosen. I'm not sure if the SD card speed will matter, but I mention it just in case. I do know that the SD card write speed is much slower with the 32u4 Basic Proto. Note that you can adapt this to read any type of sensor, such as a I2C or SPI based sensor, or from one or more of the microcontroller analog inputs.
The video below demonstrates the user interface using the display touchscreen.
Code:
/*
Event Data Logger
Adafruit FeatherWing 3.5" 480x320 TFT
Adafruit Feather M0 Basic
*/
// In Arduino IDE, select 'Tools','Board','Adafruit Boards','Adafruit Feather ...'
// Find USB port in Windows by looking at ...
const String codeVersion = "v0.0.1";
const boolean bDebug = true;
/////////////////////////////////////////////////////////////////////////
// Built in LED(s) & LiPoly Battery
// #0 ESP8266
// #13 ESP32, STM32F2, Teensy, 32u4, 382P, M0, M4
const byte pinBuiltInLED = 13;
// A6 328P,
// A7 M0
//const byte pinLiPoly = A6
void blinkLED(byte ledPIN){
// consumes 300 ms.
for(int i = 5; i>0; i--){
digitalWrite(ledPIN, HIGH);
delay(30);
digitalWrite(ledPIN, LOW);
delay(30);
}
} //blinkLED()
void blinkERR(byte ledPIN){
// S-O-S
const int S = 150, O = 300;
for(int i = 3; i>0; i--){
digitalWrite(ledPIN, HIGH);
delay(S);
digitalWrite(ledPIN, LOW);
delay(S);
}
delay(200);
for(int i = 3; i>0; i--){
digitalWrite(ledPIN, HIGH);
delay(O);
digitalWrite(ledPIN, LOW);
delay(O);
}
delay(200);
for(int i = 3; i>0; i--){
digitalWrite(ledPIN, HIGH);
delay(S);
digitalWrite(ledPIN, LOW);
delay(S);
}
delay(200);
} // blinkERR()
/////////////////////////////////////////////////////////////////////////
// 3.5" 480x320 TFT FeatherWing - AF Product #3651
// Adafruit HX8357 Library, Adafruit GFX Library, Adafruit ImageReader Library
// Adafruit HX8357 Library (search for "HX8357")
// Adafruit GFX Library
// https://github.com/adafruit/Adafruit_HX8357_Library
// https://github.com/adafruit/Adafruit-GFX-Library
#include <SPI.h>
#include "Adafruit_GFX.h"
#include "Adafruit_HX8357.h"
#ifdef ESP8266
#define STMPE_CS 16
#define TFT_CS 0
#define TFT_DC 15
#define SD_CS 2
#endif
#ifdef ESP32
#define STMPE_CS 32
#define TFT_CS 15
#define TFT_DC 33
#define SD_CS 14
#endif
#ifdef TEENSYDUINO
#define TFT_DC 10
#define TFT_CS 4
#define STMPE_CS 3
#define SD_CS 8
#endif
#ifdef ARDUINO_STM32_FEATHER
#define TFT_DC PB4
#define TFT_CS PA15
#define STMPE_CS PC7
#define SD_CS PC5
#endif
#ifdef ARDUINO_NRF52832_FEATHER
#define STMPE_CS 30
#define TFT_CS 31
#define TFT_DC 11
#define SD_CS 27
#endif
#if defined(ARDUINO_MAX32620FTHR) || defined(ARDUINO_MAX32630FTHR)
#define TFT_DC P5_4
#define TFT_CS P5_3
#define STMPE_CS P3_3
#define SD_CS P3_2
#endif
// Anything else!
#if defined (__AVR_ATmega32U4__) || defined(ARDUINO_SAMD_FEATHER_M0) || defined (__AVR_ATmega328P__) || \
defined(ARDUINO_SAMD_ZERO) || defined(__SAMD51__) || defined(__SAM3X8E__) || defined(ARDUINO_NRF52840_FEATHER)
#define STMPE_CS 6
#define TFT_CS 9
#define TFT_DC 10
#define SD_CS 5
#endif
#define TFT_RST -1
// Use hardware SPI and the above for CS/DC
Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST);
// Micro SD card mounted to the 3.5" 480x320 TFT FeatherWing
// Arduino standard SD library: https://github.com/arduino-libraries/SD
// NOTE: Typical SD write & flush takes 20 to 30 ms
// Write without flush can achieve 1 kHz with Feather M0 for faster microcontroller.
#include <SPI.h>
#include <SD.h>
// SDCS (chip select) pin is #10 on 32u4
// SDCS (chip select) pin is #5 on M0 mounted to 480x320 TFT 3651 FeatherWing
const int SDchipSelect = 5;
File sdfile;
// Initialize touchscreen driver
#include <Adafruit_STMPE610.h>
Adafruit_STMPE610 ts = Adafruit_STMPE610(STMPE_CS);
// Calibration data for the raw touch data to the screen coordinates
#define TS_MINX 3800
#define TS_MAXX 100
#define TS_MINY 100
#define TS_MAXY 3750
const unsigned long touchDelayMs = 1000;
unsigned long lastTouchTFT = 0;
/////////////////////////////////////////////////////////////////////////
float fGetLgSignedRandFloat() {
//long myLong = random(100, 999);
long myLong = random(100000000, 999999999);
long divisor;
int expnt = random(2,6);
switch (expnt) {
case 2:
divisor = 100;
break;
case 3:
divisor = 1000;
break;
case 4:
divisor = 10000;
break;
case 5:
divisor = 100000;
default:
divisor = 10;
}
float f = (float)myLong / divisor;
int sign = random(10);
if (sign <= 5) {
f = f * (-1.0);
}
return f;
}
/////////////////////////////////////////////////////////////////////////
// ChtXYplot
//
// Derived from:
// Data visualization library based on Adafruit GFX
// by Kris Kasprzak
//
// https://forum.arduino.cc/t/another-free-graph-function-for-plotting-in-cartesian-space/354751
// https://github.com/KrisKasprzak/GraphingFunction/blob/master/Graph.ino
#define LTBLUE 0xB6DF
#define LTTEAL 0xBF5F
#define LTGREEN 0xBFF7
#define LTCYAN 0xC7FF
#define LTRED 0xFD34
#define LTMAGENTA 0xFD5F
#define LTYELLOW 0xFFF8
#define LTORANGE 0xFE73
#define LTPINK 0xFDDF
#define LTPURPLE 0xCCFF
#define LTGREY 0xE71C
#define BLUE 0x001F
#define TEAL 0x0438
#define GREEN 0x07E0
#define CYAN 0x07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define ORANGE 0xFC00
#define PINK 0xF81F
#define PURPLE 0x8010
#define GREY 0xC618
#define WHITE 0xFFFF
#define BLACK 0x0000
#define DKBLUE 0x000D
#define DKTEAL 0x020C
#define DKGREEN 0x03E0
#define DKCYAN 0x03EF
#define DKRED 0x6000
#define DKMAGENTA 0x8008
#define DKYELLOW 0x8400
#define DKORANGE 0x8200
#define DKPINK 0x9009
#define DKPURPLE 0x4010
#define DKGREY 0x4A49
// these are the only external variables used by the graph function
// it's a flag to draw the coordinate system only on the first call to the ChtXYplot() function
// and will mimize flicker
// also create some variables to store the old x and y, if you draw 2 graphs on the same display
// you will need to store ox and oy per each display
boolean display1 = true;
double ox, oy ;
void ChtXYplot(Adafruit_HX8357 &d, double x, double y, double gx, double gy, double w, double h, double xlo, double xhi, double xinc, double ylo, double yhi, double yinc, String title, String xlabel, String ylabel, unsigned int gcolor, unsigned int acolor, unsigned int pcolor, unsigned int tcolor, unsigned int bcolor, boolean &redraw);
double y, xlo, xhi, xinc, ylo, yhi, yinc;
unsigned int x = 0;
/////////////////////////////////////////////////////////////////////////
// SD Card
char filename[15];
unsigned long samples = 0;
// Below set as default based on results from GetSdCardWriteSpeed()
// and used by SdRecordByMaxSampleInterval()
unsigned long SdSampleIntervalMs = 1; // 1 ms = 1 kHz
unsigned long SdSampleIntervalLap = millis(); // timer
byte stateSdRecord = LOW;
void DeleteAllFilesOnSdCard(void) {
// Get the files on the SD card..
sdfile = SD.open("/");
while (true) {
File f = sdfile.openNextFile();
if (! f) {
Serial.println("No files on SD card to delete");
break;
} else {
Serial.println(f.name());
if (SD.exists(f.name())) SD.remove(f.name());
}
f.close();
} // while()
sdfile.close();
} // DeleteAllFilesOnSdCard()
void GetSdCardWriteSpeed(void) {
// Measures the SD Card write speed without flush() between
// writes and updates the global variable SdSampleIntervalMs.
// Requires global variables: filename, pinBuiltInLED, samples,
strcpy(filename, "/SPEEDTST.CSV");
if (SD.exists(filename)) SD.remove(filename);
// Open file on SD Card for writing
sdfile = SD.open(filename, FILE_WRITE);
if (! sdfile) {
if (bDebug == true) Serial.print("ERROR - unable to create '");
if (bDebug == true) Serial.print(filename); Serial.println("'");
DisplayError();
while (1) {
blinkERR(pinBuiltInLED);
}
}
if (bDebug == true) Serial.println("");
unsigned long timerIntervalUs = 2000000; // 2 sec
samples = 0;
digitalWrite(pinBuiltInLED, HIGH);
unsigned long sdTimerStart = micros();
while (micros() - sdTimerStart < timerIntervalUs) {
float f = fGetLgSignedRandFloat();
sdfile.print(millis());
sdfile.print(";");
sdfile.println(f);
samples++;
} // while
unsigned long sdTimeElapsed = micros() - sdTimerStart;
// Execute a flush() to insure it is written since no sdfile.close() will be issued.
sdfile.flush();
sdfile.close();
SdSampleIntervalMs = ceil(float(sdTimeElapsed)/1000.0/float(samples));
if (bDebug == true) {
Serial.print(samples); Serial.print(" samples in "); Serial.print(sdTimeElapsed); Serial.print(" us"); Serial.print("\t"); Serial.print(float(sdTimeElapsed)/float(samples)); Serial.println(" us/sample");
Serial.print(samples); Serial.print(" samples in "); Serial.print(sdTimeElapsed/1000); Serial.print(" ms"); Serial.print("\t"); Serial.print(float(sdTimeElapsed)/1000.0/float(samples)); Serial.println(" ms/sample");
Serial.print(samples); Serial.print(" samples in "); Serial.print(sdTimeElapsed/1000/1000); Serial.print(" s");
Serial.print("\t"); Serial.print(1.0/(float(sdTimeElapsed)/1000.0/1000.0/float(samples))); Serial.println(" Hz");
Serial.print("SdSampleIntervalMs = "); Serial.print(SdSampleIntervalMs); Serial.println(" ms");
Serial.println(" ");
}
if (SD.exists(filename)) SD.remove(filename);
digitalWrite(pinBuiltInLED, LOW);
} // GetSdCardWriteSpeed()
void OpenNewFileOnSdCard(void) {
// Create a filename reference to a file that doesn't exist 'ANALOG00.TXT'..'ANALOG99.TXT'
//char filename[15];
// /DTALOG00.TXT
// 012345678
strcpy(filename, "/DTALOG00.CSV");
for (uint8_t i = 0; i < 100; i++) {
filename[7] = '0' + i/10;
filename[8] = '0' + i%10;
// create if does not exist, do not open existing, write, sync after write
if (SD.exists(filename)){
if (bDebug == true) {
Serial.print("File '");
Serial.print(filename);
Serial.println("' already exists");
}
} else {
if (bDebug == true) {
Serial.print("New file will be '");
Serial.print(filename);
Serial.println("'");
}
break;
}
}
// Open file on SD Card for writing
sdfile = SD.open(filename, FILE_WRITE);
if (! sdfile) {
if (bDebug == true) {
Serial.print("ERROR - unable to create '");
Serial.print(filename); Serial.println("'");
}
DisplayError();
while (1) {
blinkERR(pinBuiltInLED);
}
}
if (bDebug == true) Serial.println("");
} // OpenNewFileOnSdCard()
void SdRecordByMaxSampleInterval() {
// Records only if stateSdRecord == HIGH
if (SdSampleIntervalLap > millis()) SdSampleIntervalLap = millis();
if (stateSdRecord == HIGH && millis() - SdSampleIntervalLap > SdSampleIntervalMs) {
// Write to SD card without flush()
y = fGetLgSignedRandFloat();
x = millis();
sdfile.print(x);
sdfile.print(";");
sdfile.println(y);
if (samples == 0) {
xlo = x; xhi = x; ylo=y; yhi=y;
} else {
if (fabs(x) < fabs(xlo)) xlo = x;
if (fabs(x) > fabs(xhi)) xhi = x;
if (y < ylo) ylo = y;
if (y > yhi) yhi = y;
}
samples++;
SdSampleIntervalLap = millis(); // reset the timer
}
} // SdRecordByMaxSampleInterval()
/////////////////////////////////////////////////////////////////////////
// activePage identifies the TFT screen page
// 0 = splash screen SplashScreen()
// 1 = Record Data RecordData()
// 2 = Recording Data RecordingData()
// 3 = Plot data PlotData()
byte activePage = 0;
unsigned long recordStartMs = 0;
unsigned long recordEndMs = 0;
void SplashScreen(void) {
activePage = 0;
// Fill screen with 16 bit SmartCost Inc. blue 0x04193d 4,25,61
// The argument is a 16-bit 5-6-5 Color value.
// http://greekgeeks.net/#maker-tools_convertColor
tft.fillScreen(0x00C7);
tft.setCursor(int(480.0/2.0-24.0/2.0*18.5),320/3); // center horizontally
tft.setTextColor(0xFFFF); // white
tft.setTextSize(3); // 26 chars wide (18.5 px/char)
tft.setTextWrap(false);
//tft.print("012345678901234567890123456");
tft.print("Mechatronic Solutions LLC");
tft.setCursor(int(480.0/2.0-6.0/2.0*12),290); // center horizontally
tft.setTextColor(0xFFFF); // white
tft.setTextSize(2); // 40 chars wide (12 px/char)
tft.setTextWrap(false);
tft.print(codeVersion);
} // SplashScreen()
/////////////////////////////////////////////////////////////////////////
void DrawScreenConstants(void) {
// Fill screen with 16 bit SmartCost Inc. blue 0x04193d 4,25,61
// The argument is a 16-bit 5-6-5 Color value.
// http://greekgeeks.net/#maker-tools_convertColor
tft.fillScreen(0x00C7);
tft.setCursor(int(480.0/2.0-19.0/2.0*18.5),10); // center horizontally
tft.setTextColor(0xFFFF); // white
tft.setTextSize(3); // 26 chars wide (18.5 px/char)
tft.setTextWrap(false);
//tft.print("012345678901234567890123456");
tft.print("Event Data Recorder");
tft.setCursor(int(480-13*6),300); // right justify
tft.setTextColor(0xFFFF); // white
tft.setTextSize(1); // 80 chars wide (6 px/char)
tft.setTextWrap(false);
//tft.print("01234567890123456789012345678901234567890123456789012345678901234567890123456789");
tft.print("SmartCost Inc");
} // DrawScreenConstants()
void RecordData(void) {
activePage = 1;
//Serial.println("RecordData()..");
DrawScreenConstants();
tft.setCursor(int(480.0/2.0-11.0/2.0*12.0),150); // center horizontally
tft.setTextColor(0xFFFF); // white
tft.setTextSize(2);
tft.setTextWrap(false);
tft.print("Record Data");
tft.setCursor(int(480.0/2.0-16.0/2.0*12.0),190); // center horizontally
tft.print("(touch to start)");
// Draw a rectangle with no fill color..
// drawRect (x top left, y top left, width, height, color)
tft.drawRect(100, 170-40, 480-100-100, (170-(170-40)+8)*2, 0xFFFF);
} // RecordData()
void RecordingData(void) {
activePage = 2;
//Serial.println("RecordingData()..");
OpenNewFileOnSdCard();
DrawScreenConstants();
// Write the SD filename to the TFT display
tft.setCursor(10,70);
tft.setTextColor(0xFFFF); // white
tft.setTextSize(2); // 40 chars wide (12 px/char)
tft.setTextWrap(false);
tft.print("File: ");
for (uint8_t i = 1; i < 13; i++) {
tft.print(filename[i]);
}
tft.setCursor(int(480.0/2.0-14.0/2.0*12.0),150); // center horizontally
tft.setTextColor(0xFFFF); // white
tft.setTextSize(2);
tft.setTextWrap(false);
tft.print("Recording Data");
tft.setCursor(int(480.0/2.0-15.0/2.0*12.0),190); // center horizontally
tft.print("(touch to stop)");
// Draw a rectangle with no fill color..
// drawRect (x top left, y top left, width, height, color)
tft.drawRect(100, 170-40, 480-100-100, (170-(170-40)+8)*2, 0xFFFF);
// trigger recording to SD card via RecordDataToSdCard()
stateSdRecord = HIGH;
recordStartMs = millis();
samples = 0;
} // RecordingData()
void PlotData(void) {
activePage = 3;
DrawScreenConstants();
// Write the SD filename to the TFT display
tft.setCursor(10,70);
tft.setTextColor(0xFFFF); // white
tft.setTextSize(2); // 40 chars wide (12 px/char)
tft.setTextWrap(false);
tft.print("File: ");
for (uint8_t i = 1; i < 13; i++) {
tft.print(filename[i]);
}
tft.setCursor(int(480.0/2.0-17.0/2.0*12.0),140); // center horizontally
tft.setTextColor(0xFFFF); // white
tft.setTextSize(2);
tft.setTextWrap(false);
tft.print("Processing Data..");
//SdSampleIntervalMs
tft.setCursor(int(480.0/2.0-25.0/2.0*12.0),200); // center horizontally
tft.setTextColor(0xFFFF); // white
tft.setTextSize(2);
tft.setTextWrap(false);
tft.print((recordEndMs - recordStartMs)/1000);
tft.print(" sec recorded at ");
// Calculate the sample rate in Hz..
float r = 1.0/(float(SdSampleIntervalMs)/1000.0);
if (r >= 1000.0) {
tft.print(r/1000.0);
tft.print(" kHz");
} else {
tft.print(r);
tft.print(" Hz");
}
if (bDebug == true) {
Serial.print(samples);
Serial.print(" samples recorded over ");
Serial.print((millis() - recordStartMs)/1000);
Serial.println(" sec");
}
// Stop data recording & execute a flush() since no sdfile.close() will be issued.
stateSdRecord = LOW;
digitalWrite(pinBuiltInLED, LOW);
sdfile.flush();
sdfile.close();
if (bDebug == true) Serial.println("SD file flushed and closed");
double gx, gy, w, h;
unsigned long lines = 0;
// Calculate the grid lines..
xinc = (xhi-xlo)/5.0;
yinc = (yhi-ylo)/5.0;
if (bDebug == true) {
Serial.print("x: "); Serial.print(xlo); Serial.print(" to "); Serial.println(xhi);
Serial.print("y: "); Serial.print(ylo); Serial.print(" to "); Serial.println(yhi);
Serial.print("xinc: "); Serial.println(xinc);
Serial.print("yinc: "); Serial.println(yinc);
}
// Plot the data
tft.fillScreen(BLACK);
lines = 0;
sdfile = SD.open(filename, FILE_READ);
if (! sdfile) {
if (bDebug == true) {
Serial.print("ERROR opening SD file ");
Serial.println(filename);
}
DisplayError();
while (1) {
blinkERR(pinBuiltInLED);
}
} else {
display1 = true;
xhi = xhi - xlo;
gx=60; w=460-gx; gy=290; h=260;
if (bDebug == true) Serial.print("Plotting "); Serial.println(filename);
//String title = "012345678901234567890123456789012456789";
String title = "Acceleration " + String(filename).substring(1,strlen(filename));
while (sdfile.available()) {
x = sdfile.parseInt() - xlo; // subtract out the X offset
y = double(sdfile.parseFloat());
if (lines < samples) {
//Serial.print(samples); Serial.print(";"); Serial.print(x); Serial.print(";"); Serial.println(y);
//ChtXYplot(display, x, y, gx, gy, w, h, xlo, xhi, xinc, ylo, yhi, yinc, title, xlabel, ylabel, gcolor, acolor, pcolor, tcolor, bcolor, boolean &redraw)
//ChtXYplot(tft, x, y, gx, gy, w, h, xlo, xhi, xinc, ylo, yhi, yinc, title, "ms", "y", DKBLUE, RED, YELLOW, WHITE, BLACK, display1);
ChtXYplot(tft, x, y, gx, gy, w, h, 0.0, xhi, xinc, ylo, yhi, yinc, title, "ms", "m/s^2", DKBLUE, RED, YELLOW, WHITE, BLACK, display1);
}
lines++;
}
sdfile.close();
}
// clear out the touchscreen buffer..
TS_Point p = ts.getPoint();
lastTouchTFT = millis();
} // PlotData()
void DisplayError(void) {
// Fill screen with FireBrick #0x8800 rgb(139, 0, 0)
// The argument is a 16-bit 5-6-5 Color value.
// http://greekgeeks.net/#maker-tools_convertColor
tft.fillScreen(0x8800);
tft.setCursor(int(480.0/2.0-5.0/2.0*24.0),320/2); // center horizontally
tft.setTextColor(0xFFFF); // white
tft.setTextSize(4); // 20 chars wide (24 px/char)
tft.setTextWrap(false);
//tft.print("012345678901234567890");
tft.print("ERROR");
while (1) {
blinkERR(pinBuiltInLED);
}
} // DisplayError()
void touchScreen() {
// ts.touched()
if (! ts.bufferEmpty()) {
TS_Point p = ts.getPoint();
// Scale from ~0->4000 to tft.width using the calibration #'s
// tft.width() = 480; tft.height() = 320
p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(), 0);
p.y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());
// tft.width() =320, tft.height() = 480 before rotation
// tft.width() =480, tft.height() = 320 after rotation
// 3800,0 3800,3800
// 0,0 0,3800
//Serial.print("Touch "); Serial.print(p.x); Serial.print(", "); Serial.println(p.y);
// Drawn button:
// 300,75 300,275
// 150,75 150,275
// Check if drawn button has been touched..
if (lastTouchTFT > millis()) lastTouchTFT = millis();
//Serial.println(millis() - lastTouchTFT);
if (millis() - lastTouchTFT > touchDelayMs && p.x > 150 && p.x < 300 && p.y >75 && p.y < 275) {
lastTouchTFT = millis();
if (bDebug == true) Serial.print("Button Touch "); Serial.print(p.x); Serial.print(", "); Serial.println(p.y);
switch (activePage) {
case 0: // splash screen
break;
case 1: // record data
RecordingData();
break;
case 2: // recording data
recordEndMs = millis();
PlotData();
break;
case 3: // plot data
//OpenNewFileOnSdCard();
RecordData();
break;
default:
DisplayError();
break;
} // switch (activePage)
} // button touch
} // ts.bufferEmpty()
} // touchScreen()
/////////////////////////////////////////////////////////////////////////
void setup() {
if (bDebug == true) {
Serial.begin(115200);
while (!Serial) {
delay(1);
}
Serial.println("\nSerial ready\n");
}
pinMode(pinBuiltInLED, OUTPUT);
digitalWrite(pinBuiltInLED, LOW);
//480x320 TFT FeatherWing #3651
if (bDebug == true) Serial.println("480x320 TFT FeatherWing #3651");
tft.begin();
/*
Serial.println("480x320 TFT FeatherWing #3651 diagnostics..");
// read diagnostics (optional but can help debug problems)
uint8_t x = tft.readcommand8(HX8357_RDPOWMODE);
Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
x = tft.readcommand8(HX8357_RDMADCTL);
Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
x = tft.readcommand8(HX8357_RDCOLMOD);
Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
x = tft.readcommand8(HX8357_RDDIM);
Serial.print("Image Format: 0x"); Serial.println(x, HEX);
x = tft.readcommand8(HX8357_RDDSDR);
Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX);
Serial.println("");
*/
// Set rotation of the screen
// rotation 1 is landscape mode with USB jack at bottom right
// rotation 3 is landscape mode with USB jack at left
tft.setRotation(1);
// Fill screen with 16 bit SmartCost Inc. blue 0x04193d 4,25,61
// The argument is a 16-bit 5-6-5 Color value.
// http://greekgeeks.net/#maker-tools_convertColor
//tft.fillScreen(0x00C7);
SplashScreen();
// Initialize Micro SD Card
if (!SD.begin(SDchipSelect)) {
if (bDebug == true) Serial.println("SD card failed, or not present");
while (1) {
blinkERR(pinBuiltInLED);
}
}
if (bDebug == true) Serial.println("\nSD card initialized.");
DeleteAllFilesOnSdCard();
GetSdCardWriteSpeed();
stateSdRecord = HIGH;
samples = 0;
// Initialize other items now (while splash screen is shown)..
// Initialize the touchscreen
if (!ts.begin()) {
if (bDebug == true) Serial.println("Touchscreen initialization error!");
DisplayError();
while (1) {
blinkERR(pinBuiltInLED);
}
}
if (bDebug == true) Serial.println("\nTouchscreen started\n");
/*
// Write text to the TFT display (in landscape mode)
// For .setTextSize(2): 40 chars wide (12 char/px) x 30 px vertical spacing
tft.setCursor(int(480.0/2.0-19.0/2.0*12.0),10); // center horizontally
tft.setTextColor(0xFFFF); // white
tft.setTextSize(2);
tft.setTextWrap(false);
//tft.print("0123456789012345678901234567890123456789");
tft.print("Event Data Recorder");
*/
RecordData();
/*
// Record an event to the SD Card and measure the average sample rate
// (sample rate is limited by how fast the data can be written to the SD Card).
// 10000000 us = 10000 ms = 10 sec
unsigned long sampleDurationUs = 10000000;
RecordEventToSdCard(sampleDurationUs);
*/
lastTouchTFT = millis();
if (bDebug == true) Serial.println("\nSetup finished");
} // setup()
void loop() {
touchScreen();
SdRecordByMaxSampleInterval();
} // loop()
void ChtXYplot(Adafruit_HX8357 &d, double x, double y, double gx, double gy, double w, double h, double xlo, double xhi, double xinc, double ylo, double yhi, double yinc, String title, String xlabel, String ylabel, unsigned int gcolor, unsigned int acolor, unsigned int pcolor, unsigned int tcolor, unsigned int bcolor, boolean &redraw) {
/*
function to draw a cartesian coordinate system and plot whatever data you want
just pass x and y and the graph will be drawn
huge arguement list
&d name of your display object
x = x data point
y = y datapont
gx = x graph location (lower left)
gy = y graph location (lower left)
w = width of graph
h = height of graph
xlo = lower bound of x axis
xhi = upper bound of x asis
xinc = division of x axis (distance not count)
ylo = lower bound of y axis
yhi = upper bound of y asis
yinc = division of y axis (distance not count)
title = title of graph
xlabel = x asis label
ylabel = y asis label
gcolor = graph line colors
acolor = axi ine colors
pcolor = color of your plotted data
tcolor = text color
bcolor = background color
&redraw = flag to redraw graph on fist call only
*/
double ydiv, xdiv;
double i;
double temp;
int rot, newrot;
if (redraw == true) {
redraw = false;
// initialize old x and old y in order to draw the first point of the graph
// but save the transformed value
// note my transform funcition is the same as the map function, except the map uses long and we need doubles
ox = (x - xlo) * ( w) / (xhi - xlo) + gx;
oy = (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy;
// draw y scale
for ( i = ylo; i <= yhi; i += yinc) {
// compute the transform
temp = (i - ylo) * (gy - h - gy) / (yhi - ylo) + gy;
if (i == 0) {
d.drawLine(gx, temp, gx + w, temp, acolor);
}
else {
d.drawLine(gx, temp, gx + w, temp, gcolor);
}
// draw the axis labels
d.setTextSize(1);
d.setTextColor(tcolor, bcolor);
d.setCursor(gx - 55, temp); // 55 offsets axis numeric label to left of axis
// +0.0E+00
d.printf("%+.1E",i);
}
// draw x scale
for (i = xlo; i <= xhi; i += xinc) {
// compute the transform
temp = (i - xlo) * ( w) / (xhi - xlo) + gx;
if (i == 0) {
d.drawLine(temp, gy, temp, gy - h, acolor);
}
else {
d.drawLine(temp, gy, temp, gy - h, gcolor);
}
// draw the axis labels
d.setTextSize(1);
d.setTextColor(tcolor, bcolor);
d.setCursor(temp, gy + 10);
// +4.1E+06
d.printf("%+.1E",i);
}
//now draw the graph labels
d.setTextSize(2);
d.setTextColor(tcolor, bcolor);
d.setCursor(gx , gy - h - 30);
d.println(title);
d.setTextSize(1);
d.setTextColor(acolor, bcolor);
d.setCursor(gx , gy + 20);
d.println(xlabel);
d.setTextSize(1);
d.setTextColor(acolor, bcolor);
d.setCursor(gx - 30, gy - h - 10);
d.println(ylabel);
}
// the coordinates are now drawn, plot the data
// the entire plotting code are these few lines...
// recall that ox and oy are initialized above
x = (x - xlo) * ( w) / (xhi - xlo) + gx;
y = (y - ylo) * (gy - h - gy) / (yhi - ylo) + gy;
d.drawLine(ox, oy, x, y, pcolor);
// it's up to you but drawing 2 more lines to give the graph some thickness
d.drawLine(ox, oy + 1, x, y + 1, pcolor);
d.drawLine(ox, oy - 1, x, y - 1, pcolor);
ox = x;
oy = y;
} // ChtXYplot()
Do you need help developing or customizing a IoT product for your needs? Send me an email requesting a free one hour phone / web share consultation.
The information presented on this website is for the author's use only. Use of this information by anyone other than the author is offered as guidelines and non-professional advice only. No liability is assumed by the author or this web site.