#include #include #include "lin_frame.h" #define IR_ARDUINO_PIN 10 #define LED_PIN 3 #define RX_PIN 8 #define TX_PIN 2 // Unused dummy pin for SoftwareSerial TX #define FAULT_PIN 9 #define CS_PIN 11 #define SYN_FIELD 0x55 #define SWM_ID 0x20 SoftwareSerial LINBusSerial(RX_PIN, TX_PIN); // RC-6 timing constants // 1 time unit (1t) = 444us 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 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) { // Disable interrupts to ensure precise IR timing noInterrupts(); // 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(1000); // Re-enable interrupts interrupts(); // Wait to let receiver process delay(40); } uint32_t get_mce_code(uint8_t button) { switch (button) { case 1: return 0x800f041e; // UP case 2: return 0x800f041f; // DOWN case 3: return 0x800f0420; // LEFT case 4: return 0x800f0421; // RIGHT case 5: return 0x800f0422; // ENTER / OK case 6: return 0x800f0423; // BACK default: return 0; } } void send_ir_for_button(uint8_t button, uint8_t toggle) { uint32_t code = get_mce_code(button); if (code == 0) return; digitalWrite(LED_PIN, HIGH); // Turn debug LED ON sendRC6_MCE(code, toggle); digitalWrite(LED_PIN, LOW); // Turn debug LED OFF } byte b, n; LinFrame frame; unsigned long last_frame_time = 0; uint8_t current_button = 0; uint8_t toggle_bit = 0; unsigned long last_ir_send_time = 0; void process_button_state(uint8_t active_button) { unsigned long now = millis(); if (active_button != 0) { if (current_button == 0) { // Button was just pressed! current_button = active_button; toggle_bit ^= 1; // Toggle bit alternates on each new press send_ir_for_button(current_button, toggle_bit); last_ir_send_time = now; } else if (current_button == active_button) { // Button is being held down! // Send repeat code every 250ms if (now - last_ir_send_time >= 250) { send_ir_for_button(current_button, toggle_bit); last_ir_send_time = now; } } else { // A different button was pressed! current_button = active_button; toggle_bit ^= 1; send_ir_for_button(current_button, toggle_bit); last_ir_send_time = now; } } else { // Idle state current_button = 0; } last_frame_time = now; } void handle_frame() { if (frame.get_byte(0) != SWM_ID) return; if (!frame.isValid()) return; // Extract the data bytes // SWM button frame has 4 data bytes uint8_t d0 = frame.get_byte(1); uint8_t d1 = frame.get_byte(2); uint8_t active_button = 0; if (d0 & 0x01) active_button = 1; // UP else if (d0 & 0x02) active_button = 2; // DOWN else if (d0 & 0x04) active_button = 3; // LEFT else if (d0 & 0x08) active_button = 4; // RIGHT else if (d1 & 0x08) active_button = 5; // ENTER / OK else if (d1 & 0x01) active_button = 6; // BACK process_button_state(active_button); } void setup() { pinMode(IR_ARDUINO_PIN, OUTPUT); digitalWrite(IR_ARDUINO_PIN, HIGH); // Idle state (HIGH) pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); // LED OFF // Enable MCP2004 pinMode(CS_PIN, OUTPUT); digitalWrite(CS_PIN, HIGH); pinMode(FAULT_PIN, OUTPUT); digitalWrite(FAULT_PIN, HIGH); LINBusSerial.begin(9600); frame = LinFrame(); last_frame_time = millis(); } void loop() { if (LINBusSerial.available()) { b = LINBusSerial.read(); n = frame.num_bytes(); if (b == SYN_FIELD && n > 2 && frame.get_byte(n - 1) == 0) { frame.pop_byte(); handle_frame(); frame.reset(); } else if (n == LinFrame::kMaxBytes) { frame.reset(); } else { frame.append_byte(b); } } // Timeout: if no LIN frames received for 200ms, assume no button is pressed if (millis() - last_frame_time > 200) { current_button = 0; } }