diff --git a/CMakeLists.txt b/CMakeLists.txt
index dc731ae..eb64962 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,6 +33,7 @@ target_sources(grbl INTERFACE
${CMAKE_CURRENT_LIST_DIR}/regex.c
${CMAKE_CURRENT_LIST_DIR}/ioports.c
${CMAKE_CURRENT_LIST_DIR}/vfs.c
+ ${CMAKE_CURRENT_LIST_DIR}/canbus.c
${CMAKE_CURRENT_LIST_DIR}/kinematics/corexy.c
${CMAKE_CURRENT_LIST_DIR}/kinematics/wall_plotter.c
${CMAKE_CURRENT_LIST_DIR}/kinematics/delta.c
diff --git a/README.md b/README.md
index ee5fadf..df8c0aa 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ It has been written to complement grblHAL and has features such as proper keyboa
---
-Latest build date is 20240624, see the [changelog](changelog.md) for details.
+Latest build date is 20240704, see the [changelog](changelog.md) for details.
__NOTE:__ Build 20240222 has moved the probe input to the ioPorts pool of inputs and will be allocated from it when configured.
The change is major and _potentially dangerous_, it may damage your probe, so please _verify correct operation_ after installing this, or later, builds.
diff --git a/canbus.c b/canbus.c
new file mode 100644
index 0000000..dc38595
--- /dev/null
+++ b/canbus.c
@@ -0,0 +1,209 @@
+/*
+
+ canbus.c -
+
+ Part of grblHAL
+
+ Copyright (c) 2022 Jon Escombe
+ Copyright (c) 2024 Terje Io
+
+ grblHAL 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.
+
+ grblHAL 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 grblHAL. If not, see .
+
+*/
+
+#include "hal.h"
+#include "task.h"
+#include "protocol.h"
+#include "canbus.h"
+
+#ifndef CANBUS_BUFFER_LEN
+#define CANBUS_BUFFER_LEN 8
+#endif
+#ifndef CANBUS_BAUDRATE
+#define CANBUS_BAUDRATE 0 // 125,000
+#endif
+
+typedef struct {
+ volatile canbus_message_t message;
+ can_rx_ptr callback;
+} canbus_rx_message;
+
+typedef struct {
+ volatile canbus_message_t message;
+ bool ext_id;
+} canbus_tx_message;
+
+typedef struct {
+ volatile uint8_t head;
+ volatile uint8_t tail;
+ volatile canbus_tx_message tx[CANBUS_BUFFER_LEN];
+} canbus_tx_buffer_t;
+
+typedef struct {
+ volatile uint8_t head;
+ volatile uint8_t tail;
+ volatile canbus_rx_message rx[CANBUS_BUFFER_LEN];
+} canbus_rx_buffer_t;
+
+static bool isEnabled = false;
+static const uint32_t baud[] = { 125000, 250000, 500000, 1000000 };
+static canbus_tx_buffer_t tx_buffer = {0};
+static canbus_rx_buffer_t rx_buffer = {0};
+
+// Weak implementations of low level functions to be provided by the driver
+
+__attribute__((weak)) bool can_start (uint32_t baud, can_rx_enqueue_fn callback)
+{
+ return false;
+}
+
+__attribute__((weak)) bool can_stop (void)
+{
+ return false;
+}
+
+__attribute__((weak)) bool can_set_baud (uint32_t baud)
+{
+ return false;
+}
+
+__attribute__((weak)) bool can_put (canbus_message_t msg, bool ext_id)
+{
+ return false;
+}
+
+__attribute__((weak)) bool can_add_filter (uint32_t id, uint32_t mask, bool ext_id, can_rx_ptr callback)
+{
+ return false;
+}
+
+// ---
+
+ISR_CODE static bool ISR_FUNC(canbus_queue_rx)(canbus_message_t message, can_rx_ptr callback)
+{
+ bool ok;
+ uint8_t next_head = (rx_buffer.head + 1) % CANBUS_BUFFER_LEN;
+
+ if((ok = next_head != rx_buffer.tail)) {
+ rx_buffer.rx[next_head].callback = callback;
+ rx_buffer.rx[next_head].message = message;
+
+ rx_buffer.head = next_head;
+ }
+
+ return ok;
+}
+
+// called every 1 ms
+static void canbus_poll (void *data)
+{
+ /* if have TX data, sends one message per iteration.. */
+ if(tx_buffer.head != tx_buffer.tail && can_put(tx_buffer.tx[tx_buffer.tail].message, tx_buffer.tx[tx_buffer.tail].ext_id))
+ tx_buffer.tail = (tx_buffer.tail + 1) % CANBUS_BUFFER_LEN;
+
+ /* if have RX data, process one message per iteration.. */
+ if(rx_buffer.head != rx_buffer.tail) {
+ if(rx_buffer.rx[rx_buffer.tail].callback)
+ rx_buffer.rx[rx_buffer.tail].callback(rx_buffer.rx[rx_buffer.tail].message);
+ rx_buffer.tail = (rx_buffer.tail + 1) % CANBUS_BUFFER_LEN;
+ }
+}
+
+static bool canbus_start (uint32_t baud)
+{
+ if((isEnabled = can_start(baud, canbus_queue_rx)))
+ task_add_systick(canbus_poll, NULL);
+
+ return isEnabled;
+}
+
+static status_code_t canbus_set_baud (setting_id_t id, uint_fast16_t value)
+{
+ settings.canbus_baud = value;
+
+ can_set_baud(baud[settings.canbus_baud]);
+
+ return can_set_baud(baud[settings.canbus_baud]) ? Status_OK : Status_SettingValueOutOfRange;
+}
+
+static uint32_t canbus_get_baud (setting_id_t setting)
+{
+ return settings.canbus_baud < (sizeof(baud) / sizeof(uint32_t)) ? settings.canbus_baud : CANBUS_BAUDRATE;
+}
+
+static const setting_group_detail_t canbus_groups [] = {
+ { Group_Root, Group_CANbus, "CAN bus"}
+};
+
+static const setting_detail_t canbus_setting_detail[] = {
+ { Setting_CANbus_BaudRate, Group_CANbus, "CAN bus baud rate", NULL, Format_RadioButtons, "125000,250000,500000,1000000", NULL, NULL, Setting_NonCoreFn, canbus_set_baud, canbus_get_baud, NULL },
+};
+
+static void canbus_settings_restore (void)
+{
+ settings.canbus_baud = CANBUS_BAUDRATE;
+
+ settings_write_global();
+}
+
+static void canbus_settings_load (void)
+{
+ canbus_start(baud[canbus_get_baud(Setting_CANbus_BaudRate)]);
+}
+
+static setting_details_t setting_details = {
+ .groups = canbus_groups,
+ .n_groups = sizeof(canbus_groups) / sizeof(setting_group_detail_t),
+ .settings = canbus_setting_detail,
+ .n_settings = sizeof(canbus_setting_detail) / sizeof(setting_detail_t),
+ .save = settings_write_global,
+ .load = canbus_settings_load,
+ .restore = canbus_settings_restore
+};
+
+// Public API
+
+bool canbus_enabled (void)
+{
+ return isEnabled;
+}
+
+bool canbus_queue_tx (canbus_message_t message, bool ext_id)
+{
+ bool ok;
+ uint8_t next_head = (tx_buffer.head + 1) % CANBUS_BUFFER_LEN;
+
+ if((ok = next_head != tx_buffer.tail)) {
+ tx_buffer.tx[tx_buffer.head].ext_id = ext_id;
+ tx_buffer.tx[tx_buffer.head].message = message;
+ tx_buffer.head = next_head;
+ }
+
+ return ok;
+}
+
+bool canbus_add_filter (uint32_t id, uint32_t mask, bool ext_id, can_rx_ptr callback)
+{
+ return can_add_filter(id, mask, ext_id, callback);
+}
+
+void canbus_init (void)
+{
+ static bool init_ok = false;
+
+ if(!init_ok) {
+ init_ok = true;
+ settings_register(&setting_details);
+ }
+}
diff --git a/canbus.h b/canbus.h
new file mode 100644
index 0000000..f402f26
--- /dev/null
+++ b/canbus.h
@@ -0,0 +1,48 @@
+/*
+
+ canbus.h -
+
+ Part of grblHAL
+
+ Copyright (c) 2022 Jon Escombe
+ Copyright (c) 2024 Terje Io
+
+ grblHAL 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.
+
+ grblHAL 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 grblHAL. If not, see .
+
+*/
+
+#ifndef _CANBUS_H_
+#define _CANBUS_H_
+
+#include
+#include
+
+typedef struct {
+ uint32_t id;
+ uint8_t len;
+ uint8_t data[8];
+} canbus_message_t;
+
+typedef bool (*can_rx_ptr)(canbus_message_t);
+typedef bool (*can_rx_enqueue_fn)(canbus_message_t msg, can_rx_ptr callback); // used internally by the driver
+
+/*
+ * Function prototypes
+ */
+void canbus_init (void);
+bool canbus_enabled (void);
+bool canbus_queue_tx (canbus_message_t message, bool ext_id);
+bool canbus_add_filter (uint32_t id, uint32_t mask, bool ext_id, can_rx_ptr callback);
+
+#endif /* _CANBUS_H_ */
diff --git a/changelog.md b/changelog.md
index 271bf13..7f93d3d 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,23 @@
## grblHAL changelog
+Build 20240704
+
+Core:
+
+* Added high level CANbus API for plugin use. If driver/board combo provides the required lowlevel HAL API the `NEWOPT` string in the `$I` output will contain the `CAN` element when CAN is enabled.
+Ref. [issue #179](https://github.com/grblHAL/STM32F4xx/issues/179).
+
+* Added soft limits check for corexy kinematics, ref. [discussion #536](https://github.com/grblHAL/core/discussions/536).
+
+Drivers:
+
+* ESP32: fixed WebUI regression. Ref. [issue #116](https://github.com/grblHAL/ESP32/issues/116).
+
+* STM32F7xx, STM32F4xx: Added lowlevel CANbus API and enabled it for some boards.
+
+---
+
+
Build 20240624
Core:
diff --git a/grbl.h b/grbl.h
index 88e7d18..fdc0090 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20240624
+#define GRBL_BUILD 20240704
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/kinematics/corexy.c b/kinematics/corexy.c
index 9350bdf..be35bbb 100644
--- a/kinematics/corexy.c
+++ b/kinematics/corexy.c
@@ -37,6 +37,7 @@
#define B_MOTOR Y_AXIS // Must be Y_AXIS
static on_report_options_ptr on_report_options;
+static travel_limits_ptr check_travel_limits;
// Returns x or y-axis "steps" based on CoreXY motor steps.
inline static int32_t corexy_convert_to_a_motor_steps (int32_t *steps)
@@ -77,12 +78,25 @@ static inline float *transform_from_cartesian (float *target, float *position)
return target;
}
+// Transform position from motor (corexy) coordinate system to cartesian coordinate system
+static inline float *transform_to_cartesian (float *target, float *position)
+{
+ uint_fast8_t idx;
+
+ target[X_AXIS] = (position[X_AXIS] + position[Y_AXIS]) * 0.5f;
+ target[Y_AXIS] = (position[X_AXIS] - position[Y_AXIS]) * 0.5f;
+
+ for(idx = Z_AXIS; idx < N_AXIS; idx++)
+ target[idx] = position[idx];
+
+ return target;
+}
+
static uint_fast8_t corexy_limits_get_axis_mask (uint_fast8_t idx)
{
return ((idx == A_MOTOR) || (idx == B_MOTOR)) ? (bit(X_AXIS) | bit(Y_AXIS)) : bit(idx);
}
-
static void corexy_limits_set_target_pos (uint_fast8_t idx) // fn name?
{
int32_t axis_position;
@@ -102,6 +116,19 @@ static void corexy_limits_set_target_pos (uint_fast8_t idx) // fn name?
}
}
+// Checks and reports if target array exceeds machine travel limits. Returns false if check failed.
+// NOTE: target for axes X and Y are in motor coordinates if is_cartesian is false.
+static bool corexy_check_travel_limits (float *target, axes_signals_t axes, bool is_cartesian)
+{
+ if(is_cartesian)
+ return check_travel_limits(target, axes, true);
+
+ float cartesian_coords[N_AXIS];
+
+ transform_to_cartesian(cartesian_coords, target);
+
+ return check_travel_limits(cartesian_coords, axes, true);
+}
// Set machine positions for homed limit switches. Don't update non-homed axes.
// NOTE: settings.max_travel[] is stored as a negative value.
@@ -207,7 +234,7 @@ static void report_options (bool newopt)
on_report_options(newopt);
if(!newopt)
- hal.stream.write("[KINEMATICS:CoreXY v2.00]" ASCII_EOL);
+ hal.stream.write("[KINEMATICS:CoreXY v2.01]" ASCII_EOL);
}
// Initialize API pointers for CoreXY kinematics
@@ -222,6 +249,9 @@ void corexy_init (void)
kinematics.homing_cycle_validate = homing_cycle_validate;
kinematics.homing_cycle_get_feedrate = homing_cycle_get_feedrate;
+ check_travel_limits = grbl.check_travel_limits;
+ grbl.check_travel_limits = corexy_check_travel_limits;
+
on_report_options = grbl.on_report_options;
grbl.on_report_options = report_options;
}
diff --git a/report.c b/report.c
index aafa32c..edbd558 100644
--- a/report.c
+++ b/report.c
@@ -38,6 +38,7 @@
#include "nvs_buffer.h"
#include "machine_limits.h"
#include "state_machine.h"
+#include "canbus.h"
#include "regex.h"
#if ENABLE_SPINDLE_LINEARIZATION
@@ -985,6 +986,9 @@ void report_build_info (char *line, bool extended)
if(hal.rtc.get_datetime)
strcat(buf, "RTC,");
+ if(canbus_enabled())
+ strcat(buf, "CAN,");
+
#ifdef PID_LOG
strcat(buf, "PID,");
#endif
diff --git a/settings.h b/settings.h
index 8c7710b..eb799f5 100644
--- a/settings.h
+++ b/settings.h
@@ -779,7 +779,8 @@ typedef struct {
control_signals_t control_disable_pullup;
coolant_state_t coolant_invert;
axes_signals_t home_invert;
- uint16_t hole_1;
+ uint8_t modbus_baud;
+ uint8_t canbus_baud;
spindle_settings_t spindle;
stepper_settings_t steppers;
reportmask_t status_report; // Mask to indicate desired report data.