Error when accessing variables from array.

Hey guys, sorry for the non descriptive title; I wasn't sure how to put it.

My project makes use of arrays such as these:

const char* const FRI[15] PROGMEM = {"CHEM", "CHEM", "SNK", "ENG", "MATH", "BIO", "BIO", "GEO", "LA2", "LNCH", "SPA", "SPA", "", "", ""};

For memory reasons, the array has been stored in PROGMEM.

The problem is, when I try and access the variables like this:

FRI[pN];

pN is an integer returned by this function:

int pCalc (int x)
{
  if(x >= timeCon(8, 10) && timeCon(8, 45) > x)
  {
    return(0);
  }

  else if(x >= timeCon(8, 45) && timeCon(9, 20) > x)
  {
    return(1);
  }

  else if(x >= timeCon(9, 20) && timeCon(9, 35) > x)
  {
    //SNACKS
    return(2);
  }

  else if(x >= timeCon(9, 35) && timeCon(10, 10) > x)
  {
    return(3);
  }

  else if(x >= timeCon(10, 10) && timeCon(10, 45) > x)
  {
    return(4);
  }

  else if(x >= timeCon(10, 45) && timeCon(11, 20) > x)
  {
    return(5);
  }

  else if(x >= timeCon(11, 20) && timeCon(11, 55) > x)
  {
    return(6);
  }

  else if(x >= timeCon(11, 55) && timeCon(12, 30) > x)
  {
    return(7);
  }

  else if(x >= timeCon(12, 30) && timeCon(1, 5) > x)
  {
    return(8);
  }

  else if(x >= timeCon(1, 5) && timeCon(1, 35) > x)
  {
    return(9);
  }

  else if(x >= timeCon(1, 35) && timeCon(2, 10) > x)
  {
    return(10);
  }

  else if(x >= timeCon(2, 10) && timeCon(2, 45) > x)
  {
    return(11);
  }

   else
   {
    return(12);
   }
}

EDIT: What is actually displayed is gibberish, random characters. Not nothing. I should probably mention that characters occupy the entire screen.

It works fine when I do this:

FRI[4];

But not when I do this:

FRI[pN];

Any help whatsoever would be appreciated.

Thanks in advance.

You need to define pN before using it, such as

byte pN = pCalc (x);

and 'x' must be populated somewhere earlier.

Also pay attention to your time conversion function, which must correctly handle noon, otherwise this will never happen:

else if(x >= timeCon(12, 30) && timeCon(1, 5) > x)

You need to tell it if the time x is AM or PM.

@lesept

I've done that already, like this:

int pN = 0;

(I'm aware that a byte would be more efficient.)

Thanks for the heads up on the second bit, I'm changing it now.

@Delta_G

OK

byte state = 0;

#include <avr/pgmspace.h>
#include <Time.h>
#include <TimeLib.h>
#include <DS3231.h>
#include "Adafruit_GFX.h"
#include <MCUFRIEND_kbv.h>

MCUFRIEND_kbv tft;
DS3231 rtc(0, 1);

#include "timeCon.h"
#include "pCalc.h"
#include "colours.h"
#include "MainMenu.h"

/*
0 = mainmenuvert
1 = mainmenuhor
2 = settings
*/

void setup()
{
  uint16_t ID = tft.readID();
  tft.begin(ID);

  tft.fillScreen(BLACK);
  tft.setCursor(45, 100);
  tft.setTextColor(CYAN, BLACK);
  tft.setTextSize(2);
  tft.print("DeskBoy Mk.5");
  tft.setCursor(80, 170);
  tft.setTextSize(1);
  tft.print("By Aditya.D.S");

  delay(2000);

  tft.fillScreen(BLACK);
  rtc.begin();
}

void loop()
{
  MainMenu();
  delay(50);
}

timeCon:

int timeCon(int h, int m)
{
  return ((h * 60) + m);
}

pCalc

int pCalc (int x)
{
  if(x >= timeCon(8, 10) && timeCon(8, 45) > x)
  {
    return(0);
  }

  else if(x >= timeCon(8, 45) && timeCon(9, 20) > x)
  {
    return(1);
  }

  else if(x >= timeCon(9, 20) && timeCon(9, 35) > x)
  {
    //SNACKS
    return(2);
  }

  else if(x >= timeCon(9, 35) && timeCon(10, 10) > x)
  {
    return(3);
  }

  else if(x >= timeCon(10, 10) && timeCon(10, 45) > x)
  {
    return(4);
  }

  else if(x >= timeCon(10, 45) && timeCon(11, 20) > x)
  {
    return(5);
  }

  else if(x >= timeCon(11, 20) && timeCon(11, 55) > x)
  {
    return(6);
  }

  else if(x >= timeCon(11, 55) && timeCon(12, 30) > x)
  {
    return(7);
  }

  else if(x >= timeCon(12, 30) && timeCon(13, 5) > x)
  {
    return(8);
  }

  else if(x >= timeCon(13, 5) && timeCon(13, 35) > x)
  {
    return(9);
  }

  else if(x >= timeCon(13, 35) && timeCon(14, 10) > x)
  {
    return(10);
  }

  else if(x >= timeCon(14, 10) && timeCon(14, 45) > x)
  {
    return(11);
  }

   else
   {
    return(12);
   }
}

MainMenu:

#include "TimeTable.h"

void MainMenu()
{
  String dt = "";
  String hStr = "";
  String mStr = "";
  String fP = "RRR";
  String f1P = "SSS";
  String f2P = "TTT";
  int hours = 0;
  int minutes = 0;
  int minSinceOne = 0;
  int pNo = 0;
  int pN = 0;
  int pN1 = 0;
  int pN2 = 0;

  dt = rtc.getTimeStr();
  hStr = dt.substring(0, dt.indexOf(":"));
  mStr = dt.substring(dt.indexOf(":") + 1, dt.lastIndexOf(":"));

  hStr = "10";
  mStr = "20";

  hours = hStr.toInt();
  minutes = mStr.toInt();

  if (hStr.length() == 1)
  {
    hStr = "0" + String(hours);
  }

  if (mStr.length() == 1)
  {
    mStr = "0" + String(minutes);
  }

  minSinceOne = timeCon(hours, minutes);

  pN = pCalc(minSinceOne);
  pN1 = pCalc(minSinceOne) + 1;
  pN2 = pCalc(minSinceOne) + 2;

  if (String("Mon").equals(rtc.getDOWStr(FORMAT_SHORT)))
  {
    fP = MON[pN];
    f1P = MON[pN1];
    f2P = MON[pN2];
  }

  else if (String("Tue").equals(rtc.getDOWStr(FORMAT_SHORT)))
  {
    fP = TUE[pN];
    f1P = TUE[pCalc(minSinceOne) + 1];
    f2P = TUE[pCalc(minSinceOne) + 2];
  }

  else if (String("Wed").equals(rtc.getDOWStr(FORMAT_SHORT)))
  {
    fP =  WED[pN];
    f1P = WED[pCalc(minSinceOne) + 1];
    f2P = WED[pCalc(minSinceOne) + 2];

  }

  else if (String("Thu").equals(rtc.getDOWStr(FORMAT_SHORT)))
  {
    fP = THU[pN];
    f1P = THU[pCalc(minSinceOne) + 1];
    f2P = THU[pCalc(minSinceOne) + 2];
  }

  else if (String("Fri").equals(rtc.getDOWStr(FORMAT_SHORT)))
  {
    fP = FRI[(int)pN];
    //f1P = FRI[pCalc(minSinceOne) + 1];
    //f2P = FRI[pCalc(minSinceOne) + 2];
  }

  else if (String("Sat").equals(rtc.getDOWStr(FORMAT_SHORT)))
  {
    fP = SAT[pN];
    f1P = SAT[pCalc(minSinceOne) + 1];
    f2P = SAT[pCalc(minSinceOne) + 2];
  }

  else if (String("Sun").equals(rtc.getDOWStr(FORMAT_SHORT)))
  {
    fP = SUN[pN];
    f1P = SUN[pCalc(minSinceOne) + 1];
    f2P = SUN[pCalc(minSinceOne) + 2];
  }

  else
  {
    fP = "X";
    f1P = "Y";
    f2P = "Z";
  }

  tft.setTextSize(7);
  tft.setTextColor(CYAN, BLACK);
  tft.setCursor(80, 50);
  tft.print(hStr);
  tft.setCursor(80, 110);
  tft.print(mStr);

  tft.setTextSize(2);
  tft.setTextColor(CYAN, BLACK);
  tft.setCursor(100, 10);
  tft.print(rtc.getDOWStr(FORMAT_SHORT));
  tft.setCursor(0, 0);
  tft.print(fP);
  //tft.setCursor(50, 130);
 // tft.print(f1P);
  //tft.setCursor(90, 130);
 // tft.print(f2P);
}

TimeTable.h

const char* const MON[15] PROGMEM = {"CHEM", "ENG", "SNK", "MATH", "MATH", "GEO", "LA2", "PHY", "PHY", "LNCH", "ENG", "HIST", "", "", ""};
const char* const TUE[15] PROGMEM = {"ENG", "HIST", "SNK", "BIO", "PHY", "MATH", "MATH", "COMP", "COMP", "LNCH", "LA2", "GEO", "", "", ""};
const char* const WED[15] PROGMEM = {"CHEM", "MATH", "SNK", "PHY", "HIST", "ENG", "LA2", "COMP", "LNCH", "ENG", "GEO", "", "", ""};
const char* const THU[15] PROGMEM = {"ENG", "LA2", "SNK" , "LIB", "MATH", "MATH", "HIST", "COMP", "COMP", "LNCH", "BIO", "GEO", "", "", ""};
const char* const FRI[15] PROGMEM = {"CHEM", "CHEM", "SNK", "ENG", "MATH", "BIO", "BIO", "GEO", "LA2", "LNCH", "SPA", "SPA", "", "", ""};
const char* const SAT[15] PROGMEM = {"", "", "", "", "", "", "", "", "", "", "", ""};
const char* const SUN[15] PROGMEM = {"", "", "", "", "", "", "", "", "", "", "", ""};

Sorry, now I see what the problem is : you can't read data from PROGMEM just as you would read it from an array. Read this and that.

You need to radically change the way you define your strings and store them in PROGMEM, and change the way you read them:

First, simplify here:

pN = pCalc(minSinceOne);
  pN1 = pCalc(minSinceOne) + 1;
  pN2 = pCalc(minSinceOne) + 2;

to

pN = pCalc(minSinceOne);
  pN1 = pN  + 1;
  pN2 = pN  + 2;

Then define your timetable as this example (for monday, first 7):

const char MON_1[] PROGMEM = "CHEM";
const char MON_2[] PROGMEM = "ENG";
const char MON_3[] PROGMEM = "SNK";
const char MON_4[] PROGMEM = "MATH";
const char MON_5[] PROGMEM = "MATH";
const char MON_6[] PROGMEM = "GEO";
const char MON_7[] PROGMEM = "LA2";
const char *const MON_table[] PROGMEM = {MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7};

and read using:

  char fP[5];
    strcpy_P(fP, (PGM_P)pgm_read_word(&(MON_table[pN])));

When I test it with hStr = "10"; and mStr = "20"; without a RTC to provide the time and force it on Monday, I really get MATH in fP

Note that this also works:

   char f1P[5];
    strcpy_P(f1P, (char *)pgm_read_word(&(MON_table[pN1])));

This puts GEO in f1P.

lesept:

.

.
.
const char *const MON_table[] PROGMEM = {MON_1, MON_2, MON_3, MON_4, MON_5};

Or, leave the the pointer table in RAM so you don't have to do the double PROGMEM access.

Do you mean doing this ?

const char MON_1[] = "CHEM";
const char MON_2[] = "ENG";
const char MON_3[] = "SNK";
const char MON_4[] = "MATH";
const char MON_5[] = "MATH";
const char MON_6[] = "GEO";
const char MON_7[] = "LA2";
const char *const MON_table[] PROGMEM = {MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7};

It doesn't work, I get weird values:

16:52:47.035 -> fP
16:52:47.035 -> ardo
16:52:47.035 -> f1P
16:52:47.035 -> o Leonardo

instead of this:

16:55:01.083 -> fP
16:55:01.083 -> MATH
16:55:01.083 -> f1P
16:55:01.083 -> GEO

For the record, I test this on a Leonardo...

lesept:
Do you mean doing this ?

No, just the opposite:

const char MON_1[] PROGMEM = "CHEM";
const char MON_2[] PROGMEM = "ENG";
const char MON_3[] PROGMEM = "SNK";
const char MON_4[] PROGMEM = "MATH";
const char MON_5[] PROGMEM = "MATH";
const char MON_6[] PROGMEM = "GEO";
const char MON_7[] PROGMEM = "LA2";
const char *const MON_table[] = {MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7};

const uint8_t numStrings = sizeof(MON_table) / sizeof(MON_table[0]);

void setup() {
  char fp[5];

  Serial.begin(115200);
  delay(1000);

  for (uint8_t i = 0; i < numStrings; i++) {
    strcpy_P(fp, MON_table[i]);
    Serial.println(fp);
  }
}

void loop() {}

Typically, you store long strings in PROGMEM because they take lots of RAM bytes. However, pointers are small by comparison. So, there's less to be gained by putting them in PROGMEM. Thus, doing so may not be worth the trouble of pulling them out to then use as pointers back into PROGMEM.

OK, thanks for the info !