Infrared (IR) communication is a common, inexpensive and easy way to control your devices. This article describes the basic knowledge of IR communication and how to transfer data via IR with PHPoC.

A program on PHPoC transforms data you want to send to pulse signal. Before going into IR emitter, the signal need modulating at particular carrier frequency. Carrier frequencies between 30kHz and 60kHz are commonly used in consumer electronics. The most common one is 38kHz. The modulated signal goes through IR emitter, and then being transformed to IR light. At IR receiver, IR light is transform to pulse signal. Another program on PHPoC will capture this pulses and map to sequence of bits (data).

The below image shows how IR emitter and receiver works.
source: http://www.sbprojects.com/








Below is waveform of signal at different points

Click image for larger version  Name:	ir_communication_waveform_1.png Views:	1 Size:	33.4 KB ID:	465

So, we have some works to do:
  • Generate carrier signal (The most common one is 38kHz) => use a timer (timer_1) to generate PWM pulse.
  • Generate data signal => use a timer (timer_2) in “toggle” mode. Note that the signal form depends on the protocol you use. You can use the well-known protocol such as RC-5, NEC or you can define a protocol by yourself (you should define start pulse, pulse form of “0” logical and “1” logical bit and end pulse).
  • Modulation: as we can see, the modulated signal (3) = (1) AND (2). So in order to modulate signal, just perform “AND” logic between (1) and (2). It’s simple to implement. Just set output mode of timer_1 and timer_2 to “open-drain” in your code. And connect both of them to a resistor (1k). other side of resistor is connected to VCC. This is called “open collector”.
  • Fortunately, we do not need to demodulate because the IR receiver do it by itself. So we just need to capture signal from output pin of IR receiver => use a timer in “capture” mode
  • Depending on the protocol you use, you can decode to get data from pulses you received. (drop the start burst, mapping pulses to “0” or “1” bit stream, drop the ending pulse). It is worth noticing that (5) is inverse of (2).

Now let’s look at an example below. In this example, I use:
  • One Grove IR receiver
  • One Grove IR emitter
  • One or two PHPoC Blues (in case of you use only one PHPoC Blue, both sender’s and receiver code is implement on one PHPoC)
  • One resistor 1kOhm.
  • Grove expansion board for PHPoC (Optional)
  • Jumper wires

Click image for larger version  Name:	ir_communication_wire_1.PNG Views:	1 Size:	96.7 KB ID:	466

or

Click image for larger version  Name:	ir_communication_wire_2.PNG Views:	1 Size:	59.9 KB ID:	467


In order to make it simple, I simplify NEC protocol which is defined as follows:
  • Logical ‘0’: a 562.5µs pulse burst followed by a 562.5µs space, with a total transmit time of 1.125ms
  • Logical ‘1’: a 562.5µs pulse burst followed by a 1.6875ms space, with a total transmit time of 2.25ms
Click image for larger version  Name:	ir_communication_logic.png Views:	1 Size:	4.5 KB ID:	468


The below image shows 5 bit “01100” in sequence:

Click image for larger version  Name:	ir_communication_waveform_2.png Views:	1 Size:	29.7 KB ID:	469

One data frame as follows:
  • a 9ms leading pulse burst (16 times the pulse burst length used for a logical data bit)
  • a 4.5ms space
  • data
  • a final 562.5µs pulse burst to signify the end of message transmission.

The full NEC protocol will be used in the next article.

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/vd_nec_infrared.php";

$unit 562.5 5;//µs
$repc 33;
$data 0x1e;
$lower_bound 13000// 13000us, since first pulse in NEC is (9ms + 4.5ms) in theory
$upper_bound 14000;

while(
1)
{    
    
infrared_recv_start($unit$repc);

    echo 
"\r\n Send      data: $data";
    
nec_infrared_send($data); // sen 8 bit
    
sleep (1);

    
infrared_recv_stop();
    if(
infrared_available($lower_bound$upper_bound))
    {
        
$dt nec_infrared_recv(8);    // receive 8 bit    
        
echo "\r\n Receive  data: $dt";
    }    
    
sleep(2);        
}

?>



Library <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.
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($address, $cmd) 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++;
    
$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


Result

Click image for larger version  Name:	ir_communication_result.png Views:	1 Size:	99.6 KB ID:	470