Skip to content

Commit

Permalink
Lock the controls UI when Grbl commands are being processed + add toa…
Browse files Browse the repository at this point in the history
…st messages
  • Loading branch information
Lyrkan committed Apr 14, 2024
1 parent d735bb6 commit 288e050
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 17 deletions.
21 changes: 18 additions & 3 deletions include/Grbl/grbl_serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#define GRBL_MESSAGE_REPORT_INTERVAL "$Report/Interval=200"

#define GRBL_TASK_NOTIFY_ACK_INDEX 0
#define GRBL_TASK_NOTIFY_ACK_SUCCESS 0
#define GRBL_TASK_NOTIFY_ACK_ERROR 1

typedef enum {
PIN_GRBL_TX = 5,
Expand Down Expand Up @@ -52,18 +54,31 @@ typedef struct {
float z;
} GrblMoveCoordinates;

typedef struct {
void (*on_success)() = NULL;
void (*on_failure)() = NULL;
void (*on_finished)() = NULL;
} GrblCommandCallbacks;

typedef struct {
char *buffer;
uint32_t ack_timeout_ms;
GrblCommandCallbacks callbacks;
} GrblCommand;

void grbl_serial_init();
GrblSerialStatus grbl_get_serial_status();
void grbl_set_serial_status(GrblSerialStatus serial_status);
bool grbl_send_message(
const char *message, bool send_to_front = false, uint32_t ack_timeout = GRBL_ACK_DEFAULT_TIMEOUT_MS);
const char *message,
bool send_to_front = false,
uint32_t ack_timeout = GRBL_ACK_DEFAULT_TIMEOUT_MS,
GrblCommandCallbacks callbacks = GrblCommandCallbacks());
bool grbl_send_init_commands();
bool grbl_send_home_command(uint8_t axis_flags);
bool grbl_send_move_command(GrblMoveCoordinates target, GrblMoveMode mode = GRBL_MOVE_MODE_UNDEFINED);
bool grbl_send_home_command(uint8_t axis_flags, GrblCommandCallbacks callbacks = GrblCommandCallbacks());
bool grbl_send_move_command(
GrblMoveCoordinates target,
GrblMoveMode mode = GRBL_MOVE_MODE_UNDEFINED,
GrblCommandCallbacks callbacks = GrblCommandCallbacks());

#endif
1 change: 1 addition & 0 deletions include/UI/overlay.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#define FLASH_MESSAGE_MAX_LENGTH 255

typedef enum {
FLASH_LEVEL_SUCCESS,
FLASH_LEVEL_INFO,
FLASH_LEVEL_WARNING,
FLASH_LEVEL_DANGER,
Expand Down
1 change: 1 addition & 0 deletions include/UI/screens/controls.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

typedef enum {
CONTROLS_UPDATE_GRBL_REPORT = 1 << 0,
CONTROLS_UPDATE_GRBL_COMMMAND_ENDED = 1 << 1,
} ControlsScreenUpdateType;

extern lv_obj_t *ui_controls_screen;
Expand Down
4 changes: 2 additions & 2 deletions src/Grbl/grbl_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ static void grbl_process_ack() {

// Notify the TX task that its previous message has been acknowledged
if (grbl_tx_task_handle != NULL) {
xTaskNotifyIndexed(grbl_tx_task_handle, GRBL_TASK_NOTIFY_ACK_INDEX, 0, eNoAction);
xTaskNotifyIndexed(grbl_tx_task_handle, GRBL_TASK_NOTIFY_ACK_INDEX, GRBL_TASK_NOTIFY_ACK_SUCCESS, eNoAction);
}
}

Expand All @@ -57,7 +57,7 @@ static void grbl_process_error(const char *error_code) {
// Notify the TX task that its previous message has been acknowledged
// TODO Differenciate acks from errors in the TX task
if (grbl_tx_task_handle != NULL) {
xTaskNotifyIndexed(grbl_tx_task_handle, GRBL_TASK_NOTIFY_ACK_INDEX, 0, eNoAction);
xTaskNotifyIndexed(grbl_tx_task_handle, GRBL_TASK_NOTIFY_ACK_INDEX, GRBL_TASK_NOTIFY_ACK_ERROR, eNoAction);
}
}

Expand Down
58 changes: 51 additions & 7 deletions src/Grbl/grbl_serial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,36 @@ static void grbl_tx_task(void *param) {
ui_overlay_add_flash_message(FLASH_LEVEL_DANGER, "An error happened when trying to send Grbl message");
}

uint32_t ack_notification_value;
if (xTaskNotifyWaitIndexed(
GRBL_TASK_NOTIFY_ACK_INDEX,
0x00,
ULONG_MAX,
NULL,
&ack_notification_value,
pdMS_TO_TICKS(message.ack_timeout_ms)) != pdTRUE) {
if (!initialized) {
// Drop current message and the ones that were already in the queue
// Drop current message
int dropped_messages = 1;
if (message.callbacks.on_failure != NULL) {
message.callbacks.on_failure();
}

if (message.callbacks.on_finished != NULL) {
message.callbacks.on_finished();
}

free(message.buffer);

// Drop remaining messages from the queue
while (xQueueReceive(grbl_tx_msg_queue, &message, 0) != pdFALSE) {
if (message.callbacks.on_failure != NULL) {
message.callbacks.on_failure();
}

if (message.callbacks.on_finished != NULL) {
message.callbacks.on_finished();
}

free(message.buffer);
dropped_messages++;
}
Expand All @@ -156,6 +175,24 @@ static void grbl_tx_task(void *param) {
flash_message[strnlen(flash_message, ARRAY_SIZE(flash_message)) - 1] = '\0'; // Remove line-ending
ui_overlay_add_flash_message(FLASH_LEVEL_WARNING, flash_message);
}

// Notify sender that a timeout occurred
if (message.callbacks.on_failure != NULL) {
message.callbacks.on_failure();
}
} else {
// If we received a ack or an error for this message
if (ack_notification_value == GRBL_TASK_NOTIFY_ACK_SUCCESS && message.callbacks.on_success != NULL) {
message.callbacks.on_success();
} else if (
ack_notification_value == GRBL_TASK_NOTIFY_ACK_ERROR && message.callbacks.on_failure != NULL) {
message.callbacks.on_failure();
}
}

// Notify sender that the command has been processed (doesn't matter if it succeeded or not)
if (message.callbacks.on_finished != NULL) {
message.callbacks.on_finished();
}

free(message.buffer);
Expand Down Expand Up @@ -223,7 +260,7 @@ void grbl_set_serial_status(GrblSerialStatus serial_status) {
ui_status_notify_update(STATUS_UPDATE_UART);
}

bool grbl_send_message(const char *message, bool send_to_front, uint32_t ack_timeout) {
bool grbl_send_message(const char *message, bool send_to_front, uint32_t ack_timeout, GrblCommandCallbacks callbacks) {
size_t message_length = strlen(message);
if (message_length > GRBL_MAX_LINE_lENGTH) {
log_e("Message length exceeds GRBL_MAX_LINE_LENGTH: %s", message);
Expand All @@ -241,6 +278,7 @@ bool grbl_send_message(const char *message, bool send_to_front, uint32_t ack_tim
GrblCommand command = {
.buffer = message_copy,
.ack_timeout_ms = ack_timeout,
.callbacks = callbacks,
};

if (send_to_front) {
Expand Down Expand Up @@ -284,7 +322,7 @@ bool grbl_send_init_commands() {
return true;
}

bool grbl_send_home_command(uint8_t axis_flags) {
bool grbl_send_home_command(uint8_t axis_flags, GrblCommandCallbacks callbacks) {
log_d(
"Sending a homing command for axis: %s%s%s",
(axis_flags & GRBL_AXIS_X) != 0 ? "X" : "",
Expand All @@ -302,10 +340,10 @@ bool grbl_send_home_command(uint8_t axis_flags) {
snprintf(buffer, ARRAY_SIZE(buffer), "%s%c", buffer, 'Z');
}

return grbl_send_message(buffer, false, GRBL_ACK_HOMING_TIMEOUT_MS);
return grbl_send_message(buffer, false, GRBL_ACK_HOMING_TIMEOUT_MS, callbacks);
}

bool grbl_send_move_command(GrblMoveCoordinates target, GrblMoveMode mode) {
bool grbl_send_move_command(GrblMoveCoordinates target, GrblMoveMode mode, GrblCommandCallbacks callbacks) {
log_i(
"Sending a%s move command for axis %s%s%s with coordinates (%.2f, %.2f, %.2f)",
mode == GRBL_MOVE_MODE_ABSOLUTE ? "n absolute"
Expand All @@ -322,11 +360,17 @@ bool grbl_send_move_command(GrblMoveCoordinates target, GrblMoveMode mode) {
switch (mode) {
case GRBL_MOVE_MODE_ABSOLUTE:
if (!grbl_send_message("G90")) {
if (callbacks.on_failure != NULL) {
callbacks.on_failure();
}
return false;
}
break;
case GRBL_MOVE_MODE_RELATIVE:
if (!grbl_send_message("G91")) {
if (callbacks.on_failure != NULL) {
callbacks.on_failure();
}
return false;
}
break;
Expand All @@ -344,5 +388,5 @@ bool grbl_send_move_command(GrblMoveCoordinates target, GrblMoveMode mode) {
snprintf(buffer, ARRAY_SIZE(buffer), "%s Z%.2f", buffer, target.z);
}

return grbl_send_message(buffer);
return grbl_send_message(buffer, false, GRBL_ACK_DEFAULT_TIMEOUT_MS, callbacks);
}
3 changes: 3 additions & 0 deletions src/UI/overlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ static void ui_overlay_display_flash_message(FlashMessage *flash) {
lv_obj_set_style_bg_opa(flash->ui_element, 200, LV_PART_MAIN | LV_STATE_DEFAULT);

switch (flash->level) {
case FLASH_LEVEL_SUCCESS:
lv_obj_set_style_bg_color(flash->ui_element, lv_color_hex(0x00db66), LV_PART_MAIN | LV_STATE_DEFAULT);
break;
case FLASH_LEVEL_INFO:
lv_obj_set_style_bg_color(flash->ui_element, lv_color_hex(0x007bff), LV_PART_MAIN | LV_STATE_DEFAULT);
break;
Expand Down
79 changes: 74 additions & 5 deletions src/UI/screens/controls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Grbl/grbl_report.h"
#include "K40/relays.h"
#include "UI/images.h"
#include "UI/overlay.h"
#include "UI/utils.h"
#include "UI/screens/controls.h"
#include "math.h"
Expand All @@ -29,6 +30,20 @@ static lv_obj_t *ui_controls_air_assist_switch;
static lv_obj_t *ui_controls_lights_switch;
static lv_obj_t *ui_controls_preview_switch;

static void ui_controls_lock_grbl_controls() {
lv_obj_add_state(ui_controls_laser_home_button, LV_STATE_DISABLED);
lv_obj_add_state(ui_controls_laser_disable_steppers_button, LV_STATE_DISABLED);
lv_btnmatrix_set_btn_ctrl_all(ui_controls_laser_move_x_matrix, LV_BTNMATRIX_CTRL_DISABLED);
lv_btnmatrix_set_btn_ctrl_all(ui_controls_laser_move_y_matrix, LV_BTNMATRIX_CTRL_DISABLED);
}

static void ui_controls_unlock_grbl_controls() {
lv_obj_clear_state(ui_controls_laser_home_button, LV_STATE_DISABLED);
lv_obj_clear_state(ui_controls_laser_disable_steppers_button, LV_STATE_DISABLED);
lv_btnmatrix_clear_btn_ctrl_all(ui_controls_laser_move_x_matrix, LV_BTNMATRIX_CTRL_DISABLED);
lv_btnmatrix_clear_btn_ctrl_all(ui_controls_laser_move_y_matrix, LV_BTNMATRIX_CTRL_DISABLED);
}

static void ui_controls_btnmatrix_handler(lv_event_t *e) {
lv_event_code_t event_code = lv_event_get_code(e);
if (event_code != LV_EVENT_VALUE_CHANGED) {
Expand All @@ -37,16 +52,34 @@ static void ui_controls_btnmatrix_handler(lv_event_t *e) {

lv_obj_t *event_target = lv_event_get_target(e);

// Detect when GRBL command are fully process to release UI
GrblCommandCallbacks grbl_command_callbacks = GrblCommandCallbacks();
grbl_command_callbacks.on_finished = []() -> void {
ui_controls_notify_update(CONTROLS_UPDATE_GRBL_COMMMAND_ENDED);
};

if (event_target == NULL) {
// Should never happen
return;
} else if (event_target == ui_controls_laser_move_x_matrix || event_target == ui_controls_laser_move_y_matrix) {
uint32_t btn_id = lv_btnmatrix_get_selected_btn(event_target);
if (btn_id == 3) { // Home
grbl_command_callbacks.on_failure = []() -> void {
ui_overlay_add_flash_message(FLASH_LEVEL_DANGER, "Homing failed or timed out");
};

if (event_target == ui_controls_laser_move_x_matrix) {
grbl_send_home_command(GRBL_AXIS_X);
grbl_command_callbacks.on_success = []() -> void {
ui_overlay_add_flash_message(FLASH_LEVEL_SUCCESS, "X axis homed");
};
ui_controls_lock_grbl_controls();
grbl_send_home_command(GRBL_AXIS_X, grbl_command_callbacks);
} else if (event_target == ui_controls_laser_move_y_matrix) {
grbl_send_home_command(GRBL_AXIS_Y);
grbl_command_callbacks.on_success = []() -> void {
ui_overlay_add_flash_message(FLASH_LEVEL_SUCCESS, "Y axis homed");
};
ui_controls_lock_grbl_controls();
grbl_send_home_command(GRBL_AXIS_Y, grbl_command_callbacks);
}
} else { // Relative move
float_t move_offset = pow10(abs((int)(btn_id - 3)) - 1) * (btn_id < 3 ? -1 : 1);
Expand All @@ -58,7 +91,13 @@ static void ui_controls_btnmatrix_handler(lv_event_t *e) {
move_target.axis_flags = GRBL_AXIS_Y;
move_target.y = move_offset;
}
grbl_send_move_command(move_target, GRBL_MOVE_MODE_RELATIVE);

grbl_command_callbacks.on_failure = []() -> void {
ui_overlay_add_flash_message(FLASH_LEVEL_DANGER, "Move command failed or timed out");
};

ui_controls_lock_grbl_controls();
grbl_send_move_command(move_target, GRBL_MOVE_MODE_RELATIVE, grbl_command_callbacks);
}
}
}
Expand All @@ -71,13 +110,37 @@ static void ui_controls_button_handler(lv_event_t *e) {

lv_obj_t *event_target = lv_event_get_target(e);

// Detect when GRBL command are fully process to release UI
GrblCommandCallbacks grbl_command_callbacks = GrblCommandCallbacks();
grbl_command_callbacks.on_finished = []() -> void {
ui_controls_notify_update(CONTROLS_UPDATE_GRBL_COMMMAND_ENDED);
};

if (event_target == NULL) {
// Should never happen
return;
} else if (event_target == ui_controls_laser_home_button) {
grbl_send_home_command(GRBL_AXIS_X | GRBL_AXIS_Y);
grbl_command_callbacks.on_failure = []() -> void {
ui_overlay_add_flash_message(FLASH_LEVEL_DANGER, "Homing failed or timed out");
};

grbl_command_callbacks.on_success = []() -> void {
ui_overlay_add_flash_message(FLASH_LEVEL_SUCCESS, "Homing done");
};

ui_controls_lock_grbl_controls();
grbl_send_home_command(GRBL_AXIS_X | GRBL_AXIS_Y, grbl_command_callbacks);
} else if (event_target == ui_controls_laser_disable_steppers_button) {
grbl_send_message("$MD");
grbl_command_callbacks.on_failure = []() -> void {
ui_overlay_add_flash_message(FLASH_LEVEL_DANGER, "Could not disable steppers");
};

grbl_command_callbacks.on_success = []() -> void {
ui_overlay_add_flash_message(FLASH_LEVEL_SUCCESS, "Steppers are now disabled");
};

ui_controls_lock_grbl_controls();
grbl_send_message("$MD", false, GRBL_ACK_DEFAULT_TIMEOUT_MS, grbl_command_callbacks);
}
}

Expand Down Expand Up @@ -319,6 +382,7 @@ void ui_controls_update(bool initialize) {

uint8_t pending_updates = xEventGroupGetBits(ui_controls_event_group);
bool pending_grbl_report_update = initialize || ((pending_updates & CONTROLS_UPDATE_GRBL_REPORT) != 0);
bool grbl_command_ended_update = initialize || ((pending_updates & CONTROLS_UPDATE_GRBL_COMMMAND_ENDED) != 0);

if (pending_grbl_report_update) {
TAKE_MUTEX(grbl_last_report_mutex)
Expand All @@ -341,6 +405,11 @@ void ui_controls_update(bool initialize) {
xEventGroupClearBits(ui_controls_event_group, CONTROLS_UPDATE_GRBL_REPORT);
}

if (grbl_command_ended_update) {
ui_controls_unlock_grbl_controls();
xEventGroupClearBits(ui_controls_event_group, CONTROLS_UPDATE_GRBL_COMMMAND_ENDED);
}

// Update relays state on an interval
static unsigned long last_update = 0;
unsigned long current_time = millis();
Expand Down

0 comments on commit 288e050

Please sign in to comment.