Project tutorial
MKR1000 Universal Remonster!

MKR1000 Universal Remonster! © GPL3+

WiFi connected universal remote with smart phone webapp.

  • 9,885 views
  • 3 comments
  • 12 respects

Components and supplies

Abx00004 iso both
Arduino MKR1000
×1
Ard nano
Arduino Nano R3
×1
Fairchild semiconductor pn2222abu. image
General Purpose Transistor NPN
Triggers the PNP transistors of IR LED array
×1
Mfr 25frf52 1k sml
Resistor 1k ohm
pulls down NPN transistor
×1
IR transmitter (generic)
IR LEDs. Get wide angle if you have a choice
×2
Fairchild pn2907abu.
General Purpose Transistor PNP
One PNP transistor per IR LED; you can do much more than two
×2

Apps and online services

About this project

I had been toying with the idea of building a WiFi connected IR blaster that can control everything in a room through a webapp running on my phone (which does not have a built in IR blaster).  

When I saw the World's Largest Arduino Maker Challenge announced right here on hackster.io I took one look at the Arduino MKR1000 specs and realized it was a perfect fit:

  • Low power 32bit ARM MCU
  • Low power 802.11 b/g/n Wi-Fi
  • Onboard Li-Po charging circuit
  • Familiar Arduino IDE
  • Tons of I/O like you'd expect from any Arduino

Since I wouldn't get my MKR1000 for at least a month, I figured I'd start working on the code. I would need to accomplish three things:

  • Connect to my home WiFi
  • Light weight web server to accept HTTP requests
  • Parse GET URL for virtual button presses
  • Send IR signal depending on URL

The MKR1000 should be able to do all this on its own, but my prototype will need a few more components.

Prototyping

Here I have an ESP8266 running the WiFi client and relaying everything to the Arduino Nano over serial. The Nano is running the webserver itself and is responsible for handling the HTTP connections and parsing the URLs. It takes the URL, interprets that as a specific button press, and sends the corresponding IR code.

There is a bit of hackery needed to get this working. First, I'm using AT commands to manage the WiFi connection through the ESP8266. To get all the communication working over software serial, I had to level shift the voltages since the ESP8266 is 3.3v and the Nano is 5v. The ESP8266 also draws a lot of power when connecting to WiFi, so it requires its own power regulator and smoothing cap.

Fortunately, the MKR1000 can do all of this and more on just a single board that's just slightly larger than the nano (4 pins longer, 3 pins wider on a breadboard). 

Since the MKR1000 is a 3.3v board the output from the I/O pins would cause the IR LED to be quite dim. I also plan on having multiple LEDs pointed in different directions, so I'll need to use the I/O pin to drive an NPN transistor which in turn switches a PNP transistor for each of the LEDs wired in parallel. These PNP transistors have the collector/emitter wired directly to the 5v supply voltage. Going this route, I can have as many IR LEDs as I want and can make an omnidirectional array.

Setting up the Backend

The web page that contains the UI is running on an underutilized C.H.I.P. $9 computer that is already setup on my home network. After doing some research, I found a great tutorial for implementing a REST API for Arduino to control LEDs on adafruit. I used their JavaScript and PHP files to submit the cURL requests and loaded that on the CHIP which is already running apache. After creating a basic HTML page for the remote control, I added the manifest.json and icon files so it can run as a native web app on an Android phone.

Writing the Code

On the MKR1000 side, I just had to switch out the ESP8266WiFi.h with the WiFi101.h library. Then I trimmed down the webserver to just accept a GET request, parse the URL, send a 200 OK, and then disconnect.  

void loop() {
 // listen for incoming clients
 WiFiClient client = server.available();
 if (!client) {
   return;
 }
   Serial.println("new client");  // can be removed after debugging
   // Wait until the client sends some data
   while(!client.available()){
     delay(1);
   }
 // Read the first line of the request (the GET string)
 String req = client.readStringUntil('\r');
 Serial.println(req);  // can be removed after debugging
 // Send request over to Arduino Nano
 Serial1.println(req);   
 client.flush();
   // give the client time to receive the data
   delay(1);
   // close the connection
   client.stop();
   client.flush();
   Serial.println("client disconnected");  // can be removed after debugging
}

Based off the URL, the Arduino would send the IR code that was mapped to the button pressed. I say "would" because I just found out the IRremote library I have used in the past does not work with the SAMD ARM based Arduinos (which the MKR1000 is).  

Rather than re-writing the library, I'll just output the commands from the MKR1000 over serial to an Arduino Nano and have that drive the LEDs.  

Looking Forward 

Once the library is ported over by the Arduino community, I'll be able to remove that portion entirely and do it all on the single MKR1000 board.  

I built another one of these using just an NodeMCU ESP8266 dev board, so in the meantime I will still be able to control my appliances.

I've been using this for about a month and it works extremely well.  

Code

WiFi_IR_Blaster_MKR1000C/C++
Web server running on MKR1000 that sends IR codes to another MCU via serial
/*
 *  Adapted from NodeMCU/ESP8266 WiFi IR Blaster by Buddy Crotty
 *  
 *  This sketch requires an Arduino MKR1000 for the web server and WiFi portions
 *  and then sends the parsed IR codes to another device that supports the IRremote.h libraries
 *  (like ATtiny85 or Arduino Uno/Nano/Mini) over software serial.
 *  
 *  This works best with another web server acting as a front end that 
 *  sends cURL requests based on which buttons are pressed. 
 *  GET request URL format: http://[ip_address]/[IRcode]
 *  
 *  IR codes can be strung together to create macros 
 *  
 */

#include <SPI.h>
#include <WiFi101.h>

char ssid[] = "SSID";      // your network SSID (name)
char pass[] = "PASSWORD";   // your network password
//int keyIndex = 0;                 // your network key Index number (needed only for WEP)

// MKR1000 Hardware Serial1(13, 14); // RX, TX
int status = WL_IDLE_STATUS;

WiFiServer server(80);

#define DEBUG true

void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  
  // check for the presence of the shield:
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue:
    while (true);
  }

  // attempt to connect to Wifi network:
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }
  server.begin();
  // you're connected now, so print out the status:
  
  Serial.println("...:: WiFi IR Blaster ::...");
  Serial.println("Webserver Started");
  printWifiStatus();
  Serial.println("");
}
 
void loop()
{
  // listen for incoming clients
  WiFiClient client = server.available();
  if (!client) {
    return;
  }
  
    Serial.println("new client");
    // Wait until the client sends some data
    while(!client.available()){
      delay(1);
    }
    
  // Read the first line of the request (the GET string)
  String req = client.readStringUntil('\r');
  Serial.println(req);
  Serial1.println(req);   
  client.flush();

    // give the client time to receive the data
     delay(1);

    // close the connection
    client.stop();
    client.flush();
    Serial.println("client disconnected");
}


void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}
WiFi_IR_Blaster_NanoC/C++
IR Blaster code running running on additional MCU (in this case an Arduino Nano). Using the vendor codes rather than Raw IR is much more efficient, but I couldn't get it to work on my appliances.
/*
 *  Adapted from NodeMCU/ESP8266 WiFi IR Blaster by Buddy Crotty
 *  
 *  To be used in conjunction with an Arduino MKR1000 running 
 *  the web server and connecting to WiFi.  
 *  
 *  This sketch runs on another Arduino and only parsed requests coming 
 *  in over serial to then send out the requested IR signal. 
 *  
 *  IR codes can be strung together to create macros 
 *  
 */
 
#include <IRremote.h>

int khz = 38; // 38kHz carrier frequency for both NEC and Samsung

IRsend irsend; //an IR led is connected to GPIO1 (physical pin 6 on an ATtiny85)
/*
  // Insert RAW IR signal for "TV Power"
unsigned int irTVpwr[] = {4650,4250, 700,1550, 650,1550, 700,1550, 650,450, 650,500, 600,500, 600,500, 600,550, 550,1700, 550,1650, 600,1650, 550,550, 600,500, 600,550, 550,550, 600,500, 600,550, 550,1650, 600,550, 550,550, 600,500, 600,550, 550,550, 600,500, 600,1650, 600,500, 600,1650, 550,1700, 550,1650, 600,1650, 550,1650, 600,1650, 600};  // SAMSUNG E0E040BF

  // Insert RAW IR signal for "TV Source"
unsigned int irTVsrc[] = {4600,4300, 700,1550, 650,1550, 650,1600, 650,450, 650,450, 600,550, 550,550, 600,500, 600,1650, 550,1650, 600,1650, 550,550, 600,500, 600,550, 550,550, 550,550, 600,1650, 550,550, 550,550, 600,500, 600,500, 600,550, 550,550, 600,500, 600,550, 550,1650, 550,1700, 550,1650, 600,1600, 600,1650, 600,1600, 600,1650, 550};  // SAMSUNG E0E0807F
  
  // Insert RAW IR signal for "TV Mute"
unsigned int irTVmute[] = {4650,4250, 700,1550, 650,1550, 700,1550, 650,450, 650,500, 600,500, 600,500, 600,500, 600,1650, 600,1600, 600,1650, 550,550, 600,500, 600,550, 550,550, 600,500, 600,1650, 550,1650, 600,1650, 550,1650, 600,550, 550,550, 550,550, 600,500, 600,550, 550,550, 550,550, 600,500, 600,1650, 550,1650, 600,1650, 550,1650, 600};  // SAMSUNG E0E0F00F
  
  // Insert RAW IR signal for "TV Volume Down"
unsigned int irTVvdn[] = {4650,4250, 700,1550, 650,1550, 700,1550, 650,450, 650,450, 650,450, 600,550, 550,550, 600,1650, 550,1650, 550,1650, 600,550, 550,550, 550,550, 600,500, 600,500, 600,1650, 600,1600, 600,500, 600,1650, 550,550, 600,500, 600,500, 600,550, 550,550, 600,500, 600,1650, 550,550, 550,1650, 600,1650, 550,1650, 600,1650, 550};  // SAMSUNG E0E0D02F
  
  // Insert RAW IR signal for "TV Volume Up"
unsigned int irTVvup[] = {4600,4300, 650,1600, 650,1550, 650,1600, 600,500, 600,550, 600,500, 600,550, 550,550, 550,1700, 550,1650, 600,1650, 550,550, 600,500, 600,550, 550,550, 600,500, 600,1650, 600,1650, 550,1650, 600,550, 550,550, 600,500, 600,550, 550,550, 600,500, 600,550, 550,550, 600,1600, 600,1650, 600,1650, 550,1650, 600,1650, 600};  // SAMSUNG E0E0E01F
  
  // Insert RAW IR signal for "TV Channel Up"
unsigned int irTVchup[] = {4650,4250, 700,1550, 650,1600, 650,1550, 650,500, 600,500, 600,500, 650,500, 600,500, 600,1650, 550,1650, 600,1650, 600,500, 600,500, 600,550, 550,550, 600,550, 550,550, 550,1650, 600,550, 600,500, 600,1650, 550,550, 600,500, 600,550, 550,1650, 600,550, 550,1650, 600,1650, 600,500, 600,1650, 600,1600, 600,1650, 600};  // SAMSUNG E0E048B7
  
  // Insert RAW IR signal for "TV Channel Down"
unsigned int irTVchdn[] = {4600,4350, 650,1550, 650,1600, 650,1600, 600,500, 600,500, 600,550, 550,550, 600,550, 550,1650, 600,1650, 550,1700, 550,550, 550,550, 600,500, 600,550, 550,550, 600,500, 600,550, 550,550, 550,550, 600,1650, 600,500, 600,500, 600,550, 550,1650, 600,1650, 600,1650, 550,1650, 600,550, 550,1650, 600,1650, 600,1650, 550};  // SAMSUNG E0E008F7
*/  
  // Insert RAW IR signal for "Receiver Power"
unsigned int irRECpwr[] = {9050,4350, 650,500, 600,1600, 600,500, 650,500, 600,1600, 600,550, 600,1600, 600,1650, 550,550, 600,500, 600,1600, 650,1600, 600,500, 600,1650, 600,1600, 600,500, 600,1650, 600,1600, 600,550, 600,1600, 600,500, 600,550, 600,1600, 600,1600, 650,500, 600,500, 600,1600, 650,500, 600,1600, 600,1650, 600,500, 600,500, 600};  // NEC 4B36D32C

  // Insert RAW IR signal for "Receiver Power On"
unsigned int irRECpwrON[] = {9000,4400, 600,550, 600,1600, 600,500, 600,550, 600,1600, 600,500, 600,1600, 650,1600, 600,1600, 600,500, 650,1600, 600,1600, 600,500, 650,1600, 600,1600, 600,500, 600,550, 600,500, 600,1600, 600,550, 600,500, 600,500, 650,500, 600,500, 600,1600, 650,1600, 600,500, 600,1600, 650,1600, 600,1600, 600,1600, 600,1600, 650};  // NEC 4BB620DF
  
  // Insert RAW IR signal for "Receiver Power Off"
unsigned int irRECpwrOFF[] = {9000,4400, 600,550, 550,1650, 600,550, 550,550, 600,1650, 550,550, 600,1650, 550,1650, 600,550, 550,550, 550,1650, 600,1650, 600,550, 550,1650, 600,1650, 550,550, 600,1650, 550,1650, 600,1650, 600,500, 600,550, 550,550, 600,1650, 550,550, 600,500, 600,550, 550,550, 550,1700, 550,1650, 600,1650, 550,550, 600,1650, 550};  // NEC 4B36E21D

  // Insert RAW IR signal for "Receiver Mute"
unsigned int irRECmute[] = {9000,4400, 650,450, 650,1600, 600,500, 600,500, 650,1600, 600,500, 600,1650, 600,1600, 600,1600, 650,500, 600,1600, 650,1600, 600,500, 600,1600, 650,1600, 600,500, 600,1650, 600,500, 600,1600, 650,500, 600,500, 600,500, 600,500, 650,500, 600,500, 600,1600, 650,500, 600,1600, 600,1600, 650,1600, 600,1650, 600,1600, 600};  // NEC 4BB6A05F
  
  // Insert RAW IR signal for "Receiver Volume Down"
unsigned int irRECvdn[] = {9150,4250, 750,350, 700,1550, 700,400, 700,450, 650,1550, 700,450, 600,1600, 650,1600, 600,1650, 600,500, 600,1650, 600,1600, 600,550, 600,1600, 600,1650, 600,500, 600,1650, 600,1600, 650,500, 600,500, 600,500, 650,500, 600,500, 600,500, 600,550, 600,500, 600,1650, 600,1600, 600,1650, 600,1650, 600,1600, 600,1650, 600};  // NEC 4BB6C03F
  
  // Insert RAW IR signal for "Receiver Volume Up"
unsigned int irRECvup[] = {9050,4400, 650,500, 600,1600, 600,550, 600,500, 600,1650, 600,500, 600,1600, 650,1600, 600,1600, 600,550, 600,1600, 600,1600, 650,500, 600,1600, 650,1600, 600,500, 600,550, 600,1600, 600,550, 600,500, 600,550, 600,500, 600,550, 600,500, 600,1600, 650,500, 600,1600, 600,1650, 600,1600, 600,1650, 600,1600, 600,1600, 600};  // NEC 4BB640BF

  // Insert RAW IR signal for "Receiver Source CBL/SAT"
unsigned int irRECsrc[] = {8950,4450, 600,500, 600,1650, 600,500, 600,500, 600,1650, 600,500, 600,1600, 600,1650, 600,1600, 600,550, 600,1600, 600,1650, 600,500, 600,1600, 600,1650, 600,500, 600,500, 600,1650, 600,1600, 600,1650, 600,500, 600,500, 600,500, 650,500, 600,1600, 600,500, 600,550, 600,500, 600,1600, 600,1650, 600,1600, 600,1650, 600};  // NEC 4BB6708F

String req = "";

void setup() {
  delay(1000);
  Serial.begin(9600);
  Serial.setTimeout(100);
  //irsend.begin();
}

void loop() {
  // Wait for serial data
  if (Serial.available() ){
    // read the incoming data from MKR1000
    req = Serial.readString();

    // Match the request
/*    if (req.indexOf("/irTVpwr") != -1){
        irsend.sendRaw(irTVpwr, sizeof(irTVpwr) / sizeof(irTVpwr[0]), khz);   
        // Serial.println("IRreq irTVpwr received");
    }
    else if (req.indexOf("/irTVsrc") != -1){
        irsend.sendRaw(irTVsrc, sizeof(irTVsrc) / sizeof(irTVsrc[0]), khz);   
        // Serial.println("IRreq irTVsrc received");
    }
    else if (req.indexOf("/irTVmute") != -1){
        irsend.sendRaw(irTVmute, sizeof(irTVmute) / sizeof(irTVmute[0]), khz);   
        // Serial.println("IRreq irTVmute received");
    }
    else if (req.indexOf("/irTVvdn") != -1){
        irsend.sendRaw(irTVvdn, sizeof(irTVvdn) / sizeof(irTVvdn[0]), khz);   
        // Serial.println("IRreq irTVvdn received");
    }
    else if (req.indexOf("/irTVvup") != -1){
        irsend.sendRaw(irTVvup, sizeof(irTVvup) / sizeof(irTVvup[0]), khz);   
        // Serial.println("IRreq irTVvup received");
    }
    else if (req.indexOf("/irTVchup") != -1){
        irsend.sendRaw(irTVchup, sizeof(irTVchup) / sizeof(irTVchup[0]), khz);   
        // Serial.println("IRreq irTVchup received");
    }
    else if (req.indexOf("/irTVchdn") != -1){
        irsend.sendRaw(irTVchdn, sizeof(irTVchdn) / sizeof(irTVchdn[0]), khz);   
        // Serial.println("IRreq irTVchdn received");
    }
    else if (req.indexOf("/irALLpwr") != -1){
        irsend.sendRaw(irRECpwrON, sizeof(irRECpwrON) / sizeof(irRECpwrON[0]), khz);   
        irsend.sendRaw(irTVpwr, sizeof(irTVpwr) / sizeof(irTVpwr[0]), khz);   
        delay(2000);
        irsend.sendRaw(irRECsrc, sizeof(irRECsrc) / sizeof(irRECsrc[0]), khz);         
        // Serial.println("IRreq irALLpwr received");
    } */
    if (req.indexOf("/irRECpwr") != -1){
        irsend.sendRaw(irRECpwr, sizeof(irRECpwr) / sizeof(irRECpwr[0]), khz);   
        // Serial.println("IRreq irRECpwr received");
    }
    else if (req.indexOf("/irRECpwrON") != -1){
        irsend.sendRaw(irRECpwrON, sizeof(irRECpwrON) / sizeof(irRECpwrON[0]), khz);   
        // Serial.println("IRreq irRECpwrON received");
    }
    else if (req.indexOf("/irRECpwrOFF") != -1){
        irsend.sendRaw(irRECpwrOFF, sizeof(irRECpwrOFF) / sizeof(irRECpwrOFF[0]), khz);   
        // Serial.println("IRreq irRECpwrOFF received");
    }
    else if (req.indexOf("/irRECmute") != -1){
        irsend.sendRaw(irRECmute, sizeof(irRECmute) / sizeof(irRECmute[0]), khz);   
        // Serial.println("IRreq irRECmute received");
    }  
    else if (req.indexOf("/irRECvdn") != -1){
        irsend.sendRaw(irRECvdn, sizeof(irRECvdn) / sizeof(irRECvdn[0]), khz);   
        // Serial.println("IRreq irRECvdn received");
    }
    else if (req.indexOf("/irRECvup") != -1){
        irsend.sendRaw(irRECvup, sizeof(irRECvup) / sizeof(irRECvup[0]), khz);   
        // Serial.println("IRreq irRECvup received");
    } 
    else {
      // Serial.println("invalid request");
      return;
    }
  }
}

Schematics

Schematic
Used the NodeMCU board since the MKR1000 wasn't available in Fritzing yet
Schematic%20view

Comments

Similar projects you might like

Home Automation with Arduino MKR1000 and Windows 10

Project tutorial by Ioannis Kydonis

  • 9,027 views
  • 1 comment
  • 30 respects

ARTIK + MKR1000 + DHT11 + MQTT

Project tutorial by vincent wong

  • 3,774 views
  • 12 comments
  • 10 respects

WiFi IR Blaster

Project tutorial by BuddyC

  • 85,577 views
  • 43 comments
  • 89 respects

Water Quality Monitoring Using MKR1000 and ARTIK Cloud

Project tutorial by Team Animo!

  • 7,075 views
  • 12 comments
  • 30 respects

MKR1000 Temp and Humidity Sensor

Project tutorial by Don Coleman

  • 18,137 views
  • 11 comments
  • 31 respects

Send MKR1000 Data to Google Sheets

Project tutorial by Stephen Borsay

  • 9,993 views
  • 28 comments
  • 31 respects
Add projectSign up / Login