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
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
or
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
The below image shows 5 bit “01100” in sequence:
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, 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>
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(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:
ht0_count_buf_size = 256 ht1_count_buf_size = 256 ht2_count_buf_size = 256 ht3_count_buf_size = 256
Result