Internet infrared (IR) remote controller

Internet infrared (IR) remote controller

Postby Khanh » Wed Jun 08, 2016 6:49 pm

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:

http://www.phpoc.com/forum/viewtopic.php?f=42&t=183

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.

Let’s see demo video

phpBB [video]


System architecture

Infared System Architecture.jpg
Infared System Architecture.jpg (44.58 KiB) Viewed 2282 times


Data Flow

Data Flow.PNG
Data Flow.PNG (9.72 KiB) Viewed 2282 times


What we need:
    - PHPoC Blue
    - Infrared emitter
    - Resistor 1kOhm
    - USB wireless LAN dongle
    - Grove expansion board for PHPoC (Optional)

Wiring

Wiring.PNG
Wiring.PNG (75.48 KiB) Viewed 2282 times


Or

Wiring with Expanation Board.PNG
Wiring with Expanation Board.PNG (250.75 KiB) Viewed 2282 times


Source Code <task0.php>

Code: Select all

<?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 http://www.phpoc.com/forum/viewtopic.php?f=42&t=183 
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($data, 32);            
        
}
    }    
}
 
?>


Source Code <index.php>

Code: Select all

<?php
    $ws_host 
= _SERVER("HTTP_HOST");
    
    
// To get the command set, please visit http://www.phpoc.com/forum/viewtopic.php?f=42&t=183 
    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">&#8593;</button type="button">
    <button type="button" id="<?echo CMD_TEMP_DEC?>" class="button">&#8595;</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>

Code: Select all

<?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(1, 2);    // first value is dummy.
    $count_buf .= int2bin(16, 2);   // 9ms leading pulse burst (16 * 562.5us)
    $count_buf .= int2bin(8, 2);    //  4.5ms space (8 * 562.5us)
    $cnt_buf_len = 3;

            
    $mask 
= 1 << ($bit_length-1);
    
    while
($mask)
    {
        $count_buf .= int2bin(1, 2);    // 562.5µs pulse burst
        $cnt_buf_len++;
        
        if
($data & $mask)
        {
            $count_buf .= int2bin(3, 2);    // logical 1: 1.6875ms space
        }
        else 
        
{
             $count_buf .= int2bin(1, 2);    // Logical 0 562.5µs space
        }
        $cnt_buf_len++;
        
        $mask 
= $mask >> 1;
    }
    
    $count_buf 
.= int2bin(1, 2);    // 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 = 1 << ($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: Select all
ht0_count_buf_size = 256
ht1_count_buf_size = 256
ht2_count_buf_size = 256
ht3_count_buf_size = 256
Khanh
 
Posts: 72
Joined: Fri Mar 11, 2016 10:57 am

Return to Project

Who is online

Users browsing this forum: No registered users and 1 guest

cron