Infrared (IR) communication using PHPoC

Infrared (IR) communication using PHPoC

Postby Khanh » Fri Jun 03, 2016 5:27 pm

Infrared (IR) communication using PHPoC

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.

Image



Below is waveform of signal at different points

waveform_1.png
waveform_1.png (32.39 KiB) Viewed 2062 times


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:

- An infrared emitter

- An infrared receiver

- 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)

wire_1.PNG
wire_1.PNG (108.59 KiB) Viewed 2062 times


or

wire_2.PNG
wire_2.PNG (65.44 KiB) Viewed 2062 times


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

logic.png
logic.png (4.28 KiB) Viewed 2062 times


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

waveform_2.png
waveform_2.png (28.7 KiB) Viewed 2062 times


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>
Code: Select all

<?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, 8 ); // 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>
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.
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(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

Result:
result.png
result.png (101.61 KiB) Viewed 2062 times
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