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])
Page last edited January 21, 2025
Text editor powered by tinymce.