diff --git a/Arduino/LIN_to_IR/LIN_to_IR.cpp b/Arduino/LIN_to_IR/LIN_to_IR.cpp index 32f429e..5744464 100644 --- a/Arduino/LIN_to_IR/LIN_to_IR.cpp +++ b/Arduino/LIN_to_IR/LIN_to_IR.cpp @@ -142,6 +142,7 @@ bool is_car_on = false; // RTI Screen variables bool screen_open = true; // Screen should open at startup +uint8_t rti_close_bytes_left = 0; unsigned long last_rti_send_time = 0; uint8_t rti_byte_index = 0; @@ -157,6 +158,7 @@ unsigned long power_button_start_time = 0; void trigger_power_button() { if (!power_button_active) { + digitalWrite(LED_PIN, HIGH); // Turn debug LED ON pinMode(RPI_POWER_PIN, OUTPUT); digitalWrite(RPI_POWER_PIN, LOW); power_button_active = true; @@ -167,10 +169,24 @@ void trigger_power_button() { void update_power_button() { if (power_button_active && (millis() - power_button_start_time >= 150)) { pinMode(RPI_POWER_PIN, INPUT); + digitalWrite(LED_PIN, LOW); // Turn debug LED OFF power_button_active = false; } } +void set_screen_state(bool open) { + if (screen_open != open) { + screen_open = open; + rti_byte_index = 0; // Reset byte index to send new sequence immediately + if (!screen_open) { + rti_close_bytes_left = 30; // Send the OFF packet (3 bytes) 10 times + trigger_power_button(); + } else { + trigger_power_button(); + } + } +} + void process_button_state(uint8_t active_button) { unsigned long now = millis(); @@ -183,8 +199,7 @@ void process_button_state(uint8_t active_button) { button_triggered = true; if (current_button == 8) { - screen_open = !screen_open; - rti_byte_index = 0; // Reset byte index to send new sequence immediately + set_screen_state(!screen_open); } else if (current_button == 9) { trigger_power_button(); } @@ -305,7 +320,7 @@ void loop() { last_lin_activity_time = millis(); if (!is_car_on) { is_car_on = true; - screen_open = true; // Automatically open screen when car turns back on + set_screen_state(true); // Automatically open screen when car turns back on } b = LINBusSerial.read(); n = frame.num_bytes(); @@ -332,7 +347,7 @@ void loop() { // Car off detection: if no LIN activity for 5 seconds, send Volume Up to trigger shutdown if (is_car_on && (millis() - last_lin_activity_time > 5000)) { is_car_on = false; - screen_open = false; // Close screen when car is off + set_screen_state(false); // Close screen when car is off for (int i = 0; i < 3; i++) { send_ir_for_button(7, i & 1); delay(100); @@ -354,8 +369,20 @@ void loop() { rti_write_byte(byte_to_send); rti_byte_index = (rti_byte_index + 1) % 3; } else { - // Stay completely silent on serial when screen is closed to let it retract/close - rti_byte_index = 0; + if (rti_close_bytes_left > 0) { + uint8_t byte_to_send = 0; + // OFF sequence: 0x4F (OFF), 0x20 (standard brightness), 0x83 (execute) + if (rti_byte_index == 0) byte_to_send = 0x4F; + else if (rti_byte_index == 1) byte_to_send = 0x20; + else byte_to_send = 0x83; + + rti_write_byte(byte_to_send); + rti_byte_index = (rti_byte_index + 1) % 3; + rti_close_bytes_left--; + } else { + // Stay completely silent on serial when screen is closed to let it retract/close + rti_byte_index = 0; + } } } } diff --git a/README.md b/README.md index 8cba524..d2ffce5 100644 --- a/README.md +++ b/README.md @@ -27,12 +27,14 @@ graph TD %% ATtiny Processing & Control ATtiny -- "RC-6 MCE IR Protocol (Bit-banged)" --> Pi_IR[Raspberry Pi GPIO 24 / IR Input] ATtiny -- "2400 Baud Serial Control" --> Screen[Volvo RTI Retractable Screen] + ATtiny -- "Direct Pin Pulse (PA2)" --> Pi_Power[Raspberry Pi GPIO 23 / Power Button] %% Pi Processing subgraph Raspberry Pi 4 [Raspberry Pi 4 - LineageOS] Pi_IR --> KM[KeyMapper / Input Dispatcher] KM -- "Volume Up Trigger > 5s LIN Idle" --> SD[OS Shutdown Command] - Hotspot[Init Services] -- "Auto-start on Boot" --> AP[Wi-Fi AP: VolvoC70_AndroidAuto] + Pi_Power --> PowerService[Android Power Manager / Screen Toggle] + Client[Wi-Fi Client] -- "Auto-connects at Boot" --> Hotspot[Phone Hotspot] end %% Video Routing @@ -68,11 +70,14 @@ The **ATtiny84** is the central hardware coordinator. It decodes steering wheel #### Core Behaviors & Logic * **Startup Extension**: The screen automatically opens upon startup (ignition on). * **Manual Screen Toggle**: Pressing **BACK + ENTER** at the same time toggles the screen between fully open and fully closed states. -* **Manual Sleep Command**: Pressing **RIGHT + ENTER** at the same time sends a Volume Down IR command to put the Raspberry Pi to sleep. +* **Manual Sleep/Wake Screen**: Pressing **RIGHT + ENTER** at the same time pulses the Raspberry Pi's Power pin (GPIO 23 connected to ATtiny PA2) for 150ms to toggle the Raspberry Pi screen's sleep/wake state. +* **Automatic Screen Power Control**: + * When the RTI screen transitions to closed (via manual toggle or car-off detection), the ATtiny automatically pulses the Power Pin (GPIO 23) to turn off the screen, and sends the serial close command 10 times consecutively to make sure it retracts immediately. + * When the RTI screen transitions to open, it also pulses the Power Pin (GPIO 23) to wake up / turn on the screen. * **Smart Power-down (Car-Off Detection)**: The ATtiny monitors LIN traffic. If no LIN communication is detected for **5 seconds**, the system automatically retracts the screen and sends a Volume Up IR command 3 times to trigger a clean Raspberry Pi OS shutdown. #### Button Map & Command Codes -When buttons are pressed, the ATtiny transmits the corresponding Microsoft MCE remote scancodes: +When buttons are pressed, the ATtiny transmits the corresponding Microsoft MCE remote scancodes or triggers hardware pins: | Input / Combo | LIN Frame Trigger | Active Button Code | MCE Scancode | Action on LineageOS / Hardware | | :--- | :--- | :--- | :--- | :--- | @@ -83,8 +88,8 @@ When buttons are pressed, the ATtiny transmits the corresponding Microsoft MCE r | **ENTER** | `d1 & 0x08` | `5` | `0x800f0422` | Select / OK | | **BACK** | `d1 & 0x01` | `6` | `0x800f0423` | Back / Exit | | **Volume Up (Shutdown)** | LIN Silent > 5s | `7` | `0x800f0410` | Trigger Soft Shutdown | -| **BACK + ENTER** | Both buttons held | `8` | *None (Local)* | Toggles Screen Motor (Open/Close) | -| **RIGHT + ENTER** | Both buttons held | `9` | `0x800f0411` | Send Sleep / Volume Down | +| **BACK + ENTER** | Both buttons held | `8` | *None (Local)* | Toggles Screen Motor (Open/Close) + pulses Power Pin | +| **RIGHT + ENTER** | Both buttons held | `9` | *None (Local)* | Pulses Power Pin (GPIO 23) to Sleep/Wake Screen | --- @@ -93,8 +98,10 @@ When buttons are pressed, the ATtiny transmits the corresponding Microsoft MCE r The Volvo RTI screen housing requires continuous serial packets at **2400 baud** over a single communication line to stay awake and raised. * **Display ON sequence**: `0x4C` (NTSC), `0x2F` (max brightness), `0x83` (execute). -* **Display OFF sequence**: `0x46` (OFF), `0x00` (min brightness), `0x83` (execute). -* **Keep-Alive Periodicity**: The ATtiny84 continuously writes these 3-byte command packets at a non-blocking 100ms interval (one byte every 100ms) to ensure smooth operation without blocking LIN bus reception. +* **Display OFF sequence**: `0x4F` (OFF), `0x20` (standard brightness), `0x83` (execute). +* **Keep-Alive / Transition Periodicity**: + * When the screen is open, the ATtiny84 continuously writes the ON sequence at a non-blocking 100ms interval (one byte every 100ms). + * When closing the screen, the ATtiny84 sends the OFF sequence 10 times consecutively (30 bytes total, sent 1 byte every 100ms) to ensure immediate retraction, and then stays completely silent on the serial line. --- @@ -113,28 +120,14 @@ To receive the direct-wired IR signal from the ATtiny, the Pi utilizes `ir-keyta reboot -p ``` -#### Wi-Fi Hotspot Auto-start at Boot -To establish wireless Android Auto on boot, a custom init script starts the Wi-Fi hotspot automatically on startup: -* **Boot script** (`/system/bin/start_hotspot.sh`): - ```sh - #!/system/bin/sh - while [ "$(getprop sys.boot_completed)" != "1" ]; do - sleep 1 - done - sleep 5 - cmd wifi start-softap VolvoC70_AndroidAuto wpa2 123456789 - ``` -* **Init configuration** (`/system/etc/init/start_hotspot.rc`): - ```rc - service start_hotspot /system/bin/sh /system/bin/start_hotspot.sh - class main - user root - group root system wifi - oneshot - disabled - - on property:sys.boot_completed=1 - start start_hotspot +#### Wi-Fi Hotspot Client Connection Setup +To allow the Raspberry Pi to connect to the phone's mobile hotspot automatically on boot: +* The Pi acts as a Wi-Fi client connecting to the hotspot SSID (e.g. `"Galaxy S24+ 9091"`). +* Captive portal checks and internet verification watchdogs are disabled on the Pi via Android settings to prevent the system from permanently disabling the auto-join state of the hotspot when internet availability is temporarily delayed: + ```bash + adb shell settings put global captive_portal_mode 0 + adb shell settings put global captive_portal_detection_enabled 0 + adb shell settings put global wifi_watchdog_on 0 ``` --- diff --git a/Raspberry/config.txt b/Raspberry/config.txt index e1de979..b227a39 100644 --- a/Raspberry/config.txt +++ b/Raspberry/config.txt @@ -83,7 +83,7 @@ dtparam=i2c_arm=on dtoverlay=gpio-ir,gpio_pin=24 # Keys -dtoverlay=gpio-key,gpio=16,keycode=116,label="POWER" +dtoverlay=gpio-key,gpio=23,keycode=116,label="POWER" #dtoverlay=gpio-key,gpio=26,keycode=115,label="VOLUME_UP" #dtoverlay=gpio-key,gpio=20,keycode=114,label="VOLUME_DOWN"