Compare commits
9 Commits
3448744673
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 179ca5e7a8 | |||
| 596f06b0ca | |||
| 9d9517605b | |||
| 4f264e1cda | |||
| 925096a698 | |||
| 190f60c5bb | |||
| 7d669b4a91 | |||
| 8d101b3162 | |||
| 77ff8b973b |
@@ -51,3 +51,8 @@ compile_commands.json
|
|||||||
Kicad/PCB_AndroidAuto-backups/
|
Kicad/PCB_AndroidAuto-backups/
|
||||||
*.lck
|
*.lck
|
||||||
|
|
||||||
|
# Local tooling & screen captures
|
||||||
|
platform-tools/
|
||||||
|
screen.png
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+203
-18
@@ -2,19 +2,49 @@
|
|||||||
#include <SoftwareSerial.h>
|
#include <SoftwareSerial.h>
|
||||||
#include "lin_frame.h"
|
#include "lin_frame.h"
|
||||||
|
|
||||||
#define IR_ARDUINO_PIN 10
|
#define IR_ARDUINO_PIN PIN_PA0 // Digital pin 10
|
||||||
#define LED_PIN 3
|
#define LED_PIN PIN_PA7 // Digital pin 3
|
||||||
|
#define RPI_POWER_PIN PIN_PA2 // GPIO 16 of the RPi is connected to PA2 of the ATtiny
|
||||||
|
|
||||||
#define RX_PIN 8
|
#define RX_PIN PIN_PB2 // Digital pin 2
|
||||||
#define TX_PIN 2 // Unused dummy pin for SoftwareSerial TX
|
#define TX_PIN PIN_PB0 // Digital pin 0 (Unused dummy pin or LIN TX)
|
||||||
#define FAULT_PIN 9
|
#define FAULT_PIN PIN_PB1 // Digital pin 1
|
||||||
#define CS_PIN 11
|
#define CS_PIN PIN_PB3 // Digital pin 11
|
||||||
|
|
||||||
|
#define RTI_TX_PIN PIN_PA5 // Digital pin 5
|
||||||
|
|
||||||
#define SYN_FIELD 0x55
|
#define SYN_FIELD 0x55
|
||||||
#define SWM_ID 0x20
|
#define SWM_ID 0x20
|
||||||
|
|
||||||
SoftwareSerial LINBusSerial(RX_PIN, TX_PIN);
|
SoftwareSerial LINBusSerial(RX_PIN, TX_PIN);
|
||||||
|
|
||||||
|
void rti_write_byte(uint8_t data) {
|
||||||
|
// Disable interrupts to ensure precise bit-banging timing
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli();
|
||||||
|
|
||||||
|
// Start bit (LOW)
|
||||||
|
digitalWrite(RTI_TX_PIN, LOW);
|
||||||
|
delayMicroseconds(417); // 1 / 2400 baud ≈ 416.67 us
|
||||||
|
|
||||||
|
// 8 Data bits (LSB first)
|
||||||
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
|
if (data & (1 << i)) {
|
||||||
|
digitalWrite(RTI_TX_PIN, HIGH);
|
||||||
|
} else {
|
||||||
|
digitalWrite(RTI_TX_PIN, LOW);
|
||||||
|
}
|
||||||
|
delayMicroseconds(417);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop bit (HIGH)
|
||||||
|
digitalWrite(RTI_TX_PIN, HIGH);
|
||||||
|
delayMicroseconds(417);
|
||||||
|
|
||||||
|
// Restore interrupts
|
||||||
|
SREG = oldSREG;
|
||||||
|
}
|
||||||
|
|
||||||
// RC-6 timing constants
|
// RC-6 timing constants
|
||||||
// 1 time unit (1t) = 444us
|
// 1 time unit (1t) = 444us
|
||||||
static const uint16_t RC6_T = 444;
|
static const uint16_t RC6_T = 444;
|
||||||
@@ -85,6 +115,8 @@ uint32_t get_mce_code(uint8_t button) {
|
|||||||
case 4: return 0x800f0421; // RIGHT
|
case 4: return 0x800f0421; // RIGHT
|
||||||
case 5: return 0x800f0422; // ENTER / OK
|
case 5: return 0x800f0422; // ENTER / OK
|
||||||
case 6: return 0x800f0423; // BACK
|
case 6: return 0x800f0423; // BACK
|
||||||
|
case 7: return 0x800f0410; // Volume Up (Shutdown)
|
||||||
|
case 9: return 0x800f0411; // Volume Down (Sleep)
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,34 +137,100 @@ unsigned long last_frame_time = 0;
|
|||||||
uint8_t current_button = 0;
|
uint8_t current_button = 0;
|
||||||
uint8_t toggle_bit = 0;
|
uint8_t toggle_bit = 0;
|
||||||
unsigned long last_ir_send_time = 0;
|
unsigned long last_ir_send_time = 0;
|
||||||
|
unsigned long last_lin_activity_time = 0;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Debouncing variables for combinations
|
||||||
|
bool combination_active = false;
|
||||||
|
uint8_t pending_button = 0;
|
||||||
|
unsigned long pending_button_time = 0;
|
||||||
|
bool button_triggered = false;
|
||||||
|
|
||||||
|
// RPi Power control variables and functions
|
||||||
|
bool power_button_active = false;
|
||||||
|
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;
|
||||||
|
power_button_start_time = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
void process_button_state(uint8_t active_button) {
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
|
|
||||||
if (active_button != 0) {
|
if (active_button != 0) {
|
||||||
if (current_button == 0) {
|
if (active_button >= 8) {
|
||||||
// Button was just pressed!
|
// Combinations trigger immediately without delay
|
||||||
|
if (current_button != active_button) {
|
||||||
current_button = active_button;
|
current_button = active_button;
|
||||||
toggle_bit ^= 1; // Toggle bit alternates on each new press
|
pending_button = 0;
|
||||||
send_ir_for_button(current_button, toggle_bit);
|
button_triggered = true;
|
||||||
last_ir_send_time = now;
|
|
||||||
} else if (current_button == active_button) {
|
if (current_button == 8) {
|
||||||
// Button is being held down!
|
set_screen_state(!screen_open);
|
||||||
// Send repeat code every 250ms
|
} else if (current_button == 9) {
|
||||||
if (now - last_ir_send_time >= 250) {
|
trigger_power_button();
|
||||||
send_ir_for_button(current_button, toggle_bit);
|
}
|
||||||
last_ir_send_time = now;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// A different button was pressed!
|
// Single buttons: delay by 80ms to check if a combination is being pressed
|
||||||
|
if (pending_button != active_button) {
|
||||||
|
pending_button = active_button;
|
||||||
|
pending_button_time = now;
|
||||||
|
button_triggered = false;
|
||||||
|
} else if (!button_triggered) {
|
||||||
|
if (now - pending_button_time >= 80) {
|
||||||
current_button = active_button;
|
current_button = active_button;
|
||||||
|
button_triggered = true;
|
||||||
toggle_bit ^= 1;
|
toggle_bit ^= 1;
|
||||||
send_ir_for_button(current_button, toggle_bit);
|
send_ir_for_button(current_button, toggle_bit);
|
||||||
last_ir_send_time = now;
|
last_ir_send_time = now;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Button was already triggered and is being held down
|
||||||
|
if (now - last_ir_send_time >= 250) {
|
||||||
|
send_ir_for_button(current_button, toggle_bit);
|
||||||
|
last_ir_send_time = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Idle state
|
// Idle state
|
||||||
current_button = 0;
|
current_button = 0;
|
||||||
|
pending_button = 0;
|
||||||
|
button_triggered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_frame_time = now;
|
last_frame_time = now;
|
||||||
@@ -150,14 +248,36 @@ void handle_frame() {
|
|||||||
uint8_t d0 = frame.get_byte(1);
|
uint8_t d0 = frame.get_byte(1);
|
||||||
uint8_t d1 = frame.get_byte(2);
|
uint8_t d1 = frame.get_byte(2);
|
||||||
|
|
||||||
|
bool enter_pressed = (d1 & 0x08) != 0;
|
||||||
|
bool back_pressed = (d1 & 0x01) != 0;
|
||||||
|
bool right_pressed = (d0 & 0x08) != 0;
|
||||||
|
|
||||||
uint8_t active_button = 0;
|
uint8_t active_button = 0;
|
||||||
|
|
||||||
|
if (back_pressed && enter_pressed) {
|
||||||
|
active_button = 8; // Special combination: BACK + ENTER (Toggle Screen)
|
||||||
|
combination_active = true;
|
||||||
|
} else if (right_pressed && enter_pressed) {
|
||||||
|
active_button = 9; // Special combination: RIGHT + ENTER (Sleep Raspberry)
|
||||||
|
combination_active = true;
|
||||||
|
} else if (combination_active) {
|
||||||
|
// A combination was active, but it's no longer detected as a combination.
|
||||||
|
// If any buttons are still pressed, ignore them to prevent false single button triggering.
|
||||||
|
if (enter_pressed || back_pressed || right_pressed || (d0 & 0x01) || (d0 & 0x02) || (d0 & 0x04)) {
|
||||||
|
active_button = 0; // Ignore
|
||||||
|
} else {
|
||||||
|
combination_active = false; // All buttons released, clear flag
|
||||||
|
active_button = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Normal single button decoding
|
||||||
if (d0 & 0x01) active_button = 1; // UP
|
if (d0 & 0x01) active_button = 1; // UP
|
||||||
else if (d0 & 0x02) active_button = 2; // DOWN
|
else if (d0 & 0x02) active_button = 2; // DOWN
|
||||||
else if (d0 & 0x04) active_button = 3; // LEFT
|
else if (d0 & 0x04) active_button = 3; // LEFT
|
||||||
else if (d0 & 0x08) active_button = 4; // RIGHT
|
else if (d0 & 0x08) active_button = 4; // RIGHT
|
||||||
else if (d1 & 0x08) active_button = 5; // ENTER / OK
|
else if (d1 & 0x08) active_button = 5; // ENTER / OK
|
||||||
else if (d1 & 0x01) active_button = 6; // BACK
|
else if (d1 & 0x01) active_button = 6; // BACK
|
||||||
|
}
|
||||||
|
|
||||||
process_button_state(active_button);
|
process_button_state(active_button);
|
||||||
}
|
}
|
||||||
@@ -169,6 +289,13 @@ void setup() {
|
|||||||
pinMode(LED_PIN, OUTPUT);
|
pinMode(LED_PIN, OUTPUT);
|
||||||
digitalWrite(LED_PIN, LOW); // LED OFF
|
digitalWrite(LED_PIN, LOW); // LED OFF
|
||||||
|
|
||||||
|
// Configure RPi power pin (High-Z input initially)
|
||||||
|
pinMode(RPI_POWER_PIN, INPUT);
|
||||||
|
|
||||||
|
// Configure RTI TX pin
|
||||||
|
pinMode(RTI_TX_PIN, OUTPUT);
|
||||||
|
digitalWrite(RTI_TX_PIN, HIGH); // Serial idle is HIGH
|
||||||
|
|
||||||
// Enable MCP2004
|
// Enable MCP2004
|
||||||
pinMode(CS_PIN, OUTPUT);
|
pinMode(CS_PIN, OUTPUT);
|
||||||
digitalWrite(CS_PIN, HIGH);
|
digitalWrite(CS_PIN, HIGH);
|
||||||
@@ -177,12 +304,24 @@ void setup() {
|
|||||||
digitalWrite(FAULT_PIN, HIGH);
|
digitalWrite(FAULT_PIN, HIGH);
|
||||||
|
|
||||||
LINBusSerial.begin(9600);
|
LINBusSerial.begin(9600);
|
||||||
|
|
||||||
|
// No timer initialization needed for bit-banging
|
||||||
|
|
||||||
frame = LinFrame();
|
frame = LinFrame();
|
||||||
last_frame_time = millis();
|
last_frame_time = millis();
|
||||||
|
last_lin_activity_time = millis();
|
||||||
|
last_rti_send_time = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
update_power_button();
|
||||||
|
|
||||||
if (LINBusSerial.available()) {
|
if (LINBusSerial.available()) {
|
||||||
|
last_lin_activity_time = millis();
|
||||||
|
if (!is_car_on) {
|
||||||
|
is_car_on = true;
|
||||||
|
set_screen_state(true); // Automatically open screen when car turns back on
|
||||||
|
}
|
||||||
b = LINBusSerial.read();
|
b = LINBusSerial.read();
|
||||||
n = frame.num_bytes();
|
n = frame.num_bytes();
|
||||||
|
|
||||||
@@ -199,6 +338,52 @@ void loop() {
|
|||||||
|
|
||||||
// Timeout: if no LIN frames received for 200ms, assume no button is pressed
|
// Timeout: if no LIN frames received for 200ms, assume no button is pressed
|
||||||
if (millis() - last_frame_time > 200) {
|
if (millis() - last_frame_time > 200) {
|
||||||
|
if (pending_button == 0) {
|
||||||
current_button = 0;
|
current_button = 0;
|
||||||
|
button_triggered = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send RTI screen serial command byte every 100ms (non-blocking)
|
||||||
|
unsigned long now = millis();
|
||||||
|
if (now - last_rti_send_time >= 100) {
|
||||||
|
last_rti_send_time = now;
|
||||||
|
|
||||||
|
if (screen_open) {
|
||||||
|
uint8_t byte_to_send = 0;
|
||||||
|
// ON sequence: 0x4C (NTSC), 0x2F (max brightness), 0x83 (execute)
|
||||||
|
if (rti_byte_index == 0) byte_to_send = 0x4C;
|
||||||
|
else if (rti_byte_index == 1) byte_to_send = 0x2F;
|
||||||
|
else byte_to_send = 0x83;
|
||||||
|
|
||||||
|
rti_write_byte(byte_to_send);
|
||||||
|
rti_byte_index = (rti_byte_index + 1) % 3;
|
||||||
|
} else {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ It decodes steering wheel navigation buttons from the vehicle's LIN bus and tran
|
|||||||
| ATtiny84 Pin | Digital Pin (Arduino) | Function | Description |
|
| ATtiny84 Pin | Digital Pin (Arduino) | Function | Description |
|
||||||
| :--- | :--- | :--- | :--- |
|
| :--- | :--- | :--- | :--- |
|
||||||
| **PA0** | `10` | IR Output | Direct-wired to Raspberry Pi GPIO 24 |
|
| **PA0** | `10` | IR Output | Direct-wired to Raspberry Pi GPIO 24 |
|
||||||
| **PA5** | `8` | LIN RX | SoftwareSerial RX from MCP2004 RXD |
|
| **PB2** | `2` | LIN RX | SoftwareSerial RX from MCP2004 RXD |
|
||||||
| **PA1** | `9` | LIN FAULT/TXE | MCP2004 Fault Detect / Transmit Enable |
|
| **PB0** | `0` | LIN TX | SoftwareSerial TX (Unused dummy) |
|
||||||
| **PA2** | `11` | LIN CS | MCP2004 Chip Select (Active High) |
|
| **PB1** | `1` | LIN FAULT/TXE | MCP2004 Fault Detect / Transmit Enable |
|
||||||
| **PA6** | `3` | Debug LED | Flash on command transmission |
|
| **PB3** | `11` | LIN CS | MCP2004 Chip Select (Active High) |
|
||||||
|
| **PA7** | `3` | Debug LED | Flash on command transmission |
|
||||||
|
|
||||||
## Key Mapping
|
## Key Mapping
|
||||||
|
|
||||||
@@ -26,9 +27,75 @@ The Steering Wheel Module (SWM) sends frames on LIN ID `0x20` with the navigatio
|
|||||||
| **RIGHT** | `d0 & 0x08` | `4` | `0x800f0421` | Navigate Right |
|
| **RIGHT** | `d0 & 0x08` | `4` | `0x800f0421` | Navigate Right |
|
||||||
| **ENTER** | `d1 & 0x08` | `5` | `0x800f0422` | Select / OK |
|
| **ENTER** | `d1 & 0x08` | `5` | `0x800f0422` | Select / OK |
|
||||||
| **BACK** | `d1 & 0x01` | `6` | `0x800f0423` | Back / Exit |
|
| **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`.*
|
*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 and Timing
|
||||||
|
|
||||||
- **Protocol**: RC-6 Mode 6A (MCE).
|
- **Protocol**: RC-6 Mode 6A (MCE).
|
||||||
@@ -45,3 +112,4 @@ The Steering Wheel Module (SWM) sends frames on LIN ID `0x20` with the navigatio
|
|||||||
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.
|
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**:
|
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.
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -514,7 +514,6 @@
|
|||||||
"priority": 2147483647,
|
"priority": 2147483647,
|
||||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
"track_width": 0.2,
|
"track_width": 0.2,
|
||||||
"tuning_profile": "",
|
|
||||||
"via_diameter": 0.6,
|
"via_diameter": 0.6,
|
||||||
"via_drill": 0.3,
|
"via_drill": 0.3,
|
||||||
"wire_width": 6
|
"wire_width": 6
|
||||||
@@ -533,7 +532,6 @@
|
|||||||
"priority": 0,
|
"priority": 0,
|
||||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
"track_width": 1.37,
|
"track_width": 1.37,
|
||||||
"tuning_profile": "",
|
|
||||||
"via_diameter": 0.6,
|
"via_diameter": 0.6,
|
||||||
"via_drill": 0.3,
|
"via_drill": 0.3,
|
||||||
"wire_width": 6
|
"wire_width": 6
|
||||||
@@ -552,7 +550,6 @@
|
|||||||
"priority": 1,
|
"priority": 1,
|
||||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
"track_width": 0.5,
|
"track_width": 0.5,
|
||||||
"tuning_profile": "",
|
|
||||||
"via_diameter": 0.8,
|
"via_diameter": 0.8,
|
||||||
"via_drill": 0.4,
|
"via_drill": 0.4,
|
||||||
"wire_width": 6
|
"wire_width": 6
|
||||||
@@ -571,14 +568,13 @@
|
|||||||
"priority": 2,
|
"priority": 2,
|
||||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
"track_width": 0.15,
|
"track_width": 0.15,
|
||||||
"tuning_profile": "",
|
|
||||||
"via_diameter": 0.5,
|
"via_diameter": 0.5,
|
||||||
"via_drill": 0.4,
|
"via_drill": 0.4,
|
||||||
"wire_width": 6
|
"wire_width": 6
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"meta": {
|
"meta": {
|
||||||
"version": 5
|
"version": 4
|
||||||
},
|
},
|
||||||
"net_colors": null,
|
"net_colors": null,
|
||||||
"netclass_assignments": null,
|
"netclass_assignments": null,
|
||||||
@@ -599,8 +595,10 @@
|
|||||||
"idf": "",
|
"idf": "",
|
||||||
"netlist": "",
|
"netlist": "",
|
||||||
"plot": "",
|
"plot": "",
|
||||||
|
"pos_files": "",
|
||||||
"specctra_dsn": "",
|
"specctra_dsn": "",
|
||||||
"step": "3D/PCB_AndroidAuto.stp",
|
"step": "3D/PCB_AndroidAuto.stp",
|
||||||
|
"svg": "",
|
||||||
"vrml": ""
|
"vrml": ""
|
||||||
},
|
},
|
||||||
"page_layout_descr_file": ""
|
"page_layout_descr_file": ""
|
||||||
@@ -724,8 +722,14 @@
|
|||||||
"page_layout_descr_file": "",
|
"page_layout_descr_file": "",
|
||||||
"plot_directory": "",
|
"plot_directory": "",
|
||||||
"reuse_designators": true,
|
"reuse_designators": true,
|
||||||
|
"space_save_all_events": true,
|
||||||
"spice_adjust_passive_values": false,
|
"spice_adjust_passive_values": false,
|
||||||
|
"spice_current_sheet_as_root": false,
|
||||||
"spice_external_command": "spice \"%I\"",
|
"spice_external_command": "spice \"%I\"",
|
||||||
|
"spice_model_current_sheet_as_root": true,
|
||||||
|
"spice_save_all_currents": false,
|
||||||
|
"spice_save_all_dissipations": false,
|
||||||
|
"spice_save_all_voltages": false,
|
||||||
"subpart_first_id": 65,
|
"subpart_first_id": 65,
|
||||||
"subpart_id_separator": 0,
|
"subpart_id_separator": 0,
|
||||||
"top_level_sheets": [
|
"top_level_sheets": [
|
||||||
|
|||||||
@@ -1,12 +1,144 @@
|
|||||||
[<img src="https://git.lurenaud.com/assets/img/logo.svg">](https://git.lurenaud.com/lurenaud/VolvoRTI)
|
[<img src="https://git.lurenaud.com/assets/img/logo.svg">](https://git.lurenaud.com/lurenaud/VolvoRTI)
|
||||||
|
|
||||||
# VolvoRTI
|
# VolvoRTI — Android Auto Retrofit for Volvo RTI
|
||||||
|
|
||||||
Android-Auto retrofit for Volvo RTI using a Raspberry pi and crankshaft
|
An integrated Android Auto retrofit system for the retracting Volvo Road and Traffic Information (RTI) navigation screen. Using a Raspberry Pi 4, custom microcontrollers, custom PCB routing, and 3D-printed mechanical enclosures, this project replaces the legacy navigation unit with a modern infotainment hub running LineageOS (Android).
|
||||||
|
|
||||||
<img src="https://git.lurenaud.com/lurenaud/VolvoRTI/raw/branch/master/Photos/focused.png" height=400>
|
<div align="center">
|
||||||
<img src="https://git.lurenaud.com/lurenaud/VolvoRTI/raw/branch/master/Photos/IMG_20210316_152909.jpg" height=400>
|
<img src="Photos/focused.png" height="300" alt="Volvo RTI Retrofit Focused Screen">
|
||||||
|
<img src="Photos/IMG_20210316_152909.jpg" height="300" alt="Volvo Screen installation in progress">
|
||||||
|
</div>
|
||||||
|
|
||||||
## Satus:
|
> [!WARNING]
|
||||||
Not working because of the too low resolution of the original screen.
|
> **Status:** This setup faces display quality limitations when driving the original Volvo LCD screen directly due to its low resolution (typically 400x234). Replacing the internal screen panel with a high-resolution LCD while retaining the retractable motor housing is highly recommended.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ System Architecture
|
||||||
|
|
||||||
|
The retrofit consists of two key layers working in unison: the **ATtiny84 Controller** (vehicle bus decoding + screen motor control) and the **core computer** (Raspberry Pi 4 running Android).
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
%% Vehicles Inputs
|
||||||
|
SWM[Steering Wheel Module - LIN Bus] -- "LIN Frame ID 0x20" --> ATtiny[ATtiny84 Controller]
|
||||||
|
CEM[Central Electronic Module - LIN] -- "LIN Keep-Alive Ping" --> ATtiny
|
||||||
|
|
||||||
|
%% 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]
|
||||||
|
Pi_Power --> PowerService[Android Power Manager / Screen Toggle]
|
||||||
|
Client[Wi-Fi Client] -- "Auto-connects at Boot" --> Hotspot[Phone Hotspot]
|
||||||
|
end
|
||||||
|
|
||||||
|
%% Video Routing
|
||||||
|
Pi_Video[Raspberry Pi HDMI] -- "HDMI Video Signal" --> Screen
|
||||||
|
|
||||||
|
classDef hardware fill:#f9f,stroke:#333,stroke-width:2px;
|
||||||
|
classDef software fill:#bbf,stroke:#333,stroke-width:2px;
|
||||||
|
class SWM,CEM,ATtiny,Screen hardware;
|
||||||
|
class KM,SD,Hotspot,AP software;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Repository Structure
|
||||||
|
|
||||||
|
* 📁 **`Arduino/`**: Microcontroller firmware written for the Arduino ecosystem.
|
||||||
|
* `LIN_to_IR/`: The unified controller firmware. Decodes Steering Wheel Module (SWM) buttons from the LIN bus, outputs RC-6 Mode 6A IR commands to control Android, and drives the Volvo screen motor serial interface (automatic opening/closing, toggle, and car power state detection).
|
||||||
|
* `RTI_Control/`: Legacy screen-only driver firmware (retains original standalone serial protocol testing logic).
|
||||||
|
* `IR_remote_test/`: Test firmware for verifying RC-6 IR signal generation.
|
||||||
|
* 📁 **`Kicad/`**: Schematic, PCB layout, footprints, and Gerber files for a custom Raspberry Pi shield hosting the MCP2004 LIN transceiver, video routing, and power control.
|
||||||
|
* 📁 **`CAD/`**: SolidWorks 3D CAD design files (`.SLDASM`, `.SLDPRT`) and 3D-printable `.STL` files for the custom mechanical Pi enclosure (`case_top` & `case_bottom`).
|
||||||
|
* 📁 **`Raspberry/`**: Configuration scripts and mapping files for LineageOS/Android, including custom `ir-keytable` mappings and key event hooks.
|
||||||
|
* 📁 **`Photos/`**: High-quality hardware builds, installation logs, and system diagrams.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Detailed Module Breakdown
|
||||||
|
|
||||||
|
### 1. Steering & Screen Integration (`Arduino/LIN_to_IR`)
|
||||||
|
|
||||||
|
The **ATtiny84** is the central hardware coordinator. It decodes steering wheel buttons from the LIN bus (`0x20`), controls the screen's extension motor via serial commands, and signals the Raspberry Pi using IR commands.
|
||||||
|
|
||||||
|
#### 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/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 or triggers hardware pins:
|
||||||
|
|
||||||
|
| Input / Combo | LIN Frame Trigger | Active Button Code | MCE Scancode | Action on LineageOS / Hardware |
|
||||||
|
| :--- | :--- | :--- | :--- | :--- |
|
||||||
|
| **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 Soft Shutdown |
|
||||||
|
| **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 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Retractable Screen Serial Driver
|
||||||
|
|
||||||
|
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**: `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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Raspberry Pi Customization (`Raspberry`)
|
||||||
|
|
||||||
|
#### Key Event Remapping
|
||||||
|
To receive the direct-wired IR signal from the ATtiny, the Pi utilizes `ir-keytable` with a custom mapping profile:
|
||||||
|
* **Configuration File**: [`Raspberry/rc-rc6-mce.toml`](Raspberry/rc-rc6-mce.toml)
|
||||||
|
* **Command KeyMapper Setup**:
|
||||||
|
```bash
|
||||||
|
# Enable accessibility service for KeyMapper
|
||||||
|
adb shell settings put secure accessibility_enabled 1
|
||||||
|
adb shell settings put secure enabled_accessibility_services io.github.sds100.keymapper/.system.accessibility.MyAccessibilityService
|
||||||
|
|
||||||
|
# Map the Volume Up trigger to execute:
|
||||||
|
reboot -p
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📷 Gallery & Renderings
|
||||||
|
|
||||||
|
### System Overview Diagram
|
||||||
|

|
||||||
|
|
||||||
|
### Prototype Hardware
|
||||||
|
<div align="center">
|
||||||
|
<img src="Photos/IMG_20210401_175706.jpg" width="45%" alt="PCB Front-side Build">
|
||||||
|
<img src="Photos/IMG_20210401_195552.jpg" width="45%" alt="Pi Shield Assembled">
|
||||||
|
</div>
|
||||||
@@ -83,7 +83,7 @@ dtparam=i2c_arm=on
|
|||||||
dtoverlay=gpio-ir,gpio_pin=24
|
dtoverlay=gpio-ir,gpio_pin=24
|
||||||
|
|
||||||
# Keys
|
# Keys
|
||||||
#dtoverlay=gpio-key,gpio=21,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=26,keycode=115,label="VOLUME_UP"
|
||||||
#dtoverlay=gpio-key,gpio=20,keycode=114,label="VOLUME_DOWN"
|
#dtoverlay=gpio-key,gpio=20,keycode=114,label="VOLUME_DOWN"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[[protocols]]
|
||||||
|
name = "rc-rc6-mce"
|
||||||
|
protocol = "rc-6"
|
||||||
|
[protocols.scancodes]
|
||||||
|
0x800f041e = "KEY_UP"
|
||||||
|
0x800f041f = "KEY_DOWN"
|
||||||
|
0x800f0420 = "KEY_LEFT"
|
||||||
|
0x800f0421 = "KEY_RIGHT"
|
||||||
|
0x800f0422 = "KEY_OK"
|
||||||
|
0x800f0423 = "KEY_EXIT"
|
||||||
|
0x800f046f = "KEY_F12"
|
||||||
Binary file not shown.
+133
@@ -0,0 +1,133 @@
|
|||||||
|
import sqlite3
|
||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
def generate_uuid():
|
||||||
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
# Configuration for the mappings
|
||||||
|
device_id = "75bae499fc7060c40ae424563b660e5122e33c7b"
|
||||||
|
device_name = "2.4G Mouse "
|
||||||
|
|
||||||
|
mappings = [
|
||||||
|
# 1. UP
|
||||||
|
{
|
||||||
|
"trigger_keyCode": 19,
|
||||||
|
"trigger_scanCode": 103,
|
||||||
|
"action_type": "KEY_EVENT",
|
||||||
|
"action_data": "19", # KEYCODE_DPAD_UP
|
||||||
|
"action_flags": 4 # Repeat until released
|
||||||
|
},
|
||||||
|
# 2. DOWN
|
||||||
|
{
|
||||||
|
"trigger_keyCode": 20,
|
||||||
|
"trigger_scanCode": 108,
|
||||||
|
"action_type": "KEY_EVENT",
|
||||||
|
"action_data": "20", # KEYCODE_DPAD_DOWN
|
||||||
|
"action_flags": 4
|
||||||
|
},
|
||||||
|
# 3. LEFT
|
||||||
|
{
|
||||||
|
"trigger_keyCode": 21,
|
||||||
|
"trigger_scanCode": 105,
|
||||||
|
"action_type": "KEY_EVENT",
|
||||||
|
"action_data": "21", # KEYCODE_DPAD_LEFT
|
||||||
|
"action_flags": 4
|
||||||
|
},
|
||||||
|
# 4. RIGHT
|
||||||
|
{
|
||||||
|
"trigger_keyCode": 22,
|
||||||
|
"trigger_scanCode": 106,
|
||||||
|
"action_type": "KEY_EVENT",
|
||||||
|
"action_data": "22", # KEYCODE_DPAD_RIGHT
|
||||||
|
"action_flags": 4
|
||||||
|
},
|
||||||
|
# 5. ENTER (Select)
|
||||||
|
{
|
||||||
|
"trigger_keyCode": 66,
|
||||||
|
"trigger_scanCode": 352,
|
||||||
|
"action_type": "KEY_EVENT",
|
||||||
|
"action_data": "23", # KEYCODE_DPAD_CENTER
|
||||||
|
"action_flags": 0
|
||||||
|
},
|
||||||
|
# 6. BACK
|
||||||
|
{
|
||||||
|
"trigger_keyCode": 4,
|
||||||
|
"trigger_scanCode": 174,
|
||||||
|
"action_type": "KEY_EVENT",
|
||||||
|
"action_data": "4", # KEYCODE_BACK
|
||||||
|
"action_flags": 0
|
||||||
|
},
|
||||||
|
# 7. Volume Up (Shutdown)
|
||||||
|
{
|
||||||
|
"trigger_keyCode": 24,
|
||||||
|
"trigger_scanCode": 115,
|
||||||
|
"action_type": "SHELL_COMMAND",
|
||||||
|
"action_data": "cmVib290IC1w", # base64 for 'reboot -p'
|
||||||
|
"action_flags": 0,
|
||||||
|
"shell_description": "Shutdown",
|
||||||
|
"shell_timeout": "10000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
conn = sqlite3.connect('key_map_database')
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Clear existing keymaps
|
||||||
|
cursor.execute("DELETE FROM keymaps;")
|
||||||
|
|
||||||
|
for m in mappings:
|
||||||
|
key_uid = generate_uuid()
|
||||||
|
trigger_json = {
|
||||||
|
"extras": [],
|
||||||
|
"flags": 0,
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
"clickType": 0,
|
||||||
|
"deviceId": device_id,
|
||||||
|
"deviceName": device_name,
|
||||||
|
"flags": 0,
|
||||||
|
"keyCode": m["trigger_keyCode"],
|
||||||
|
"scanCode": m["trigger_scanCode"],
|
||||||
|
"uid": key_uid
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mode": 2
|
||||||
|
}
|
||||||
|
|
||||||
|
action_uid = generate_uuid()
|
||||||
|
action_item = {
|
||||||
|
"data": m["action_data"],
|
||||||
|
"extras": [],
|
||||||
|
"flags": m["action_flags"],
|
||||||
|
"type": m["action_type"],
|
||||||
|
"uid": action_uid
|
||||||
|
}
|
||||||
|
|
||||||
|
if m["action_type"] == "SHELL_COMMAND":
|
||||||
|
action_item["extras"] = [
|
||||||
|
{"data": m["shell_description"], "id": "extra_shell_command_description"},
|
||||||
|
{"data": m["shell_timeout"], "id": "extra_shell_command_timeout"}
|
||||||
|
]
|
||||||
|
|
||||||
|
action_list_json = [action_item]
|
||||||
|
|
||||||
|
row_uid = generate_uuid()
|
||||||
|
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO keymaps (trigger, action_list, constraint_list, constraint_mode, flags, is_enabled, uid, group_uid)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""", (
|
||||||
|
json.dumps(trigger_json),
|
||||||
|
json.dumps(action_list_json),
|
||||||
|
"[]",
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
row_uid,
|
||||||
|
None
|
||||||
|
))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
print("Successfully populated key_map_database!")
|
||||||
Reference in New Issue
Block a user