Maker.io main logo

Cheekmate - a Wireless Haptic Communication System

20

2022-11-15 | By Adafruit Industries

License: See Original Project Motors Wearables

Courtesy of Adafruit

Guide by Lady Ada

Overview

Social media is abuzz lately over the prospect of cheating in ‎tournament strategy games. Is it happening? How is that possible ‎with officials watching? Could there be a hidden receiver ‎somewhere?

We’ll investigate this by making a simple one-way hidden communicator using Adafruit parts and the Adafruit IO service. Not for actual cheating of course!‎

CONTENT WARNING: Bottom portion of this guide shows raw meat.‎

Parts

The project requires a soldering iron and related paraphernalia, and ‎the following Adafruit items:‎

Hardware

For expediency, we’ll make an assumption that only one-‎way communication is needed. In tournament games like chess, the ‎current state of the board is projected for all to see. An ‎observer accomplice in the spectator gallery (or off-site if streamed) ‎could do the work of feeding game state to an AI engine, then ‎relaying moves to the player. Technically there’s nothing ‎preventing input and two-way communication for solo use, but this ‎muddies the waters for testing the core idea.‎

An Adafruit QT Py ESP32-S2 provides the brains. Inexpensive, ‎incredibly tiny, and has built-in Wi-Fi. This can communicate with ‎a mobile hotspot (e.g., cell phone with “Wi-Fi tethering” feature) ‎carried by the accomplice theorized above.‎

esp_1

How to communicate to the player? A graphical display is right out, ‎as are visible LEDs and audible speakers. It must be silent, but ‎deadly to one’s opponent. So, we’ll use the same sort of ‎tiny vibration motor that’s in your mobile phone. A small driver ‎board accompanies this, as the motor requires more current than ‎can be driven directly from a microcontroller pin.‎

how_2

how_3

Such a receiver needs to be discreet…watches or jewelry are too ‎conspicuous (and might not be allowed by tournament rules). It ‎must be concealable, perhaps inside a shoe or under one’s armpit. ‎These body parts are naturally prone to sweat, suggesting some kind ‎of moisture-proof enclosure.‎

Social Media Internet Cops keep DEMANDING that we warn people ‎this doesn't have a flared base. We don't know what they are ‎imagining people are going to do with this project???‎

These soda bottle preforms were left over from a prior project. ‎They’re waterproof and practically indestructible…they’ve taken a ‎pounding and we’ve never wrecked ’em. The smooth shape glides ‎easily into…a back pocket. Similar capsules can be found on Amazon, ‎eBay, etc.‎

bottles_4

Circuit

Here’s a schematic view of the parts laid out for clarity. In physical ‎reality, the microcontroller and battery charger boards are soldered ‎back-to-back with headers to all pins. The motor controller has ‎identical connectors on either end…it doesn’t matter which way you ‎stick it in.‎

scheme_5

And the actual physical circuit. Battery wires are doubled back to fit ‎all parts down the tube:‎

circuit_6

The interior of the tube is tapered slightly, and it was necessary to ‎sand about 1/8" width from the motor driver to make it fit down in ‎the narrow end. Best done on the edge with the motor connections, ‎as the other edge sits close to a PCB trace.‎

The vibration motor is taped to the haptic controller board, and some ‎craft foam is inserted alongside to keep these firmly pressed against ‎the tube body to better conduct the vibration.‎

A 100 mm STEMMA cable gives enough slack that the motor and ‎controller can stay put while other parts are removable to access the ‎power switch or for charging and uploading code.‎

Once capped, the whole circuit is well protected from the elements!‎

cap_7

cap_8

If expanding on this project to add outside sensor or tactile inputs, ‎one could incorporate a cable gland to maintain a tight seal.‎

Adafruit IO Setup

We’ll use Adafruit IO as a backend, its simplicity is a huge asset to ‎this project. If you’ve not used the service, head to the Welcome to ‎Adafruit IO guide for an explainer and to set up an account. The ‎basic service is free and private!‎

So, let’s assume at this point you have an account set up and are at ‎the io.adafruit.com home page…‎

Create a New Feed

Feeds provide the conduit for getting data to devices like our ‎receiver unit.‎

From the navigation bar second to top, select “Feeds,” and then ‎‎“New Feed.”

Give the feed a useful name (e.g., “Cheekmate” to match this project) ‎and click the “Create” button. You’ll now see it in a list of feeds (or as ‎the sole feed, if first time using the service).‎

Note the “Key” name assigned to the feed; typically, a lowercase ‎version of the feed name you entered. This key is needed later ‎when setting up the code…or return to the Feeds form later to get ‎it when needed.‎

feed_9

feed_10

feed_11

Create a New Dashboard

A dashboard provides a user interface for entering data into the ‎above feed.‎

Click “Dashboards” from the navigation bar, and then “New ‎Dashboard,” assign it a name (this can be the same as the feed if ‎you want), and “Create.”

The dashboard now appears in a list (or as the sole dashboard to ‎start). Click the dashboard name in the list and we’ll create a simple ‎form for entering messages…‎

dashboard_13

dashboard_12

dashboard_14

Add a Text Field

Our new “Cheekmate” dashboard is initially blank. Near the top right ‎of the form, click the gear icon to pop open the Dashboard ‎Settings menu. Select the “Create New Block” item to add a UI ‎element…‎

menu_15

menu_16

Choose the simple Text block — it provides a single-line field for ‎entering text, that’s all we need here.‎

You’ll be asked to connect this to a feed (a destination to which any ‎text entered in the field will be sent). Select the “Cheekmate” feed ‎created earlier (or whatever name you chose), and then the “Next ‎step” button.‎

Now you can customize the look a little, like selecting the Large font ‎so it’s easy to use the dashboard from a mobile phone. Click “Create ‎block” when it’s all to your liking.‎

block_17

block_18

block_19

Optional but recommended: from the Dashboard Settings menu, ‎select “Edit Layout” to adjust the size or position of the text field so ‎it’s easier to tap. Click “Save Layout” when done.‎

save_20

save_21

Adafruit IO Username and Key

This information is needed later when setting up the code.‎

Click the Key icon near the top right of the main Adafruit IO page to ‎access your Adafruit IO key.‎

This is a seemingly random long sequence of letters and numbers ‎that uniquely identifies you to the system and will be inserted into ‎the project code to grant it access.‎

Never share this key. If you post project code on GitHub or similar, ‎remember to strip it out before committing.‎

key_23

key_22

CircuitPython Code

Code for this project is available both for CircuitPython and ‎for Arduino; you can use one or the other, whichever is more your ‎programming style. Arduino is on the next page, CircuitPython is ‎below.‎

If you’ve not used CircuitPython before, begin with the Welcome to ‎CircuitPython guide which will walk you through downloading and ‎installation.‎

Click the “Download Project Bundle” button below to get all the ‎library files packed in along with the project’s main code.py file. You ‎will still need to create a secrets.py file with Wi-Fi and Adafruit IO ‎credentials, explained later on this page.

Otherwise, if you want to assemble things manually, the project ‎requires the following CircuitPython libraries, which can be found in ‎the library bundle matching the version of CircuitPython you’re ‎using:‎

  • adafruit_drv2605.mpy

  • adafruit_io

  • adafruit_minimqtt

  • adafruit_requests.mpy

  • neopixel.mpy

These go inside the lib folder on the CIRCUITPY drive. “.mpy” items ‎are individual files, others require the full folder.‎

folder_24

Download Project Bundle

 

Copy Code
# SPDX-FileCopyrightText: Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
CHEEKMATE: secret message receiver using WiFi, Adafruit IO and a haptic
buzzer. Periodically polls an Adafruit IO dashboard, converting new messages
to Morse code.

secrets.py file must be present and contain WiFi & Adafruit IO credentials.
"""

import gc
import time
import ssl
import adafruit_drv2605
import adafruit_requests
import board
import busio
import neopixel
import socketpool
import supervisor
import wifi
from adafruit_io.adafruit_io import IO_HTTP

try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

# CONFIGURABLE GLOBALS -----------------------------------------------------

FEED_KEY = "cheekmate"  #  Adafruit IO feed name
POLL = 10  #               Feed polling interval in seconds
REPS = 3  #                Max number of times to repeat new message
WPM = 15  #                Morse code words-per-minute
BUZZ = 255  #              Haptic buzzer amplitude, 0-255
LED_BRIGHTNESS = 0.2  #    NeoPixel brightness 0.0-1.0, or 0 to disable
LED_COLOR = (255, 0, 0)  # NeoPixel color (R, G, B), 0-255 ea.

# 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.
DOT_LENGTH = 1.2 / WPM  #         Duration of one Morse dot
DASH_LENGTH = DOT_LENGTH * 3.0  # Duration of one Morse dash
SYMBOL_GAP = DOT_LENGTH  #        Duration of gap between dot or dash
CHARACTER_GAP = DOT_LENGTH * 3  # Duration of gap between characters
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.
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": "----.",
    "+": ".-.-.",
    "=": "-...-",
}

# SOME FUNCTIONS -----------------------------------------------------------


def buzz_on():
    """Turn on LED and haptic motor."""
    pixels[0] = LED_COLOR
    drv.mode = adafruit_drv2605.MODE_REALTIME


def buzz_off():
    """Turn off LED and haptic motor."""
    pixels[0] = 0
    drv.mode = adafruit_drv2605.MODE_INTTRIG


def play(string):
    """Convert a string to Morse code, output to both the onboard LED
       and the haptic motor."""
    gc.collect()
    for symbol in string.upper():
        if code := MORSE.get(symbol):  # find Morse code for character
            for mark in code:
                buzz_on()
                time.sleep(DASH_LENGTH if mark == "-" else DOT_LENGTH)
                buzz_off()
                time.sleep(SYMBOL_GAP)
            time.sleep(CHARACTER_GAP - SYMBOL_GAP)
        else:
            time.sleep(MEDIUM_GAP)


# NEOPIXEL INITIALIZATION --------------------------------------------------

# This assumes there is a board.NEOPIXEL, which is true for QT Py ESP32-S2
# and some other boards, but not ALL CircuitPython boards. If adapting the
# code to another board, you might use digitalio with board.LED or similar.
pixels = neopixel.NeoPixel(
    board.NEOPIXEL, 1, brightness=LED_BRIGHTNESS, auto_write=True
)

# HAPTIC MOTOR CONTROLLER INIT ---------------------------------------------

# board.SCL1 and SDA1 are the "extra" I2C interface on the QT Py ESP32-S2's
# STEMMA connector. If adapting to a different board, you might want
# board.SCL and SDA as the sole or primary I2C interface.
i2c = busio.I2C(board.SCL1, board.SDA1)
drv = adafruit_drv2605.DRV2605(i2c)

# "Real-time playback" (RTP) is an unusual mode of the DRV2605 that's not
# handled in the library by default, but is desirable here to get accurate
# Morse code timing. This requires bypassing the library for a moment and
# writing a couple of registers directly...
while not i2c.try_lock():
    pass
i2c.writeto(0x5A, bytes([0x1D, 0xA8]))  # Amplitude will be unsigned
i2c.writeto(0x5A, bytes([0x02, BUZZ]))  # Buzz amplitude
i2c.unlock()

# WIFI CONNECT -------------------------------------------------------------

try:
    print("Connecting to {}...".format(secrets["ssid"]), end="")
    wifi.radio.connect(secrets["ssid"], secrets["password"])
    print("OK")
    print("IP:", wifi.radio.ipv4_address)

    pool = socketpool.SocketPool(wifi.radio)
    requests = adafruit_requests.Session(pool, ssl.create_default_context())
    # WiFi uses error messages, not specific exceptions, so this is "broad":
except Exception as error:  # pylint: disable=broad-except
    print("error:", error, "\nBoard will reload in 15 seconds.")
    time.sleep(15)
    supervisor.reload()

# ADAFRUIT IO INITIALIZATION -----------------------------------------------

aio_username = secrets["aio_username"]
aio_key = secrets["aio_key"]
io = IO_HTTP(aio_username, aio_key, requests)

# SUCCESSFUL STARTUP, PROCEED INTO MAIN LOOP -------------------------------

buzz_on()
time.sleep(0.75)  # Long buzz indicates everything is OK
buzz_off()

current_message = ""  # No message on startup
rep = REPS  #           Act as though message is already played out
last_time = -POLL  #    Force initial Adafruit IO polling

while True:  # Repeat forever...

    now = time.monotonic()
    if now - last_time >= POLL:  #            Time to poll Adafruit IO feed?
        last_time = now  #                    Do it! Do it now!
        feed = io.get_feed(FEED_KEY)
        new_message = feed["last_value"]
        if new_message != current_message:  # If message has changed,
            current_message = new_message  #  Save it,
            rep = 0  #                        and reset the repeat counter

    # 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(current_message)
        time.sleep(MEDIUM_GAP)
        rep += 1

View on GitHub

secrets.py

If you’ve previously worked with CircuitPython Wi-Fi projects, you ‎might already have this file on the drive, or another CircuitPython ‎board. If not, it’s easy enough to create anew. Using your text editor ‎of preference, create a new file on the CIRCUITPY drive, ‎called secrets.py.‎

Copy and paste the following exactly as it is, as a starting point:‎

Download File

Copy Code
secrets = {
    'ssid' : 'wifi_network_name',
    'password' : 'wifi_password',
    'aio_username' : 'adafruit_io_username',
    'aio_key' : 'adafruit_io_key'
    }

This is a list of Python 'key' : 'value' pairs. Do not edit the keys (the ‎part before the colon : on each line), just the values, being careful to ‎keep both 'quotes' around strings and the comma at the end of each ‎line.‎

Replace wifi_network_name and wifi_password with the name or ‎‎“SSID” of your wireless network and the password for access. If ‎tethering from a phone, one or both might be auto generated…this ‎information will be somewhere in the phone settings. Only 2.4 GHz ‎networks are supported; 5 GHz is not compatible with ESP32.‎

Replace adafruit_io_username and adafruit_io_key with your name ‎and unique key as explained on the “Adafruit IO Setup” page.‎

Arduino Code

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 Wi-Fi 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:‎

Download Arduino “Cheekmate” Code

Or here they are in line for your perusal:‎

Download File

Copy Code
// 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
}

View on GitHub

Download File

Copy Code
// 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])

View on GitHub

Testing and Analysis

When powered on, the device will take perhaps 20 seconds ‎to connect to the wireless network and authenticate with Adafruit ‎IO. On success it will emit a single long buzz and light the onboard ‎LED.‎

If you do not get this buzz: there’s an issue with the Wi-Fi or ‎Adafruit IO credentials, or the wiring between board and motor ‎driver. Connecting the board to USB and watching with the Arduino ‎serial monitor or other serial tool (e.g., Tio or screen) will give some ‎indication of where the problem lies.‎

So, let’s say at this point you’re buzzed and working…‎

Return to the “Dashboards” tab of Adafruit IO and pick your ‎Cheekmate dashboard from the list.‎

Type a brief message in the text field and press return or click or ‎tab out of the field.

Within a few seconds, this should be relayed to the device, which will ‎start to flash and buzz with a Morse code version of the message.‎

The message will repeat up to three times, unless a new message is ‎received during that time, in which case the current message ‎finishes and the new one repeats three times.‎

test_25

Meat and Greet

So, we know the code and device work in open air, but what about in ‎a hypothetical use case? There are two things to find here:‎

  • Bodies are mostly water, and RF energy is greatly attenuated ‎in water. Can signals penetrate if the device is nestled in, say, ‎one’s armpit?‎

  • Once surrounded by flesh, is the vibration motor sufficiently ‎muffled to avoid detection, or does it give away the gag?‎

Without a willing partner to test and record findings with, it seemed ‎most objective to use a proxy with similar characteristics…like a ‎quantity of meat. Initial plan was to shove the device between two ‎large hams, but it turns out ham is really expensive in the off season.‎

Pound for pound, bone-in pork butt roast is quite affordable!‎

A channel was cut through the middle, into which the device was ‎firmly lodged.‎

meat_26

meat_27

In Action

The unit was powered on, sealed, and inserted. A Wi-Fi access point ‎was about 30 feet away, through two walls and a couple inches of ‎meat now. The end cap did protrude slightly, so it’s not a perfect test ‎for Wi-Fi penetration, but fixing this would require a bigger butt ‎roast.‎

Secret messages were then entered in the project’s Adafruit IO ‎Dashboard. Here’s what happened:‎

 

 

Analysis

While not a thoroughly scientific test, it does shine a light on the ‎tenable aspects of the cheat device theory:

  • The circuit, and the internet dashboard, were both incredibly ‎simple to build and code; it does not require extensive ‎engineering skills. The hardest parts would be a bit of soldering ‎and memorizing Morse code.

  • ‎Wi-Fi had no problem penetrating at this distance and through ‎this medium. If an internet connection can be established ‎through an accomplice, and data relayed through wireless, ‎messages can be relayed.‎

However, working against it…‎

  • The vibration motor, even when muffled through pounds of ‎flesh, is anything but subtle. Officials or other players would be ‎immediately aware. The vibration could be dialed down to a ‎calmer level, but risks messages not being interpreted clearly ‎as they’re harder to sense.‎

Thus, a reasonable conclusion is that such an idea is plausible, but ‎unlikely. With refinement, a more discreet device could surely be ‎developed…but, with the risk still present of being discovered, ‎banned from competition, and being the butt of jokes for ‎generations to come. One’s time is likely better spent learning and ‎practicing game strategy.‎

A series of escalating measures and countermeasures come to ‎mind, and it’s not clear there’s any real endgame to this.‎

Metal detectors are already in use at some events, but these are ‎usually calibrated to ignore small nuisance items like coins or keys…a ‎well-crafted receiver might slip through.‎

Blocking wireless signals would seem an obvious choice…but FCC ‎laws prevent this. A deep-pocketed tournament might manage this ‎by hosting events offshore, beyond Federal jurisdiction. Alternately, ‎players might compete inside a Faraday cage, Thunderdome-style.‎

These measures might still be circumvented by eliminating the off-‎site component, with self-contained game AI carried on one’s ‎person. A Raspberry Pi Zero would be a bit of a stretch…but devices ‎are continually getting smaller and more powerful, and soon (if not ‎already) something could tuck into one’s navel or another cavity.‎

measures_28

Mfr Part # 5325
STEMMA QT QT PY ESP32-S2 WIFI
Adafruit Industries LLC
Mfr Part # 2305
EVAL BOARD FOR DRV2605L
Adafruit Industries LLC
Mfr Part # 1201
VIBRATION ERM MTR 11000 RPM 5V
Adafruit Industries LLC
Mfr Part # 5397
QT PY LIION OR LIPOLY CHARGER
Adafruit Industries LLC
Mfr Part # 4210
JST SH 4-PIN CABLE - QWIIC COMPA
Adafruit Industries LLC
Add all DigiKey Parts to Cart
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.