Are multidimensional arrays the best solution for my problem?

Hi,

I’m an Arduino noob using a nano for a couple of weeks. I love it! I should have played with these years ago… I’m working on a multi LED display board using WS2811 LED strings and the FastLED library, however my question is really a C question and is unrelated to FastLED.

Apologies in advance if I’m not posting code as per forum protocol. I didn’t want to supply the entire sketch as it is fairly big.

As part of a display loop which plots different colours on the LED string, I wanted a simple function to flash all of the LEDs on the string to display a morse code message.

//Define array:

// Set up strings for morse code.
const char MORSE0 = “.- .-. - … …- .-. / … … / .- – .- --… … -. --.”; //arthur is amazing

//In the main program loop the function is called:

//DELTA_HUE, BRIGHT, START_POS, END_POS, SPEED (higher number = slower)
MorseCode(4, PLOT_NORM, 0, NUM_LEDS - 1, 150);
FastLED.delay(300);

//Morse function:
void MorseCode(uint8_t DELTA_HUE, uint8_t BRIGHT, int START_POS, int END_POS, int SPEED) {
// Print morse code. TODO - Other messages (only MORSE0 currently) and more variables (colours etc).
String CHAR;
fill_solid(leds, NUM_LEDS, 0); // Turns all LEDs off
FastLED.delay(SPEED * 7);
for (int MORSE_CHAR = 0; MORSE_CHAR < sizeof(MORSE0); MORSE_CHAR++) {
(CHAR) = MORSE0[MORSE_CHAR];
if (CHAR.equals(".")) {
RainbowWord(MORSE_CHAR * 32, DELTA_HUE, BRIGHT, START_POS, END_POS); //Displays rainbow
FastLED.delay(SPEED); //Delay
fill_solid(leds, NUM_LEDS, 0);
}
else if (CHAR.equals("-")) {
RainbowWord(MORSE_CHAR * 32, DELTA_HUE, BRIGHT, START_POS, END_POS); //Displays rainbow
FastLED.delay(SPEED * 3); //Delay
fill_solid(leds, NUM_LEDS, 0); // Turns all LEDs off
}
else if (CHAR.equals(" “)) {
FastLED.delay(SPEED); //Delay
}
else if (CHAR.equals(”/")) {
FastLED.delay(SPEED * 5);
}
FastLED.delay(SPEED);
}
fill_solid(leds, NUM_LEDS, 0); // Turns all LEDs off
FastLED.delay(SPEED * 7);
}

The above works fine, but I want to be able to store multiple messages (which eventually I plan to move to PROGMEM to save on SRAM - but one step at a time!).

When I try to make MORSE multidimensional like this:

const char MORSE[0] = “.- .-. - … …- .-. / … … / .- – .- --… … -. --.”; //arthur is amazing
const char MORSE[1] = “.- .-. -/ … … / .- – .- --… … -. --.”; //some other message

I get this error:

error: declaration of ‘MORSE’ as multidimensional array must have bounds for all dimensions except the first

Would using substring be a better solution? Or something else? I’m confused as to what makes an array element contain a single character versus a multi-character string?

I’d be grateful for some advice.

Thanks

Olly
.

That is not the way to declare or initialize a multidimensional array. Look up the right way or wait for other replies.

By the way, this

(CHAR)= ...

could be just this

CHAR=...

not to mention that it is silly to name a String variable as CHAR, and the usual advice is to avoid the Strng type in Arduino programs. It is advised to use the C string instead.

First, welcome to the Forum. It might help you to get more responses if you read Nick Gammon's post on How to Use This Forum at the top of the home page, especially the use of code tags when posting code.

Second, it would be easier if you put the Morse Table in your code and then translated the messages using character arrays and C strings. See my earlier comment, post #6, here.

Three, avoid the String class and use C char arrays for stings instead. The String class tends to bloat your code.

Fourth, the common C style is to only use all caps for symbolic constants and *enum*s, rather than common variables.

Finally, with your source code in the IDE, use Ctrl-T to reformat your code to a common C style. It usually makes it easier to read for the rest of us.

   char MORSE[2][] = { /* Initialization data goes here for the 1st dimension */ ,
                                   /* Initialization data goes here for the 2nd dimension */ };

Below is demo code showing how you might structure your messages, including how to access single characters, which will hopefully show you how to remove the String class from your program.

const char *arthurIsAmazing =   ".- .-. - .... ..- .-. / .. ... / .- -- .- --.. .. -. --."; //arthur is amazing
const char *somerOtherMessage = ".- .-. -/ .. ... / .- -- .- --.. .. -. --."; //some other message

const char *messages[] = {arthurIsAmazing, somerOtherMessage};

void setup() {
  Serial.begin(9600);
  Serial.print("msg 1   ");Serial.println(messages[0]);
  Serial.print("msg 2   ");Serial.println(messages[1]);

  Serial.println("message 1 characters:  ");
  for(int i=0; i < strlen(messages[0]); i++)
  {
     Serial.print(*(messages[0]+i));
  }

}

void loop() {
 
}

Serial output:

msg 1   .- .-. - .... ..- .-. / .. ... / .- -- .- --.. .. -. --.
msg 2   .- .-. -/ .. ... / .- -- .- --.. .. -. --.
message 1 characters:  
.- .-. - .... ..- .-. / .. ... / .- -- .- --.. .. -. --.

Thanks very much for all of your helpful responses, particularly Blue Eyes for taking the time to write me a demo!

Olly .

Hmm, I’m struggling to get strncmp to do what I want. I would be very grateful if someone could point out my obvious error, or perhaps if the approach is wrong?

As per most of the replies in this thread, I used char instead of String. .equals did not work with this, so I tried strcmp and then strncmp, but both are outputting 106 whether the character is a match or not. I expected a value between -1 and +1 from the documentation I read.

Define morse messages:

const char *morse0 = "...-";
const char *morse1 = "---.";
const char *morse2 = "-.--";
const char *morse3 = ".-..";

// The following needs to be updated if more morse messages are added.
const char *morseMessages[] = {morse0, morse1, morse2, morse3};
const uint8_t numMorseMessages = 4;

Call function:

MorseCode(4, plotNorm, 0, numLeds - 1, 300, 3);

Function:

void MorseCode(uint8_t deltaHue, uint8_t bright, int startPos, int endPos, int delayAmount, int messageNum) {
  // Print morse code.
  fill_solid(leds, numLeds, 0);
  FastLED.delay(delayAmount * 7);
  for (int morseCharacter = 0; morseCharacter < strlen(morseMessages[messageNum]); morseCharacter++) {
    //#for (int morseCharacter = 0; morseCharacter < sizeof(morse0); morseCharacter++) {
    char character = (*(morseMessages[messageNum] + morseCharacter));
    Serial.println(character);
    Serial.println(strncmp(character, ".", 1));
    if (strncmp(character, ".") == 0) {
      RainbowWord(morseCharacter * 32, deltaHue, bright, startPos, endPos);
      FastLED.delay(delayAmount);
      fill_solid(leds, numLeds, 0);
    }
    else if (strncmp(character, "-", 1) == 0) {
      RainbowWord(morseCharacter * 32, deltaHue, bright, startPos, endPos);
      FastLED.delay(delayAmount * 3);
      fill_solid(leds, numLeds, 0);
    }
    else if (strncmp(character, " ", 1) == 0) {
      FastLED.delay(delayAmount);
    }
    else if (strncmp(character, "/", 1) == 0) {
      FastLED.delay(delayAmount * 5);
    }
    FastLED.delay(delayAmount);
  }
  fill_solid(leds, numLeds, 0);
  FastLED.delay(delayAmount * 7);
}

Serial output:

.
106
-
106
.
106
.
106

Thanks

Olly.
.

    if (strncmp(character, ".") == 0) {

The string functions expect strings. character is NOT a string.

   if(character == '.')
   { // Down here, where it belongs

Thanks very much PaulS. That works perfectly. I had tried

if(character == ".")

which does not work, so apparently I need to go and read up on when to use single and double quotes in C!

As for your comment on where the open curly bracket should be - I completely understand that it’s important to follow good practice, but I picked up that style from some code I found somewhere. It’s hard to know what is correct and what isn’t when hacking around with snippets acquired from various sources.

Thanks again.

olly69: apparently I need to go and read up on when to use single and double quotes in C!

So it seems that single quotes denote characters and double denote strings. A single character in double quotes would be stored as a 2 character array (the second char a string terminator). Is that more or less correct?

Sounds correct to me. The string terminator is the null character, which is a byte containing zero.

By the way, there are various ways to format lines. The Arduino IDE does it one way, I learned another way. Just understand the pros and cons of the various ways, choose your way, and be consistent.