I posted earlier about the dirt-cheap Vankyo projectors that some people use for their displays (for rear or front projection). These things are about as cheap as you can get for reasonable quality.

One problem with them is there is no way to control the power without either physically pressing a button on top of it, or using the remote control that comes with it. There are no USB or serial control options, so the “Projector Control” plugin for FPP won’t do any good.

I decided that what I needed was an “IR Blaster” running on the Raspberry Pi that is also running the projector’s video as an FPP Remote device. Well, I couldn’t get that to work. All of the tutorials and information I could find online for using the RPi as an IR Remote used old deprecated software that won’t run on the current Raspberry Pi OS or newer Linux Kernels.

So, I went with my back-up plan- good ol’ NodeMCU/ESPs. In this case- I ended up using an ESP-32 “Dev Kit” because that was the first one I could find that I didn’t need to solder.

I used this as a starting point for the hardware setup, but didn’t use their code because I’m not integrating with Alexa:

I also had to make some changes because I’m using an ESP-32, and not an older 16-bit ESP8266.

So, here is the hardware/wiring diagram for what I need to do:

aww board 2
ESP IR Blaster

I’m using a common BC-547 NPN transistor and an IR LED from an LED assortment I bought a few years ago. You can get both on Amazon cheaply. Actually- I think you need to buy at least 10-20 of them anyway.

Here is my parts list with links:
ESP32 Dev Kit: https://amzn.to/3nFMhhi (I used this below.)
ESP-12: https://amzn.to/3mBl1Q5 (Cheaper alternative to the above. Pinouts are different!)
BC-547 NPN Transistors: https://amzn.to/3r57cfT
IR LED Kit: https://amzn.to/3gXmlLH
Resistor Kit: https://amzn.to/37slzmV (You may be set for life.)
Solderless Breadboard with Jumpers: https://amzn.to/34mNXov

Their diagram uses an ESP-12 NodeMCU, while I’m using an ESP-32, so the pinouts are a bit different. Here is what my testing implementation looks like:

IMG 3603
ESP32 IR Blaster

I’m using the same GPIO4 as they are, which is “D4” on the ESP-32. It’s probably hard to see in the picture, but it is hooked up according to their diagram:

  • 3v3 -> IR LED Anode
  • IR LED Cathode -> NPN Collector
  • NPN Base -> 1k Resistor -> GPIO4
  • NPN Emitter -> GND

The transistor is just boosting the current available to the IR LED, since the GPIO out has a very low current rating which wouldn’t be able to drive it. I’ve read that some people just connect the IR LED between the GPIO and GND and call it good, but I can’t see that really working in most cases.

So- we have an IR sender, now we need to program it…

I decided I wanted to turn the projector on and off in exactly the same way as I control the power to the rest of my display- using E1.31 (DMX) signaling. I already had code to control a single relay, and even multiple relays, so I figured I would adapt it for this…

So, here it is:
I updated this code in January 2021 to remove an unnecessary delay in the main loop and add additional documentation and serial output for testing. I also moved this to a D1 Mini.

/* DMX_Projector_Power
 * IR control for popular Vankyo projectors 
 * Works with ESP, ESP32 devides like D1 Mini and ESP-32 DevKit.
 * by Wolf I. Butler, Updated January, 2021
#include <IRremoteESP8266.h>
#include <IRsend.h>
//#include <WiFi.h>           // Use this for ESP32. 
#include <ESP8266WiFi.h>  // Use this for ESP8266
#include <ESPAsyncE131.h>   // E1.31 Library https://github.com/forkineye/E131

IRsend irsend(4);  // IR LED Pin as per our circuit. Change this to the GPIO pin you are using.

//Set your WiFi network information below...
const char* ssid = "Your_WIFI_SSID"; // your network AP SSID
const char* password = "Your_WIFI_Password"; // your network AP password

//Set the E1.31 (DMX) Universe and channel you want to use.
const int UNIVERSE = 1;       // Universe to listen to.
const int UNIVERSE_COUNT = 1; // Universe count. Just leave at 1.

//Enter the DMX Channel you want to use, -1 below.
//So, for Channel 1, enter 0. For Channel 21, enter 20.
const int CHANNEL = 20 ;      // E1.31 Channel to listen for.

//Projector state flag. Don't change.
bool isProjectorOn = false;

// ESPAsyncE131 instance with UNIVERSE_COUNT buffer slots

void setup() {
  Serial.print("Connecting to ");


  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    //Loop (forever) until WiFi is connected.
  if (e131.begin(E131_MULTICAST, UNIVERSE, UNIVERSE_COUNT))   // Listen via Multicast
         Serial.println(F("Connected to WIFI..."));
         Serial.println(F("*** e131.begin failed ***"));

  //Attempt to ensure that the projector is in a known state (OFF) when started:
  Serial.println(F("Resetting Projector. Please wait..."));
  Serial.println(F("Powering Projector on..."));  
  irsend.sendNEC(0xFF15EA); //If off, turn on. If on, does nothing because of the following...  
  delay(2000);  //2 second delay
  irsend.sendNEC(0xFF59A6); //Down button. Cancels initial power command if projector was on.
  Serial.println(F("Waiting for Projector start-up..."));    
  delay(15000); //10 second delay. Enough time to warm up if it was just started.
  Serial.println(F("Turning Projector off..."));  
  irsend.sendNEC(0xFF15EA); //Power...
  delay(2000);  //2 second delay
  irsend.sendNEC(0xFF15EA); //Off.
  Serial.println(F("Online, listening for E1.31 data..."));

//Don't change anything below...
//Any DMX value under 127 is "Off", above 127 is "On".
//Because the power function is only a toggle- using isProjectorON flag to try to insure this works right.

void loop() {
  if (!e131.isEmpty()) {
    e131_packet_t packet;
    e131.pull(&packet);     // Pull packet from ring buffer  
    if (packet.property_values[CHANNEL] > 127) {
      if (!isProjectorOn) {
        irsend.sendNEC(0xFF59A6); //Down button. Cancels initial power power-off dialog if projector was already on.  
        isProjectorOn = true;
        Serial.println("Projector On!");
    else {
      if (isProjectorOn) {
        isProjectorOn = false;
        Serial.println("Projector Off!");

You will need to install the ESP boards managers in Arduino IDE for the board you are using. I work with both ESP-12/D1 Minis and ESP-32, so I have both installed. Here is a screen grab of my Additional Boards Manager URLs.

Note that I am using the “dev” branch for ESP-32. This is necessary to support Mac Big Sur (as of December, 2020).

Screen Shot 2021 01 08 at 6.53.40 PM

The “IRremoteESP8266” (which includes IRsend.h) and “WiFi” libraries are available in the Arduino IDE’s Library Manager. You will also need to add the ForkInEye ESPAsyncE131 library to the Arduino IDE.


This code works for ESP8266 and ESP32. I’ve just found that you need to change the WiFi library. You may also need to change the GPIO. As noted in the code- be careful what pin you use- the pin labels don’t necessarily match the GPIO number.

Note: I’m using DHCP and Multicast for my power controls, so there isn’t anywhere to set a fixed IP address. You can adapt this if-needed. Look up the WiFi.h or ESP8266WiFi.h libraries to reference how to specify a fixed IP. The documentation for ESPAsyncE131 has instructions for listening to Unicast.

So, what does it do?

The projector itself uses “NEC” formatted remote codes. Specifically HEX “FF15EA” as a power toggle. Unfortunately it doesn’t have a distinctive “power on” and “power off” code- just a power toggle. To power off the projector, the power button needs to be pressed twice with a few seconds in-between presses.

Since there is no feedback to tell the controller what the current state of the projector is- it first tries to make sure the projector is turned on. It sends a power command followed by a down-arrow. This will cancel a pending shut-down if the projector was already on. Otherwise- it waits for 15 seconds, which is as long as it takes before the projector will accept remote commands if it has first been turned on.

Now that the projector is known to be on- the controller turns it off by sending two power commands with a brief pause in-between. Now- the controller knows the projector is really off. Without going through all of this- the projector may be left in a state where the “Press Power again to turn off.” message is across the screen, or it could turn the projector on when it is supposed to be turning it off.

After all this- the controller starts listening for for E1.31 packets addressed to it (by Universe and Channel). Similar to the relay controllers- any “dimmer” value less than or equal to 127 will turn it “Off”, and any value higher than 127 will turn it “On”. Because the power is a toggle, and does not have distinct on and off modes- a flag called isProjectorOn is used to track the state and prevent multiple codes from being sent. So- the power command is only sent once if the dimmer value goes above 127, and the double-power command is only sent once if it goes below.

Test Implementation

Screen Shot 2020 11 11 at 1.41.52 PM
IR Power Control

I’m just plugging the board into the projector’s USB port for power. The port stays active all the time, even if the projector is “off”. Because the projector’s IR receiver and the IR LED are VERY directional- I have temporarily just extended the LED with a couple of jumper wires, and have it bent so it is aiming at the IR port on the projector.

At some point I may switch to a smaller NodeMCU like a D1 Mini, and put the whole thing in a 3D printed box, but for now- it’s working and I don’t see a need to mess with it.

If I do another (nicer) build, or clean this one up a bit- I’ll post more pictures and info.

Post Test Build Notes:

Since the above works, and I’ve been busy with other things, I haven’t done any new builds of this. I did slightly change the way the IR LED is positioned, mostly because what I did above essentially makes a cat toy. I used a couple of Scotchlok connectors to hook the LED up, which are then fastened to the projector using double-stick tape.

IMG 3684
Hey, it works…

The breadboard is just stuck to the top of the projector so it doesn’t get knocked off.

This setup has been working really well. The only issue I’ve had is while debugging pixel and other issues- I ended up rebooting my WiFi AP several times. I can probably add a test/reconnection routine for WiFi in my main loop, but for now- since it only tries to connect on boot- I need to make sure to reboot it if I do anything with WiFi to make sure it is connected. The rest of my systems are all on relay-controlled power, so I could also accomplish this by just adding the projector/control to a relay. This has only been a problem a few times during testing, and it has worked flawlessly so far in my light show.

Build Update:

(December 2020)
I finally got around to making a bit more of a permanent build. Unfortunately it is almost as ugly as what I had before. In fairness to myself- my “good” pencil-tip soldering iron crapped-out, and my backup (and older-heavy chisel-point iron) just wasn’t up to the task.

Here is a quickie diagram I did for myself just to figure out how to line everything up…

Screen Shot 2021 01 03 at 4.42.44 PM
Rough Perf-Board Diagram

This should have been simple, but without the right tool for the job it ended up being a mess. To top it all off- I had the polarity wrong on the IR LED, so had to cut the leads and patch it back in the right way. I wasn’t going to try to desolder it. I don’t want to even look at the back of this thing again- it’s a disaster. Trying to use a wide-tipped soldering iron on anything this small was asking for trouble.

IMG 0162
Completed Controller

It was a soldering nightmare, but it works. I’ve got an actual electronics soldering station on-order, so I might re-do this again at some point. Unfortunately to add insult to injury- Apple pretty much killed Arduino IDE again with Big Sur, so I can’t compile/flash my sketch onto a new MCU either.

The white tabs are for 3M Command hook-and-loop, which I’m using to stick it to the back of the projector…7

IMG 0163
Completed (?)

In all honesty- I didn’t think this through very well. I should have measured things and planned a bit better. I had intended for the board to sit underneath the IR window, but it is too big to do so. Oriented the way it is- the USB plug sticks out the top, and WIFI is blocked more by the projector since the antenna is on the bottom. If I do it again- I’m going use a D1 Mini on a smaller perf-board, and will orient things much better.

Build Update 2

(Junuary, 2021)
Okay, so I couldn’t leave well enough alone…

I re-did my build using a D1 Mini, and since I have a proper soldering iron now- I was able to do it a bit better. Heck, I’ll even show the back. I did screw up and use the wrong GPIO, so had to fudge it a bit (and there is still solder on and a jumper wire on those pads), but overall it is a LOT cleaner than the mess I ended up making above.

IMG 0171 1
Projector Control “Final” Build. Bottom. Not as nice as I would like it, but it works.

Here is the top of the board, without the D1 Mini installed. Because of the stand-offs there is enough room under the D1 Mini for the minimal components needed. I was also able to optimize the locations because the D1 Mini’s 3v3 pin is on the opposite side of GND and GPIO4 (D2).

IMG 0170
Projector Control “Final” Build. Top. No D1 Mini.

And here it is with the D1 Mini plugged in…

IMG 0172
Projector Control “Final” Build. Top with D1 Mini.

And here it is mounted to the back of the projector. I just used double-stick tape.

IMG 0174
Projector Control “Final” Build. Mounted on Projector.

As you can see- this design fits much better than the previous ones. USB power is still provided by the projector, so the whole thing is pretty self-contained. The D1 Mini is functionally the same in this application as the ESP-32. Performance isn’t a factor, and I’m not seeing any WIFI issues in testing. Of course- once I get things set up for the holidays this year (in 10 months), I’ll re-assess and update.