Introduction

Internet IR remote controller is great solution to control your device when you are far from your house or office. However, when you are in your home or your office, you may not want to open your phone or computer. You just want to take the normal IR remote controller and control your devices. In this case, Internet IR remote controller seems to not so convenient as normal IR controller. Nonetheless, other situations come up:
  • There some obstacles between you and your device (in my case, I put my air-conditioner to noise-absorbed box)
  • The distance between your desk and your device is farther than IR range.


The solution for these cases is IR repeater which extends the range of remote-controller and overcome obstacles.

The implementation is quite simple if you already read my previous articles about IR. You don’t need to analyze the signal from remote-controller. You also don’t need to know devices address and command set. You only need to captures pulse chains from IR receiver, send out this chain to IR emitter, so it works for all devices.



Things Used In This Project
  • One PHPoC Blue
  • One IR receiver
  • One IR emitter
  • One Resistor 1kΩ
  • Grove expansion board (optional)


Wiring Diagram

Click image for larger version  Name:	IR_repeater_wiring.PNG Views:	2 Size:	59.9 KB ID:	488



Source Code

This code shows the implementation of IR repeater using PHPoC.
<task0.php>
PHP Code:
<?php

if(_SERVER("REQUEST_METHOD"))
    exit; 
// avoid php execution via http request

include_once "/lib/sd_340.php";
include_once 
"/lib/vd_nec_infrared.php";

$rbuf "";
$unit 562.5 5;//µs
$repc 80;
$lower_bound 1000;
$upper_bound 14000;

while(
1)
{        

    
//IR Repeater Function
    
infrared_capture_start($unit$repc);
    
usleep(500000);    
    
infrared_recv_stop();

    if(
infrared_available($lower_bound$upper_bound))
    {
        echo 
"\r\n";
        
$count_buf int2bin(12);    // first value is dummy.
        
$cnt_buf_len 1;
        for(
$i 0$i <= $repc$i++)
        {
            
//get width of pulses
            
$count infrared_get_raw_count($i);
            if(
$count)
            {
                
$count_buf .= int2bin($count2);  
                
$cnt_buf_len++;
                echo 
"$count, ";
            }
        }
        
infrared_emit($count_buf$cnt_buf_len$unit);    
    }        
}

?>

As you can see on above code, no device address, no command and bit pattern are required. Therefore, it works for all kinds of devices which use NEC or variation of NEC protocol (for other protocols, you may need to change $unit, $repc, $lower_bound and $upper_bound).



Combination of IR Repeater, Internet IR Remote Controller and Temperature Monitoring

Taking advantage of network function on PHPoC, I integrated the Internet IR remote control function into IR repeating function, so you can use both internet IR controller and IR repeater at the same time with the same hardware (Note that you need to insert USB wireless LAN dongle). To simplify, I only add one power button to turn on/off.

Furthermore, a temperature sensor is added to monitor temperature (TH02 temp&humi sensor)

Source code for this:
<task0.php>
PHP Code:
<?php

if(_SERVER("REQUEST_METHOD"))
    exit; 
// avoid php execution via http request

include_once "/lib/sd_340.php";
include_once 
"/lib/sn_tcp_ws.php";
include_once 
"/lib/vd_nec_infrared.php";
include_once 
"/lib/vd_th02.php";

define("AIRCON_ADDRESS",   0x01ff);

$rbuf "";
$unit 562.5 5;//µs
$repc 80;
$lower_bound 1000;
$upper_bound 14000;

i2c_TH02_setup(0);
ws_setup(0"phpoc_infrared_relay""csv.phpoc");

$max_samples 15;
$sample_count 0;

$temp_str "";

while(
1)
{        
    if(
ws_state(0) == TCP_CONNECTED)
    {
        
//Internet IR controller Function
        
$rlen ws_read_line(0$rbuf);

        if(
$rlen)
        {
            
$cmd = (int)$rbuf;
            
$data = (AIRCON_ADDRESS << 16) | ($cmd <<8) | ((~$cmd)&0xff);
            
nec_infrared_send($data32);            
        }

        
// Temperature Monitoring
        
$temp TH02_read_temperature();
        
$temp sprintf("%.01f"$temp);
        if(
$sample_count == 0)    
        {
            
$temp_str $temp;
        }
        else
            
$temp_str .= ",$temp;

        
$sample_count++;

        if(
$sample_count $max_samples)
        {
            
$pos strpos($temp_str",");

            if( 
is_int($pos) )
                
$temp_str substr($temp_str$pos+1);

            
$sample_count $max_samples;
        }

        
$wbuf "[$temp_str]";

        
ws_write(0$wbuf);

    }    

    
//IR Repeater Function
    
infrared_capture_start($unit$repc);
    
usleep(500000);    
    
infrared_recv_stop();

    if(
infrared_available($lower_bound$upper_bound))
    {
        echo 
"\r\n";
        
$count_buf int2bin(12);    // first value is dummy.
        
$cnt_buf_len 1;
        for(
$i 0$i <= $repc$i++)
        {
            
//get width of pulses
            
$count infrared_get_raw_count($i);
            if(
$count)
            {
                
$count_buf .= int2bin($count2);  
                
$cnt_buf_len++;
                echo 
"$count, ";
            }
        }
        
infrared_emit($count_buf$cnt_buf_len$unit);    
    }        
}

?>



Because we have web user interface, we need to add index.php file source as follow
<index.php>
Code:
<?php
 // To get the command set, please visit http://www.phpoc.com/forum/viewtopic.php?f=42&t=183
 define("CMD_POWER",   57);
?>
<!DOCTYPE html>
<html>
<head>
<title>PHPoC Infrared Control</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7">
<style type="text/css">
body { text-align: center; position: relative;}
#view {
 font-weight: bold; font-family : verdana, Helvetica, Arial, verdana, sans-serif, gulim; font-size:14pt; padding:10px;
 background-color: rgb(6, 38, 111);
 color: white;
}
#temp {
 font-weight: bold; font-family : verdana, Helvetica, Arial, verdana, sans-serif, gulim; font-size:25pt;
 margin-bottom: 5px;
}
#blink_ {
 color: rgb(0,153,153);
}
#graph {
    border: 1px solid #000;
 position: relative;
 margin-bottom: 5px;
 display: none;
}
#remote_con {
    margin-right: auto;
    margin-left: auto;
 width:100px; height:110px;
    position: relative;
    margin-bottom: 10px;
}
.button {
 position: absolute;
 left: 0px; top: 0px;
 width:100px; height:100px;
 background: url(inactive.png) no-repeat;
 background-size: contain;
}

</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript">
var CMD_POWER =   57;

var cmd = 0;
var canvas_width = 400;
var canvas_height = 240;
var padding = 35;
var pivot_x = padding;
var pivot_y = canvas_height - padding;
var ws = null;
var cvs = null, ctx = null;
var step_x = 30;
var step_y = 30;
var max_samples = 15;

function init()
{
 var view = document.querySelector("#view");
    view.addEventListener("click", option_onclick);
 //Temperature part
 cvs = document.getElementById("graph");
 cvs.width = canvas_width;
 cvs.height = canvas_height;
 ctx = cvs.getContext("2d");

 ctx.translate(pivot_x, pivot_y);
 //ctx.rotate(Math.PI *3 / 2);

 draw_all_text();
 draw_baseline();

 //Control part
 var remote_con = document.querySelector("#remote_con");
    remote_con.addEventListener("touchstart", mouse_down);
    remote_con.addEventListener("touchend", mouse_up);
    remote_con.addEventListener("touchcancel", mouse_up);
    remote_con.addEventListener("mousedown", mouse_down);
    remote_con.addEventListener("mouseup", mouse_up);
    remote_con.addEventListener("mouseout", mouse_up);      
}

function ws_onmessage(e_msg)
{
    console.log(e_msg.data);
 var arr = JSON.parse(e_msg.data);
 update_view(arr);
}

function ws_onopen()
{
 document.getElementById("ws_state").innerHTML = "OPEN";
 document.getElementById("wc_conn").innerHTML = "Disconnect";
 document.getElementById(CMD_POWER).style.backgroundImage = "url('unpressed.png')";
}
function ws_onclose()
{
 document.getElementById("ws_state").innerHTML = "CLOSED";
 document.getElementById("wc_conn").innerHTML = "Connect";
 document.getElementById(CMD_POWER).style.backgroundImage = "url('inactive.png')";
 document.getElementById("blink_").innerHTML = "null";
 ctx.clearRect(-pivot_x, -pivot_y, canvas_width, canvas_height);
 draw_all_text();
 draw_baseline();

 console.log("socket was closed");

 ws.onopen = null;
 ws.onclose = null;
 // ws.onmessage = null;
 ws = null;
}
function wc_onclick()
{
 if(ws == null)
 {
  ws = new WebSocket("ws://<?echo _SERVER('HTTP_HOST')?>/phpoc_infrared_relay", "csv.phpoc");
  document.getElementById("ws_state").innerHTML = "CONNECTING";

  ws.onopen = ws_onopen;
  ws.onclose = ws_onclose;
  ws.onmessage = ws_onmessage;
 }
 else
  ws.close();
}
function option_onclick(event)
{
 if (event.target !== event.currentTarget)
 {
  var id = event.target.value;
  if(event.target.checked)
   $("#"+id).show();
  else
   $("#"+id).hide();
    }
    //event.stopPropagation();  
    //event.preventDefault();  
}
function mouse_down(event)
{
 if (event.target !== event.currentTarget)
 {
  var id = event.target.id;

  if(ws != null)
   event.target.style.backgroundImage = "url('pressed.png')";

  console.log(id);
  send_command(id);
    }
    event.stopPropagation();  
    event.preventDefault();  
}

function mouse_up(event)
{
 if (event.target !== event.currentTarget)
 {
  if(ws != null)
   event.target.style.backgroundImage = "url('unpressed.png')";
    }
    event.stopPropagation();  
    event.preventDefault();  
}

function send_command(cmd)
{
 if(ws != null)
  if(ws.readyState == 1)
   ws.send(cmd + "\r\n");  
}

//Drawing
function draw_point(x, y, radius , color )
{
 ctx.fillStyle = color;
 ctx.beginPath();
 ctx.arc(x, y, radius, 0, 2*Math.PI);
 //ctx.closePath();  
 ctx.fill();
}
function draw_all_text()
{
 var y = 0;
 var x = -25;
 ctx.font = "12px calibri";
 ctx.fillStyle = "gray";
 ctx.textBaseline="middle";

 ctx.fillText("  0", x, y); y -= step_y;
 ctx.fillText(" 10", x, y); y -= step_y;
 ctx.fillText(" 20", x, y); y -= step_y;
 ctx.fillText(" 30", x, y); y -= step_y;
 ctx.fillText(" 40", x, y); y -= step_y;
 ctx.fillText(" 50", x, y); y -= step_y;

 /* x axis labels */
 ctx.fillText("Time (Sec.)", canvas_width - 2*padding - 30, 15);

 /* y axis labels */
 ctx.fillText("Temps ('c)", -20, - 5*step_y - 20);
}
function draw_baseline()
{
 ctx.lineWidth = 0.5;    
    ctx.strokeStyle = "gray";

    for(var i = 0; i <= 5; i++)
 {
  ctx.beginPath();
  ctx.moveTo(-5, -(i*step_y));
  ctx.lineTo(canvas_width - 2*padding, -(i*step_y));
  ctx.stroke();
 }
 ctx.lineWidth = 3;
 ctx.strokeStyle = "#707070";
 // x axis  
 ctx.beginPath();
 ctx.moveTo(0, 0);
 ctx.lineTo(canvas_width - 2*padding + 10, 0);
 ctx.stroke();
 // y axis  
 ctx.beginPath();
 ctx.moveTo(0, 0);
 ctx.lineTo(0, - 5*step_y - 10);
 ctx.stroke();
}

function draw_scratch(data)
{
 var x = canvas_width -2*padding;
 step_x = x / (max_samples-1);
 ctx.lineWidth = 3;    
    ctx.strokeStyle = "#20B2AA";

 ctx.beginPath();
 for(var i = data.length - 1; i >= 0; i--)
 {
  var y = - data[i] *step_y/10;
  ctx.lineTo(x, y);
  x -= step_x;  
 }
 ctx.stroke();
 x = canvas_width -2*padding;
 for(var i = data.length - 1; i >= 0; i--)
 {
  var y = - data[i] *step_y/10;
  draw_point(x, y, 3, "#20B2AA");
  x -= step_x;  
 }
 //Latest Temperature
 x = canvas_width -2*padding;
 y = - data[data.length - 1] *step_y/10;
 draw_point(x, y, 4, 'red');
 ctx.fillText(" " + data[data.length - 1] + "'C", x + 5, y);

 document.getElementById("blink_").innerHTML = data[data.length - 1];

}

function update_view(data)
{
 ctx.clearRect(-pivot_x, -pivot_y, canvas_width, canvas_height);
 draw_all_text();
 draw_baseline();
 draw_scratch(data);
}
function blinker() {
    $('#blink_').fadeOut(300);
    $('#blink_').fadeIn(1200);
}

setInterval(blinker, 1500);

window.onload = init;
</script>
</head>
<body>
<div id="view">
 <input type="checkbox" name="view_option" value="temp" checked> Temprature
 <input type="checkbox" name="view_option" value="graph"> Graph
 <input type="checkbox" name="view_option" value="remote_con" checked> Remote-Con
</div>
<p id="temp"> Temp: <span id="blink_">null</span> &deg;C</p>

<canvas id="graph"></canvas><br>
<div id="remote_con">
    <div id="<?php echo CMD_POWER?>" class="button"></div>
</div>
<p>
WebSocket : <span id="ws_state">null</span><br>
</p>
<button id="wc_conn" type="button" onclick="wc_onclick();">Connect</button>
</body>
</html>


User Interface

Click image for larger version  Name:	IR_repeater_ui.PNG Views:	1 Size:	39.0 KB ID:	489



Libraries