IR Remote (Syma s026) dedicated board – V2 – AtTiny45
June 19, 2010 14 Comments
Right, the previous post was about creating a dedicated board that can do 2 things:
- read and decrypt the IR commands sent by the Syma s026 remote control
- send them via a serial link (to the “central” MCU or elsewhere)
Now it kept bothering me that, for such a simple task, I had to use as big an MCU as AtMega328. I love the small AtTiny13 but it has no serial port, and not enough memory to implement my own software serial protocol…
However, after moult research and playing around, I realised that “serial” does NOT mean “USART” and that, especially if we need it only 1 way, implementing your own software protocol is REALLY easy… it turns out, my IR Beacon project is doing exactly this, but over IR… it should be only simpler to do it over a wire !
So here it is, I start my project with the small and nice AtTiny13A, but will later replace it with AtTiny45V, simply because I’m using interrupts and this requires slightly more Flash memory (but I’m sure the code could be optimized to fit on to an attiny13).
TIMING is really the main lait-motif of this project and hence I use extensively one of the 2 timers on the tiny45:
- to time the incoming IR codes
- to time the outgoing serial pulses
Some useful stuff from the AtTiny45V docs :
More photos of the final product :
The C code going on the AtTiny45 (compile using AVR Studio with g++) :
//#define F_CPU 8000000UL #include <avr/io.h> #include <avr/interrupt.h> #define LED_PIN PB0 #define SERIAL_PIN PB3 #define IR_PIN PB4 #define PORT PORTB #define DDRD DRB #define PIN PINB #define TIMER_COUNT TCNT0 // SERIAL SIGNALS #define BYTE_SIZE = 8 const unsigned long T0 = 500; // in microseconds const unsigned long T1 = T0 * 3; const unsigned long START = T0 * 8; const unsigned long BETWEEN_BITS = T0; // IR SIGNALS #define SIGNAL_SIZE 26 // MAX is 29 #define SIGNAL_SPEED 7//from 0 to here #define SIGNAL_DIR 13//from end of SignalSpeed to here #define SIGNAL_BUTTONS 17//from end of SignalDir to here #define SIGNAL_VERTICAL 21//from end of SignalButtons to here #define SIGNAL_TRIMMER 26//from end of SignalVertical to here #define PULSE_START_MIN 1500 // in Micro Seconds #define PULSE_START_MAX 2000 #define PULSE_0_MIN 300 #define PULSE_0_MAX 750 #define PULSE_1_MIN 750 #define PULSE_1_MAX 1200 #define VALUE_LEFT_BUTTON 22 #define VALUE_RIGHT_BUTTON 26 void ledON(){ PORT |= _BV(LED_PIN); // turn on the LED } void ledOFF(){ PORT &= ~_BV(LED_PIN); // turn off the LED } /* TIMING STUFF */ void timerConf(){ TCCR0A = 0x00; TCCR0B = 1 << CS01; // prescaler 8. IF F_CPU is 8MHz then counting directly in uSecs TIMSK = 1 << TOIE0; //Overflow Interrupt Enabled //Enable Global Interrupts sei(); } // don't forget the VOLATILE, otherwise it will be seen as 0 from the outside !!! volatile unsigned long _timerOverflow; ISR(TIMER0_OVF_vect){ _timerOverflow++; } void resetTimer(){ TIMER_COUNT = 0; _timerOverflow = 0; } unsigned long getTimer(){ return _timerOverflow * 256 + TIMER_COUNT; } void delay_us(unsigned long microSecs){ resetTimer(); while(getTimer() < microSecs) {}; } /* TIMING STUFF - END */ void serialSendStart(){ PORT |= _BV(SERIAL_PIN); // high delay_us(START); PORT &= ~_BV(SERIAL_PIN); // low delay_us(BETWEEN_BITS); } void serialSendBits(unsigned char data){ for(unsigned char i = 0; i < 8; i++){ PORT |= _BV(SERIAL_PIN); // high if(data & 0x01) delay_us(T1); else delay_us(T0); PORT &= ~_BV(SERIAL_PIN); // low delay_us(BETWEEN_BITS); data >>= 1; // get the next most significant bit } } unsigned char irState(){ return PIN & _BV(IR_PIN); // get the value of the IR PIN, 0 or 1 } static unsigned char getBit(int pulse){ if(pulse > PULSE_0_MIN && pulse < PULSE_0_MAX) return 0; if(pulse > PULSE_1_MIN && pulse < PULSE_1_MAX) return 1; return 111; // BAAAD } unsigned long _currTimer; int getIRLowPulse(unsigned int timeoutMicroSecs){ resetTimer(); // wait until it gets LOW or timesout _currTimer = getTimer(); while(irState() && _currTimer < timeoutMicroSecs) { _currTimer = getTimer(); } // time out if(_currTimer >= timeoutMicroSecs) return -1; // start conting the pulse resetTimer(); _currTimer = getTimer(); while(! irState() && _currTimer < timeoutMicroSecs) { _currTimer = getTimer(); } // pulse too long if(_currTimer >= timeoutMicroSecs) return -2; return _currTimer; } int _pulseCount = -1; unsigned char _bit = 0; unsigned char _bits[SIGNAL_SIZE]; int _pulse; unsigned char i = 0; unsigned char _speed, _dir, _buttons, _vertical, _trimmer; unsigned char _speedPrev, _dirPrev, _buttonsPrev, _verticalPrev, _trimmerPrev; void send(){ ledON(); _speed = 0; _dir = 0; _buttons = 0; _vertical = 0; _trimmer = 0; for(i=0; i<SIGNAL_SIZE; i++){ if (_bits[i] == 1){ if (i < SIGNAL_SPEED) _speed += (1 << (SIGNAL_SPEED - i)); else if (i < SIGNAL_DIR) _dir += (1 << (SIGNAL_DIR - i)); else if (i < SIGNAL_BUTTONS) _buttons += (1 << (SIGNAL_BUTTONS - i)); else if (i < SIGNAL_VERTICAL) _vertical += (1 << (SIGNAL_VERTICAL - i)); else if (i < SIGNAL_TRIMMER) _trimmer += (1 << (SIGNAL_TRIMMER - i)); } } if(_buttons == VALUE_LEFT_BUTTON) _buttons = 1; else if (_buttons == VALUE_RIGHT_BUTTON) _buttons = 2; else _buttons = 0; if(_speed != _speedPrev || _dir != _dirPrev || _buttons != _buttonsPrev || _vertical != _verticalPrev || _trimmer != _trimmerPrev){ _speedPrev = _speed; _dirPrev = _dir; _buttonsPrev = _buttons; _verticalPrev = _vertical; _trimmerPrev = _trimmer; serialSendStart(); serialSendBits(_speed); serialSendBits(_dir); serialSendBits(_vertical); serialSendBits(_trimmer); serialSendBits(_buttons); } ledOFF(); } void init(){ timerConf(); // make both LED and SERIAL pins outputs DDR |= _BV(LED_PIN) | _BV(SERIAL_PIN); // IR_LED is an input DDR &= ~_BV(IR_PIN); // SERIAL PORT start LOW PORT &= ~_BV(SERIAL_PIN); } int main(void){ init(); while(1){ _pulse = getIRLowPulse(10000); if (_pulse > PULSE_START_MIN && _pulse < PULSE_START_MAX){ //start counting pulses _pulseCount = 0; }else if (_pulseCount >= 0){ //continue counting pulses _bit = getBit(_pulse); // if invalid bit, stop here if(_bit > 1){ _pulseCount = -1; }else{ _bits[_pulseCount] = _bit; // good, wait for next bit _pulseCount++; // FINAL, we've read what we wanted let our caller know ! if (_pulseCount == SIGNAL_SIZE) { send(); // end of this command, so reInit so that we wait for another start _pulseCount = -1; } } } } return 0; }
Thanks for this article!
Is there a simple way to convert this to Arduino ?
I would like to make a tiny receiver that uses ATTINY 45 and Syma transmitter to control it.
Depends on what you mean by “simple”…:)
Well, I mean a general algorithm for something like that using Arduino compatible code instead of C code
Its not public yet 🙂 (its about remote controll signals recognition), I’ll write a blogpost about it and I’ll put here a link.
Where I can get a circuit? (I should know how its connected together) Its not placed there… or I dont see it?
I’m not sure I understand your question..
This is a circuit that I built by myself from scratch. It’s NOT based on any schematic that you could find somewhere, I have simply put together the MCU with 2 IR sensors (and a LED and it’s resistor simply to get some visual feedback).
I have not taken the time to make a diagram, you should be able to figure out the connections from the code.
Dan
Connections are exactly what I need. Tell me please which pins are connected to what. I know I can extract it from code, but if you have it in mind you will save me a time.
Thanks
If you look at the beginning of the code you’ll see:
#define LED_PIN PB0 #define SERIAL_PIN PB3 #define IR_PIN PB4
Which means : – connect the LED to the PB0 pin (through a 3-500Ohms resistor in series) – the output serial pin is PB3 – connect BOTH IR sensors in PARALLEL to PB4 (their “signal” pin, and obviously their GND and VCC to the corresponding MCU pins)
And that’s it, it’s really simple as you can see.
Could you please post a link or give more explanations about what project you need this for ? I’d be curious to find out more…
Dan
Pingback: ATtiny Hacks: Infrared guidance and navigation « FOOTBALL, SEX & ALCOHOL
Pingback: ATtiny Hacks: Infrared guidance and navigation - Hack a Day
Pingback: Quadcopter – home made « Robotics / Electronics / Physical Computing
Pingback: Lego NXT – Arduino Truck « Robotics / Electronics / Physical Computing
Just found your post and it’s great ! Simple enough for an intermediate-level guy like me, great idea and great explanations !
Have you tried using a PIC MCU rather than an AVR ?
Very detailed explanation and great idea !
Thanks for sharing !