In this article, I am going to show how to replace your IR remote controller with “internet IR remote controller”. With this application, we can turn on/off or control your devices on our way to work/home, and just come to offices/ back to home and enjoy the conditioned room without waiting.

Before going into detail. Let’s review some previous articles:

Article 1 describes the basic knowledge of IR communication and how to transmit/receive data through IR using PHPoC.

Article 2 shows how to get a command set of the remote controllers. Through this article, I already known the command set of a remote controller.

Demo





System Architecture

Click image for larger version  Name:	Internet_IR_System Architecture.jpg Views:	1 Size:	25.2 KB ID:	483



Data Flow

Click image for larger version  Name:	Internet_IR_Data Flow.PNG Views:	1 Size:	5.8 KB ID:	484


Things Used In This Project
  • PHPoC Blue
  • Infrared emitter
  • Resistor 1kOhm
  • USB wireless LAN dongle
  • Grove expansion board for PHPoC (Optional)


Wiring Diagram

Click image for larger version  Name:	Internet_IR_Wiring.PNG Views:	1 Size:	75.8 KB ID:	485

Or
Click image for larger version  Name:	Internet_IR_Wiring with Expanation Board.PNG Views:	1 Size:	232.5 KB ID:	486


Source Code

Source Code <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";

define("AIRCON_ADDRESS",   0x01ff);

// To get the command set, please visit https://forum.phpoc.com/blogs/khanh-s-blog/471-capture-and-analyze-infrared-ir-signal-from-remote-controller
define("CMD_POWER",   57);
define("CMD_AUTO",   105);
define("CMD_WIND_STRONG",   153);
define("CMD_WIND_NORMAL",   217);
define("CMD_WIND_WEAK",   89);
define("CMD_TEMP_INC",   41);
define("CMD_TEMP_DEC",   169);
define("CMD_FUNCTION",   185);
define("CMD_TIMER",   121);
define("CMD_SLEEP",   249);

$rbuf "";

ws_setup(0"phpoc_serial_camera""csv.phpoc");


while(
1)
{        
    if(
ws_state(0) == TCP_CONNECTED)
    {
        
$rlen ws_read_line(0$rbuf);

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

?>


Source Code <index.php>

PHP Code:
<?php
    $ws_host 
_SERVER("HTTP_HOST");

    
// To get the command set, please visit https://forum.phpoc.com/blogs/khanh-s-blog/471-capture-and-analyze-infrared-ir-signal-from-remote-controller
    
define("CMD_POWER",   57);
    
define("CMD_AUTO",   105);
    
define("CMD_WIND_STRONG",   153);
    
define("CMD_WIND_NORMAL",   217);
    
define("CMD_WIND_WEAK",   89);
    
define("CMD_TEMP_INC",   41);
    
define("CMD_TEMP_DEC",   169);
    
define("CMD_FUNCTION",   185);
    
define("CMD_TIMER",   121);
    
define("CMD_SLEEP",   249);
?>
<!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; }
#img {
    margin-right: auto;
    margin-left: auto;
    width: 320px;
    height: 240px;
    background-size: contain;
    position: relative;
    margin-bottom: 10px;
    border: 1px solid #000;
}
#container {
    margin-right: auto;
    margin-left: auto;
    width: 480px;
    height: 480px;
    position: relative;
    margin-bottom: 10px;
    border: 1px solid #000;
}
.button {
    position: absolute;
    width:100px; height:50px;
    background-color: gray;
}

</style>
<script type="text/javascript">
var CMD_POWER =   57;
var CMD_AUTO =   105;
var CMD_WIND_STRONG =   153;
var CMD_WIND_NORMAL =   217;
var CMD_WIND_WEAK =   89;
var CMD_TEMP_INC =   41;
var CMD_TEMP_DEC =   169;
var CMD_FUNCTION =   185;
var CMD_TIMER =   121;
var CMD_SLEEP =   249;

var btn_array = [
    CMD_AUTO,
    CMD_WIND_STRONG,
    CMD_WIND_NORMAL,
    CMD_WIND_WEAK,
    CMD_TEMP_INC,
    CMD_TEMP_DEC,
    CMD_FUNCTION,
    CMD_TIMER,
    CMD_SLEEP,
    CMD_POWER
]
var cmd = 0;
var ws = null;

function init()
{

    var container = document.querySelector("#container");
    container.addEventListener("touchstart", mouse_down);
    container.addEventListener("touchend", mouse_up);
    container.addEventListener("touchcancel", mouse_up);
    container.addEventListener("mousedown", mouse_down);
    container.addEventListener("mouseup", mouse_up);
    container.addEventListener("mouseout", mouse_up);    

    var btn_width = 80;
    var btn_height = 40;
    var gap = 20;

    document.getElementById("container").style.width = (3*btn_width + 4*gap) + "px";
    document.getElementById("container").style.height = (5*btn_height + 6*gap) + "px";

    var y = 20;
    var x = 20;
    for(var i = 0; i < 4; i++)
    {
        document.getElementById(btn_array[i]).style.width = btn_width + "px";
        document.getElementById(btn_array[i]).style.height = btn_height + "px";
        document.getElementById(btn_array[i]).style.left = x + "px";
        document.getElementById(btn_array[i]).style.top = y + "px";
        y += btn_height + gap;
    }
    x += btn_width + gap;
    y = 20;
    for(var i = 4; i < 6; i++)
    {
        document.getElementById(btn_array[i]).style.width = btn_width + "px";
        document.getElementById(btn_array[i]).style.height = btn_height + "px";
        document.getElementById(btn_array[i]).style.left = x + "px";
        document.getElementById(btn_array[i]).style.top = y + "px";
        y += btn_height + gap;
        if(i == 4)
            y += btn_height + gap;
    }
    x += btn_width + gap;
    y = 20;
    for(var i = 6; i < 10; i++)
    {
        document.getElementById(btn_array[i]).style.width = btn_width + "px";
        document.getElementById(btn_array[i]).style.height = btn_height + "px";
        document.getElementById(btn_array[i]).style.left = x + "px";
        document.getElementById(btn_array[i]).style.top = y + "px";
        y += btn_height + gap;
    }
    document.getElementById(btn_array[9]).style.top = y + "px";
    document.getElementById(btn_array[9]).style.borderRadius = btn_height + "px";
}
function ws_onmessage(e_msg)
{

}

function ws_onopen()
{
    document.getElementById("ws_state").innerHTML = "OPEN";
    document.getElementById("wc_conn").innerHTML = "Disconnect";
}
function ws_onclose()
{
    document.getElementById("ws_state").innerHTML = "CLOSED";
    document.getElementById("wc_conn").innerHTML = "Connect";
    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 $ws_host?>/phpoc_infrared", "csv.phpoc");
        document.getElementById("ws_state").innerHTML = "CONNECTING";

        ws.onopen = ws_onopen;
        ws.onclose = ws_onclose;
        ws.onmessage = ws_onmessage;
    }
    else
        ws.close();
}
function mouse_down(event)
{
    if (event.target !== event.currentTarget)
    {
        var id = event.target.id;
        document.getElementById(id).style.backgroundColor = "rgb(0, 255, 255)";
        //console.log(id);
        send_command(id);
    }
    event.stopPropagation();    
    event.preventDefault();    
}

function mouse_up(event)
{
    if (event.target !== event.currentTarget)
    {
        var id = event.target.id;
        document.getElementById(id).style.backgroundColor = "gray";
    }
    event.stopPropagation();  
    event.preventDefault();    
}

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

window.onload = init;
</script>
</head>
<body>
<div id="container">
    <button type="button" id="<?echo CMD_POWER?>" class="button">전원</button type="button">
    <button type="button" id="<?echo CMD_AUTO?>" class="button">자동</button type="button">
    <button type="button" id="<?echo CMD_WIND_STRONG?>" class="button">강</button type="button">
    <button type="button" id="<?echo CMD_WIND_NORMAL?>" class="button">중</button type="button">
    <button type="button" id="<?echo CMD_WIND_WEAK?>" class="button">하</button type="button">
    <button type="button" id="<?echo CMD_TEMP_INC?>" class="button">↑</button type="button">
    <button type="button" id="<?echo CMD_TEMP_DEC?>" class="button">↓</button type="button">
    <button type="button" id="<?echo CMD_FUNCTION?>" class="button">기능</button type="button">
    <button type="button" id="<?echo CMD_TIMER?>" class="button">타이머</button type="button">
    <button type="button" id="<?echo CMD_SLEEP?>" class="button">수면</button type="button">
</div>
<p>
WebSocket : <span id="ws_state">null</span><br>
</p>
<button id="wc_conn" type="button" onclick="wc_onclick();">Connect</button>
</body>
</html>


Library <lib/vd_nec_infrared.php>

PHP Code:
<?php

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

include_once "/lib/sd_340.php";

define("BASIC_CLOCK",   42000000); // basic clock of PHPoC 42MHz

$recv_ht_id 0// timer id which connect to an infrared receiver to capture data
$emit_control_ht_id 1// timer id which control an infrared emitter
$emit_carrier_ht_id 2// timer id which create the carier for infrared modulation

function infrared_setup($rec_ht_id$control_ht_id$carrier_ht_id)
{
    global 
$recv_ht_id;
    global 
$emit_control_ht_id;
    global 
$emit_carrier_ht_id;

    
$recv_ht_id $rec_ht_id;
    
$emit_control_ht_id $control_ht_id;
    
$emit_carrier_ht_id $carrier_ht_id;
}
/*
This function make timer to start capturing signal from an infrared receiver.
It is used to analyze the pulse chain, so it it set to capture toggle mode.
Paramerer:
    -$unit (microsecond);
    -$repc: the number of pulse need to be captured
*/
function infrared_capture_start($unit$repc)
{
    global 
$recv_ht_id;

    
$repc++; // plus one dummy pulse
    
$unit $unit BASIC_CLOCK 1000000;
    
// setup capture timer
    
ht_ioctl($recv_ht_id"reset");
    
ht_ioctl($recv_ht_id"set div $unit");
    
ht_ioctl($recv_ht_id"set mode capture toggle");
    
ht_ioctl($recv_ht_id"set trigger from pin fall");
    
ht_ioctl($recv_ht_id"set repc $repc");
    
ht_ioctl($recv_ht_id"start"); // start trigger pulse    
}

/*
This function make timer to start capturing signal from an infrared receiver.
It is used to get data, so it it set to capture fall mode.
Paramerer:
    -$unit (microsecond);
    -$repc: the number of pulse need to be captured
*/
function infrared_recv_start($unit$repc)
{
    global 
$recv_ht_id;

    
$repc++; // plus one dummy pulse
    
$unit $unit BASIC_CLOCK 1000000;
    
// setup capture timer
    
ht_ioctl($recv_ht_id"reset");
    
ht_ioctl($recv_ht_id"set div $unit");
    
ht_ioctl($recv_ht_id"set mode capture fall");
    
ht_ioctl($recv_ht_id"set trigger from pin fall");
    
ht_ioctl($recv_ht_id"set repc $repc");
    
ht_ioctl($recv_ht_id"start"); // start trigger pulse    
}

function 
infrared_recv_stop()
{
    global 
$recv_ht_id;

    
ht_ioctl($recv_ht_id"stop");
}

function 
infrared_carrier_start($freq)
{
    global 
$emit_carrier_ht_id;

    
$div BASIC_CLOCK / ($freq 2);

    
ht_ioctl($emit_carrier_ht_id"reset");
    
ht_ioctl($emit_carrier_ht_id"set div $div"); // div 13.14us
    
ht_ioctl($emit_carrier_ht_id"set mode output pwm");
    
ht_ioctl($emit_carrier_ht_id"set output od");
    
ht_ioctl($emit_carrier_ht_id"set count 1 1");
    
ht_ioctl($emit_carrier_ht_id"start");
}

function 
infrared_carrier_stop()
{
    global 
$emit_carrier_ht_id;

    
ht_ioctl($emit_carrier_ht_id"stop");
    
ht_ioctl($emit_carrier_ht_id"set output high");
}

/*
see nec_infrared_send($data, $bit_length) function to know how to use this function
*/
function infrared_emit($count_buf$cnt_buf_len$unit)
{
    global 
$emit_control_ht_id;

    
$unit $unit BASIC_CLOCK 1000000;

    
infrared_carrier_start(38000); //enable 38KHz PWM signal - carrier frequency

    //ht_ioctl($emit_control_ht_id, "reset");
    
ht_ioctl($emit_control_ht_id"set div $unit");
    
ht_ioctl($emit_control_ht_id"set mode output toggle"); // set mode: toggle
    
ht_ioctl($emit_control_ht_id"set output od");

    
$pid_ht pid_open("/mmap/ht$emit_control_ht_id");
    
pid_write($pid_ht$count_buf);
    
pid_ioctl($pid_ht"set repc $cnt_buf_len");
    
pid_close($pid_ht);

    
ht_ioctl($emit_control_ht_id"start"); // start HT

    
while(ht_ioctl($emit_control_ht_id"get state"));

    
ht_ioctl($emit_control_ht_id"stop");

    
infrared_carrier_stop(); // stop to save energy.
}

/*
Paramerer:
    -$lower_bound: minimum value of the first captured pulse in microsecond
    -$upper_bound: minimum value of the first captured pulse in microsecond
Return:
    -true: value of the first captured pulse varies from $lower_bound to $upper_bound
    -false: otherwise.
*/
function infrared_available($lower_bound$upper_bound)
{
    global 
$recv_ht_id;

    
$unit ht_ioctl($recv_ht_id"get div"); // in number clock stick
    
$unit $unit 1000000 BASIC_CLOCK// in microsecond
    
$lower_bound /= $unit;
    
$upper_bound /= $unit;

    
$count ht_ioctl($recv_ht_id"get count 1");

    if( (
$count >= $lower_bound) && ($count <= $upper_bound))
        return 
true;

    return 
false;
}

/*
Paramerer: $count_id start from 0.
Return: width of captured pulse in microsecond
*/
function infrared_get_count($count_id)
{
    global 
$recv_ht_id;

    
$count_id++; // due to the dummy value (first value)
    
$unit ht_ioctl($recv_ht_id"get div");
    
$count ht_ioctl($recv_ht_id"get count $count_id");
    
$reval $unit $count 1000000 BASIC_CLOCK;

    return 
$reval;
}

function 
nec_infrared_send($data$bit_length)
{

    
$unit 562.5;// 562.5µs

    
$count_buf int2bin(12);    // first value is dummy.
    
$count_buf .= int2bin(162);   // 9ms leading pulse burst (16 * 562.5us)
    
$count_buf .= int2bin(82);    //  4.5ms space (8 * 562.5us)
    
$cnt_buf_len 3;


    
$mask << ($bit_length-1);

    while(
$mask)
    {
        
$count_buf .= int2bin(12);    // 562.5µs pulse burst
        
$cnt_buf_len++;

        if(
$data $mask)
        {
            
$count_buf .= int2bin(32);    // logical 1: 1.6875ms space
        
}
        else
        {
             
$count_buf .= int2bin(12);    // Logical 0 562.5µs space
        
}
        
$cnt_buf_len++;

        
$mask $mask >> 1;
    }

    
$count_buf .= int2bin(12);    // 562.5µs stop code
    
$cnt_buf_len++;

    
infrared_emit($count_buf$cnt_buf_len$unit);    
}

function 
nec_infrared_recv($bit_length)
{    
    
$data 0;
    
$mask << ($bit_length -1);
    for(
$i 1$i <= $bit_length$i++)
    {
        
$us infrared_get_count($i);
        if(
$us 2500)
        if(
$us 1500)
        {
            
$data |= ($mask>> ($i-1));
        }
    }
    return 
$data;
}


?>


Config file <phpoc.ini>

Code:
ht0_count_buf_size = 256
ht1_count_buf_size = 256
ht2_count_buf_size = 256
ht3_count_buf_size = 256