IR transmitter and ATtiny85 issue

Hi all. Relatively new to microcontrollers, and running up against an issue bringing an IR transmitter over to the ATtiny85.

I have successfully used an Uno to transmit a series of IR codes over IR LEDs using the IRremote library. When I try to port this over to the ATtiny85, though, I'm getting a series of errors that seem to stem from the IRremote library. I've tried both the most recent version of the library as well as an earlier version (2.8.0, at ChatGPT's suggestion -- still getting the error messages).

I've successfully coded the ATtiny85 with a few basic projects (blink, etc), so the programming setup is not the issue -- I'm connecting without a problem. But even when I simply compile the code for the ATtiny85, I'm still getting errors.

Here is the code I'm using:

#include <IRremote.h>

// Pin definitions
#define BUTTON_PIN 2   // ATtiny85 Physical Pin 7 (PB2)
#define IR_LED_PIN 4   // ATtiny85 Physical Pin 3 (PB4)

// IR transmitter setup
IRsend irsend;  // Declare globally

bool buttonPressed = false;

// Define the list of off codes for various brands
unsigned long offCodes[] = {
  0xA90,       // Sony
  0xE0E040BF,  // Samsung
  0x20DF10EF,  // LG
  0x4004,      // Panasonic
};

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);  // Enable internal pull-up resistor for button
}

void loop() {
  if (digitalRead(BUTTON_PIN) == LOW) {  // Check if button is pressed
    if (!buttonPressed) {
      buttonPressed = true;
      sendOffSequence();  // Send the IR "OFF" codes
    }
  } else {
    buttonPressed = false;
  }
}

void sendOffSequence() {
  for (unsigned int i = 0; i < sizeof(offCodes) / sizeof(offCodes[0]); i++) {
    irsend.sendNEC(offCodes[i], 32);  // Send NEC signal (32-bit)
    delay(200);                       // Small delay between signals
  }
}

and here is the error message I'm receiving

In file included from /Users/aaa/Documents/Arduino/IR Blasters/IR_On_Off_IRRemote2_8_0_for_ATTiny85/IR_On_Off_IRRemote2_8_0_for_ATTiny85.ino:1:0:
/Users/aaa/Documents/Arduino/libraries/IRremote/src/IRremote.h: In member function 'bool IRrecv::decode(decode_results*)':
/Users/aaa/Documents/Arduino/libraries/IRremote/src/IRremote.h:32:9: error: 'Serial' was not declared in this scope
         Serial.println(F("**************************************************************************************************"));
         ^~~~~~
/Users/aaa/Documents/Arduino/libraries/IRremote/src/IRremote.h:32:9: note: suggested alternative: 'Stream'
         Serial.println(F("**************************************************************************************************"));
         ^~~~~~
         Stream

exit status 1

Compilation error: exit status 1

I am by no means an experienced coder -- I've honestly used ChatGPT to help me navigate some of this, with varying results.

Is there something I'm missing here? Or an easier/better way for me to code this IR transmitter to send these codes?

Any non-AI-generated real-live human responses would be very welcome.
Thanks!

I suggest you look through the ATtiny core to see what conditions are necessary for the Serial object to exist. Then peruse the ATtiny85 datasheet (the full datasheet, not the summary), to see if that processor satisfies those conditions.

And don't rely on ChatGPT until you know enough to be able to tell when it's making stuff up.

Which board package or core are you using for the attiny85 ?

The errors you are getting are typical of chatGPT. It does not know the rules for the various microprocessors or their restrictions.

Using the bot is almost always a waste of time for beginners. Forum members are unlikely to help correct bot generated code, either, because they know it took essentially no effort on your part to generate, and consider it a waste of their time.

1 Like

An old Instructable. ATTiny85 "IRremote" sketches seem to refer to Nick Gammon's IRremote library (in a zip file).

That's a real bummer. Seems like there's a lacuna between the total noob end of things and getting to use some of the parts like the ATtiny or other similar components -- there's only so many (not great) youtube tutorials on the subject, and I'm not school age any more -- trying to teach myself will only get me so far. Tried GPT as an alternative in order to learn more, and sorting through the 234 page datasheet of the ATtiny85 is beyond my abilities to comprehend at this phase, though I really did try.

Perhaps there's another component besides the ATtiny85 that is smaller than a Nano that's more suitable for this project. Or another community I can find that would be more willing to help.

Thanks.

Thanks, I appreciate it.

Going through the Arduino IDE, loaded attiny 1.0.2 by David A. Mellis. (This is what you're asking, yes?)

I hope so.

There was a time when the following core was recommended for the ATTINY because the David A. Mellis appears to have been abandoned: GitHub - SpenceKonde/ATTinyCore: Arduino core for ATtiny 1634, 828, x313, x4, x41, x5, x61, x7 and x8 . However, it has suffered from some lack of attention lately and you may be confronted with problems related to expired certificates if you attempt to install it. I am sorry that I do not know what the latest position is.

That is because there are thousands of MCUs like the ATtiny, each with few Arduino adherents, and few of those on this forum ready and willing to help. Your focus on using the ATtiny85 is large part of the problem.

If board size is the issue, there is no need to compromise on processor power. Consider tiny boards like the QT Py series or the Xiao series, for which there are libraries and code examples for countless projects. Or this mini Uno.

Thank you, this is very helpful -- yes, board size is the issue, and I was led toward the ATtiny85 as a potential solution -- I appreciate your pointing toward these others for me to check out.

use #include <IRremote.hpp> and enjoy

I have just managed to make working the IR interface between the Attiny85 and and Arduino Nano. It's been very hard but, as usual, the problem was trivial. I've tryed several programs most of them generated by AI (I used Claude Sonnet). After several test my conclussion was that I had a problem with the timings of the NEC protocal at the Attiny85 (in fact, the Arduino was able to read IR code for market IR TV remote controls.
I asked Claude to prepare a program that modified with small timing increments the different timings of the NEC protocol wating 2 seconds between the different test. I put a LED in parallel with the IR Led Trasmitter and i saw that the wating time between tests in the transmitter actually was 16 seconds. Obviously the Attiny85 was working at 1MHz despite in the IDE the configuration was 8 MHz.
Asking Claude how to solve this problem the answer was too simple: set-ut the clock frequency in the Arduino IDE at 8 MHz (it was already at tha frequency) and selecting "Tools -> Burn Bootloader". Done. Problem solved.
Now the Arduino receiver decodes perfectly the NEC codes transmitted by the Attiny85. The test program continues to change slightly the timings of the NEC protocol and the recever decodes without problems for all the timings combinations.

Follows the Arduino and the Attiny85 code... remember, don't trust if you set up 8MHz on the Arduino IDE, it's not enough, if your Attiny85 comes from the fabric at 1MHz you have to "Burn Bootloader" to change it to 8MHz

Arduino Code:

/*
 * Arduino Nano IR NEC Protocol Receiver
 * 
 * This code implements a simple IR receiver for the NEC protocol
 * that displays received codes on the Serial Monitor.
 * 
 * Circuit:
 * - IR receiver module (TSOP38238, VS1838B, etc.) connected to:
 *   - Signal pin to Arduino pin D7
 *   - VCC to Arduino 5V
 *   - GND to Arduino GND
 * 
 * The code will print the complete 32-bit NEC code (in hex) when received,
 * along with the separated address and command values.
 */

#include <IRremote.h>

// IR receiver connected to pin D7
#define IR_RECEIVE_PIN 7

void setup() {
  // Initialize serial communication
  Serial.begin(9600);
  while (!Serial) {
    ; // Wait for serial port to connect
  }
  
  Serial.println(F("Arduino Nano IR NEC Receiver"));
  Serial.println(F("Waiting for IR signals..."));
  
  // Start the IR receiver
  IrReceiver.begin(IR_RECEIVE_PIN);
}

void loop() {
  // Check if IR data is available
  if (IrReceiver.decode()) {
    // Check if it's an NEC protocol signal
    if (IrReceiver.decodedIRData.protocol == NEC) {
      // Extract the decoded values
      uint16_t address = IrReceiver.decodedIRData.address;
      uint16_t command = IrReceiver.decodedIRData.command;
      uint32_t fullCode = ((uint32_t)address << 16) | ((uint32_t)command << 8) | ((uint32_t)(~command & 0xFF));
      
      // Print the full 32-bit code in hexadecimal
      Serial.print(F("Received NEC Code: 0x"));
      
      // Format the complete 32-bit code as 8 hex digits with leading zeros
      if (fullCode < 0x10000000) Serial.print("0");
      if (fullCode < 0x1000000) Serial.print("0");
      if (fullCode < 0x100000) Serial.print("0");
      if (fullCode < 0x10000) Serial.print("0");
      if (fullCode < 0x1000) Serial.print("0");
      if (fullCode < 0x100) Serial.print("0");
      if (fullCode < 0x10) Serial.print("0");
      Serial.println(fullCode, HEX);
      
      // Print the address and command separately
      Serial.print(F("Address: 0x"));
      if (address < 0x10) Serial.print("0");
      Serial.println(address, HEX);
      
      Serial.print(F("Command: 0x"));
      if (command < 0x10) Serial.print("0");
      Serial.println(command, HEX);
      
      // Print the inverted command
      uint8_t invCommand = ~command & 0xFF;
      Serial.print(F("Inverted Command: 0x"));
      if (invCommand < 0x10) Serial.print("0");
      Serial.println(invCommand, HEX);
      
      Serial.println();
    } 
    else {
      // If it's not NEC protocol
      Serial.print(F("Received non-NEC protocol: "));
      Serial.println(IrReceiver.getProtocolString());
    }
    
    // Reset the receiver for the next code
    IrReceiver.resume();
  }
  
  // Small delay to avoid flooding the serial monitor
  delay(100);
}
/>
Codice Attiny85:

<
'''ccp
/*
 * ATtiny85 IR NEC Protocol Timing Search
 * 
 * This code systematically tests different fixed timing combinations
 * to find the optimal settings for the NEC protocol.
 * 
 * Circuit:
 * - IR LED connected to pin PB1 (physical pin 6) with appropriate current limiting resistor
 * - Button connected to pin PB4 (physical pin 3) to ground with internal pull-up enabled
 * 
 * Set ATtiny85 to run at 8MHz internal clock for best results
 */

#include <avr/io.h>
#include <util/delay.h>

// IR LED pin
#define IR_LED PB1

// Button pin
#define BUTTON_PIN PB4

// Function declarations
void sendNEC(uint8_t paramSet, uint8_t address, uint8_t command);
void sendIRMark(uint16_t duration);
void sendIRSpace(uint8_t spaceType, uint8_t paramSet);
void sendByte(uint8_t paramSet, uint8_t data);

// Space types
#define HEADER_SPACE 0
#define ONE_SPACE 1
#define ZERO_SPACE 2

int main(void) {
  // Set IR LED pin as output
  DDRB |= (1 << IR_LED);
  
  // Set button pin as input with pull-up
  DDRB &= ~(1 << BUTTON_PIN);
  PORTB |= (1 << BUTTON_PIN);
  
  // Sample address and command values
  uint8_t address = 0x00;  // Device address
  uint8_t command = 0x01;  // Command code (e.g., power on)
  
  // Parameter set index (we'll test 12 different combinations)
  uint8_t paramSet = 0;
  
  // Wait a bit before starting
  _delay_ms(1000);
  
  // Main loop
  while (1) {
    // Send the code three times with current parameters
    sendNEC(paramSet, address, command);
    _delay_ms(100);
    sendNEC(paramSet, address, command);
    _delay_ms(100);
    sendNEC(paramSet, address, command);
    
    // Increment parameter set for next iteration
    paramSet++;
    if (paramSet >= 12) {  // Reset after trying all combinations
      paramSet = 0;
    }
    
    // Wait before trying the next combination
    _delay_ms(2000);
  }
  
  return 0;
}

// Send a complete NEC IR code with specific parameter set
void sendNEC(uint8_t paramSet, uint8_t address, uint8_t command) {
  // Send header with header mark based on parameter set
  // We'll try 3 different header mark values: 8500, 9000, 9500
  if (paramSet % 3 == 0) {
    sendIRMark(8500);
  } else if (paramSet % 3 == 1) {
    sendIRMark(9000);
  } else {
    sendIRMark(9500);
  }
  
  // Send header space
  sendIRSpace(HEADER_SPACE, paramSet / 3);
  
  // Send address and inverted address
  sendByte(paramSet, address);
  sendByte(paramSet, ~address);
  
  // Send command and inverted command
  sendByte(paramSet, command);
  sendByte(paramSet, ~command);
  
  // Send stop bit with bit mark based on parameter set
  if ((paramSet / 6) % 2 == 0) {
    sendIRMark(560);
  } else {
    sendIRMark(580);
  }
  
  // Ensure a minimum gap between transmissions
  _delay_ms(40);
}

// Send an IR mark (burst of 38kHz carrier)
void sendIRMark(uint16_t duration) {
  // Calculate number of carrier cycles needed
  uint16_t cycles = duration / 26;  // 26µs per cycle at 38kHz
  
  for (uint16_t i = 0; i < cycles; i++) {
    PORTB |= (1 << IR_LED);   // LED on
    _delay_us(10);            // On time
    PORTB &= ~(1 << IR_LED);  // LED off
    _delay_us(16);            // Off time
  }
}

// Send an IR space with delay based on parameter set
void sendIRSpace(uint8_t spaceType, uint8_t variant) {
  // Ensure LED is off during space
  PORTB &= ~(1 << IR_LED);
  
  if (spaceType == HEADER_SPACE) {
    // Header space (4 variants: 4300, 4500, 4700, 4900)
    if (variant % 4 == 0) {
      _delay_us(4300);
    } else if (variant % 4 == 1) {
      _delay_us(4500);
    } else if (variant % 4 == 2) {
      _delay_us(4700);
    } else {
      _delay_us(4900);
    }
  } 
  else if (spaceType == ONE_SPACE) {
    // One space (3 variants: 1650, 1690, 1730)
    if (variant % 3 == 0) {
      _delay_us(1650);
    } else if (variant % 3 == 1) {
      _delay_us(1690);
    } else {
      _delay_us(1730);
    }
  } 
  else if (spaceType == ZERO_SPACE) {
    // Zero space (2 variants: 560, 580)
    if (variant % 2 == 0) {
      _delay_us(560);
    } else {
      _delay_us(580);
    }
  }
}

// Send a byte (8 bits, LSB first)
void sendByte(uint8_t paramSet, uint8_t data) {
  for (uint8_t i = 0; i < 8; i++) {
    // Send bit mark based on parameter set
    if ((paramSet / 6) % 2 == 0) {
      sendIRMark(560);
    } else {
      sendIRMark(580);
    }
    
    // Send one or zero space based on data bit
    if (data & 1) {
      sendIRSpace(ONE_SPACE, paramSet);
    } else {
      sendIRSpace(ZERO_SPACE, paramSet);
    }
    
    data >>= 1;  // Shift to next bit
  }
}