too much GLOBAL VARIABLES ?

Status
Not open for further replies.

AdmiralCrunch

Well-known member
Hey :)

i have a ciruit with some multiplexers and LEDs and one switch connected directly in pin 13
everything works fine, so i worked on my code and set a class-variable

Code:
String ctrlNote_values[127] = {"C0", "C0#", "D0", "D0#", "E0", "F0", "F0#", "G0", "G0#", "A0", "A0#", "B0", "C1", "C1#", "D1", "D1#", "E1", "F1", "F1#", "G1", "G1#", "A1", "A1#", "B1","C2", "C2#", "D2", "D2#", "E2", "F2", "F2#", "G2", "G2#", "A2", "A2#", "B2", "C3", "C3#", "D3", "D3#", "E3", "F3", "F3#", "G3", "G3#", "A3", "A3#", "B3", "C4", "C4#", "D4", "D4#", "E4", "F4", "F4#", "G4", "G4#", "A4", "A4#", "B4", "C5", "C5#", "D5", "D5#", "E5", "F5", "F5#", "G5", "G5#", "A5", "A5#", "B5", "C6", "C6#", "D6", "D6#", "E6", "F6", "F6#", "G6", "G6#", "A6", "A6#", "B6", "C7", "C7#", "D7", "D7#", "E7", "F7", "F7#", "G7", "G7#", "A7", "A7#", "B7", "C8", "C8#", "D8", "D8#", "E8", "F8", "F8#", "G8", "G8#", "A8", "A8#", "B8", "C9", "C9#", "D9", "D9#", "E9", "F9", "F9#", "G9", "G9#", "A9", "A9#", "B9", "C10", "C10#", "D10", "D10#", "E10", "F10", "F10#"};

Der Sketch verwendet 32.356 Bytes (12%) des Programmspeicherplatzes. Das Maximum sind 262.144 Bytes.
Globale Variablen verwenden 40.452 Bytes (61%) des dynamischen Speichers, 25.084 Bytes für lokale Variablen

after uploading to teensy,the whole circuit doesn't work.. only the intern LED lights up when the switch attached to pin13 is pressed... I found out, that it has something to do with the size this variable needs..

when you make it smaller
Code:
String ctrlNote_values[127] = {"C0", "C0#", "D0", "D0#", "E0", "F0", "F0#", "G0", "G0#", "A0", "A0#", "B0", "C1", "C1#", "D1", "D1#", "E1", "F1", "F1#", "G1", "G1#", "A1", "A1#", "B1","C2", "C2#", "D2", "D2#", "E2", "F2", "F2#", "G2", "G2#", "A2", "A2#", "B2", "C3", "C3#", "D3", "D3#", "E3", "F3", "F3#", "G3", "G3#", "A3", "A3#", "B3", "C4", "C4#", "D4", "D4#", "E4", "F4", "F4#", "G4", "G4#", "A4", "A4#", "B4", "C5", "C5#", "D5", "D5#", "E5", "F5", "F5#", "G5", "G5#", "A5", "A5#", "B5", "C6", "C6#", "D6", "D6#", "E6", "F6", "F6#", "G6", "G6#", "A6"};

Der Sketch verwendet 31.988 Bytes (12%) des Programmspeicherplatzes. Das Maximum sind 262.144 Bytes.
Globale Variablen verwenden 40.452 Bytes (61%) des dynamischen Speichers, 25.084 Bytes für lokale Variablen

everything works again .. oO

I'm completely confused .. can someone explain that behaviour?
 
Hard to know without seeing more...

Things like how many strings is it actually? In both cases your array is defined to 127 elements.

Are these strings changeable? If not does it help to define them as const? again hard to know without seeing...
 
Hard to know without seeing more...
Yes I know, sorry.. I didn't want to blow things up.. so I posted only the part I investigated on..

Things like how many strings is it actually? In both cases your array is defined to 127 elements.

Are these strings changeable? If not does it help to define them as const? again hard to know without seeing...
Those could be const, indeed .. (as I am a c++ noob, is there something like a const array?) ..

there are a few variables in my class.. but specially, when I make the one above with all 127 indexes set, this behaviour that the circuit doesn't work.. occurs
strange thing is, that I only need to delete the values of the indexes.. the array has still 127 of them.. and everything works again
 
Those could be const, indeed .. (as I am a c++ noob, is there something like a const array?) ..

there are a few variables in my class.. but specially, when I make the one above with all 127 indexes set, this behaviour that the circuit doesn't work.. occurs
strange thing is, that I only need to delete the values of the indexes.. the array has still 127 of them.. and everything works again

This sounds like memory access error in an other part of your program, something like read pointer accidentally accesses the top end of your 127 string element array and depending what is written there (zeros, or note string) crashes or not.
You would need the Map file where to see which arrays are allocated in vicinity and check code accessing these data.
(Don't know how to create Map file with Arduino IDE, maybe someone else can help)
 
How are you accessing the elements? Arrays in Arduino are zero indexed, so if you use some thing like
Code:
string myStringArray[127] =  { /*a great many string elements here*/ };
string foo = myStringArray[127]; // attempt to read the 127th element actually reads beyond end of array

in a for loop or something you'll get something strange because you're reading beyond the end of the array. Assuming you're using the Arduino IDE there's a number of ways you can declare/define an array but in this case when you're defining all the elements straight away ( and a great number of them ) i'd go with

Code:
string myStringArray[] = { /*a great many string elements here*/ };

because the compiler will count the number of elements and assign the memory accordingly. This will save you headaches if you accidently assign too many elements, and stop you wasting memory if you assign less. The reference page is

https://www.arduino.cc/en/Reference/Array

Yes you can declare the array as const if you're only ever reading from it, though I don't know if that would make any difference in this case. I'm no guru but I would think that the compiler would still create a global varible in this case becuase I can't think of any other way to handle it.
Please post more code, everyone wants to know how you read from your array!
 
hey guys..

you are both right, but how can that be, when i delete some of the values (i mean, the indexes are still there --> "String ctrlNote_values[127]") .. then it works.. correct me, if i am wrong, but doesn't that imply that my array-accessing-loop is ok?


anyway.. I have this class
Code:
class btnTrackSelect {
    
  public: 
    btnTrackSelect(); 
    
    int btnStatus;
    int btnStatusOld;
    
    // MIDI-Channel
    int ctrlChannel = 1;  
    int ctrlChannel_valRange[2] = {1,64};    
    
    // TYPE - note/ctrl
    int ctrlType = 1;
    String ctrlType_values[2] = {"ctrl", "note"};    
    int ctrlType_valRange[2] = {1,4};
    
    // CC CONTROLNUBER 1-127
    int ctrlNumber = 20;    
    int ctrlNumber_valRange[2] = {1,508};
    
    // NOTE 
    int ctrlNote = 60;      
   
     String ctrlNote_values[127] = {"C0", "C0#", "D0", "D0#", "E0", "F0", "F0#", "G0", "G0#", "A0", "A0#", "B0", "C1", "C1#", "D1", "D1#", "E1", "F1", "F1#", "G1", "G1#", "A1", "A1#", "B1","C2", "C2#", "D2", "D2#", "E2", "F2", "F2#", "G2", "G2#", "A2", "A2#", "B2", "C3", "C3#", "D3", "D3#", "E3", "F3", "F3#", "G3", "G3#", "A3", "A3#", "B3", "C4", "C4#", "D4", "D4#", "E4", "F4", "F4#", "G4", "G4#", "A4", "A4#", "B4", "C5", "C5#", "D5", "D5#", "E5", "F5", "F5#", "G5", "G5#", "A5", "A5#", "B5", "C6", "C6#", "D6", "D6#", "E6", "F6", "F6#", "G6", "G6#", "A6", "A6#", "B6", "C7", "C7#", "D7", "D7#", "E7", "F7", "F7#", "G7", "G7#", "A7", "A7#", "B7", "C8", "C8#", "D8", "D8#", "E8", "F8", "F8#", "G8", "G8#", "A8", "A8#", "B8", "C9", "C9#", "D9", "D9#", "E9", "F9", "F9#", "G9", "G9#", "A9", "A9#", "B9", "C10", "C10#", "D10", "D10#", "E10", "F10", "F10#"}; 
     
    int ctrlNote_valRange[2] = {1,508};
    
    
    // CC VALUE
    int value = 127;  
    // VELOCITY
    int velocity = 127;  
    
       
  private:
    
    
};

and in my loop i do
Code:
// READ ENCODER 3
    int encoder3_newPosition = encoder3.read();
    if(encoder3_newPosition != cfg.encoder3_oldPosition) {      
      if(cfg.encoder3_valRange[0] <= encoder3_newPosition && cfg.encoder3_valRange[1] >= encoder3_newPosition) {
        cfg.encoder3_oldPosition = encoder3_newPosition;  
        // WRITE TO OBJECT
        if(cfg.editCtrl == "btnTrackSelect") {      
          btnTrackSelect[cfg.editCtrlNr].ctrlType = encoder3_newPosition >> 2;   
          Serial.print("3) von "); Serial.print(cfg.encoder3_valRange[0]);
          Serial.print(" bis "); Serial.print(cfg.encoder3_valRange[1]); 
          Serial.print(" - VALUE: "); Serial.print(encoder3_newPosition >> 2);
          Serial.print("("); Serial.print(encoder3_newPosition); Serial.print(")"); 
          Serial.print(" --- "); [B][U][COLOR="#B22222"]Serial.print(btnTrackSelect[cfg.editCtrlNr].ctrlNote_values[(btnTrackSelect[cfg.editCtrlNr].ctrlNote)]);[/COLOR][/U][/B]
          Serial.println();           
        } 
        encoder3.write(cfg.encoder3_oldPosition);
      } else {
        encoder3.write(cfg.encoder3_oldPosition);
      }   
    }
 
It could be that the constructors for all these 127 String variables in the array uses up to much heap space, these constructors are automatically called when string array is created with initializers. And using to much heap cannot be seen from static memory usage, but the crash is almost inevitable.

So try not to use string objects for constant char arrays.
 
1.) It has perhaps not been made clear to AdmiralCrunch that a String in C++ is a complex object with variables and methods and eats much more space than just the few chars. String objects are convenient to use but they eat lots of runtime memory, once initialized, almost independent of the string length. For these simple tasks as yours in special and on embedded systems in general, it is more economic to work with arrays of char.
2.) When it comes to midi notes, there are 128, not 127 of these (0 to 127 both including). But String- or char-wise, only 12 are needed: "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", and "B" since these repeat over the whole range. When it comes now to show the note name for a given midi note, a little calculus can easily be done with one integer division, one multiplication and one subtraction. Lets take the note = 81 ==> octave = note / 12 = 6 ==> name = note - (octave * 12) = 9. Our ninth String or char array is "A", combined with the octave gives "A6". That is much more efficient in terms of memory consumption.
Code:
    void dispNote(uint8_t midi) {
        const char noteStr[12][3] {"C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "};
        char disp_buf[6];
    // separate note and octave:
        uint8_t octave = midi / 12;
        uint8_t note = midi - (12 * octave);
    // assemble everything in the display buffer:
        strcpy(disp_buf, noteStr[note]);
        snprintf(disp_buf+2, 6, "%d", octave);
    // show the result:
        Serial.println(disp_buf);
    }
 
Last edited:
That code is still not enough (for me) to figure out what is wrong, or exactly how you're accessing the array. The line
Code:
Serial.print(btnTrackSelect[cfg.editCtrlNr].ctrlNote_values[(btnTrackSelect[cfg.editCtrlNr].ctrlNote)]);

still seems beyond my ability to grasp!

That being said both mlu and theremingenieur have made good points! Your global varible % is quite high and if you're making heavy usage of string you could well run out of memory causing problems. If it is a memory problem like this then it can be non-trivial to find exactly where its occurring. I'd swap in theremigenieur's code - if your global varible % drops like a brick and it stops crashing that'll be the problem!
If it still gives you trouble we'll have another look at it, though I imagine swapping to char arrays will mean a lot of code change making exactly the same error unlikely! :)
 
Maybe some additional notes; solutions have been presented already.

The actual problem is a mix of memory usage, memory layout and the fact there is no MMU which could catch such faults in a somewhat more visible manner.

As outlined, the heap is used heavily. The heap grows from the bottom (lower addresses to higher ones), while the program stack goes the other way around: It starts at the "end" of the memory and goes down to the beginning. It is assumed the stack has a maximum size of like 2k or so (iirc), but there is nothing preventing it from growing larger. The heap has no such protections, either (which is, I think, a code configuration fault). As a result, it is possible to have these two things grow into each other, which means at some point there will be some kind of stack corruption. That usually leads to the Teensy "not working"; chances are high there was a core exception being thrown which ends up in an exception handler doing the infinite loop thing.
 
Sorry, but I will repeat again what others have said.

First as every forum window shows: Always post complete source code & details to reproduce any issue!

As Edward mentioned, the line scares me:
Code:
Serial.print(btnTrackSelect[cfg.editCtrlNr].ctrlNote_values[(btnTrackSelect[cfg.editCtrlNr].ctrlNote)]);
As it appears like you are using an array of these class objects? Is it global or defined in a function? How big?

Already mentioned but String objects do lots of heap stuff, which I usually try to avoid. If you really think you need to use strings and for example you create more than one of these objects, you should probably make the ctrlNote_values be a static member of your class, so only one instance of it is created.

And I too would probably do something similar to what theremingenieur mentioned above.
 
Status
Not open for further replies.
Back
Top