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
}
}