Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ef969ff
Added CAN host support
rondlh Nov 26, 2024
2647273
Configuration.h MOTHERBOARD fix
rondlh Nov 26, 2024
9508283
clean up
thinkyhead Nov 26, 2024
5b97aef
Added FDCAN tool head part
rondlh Nov 29, 2024
a0ce07a
Rename BTT EBB42 environment, drop "ilament_extruder" from the CAN en…
rondlh Nov 30, 2024
dd5a2ff
Use BTT EBB32 base/common environment
thisiskeithb Nov 30, 2024
73bb491
Format MONSTER8 pins
thisiskeithb Nov 30, 2024
20ea87f
env:BTT_EBB42_V1_1_FDCAN build_flags fix
rondlh Dec 1, 2024
b6c8d50
Merge remote-tracking branch 'upstream/bugfix-2.1.x' into bugfix-2.1.x
rondlh Dec 2, 2024
adf012a
Add clock difference drift calculation
rondlh Dec 6, 2024
9af20e0
Merge upstream changes to endstops.cpp affecting CAN bus virtual IO
rondlh Dec 9, 2024
45ebd12
Undo Filament sensor missing text bugfix to solve merge conflict
rondlh Dec 13, 2024
8d49354
Added basic support for the ADXL345 3-axis accelerometer
rondlh Dec 13, 2024
d7e679d
Cleanup, improve FDCAN bus timing/sampling accuracy
rondlh Dec 23, 2024
7da4c13
Added FDCAN host for STM32H7, wear leveling for STM32H7
rondlh Jan 10, 2025
2536a09
Fix a few error defines
rondlh Jan 10, 2025
53be094
Update to safer SString class
rondlh Jan 11, 2025
f5e0b5e
Removed STM32H7 SERIAL_DMA and Flash Wear leveling and accelerometer
rondlh Jan 11, 2025
74a17cf
Small improvements and cleanup
rondlh Jan 11, 2025
28ed569
Auto initialize CAN device, auto calculate sample timing
rondlh Jan 24, 2025
22bca33
Trying to resolve some conflicts with bugfix-2.1.x
rondlh Jan 29, 2025
ddcaeb8
Resolving conflicts, second try
rondlh Jan 29, 2025
1765205
Merge branch 'bugfix-2.1.x' into pr/27547
thinkyhead Jun 14, 2025
0c4a052
review and clean up
thinkyhead Jun 14, 2025
4f8116a
add test, try to fix build
thinkyhead Jun 14, 2025
f7c6fd0
feature filter, etc
thinkyhead Jun 14, 2025
4deb3bf
more cleanup, todo
thinkyhead Jun 15, 2025
949ff20
Update to bugfix 2025-08-17
rondlh Sep 3, 2025
d70599b
Revert "Update to bugfix 2025-08-17"
rondlh Sep 3, 2025
f01420a
Merge branch 'bugfix-2.1.x' of https://github.com/rondlh/Marlin-CAN_H…
rondlh Sep 3, 2025
e6c7803
Copy missing CAN files to the repository
rondlh Sep 3, 2025
dd93412
Merge remote-tracking branch 'upstream/bugfix-2.1.x' into bugfix-2.1.x
rondlh Sep 3, 2025
4f0ac23
Small fixes to align code to latest bugfix
rondlh Sep 3, 2025
5e4e5b4
Merge upstream corrected typos, CANBUS files are in HAL\STM32\can
rondlh Sep 6, 2025
9f69cc0
Update inclusion of CAN_HOST for STM32F4 platform
rondlh Sep 6, 2025
c135d54
Fix mistakenly deleted env entry BTT_EBB42_V1_1_FDCAN
rondlh Sep 8, 2025
a4ef058
Update to bugfix 2026-04-12
rondlh Apr 16, 2026
30a67dc
Sync to Bugfix 2026-04-17, fix some merge errors
rondlh Apr 17, 2026
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
4 changes: 4 additions & 0 deletions Marlin/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@
//#define RS485_BUS_BUFFER_SIZE 128
#endif

// Enable CAN bus support and protocol
//#define CAN_MASTER
//#define CAN_TOOLHEAD

// Enable the Bluetooth serial interface on AT90USB devices
//#define BLUETOOTH

Expand Down
782 changes: 782 additions & 0 deletions Marlin/src/HAL/STM32/CAN.cpp

Large diffs are not rendered by default.

588 changes: 588 additions & 0 deletions Marlin/src/HAL/STM32/FDCAN.cpp

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions Marlin/src/HAL/STM32/Servo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@

#include "Servo.h"

#if ENABLED(CAN_MASTER)
#include "../shared/CAN.h"
#endif

static uint_fast8_t servoCount = 0;
static libServo *servos[NUM_SERVOS] = {0};
constexpr millis_t servoDelay[] = SERVO_DELAY;
Expand Down Expand Up @@ -71,6 +75,18 @@ int8_t libServo::attach(const int pin, const int min, const int max) {
}

void libServo::move(const int value) {

#if ENABLED(CAN_MASTER) // Forward direct Servo command to head
constexpr int angles[2] = Z_SERVO_ANGLES;
// Translate M280 S10 to M401, M280 S90 to M402
if (value == angles[0])
CAN_Send_Gcode_2params('M', 401, 0, 0, 0, 0); // Deploy Angle: Send "M401" instead, enables interrupt etc.
else if (value == angles[1])
CAN_Send_Gcode_2params('M', 402, 0, 0, 0, 0); // Stow Angle: Send "M402" instead, enables interrupt etc.
else
CAN_Send_Gcode_2params('M', 280, 'S', value, 'P', 0); // M280 S[value] P0
#endif

if (attach(0) >= 0) {
stm32_servo.write(value);
safe_delay(delay);
Expand Down
50 changes: 50 additions & 0 deletions Marlin/src/HAL/shared/CAN.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once

#include "../../inc/MarlinConfigPre.h"

//#define CAN_DEBUG // Define to show gcodes send to HEAD

#define SOUND_OK 880
#define SOUND_ERROR 40

extern uint32_t CAN_io_state; // CAN virtual IO variable

#define CAN_IO_MASK 0b11111 // Masks for the 5 virtual IO bits
#define CAN_PROBE_MASK 1 // Virtual IO bit
#define CAN_FILAMENT_MASK 2 // Virtual IO bit
#define CAN_X_ENDSTOP_MASK 4 // Virtual IO bit
#define CAN_Y_ENDSTOP_MASK 8 // Virtual IO bit
#define CAN_Z_ENDSTOP_MAS 16 // Virtual IO bit
#define CAN_STRING_MESSAGE_MASK 32 // Signals the head sends a string message
#define CAN_REQUEST_SETUP_MASK 64 // Signals the head requests setup information
#define CAN_TMC_OT_MASK 128 // Signals the head signals a TMC Over Temp error
#define CAN_CAN_REQUEST_TIME_SYNC_MASK 256 // Signals the head requested a time sync
#define CAN_ERROR_MASK 512 // Signals the head encountered an error

HAL_StatusTypeDef CAN1_Start(); // FUNCTION PROTOTYPES
HAL_StatusTypeDef CAN1_Stop();
HAL_StatusTypeDef CAN_Send_Gcode();
HAL_StatusTypeDef CAN_Send_Gcode_2params(uint32_t Gcode_type, uint32_t Gcode_no, uint32_t parameter1, float value1, uint32_t parameter2, float value2);
void CAN_Send_Setup(); // Send host configuration to head
void CAN_Idle(); // Idle CAN task
48 changes: 48 additions & 0 deletions Marlin/src/HAL/shared/FDCAN.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#pragma once

// IMPORTANT NOTES
// ===============
// In \Users\<username>\.platformio\packages\framework-arduinoststm32@4.20600.231001\libraries\SrcWrapper\src\HardwareTimer.cpp
// Add function "__weak" in front of "void TIM16_IRQHandler(void)"
// NOTE: Every CAN message is accepted in FIFO0 if: 1. No Standard/Extended filters are set. 2. No global filters is set.

#define CAN_IO_MASK 0b11111 // Virtual IO Mask, 5 bits are used for IO signalling
#define CAN_PROBE_BIT 0 // Virtual IO bit
#define CAN_FILAMENT_BIT 1 // Virtual IO bit
#define CAN_X_ENDSTOP_BIT 2 // Virtual IO bit
#define CAN_Y_ENDSTOP_BIT 3 // Virtual IO bit
#define CAN_Z_ENDSTOP_BIT 4 // Virtual IO bit
#define CAN_STRING_MESSAGE_BIT 5 // Signals the head sends a string message
#define CAN_REQUEST_SETUP_BIT 6 // Signals the head requests setup information
#define CAN_TMC_OT_BIT 7 // Signals the head encountered a TMC Over Temp error
#define CAN_REQUEST_TIME_SYNC_BIT 8 // Signals a request for time sync
#define CAN_ERROR_BIT 9 // Signals the head encountered an error

#define GCODE_TYPE_D 0
#define GCODE_TYPE_G 1
#define GCODE_TYPE_M 2
#define GCODE_TYPE_T 3

#define IDENTIFIER_PARAMTER1_OFFSET 0
#define IDENTIFIER_PARAMTER2_OFFSET 5
#define IDENTIFIER_GCODE_NUMBER_OFFSET 10
#define IDENTIFIER_GCODE_TYPE_OFFSET 23
#define IDENTIFIER_PARAMETER_COUNT_OFFSET 25

#define DATALENGTH_OFFSET 16

#define IDENTIFIER_PARAMETER_COUNT_MASK 0b111
#define IDENTIFIER_PARAMETER_MASK 0b11111
#define IDENTIFIER_GCODE_TYPE_MASK 0b11 // GCODE TYPE
#define IDENTIFIER_GCODE_NUMBER_MASK 0b1111111111111 // GCODE NUMBER ONLY
#define IDENTIFIER_GCODE_MASK 0b111111111111111 // GCODE TYPE AND NUMBER

#define STDID_FIFO_BIT 0b10000000000

// ERROR CODES
#define CAN_ERROR_FIFO_OVERFLOW 1
#define CAN_ERROR_INCOMPLETE_GCODE_RECEIVED 2
#define CAN_ERROR_MARLIN_CMD_BUFFER_OVERFLOW 4

HAL_StatusTypeDef CAN_Send_String(const char * message); // Send CAN string to host
HAL_StatusTypeDef FDCAN2_Start(void); // Start the CAN bus
34 changes: 33 additions & 1 deletion Marlin/src/MarlinCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@
#include "HAL/shared/esp_wifi.h"
#include "HAL/shared/cpu_exception/exception_hook.h"

#if ENABLED(CAN_MASTER)
#include "HAL/shared/CAN.h"
#endif

#if ENABLED(CAN_TOOLHEAD)
#include "HAL/shared/FDCAN.h"
#endif

#if ENABLED(WIFISUPPORT)
#include "HAL/shared/esp_wifi.h"
#endif
Expand Down Expand Up @@ -882,6 +890,16 @@ void idle(const bool no_stepper_sleep/*=false*/) {
// Manage Fixed-time Motion Control
TERN_(FT_MOTION, ftMotion.loop());

#if ENABLED(CAN_MASTER)
void CAN_idle(); // Function Prototype
CAN_idle(); // Call CAN idle task
#endif

#if ENABLED(CAN_TOOLHEAD)
void FDCAN_idle(); // Function prototype
FDCAN_idle(); // Call FDCAN idle task
#endif // CAN_TOOLHEAD

IDLE_DONE:
TERN_(MARLIN_DEV_MODE, idle_depth--);

Expand Down Expand Up @@ -1190,7 +1208,21 @@ void setup() {
while (!MYSERIAL3.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ }
#endif
#endif
SERIAL_ECHOLNPGM("start");
SERIAL_ECHOLNPGM("start\n");

#if ENABLED(CAN_MASTER)
SERIAL_ECHOLN(
F(">>> CAN1 Start: "),
CAN1_Start() == HAL_OK ? F("OK") : F("FAILED!")
);
#endif

#if ENABLED(CAN_TOOLHEAD)
SERIAL_ECHOLN(
F(">>> FDCAN2 Start: "),
FDCAN2_Start() == HAL_OK ? F("OK") : F("FAILED!")
);
#endif

// Set up these pins early to prevent suicide
#if HAS_KILL
Expand Down
12 changes: 11 additions & 1 deletion Marlin/src/feature/runout.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
#include "pause.h" // for did_pause_print
#include "../MarlinCore.h" // for printingIsActive()

#if ENABLED(CAN_MASTER)
#include "../HAL/shared/CAN.h"
#endif

#include "../inc/MarlinConfig.h"

#if ENABLED(EXTENSIBLE_UI)
Expand All @@ -53,6 +57,12 @@

#define FILAMENT_IS_OUT() (READ(FIL_RUNOUT_PIN) == FIL_RUNOUT_STATE)

#if ENABLED(CAN_MASTER)
#define RUNOUT_STATE(N) bool(CAN_io_state & CAN_FILAMENT_MASK) // CAN Virtual Filament Runout pin
#else
#define RUNOUT_STATE(N) READ(FIL_RUNOUT##N##_PIN) // DIO Filament Runout pin
#endif

typedef Flags<
#if NUM_MOTION_SENSORS > NUM_RUNOUT_SENSORS
NUM_MOTION_SENSORS
Expand Down Expand Up @@ -207,7 +217,7 @@ class FilamentSensorBase {

// Return a bitmask of runout pin states
static uint8_t poll_runout_pins() {
#define _OR_RUNOUT(N) | (READ(FIL_RUNOUT##N##_PIN) ? _BV((N) - 1) : 0)
#define _OR_RUNOUT(N) | (RUNOUT_STATE(N) ? _BV((N) - 1) : 0)
return (0 REPEAT_1(NUM_RUNOUT_SENSORS, _OR_RUNOUT));
#undef _OR_RUNOUT
}
Expand Down
14 changes: 14 additions & 0 deletions Marlin/src/gcode/gcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ GcodeSuite gcode;
#include "../feature/fancheck.h"
#endif

#if ENABLED(CAN_MASTER)
#include "../HAL/shared/CAN.h"
#include "../libs/buzzer.h"
#endif

#include "../MarlinCore.h" // for idle, kill

// Inactivity shutdown
Expand Down Expand Up @@ -323,6 +328,15 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {

KEEPALIVE_STATE(IN_HANDLER);

#if ENABLED(CAN_MASTER)
if (CAN_Send_Gcode() != HAL_OK) { // Send command to head
SERIAL_ECHOLN(F("Error: CAN failed to send \""), parser.command_ptr, '"');
#ifndef CAN_DEBUG
BUZZ(1, SOUND_ERROR);
#endif
}
#endif

/**
* Block all Gcodes except M511 Unlock Printer, if printer is locked
* Will still block Gcodes if M511 is disabled, in which case the printer should be unlocked via LCD Menu
Expand Down
61 changes: 51 additions & 10 deletions Marlin/src/gcode/temp/M306.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,34 @@ void GcodeSuite::M306() {

#if ENABLED(MPC_AUTOTUNE)
if (parser.seen_test('T')) {
Temperature::MPCTuningType tuning_type;
const uint8_t type = parser.byteval('S', 0);
switch (type) {
case 1: tuning_type = Temperature::MPCTuningType::FORCE_DIFFERENTIAL; break;
case 2: tuning_type = Temperature::MPCTuningType::FORCE_ASYMPTOTIC; break;
default: tuning_type = Temperature::MPCTuningType::AUTO; break;
}
LCD_MESSAGE(MSG_MPC_AUTOTUNE);
thermalManager.MPC_autotune(e, tuning_type);
ui.reset_status();

#if ENABLED(CAN_MASTER) // MPC Autotune info

SERIAL_ECHOLNPGM(
">>> Forwarding M306 to head board\n"
">>> Store MPC setup in the host Configuration.h or use M500\n"
">>> MPC heater power is: ", p_float_t(MPC_HEATER_POWER, 1), " Watts\n"
">>> Please wait for the auto tune results..."
);

#else

Temperature::MPCTuningType tuning_type;
const uint8_t type = parser.byteval('S', 0);
switch (type) {
case 1: tuning_type = Temperature::MPCTuningType::FORCE_DIFFERENTIAL; break;
case 2: tuning_type = Temperature::MPCTuningType::FORCE_ASYMPTOTIC; break;
default: tuning_type = Temperature::MPCTuningType::AUTO; break;
}
LCD_MESSAGE(MSG_MPC_AUTOTUNE);
thermalManager.MPC_autotune(e, tuning_type);
ui.reset_status();

#if ENABLED(CAN_TOOLHEAD)
M306_report(true); // Report M306 settings to CAN host
#endif
#endif

return;
}
#endif
Expand All @@ -91,6 +109,11 @@ void GcodeSuite::M306_report(const bool forReplay/*=true*/) {
TERN_(MARLIN_SMALL_BUILD, return);

report_heading(forReplay, F("Model predictive control"));

#if ENABLED(CAN_MASTER) // MPC Autotune info
if (forReplay) SERIAL_ECHOLNPGM(">>> Host M306 MPC settings:");
#endif

HOTEND_LOOP() {
report_echo_start(forReplay);
MPC_t &mpc = thermalManager.temp_hotend[e].mpc;
Expand All @@ -105,6 +128,24 @@ void GcodeSuite::M306_report(const bool forReplay/*=true*/) {
#endif
SERIAL_ECHOLNPGM(" H", p_float_t(mpc.filament_heat_capacity_permm, 4));
}

#if ENABLED(CAN_TOOLHEAD) // Report M306 Autotune results to host
if (forReplay) {
MPC_t &mpc = thermalManager.temp_hotend[0].mpc;
MString<100> buffer(F("M306 E0 P"), p_float_t(mpc.heater_power, 2),
" C", p_float_t(mpc.block_heat_capacity, 2),
" R", p_float_t(mpc.sensor_responsiveness, 4),
" A", p_float_t(mpc.ambient_xfer_coeff_fan0, 4),
#if ENABLED(MPC_INCLUDE_FAN)
" F", p_float_t(mpc.fanCoefficient(), 4),
#endif
" H", p_float_t(mpc.filament_heat_capacity_permm, 4),
'\n');

HAL_StatusTypeDef CAN_Send_String(const char * message); // Function Prototype
CAN_Send_String(buffer);
}
#endif // CAN_TOOLHEAD
}

#endif // MPCTEMP
21 changes: 20 additions & 1 deletion Marlin/src/module/endstops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
#include "probe.h"
#endif

#if HAS_FILAMENT_SENSOR && !MULTI_FILAMENT_SENSOR
#include "../feature/runout.h"
#endif

#define DEBUG_OUT ALL(USE_SENSORLESS, DEBUG_LEVELING_FEATURE)
#include "../core/debug_out.h"

Expand All @@ -77,6 +81,12 @@ Endstops::endstop_mask_t Endstops::live_state = 0;
#else
#define READ_ENDSTOP(P) READ(P)
#endif
#elif ENABLED(CAN_MASTER) // Read virtual CAN IO Probe status if needed
#if HAS_BED_PROBE
#define READ_ENDSTOP(P) ((P == Z_MIN_PIN) ? PROBE_READ() : READ(P))
#else
#define READ_ENDSTOP(P) READ(P)
#endif
#else
#define READ_ENDSTOP(P) READ(P)
#endif
Expand Down Expand Up @@ -525,8 +535,11 @@ void __O2 Endstops::report_states() {
print_es_state(extDigitalRead(pin) != state);
}
#undef _CASE_RUNOUT

#elif HAS_FILAMENT_SENSOR
print_es_state(READ(FIL_RUNOUT1_PIN) != FIL_RUNOUT1_STATE, F(STR_FILAMENT));

print_es_state(RUNOUT_STATE(1) != FIL_RUNOUT1_STATE, F(STR_FILAMENT));

#endif

TERN_(BLTOUCH, bltouch._reset_SW_mode());
Expand Down Expand Up @@ -664,6 +677,12 @@ void Endstops::update() {
// When closing the gap check the enabled probe
if (probe_switch_activated())
UPDATE_LIVE_STATE(Z, TERN(USE_Z_MIN_PROBE, MIN_PROBE, MIN));

#if ENABLED(CAN_TOOLHEAD)
HAL_StatusTypeDef CAN_Send_Message(bool TempUpdate); // Function Prototype
CAN_Send_Message(false); // Send Virtual IO update without temperature report
#endif // CAN_TOOLHEAD

#endif

#if USE_Z_MAX
Expand Down
Loading