diff --git a/README.md b/README.md
index b30da88..89b8087 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
## grblHAL ##
-Latest build date is 20250702, see the [changelog](changelog.md) for details.
+Latest build date is 20250705, see the [changelog](changelog.md) for details.
> [!NOTE]
> A settings reset will be performed on an update of builds prior to 20241208. Backup and restore of settings is recommended.
diff --git a/changelog.md b/changelog.md
index ce1bca7..6e1cd4b 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,24 @@
## grblHAL changelog
+Build 20250705
+
+Core:
+
+* Added ioports API call for remapping port numbers.
+
+* Added HAL support for setting serial stream format. Added setting `$681` for configuring Modbus RTU stream parity, available when the underlying driver supports format setting.
+Ref. STM32F7xx driver PR [#51](https://github.com/dresco/STM32H7xx/pull/51).
+
+Drivers:
+
+* STM32F4xx: added basic support for setting serial stream format, parity only for now.
+
+Plugins:
+
+* Plasma: updated to use new call for remapping portnumbers for virtual ports. Potential fix for ioSender issue [#470](https://github.com/terjeio/ioSender/issues/470).
+
+---
+
Build 20250702
Core:
diff --git a/config.h b/config.h
index 1fde663..9997066 100644
--- a/config.h
+++ b/config.h
@@ -2055,6 +2055,24 @@ G90
#endif
///@}
+/*! @name $374 - Settings_ModBus_BaudRate
+Default baud rate for ModBus RTU stream.
+*/
+///@{
+#if !defined DEFAULT_MODBUS_STREAM_BAUD || defined __DOXYGEN__
+#define DEFAULT_MODBUS_STREAM_BAUD 3 // 0 = 2400, 1 = 4800, 2 = 9600, 3 = 19200, 4 = 38400, 5 = 115200
+#endif
+///@}
+
+/*! @name $681 - Setting_ModBus_StreamFormat
+Default stream format settings for ModBus RTU stream.
+*/
+///@{
+#if !defined DEFAULT_MODBUS_STREAM_PARITY || defined __DOXYGEN__
+#define DEFAULT_MODBUS_STREAM_PARITY 0 // 0 = None, 1 = Even, 2 = Odd
+#endif
+///@}
+
// Axis settings (Group_XAxis - Group_VAxis)
/*! @name $10x - Setting_AxisStepsPerMM
diff --git a/grbl.h b/grbl.h
index 9c722c6..7d246d3 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20250702
+#define GRBL_BUILD 20250705
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/ioports.c b/ioports.c
index b9cb565..4a092ce 100644
--- a/ioports.c
+++ b/ioports.c
@@ -532,6 +532,31 @@ bool ioport_digital_pwm_config (uint8_t port, pwm_config_t *config)
return ok && pin->config(pin, config, false);
}
+/*! \brief Remap (virtual) port.
+\param type as an \a #io_port_type_t enum value.
+\param dir as an \a #io_port_direction_t enum value.
+\param port_from the assigned port number.
+\param port_to the remapped port number. The original port number will be shadowed.
+\returns \a true if successful, \a false if original port is already claimed.
+*/
+bool ioport_remap (io_port_type_t type, io_port_direction_t dir, uint8_t port_from, uint8_t port_to)
+{
+ uint8_t org_port;
+ bool ok;
+ io_ports_private_t *cfg = get_port_data(type, dir);
+
+ if((ok = (cfg->claimed.mask & (1UL << port_to))) == 0) {
+
+ if((org_port = map_reverse(cfg, port_from)) != port_to)
+ cfg->map[org_port] = 255;
+
+ cfg->free = -1;
+ cfg->map[port_to] = port_from;
+ }
+
+ return ok;
+}
+
// HAL wrapper/veneers
__STATIC_FORCEINLINE bool is_match (io_ports_list_t *io_port, io_port_type_t type, io_port_direction_t dir, uint8_t port)
diff --git a/ioports.h b/ioports.h
index 8dcb149..ecd6759 100644
--- a/ioports.h
+++ b/ioports.h
@@ -269,6 +269,7 @@ typedef struct {
bool ioports_add_analog (io_analog_t *ports);
bool ioports_add_digital (io_digital_t *ports);
void ioports_add_settings (driver_settings_load_ptr settings_loaded, setting_changed_ptr setting_changed);
+bool ioport_remap (io_port_type_t type, io_port_direction_t dir, uint8_t from, uint8_t to);
void ioport_save_input_settings (xbar_t *xbar, gpio_in_config_t *config);
void ioport_save_output_settings (xbar_t *xbar, gpio_out_config_t *config);
void ioport_setting_changed (setting_id_t id);
diff --git a/modbus_rtu.c b/modbus_rtu.c
index a91d23d..0ce8fc6 100644
--- a/modbus_rtu.c
+++ b/modbus_rtu.c
@@ -33,10 +33,6 @@
#include "state_machine.h"
#include "modbus.h"
-#ifndef MODBUS_BAUDRATE
-#define MODBUS_BAUDRATE 3 // 19200
-#endif
-
typedef enum {
ModBus_Idle,
ModBus_Silent,
@@ -52,7 +48,8 @@ typedef void (*stream_set_direction_ptr)(bool tx);
typedef struct {
set_baud_rate_ptr set_baud_rate;
- stream_set_direction_ptr set_direction; // NULL if auto direction
+ set_format_ptr set_format; //!< Optional handler for setting the stream format.
+ stream_set_direction_ptr set_direction; //!< NULL if auto direction
get_stream_buffer_count_ptr get_tx_buffer_count;
get_stream_buffer_count_ptr get_rx_buffer_count;
stream_write_n_ptr write;
@@ -389,7 +386,7 @@ static uint32_t get_baudrate (uint32_t rate)
return idx;
} while(idx);
- return MODBUS_BAUDRATE;
+ return DEFAULT_MODBUS_STREAM_BAUD;
}
static const setting_group_detail_t modbus_groups [] = {
@@ -398,8 +395,9 @@ static const setting_group_detail_t modbus_groups [] = {
static status_code_t modbus_set_baud (setting_id_t id, uint_fast16_t value)
{
- modbus.baud_rate = baud[(uint32_t)value];
- silence_timeout = silence.timeout[(uint32_t)value];
+ settings.modbus_baud = (uint8_t)value;
+ modbus.baud_rate = settings.modbus_baud = baud[settings.modbus_baud];
+ silence_timeout = silence.timeout[settings.modbus_baud];
stream.set_baud_rate(modbus.baud_rate);
return Status_OK;
@@ -410,9 +408,30 @@ static uint32_t modbus_get_baud (setting_id_t setting)
return get_baudrate(modbus.baud_rate);
}
+static status_code_t modbus_set_format (setting_id_t id, uint_fast16_t value)
+{
+ if(stream.set_format) {
+ settings.modbus_stream_format.parity = (serial_parity_t)value;
+ stream.set_format(settings.modbus_stream_format);
+ }
+
+ return stream.set_format ? Status_OK : Status_SettingDisabled;
+}
+
+static uint32_t modbus_get_format (setting_id_t setting)
+{
+ return (uint32_t)settings.modbus_stream_format.parity;
+}
+
+static bool can_set_format (const setting_detail_t *setting, uint_fast16_t offset)
+{
+ return stream.set_format != NULL;
+}
+
static const setting_detail_t modbus_settings[] = {
{ Settings_ModBus_BaudRate, Group_ModBus, "ModBus baud rate", NULL, Format_RadioButtons, "2400,4800,9600,19200,38400,115200", NULL, NULL, Setting_NonCoreFn, modbus_set_baud, modbus_get_baud, NULL },
- { Settings_ModBus_RXTimeout, Group_ModBus, "ModBus RX timeout", "milliseconds", Format_Integer, "####0", "50", "250", Setting_NonCore, &modbus.rx_timeout, NULL, NULL }
+ { Settings_ModBus_RXTimeout, Group_ModBus, "ModBus RX timeout", "milliseconds", Format_Integer, "####0", "50", "250", Setting_NonCore, &modbus.rx_timeout, NULL, NULL },
+ { Setting_ModBus_StreamFormat, Group_ModBus, "ModBus serial format", NULL, Format_RadioButtons, "8-bit no parity, 8-bit even parity, 8-bit odd parity", NULL, NULL, Setting_NonCoreFn, modbus_set_format, modbus_get_format, can_set_format }
};
static void modbus_settings_save (void)
@@ -423,7 +442,7 @@ static void modbus_settings_save (void)
static void modbus_settings_restore (void)
{
modbus.rx_timeout = 50;
- modbus.baud_rate = baud[MODBUS_BAUDRATE];
+ modbus.baud_rate = baud[DEFAULT_MODBUS_STREAM_BAUD];
hal.nvs.memcpy_to_nvs(nvs_address, (uint8_t *)&modbus, sizeof(modbus_settings_t), true);
}
@@ -438,6 +457,9 @@ static void modbus_settings_load (void)
silence_timeout = silence.timeout[get_baudrate(modbus.baud_rate)];
stream.set_baud_rate(modbus.baud_rate);
+
+ if(stream.set_format)
+ stream.set_format(settings.modbus_stream_format);
}
static void onReportOptions (bool newopt)
@@ -445,7 +467,7 @@ static void onReportOptions (bool newopt)
on_report_options(newopt);
if(!newopt)
- report_plugin("MODBUS", "0.19");
+ report_plugin("MODBUS", "0.20");
}
static bool modbus_rtu_isup (void)
@@ -500,11 +522,12 @@ static bool claim_stream (io_stream_properties_t const *sstream)
if(sstream->type == StreamType_Serial && (stream_instance >= 0
? sstream->instance == (uint8_t)stream_instance
: sstream->flags.modbus_ready && !sstream->flags.claimed)) {
- if((claimed = sstream->claim(baud[MODBUS_BAUDRATE])) && stream_is_valid(claimed)) {
+ if((claimed = sstream->claim(baud[DEFAULT_MODBUS_STREAM_BAUD])) && stream_is_valid(claimed)) {
claimed->set_enqueue_rt_handler(stream_buffer_all);
stream.set_baud_rate = claimed->set_baud_rate;
+ stream.set_format = claimed->set_format; //!< Optional handler for setting the stream format.
stream.get_tx_buffer_count = claimed->get_tx_buffer_count;
stream.get_rx_buffer_count = claimed->get_rx_buffer_count;
stream.write = claimed->write_n;
diff --git a/settings.c b/settings.c
index d387d8b..0e10fcc 100644
--- a/settings.c
+++ b/settings.c
@@ -354,6 +354,9 @@ PROGMEM const settings_t defaults = {
.safety_door.spindle_on_delay = DEFAULT_SAFETY_DOOR_SPINDLE_DELAY,
.safety_door.coolant_on_delay = DEFAULT_SAFETY_DOOR_COOLANT_DELAY,
+ .modbus_baud = DEFAULT_MODBUS_STREAM_BAUD,
+ .modbus_stream_format = (DEFAULT_MODBUS_STREAM_PARITY << 4),
+
.rgb_strip.length0 = DEFAULT_RGB_STRIP0_LENGTH,
.rgb_strip.length1 = DEFAULT_RGB_STRIP1_LENGTH
};
diff --git a/settings.h b/settings.h
index ef4f8ca..32a1202 100644
--- a/settings.h
+++ b/settings.h
@@ -26,6 +26,7 @@
#include "config.h"
#include "system.h"
+#include "stream.h"
#include "plugins.h"
// Version of the persistent storage data. Always stored in byte 0 of non-volatile storage.
@@ -459,6 +460,7 @@ typedef enum {
Setting_RelayPortToolsetter = 678,
Setting_RelayPortProbe2 = 679,
Setting_StepperEnableDelay = 680,
+ Setting_ModBus_StreamFormat = 681,
Setting_SpindlePWMOptions1 = 709,
@@ -901,7 +903,8 @@ typedef struct {
stepper_spindle_settings_flags_t stepper_spindle_flags;
uint16_t stepper_enable_delay; // Move to stepper_settings_t
tool_id_t tool_id;
- char reserved[10]; // Reserved For future expansion
+ serial_format_t modbus_stream_format;
+ char reserved[9]; // Reserved For future expansion
} settings_t;
typedef enum {
diff --git a/stream.h b/stream.h
index a87c033..8947a1b 100644
--- a/stream.h
+++ b/stream.h
@@ -91,6 +91,35 @@ typedef enum {
StreamType_Null
} stream_type_t;
+typedef enum {
+ Serial_8bit = 0,
+ Serial_7bit
+} serial_width_t;
+
+typedef enum {
+ Serial_StopBits2 = 0,
+ Serial_StopBits1_5,
+ Serial_StopBits1,
+ Serial_StopBits0_5,
+} serial_stopbits_t;
+
+typedef enum {
+ Serial_ParityNone = 0,
+ Serial_ParityEven,
+ Serial_ParityOdd,
+ Serial_ParityMarkSPace,
+} serial_parity_t;
+
+typedef union {
+ uint8_t value;
+ struct {
+ uint8_t width :2,
+ stopbits :2,
+ parity :2,
+ unused :2;
+ };
+} serial_format_t;
+
typedef union {
uint8_t value;
struct {
@@ -162,6 +191,11 @@ set it to protocol_enqueue_realtime_command() on initialization.
*/
typedef enqueue_realtime_command_ptr (*set_enqueue_rt_handler_ptr)(enqueue_realtime_command_ptr handler);
+/*! \brief Pointer to function for setting the stream format.
+\param format a \a serial_format_t struct.
+\returns true if successful.
+*/
+typedef bool (*set_format_ptr)(serial_format_t format);
/*! \brief Pointer to function for setting the stream baud rate.
\param baud_rate
@@ -262,6 +296,7 @@ typedef struct {
get_stream_buffer_count_ptr get_tx_buffer_count; //!< Optional handler for getting number of characters in the output buffer(s). Count shall include any unsent characters in any transmit FIFO and/or transmit register. Required for Modbus support.
flush_stream_buffer_ptr reset_write_buffer; //!< Optional handler for flushing the output buffer. Any transmit FIFO shall be flushed as well. Required for Modbus support.
set_baud_rate_ptr set_baud_rate; //!< Optional handler for setting the stream baud rate. Required for Modbus support, recommended for Bluetooth support.
+ set_format_ptr set_format; //!< Optional handler for setting the stream format.
on_linestate_changed_ptr on_linestate_changed; //!< Optional handler to be called when line state changes. Set by client.
vfs_file_t *file; //!< File handle, non-null if streaming from a file.
} io_stream_t;