Files
pi-pm3/UPSTREAM_CONTRIBUTIONS.md
michael 4f35df1781 Initial commit - Phase 3/4
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 13:46:22 -08:00

14 KiB

Upstream Contributions Tracker

Project: RfidResearchGroup/proxmark3 (Iceman Fork) Repository: https://github.com/RfidResearchGroup/proxmark3 Purpose: Track fixes and improvements discovered during Dangerous Pi development for potential PR submission


🐛 Bug Fixes

1. Python Bindings - Missing QR Code Source File HIGH PRIORITY

Issue: Python experimental library fails to load with undefined symbol: qrcode_print_matrix_utf8

Root Cause:

  • File: client/experimental_lib/CMakeLists.txt
  • The qrcode/qrcode.c source file is used by the main client but not included in the experimental library's TARGET_SOURCES list
  • This causes undefined symbols when building the Python bindings shared library

Fix Applied:

# File: client/experimental_lib/CMakeLists.txt
# Line: ~434 (after wiegand_formatutils.c)

set (TARGET_SOURCES
    ...
    ${PM3_ROOT}/client/src/util.c
    ${PM3_ROOT}/client/src/wiegand_formats.c
    ${PM3_ROOT}/client/src/wiegand_formatutils.c
+   ${PM3_ROOT}/client/src/qrcode/qrcode.c        # ADD THIS LINE
    ${CMAKE_BINARY_DIR}/version_pm3.c
)

Testing Methodology:

  1. Clone proxmark3 repository
  2. Build experimental library:
    cd client/experimental_lib
    bash 00make_swig.sh
    bash 01make_lib.sh
    
  3. Test Python module:
    cd example_py
    bash 01link_lib.sh
    PYTHONPATH=../../pyscripts python3 -c "import pm3; print('Success!')"
    
  4. Before fix: ImportError with undefined symbol
  5. After fix: Module imports successfully

Impact:

  • Fixes Python bindings for all users
  • Enables native Python integration without subprocess overhead
  • Critical for multi-device applications and automation

Verification:

  • Library builds without errors
  • Python module imports successfully
  • No undefined symbols in shared library
  • Tested on Ubuntu 24.04 with Python 3.12

PR Notes:

  • Small, focused fix (1 line change)
  • Does not break any existing functionality
  • Aligns with existing build structure
  • Should be straightforward to merge

📝 Enhancement Proposals

🔬 Potential Future Contributions

2. LED Control with Hardware PWM Brightness (hw led) PRODUCTION READY

Status: COMPLETE - INTEGRATED INTO DANGEROUS PI Date Completed: November 26, 2025 (basic), December 2, 2025 (PWM enhancement) Ready for Upstream PR: YES

Problem Solved:

  • LED identification previously required workaround: hw tune --lf --duration 50
  • Caused unwanted antenna activity and RF emissions
  • No direct LED control available from client commands
  • Multi-device setups had no way to visually identify which physical device was which
  • No brightness control for nuanced device identification

Implementation: Full-featured LED control with hardware PWM brightness

  • Command: hw led
  • Actions: --on, --off, --blink <pattern>, --brightness <0-100> (NEW!)
  • LED Selection: --led <a|b|c|d|all> (comma-separated for multiple)
  • Blink Patterns:
    • slow (500ms interval)
    • fast (100ms interval)
    • veryfast (50ms interval)
    • custom (user-defined via --interval)
  • PWM Brightness Control (NEW!):
    • Hardware PWM at 48 kHz (flicker-free)
    • 0-100% brightness range
    • LED A and LED B support (PA0/PWM0, PA2/PWM2)
    • Zero CPU overhead (hardware-controlled)
  • Additional Options: --duration, --count, --identify, --clear

Files Modified:

Phase 1 - Basic LED Control:

  • include/pm3_cmd.h - Added CMD_LED_CONTROL (0x011A) and payload_led_control_t struct
  • client/src/cmdhw.c - Implemented CmdLED() function with CLIParser (~170 lines)
  • armsrc/appmain.c - Added firmware handler case (~35 lines)

Phase 2 - PWM Brightness Enhancement:

  • common_arm/ticks.c - Reallocated timing from PWM0→PWM1 (2 functions, 18 lines)
  • common_arm/usb_cdc.c - Reallocated USB timing from PWM0→PWM1 (1 function, 9 lines)
  • armsrc/util.c - Reallocated button timing PWM0→PWM1 + added PWM LED functions (6 lines + 80 lines)
  • armsrc/util.h - Added function declarations (2 lines)
  • armsrc/appmain.c - Updated CMD_LED_CONTROL for PWM mode (action=3)
  • client/src/cmdhw.c - Added --brightness parameter with validation (~30 lines)

🔴 CRITICAL DISCOVERY - PWM Channel Reallocation:

Key Technical Innovation: Timing functions previously monopolized PWM0, blocking LED_A from brightness control.

Solution: Reallocated all timing functions from PWM0 to PWM1:

  • PWM channels 0-3 have identical timer/counter functionality
  • PWM0 usage for timing was software convention, not hardware requirement
  • Moving to PWM1 freed PWM0 for LED_A, enabling 2-LED brightness control
  • Zero performance impact (PWM1 counter works identically to PWM0)

Impact:

  • LED_A (PA0/PWM0) now supports brightness control
  • LED_B (PA2/PWM2) now supports brightness control
  • All timing operations remain functional (verified with hw status)
  • No interference with RF operations

🔴 CRITICAL DISCOVERY - SWIG Rebuild Requirement:

When adding new commands to pm3_cmd.h, the SWIG Python wrapper MUST be rebuilt:

cd client/experimental_lib
./00make_swig.sh
./01make_lib.sh

This requirement is NOT documented in the Proxmark3 build docs and caused significant debugging time. Without rebuilding SWIG:

  • Firmware compiles and works
  • Client compiles and works
  • Python bindings don't recognize the new command (uses stale library)

Recommendation: Document this requirement in Proxmark3 development guide.

🔴 CRITICAL DISCOVERY - PM3 Easy LED Order:

PM3 Easy requires LED_ORDER=PM3EASY flag in Makefile.platform for correct LED pin mapping:

PLATFORM=PM3GENERIC
LED_ORDER=PM3EASY

Without this flag, LED_B maps to PA8 (no PWM) instead of PA2 (PWM2 capable).

Recommendation: This is documented in Makefile.platform.sample but easy to miss. Consider making LED order detection automatic or adding build warning.

Benefits:

  • Safe identification without RF emissions
  • Perfect for multi-device setups (Dangerous Pi use case)
  • Granular control of individual LEDs
  • Multiple blink patterns for status indication
  • Hardware PWM brightness control for nuanced identification
  • Smooth dimming effects (48 kHz, flicker-free)
  • Zero CPU overhead for brightness control
  • Clean integration with SWIG Python wrapper (once rebuilt)
  • Follows Iceman fork conventions (CLIParser, SendCommandNG, reply_ng)
  • Hardware agnostic (works on PM3 Easy, RDV4, etc.)
  • Zero breaking changes
  • Backward compatible (existing commands unchanged)

Testing Results:

  • Tested on Proxmark3 Easy hardware (with LED_ORDER=PM3EASY)
  • All LED colors confirmed: Green (A), Blue (B), Orange (C), Red (D)
  • Individual LED control verified
  • Multiple LED control verified (comma-separated)
  • All blink patterns functional (slow/fast/veryfast/custom)
  • Duration and count parameters working
  • Identify mode working (--identify = fast blink all LEDs 4x)
  • PWM brightness 0-100% verified on LED A and LED B
  • Smooth dimming with no flicker observed
  • Timing operations verified functional after PWM reallocation (hw status)
  • PWM mode switching tested (PWM→on→off→blink)
  • Simultaneous LED A+B brightness control working
  • Pulse/breathing animation demos created and tested
  • Python SWIG integration verified (after rebuild)
  • Integrated into Dangerous Pi device manager

Dangerous Pi Integration: Updated app/backend/managers/pm3_device_manager.py:

# OLD workaround:
# result = await device.worker.execute_command("hw tune --lf --duration 50")

# NEW clean implementation:
result = await device.worker.execute_command(f"hw led --identify --duration {duration_ms}")

Documentation Created:

  • LED_COMMAND_PROPOSAL.md - Original design specification
  • LED_MAPPINGS.md - Hardware-specific LED color mappings
  • LED_MAPPINGS_PM3_EASY.md - PM3 Easy specific mappings with PWM capabilities
  • LED_CONTROL_IMPLEMENTATION_SUMMARY.md - Technical implementation details
  • PM3_PWM_INVESTIGATION.md - Hardware PWM capability analysis
  • PWM_CHANNEL_REALLOCATION_ANALYSIS.md - Detailed PWM reallocation design
  • PWM_ENHANCEMENT_COMPLETE.md - Complete PWM implementation summary
  • PM3_EASY_PWM_FINAL.md - Final configuration guide for PM3 Easy
  • UPSTREAM_LED_CONTROL.md - Upstream contribution guide with patch generation

Command Examples:

# Basic control
hw led --led a --on                              # Turn on green LED
hw led --led b,c --off                           # Turn off blue and orange LEDs
hw led --led all --on                            # Turn on all LEDs

# Blink patterns
hw led --led a --blink fast --count 5            # Fast blink 5 times
hw led --led all --blink slow --duration 2000    # Slow blink for 2 seconds
hw led --led d --blink custom --interval 200     # Custom 200ms blink

# PWM brightness control (NEW!)
hw led --led a --brightness 0                    # Green LED off
hw led --led a --brightness 25                   # Green LED dim
hw led --led a --brightness 50                   # Green LED medium
hw led --led a --brightness 75                   # Green LED bright
hw led --led a --brightness 100                  # Green LED full

hw led --led b --brightness 50                   # Blue LED at 50%

# Multi-device identification with brightness levels (NEW!)
hw led --led a --brightness 20                   # Device 1 - dim green
hw led --led b --brightness 50                   # Device 2 - medium blue
hw led --led a --brightness 80                   # Device 3 - bright green

# Device identification (multi-device setups)
hw led --identify                                # Fast blink all LEDs 4 times
hw led --clear                                   # Turn all LEDs off

Python Usage (via SWIG):

import pm3

p = pm3.pm3('/dev/ttyACM0')

# Simple control
p.console('hw led --led a --on')

# PWM brightness control (NEW!)
p.console('hw led --led a --brightness 50')
p.console('hw led --led b --brightness 75')

# Multi-device identification with brightness
p.console('hw led --led a --brightness 30')  # Dim green = Device 1

# Multi-device identification
p.console('hw led --identify')

# Custom patterns
p.console('hw led --led all --blink fast --count 3')

Technical Details:

PWM Configuration:

  • Frequency: 48 kHz (MCK / 1000) - flicker-free
  • Resolution: 1000 steps (0.1% precision, exposed as 0-100%)
  • Duty Cycle: Inverted for active-low LEDs
  • CPU Overhead: Zero (hardware-controlled)
  • Protocol: Reuses pattern field of payload_led_control_t for brightness value

LED Hardware Mappings (PM3 Easy):

  • LED_A (Green): GPIO PA0 → PWM0
  • LED_B (Blue): GPIO PA2 → PWM2
  • LED_C (Orange): GPIO PA9 → No PWM
  • LED_D (Red): GPIO PA8 → No PWM

PWM Channel Allocation:

  • PWM0: LED_A brightness (after reallocation)
  • PWM1: All timing functions (SpinDelay, USB timing, button timing)
  • PWM2: LED_B brightness
  • PWM3: Unused/spare

Build Configuration:

For PM3 Easy hardware, LED_ORDER=PM3EASY must be set:

PLATFORM=PM3GENERIC
LED_ORDER=PM3EASY

For Qt-free builds (recommended to avoid snap conflicts):

SKIPQT=1 make -j8 all

PR Readiness:

  • Code complete and tested
  • Follows coding standards
  • No compiler warnings
  • Comprehensive documentation
  • Backward compatible (no breaking changes)
  • Ready for patch generation
  • Includes demo scripts (pulse_demo_simple.sh)
  • Needs testing on RDV4 hardware (if available)
  • Needs testing on standard PM3 Generic (non-Easy)

PR Priority: HIGH - Complete, production-tested implementation solving real multi-device use case with innovative PWM enhancement

Estimated Review Complexity: MEDIUM-HIGH (clean code, ~350 lines total across 7 files, follows conventions, includes novel PWM reallocation)

📋 PR Submission Checklist

Before Submitting CMakeLists.txt Fix:

  • Verify fix works on clean clone
  • Test on multiple platforms (Linux, macOS if possible)
  • Check if issue already reported
  • Search for existing PRs with similar fixes
  • Review Proxmark3 contribution guidelines
  • Prepare clear PR description with before/after
  • Include testing methodology
  • Reference any related issues

PR Template Draft:

## Description
Fixes undefined symbol error when importing Python bindings

## Problem
The experimental library Python bindings fail to load with:
`undefined symbol: qrcode_print_matrix_utf8`

## Root Cause
`qrcode/qrcode.c` is used by cmddata.c but not included in experimental library sources

## Solution
Add qrcode/qrcode.c to TARGET_SOURCES in client/experimental_lib/CMakeLists.txt

## Testing
- Compiled on Ubuntu 24.04
- Python 3.12 successfully imports pm3 module
- No undefined symbols in ldd/nm output

## Checklist
- [x] Code compiles without errors
- [x] No new warnings introduced
- [x] Tested on real hardware
- [x] Does not break existing functionality

🎯 Dangerous Pi Specific Notes

Context: These discoveries came from developing multi-PM3 support for Dangerous Pi, a Raspberry Pi-based RFID security research platform.

Key Requirements That Led to Discoveries:

  1. Need for multiple PM3 devices simultaneously
  2. Python-based backend API (FastAPI)
  3. Per-device session management
  4. Automated device identification
  5. Headless operation (no GUI)

Lessons Learned:

  • Python bindings are viable for production with this fix
  • Multi-device support requires careful USB enumeration
  • Serial number tracking is essential for device persistence
  • Async/await works well with PM3 operations

Last Updated: 2025-12-02 Maintainer: Dangerous Pi Team Contact: (to be added when ready to submit PRs)