-
-
Notifications
You must be signed in to change notification settings - Fork 19.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CAN bus host support for Marlin running MKS Monster 8 V1/V2 board (STM32F4) tech demo #27547
base: bugfix-2.1.x
Are you sure you want to change the base?
Conversation
Added experimental CAN host support to communicate with BTT EBB42 V1.2 CAN head board running Marlin
Fix typo in Configuration.,h for MOTHERBOARD
16f4aea
to
96cc5d3
Compare
96cc5d3
to
9508283
Compare
I cleaned it up and got it building so you can continue to test and refine. The handling of the probe probably needs to be more specific. Users need to be able to mix-and-match endstops and probes according to their designs, but we can also add options to enable specific CAN peripherals that have known protocols and hardware such as built in Z probes and sensorless XY endstops, etc. |
@thinkyhead Thanks, well done, you did a lot of good work there. You could have told me that I completely forgot to include CAN.h, sorry about that!
Agree, this needs some work, preparations are made, but it's currently only implemented for the Z-probe. Here are the masks to get the relevant bits stored in CAN_io_state:
One should be able to define which CAN virtual IO bits are relevant for the host, only these endstop results should be transferred to the host process. I'm not sure how I could transparently transfer the virtual endstop status into Marlin.
I believe most Z-probes should work, they are configured on the head side. Servo commands are forwarded to the head, so BLTouch/3DTtouch and servo probes should work (I use a BLTouch). What CAN peripherals do you have in mind? Stepper drivers? I have not considered sensorless XY endstops on the head board, I assumed to leave this on the host side. The board that controls X and Y should be the host. I would be quite interested to get the TMC2209 and ADXL345 3-axis accelerometer working. |
you'll need clock synchronization between the can boards (and execute based on synchronized time). https://github.com/Duet3D/CANlib/blob/3.5-dev/doc/Duet3CAN-FDProtocol.md#time-synchronisation may be a useful read. |
Great tip, thanks. I had a look, they report that FD CAN is needed, because they want to transmit more than 8 bytes per message, which is not supported by CAN. The STM32F4xx does not support FD CAN. The STM32G0 (BTT EBB42 V1.2) supports FD CAN. Currently I don't see a real reason why time synchronization could not work over CAN because two 32bit timestamps will fit within the available 8 bytes, so a NTP style sync should work. |
I'm testing a NTP like time sync, here's the code running on the tool head: The numbers are timestamps based on micros()
Here 2 logs: t0: 150581211 us t0: 210998246 us I'm not sure how accurate the time synchronization needs to be, anybody? |
TL;DR if you look elsewhere, RRFs time-sync protocol is precise for up to 10us. Maybe a couple notes here:
Please also consider that it took RRF about 5 years to remove most, but not all limitations when using CAN-FD connected expansion boards, many of which relate to board-to-board communication delays or logic challenges. There are some further differences between Klipper's CAN implementation and RRFs CAN-FD implementation:
|
Added FDCAN tool head part Added NTP style CAN bus time sync (test)
Thanks a lot for the detailed reply!
OK, I will target a time-sync below 10us.
Some background. The used CAN bus speed is 1M bit, which is the max for the STM32F4 board, the STM32G0 board supports 2M bit. A Standard ID (11 bits) CAN message with an 8 byte payload (2 x 32bit timestamp) is already 115 bits (if I counted it correctly), and thus 115us for one way of the message. I could perhaps optimize that a little bit, but not by much.
The boards I use have low cost crystal oscillators which have a typical accuracy of +/- 20ppm. The drift I found is about 14ppm which is the difference between 2 boards (not a bad result!). This will be the same for any firmware that is running on the boards. The software will have to actively compensate for the drift. And a suitable resync interval can be chosen to guarantee that the maximal deviation stays below 10us. I think that is possible, but more testing is needed.
I am already using this setup for probing (BLTouch), which works very reliably. The probing process is fully interrupt driven (on both sides). The CAN bus just adds a relatively constant time delay of about 100us. As long as the time delay is constant there will be no effect on the probing results. Note that depending on your hardware and Marlin setup, probing will be done with an accuracy of 1ms (1000 samples/s), so the CAN bus should not be the limiting factor.
That is interesting, I need to read more about this. Internally Marlin has move blocks. I would expect to send these blocks to the tool head with a start timestamp. The tool head probably needs to adjust the move block to compensate for the difference in clock frequency, but the step count must not be affected.
Right, there is a lot of work to do, I try to start simple, then go step by step. |
I just want to say that I am absolutely positively excited about this and I just want to make sure CAN / CAN-FD in Marlin will be as delightful an experience as in RRF (-: |
I just posted the tool head side of the CAN software, which is running on a BTT EBB42 V1.2 board (STM32G0). The board has FD CAN support at 2M bit/s, but because the host (STM32F407) only has CAN support, the FDCAN features cannot be used. IMPORTANT NOTE:It seems the used framework to compile Marlin for the STM32G0 does not have full support for the CAN callbacks. To solve this I needed to override a function, which means that I had to make a change to the framework. I talked to the developers about this a long time ago, but have not seen any action on it yet, perhaps it will only be done in a future version. The framework change that is required (Windows environment in PlatformIO). Note that this part "[email protected]" of the framework path could be different, depending on which frameworks you have installed already. An easy way to find where the framework files are located is by using the "Go to definition" function in PlatformIO (F12 or right click) on a function that is provided by the framework. For example you can right click on "HAL_FDCAN_GetTxFifoFreeLevel". |
I would like to add that there are CAN-FD modules for RRF in stepstick or EXP1/EXP2 format that add CAN-FD via SPI. I believe @thinkyhead might have gotten one in the past, and if there is interest I could try to get you one.
|
Me too. Fewer wires to the toolhead will be so nice! |
just a note that I think the deadline timeout of Klipper likely is 250msec not usec. Polling rate of USB is 125usec already, so reply/response hits those 250usec in an ideal stream without any processing. Sorry for mixing up the millis and the micros! |
…vironment Remove unneeded line from the build_flags: "-DTIMER_SERIAL=TIM4"
... and reorder so CAN is primary
Fixes an issue in the env:BTT_EBB42_V1_1_FDCAN build_flags
Here some new data which shows that the host and tool head time can be calculated accurately based on a few time sync cycles. Note that the earlier and shorter test periods are less accurate. Later we are within 1us even after 3 minutes. t0: 65082290 us ================================ t0: 79930405 us ========================= t0: 259642612 us |
Thanks for your kind offer, I guess you mean one of those MCP2518FD based boards. That is perhaps something to look into at a later time. I believe FDCAN is not required for this task, CAN has the needed speed and bandwidth. FDCAN can run faster and can send more data (64 bytes instead of 8 bytes) per message. That would make things easier of course.
I'm not sure if we are talking about the same thing. The data log are only about a NTP like time sync protocol I'm testing. @thisiskeithb Thanks again for the great support. In my view there are 2 main task to get synchronized motion working:
|
Some thoughts on motion synchronization. Even if we can accurately start a movement, the movement duration will not be the same because of the clock rates difference. We can try to adjust the movement speed, but I believe there is not enough resolution to make the required tiny adjustments. Could the following approach work?
I will investigate the motion blocks to get more insights. |
Description
This is NOT a real pull request, just some code that might be useful for inspiration. I hope I don't break the rules by doing this, if so I will delete the PR asap. This is very experimental code, but it has been working well for me for quite some time. Unfortunately the code is hardware specific. I have converted 2 printers with this setup already, and consider to convert an IDEX printer too.
What is this code?
I have added CAN bus support to Marlin based on the MKS Monster8 V1/V2 board. This way the board can communicate with a BTT EBB42 V1.2 (STM32G0) board. The EBB42 also runs Marlin, but the client side of the code (will post it if there is interest).
The head board controls the hotend heater, auto fan, cooling fan, bed level sensor, neopixel, but NOT the extruder stepper (I could not get that working yet). The head reports back the extruder temperature, probe status, endstops, and filament detector status.
This works perfectly fine and stable, only the CAN cable (4 wires) and stepper cable go to the head. This might be reduced to only the CAN cable if I can manage to synchronize the head stepper with the host requirements.
I've made a protocol that can send gcode to the head via the CAN bus, to make this efficient I encode a lot of the gcode in the identifier of the CAN message:
The host filters the gcode, because only very few gcodes are relevant for the head. A gcode starts with an extended ID message, and additional standard ID messages are send if there are more than 2 parameters (currently up to 7 parameters are supported). The parameters values are encodes as floats and send via the data part of the CAN message (0-8 bytes of data).
The head converts the message back to gcode and executes it on the head.
This whole process is interrupt based, and quite fast.
On the head a Z-probe trigger causes an interrupt which creates an CAN message to inform the host, which triggers an interrupt on the host to report the probe trigger.
Hotend temperature reports are only send about twice a second, it's fully managed by the head, but the host "thinks" it is managing this.
Some things to consider:
The CAN bus module must be enabled in platform IO to enable the required CAN libraries.
MPC only - I have only implemented MPC control for the hotend (no PID), which works in a transparent way. If the host received a "M306 T"(MPC autotune) gcode, the this gcode is aborted for the host and forwarded to the head. The head will do the autotune task and then sends back a report to the host about it's findings. The host stores the MPC settings. The MPC (and other) settings are send to the host at startup.
Thermistor selection is done by the head! The host thermistor selection is irrelevant! This is decided at compile time and cannot be changed by sending configuration gcodes to the head. For this reason I let the head send a message to the host to inform the user which thermistor is selected.
BLTouch/3D touch - They are connected to the head, but Marlin has some "underwater" activities that need to be forwarded to the host. So servo activity is forwarded to the host. I use BLtouch on the head, but it should work with a 3d touch too. (Actually I have simplified the use of the BLTouch to act more like a 3D Touch by keeping it in Switch mode all the time).
If the CAN bus is selected on the MKS Monster V1/V2 then the I2C bus is disconnected from the onboard EEPROM, so it will not be available anymore. Program memory can be used instead, or 2 patch wires can restore the connection to the EEPROM (I use PA8 and PC9).
The EBB42 uses a STM32G0, which as FDCAN support (more advanced), which is quite different from the CAN implementation on of the host
The STM32 CAN hardware has 2 FIFO buffers with only 3 positions for the CAN messages. This is not a lot, but sufficient (especially if the head stepper driver is not used). I use the CAN message filter to store the incoming messages alternating in FIFO0 and FIFO1 so effectively 6 buffers are used. For this reason I toggle 1 bit in the message identifier ID so the receiver forwards the message to the FIFO I target.
Requirements
MKS Monster 8 V1/V2 motherboard
BTT EBB42 V1.2 CAN bus head module
Benefits
Much easier wiring, only CAN bus (4 wires) and stepper (4 wires). The head has a TMC stepper driver on board, but I could not get it synchronized correctly with the host. It can control the hotend, auto fan, controller fan, neopixel, servo probe and report back the temperature, probe and endstop status.
Nothing really, it's a tech demo for inspiration.
Configurations
The whole configuration is available, I only made minimal changes to the source files.
Related Issues
#7735