ATTiny13 reduced variables

I'm trying to program an ATTiny13 with a Morse-code transmission program. The problem is I only have 64 bytes available for variables. I have 1K available for the program.

My current version of the program uses 512 bytes of storage and 212 bytes for variables. I wish I could remap the storage on the ATTiny13.

I'm trying to work out a way of doing the code so that I can edit the message easily but it's looking like that won't be possible.

For suggestions, post the "current version" code, using code tags.

#define DOT_DURATION 50   // Duration of a dot in milliseconds
#define DASH_DURATION (3 * DOT_DURATION)   // Duration of a dash
#define SYMBOL_SPACE DOT_DURATION   // Space between symbols in the same letter
#define LETTER_SPACE (3 * DOT_DURATION)   // Space between letters
#define WORD_SPACE (7 * DOT_DURATION)   // Space between words
#define MESSAGE_DELAY 30000   // 30-second delay between messages

#define MORSE_PIN 4   // Define the pin used for Morse code output

class MorseCodeSender {
private:
    const char* morse_code[26] = {
        ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---",   // A-J
        "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-",     // K-T
        "..-", "...-", ".--", "-..-", "-.--", "--.."                           // U-Z
    };

    void sendDot() {
        digitalWrite(MORSE_PIN, HIGH);   // Set the pin high
        delay(DOT_DURATION);
        digitalWrite(MORSE_PIN, LOW);   // Set the pin low
        delay(SYMBOL_SPACE);
    }

    void sendDash() {
        digitalWrite(MORSE_PIN, HIGH);   // Set the pin high
        delay(DASH_DURATION);
        digitalWrite(MORSE_PIN, LOW);   // Set the pin low
        delay(SYMBOL_SPACE);
    }

    void sendChar(char c) {
        if (c >= 'A' && c <= 'Z') {
            const char* morse = morse_code[c - 'A'];
            while (*morse) {
                if (*morse == '.') {
                    sendDot();
                } else if (*morse == '-') {
                    sendDash();
                }
                morse++;
            }
            delay(LETTER_SPACE - SYMBOL_SPACE);   // Space between letters
        } else if (c == ' ') {
            delay(WORD_SPACE - LETTER_SPACE);   // Space between words
        }
    }

public:
    void sendMessage(const char* message) {
        while (*message) {
            sendChar(toupper(*message));
            message++;
        }
    }
};

MorseCodeSender sender;

void setup() {
    pinMode(MORSE_PIN, OUTPUT);

    // Message to send
    //const char* message = "test test test BTG emergency morse transmitter test test test";
    const char* message = "btoqp fgmir zdfsb ydvnf rqrtb ndmki ymzvh aylcz oifbq fbsth muucc kkwmv vxhbd urmab ucdjk oarql qoadl tgmi";
    //text is enigma coded and reads "Wir verlassen unsere Basis. Wir wurden von Marsianern überrannt. Sie haben unseren Kommandanten gefressen."
    //English is "We are leaving our base. We have been overrun by Martians. They have eaten our commander."
    while (1) {
        sender.sendMessage(message);
        delay(MESSAGE_DELAY);   // 30-second delay between repeated messages
    }
}

void loop() {
    // Nothing to do in the loop function since everything is handled in setup
}

The character data is taking up the dynamic memory. Store it in program memory, using PROGMEM constructs.

That;s interesting. I did not know of this way of doing things. Would it work for both the morse conversions and the message? That's a really neat way of remapping memory.

I've never tried this before.

It is a popular approach for saving RAM memory on AVR processors. Not supported on many of the others.

Would it work for both the morse conversions and the message?

You can put any constant data you want into program memory. You must use the special functions mentioned on the reference page to retrieve the data.

I've had a go but I'm getting some errors. I think I've got it set correctly but I'm not sure...

errors:

Sketch uses 718 bytes (70%) of program storage space. Maximum is 1024 bytes.
Global variables use 236 bytes (368%) of dynamic memory, leaving -172 bytes for local variables. Maximum is 64 bytes.
Not enough memory; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing your footprint.
data section exceeds available space in board
Error compiling for board ATtiny13.

code:

#define DOT_DURATION 50   // Duration of a dot in milliseconds
#define DASH_DURATION (3 * DOT_DURATION)   // Duration of a dash
#define SYMBOL_SPACE DOT_DURATION   // Space between symbols in the same letter
#define LETTER_SPACE (3 * DOT_DURATION)   // Space between letters
#define WORD_SPACE (7 * DOT_DURATION)   // Space between words
#define MESSAGE_DELAY 30000   // 30-second delay between messages

#define MORSE_PIN 4   // Define the pin used for Morse code output

class MorseCodeSender {
private:
    const PROGMEM char* morse_code[26] = {
        ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---",   // A-J
        "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-",     // K-T
        "..-", "...-", ".--", "-..-", "-.--", "--.."                           // U-Z
    };

    void sendDot() {
        digitalWrite(MORSE_PIN, HIGH);   // Set the pin high
        delay(DOT_DURATION);
        digitalWrite(MORSE_PIN, LOW);   // Set the pin low
        delay(SYMBOL_SPACE);
    }

    void sendDash() {
        digitalWrite(MORSE_PIN, HIGH);   // Set the pin high
        delay(DASH_DURATION);
        digitalWrite(MORSE_PIN, LOW);   // Set the pin low
        delay(SYMBOL_SPACE);
    }

    void sendChar(char c) {
        if (c >= 'A' && c <= 'Z') {
            const char* morse = morse_code[c - 'A'];
            while (*morse) {
                if (*morse == '.') {
                    sendDot();
                } else if (*morse == '-') {
                    sendDash();
                }
                morse++;
            }
            delay(LETTER_SPACE - SYMBOL_SPACE);   // Space between letters
        } else if (c == ' ') {
            delay(WORD_SPACE - LETTER_SPACE);   // Space between words
        }
    }

public:
    void sendMessage(const char* message) {
        while (*message) {
            sendChar(toupper(*message));
            message++;
        }
    }
};

MorseCodeSender sender;

void setup() {
    pinMode(MORSE_PIN, OUTPUT);
    Serial.begin(9600);
    while (!Serial);  // wait for serial port to connect. Needed for native USB

    // Message to send
    //const char* message = "test test test BTG emergency morse transmitter test test test";
    const PROGMEM char* message = "btoqp fgmir zdfsb ydvnf rqrtb ndmki ymzvh aylcz oifbq fbsth muucc kkwmv vxhbd urmab ucdjk oarql qoadl tgmi";
    //text is enigma coded and reads "Wir verlassen unsere Basis. Wir wurden von Marsianern überrannt. Sie haben unseren Kommandanten gefressen."
    //English is "We are leaving our base. We have been overrun by Martians. They have eaten our commander."
    while (1) {
        sender.sendMessage(message);
        delay(MESSAGE_DELAY);   // 30-second delay between repeated messages
    }
}

void loop() {
    // Nothing to do in the loop function since everything is handled in setup
}

This is probably putting the pointers into program memory, and the data into dynamic memory. The Arduino PROGMEM reference page gives an example of a better approach.

You should find this thread helpful: Standalone attiny13a morse code generator - weird behavior

I'm not seeing it. I hate to sound so thick.

That line defines 26 pointers to 26 character arrays. in other words, an array of strings.

Those are two completely different data types! Where do those different data types end up in memory?

The PROGMEM Arduino Reference page gives a specific example of how to deal with this.

Ah. Yes. I see it now. Thankyou. I'll have to attack this when I'm less tired. Working from 5:30am to 4:20pm Monday to Friday is pretty brain frying. I'm mostly recovered by Sunday.

On an Arduino Uno, the above allocates over 160 bytes of dynamic memory for the TX/RX buffers. Since Serial not used, you should remove those lines.

Having a separate array of pointers is going to be pretty inefficient, storage-wise (two bytes of pointers and a null for each string, some of which are only 2 bytes long.)

I'd be very tempted to do something like:

 const char PROGMEM morse_code[] = {
        ".- -... -.-. -.. . ..-. --. .... .. .--- "   // A-J
        "-.- .-.. -- -. --- .--. --.- .-. ... - "     // K-T
        "..- ...- .-- -..- -.-- --.."                 // U-Z
    };

(with a space separating each "letter." To convert, you'd need to 'search' for the Nth space for each letter (25th space has the info Z after it, for instance.) "slow" compared to the direct lookup, but I'm pretty sure it would be plenty fast for anything human-oriented.)

const char* morse_code[26] = {
        ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---",   // A-J
        "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-",     // K-T
        "..-", "...-", ".--", "-..-", "-.--", "--.."                           // U-Z
    };

In a similar topic before, I suggested a way to compress this data:
".-" becomes "i"
".." becomes ":"
"--" becomes "="
"-." becomes "!"

const char morse_code[53] =
        ".-!:!!!.. :!=.::..i=!-i:---.=-i!=ii.:.- :-:ii-!i!==:";                           // A-Z

Now, each morse character takes only 2 ASCII characters (notice I needed to pad e and t with a space to make them all 2 characters long).

void sendChar(char c) {
        if (c >= 'A' && c <= 'Z') {
            for (byte j=(c - 'A')*2; j<=(c - 'A')*2+1; j++) {
                switch (morse_code[j]) {
                  case '.': sendDot(); break;
                  case '-': sendDash(); break;
                  case ':': sendDot(); sendDot(); break;
                  case '=': sendDash(); sendDash(); break;
                  case 'i': sendDot(); sendDash(); break;
                  case '!': sendDash(); sendDot(); break;
                }
            }
            delay(LETTER_SPACE - SYMBOL_SPACE);   // Space between letters
        } else if (c == ' ') {
            delay(WORD_SPACE

Combine this with the PROGMEM suggestion and the memory requirements will be far smaller.

With there only being a maximum of 4 dots, dash or nothing i think that can be reduced to 1 byte.

"-..."

can be converted to binary 0b11010101

".-"

can be 0b00000111

"-.."

can be 0b00110101 etc.

With only so few characters to be required it fits easily enough as it is, but it is an optimization.

edit: or probably easier to process if done left to right.
With morse actually being more extensive than just the letters, i am thinking that the complete thing might be possible in just a single byte. That would complicate matters significantly and probably isn't worth it, but in my mind it can be done. Using 2 bits to define if 5 or 6 signals are used or in case of less use a 3rd bit for that, and use just bit on or off for dash or dot.

Lines removed, thank you.

I'm working on the code right now and I hope getting somewhere.


const char lettera[] PROGMEM = ".-"; 
const char letterb[] PROGMEM = "-...";
const char letterc[] PROGMEM = "-.-.";
const char letterd[] PROGMEM = "-..";
const char lettere[] PROGMEM = ".";
const char letterf[] PROGMEM = "..-.";
const char letterg[] PROGMEM = "--."; 
const char letterh[] PROGMEM = "....";
const char letteri[] PROGMEM = "..";
const char letterj[] PROGMEM = ".---";
const char letterk[] PROGMEM = "-.-";
const char letterl[] PROGMEM = ".-..";
const char letterm[] PROGMEM = "--"; 
const char lettern[] PROGMEM = "-.";
const char lettero[] PROGMEM = "---";
const char letterp[] PROGMEM = ".--.";
const char letterq[] PROGMEM = "--.-";
const char letterr[] PROGMEM = ".-.";
const char letters[] PROGMEM = "..."; 
const char lettert[] PROGMEM = "-";
const char letteru[] PROGMEM = "..-";
const char letterv[] PROGMEM = "...-";
const char letterw[] PROGMEM = ".--";
const char letterx[] PROGMEM = "-..-";
const char lettery[] PROGMEM = "-.--"; 
const char letterz[] PROGMEM = "--..";
const char *const string_table[] PROGMEM = {lettera, letterb, letterc, letterd, lettere, letterf, letterg, letterh, letteri, letterj, letterk,
                letterm, lettern, lettero, letterp, letterq, letterr, letters, lettert, letteru, letterv, letterw, letterx,
                lettery, letterz};

char buffer[4];  // make sure this is large enough for the largest string it must hold

That all works. I'm going to convert the letters from the message into ascii and then subtract 96 so it'll give me the correct part of the array. At least, I hope that's how it's going to work.

Well, I've got this far and now I'm still out of memory - but not by so much.

data section exceeds available space in board
Sketch uses 584 bytes (57%) of program storage space. Maximum is 1024 bytes.
Global variables use 112 bytes (175%) of dynamic memory, leaving -48 bytes for local variables. Maximum is 64 bytes.
Not enough memory; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing your footprint.
Error compiling for board ATtiny13.

#define DASH_DURATION (3 * DOT_DURATION)   // Duration of a dash
#define SYMBOL_SPACE DOT_DURATION   // Space between symbols in the same letter
#define LETTER_SPACE (3 * DOT_DURATION)   // Space between letters
#define WORD_SPACE (7 * DOT_DURATION)   // Space between words
#define MESSAGE_DELAY 30000   // 30-second delay between messages

#define MORSE_PIN 4   // Define the pin used for Morse code output
#include <avr/pgmspace.h>
const char lettera[] PROGMEM = ".-"; 
const char letterb[] PROGMEM = "-...";
const char letterc[] PROGMEM = "-.-.";
const char letterd[] PROGMEM = "-..";
const char lettere[] PROGMEM = ".";
const char letterf[] PROGMEM = "..-.";
const char letterg[] PROGMEM = "--."; 
const char letterh[] PROGMEM = "....";
const char letteri[] PROGMEM = "..";
const char letterj[] PROGMEM = ".---";
const char letterk[] PROGMEM = "-.-";
const char letterl[] PROGMEM = ".-..";
const char letterm[] PROGMEM = "--"; 
const char lettern[] PROGMEM = "-.";
const char lettero[] PROGMEM = "---";
const char letterp[] PROGMEM = ".--.";
const char letterq[] PROGMEM = "--.-";
const char letterr[] PROGMEM = ".-.";
const char letters[] PROGMEM = "..."; 
const char lettert[] PROGMEM = "-";
const char letteru[] PROGMEM = "..-";
const char letterv[] PROGMEM = "...-";
const char letterw[] PROGMEM = ".--";
const char letterx[] PROGMEM = "-..-";
const char lettery[] PROGMEM = "-.--"; 
const char letterz[] PROGMEM = "--..";
const char *const string_table[] PROGMEM = {lettera, letterb, letterc, letterd, lettere, letterf, letterg, letterh, letteri, letterj, letterk,
                letterm, lettern, lettero, letterp, letterq, letterr, letters, lettert, letteru, letterv, letterw, letterx,
                lettery, letterz};

//char buffer[4];  // make sure this is large enough for the largest string it must hold

class MorseCodeSender {
//private:
//    const char* PROGMEM morse_code[26] = {
//        ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---",   // A-J
//        "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-",     // K-T
//        "..-", "...-", ".--", "-..-", "-.--", "--.."                           // U-Z
//    };

    void sendDot() {
        digitalWrite(MORSE_PIN, HIGH);   // Set the pin high
        delay(DOT_DURATION);
        digitalWrite(MORSE_PIN, LOW);   // Set the pin low
        delay(SYMBOL_SPACE);
    }

    void sendDash() {
        digitalWrite(MORSE_PIN, HIGH);   // Set the pin high
        delay(DASH_DURATION);
        digitalWrite(MORSE_PIN, LOW);   // Set the pin low
        delay(SYMBOL_SPACE);
    }

    void sendChar(char c) {
        if (c >= 'A' && c <= 'Z') {
            const char* morse = string_table[c - 'A'];
            while (*morse) {
                if (*morse == '.') {
                    sendDot();
                } else if (*morse == '-') {
                    sendDash();
                }
                morse++;
            }
            delay(LETTER_SPACE - SYMBOL_SPACE);   // Space between letters
        } else if (c == ' ') {
            delay(WORD_SPACE - LETTER_SPACE);   // Space between words
        }
    }

public:
    void sendMessage(const char* message) {
        while (*message) {
            sendChar(toupper(*message));
            message++;
        }
    }
};

MorseCodeSender sender;

void setup() {
    pinMode(MORSE_PIN, OUTPUT);


    // Message to send
    //const char* message = "test test test BTG emergency morse transmitter test test test";
    const PROGMEM char* message = "btoqp fgmir zdfsb ydvnf rqrtb ndmki ymzvh aylcz oifbq fbsth muucc kkwmv vxhbd urmab ucdjk oarql qoadl tgmi";
    //text is enigma coded and reads "Wir verlassen unsere Basis. Wir wurden von Marsianern überrannt. Sie haben unseren Kommandanten gefressen."
    //English is "We are leaving our base. We have been overrun by Martians. They have eaten our commander."
    while (1) {
        sender.sendMessage(message);
        delay(MESSAGE_DELAY);   // 30-second delay between repeated messages
    }
}

void loop() {
    // Nothing to do in the loop function since everything is handled in setup
}
type or paste code here

I tidied the code a bit but still get the same overflow.

**
#define MORSE_PIN 4   // Define the pin used for Morse code output
#include <avr/pgmspace.h>
const char lettera[] PROGMEM = ".-"; 
const char letterb[] PROGMEM = "-...";
const char letterc[] PROGMEM = "-.-.";
const char letterd[] PROGMEM = "-..";
const char lettere[] PROGMEM = ".";
const char letterf[] PROGMEM = "..-.";
const char letterg[] PROGMEM = "--."; 
const char letterh[] PROGMEM = "....";
const char letteri[] PROGMEM = "..";
const char letterj[] PROGMEM = ".---";
const char letterk[] PROGMEM = "-.-";
const char letterl[] PROGMEM = ".-..";
const char letterm[] PROGMEM = "--"; 
const char lettern[] PROGMEM = "-.";
const char lettero[] PROGMEM = "---";
const char letterp[] PROGMEM = ".--.";
const char letterq[] PROGMEM = "--.-";
const char letterr[] PROGMEM = ".-.";
const char letters[] PROGMEM = "..."; 
const char lettert[] PROGMEM = "-";
const char letteru[] PROGMEM = "..-";
const char letterv[] PROGMEM = "...-";
const char letterw[] PROGMEM = ".--";
const char letterx[] PROGMEM = "-..-";
const char lettery[] PROGMEM = "-.--"; 
const char letterz[] PROGMEM = "--..";
const char *const string_table[] PROGMEM = {lettera, letterb, letterc, letterd, lettere, letterf, letterg, letterh, letteri, letterj, letterk,
                letterm, lettern, lettero, letterp, letterq, letterr, letters, lettert, letteru, letterv, letterw, letterx,
                lettery, letterz};
class MorseCodeSender {
    void sendDot() {
        digitalWrite(MORSE_PIN, HIGH);   // Set the pin high
        delay(50);
        digitalWrite(MORSE_PIN, LOW);   // Set the pin low
        delay(50);
    }

    void sendDash() {
        digitalWrite(MORSE_PIN, HIGH);   // Set the pin high
        delay(150);
        digitalWrite(MORSE_PIN, LOW);   // Set the pin low
        delay(50);
    }

    void sendChar(char c) {
        if (c >= 'A' && c <= 'Z') {
            const char* morse = string_table[c - 'A'];
            while (*morse) {
                if (*morse == '.') {
                    sendDot();
                } else if (*morse == '-') {
                    sendDash();
                }
                morse++;
            }
            delay(100);   // Space between letters
        } else if (c == ' ') {
            delay(200);   // Space between words
        }
    }

public:
    void sendMessage(const char* message) {
        while (*message) {
            sendChar(toupper(*message));
            message++;
        }
    }
};

MorseCodeSender sender;

void setup() {
    pinMode(MORSE_PIN, OUTPUT);


    // Message to send
    const PROGMEM char* message = "btoqp fgmir zdfsb ydvnf rqrtb ndmki ymzvh aylcz oifbq fbsth muucc kkwmv vxhbd urmab ucdjk oarql qoadl tgmi";
    //text is enigma coded and reads "Wir verlassen unsere Basis. Wir wurden von Marsianern überrannt. Sie haben unseren Kommandanten gefressen."
    //English is "We are leaving our base. We have been overrun by Martians. They have eaten our commander."
    while (1) {
        sender.sendMessage(message);
        delay(30000);   // 30-second delay between repeated messages
    }
}

void loop() {
    // Nothing to do in the loop function since everything is handled in setup
}**

FOUND IT!

The message was too long for the microcontroller to handle.

I'll use an ATTiny85 instead. That leaves me with 10 ATTiny13s that I have yet to find a use for.

That can be compressed, too, but the point is to have fun!

That leaves me with 10 ATTiny13s that I have yet to find a use for

A small Bitcoin mining array, perhaps?