Autocatcher/Gotcha/Pokemon Go Plus device emulator for Pokemon Go, with autospin and autocatch capabilities.
Note
This repository is based on yohane's initial work, spezifisch's fork and paper183's fork.
Warning
This repository doesn't contain the secret blobs needed to make a working emulator for a PGP! You need to dump these secrets yourself from your own original device (see References for some pointers).
- Features
- Hardware
- Installation & Setup
- Configuration
- Usage Guide
- Architecture
- Project Structure
- Code Organization
- Testing Guide
- Manual Testing Guide
- Troubleshooting
- Contributing
- References
- ESP-IDF with BLE support
- Tailored for ESP32-C3, draws around 0.05A on average
- Settings menu using serial port
- Statically loaded secrets (see Load Pokemon Go Plus Secrets) in NVS
- Toggle autocatch/autospin
- Android 15+ support
- iOS support
- Connect up to 4 different devices simultaneously with independent settings
- Session key caching - reconnect without passkey after initial pairing
- Per-device settings - each connected device can have unique configuration
- LED pattern recognition for multiple scenarios:
- Pokemon caught
- Pokemon fled
- Pokestop spin
- Bag full
- Box full
- Pokeballs empty
- Randomized delays for button press and duration
- Settings persistence - all settings saved to NVS across reboots
- pogoplusle - compatible, benefits from auto reconnect and seamless handshake
- Microcontroller: ESP32-C3 Super Mini
- 3d printed case: Makerworld Link
- ESP-IDF Version: v5.4.1
- Average Power Draw: ~0.05A
- Communication Protocol: BLE (Bluetooth Low Energy)
- Storage: NVS (Non-Volatile Storage) for settings persistence
The project builds from the command line only. There is no VSCode extension workflow.
- Install dependencies and clone ESP-IDF v5.4.1 to
~/esp/v5.4.1/esp-idf:make install-deps
- Set up the ESP-IDF tools for the esp32c3 target:
Run this once per machine (not once per shell β
make setup-esp-idf
make buildandmake cleansource ESP-IDF'sexport.shfor you on every run).
Each release includes pgpemu-firmware-<tag>.zip (app, bootloader, partition
table). It is secret-free: you must build and flash your own NVS image from
your secrets.csv. See FLASHING.md inside the zip.
make build # compiles the firmware, does not flash
make clean # removes the build directory
make flash # builds, then flashes over the ESP32-C3's built-in USB-JTAG
make monitor # opens the serial monitor
make run # flash + monitor in one go
make test # runs the PC unit test suite
make format # runs clang-format on the firmware sourcesmake flash uses OpenOCD over the ESP32-C3's built-in USB-JTAG adapter β
the same transport as the VSCode extension's "JTAG" flash method, not
esptool over plain serial.
make monitor and make run default to port /dev/tty.usbmodem101.
Override it if your device enumerates elsewhere:
make monitor PORT=/dev/tty.usbmodemXXXXWarning
This fork only allows one secret per flashed device (statically loaded). Other forks allow dynamic secret loading via serial ports, but this implementation prioritizes simplicity and reliability.
- Extract secrets from your original Pokemon Go Plus device (see References)
- Rename secrets.example.csv to secrets.csv
- Update the values following the schema:
# Secrets options # this section is for secrets only
key,type,encoding,value # /!\ do not edit this line
pgpsecret,namespace,, # /!\ do not edit this line
name,data,string,"PKLMGOPLUS" # <- the name of your device
mac,data,hex2bin,7c.......... # <- the mac address you've extracted - should be 12 char long
dkey,data,hex2bin,9b......... # <- the device key you've extracted - should be 32 char long
blob,data,hex2bin,16......... # <- the blob data you've extracted - should be 512 char long
# Settings options # this section is for flash-time settings, it can be overridden later from the uart menu
user_settings,namespace,, # /!\ do not edit this line
catch,data,i8,1 # enable autocatch - 1 = yes, 0 = no
spin,data,i8,1 # enable autospin - 1 = yes, 0 = no
button,data,i8,1 # enable input button on pokemon encounter - 1 = yes, 0 = no
llevel,data,i8,2 # esp monitor log level - 1 = debug, 2 = info, 3 = verbose
maxcon,data,u8,2 # max allowed bluetooth connection to the device, up to 4After updating secrets.csv, rebuild and flash your device.
Configuration and diagnostics are done from the PGPemu Companion Android
app (companion-app/) over BLE β the on-device serial menu has been
removed.
Download the signed APK (pgpemu-companion-<tag>.apk) from the latest
GitHub Release and install it,
or build and install from source:
- Open
companion-app/in Android Studio (or run Gradle directly:cd companion-app && ./gradlew installDebug). - Requires Android 9 (API 28) or newer.
- Install to a phone with Bluetooth Low Energy support.
- Power on the PGPemu device.
- Open the app and tap Connect β it scans for the device, connects over BLE, and completes bonding automatically.
- Once connected, the screen switches from the Connect button to Disconnect and the sections below become visible.
The app is a single scrolling screen, ordered by risk/frequency:
- Status β read-only: LED state, advertising state, active connection count, log level.
- Device Profiles β one row per device slot (0-3), each with Autospin and Autocatch toggle chips.
- Settings β advertising on/off switch, max-connections stepper, a Cycle log level button, and Save settings to persist to flash.
- Diagnostics β on-demand dumps (runtime stats, FreeRTOS task list, BLE client states), each with its own Refresh button, plus Disconnect all clients.
- Danger Zone β Reset secrets and Restart device. Both open a
confirmation sheet that names the affected device and requires typing
RESETorRESTARTbefore the action is enabled β these are silent, irreversible BLE operations with no undo.
Pokemon GO App (Smartphone)
β
BLE GATT
β
ESP32-C3
βββββββββββββββββββββββββββββββββββββββββββββ
β β
β BLE GATT Server (pgp_gatts.c) β
β ββ Handles Pokemon Go protocol β
β β
ββ Connection Management (pgp_handshake_multi.c)
β ββ Tracks up to 4 simultaneous devices β
β ββ Session key caching β
β β
ββ Settings & Storage (config_storage.c) β
β ββ Per-device settings β
β ββ Session key persistence β
β ββ NVS (flash storage) β
β β
ββ Feature Tasks β
β ββ LED Handler (pgp_led_handler.c) β
β ββ Auto Button (pgp_autobutton.c) β
β β
ββ System Services β
ββ Bluetooth GAP (pgp_gap.c) β
ββ Statistics (stats.c) β
β
ββ Serial Monitor (console output)
-
Connection Establishment:
- Pokemon Go app scans for PGPemu
- Initiates BLE GATT connection (pgp_gatts.c)
- Device handshake (pgp_handshake_multi.c)
- Full handshake with passkey on first connection (~3-5 seconds)
- Session key caching to NVS (config_storage.c)
-
Reconnection:
- Pokemon Go reconnects to previously paired device
- Session key retrieved from NVS
- Expedited handshake (~1 second, no passkey needed)
-
Settings Management:
- Each connection tracked by
conn_id - Per-device
DeviceSettingsstructure in RAM - Settings persisted to NVS by device MAC address
- Settings loaded on device connection
- Each connection tracked by
-
Feature Triggering:
- LED patterns detected (pgp_led_handler.c)
- Pattern triggers auto actions (pgp_autobutton.c)
- Multi-device connection tracking - manages up to 4 simultaneous connections
- State machine - tracks cert_state, recon_key (reconnect key), notify state
- Connection mapping - maps BLE connection IDs to device indices
- Mutual exclusion - protects active_connections counter with mutex
- Session key persistence - reads/writes from NVS by MAC address
- Device settings storage - FNV-1a hash for key generation
- NVS helpers - wrapper functions with error checking
- Mutex protection - settings_mutex for thread-safe access
- GATT characteristic handlers - reads/writes from Pokemon Go
- Handshake protocol - certificate exchange, key negotiation
- Notification system - sends data to Pokemon Go
- LED pattern parsing - recognizes game state from LED colors
- Action triggers - calls auto functions based on patterns
- Automatic actions - simulates button presses, changes settings
- Per-device settings - operates on specific device's settings
- Timing logic - randomized delays for natural behavior
pgpemu/
βββ pgpemu-esp32/ # ESP32-C3 Firmware
β βββ main/
β β βββ Core Connection Management:
β β β βββ pgp_handshake_multi.c(.h) # Multi-device connection tracker
β β β βββ pgp_gatts.c(.h) # BLE GATT server
β β β βββ pgp_handshake.c(.h) # Encryption/decryption
β β β
β β βββ Settings & Storage:
β β β βββ config_storage.c(.h) # NVS persistence, device settings
β β β βββ config_secrets.c(.h) # Secret management
β β β βββ settings.c(.h) # Settings logic
β β β βββ nvs_helper.c(.h) # NVS utilities
β β β
β β βββ Features:
β β β βββ pgp_led_handler.c(.h) # LED pattern β action
β β β βββ pgp_autobutton.c(.h) # Button press simulation
β β β βββ button_input.c(.h) # GPIO handler
β β β
β β βββ Communication:
β β β βββ pgp_bluetooth.c(.h) # BLE initialization
β β β βββ pgp_gap.c(.h) # BLE device discovery
β β β βββ pgp_cert.c(.h) # Certificate handling
β β β
β β βββ System:
β β β βββ pgpemu.c # Main entry point
β β β βββ stats.c(.h) # Statistics tracking
β β β βββ log_tags.c(.h) # Log definitions
β β β
β β βββ pc/ # PC Unit Tests (all tests pass)
β β βββ test_regression.c # 46 assertions - Critical bugs
β β βββ test_edge_cases.c # 76 assertions - Boundaries
β β βββ test_error_handling.c # Error recovery
β β βββ test_settings.c # Settings logic
β β βββ test_handshake_multi.c # Multi-device connections
β β βββ test_config_storage.c # NVS persistence
β β βββ test_nvs_helper.c # NVS utilities
β β βββ run_tests.sh # Test runner script
β β
β βββ CMakeLists.txt # Build configuration
β βββ sdkconfig # ESP-IDF configuration (v5.4.1)
β βββ secrets.example.csv # Template for device secrets
β βββ .clang-format # Code style configuration
β βββ .clang-tidy # Static analysis rules
β βββ Makefile.test # Test compilation rules
β
βββ scripts/ # Helper scripts
β βββ setup.fish # Fish shell setup script
β
βββ README.md # This file - Single source of truth
βββ MANUAL_TESTING_GUIDE.md # β οΈ Deprecated - Content merged into README.md
βββ TEST_SUITE_GUIDE.md # β οΈ Deprecated - Content merged into README.md
βββ FUNDING.yml # GitHub funding info
βββ LICENSE # Project license
βββ run_tests.sh # Root-level test runner
βββ Makefile # Root-level build helpers
The PGPemu codebase is organized in distinct layers with clear separation of concerns:
button_input.c- GPIO input handling- Direct interaction with ESP32-C3 hardware
pgp_gap.c- BLE device discoverypgp_bluetooth.c- BLE initializationpgp_cert.c- Certificate handling
pgp_handshake_multi.c- Multi-device trackingpgp_gatts.c- GATT protocol implementationpgp_handshake.c- Encryption/decryption
config_storage.c- NVS operationsconfig_secrets.c- Secret managementsettings.c- Settings logicnvs_helper.c- NVS utility functions
pgp_led_handler.c- LED pattern recognitionpgp_autobutton.c- Button automation
pgpemu.c- Main entry and task initializationstats.c- Statistics collectionlog_tags.c- Log tag definitions
pgpemu.c (main)
ββ pgp_bluetooth.c
β ββ pgp_gap.c
β ββ pgp_gatts.c
β ββ pgp_handshake_multi.c
β β ββ config_storage.c
β β ββ nvs_helper.c
β ββ pgp_handshake.c
β ββ pgp_cert.c
β
ββ pgp_led_handler.c
β ββ pgp_autobutton.c
β ββ settings.c
β ββ config_storage.c
β
ββ stats.c
# Run all tests
./run_tests.sh
# Expected output: All 260 assertions pass in ~2 seconds| Test Module | Assertions | Coverage | Status |
|---|---|---|---|
| test_regression.c | 42 | Critical bug prevention | β PASS |
| test_edge_cases.c | 71 | Boundary conditions | β PASS |
| test_settings.c | 20 | Settings logic | β PASS |
| test_config_storage.c | 37 | NVS persistence | β PASS |
| test_handshake_multi.c | 18 | Multi-device connections | β PASS |
| test_nvs_helper.c | 23 | NVS utilities | β PASS |
| test_error_handling.c | 37 | Error recovery & resilience | β PASS |
| cert-test.c | 4 | Certificate validation | β PASS |
| TOTAL | 260 | 100% coverage | 100% PASS |
This comprehensive test suite contains 260 total assertions across 8 test modules covering critical functionality, edge cases, error handling, and certificate validation.
Bug #1: Device Settings Pointer Initialization
- Issue:
read_stored_device_settings()return value wasn't checked - Test: Verify return value checking prevents NULL dereference
- Impact: Critical - prevents crashes on device connect
Bug #2: Settings Mutex Memory Leak
- Issue: New mutex created but never freed in device settings
- Test: Track malloc/free counts, verify balanced calls
- Impact: Critical - prevents memory exhaustion with connections
Bug #3: Race Condition on active_connections
- Issue: Connection counter modified without mutex protection
- Test: Verify mutex acquire/release around counter operations
- Impact: Critical - ensures accurate connection tracking
Bug #4: Stale Session Cache Clearing
- Issue: Cached session keys become invalid after Android credential reset, causing reconnection failures
- Test: Verify early disconnect detection (< 5s, cached session, reason 0x13) triggers cache clearing
- Impact: Critical - prevents persistent connection failures requiring manual intervention
Bug #6: Invalid MAC Address (BDA) Handling
- Issue: No validation of device Bluetooth addresses
- Test: Reject NULL, all-zeros, all-ones MAC addresses
- Impact: High - prevents operations on malformed addresses
- Maximum Connections: Accept 1-4 devices, reject 5th
- Probability Values: Valid 0-9, invalid 10+
- Array Indices: Valid 0-3, reject negative or 4+
- NVS Key Length: Valid 1-15 chars, invalid empty or 16+
- Boolean Toggle Logic: Repeated toggles, NULL handling
- NVS read errors with defaults
- malloc failure recovery
- Mutex creation failures
- Data validation and sanitization
# Run all tests
./run_tests.sh
# Run specific test module
./run_tests.sh test_regression
./run_tests.sh test_edge_cases
./run_tests.sh test_error_handling
# Manual compilation
cd pgpemu-esp32/main/pc
gcc -o test_regression test_regression.c && ./test_regression
gcc -o test_edge_cases test_edge_cases.c && ./test_edge_cases
gcc -o test_error_handling test_error_handling.c && ./test_error_handling
# Certificate validation test
gcc -o cert-test cert-test.c aes.c && ./cert-testWhen adding new features:
- Run existing tests to verify no regressions
- Identify tested boundary values in edge_cases.c
- Add new test cases for new edge cases
- Update regression tests if feature affects critical paths
When fixing bugs:
- Create a regression test in test_regression.c
- Document what was broken and how the test catches it
- Ensure all 267 assertions pass
- Commit with reference to the bug fix
When modifying core logic:
- Run full test suite:
./run_tests.sh - Verify your changes are covered by tests
- Add edge case tests for new boundary values
- Check error handling paths still work
After PC unit tests pass (all 260 assertions), use these procedures to validate features on actual hardware.
- ESP32 device flashed with latest PGPemu firmware
- Pokemon Go app installed on smartphone
- Bluetooth-capable smartphone
- Serial monitor (e.g.,
make monitor) - Optional: 2-4 additional devices for multi-device testing
make buildWatch for compilation errors. Known issues:
- TickType_t format specifier warnings in pgp_handshake_multi.c (non-critical on ESP32)
make flash # builds, then flashes over the built-in USB-JTAGmake monitor # or: make run (flash + monitor)Keep this running during all test procedures to observe device behavior.
Objective: Verify that a device can reconnect without re-entering the passphrase.
Setup:
- Flash device and open serial monitor
- Let device boot and show startup logs
- Open Pokemon Go app
Procedure:
-
Initial Connection:
- Scan for PGPemu in Pokemon Go Bluetooth settings
- Select PGPemu device (should be named Pokemon Go Plus)
- Observe serial logs showing successful handshake (approx 3-5 seconds)
- Connection should establish and device should show "connected" status
-
Disconnect:
- In Pokemon Go, disconnect from PGPemu
- Wait 2 seconds
- Observe device logs: you should see "connection_stop" message
-
Reconnect:
- In Pokemon Go, reconnect to PGPemu (don't scan for new device, select previously paired)
- DO NOT enter passphrase - device should use cached session key
- Observe serial logs showing expedited handshake (< 1 second)
- Connection should establish without passphrase prompt
Expected Outcome:
- First connection: Full handshake with passphrase, ~3-5 seconds
- Second connection: Session key reuse, ~1 second, NO passphrase needed
- Serial output shows:
[handshake] Attempting to retrieve cached session keys... [handshake] Session keys found in NVS, skipping full handshake
Pass Criteria: β Device reconnects in <2 seconds without passphrase
Objective: Verify that session keys persist after device power cycle.
Setup:
- Complete Test 2.1 successfully
- Device should be connected
Procedure:
-
Establish and Record State:
- Verify device is connected with cached session key
- Note the device's MAC address from logs (format:
AA:BB:CC:DD:EE:FF)
-
Power Cycle Device:
- Disconnect from Pokemon Go
- Unplug device
- Wait 5 seconds
- Plug device back in
- Wait for device to boot (observe startup logs)
-
Verify Cache Persistence:
- Open Pokemon Go
- Reconnect to PGPemu
- Should reconnect without passphrase in <2 seconds
- Check NVS logs showing cache retrieval
Expected Outcome:
- Session key remains in NVS storage after power cycle
- Reconnection succeeds without passphrase
- Serial logs show cache hit after boot
Pass Criteria: β Session persists across power cycles, no passphrase required
Objective: Verify that cached keys are device-specific (MAC-based).
Setup:
- Device A flashed and tested (has cached session keys)
- Device B (different device, different MAC address) flashed
Procedure:
-
Attempt Cross-Device Reconnection:
- Device A previously paired and cached
- Attempt to use cached key from Device A on Device B (not possible directly, but test MAC isolation)
- If possible in setup, swap firmware images to verify device isolation
-
Verify Device B Requires Passphrase:
- Device B should not have cached keys
- Should prompt for passphrase on first connection
- Handshake should be full (~3-5 seconds)
Expected Outcome:
- Each device maintains independent cache
- Device MAC address is used to generate cache key
- Different MACs don't share cached session keys
Pass Criteria: β Devices remain isolated, each requires independent passphrase
Objective: Verify that device settings persist independently.
Setup:
- Single device flashed and connected
- Serial monitor active
Procedure:
-
Configure Device Settings via UART:
- Open serial monitor
- Send UART command:
0s(toggle autospin for device 0) - Observe logs showing toggle and NVS write
-
Disconnect and Reconnect:
- Disconnect from Pokemon Go
- Wait 2 seconds
- Reconnect to Pokemon Go
-
Verify Settings Loaded:
- Connected device should show saved autospin state
- Check serial logs confirming loaded settings
- Settings should match what was configured in step 1
-
Modify and Verify Again:
- Send:
0c(toggle autocatch) - Disconnect/reconnect
- Autocatch state should persist
- Send:
Expected Outcome:
- Settings saved to NVS after each UART command
- Settings loaded when device connects
- Persists across disconnect/reconnect cycles
Pass Criteria: β All device settings persist independently
Objective: Verify that 4 devices can have different settings simultaneously.
Setup:
- 4 devices flashed (or simulate with controlled connections)
- All devices within range
Procedure:
-
Initial Configuration:
- Device 0: Autospin ON, Probability 5
- Device 1: Autospin OFF, Probability 3
- Device 2: Autospin ON, Probability 8
- Device 3: Autospin OFF, Probability 1
Configure via UART:
0s (toggle autospin device 0, becomes ON) 0p5 1s (toggle autospin device 1, becomes OFF) 1p3 2p8 3s (toggle autospin device 3, becomes OFF) 3p1 -
Verify Independent Operation:
- Connect Device 0 to Pokemon Go β autospin ON, prob=5
- Disconnect, connect Device 1 β autospin OFF, prob=3
- Each device should show its own settings
- Check serial logs confirming per-device loading
-
Simultaneous Multi-Device:
- Connect up to 4 devices simultaneously to Pokemon Go
- Each device should operate with its independent settings
- No cross-contamination of settings between devices
Expected Outcome:
- Each device slot maintains independent settings
- Settings loaded correctly when device connects
- No interference between simultaneous connections
Pass Criteria: β 4 devices operate simultaneously with independent settings
Objective: Verify toggling one device's settings doesn't affect others.
Setup:
- 2+ devices connected
- Device 0: Autospin ON
- Device 1: Autospin OFF
Procedure:
-
Record Initial States:
- Observe device 0 autospin: ON
- Observe device 1 autospin: OFF
-
Toggle Device 0:
- Send UART:
0s(toggle device 0 autospin OFF) - Observe: Device 0 now OFF
- Send UART:
-
Verify Device 1 Unchanged:
- Check device 1 autospin status: should still be OFF
- Verify logs show no changes to device 1
-
Toggle Device 1:
- Send UART:
1s(toggle device 1 autospin ON) - Verify device 0 still OFF, device 1 now ON
- Send UART:
Expected Outcome:
- Settings mutations are device-specific
- No cross-device interference
- Each device maintains independent state
Pass Criteria: β Settings changes isolated between devices
Objective: Verify full feature set with 4 devices.
Setup:
- 4 devices available
- Each configured with unique settings:
- Device 0: Autospin ON, Autocatch OFF, Prob=2
- Device 1: Autospin OFF, Autocatch ON, Prob=5
- Device 2: Autospin ON, Autocatch ON, Prob=8
- Device 3: Autospin OFF, Autocatch OFF, Prob=0
Procedure:
-
Configure All Devices:
0s # Device 0: autospin ON 0p2 1s # Device 1: autospin OFF 1c # Device 1: autocatch ON 1p5 2p8 3s # Device 3: autospin OFF 3c # Device 3: autocatch OFF
-
Simultaneous Connection:
- Connect all 4 devices to Pokemon Go simultaneously
- Verify all connect without passphrase (cached keys)
- Settings loaded correctly for each
-
Mixed Operations:
- Device 0 autospin ON (prob=2)
- Device 2 autocatch ON (prob=8)
- Verify other devices unaffected
- Each device operates independently
-
Disconnect One:
- Disconnect Device 1
- Active connections count: 3
- Remaining devices continue operating
-
Reconnect:
- Reconnect Device 1
- Uses cached session key
- Settings loaded: autocatch ON, prob=5
- No passphrase required
Expected Outcome:
- 4 devices with independent settings
- All share session persistence feature
- Settings survive reconnections
Pass Criteria: β Full feature integration works with 4 devices
Objective: Verify stability during rapid connection cycles.
Procedure:
- Connect device
- Immediately disconnect (< 1 second)
- Immediately reconnect
- Repeat 5 times
- Observe no crashes or memory leaks
Expected Outcome:
- Device handles rapid reconnects gracefully
- No log errors or crashes
- Session keys not corrupted
Pass Criteria: β Device stable under rapid reconnects
Objective: Verify 4-device limit enforcement.
Procedure:
- Connect 4 devices
- Attempt to connect 5th device
- Observe connection rejection
Expected Outcome:
- 5th device fails to connect
- Clear error message in logs
- Connected 4 devices unaffected
Pass Criteria: β Max connection limit enforced
Objective: Verify stability over extended runtime.
Procedure:
- Connect device
- Let device run for 1 hour with normal Pokemon Go usage
- Monitor serial logs for errors
- Observe NVS operations
Expected Outcome:
- No memory leaks
- No NVS corruption
- Settings remain consistent
Pass Criteria: β Device stable over long sessions
Use this checklist to track manual testing progress:
- Test 2.1: Single device reconnection without passphrase
- Test 2.2: Session key persistence across power cycle
- Test 2.3: MAC address mismatch detection
- Test 3.1: Device settings persistence
- Test 3.2: Multi-device independent settings
- Test 3.3: Settings isolation
- Test 4.1: Four devices with different settings
- Test 5.1: Rapid disconnect/reconnect
- Test 5.2: Connection at max capacity
- Test 5.3: Very long sessions
ESP-IDF not found
- Ensure ESP-IDF v5.4.1 is installed at
~/esp/v5.4.1/esp-idf(make install-deps) make buildandmake cleansourceexport.shfor you; run. ~/esp/v5.4.1/esp-idf/export.shyourself only when callingidf.pydirectly (e.g. to flash)- Check:
echo $IDF_PATHshould point to esp-idf directory
Compilation errors in pgp_handshake_multi.c
- TickType_t warnings are non-critical on ESP32
- These come from FreeRTOS headers and don't affect functionality
Tool not found (gcc, make, etc.)
- Run
make setup-esp-idfonce to install the toolchain for esp32c3 - Ensure ESP-IDF tools are in PATH:
. $IDF_PATH/tools/tools.sh
Device not found
- Verify USB cable is working (try another port) β data-only cables won't expose JTAG
- Check device shows up:
ls /dev/tty*orls /dev/cu.* make flashuses the built-in USB-JTAG (OpenOCD), no port argument neededmake monitor/make runneed the serial port:make monitor PORT=/dev/tty.usbmodemXXXX- Try different USB port on computer
Permission denied when flashing
- Linux/Mac: Add your user to dialout group:
sudo usermod -a -G dialout $USER - Windows: Run command prompt as administrator
- Reconnect device after permission changes
Flash timeout / OpenOCD can't find the device
- Ensure power supply can handle device: ~0.05A average
- Try a different USB port (some ports have current limiting)
- Only one process can hold the JTAG adapter at a time β close any other OpenOCD/debugger session first
Device not appearing in Pokemon Go scan
- Check serial logs for BLE advertisement messages
- Run
make monitorand look for "advertising" messages - Verify Bluetooth is enabled on phone
- Try restarting Pokemon Go app
- Check device isn't at max connection limit (use
b1to reset)
Connection fails with passphrase
- Verify correct passphrase: 000000 (six zeros)
- Check handshake completes in serial logs (3-5 second timeout)
- Ensure device isn't in mode with suspended advertising
- Try unplugging and re-plugging device
Session key not cached
- Check NVS partition has space (look for NVS error logs)
- Verify handshake completes fully (don't disconnect during handshake)
- Look for "persist_device_session_keys" messages in logs
- Try full NVS erase and re-flash:
idf.py erase-flash
Settings not loaded on reconnect
- Verify device MAC address is correct in logs
- Check NVS key generation (should be 15 hex characters)
- Ensure
read_stored_device_settings()called on connect - Verify NVS partition has settings data (use
xscommand)
Device is slow to respond
- Check log level isn't set to DEBUG (too much logging slows device)
- Reduce number of simultaneous connections if possible
- Verify no other tasks are blocking (check with
tcommand) - Check power supply voltage (low voltage slows device)
Autocatch/Autospin delayed
- Verify LED handler task is running (look for LED parse logs)
- Check FreeRTOS task list with
tcommand
Devices interfere with each other
- Verify conn_id_map not corrupting (check
bsoutput) - Ensure each device has independent DeviceSettings struct
- Check mutex protects device settings (look for "acquiring mutex" logs)
- Try reducing max connections (
b2to limit to 2 devices)
One device's settings affect another
- Confirm device MAC addresses are unique in logs
- Check NVS keys are generated correctly (should differ per MAC)
- Verify settings mutex isn't holding lock too long
- Try clearing all connections (
br) and reconnecting
Connection limit not enforced
- Check max connection setting (use
sto view) - Verify
b[1,4]command works to change limit - Look for "max connections reached" message in logs
- Ensure 5th device actually attempts connection (not blocked elsewhere)
Serial menu not responding
- Check baud rate is correct (usually 115200)
- Try pressing Enter key a few times to wake device
- Verify correct serial port selected
- Check terminal emulator isn't in read-only mode
- Restart device with
Rcommand
Commands not recognized
- Commands are case-sensitive
- Ensure no extra spaces in command
- Verify full command sent (e.g.,
0p5not just0p) - Check device is responding to other commands first
Settings not saving
- Use
Scommand to explicitly save settings - Note: Settings auto-save on change,
Smay not be needed - Verify NVS is not full (would prevent writes)
- Check error messages in logs for NVS failures
- Language: C99
- Formatting: Use
clang-formatwith provided.clang-formatconfig - Naming conventions:
- Functions:
snake_case - Constants:
UPPER_CASE - Private functions: prefix with underscore
_private_function() - Type names:
snake_case_tfor typedef structs
- Functions:
-
Run all tests and ensure 100% pass rate:
./run_tests.sh # Must show: All 260 assertions passed -
Format your code:
cd pgpemu-esp32 clang-format -i main/*.c main/*.h
-
Run static analysis:
clang-tidy main/*.c -- -I./main -
Build the project:
make build
-
No warnings or errors should appear in build output
Use the following format for commit messages:
<type>: <subject>
<body>
<footer>
Types:
feat: A new featurefix: A bug fixrefactor: Code refactoring without feature changestest: Adding or updating testsdocs: Documentation updateschore: Build system, dependencies, etc.
Examples:
feat: Add per-device connection limit setting
Add ability to set the maximum number of simultaneous Bluetooth
connections (1-4) from the UART menu. Settings are persisted to
NVS and survive power cycles.
Fixes: #45
fix: Prevent NULL dereference in read_stored_device_settings
Check return value from read_stored_device_settings() before
dereferencing the result. This prevents crashes when loading
device settings fails.
Includes regression test to prevent reoccurrence.
test: Add comprehensive test suite with 267 assertions
- test_regression.c: 46 assertions for critical bugs
- test_edge_cases.c: 76 assertions for boundaries
- test_settings.c: 26 assertions for settings logic
- test_config_storage.c: 37 assertions for NVS persistence
- test_handshake_multi.c: 18 assertions for multi-device connections
- test_nvs_helper.c: 23 assertions for NVS utilities
- test_error_handling.c: 37 assertions for error recovery
- cert-test.c: 4 assertions for certificate validation
All tests pass on PC without hardware.
- Identify the layer where your feature belongs (see Code Organization)
- Add unit tests first in the appropriate test file
- Implement the feature to pass the tests
- Run full test suite to ensure no regressions
- Update documentation if adding new user-facing functionality
- Manual test on hardware following Manual Testing Guide
- Commit with proper message following format above
- Define the setting in
settings.cwith initialization and toggle logic - Add to
DeviceSettingsstruct insettings.hif per-device - Add persistence to
config_storage.cif needs to survive power cycle - Add UART command in
uart.cfor user configuration - Add test cases in
test_settings.candtest_edge_cases.c - Document in serial menu help text (uart.c)
- Update Configuration section in this README
- https://github.com/yohanes/pgpemu - Original PGPEMU implementation
- https://github.com/spezifisch/pgpemu - Spezifisch's fork
- https://github.com/paper183/pgpemu - Paper183's fork
- https://www.reddit.com/r/pokemongodev/comments/5ovj04/pokemon_go_plus_reverse_engineering_write_up/
- https://www.reddit.com/r/pokemongodev/comments/8544ol/my_1_gotcha_is_connecting_and_spinning_catching_2/
- https://tinyhack.com/2018/11/21/reverse-engineering-pokemon-go-plus/
- https://tinyhack.com/2019/05/01/reverse-engineering-pokemon-go-plus-part-2-ota-signature-bypass/
- https://coderjesus.com/blog/pgp-suota/ - Most comprehensive write-up
- https://github.com/Jesus805/Suota-Go-Plus - Bootloader exploit + secrets dumper
- ESP-IDF v5.4.1 Documentation
- ESP32-C3 Getting Started Guide
- NVS (Non-Volatile Storage) API
- BLE API Reference
This project is licensed under LICENSE.