diff --git a/Arduino/IR_remote_test/IR_remote_test.cpp b/Arduino/IR_remote_test/IR_remote_test.cpp new file mode 100644 index 0000000..27a4419 --- /dev/null +++ b/Arduino/IR_remote_test/IR_remote_test.cpp @@ -0,0 +1,80 @@ +#include + +#define IR_ARDUINO_PIN 10 +#define LED_PIN 3 + +// RC-6 timing constants +// 1 time unit (1t) = 444us (16 cycles of 36kHz carrier) +static const uint16_t RC6_T = 444; + +// Mark = LOW on wire (simulating TSOP receiving an IR burst) +void sendMark(uint16_t us) { + digitalWrite(IR_ARDUINO_PIN, LOW); + delayMicroseconds(us); +} + +// Space = HIGH on wire (simulating TSOP idle) +void sendSpace(uint16_t us) { + digitalWrite(IR_ARDUINO_PIN, HIGH); + delayMicroseconds(us); +} + +// Send a single RC-6 bit using Manchester encoding +// RC-6 Logic '1': Mark then Space +// RC-6 Logic '0': Space then Mark +void sendRC6Bit(uint8_t bit, uint8_t width) { + if (bit) { + sendMark(RC6_T * width); + sendSpace(RC6_T * width); + } else { + sendSpace(RC6_T * width); + sendMark(RC6_T * width); + } +} + +// Send RC-6 Mode 6A (MCE) frame +void sendRC6_MCE(uint32_t data, uint8_t toggle) { + // Leader: 6t mark + 2t space + sendMark(RC6_T * 6); + sendSpace(RC6_T * 2); + + // Start bit: always 1 + sendRC6Bit(1, 1); + + // Mode bits: 1, 1, 0 (mode 6) + sendRC6Bit(1, 1); + sendRC6Bit(1, 1); + sendRC6Bit(0, 1); + + // Toggle bit (double width = 2t per half-bit) + sendRC6Bit(toggle & 1, 2); + + // 32 data bits, MSB first + for (int i = 31; i >= 0; i--) { + sendRC6Bit((data >> i) & 1, 1); + } + + // Final return to idle state (HIGH) + sendSpace(40000); +} + +static uint8_t toggleBit = 0; + +void setup() { + pinMode(IR_ARDUINO_PIN, OUTPUT); + digitalWrite(IR_ARDUINO_PIN, HIGH); // Idle state + + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, LOW); +} + +void loop() { + digitalWrite(LED_PIN, HIGH); + + // MCE scancode for Volume Up: 0x800f0410 + sendRC6_MCE(0x800f0410, toggleBit); + toggleBit ^= 1; // MCE requires alternating toggle bit + + digitalWrite(LED_PIN, LOW); + delay(1000); // Send every 1 second +} diff --git a/Arduino/IR_remote_test/README.md b/Arduino/IR_remote_test/README.md new file mode 100644 index 0000000..61ebbc0 --- /dev/null +++ b/Arduino/IR_remote_test/README.md @@ -0,0 +1,26 @@ +# IR Remote Emulation Test (ATtiny84 to Raspberry Pi) + +This folder contains a test environment to verify direct communication between an ATtiny84 and a Raspberry Pi running LineageOS (Android). The goal is to send remote control commands directly over a wire, bypassing the need for physical infrared (IR) LEDs and receivers. + +## Hardware Setup +- **Microcontroller**: ATtiny84 running at 8MHz (3.3V) +- **Target**: Raspberry Pi running LineageOS (3.3V GPIO) +- **Connection**: A direct wire from ATtiny84 **PA0** (Physical Pin 13 / Arduino Digital 10) to Raspberry Pi **GPIO 24** (Physical Header Pin 18). Both devices share a common ground. +- **Debug**: An LED on ATtiny digital pin 3 (PA6 / Physical Pin 7) flashes when a command is transmitted. + +## What We Learned +During testing via ADB, we discovered several crucial details about how the Raspberry Pi handles IR signals in LineageOS: + +1. **Protocol Expectation**: The Raspberry Pi's `gpio_ir_recv` driver defaults to the **RC-6** protocol and uses the Microsoft MCE remote keymap (`rc-rc6-mce`). Sending standard NEC protocol will fail unless the kernel is manually reconfigured. +2. **Signal Polarity**: The `gpio-ir-recv` device tree node uses the `GPIO_ACTIVE_LOW` flag. This means the Linux driver expects a signal identical to the output of a standard TSOP IR receiver: + - **Idle (Space)** = `HIGH` (3.3V) + - **Active Pulse (Mark)** = `LOW` (0V) +3. **Encoding Rules**: RC-6 uses Manchester encoding. A Logic '1' is defined as a Mark followed by a Space (in our direct-wire context: `LOW` then `HIGH`), while a Logic '0' is a Space followed by a Mark (`HIGH` then `LOW`). + +## Implementation details +The code in `IR_remote_test.cpp` implements a custom RC-6 Mode 6A (MCE) bit-banged sender. It bypasses 36kHz carrier modulation since the wire connects directly to the demodulated input GPIO on the Pi. + +It sends the specific 32-bit MCE scancode `0x800f0410` (Volume Up), including the proper RC-6 leader pulses, start bit, mode bits, and an alternating toggle bit. + +## Conclusion +By properly matching the protocol (RC-6 MCE) and the TSOP hardware polarity (Mark=LOW, Space=HIGH), we were able to successfully inject remote control commands directly into the Android input subsystem without needing to modify any Android `.kl` (keylayout) files or custom kernel drivers. The Pi natively decodes the bit-banged signal as a standard `KEY_VOLUMEUP` hardware event. diff --git a/platformio.ini b/platformio.ini index 0920d87..5a85524 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,30 +1,38 @@ [platformio] -src_dir = Arduino/RTI_Control +src_dir = . [env:attiny84] platform = atmelavr board = attiny84 framework = arduino - -; Set clock frequency to 8MHz (Internal) board_build.f_cpu = 8000000L - -; Fuses for ATtiny84 (8MHz internal oscillator, BOD disabled) -; These are just for reference, PIO doesn't always set them automatically without extra steps -; board_fuses.lfuse = 0xE2 -; board_fuses.hfuse = 0xDF -; board_fuses.efuse = 0xFF - -; Upload protocol -; If using Arduino as ISP: -; upload_protocol = arduinoisp -; upload_port = /dev/ttyACM0 -; upload_speed = 19200 - -; If using USBasp: -; upload_protocol = usbasp -; upload_flags = -Pusb - -; Library dependencies +build_src_filter = -<*> + lib_deps = SoftwareSerial + +; Environment for programming via Arduino Pro Micro as ISP +[env:attiny84_isp] +platform = atmelavr +board = attiny84 +framework = arduino +board_build.f_cpu = 8000000L +build_src_filter = -<*> + +upload_protocol = stk500v1 +upload_speed = 19200 +; Set your Pro Micro port here +upload_port = /dev/ttyACM0 +upload_flags = + -P$UPLOAD_PORT + -b$UPLOAD_SPEED +lib_deps = + SoftwareSerial + +; Environment specifically for the blink test on pin 7 +[env:attiny84_blink] +extends = env:attiny84_isp +build_src_filter = -<*> + + +; Environment for testing IR remote signals to Raspberry Pi +[env:IR_remote_test] +extends = env:attiny84_isp +build_src_filter = -<*> +