Skip to content

🧪 mazda: relay stock LKAS and lane alerts - #2

Closed
mzdnick wants to merge 12 commits into
zoompilot:mazda-devfrom
mzdnick:mazda-stock-lkas-relay
Closed

🧪 mazda: relay stock LKAS and lane alerts#2
mzdnick wants to merge 12 commits into
zoompilot:mazda-devfrom
mzdnick:mazda-stock-lkas-relay

Conversation

@mzdnick

@mzdnick mzdnick commented Aug 22, 2026

Copy link
Copy Markdown

Problem

With openpilot onroad but disengaged, the car's stock LKAS correction and lane-departure display are dead. The camera still transmits CAM_LKAS (0x243, ~16 Hz) and CAM_LANEINFO (0x440, ~2 Hz), but those frames never reach the car: both addresses sit in the Mazda TX allowlist with check_relay = true, and with no forward hook registered, the panda's static rule — never forward what we transmit — drops them unconditionally. The EPS sees one sender: openpilot, sending zero torque at 100 Hz.

Fix

Bus ownership now follows engagement.

  • New mazda_fwd_hook forwards the camera's 0x243 and 0x440 to the dash while openpilot is not controlling; the tx hook drops our own frames meanwhile. One sender per state; handoffs only at engage and disengage edges. "Controlling" is controls_allowed || controls_allowed_lateral (MADS-aware: lateral-only engagement takes the bus).
  • The TX entries keep check_relay (harness fault detection unchanged) but opt out of static blocking; the dynamic hook decides.
  • While engaged, our messages relay the camera's content instead of replacing it: create_steering_control preserves the camera bits of 0x243, and create_alert_command re-sends the whole decoded CAM_LANEINFO frame, overriding only the hands warnings. openpilot no longer fabricates ldw alerts; the dash stays camera-driven in both states, and the car's own lane-departure setting governs. (An earlier curated signal list flashed the wrong side on device — the dash reads more than the LDW_WARN_LL/RL bits.)
  • The shared torque tests' "zero-torque commands transmit while disengaged" contract no longer holds for Mazda, so the base parametrizes it as DISENGAGED_IDLE_STEER_TX (default True, same pattern as NO_STEER_REQ_BIT); the Mazda class sets False.

Validation

  • test_stock_relay drives set_controls_allowed / set_controls_allowed_lateral through all four engagement combinations, asserting forward and TX for both addresses. The lateral-only row proves the lateral flag alone grants ownership.
  • Controller and CarState tests cover the engaged relays: TestSteeringCommand, TestAlertCommand, TestRelayEmission, TestCamRelaySources.

Open items

  • The 0x440 DBC defines only 24 of 64 bits; undefined bits go out as packer defaults. Fallback if the dash depends on one: raw-byte relay.
  • The safety header change requires a panda firmware rebuild and reflash.

AI Usage

Disclaimer: GLM-5.3 by Z.ai was used to help develop, debug, and document this submission. All changes were reviewed and validated by a human.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributing to opendbc! In order for us to review your PR as quickly as possible, check the following:

  • Convert your PR to a draft unless it's ready to review
  • Read the contributing docs
  • Before marking as "ready for review", ensure:
    • the goal is clearly stated in the description
    • all the tests are passing
    • include a route or your device' dongle ID if relevant

@mzdnick mzdnick changed the title mazda: pass the stock camera's LKAS and lane alerts through to the dash mazda: relay stock LKAS and lane alerts Aug 22, 2026
@mzdnick
mzdnick marked this pull request as draft August 22, 2026 08:28
@mzdnick
mzdnick force-pushed the mazda-stock-lkas-relay branch from 51abe8a to 1db28d0 Compare August 22, 2026 09:04
@mzdnick mzdnick changed the title mazda: relay stock LKAS and lane alerts 🧪 mazda: relay stock LKAS and lane alerts Aug 22, 2026
The rebuilt CAM_LANEINFO decoded and re-encoded the camera's frame
through a DBC that describes 24 of its 64 bits, so 41 bits -- all of
byte 2 among them -- went out as zeros whenever our frame was on the
bus. The dash reads those bits, and on-device testing showed an
intermittent front-camera fault and a missing engaged departure display.
Relay the camera's exact bytes instead, on each new camera frame, with
only the three hands-warn bits masked -- and only while steering.

CAM_LKAS now carries the camera's LDW and LINE_NOT_VISIBLE bits so an
engaged departure still reaches the dash, and its counter continues the
camera's sequence at each engage edge instead of jumping by an arbitrary
phase.
It counts controller frames since the last camera frame, not misses.
…arning

Device testing of the byte-exact 0x440 relay still showed the v1
wrong-side signature engaged (right departure -> left flash, left ->
nothing), so the dash reads departure-side bits from 0x243 as well. The
engaged 0x243 is now an overlay on the camera's exact frame: openpilot
writes only the torque field, the counter and the zero-angle pattern,
adjusting the checksum by exactly the fields touched (a delta off the
camera's own checksum, so bits outside the formula's model keep the
camera's own contributions). Every other bit, defined or not, is the
camera's.

The dash hands warning is the camera's in both states now: relaying
openpilot's steerRequired events painted an orange steering wheel for
every wheel-touch and distraction alert.
The steer_required parameter on the HUD relay was dead once the camera
owned the hands warning; the masks, their tests and the stale comments
went with it. cam_laneinfo was a dead write -- the FSC settle gate reads
the parser directly -- and the overlay now derives the counter and
torque for its checksum delta from the curated bytes instead of taking
them as parameters. The FRAME_RAW container signals gain CM_ comments,
since no other DBC uses that construct.
v4 on-device: engaged lane-departure warnings are correct on both sides,
but openpilot could only steer while the camera saw lanes. The EPS gates
torque on the camera's line-visibility state in 0x243, and the overlay
relayed it -- the curated build had always forced it off ("they all work
just fine if set to zero"). The overlay now clears the bit and the
checksum delta pays for it; LDW and the undocumented side bits still
ride through, so the dash alerts stay correct. The curated fallback
returns to upstream's zeros for both bits.
v5 on-device: steering and lane-departure warnings are correct in both
states. Two gaps left. The camera's hands warning, relayed byte-exact
while engaged, tracks "LAS applying torque" rather than the driver, so
the orange wheel was on nearly whenever lane lines were drawn. And with
the steerRequired mapping deleted, openpilot alerts -- the turn-limit
warning above all -- lost their dash channel, which the stock setup had
always given them.

While openpilot steers, the three hands-warn bits now carry openpilot's
hold-the-wheel alert: cleared when quiet, set when steerRequired is up
(the pre-branch behavior). While not steering, the camera's own warning
passes through untouched.
The LAS visuals were a side effect of the passthrough: while engaged,
the camera's lane lines drew on the dash and HUD alongside openpilot's
own screen. Per the keep-the-alerts tradeoff, the relay now blanks
LANE_LINES (LKAS disabled) whenever openpilot steers without an alert
of its own, and relays the frame byte-exact while a hold-the-wheel
alert is up -- the turn-limit warning keeps the rendering path the car
already knows, lines and wheel together. Not steering, the camera's
frame passes through untouched, flags-up windows included.
The 2 Hz hold grid keyed off the controller frame counter, whose phase
has no relation to when the camera went quiet. The first hold could
land up to 0.5 s past the 1 s stale window, so the dash briefly lost
its lane display on a camera dropout. Key the grid to
laneinfo_age_frames instead: the first hold fires 1 s after the
camera's last frame, then every 0.5 s.
Comment audit: 26 lines to 9. Each invariant is now stated once, at
the place a reader needs it. The EPS-gates-torque fact appears once
(at the write masks) instead of three times; the LAS-vs-driver story
lives at the call site; fix history and caller behavior move out of
mazdacan. No code change.
The orange steering wheel is not a hands-on-wheel icon: in stock it
lights while the LAS corrects back to the lane, the EPS applying
torque. The DBC's HANDS_* signal names misled the relay's naming and
comments. The parameter becomes steer_indicator, the masks
STEER_IND_B6/B7, the comments state the observed meaning, and the DBC
signals carry CM_ notes recording it. Also picks up the stale-hold
watchdog switching to the age counter and trimmed comments.
@mzdnick mzdnick closed this Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant