diff --git a/bridge/bridge_ios.cpp b/bridge/bridge_ios.cpp index cf2b0d69..69c8af52 100644 --- a/bridge/bridge_ios.cpp +++ b/bridge/bridge_ios.cpp @@ -51,6 +51,10 @@ static void carStateToBridge(const CarState& cs, BridgeCarState* out) { out->tpmsTempFR = cs.tpmsTempFR; out->tpmsTempRL = cs.tpmsTempRL; out->tpmsTempRR = cs.tpmsTempRR; + out->tpmsBattFL = cs.tpmsBattFL; + out->tpmsBattFR = cs.tpmsBattFR; + out->tpmsBattRL = cs.tpmsBattRL; + out->tpmsBattRR = cs.tpmsBattRR; } static std::atomic receiveLoopRunning{false}; diff --git a/bridge/bridge_ios.h b/bridge/bridge_ios.h index c0743a8b..6d38572c 100644 --- a/bridge/bridge_ios.h +++ b/bridge/bridge_ios.h @@ -51,7 +51,7 @@ typedef struct { double experimentalMode; double changingLane; - // TPMS: pressure (bar, -1 = none) and temperature (degC, -100 = none) per wheel. + // TPMS: pressure (bar, -1 = none), temperature (degC, -100 = none) and sensor battery (V, -1 = none) per wheel. double tpmsPressureFL; double tpmsPressureFR; double tpmsPressureRL; @@ -60,6 +60,10 @@ typedef struct { double tpmsTempFR; double tpmsTempRL; double tpmsTempRR; + double tpmsBattFL; + double tpmsBattFR; + double tpmsBattRL; + double tpmsBattRR; } BridgeCarState; // Context diff --git a/bridge/car/car_state.h b/bridge/car/car_state.h index 19888cb1..2fda0025 100644 --- a/bridge/car/car_state.h +++ b/bridge/car/car_state.h @@ -46,8 +46,8 @@ struct CarState { double experimentalMode = 0; double changingLane = 0; - // Tyre pressure (bar) and temperature (degC) per wheel from VCSEC_TPMSData; - // pressure -1 = no reading, temperature -100 = no reading. + // Tyre pressure (bar), temperature (degC) and sensor battery (V) per wheel from + // VCSEC_TPMSData; pressure -1, temperature -100 and battery -1 = no reading. double tpmsPressureFL = -1; double tpmsPressureFR = -1; double tpmsPressureRL = -1; @@ -56,6 +56,10 @@ struct CarState { double tpmsTempFR = -100; double tpmsTempRL = -100; double tpmsTempRR = -100; + double tpmsBattFL = -1; + double tpmsBattFR = -1; + double tpmsBattRL = -1; + double tpmsBattRR = -1; // Vehicle VIN, empty until fully assembled by the mapper. static constexpr size_t VIN_LENGTH = 17; @@ -64,7 +68,7 @@ struct CarState { static constexpr size_t VIN_DOUBLE_COUNT = 3; 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 TPMS_COUNT = 12; static constexpr size_t FIELD_COUNT = TPMS_OFFSET + TPMS_COUNT; void toArray(double* out) const { @@ -117,7 +121,7 @@ struct CarState { std::memcpy(padded, vin, VIN_LENGTH); std::memcpy(out + VIN_OFFSET, padded, sizeof(padded)); - // TPMS (after VIN): pressures FL FR RL RR, then temperatures FL FR RL RR. + // TPMS (after VIN): pressures FL FR RL RR, temperatures FL FR RL RR, sensor batteries FL FR RL RR. out[TPMS_OFFSET + 0] = tpmsPressureFL; out[TPMS_OFFSET + 1] = tpmsPressureFR; out[TPMS_OFFSET + 2] = tpmsPressureRL; @@ -126,5 +130,9 @@ struct CarState { out[TPMS_OFFSET + 5] = tpmsTempFR; out[TPMS_OFFSET + 6] = tpmsTempRL; out[TPMS_OFFSET + 7] = tpmsTempRR; + out[TPMS_OFFSET + 8] = tpmsBattFL; + out[TPMS_OFFSET + 9] = tpmsBattFR; + out[TPMS_OFFSET + 10] = tpmsBattRL; + out[TPMS_OFFSET + 11] = tpmsBattRR; } }; diff --git a/bridge/car/cars/tesla.h b/bridge/car/cars/tesla.h index e0cff503..bd212ff1 100644 --- a/bridge/car/cars/tesla.h +++ b/bridge/car/cars/tesla.h @@ -187,7 +187,7 @@ class TeslaDashKitMapper : public CarStateMapper { updateVehicleBus(cp, cs); vin_.update(cp, cs); // TPMS: pressures come from mux 5 (all four wheels, 0.025 bar resolution); - // temperatures from muxes 0-3 (one sensor each). Their per-sensor pressure is + // temperatures and sensor battery voltages from muxes 0-3 (one sensor each). Their per-sensor pressure is // coarser (0.05 bar) and would alternate with mux 5, so it is not used. // Fields keep their last value until a frame carries that wheel again. // getRaw fails until a frame has been seen; get() would read 0 and look like mux 0. @@ -205,6 +205,9 @@ class TeslaDashKitMapper : public CarStateMapper { double* tempOfMux[4] = {&cs.tpmsTempRR, &cs.tpmsTempRL, &cs.tpmsTempFR, &cs.tpmsTempFL}; double t = cp.get(1, "VCSEC_TPMSData", "VCSEC_TPMSTemperature" + std::to_string(tpmsIndex)); if (t < 214.5) *tempOfMux[tpmsIndex] = t; + double* battOfMux[4] = {&cs.tpmsBattRR, &cs.tpmsBattRL, &cs.tpmsBattFR, &cs.tpmsBattFL}; + double v = cp.get(1, "VCSEC_TPMSData", "VCSEC_TPMSBatVoltage" + std::to_string(tpmsIndex)); + if (v < 4.045) *battOfMux[tpmsIndex] = v; // raw 0xFF (4.05 V) = not available } } } diff --git a/dash-apps/README.md b/dash-apps/README.md index 6951f3c7..530f404a 100644 --- a/dash-apps/README.md +++ b/dash-apps/README.md @@ -95,6 +95,14 @@ Values not received yet are `0` / `false`. For speed limit, stop line and energy | `maxRegenPower`, `maxDischargePower` | kW | current limits | | `packTMin`, `packTMax` | °C, or °F when `isImperial` | | +**Tyres** (Tesla via DashKit) + +| key | type | meaning | +|---|---|---| +| `tpmsPressureFL`, `tpmsPressureFR`, `tpmsPressureRL`, `tpmsPressureRR` | bar | -1 = no reading | +| `tpmsTempFL`, `tpmsTempFR`, `tpmsTempRL`, `tpmsTempRR` | °C | -100 = no reading | +| `tpmsBattFL`, `tpmsBattFR`, `tpmsBattRL`, `tpmsBattRR` | V | sensor battery, -1 = no reading | + The app's CarState also has climate fields and the VIN; dash apps don't receive those yet. ## Rive diff --git a/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/datamodel/dash/CarState.kt b/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/datamodel/dash/CarState.kt index faa9247d..15c39208 100644 --- a/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/datamodel/dash/CarState.kt +++ b/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/datamodel/dash/CarState.kt @@ -43,7 +43,7 @@ data class CarState( val changingLane: Boolean = false, - // TPMS: pressure (bar, -1 = none) and temperature (degC, -100 = none) per wheel. + // TPMS: pressure (bar, -1 = none), temperature (degC, -100 = none) and sensor battery (V, -1 = none) per wheel. val tpmsPressureFL: Float = -1f, val tpmsPressureFR: Float = -1f, val tpmsPressureRL: Float = -1f, @@ -52,6 +52,10 @@ data class CarState( val tpmsTempFR: Float = -100f, val tpmsTempRL: Float = -100f, val tpmsTempRR: Float = -100f, + val tpmsBattFL: Float = -1f, + val tpmsBattFR: Float = -1f, + val tpmsBattRL: Float = -1f, + val tpmsBattRR: Float = -1f, // Empty until fully assembled from the CAN stream by the native mapper. val vin: String = "" @@ -66,7 +70,7 @@ data class CarState( ) companion object { - const val FIELD_COUNT = 47 + const val FIELD_COUNT = 51 private const val KM_TO_MILES = 0.621371f private val MILES_COUNTRIES = setOf("US", "GB", "MM", "LR") diff --git a/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/datamodel/dash/DashValues.kt b/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/datamodel/dash/DashValues.kt index d078a87d..d74d7133 100644 --- a/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/datamodel/dash/DashValues.kt +++ b/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/datamodel/dash/DashValues.kt @@ -36,6 +36,18 @@ fun DashState.dashValues(systemDarkMode: Boolean? = null): Map { "maxDischargePower" to c.maxDischargePower, "packTMin" to c.packTMin, "packTMax" to c.packTMax, + "tpmsPressureFL" to c.tpmsPressureFL, + "tpmsPressureFR" to c.tpmsPressureFR, + "tpmsPressureRL" to c.tpmsPressureRL, + "tpmsPressureRR" to c.tpmsPressureRR, + "tpmsTempFL" to c.tpmsTempFL, + "tpmsTempFR" to c.tpmsTempFR, + "tpmsTempRL" to c.tpmsTempRL, + "tpmsTempRR" to c.tpmsTempRR, + "tpmsBattFL" to c.tpmsBattFL, + "tpmsBattFR" to c.tpmsBattFR, + "tpmsBattRL" to c.tpmsBattRL, + "tpmsBattRR" to c.tpmsBattRR, // Rive numbers are 32-bit floats, too coarse for epoch milliseconds. "currentTime" to secondsSinceLocalMidnight(currentTime), "phoneBattery" to phoneBattery.toFloat(), diff --git a/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/js/CarStateBridge.kt b/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/js/CarStateBridge.kt index b7ae1c5d..018ba450 100644 --- a/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/js/CarStateBridge.kt +++ b/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/js/CarStateBridge.kt @@ -50,6 +50,18 @@ class CarStateBridge( @Volatile private var renderQuality: Int = 3 @Volatile private var darkModeBackgroundGray: Int = 0 @Volatile private var changingLane: Boolean = false + @Volatile private var tpmsPressureFL: Float = -1f + @Volatile private var tpmsPressureFR: Float = -1f + @Volatile private var tpmsPressureRL: Float = -1f + @Volatile private var tpmsPressureRR: Float = -1f + @Volatile private var tpmsTempFL: Float = -100f + @Volatile private var tpmsTempFR: Float = -100f + @Volatile private var tpmsTempRL: Float = -100f + @Volatile private var tpmsTempRR: Float = -100f + @Volatile private var tpmsBattFL: Float = -1f + @Volatile private var tpmsBattFR: Float = -1f + @Volatile private var tpmsBattRL: Float = -1f + @Volatile private var tpmsBattRR: Float = -1f @Volatile private var speedCameraDistance: Int = -1 @Volatile private var dataSourceType: String = "" @@ -82,6 +94,18 @@ class CarStateBridge( experimentalMode = state.experimentalMode madsActive = state.madsActive changingLane = state.changingLane + tpmsPressureFL = state.tpmsPressureFL + tpmsPressureFR = state.tpmsPressureFR + tpmsPressureRL = state.tpmsPressureRL + tpmsPressureRR = state.tpmsPressureRR + tpmsTempFL = state.tpmsTempFL + tpmsTempFR = state.tpmsTempFR + tpmsTempRL = state.tpmsTempRL + tpmsTempRR = state.tpmsTempRR + tpmsBattFL = state.tpmsBattFL + tpmsBattFR = state.tpmsBattFR + tpmsBattRL = state.tpmsBattRL + tpmsBattRR = state.tpmsBattRR } @JavascriptInterface fun getEgoSteeringAngle(): Float = egoSteeringAngle @@ -122,6 +146,18 @@ class CarStateBridge( @JavascriptInterface fun getRenderQuality(): Int = renderQuality @JavascriptInterface fun getDarkModeBackgroundGray(): Int = darkModeBackgroundGray @JavascriptInterface fun isChangingLane(): Boolean = changingLane + @JavascriptInterface fun getTpmsPressureFL(): Float = tpmsPressureFL + @JavascriptInterface fun getTpmsPressureFR(): Float = tpmsPressureFR + @JavascriptInterface fun getTpmsPressureRL(): Float = tpmsPressureRL + @JavascriptInterface fun getTpmsPressureRR(): Float = tpmsPressureRR + @JavascriptInterface fun getTpmsTempFL(): Float = tpmsTempFL + @JavascriptInterface fun getTpmsTempFR(): Float = tpmsTempFR + @JavascriptInterface fun getTpmsTempRL(): Float = tpmsTempRL + @JavascriptInterface fun getTpmsTempRR(): Float = tpmsTempRR + @JavascriptInterface fun getTpmsBattFL(): Float = tpmsBattFL + @JavascriptInterface fun getTpmsBattFR(): Float = tpmsBattFR + @JavascriptInterface fun getTpmsBattRL(): Float = tpmsBattRL + @JavascriptInterface fun getTpmsBattRR(): Float = tpmsBattRR @JavascriptInterface fun getSpeedCameraDistance(): Int = speedCameraDistance fun updateSpeedCameraDistance(distanceMeters: Int) { diff --git a/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/vehicle/CanFrameDecoder.kt b/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/vehicle/CanFrameDecoder.kt index 603df1ef..d7b4f8a2 100644 --- a/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/vehicle/CanFrameDecoder.kt +++ b/dashpilot-android/app/src/main/java/com/softwiredtech/dashpilot/vehicle/CanFrameDecoder.kt @@ -66,7 +66,7 @@ class CanFrameDecoder( hvacKeepClimateOn = values[34].toFloat(), powerLevel = values[35].toFloat(), vin = decodeVin(values, offset = 36), - // TPMS after VIN (see CarState::TPMS_OFFSET): pressures FL FR RL RR, temperatures FL FR RL RR. + // TPMS after VIN (see CarState::TPMS_OFFSET): pressures, temperatures, sensor batteries; FL FR RL RR each. tpmsPressureFL = values[39].toFloat(), tpmsPressureFR = values[40].toFloat(), tpmsPressureRL = values[41].toFloat(), @@ -74,7 +74,11 @@ class CanFrameDecoder( tpmsTempFL = values[43].toFloat(), tpmsTempFR = values[44].toFloat(), tpmsTempRL = values[45].toFloat(), - tpmsTempRR = values[46].toFloat() + tpmsTempRR = values[46].toFloat(), + tpmsBattFL = values[47].toFloat(), + tpmsBattFR = values[48].toFloat(), + tpmsBattRL = values[49].toFloat(), + tpmsBattRR = values[50].toFloat() ) } diff --git a/dashpilot-ios/dashpilot/JS/CarStateBridge.swift b/dashpilot-ios/dashpilot/JS/CarStateBridge.swift index 9601650a..fb423453 100644 --- a/dashpilot-ios/dashpilot/JS/CarStateBridge.swift +++ b/dashpilot-ios/dashpilot/JS/CarStateBridge.swift @@ -5,6 +5,6 @@ extension DashState { let cs = carState let ds = displaySettings let source = dataSourceType.map { "\"\($0.rawValue)\"" } ?? "null" - return "{\"egoSteeringAngle\":\(cs.egoSteeringAngle),\"egoSpeed\":\(cs.egoSpeed),\"leftBlinker\":\(cs.leftBlinker),\"rightBlinker\":\(cs.rightBlinker),\"gear\":\(cs.gear),\"adasOn\":\(cs.adasOn),\"leftBlindSpot\":\(cs.leftBlindSpot),\"rightBlindSpot\":\(cs.rightBlindSpot),\"fusedSpeedLimit\":\(cs.fusedSpeedLimit),\"stopLineDist\":\(cs.stopLineDist),\"trafficLightColor\":\(cs.trafficLightColor),\"anyDoorOpen\":\(cs.anyDoorOpen),\"buckleStatus\":\(cs.buckleStatus),\"accSetSpeed\":\(cs.accSetSpeed),\"fullPackEnergy\":\(cs.fullPackEnergy),\"nominalEnergyRemaining\":\(cs.nominalEnergyRemaining),\"energyBuffer\":\(cs.energyBuffer),\"maxRegenPower\":\(cs.maxRegenPower),\"maxDischargePower\":\(cs.maxDischargePower),\"packVoltage\":\(cs.packVoltage),\"packCurrent\":\(cs.packCurrent),\"powerLevel\":\(cs.powerLevel),\"packTMin\":\(cs.packTMin),\"packTMax\":\(cs.packTMax),\"odometer\":\(cs.odometer),\"madsActive\":\(cs.madsActive),\"selfdriveActive\":\(cs.selfdriveActive),\"experimentalMode\":\(cs.experimentalMode),\"changingLane\":\(cs.changingLane),\"phoneBattery\":\(phoneBattery),\"currentTime\":\(currentTime),\"dataSourceType\":\(source),\"showPhoneBattery\":\(ds.showPhoneBattery),\"showCarBattery\":\(ds.showCarBattery),\"showOdometer\":\(ds.showOdometer),\"isImperial\":\(ds.useImperial),\"darkMode\":\(ds.darkMode),\"alwaysOnBlindSpotMonitor\":\(ds.alwaysOnBlindSpotMonitor),\"renderQuality\":\(ds.renderQuality),\"darkModeBackgroundGray\":\(ds.darkModeBackgroundGray)}" + return "{\"egoSteeringAngle\":\(cs.egoSteeringAngle),\"egoSpeed\":\(cs.egoSpeed),\"leftBlinker\":\(cs.leftBlinker),\"rightBlinker\":\(cs.rightBlinker),\"gear\":\(cs.gear),\"adasOn\":\(cs.adasOn),\"leftBlindSpot\":\(cs.leftBlindSpot),\"rightBlindSpot\":\(cs.rightBlindSpot),\"fusedSpeedLimit\":\(cs.fusedSpeedLimit),\"stopLineDist\":\(cs.stopLineDist),\"trafficLightColor\":\(cs.trafficLightColor),\"anyDoorOpen\":\(cs.anyDoorOpen),\"buckleStatus\":\(cs.buckleStatus),\"accSetSpeed\":\(cs.accSetSpeed),\"fullPackEnergy\":\(cs.fullPackEnergy),\"nominalEnergyRemaining\":\(cs.nominalEnergyRemaining),\"energyBuffer\":\(cs.energyBuffer),\"maxRegenPower\":\(cs.maxRegenPower),\"maxDischargePower\":\(cs.maxDischargePower),\"packVoltage\":\(cs.packVoltage),\"packCurrent\":\(cs.packCurrent),\"powerLevel\":\(cs.powerLevel),\"packTMin\":\(cs.packTMin),\"packTMax\":\(cs.packTMax),\"odometer\":\(cs.odometer),\"madsActive\":\(cs.madsActive),\"selfdriveActive\":\(cs.selfdriveActive),\"experimentalMode\":\(cs.experimentalMode),\"changingLane\":\(cs.changingLane),\"tpmsPressureFL\":\(cs.tpmsPressureFL),\"tpmsPressureFR\":\(cs.tpmsPressureFR),\"tpmsPressureRL\":\(cs.tpmsPressureRL),\"tpmsPressureRR\":\(cs.tpmsPressureRR),\"tpmsTempFL\":\(cs.tpmsTempFL),\"tpmsTempFR\":\(cs.tpmsTempFR),\"tpmsTempRL\":\(cs.tpmsTempRL),\"tpmsTempRR\":\(cs.tpmsTempRR),\"tpmsBattFL\":\(cs.tpmsBattFL),\"tpmsBattFR\":\(cs.tpmsBattFR),\"tpmsBattRL\":\(cs.tpmsBattRL),\"tpmsBattRR\":\(cs.tpmsBattRR),\"phoneBattery\":\(phoneBattery),\"currentTime\":\(currentTime),\"dataSourceType\":\(source),\"showPhoneBattery\":\(ds.showPhoneBattery),\"showCarBattery\":\(ds.showCarBattery),\"showOdometer\":\(ds.showOdometer),\"isImperial\":\(ds.useImperial),\"darkMode\":\(ds.darkMode),\"alwaysOnBlindSpotMonitor\":\(ds.alwaysOnBlindSpotMonitor),\"renderQuality\":\(ds.renderQuality),\"darkModeBackgroundGray\":\(ds.darkModeBackgroundGray)}" } } diff --git a/dashpilot-ios/dashpilot/Models/CarState.swift b/dashpilot-ios/dashpilot/Models/CarState.swift index 1030a748..d08b8d48 100644 --- a/dashpilot-ios/dashpilot/Models/CarState.swift +++ b/dashpilot-ios/dashpilot/Models/CarState.swift @@ -43,7 +43,7 @@ struct CarState: Codable { var experimentalMode: Bool = false var changingLane: Bool = false - // Tyre pressure (bar, -1 = none) and temperature (degC, -100 = none) per wheel. + // Tyre pressure (bar, -1 = none), temperature (degC, -100 = none) and sensor battery (V, -1 = none) per wheel. var tpmsPressureFL: Float = -1 var tpmsPressureFR: Float = -1 var tpmsPressureRL: Float = -1 @@ -52,6 +52,10 @@ struct CarState: Codable { var tpmsTempFR: Float = -100 var tpmsTempRL: Float = -100 var tpmsTempRR: Float = -100 + var tpmsBattFL: Float = -1 + var tpmsBattFR: Float = -1 + var tpmsBattRL: Float = -1 + var tpmsBattRR: Float = -1 init() {} @@ -100,6 +104,10 @@ struct CarState: Codable { tpmsTempFR = Float(bridge.tpmsTempFR) tpmsTempRL = Float(bridge.tpmsTempRL) tpmsTempRR = Float(bridge.tpmsTempRR) + tpmsBattFL = Float(bridge.tpmsBattFL) + tpmsBattFR = Float(bridge.tpmsBattFR) + tpmsBattRL = Float(bridge.tpmsBattRL) + tpmsBattRR = Float(bridge.tpmsBattRR) } private static let kmToMiles: Float = 0.621371 diff --git a/dashpilot-ios/dashpilot/Models/DashValues.swift b/dashpilot-ios/dashpilot/Models/DashValues.swift index eb4a0bec..becc8db5 100644 --- a/dashpilot-ios/dashpilot/Models/DashValues.swift +++ b/dashpilot-ios/dashpilot/Models/DashValues.swift @@ -40,6 +40,18 @@ extension DashState { ("maxDischargePower", .number(c.maxDischargePower)), ("packTMin", .number(c.packTMin)), ("packTMax", .number(c.packTMax)), + ("tpmsPressureFL", .number(c.tpmsPressureFL)), + ("tpmsPressureFR", .number(c.tpmsPressureFR)), + ("tpmsPressureRL", .number(c.tpmsPressureRL)), + ("tpmsPressureRR", .number(c.tpmsPressureRR)), + ("tpmsTempFL", .number(c.tpmsTempFL)), + ("tpmsTempFR", .number(c.tpmsTempFR)), + ("tpmsTempRL", .number(c.tpmsTempRL)), + ("tpmsTempRR", .number(c.tpmsTempRR)), + ("tpmsBattFL", .number(c.tpmsBattFL)), + ("tpmsBattFR", .number(c.tpmsBattFR)), + ("tpmsBattRL", .number(c.tpmsBattRL)), + ("tpmsBattRR", .number(c.tpmsBattRR)), // Rive numbers are 32-bit floats, too coarse for epoch milliseconds. ("currentTime", .number(Self.secondsSinceLocalMidnight(currentTime))), ("phoneBattery", .number(Float(phoneBattery))),