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 Requirements
- 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
- 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
- Retoggle feature - automatically disable/enable autocatch or autospin when certain conditions occur (bag full, box full, etc.)
- 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
- 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
- Serial Interface: USB UART for configuration menu
You need ESP-IDF v5.4.1 installed. See the get started with ESP-IDF for ESP32-C3 guide.
- Install the VSCode ESP-IDF extension
- Open the folder ./pgpemu-esp32 as a new project in VSCode
- Configure the extension:
- Shift+⌘+P > ESP-IDF: Select Port to Use > choose your device (e.g.
/dev/tty.usbmodem101) - Shift+⌘+P > ESP-IDF: Set Espressif Device Target >
esp32c3 - Shift+⌘+P > ESP-IDF: Select Flash Method >
JTAG - Shift+⌘+P > ESP-IDF: Build, Flash and Monitor
- Shift+⌘+P > ESP-IDF: Select Port to Use > choose your device (e.g.
cd pgpemu-esp32
# Build the project
idf.py build
# Flash to device (adjust port as needed)
idf.py flash -p /dev/ttyUSB0 -b 921600
# Monitor serial output
idf.py monitor -p /dev/ttyUSB0Warning
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
spinp,data,u8,0 # spin probability out of 10, low storage capacity can lead to full bag issues, this allows spinning to be enabled while aiming to reduce chances of full bags - 0 = spin everything, 1 to 9 = N/10
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.
---HELP---
Secrets: PKLMGOPLUS
User Settings:
- as - toggle autospin
- a[0,9] - autospin probability
- ac - toggle autocatch
- l - cycle through log levels
- S - save user settings permanently
Commands:
- ? - help
- r - show runtime counter
- t - show FreeRTOS task list
- s - show all configuration values
- R - restart
Hardware:
- B - toggle input button
Secrets:
- xs - show loaded secrets
- xr - reset loaded secrets
Bluetooth:
- bA - start advertising
- ba - stop advertising
- bs - show client states
- br - clear connections
- b[1,4] - set maximum client connections (e.g. 3 clients max. with 'b3', up to 4)
---SETTINGS---
- Autospin: on
- Autospin probability: 4
- Autocatch: on
- Input button: on
- Log level: 2
- Connections: 1 / 2
---STATS---
Connection 0:
- Caught: 7
- Fled: 4
- Spin: 0
---STATS---
Connection 1:
- Caught: 35
- Fled: 24
- Spin: 60
I (1716661) pgp_handshake: active_connections: 1
I (1716661) pgp_handshake: conn_id_map:
I (1716661) pgp_handshake: 0: ffff
I (1716661) pgp_handshake: 1: ffff
...
Format: <device_idx><command>[<value>]
<device_idx>: 0-3 for device 0-3<command>:s: Toggle autospin for devicec: Toggle autocatch for devicep<value>: Set autospin probability (0-9)l: Cycle log level for device
Examples:
0s → Toggle autospin on device 0
1c → Toggle autocatch on device 1
2p5 → Set device 2 probability to 50%
0l → Cycle log level for device 0
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) │
│ └─ Auto Setting (pgp_autosetting.c) │
│ │
├─ Serial Interface (uart.c) │
│ └─ Configuration menu │
│ │
└─ 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, pgp_autosetting.c)
- Retoggle feature temporarily disables/enables features
- 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
- Retoggle queue - schedules setting changes
- 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
│ │ │ ├── pgp_autosetting.c(.h) # Retoggle feature
│ │ │ └── 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
│ │ │ └── uart.c(.h) # Serial menu interface
│ │ │
│ │ ├── 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
│ ├── monitor.fish # Fish shell monitor script
│ └── 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 handlinguart.c- Serial communication- 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 automationpgp_autosetting.c- Setting changes (retoggle)
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
│ └─ pgp_autosetting.c
│ └─ settings.c
│ └─ config_storage.c
│
├─ uart.c
│ └─ settings.c
│ └─ config_secrets.c
│
└─ stats.c
# Run all tests
./run_tests.sh
# Expected output: All 267 assertions pass in ~2 seconds| Test Module | Assertions | Coverage | Status |
|---|---|---|---|
| test_regression.c | 46 | Critical bug prevention | ✓ PASS |
| test_edge_cases.c | 76 | Boundary conditions | ✓ PASS |
| test_settings.c | 26 | 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 | 267 | 100% coverage | 100% PASS |
This comprehensive test suite contains 267 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 #5: Uninitialized Retoggle Fields
- Issue: Device retoggle state fields not initialized to zero
- Test: Verify all fields initialized to false/0 at startup
- Impact: High - prevents undefined behavior from garbage values
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+
- Timing Boundaries: Handle time 0 and max uint32
- 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 267 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.,
idf.py monitor) - Optional: 2-4 additional devices for multi-device testing
cd pgpemu-esp32
idf.py buildWatch for compilation errors. Known issues:
- TickType_t format specifier warnings in pgp_handshake_multi.c (non-critical on ESP32)
idf.py flash -p /dev/ttyUSB0 -b 921600 # Adjust port as neededidf.py monitor -p /dev/ttyUSB0Keep 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
- Send UART command:
0p5(set autospin probability to 50%) - Observe probability set in logs
-
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 autospin is toggled OFF then ON when bag is full.
Setup:
- Device connected to Pokemon Go
- Pokemon Go app running with autospin enabled
- Fill bag in game (triggers white LED on device)
Procedure:
-
Normal Operation:
- Play normally, autospin toggling Pokemon
- Observe device LED changes
-
Trigger Bag Full:
- Collect items until bag is full
- Pokemon Go app will send bag-full signal
- Device shows white LED indicator
-
Observe Retoggle:
- Device receives LED pattern
- Autospin toggles OFF (stops catching)
- After 300ms, autospin toggles back ON
- Behavior: Device stops catching momentarily, then resumes
- Check serial logs showing retoggle queue entry/processing
Expected Outcome:
- White LED triggers autospin OFF → ON sequence
- 300ms delay between OFF and ON
- Retoggle only affects the connected device
- Other devices (if connected) unaffected
Pass Criteria: ✓ Autospin retoggle correctly handles bag full
Objective: Verify autocatch is toggled when item box is full.
Setup:
- Device connected to Pokemon Go
- Autocatch enabled
- Fill item box in game
Procedure:
-
Fill Item Box:
- Collect items until item box is full
- Pokemon Go sends box-full signal
- Device shows red solid LED
-
Observe Retoggle:
- Device receives LED pattern
- Autocatch toggles OFF (stops catching items)
- After 300ms, autocatch toggles back ON
- Observe device behavior: stops catching momentarily, resumes
-
Verify Persistence:
- Disconnect and reconnect device
- Retoggle settings should be cleared (not persistent on reconnect)
Expected Outcome:
- Red solid LED triggers autocatch OFF → ON
- 300ms delay
- Only affects current device
- Retoggle clears after device disconnects
Pass Criteria: ✓ Autocatch retoggle correctly handles box full
Objective: Verify autocatch toggles when pokéballs empty.
Setup:
- Device connected with autocatch enabled
- Use up all pokéballs in game
Procedure:
-
Empty Pokéballs:
- Use all pokéballs catching Pokemon
- Pokemon Go sends pokéballs-empty signal
- Device shows red blinking LED
-
Observe Retoggle:
- Autocatch toggles OFF
- After 300ms, toggles back ON
- Behavior: Device stops catching, resumes
-
Multi-Device Verification:
- If 2+ devices connected, verify other devices unaffected
- Only the device receiving the signal retoggle
Expected Outcome:
- Red blinking LED triggers retoggle
- Isolated to receiving device
- Settings return to previous state after 300ms
Pass Criteria: ✓ Pokéballs empty retoggle works correctly
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:
- Bag fills on Device 0 → only autospin retoggle
- Pokéballs empty on Device 2 → only autocatch retoggle
- 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
- Retoggle isolated per device
- 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: Bag full retoggle
- Test 4.2: Box full retoggle
- Test 4.3: Empty pokéballs retoggle
- Test 5.1: Four devices with different settings
- Test 6.1: Rapid disconnect/reconnect
- Test 6.2: Connection at max capacity
- Test 6.3: Very long sessions
ESP-IDF not found
- Ensure ESP-IDF v5.4.1 is installed
- Run:
. ~/esp/esp-idf/export.shto set up environment - 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.)
- Ensure ESP-IDF tools are in PATH
- Run:
. $IDF_PATH/tools/tools.sh - Or use VSCode extension which manages tools automatically
Device not found
- Verify USB cable is working (try another port)
- Check device shows up:
ls /dev/tty*orls /dev/cu.* - Ensure correct port is specified:
-p /dev/ttyUSB0(adjust as needed) - 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
- Try slower baud rate:
-b 115200(instead of 921600) - Ensure power supply can handle device: ~0.05A average
- Try different USB port (some ports have current limiting)
Device not appearing in Pokemon Go scan
- Check serial logs for BLE advertisement messages
- Run
idf.py 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 retoggle queue isn't stuck (full queue blocks new entries)
- Ensure probability value is correct (0 = always spin/catch)
- Check FreeRTOS task list with
tcommand
Retoggle not working
- Verify LED pattern detection in logs
- Check retoggle queue has space (try
brto clear connections) - Ensure autosetting_task is running (look in task list)
- Verify the correct LED pattern is being detected
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 267 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:
idf.py 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 autocatch probability setting
Add ability to set independent autocatch probability (0-9)
for each connected device. Probabilities 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.