Add IR_remote_test environment with RC-6 emulation
This commit is contained in:
@@ -0,0 +1,80 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
+29
-21
@@ -1,30 +1,38 @@
|
|||||||
[platformio]
|
[platformio]
|
||||||
src_dir = Arduino/RTI_Control
|
src_dir = .
|
||||||
|
|
||||||
[env:attiny84]
|
[env:attiny84]
|
||||||
platform = atmelavr
|
platform = atmelavr
|
||||||
board = attiny84
|
board = attiny84
|
||||||
framework = arduino
|
framework = arduino
|
||||||
|
|
||||||
; Set clock frequency to 8MHz (Internal)
|
|
||||||
board_build.f_cpu = 8000000L
|
board_build.f_cpu = 8000000L
|
||||||
|
build_src_filter = -<*> +<Arduino/RTI_Control/>
|
||||||
; 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
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
SoftwareSerial
|
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 = -<*> +<Arduino/RTI_Control/>
|
||||||
|
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 = -<*> +<Arduino/Blink_Test/>
|
||||||
|
|
||||||
|
; Environment for testing IR remote signals to Raspberry Pi
|
||||||
|
[env:IR_remote_test]
|
||||||
|
extends = env:attiny84_isp
|
||||||
|
build_src_filter = -<*> +<Arduino/IR_remote_test/>
|
||||||
|
|||||||
Reference in New Issue
Block a user