Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
efe9a7c8eThis file is pinned to the selected release. Download the original to open it in your design tool.
// marlin_server.cpp
#include "marlin_server.h"
#include <stdarg.h>
#include "dbg.h"
#include "app.h"
#include "bsod.h"
#include "cmsis_os.h"
#include "../Marlin/src/lcd/extensible_ui/ui_api.h"
#include "../Marlin/src/gcode/queue.h"
#include "../Marlin/src/gcode/parser.h"
#include "../Marlin/src/module/planner.h"
#include "../Marlin/src/module/stepper.h"
#include "../Marlin/src/module/temperature.h"
#include "../Marlin/src/module/probe.h"
#include "../Marlin/src/module/configuration_store.h"
#include "../Marlin/src/module/printcounter.h"
#include "../Marlin/src/feature/host_actions.h"
#include "../Marlin/src/feature/babystep.h"
#include "../Marlin/src/feature/pause.h"
#include "../Marlin/src/sd/cardreader.h"
#include "../Marlin/src/libs/nozzle.h"
#ifdef LCDSIM
#include "lcdsim.h"
#endif // LCDSIM
#define DBG _dbg1 //enabled level 1
//#define DBG(...)
#define DBG_XUI DBG //trace ExtUI events
//#define DBG_XUI(...) //disable trace
//#define DBG_REQ DBG //trace requests
#define DBG_REQ(...) //disable trace
#define DBG_HOST DBG //trace host_actions
//#define DBG_HOST(...) //disable trace
// host_actions definitions
#define HOST_PROMPT_LEN_MAX 32 // max 32 chars prompt text
#define HOST_BUTTON_LEN_MAX 16 // max 16 chars button text
#define HOST_BUTTON_CNT_MAX 4 // max 4 buttons
#pragma pack(push)
#pragma pack(1)
typedef struct _marlin_server_t
{
uint16_t flags; // server flags (MARLIN_SFLG)
uint64_t notify_events; // event notification mask
uint64_t notify_changes; // variable change notification mask
marlin_vars_t vars; // cached variables
char request[MARLIN_MAX_REQUEST];
int request_len;
uint64_t client_events[MARLIN_MAX_CLIENTS]; // client event mask
uint64_t client_changes[MARLIN_MAX_CLIENTS]; // client variable change mask
variant8_t client_events_notsent[MARLIN_MAX_CLIENTS][4]; // buffer for events that are not sent
uint32_t last_update; // last update tick count
uint8_t idle_cnt; // idle call counter
uint8_t pqueue_head; // copy of planner.block_buffer_head
uint8_t pqueue_tail; // copy of planner.block_buffer_tail
uint8_t pqueue; // calculated number of records in planner queue
uint8_t gqueue; // copy of queue.length - number of commands in gcode queue
uint32_t command; // actually running command
marlin_mesh_t mesh; // meshbed leveling
uint64_t mesh_point_notsent[MARLIN_MAX_CLIENTS];// mesh point mask (points that are not sent)
} marlin_server_t;
#pragma pack(pop)
PromptReason host_prompt_reason = PROMPT_NOT_DEFINED;
extern "C"
{
#ifndef _DEBUG
extern IWDG_HandleTypeDef hiwdg; //watchdog handle
#endif //_DEBUG
//-----------------------------------------------------------------------------
// variables
osThreadId marlin_server_task = 0; // task handle
osMessageQId marlin_server_queue = 0; // input queue (uint8_t)
osSemaphoreId marlin_server_sema = 0; // semaphore handle
marlin_server_t marlin_server; // server structure - initialize task to zero
marlin_server_idle_t* marlin_server_idle_cb = 0; // idle callback
//==========MSG_STACK===================
// top of the stack is at [0]
msg_stack_t msg_stack = {'\0', 0};
void _add_status_msg(const char * const popup_msg){
char message[MSG_MAX_LENGTH];
size_t str_size = strlen(popup_msg);
if(str_size >= MSG_MAX_LENGTH - 1){
strncpy(message, popup_msg, MSG_MAX_LENGTH - 1); // popup_msg is not always null-terminated...
message[MSG_MAX_LENGTH - 1] = '\0';
} else {
strncpy(message, popup_msg, str_size);
message[str_size] = '\0';
}
for(uint8_t i = msg_stack.count; i ; i--){
if(i == MSG_STACK_SIZE)
i--; // last place of the limited stack will be always overwritten
strncpy(msg_stack.msg_data[i], msg_stack.msg_data[i - 1], MSG_MAX_LENGTH);
}
strncpy(msg_stack.msg_data[0], message, MSG_MAX_LENGTH);
if(msg_stack.count < MSG_STACK_SIZE)
msg_stack.count++;
}
// host_actions vars
char host_prompt[HOST_PROMPT_LEN_MAX] = "";
char host_prompt_button[HOST_BUTTON_CNT_MAX][HOST_PROMPT_LEN_MAX];
uint8_t host_prompt_buttons = 0;
host_prompt_button_t host_prompt_button_clicked = HOST_PROMPT_BTN_None;
//-----------------------------------------------------------------------------
// external variables from marlin_client
extern osThreadId marlin_client_task[MARLIN_MAX_CLIENTS]; // task handles
extern osMessageQId marlin_client_queue[MARLIN_MAX_CLIENTS]; // input queue handles (uint32_t)
//-----------------------------------------------------------------------------
// forward declarations of private functions
int _send_notify_to_client(osMessageQId queue, variant8_t msg);
int _send_notify_event_to_client(int client_id, osMessageQId queue, uint8_t evt_id, uint32_t usr32, uint16_t usr16);
uint64_t _send_notify_events_to_client(int client_id, osMessageQId queue, uint64_t evt_msk);
void _send_notify_event(uint8_t evt_id, uint32_t usr32, uint16_t usr16);
int _send_notify_change_to_client(osMessageQId queue, uint8_t var_id, variant8_t var);
uint64_t _send_notify_changes_to_client(int client_id, osMessageQId queue, uint64_t var_msk);
void _server_update_gqueue(void);
void _server_update_pqueue(void);
uint64_t _server_update_vars(uint64_t force_update_msk);
int _process_server_request(char* request);
int _server_set_var(char* name_val_str);
//-----------------------------------------------------------------------------
// server side functions
void marlin_server_init(void)
{
memset(&marlin_server, 0, sizeof(marlin_server_t));
osMessageQDef(serverQueue, 64, uint8_t);
marlin_server_queue = osMessageCreate(osMessageQ(serverQueue), NULL);
osSemaphoreDef(serverSema);
marlin_server_sema = osSemaphoreCreate(osSemaphore(serverSema), 1);
marlin_server.flags = MARLIN_SFLG_PROCESS | MARLIN_SFLG_STARTED;
marlin_server.notify_events = MARLIN_EVT_MSK_DEF;
marlin_server.notify_changes = MARLIN_VAR_MSK_DEF;
marlin_server_task = osThreadGetId();
marlin_server.mesh.xc = 4;
marlin_server.mesh.yc = 4;
}
int marlin_server_cycle(void)
{
int count = 0;
int client_id;
uint64_t msk = 0;
uint64_t changes = 0;
osMessageQId queue;
osEvent ose;
uint32_t tick;
char ch;
if (marlin_server.flags & MARLIN_SFLG_PENDREQ)
{
if (_process_server_request(marlin_server.request))
{
marlin_server.request_len = 0;
count ++;
marlin_server.flags &= ~MARLIN_SFLG_PENDREQ;
}
}
if ((marlin_server.flags & MARLIN_SFLG_PENDREQ) == 0)
while ((ose = osMessageGet(marlin_server_queue, 0)).status == osEventMessage)
{
ch = (char)((uint8_t)(ose.value.v));
switch (ch)
{
case '\r':
case '\n':
ch = 0;
break;
}
if (marlin_server.request_len < MARLIN_MAX_REQUEST)
marlin_server.request[marlin_server.request_len++] = ch;
else
{
//TODO: request too long
marlin_server.request_len = 0;
}
if ((ch == 0) && (marlin_server.request_len > 1))
{
if (_process_server_request(marlin_server.request))
{
marlin_server.request_len = 0;
count ++;
}
else
{
marlin_server.flags |= MARLIN_SFLG_PENDREQ;
break;
}
}
}
// update gqueue (gcode queue)
_server_update_gqueue();
// update pqueue (planner queue)
_server_update_pqueue();
// update variables
tick = HAL_GetTick();
if ((tick - marlin_server.last_update) > MARLIN_UPDATE_PERIOD)
{
marlin_server.last_update = tick;
changes = _server_update_vars(marlin_server.notify_changes);
}
// send notifications
for (client_id = 0; client_id < MARLIN_MAX_CLIENTS; client_id++)
if ((queue = marlin_client_queue[client_id]) != 0)
{
marlin_server.client_changes[client_id] |= changes;
// send change notifications, clear bits for successful sent notification
if ((msk = marlin_server.client_changes[client_id]) != 0)
marlin_server.client_changes[client_id] &= ~_send_notify_changes_to_client(client_id, queue, msk);
// send events, clear bits for successful sent notification
if ((msk = marlin_server.client_events[client_id]) != 0)
marlin_server.client_events[client_id] &= ~_send_notify_events_to_client(client_id, queue, msk);
}
#ifndef _DEBUG
if ((marlin_server.flags & MARLIN_SFLG_PROCESS) == 0)
HAL_IWDG_Refresh(&hiwdg); // this prevents iwdg reset while processing disabled
#endif //_DEBUG
return count;
}
#define MARLIN_IDLE_CNT_BUSY 2
int marlin_server_loop(void)
{
if (marlin_server.idle_cnt >= MARLIN_IDLE_CNT_BUSY)
if (marlin_server.flags & MARLIN_SFLG_BUSY)
{
//_dbg("SVR: READY");
marlin_server.flags &= ~MARLIN_SFLG_BUSY;
_send_notify_event(MARLIN_EVT_Ready, 0, 0);
if (marlin_server.command != MARLIN_CMD_NONE)
{
_send_notify_event(MARLIN_EVT_CommandEnd, marlin_server.command, 0);
marlin_server.command = MARLIN_CMD_NONE;
}
}
marlin_server.idle_cnt = 0;
return marlin_server_cycle();
}
int marlin_server_idle(void)
{
if (marlin_server.idle_cnt < MARLIN_IDLE_CNT_BUSY)
marlin_server.idle_cnt++;
else
if ((marlin_server.flags & MARLIN_SFLG_BUSY) == 0)
{
//_dbg("SVR: BUSY");
marlin_server.flags |= MARLIN_SFLG_BUSY;
_send_notify_event(MARLIN_EVT_Busy, 0, 0);
if (parser.command_letter == 'G')
switch (parser.codenum)
{
case 28:
case 29:
marlin_server.command = MARLIN_CMD_G + parser.codenum;
break;
}
else if (parser.command_letter == 'M')
switch (parser.codenum)
{
case 600:
case 701:
case 702:
marlin_server.command = MARLIN_CMD_M + parser.codenum;
break;
}
if (marlin_server.command != MARLIN_CMD_NONE)
_send_notify_event(MARLIN_EVT_CommandBegin, marlin_server.command, 0);
}
return marlin_server_cycle();
}
int marlin_server_processing(void)
{
return (marlin_server.flags & MARLIN_SFLG_PROCESS)?1:0;
}
void marlin_server_start_processing(void)
{
marlin_server.flags |= MARLIN_SFLG_PROCESS;
_send_notify_event(MARLIN_EVT_StartProcessing, 0, 0);
}
void marlin_server_stop_processing(void)
{
marlin_server.flags &= ~MARLIN_SFLG_PROCESS;
//TODO: disable heaters and safe state
_send_notify_event(MARLIN_EVT_StopProcessing, 0, 0);
}
marlin_vars_t* marlin_server_vars(void)
{
return &(marlin_server.vars);
}
void marlin_server_update(uint64_t update)
{
int client_id;
osMessageQId queue;
_server_update_vars(update);
for (client_id = 0; client_id < MARLIN_MAX_CLIENTS; client_id++)
if ((queue = marlin_client_queue[client_id]) != 0)
marlin_server.client_changes[client_id] &= ~_send_notify_changes_to_client(client_id, queue, update);
// _dbg0("UPDATE");
}
void marlin_server_do_babystep_Z(float offs)
{
babystep.add_steps(Z_AXIS, offs * planner.settings.axis_steps_per_mm[Z_AXIS]);
babystep.task();
}
int marlin_server_enqueue_gcode(const char* gcode)
{
return queue.enqueue_one(gcode)?1:0;
}
void marlin_server_settings_save(void)
{
(void)settings.save();
}
void marlin_server_settings_load(void)
{
(void)settings.load();
}
void marlin_server_manage_heater(void)
{
thermalManager.manage_heater();
}
void marlin_server_quick_stop(void)
{
planner.quick_stop();
}
void marlin_server_print_abort(void)
{
wait_for_heatup = wait_for_user = false;
card.flag.abort_sd_printing = true;
print_job_timer.stop();
// planner.quick_stop();
// marlin_server_park_head();
// planner.synchronize();
// queue.inject_P("M125");
}
void marlin_server_print_pause(void)
{
card.pauseSDPrint();
print_job_timer.pause();
queue.inject_P("M125");
}
void marlin_server_print_resume(void)
{
wait_for_user = false;
queue.inject_P("M24");
}
void marlin_server_park_head(void)
{
xyz_pos_t park_point = NOZZLE_PARK_POINT;
nozzle.park(2, park_point);
}
//-----------------------------------------------------------------------------
// private functions
// send notify message (variant8_t) to client queue (called from server thread)
int _send_notify_to_client(osMessageQId queue, variant8_t msg)
{
//synchronization not necessary because only server thread can write to this queue
if (queue == 0) return 0;
if (osMessageAvailableSpace(queue) < 2) return 0;
osMessagePut(queue, *(((uint32_t*)(&msg))+0), osWaitForever);
osMessagePut(queue, *(((uint32_t*)(&msg))+1), osWaitForever);
return 1;
}
// send event notification to client (called from server thread)
int _send_notify_event_to_client(int client_id, osMessageQId queue, uint8_t evt_id, uint32_t usr32, uint16_t usr16)
{
variant8_t msg;
msg = variant8_user(usr32);
msg.usr16 = usr16;
msg.usr8 = evt_id;
return _send_notify_to_client(queue, msg);
}
// send event notification to client - multiple events (called from server thread)
// returns mask of succesfull sent events
uint64_t _send_notify_events_to_client(int client_id, osMessageQId queue, uint64_t evt_msk)
{
uint64_t sent = 0;
uint64_t msk = 1;
for (uint8_t evt_id = 0; evt_id < 64; evt_id++)
{
if (msk & evt_msk)
switch (evt_id)
{
// Idle and PrinterKilled events not used
//case MARLIN_EVT_Idle:
//case MARLIN_EVT_PrinterKilled:
// Events without arguments
case MARLIN_EVT_Startup:
case MARLIN_EVT_MediaInserted:
case MARLIN_EVT_MediaError:
case MARLIN_EVT_MediaRemoved:
case MARLIN_EVT_PrintTimerStarted:
case MARLIN_EVT_PrintTimerPaused:
case MARLIN_EVT_PrintTimerStopped:
case MARLIN_EVT_FilamentRunout:
case MARLIN_EVT_FactoryReset:
case MARLIN_EVT_LoadSettings:
case MARLIN_EVT_StoreSettings:
case MARLIN_EVT_StartProcessing:
case MARLIN_EVT_StopProcessing:
case MARLIN_EVT_Busy:
case MARLIN_EVT_Ready:
if (_send_notify_event_to_client(client_id, queue, evt_id, 0, 0))
sent |= msk; // event sent, set bit
break;
//case MARLIN_EVT_Error:
// StatusChanged event - one string argument
case MARLIN_EVT_StatusChanged:
if (_send_notify_event_to_client(client_id, queue, evt_id, 0, 0))
sent |= msk; // event sent, set bit
break;
// CommandBegin/End - one ui32 argument (CMD)
//case MARLIN_EVT_CommandBegin:
//case MARLIN_EVT_CommandEnd:
// if (_send_notify_event_to_client(client_id, queue, evt_id, 0, 0))
// sent |= msk; // event sent, set bit
// break;
//case MARLIN_EVT_PlayTone:
//case MARLIN_EVT_UserConfirmRequired:
case MARLIN_EVT_MeshUpdate:
if (marlin_server.mesh_point_notsent[client_id])
{
uint8_t x;
uint8_t y;
uint64_t mask = 1;
for (y = 0; y < marlin_server.mesh.yc; y++)
for (x = 0; x < marlin_server.mesh.xc; x++)
{
if (mask & marlin_server.mesh_point_notsent[client_id])
{
uint8_t index = x + marlin_server.mesh.xc * y;
float z = marlin_server.mesh.z[index];
uint32_t usr32 = variant8_flt(z).ui32;
uint16_t usr16 = x | ((uint16_t)y << 8);
if (_send_notify_event_to_client(client_id, queue, evt_id, usr32, usr16))
marlin_server.mesh_point_notsent[client_id] &= ~mask;
}
mask <<= 1;
}
if (marlin_server.mesh_point_notsent[client_id] == 0)
sent |= msk; // event sent, set bit
}
break;
case MARLIN_EVT_Acknowledge:
if (_send_notify_event_to_client(client_id, queue, evt_id, 0, 0))
sent |= msk; // event sent, set bit
break;
}
msk <<= 1;
}
return sent;
}
// send event notification to all clients (called from server thread)
void _send_notify_event(uint8_t evt_id, uint32_t usr32, uint16_t usr16)
{
if ((marlin_server.notify_events & ((uint64_t)1 << evt_id)) == 0) return;
for (int client_id = 0; client_id < MARLIN_MAX_CLIENTS; client_id++)
if (_send_notify_event_to_client(client_id, marlin_client_queue[client_id], evt_id, usr32, usr16) == 0)
{
marlin_server.client_events[client_id] |= ((uint64_t)1 << evt_id); // event not sent, set bit
if (evt_id == MARLIN_EVT_MeshUpdate)
{
uint8_t x = usr16 & 0xff; // x index
uint8_t y = usr16 >> 8; // y index
uint8_t index = x + marlin_server.mesh.xc * y; // index
uint64_t mask = ((uint64_t)1 << index); // mask
marlin_server.mesh_point_notsent[client_id] |= mask;
}
}
}
// send variable change notification to client (called from server thread)
int _send_notify_change_to_client(osMessageQId queue, uint8_t var_id, variant8_t var)
{
var.usr8 = var_id | MARLIN_USR8_VAR_FLG;
return _send_notify_to_client(queue, var);
}
// send variable change notification to client - multiple vars (called from server thread)
uint64_t _send_notify_changes_to_client(int client_id, osMessageQId queue, uint64_t var_msk)
{
variant8_t var;
uint64_t sent = 0;
uint64_t msk = 1;
for (uint8_t var_id = 0; var_id < 64; var_id++)
{
if (msk & var_msk)
{
var = marlin_vars_get_var(&(marlin_server.vars), var_id);
if (var.type != VARIANT8_EMPTY)
if (_send_notify_change_to_client(queue, var_id, var))
sent |= msk;
}
msk <<= 1;
}
return sent;
}
void _server_update_gqueue(void)
{
if (marlin_server.gqueue != queue.length)
{
marlin_server.gqueue = queue.length;
// _dbg("gqueue: %2d", marlin_server.gqueue);
}
}
void _server_update_pqueue(void)
{
if ((marlin_server.pqueue_head != planner.block_buffer_head) ||
(marlin_server.pqueue_tail != planner.block_buffer_tail))
{
marlin_server.pqueue_head = planner.block_buffer_head;
marlin_server.pqueue_tail = planner.block_buffer_tail;
marlin_server.pqueue = (marlin_server.pqueue_head >= marlin_server.pqueue_tail)?
(marlin_server.pqueue_head - marlin_server.pqueue_tail):
(BLOCK_BUFFER_SIZE + marlin_server.pqueue_head - marlin_server.pqueue_tail);
// _dbg("pqueue: %2d", marlin_server.pqueue);
}
}
// update server variables defined by 'update', returns changed variables mask (called from server thread)
uint64_t _server_update_vars(uint64_t update)
{
int i;
variant8_t v;
uint64_t changes = 0;
if (update == 0) return 0;
if (update & MARLIN_VAR_MSK(MARLIN_VAR_GQUEUE))
if (marlin_server.vars.gqueue != marlin_server.gqueue)
{
marlin_server.vars.gqueue = marlin_server.gqueue;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_GQUEUE);
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_PQUEUE))
if (marlin_server.vars.pqueue != marlin_server.pqueue)
{
marlin_server.vars.pqueue = marlin_server.pqueue;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_PQUEUE);
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_MOTION))
{
v.ui8 = 0;
if (stepper.axis_is_moving(X_AXIS)) v.ui8 |= 0x01;
if (stepper.axis_is_moving(Y_AXIS)) v.ui8 |= 0x02;
if (stepper.axis_is_moving(Z_AXIS)) v.ui8 |= 0x04;
if (stepper.axis_is_moving(E_AXIS)) v.ui8 |= 0x08;
if (marlin_server.vars.motion != v.ui8)
{
marlin_server.vars.motion = v.ui8;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_MOTION);
}
}
if (update & MARLIN_VAR_MSK_IPOS_XYZE)
{
for (i = 0; i < 4; i++)
if (update & MARLIN_VAR_MSK(MARLIN_VAR_IPOS_X + i))
{
v.i32 = stepper.position((AxisEnum)i);
if (marlin_server.vars.ipos[i] != v.i32)
{
marlin_server.vars.ipos[i] = v.i32;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_IPOS_X + i);
}
}
}
if (update & MARLIN_VAR_MSK_POS_XYZE)
{
for (i = 0; i < 4; i++)
if (update & MARLIN_VAR_MSK(MARLIN_VAR_POS_X + i))
{
v.flt = planner.get_axis_position_mm((AxisEnum)i);
if (marlin_server.vars.pos[i] != v.flt)
{
marlin_server.vars.pos[i] = v.flt;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_POS_X + i);
}
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_TEMP_NOZ))
{
v.flt = thermalManager.temp_hotend[0].celsius;
if (marlin_server.vars.temp_nozzle != v.flt)
{
marlin_server.vars.temp_nozzle = v.flt;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_TEMP_NOZ);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_TTEM_NOZ))
{
v.flt = thermalManager.temp_hotend[0].target;
if (marlin_server.vars.target_nozzle != v.flt)
{
marlin_server.vars.target_nozzle = v.flt;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_TTEM_NOZ);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_TEMP_BED))
{
v.flt = thermalManager.temp_bed.celsius;
if (marlin_server.vars.temp_bed != v.flt)
{
marlin_server.vars.temp_bed = v.flt;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_TEMP_BED);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_TTEM_BED))
{
v.flt = thermalManager.temp_bed.target;
if (marlin_server.vars.target_bed != v.flt)
{
marlin_server.vars.target_bed = v.flt;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_TTEM_BED);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_Z_OFFSET))
{
v.flt = probe_offset.z;
if (marlin_server.vars.z_offset != v.flt)
{
marlin_server.vars.z_offset = v.flt;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_Z_OFFSET);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_FANSPEED))
{
v.ui8 = thermalManager.fan_speed[0];
if (marlin_server.vars.fan_speed != v.ui8)
{
marlin_server.vars.fan_speed = v.ui8;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_FANSPEED);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_PRNSPEED))
{
v.ui16 = (unsigned)feedrate_percentage;
if (marlin_server.vars.print_speed != v.ui16)
{
marlin_server.vars.print_speed = v.ui16;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_PRNSPEED);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_FLOWFACT))
{
v.ui16 = (unsigned)planner.flow_percentage[0];
if (marlin_server.vars.flow_factor != v.ui16)
{
marlin_server.vars.flow_factor = v.ui16;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_FLOWFACT);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_WAITHEAT))
{
v.ui8 = wait_for_heatup?1:0;
if (marlin_server.vars.wait_heat != v.ui8)
{
marlin_server.vars.wait_heat = v.ui8;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_WAITHEAT);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_WAITUSER))
{
v.ui8 = wait_for_user?1:0;
if (marlin_server.vars.wait_user != v.ui8)
{
marlin_server.vars.wait_user = v.ui8;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_WAITUSER);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_SD_PRINT))
{
v.ui8 = card.flag.sdprinting?1:0;
if (marlin_server.vars.sd_printing != v.ui8)
{
marlin_server.vars.sd_printing = v.ui8;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_SD_PRINT);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_SD_PDONE))
{
v.ui8 = card.percentDone();
if (marlin_server.vars.sd_percent_done != v.ui8)
{
marlin_server.vars.sd_percent_done = v.ui8;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_SD_PDONE);
}
}
if (update & MARLIN_VAR_MSK(MARLIN_VAR_DURATION))
{
v.ui32 = print_job_timer.duration();
if (marlin_server.vars.print_duration != v.ui32)
{
marlin_server.vars.print_duration = v.ui32;
changes |= MARLIN_VAR_MSK(MARLIN_VAR_DURATION);
}
}
return changes;
}
// process request on server side
int _process_server_request(char* request)
{
int processed = 0;
uint64_t msk;
float offs;
int ival;
int client_id = *(request++) - '0';
if ((client_id < 0) || (client_id >= MARLIN_MAX_CLIENTS)) return 1;
DBG_REQ("SRV: REQ %c%s", '0' + client_id, request);
if (strncmp("!g ", request, 3) == 0)
{
processed = marlin_server_enqueue_gcode(request + 3);
}
else if (strcmp("!start", request) == 0)
{
marlin_server_start_processing();
processed = 1;
}
else if (strcmp("!stop", request) == 0)
{
marlin_server_stop_processing();
processed = 1;
}
else if (strncmp("!var ", request, 5) == 0)
{
_server_set_var(request + 5);
processed = 1;
}
else if (sscanf(request, "!update %" PRIu64, &msk) == 1)
{
marlin_server_update(msk);
processed = 1;
}
else if (sscanf(request, "!babystep_Z %f", &offs) == 1)
{
marlin_server_do_babystep_Z(offs);
processed = 1;
}
else if (strcmp("!save", request) == 0)
{
marlin_server_settings_save();
processed = 1;
}
else if (strcmp("!load", request) == 0)
{
marlin_server_settings_load();
processed = 1;
}
else if (strcmp("!updt", request) == 0)
{
marlin_server_manage_heater();
processed = 1;
}
else if (strcmp("!qstop", request) == 0)
{
marlin_server_quick_stop();
processed = 1;
}
else if (strcmp("!pabort", request) == 0)
{
marlin_server_print_abort();
processed = 1;
}
else if (strcmp("!ppause", request) == 0)
{
marlin_server_print_pause();
processed = 1;
}
else if (strcmp("!presume", request) == 0)
{
marlin_server_print_resume();
processed = 1;
}
else if (strcmp("!park", request) == 0)
{
marlin_server_park_head();
processed = 1;
}
else if (sscanf(request, "!hclick %d", &ival) == 1)
{
host_prompt_button_clicked = (host_prompt_button_t)ival;
processed = 1;
}
if (processed)
_send_notify_event_to_client(client_id, marlin_client_queue[client_id], MARLIN_EVT_Acknowledge, 0, 0);
return processed;
}
// set variable from string request
int _server_set_var(char* name_val_str)
{
int var_id;
char* val_str = strchr(name_val_str, ' ');
*(val_str++) = 0;
if ((var_id = marlin_vars_get_id_by_name(name_val_str)) >= 0)
{
if (marlin_vars_str_to_value(&(marlin_server.vars), var_id, val_str) == 1)
{
switch (var_id)
{
case MARLIN_VAR_TTEM_NOZ:
thermalManager.setTargetHotend(marlin_server.vars.target_nozzle, 0);
break;
case MARLIN_VAR_TTEM_BED:
thermalManager.setTargetBed(marlin_server.vars.target_bed);
break;
case MARLIN_VAR_Z_OFFSET:
probe_offset.z = marlin_server.vars.z_offset;
break;
case MARLIN_VAR_FANSPEED:
thermalManager.set_fan_speed(0, marlin_server.vars.fan_speed);
break;
case MARLIN_VAR_PRNSPEED:
feedrate_percentage = (int16_t)marlin_server.vars.print_speed;
break;
case MARLIN_VAR_FLOWFACT:
planner.flow_percentage[0] = (int16_t)marlin_server.vars.flow_factor;
break;
case MARLIN_VAR_WAITHEAT:
wait_for_heatup = marlin_server.vars.wait_heat?true:false;
break;
case MARLIN_VAR_WAITUSER:
wait_for_user = marlin_server.vars.wait_user?true:false;
break;
}
}
}
// _dbg("_server_set_var %d %s %s", var_id, name_val_str, val_str);
return 1;
}
} // extern "C"
//-----------------------------------------------------------------------------
// ExtUI event handlers
namespace ExtUI
{
void onStartup()
{
DBG_XUI("XUI: onStartup");
_send_notify_event(MARLIN_EVT_Startup, 0, 0);
}
void onIdle()
{
marlin_server_idle();
if (marlin_server_idle_cb)
marlin_server_idle_cb();
}
void onPrinterKilled(PGM_P const msg, PGM_P const component)
{
//_dbg("onPrinterKilled %s", msg);
temp_error(msg, component);
}
void onMediaInserted()
{
DBG_XUI("XUI: onMediaInserted");
_send_notify_event(MARLIN_EVT_MediaInserted, 0, 0);
}
void onMediaError()
{
DBG_XUI("XUI: onMediaError");
_send_notify_event(MARLIN_EVT_MediaError, 0, 0);
}
void onMediaRemoved()
{
DBG_XUI("XUI: onMediaRemoved");
_send_notify_event(MARLIN_EVT_MediaRemoved, 0, 0);
}
void onPlayTone(const uint16_t frequency, const uint16_t duration)
{
DBG_XUI("XUI: onPlayTone");
_send_notify_event(MARLIN_EVT_PlayTone, frequency, duration);
}
void onPrintTimerStarted()
{
DBG_XUI("XUI: onPrintTimerStarted");
_send_notify_event(MARLIN_EVT_PrintTimerStarted, 0, 0);
}
void onPrintTimerPaused()
{
DBG_XUI("XUI: onPrintTimerPaused");
_send_notify_event(MARLIN_EVT_PrintTimerPaused, 0, 0);
}
void onPrintTimerStopped()
{
DBG_XUI("XUI: onPrintTimerStopped");
_send_notify_event(MARLIN_EVT_PrintTimerStopped, 0, 0);
}
void onFilamentRunout(const extruder_t extruder)
{
DBG_XUI("XUI: onFilamentRunout");
_send_notify_event(MARLIN_EVT_FilamentRunout, 0, 0);
}
void onUserConfirmRequired(const char * const msg)
{
DBG_XUI("XUI: onUserConfirmRequired: %s", msg);
_send_notify_event(MARLIN_EVT_UserConfirmRequired, 0, 0);
}
void onStatusChanged(const char * const msg)
{
DBG_XUI("XUI: onStatusChanged: %s", msg);
_send_notify_event(MARLIN_EVT_StatusChanged, 0, 0);
if (strcmp(msg, "Prusa-mini Ready.") == 0)
{ } //TODO
else if (strcmp(msg, "TMC CONNECTION ERROR") == 0)
_send_notify_event(MARLIN_EVT_Error, MARLIN_ERR_TMCDriverError, 0);
else if (strcmp(msg, "Probing failed") == 0)
_send_notify_event(MARLIN_EVT_Error, MARLIN_ERR_ProbingFailed, 0);
else {
if(msg && msg[0] != 0){ //empty message filter
//TODO: selective filter
_add_status_msg(msg);
_send_notify_event(MARLIN_EVT_Message, 0, 0);
}
}
}
void onFactoryReset()
{
DBG_XUI("XUI: onFactoryReset");
_send_notify_event(MARLIN_EVT_FactoryReset, 0, 0);
}
void onLoadSettings(char const*)
{
DBG_XUI("XUI: onLoadSettings");
_send_notify_event(MARLIN_EVT_LoadSettings, 0, 0);
}
void onStoreSettings(char *)
{
DBG_XUI("XUI: onStoreSettings");
_send_notify_event(MARLIN_EVT_StoreSettings, 0, 0);
}
void onConfigurationStoreWritten(bool success)
{
DBG_XUI("XUI: onConfigurationStoreWritten");
}
void onConfigurationStoreRead(bool success)
{
DBG_XUI("XUI: onConfigurationStoreRead");
}
void onMeshUpdate(const uint8_t xpos, const uint8_t ypos, const float zval)
{
DBG_XUI("XUI: onMeshUpdate x: %u, y: %u, z: %.2f", xpos, ypos, (double)zval);
uint8_t index = xpos + marlin_server.mesh.xc * ypos;
uint32_t usr32 = variant8_flt(zval).ui32;
uint16_t usr16 = xpos | ((uint16_t)ypos << 8);
marlin_server.mesh.z[index] = zval;
_send_notify_event(MARLIN_EVT_MeshUpdate, usr32, usr16);
}
}
//-----------------------------------------------------------------------------
// host_actions callbacks
void host_action(const char * const pstr, const bool eol)
{
}
//Marlin kill handler
// message reported to serial: MSG_ERR_KILLED "Printer halted. kill() called!"
// known kill calls:
// kill()
// Marlin/src/gcode/control/M108_M112_M410.cpp, line 44
// Marlin/src/gcode/queue.cpp, line 666
// Marlin/src/module/temperature.cpp, line 921
// Marlin/src/module/temperature.cpp, line 1141
// Marlin/src/Marlin.cpp, line 446
// Marlin/src/Marlin.cpp, line 514
// kill("Driver error")
// Marlin/src/feature/tmc_util.cpp, line 215
// kill(PSTR(MSG_ERR_PROBING_FAILED)) "Probing failed"
// Marlin/src/Marlin.cpp, line 401
// kill(PSTR(MSG_ERR_HOMING_FAILED)) "Homing failed"
// Marlin/src/module/endstops.cpp, line 290
// _temp_error(heater, PSTR(MSG_T_HEATING_FAILED), TEMP_ERR_PSTR(MSG_HEATING_FAILED_LCD, heater)) "Heating failed"
// Marlin/src/module/temperature.cpp, line 514
// _temp_error(e, PSTR(MSG_T_HEATING_FAILED), TEMP_ERR_PSTR(MSG_HEATING_FAILED_LCD, e)) "Heating failed"
// Marlin/src/module/temperature.cpp, line 959
// _temp_error(-1, PSTR(MSG_T_HEATING_FAILED), TEMP_ERR_PSTR(MSG_HEATING_FAILED_LCD, -1)) "Heating failed"
// Marlin/src/module/temperature.cpp, line 999
// _temp_error(-2, PSTR(MSG_T_HEATING_FAILED), TEMP_ERR_PSTR(MSG_HEATING_FAILED_LCD, -2)) "Heating failed"
// Marlin/src/module/temperature.cpp, line 1069
// _temp_error(heater, PSTR(MSG_T_THERMAL_RUNAWAY), TEMP_ERR_PSTR(MSG_THERMAL_RUNAWAY, heater)) "Thermal Runaway"
// Marlin/src/module/temperature.cpp, line 517
// _temp_error(heater_id, PSTR(MSG_T_THERMAL_RUNAWAY), TEMP_ERR_PSTR(MSG_THERMAL_RUNAWAY, heater_id)) "Thermal Runaway"
// Marlin/src/module/temperature.cpp, line 1914
// _temp_error(heater, PSTR(MSG_T_MAXTEMP), TEMP_ERR_PSTR(MSG_ERR_MAXTEMP, heater)) "MAXTEMP triggered"
// Marlin/src/module/temperature.cpp, line 732
// _temp_error(heater, PSTR(MSG_T_MINTEMP), TEMP_ERR_PSTR(MSG_ERR_MINTEMP, heater)) "MINTEMP triggered"
// Marlin/src/module/temperature.cpp, line 736
// _temp_error(0, PSTR(MSG_REDUNDANCY), PSTR(MSG_ERR_REDUNDANT_TEMP)) "Heater switched off. Temperature difference between temp sensors is too high !"
// Marlin/src/module/temperature.cpp, line 968
void host_action_kill()
{
#ifdef LCDSIM
char text[85]; //max 4 lines of 20 chars + 4x '\n' + '\x00'
lcdsim_grab_text(text); //grab text from display buffer
bsod(text); //BSOD (endless loop with disabled interrupts)
#endif // LCDSIM
}
void host_action_safety_timer_expired()
{
DBG_HOST("host_action_safety_timer_expired");
_send_notify_event(MARLIN_EVT_SafetyTimerExpired, 0, 0);
}
void host_action_pause(const bool eol/*=true*/)
{
DBG_HOST("host_action_pause");
}
void host_action_resume()
{
DBG_HOST("host_action_resume");
}
void host_action_cancel()
{
DBG_HOST("host_action_cancel");
}
void host_action_paused(const bool eol/*=true*/)
{
DBG_HOST("host_action_paused");
}
void host_action_resumed()
{
DBG_HOST("host_action_resumed");
}
void host_response_handler(const uint8_t response)
{
DBG_HOST("host_response_handler %d", (int)response);
}
void host_action_prompt_begin(const char * const pstr, const bool eol)
{
DBG_HOST("host_action_prompt_begin '%s' %d", pstr, (int)eol);
strcpy(host_prompt, pstr);
host_prompt_buttons = 0;
}
void host_action_prompt_button(const char * const pstr)
{
DBG_HOST("host_action_prompt_button '%s'", pstr);
if (host_prompt_buttons < HOST_BUTTON_CNT_MAX)
{
strcpy(host_prompt_button[host_prompt_buttons], pstr);
host_prompt_buttons++;
}
}
void host_action_prompt_end()
{
DBG_HOST("host_action_prompt_end");
*host_prompt = 0;
host_prompt_buttons = 0;
}
void host_action_prompt_show()
{
DBG_HOST("host_action_prompt_show");
marlin_host_prompt_t prompt = {
marlin_host_prompt_by_text(host_prompt),
host_prompt_buttons,
{
marlin_host_prompt_button_by_text(host_prompt_button[0]),
marlin_host_prompt_button_by_text(host_prompt_button[1]),
marlin_host_prompt_button_by_text(host_prompt_button[2]),
marlin_host_prompt_button_by_text(host_prompt_button[3]),
}
};
int paused = 0;
uint32_t ui32 = marlin_host_prompt_encode(&prompt);
_send_notify_event(MARLIN_EVT_HostPrompt, ui32, 0);
switch (prompt.type)
{
case HOST_PROMPT_Paused:
paused = 1;
while (paused)
{
host_prompt_button_clicked = HOST_PROMPT_BTN_None;
while (host_prompt_button_clicked == HOST_PROMPT_BTN_None)
{
idle(); // call Marlin idle()
}
switch (host_prompt_button_clicked)
{
case HOST_PROMPT_BTN_Continue:
paused = 0;
break;
case HOST_PROMPT_BTN_PurgeMore:
do_pause_e_move(ADVANCED_PAUSE_PURGE_LENGTH, ADVANCED_PAUSE_PURGE_FEEDRATE);
paused = 0; //TODO: temporarily skip the loop because of unload UI
break;
default:
paused = 0;
break;
}
}
case HOST_PROMPT_LoadFilament:
wait_for_user = false; //hack
break;
case HOST_PROMPT_FilamentRunout:
break;
default:
break;
}
*host_prompt = 0;
host_prompt_buttons = 0;
}
void host_prompt_do(const PromptReason type, const char * const pstr, const char * const pbtn)
{
DBG_HOST("host_prompt_do %d '%s' '%s'", (int)type, pstr, pbtn);
}