Skip to content

Conversation

@projectgus
Copy link
Contributor

@projectgus projectgus commented Dec 15, 2025

Summary

This work follows on from #13149 and closes #12337. The goal is to have a machine.CAN API that can be used on multiple ports, and an implementation that works on stm32 port. The existing pyb.CAN API is being kept alongside this.

The machine.CAN API has changed substantially from the draft submitted in #13149. See comment with more explanation about what happened.

Status

I'm afraid this PR is still not complete, although TX and RX functionality work and the supplied new tests all pass. Remaining items:

  • Decision about scoped Enums (see comment).
  • Implement CAN.recv() error flags and add test coverage.
  • Implement controller errors: CAN.get_state(), CAN.State enum, CAN.IRQ_STATE interrupt trigger.
  • Implement CAN.get_counters()
  • Implement CAN.get_timings()
  • Implement CAN.reset() and CAN.restart(), and/or simplify the API to remove one of these.
  • Implement CAN.mode() and add test coverage.
  • Verify pyb.CAN still works, using multi_pyb_can tests.
  • Look at possible code size reductions

Future PRs

  • Create can & aiocan modules in micropython-lib. The goal of machine.CAN is to only implement the bare bones needed to make a higher level "Pythonic" CAN library on top.
  • Implement CAN-FD support (the current PR works on STM32 CAN-FD controller peripherals, but only in Classic CAN mode).

Testing

  • The added tests pass when run between a NUCLEO_H723ZG board (fdcan peripheral) and a PYBDV11 (bxCAN peripheral).
  • Still need to test using an STM32G4 (simplified version of the H7 fdcan peripheral, has subtly different HAL API).
  • As noted above, haven't tested for regressions on pyb.CAN yet.

Trade-offs and Alternatives

  • The biggest trade-off here was deciding to abandon the more ambitious API design from docs: Add machine.CAN low-level API docs. #13149 as it was too hard to implement properly. 😆. This pushes some more of the complexity into the eventual micropython-lib libraries, but it should be easier to implement all of it in Python now the Python-C interface is simpler.

@projectgus projectgus added port-stm32 extmod Relates to extmod/ directory in source labels Dec 15, 2025
@codecov
Copy link

codecov bot commented Dec 15, 2025

Codecov Report

❌ Patch coverage is 0% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.33%. Comparing base (e67d4a2) to head (eb7affc).
⚠️ Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
py/objlist.c 0.00% 11 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #18572      +/-   ##
==========================================
- Coverage   98.38%   98.33%   -0.05%     
==========================================
  Files         171      171              
  Lines       22300    22309       +9     
==========================================
- Hits        21939    21937       -2     
- Misses        361      372      +11     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link

github-actions bot commented Dec 15, 2025

Code size report:

Reference:  esp32/mpconfigport: Enable Zcmp opcodes for ESP32P4. [6341258]
Comparison: WIP: stm32: Add machine.CAN implementation. [merge of eb7affc]
  mpy-cross:    +0 +0.000% 
   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32: +4340 +1.097% PYBV10[incl +4(bss)]
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@sveinungkb
Copy link

Thanks for keeping at this! I've been using the previous branch for a while.

@dpgeorge
Copy link
Member

Thanks for putting this PR up!

I tested with 2x PYBV10 connected through some CAN tranceivers and the new tests added here all pass.

Testing the existing multi_pyb_can tests, the multi_pyb_can/rx_callback.py test fails with TIMEOUT. The other test still passes.

I noticed there's not a simple multitest that just sends and recv's a few frames, without using IRQs. I managed to get something simple working with a poll on can.recv() and it worked. Maybe it's worth adding a simple test like that? At least it would test that the CAN link is up and working, and serves as the simplest, working example.

@@ -0,0 +1,74 @@
from machine import CAN
Copy link
Member

Choose a reason for hiding this comment

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

Maybe these tests can go in tests/multi_extmod/ and be prefixed with machine_can_? Tests in that directory most likely require hardware connections anyway, and if you just want to run CAN tests then can do:

$ ./run-multitests.py -t a0 -t a1 multi_extmod/machine_can_*.py

.. note:: Specific controller hardware may accept additional optional
parameters for hardware-specific features such as oversampling.

.. method:: CAN.init_fd(bitrate, sample_point=None, sjw=None, tseg1=None, tseg2=None)
Copy link
Member

Choose a reason for hiding this comment

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

Does it make sense to combine this into .init() but with additional keyword arguments to set the timing?

I'm just thinking it's a little awkward to init FD mode with:

can = machine.CAN(1, 500_000, CAN.Mode.SILENT)
can.init_fd(1_000_000)
can.mode(CAN.Mode.NORMAL)

Or maybe make it so the mode defaults to SILENT?

Not sure, just some ideas.

Copy link
Member

@dpgeorge dpgeorge Dec 17, 2025

Choose a reason for hiding this comment

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

Another option could be to require calling init_fd() first, then init() (although that does require constructing via just CAN(1)).

Simplifies the pattern of an optional arg which can be a list of at
least a certain length, otherwise one is lazily initialised.

Signed-off-by: Angus Gratton <[email protected]>
API is different to the original machine.CAN proposal, as numerous
shortcomings were found during initial implementation.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <[email protected]>
Partially working, some API still unimplemented.

Adds new multi_can tests which pass when testing between NUCLEO_H723ZG and
PYBDV11.

This work was mostly funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <[email protected]>
@projectgus
Copy link
Contributor Author

Still quite a bit to do (next year now), but it's coming along!

Also have some tests written for error state transitions and restart, but they need a little bit of tidying up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extmod Relates to extmod/ directory in source port-stm32

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New machine.CAN driver

3 participants