Module 4: The Engage/Override State Machine

Goal. Match a real sequence of selfdriveState.state changes, captured from the demo route, to the exact lines of code that produced each one — and learn to tell this state machine apart from a similarly-named one sunnypilot adds alongside it, which this module's capture is not showing.

Setup. .venv active, tools/replay/replay available.

Predict

selfdriveState.state can be disabled, preEnabled, enabled, softDisabling, or overriding. Before reading any code: is it possible to go straight from disabled to overriding, with no enabled in between, on the very cycle the driver engages? Write down yes/no and, if yes, what you think has to be true at that exact instant for it to happen.

Run

Watch selfdriveState.state and print every change, alongside the carState/carControl context at that instant:

sm = messaging.SubMaster(['selfdriveState', 'carState', 'carControl'])

last_state = None
while transitions < 8:
    sm.update(100)
    if not sm.valid['selfdriveState']:
        continue
    state = str(sm['selfdriveState'].state)
    if state != last_state:
        transitions += 1
        print(f"[{transitions}] state -> {state} (...)")
        last_state = state

(essence of snippets/dump_state_transitions.py; full script on the Source page), run against ./tools/replay/replay --demo --no-vipc -c 5 -x 3 --no-loop.

Observe

[1] state -> disabled  (gasPressed=False, vEgo=0.00, latActive=False, longActive=False, alertType='')
[2] state -> overriding  (gasPressed=True, vEgo=14.47, latActive=False, longActive=False, alertType='pcmEnable/enable')
[3] state -> enabled  (gasPressed=False, vEgo=15.64, latActive=True, longActive=False, alertType='gasPressedOverride/overrideLongitudinal')
[4] state -> overriding  (gasPressed=False, vEgo=16.16, latActive=True, longActive=True, alertType='steerOverride/overrideLateral')
[5] state -> enabled  (gasPressed=False, vEgo=16.17, latActive=True, longActive=True, alertType='steerOverride/overrideLateral')
[6] state -> overriding  (gasPressed=False, vEgo=16.53, latActive=True, longActive=True, alertType='steerOverride/overrideLateral')
[7] state -> enabled  (gasPressed=False, vEgo=16.55, latActive=True, longActive=True, alertType='steerOverride/overrideLateral')
[8] state -> overriding  (gasPressed=False, vEgo=22.90, latActive=True, longActive=True, alertType='preLaneChangeLeft/warning')

(full capture: Log page)

Transition [2] answers Predict directly: the system went straight from disabled to overriding, never passing through plain enabled — and it happened with gasPressed=True at that exact instant.

Explain

The DISABLED branch of the state machine that publishes this field is what decides transition [2]:

    # DISABLED
    elif self.state == State.disabled:
      if events.contains(ET.ENABLE):
        if events.contains(ET.NO_ENTRY):
          self.current_alert_types.append(ET.NO_ENTRY)

        else:
          if events.contains(ET.PRE_ENABLE):
            self.state = State.preEnabled
          elif events.contains(ET.OVERRIDE_LATERAL) or events.contains(ET.OVERRIDE_LONGITUDINAL):
            self.state = State.overriding
          else:
            self.state = State.enabled
          self.current_alert_types.append(ET.ENABLE)

(selfdrive/selfdrived/state.py lines 77-90.) Engaging (ET.ENABLE, from the cruise-main button, the alert name pcmEnable in your capture) doesn't unconditionally land on enabled: if an override condition is also true on that same cycle, the machine routes straight to overriding instead. gasPressed=True at the moment of engagement is exactly such a condition — EventName.gasPressedOverride maps to ET.OVERRIDE_LONGITUDINAL (selfdrive/selfdrived/events.py line 550-551) — which is why transition [2] skipped enabled entirely, and why the driver releasing the gas one cycle later (transition [3], gasPressed now False) is what finally let it settle into plain enabled, via the overriding state's own exit condition:

        # OVERRIDING
        elif self.state == State.overriding:
          if events.contains(ET.SOFT_DISABLE):
            self.state = State.softDisabling
            self.soft_disable_timer = int(SOFT_DISABLE_TIME / DT_CTRL)
            self.current_alert_types.append(ET.SOFT_DISABLE)
          elif not (events.contains(ET.OVERRIDE_LATERAL) or events.contains(ET.OVERRIDE_LONGITUDINAL)):
            self.state = State.enabled
          else:
            self.current_alert_types += [ET.OVERRIDE_LATERAL, ET.OVERRIDE_LONGITUDINAL]

(selfdrive/selfdrived/state.py lines 66-75.) Its second branch is the exit condition: leave overriding for enabled exactly when neither override event is present anymore. Transitions [4] through [7] are this same exit/re-entry rule firing twice more, this time driven by ET.OVERRIDE_LATERAL (the steerOverride alert — the driver nudging the wheel) rather than the gas.

Aside: a second, similarly-named state machine that this capture is not showing

selfdriveState.state, which everything above traces, is published by self.state_machine.state in selfdrive/selfdrived/selfdrived.py, an instance of openpilot.selfdrive.selfdrived.state.StateMachine — code that predates sunnypilot and belongs to openpilot itself. A few lines later in the same function, sunnypilot's own addition publishes a second, separately-named field:

    ss_sp = ss_sp_msg.selfdriveStateSP
    mads = ss_sp.mads
    mads.state = self.mads.state_machine.state

(selfdrive/selfdrived/selfdrived.py lines 584-586.) self.mads.state_machine is sunnypilot.mads.state.StateMachine, a different class, defined in a different file, whose states are disabled, paused, enabled, softDisabling, overriding — four of five names identical to the ones you just traced, but published under selfdriveStateSP.mads.state, not selfdriveState.state. The one genuinely new value, paused, is the sunnypilot-specific concept this second machine exists for: pausing steering assistance on its own, independent of whether cruise (longitudinal) stays engaged — something the base engage/disengage machine you traced above has no equivalent of. Searching this codebase for "the state machine" without checking which file you landed in is the easy way to end up explaining the wrong one; this workshop's own first draft of this module did exactly that before the mistake was caught against this capture.

Diagram: states actually visited

Engage/override states, this capture's path highlighted

Quiz

  1. Transition [8] shows overriding with alertType='preLaneChangeLeft/warning', but neither gasPressed nor an obvious steering nudge is named in the captured context. Given the DISABLED/OVERRIDING branches you just read, what event type must have been true at that instant, and what does that suggest about how an in-progress automatic lane change interacts with this state machine?
  2. ACTIVE_STATES = (State.enabled, State.softDisabling, State.overriding) and ENABLED_STATES = (State.preEnabled, *ACTIVE_STATES). Looking at transition [2]'s context (latActive=False, longActive=False) versus transition [4]'s (latActive=True, longActive=True) — both are overriding — what does that tell you about whether overriding alone determines whether the actuators are active?
  3. If you needed to know whether sunnypilot's lane-centering-only pause feature was currently active, which cereal field would you subscribe to: selfdriveState.state or selfdriveStateSP.mads.state? Why?

→ Answer Key — Module 4

Answer Key — Module 4
  1. ET.OVERRIDE_LATERAL must have been true (it's the only remaining path into overriding from enabled besides ET.OVERRIDE_LONGITUDINAL, which gasPressed=False rules out here). That suggests an in-progress automatic lane change is modeled, at least partly, through the same lateral-override channel a manual steering nudge uses — the state machine doesn't appear to distinguish "driver grabbed the wheel" from "a lane change is asserting extra lateral authority" at this level; both raise the same event type.
  2. No — overriding alone doesn't determine actuator activity; both latActive and longActive come from carControl, computed separately from selfdriveState.active (overriding is one of the ACTIVE_STATES, so active=True in both cases) combined with whichever of lateral/longitudinal control is actually enabled for this car and mode at that moment. disabled -> overriding at engagement (transition [2]) can validly have both false for a moment while the rest of the system is still catching up to the new state.
  3. selfdriveStateSP.mads.state. selfdriveState.state is the base engage/disengage machine and has no paused value at all; only sunnypilot.mads.state.StateMachine, published under selfdriveStateSP.mads.state, models the steering-only pause sunnypilot adds.

↑ Back to Quiz 4

→ Module 5: The Safety Boundary