Overview
A media remote receiver for your PC/MAC/thing supporting keyboard media keys.
- Also works with many smart TVs and phones.
- Flexible CircuitPython-based solution can easily be adapted to other microcontrollers/IR remotes.
- Built-in IR signal decoder utility on serial monitor output.
- An easy, inexpensive, solderless build! (if desired)
Build & variants
If this particular build is not for you, check out alternative variants in the note here.
Program your device
Installing CircuitPython environment
- Download .uf2 file here:
- Plug in USB cable from PC to RP2040 board while holding the BOOTSEL button.
- A new drive should get mounted on your system.
- Drag/drop downloaded .uf2 file onto newly mounted drive (RPI-RP2).
Installing libraries and project code
From "CircuitPython Library Bundle" (download here):
- Copy from bundle .zip file to the microcontroller [drive:]\lib\ folder:
adafruit_hid
From the PCMediaRemote release page (download here):
- Download and unzip the "Source code (zip)" file from the "Assets" section.
- Copy the "unzipped" custom project code to microcontroller [drive:]:
- [PCMediaRemote folder]\lib_cktpy\* => [drive:]\lib\
- [PCMediaRemote folder]\pkg_install\MediaRemote_RP2040\*.py => [drive:]\
NOTE: Feel free to check for newer versions of PCMediaRemote on the releases page. Code from the main branch might also be functional, but explicit releases are less likely to have issues.
Customizing your solution
You can directly modify the main.py file on the [drive:]\ folder. Every time you save the file, CircuitPython will restart with the updates applied.
Note that if the file gets corrupted for some reason, your changes will be lost. It is recommended to work from a folder on your PC, and manually upload (ex: drag/drop) the changes to the CircuitPython drive.
Code breakdown
Reactive programming
This project implements/uses event handlers ("reactive programming") to simplify the code and help with readability.
- With reactive programming, a solution does not have to explicitly tell the microcontroller to scan for IR messages, then figure out somehow if a button was pressed, released, etc.
- Instead, the program uses a more "declarative" approach where it merely has to describe what to do when a button press (or button release) is detected.
- Because of this different approach, the code might look a bit unusual compared to other project examples.
That said, the example should be relatively easy to modify for your particular needs.
Understanding main.py (link)
The code is composed of 3 main objects/data structures:
-
SIGNAL_MAP: Creates a mapping between decoded IR signals and which "function" (play/pause/...) they represent. -
KEYMAP: Creates a mapping between button "functions" (play/pause/...), and what key to send to the PC. -
IRDetect: A declarative way to describe how to react to button press/release events.
Note that in the example, SIGNAL_MAP is constructed from the combination of SIGNAL_MAP_ADAFRUIT_389_CCC/_EXTRAS, and SIGNAL_MAP_LG_CCC/EXTRAS. The code calls SIGNAL_MAP.update(...) to add new (or update existing) key:value mappings of the SIGNAL_MAP python dict for both remotes.
To support a different IR remote, the SIGNAL_MAP object should add in its specific IR code mappings. Follow the pattern used by SIGNAL_MAP_LG_CCC to add your own custom "SIGNAL_MAP_X". Also feel free to remove predefined SIGNAL_MAP_* values to minimize accidental detection of these other remotes.
By default, PCMediaRemote uses irdetect to decode NEC control codes:
irdetect = IRDetect(rx, PDE.DecoderNEC(), PDE.DecoderNECRPT(), msgRPT=PDE.IRMSG32_NECRPT)
To detect Samsung codes instead, substitute PDE.DecoderNEC() => PDE.DecoderSamsung().
Capturing IR control codes of other remotes
To make supporting other remotes a bit easier, PCMediaRemote includes a built-in feature that prints out any NEC code it detects to the serial output. Some information on how to access the serial output is provided in the learn guide "Connecting to the serial console". The following shows the type of output you can expect from the console:
MediaRemote: ready to receive! New message: NEC 0x20DF40BF (VOL+) Repeat! New message: NEC 0x20DFC03F (VOL-) Repeat! Unknown message: NEC 0x20DF8E71
In the serial output above, the first 2 messages are known and originate from VOL+/- buttons.
When a message is not mapped to anything (despite being recognized/decoded): it will also get printed to console, as shown for the third and final message. Simply write down this code, and add it to your SIGNAL_MAP dictionary.
Note that Python requires the "0x" prefix to recognize the code as a hexadecimal (hex) number.
About the IR library
The code uses a custom module to perform Consumer electronics IR communications (CelIRcom - to encode/decode IR signals). In turn, CelIRcom heavily leverages the pulseio module - a "core module" of CircuitPython.
- Currently only supports NEC (/Samsung) protocol, with some support for a Sony protocol.
- Preferable to chose an IR remote (universal remote?) that "speaks" NEC.
If your particular remote doesn't seem to be working, the following should work "out-of-the-box" with the provided code:
Please ensure the remote you select is capable of sending out NEC/Samsung codes. Otherwise, the IR-library used for this project will not be able to decode the signals. A good way to check is to look at the serial output from the PCMediaRemote project. If it prints out detected signals, that means the signals are being decoded.
Universal remotes that come bundled with satellite receivers are also good, widely available options. For example, the "TV" device of an Arris IRC600 remote should also "just work" with the PCMediaRemote default configuration (if programmed to LG code "11423").
Other documentation
This page (PC media remote) was last updated on January 04, 2025.