Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bridge/bridge_ios.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ static void carStateToBridge(const CarState& cs, BridgeCarState* out) {
out->tpmsTempFR = cs.tpmsTempFR;
out->tpmsTempRL = cs.tpmsTempRL;
out->tpmsTempRR = cs.tpmsTempRR;
out->doorOpenFL = cs.doorOpenFL;
out->doorOpenFR = cs.doorOpenFR;
out->doorOpenRL = cs.doorOpenRL;
out->doorOpenRR = cs.doorOpenRR;
out->trunkOpen = cs.trunkOpen;
out->trunkPosition = cs.trunkPosition;
}

static std::atomic<bool> receiveLoopRunning{false};
Expand Down
8 changes: 8 additions & 0 deletions bridge/bridge_ios.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ typedef struct {
double tpmsTempFR;
double tpmsTempRL;
double tpmsTempRR;

// Closures: doors/trunk 0/1 (1 = latch not closed), trunk angle in degrees.
double doorOpenFL;
double doorOpenFR;
double doorOpenRL;
double doorOpenRR;
double trunkOpen;
double trunkPosition;
} BridgeCarState;

// Context
Expand Down
23 changes: 22 additions & 1 deletion bridge/car/car_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ struct CarState {
double tpmsTempRL = -100;
double tpmsTempRR = -100;

// Closures from VCLEFT_doorStatus / VCRIGHT_doorStatus latch state: 1 while
// the latch is anything but closed (opened, opening, ajar, closing), else 0.
double doorOpenFL = 0;
double doorOpenFR = 0;
double doorOpenRL = 0;
double doorOpenRR = 0;
double trunkOpen = 0;
// Powered liftgate angle in degrees from VCLEFT_liftgateStatus (mux 1);
// 0 when closed or when the car has no powered liftgate.
double trunkPosition = 0;

// Vehicle VIN, empty until fully assembled by the mapper.
static constexpr size_t VIN_LENGTH = 17;
char vin[VIN_LENGTH + 1] = {};
Expand All @@ -65,7 +76,9 @@ struct CarState {
static constexpr size_t VIN_OFFSET = 36;
static constexpr size_t TPMS_OFFSET = VIN_OFFSET + VIN_DOUBLE_COUNT;
static constexpr size_t TPMS_COUNT = 8;
static constexpr size_t FIELD_COUNT = TPMS_OFFSET + TPMS_COUNT;
static constexpr size_t DOOR_OFFSET = TPMS_OFFSET + TPMS_COUNT;
static constexpr size_t DOOR_COUNT = 6;
static constexpr size_t FIELD_COUNT = DOOR_OFFSET + DOOR_COUNT;

void toArray(double* out) const {
// Party bus
Expand Down Expand Up @@ -126,5 +139,13 @@ struct CarState {
out[TPMS_OFFSET + 5] = tpmsTempFR;
out[TPMS_OFFSET + 6] = tpmsTempRL;
out[TPMS_OFFSET + 7] = tpmsTempRR;

// Closures (after TPMS): doors FL FR RL RR, trunk open, trunk position.
out[DOOR_OFFSET + 0] = doorOpenFL;
out[DOOR_OFFSET + 1] = doorOpenFR;
out[DOOR_OFFSET + 2] = doorOpenRL;
out[DOOR_OFFSET + 3] = doorOpenRR;
out[DOOR_OFFSET + 4] = trunkOpen;
out[DOOR_OFFSET + 5] = trunkPosition;
}
};
25 changes: 25 additions & 0 deletions bridge/car/cars/tesla.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,29 @@ static inline double powerLevel(const CarState& cs) {
return std::clamp(kw / limit, -1.0, 1.0) * 100.0;
}

// VCLEFT/VCRIGHT_*LatchStatus: 0 SNA, 1 OPENED, 2 CLOSED, 3 CLOSING, 4 OPENING,
// 5 AJAR, 6 TIMEOUT, 7 DEFAULT, 8 FAULT. Open until the latch reports closed;
// unknown/fault states read as closed so a stale bus doesn't light a warning.
static inline double latchOpen(double latchStatus) {
int s = static_cast<int>(latchStatus);
return (s == 1 || s == 3 || s == 4 || s == 5) ? 1.0 : 0.0;
}

// Per-door and trunk latch state plus the powered-liftgate angle, from the
// vehicle bus (DashKit forwards 0x102, 0x103 and 0x142).
static inline void updateClosures(const CANParsers& cp, CarState& cs) {
cs.doorOpenFL = latchOpen(cp.get(1, "VCLEFT_doorStatus", "VCLEFT_frontLatchStatus"));
cs.doorOpenRL = latchOpen(cp.get(1, "VCLEFT_doorStatus", "VCLEFT_rearLatchStatus"));
cs.doorOpenFR = latchOpen(cp.get(1, "VCRIGHT_doorStatus", "VCRIGHT_frontLatchStatus"));
cs.doorOpenRR = latchOpen(cp.get(1, "VCRIGHT_doorStatus", "VCRIGHT_rearLatchStatus"));
cs.trunkOpen = latchOpen(cp.get(1, "VCRIGHT_doorStatus", "VCRIGHT_trunkLatchStatus"));
// VCLEFT_liftgateStatus is multiplexed: the angle lives on mux 1. Keep the
// last angle across mux-0 frames. Closed reads slightly negative on some cars.
if (static_cast<int>(cp.get(1, "VCLEFT_liftgateStatus", "VCLEFT_liftgateStatusIndex")) == 1) {
cs.trunkPosition = std::max(0.0, cp.get(1, "VCLEFT_liftgateStatus", "VCLEFT_liftgatePosition"));
}
}

static inline void updateVehicleBus(const CANParsers& cp, CarState& cs) {
// Vehicle bus specific signals
// BMS_energyStatus is multiplexed: mux 0 has pack/remaining energy, mux 1 has energyBuffer
Expand Down Expand Up @@ -175,6 +198,8 @@ class TeslaDashKitMapper : public CarStateMapper {
cs.buckleStatus = cp.get(1, "VCLEFT_switchStatus", "VCLEFT_frontBuckleSwitch") == 2.0 ? 1.0 : 0.0;
}

updateClosures(cp, cs);

// Climate setpoint temperature (degC) from the vehicle bus.
cs.acTemp = cp.get(1, "UI_hvacRequest", "UI_hvacReqTempSetpointLeft");
cs.acTempRight = cp.get(1, "UI_hvacRequest", "UI_hvacReqTempSetpointRight");
Expand Down
3 changes: 3 additions & 0 deletions dash-apps/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ Values not received yet are `0` / `false`. For speed limit, stop line and energy
| `egoSteeringAngle` | degrees | steering wheel angle, negative = left |
| `leftBlinker`, `rightBlinker` | 0–2 | 0 off, 1 blinking (lamp dark), 2 blinking (lamp lit) |
| `anyDoorOpen` | 0/1 | |
| `doorOpenFL`, `doorOpenFR`, `doorOpenRL`, `doorOpenRR` | 0/1 | per door, 1 until the latch reports closed. **DashKit only** |
| `trunkOpen` | 0/1 | rear trunk latch. **DashKit only** |
| `trunkPosition` | degrees | powered liftgate angle, 0 when closed or not powered. **DashKit only** |
| `buckleStatus` | 0/1 | 1 = driver buckled |
| `odometer` | number | km, or miles when `isImperial` |

Expand Down
6 changes: 6 additions & 0 deletions dash-apps/web-compose/core/host.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
stopLineDist: "getStopLineDist",
trafficLightColor: "getTrafficLightColor",
anyDoorOpen: "getAnyDoorOpen",
doorOpenFL: "getDoorOpenFL",
doorOpenFR: "getDoorOpenFR",
doorOpenRL: "getDoorOpenRL",
doorOpenRR: "getDoorOpenRR",
trunkOpen: "getTrunkOpen",
trunkPosition: "getTrunkPosition",
buckleStatus: "getBuckleStatus",
accSetSpeed: "getAccSetSpeed",
fullPackEnergy: "getFullPackEnergy",
Expand Down
18 changes: 18 additions & 0 deletions dash-apps/web-compose/core/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
"signal.leftBlindSpot": "Left blind spot",
"signal.rightBlindSpot": "Right blind spot",
"signal.anyDoorOpen": "Door open",
"signal.doorOpenFL": "Driver door",
"signal.doorOpenFR": "Passenger door",
"signal.doorOpenRL": "Rear left door",
"signal.doorOpenRR": "Rear right door",
"signal.trunkOpen": "Trunk open",
"signal.trunkPosition": "Trunk angle",
"signal.seatbeltUnbuckled": "Seatbelt",
"widget.unknown": "Unavailable widget",
"editor.title": "Edit dashboard",
Expand Down Expand Up @@ -100,6 +106,12 @@
"signal.leftBlindSpot": "Bal holttér",
"signal.rightBlindSpot": "Jobb holttér",
"signal.anyDoorOpen": "Nyitott ajtó",
"signal.doorOpenFL": "Vezetőajtó",
"signal.doorOpenFR": "Utasajtó",
"signal.doorOpenRL": "Bal hátsó ajtó",
"signal.doorOpenRR": "Jobb hátsó ajtó",
"signal.trunkOpen": "Csomagtartó nyitva",
"signal.trunkPosition": "Csomagtartó szöge",
"signal.seatbeltUnbuckled": "Biztonsági öv",
"widget.unknown": "Nem elérhető elem",
"editor.title": "Műszerfal szerkesztése",
Expand Down Expand Up @@ -161,6 +173,12 @@
"signal.leftBlindSpot": "Toter Winkel links",
"signal.rightBlindSpot": "Toter Winkel rechts",
"signal.anyDoorOpen": "Tür offen",
"signal.doorOpenFL": "Fahrertür",
"signal.doorOpenFR": "Beifahrertür",
"signal.doorOpenRL": "Tür hinten links",
"signal.doorOpenRR": "Tür hinten rechts",
"signal.trunkOpen": "Kofferraum offen",
"signal.trunkPosition": "Kofferraumwinkel",
"signal.seatbeltUnbuckled": "Sicherheitsgurt",
"widget.unknown": "Element nicht verfügbar",
"editor.title": "Dashboard bearbeiten",
Expand Down
7 changes: 7 additions & 0 deletions dash-apps/web-compose/core/signals.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@
read: (r) => num(r.maxDischargePower) || undefined },
{ id: "odometer", kind: "number", unit: "dist", precision: 0, category: "vehicle", bus: "vehicle",
read: (r) => num(r.odometer) || undefined },
{ id: "trunkPosition", kind: "number", unit: "deg", precision: 0, range: () => [0, 95], category: "vehicle", bus: "vehicle",
read: (r) => num(r.trunkPosition) },

// surroundings
{ id: "stopLineDist", kind: "number", unit: "meter", precision: 0, range: () => [0, 200], category: "assist", bus: "any",
Expand All @@ -116,6 +118,11 @@
{ id: "leftBlindSpot", kind: "bool", unit: "none", category: "signals", bus: "any", read: (r) => flag(r.leftBlindSpot) },
{ id: "rightBlindSpot", kind: "bool", unit: "none", category: "signals", bus: "any", read: (r) => flag(r.rightBlindSpot) },
{ id: "anyDoorOpen", kind: "bool", unit: "none", category: "warnings", bus: "any", read: (r) => flag(r.anyDoorOpen) },
{ id: "doorOpenFL", kind: "bool", unit: "none", category: "warnings", bus: "vehicle", read: (r) => flag(r.doorOpenFL) },
{ id: "doorOpenFR", kind: "bool", unit: "none", category: "warnings", bus: "vehicle", read: (r) => flag(r.doorOpenFR) },
{ id: "doorOpenRL", kind: "bool", unit: "none", category: "warnings", bus: "vehicle", read: (r) => flag(r.doorOpenRL) },
{ id: "doorOpenRR", kind: "bool", unit: "none", category: "warnings", bus: "vehicle", read: (r) => flag(r.doorOpenRR) },
{ id: "trunkOpen", kind: "bool", unit: "none", category: "warnings", bus: "vehicle", read: (r) => flag(r.trunkOpen) },
{ id: "seatbeltUnbuckled", kind: "bool", unit: "none", category: "warnings", bus: "any",
read: (r) => (r.buckleStatus === undefined ? undefined : Number(r.buckleStatus) === 0) },
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ object VehicleControl {
const val CMD_AC_SWING_INTENSITY: Int = 0x4D
const val CMD_AC_SWING_TOGGLE: Int = 0x4E

// --- Door unlatch (VCSEC_requests 0x1F9); value = door index ---
const val CMD_DOOR_UNLATCH: Int = 0x50
const val DOOR_FRONT_LEFT: Int = 0
const val DOOR_FRONT_RIGHT: Int = 1
const val DOOR_REAR_LEFT: Int = 2
const val DOOR_REAR_RIGHT: Int = 3

/** Bind (or clear, with actionValue 0) an N-finger tap to a control action. */
fun sendFingerAction(manager: DashKitBleManager, fingers: Int, actionValue: Int): Boolean =
send(manager, CMD_MULTI_FINGER_ACTION, (fingers shl 8) or (actionValue and 0xFF))
Expand All @@ -119,6 +126,10 @@ object VehicleControl {
fun sendSportKickdownThreshold(manager: DashKitBleManager, percent: Int): Boolean =
send(manager, CMD_SPORT_KICKDOWN_THRESHOLD, percent)

/** Pop one door latch (firmware refuses it in R/N/D or while moving). */
fun sendDoorUnlatch(manager: DashKitBleManager, door: Int): Boolean =
send(manager, CMD_DOOR_UNLATCH, door)

fun sendAcSwingToggle(manager: DashKitBleManager): Boolean =
send(manager, CMD_AC_SWING_TOGGLE, 0)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ data class CarState(
val tpmsTempRL: Float = -100f,
val tpmsTempRR: Float = -100f,

// Closures: 1 while the door/trunk latch is not closed, else 0. Trunk angle
// in degrees from the powered liftgate (0 when closed or not powered).
val doorOpenFL: Float = 0f,
val doorOpenFR: Float = 0f,
val doorOpenRL: Float = 0f,
val doorOpenRR: Float = 0f,
val trunkOpen: Float = 0f,
val trunkPosition: Float = 0f,

// Empty until fully assembled from the CAN stream by the native mapper.
val vin: String = ""
) {
Expand All @@ -66,7 +75,7 @@ data class CarState(
)

companion object {
const val FIELD_COUNT = 47
const val FIELD_COUNT = 53
private const val KM_TO_MILES = 0.621371f

private val MILES_COUNTRIES = setOf("US", "GB", "MM", "LR")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ fun DashState.dashValues(systemDarkMode: Boolean? = null): Map<String, Any> {
"leftBlinker" to c.leftBlinker,
"rightBlinker" to c.rightBlinker,
"anyDoorOpen" to c.anyDoorOpen,
"doorOpenFL" to c.doorOpenFL,
"doorOpenFR" to c.doorOpenFR,
"doorOpenRL" to c.doorOpenRL,
"doorOpenRR" to c.doorOpenRR,
"trunkOpen" to c.trunkOpen,
"trunkPosition" to c.trunkPosition,
"buckleStatus" to c.buckleStatus,
"odometer" to c.odometer,
"adasOn" to c.adasOn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ class CarStateBridge(
@Volatile private var trafficLightColor: Float = 0f
@Volatile private var buckleStatus: Float = 0f
@Volatile private var anyDoorOpen: Float = 0f
@Volatile private var doorOpenFL: Float = 0f
@Volatile private var doorOpenFR: Float = 0f
@Volatile private var doorOpenRL: Float = 0f
@Volatile private var doorOpenRR: Float = 0f
@Volatile private var trunkOpen: Float = 0f
@Volatile private var trunkPosition: Float = 0f
@Volatile private var accSetSpeed: Float = 0f
@Volatile private var fullPackEnergy: Float = 0f
@Volatile private var nominalEnergyRemaining: Float = 0f
Expand Down Expand Up @@ -67,6 +73,12 @@ class CarStateBridge(
trafficLightColor = state.trafficLightColor
buckleStatus = state.buckleStatus
anyDoorOpen = state.anyDoorOpen
doorOpenFL = state.doorOpenFL
doorOpenFR = state.doorOpenFR
doorOpenRL = state.doorOpenRL
doorOpenRR = state.doorOpenRR
trunkOpen = state.trunkOpen
trunkPosition = state.trunkPosition
accSetSpeed = state.accSetSpeed
fullPackEnergy = state.fullPackEnergy
nominalEnergyRemaining = state.nominalEnergyRemaining
Expand Down Expand Up @@ -97,6 +109,12 @@ class CarStateBridge(
@JavascriptInterface fun getTrafficLightColor(): Float = trafficLightColor
@JavascriptInterface fun getBuckleStatus(): Float = buckleStatus
@JavascriptInterface fun getAnyDoorOpen(): Float = anyDoorOpen
@JavascriptInterface fun getDoorOpenFL(): Float = doorOpenFL
@JavascriptInterface fun getDoorOpenFR(): Float = doorOpenFR
@JavascriptInterface fun getDoorOpenRL(): Float = doorOpenRL
@JavascriptInterface fun getDoorOpenRR(): Float = doorOpenRR
@JavascriptInterface fun getTrunkOpen(): Float = trunkOpen
@JavascriptInterface fun getTrunkPosition(): Float = trunkPosition
@JavascriptInterface fun getAccSetSpeed(): Float = accSetSpeed
@JavascriptInterface fun getFullPackEnergy(): Float = fullPackEnergy
@JavascriptInterface fun getNominalEnergyRemaining(): Float = nominalEnergyRemaining
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.material.icons.rounded.SouthEast
import androidx.compose.material.icons.rounded.EvStation
import androidx.compose.material.icons.rounded.Flip
import androidx.compose.material.icons.rounded.Inbox
import androidx.compose.material.icons.rounded.MeetingRoom
import androidx.compose.material.icons.rounded.PushPin
import androidx.compose.material.icons.rounded.SwapHoriz
import androidx.compose.material.icons.rounded.Thermostat
Expand Down Expand Up @@ -175,6 +176,54 @@ val vehicleControls: List<ControlAction> = listOf(
manager?.let { VehicleControl.sendMirrorDipToggle(it) }
}
),
// Momentary latch release, like the Tesla app's "Unlatch Door". Firmware
// ignores it while the car is in gear or moving.
ControlAction(
id = "unlatch_front_left",
icon = Icons.Rounded.MeetingRoom,
labelRes = { R.string.control_unlatch_front_left },
active = { false },
gestureValue = 10,
perform = { manager ->
manager?.let { VehicleControl.sendDoorUnlatch(it, VehicleControl.DOOR_FRONT_LEFT) }
}
),
// Momentary latch release, like the Tesla app's "Unlatch Door". Firmware
// ignores it while the car is in gear or moving.
ControlAction(
id = "unlatch_front_right",
icon = Icons.Rounded.MeetingRoom,
labelRes = { R.string.control_unlatch_front_right },
active = { false },
gestureValue = 11,
perform = { manager ->
manager?.let { VehicleControl.sendDoorUnlatch(it, VehicleControl.DOOR_FRONT_RIGHT) }
}
),
// Momentary latch release, like the Tesla app's "Unlatch Door". Firmware
// ignores it while the car is in gear or moving.
ControlAction(
id = "unlatch_rear_left",
icon = Icons.Rounded.MeetingRoom,
labelRes = { R.string.control_unlatch_rear_left },
active = { false },
gestureValue = 12,
perform = { manager ->
manager?.let { VehicleControl.sendDoorUnlatch(it, VehicleControl.DOOR_REAR_LEFT) }
}
),
// Momentary latch release, like the Tesla app's "Unlatch Door". Firmware
// ignores it while the car is in gear or moving.
ControlAction(
id = "unlatch_rear_right",
icon = Icons.Rounded.MeetingRoom,
labelRes = { R.string.control_unlatch_rear_right },
active = { false },
gestureValue = 13,
perform = { manager ->
manager?.let { VehicleControl.sendDoorUnlatch(it, VehicleControl.DOOR_REAR_RIGHT) }
}
),
// Glovebox is an electronic latch release: open only (closed by hand).
ControlAction(
id = "glovebox",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,14 @@ class CanFrameDecoder(
tpmsTempFL = values[43].toFloat(),
tpmsTempFR = values[44].toFloat(),
tpmsTempRL = values[45].toFloat(),
tpmsTempRR = values[46].toFloat()
tpmsTempRR = values[46].toFloat(),
// Closures after TPMS (see CarState::DOOR_OFFSET): doors FL FR RL RR, trunk open, trunk angle.
doorOpenFL = values[47].toFloat(),
doorOpenFR = values[48].toFloat(),
doorOpenRL = values[49].toFloat(),
doorOpenRR = values[50].toFloat(),
trunkOpen = values[51].toFloat(),
trunkPosition = values[52].toFloat()
)
}

Expand Down
4 changes: 4 additions & 0 deletions dashpilot-android/app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@
<string name="control_mirror_dip">Spiegelabsenkung umschalten</string>
<string name="control_ac_swing">Klima-Schwenkmodus umschalten</string>
<string name="control_glovebox">Handschuhfach öffnen</string>
<string name="control_unlatch_front_left">Fahrertür entriegeln</string>
<string name="control_unlatch_front_right">Beifahrertür entriegeln</string>
<string name="control_unlatch_rear_left">Tür hinten links entriegeln</string>
<string name="control_unlatch_rear_right">Tür hinten rechts entriegeln</string>

<!-- Automations -->
<string name="automations_title">Automatisierungen</string>
Expand Down
4 changes: 4 additions & 0 deletions dashpilot-android/app/src/main/res/values-hu/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@
<string name="control_mirror_dip">Tükördöntés váltása</string>
<string name="control_ac_swing">Klíma legyező mód váltása</string>
<string name="control_glovebox">Kesztyűtartó nyitása</string>
<string name="control_unlatch_front_left">Vezetőajtó kioldása</string>
<string name="control_unlatch_front_right">Utasajtó kioldása</string>
<string name="control_unlatch_rear_left">Bal hátsó ajtó kioldása</string>
<string name="control_unlatch_rear_right">Jobb hátsó ajtó kioldása</string>

<!-- Automations -->
<string name="automations_title">Automatizációk</string>
Expand Down
Loading
Loading