TV-B-Gone for the Arduino

I've ported the TV-B-Gone code to run on the Arduino board. If you haven't seen a TV-B-Gone, it's a cute gadget that you point at a TV that's bothering you, and it turns the TV off. Internally, it's an infrared remote that broadcasts more than 100 different off codes that work on almost any TV. I figured it would be interesting to get the TV-B-Gone running on the Arduino.
TV-B-Gone running on an Arduinio

Update (November 2010)

I have a new, improved version of the software. Details and download are here.

The software

I started with the TV-B-Gone firmware, which is available under Creative Commons license. The TV-B-Gone runs on an ATtiny85 microcontroller, not the ATmega328 used by the Arduino, so some porting was required. One tricky part of the port is the IR signals are generated using the low-level hardware timer for PWM (pulse-width modulation). Since the ATtiny85 runs at 8MHz, and the Arduino runs at 16MHz, the timing code needed to be re-written, both the 10us delay loop and the timer register operations. The quick summary is I'm using Timer 2, scaling the clock by 8, using OCR2A to control the frequency and OCR2B to control the duty cycle, and have output on pin 3 (OC2B). (You are not expected to understand this. See my article on Secrets of Arduino PWM for details of how these timers work.)
#define freq_to_timerval(x) (F_CPU / 8 / x - 1)
...
    pinMode(IRLED, OUTPUT);
    TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
    TCCR2B = _BV(WGM22) | _BV(CS21);
...
    OCR2A = freq; 
    OCR2B = freq / 3; // 33% duty cycle
Another tricky thing is the original firmware stores the IR codes in program space, not RAM. The microcontrollers use a Harvard architecture, which means they have separate memory and data paths for code and data, unlike normal processors that use a von Neumann architecture. Because RAM storage is so small on these microcontrollers, the IR codes are stored in program space, with the result you need to use special methods to access them, rather than normal C pointers. Program memory storage is indicated with the PROGMEM macro and pulled out of memory using special functions such as pgm_read_byte. This makes the code somewhat confusing, for example:
const struct IrCode *NApowerCodes[] PROGMEM = {
  &code_na000Code,
...
}

data_ptr = (PGM_P)pgm_read_word(NApowerCodes+i);  

The original firmware stores the codes as compressed indices into tables of durations. See the TV-B-Gone design for details. Unfortunately, the Arduino's compiler didn't like the zero-length array in the IrCode structure, so I needed to rewrite the long file of codes to include another level of indirection.

Apart from these factors, porting was straightforward, and I tried to keep the original code where possible. I ripped out all the power-up and watchdog code. I replaced the low-level serial code with the Arduino's Serial library. Finally, I packaged it up into an Arduino sketch, which you can download. (Update: get the improved version here.)

The hardware

The hardware is straightfoward. I use pin 13 for the status LED; some Arduinos conveniently have an LED already wired up. Pin 3 is the PWM output to the IR LED through a 100 ohm resistor. If you want more range, add a driver transistor and use multiple IR LEDs. Pin 0 (update: pin 5) selects North American or European codes; leave it unconnected for North America and ground it for European. Pin 1 (update: 2) is connected via a pushbutton to ground to trigger the code transmission. (Update: pins have changed in the new version.)

To use the Arduino TV-B-Gone, point the IR LED at your TV, push the button, and your TV should turn off when the circuit hits the right code, which could take up to a minute. The visible LED should flash for each code that is transmitted. If the circuit doesn't work, use a cellphone camera to verify that the IR LED is transmitting. Don't expect more than a few feet range unless you use a transistor to increase the power. The code will print debugging output to the serial port if you set DEBUG in main.h.

Summary

Is the Arduino TV-B-Gone practical? If you want a TV-B-Gone that you can carry around to amaze your friends, the kit or product is much more compact, has high-powered IR outputs for longer range, and is nicely packaged. However, the Arduino platform is convenient for experimenting, and many people already have it, so I expect people will find it interesting.

You might wonder why I'm not using my Arduino Infrared Library for this project. Actually, I plan to at some point, but I wanted to get the straightforward port working first. In addition, my library will need some extensions to support all the codes that the TV-B-Gone supports.

I hope you enjoy the Arduino TV-B-Gone, and remember to use it for good, not evil!

43 comments:

  1. Hi,

    I use an atmega 328 on my arduino and i don't have light on pin 3 for the IrLED

    thank you for your article and the sources

    [email protected]

    ReplyDelete
  2. Sorry, it works very well on philips tv

    ReplyDelete
  3. Great work! I've been waiting for this since I first laid eyes on the Arduino and the TV-B-Gone.

    ReplyDelete
  4. I loved this so much i built a PCB for it.

    http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1261875095

    ReplyDelete
  5. hi there, i've tried your circuit on a breadboard with half success, i can get it to work with the pin 0 grounded for the EU codes, my option was to set the default one in the sketch to EU instead of the US, can you please post the full circuit schematic, perhaps in fritzing

    Gratz, Afonso

    ReplyDelete
  6. this is neat, but could you make a version to turn every tv to the discovery channel?

    Imagine a packed sportsbar on the big game switched over to mythbusters!

    Blowed 'em up real good!

    ReplyDelete
  7. I've added sleep() to this, as leaving the arduino running full bore, all the time, the batteries wouldn't last long!

    I didnt want to toss yet-another-version out there, so I'll leave it to the reader to add, and Ken to incorporate into Version 2. You can read about it on my blog... http://www.ka1kjz.com/?p=561

    ReplyDelete
  8. Re ladyada's four LED model (1.2).
    ( See http://www.ladyada.net/make/tvbgone/design.html ).

    Unless I'm mistaken, the PNP transistor that drives the four NPNs will introduce an inversion in the output. Am I misreading the schematic? Is this an issue? Or does she provide inverted input to the PNP in this model?

    ReplyDelete
  9. What could be the range with one IR led ?

    ReplyDelete
  10. Hello i am using arduino diecimila (USB Powered) and sketch works only when using DEBUG = 1. I've tried to comment out Serial.begin(9600); line to disable serial even with DEBUG enabled, but it also didn't worked. When i left serial debuging enabled, i was able to turn my s*ny TV off and on, but i think it causes suboptimal speed of transmission, when i need to wait for serial line before each code is transmited...

    and BTW i am getting
    NA Codesize: some_reasonable number,
    but
    EU Codesize: 0

    (i live in EU so i have shorted 0 and GND together. I don't know what should i think about this...)

    ReplyDelete
  11. It seems the EU codes are disabled by default. You can add "#define EU_CODES" to the beginning of main.h to enable them.

    ReplyDelete
  12. Hi,

    When I try and compile the sketch I get the errors:




    o:(.progmem.data+0x0): multiple definition of `NApowerCodes'
    /var/folders/sJ/sJhEJAz6HZiIGNVVaTTBwk+++TI/-Tmp-/build7674028882894173597.tmp/TVB/WORLDcodes.cpp.o:(.progmem.data+0x0): first defined here

    o
    o:(.progmem.data+0xe6): multiple definition of `EUpowerCodes'
    /var/folders/sJ/sJhEJAz6HZiIGNVVaTTBwk+++TI/-Tmp-/build7674028882894173597.tmp/TVB/WORLDcodes.cpp.o:(.progmem.data+0x112): first defined here

    o:(.data.num_NAcodes+0x0): multiple definition of `num_NAcodes'
    /var/folders/sJ/sJhEJAz6HZiIGNVVaTTBwk+++TI/-Tmp-/build7674028882894173597.tmp/TVB/WORLDcodes.cpp.o:(.data.num_NAcodes+0x0): first defined here

    o:(.data.num_EUcodes+0x0): multiple definition of `num_EUcodes'
    /var/folders/sJ/sJhEJAz6HZiIGNVVaTTBwk+++TI/-Tmp-/build7674028882894173597.tmp/TVB/WORLDcodes.cpp.o:(.bss.num_EUcodes+0x0): first defined here

    Can anybody help me with this?
    Am I missing something obvious?

    Thanks

    ReplyDelete
  13. Does anyone know if the codes work on Stereo equipment rather than TV's?

    My neighbour has loud parties and this would put a spoke in the wheels :-)

    ReplyDelete
  14. i built this using a arduino pro mini 16mhz 5v

    it runs and you can see the IR flashing away (using a phone cam), but it never turns my tv off :(

    im from the uk and have it set to EU

    ReplyDelete
  15. The EU_CODES seems to be broken.

    ReplyDelete
  16. iv added the define for that EU codes and it is running through like 120 of them but none seam to switch any of the tvs off in my house, could it be a timing issue?

    ReplyDelete
  17. I haven't tested the EU codes. Since I don't have a European TV, it's hard for me to figure out what the problem is. (By the way, is it the same Anonymous or 3 different Anonymouses who are trying to get EU codes working?)

    ReplyDelete
  18. two of them where me hehe, it runs fine but does not turn any tvs off, should i try it on a few more tvs?

    im running the 328 arduino mini

    ReplyDelete
  19. @russdx: I haven't tried the Arduino 328 mini, so I don't know if that's causing the problems. My first thought was the frequency might be different, but the 328 mini and the Duemilanove both run at 16 MHz. My second thought is the pin connections might be different with the mini, so make sure you're actually getting an output from the IR LED. (You can use a cellphone camera to see the IR.) My third thought is that the EU code might not be working (since I didn't test it at all, not having European televisions lying around). Have any of my European readers managed to get the EU codes working?

    It's also possible the codes just don't work with your TVs, but that seems unlikely since the TV-B-Gone has so many codes. You could try the US codes and see if that works any better.

    ReplyDelete
  20. Hi !
    I've just tried your TV-B-Gone with a duemilanove (so atmega 328P) and worked very well with my french TV (so EU codes worked fine for me). Thanks a lot !
    I'm currently working on a transportable version so i can test it in a TV shop tomorrow ;)
    Since i'm planning to organize a workshop to build your arduino tvBgone, i'd like to know if it works with Atmega168. Because they're cheaper than the 328P and would reduce the workshop participation fee.
    Thanks again,
    phae

    ReplyDelete
  21. Hi !

    Is it working well on ATmega 168 ?
    (as I said earlier the 168's are cheaper to build a barebone arduino)

    Thanks

    ReplyDelete
  22. yeah the little ir sensor is flashing away

    very strange, i will try it on a few more tvs trying us/eu codes

    ReplyDelete
  23. Mitch Altman fixed your code here:

    http://github.com/zoobab/tvbgone-arduino/

    The EU codes were not working properly.

    ReplyDelete
  24. Mitch Altman and I have made some improvements to the code; among other things, the European codes now work. See Improved Arduino TV-B-Gone for details.

    ReplyDelete
  25. Works very well for me, thank you!

    ReplyDelete
  26. This comment has been removed by the author.

    ReplyDelete
  27. Hey. Which compiler do you use??? How could you include main.h and so on??? If i load TVB.pde a have millions of mistakes. Could you help me please.

    ReplyDelete
  28. I have the same error described here:

    http://arduino.cc/forum/index.php?topic=51923.0;wap2

    (same as quoted by James above)

    I am using BoArdino that I made in Mitch's "Arduino for newbies" workshop.
    I put the sketch in the library folder thus:
    \Arduino\arduino-1.0.2\libraries\TVB

    thanks for advice,
    Hilary
    [email protected]

    ReplyDelete
  29. I would like to run this on 3.3v 8MHz arduino. Seems timing will be an issue. What you would recommend?

    ReplyDelete
  30. Hello Ken,

    Really wonderful work.

    I am from India, and trying to make this circuit.

    So do I need to put India codes for this, as I can see north america and European codes or is this an universal circuit?

    Also, I am not able to compile the code, receiving errors

    TVB\WORLDcodes.cpp.o:(.progmem.data+0x0): multiple definition of `NApowerCodes'
    WORLDcodes.cpp.o:(.progmem.data+0x0): first defined here
    TVB\WORLDcodes.cpp.o:(.progmem.data+0x112): multiple definition of `EUpowerCodes'
    WORLDcodes.cpp.o:(.progmem.data+0x112): first defined here
    TVB\WORLDcodes.cpp.o:(.data.num_NAcodes+0x0): multiple definition of `num_NAcodes'
    WORLDcodes.cpp.o:(.data.num_NAcodes+0x0): first defined here
    TVB\WORLDcodes.cpp.o:(.data.num_EUcodes+0x0): multiple definition of `num_EUcodes'
    WORLDcodes.cpp.o:(.data.num_EUcodes+0x0): first defined here

    I have not put the code in the libraries folder, but inside the sketchbook folder, still getting the error.

    What could be the reason for this?

    Thanks

    ReplyDelete
  31. Hello.

    I'm trying to make it working with my Arduino nano v3, but it doesn't work...
    Actually, it works with my Arduino Uno, but not with the Nano.

    Could you tell me what to do ?

    ReplyDelete
  32. it gives an error for my :p
    I tried it on arduino mega adk...

    ReplyDelete
  33. I updated the TV-B-Gone sketch so that it works on all version of the Arduino software to date (through V1.6.5). Please see my Arduino For Total Newbies workshop page for the link to download the TV-B-Gone sketch (download #3):
    http://cornfieldelectronics.com/cfe/projects/tvbg_arduino/tvbg_arduino_workshop.php

    ReplyDelete
  34. Oh, and this new sketch also works with Leonardo.

    ReplyDelete
  35. Did Ken Shirriff ever port his TV-B-Gone Code over to use his Arduino Infrared Library?

    If so, anyone with a URL to the ported code?

    ReplyDelete
  36. No, I never ported the TV-B-Gone code to use my IR library. It worked fine without it, so I didn't see the point.

    ReplyDelete
  37. Has anyone experimented in removing all the 38Khz timing code from the sketch, and switch the code over to use the library Ken has supplied, and then using an lm555, triggering pin 4 on the lm555 which supplies the 38Khz modulation, and frees the arduino to ONLY be responsible for sending the code bits?

    This should allow us to simply use the lirc codes, and be any to choose any and all codes from there, and it simply becomes, "plug in the code and play!"

    ReplyDelete
  38. hi, does this work on an digispark?
    (with an attiny at16 mhz)

    ReplyDelete
  39. I have put the latest version of TV-B gone on a solderless breadboard.

    Using only one IR LED thus far but will add more and spray IR stuff from a great distance.
    I am using direct drive from pin 3 on my Uno with a 10 ohm resistor in series with the LED to ground. Might add 2N2222 transistors instead of the direct from the Uno pin #3.
    I also changed the pinMode for the NA / EU option to default pulled up rather than rely "floating" which could change while running due to all sorts of stuff.
    Temperature change, RF interference, static charge etc.
    pinMode INPUT_PULLUP worked with out problem.

    Only problem so far is that for my Sony TV it turns the TV off and then shortly after turns it back on again. And quick test of another TV, a Visio gets no action.
    Further testing on that is needed.
    Would be cool to serial.print (while debugging) the BRAND NAME as the codes are output by the LED.

    I wish the codes were commented by brand in the list. The construction of codes is not really user friendly. It must have been terribly tedious to come up with the format.

    But happy to know that my initial try is 99.9% functional. See me smile.

    ReplyDelete
  40. I came across info to enable Debugging by setting DEBUG 1. So I did
    And I can see using Serial.print whats going on but it's too fast to really read.
    Anyone got an idea of where / how to put a conditional delay statement in it.

    I tried to insert a DELAY (1000); it didn't work. I thought I took that out but now getting a compiler error. I probably typo'ed the code. I shall now replace the stuff in the library and try again.

    And. . .has anyone made TVB Gone work on a Nano? I'd love to have the small physical size.

    ReplyDelete
  41. After a bit of "no that didn't work either..."
    I found that the DEBUGP is a function that appears to be called to send
    the debug code IF DEBUG is set to 1

    Here's the code I added
    /*ADDING CODE TO SLOW THE DEBUG DISPLAY SO IT CAN BE READ [email protected]


    DEBUGP (delay (1000)); // Insert this line at approx line #348
    // ^^^^^^^^^^^ ONE SECOND DELAY.

    THE CODE BELOW IS ONLY TO SHOW THE LOCATION
    */
    // print out the address in ROM memory we're reading
    DEBUGP(putstring("\n\rAddr: ");
    putnum_uh((uint16_t)data_ptr));


    // Read the carrier frequency from the first byte of code structure
    const uint8_t freq = pgm_read_byte(data_ptr++);
    // set OCR for Timer1 to output this POWER code's carrier frequency
    #ifdef __AVR_ATmega32U4__
    OCR0A = freq;
    OCR0B = freq / 3; // 33% duty cycle
    #else
    OCR2A = freq;
    OCR2B = freq / 3; // 33% duty cycle

    ReplyDelete