116 lines
5.7 KiB
Markdown
116 lines
5.7 KiB
Markdown
# Volvo LIN-to-IR Controller (ATtiny84)
|
|
|
|
This project implements the production firmware for the ATtiny84 to interface a Volvo V50 Steering Wheel Module (SWM) LIN bus with a Raspberry Pi running LineageOS (Android).
|
|
|
|
It decodes steering wheel navigation buttons from the vehicle's LIN bus and translates them into bit-banged RC-6 Mode 6A (MCE) remote control commands sent over a direct wire connection.
|
|
|
|
## Pin Configurations (ATtiny84)
|
|
|
|
| ATtiny84 Pin | Digital Pin (Arduino) | Function | Description |
|
|
| :--- | :--- | :--- | :--- |
|
|
| **PA0** | `10` | IR Output | Direct-wired to Raspberry Pi GPIO 24 |
|
|
| **PB2** | `2` | LIN RX | SoftwareSerial RX from MCP2004 RXD |
|
|
| **PB0** | `0` | LIN TX | SoftwareSerial TX (Unused dummy) |
|
|
| **PB1** | `1` | LIN FAULT/TXE | MCP2004 Fault Detect / Transmit Enable |
|
|
| **PB3** | `11` | LIN CS | MCP2004 Chip Select (Active High) |
|
|
| **PA7** | `3` | Debug LED | Flash on command transmission |
|
|
|
|
## Key Mapping
|
|
|
|
The Steering Wheel Module (SWM) sends frames on LIN ID `0x20` with the navigation key statuses. The firmware decodes these and maps them to the following Microsoft MCE remote scancodes:
|
|
|
|
| Button | LIN Frame Trigger | Active Button Code | MCE Scancode | Action on LineageOS |
|
|
| :--- | :--- | :--- | :--- | :--- |
|
|
| **UP** | `d0 & 0x01` | `1` | `0x800f041e` | Navigate Up |
|
|
| **DOWN** | `d0 & 0x02` | `2` | `0x800f041f` | Navigate Down |
|
|
| **LEFT** | `d0 & 0x04` | `3` | `0x800f0420` | Navigate Left |
|
|
| **RIGHT** | `d0 & 0x08` | `4` | `0x800f0421` | Navigate Right |
|
|
| **ENTER** | `d1 & 0x08` | `5` | `0x800f0422` | Select / OK |
|
|
| **BACK** | `d1 & 0x01` | `6` | `0x800f0423` | Back / Exit |
|
|
| **Volume Up (Shutdown)** | LIN Silent > 5s | `7` | `0x800f0410` | Trigger Shutdown (via KeyMapper) |
|
|
|
|
*Note: In previous revisions, `ENTER` and `BACK` were reversed. This has been corrected so that pressing `ENTER` maps to `0x800f0422` and `BACK` maps to `0x800f0423`.*
|
|
|
|
## Car-Off (Shutdown) Detection
|
|
|
|
To automatically power off the Raspberry Pi when the car is turned off:
|
|
1. **Activity Monitor**: The ATtiny monitors LIN bus activity. The Central Electronic Module (CEM) continuously polls the LIN bus when the ignition is on.
|
|
2. **Timeout**: If no LIN serial data is received for 5 seconds, the ATtiny assumes the car is off.
|
|
3. **Shutdown Signal**: It transitions the car status to "off" and sends the MCE code `0x800f0410` (mapped to `KEY_VOLUMEUP`) 3 times to ensure transmission reliability.
|
|
4. **State Protection**: It will not send the shutdown command again until the car is turned back on and LIN traffic resumes (which sets `is_car_on` back to `true`).
|
|
|
|
## Raspberry Pi Configuration
|
|
|
|
### 1. Keymap Setup
|
|
Remap the MCE remote codes on the Raspberry Pi:
|
|
- The custom TOML configuration is saved at `/data/rc-rc6-mce.toml` on the Pi.
|
|
- To apply it dynamically:
|
|
```bash
|
|
/vendor/bin/ir-keytable -w /data/rc-rc6-mce.toml
|
|
```
|
|
|
|
### 2. KeyMapper Setup
|
|
Instead of MacroDroid, **KeyMapper** is used to intercept the `KEY_VOLUMEUP` trigger from the direct-wired IR receiver and execute the shutdown command.
|
|
|
|
To configure KeyMapper completely via command-line:
|
|
- **Enable Accessibility Service**:
|
|
```bash
|
|
adb shell settings put secure accessibility_enabled 1
|
|
adb shell settings put secure enabled_accessibility_services io.github.sds100.keymapper/.system.accessibility.MyAccessibilityService
|
|
```
|
|
- **Grant Do Not Disturb Access** (to prevent warnings on volume key mapping):
|
|
```bash
|
|
adb shell settings put secure enabled_notification_policy_access_packages io.github.sds100.keymapper
|
|
```
|
|
- **Map Trigger and Action**:
|
|
The preconfigured KeyMapper SQLite database is pushed directly to the device. The Volume Up key is mapped to run the command:
|
|
```bash
|
|
reboot -p
|
|
```
|
|
|
|
### 3. Wi-Fi Hotspot Auto-start Setup
|
|
To enable the Wi-Fi hotspot (`VolvoC70_AndroidAuto` with passphrase `123456789`) automatically at startup:
|
|
- A boot shell script `/system/bin/start_hotspot.sh` is defined to wait for boot completion and start the SoftAP.
|
|
- A custom Android init service configuration `/system/etc/init/start_hotspot.rc` starts it when `sys.boot_completed` transitions to `1`.
|
|
|
|
**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 Service (`/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
|
|
```
|
|
|
|
## Protocol and Timing
|
|
|
|
- **Protocol**: RC-6 Mode 6A (MCE).
|
|
- **Time Unit (1T)**: `444us`.
|
|
- **Modulation**: None. Timing is bit-banged directly since the output pin is wired directly to the Pi's IR receiver GPIO (which expects demodulated active-low signals).
|
|
- **Idle (Space)**: `HIGH` (3.3V).
|
|
- **Active Pulse (Mark)**: `LOW` (0V).
|
|
- **Toggle Bit**: Alternates state on each new button press, but remains constant for repeated holds.
|
|
- **Repeat Interval**: Holds trigger repeat IR commands sent every `250ms`.
|
|
|
|
## Software Architecture
|
|
|
|
1. **SoftwareSerial Interrupt Isolation**:
|
|
Since SoftwareSerial RX interrupts consume about 1ms per byte, they will disrupt the precise microsecond-level timing of bit-banged IR frames. To avoid this, interrupts are disabled (`noInterrupts()`) during the 37ms window of IR transmission and re-enabled (`interrupts()`) immediately afterward.
|
|
2. **Release detection**:
|
|
The SWM continuously transmits frames on the LIN bus. If no button frames are detected or a frame with all 0s is received, the current active button resets to idle. A timeout of `200ms` ensures that if the LIN bus goes quiet, button repeats cease immediately.
|
|
|