0

I've been struggling with weird behavior, crashes and general problems when trying to use an array of four uint16_t groups of data. When I create four separate variables and load that into an array, it works OK and is smaller.

This is for use with the irlib2 SendBase and HashRaw components.

What's going on here?

The code utterly fails with crashes, unresponsive hardware when I use the following variable assignment with an arduino micro. Additionally this version uses about 400 bytes more of memory than the second example

failed variable assignemnt

uint16_t sources[4][RAW_DATA_LEN] = {
  {8550, 4306, 530, 1606, 530, 566, 502, 1610, //POWER ON/OFF
  530, 566, 502, 574, 506, 1630, 506, 566,
  506, 1630, 506, 566, 502, 1610, 530, 566,
  502, 1634, 506, 1606, 530, 570, 498, 1634,
  506, 570, 510, 562, 506, 566, 502, 1634,
  506, 1606, 530, 1610, 530, 562, 538, 538,
  530, 542, 538, 1598, 538, 1570, 558, 542,
  538, 538, 530, 542, 538, 1598, 530, 1578,
  558, 1578, 562, 1000},

  {8546, 4310, 558, 1578, 562, 538, 498, 1638, //sourceCD
  530, 542, 506, 570, 502, 1634, 502, 570,
  498, 1638, 534, 538, 498, 1638, 530, 542,
  506, 1606, 554, 1582, 558, 538, 510, 1602,
  554, 546, 506, 566, 502, 570, 510, 1602,
  554, 1582, 558, 538, 510, 566, 502, 1606,
  554, 546, 502, 1610, 558, 1574, 554, 546,
  502, 570, 510, 1602, 526, 1610, 526, 574,
  506, 1602, 526, 1000},


  {8550, 4306, 530, 1606, 534, 566, 502, 1606,  //sourceCDR
  534, 566, 502, 570, 510, 1602, 526, 570,
  510, 1602, 534, 566, 502, 1606, 534, 566,
  506, 1602, 530, 1606, 534, 566, 502, 1610,
  530, 570, 498, 574, 506, 566, 502, 570,
  510, 1602, 526, 570, 510, 566, 502, 570,
  510, 1602, 526, 1610, 526, 1606, 534, 1602,
  534, 566, 502, 1610, 530, 1602, 534, 1602,
  526, 574, 506, 1000},

  {8550, 4310, 526, 1634, 506, 566, 502, 1634,  //sourceAUX
  506, 566, 502, 574, 506, 1626, 510, 566,
  506, 1630, 506, 566, 502, 1634, 506, 566,
  502, 1634, 506, 1606, 530, 566, 506, 1630,
  506, 566, 502, 1634, 506, 1630, 506, 1630,
  510, 1602, 526, 570, 510, 566, 502, 1634,
  502, 570, 502, 570, 510, 566, 502, 570,
  510, 562, 506, 1630, 506, 1630, 510, 562,
  506, 1630, 510, 1000}
} ;

When I instead assign each grouping to a variable and then create an array of those variables, the code is significantly smaller (about 400 bytes) and appears to run as expected without crashes. Successful variable assignment

#define RAW_DATA_LEN 68
uint16_t powerOnOff[RAW_DATA_LEN] = {
  8550, 4306, 530, 1606, 530, 566, 502, 1610,
  530, 566, 502, 574, 506, 1630, 506, 566,
  506, 1630, 506, 566, 502, 1610, 530, 566,
  502, 1634, 506, 1606, 530, 570, 498, 1634,
  506, 570, 510, 562, 506, 566, 502, 1634,
  506, 1606, 530, 1610, 530, 562, 538, 538,
  530, 542, 538, 1598, 538, 1570, 558, 542,
  538, 538, 530, 542, 538, 1598, 530, 1578,
  558, 1578, 562, 1000
};

uint16_t sourceCD[RAW_DATA_LEN] = {
  8546, 4310, 558, 1578, 562, 538, 498, 1638,
  530, 542, 506, 570, 502, 1634, 502, 570,
  498, 1638, 534, 538, 498, 1638, 530, 542,
  506, 1606, 554, 1582, 558, 538, 510, 1602,
  554, 546, 506, 566, 502, 570, 510, 1602,
  554, 1582, 558, 538, 510, 566, 502, 1606,
  554, 546, 502, 1610, 558, 1574, 554, 546,
  502, 570, 510, 1602, 526, 1610, 526, 574,
  506, 1602, 526, 1000
};

uint16_t sourceAUX[RAW_DATA_LEN] = {
  8550, 4310, 526, 1634, 506, 566, 502, 1634,
  506, 566, 502, 574, 506, 1626, 510, 566,
  506, 1630, 506, 566, 502, 1634, 506, 566,
  502, 1634, 506, 1606, 530, 566, 506, 1630,
  506, 566, 502, 1634, 506, 1630, 506, 1630,
  510, 1602, 526, 570, 510, 566, 502, 1634,
  502, 570, 502, 570, 510, 566, 502, 570,
  510, 562, 506, 1630, 506, 1630, 510, 562,
  506, 1630, 510, 1000
};

uint16_t sourceCDR[RAW_DATA_LEN] = {
  8550, 4306, 530, 1606, 534, 566, 502, 1606,
  534, 566, 502, 570, 510, 1602, 526, 570,
  510, 1602, 534, 566, 502, 1606, 534, 566,
  506, 1602, 530, 1606, 534, 566, 502, 1610,
  530, 570, 498, 574, 506, 566, 502, 570,
  510, 1602, 526, 570, 510, 566, 502, 570,
  510, 1602, 526, 1610, 526, 1606, 534, 1602,
  534, 566, 502, 1610, 530, 1602, 534, 1602,
  526, 574, 506, 1000
};

//Set up array of codes
uint16_t sources[4] = {powerOnOff, sourceCD, sourceCDR, sourceAUX};

This is the code as it stands in an unfinished state. In an effort to trace back the crashes and odd behavior I systematically removed most of the code to the point where the last if statement is sending the IR codes.

Entire Code

#include <movingAvg.h>    // moving average library
#include <IRLibSendBase.h>    //We need the base code
#include <IRLib_HashRaw.h>    //Only use raw sender
#include <elapsedMillis.h>    //measure elapsed time

// ====REMOTE CODES====
//define the IR Sender
IRsendRaw mySender;
#define RAW_DATA_LEN 68
uint16_t powerOnOff[RAW_DATA_LEN] = {
  8550, 4306, 530, 1606, 530, 566, 502, 1610,
  530, 566, 502, 574, 506, 1630, 506, 566,
  506, 1630, 506, 566, 502, 1610, 530, 566,
  502, 1634, 506, 1606, 530, 570, 498, 1634,
  506, 570, 510, 562, 506, 566, 502, 1634,
  506, 1606, 530, 1610, 530, 562, 538, 538,
  530, 542, 538, 1598, 538, 1570, 558, 542,
  538, 538, 530, 542, 538, 1598, 530, 1578,
  558, 1578, 562, 1000
};

uint16_t sourceCD[RAW_DATA_LEN] = {
  8546, 4310, 558, 1578, 562, 538, 498, 1638,
  530, 542, 506, 570, 502, 1634, 502, 570,
  498, 1638, 534, 538, 498, 1638, 530, 542,
  506, 1606, 554, 1582, 558, 538, 510, 1602,
  554, 546, 506, 566, 502, 570, 510, 1602,
  554, 1582, 558, 538, 510, 566, 502, 1606,
  554, 546, 502, 1610, 558, 1574, 554, 546,
  502, 570, 510, 1602, 526, 1610, 526, 574,
  506, 1602, 526, 1000
};

//uint16_t sourceAUX[RAW_DATA_LEN] = {
//  8550, 4310, 526, 1634, 506, 566, 502, 1634,
//  506, 566, 502, 574, 506, 1626, 510, 566,
//  506, 1630, 506, 566, 502, 1634, 506, 566,
//  502, 1634, 506, 1606, 530, 566, 506, 1630,
//  506, 566, 502, 1634, 506, 1630, 506, 1630,
//  510, 1602, 526, 570, 510, 566, 502, 1634,
//  502, 570, 502, 570, 510, 566, 502, 570,
//  510, 562, 506, 1630, 506, 1630, 510, 562,
//  506, 1630, 510, 1000
//};

uint16_t sourceCDR[RAW_DATA_LEN] = {
  8550, 4306, 530, 1606, 534, 566, 502, 1606,
  534, 566, 502, 570, 510, 1602, 526, 570,
  510, 1602, 534, 566, 502, 1606, 534, 566,
  506, 1602, 530, 1606, 534, 566, 502, 1610,
  530, 570, 498, 574, 506, 566, 502, 570,
  510, 1602, 526, 570, 510, 566, 502, 570,
  510, 1602, 526, 1610, 526, 1606, 534, 1602,
  534, 566, 502, 1610, 530, 1602, 534, 1602,
  526, 574, 506, 1000
};

//Set up array of codes
uint16_t *sources[3] = {powerOnOff, sourceCD, sourceCDR};//, sourceAUX};


//This causes some sort of horrible crash - maybe an overflow of the uint16 data type?
//#define RAW_DATA_LEN 68
//uint16_t sources[4][RAW_DATA_LEN] = {
//  {8550, 4306, 530, 1606, 530, 566, 502, 1610, //POWER ON/OFF
//  530, 566, 502, 574, 506, 1630, 506, 566,
//  506, 1630, 506, 566, 502, 1610, 530, 566,
//  502, 1634, 506, 1606, 530, 570, 498, 1634,
//  506, 570, 510, 562, 506, 566, 502, 1634,
//  506, 1606, 530, 1610, 530, 562, 538, 538,
//  530, 542, 538, 1598, 538, 1570, 558, 542,
//  538, 538, 530, 542, 538, 1598, 530, 1578,
//  558, 1578, 562, 1000},
//
//  {8546, 4310, 558, 1578, 562, 538, 498, 1638, //sourceCD
//  530, 542, 506, 570, 502, 1634, 502, 570,
//  498, 1638, 534, 538, 498, 1638, 530, 542,
//  506, 1606, 554, 1582, 558, 538, 510, 1602,
//  554, 546, 506, 566, 502, 570, 510, 1602,
//  554, 1582, 558, 538, 510, 566, 502, 1606,
//  554, 546, 502, 1610, 558, 1574, 554, 546,
//  502, 570, 510, 1602, 526, 1610, 526, 574,
//  506, 1602, 526, 1000},
//
//
//  {8550, 4306, 530, 1606, 534, 566, 502, 1606,  //sourceCDR
//  534, 566, 502, 570, 510, 1602, 526, 570,
//  510, 1602, 534, 566, 502, 1606, 534, 566,
//  506, 1602, 530, 1606, 534, 566, 502, 1610,
//  530, 570, 498, 574, 506, 566, 502, 570,
//  510, 1602, 526, 570, 510, 566, 502, 570,
//  510, 1602, 526, 1610, 526, 1606, 534, 1602,
//  534, 566, 502, 1610, 530, 1602, 534, 1602,
//  526, 574, 506, 1000},
//
//  {8550, 4310, 526, 1634, 506, 566, 502, 1634,  //sourceAUX
//  506, 566, 502, 574, 506, 1626, 510, 566,
//  506, 1630, 506, 566, 502, 1634, 506, 566,
//  502, 1634, 506, 1606, 530, 566, 506, 1630,
//  506, 566, 502, 1634, 506, 1630, 506, 1630,
//  510, 1602, 526, 570, 510, 566, 502, 1634,
//  502, 570, 502, 570, 510, 566, 502, 570,
//  510, 562, 506, 1630, 506, 1630, 510, 562,
//  506, 1630, 510, 1000}
//} ;


// ====PIN ASSIGNMENTS====
const int audioPin1 = A1;     //Channel 1
const int audioPin2 = A2;
const int debugPin = A3;     //when low, run in debug mode
const int statusLight = 10;   //active channel indicator light


//  ====CHANNEL MEASUREMENTS====
# define CHANNELS 3     //number of channels to sample from
int audioChannels[CHANNELS] = {audioPin1, audioPin1, audioPin2};      //element 0 will be ignored
int channelValues[CHANNELS] = {0, 0, 0};
const int SAMPLES = 300;
movingAvg audioAverages[CHANNELS] = {movingAvg(SAMPLES), movingAvg(SAMPLES), movingAvg(SAMPLES)};

//  ====CHANNEL VARIABLES====
int currentChannel = 0;     //channel that is currently active (0 is off)
int previousChannel = 0;    //channel that was active before change (0 is off)
const int audioThreshold = 15;      //minimum value for an "active" channel

//  ====TIMERS===
int counter = 0;
const int heartBeat = 500;
int channelReleaseTimeOut = 5000;      //time to wait before releasing an inactive timer
int powerTimeOut = 10000;       //time to wait before turnning off
int powerOnDelay = 7000;
elapsedMillis channelReleaseTimer = 0;
elapsedMillis powerTimer = 0;

// ====CONTROL VARIABLES====
bool debugMode = true;

void sendCode(int myChannel=0) {
//  debug("sending code", -1);
  for (int i=0; i < 20; i++) {
//  INCLUDING THE FOLLOWING LINE BREAKS THE DEBUG FUNCTION AND CAUSES OTHER STRANGE BEHAVIOR
//    mySender.send(sources[myChannel], RAW_DATA_LEN, 36);
    Serial.println("send code here");
    delay(2);
  }
} //END sendCode()

int findActiveChannel() {   //returns first active channel in the array or 0 if none are active
  int myChannel = 0;
  for (int i = 0; i < CHANNELS; i++) {
    if (channelValues[i] >= audioThreshold) {
      myChannel = i;
      break;
    }
  }
  return myChannel;
} //END findActiveChannel()

void debug(String message, int val) {

  if (debugMode) {
    Serial.print(message);
    Serial.println(val);
  }
} //END debug()

void setup() {
  delay(1000); //delay in case of runaway loop - allow programmer time to interrupt
  //  ====PIN SETUP====
  pinMode(statusLight, OUTPUT);
  pinMode(debugPin, INPUT);
  pinMode(audioPin1, INPUT);
  pinMode(audioPin2, INPUT);

  if (debugMode) {
    Serial.begin(9600);
    delay(2000);
    debug("starting up ", -1);
  }

  // ====INIT VARIABLES====
  for (int i = 0; i < CHANNELS; i++) { //init and reset the moving averages
    audioAverages[i].begin();
    audioAverages[i].reset();
  }
}

void loop() {
  int activeChannel = 0;
  for (int i=0; i < CHANNELS; i++) {      //sample channels
    int audioValue = 0;
    if (i > 0) {      //only sample channels > 0
      audioValue = analogRead(audioChannels[i]) - 512;      //voltage divider on amp circuit shifts all values +512 (2.5V)
      audioValue = abs(audioValue);     //abs() funciton is actually a macro; needs to be on own line
      channelValues[i] = audioAverages[i].reading(audioValue);    //update and store the moving average for each channel
    }

//    if (counter >= heartBeat) {
//      debug("channel: ", i);
//      debug("    audioValue: ", audioValue);
//      debug("           avg: ", channelValues[i]);
//    } 
  }     //end sample channels

//  if (counter >= heartBeat) {
//    debug("======================", -1);
//  }


  if (channelValues[currentChannel] >= audioThreshold) {      //reset the channel release timer if the current channel is active
    channelReleaseTimer = 0;
    powerTimer = 0; 
  } else {
    activeChannel = findActiveChannel();

    //DEBUGGING - show countdown to channel release
    if(channelReleaseTimer < channelReleaseTimeOut and counter >= 500) {
      debug("Channel became inactive: ", currentChannel);
      debug("     releasing in: ", channelReleaseTimeOut - channelReleaseTimer);
    } 
    //END DEBUGGING

    if (activeChannel != currentChannel and channelReleaseTimer >=channelReleaseTimeOut) {      //change channel 
      debug("Changing channel from: ", currentChannel);
      debug("Changing channel to: ", activeChannel);
      currentChannel = activeChannel;
      powerTimer = 0; 
    }
  }
  //END else find active channel

  if (currentChannel > 0) {     //reset the power timeout if there is an active channel
    powerTimer = 0;
  }

  if (previousChannel != currentChannel) {      //check for a channel change and send appropriate codes
    if (previousChannel == 0) {     //state change from off to on
      digitalWrite(statusLight, true);
      debug("power state change -> ON ", -1);
      sendCode(0);
      debug("     delaying for reciver to power up: ", powerOnDelay);
      delay(powerOnDelay);
      previousChannel = currentChannel;
    }

    if (currentChannel < 1 and powerTimer >= powerTimeOut) {
      digitalWrite(statusLight, false);
      debug("power state change -> OFF ", -1);
      sendCode(0);     
      previousChannel = currentChannel;
    }

    if (currentChannel > 0) {
      sendCode(currentChannel);
      previousChannel = currentChannel;
    }

  }


  if (counter >= heartBeat) {
    debug("\nheartBeat ", -1);
    counter = 0;
  }

  counter++;
  delay(2);

}

Including the line mySender.send(sources[myChannel], RAW_DATA_LEN, 36); in the sendCode() function causes the debug() function to behave erratically and the arduino to get stuck in a variety of states that require a hard reset, sometimes disconnecting the power.

This is the expected output from the serial console:

20:18:20.188 -> Channel became inactive: 2
20:18:20.188 ->      releasing in: 4666
20:18:20.188 -> 
20:18:20.188 -> heartBeat -1
20:18:21.377 -> Channel became inactive: 2
20:18:21.377 ->      releasing in: 3492
20:18:21.377 -> 
20:18:21.377 -> heartBeat -1
20:18:22.528 -> Channel became inactive: 2
20:18:22.528 ->      releasing in: 2317
20:18:22.528 -> 
20:18:22.528 -> heartBeat -1
20:18:23.713 -> Channel became inactive: 2
20:18:23.713 ->      releasing in: 1144
20:18:23.713 -> 
20:18:23.713 -> heartBeat -1
20:18:24.864 -> Changing channel from: 2
20:18:24.864 -> Changing channel to: 1
20:18:24.864 -> send code here

When including the mysender line indicated above, the serial output becomes as follows for a similar situation:

0:21:14.965 -> 1
20:21:14.965 -> 4984
20:21:14.965 -> -1
20:21:16.121 -> 1
20:21:16.121 -> 4986
20:21:16.121 -> -1
20:21:17.299 -> 1
20:21:17.299 -> 3814
20:21:17.299 -> -1
20:21:18.463 -> 1
20:21:18.463 -> 2640
20:21:18.463 -> -1
20:21:19.650 -> 1
20:21:19.650 -> 1468
20:21:19.650 -> -1
20:21:20.804 -> 1
20:21:20.804 -> 294
20:21:20.804 -> -1
20:21:21.116 -> 1
20:21:21.116 -> 0
20:21:21.998 -> -1
  • 1
    In the first example, you probably mean uint16_t sources[4][RAW_DATA_LEN] (4, not 2). In the second one, uint16_t *sources[4] (mind the star). With these fixes, I would expect both to work, But it is impossible to tell what's wrong with your program without seeing it. – Edgar Bonet Mar 27 at 20:48
  • those look like remote control codes ..... what is the device? ..... there may be a way to reduce the amount of data – jsotola Mar 27 at 22:15
  • @EdgarBonet Thanks for pointing out the typo in the snip. It does read uint16_t sources[4][RAW_DATA_LEN] in the actual code. The second example appears to work without the star which I find to be unexpected as I was lead to believe that I needed the star to indicate a pointer reference. Both compile, and run though the first example causes issues when a function uses that variable. – Aaron Ciuffo Mar 28 at 8:09
  • 1
    If you don't have the star, the second version should not work. That is, unless you do something very wacky in your code. Which seems to be the case: your program may be bug-compatible with the buggy version of the array definitions, and incompatible with the correct definitions. But then, let me repeat again: “it is impossible to tell what's wrong with your program without seeing it”. – Edgar Bonet Mar 28 at 9:32
  • @EdgarBonet Thanks for your patience. I've added the entire program. I'm running this against the 1.6.21 board manager due to some incompatibility with the IRLIB2 libraries as suggested here – Aaron Ciuffo Mar 28 at 16:14
1

I have no solution for you, but I would argue that your problem has nothing to do the choice between an array of arrays and an array of pointers. You will have to search somewhere else.

I tried a minimal test by removing everything that is not central to the question. Assuming you are using this library, the prototype of IRsendRaw::send() is

void IRsendRaw::send(uint16_t *buf, uint8_t len, uint8_t khz)

I thus wrote this dummy test class to replace the library:

class IRsendRaw {
public:
    void send(uint16_t *buf, uint8_t len, uint8_t khz) {
        Serial.print(F("Sending at "));
        Serial.print(khz);
        Serial.println(F(" kHz"));
        for (int i = 0; i < len; i++) {
            Serial.print(" ");
            Serial.print(buf[i]);
        }
        Serial.println();
    }
};

and used it in the following test program:

// Comment this line out to use an array of arrays instead:
#define ARRAY_OF_POINTERS

// ====REMOTE CODES====
//define the IR Sender
IRsendRaw mySender;

#define RAW_DATA_LEN 68

#ifdef ARRAY_OF_POINTERS

uint16_t powerOnOff[RAW_DATA_LEN] = {
  // ...
};

// And so on sourceCD[], sourceCDR[]...

//Set up array of codes
uint16_t *sources[3] = {powerOnOff, sourceCD, sourceCDR};//, sourceAUX};

#else

//This causes some sort of horrible crash
//- maybe an overflow of the uint16 data type?
uint16_t sources[4][RAW_DATA_LEN] = {
  {8550, 4306, 530, 1606, 530, 566, 502, 1610, //POWER ON/OFF
  // ...
  506, 1630, 510, 1000}
};

#endif

void setup()
{
    Serial.begin(9600);
#ifdef ARRAY_OF_POINTERS
    Serial.println(F("== Using an array of pointers =="));
#else
    Serial.println(F("== Using an array of arrays =="));
#endif
    for (int myChannel = 0; myChannel < 3; myChannel++) {
        Serial.print(F("\nChannel "));
        Serial.println(myChannel);
        mySender.send(sources[myChannel], RAW_DATA_LEN, 36);
    }
}

void loop(){}

The parts with ellipses (“...”) are just copied from your question. Now, this test program works as expected, both in “array of pointers” and in “array of arrays” mode.

It would then seem your problem comes from somewhere else. How full is the memory of your Arduino? Quite often memory problems lead to strange behaviors, where a crash can be triggered by a piece of code that is perfectly legitimate and has nothing to do with the root cause of the crash.

  • I think you're right. I'm starting to think this is a memory issue as well. The compiler reports that the sketch is 8408 bytes (29%) of program space. Global vars use 727 bytes (28%) of dynamic memory. I've narrowed down the problem to the function that calls the ir sender. Moving that fragment into another sketch works fine. Commenting out chunks of the logic in this sketch and getting the the dynamic memory down around 23% also works. Perhaps this micro has some bad memory? Is there a way to test for that? – Aaron Ciuffo Mar 28 at 20:54
  • @AaronCiuffo: You can find Arduino memory test sketches designed to test old RAM ICs. I am not aware of any published sketch designed to test the Arduino's own memory. The chances of the internal SRAM going bad are almost zero. What you can find is code for measuring the memory usage at run time, which accounts for heap and stack allocation. That could be a good thing to check. – Edgar Bonet Mar 29 at 8:38
  • 1
    I found a great article on this over at Adafruiit Memories of an Arduino. It covers some steps to take to decrease memory usage such as using the F() macro and moving large constants into PROGMEM. I'll give that a shot. Thanks again for your help. – Aaron Ciuffo Mar 29 at 9:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.