This is only marginally more advanced than the blinking light example in my first Arduino post, but perhaps a little more interesting!

Again it uses the built-in LED, so no connections are required.

Version 1 – Functions only

#include <USBAPI.h>

void on() {
    digitalWrite(LED_BUILTIN, HIGH);
}

void off() {
    digitalWrite(LED_BUILTIN, LOW);
}

// https://en.wikipedia.org/wiki/Morse_code
// [...] a simple calculation shows that the dot length at 20 words per minute is 60 milliseconds
// But even knowing the expected output, I can't follow anything less than 250ms!
#define DOT_TIME 250

void dot() {
    on();
    delay(DOT_TIME);
    off();
    // Each dot or dash within a character is followed by period of signal absence, called a space, equal to the dot duration
    delay(DOT_TIME);
}

void dash() {
    on();
    // The duration of a dash is three times the duration of a dot
    delay(3 * DOT_TIME);
    off();
    delay(DOT_TIME);
}

void next_letter() {
    // The letters of a word are separated by a space of duration equal to three dots
    // (We've already had one by this point - see dot() and dash())
    delay((3 - 1) * DOT_TIME);
}

void space() {
    // The words are separated by a space equal to seven dots
    // (We've already had three by this point - see next_letter())
    delay((7 - 3) * DOT_TIME);
}

void d() {
    dash(); dot(); dot();
    next_letter();
}

void e() {
    dot();
    next_letter();
}

void h() {
    dot(); dot(); dot(); dot();
    next_letter();
}

void l() {
    dot(); dash(); dot(); dot();
    next_letter();
}

void o() {
    dash(); dash(); dash();
    next_letter();
}

void r() {
    dash(); dot(); dash();
    next_letter();
}

void w() {
    dot(); dash(); dash();
    next_letter();
}

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
    // Expected output:
    // .... . .-.. .-.. ---
    h(); e(); l(); l(); o();
    space();
    // .-- --- .-. .-.. -..
    w(); o(); r(); l(); d();
    space();
    space();
    space();
}

Version 2 – Strings and switch statements

#include <USBAPI.h>

void on() {
    digitalWrite(LED_BUILTIN, HIGH);
}

void off() {
    digitalWrite(LED_BUILTIN, LOW);
}

// https://en.wikipedia.org/wiki/Morse_code
// [...] a simple calculation shows that the dot length at 20 words per minute is 60 milliseconds
// But even knowing the expected output, I can't follow anything less than 250ms!
#define DOT_TIME 250

void dot() {
    on();
    delay(DOT_TIME);
    off();
    // Each dot or dash within a character is followed by period of signal absence, called a space, equal to the dot duration
    delay(DOT_TIME);
}

void dash() {
    on();
    // The duration of a dash is three times the duration of a dot
    delay(3 * DOT_TIME);
    off();
    delay(DOT_TIME);
}

void next_letter() {
    // The letters of a word are separated by a space of duration equal to three dots
    // We've already had one by this point - see dot() and dash()
    delay((3 - 1) * DOT_TIME);
}

void space() {
    // The words are separated by a space equal to seven dots
    // We've already had three by this point - see next_letter()
    delay((7 - 3) * DOT_TIME);
}

void next_message() {
    // Can't see any recommendations for this, so chose three times a space
    // We've already had three by this point - see next_letter()
    delay((21 - 3) * DOT_TIME);
}

void error(const String& message) {
    Serial.println(message);
    // Keep the LED on for a long time to signal a problem, but then continue
    on();
    delay(10 * DOT_TIME);
    off();
    delay(DOT_TIME);
}

void morse(const String& symbols) {
    for (unsigned int i = 0, len = symbols.length(); i < len; i++) {

        const char symbol = symbols.charAt(i);
        switch (symbol) {
            case '.': dot(); break;
            case '-': dash(); break;
            default: error(String("morse() can't handle '") + symbol + "'"); break;
        }
    }

    next_letter();
}

void transmit_character(const char letter) {
    switch (toLowerCase(letter)) {
        case 'a': morse(".-"); break;
        case 'b': morse("-..."); break;
        case 'c': morse("-.-."); break;
        case 'd': morse("-.."); break;
        case 'e': morse("."); break;
        case 'f': morse("..-."); break;
        case 'g': morse("--."); break;
        case 'h': morse("...."); break;
        case 'i': morse(".."); break;
        case 'j': morse(".---"); break;
        case 'k': morse("-.-"); break;
        case 'l': morse(".-.."); break;
        case 'm': morse("--"); break;
        case 'n': morse("-."); break;
        case 'o': morse("---"); break;
        case 'p': morse(".--."); break;
        case 'q': morse("--.-"); break;
        case 'r': morse(".-."); break;
        case 's': morse("..."); break;
        case 't': morse("-"); break;
        case 'u': morse("..-"); break;
        case 'v': morse("...-"); break;
        case 'w': morse(".--"); break;
        case 'x': morse("-..-"); break;
        case 'y': morse("-.--"); break;
        case 'z': morse("--.."); break;
        case '0': morse("-----"); break;
        case '1': morse(".---"); break;
        case '2': morse("..---"); break;
        case '3': morse("...--"); break;
        case '4': morse("....-"); break;
        case '5': morse("....."); break;
        case '6': morse("-...."); break;
        case '7': morse("--..."); break;
        case '8': morse("---.."); break;
        case '9': morse("----."); break;
        case ' ': space(); break;
        default: error(String("transmit_character() can't handle '") + letter + "'"); break;
    }
}

void transmit_message(const String& message) {
    for (unsigned int i = 0, len = message.length(); i < len; i++) {
        transmit_character(message.charAt(i));
    }

    next_message();
}

void setup() {
    Serial.begin(9600);
    pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
    // Expected output:
    // .... . .-.. .-.. ---
    // .-- --- .-. .-.. -..
    transmit_message("Hello World");
}

References