Skip to content
Merged
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
1 change: 1 addition & 0 deletions esp32/.firmware/can_signals.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#define SIG_AP_UI_FSD_SELECTED_MASK 0x01u

#define SIG_AP_FSD_ENABLE_BIT 46
#define SIG_AP_CONTINUE_ON_GREEN_BIT 39
#define SIG_AP_NAG_CLEAR_BIT 19
#define SIG_AP_HW4_FSD_ENABLE_BIT 60
#define SIG_AP_HW4_EMERGENCY_VEHICLE_BIT 59
Expand Down
8 changes: 8 additions & 0 deletions esp32/.firmware/fsd_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ void fsd_state_init(FSDState *state, TeslaHWVersion hw) {
state->ignore_ota = false;
state->emergency_vehicle_detect = false;
state->summon_unlock = false; // opt-in Summon EU Unlock, default OFF
state->continue_on_green = false; // opt-in Continue on Green, default OFF
state->fsd_unlock = false;
state->force_fsd = false;
state->china_mode = false;
Expand Down Expand Up @@ -203,6 +204,13 @@ bool fsd_handle_autopilot_frame(FSDState *state, CanFrame *frame) {
// mux 0 is the authoritative "is FSD requested" mux
if (mux == CAN_MUX_0) state->fsd_enabled = fsd_ui;

// bit39 continue-on-green with lead car (ev-open-can-tools TSLLC plugin);
// same 0x3FD mux0 frame on HW3/HW4, so apply before the HW split; pairs with TLSSC
if (mux == CAN_MUX_0 && state->continue_on_green && state->fsd_enabled) {
set_bit(frame, SIG_AP_CONTINUE_ON_GREEN_BIT, true); // bit39 continue-on-green
modified = true;
}

if (state->hw_version == TeslaHW_HW3) {
// ── HW3 ──────────────────────────────────────────────────────────────
if (mux == CAN_MUX_0 && state->fsd_unlock && state->fsd_enabled) {
Expand Down
8 changes: 6 additions & 2 deletions esp32/.firmware/prefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ void prefs_load(FSDState *state) {
state->precondition = g_prefs.getBool("precond",false);
state->emergency_vehicle_detect = g_prefs.getBool("emrg", false);
state->summon_unlock = g_prefs.getBool("summon", false);
state->continue_on_green = g_prefs.getBool("cog", false);
state->bms_output = g_prefs.getBool("bms", false);
state->firmware_14x_warning = g_prefs.getBool("14x", true);
state->blackbox_enabled = g_prefs.getBool("bbx", BLACKBOX_DEFAULT_ENABLED);
Expand Down Expand Up @@ -63,9 +64,10 @@ void prefs_load(FSDState *state) {
state->cfg_steer_hi = g_prefs.getUChar("cshi", 1);
state->cfg_steer_lo = g_prefs.getUChar("cslo", 0);

Serial.printf("[NVS] Loaded: FSDUnlock=%d NAG=%d ContinuousAP=%d IgnoreOTA=%d China=%d Chime=%d Summon=%d Sleep=%u AP=\"%s\" STA=\"%s\" HIDDEN=%d\n",
Serial.printf("[NVS] Loaded: FSDUnlock=%d NAG=%d ContinuousAP=%d IgnoreOTA=%d China=%d Chime=%d Summon=%d COG=%d Sleep=%u AP=\"%s\" STA=\"%s\" HIDDEN=%d\n",
state->fsd_unlock, state->nag_killer, state->continuous_ap, state->ignore_ota,
state->china_mode, state->suppress_speed_chime, state->summon_unlock,
state->continue_on_green,
state->sleep_idle_ms, state->wifi_ssid, state->wifi_sta_ssid,
state->wifi_hidden);
g_prefs.end();
Expand Down Expand Up @@ -99,6 +101,7 @@ void prefs_save(const FSDState *state) {
g_prefs.putBool("precond",state->precondition);
g_prefs.putBool("emrg", state->emergency_vehicle_detect);
g_prefs.putBool("summon", state->summon_unlock);
g_prefs.putBool("cog", state->continue_on_green);
g_prefs.putBool("bms", state->bms_output);
g_prefs.putBool("14x", state->firmware_14x_warning);
g_prefs.putBool("bbx", state->blackbox_enabled);
Expand Down Expand Up @@ -131,9 +134,10 @@ void prefs_save(const FSDState *state) {
g_prefs.putUChar("cshi", state->cfg_steer_hi);
g_prefs.putUChar("cslo", state->cfg_steer_lo);

Serial.printf("[NVS] Saved: FSDUnlock=%d NAG=%d ContinuousAP=%d IgnoreOTA=%d China=%d Chime=%d Summon=%d Sleep=%u AP=\"%s\" STA=\"%s\" HIDDEN=%d\n",
Serial.printf("[NVS] Saved: FSDUnlock=%d NAG=%d ContinuousAP=%d IgnoreOTA=%d China=%d Chime=%d Summon=%d COG=%d Sleep=%u AP=\"%s\" STA=\"%s\" HIDDEN=%d\n",
state->fsd_unlock, state->nag_killer, state->continuous_ap, state->ignore_ota,
state->china_mode, state->suppress_speed_chime, state->summon_unlock,
state->continue_on_green,
state->sleep_idle_ms, state->wifi_ssid, state->wifi_sta_ssid,
state->wifi_hidden);
g_prefs.end();
Expand Down
18 changes: 18 additions & 0 deletions esp32/.firmware/web_dashboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,10 @@ input:checked+.sl2:before{transform:translateX(20px);background:#fff}
<span class="lbl">Summon EU Unlock</span>
<label class="sw"><input type="checkbox" id="swSummon" onchange="cmd('summon_unlock',this.checked)"><span class="sl2"></span></label>
</div>
<div class="row">
<span class="lbl">Continue on Green<br><small style="color:var(--muted)">pairs with TLSSC</small></span>
<label class="sw"><input type="checkbox" id="swCog" onchange="cmd('continue_on_green',this.checked)"><span class="sl2"></span></label>
</div>
<div class="row" style="display:block">
<div id="pmSuggest" style="display:none;margin:0 0 8px;padding:8px 10px;border:1px solid var(--accent);border-radius:6px;background:var(--card2)">
<div style="font-size:12px;color:var(--text)">Looks like variant <b id="pmName">?</b> &mdash; the standard parser can't read AP-state on this bus.</div>
Expand Down Expand Up @@ -961,6 +965,7 @@ function upd(d){
if(document.getElementById('rowChime')) document.getElementById('rowChime').style.display=d.isa_speed_enabled?'flex':'none';
if(document.getElementById('swTlssc')) document.getElementById('swTlssc').checked=d.tlssc_restore;
if(document.getElementById('swSummon')) document.getElementById('swSummon').checked=d.summon_unlock;
if(document.getElementById('swCog')) document.getElementById('swCog').checked=d.continue_on_green;
if(document.getElementById('swDisp')) document.getElementById('swDisp').checked=!!d.display_enabled;
if(document.activeElement.id!=='dispBr' && document.getElementById('dispBr'))
document.getElementById('dispBr').value=d.display_brightness||50;
Expand Down Expand Up @@ -1547,6 +1552,7 @@ static String build_json() {
j += "\"suppress_speed_chime\":"; j += state.suppress_speed_chime ? "true" : "false"; j += ',';
j += "\"tlssc_restore\":"; j += state.tlssc_restore ? "true" : "false"; j += ',';
j += "\"summon_unlock\":"; j += state.summon_unlock ? "true" : "false"; j += ',';
j += "\"continue_on_green\":"; j += state.continue_on_green ? "true" : "false"; j += ',';
j += "\"firmware_14x_warning\":"; j += state.firmware_14x_warning ? "true" : "false"; j += ',';
#if defined(BOARD_TTGO_DISPLAY)
j += "\"display_enabled\":"; j += state.display_enabled ? "true" : "false"; j += ',';
Expand Down Expand Up @@ -1991,6 +1997,18 @@ static void ws_event(uint8_t num, WStype_t type,
Serial.printf("[Web] Summon EU Unlock: %s\n", enabled ? "ON" : "OFF");
prefs_save(&saved);
}
} else if (strstr(buf, "\"continue_on_green\"")) {
if (vptr) {
while (*vptr == ' ' || *vptr == ':') vptr++;
bool enabled = (strncmp(vptr, "true", 4) == 0);
FSDState saved;
state_enter();
g_state->continue_on_green = enabled;
saved = *g_state;
state_exit();
Serial.printf("[Web] Continue on Green: %s\n", enabled ? "ON" : "OFF");
prefs_save(&saved);
}
} else if (strstr(buf, "\"dump\"")) {
if (vptr) {
while (*vptr == ' ' || *vptr == ':') vptr++;
Expand Down
7 changes: 7 additions & 0 deletions fsd_logic/fsd_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ void fsd_state_init(FSDState* state, TeslaHWVersion hw) {
state->das_prev_hands_on_state = 0xFF; // escalation-edge baseline (#100)
state->enhanced_autopilot = false;
state->summon_unlock = false; // opt-in Summon EU Unlock, default OFF
state->continue_on_green = false; // opt-in Continue on Green, default OFF
state->speed_profile_locked = false;
state->hw4_offset = 0;
}
Expand Down Expand Up @@ -215,6 +216,12 @@ bool fsd_handle_autopilot_frame(FSDState* state, CANFRAME* frame, uint32_t now_m
modified = true;
}

// bit39 continue-on-green with lead car (ev-open-can-tools TSLLC plugin); pairs with TLSSC/bit38
if(mux == 0 && state->continue_on_green && state->fsd_enabled) {
fsd_set_bit(frame, 39, true);
modified = true;
}

if(state->hw_version == TeslaHW_HW3) {
if(mux == 0 && state->fsd_enabled) {
int raw = (int)((frame->buffer[3] >> 1) & 0x3F) - 30;
Expand Down
1 change: 1 addition & 0 deletions fsd_logic/fsd_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ typedef struct FSDState {
// --- 0x3FD mux1 extras ---
bool assist_show_lane_graph; // bit45: lane visualization on non-FSD tier
bool assist_tlssc_bit38; // bit38 on mux0: explicit TLSSC enable (complementary to 0x331)
bool continue_on_green; // bit39 on mux0 (UI_fsdContinueOnGreenWithCIPV): continue through a green light behind a lead car without stalk confirm; opt-in, default OFF, pairs with TLSSC/bit38

// --- telemetry disable (0x3F8 bit43) ---
bool assist_telemetry_off; // force UI_enableTripTelemetry=0
Expand Down
56 changes: 56 additions & 0 deletions test/test_fsd_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,61 @@ static void test_summon_unlock(void) {
CHECK((f.buffer[5] & 0x80) != 0, "HW4 mux1 summon-off bit47 still set (prior behavior)");
}

// ── Continue on Green (0x3FD mux0 bit39) — opt-in, HW3 + HW4 ──────────────────
// ev-open-can-tools TSLLC plugin sets UI_fsdContinueOnGreenWithCIPV (bit39) on
// mux0. It is gated by continue_on_green + fsd_enabled and applies before the
// HW3/HW4 split, so bit39 must appear for both hardware versions when ON, and
// never when the toggle is OFF. bit39 -> byte 4 bit 7 -> mask 0x80.
static void test_continue_on_green(void) {
CANFRAME f;

// ── HW3 ──
FSDState h3;
memset(&h3, 0, sizeof(h3));
h3.hw_version = TeslaHW_HW3;
h3.force_fsd = true; // makes mux0 select FSD -> fsd_enabled = true

// ON: bit39 set on mux0.
h3.continue_on_green = true;
zero(&f);
f.data_lenght = 8;
f.buffer[0] = 0; // mux0
CHECK(fsd_handle_autopilot_frame(&h3, &f, 0), "HW3 mux0 cog-on reports modified");
CHECK(h3.fsd_enabled, "HW3 mux0 sets fsd_enabled");
CHECK((f.buffer[4] & 0x80) != 0, "HW3 mux0 cog-on bit39 set");

// OFF: bit39 NOT set.
h3.continue_on_green = false;
zero(&f);
f.data_lenght = 8;
f.buffer[0] = 0;
fsd_handle_autopilot_frame(&h3, &f, 0);
CHECK((f.buffer[4] & 0x80) == 0, "HW3 mux0 cog-off bit39 NOT set");

// ── HW4 ──
FSDState h4;
memset(&h4, 0, sizeof(h4));
h4.hw_version = TeslaHW_HW4;
h4.force_fsd = true;

// ON: bit39 set on mux0.
h4.continue_on_green = true;
zero(&f);
f.data_lenght = 8;
f.buffer[0] = 0;
CHECK(fsd_handle_autopilot_frame(&h4, &f, 0), "HW4 mux0 cog-on reports modified");
CHECK(h4.fsd_enabled, "HW4 mux0 sets fsd_enabled");
CHECK((f.buffer[4] & 0x80) != 0, "HW4 mux0 cog-on bit39 set");

// OFF: bit39 NOT set.
h4.continue_on_green = false;
zero(&f);
f.data_lenght = 8;
f.buffer[0] = 0;
fsd_handle_autopilot_frame(&h4, &f, 0);
CHECK((f.buffer[4] & 0x80) == 0, "HW4 mux0 cog-off bit39 NOT set");
}

// ── 0x399 ISA speed chime: bit + Tesla additive checksum ──────────────────────
static void test_isa_checksum(void) {
CANFRAME f;
Expand Down Expand Up @@ -1944,6 +1999,7 @@ int main(void) {
test_autopilot_hw4();
test_autopilot_hw3();
test_summon_unlock();
test_continue_on_green();
test_isa_checksum();
test_di_speed();
test_tlssc_restore();
Expand Down
Loading