Pages: [1]   Go Down
Author Topic: Arduino Universal Remote  (Read 3986 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 73
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

0
Offline Offline
Faraday Member
**
Karma: 16
Posts: 2855
ruggedcircuits.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 73
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#
# 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:
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.
Logged

0
Offline Offline
Faraday Member
**
Karma: 16
Posts: 2855
ruggedcircuits.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 73
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
« Last Edit: August 19, 2010, 01:32:06 pm by dlevans » Logged

0
Offline Offline
Faraday Member
**
Karma: 16
Posts: 2855
ruggedcircuits.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
 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
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 73
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 73
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

rawir.h:

Code:
// 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:
// 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:
// 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.
Logged

Left Coast, USA
Offline Offline
Sr. Member
****
Karma: 7
Posts: 499
Sometimes I just can't help myself.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
   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.
« Last Edit: August 21, 2010, 10:36:04 am by davekw7x » Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 73
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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


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?
« Last Edit: August 23, 2010, 02:46:27 am by dlevans » Logged

Pages: [1]   Go Up
Jump to: