Go Down

Topic: Arduino Universal Remote (Read 4 times) previous topic - next topic

Dustin

I've seen tons of threads and topics on how to read the IR code of a TV remote. I'm wanting to SEND the commands. The commands will be in Pronto format from http://www.remotecentral.com/

The project will end up being an Arduino with a wireless shield and an IR LED. There will be an app on a computer or other remote device that has the codes for the devices. The computer connects to the Arduino and sends the commands over the network.

Would LIRC http://www.lirc.org/ format work better?

Any help would be appreciated.

RuggedCircuits

Quote
Would LIRC http://www.lirc.org/ format work better?


Work better than what?

What are the constraints and/or goals?

--
Check out our new shield: http://www.ruggedcircuits.com/html/gadget_shield.html

Dustin

Would the LIRC format work better for coding. There aren't really any constraints on the final project I'm just trying to learn a basic on/off IR command for my TV and work my way up from there. Eventually there will be an Android App that goes on a phone that connects to the Arduino over wifi. The Android App sends the IR code to the Arduino that broadcasts it to the device. Once I get the sending of the IR codes down and at the right frequency I'll be good to go. Every other forum I've seen is just a way to record the IR codes. The codes I need are already online so I don't need to read any IR codes, just send them.

LIRC format:
Code: [Select]
#
# this config file was automatically generated
# using lirc-0.7.1(serial) on Thu May  5 21:20:20 2005
#
# contributed by martin: mab893|hotmail.com
#
# brand:                       Samsung
# model no. of remote control: none
# devices being controlled by this remote: Samsung Dynaflat 24" TV
#

begin remote

 name          Samsung-Dynaflat_TV
 bits           16
 flags SPACE_ENC|CONST_LENGTH
 eps            30
 aeps          100

 header       4909  4320
 one           818  1425
 zero          818   325
 ptrail        774
 pre_data_bits   16
 pre_data       0xE0E0
 gap          107272
 toggle_bit      0


     begin codes
         POWER                    0x40BF
         TV/VIDEO                 0x807F
         1                        0x20DF
         2                        0xA05F
         3                        0x609F
         4                        0x10EF
         5                        0x906F
         6                        0x50AF
         7                        0x30CF
         8                        0xB04F
         9                        0x708F
         0                        0x8877
         +100                     0xC43B
         PrevChannel              0xC837
         SURF                     0xBC43
         SLEEP                    0xC03F
         MTS                      0x00FF
         MUTE                     0xF00F
         DISPLAY                  0xF807
         VOL-                     0xD02F
         VOL+                     0xE01F
         CH+                      0x48B7
         CH-                      0x08F7
         MENU                     0x58A7
     end codes

end remote





Pronto Code format (for power):
Code: [Select]
0000 006d 0022 0003 00a9 00a8 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0040 0015 0015 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 0702 00a9 00a8 0015 0015 0015 0e6e

Hope that helps.

RuggedCircuits

The LIRC format makes more sense to me, but I'm not familiar with the Pronto code.

I'm not sure you're focusing on the right problem, though. The real challenge it seems is converting the sequence of 1's and 0's in whatever code you choose into modulating an IR LED. I think once you're comfortable with doing that then the encoding format is just a matter of choice.

--
Check out our new shield: http://www.ruggedcircuits.com/html/gadget_shield.html

Dustin

#4
Aug 19, 2010, 08:10 pm Last Edit: Aug 19, 2010, 08:32 pm by dlevans Reason: 1
Agreed. Once I figure out the blinking of the LED the 1's and 0's don't seem that hard (I mean, the code is already there) but how do I turn this:
"begin codes
         POWER                    0x40BF
         TV/VIDEO                 0x807F"

To on X times off X times? I feel once I get that figured out I'll be golden... or maybe I'm simplifying this a little to much?

I know that the frequency of the device is 38Khz if it helps.

RuggedCircuits

My understanding is that you have to convert those hex numbers (e.g., 0x40BF) bit-by-bit into IR transmissions. Further up in your LIRC config file it says:

Code: [Select]
 one           818  1425
 zero          818   325


My interpretation of that is that a binary 1 is represented by 818 microseconds of off time (no IR transmission) followed by 1425 microseconds of on time (IR transmission at 38 kHz), while a binary 0 is 818 microseconds off and 325 microseconds on.

Unless it's exactly backwards.

So you'd have to "write out" the POWER code 0x40BF in binary:

0100000010111111

then for each digit transmit a 0 as 818/325 and a 1 as 818/1425.

There is more to it than that as suggested by the LIRC entry: there is a header, "predata", gap, etc. I think you'll need to do a bit more research but hopefully the above points you in the right direction.

--
Check out our new shield: http://www.ruggedcircuits.com/html/gadget_shield.html

Dustin

I'm learning... I've been reading this site:
http://www.rockabilly.net/infrared/IR-PWM.shtml

It pretty much shows where the header and all of that technical stuff is. I'm just trying to make heads and tails of it all.

Dustin

Well I found the source code to a Sony PSP Universal Remote from a homebrew site.

rawir.h:

Code: [Select]

// Raw IR playback
//////////////////////////////////////////////////////////////////////////

typedef struct _RAW_IR_DATA
{
   int timing_val;
       // used for carrier frequency (freq = timing_val * 2 kHz)
       //  and for PWM sample ticks (1 tick = timing_val * 1.25 us)
   const u32* raw_buffer;
   int raw_count;
} RAW_IR_DATA;

int SendRawIR(const RAW_IR_DATA* dataP, int count);
   // return 0 if ok, -1 if error


// Common settings:
// timing_val = 20: freq = 40kHz, tick = 25us
// timing_val = 18: freq = 36kHz, tick = 22.5us
// NOTE: frequency and timing are *both* directly proportional to 'timing_val'


//////////////////////////////////////////////////////////////////////////
// utilities for building the "raw_buffer"
// each 32 bit value represents a PWM sample
//      first byte = duration (in ticks)
//               range 1->255. For longer delays, use two raw_buffer entries
//      second byte = 0
//      third byte = 1 for IR on (eg: start bit),
//                0 for IR off (eg: stop bit/idle)
//      fourth byte = 0

static inline u32 IR_SIGNAL_ON(int ticks) // .025 ms ticks
{
   assert(ticks >= 0 && ticks < 256);
   return ticks | 0x10000;
}
static inline u32 IR_SIGNAL_OFF(int ticks) // .025 ms ticks
{
   assert(ticks >= 0 && ticks < 256);
   return ticks;
}

#define IR_SIGNAL_IS_ON(lVal) (((lVal) & 0x10000) != 0)

//////////////////////////////////////////////////////////////////////////


mceir.c:

Code: [Select]

// Media Center IR (RC6 remote)

#include "std.h"
#include "mceir.h"

#include "rawir.h"

//////////////////////////////////////////////////////////////////////
// all messages sent with RC6 "6A" mode
     // LEAD + 1110 + TR_BIT_0
   // 16 bit device code: 0x800F
   // first byte of 0bX1000100
         // X toggles between 0 and 1

#define MCE_DEVICE_CODE 0x800F
#define MCE_FIRSTBYTE_A 0xC4
#define MCE_FIRSTBYTE_B 0x44

// see 'mceir.h' for codes (last byte of message)

//////////////////////////////////////////////////////////////////////
// RC6 - mode 6A
//   from - http://www.xs4all.nl/~sbp/knowledge/ir/rc6.htm

// timing_val = 18
//  carrier freq = 36kHz
//  1 tick = 22.5us
// ~20 ticks for basic time unit (444.44 us)

#define TIME_1 20
#define TIME_2 (TIME_1*2)
#define TIME_6 (TIME_1*6)

#define OPTIMIZE

// Manchester data encoding
static int AddDataBit0(u32* raw_buffer, int iL)
{
#ifdef OPTIMIZE
   if (!IR_SIGNAL_IS_ON(raw_buffer[iL-1]))
         raw_buffer[iL-1] += TIME_1; // merge it
   else
         raw_buffer[iL++] = IR_SIGNAL_OFF(TIME_1);
#else
         raw_buffer[iL++] = IR_SIGNAL_OFF(TIME_1);
#endif
   raw_buffer[iL++] = IR_SIGNAL_ON(TIME_1);
   return iL;
}

static int AddDataBit1(u32* raw_buffer, int iL)
{
#ifdef OPTIMIZE
   if (IR_SIGNAL_IS_ON(raw_buffer[iL-1]))
         raw_buffer[iL-1] += TIME_1; // merge it
   else
         raw_buffer[iL++] = IR_SIGNAL_ON(TIME_1);
#else
   raw_buffer[iL++] = IR_SIGNAL_ON(TIME_1);
#endif
   raw_buffer[iL++] = IR_SIGNAL_OFF(TIME_1);
   return iL;
}

static int AddDataByte(u32* raw_buffer, int iL, u8 bData)
   // return new 'iL'
{
   int i;
   for (i = 0; i < 8; i++)
   {
       if (bData & (0x80 >> i))
                 iL = AddDataBit1(raw_buffer, iL);
       else
                 iL = AddDataBit0(raw_buffer, iL);
   }
   return iL;
}

void send_media_center(u8 code)
{
   static bool s_bFlip = false;
   u32 raw_buffer[100]; // big enough for worst case

printf("[RC6:$%x] ", code);
   int iL = 0;

   // leader
   raw_buffer[iL++] = IR_SIGNAL_ON(TIME_6);
   raw_buffer[iL++] = IR_SIGNAL_OFF(TIME_2);
   // DATA 1110 (lead bit, mode 6)
   raw_buffer[iL++] = IR_SIGNAL_ON(TIME_1);
   raw_buffer[iL++] = IR_SIGNAL_OFF(TIME_1); // "1"
   raw_buffer[iL++] = IR_SIGNAL_ON(TIME_1);
   raw_buffer[iL++] = IR_SIGNAL_OFF(TIME_1); // "1"
   raw_buffer[iL++] = IR_SIGNAL_ON(TIME_1);
#ifdef OPTIMIZE
   raw_buffer[iL++] = IR_SIGNAL_OFF(TIME_1+TIME_1); // finish "1" and start "0"
#else
   raw_buffer[iL++] = IR_SIGNAL_OFF(TIME_1); // "1"
   raw_buffer[iL++] = IR_SIGNAL_OFF(TIME_1); // start "0"
#endif
   raw_buffer[iL++] = IR_SIGNAL_ON(TIME_1); // finish "0"
   // trail bit 0
   raw_buffer[iL++] = IR_SIGNAL_OFF(TIME_2);
   raw_buffer[iL++] = IR_SIGNAL_ON(TIME_2);

   // send customer code as two bytes
     iL = AddDataByte(raw_buffer, iL, (MCE_DEVICE_CODE>>8) & 0xFF);
     iL = AddDataByte(raw_buffer, iL, MCE_DEVICE_CODE & 0xFF);

   // flip the code prefix so receiver can detect double press vs. reflection
     iL = AddDataByte(raw_buffer, iL,
       s_bFlip ? MCE_FIRSTBYTE_A : MCE_FIRSTBYTE_B);
   s_bFlip = !s_bFlip;

   // the interesting part
     iL = AddDataByte(raw_buffer, iL, code);

   // stop bit
   raw_buffer[iL++] = IR_SIGNAL_OFF(TIME_6);
   raw_buffer[iL++] = IR_SIGNAL_OFF(50); // overkill

   assert(iL <= sizeof(raw_buffer)/sizeof(u32));

     RAW_IR_DATA irdata;
   irdata.timing_val = 36/2; // 36kHz carrier
   irdata.raw_buffer = raw_buffer;
   irdata.raw_count = iL;

   int err = SendRawIR(&irdata, 8); // repeat 8 times
   if (err != 0)
       printf("send_media_center $%x failed\n", code);
}

//////////////////////////////////////////////////////////////////////


mceir.h:

Code: [Select]

// media center

void send_media_center(u8 code);

//////////////////////////////////////////////////////////////////////
// Media Center codes
   // only specify last byte of message

#define MCE_BUTTON1 0x01
#define MCE_BUTTON2 0x02
#define MCE_BUTTON3 0x03
#define MCE_BUTTON4 0x04
#define MCE_BUTTON5 0x05
#define MCE_BUTTON6 0x06
#define MCE_BUTTON7 0x07
#define MCE_BUTTON8 0x08
#define MCE_BUTTON9 0x09
 
#define MCE_BUTTON_WHAT 0x00
#define MCE_BUTTON_OK 0x22
#define MCE_BUTTON_LEFT 0x20
#define MCE_BUTTON_RIGHT 0x21
#define MCE_BUTTON_DOWN 0x1F
#define MCE_BUTTON_UP 0x1E
#define MCE_BUTTON_PLAY 0x16
#define MCE_BUTTON_POWER 0x0C
#define MCE_BUTTON_STOP 0x19
#define MCE_BUTTON_REC 0x17
#define MCE_BUTTON_REW 0x15
#define MCE_BUTTON_FWD 0x14
#define MCE_BUTTON_PAUSE 0x18
#define MCE_BUTTON_NEXT 0x1A
#define MCE_BUTTON_PREV 0x1B
#define MCE_BUTTON_EHOME 0x0D
#define MCE_BUTTON_BACK 0x23
#define MCE_BUTTON_GUIDE 0x26
#define MCE_BUTTON_INFO 0x0F
#define MCE_BUTTON_LIVETV 0x25
#define MCE_BUTTON_VIDEO 0x4A
#define MCE_BUTTON_MUSIC 0x47
#define MCE_BUTTON_TV 0x46
#define MCE_BUTTON_PICTURES 0x49
#define MCE_BUTTON_VOL_UP 0x10
#define MCE_BUTTON_VOL_DOWN 0x11
#define MCE_BUTTON_MUTE 0x0E
#define MCE_BUTTON_CH_UP 0x12
#define MCE_BUTTON_CH_DOWN 0x13
#define MCE_BUTTON_ENTER 0x0B
#define MCE_BUTTON_CLEAR 0x0A
#define MCE_BUTTON_DVDMENU 0x24
#define MCE_BUTTON_RECTV 0x48

//////////////////////////////////////////////////////////////////////


It's almost 2 am so I haven't fully dug around the files but hopefully this will help me.

davekw7x

#8
Aug 21, 2010, 05:10 pm Last Edit: Aug 21, 2010, 05:36 pm by davekw7x Reason: 1
Here's what I did to emulate the Sony protocol on my Universal X10 remote:

I downloaded IRremote.zip from http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html and unzipped it in my sketchbook/libraries directory. (I'm using arduino-0018)

This has code to send and receive a few different protocols, including Sony, RC5 and RC6.

The transmit code is written to use Arduino PWM pin 3.  This is hard-coded and can't be changed at run time.  (The receive stuff can use any pin.)

I breadboarded an IR led to use Arduino pin 3 and tested it (successfully) with the IRsend example sketch that is supplied with the library.

There's a little more to the story:

Since I am using a "Gadget Shield" that is wired to transmit on Arduino PWM pin 9, I modified the code in the IRsend functions to use Timer1 and OCR1A/COM1A instead of Timer2 and OCR2B/COM2B.  It's not difficult, but it takes a little study.

Bottom line: If you can use PWM pin 3 for transmitting, the IRremote library may be just the ticket.  If you need another pin, it may be a "learning opportunity," but that's why we come here, right?


Regards,

Dave

Footnote: The IRsendDemo sketch has a statement like the following to transmit a particular 12-bit Sony code:
Code: [Select]

   irsend.sendSony(0xa90, 12); // Sony TV power code


It is as simple as that!

There is an IRrecvDemo sketch that can be used to learn the codes for any of the supported protocols.

Dustin

#9
Aug 22, 2010, 11:54 am Last Edit: Aug 23, 2010, 09:46 am by dlevans Reason: 1
Code: [Select]
const int TVpin = 3;
char byteIn;

const int power[] = {224, 224, 64, 191};
const int vol_up[] = {224, 224, 224, 31};
const int vol_down[] = {224, 224, 208, 47};
const int ch_up[] = {224, 224, 72, 183};
const int ch_down[] = {224, 224, 8, 247};
const int tv_menu[] = {224, 224, 88, 167};

const int LEADER_PULSE=4400;
const int PERIOD=26;
const int WAIT_TIME=11;
const int PULSE_BIT=600;
const int PULSE_ONE=1600;
const int PULSE_ZERO=500;

void blastON(const int time, const int pin) {
 int scratch = time;
 while(scratch > PERIOD)
 {
   digitalWrite(pin, HIGH);
   delayMicroseconds(WAIT_TIME);
   digitalWrite(pin, LOW);
   delayMicroseconds(WAIT_TIME);
   scratch = scratch - PERIOD;
 }
}

void blastOFF(int time) {
 delayMicroseconds(time);
}

void blastByte(const int code, const int pin) {
 int i;
 for(i = 7; i > -1; i--)
 {
   
   if(1 << i & code) //check if the significant bit is 1
   {
     blastON(PULSE_BIT, pin);
     //Serial.print("1");
     blastOFF(PULSE_ONE);
   }
   else
   {
     blastON(PULSE_BIT, pin);
     //Serial.print("0");
     blastOFF(PULSE_ZERO);
   }
 }
 //Serial.print("\n");
}

void command(const int irCode[], const int pin)
{
 int i;
 blastON(LEADER_PULSE-200, pin);
 blastOFF(LEADER_PULSE);
 for(i = 0; i < 4; i++)
 {
   blastByte(irCode[i], pin);
 }
 blastON(PULSE_BIT,pin);
 //blastOFF(LEADER_PULSE);
 delay(47);
}

void setup() {
 Serial.begin(9600);
 Serial.print("Welcome to Arduino. Enjoy your stay.\n");
 Serial.print("Poor Man's Remote: Menu\n1) TV Power\n2) TV Volume Up\n3) TV Volume Down\n4) TV Channel Up\n5) TV Channel Down\n6) TV Menu\n");
 pinMode(TVpin, OUTPUT);
}
void loop() {
 while( Serial.available() > 0)
 {
   byteIn = Serial.read();
   switch(byteIn)
   {
     case '1':
       Serial.print("Sending TV Power...\n");
       for(int i = 0; i < 4; i++)
       {
         command(power, TVpin);
       }
       Serial.print("Sent.\n");
       break;
     case '2':
       Serial.print("Sending TV Volume Up...\n");
        for(int i = 0; i < 4; i++)
       {
         command(vol_up, TVpin);
       }
       Serial.print("Sent.\n");
       break;
     case '3':
       Serial.print("Sending TV Volume Down...\n");
       command(vol_down, TVpin);
       Serial.print("Sent.\n");
       break;
     case '4':
       Serial.print("Sending TV Channel Up...\n");
       command(ch_up, TVpin);
       Serial.print("Sent.\n");
       break;
   case '5':
       Serial.print("Sending TV Channel Down...\n");
       command(ch_down, TVpin);
       Serial.print("Sent.\n");
       break;
   case '6':
       Serial.print("Sending TV Menu...\n");
       command(tv_menu, TVpin);
       Serial.print("Sent.\n");
       break;
     default:
       Serial.print("Hey! Listen to directions, idiot.\n");
   }
     Serial.print("Poor Man's Remote: Menu\n1) TV Power\n2) TV Volume Up\n3) TV Volume Down\n4) TV Channel Up\n5) TV Channel Down\n6) TV Menu\n");
 }
}


All credit for this sketch comes from this thread:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1270451288

I just figured out what he did and replicated it.

Okay so I'm going to try and explain this:

This is the Power Command for a Samsung tv:
Code: [Select]
0000 006d 0022 0003 00a9 00a8 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 003f 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003f 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0040 0015 0015 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 003f 0015 0702 00a9 00a8 0015 0015 0015 0e6e

0000 006d 0022 0003 00a9 00a8  This is the header for "learned" IR commands, the lead in bit, the frequency and all of that other stuff.

0015 003f   this is a bit for a "1"
0015 0015  this is a bit for a "0"

0015 0702 00a9 00a8 0015 0015 0015 0e6e  this is the rest of the header, leadin, lead out, etc stuff.

All you need (according to that sketch I found (and edited) all you need is the 1's and 0's from the 0015 003f and 0015 0015 bits. So for example the power command I showed you earlier would convert to binary like this:

11100000111000000100000010111111

You convert this binary number into decimal (I use http://home2.paulschou.net/tools/xlate/) and you get this number:

224 224 64 191  

Which is our power command.

An easy way that I've found to do the hex codes to binary is copy them all in wordpad, separate the leadin headers, search for 0015 0015 and replace them with 0's, search for 0015 003f and replace them with 1's, finally replace " " (space) with "" (nothing) and you should end up with a binary number with no spaces. Put that binary number in the site and click convert, copy down the dec numbers, plug them into the sketch and you're done.

Video of it working:
http://www.youtube.com/watch?v=OMUjBwMnjTk

Thank you for all of your help. I know this works for Samsung but is there any way to make a truly universal remote? Should I make a library?

Go Up