Teensy Program Includes and  Defines

/* portableReadout.ino using Teensy 3.6 and 3.2" color TfT touchscreen  Display

   Controller ILL9341 320x240, with touchscreen XPT2046

   Using the Teensy 3.6 and BME 680 sensor

   Implements the Teensy built-in RTC and the built-in SD.

   A receiver gets RF sensor messages from six transmitters on the 433 MHz band.

   There is 2-way communications with a Picaxe 08M2 which control power.

   This has a 'standby' mode which records one of each message on a 15-minute cycle.

   It also records and displays the sensor outputs of the BME680

   The contents of any day file can be read out and sent to the USB.

   Provides for Daily Saving Time and minutes adjustment

   It is powered by a Li Ion battey and has a charger.

   New version John Saunders 9/18/2023

*/

#include "ILI9341_t3.h"

#include "SPI.h"

#define BUILTIN_SDCARD 254

#include "SD.h"

#include "XPT2046_Touchscreen.h"

#include "EEPROM.h"

#include <font_Arial.h> // from ILI9341_t3


#define blueLED 6

#define intPort 33

#define chargePort 23

#define blPort 35         //positive turns on backlight, 56 to 91 MA at  48MHz

#define PIN_T_CS 4

#define PIN_IRQ 5

#define PIN_TFT_CS 10

#define PIN_DC 9

#define PIN_RST 24

#define picIRQ 7


#define TFT_BLACK       0x0000      /*   0,   0,   0 */

#define TFT_NAVY        0x000F      /*   0,   0, 128 */

#define TFT_DARKGREEN   0x03E0      /*   0, 128,   0 */

#define TFT_DARKCYAN    0x03EF      /*   0, 128, 128 */

#define TFT_MAROON      0x7800      /* 128,   0,   0 */

#define TFT_PURPLE      0x780F      /* 128,   0, 128 */

#define TFT_OLIVE       0x7BE0      /* 128, 128,   0 */

#define TFT_LIGHTGREY   0xD69A      /* 211, 211, 211 */

#define TFT_DARKGREY    0x7BEF      /* 128, 128, 128 */

#define TFT_BLUE        0x001F      /*   0,   0, 255 */

#define TFT_GREEN       0x07E0      /*   0, 255,   0 */

#define TFT_CYAN        0x07FF      /*   0, 255, 255 */

#define TFT_RED         0xF800      /* 255,   0,   0 */

#define TFT_MAGENTA     0xF81F      /* 255,   0, 255 */

#define TFT_YELLOW      0xFFE0      /* 255, 255,   0 */

#define TFT_WHITE       0xFFFF      /* 255, 255, 255 */

#define TFT_ORANGE      0xFDA0      /* 255, 180,   0 */

#define TFT_GREENYELLOW 0xB7E0      /* 180, 255,   0 */

#define TFT_PINK        0xFE19      /* 255, 192, 203 */ //Lighter pink, was 0xFC9F

#define TFT_BROWN       0x9A60      /* 150,  75,   0 */

#define TFT_GOLD        0xFEA0      /* 255, 215,   0 */

#define TFT_SILVER      0xC618      /* 192, 192, 192 */

#define TFT_SKYBLUE     0x867D      /* 135, 206, 235 */

#define TFT_VIOLET      0x915C      /* 180,  46, 226 */


// Touchscreen Button values for sensing.

// Button indices are 0 - 3 CW from top  left P, N, B, Y + C in center

// X increases left to right


#define YP_X_MIN 620

#define YP_X_MAX 1800


#define BN_X_MIN 2400

#define BN_X_MAX 3300


// Y increases top to bottom

#define PN_Y_MIN 2220

#define PN_Y_MAX 2830


#define YB_Y_MIN 3300

#define YB_Y_MAX 3600


#define C_X_MIN   870

#define C_X_MAX  3120

#define C_Y_MIN  1400

#define C_Y_MAX  1900


//                         P          N         B        Y          C

const int buttXmin[5] = {YP_X_MIN, BN_X_MIN, BN_X_MIN, YP_X_MIN, C_X_MIN};

const int buttXmax[5] = {YP_X_MAX, BN_X_MAX, BN_X_MAX, YP_X_MAX, C_X_MAX};

const int buttYmin[5] = {PN_Y_MIN, PN_Y_MIN, YB_Y_MIN, YB_Y_MIN, C_Y_MIN};

const int buttYmax[5] = {PN_Y_MAX, PN_Y_MAX, YB_Y_MAX, YB_Y_MAX, C_Y_MAX};

Teensy Program Display Definitions


//                         P          N         B        Y          C

const int buttXmin[5] = {YP_X_MIN, BN_X_MIN, BN_X_MIN, YP_X_MIN, C_X_MIN};

const int buttXmax[5] = {YP_X_MAX, BN_X_MAX, BN_X_MAX, YP_X_MAX, C_X_MAX};

const int buttYmin[5] = {PN_Y_MIN, PN_Y_MIN, YB_Y_MIN, YB_Y_MIN, C_Y_MIN};

const int buttYmax[5] = {PN_Y_MAX, PN_Y_MAX, YB_Y_MAX, YB_Y_MAX, C_Y_MAX};


// Touchscreen button size and placeement. These correspond to the index in buttInx

//                     C

//                 P       N

//                 Y       B

//                         P    N     B    Y    C

const int buttXpos[5]  = { 20, 170,  170,  20, 40};

const int buttYpos[5]  = {140, 140, 200, 200, 70};

const int buttWidth[5] = {130, 130, 130, 130, 250};

const int buttDepth[5] = { 40,  40,  40,  40,  50};



const char *navButtons[] = {      // Navigation button labels2)

  //    0         1         2           3            4            5          6          7

  "Messages", " Temps", "Humidity", "Voltages", " Gas Qual", " Garage", "Activity", "Year Set",

  //   8           9            10           11           12          13          14

  "Month Set", "Date Set" , "Adj Year", "Adj Month", "Adj Date", "Adj Hours", "Adj Minutes",

  // 15             16      17          18            19

  "ST/DST", "   Home",  "Settings",    "Rec Time", "View Time",

};


const char *actionButtons[] = {     //Action button labels

  // A = 0         B = 1       C = 2           D = 3

  "Edit Clock", "Playback",  "Standard",    "Day Sav",

  // E = 4       F = 5         G = 6

  "increase", "decrease", "Store Changes",

  //     H = 7       I = 8         J = 9       K = 10         L = 11

  "   Read File", "Next Date", "Prev Date", "Next Month", "Prev Month",

  //      M            N            O           P              Q

  "Store Changes", "decrease", "increase",  "decrease",    "increase"


};


const char *headers[] = {           // Goes at the top of most pages

  //   0             1                     2                     3              4

  "Temperatures", "Humidities", "Charge and Battery Voltage", "Gas Quality", "Garage State",

  //         5               6                  7                 8         9

  "Read Year Set", "Read Month Set",  "Read Date Set", "Adjust Year", "Adjust Month",

  //      10             11              12               13

  "Adjust Date",  "Adjust Hour",  "Adjust Minutes", "Daylight Saving",

  //        14                        15                  16

  "John Saunders' contraption", "Activity Monitor", "Settings Menu" ,

  //     17                 18

  "Record Period = ", "Activity View Time = "

};


struct page_t {

  uint8_t hdrInx;           //index into headers, 21 for no header

  uint8_t tdDisp;           // 0 = dispBigTime(), 1 = dispTimeDate(), 2 = dispReadDate(), 3 dispTimeDate

  uint8_t buttInx[5];       // Index to navButtons if numeric,jumps to next sysMode

  // Index to actionButtons if alpha, index denotes action

};


page_t Pages[] = {

  //    messages - 0                   temperatures - 1             humidities - 2

  { 21, 21, 21, 21, 1, 16, 21},   {0, 3, 21, 21, 2, 0, 21},       {1, 3, 21, 21, 3, 1, 21},


  //    voltages - 3                   Gas quality -4                  Garage - 5

  {2, 3, 21, 21, 4, 2, 21},      {3, 3, 21, 21, 5, 3, 21},       {4, 3, 21, 21, 0, 4, 21},


  //     activity- 6                       Year set - 7                  month set - 8

  {15, 3, 21, 21, 0, 16, 21},   {5, 2, 'F', 'E', 8, 9, 'H'},    {6, 2, 'L', 'K', 9, 7, 'H'},


  //    Date set - 9                   Adjust Year - 10             Adjust Month     - 11

  {7, 2, 'J', 'I', 7, 8, 'H'},   {8, 1, 'F', 'E', 11, 16, 'G'},  {9, 1, 'F', 'E', 12, 10, 'G'},


  //    Adjust Date - 12               Adjust Hours = 13           // Adjust Minutes - 14

  {10, 1, 'F', 'E', 13, 11, 'G'}, {11, 1, 'F', 'E', 14, 12, 'G'}, {12, 1, 'F', 'E', 15, 13, 'G'},


  //   Adjust Daylight Savings -15            main - 16               settings - 17

  {13, 3, 'C', 'D', 16, 21, 'M'},  {14, 0, 17, 'B', 6, 0, 21},  {16, 3, 'A', 15, 19, 18, 21},


  // Record Interval - 18            Activity Time - 19

  {17, 4, 21, 21, 'N', 'O', 'M'},  {18, 5, 21, 21, 'P', 'Q', 'M'}

};


// Objects:


ILI9341_t3 TFTscreen = ILI9341_t3(PIN_TFT_CS, PIN_DC, PIN_RST, 11, 13, 12);


//XPT2046_Touchscreen ts = XPT2046_Touchscreen(PIN_T_CS, PIN_IRQ);

XPT2046_Touchscreen ts = XPT2046_Touchscreen(PIN_T_CS);  // Interrupt disabled


// --------------- DME688 includes, etc ---------------

// I2C interface address = 0x77, SCL (yellow pin 19), SDA (blue pin 18)

#include "Zanshin_BME680.h"

BME680_Class BME680;


// Several other SD libraries did not recognize BUILTIN_SDCARD and the SD needed updating

File file;  //The SD object is in the SD library.

Teensy Program Timekeeping Function


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

              Timekeeping functions

**********************************************************************

*/


#include "teensyTimeLib.h"


// RTC object is pre-instantiated in the library:

tmElements_t locTm, editTm;

utime_t  locTt, untTt, editTt;

uint8_t tzHours;                  //Hours fro GMT

uint8_t clockList[7];

uint8_t editList[7];

const char *dayName[8] = {

  "Sunday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"

};

const char *monthName[13] = {

  "January", "January", "February", "March", "April", "May", "June",

  "July", "August", "September", "October", "November", "December"

};

void timeDateGet(void) {

  locTt = untTt - (tzHours * SECS_PER_HOUR);

  breakTime(locTt, &locTm);

  clockList[0] = locTm.Second;           // Not adjustable 0 - 59

  clockList[1] = locTm.Minute;           // 0 - 59

  clockList[2] = locTm.Hour;             // 0 - 23

  clockList[5] = locTm.Year;             // - 2000

  clockList[6] = locTm.Wday;             // Not adjustable 1 (Sun) - 7

  clockList[4] = locTm.Day;

  clockList[3] = locTm.Month;

}

Teensy Program Formatting Functions

char chargeFlag;



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

  Display functions

**********************************************************************

*/

int sysMode;

const int hdrPos = 40;


void displayTimeDate(void) {

  char timeDateBuf[30];

  static uint8_t oldHour, oldMin, oldSec;

  TFTscreen.setTextColor(TFT_BLACK); // erase previous time

  sprintf(timeDateBuf, "   %02u:%02u:%02u %02u/%02u/%02u", oldHour, oldMin, oldSec, clockList[3], clockList[4], clockList[5]);

  TFTscreen.setCursor(0, 13);

  TFTscreen.print(timeDateBuf);

  sprintf(timeDateBuf, "   %02u:%02u:%02u %02u/%02u/%02u", clockList[2], clockList[1], clockList[0], clockList[3], clockList[4], clockList[5]);

  TFTscreen.setTextColor(TFT_GOLD); // prints orange lettrs to TFT display

  TFTscreen.setCursor(0, 13);

  TFTscreen.setFont(Arial_16);

  TFTscreen.print(timeDateBuf);

  oldHour = clockList[2];

  oldMin = clockList[1];

  oldSec = clockList[0];

}

void displayEditDate(void) {

  char timeDateBuf[30];

  static uint8_t oldHour, oldMin, oldSec;

  TFTscreen.setTextColor(TFT_BLACK); // erase previous time

  sprintf(timeDateBuf, "   %02u:%02u:%02u %02u/%02u/%02u", oldHour, oldMin, oldSec, editList[3], editList[4], editList[5]);

  TFTscreen.setCursor(0, 13);

  TFTscreen.print(timeDateBuf);

  sprintf(timeDateBuf, "   %02u:%02u:%02u %02u/%02u/%02u", editList[2], editList[1], editList[0], editList[3], editList[4], editList[5]);

  TFTscreen.setTextColor(TFT_WHITE); // prints orange lettrs to TFT display

  TFTscreen.setCursor(0, 13);

  TFTscreen.setFont(Arial_16);

  TFTscreen.print(timeDateBuf);

  oldHour = editList[2];

  oldMin = editList[1];

  oldSec = editList[0];

}


void displayPeriod(int pVal) {

  TFTscreen.fillRect(260, hdrPos, 60, 20, TFT_BLACK);

  TFTscreen.setFont(Arial_16);

  TFTscreen.setTextColor(TFT_WHITE);

  TFTscreen.setCursor(260, hdrPos);

  TFTscreen.print(pVal, DEC);

}


void displayBigTime(int dpos, int tpos) {

  char timeBuf[25];

  static uint8_t oldHour, oldMin, oldSec;

  sprintf(timeBuf, " %s,%s %02u,20%02u", dayName[clockList[6]], monthName[clockList[3]], clockList[4], clockList[5]);

  TFTscreen.setFont(Arial_16);

  TFTscreen.fillRect(0, dpos, 320, 25, TFT_BLACK);

  TFTscreen.setTextColor(TFT_GREEN);

  TFTscreen.setCursor(0, dpos);

  TFTscreen.print(timeBuf);

  sprintf(timeBuf, "%02u:%02u:%02u", oldHour, oldMin, oldSec);

  TFTscreen.setFont(Arial_40);

  TFTscreen.setTextColor(TFT_BLACK); // erase previous time

  TFTscreen.setCursor(0, tpos);

  TFTscreen.print(timeBuf);

  sprintf(timeBuf, "%02u:%02u:%02u", clockList[2], clockList[1], clockList[0]);

  TFTscreen.setTextColor(TFT_ORANGE);

  TFTscreen.setCursor(0, tpos);

  TFTscreen.print(timeBuf);

  TFTscreen.setFont(Arial_16);

  TFTscreen.setCursor(235, (tpos + 15));

  if (tzHours == 7) {

    TFTscreen.print(" PDST");

  }

  if (tzHours == 8) {

    TFTscreen.print(" PST");

  }

  oldHour = clockList[2];

  oldMin = clockList[1];

  oldSec = clockList[0];

}


uint8_t readDateVal;

uint8_t readMonthVal;

uint8_t readYearVal;


void displayReadDate(int pos) {

  char readDateBuf[20];

  TFTscreen.fillRect(10, (pos - 12), 230, 20, TFT_BLACK);

  TFTscreen.setCursor(10, pos);

  TFTscreen.setFont(Arial_16);

  sprintf(readDateBuf, "%02u/%02u/20%02u", readMonthVal, readDateVal, readYearVal);

  TFTscreen.setTextColor(TFT_YELLOW); // prints orange lettrs to TFT display

  TFTscreen.print(readDateBuf);

}


void dispHourMin(uint8_t inx) {

  TFTscreen.fillRect(100, 140, 98, 30, TFT_BLACK);

  TFTscreen.setCursor((buttXpos[4] + 180), (buttYpos[4] + 10));

  TFTscreen.setFont(Arial_20);

  TFTscreen.setTextColor(TFT_ORANGE);

  TFTscreen.print(editList[inx]);

}


void dispHeader(void) {

  uint8_t index;

  TFTscreen.fillRect(0, hdrPos, 339, 20, TFT_BLACK);

  TFTscreen.setFont(Arial_16);

  TFTscreen.setTextColor(TFT_WHITE);

  TFTscreen.setCursor(5, hdrPos);

  index = Pages[sysMode].hdrInx;

  if (index != 21) {

    TFTscreen.print(headers[index]);

  }

  if (sysMode == 6) {

    displayBattVolt(255, hdrPos);

  }

}


void displayBattVolt(uint8_t xPos, uint8_t yPos) {

  TFTscreen.setCursor(xPos, yPos);

  TFTscreen.setFont(Arial_16);

  TFTscreen.setTextColor(TFT_GREEN);

  if (msgs[11].msgData[6] > '3') {

    TFTscreen.setTextColor(TFT_BLUE);

  }

  else {

    if (msgs[11].msgData[8] < '5') {

      TFTscreen.setTextColor(TFT_RED);

    }

  }

  for (int i = 6; i < 10; i++) {

    TFTscreen.write(msgs[11].msgData[i]);

  }

}


void displayMsgNames(uint8_t index, int pos) {

  TFTscreen.fillRect(0, pos, 339, 20, TFT_BLACK);

  TFTscreen.setFont(Arial_16);

  TFTscreen.setTextColor(TFT_WHITE);

  TFTscreen.setCursor(15, pos);

  TFTscreen.print(msgNames[index]);

  TFTscreen.setCursor(150, pos);

  TFTscreen.print(msgs[index].ageing);

}


void displayButtons(void) {   // Displays the buttons for the current sysMode

  uint8_t buttVal;

  for (int buttID = 0; buttID < 5; buttID++) {

    buttVal = Pages[sysMode].buttInx[buttID];

    if (((buttVal != 0) && (buttVal != 21)) || ((buttVal == 0) && (buttID < 4) )) {                   // erase old button

      TFTscreen.fillRect(buttXpos[buttID], buttYpos[buttID], buttWidth[buttID], buttDepth[buttID], TFT_BLACK);

      delay(10);

      TFTscreen.drawRect(buttXpos[buttID], buttYpos[buttID],  buttWidth[buttID], buttDepth[buttID], TFT_WHITE);

      delay(10);

    }

    TFTscreen.setCursor((buttXpos[buttID] + 10), (buttYpos[buttID] + 10));

    TFTscreen.setFont(Arial_16);

    TFTscreen.setTextColor(TFT_WHITE);

    if (((buttVal > 0) && (buttVal < 20))  || ((buttVal == 0) && (buttID < 4) )) {

      TFTscreen.print(navButtons[buttVal]);

    }

    if ((buttVal >= 'A') && (buttVal < 'Z')) {

      TFTscreen.print(actionButtons[(buttVal - 'A')]);

    }

    delay(20);

  }

}

Teensy Program Receiver Functions


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

    RF Message functions for parsing and display of individual fields

**********************************************************************

*/


// index into mesgs table from keyCode - 'n', the ID which starts each message

//                      n   o   p   q  r  s  t  u   v   w   x  y   z

const int msgInx[13] = {3, 15, 15, 11, 2, 1, 0, 5, 15, 15,  6, 15, 4};

const int msgSize = 35;

const int nameSize = 14;


struct msg_t {

  char code;

  uint8_t len;

  uint8_t hours;

  uint8_t mins;

  uint16_t ageing;            //0 initially, 1 if a message has been received

  char msgData[msgSize];

};


const char *msgNames[] = {           // Goes at the top of most pages

  // 0       1          2              3              4

  "Time", "Solar", "Barometer", "Gas Quality", "Gas Utility",

  //     5             6           7          8           9

  "Garage Fan", "NIXIE Clock",  "Test", "Measurement", "Power",

  // 10          11         12

  "Event",   "Battery", "Environmental"

};

const int numMsgs = 13;

msg_t msgs[numMsgs] = {   //Includes both RF and BME680 fields. These values are meaningless placeholders

  {'t', 10, 14, 23, 0, ",04,24,11,04, "            },     // 0

  {'s', 17, 24, 11, 0, ",068, 057, 070, 557, "     },     // 1

  {'r', 19, 14, 23, 0, ",987,176,0187,0444, "      },     // 2

  {'n', 17, 14, 23, 0, ",A, 14.173, 24.309, "      },     // 3

  {'z', 14, 14, 23, 0, ",0083.8,00479, "           },     // 4

  {'u', 11, 14, 23, 0, ",077, D, C, S, "           },     // 5

  {'x', 16, 14, 23, 0, ",1,P,D,11,08,526, "        },     // 6

  {'p',  8, 14, 23, 0, ",38.562, "                 },     // 7

  {'v',  6, 14, 23, 0, ",29.87, "                  },     // 8

  {'w',  11, 14, 23, 0, ",-27888.71, "             },     // 9

  {'o',  8, 14, 23, 0, ",077.21, "                 },     // 10

  {'q', 13, 14, 23, 0, ",4.98,3.28,S, "            },     // 11

  {'e',  8, 14, 23, 0, ",42.8,29.93,-2056,4.48 "   },     // 12

};


struct fld_t {          //Defines how to select a field from a message and display it

  uint8_t fldLabel;     //Index into labels

  uint8_t msgInx;       //Index into msgs

  uint8_t fldUnit;      //count of commas to begining of field

  uint8_t dp;   //Decimal Point: 0 = none, 1 = after 1 digit , 2 = after 2 digits, etc

  uint8_t unitInx;      //Index into units

};


// Each entry corresponds to a page and each item to a row.


fld_t flds_1[] = {  //sysMode 1, Rows 1 - 5, etc

  {0, 12, 0, 0, 6}, {1, 1, 0, 0, 6}, {2, 4, 0, 0, 6}, {3, 5, 0, 0, 6}, {14, 2, 0, 0, 7}  //0

};

fld_t flds_2[] = {

  {0, 12, 1, 0, 3}, {1, 1, 1, 0, 3}, {4, 1, 2, 0, 4}, {7, 2, 2, 0, 8}, {8, 2, 3, 0, 8}

};

fld_t flds_3[] = {

  {18, 11, 0, 0, 5}, {6, 2, 1, 2, 5}, {17, 11, 1, 0, 5}, {16, 1, 3, 1, 5}, {5, 4, 1, 0, 0}

};

fld_t flds_4[] = {

  {0, 12, 3, 0, 9}, {11, 3, 1, 0, 9}, {12, 3, 2, 0, 9}, {13, 3, 0, 0, 0}

};

fld_t flds_5[] = {

  {9, 5, 1, 0, 0}, {10, 5, 2, 0, 0}, {15, 5, 3, 0, 0}

};


fld_t *fields[] = {flds_1, flds_2, flds_3, flds_4, flds_5};


const char*labels[] = {

  //   0          1        2       3             4

  "Local", "Outside",  "Den", "Garage", "Solar Current",

  //    5              6                 7               8

  "Den Light", "Solar Charging", "Today Charge", "Yesterday Charge",

  //    9              10            11       12         13

  "Garage Door", "Garage Fan", "Gas Hot", "Gas Cold", "Gas Alarm",

  //   14             15                 16              17        18

  "Barometer", "Garage Temperature", "Solar Battery", "Battery", "Charging"

};


const char*units[] = {

  //0      1      2      3       4      5     6     7        8       9

  " ",   ": ",   " / ",   " % ",   "MA",   "V", "*F", "inHg",  "MAH",  "Kohm",

  //10      11      12          13       14     15

  "up",  "dowm", "running", "stopped", "hot", "cold"

};


int getMsgIndex(char keyCode) {         //Reverse lookup

  return msgInx[(int)keyCode - 110];

};


char picState;


void displayField(int pageInx, int row, int pos) {

  int commaLoc[8];

  int commaIndex = 0;

  int startIndex, endIndex;

  int labelIndex;

  char recChar;

  int dpInx;

  fld_t selFld = fields[pageInx][row];

  for (int i = 0; i < msgSize; i++) {

    recChar = msgs[selFld.msgInx].msgData[i];

    if (recChar == ',') {

      commaLoc[commaIndex++] = i;

    }

  }

  labelIndex = selFld.fldLabel;

  TFTscreen.setCursor(0, pos);

  TFTscreen.fillRect(0, pos, 319, 25, TFT_BLACK); // Erase previous display

  TFTscreen.setCursor(0, pos);

  TFTscreen.setTextColor(TFT_GREEN);

  TFTscreen.print(labels[labelIndex]);

  TFTscreen.print('=');

  TFTscreen.setTextColor(TFT_WHITE);

  startIndex = commaLoc[selFld.fldUnit] + 1;

  endIndex =  commaLoc[(selFld.fldUnit + 1)];

  recChar = msgs[selFld.msgInx].msgData[1];

  switch (labelIndex) {                     //Some sensor values need tweeking for display

    case 9:                      //Up/Down

      if (recChar == 'U') {

        TFTscreen.print(units[10]);

      }

      else {

        TFTscreen.print(units[11]);

      }

      break;

    case 10:                      //Run/Stop

      if (recChar == 'R') {

        TFTscreen.print(units[12]);

      }

      else {

        TFTscreen.print(units[13]);

      }

      break;

    case 15:                      //Hot/Cold

      if (recChar == 'H') {

        TFTscreen.print(units[14]);

      }

      else {

        TFTscreen.print(units[15]);

      }

      break;

    case 14:                     //Barometer

      if (recChar > (int)'5') {

        TFTscreen.print("29.");

      }

      else {

        TFTscreen.print("30.");

      }

    default:

      dpInx = selFld.dp  + startIndex;

      for (int i = startIndex; i < endIndex; i++) {

        if (i > msgSize) {

          break;

        }

        if ((dpInx > startIndex) && (i == dpInx)) {

          TFTscreen.print('.');

        }

        TFTscreen.print(msgs[selFld.msgInx].msgData[i]);

      }

      TFTscreen.setTextColor(TFT_GREEN);

      TFTscreen.print(' ');

      TFTscreen.print(units[selFld.unitInx]);

      break;

  }

}


const float chargeRatio = 0.436;    //Vref * dividerMult / 2048

const float vRef = 3.264;           //For charging voltage measurement

const float dividerMult = 2.93;


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

  Receiver functions

**********************************************************************

*/

const int maxCols = 33;

unsigned pulseLength;

const int pwMin = 145;

char rxBuf[maxCols];                //Passed from getPicaxeData() to loop()

bool newMsg = false;                //Passed from getPicaxeData() to loop()



void getRecMsg(void) {                // Input from Receiver, invoked by interrupt

  byte colCount = 0;

  char recChar;

  unsigned pwLen = 0;

  pulseLength = 0;

  int hdrChkSum = 0;

  while (digitalRead(intPort) > 0) { // 16.8 ms, threshold = 1.2V

    pwLen++;

    delayMicroseconds(100);

  }

 // digitalWrite(blueLED,HIGH);

  if (pwLen > pwMin) {          //Fisrst check on the mssage integrity

    pulseLength = pwLen;

    while ((Serial5.available() > 0) && (colCount < maxCols)) {

      recChar = Serial5.read();

      if ( !newMsg && (recChar >= ' ') && (recChar <= 'z')) {

        //if ((recChar >= ' ') && (recChar <= 'z')) {

        // Don't overwrite undisplayed message

        rxBuf[colCount++] = recChar;

      }

    }

    // Second check is on the unlock code by gettig the checksum

    // of the fixed unlock code at the beginnng of the message

    for (int i = 0; i < 7; i++) {

      hdrChkSum += rxBuf[i];

    }

    if ((hdrChkSum == 390) && (newMsg == false)) {   //Known value, now check the message checksum

      newMsg = true;

    }

  }

  //digitalWrite(blueLED,LOW);

}


/*

  I originally put the EEPROM.write() code in here but it crashed the Teensy

  However the EEPROM code executes normally in loop()!

  Only the minimum checks are done in the  nterrupt function

  The fourth check is to get the checksum of the data part of the message,

  and comparing it to the last 2 characters of the message

*/

uint8_t verifyRecMsg(void) {        //Checking rxBuf:

  uint8_t lastCharPos, chkHex, testVal, chkSum = 0;

  char  keyCode, chkChar, msgLenChar;

  keyCode = rxBuf[7];

  int msgIndex = getMsgIndex(keyCode);

  delay(10);

  if (msgIndex < 0 || msgIndex > 10) {  // Checking for valid message type

    return 15;

  }

  msgLenChar = rxBuf[9];

  if (msgLenChar < 61 || msgLenChar > 110) {  //Checking for reasonable message length

    return 15;

  }

  lastCharPos = msgLenChar - 49;

  /*

    if (keyCode == 'x') {

    Serial.print("Received NIXIE message:");

    Serial.println(rxBuf);

    }

  */

  for (int i = 11; i < lastCharPos; i++) { // Sum active message characters

    chkSum += (uint8_t )rxBuf[i];

  }

  chkHex = chkSum / 16;

  chkChar = rxBuf[(lastCharPos + 1)];

  if (isAlpha(chkChar)) {

    testVal = (uint8_t)(chkChar - 55);

  }

  else {

    testVal = (uint8_t)(chkChar - 48);

  }

  if (testVal != chkHex) {

    Serial.println(rxBuf);

    Serial.print("Failed message ");

    Serial.print(keyCode);

    Serial.print(" checksum 1 characters ");

    Serial.print(chkHex, HEX);

    Serial.print(':');

    Serial.println((char)chkChar);

    return 15;

  }

  chkHex = chkSum & 0x0F;

  chkChar = rxBuf[(lastCharPos + 2)];

  if (isAlpha(chkChar)) {

    testVal = (uint8_t)(chkChar - 55);

  }

  else {

    testVal = (uint8_t)(chkChar - 48);

  }

  if (testVal != chkHex) {

    Serial.println(rxBuf);

    Serial.print("Failed message ");

    Serial.print(keyCode);

    Serial.print(" checksum 2 characters ");

    Serial.print(chkHex, HEX);

    Serial.print(':');

    Serial.println((char)chkChar);

    return 15;

  }

  /*

    if (keyCode == 'x') {

    Serial.println("Passed NIXIE message checksum 1");

    }

  */

  if ((lastCharPos > 10) && (lastCharPos <= maxCols)) {

    msgs[msgIndex].ageing = (60 * clockList[2]) + clockList[1] - (60 * msgs[msgIndex].hours) - msgs[msgIndex].mins;

    digitalWrite(blueLED, HIGH);

    msgs[msgIndex].len = lastCharPos - 10;;

    msgs[msgIndex].code = keyCode;

    msgs[msgIndex].mins = clockList[1];

    msgs[msgIndex].hours = clockList[2];

    for (int i = 10; i <= lastCharPos; i++) {

      msgs[msgIndex].msgData[(i - 10)] = rxBuf[i];

    }

    msgs[msgIndex].msgData[++lastCharPos] = 0;

    delay(300);

    digitalWrite(blueLED, LOW);

  }

  return msgIndex;

}


Teensy Program Display Functions

char chargeFlag;



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

  Display functions

**********************************************************************

*/

int sysMode;

const int hdrPos = 40;


void displayTimeDate(void) {

  char timeDateBuf[30];

  static uint8_t oldHour, oldMin, oldSec;

  TFTscreen.setTextColor(TFT_BLACK); // erase previous time

  sprintf(timeDateBuf, "   %02u:%02u:%02u %02u/%02u/%02u", oldHour, oldMin, oldSec, clockList[3], clockList[4], clockList[5]);

  TFTscreen.setCursor(0, 13);

  TFTscreen.print(timeDateBuf);

  sprintf(timeDateBuf, "   %02u:%02u:%02u %02u/%02u/%02u", clockList[2], clockList[1], clockList[0], clockList[3], clockList[4], clockList[5]);

  TFTscreen.setTextColor(TFT_GOLD); // prints orange lettrs to TFT display

  TFTscreen.setCursor(0, 13);

  TFTscreen.setFont(Arial_16);

  TFTscreen.print(timeDateBuf);

  oldHour = clockList[2];

  oldMin = clockList[1];

  oldSec = clockList[0];

}

void displayEditDate(void) {

  char timeDateBuf[30];

  static uint8_t oldHour, oldMin, oldSec;

  TFTscreen.setTextColor(TFT_BLACK); // erase previous time

  sprintf(timeDateBuf, "   %02u:%02u:%02u %02u/%02u/%02u", oldHour, oldMin, oldSec, editList[3], editList[4], editList[5]);

  TFTscreen.setCursor(0, 13);

  TFTscreen.print(timeDateBuf);

  sprintf(timeDateBuf, "   %02u:%02u:%02u %02u/%02u/%02u", editList[2], editList[1], editList[0], editList[3], editList[4], editList[5]);

  TFTscreen.setTextColor(TFT_WHITE); // prints orange lettrs to TFT display

  TFTscreen.setCursor(0, 13);

  TFTscreen.setFont(Arial_16);

  TFTscreen.print(timeDateBuf);

  oldHour = editList[2];

  oldMin = editList[1];

  oldSec = editList[0];

}


void displayPeriod(int pVal) {

  TFTscreen.fillRect(260, hdrPos, 60, 20, TFT_BLACK);

  TFTscreen.setFont(Arial_16);

  TFTscreen.setTextColor(TFT_WHITE);

  TFTscreen.setCursor(260, hdrPos);

  TFTscreen.print(pVal, DEC);

}


void displayBigTime(int dpos, int tpos) {

  char timeBuf[25];

  static uint8_t oldHour, oldMin, oldSec;

  sprintf(timeBuf, " %s,%s %02u,20%02u", dayName[clockList[6]], monthName[clockList[3]], clockList[4], clockList[5]);

  TFTscreen.setFont(Arial_16);

  TFTscreen.fillRect(0, dpos, 320, 25, TFT_BLACK);

  TFTscreen.setTextColor(TFT_GREEN);

  TFTscreen.setCursor(0, dpos);

  TFTscreen.print(timeBuf);

  sprintf(timeBuf, "%02u:%02u:%02u", oldHour, oldMin, oldSec);

  TFTscreen.setFont(Arial_40);

  TFTscreen.setTextColor(TFT_BLACK); // erase previous time

  TFTscreen.setCursor(0, tpos);

  TFTscreen.print(timeBuf);

  sprintf(timeBuf, "%02u:%02u:%02u", clockList[2], clockList[1], clockList[0]);

  TFTscreen.setTextColor(TFT_ORANGE);

  TFTscreen.setCursor(0, tpos);

  TFTscreen.print(timeBuf);

  TFTscreen.setFont(Arial_16);

  TFTscreen.setCursor(235, (tpos + 15));

  if (tzHours == 7) {

    TFTscreen.print(" PDST");

  }

  if (tzHours == 8) {

    TFTscreen.print(" PST");

  }

  oldHour = clockList[2];

  oldMin = clockList[1];

  oldSec = clockList[0];

}


uint8_t readDateVal;

uint8_t readMonthVal;

uint8_t readYearVal;


void displayReadDate(int pos) {

  char readDateBuf[20];

  TFTscreen.fillRect(10, (pos - 12), 230, 20, TFT_BLACK);

  TFTscreen.setCursor(10, pos);

  TFTscreen.setFont(Arial_16);

  sprintf(readDateBuf, "%02u/%02u/20%02u", readMonthVal, readDateVal, readYearVal);

  TFTscreen.setTextColor(TFT_YELLOW); // prints orange lettrs to TFT display

  TFTscreen.print(readDateBuf);

}


void dispHourMin(uint8_t inx) {

  TFTscreen.fillRect(100, 140, 98, 30, TFT_BLACK);

  TFTscreen.setCursor((buttXpos[4] + 180), (buttYpos[4] + 10));

  TFTscreen.setFont(Arial_20);

  TFTscreen.setTextColor(TFT_ORANGE);

  TFTscreen.print(editList[inx]);

}


void dispHeader(void) {

  uint8_t index;

  TFTscreen.fillRect(0, hdrPos, 339, 20, TFT_BLACK);

  TFTscreen.setFont(Arial_16);

  TFTscreen.setTextColor(TFT_WHITE);

  TFTscreen.setCursor(5, hdrPos);

  index = Pages[sysMode].hdrInx;

  if (index != 21) {

    TFTscreen.print(headers[index]);

  }

  if (sysMode == 6) {

    displayBattVolt(255, hdrPos);

  }

}


void displayBattVolt(uint8_t xPos, uint8_t yPos) {

  TFTscreen.setCursor(xPos, yPos);

  TFTscreen.setFont(Arial_16);

  TFTscreen.setTextColor(TFT_GREEN);

  if (msgs[11].msgData[6] > '3') {

    TFTscreen.setTextColor(TFT_BLUE);

  }

  else {

    if (msgs[11].msgData[8] < '5') {

      TFTscreen.setTextColor(TFT_RED);

    }

  }

  for (int i = 6; i < 10; i++) {

    TFTscreen.write(msgs[11].msgData[i]);

  }

}


void displayMsgNames(uint8_t index, int pos) {

  TFTscreen.fillRect(0, pos, 339, 20, TFT_BLACK);

  TFTscreen.setFont(Arial_16);

  TFTscreen.setTextColor(TFT_WHITE);

  TFTscreen.setCursor(15, pos);

  TFTscreen.print(msgNames[index]);

  TFTscreen.setCursor(150, pos);

  TFTscreen.print(msgs[index].ageing);

}


void displayButtons(void) {   // Displays the buttons for the current sysMode

  uint8_t buttVal;

  for (int buttID = 0; buttID < 5; buttID++) {

    buttVal = Pages[sysMode].buttInx[buttID];

    if (((buttVal != 0) && (buttVal != 21)) || ((buttVal == 0) && (buttID < 4) )) {                   // erase old button

      TFTscreen.fillRect(buttXpos[buttID], buttYpos[buttID], buttWidth[buttID], buttDepth[buttID], TFT_BLACK);

      delay(10);

      TFTscreen.drawRect(buttXpos[buttID], buttYpos[buttID],  buttWidth[buttID], buttDepth[buttID], TFT_WHITE);

      delay(10);

    }

    TFTscreen.setCursor((buttXpos[buttID] + 10), (buttYpos[buttID] + 10));

    TFTscreen.setFont(Arial_16);

    TFTscreen.setTextColor(TFT_WHITE);

    if (((buttVal > 0) && (buttVal < 20))  || ((buttVal == 0) && (buttID < 4) )) {

      TFTscreen.print(navButtons[buttVal]);

    }

    if ((buttVal >= 'A') && (buttVal < 'Z')) {

      TFTscreen.print(actionButtons[(buttVal - 'A')]);

    }

    delay(20);

  }

}

Teensy Program File Functions

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

  File functions

**********************************************************************

*/


void recordMsg(int index) {

  /* Records on one line the contents of rxBuf

    The filename is /yy/mm/dd

    The file format is hh:mm:ss,r,(data) r is keycode

  */

  File recFile;

  char filename[20];

  char recMsg[45];

  transmit('B');

  for (int i = 0; i < msgSize; i++) {

    recMsg[i] = 0;

  }

  sprintf(recMsg, "%c,%02u:%02u%s\r\n", msgs[index].code, msgs[index].hours, msgs[index].mins, msgs[index].msgData);


  sprintf(filename, "/%02u/%02u/%02u.csv", clockList[5], clockList[3], clockList[4]);

  recFile = SD.open(filename, FILE_WRITE);

  if (recFile) {

    // Serial.print("Writing ");

    // Serial.println(filename);

    recFile.write(recMsg);

    recFile.close();

  }

  else {

    Serial.print("Failed to write to ");

    Serial.println(filename);

  }

  Serial.print(recMsg);

  transmit('N');

}


void displaySdFile() {

  File file;

  char picChar;

  uint8_t bufPos;

  char filename[20];

  char buf[35];

  int lineCount = 0;

  transmit('B');

  sprintf(filename, "/%02u/%02u/%02u.csv", readYearVal, readMonthVal, readDateVal);

  file = SD.open(filename, FILE_READ);

  if (file) {

    Serial.print(filename);

    Serial.println(" is opened for reading");

    bufPos = 0;

    while (file.available()) {

      do {

        picChar = file.read();

        if (bufPos < 35) {

          buf[bufPos++] = picChar;

        }

        if (picChar == 10) {

          ++lineCount;

        }

      }  while ((picChar > 31) && (picChar < 128));

      buf[bufPos] = 0;

      Serial.print(buf);

      bufPos = 0;

    }

  }

  else {

    Serial.print("Unable to open file ");

    Serial.println(filename);

  }

  file.close();

  TFTscreen.setCursor(0, 67);

  TFTscreen.print(lineCount);

  TFTscreen.print(" records read");

  TFTscreen.setCursor(0, 90);

  TFTscreen.print("File ");

  TFTscreen.print(filename);

  TFTscreen.println(" is closed");

  Serial.print("File ");

  Serial.print(filename);

  Serial.println(" is closed");

  transmit('N');

}

Teensy Program Sensor Function

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

           BME680 and input functions

**********************************************************************

*/


void getEnvironmental() {

  int32_t  temp, humidity, pressure, gas;

  int32_t altVal, pVal;

  BME680.getSensorData(temp, humidity, pressure, gas);

  altVal = (temp * 9.0) / 5.0;

  altVal += 3200;

  pVal = pressure * 0.02983;

  sprintf(msgs[12].msgData, ",%03d.%01d,%03d,%02d.%02d,%5d ",

          (int8_t)(altVal / 100), (uint8_t)(altVal % 100),                     // Temperature in decidegrees

          (int8_t)(humidity / 1000),                                           // Humidity in milli-percent

          (int16_t)(pVal / 100), (uint8_t)(pVal % 100),                        // Pressure in hepa Pascals or in Hg

          (int16_t)(gas / 100));                                               // Gas Quality

  temp = analogRead(chargePort) * chargeRatio;

  sprintf(msgs[8].msgData, ",%01d.%02d,", (int16_t)(temp / 100), (uint8_t)(temp % 100));

  chargeFlag = (temp / 100) + '0';

}


void transmit(char busyFlag) {

  // Transmit the status message

  Serial3.write('~');

  delay(5);

  Serial3.write('<');

  Serial3.write(chargeFlag);

  Serial3.write(',');

  Serial3.write(busyFlag);

  Serial3.println('>');

}

Teensy Program Touchscreen Function


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

  Touchscreen function

**********************************************************************

*/


bool is_pressed(int index) {            // button 0-4

  bool pressed = false;

  TS_Point p = ts.getPoint();

  if ((p.y < buttYmax[index]) && (p.y > buttYmin[index]) && (p.x < buttXmax[index]) && (p.x > buttXmin[index])) {

    TFTscreen.fillRect(buttXpos[index], buttYpos[index], buttWidth[index], buttDepth[index], TFT_SKYBLUE);

    delay(300);

    TFTscreen.fillRect(buttXpos[index], buttYpos[index], buttWidth[index], buttDepth[index], TFT_BLACK);

    delay(300);

    pressed = true;

  }

  return pressed;

}

Teensy Program Setup Function


uint16_t secCount = 0;

uint8_t oldSec = 10;


char picCmd = '1';            //1 = black button on, 0 = black button off

const uint16_t maxSecCount = 900;

uint8_t blTurnoffCountMax = 20;

uint8_t ticInterval = 200;


void setup() {

  int EEPROMaddr;

  Serial.begin(9600);         //USB

  Serial5.begin(2400);        //Receiver

  Serial3.begin(9600);        //Picaxe

  Serial4.begin(9600);        //Monitor

  Wire.begin();

  pinMode(blueLED, OUTPUT);

  pinMode(PIN_IRQ, INPUT_PULLUP);

  pinMode(intPort, INPUT_PULLUP);

  pinMode(PIN_RST, OUTPUT);

  pinMode(blPort, OUTPUT);

  digitalWrite(blPort, HIGH);

  initRTC(); // set the Time library to use Teensy 3.5's RTC to keep time

  //tmElements_t nowTime = {0, 31, 18, 4, 21, 9, 23};

  //setRTCtm(&nowTime);

  // BME688

  BME680.begin(I2C_STANDARD_MODE); // Start BME680 using I2C protocol

  BME680.setOversampling(TemperatureSensor, Oversample16); // Use enumerated type values

  BME680.setOversampling(HumiditySensor,   Oversample16); // Use enumerated type values

  BME680.setOversampling(PressureSensor,   Oversample16); // Use enumerated type values

  BME680.setIIRFilter(IIR4); // Use enumerated type values

  BME680.setGas(320, 150); // 320c for 150 milliseconds

  digitalWrite(PIN_RST, LOW);

  delay(1);

  digitalWrite(PIN_RST, HIGH);

  delay(100);

  TFTscreen.begin();

  TFTscreen.setRotation(3);

  TFTscreen.fillScreen(TFT_BLACK); // prints black screen to TFT display

  TFTscreen.setTextColor(TFT_GREEN);

  TFTscreen.setFont(Arial_14);

  delay(5);

  Serial.println("Colorscreen started.");


  if (!ts.begin()) {

    Serial.println("Unable to start touchscreen.");

  }

  else {

    Serial.println("Touchscreen started.");

    ts.setRotation(1);            //Was 0 before upgrade

  }


  if (!SD.begin(BUILTIN_SDCARD)) {

    Serial.println("SD not present");

  }

  else {

    Serial.println("SD initialized");

  }

  tzHours = EEPROM.read(0);

  blTurnoffCountMax = EEPROM.read(1);

  // EEPROM.write(2, 12);

  ticInterval = EEPROM.read(2);

  for (int i = 0; i < 7; i++) {

    EEPROMaddr = 40 * i;

    msgs[i].len = EEPROM.read(EEPROMaddr);

    msgs[i].code = EEPROM.read(EEPROMaddr + 1);

    msgs[i].mins = EEPROM.read(EEPROMaddr + 2);

    msgs[i].hours = EEPROM.read(EEPROMaddr + 3);

    for (int j = 0; j < msgSize; j++) {

      msgs[i].msgData[j] = EEPROM.read(EEPROMaddr + 4 + j);

    }

  }

  picState = 'B';

  newMsg = false;

  secCount = 0;

  sysMode =  16;

  attachInterrupt(digitalPinToInterrupt(intPort), getRecMsg, RISING);

}

Teensy Program Loop Function

void loop() {

  int gotIndex = 15;

  int row;

  static int prevMode = 34;

  uint8_t EEPROMaddr;

  static bool tic = false;

  static uint16_t count, envCount;

  static uint16_t blTurnoffCount;

  static char oldPicState = 'B';

  bool battFlag = false;

  bool eepromFlag = false;

  const uint8_t activePos = 65;

  tic = false;

  if (Serial3.available() > 7) {                  //First measure the charge voltage

    delay(10);

    // Receive the battery voltage message

    msgs[11].msgData[0] = ',';

    Serial3.read();   //'<';

    msgs[11].msgData[5] = ',';

    for (int i = 6; i < 10; i++) {

      picCmd = Serial3.read();

      if ((i == 6) && (picCmd < '5')) {

        battFlag = true;

      }

      if (battFlag) {

        msgs[11].msgData[i] = picCmd;

      }

    }

    msgs[11].msgData[10] = ',';

    picCmd = Serial3.read();   //'>';

    if (picCmd == '>') {

      msgs[11].hours = clockList[2];      //To time-tag BME680 file entries

      msgs[11].mins = clockList[1];

      msgs[11].msgData[12] = 0;

      picState = Serial3.read();

      if (battFlag) {

        msgs[11].msgData[11] = picState;             //Picaxe ststus: S, B, R or O

      }

      if (oldPicState != picState) {

        Serial.print("Picaxe State change:");

        Serial.print(oldPicState);

        Serial.print(" to ");

        Serial.println(picState);

        if((oldPicState == 'R') && (picState == 'O')) {

          eepromFlag = true;

        }

      }

    }

    while (Serial3.available()) {

      Serial3.read();

    }

  }


  if (ts.touched())  {

    uint8_t buttVal;

    int deltaVal = 4;

    uint8_t editVal;

    uint8_t editIndex;

    for (int buttID = 0; buttID < 5; buttID++) {

      buttVal = Pages[sysMode].buttInx[buttID];   //Can be a (binary) number or a letter

      if (is_pressed(buttID)) {

        tic = true;

        if (buttVal < 30) {                       //For navigation

          sysMode = buttVal;

          if (buttVal == 16) {

            eepromFlag = true;

          }

        }

        else {                                    //A letter for an action

          switch (buttVal) {

            case 'A':

              editTt = untTt;                   //For adjusting clock

              breakTime(editTt, &editTm);

              editList[0] = editTm.Second;

              editList[1] = editTm.Minute;           // 0 - 59

              editList[2] = editTm.Hour;             // 0 - 23

              editList[5] = editTm.Year;             // - 2000

              editList[3] = editTm.Day;

              editList[4] = editTm.Month;

              sysMode = 10;

              break;

            case 'B':

              readDateVal = clockList[4];             //Convenient preset for file operations

              readMonthVal = clockList[3];

              readYearVal = clockList[5];

              sysMode = 9;

              break;

            case 'C':

              tzHours = 8;

              break;

            case 'D':

              tzHours = 7;

              break;

            case 'F':

              deltaVal -= 2;

            case 'E':

              deltaVal -= 2;

              switch (sysMode) {

                case 10:

                  editIndex = 5;

                  break;

                case 11:

                  editIndex = 4;

                  break;

                case 12:

                  editIndex = 3;

                  break;

                case 13:

                  editIndex = 2;

                  break;

                case 14:

                  editIndex = 1;

                  break;

              }

              editVal = editList[editIndex];

              editVal += deltaVal - 1;

              editList[editIndex] = editVal;

              dispHourMin(editIndex);

              break;

            case 'G':

              editList[0] = editTm.Second;

              editTm.Minute = editList[1];         // 0 - 59

              editTm.Hour = editList[2];           // 0 - 23

              editTm.Year = editList[5];           // - 2000

              editTm.Day = editList[3];

              editTm.Month = editList[4];

              setRTCtm(&editTm);

              sysMode = 16;

              break;

            case 'H':

              if (msgs[11].msgData[7] != 'O') {

                displaySdFile();

                sysMode = 16;

              }

              break;

            case 'I':

              if (readDateVal < 31) {

                ++readDateVal;

              }

              break;

            case 'J':

              if (readDateVal > 1) {

                --readDateVal;

              }

              break;

            case 'K':

              if (readMonthVal < 12) {

                ++readMonthVal;

              }

              else {

                ++readYearVal;

                readMonthVal = 1;

              }

              break;

            case 'L':

              if (readMonthVal > 1) {

                --readMonthVal;

              }

              else {

                --readYearVal;

                readMonthVal = 12;

              }

              break;

            case 'M':

              eepromFlag = true;

              sysMode = 16;

              break;

            case 'N':

              deltaVal -= 2;

            case 'O':

              deltaVal -= 2;

              ticInterval += deltaVal - 1;

              break;

            case 'P':

              deltaVal -= 2;

            case 'Q':

              deltaVal -= 2;

              blTurnoffCountMax += deltaVal - 1;

              break;

          }

        }

      }

      delay(300);

    }

  }

  untTt = readRTC();

  timeDateGet();

  count = clockList[0];

  if (count != oldSec) {

    oldSec = count;

    if (secCount < maxSecCount) {

      ++secCount;

    }

    else {

      secCount = 0;

    }

    if ((sysMode == 6) && (chargeFlag < '3')) {

      if (blTurnoffCount > 0) {

        --blTurnoffCount;

      }

      else {

        digitalWrite(blPort, LOW);

      }

    }

    switch (Pages[sysMode].tdDisp) {

      case 0:                                   //Initial big time

        displayBigTime(15, 80);

        break;

      case 1:                                 //Adjust clock

        displayEditDate();

        break;

      case 2:                                   // Date & Month set

        displayReadDate(22);

        break;

      case 3:

        displayTimeDate();                      //Most

        break;

      case 4:

        displayPeriod(10 * ticInterval);

        break;

      case 5:

        displayPeriod(blTurnoffCountMax);

        break;

    }

    envCount = secCount % (10 * ticInterval);

    if ((envCount == 11) || (envCount == 12)) {

     // if ((envCount == 8) || (envCount == 11) || (envCount == 12)) {

      getEnvironmental();

      msgs[envCount].hours = clockList[2];      //To time-tag BME680 file entries

      msgs[envCount].mins = clockList[1];

      recordMsg(envCount);

      if (sysMode != 9) {                       //Initial is already updated every second

        tic = true;

      }

    }

  }


  if (newMsg) {

    gotIndex = verifyRecMsg();

    //Serial.print("Got message = ");

   // Serial.println(gotIndex);

    if ((gotIndex < 7)) {

      recordMsg(gotIndex);

      digitalWrite(blPort, HIGH);

      blTurnoffCount = blTurnoffCountMax;

      tic = true;

      if (sysMode == 6) {

        displayMsgNames(gotIndex, activePos);

      }

    }

    if (sysMode == 6) {

      const uint8_t rowPos[4] = {activePos + 25, activePos + 50 , activePos + 75 , activePos + 100};

      switch (gotIndex) {

        case 1:

          displayField(0, 1, rowPos[0]);     //Outside temperature

          displayField(1, 1, rowPos[1]);     //Outside humidity

          displayField(1, 2, rowPos[2]);    //Solar Current

          displayField(2, 3, rowPos[3]);    //Solar battery voltage

          break;

        case 2:

          displayField(0, 4, rowPos[0]);    //Barometer

          displayField(1, 3, rowPos[1]);    //Today Charge

          displayField(1, 4, rowPos[2]);    //Yesterday MAH

          displayField(2, 1, rowPos[3]);     //Solar  Charging voltage

          break;

        case 3:

          displayField(3, 1, rowPos[0]);    //Den Gas Quality Hot

          displayField(3, 2, rowPos[1]);    //Den Gas Quality Cold

          displayField(3, 3, rowPos[2]);    //Gas Alarm

          break;

        case 4:

          displayField(0, 2, rowPos[0]);    //Den temperature

          displayField(2, 4, rowPos[1]);    //Den Light

          break;

        case 5:

          displayField(4, 0, rowPos[0]);     //Garage door

          displayField(4, 1, rowPos[1]);     //Garage Fan

          displayField(4, 2, rowPos[2]);    //Garage temperature

          break;

      }

    }

  }

  newMsg = false;


  if (sysMode != prevMode) {

    prevMode = sysMode;

    TFTscreen.fillRect(0, 0, 310, 205, TFT_BLACK);

    prevMode = sysMode;

    tic = true;

    digitalWrite(blPort, HIGH);

    blTurnoffCount = blTurnoffCountMax;

  }

  if (tic ) {

    displayButtons();

    dispHeader();

  }

  if (tic && (sysMode <= 6)) {

    switch (sysMode) {

      case 0:

        TFTscreen.setTextColor(TFT_WHITE);

        for (int j = 0; j < 7; j++) {

          if ((msgs[j].len > 0)) {

            row = 15 + (25 *  j);

            TFTscreen.setCursor(0, row);

            TFTscreen.fillRect(0, row, 319, 20, TFT_BLACK); // Erase previous display

            TFTscreen.setCursor(0, row);

            TFTscreen.print(msgs[j].code);

            TFTscreen.print(',');

            TFTscreen.print(msgs[j].hours);

            TFTscreen.print(':');

            TFTscreen.print(msgs[j].mins);

            for (int i = 0; i <= msgs[j].len; i++) {

              TFTscreen.print(msgs[j].msgData[i]);

            }

          }

        }

        break;

      case 1:

        displayField(0, 0, 65);     //Local temperature

        displayField(0, 1, 90);     //Outside temperature

        displayField(0, 2, 115);    //Den temperature

        displayField(0, 3, 140);    //Garage Temperature

        displayField(0, 4, 165);    //Barometer

        break;

      case 2:

        displayField(1, 0, 65);     //Local humidity

        displayField(1, 1, 90);     //Outside humidity

        displayField(1, 2, 115);    //Solar Current

        displayField(1, 3, 140);    //Today Charge

        displayField(1, 4, 165);    //Yesterday MAH

        break;

      case 3:

        displayField(2, 0, 65);     //Local Charging voltage

        displayField(2, 1, 90);     //Solar  Charging voltage

        displayField(2, 2, 115);    //Local battery voltage

        displayField(2, 3, 140);    //Solar battery voltage

        displayField(2, 4, 165);    //Den Light

        break;

      case 4:

        displayField(3, 0, 65);     //Local Gas Quality

        displayField(3, 1,  90);    //Den Gas Quality Hot

        displayField(3, 2, 115);    //Den Gas Quality Cold

        displayField(3, 3, 140);    //Gas Alarm

        break;

      case 5:

        displayField(4, 0, 65);     //Garage door

        displayField(4, 1, 90);     //Garage Fan

        displayField(4, 2, 115);    //Garage temperature

        break;

    }

  }

  tic = false;

  if (eepromFlag) {

    EEPROM.write(0, tzHours);

    EEPROM.write(1, blTurnoffCountMax);

    EEPROM.write(2, ticInterval);

    for (int i = 1; i < 7; i++) {

      EEPROMaddr = 40 * i;

      EEPROM.write(EEPROMaddr, msgs[i].len);

      EEPROM.write((EEPROMaddr + 1), msgs[i].code);

      EEPROM.write((EEPROMaddr + 2), msgs[i].mins);

      EEPROM.write((EEPROMaddr + 3), msgs[i].hours);

      for (int j = 0; j < msgSize; j++) {

        EEPROM.write((EEPROMaddr + 4 + j), msgs[i].msgData[j]);

      }

    }

  }

  eepromFlag = false;

  oldPicState = picState;

  delay(10);

}