The Arduino version of the code does essentially the same thing; you can use one or the other, whichever is more your programming style.

This requires the Adafruit DRV2605 and Adafruit IO libraries. Installing these using the Arduino Library Manager is recommended, as it will take care of all prerequisites: Sketch→Include Library→Manage Libraries…

There are two files in this project. One contains the bulk of the code, the other has configurable settings such as the WiFi network name and password, plus the Adafruit IO account credentials and feed name. You’ll need to edit the latter file (config.h) with all your particulars…it’s all named descriptively and should be clear what goes where. Make sure the correct board type is selected before uploading.

You can either download a ZIP with both files:

Or here they are inline for your perusal:

// SPDX-FileCopyrightText: Adafruit Industries
//
// SPDX-License-Identifier: MIT

/*
CHEEKMATE: secret message receiver using WiFi, Adafruit IO and
a haptic buzzer. Monitors an Adafruit IO feed, converting new
messages to Morse code.

WiFi & Adafruit IO credentials are in the accompanying config.h file.
*/

#include <AdafruitIO_WiFi.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_DRV2605.h>
#include "config.h" // SET UP WIFI AND ADAFRUIT IO CREDENTIALS HERE

AdafruitIO_WiFi   io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS);
AdafruitIO_Feed  *feed = io.feed(FEED_NAME, FEED_OWNER);
Adafruit_NeoPixel led(1, PIN_NEOPIXEL);
Adafruit_DRV2605  drv;
char              message[51];
int               rep = REPS; // Act as though message is already played out

// Runs once at startup
void setup() {
  Serial.begin(115200);

  led.begin();
  led.setBrightness(LED_BRIGHTNESS);
  led.show();

  // Wire1 is the "extra" I2C interface on the QT Py ESP32-S2's
  // STEMMA connector. If adapting to a different board, you might
  // want &Wire for the sole or primary I2C interface.
  drv.begin(&Wire1);
  drv.writeRegister8(0x1D, 0xA8); // Amplitude will be unsigned
  drv.setRealtimeValue(BUZZ);

  feed->onMessage(handleMessage); // Set up message handler for feed

  Serial.print("Connecting to Adafruit IO");
  io.connect();
  while(io.status() < AIO_CONNECTED) { // Wait for connection
    Serial.write('.');
    delay(500);
  }
  Serial.println(io.statusText());

  buzz_on();
  delay(750); // Long buzz indicates everything is OK
  buzz_off();
}

// Runs repeatedly until reset or power-off
void loop() {
  io.run(); // Must periodically call Adafruit IO event manager
  // Play last message up to REPS times. If a new message has come
  // along in the interim, old message may repeat less than this,
  // and new message resets the count.
  if (rep < REPS) {
    play(message);
    delay(MEDIUM_GAP);
    rep++;
  }
}

// Turn on LED and haptic motor 
void buzz_on() {
  led.setPixelColor(0, LED_COLOR);
  led.show();
  drv.setMode(DRV2605_MODE_REALTIME);
}

// Turn off LED and haptic motor
void buzz_off() {
  led.setPixelColor(0, 0);
  led.show();
  drv.setMode(DRV2605_MODE_INTTRIG);
}

// Convert a string to Morse code, output to both the onboard LED
// and the haptic motor.
void play(char *str) {
  while(char c = toupper(*str++)) { // Upper-caseify each character of string...
    int i=0;
    // Scan Morse dictionary (in config.h) for a match
    for (; i<NUM_SYMBOLS && morse[i].symbol != c; i++);
    if (i < NUM_SYMBOLS) { // Found one!
      char mark;
      for (int j=0; (mark = morse[i].mark[j]); j++) {
        buzz_on();
        delay(mark == '-' ? DASH_LENGTH : DOT_LENGTH);
        buzz_off();
        delay(SYMBOL_GAP);
      }
      delay(CHARACTER_GAP - SYMBOL_GAP);
    } else { // Not in dictionary, prob. a space
      delay(MEDIUM_GAP);
    }
  }
}

// Called when feed receives a message.
void handleMessage(AdafruitIO_Data *data) {
  // Limit incoming message to fit char buffer + NUL
  strncpy(message, data->toChar(), sizeof message - 1);
  Serial.printf("Received '%s'\n", message);
  rep = 0; // Reset the message repeat counter
}
// SPDX-FileCopyrightText: Adafruit Industries
//
// SPDX-License-Identifier: MIT

#define WIFI_SSID "your_wifi_ssid"
#define WIFI_PASS "your_wifi_password"

// visit io.adafruit.com if you need to create an account,
// or if you need your Adafruit IO key.
#define IO_USERNAME "your_io_username"
#define IO_KEY      "your_io_key"

#define FEED_OWNER "feed_owner_name"
#define FEED_NAME  "cheekmate"

#define REPS           3        // Max number of times to repeat new message
#define WPM            15       // Morse code words-per-minute
#define BUZZ           255      // Haptic buzzer amplitude, 0-255
#define LED_BRIGHTNESS 50       // NeoPixel brightness 1-255, or 0 to disable
#define LED_COLOR      0xFF0000 // NeoPixel color (RGB hexadecimal)

// These values are derived from the 'WPM' setting above and do not require
// manual editing. The dot, dash and gap times are set according to accepted
// Morse code procedure.
#define DOT_LENGTH    1200 / WPM       // Duration of one Morse dot
#define DASH_LENGTH   (DOT_LENGTH * 3) // Duration of one Morse dash
#define SYMBOL_GAP    DOT_LENGTH       // Duration of gap between dot or dash
#define CHARACTER_GAP (DOT_LENGTH * 3) // Duration of gap between characters
#define MEDIUM_GAP    (DOT_LENGTH * 7) // Duraction of gap between words

// Morse code symbol-to-mark conversion dictionary. This contains the
// standard A-Z and 0-9, and extra symbols "+" and "=" sometimes used
// in chess. If other symbols are needed for this or other games, they
// can be added to the end of the list.
const struct {
  char symbol;
  const char *mark;
} morse[] = {
    'A', ".-",
    'B', "-...",
    'C', "-.-.",
    'D', "-..",
    'E', ".",
    'F', "..-.",
    'G', "--.",
    'H', "....",
    'I', "..",
    'J', ".---",
    'K', "-.-",
    'L', ".-..",
    'M', "--",
    'N', "-.",
    'O', "---",
    'P', ".--.",
    'Q', "--.-",
    'R', ".-.",
    'S', "...",
    'T', "-",
    'U', "..-",
    'V', "...-",
    'W', ".--",
    'X', "-..-",
    'Y', "-.--",
    'Z', "--..",
    '0', "-----",
    '1', ".----",
    '2', "..---",
    '3', "...--",
    '4', "....-",
    '5', ".....",
    '6', "-....",
    '7', "--...",
    '8', "---..",
    '9', "----.",
    '+', ".-.-.",
    '=', "-...-",
};
#define NUM_SYMBOLS (sizeof morse / sizeof morse[0])

This guide was first published on Oct 05, 2022. It was last updated on Jul 20, 2024.

This page (Arduino Code) was last updated on Jul 17, 2024.

Text editor powered by tinymce.