OVMS3/OVMS.V3/components/vehicle_vweup/src/vweup_t26.cpp

1584 lines
40 KiB
C++

/*
; Project: Open Vehicle Monitor System
; Date: 5th July 2018
;
; Changes:
; 1.0 Initial release
;
; (C) 2011 Michael Stegen / Stegen Electronics
; (C) 2011-2018 Mark Webb-Johnson
; (C) 2011 Sonny Chen @ EPRO/DX
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
*/
/*
; Subproject: Integration of support for the VW e-UP via Comfort CAN
; Date: 28th January 2021
;
; Changes:
; 0.1.0 Initial code
: Code frame with correct TAG and vehicle/can registration
;
; 0.1.1 Started with some SOC capture demo code
;
; 0.1.2 Added VIN, speed, 12 volt battery detection
;
; 0.1.3 Added ODO (Dimitrie78), fixed speed
;
; 0.1.4 Added WLTP based ideal range, uncertain outdoor temperature
;
; 0.1.5 Finalized SOC calculation (sharkcow), added estimated range
;
; 0.1.6 Created a climate control first try, removed 12 volt battery status
;
; 0.1.7 Added status of doors
;
; 0.1.8 Added config page for the webfrontend. Features canwrite and modelyear.
; Differentiation between model years is now possible.
;
; 0.1.9 "Fixed" crash on climate control. Added A/C indicator.
; First shot on battery temperatur.
;
; 0.2.0 Added key detection and car_on / pollingstate routine
;
; 0.2.1 Removed battery temperature, corrected outdoor temperature
;
; 0.2.2 Collect VIN only once
;
; 0.2.3 Redesign climate control, removed bluetooth template
;
; 0.2.4 First implementation of the ringbus and ocu heartbeat
;
; 0.2.5 Fixed heartbeat
;
; 0.2.6 Refined climate control template
;
; 0.2.7 Implemented dev_mode, 5A7, 69E climate control messages
; 0.2.8 Refactoring for T26A <-> OBD source separation (by SokoFromNZ)
;
; 0.2.9 Fixed climate control
;
; 0.3.0 Added upgrade policy for pre vehicle id splitting versions
;
; 0.3.1 Removed alpha OBD source, corrected class name
;
; 0.3.2 First implementation of charging detection
;
; 0.3.3 Buffer 0x61C ghost messages
;
; 0.3.4 Climate Control is now beginning to work
:
; 0.3.5 Stabilized Climate Control
;
; 0.3.6 Corrected log tag and namespaces
;
; 0.3.7 Add locked detection, add climate control via Homelink for iOS
;
; 0.3.8 Add lights, rear doors and trunk detection
;
; 0.3.9 Corrected estimated range
;
; 0.4.0 Implemnted ICCB charging detection
;
; 0.4.1 Corrected estimated range
;
; 0.4.2 Corrected locked status, cabin temperature
:
; 0.4.3 Added "feature" parameters for model year and cabin temperature setting
;
; 0.4.4 Respond to vehicle turning climate control off
;
; 0.4.5 Moved to unified ODB/T26 code
;
; 0.4.6 Corrected cabin temperature selection
;
; 0.4.7 Beautified code
;
; 0.4.8 Started using ms_v_env_charging12v
;
; 0.4.9 Added T26 awake detection for OBD
;
; (C) 2021 Chris van der Meijden
;
; Big thanx to sharkcow, Dimitrie78, E-lmo, Dexter and 'der kleine Nik'.
*/
#include "ovms_log.h"
static const char *TAG = "v-vweup";
#include <stdio.h>
#include "pcp.h"
#include "vehicle_vweup.h"
#include "vweup_t26.h"
#include "metrics_standard.h"
#include "ovms_webserver.h"
#include "ovms_events.h"
#include "ovms_metrics.h"
void OvmsVehicleVWeUp::sendOcuHeartbeat(TimerHandle_t timer)
{
// Workaround for FreeRTOS duplicate timer callback bug
// (see https://github.com/espressif/esp-idf/issues/8234)
static TickType_t last_tick = 0;
TickType_t tick = xTaskGetTickCount();
if (tick < last_tick + xTimerGetPeriod(timer) - 3) return;
last_tick = tick;
OvmsVehicleVWeUp *vweup = (OvmsVehicleVWeUp *)pvTimerGetTimerID(timer);
vweup->SendOcuHeartbeat();
}
void OvmsVehicleVWeUp::ccCountdown(TimerHandle_t timer)
{
// Workaround for FreeRTOS duplicate timer callback bug
// (see https://github.com/espressif/esp-idf/issues/8234)
static TickType_t last_tick = 0;
TickType_t tick = xTaskGetTickCount();
if (tick < last_tick + xTimerGetPeriod(timer) - 3) return;
last_tick = tick;
OvmsVehicleVWeUp *vweup = (OvmsVehicleVWeUp *)pvTimerGetTimerID(timer);
vweup->CCCountdown();
}
void OvmsVehicleVWeUp::T26Init()
{
ESP_LOGI(TAG, "Starting connection: T26A (Comfort CAN)");
memset(m_vin, 0, sizeof(m_vin));
RegisterCanBus(3, CAN_MODE_ACTIVE, CAN_SPEED_100KBPS);
MyConfig.RegisterParam("xvu", "VW e-Up", true, true);
vin_part1 = false;
vin_part2 = false;
vin_part3 = false;
vweup_remote_climate_ticker = 0;
ocu_awake = false;
ocu_working = false;
ocu_what = false;
ocu_wait = false;
vweup_cc_on = false;
vweup_cc_turning_on = false;
vweup_cc_temp_int = 22;
fas_counter_on = 0;
fas_counter_off = 0;
signal_ok = false;
cc_count = 0;
cd_count = 0;
t26_12v_boost = false;
t26_car_on = false;
t26_ring_awake = false;
t26_12v_boost_cnt = 0;
t26_12v_wait_off = 0;
dev_mode = false; // true disables writing on the comfort CAN. For code debugging only.
StandardMetrics.ms_v_env_locked->SetValue(true);
StandardMetrics.ms_v_env_headlights->SetValue(false);
StandardMetrics.ms_v_env_charging12v->SetValue(false);
StandardMetrics.ms_v_env_awake->SetValue(false);
StandardMetrics.ms_v_env_aux12v->SetValue(false);
StandardMetrics.ms_v_env_on->SetValue(false);
if (HasNoOBD()) {
StandardMetrics.ms_v_charge_mode->SetValue("standard");
}
}
void OvmsVehicleVWeUp::T26Ticker1(uint32_t ticker)
{
// Autodisable climate control ticker (30 min.)
if (vweup_remote_climate_ticker != 0) {
vweup_remote_climate_ticker--;
if (vweup_remote_climate_ticker == 1) {
SendCommand(AUTO_DISABLE_CLIMATE_CONTROL);
}
}
// Car disabled climate control
if (!StandardMetrics.ms_v_env_on->AsBool() &&
vweup_remote_climate_ticker < 1770 &&
vweup_remote_climate_ticker != 0 &&
!StandardMetrics.ms_v_env_hvac->AsBool())
{
ESP_LOGI(TAG, "Car disabled Climate Control or cc did not turn on");
vweup_remote_climate_ticker = 0;
vweup_cc_on = false;
ocu_awake = true;
}
if (StdMetrics.ms_v_bat_12v_voltage->AsFloat() < 13 && !t26_ring_awake && StandardMetrics.ms_v_env_charging12v->AsBool()) {
// Wait for 12v voltage to come up to 13.2v while getting a boost:
t26_12v_boost_cnt++;
if (t26_12v_boost_cnt > 20) {
ESP_LOGI(TAG, "Car stopped itself charging the 12v battery");
StandardMetrics.ms_v_env_charging12v->SetValue(false);
StandardMetrics.ms_v_env_aux12v->SetValue(false);
t26_12v_boost = false;
t26_12v_boost_cnt = 0;
// Clear powers & currents that are not supported by T26:
StdMetrics.ms_v_bat_current->SetValue(0);
StdMetrics.ms_v_bat_power->SetValue(0);
StdMetrics.ms_v_bat_12v_current->SetValue(0);
StdMetrics.ms_v_charge_12v_current->SetValue(0);
StdMetrics.ms_v_charge_12v_power->SetValue(0);
t26_12v_wait_off = 120; // Wait for two minutes before allowing new polling
PollSetState(VWEUP_OFF);
}
}
if (StdMetrics.ms_v_bat_12v_voltage->AsFloat() >= 13 && t26_12v_boost_cnt == 0) {
t26_12v_boost_cnt = 20;
}
if (t26_12v_boost_last_cnt == t26_12v_boost_cnt && t26_12v_boost_cnt != 0 && t26_12v_boost_cnt != 20) {
// We are not waiting to charging 12v to come up anymore:
t26_12v_boost_cnt = 0;
}
if (t26_12v_wait_off != 0) {
t26_12v_wait_off--;
}
t26_12v_boost_last_cnt = t26_12v_boost_cnt;
}
// Takes care of setting all the state appropriate when the car is on
// or off.
//
void OvmsVehicleVWeUp::vehicle_vweup_car_on(bool turnOn)
{
if (turnOn && !StandardMetrics.ms_v_env_on->AsBool()) {
// Log once that car is being turned on
ESP_LOGI(TAG, "CAR IS ON");
StandardMetrics.ms_v_env_on->SetValue(true);
if (!StandardMetrics.ms_v_charge_inprogress->AsBool()) {
t26_12v_boost_cnt = 0;
t26_12v_wait_off = 0;
SetUsePhase(UP_Driving);
StdMetrics.ms_v_door_chargeport->SetValue(false);
PollSetState(VWEUP_ON);
} else {
PollSetState(VWEUP_CHARGING);
}
ResetTripCounters();
// Turn off possibly running climate control timer
if (ocu_awake) {
xTimerStop(m_sendOcuHeartbeat, 0);
xTimerDelete(m_sendOcuHeartbeat, 0);
m_sendOcuHeartbeat = NULL;
}
if (cc_count != 0) {
xTimerStop(m_ccCountdown, 0);
xTimerDelete(m_ccCountdown, 0);
m_ccCountdown = NULL;
}
ocu_awake = false;
ocu_working = false;
vweup_remote_climate_ticker = 0;
fas_counter_on = 0;
fas_counter_off = 0;
t26_car_on = true;
StandardMetrics.ms_v_env_charging12v->SetValue(true);
StandardMetrics.ms_v_env_aux12v->SetValue(true);
}
else if (!turnOn && StandardMetrics.ms_v_env_on->AsBool()) {
// Log once that car is being turned off
ESP_LOGI(TAG, "CAR IS OFF");
t26_car_on = false;
t26_12v_boost = false;
StandardMetrics.ms_v_env_on->SetValue(false);
// StandardMetrics.ms_v_charge_voltage->SetValue(0);
// StandardMetrics.ms_v_charge_current->SetValue(0);
if (StandardMetrics.ms_v_charge_inprogress->AsBool()) {
PollSetState(VWEUP_CHARGING);
} else {
PollSetState(VWEUP_AWAKE);
}
}
}
void OvmsVehicleVWeUp::IncomingFrameCan3(CAN_frame_t *p_frame)
{
uint8_t *d = p_frame->data.u8;
static bool isCharging = false;
static bool lastCharging = false;
// This will log all incoming frames
// ESP_LOGD(TAG, "IFC %03x 8 %02x %02x %02x %02x %02x %02x %02x %02x", p_frame->MsgID, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]);
switch (p_frame->MsgID) {
case 0x61A: // SOC.
// If available, OBD is normally responsible for the SOC, but K-CAN occasionally
// sends SOC updates while OBD is in state OFF:
if (HasNoOBD() || IsOff()) {
StandardMetrics.ms_v_bat_soc->SetValue(d[7] / 2.0);
}
if (HasNoOBD()) {
if (vweup_modelyear >= 2020) {
StandardMetrics.ms_v_bat_range_ideal->SetValue((260 * (d[7] / 2.0)) / 100.0); // This is dirty. Based on WLTP only. Should be based on SOH.
}
else {
StandardMetrics.ms_v_bat_range_ideal->SetValue((160 * (d[7] / 2.0)) / 100.0); // This is dirty. Based on WLTP only. Should be based on SOH.
}
}
break;
case 0x52D: // KM range left (estimated).
if (d[0] != 0xFE) {
if (d[1] == 0x41) {
StandardMetrics.ms_v_bat_range_est->SetValue(d[0] + 255);
}
else {
StandardMetrics.ms_v_bat_range_est->SetValue(d[0]);
}
}
break;
case 0x65F: // VIN
switch (d[0]) {
case 0x00:
// Part 1
m_vin[0] = d[5];
m_vin[1] = d[6];
m_vin[2] = d[7];
vin_part1 = true;
break;
case 0x01:
// Part 2
if (vin_part1) {
m_vin[3] = d[1];
m_vin[4] = d[2];
m_vin[5] = d[3];
m_vin[6] = d[4];
m_vin[7] = d[5];
m_vin[8] = d[6];
m_vin[9] = d[7];
vin_part2 = true;
}
break;
case 0x02:
// Part 3 - VIN complete
if (vin_part2 && !vin_part3) {
m_vin[10] = d[1];
m_vin[11] = d[2];
m_vin[12] = d[3];
m_vin[13] = d[4];
m_vin[14] = d[5];
m_vin[15] = d[6];
m_vin[16] = d[7];
m_vin[17] = 0;
vin_part3 = true;
StandardMetrics.ms_v_vin->SetValue((string)m_vin);
}
break;
}
break;
case 0x65D: { // ODO
float odo = (float) (((uint32_t)(d[3] & 0xf) << 12) | ((UINT)d[2] << 8) | d[1]);
StandardMetrics.ms_v_pos_odometer->SetValue(odo);
break;
}
case 0x320: // Speed
StandardMetrics.ms_v_pos_speed->SetValue(((d[4] << 8) + d[3] - 1) / 190);
UpdateTripOdo();
if (HasNoOBD())
CalculateAcceleration(); // only necessary until we find acceleration on T26
break;
case 0x527: // Outdoor temperature
StandardMetrics.ms_v_env_temp->SetValue((d[5] - 100) / 2);
break;
case 0x381: // Vehicle locked
if (d[0] == 0x02) {
StandardMetrics.ms_v_env_locked->SetValue(true);
}
else {
StandardMetrics.ms_v_env_locked->SetValue(false);
}
break;
case 0x3E3: // Cabin temperature
if (d[2] != 0xFF) {
StandardMetrics.ms_v_env_cabintemp->SetValue((d[2] - 100) / 2);
// Set PEM inv temp to support older app version with cabin temp workaround display
// StandardMetrics.ms_v_inv_temp->SetValue((d[2] - 100) / 2);
}
break;
case 0x470: // Doors
StandardMetrics.ms_v_door_fl->SetValue((d[1] & 0x01) > 0);
StandardMetrics.ms_v_door_fr->SetValue((d[1] & 0x02) > 0);
StandardMetrics.ms_v_door_rl->SetValue((d[1] & 0x04) > 0);
StandardMetrics.ms_v_door_rr->SetValue((d[1] & 0x08) > 0);
StandardMetrics.ms_v_door_trunk->SetValue((d[1] & 0x20) > 0);
StandardMetrics.ms_v_door_hood->SetValue((d[1] & 0x10) > 0);
break;
case 0x531: // Head lights
if (d[0] > 0) {
StandardMetrics.ms_v_env_headlights->SetValue(true);
}
else {
StandardMetrics.ms_v_env_headlights->SetValue(false);
}
break;
// Check for running hvac.
case 0x3E1:
if (d[4] > 0) {
StandardMetrics.ms_v_env_hvac->SetValue(true);
}
else {
StandardMetrics.ms_v_env_hvac->SetValue(false);
}
break;
case 0x571: // 12 Volt
StandardMetrics.ms_v_charge_12v_voltage->SetValue(5 + (0.05 * d[0]));
break;
case 0x61C: // Charge detection
cd_count++;
if (d[2] < 0x07) {
isCharging = true;
}
else {
isCharging = false;
}
if (isCharging != lastCharging) {
// count till 3 messages in a row to stop ghost triggering
if (isCharging && cd_count == 3) {
cd_count = 0;
SetUsePhase(UP_Charging);
ResetChargeCounters();
StandardMetrics.ms_v_door_chargeport->SetValue(true);
StandardMetrics.ms_v_charge_pilot->SetValue(true);
SetChargeState(true);
StandardMetrics.ms_v_env_charging12v->SetValue(true);
StandardMetrics.ms_v_env_aux12v->SetValue(true);
ESP_LOGI(TAG, "Car charge session started");
t26_12v_wait_off = 0;
t26_12v_boost_cnt = 0;
PollSetState(VWEUP_CHARGING);
}
if (!isCharging && cd_count == 3) {
cd_count = 0;
StandardMetrics.ms_v_charge_pilot->SetValue(false);
SetChargeState(false);
if (StandardMetrics.ms_v_env_on->AsBool()) {
SetUsePhase(UP_Driving);
StdMetrics.ms_v_door_chargeport->SetValue(false);
PollSetState(VWEUP_ON);
} else {
PollSetState(VWEUP_AWAKE);
}
ESP_LOGI(TAG, "Car charge session ended");
}
}
else {
cd_count = 0;
}
if (cd_count == 0) {
lastCharging = isCharging;
}
break;
case 0x575: // Key position
switch (d[0]) {
case 0x00: // No key
vehicle_vweup_car_on(false);
StandardMetrics.ms_v_env_awake->SetValue(false);
break;
case 0x01: // Key in position 1, no ignition
StandardMetrics.ms_v_env_awake->SetValue(true);
vehicle_vweup_car_on(false);
break;
case 0x03: // Ignition is turned off
break;
case 0x05: // Ignition is turned on
break;
case 0x07: // Key in position 2, ignition on
vehicle_vweup_car_on(true);
break;
case 0x0F: // Key in position 3, start the engine
break;
}
break;
case 0x400: // Welcome to the ring from ILM
case 0x40C: // We know this one too. Climatronic.
case 0x436: // Working in the ring.
case 0x439: // Who are 436 and 439 and why do they differ on some cars?
if (d[0] == 0x00 && !ocu_awake && !StandardMetrics.ms_v_charge_inprogress->AsBool() && !t26_12v_boost && !t26_car_on && d[1] != 0x31 && t26_12v_wait_off == 0) {
// The car wakes up to charge the 12v battery
StandardMetrics.ms_v_env_charging12v->SetValue(true);
StandardMetrics.ms_v_env_aux12v->SetValue(true);
t26_ring_awake = true;
t26_12v_boost = true;
t26_12v_boost_cnt = 0;
PollSetState(VWEUP_AWAKE);
ESP_LOGI(TAG, "Car woke up. Will try to charge 12v battery");
}
if (d[1] == 0x31 && ocu_awake) {
// We should go to sleep, no matter what
ESP_LOGI(TAG, "Comfort CAN calls for sleep");
xTimerStop(m_sendOcuHeartbeat, 0);
xTimerDelete(m_sendOcuHeartbeat, 0);
m_sendOcuHeartbeat = NULL;
if (cc_count != 0) {
xTimerStop(m_ccCountdown, 0);
xTimerDelete(m_ccCountdown, 0);
m_ccCountdown = NULL;
}
ocu_awake = false;
ocu_working = false;
vweup_remote_climate_ticker = 0;
fas_counter_on = 0;
fas_counter_off = 0;
t26_12v_boost = false;
if (StandardMetrics.ms_v_charge_inprogress->AsBool()) {
PollSetState(VWEUP_CHARGING);
} else {
t26_ring_awake = false;
PollSetState(VWEUP_AWAKE);
}
break;
}
if (d[0] == 0x00 && d[1] != 0x31 && !t26_ring_awake) {
t26_ring_awake = true;
ESP_LOGI(TAG, "Ring awake");
if (t26_12v_wait_off != 0) {
ESP_LOGI(TAG, "ODB AWAKE ist still blocked");
}
}
if (d[1] == 0x31 && t26_ring_awake) {
t26_ring_awake = false;
ESP_LOGI(TAG, "Ring asleep");
}
if (d[0] == 0x1D) {
// We are called in the ring
unsigned char data[8];
uint8_t length;
length = 8;
canbus *comfBus;
comfBus = m_can3;
data[0] = 0x00; // As it seems that we are always the highest in the ring we always send to 0x400
if (ocu_working) {
data[1] = 0x01;
if (dev_mode) {
ESP_LOGI(TAG, "OCU working");
}
}
else {
data[1] = 0x11; // Ready to sleep. This is very important!
if (dev_mode) {
ESP_LOGI(TAG, "OCU ready to sleep");
}
}
data[2] = 0x02;
data[3] = d[3]; // Not understood
data[4] = 0x00;
if (ocu_what) {
// This is not understood and not implemented yet.
data[5] = 0x14;
}
else {
data[5] = 0x10;
}
data[6] = 0x00;
data[7] = 0x00;
vTaskDelay(50 / portTICK_PERIOD_MS);
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x43D, length, data); // We answer
}
ocu_awake = true;
break;
}
break;
default:
// This will log all unknown incoming frames
// ESP_LOGD(TAG, "IFC %03x 8 %02x %02x %02x %02x %02x %02x %02x %02x", p_frame->MsgID, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]);
break;
}
}
OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::RemoteCommandHandler(RemoteCommand command)
{
ESP_LOGI(TAG, "RemoteCommandHandler");
vweup_remote_command = command;
SendCommand(vweup_remote_command);
if (signal_ok) {
signal_ok = false;
return Success;
}
return Fail;
}
////////////////////////////////////////////////////////////////////////
// Send a RemoteCommand on the CAN bus.
//
// Does nothing if @command is out of range
//
void OvmsVehicleVWeUp::SendCommand(RemoteCommand command)
{
switch (command) {
case ENABLE_CLIMATE_CONTROL:
if (StandardMetrics.ms_v_env_on->AsBool()) {
ESP_LOGI(TAG, "Climate Control can't be enabled - car is on");
break;
}
if (fas_counter_off > 0 && fas_counter_off < 10) {
ESP_LOGI(TAG, "Climate Control can't be enabled - CC off is still running");
break;
}
if (vweup_remote_climate_ticker == 0) {
vweup_remote_climate_ticker = 1800;
}
else {
ESP_LOGI(TAG, "Enable Climate Control - already enabled");
break;
}
vweup_cc_turning_on = true;
CommandWakeup();
ESP_LOGI(TAG, "Enable Climate Control");
m_ccCountdown = xTimerCreate("VW e-Up CC Countdown", 1000 / portTICK_PERIOD_MS, pdTRUE, this, ccCountdown);
xTimerStart(m_ccCountdown, 0);
signal_ok = true;
break;
case DISABLE_CLIMATE_CONTROL:
if (!vweup_cc_on) {
ESP_LOGI(TAG, "Climate Control can't be disabled - CC is not enabled.");
break;
}
if (StandardMetrics.ms_v_env_on->AsBool()) {
ESP_LOGI(TAG, "Climate Control is already disabled - car is on");
break;
}
if ((fas_counter_on > 0 && fas_counter_on < 10) || ocu_wait) {
// Should we implement a "wait for turn off" timer here?
ESP_LOGI(TAG, "Climate Control can't be disabled - CC on is still running");
break;
}
if (vweup_remote_climate_ticker == 0) {
ESP_LOGI(TAG, "Disable Climate Control - already disabled");
break;
}
ESP_LOGI(TAG, "Disable Climate Control");
vweup_remote_climate_ticker = 0;
CCOff();
ocu_awake = true;
signal_ok = true;
break;
case AUTO_DISABLE_CLIMATE_CONTROL:
if (StandardMetrics.ms_v_env_on->AsBool()) {
// Should not be possible
ESP_LOGI(TAG, "Error: CC AUTO_DISABLE when car is on");
break;
}
vweup_remote_climate_ticker = 0;
ESP_LOGI(TAG, "Auto Disable Climate Control");
CCOff();
ocu_awake = true;
signal_ok = true;
break;
default:
return;
}
}
// Wakeup implentation over the VW ring commands
// We need to register in the ring with a call to our self from 43D with 0x1D in the first byte
//
OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::CommandWakeup()
{
if (!vweup_enable_write) {
return Fail;
}
if (!ocu_awake && !StandardMetrics.ms_v_env_on->AsBool()) {
unsigned char data[8];
uint8_t length;
length = 8;
unsigned char data2[2];
uint8_t length2;
length2 = 2;
canbus *comfBus;
comfBus = m_can3;
// This is a very dirty workaround to get the wakup of the ring working.
// We send 0x69E some values without knowing what they do.
// This throws an AsynchronousInterruptHandler error, but wakes 0x400
// So we wait two seconds and then call us in the ring
data[0] = 0x14;
data[1] = 0x42;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length2, data2);
}
vTaskDelay(2000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "Sent Wakeup Command - stage 1");
data[0] = 0x1D; // We call ourself to wake up the ring
data[1] = 0x02;
data[2] = 0x02;
data[3] = 0x00;
data[4] = 0x00;
data[5] = 0x14; // What does this do?
data[6] = 0x00;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x43D, length, data);
}
vTaskDelay(50 / portTICK_PERIOD_MS);
data[0] = 0x00; // We need to talk to 0x400 first to get accepted in the ring
data[1] = 0x01;
data[2] = 0x02;
data[3] = 0x04; // This could be a problem
data[4] = 0x00;
data[5] = 0x14; // What does this do?
data[6] = 0x00;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x43D, length, data);
}
ocu_working = true;
ocu_awake = true;
vTaskDelay(50 / portTICK_PERIOD_MS);
m_sendOcuHeartbeat = xTimerCreate("VW e-Up OCU heartbeat", 1000 / portTICK_PERIOD_MS, pdTRUE, this, sendOcuHeartbeat);
xTimerStart(m_sendOcuHeartbeat, 0);
ESP_LOGI(TAG, "Sent Wakeup Command - stage 2");
StandardMetrics.ms_v_env_charging12v->SetValue(true);
StandardMetrics.ms_v_env_aux12v->SetValue(true);
t26_12v_boost_cnt = 0;
t26_12v_wait_off = 0;
if (!StandardMetrics.ms_v_charge_inprogress->AsBool()) {
PollSetState(VWEUP_AWAKE);
} else {
PollSetState(VWEUP_CHARGING);
}
}
// This can be done better. Gives always success, even when already awake.
return Success;
}
void OvmsVehicleVWeUp::SendOcuHeartbeat()
{
if (!vweup_cc_on) {
fas_counter_on = 0;
if (fas_counter_off < 10 && !vweup_cc_turning_on) {
fas_counter_off++;
ocu_working = true;
if (dev_mode) {
ESP_LOGI(TAG, "OCU working");
}
}
else {
if (vweup_cc_turning_on) {
ocu_working = true;
if (dev_mode) {
ESP_LOGI(TAG, "OCU working");
}
}
else {
ocu_working = false;
if (dev_mode) {
ESP_LOGI(TAG, "OCU ready to sleep");
}
}
}
}
else {
fas_counter_off = 0;
ocu_working = true;
if (dev_mode) {
ESP_LOGI(TAG, "OCU working");
}
if (fas_counter_on < 10) {
fas_counter_on++;
}
}
unsigned char data[8];
uint8_t length;
length = 8;
canbus *comfBus;
comfBus = m_can3;
data[0] = 0x00;
data[1] = 0x00;
data[2] = 0x00;
data[3] = 0x00;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x5A9, length, data);
}
if ((fas_counter_on > 0 && fas_counter_on < 10) || (fas_counter_off > 0 && fas_counter_off < 10)) {
data[0] = 0x60;
if (dev_mode) {
ESP_LOGI(TAG, "OCU Heartbeat - 5A7 60");
}
}
else {
data[0] = 0x00;
if (dev_mode) {
ESP_LOGI(TAG, "OCU Heartbeat - 5A7 00");
}
}
data[1] = 0x16;
data[2] = 0x00;
data[3] = 0x00;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x5A7, length, data);
}
}
void OvmsVehicleVWeUp::CCCountdown()
{
cc_count++;
ocu_wait = true;
if (cc_count == 5) {
CCOnP(); // This is weird. We first need to send a Climate Contol Off before it will turn on ???
}
if (cc_count == 10) {
CCOn();
xTimerStop(m_ccCountdown, 0);
xTimerDelete(m_ccCountdown, 0);
m_ccCountdown = NULL;
ocu_wait = false;
ocu_awake = true;
cc_count = 0;
}
}
void OvmsVehicleVWeUp::CCOn()
{
unsigned char data[8];
uint8_t length;
length = 8;
unsigned char data_s[4];
uint8_t length_s;
length_s = 4;
canbus *comfBus;
comfBus = m_can3;
data[0] = 0x60;
data[1] = 0x16;
data[2] = 0x00;
data[3] = 0x00;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x5A7, length, data);
}
// d5 could be a counter?
data[0] = 0x80;
data[1] = 0x20;
data[2] = 0x29;
data[3] = 0x59;
data[4] = 0x21;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x01;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0xC0;
data[1] = 0x06;
data[2] = 0x00;
data[3] = 0x10;
data[4] = 0x1E;
data[5] = 0xFF;
data[6] = 0xFF;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0xC1;
data[1] = 0xFF;
data[2] = 0xFF;
data[3] = 0xFF;
data[4] = 0xFF;
data[5] = 0x01;
data[6] = 0x78; // This is the target temperature. T = 10 + d6/10
if (vweup_cc_temp_int == 15) {
data[6] = 0x32;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 15");
}
}
if (vweup_cc_temp_int == 16) {
data[6] = 0x3C;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 16");
}
}
if (vweup_cc_temp_int == 17) {
data[6] = 0x46;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 17");
}
}
if (vweup_cc_temp_int == 18) {
data[6] = 0x50;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 18");
}
}
if (vweup_cc_temp_int == 19) {
data[6] = 0x5A;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 19");
}
}
if (vweup_cc_temp_int == 20) {
data[6] = 0x64;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 20");
}
}
if (vweup_cc_temp_int == 21) {
data[6] = 0x6E;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 21");
}
}
if (vweup_cc_temp_int == 22) {
data[6] = 0x78;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 22");
}
}
if (vweup_cc_temp_int == 23) {
data[6] = 0x82;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 23");
}
}
if (vweup_cc_temp_int == 24) {
data[6] = 0x8C;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 24");
}
}
if (vweup_cc_temp_int == 25) {
data[6] = 0x96;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 25");
}
}
if (vweup_cc_temp_int == 26) {
data[6] = 0xA0;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 26");
}
}
if (vweup_cc_temp_int == 27) {
data[6] = 0xAA;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 27");
}
}
if (vweup_cc_temp_int == 28) {
data[6] = 0xB4;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 28");
}
}
if (vweup_cc_temp_int == 29) {
data[6] = 0xBE;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 29");
}
}
if (vweup_cc_temp_int == 30) {
data[6] = 0xC8;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 30");
}
}
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0xC2;
data[1] = 0x1E;
data[2] = 0x1E;
data[3] = 0x0A;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x08;
data[7] = 0x4F;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0xC3;
data[1] = 0x70;
data[2] = 0x74;
data[3] = 0x69;
data[4] = 0x6F;
data[5] = 0x6E;
data[6] = 0x65;
data[7] = 0x6E;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0x60;
data[1] = 0x16;
data[2] = 0x00;
data[3] = 0x00;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x5A7, length, data);
}
data_s[0] = 0x29;
data_s[1] = 0x58;
data_s[2] = 0x00;
data_s[3] = 0x01;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length_s, data_s);
}
ESP_LOGI(TAG, "Wrote second stage Climate Control On Messages to Comfort CAN.");
vweup_cc_on = true;
vweup_cc_turning_on = false;
StandardMetrics.ms_v_env_charging12v->SetValue(true);
StandardMetrics.ms_v_env_aux12v->SetValue(true);
}
void OvmsVehicleVWeUp::CCOnP()
{
unsigned char data[8];
uint8_t length;
length = 8;
unsigned char data_s[4];
uint8_t length_s;
length_s = 4;
canbus *comfBus;
comfBus = m_can3;
data[0] = 0x60;
data[1] = 0x16;
data[2] = 0x00;
data[3] = 0x00;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x5A7, length, data);
}
// d5 could be a counter
data[0] = 0x80;
data[1] = 0x20;
data[2] = 0x29;
data[3] = 0x59;
data[4] = 0x22;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x01;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0x60;
data[1] = 0x16;
data[2] = 0x00;
data[3] = 0x00;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x5A7, length, data);
}
data[0] = 0xC0;
data[1] = 0x06;
data[2] = 0x00;
data[3] = 0x10;
data[4] = 0x1E;
data[5] = 0xFF;
data[6] = 0xFF;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0xC1;
data[1] = 0xFF;
data[2] = 0xFF;
data[3] = 0xFF;
data[4] = 0xFF;
data[5] = 0x01;
data[6] = 0x78; // This is the target temperature. T = 10 + d6/10
if (vweup_cc_temp_int == 15) {
data[6] = 0x32;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 15");
}
}
if (vweup_cc_temp_int == 16) {
data[6] = 0x3C;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 16");
}
}
if (vweup_cc_temp_int == 17) {
data[6] = 0x46;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 17");
}
}
if (vweup_cc_temp_int == 18) {
data[6] = 0x50;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 18");
}
}
if (vweup_cc_temp_int == 19) {
data[6] = 0x5A;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 19");
}
}
if (vweup_cc_temp_int == 20) {
data[6] = 0x64;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 20");
}
}
if (vweup_cc_temp_int == 21) {
data[6] = 0x6E;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 21");
}
}
if (vweup_cc_temp_int == 22) {
data[6] = 0x78;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 22");
}
}
if (vweup_cc_temp_int == 23) {
data[6] = 0x82;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 23");
}
}
if (vweup_cc_temp_int == 24) {
data[6] = 0x8C;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 24");
}
}
if (vweup_cc_temp_int == 25) {
data[6] = 0x96;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 25");
}
}
if (vweup_cc_temp_int == 26) {
data[6] = 0xA0;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 26");
}
}
if (vweup_cc_temp_int == 27) {
data[6] = 0xAA;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 27");
}
}
if (vweup_cc_temp_int == 28) {
data[6] = 0xB4;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 28");
}
}
if (vweup_cc_temp_int == 29) {
data[6] = 0xBE;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 29");
}
}
if (vweup_cc_temp_int == 30) {
data[6] = 0xC8;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 30");
}
}
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0xC2;
data[1] = 0x1E;
data[2] = 0x1E;
data[3] = 0x0A;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x08;
data[7] = 0x4F;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0xC3;
data[1] = 0x70;
data[2] = 0x74;
data[3] = 0x69;
data[4] = 0x6F;
data[5] = 0x6E;
data[6] = 0x65;
data[7] = 0x6E;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data_s[0] = 0x29;
data_s[1] = 0x58;
data_s[2] = 0x00;
data_s[3] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length_s, data_s);
}
ESP_LOGI(TAG, "Wrote first stage Climate Control On Messages to Comfort CAN.");
}
void OvmsVehicleVWeUp::CCOff()
{
unsigned char data[8];
uint8_t length;
length = 8;
unsigned char data_s[4];
uint8_t length_s;
length_s = 4;
canbus *comfBus;
comfBus = m_can3;
data[0] = 0x60;
data[1] = 0x16;
data[2] = 0x00;
data[3] = 0x00;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x5A7, length, data);
}
// d5 could be a counter
data[0] = 0x80;
data[1] = 0x20;
data[2] = 0x29;
data[3] = 0x59;
data[4] = 0x22;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x01;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0x60;
data[1] = 0x16;
data[2] = 0x00;
data[3] = 0x00;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x5A7, length, data);
}
data[0] = 0xC0;
data[1] = 0x06;
data[2] = 0x00;
data[3] = 0x10;
data[4] = 0x1E;
data[5] = 0xFF;
data[6] = 0xFF;
data[7] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0xC1;
data[1] = 0xFF;
data[2] = 0xFF;
data[3] = 0xFF;
data[4] = 0xFF;
data[5] = 0x01;
data[6] = 0x78; // This is the target temperature. T = 10 + d6/10
if (vweup_cc_temp_int == 19) {
data[6] = 0x5A;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 19");
}
}
if (vweup_cc_temp_int == 20) {
data[6] = 0x64;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 20");
}
}
if (vweup_cc_temp_int == 21) {
data[6] = 0x6E;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 21");
}
}
if (vweup_cc_temp_int == 22) {
data[6] = 0x78;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 22");
}
}
if (vweup_cc_temp_int == 23) {
data[6] = 0x82;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 23");
}
}
if (vweup_cc_temp_int == 24) {
data[6] = 0x8C;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 24");
}
}
if (vweup_cc_temp_int == 25) {
data[6] = 0x96;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 25");
}
}
if (vweup_cc_temp_int == 26) {
data[6] = 0xA0;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 26");
}
}
if (vweup_cc_temp_int == 27) {
data[6] = 0xAA;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 27");
}
}
if (vweup_cc_temp_int == 28) {
data[6] = 0xB4;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 28");
}
}
if (vweup_cc_temp_int == 29) {
data[6] = 0xBE;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 29");
}
}
if (vweup_cc_temp_int == 30) {
data[6] = 0xC8;
if (dev_mode) {
ESP_LOGI(TAG, "Cabin temperature set: 30");
}
}
data[0] = 0xC2;
data[1] = 0x1E;
data[2] = 0x1E;
data[3] = 0x0A;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x08;
data[7] = 0x4F;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data[0] = 0xC3;
data[1] = 0x70;
data[2] = 0x74;
data[3] = 0x69;
data[4] = 0x6F;
data[5] = 0x6E;
data[6] = 0x65;
data[7] = 0x6E;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length, data);
}
data_s[0] = 0x29;
data_s[1] = 0x58;
data_s[2] = 0x00;
data_s[3] = 0x00;
if (vweup_enable_write && !dev_mode) {
comfBus->WriteStandard(0x69E, length_s, data_s);
}
ESP_LOGI(TAG, "Wrote Climate Control Off Message to Comfort CAN.");
vweup_cc_on = false;
}
OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::CommandLock(const char *pin)
{
ESP_LOGI(TAG, "CommandLock");
return NotImplemented;
}
OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::CommandUnlock(const char *pin)
{
ESP_LOGI(TAG, "CommandUnlock");
return NotImplemented;
}
OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::CommandActivateValet(const char *pin)
{
ESP_LOGI(TAG, "CommandActivateValet");
return NotImplemented;
}
OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::CommandDeactivateValet(const char *pin)
{
ESP_LOGI(TAG, "CommandLDeactivateValet");
return NotImplemented;
}
OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::CommandStartCharge()
{
ESP_LOGI(TAG, "CommandStartCharge");
return NotImplemented;
}
OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::CommandStopCharge()
{
ESP_LOGI(TAG, "CommandStopCharge");
return NotImplemented;
}
OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::CommandHomelink(int button, int durationms)
{
// This is needed to enable climate control via Homelink for the iOS app
ESP_LOGI(TAG, "CommandHomelink");
if (button == 0 && vweup_enable_write) {
return RemoteCommandHandler(ENABLE_CLIMATE_CONTROL);
}
if (button == 1 && vweup_enable_write) {
return RemoteCommandHandler(DISABLE_CLIMATE_CONTROL);
}
return NotImplemented;
}
OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::CommandClimateControl(bool climatecontrolon)
{
ESP_LOGI(TAG, "CommandClimateControl");
if (vweup_enable_write) {
return RemoteCommandHandler(climatecontrolon ? ENABLE_CLIMATE_CONTROL : DISABLE_CLIMATE_CONTROL);
}
else {
return NotImplemented;
}
}