00. Download - Install
ESP32 Hardware Compatibility:
- 100% Variant Agnostic: Uses low-level ESP-IDF `soc` macro checks to safely lock and manipulate the Physical Layer (PHY) across the ESP32, ESP32-S2, ESP32-S3, ESP32-C3, and ESP32-C6 without crashing native hardware controllers.
Get the latest release directly from GitHub:
Download Last ReleaseInstall via the Arduino IDE Library Manager (Sketch > Include Library > Add .ZIP Library).
01. Architecture
CATMOUSE was conceived as an experimental game of wireless hide-and-seek, but results in an incredibly resilient, zero-configuration networking strategy that actively circumvents external radio jamming by utilizing the entirety of the 2.4Ghz physical spectrum dynamically.
The CAT (Hunter)
Boots in LISTENING mode, actively scanning all 13 channels until it catches
a Mouse's Squeak. Once locked on, it initiates aggressive sweeping by firing
targeted payloads bearing its gameID, leveraging hardware ACKs.
Stamina: A Cat's sweep rate slows down the longer it misses (saving power and
minimizing airwave congestion). When it scores a Hit, it enters a 5ms interval "Frenzy,"
launching hyper-aggressive strikes before the Mouse flies to a new frequency.
The MOUSE (Hider)
A passive hider. It randomly selects a Wi-Fi channel and a hiding duration.
If it receives a packet from a Cat bearing the correct gameID, it triggers a
'HIT' callback and immediately flees to a new random channel.
The Squeak: The
Mouse occasionally unleashes a public broadcast Squeak on its active channel. Cats in
LISTENING mode can catch this Squeak, seamlessly locking onto the Mouse's MAC address
without hardcoding.
02. API Reference
bool beginAsCat(uint8_t gameID, uint8_t* targetMac = nullptr, unsigned long attackRateMs = 10)
Initializes the device as the Hunter on a specific
gameID. If you leave targetMac as a nullptr, the Cat
enters LISTENING mode and actively scans channels until it catches a Mouse Squeak to automatically determine the MAC
target. Returns false if ESP-NOW initialization fails.
bool beginAsMouse(uint8_t gameID, unsigned long hideMinMs = 100, unsigned long hideMaxMs = 500, unsigned long squeakIntervalMs = 2000)
Initializes the device as the Target locking into a
gameID. Configures the min/max window for autonomous channel hopping and the
Squeak broadcast interval. Returns false if ESP-NOW initialization fails.
void end()
Tears down ESP-NOW, unregisters all callbacks, and resets the internal state. Call this to cleanly stop the protocol before re-initializing or shutting down.
void update()
The main timer engine. Analyzes state changes, updates stamina, triggers Squeaks, schedules transmits, and manages hops. Must run in loop().
uint32_t getHitsScored()
Returns the internal telemetry. If Cat: successful Hits tracked. If Mouse: successful escapes.
uint32_t getSweepsMissed()
Returns the number of channels the Cat has swept without securing an ACK. Useful for monitoring hunt efficiency.
03. Event Callbacks
void onHit(HitCallback cb)
Registers an external function to trigger when contact is
made. If assigned to the Cat, it fires when the target is found. If assigned to the Mouse,
it fires when it is caught. The callback brings the uint8_t channel int with
it.
void onMiss(MissCallback cb)
Registers an external function to trigger when a payload
fails to secure an ACK (meaning the target is not on the current channel). The callback
brings the uint8_t channel int with it.
04. Implementation
Example: The Cat (Listener)
#include <CatMouse.h>
CatMouse cat;
// Define your Game ID. Cats will only hunt Mice with matching IDs.
const uint8_t gameID = 42;
void onCatHit(uint8_t channel) {
Serial.printf("HIT! Found Mouse on Channel: %d | Hits Scored: %d\n", channel,
cat.getHitsScored());
}
void onCatMiss(uint8_t channel) {
Serial.printf("Miss... Sweeping Channel: %d | Sweeps: %d\n", channel,
cat.getSweepsMissed());
}
void setup() {
Serial.begin(115200);
// Initialize as CAT using just the Game ID (no MAC address)
// The Cat will boot in LISTENING mode, scanning channels until it hears a Squeak
if (!cat.beginAsCat(gameID, nullptr, 10)) {
Serial.println("Failed to initialize Cat! Check ESP-NOW.");
while (true) delay(1000);
}
// Register the result callbacks
cat.onHit(onCatHit);
cat.onMiss(onCatMiss);
Serial.println("Cat is scanning channels, listening for the first Mouse squeak...");
}
void loop() {
// Calling update() triggers listening, and eventually sweeps & stamina logic
cat.update();
}
Example: The Mouse (Hider)
#include <CatMouse.h>
CatMouse mouse;
// Define your Game ID. The Mouse will only respond to Cats with this ID.
const uint8_t gameID = 42;
// Runs when the Cat finds the Mouse on the current channel
void onMouseCaught(uint8_t channel) {
Serial.printf("CAUGHT! The Cat found me on Channel: %d ! Fleeing...\n",
channel);
}
void setup() {
Serial.begin(115200);
// Initialize as MOUSE with the Game ID.
// It will hide on a random channel for anywhere between 100ms and 500ms
// Squeaks every 2000ms (default) to let Cats discover it
if (!mouse.beginAsMouse(gameID, 100, 500)) {
Serial.println("Failed to initialize Mouse! Check ESP-NOW.");
while (true) delay(1000);
}
// Register the callback for when the Cat scores a direct hit
mouse.onHit(onMouseCaught);
Serial.println("Mouse is hiding and occasionally squeaking...");
}
void loop() {
// Calling update() automatically triggers hops and periodic Squeak broadcasts
mouse.update();
}