Author Topic: memory limitations or other problem?  (Read 6201 times)

gogol

  • Sr. Member
  • ****
  • Posts: 398
memory limitations or other problem?
« on: March 07, 2013, 02:21:05 am »
Hello digispark users,

finally my digisparks arrived here in germany, and I started to remember my C knowledge from the past.  My only experience up to now with embedded systems is the Lego NXT with NXC.  Given that and that my practical C experience is older than 10 years, that might be likely the reason for my problems.

To get used to the IDE and the work flow, my initial idea was to write a small blink program, which blinks a given text in morsecode, just with the on board LED.
As I was not able to get that working, I tried,  to debug the program.  That seems to be an very important point, as I was till today not able, to get some serial or USB communication ready, which would allow me reading printed debug statements.  If a user has a simple how-to, how to debug sketches under windows, it would be more than welcome as well!

So I decided to use the digispark-LCD display, which I ordered as well, for debugging.  The following program is now stripped down to demonstrate my problem. it can be used together with the LCD shield.

The program looks up each character of a given string (here char greeting[]) in the string const char morse_index[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ". The position of the character is equal to the position of the final morsecode in the array const char * const morse_codes[].
Here the problem is starting! When I define morse_codes with real morsecodes the application just stucks. When I define shorter strings -like in the example- the application just works as it should do.
With the longer strings compiling and downloading works just perfect, but at that point, the application should start, my PC tells me, that an USB device was not recognized.  Sometimes the application is starting, but only showing garbage in the display.

Whats going wrong? What have I missed?  Thanks for any hint!

Quote
#include <TinyWireM.h>                  // I2C Master lib for ATTinys which use USI - comment this out to use with standard arduinos
#include <LiquidCrystal_I2C.h>          // for LCD w/ GPIO MODIFIED for the ATtiny85
#define GPIO_ADDR     0x27             // (PCA8574A A0-A2 @5V) typ. A0-A3 Gnd 0x20 / 0x38 for A - 0x27 is the address of the Digispark LCD modules.
LiquidCrystal_I2C lcd(GPIO_ADDR,16,2);  // set address & 16 chars / 2 lines


const char morse_index[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
/*
  //does not work with this data :-(
const char * const morse_codes[] = { ". -",       "- . . .",   "- . - .",   "- . .",     ".",         ". . - .",     "- - .",     ". . . .",
                                     ". .",       ". - - -",   "- . -",     ". - . .",   "- -",       "- .",         "- - -",     ". - - .",
                                     "- - . -",   ". - .",     ". . .",     "-",         ". . -",     ". . . -",     ". - -",     "- . . -",
                                     "- . - -",   "- - . .",   "- - - - -", ". - - - -", ". . - - -", ". . . - -",   ". . . . -", ". . . . .",
                                     "- . . . .", "- - . . .", "- - - . .", "- - - - .", "    "};

*/
//however works with this definition

const char * const morse_codes[]=    {"AAA", "BB", "CCC", "DD", "EEE", "FF", "GGG", "HH", "III", "JJ", "KKK", "LL", //12
                                      "MM", "NN", "OO", "PP", "QQ", "RR", "SS", "TT", "UU", "VV", "WW", "XX", //12
                                      "YY", "ZZ", "00", "11", "22", "33", "44", "55", "66", "77", "88", "99999999", "__" }; //13

//but not with that definition :-(
/*const char * const morse_codes[]=    {"AAAAA", "BBBBB", "CCCCC", "DDDDD", "EEEEE", "FFFFF", "GGGGG", "HHHHH", "IIIII", "JJJJJ", "KKKKK", "LLLLL", //12
                                      "MMMMM", "NNNNN", "OOOOO", "PPPPP", "QQQQQ", "RRRRR", "SSSSS", "TTTTT", "UUUUU", "VVVVV", "WWWWW", "XXXXX", //12
                                      "YYYYY", "ZZZZZ", "00000", "11111", "22222", "33333", "44444", "55555", "66666", "77777", "88888", "99999", "_____" }; //13
  */                     
int firstPos( char index, const char *searchIn) {
  int retval = -1;
  for ( int len = 0; len < strlen(searchIn); len++) {
    if (index == searchIn[len]) {
       retval = len;
       break;
    } 
  } 
  return retval; 



void send_char( char snd_char) {
  int posi=firstPos(snd_char, morse_index);
  lcd.setCursor(0, 0);
  char message[17];
  sprintf(message, "Char: %c : %3d", snd_char, posi);
  lcd.print(message);
  lcd.setCursor(0, 1);
  if ( posi >= 0 ) {
    sprintf(message, "Code: %-9s", morse_codes[posi]);
    lcd.print(message); 
  }  else {
    lcd.print("Code: n/a    ");
  } 
  delay(850);
 


void send_string( char *text_to_send) {
  for ( byte len = 0; len < strlen(text_to_send); len++) {
    send_char(text_to_send[len]);
  } 




char greeting[] = "abcdAaBCcDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";


void setup() {               
 
  TinyWireM.begin();                    // initialize I2C lib - comment this out to use with standard arduinos
  lcd.init();                           // initialize the lcd
  lcd.backlight();                      // Print a message to the LCD.
  pinMode(1, OUTPUT); //LED on Model A   
}


void loop() {
  send_string(greeting);
}





regards

   Gogol


Follow Up:

Reading this Thread: http://digistump.com/board/index.php/topic,686.0.html it looks like, that I have the same problem. I was not aware, that I have only 512 bytes for heap and stack (what this thread is telling).
I have now rewritten the example, following the advice from the other thread, and separated the single codes into individual functions.
Doing this, I should have only one code at a given time on the stack, however i still run into problems.

When I compile the whole sketch without comments/remarks it compiles to 4566 bytes, which should be far from the 6010 my Spark has available.  When I comment out the last two lines, like in the following example, the sketch compiles to 4490 bytes.  With the two last lines commented out, the program works, with those two lines in, the program stops at random points, the Spark reboots and continues. Sometimes it does a full loop, before rebooting, sometimes it does that every 3 seconds.

Quote
#include <TinyWireM.h>                  // I2C Master lib for ATTinys which use USI - comment this out to use with standard arduinos
#include <LiquidCrystal_I2C.h>          // for LCD w/ GPIO MODIFIED for the ATtiny85
#define GPIO_ADDR     0x27             // (PCA8574A A0-A2 @5V) typ. A0-A3 Gnd 0x20 / 0x38 for A - 0x27 is the address of the Digispark LCD modules.
LiquidCrystal_I2C lcd(GPIO_ADDR,16,2);  // set address & 16 chars / 2 lines

void morse_A(char *morse){strcpy(morse, ". -");}
void morse_B(char *morse){strcpy(morse, "- . . .");}
void morse_C(char *morse){strcpy(morse, "- . - .");}
void morse_D(char *morse){strcpy(morse, "- . .");}
void morse_E(char *morse){strcpy(morse, ".");}
void morse_F(char *morse){strcpy(morse, ". . - .");}
void morse_G(char *morse){strcpy(morse, "- - .");}
void morse_H(char *morse){strcpy(morse, ". . . .");}
void morse_I(char *morse){strcpy(morse, ". .");}
void morse_J(char *morse){strcpy(morse, ". - - -");}
void morse_K(char *morse){strcpy(morse, "- . -");}
void morse_L(char *morse){strcpy(morse, ". - . .");}
void morse_M(char *morse){strcpy(morse, "- -");}
void morse_N(char *morse){strcpy(morse, "- .");}
void morse_O(char *morse){strcpy(morse, "- - -");}
void morse_P(char *morse){strcpy(morse, ". - - .");}
void morse_Q(char *morse){strcpy(morse, "- - . -");}
void morse_R(char *morse){strcpy(morse, ". - .");}
void morse_S(char *morse){strcpy(morse, ". . .");}
void morse_T(char *morse){strcpy(morse, "-");}
void morse_U(char *morse){strcpy(morse, ". . -");}
void morse_V(char *morse){strcpy(morse, ". . . -");}
void morse_W(char *morse){strcpy(morse, ". - -");}
void morse_X(char *morse){strcpy(morse, "- . . -");}
void morse_Y(char *morse){strcpy(morse, "- . - -");}
void morse_Z(char *morse){strcpy(morse, "- - . .");}
void morse_0(char *morse){strcpy(morse, "- - - - -");}
void morse_1(char *morse){strcpy(morse, ". - - - -");}
void morse_2(char *morse){strcpy(morse, ". . - - -");}
void morse_3(char *morse){strcpy(morse, ". . . - -");}
void morse_4(char *morse){strcpy(morse, ". . . . -");}
void morse_5(char *morse){strcpy(morse, ". . . . .");}
void morse_6(char *morse){strcpy(morse, "- . . . .");}
void morse_7(char *morse){strcpy(morse, "- - . . .");}
void morse_8(char *morse){strcpy(morse, "- - - . .");}
/* the two lines are commented out, to make the sketch working 
void morse_9(char *morse){strcpy(morse, "- - - - .");}
void morse_blank (char *morse){strcpy(morse, "    ");}
*/   


void char2morse( char chr, char *morse) {
 
  strcpy(morse, "");
  switch (chr) {
    case 'A' : morse_A( morse); break;
    case 'B' : morse_B( morse); break;
    case 'C' : morse_C( morse); break;
    case 'D' : morse_D( morse); break;
    case 'E' : morse_E( morse); break;
    case 'F' : morse_F( morse); break;
    case 'G' : morse_G( morse); break;
    case 'H' : morse_H( morse); break;
    case 'I' : morse_I( morse); break;
    case 'J' : morse_J( morse); break;
    case 'K' : morse_K( morse); break;
    case 'L' : morse_L( morse); break;
    case 'M' : morse_M( morse); break;
    case 'N' : morse_N( morse); break;
    case 'O' : morse_O( morse); break;
    case 'P' : morse_P( morse); break;
    case 'Q' : morse_Q( morse); break;
    case 'R' : morse_R( morse); break;
    case 'S' : morse_S( morse); break;
    case 'T' : morse_T( morse); break;
    case 'U' : morse_U( morse); break;
    case 'V' : morse_V( morse); break;
    case 'W' : morse_W( morse); break;
    case 'X' : morse_X( morse); break;
    case 'Y' : morse_Y( morse); break;
    case 'Z' : morse_Z( morse); break;
    case '0' : morse_0( morse); break;
    case '1' : morse_1( morse); break;
    case '2' : morse_2( morse); break;
    case '3' : morse_3( morse); break;
    case '4' : morse_4( morse); break;
    case '5' : morse_5( morse); break;
    case '6' : morse_6( morse); break;
    case '7' : morse_7( morse); break;
    case '8' : morse_8( morse); break;
  /* the two lines are commented out, to make the sketch working 
    case '9' : morse_9( morse); break;
    case ' ' : morse_blank ( morse); break;
    */
  } 
}


void send_char( char snd_char) {
  lcd.setCursor(0, 0);
  char message[17];
  sprintf(message, "Char: %c ", snd_char);
  lcd.print(message);
  lcd.setCursor(0, 1);
  char code[12];
  char2morse(snd_char, code);
  if ( strlen(code) > 0 ) {
    sprintf(message, "Code: %-9s", code);
    lcd.print(message); 
  }  else {
    lcd.print("Code: n/a    ");
  } 
  delay(850);
 


void send_string( char *text_to_send) {
  for ( byte len = 0; len < strlen(text_to_send); len++) {
    send_char(text_to_send[len]);
  } 




char greeting[] = "AbCDeFGHIJkLMNOPQRSTUVWXYZ0123456789 ";


void setup() {               
 
  TinyWireM.begin();   
  lcd.init();           
  lcd.backlight();     
}


void loop() {
  send_string(greeting);
}



Any additional hints ??

regards

  Gogol




« Last Edit: March 07, 2013, 05:37:55 am by gogol »

semicolo

  • Full Member
  • ***
  • Posts: 137
Re: memory limitations or other problem?
« Reply #1 on: March 07, 2013, 07:21:27 am »
How about storing your strings in flash with the PROGMEM keyword and copy to ram when you need one?
http://www.arduino.cc/en/Reference/PROGMEM

If you want to save some space, don't use sprintf, it takes a lot of space

digistump

  • Administrator
  • Hero Member
  • *****
  • Posts: 1465
Re: memory limitations or other problem?
« Reply #2 on: March 07, 2013, 09:23:43 am »
gogol - what OS? I can confirm that sketches nearly to the 6010 max work for me on Mac and Windows, though I haven't confirmed that on Linux, usually OS shouldn't matter but there was a compiler bug we had to fix on OSX and Win for sketches near the max.

gogol

  • Sr. Member
  • ****
  • Posts: 398
Re: memory limitations or other problem?
« Reply #3 on: March 07, 2013, 09:39:04 am »
Hello Semicolo and all other readers,

understanding the architecture of the ATtiny right, the method Bluebie mentioned in the other thread, should work from memory usage like PROGMEM, but without the PROGMEM quirks. In addition the code should be more compatible.
When I have multiple procedures, each with an certain amount of stack-variables, the procedure is located in the flash memory, and the stack is only populated, when the procedure is called.

I know, that sprintf and other methods are memory consuming.  My problem is, that I wish to know, when and which part of the memory is overloaded. I am sure, that I can reduce the footprint of my program. But I see the current state, as an problem, because I can't monitor, whats going wrong.

How can I monitor heap and stack usage?
How can I send debug print statements to a kind of console, readable from the host-PC? Good, if that is not to resource-consuming like the LCD-Display is!

With more complex programs I will be sooner or later exactly on the same point, where I realize a weird program behavior, but I have no clue, how to trace the culprit down.

I got my first program running, which will now blink "HELLO DIGISPARK" in morse-code.

Here the code:
Quote
#define DIT 100     // base time for morse code. short 1 dit, long 3 dit, space 7 dit, chracter separation 3 dit
#define M_LEN 12    // length for morse-string
// index of single morsestrings. the order of characters in that string needs to be the same, as the order in the byte-array morse_codes
const char morse_index[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
// coded morse codes. each byte is one morse-sign.  bit-value of 0 means short, bit-value of 1 means long
// the code starts with the first bit, which is different, than the predecessor. that allows to store codes with different length
// in one Byte.  an morse-code starting with long (1) is padded with starting 0s, an morse-code starting with 0 is padded with 1s
byte morse_codes[] = {0B11111101, 0B00001000, 0B00001010, 0B00000100, 0B11111110, 0B11110010, 0B00000110, 0B11110000, 0B11111100,
                      0B11110111, 0B00000101, 0B11110100, 0B00000011, 0B00000010, 0B00000111, 0B11110110, 0B00001101, 0B11111010,
                      0B11111000, 0B00000001, 0B11111001, 0B11110001, 0B11111011, 0B00001001, 0B00001011, 0B00001100, 0B00011111,
                      0B11101111, 0B11100111, 0B11100011, 0B11100001, 0B11100000, 0B00010000, 0B00011000, 0B00011100, 0B00011110 };

int firstPos( char index, const char *searchIn) {
  // searches the first position of an character in a given string
  int retval = -1;
  for ( int len = 0; len < strlen(searchIn); len++) {
    if (index == searchIn[len]) {
       retval = len;
       break;
    }
  }
  return retval;
}

char bit2morse( char bitmorse, char *retval) {
  strcpy( retval, "");
  byte valid = 0;
  byte start=(bitRead(bitmorse, 7))?1:0;
  for  (int i=6 ; i>=0; i--) {
    byte curr=(bitRead(bitmorse, i))?1:0;
    if (( valid) || (curr != start)) {
      if (curr) {
         strcat(retval, "-");
      } else {
         strcat(retval, ".");
      }   
      if ( i ) {strcat(retval, " ");}
      valid = 1;
    } 
  }


void char2morse( char chr, char *morse) {
  if (' ' == chr) { //blank
    strcpy(morse, "    ");
    return ;
  } else {
    strcpy(morse, ""); 
  } 
  int bitPos=firstPos(chr, morse_index);
  bit2morse(morse_codes[bitPos], morse);
}


void send_char( char snd_char) {
  char code[M_LEN];
  memset( code, 0x0, M_LEN);
  char2morse(snd_char, code);
  for ( byte len = 0; len < strlen(code); len++) {
    switch (code[len]) {
      case '.':
        digitalWrite(1, HIGH);
        delay(DIT);
        digitalWrite(1, LOW);
        break;
      case ' ':
        delay(DIT);
        break;
      case '-':   
        digitalWrite(1, HIGH);
        delay(3 * DIT);
        digitalWrite(1, LOW);
        break;
    } 
  }
  delay ( 3 * DIT);
 


void send_string( char *text_to_send) {
  for ( byte len = 0; len < strlen(text_to_send); len++) {
    send_char(text_to_send[len]);
  } 



char greeting[] = "HELLO DIGISPARK  ";

void setup() {               
  pinMode(1, OUTPUT); //LED on Model A 
}

void loop() {
  send_string(greeting);
}



Regards

  Gogol
« Last Edit: March 07, 2013, 09:43:29 am by gogol »

gogol

  • Sr. Member
  • ****
  • Posts: 398
Re: memory limitations or other problem?
« Reply #4 on: March 07, 2013, 09:41:02 am »
Hello digistump,

I am working with Win7, x64. Tried it as well with Vista x64 .

Regards

  Gogol
« Last Edit: March 07, 2013, 09:48:42 am by gogol »

grimbo

  • Newbie
  • *
  • Posts: 1
Re: memory limitations or other problem?
« Reply #5 on: June 16, 2013, 04:50:19 pm »
Any solution to this problem? I am also getting memory corruption.

If I change:

Code: [Select]
  outputMessage("0123456789 0123456789 0123456789");
to

Code: [Select]
  outputMessage("0123456789 0123456789 0123456789 0123456789");
I get random errors.
« Last Edit: June 17, 2013, 01:15:09 am by grimbo »