I think I'm storing too big of an array

Hey everyone,

So what's interesting is that one line of code, which compiles just fine, is making my entire project go crazy. I think I've narrowed it down to affecting assigned variables.

Now I have these 2 codes in 2 different places, very similar in that they have a giant array.

unsigned int Signal_Open[] = {1144,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383}; //AnalysIR Batch Export - RAW irsend.sendRaw(Signal_Open, sizeof(Signal_Open)/sizeof(int), 38);

and

unsigned int Signal_Stop[] = {1196,416,1248,416,416,1274,1248,416,416,1274,416,1274,416,1274,416,1274,1248,416,416,1274,416,1274,416,16383,1248,416,1248,416,416,1274,1248,416,416,1274,416,1274,416,1274,416,1274,1248,416,416,1274,416,1274,416,16383,1248,416,1248,416,416,1274,1248,416,416,1274,416,1274,416,1274,416,1274,1248,416,416,1274,416,1274,416,16383,1248,416,1248,416,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,16383}; //AnalysIR Batch Export - RAW irsend.sendRaw(Signal_Stop, sizeof(Signal_Stop)/sizeof(int), 38);

What supposedly stops the output from going crazy (in other areas of code I've tested): - commenting out one of the irsend lines. - cropping one of the arrays down to about 1/3 the size

I don't think it's logic related because if I comment out one of the irsend lines, then the other works just fine. Now maybe it's an irsend limitation? But cropping one of the arrays down and leaving both irsends active fixes it as well.

When I say output going crazy, what I mean more specifically is that anything involved with variables just prints out nothing. Straight text string prints still keep working, my script is still looping. Menus still work (minus the variable lcd prints).

Anyone have any ideas on what I can do? I need to store 3 of those giant arrays because they're raw IR codes, and it's nearly impossible to decode them.

thx in advance!

Well, the good news is that it isn't the size of the arrays. The two arrays total 192 bytes, well within the RAM size available on an Uno (for example).

The bad news is that we don't have any mind readers that hang around here, so we are having a hard time trying to figure out what library you are using, how many other variables you are declaring, and what else your code is doing.

Full code == full help.

Not full code == See first two paragraphs.

Haha my bad. I actually tried to slim it down cause it was too big… I’ll just split it into multiple posts.

#include <Wire.h>
#include "RTClib.h" // chronodot for timekeeping
#include <Adafruit_MCP23017.h>  // lcd
#include <Adafruit_RGBLCDShield.h>    // lcd
#include <Time.h>      // for time manipulation
#include <EEPROM.h>    // data persistence
#include "EEPROMAnything.h"  // data persistence
#include <IRremote.h>    // infrared 
#include <avr/pgmspace.h>  // for PROGEMEM

Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();

// These #defines make it easy to set the backlight color
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7

// set default alarm
byte alarmHr = 6;
byte alarmMin = 58;


time_t alarm_t;

// Data persistentce variables
struct config_t
{
    long alarm_t;
    
} configuration;

RTC_DS1307 RTC;

// IR setup
int STATUS_PIN = 13;

IRsend irsend;

void setup() {
 
  // Begin the Serial connection 
  Serial.begin(9600);
 
  // Instantiate the RTC
  Wire.begin();
  RTC.begin();
 
  // Check if the RTC is running.
  if (! RTC.isrunning()) {
    Serial.println("RTC is NOT running");
  }

  // This section grabs the current datetime and compares it to
  // the compilation time.  If necessary, the RTC is updated.
  DateTime now = RTC.now();
  DateTime compiled = DateTime(__DATE__, __TIME__);
  if (now.unixtime() < compiled.unixtime()) {
    Serial.println("RTC is older than compile time! Updating");
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }
 
  Serial.println("RTC Setup complete.");
  
  
  // LCD SETUP
  // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  
  // Read from EEPROM
  EEPROM_readAnything(0, configuration);
  
  // IR setup
  pinMode(STATUS_PIN, OUTPUT);
  
  setAlarmTime();
  setCurrTime();
}

String current_state = 0;

void loop() {
  
  alarmCheck();
  
  uint8_t buttons = lcd.readButtons();
  
  if (buttons) {
    
    // SELECT button changes state to menu,
    // changes state back when pressed again
    if (buttons & BUTTON_SELECT) {
      lcd.clear();
      lcd.setCursor(0,0);
      
      mainMenu();      // goes to main menu
      setCurrTime();  // now we're back, so lets go back to clock
      
    }
    
    
    switch(buttons){
      case BUTTON_LEFT:
        curtain_open();
        delay(200);
        break;
        
      
      case BUTTON_RIGHT:
        //unsigned int Signal_Close[] = {1222,416,1248,416,416,1248,1248,416,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,884,780,104,1638,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,962,702,104,1638,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,416,1248,416,1248,416,1248,416,1248,416,16383}; //AnalysIR Batch Export - RAW
        //irsend.sendRaw(Signal_Close, sizeof(Signal_Close)/sizeof(int), 38); 
        delay(200);
        break;
      
    }
  
  
   
  }
  
  

}

// Lets user know that it is indeed
// it mane
void dasItMane(){
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("DAS IT MANE");
  
  lcd.setCursor(0,1);
  lcd.print("..das it");
  
  delay(1000);
  
}

// Blasts open IR signal
// until either interval is up or button press
void curtain_open(){
  // Change LCD
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Opening curtains");
  
  // Send signal
  unsigned int Signal_Open[] = {1144,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383}; //AnalysIR Batch Export - RAW
  irsend.sendRaw(Signal_Open, sizeof(Signal_Open)/sizeof(int), 38);
  
  do{
    // check for button press
    uint8_t buttons = lcd.readButtons();
    
    if(buttons){
      curtain_stop();
      delay(200);
      break;

    }
  }while(1==1);
  
}

// Curtain close

// curtain stop


void curtain_stop(){
  // send stop IR signal
  unsigned int Signal_Stop[] = {1196,416,1248,416,416,1274,1248,416,416,1274,416,1274,416,1274,416,1274,1248,416,416,1274,416,1274,416,16383,1248,416,1248,416,416,1274,1248,416,416,1274,416,1274,416,1274,416,1274,1248,416,416,1274,416,1274,416,16383,1248,416,1248,416,416,1274,1248,416,416,1274,416,1274,416,1274,416,1274,1248,416,416,1274,416,1274,416,16383,1248,416,1248,416,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,16383}; //AnalysIR Batch Export - RAW
  irsend.sendRaw(Signal_Stop, sizeof(Signal_Stop)/sizeof(int), 38);
  
  dasItMane();
}


// EEPROM save
void save(){
  EEPROM_writeAnything(0, configuration);
  
  // Saving
  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print("...saving");
  
  delay(1000);
  
}

void setAlarmTime(){
  // First check if alarm is already
  // saved in EEPROM
  if(configuration.alarm_t != 0){
    // lets use that then
    alarm_t = configuration.alarm_t;
    alarmHr = hour(alarm_t);
    alarmMin = minute(alarm_t);
  }
  else{
    // otherwise use defaults 
    tmElements_t alarm_tm;
    
    alarm_tm.Second = 0;
    alarm_tm.Hour = alarmHr;
    alarm_tm.Minute = alarmMin;
    
    // This part doesn't really matter
    // Since alarm is on daily basis
    alarm_tm.Day = 26;
    alarm_tm.Month = 3;
    alarm_tm.Year = 2014-1970;
    
    // set the time_t element
    alarm_t=makeTime(alarm_tm);
    
    // store to eeprom
    configuration.alarm_t = alarm_t;
  }
  Serial.print("Setting alarm to go off at: ");
  Serial.print(hour(configuration.alarm_t));
  Serial.print(":");
  Serial.println(minute(configuration.alarm_t));

  
}

// Sets current time to LCD screen
void setCurrTime(){
  DateTime now = RTC.now(); // get curr time
  String print_str;
  
  lcd.clear();            // clean wahtevers on screen
  lcd.setCursor(0,0);
  
  // Get formatted time
  print_str = getFormattedTime(now.hour(),now.minute(),12);
  
  // Log it
  Serial.print("Setting the time as: ");
  Serial.print(print_str);
  Serial.println();
  
  // write datdere time
  lcd.print(print_str);
  
  
}

// Takes hour and min and returns in 
// string in 00:00 format.
// Set tFormat to 12 for 12 hour clock
String getFormattedTime(int tHour, int tMin, int tFormat){
  
  String hour_suffix = "";
  
  // If on 12H, conv tHour to 12H
  if(tFormat == 12){
    if(tHour > 12){
      tHour = tHour - 12;
      hour_suffix = "PM";
      
    }else{
      hour_suffix = "AM";
      
    }
    
    if(tHour == 0){
      tHour = 12;
    }
  }
  
  
  // conv single digit min to
  // double digit
  String minute_prefix = "";
  if(tMin <= 9){
    minute_prefix = "0";
    
  }
  
  // setup output string
  String output = String(tHour);
  output = String(output + ":" + minute_prefix + tMin + " " + hour_suffix);
  
  // Return it
  return String(output);
  
}

Part 2:

Edit: Might as well tell what I’m doing. It’s an alarm clock that controls my curtains. So it has RTC to track time and then opens my curtains at a set a time. The code is far from finished. Right now I’m trying to get BUTTON_LEFT to open the curtains and then when I press another key the curtains stop moving .

int alarmOn = false;

void alarmCheck(){
   // Get the current time
   DateTime now = RTC.now(); 
   
   /**Logging for debugging
   Serial.print("AlarmHr is ");
   Serial.print(alarmHr);
   Serial.print(" and current hour is ");
   Serial.print(now.hour(),DEC);
   Serial.println();
   
   Serial.print("AlarmMin is ");
   Serial.print(alarmMin);
   Serial.print(" and current min is ");
   Serial.print(now.minute(),DEC);
   Serial.println();
   **/
   
   if( alarmHr == now.hour() && alarmMin == now.minute() ){
     alarmOn = true;
   }
   
   if(alarmOn == true){
     Serial.print("Alarm ON DING DING IDNG");
     Serial.println();
   }

  
}



void mainMenu() {
  String menu_firstrow_str = "MENU";
  String menu_secondrow_str;
  
  int menuposition = 0;
  byte num_submenus = 2; //count starts from 0
  byte leave_menu=0;
 
 
  do{
    
    uint8_t buttons = lcd.readButtons();
    byte go_submenu =0;
   
    // Sift through menu when up/down gets pressed
    switch(buttons) {
      case BUTTON_UP:
        menuposition += 1;
        if(menuposition > num_submenus) menuposition=0;
        delay(200);
        break;
      case BUTTON_DOWN:
        menuposition -= 1;
        if(menuposition < 0) menuposition=num_submenus;
        delay(200);
        break;
      case BUTTON_LEFT:
        leave_menu = 1;
        delay(200);
        break;
      case BUTTON_RIGHT:
        go_submenu = 1;
        delay(200);
        break;
 
    }
    
    if(leave_menu == 1){
      break;
    }
    
    //If button is pressed, refresh display
    if(buttons){
      switch(menuposition){
        case 0:                     // set alarm
          if(go_submenu == 1) showSetAlarmMenu();
          menu_secondrow_str = " Set alarm";
          break;
        case 1:                    // set lcd color
          menu_secondrow_str = " Set LCD color";
          break;
        case 2:                    // set time
          menu_secondrow_str = " Set time";
          break;
        
      }
      
      // Refresh LCD with relevent text
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print(menu_firstrow_str);
      lcd.setCursor(0,1);
      lcd.print(menu_secondrow_str);
      
    }
    
    
    //delay(200);
  }while(1==1);
  
}


void showSetAlarmMenu(){
  lcd.clear();
  lcd.setCursor(0,0);
  
  lcd.print("Set alarm");
  
  
  int blinkState = LOW;             // used to track last state
  long previousMillis = 0;        // will store last time state was updated
  long interval = 500;           // interval at which to blink (milliseconds)
  
  // where to set lcd.cursor to flash 2 chars
  // start positions of Hrs, Min, and AM/PM
  byte flash_coordinates[] = {1,4,7};  
  int flash_coord_key = 0;
  
  do{
    
    // START TEXT FLASHING
    // check to see if it's time to blink the number; that is, if the 
    // difference between the current time and last time you blinked 
    // the number is bigger than the interval at which you want to 
    // blink the number.
    unsigned long currentMillis = millis();
    
    if(currentMillis - previousMillis > interval) {
      // save the last time you blinked the number 
      previousMillis = currentMillis;   
  
      // if the number is off turn it on and vice-versa:
      if (blinkState == LOW){
        blinkState = HIGH;
        
        // if single digit hour, push cursor 1 over

        if((0 < alarmHr && alarmHr <= 9) || (12 < alarmHr && alarmHr <= 21))
          lcd.setCursor(2,1);
        else
          lcd.setCursor(1,1);
          
        lcd.print(getFormattedTime(hour(alarm_t),minute(alarm_t),12));

      }
      else{
        blinkState = LOW;
        
        // flash 2 chars starting at
        // the cursor coordinates
        lcd.setCursor(flash_coordinates[flash_coord_key],1);
        lcd.print("  ");
        
      }
      
    }
    // END TEXT FLASHING

    uint8_t buttons = lcd.readButtons();
    byte leave_menu = 0;
   
    // Change numbers when up/down gets pressed
    switch(buttons) {
      case BUTTON_UP:
        switch(flash_coord_key){
          case 0:        // Add hour
            changeAlarmTime(60*60);
            break;
          case 1:        // Add minute
            changeAlarmTime(60);
            break;
          case 2:        // Toggle AM/PM by adding 12 hours
            changeAlarmTime(60*60*12L);
            break;
        }
        delay(200);
        break;
        
      case BUTTON_DOWN:
        switch(flash_coord_key){
          case 0:       // subtract hour
            changeAlarmTime(60*60*-1);
            break;
          case 1:        // subtract minute
            changeAlarmTime(60*-1);
            break;
          case 2:        // Toggle AM/PM by adding 12 hours
            changeAlarmTime(60*60*-12L);
            break;

        }
        delay(200);
        break;
      
      // Go through time elements with LEFT & RIGHT
      case BUTTON_RIGHT:     
        flash_coord_key += 1;
        if(flash_coord_key > 2) flash_coord_key=0;
        delay(200);
        break;
        
      case BUTTON_LEFT:
        flash_coord_key -= 1;
        if(flash_coord_key<0)flash_coord_key=2;
        delay(200);
        break;
      
      // save & exit on SELECT
      case BUTTON_SELECT:
        save();    // save first
        leave_menu = 1;
        delay(200);
        break;
    }
    
    if(leave_menu == 1){
      break;
    }
    
    
  }while(1==1);
 
  
}

// for adding/subtracting seconds on alarm_t
// DOES NOT SAVE TO PERSISTANCE, make sure you save()
void changeAlarmTime(long add_seconds){
  
  // add secs to unix
  alarm_t = alarm_t + add_seconds;
  
  // save alarmHr and alarmMin
  alarmHr = hour(alarm_t);
  alarmMin = minute(alarm_t);
  
  // store to eeprom var (dont forget to save)
  configuration.alarm_t = alarm_t;
  Serial.print("Changed alarm to go off at: ");
  Serial.print(hour(configuration.alarm_t));
  Serial.print(":");
  Serial.println(minute(configuration.alarm_t));
  
}

Well, that's a pretty big sketch, in terms of RAM usage. What does the IDE report for Sketch and Global Variables usage?

Two things of note...

Strings are not a good thing in a small RAM spaces, such as is found in the Arduino. Every time you change a String, it makes a copy, and deallocates the previous copy. This is not a bad thing on a PC with a few gigs of RAM to play with, but with only a few K in the Arduino, it is very easy to end up with fragmented RAM. The solution? Learn how to use strings, as opposed to Strings (note the capital letter). A string, in C++ is an array of char, and you can do everything with a char array you will ever need to do.

The second thing is constant strings, as you have in your sketch, like Serial.println("RTC is NOT running");. The string in this case is stored in RAM. You can make it store the constant string in program memory with the F() macro, like this:

Serial.println(F("RTC is NOT running"));

There is probably more trimming you can do, but lets work on these two things first.

What I think is happening is your are running out of memory, but only because you keep make new arrays each time you send out the IRsignal.

Put these at the top of your code [u]globally[/u] and get rid of them where they are currently. You can just comment them out if you want.

//unsigned int Signal_Close[] = {1222,416,1248,416,416,1248,1248,416,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,884,780,104,1638,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,962,702,104,1638,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,416,1248,416,1248,416,1248,416,1248,416,16383}; //AnalysIR Batch Export - RAW

unsigned int Signal_Open[] = {1144,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383,1248,416,1248,416,416,1248,1248,416,416,1248,416,1248,416,1248,1248,416,416,1248,416,1248,416,1248,416,16383}; //AnalysIR Batch Export - RAW

unsigned int Signal_Stop[] = {1196,416,1248,416,416,1274,1248,416,416,1274,416,1274,416,1274,416,1274,1248,416,416,1274,416,1274,416,16383,1248,416,1248,416,416,1274,1248,416,416,1274,416,1274,416,1274,416,1274,1248,416,416,1274,416,1274,416,16383,1248,416,1248,416,416,1274,1248,416,416,1274,416,1274,416,1274,416,1274,1248,416,416,1274,416,1274,416,16383,1248,416,1248,416,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,1274,416,16383}; //AnalysIR Batch Export - RAW

Moved the array globally, made no difference. I also tested the code and it doesn't seem to be stuck in a loop, at least for the irsend or array declarations. I did the declaration before the do while loop.

Those arrays do not change at run time, do they? If not, they are ideal candidates for storing in PROGMEM.

Okay I think it may actually be looping somehow, but it's really weird.

I moved the irsend outside of the do while loop like so:

void curtain_open(){
  // Change LCD
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(F("Opening curtains"));
  
  // Send signal
  
  irsend.sendRaw(Signal_Open, sizeof(Signal_Open)/sizeof(int), 38);
  Serial.println(F("Opening curtain"));
  
  curtain_stop();
  
  do{
    // check for button press
    uint8_t buttons = lcd.readButtons();
    
    if(buttons){
      Serial.print(F("Stopping"));
      //curtain_stop();
      delay(200);
      break;

    }
  }while(1==1);
  
}
void curtain_stop(){
  // send stop IR signal
  irsend.sendRaw(Signal_Stop, sizeof(Signal_Stop)/sizeof(int), 38);
  
  dasItMane();
}

I moved curtain_stop, which calls an irsend, outside of the do while.

The variables seem to work normally now. But it makes no sense because curtain_stop() doesn't get called unless a button is pressed. The variables messing up starts from program execution. Like if on button press if it kept looping I could understand....

So does this mean it wasn't a memory issue afterall?

Btw Paul S those arrays are basically static, so I'll take your suggestion and try putting it in progmem.

NInja edit: Here's my serial log

RRTC Setup complete.
Setting alarm to go off at: 15:57
Setting the time as: 12:09 AM
Opening curtain
Stopping
Opening curtain
Stopping

I pressed curtain button, then another button, curtain button again , then another button. So it doesnt' seem to be stuck in an infinite loop...

EDIT: I think it only looks like it works because I was able to shave down the memory usage with F(). When I add in my Signal_Close(), same old error. I'm not so sure I can get away with using progmem because I'm using a 3rd party library...

I guess I have to edit the IRlib to support progmem?..

Old post:

It sort of works now!

But it makes no sense! I'm not even sure why it works now. I put all text strings in F() as lar3y suggested but at first it didn't work. So as I moved curtain_stop() around, I eventually moved it back into the do while loop, even allowing it to loop infiinitely.... and it just works?

Anyways final working code:

void curtain_open(){
  // Change LCD
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(F("Opening curtains"));
  
  // Send signal
  
  irsend.sendRaw(Signal_Open, sizeof(Signal_Open)/sizeof(int), 38);
  Serial.println(F("Opening curtain"));
  
  
  do{
    
    // check for button press
    uint8_t buttons = lcd.readButtons();
    
    if(buttons){
      Serial.println(F("Stopping"));
      curtain_stop();
      delay(200);
      break;

    }
  }while(1==1);
  
}
// curtain stop
void curtain_stop(){
  // send stop IR signal
  irsend.sendRaw(Signal_Stop, sizeof(Signal_Stop)/sizeof(int), 38);
  
  dasItMane();
}

Thanks everyone for taking the time to help!!