Maker.io main logo

Cheekmate - a Wireless Haptic Communication System

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

制造商零件编号 5325
STEMMA QT QT PY ESP32-S2 WIFI
Adafruit Industries LLC
制造商零件编号 2305
EVAL BOARD FOR DRV2605L
Adafruit Industries LLC
制造商零件编号 1201
VIBRATION ERM MTR 11000 RPM 5V
Adafruit Industries LLC
制造商零件编号 5397
QT PY LIION OR LIPOLY CHARGER
Adafruit Industries LLC
制造商零件编号 4210
JST SH 4-PIN CABLE - QWIIC COMPA
Adafruit Industries LLC
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum