[Android Head-Units] snakedoc73:
Aloha friends. Things are becoming quite cluttered concerning the k706 [qf001] device. I've spent weeks to "dive" into the device and discovered many useful things that are usefull for operating the device efficiently and stably in a self-created environment. This environment is relatively simple to set up and requires minimal intervention since I've already made many things available here for you. I've essentially deconstructed the entire os and identified all the "crucial non-typical Android elements" in the device, adding my own elements. The issue with the k706 is that many hardware components are prioritized but somehow communicate with Android. I've extracted many these "intent/action" interactions and making them available here. I've also found [from an other User] a reasonable root solution without making significant modifications inn the bootloder/etc. > that reducing the chance to bricking the device. Here's some preliminary information, but I'll make another post to organize the elements properly. Please note, all actions are at your own risk and without warranty!!!!!

These instructions are for adv. users, they know what they do!!!

Codes for the factory settings menu(copied from the beginning of, but for the further infos important):

8888 - access to advanced settings

114477 - activates developer mode (than you have to do the usual procedere: touch the build number on the system info screen several times until it says that you are developer now.)

22558800 factory reset

996633 - Exports some config file > userfull to modify systemvariables ore READOUT THE steering learning codes (when you used buttons with resistors ory rotary knob for volume control).....

ROOTING

=======

ROOT-access is not neccessary but it´s very usefull to use some soft like titaniumbackup, rootexplorer, TASKER (with the root options, TASKER can run also w/om root).

ROOTING via ADB

attention: ONLY tested on QF001.20240119.125814xxxxxxxx / QF05.V02.03.20240106.002121

Device must be in stock mode with shown version and factory reset (can try w/o restet but SAVE YOUR DATA!!!). NORMALY(!) it´s not possible to brick the unit, because you don´t modify MCU,etc..This root-method based on the already existing root on ADB!

the HU must be in develop. mode with ´USB-Debugging´ active.

- rooting with WINDOWS10/11 (download -std. adb-dev.plattform-tools-). pls. run CMD as

admin

- needed files: boot-patched.img, vbmeta-patched.img, magisk-manager-27-0.apk

(put the first 2 files in your ADB dir directly)

- rooting via wireless adb (HU must be in same network)

- adb connect [ip] on port 9876 example > adb connect 192.168.1.101:9876

-when successfull connected:

1.) copy files to device:

adb push boot-patched.img /sdcard

adb push vbmeta-patched.img /sdcard

2.) "inject" uploaded files > when done don´t leave the adb shell... you need adb shell in step 3. too!

adb shell

dd bs=1024 count=35840 if=/sdcard/boot-patched.img of=/dev/block/mmcblk0p34

dd bs=1024 count=1024 if=/sdcard/vbmeta-patched.img of=/dev/block/mmcblk0p40

dd bs=1024 count=1024 if=/sdcard/vbmeta-patched.img of=/dev/block/mmcblk0p41

3.) rename the "stock/old" su, because magisk don´t work when the an existing other su

mount -o rw,remount /

cd /system/xbin

mv ./su /.su_old

mount -o ro,remount /

exit

adb disconnect

4.) restart device install magisk. magisk will make the rest mods.... ROOTING DONE!

>>>i made this several times, it works perfectly!<<<

SOME OTHER USEFULL INFORMATIONS for adv. Users (i sorted these when i done my K706-project)

====================================================

com.qf.action ACTIONS (for use in TASKER,etc.)

------------------------------------------------------

2 OF THE MOST IMPORTANT ACTIONS

com.qf.action.ACC_ON > broadcast IGNITION ON

com.qf.action.ACC_OFF > broadcast IGNITION OFF (11 seconds time to sent other

command after the ignition is off (screen goes off) to the HU (via for example TASKER).

After that, the device is in sleep mode.

com.qf.action.BT_STATE

com.qf.action.MUTE_EQ

com.qf.action.READY_GO_SLEEP_PRE

com.qf.action.STARTUP_RECENT

com.qf.action.VOLUME_CHANGED

com.qf.action.bt.battery

com.qf.action.bt.pan.change

com.qf.action.car_backlight_changed

com.qf.action.car_backlight_control

com.qf.action.car_screen_close

com.qf.action.car_volume_changed

com.qf.action.car_volume_control

com.qf.action.update_battery_power

com.qf.intent Actions:

------------------------

com.qf.intent.ACTION_AUDIOSTART

com.qf.intent.ACTION_AUDIOSTOP

ROTARY VOLUME KNOB

-------------__------------

i also wrote a "rotary knob box" IDE sketch to user an rotary knob for volume control (mute, an screen off)

Rotary-Encode is KY-040, Arduino is NANO, pins seen in code. Arduino powered over ACC and the simulated resistor wire is connecte to the wire "KEY1". i used supermicro-relais connnect directly to the arduino pins to isolate the "resistor-section" 100% from the arduino. This ist for more adv. Users tu make this "arduino rotary knob box". >don´t forget to use the relais with diodes (parallel connect to the relais-coils) to protect the arduino controller fpr overcorrent, typical prodoced by the relay self when release contacts!

example script:

// version 1.03

// Rotary Encoder with integrated push-button function to emulate resistance values for the K706 (or other Android car radio control)

// Different actions simulate different resistances (you can use other Resistors like in my Sketch, but with a min. dif. of 500Ohm):

// - Turning up the volume: Simulates a resistance of 1.8 kOhm

// - Turning down the volume: Simulates a resistance of 2.7 kOhm

// - Single button press: Simulates a resistance of 4.52 kOhm

// - Double button press: Simulates a resistance of 870 Ohm

// The learning mode of the Rotary Box allows for the control to be programmed to the radio. In this mode, the actions

// (Volume up, Volume down, Button press, Double button press) are held by the box for 3 seconds to allow the radio to recognize the resistances.

// This is necessary because, in normal operation mode, the simulated resistances cannot be correctly detected by the radio

// due to the short duration of the action.

//

// Activating learning mode:

// - To enter learning mode, the button must be pressed and held before turning on the box/radio,

// turn on the box/radio and release after 5 seconds.

//

// Exiting learning mode:

// - The learning mode automatically ends if the box (depending on the connection method) is powered down by turning off

// the radio/ignition/etc. for 10 seconds. Subsequently, the box restarts in normal mode.

// Pin definitions

#define CLK_PIN 7

#define DT_PIN 6

#define BUTTON_PIN 5

#define RELAY_LOUDER 9

#define RELAY_QUIETER 10

#define BUTTON_FUNCTION_PIN 11

// Time constants

const unsigned long POST_ROTATION_BLOCK_TIME = 1;

int RELAY_ON_DELAY = 30; // Dynamically based on mode

int RELAY_OFF_DELAY = 30;

const unsigned long DEBOUNCE_TIME = 20;

const unsigned long DOUBLE_CLICK_TIME = 300;

int BUTTON_HOLD_TIME = 190; // Dynamically based on mode

const unsigned long LEARNING_MODE_TIMEOUT = 20000; // 20 seconds for restart in learning mode

// State variables and timestamps

int lastCLKState;

int currentCLKState;

unsigned long lastRotationDetectedTime = 0;

unsigned long lastRelayActivationTime = 0;

bool rotaryEnabled = true;

unsigned long lastInputTime = 0; // Time of the last input

// Variables for button logic

unsigned long lastButtonPressTime = 0;

bool buttonPressed = false;

int buttonPressCounter = 0;

// Learning mode variable

bool learningMode = false;

void setup() {

pinMode(CLK_PIN, INPUT);

pinMode(DT_PIN, INPUT);

pinMode(BUTTON_PIN, INPUT_PULLUP);

pinMode(RELAY_LOUDER, OUTPUT);

pinMode(RELAY_QUIETER, OUTPUT);

pinMode(BUTTON_FUNCTION_PIN, OUTPUT);

lastCLKState = digitalRead(CLK_PIN);

// Direct activation of learning mode if the button is pressed at startup

if (digitalRead(BUTTON_PIN) == LOW) {

learningMode = true; // Activate learning mode immediately

BUTTON_HOLD_TIME = 3000; // Set time variables for learning mode

RELAY_ON_DELAY = 3000;

}

lastInputTime = millis(); // Initialize the timer for the last input

}

void loop() {

handleButton();

if(rotaryEnabled) {

handleRotary();

}

// Check if 20 seconds have passed without input (only in learning mode)

if (learningMode && millis() - lastInputTime > LEARNING_MODE_TIMEOUT) {

setup(); // Perform a "soft reset"

}

}

void handleRotary() {

currentCLKState = digitalRead(CLK_PIN);

if (currentCLKState != lastCLKState) {

if ((millis() - lastRelayActivationTime) >= POST_ROTATION_BLOCK_TIME) {

if (digitalRead(DT_PIN) != currentCLKState) {

activateRelay(RELAY_LOUDER);

lastRotationDetectedTime = millis();

} else {

activateRelay(RELAY_QUIETER);

lastRotationDetectedTime = millis();

}

lastRelayActivationTime = millis();

}

lastCLKState = currentCLKState;

lastInputTime = millis(); // Update the timer for the last input

}

}

void handleButton() {

bool currentButtonState = digitalRead(BUTTON_PIN) == LOW;

if (currentButtonState && !buttonPressed && (millis() - lastButtonPressTime) > DEBOUNCE_TIME) {

buttonPressed = true;

lastButtonPressTime = millis();

buttonPressCounter++;

lastInputTime = millis(); // Update

-------------------------------------------------------------

other infos in my next/complete post. regards, chris.