From 180f9fa9fcf258c2c09de21ea61b2ffb207a5d02 Mon Sep 17 00:00:00 2001 From: Terje Io Date: Wed, 7 Sep 2022 21:08:11 +0200 Subject: [PATCH] Added `$RTC` system command for outputting or setting current real time clock date and time. Uses ISO8601 format. Driver developers: check the changelog! --- CMakeLists.txt | 2 +- README.md | 4 +- changelog.md | 32 ++++++++++++-- grbl.h | 2 +- grbllib.c | 2 +- hal.h | 10 +++-- limits.c => machine_limits.c | 4 +- limits.h => machine_limits.h | 8 ++-- motion_control.c | 2 +- nuts_bolts.c | 64 ++++++++++++++++++++++++++- nuts_bolts.h | 3 ++ plugins.h | 1 + protocol.c | 2 +- report.c | 40 +++++++++++++++-- report.h | 3 ++ settings.c | 2 +- settings.h | 7 ++- stream.h | 4 +- system.c | 26 ++++++++++- vfs.c | 83 ++++++++++++++++++++++++++++++------ vfs.h | 25 +++++++++-- 21 files changed, 282 insertions(+), 44 deletions(-) rename limits.c => machine_limits.c (99%) rename limits.h => machine_limits.h (87%) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd74df4..db8fcc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ target_sources(grbl INTERFACE ${CMAKE_CURRENT_LIST_DIR}/coolant_control.c ${CMAKE_CURRENT_LIST_DIR}/nvs_buffer.c ${CMAKE_CURRENT_LIST_DIR}/gcode.c - ${CMAKE_CURRENT_LIST_DIR}/limits.c + ${CMAKE_CURRENT_LIST_DIR}/machine_limits.c ${CMAKE_CURRENT_LIST_DIR}/motion_control.c ${CMAKE_CURRENT_LIST_DIR}/my_plugin.c ${CMAKE_CURRENT_LIST_DIR}/nuts_bolts.c diff --git a/README.md b/README.md index a726af0..2340a91 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ It has been written to complement grblHAL and has features such as proper keyboa --- -Latest build date is 20220904, see the [changelog](changelog.md) for details. +Latest build date is 20220907, see the [changelog](changelog.md) for details. __NOTE:__ A settings reset will be performed on an update for versions earlier than 20211122. Backup and restore of settings is recommended. __IMPORTANT!__ A new setting has been introduced for ganged axes motors in version 20211121. I have only bench tested this for a couple of drivers, correct function should be verified after updating by those who have more than three motors configured. @@ -84,4 +84,4 @@ List of Supported G-Codes: Some [plugins](https://github.com/grblHAL/plugins) implements additional M-codes. --- -2022-09-04 +2022-09-07 diff --git a/changelog.md b/changelog.md index 0ccf718..b9927a9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,20 +1,46 @@ ## grblHAL changelog +20220905: + +Core: + +* Added `$RTC` system command for outputting or setting current real time clock date and time. Uses ISO8601 format. +__Driver developers:__ +_grbl/limits.h_ has been renamed to _grbl/machine_limits.h_ (along with the _.c_ counterpart). +[hal.enumerate_pins](http://svn.io-engineering.com/grblHAL/html/structgrbl__hal__t.html#a661c9aa458a2e6fc5fb1657e121999a3) and the associated [callback function](http://svn.io-engineering.com/grblHAL/html/hal_8h.html#a41e902cfc3da615f9494aba956d895ba) parameter has a new signature, a void pointer has been added. Driver implementations should pass this on to the callback. +The HAL version number has been increased to 10 due to this, update _driver.c_ to match! + +Plugins: + +* WebUI: added many ESP commands to v3 command handler, some code refactoring. Still WIP. + +* Networking: some minor bug fixes. + +Drivers: + +* All: updated for _grbl/limits.h_ name change and HAL version number increase. + +* iMXRT1062, STM32F4xx, STM32F7xx and ESP32: Added RTC support. + +* ESP32: WebUI backend support improved. + +--- + 20220904: Core: -* Added optional RTC (Real Time Clock) support to the HAL. VFS improvements. +* Added optional RTC \(Real Time Clock\) support to the HAL. VFS improvements. Plugins: -* Networking: improved websocket protocol handling. +* Networking: improved websocket subprotocol handling. * WebUI: separated command handlers for v2 and v3 and improved detection of v3 clients. Now sets RTC from ESP800 if HAL allows. Drivers: -* RP2040: Added RTC support++. +* RP2040: Added RTC support++. * iMXRT1062: updated uSDFS patch - needed for VFS changes. diff --git a/grbl.h b/grbl.h index cebad7c..51c4b3d 100644 --- a/grbl.h +++ b/grbl.h @@ -34,7 +34,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20220904 +#define GRBL_BUILD 20220906 // The following symbols are set here if not already set by the compiler or in config.h // Do NOT change here! diff --git a/grbllib.c b/grbllib.c index 0997a67..576a42a 100644 --- a/grbllib.c +++ b/grbllib.c @@ -30,7 +30,7 @@ #include "tool_change.h" #include "override.h" #include "protocol.h" -#include "limits.h" +#include "machine_limits.h" #include "report.h" #include "state_machine.h" #include "nvs_buffer.h" diff --git a/hal.h b/hal.h index 53c5aa8..e434052 100644 --- a/hal.h +++ b/hal.h @@ -40,7 +40,7 @@ #include "ioports.h" #include "plugins.h" -#define HAL_VERSION 9 +#define HAL_VERSION 10 /// Bitmap flags for driver capabilities, to be set by driver in driver_init(), flags may be cleared after to switch off option. typedef union { @@ -104,14 +104,15 @@ typedef struct { /*! \brief Pointer to callback function for pin enumerations. \param pin pointer to the \a xbar_t structure holding the pin information. */ -typedef void (*pin_info_ptr)(xbar_t *pin); +typedef void (*pin_info_ptr)(xbar_t *pin, void *data); /*! \brief Pointer to function for enumerate pin information. \param low_level true if low level information is required, false if used for reporting. \param callback pointer to a \a pin_info_ptr type function to receive the pin information. The callback function will be called for each pin. */ -typedef void (*enumerate_pins_ptr)(bool low_level, pin_info_ptr callback); +typedef void (*enumerate_pins_ptr)(bool low_level, pin_info_ptr callback, void *data); + /************* * Coolant * @@ -359,6 +360,7 @@ typedef struct { probe_connected_toggle_ptr connected_toggle; //!< Optional handler for toggling probe connected status. } probe_ptrs_t; + /******************************* * Tool selection and change * *******************************/ @@ -385,6 +387,7 @@ typedef struct { tool_change_ptr change; //!< Optional handler for executing a tool change (M6). } tool_ptrs_t; + /***************** * User M-code * *****************/ @@ -439,6 +442,7 @@ typedef struct { user_mcode_execute_ptr execute; //!< Handler for executing a user defined M-code. } user_mcode_ptrs_t; + /******************* * Encoder input * *******************/ diff --git a/limits.c b/machine_limits.c similarity index 99% rename from limits.c rename to machine_limits.c index 8d82c57..c6bf96d 100644 --- a/limits.c +++ b/machine_limits.c @@ -1,5 +1,5 @@ /* - limits.c - code pertaining to limit-switches and performing the homing cycle + machine_limits.c - code pertaining to limit-switches and performing the homing cycle Part of grblHAL @@ -29,7 +29,7 @@ #include "nuts_bolts.h" #include "protocol.h" #include "motion_control.h" -#include "limits.h" +#include "machine_limits.h" #include "tool_change.h" #include "state_machine.h" #ifdef KINEMATICS_API diff --git a/limits.h b/machine_limits.h similarity index 87% rename from limits.h rename to machine_limits.h index 1d234e1..7719372 100644 --- a/limits.h +++ b/machine_limits.h @@ -1,9 +1,9 @@ /* - limits.h - code pertaining to limit-switches and performing the homing cycle + machine_limits.h - code pertaining to limit-switches and performing the homing cycle Part of grblHAL - Copyright (c) 2017-2018 Terje Io + Copyright (c) 2017-2022 Terje Io Copyright (c) 2012-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -21,8 +21,8 @@ along with Grbl. If not, see . */ -#ifndef _LIMITS_H_ -#define _LIMITS_H_ +#ifndef _MACHINE_LIMITS_H_ +#define _MACHINE_LIMITS_H_ #include "nuts_bolts.h" diff --git a/motion_control.c b/motion_control.c index 73307b4..cb1e73a 100644 --- a/motion_control.c +++ b/motion_control.c @@ -32,7 +32,7 @@ #include "hal.h" #include "nuts_bolts.h" #include "protocol.h" -#include "limits.h" +#include "machine_limits.h" #include "state_machine.h" #include "motion_control.h" #include "tool_change.h" diff --git a/nuts_bolts.c b/nuts_bolts.c index 3417877..61fd90c 100644 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2017-2021 Terje Io + Copyright (c) 2017-2022 Terje Io Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "hal.h" #include "protocol.h" @@ -263,6 +265,66 @@ float convert_delta_vector_to_unit_vector (float *vector) return magnitude; } +// parse ISO8601 datetime: YYYY-MM-DDTHH:MM:SSZxxx +struct tm *get_datetime (const char *s) +{ + static struct tm dt; + PROGMEM static const uint8_t mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + char *s1 = (char *)s, c; + uint_fast16_t idx = 0, value = 0; + + memset(&dt, 0, sizeof(struct tm)); + dt.tm_year = dt.tm_mon = dt.tm_mday = dt.tm_hour = dt.tm_min = dt.tm_sec = -1; + + do { + c = *s1++; + + if(isdigit(c)) + value = (value * 10) + c - '0'; + + else if(!(c == '-' || c == ':' || c == 'T' || c == 'Z' || c == '\0')) + break; + + else { + switch(idx) { + case 0: + if(c == '-' && value >= 1970 && value <= 2099) + dt.tm_year = value - 1900; + break; + + case 1: + if(c == '-' && value >= 1 && value <= 12) + dt.tm_mon = value - 1; + break; + + case 2: + if(c == 'T' && value >= 1 && value <= (mdays[dt.tm_mon >= 0 ? dt.tm_mon : 0] + (dt.tm_mon == 1 && dt.tm_year != 100 && (dt.tm_year % 4) == 0 ? 1 : 0))) + dt.tm_mday = value; + break; + + case 3: + if(c == ':' && value <= 23) + dt.tm_hour = value; + break; + + case 4: + if(c == ':' && value <= 59) + dt.tm_min = value; + break; + + case 5: + if((c == 'Z' || c == '\0') && value <= 59) + dt.tm_sec = value; + break; + } + idx++; + value = 0; + } + } while(c); + + return (dt.tm_year | dt.tm_mon | dt.tm_mday | dt.tm_hour | dt.tm_min | dt.tm_sec) > 0 ? &dt : NULL; +} // calculate checksum byte for data uint8_t calc_checksum (uint8_t *data, uint32_t size) { diff --git a/nuts_bolts.h b/nuts_bolts.h index faecef4..ffb8ae8 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -206,6 +206,9 @@ void delay_sec(float seconds, delaymode_t mode); float convert_delta_vector_to_unit_vector(float *vector); +// parse ISO8601 datetime +struct tm *get_datetime (const char *s); + // calculate checksum byte for data uint8_t calc_checksum (uint8_t *data, uint32_t size); diff --git a/plugins.h b/plugins.h index 2034102..ed68b07 100644 --- a/plugins.h +++ b/plugins.h @@ -67,6 +67,7 @@ typedef union { typedef char ssid_t[65]; typedef char password_t[33]; typedef char hostname_t[33]; +typedef char sntp_server_t[129]; // URI typedef struct { char ip[16]; diff --git a/protocol.c b/protocol.c index 21dfff4..44fd60f 100644 --- a/protocol.c +++ b/protocol.c @@ -32,7 +32,7 @@ #include "motion_control.h" #include "sleep.h" #include "protocol.h" -#include "limits.h" +#include "machine_limits.h" #ifndef RT_QUEUE_SIZE #define RT_QUEUE_SIZE 8 // must be a power of 2 diff --git a/report.c b/report.c index 177ed3b..5116518 100644 --- a/report.c +++ b/report.c @@ -36,7 +36,7 @@ #include "hal.h" #include "report.h" #include "nvs_buffer.h" -#include "limits.h" +#include "machine_limits.h" #include "state_machine.h" #include "regex.h" @@ -842,6 +842,8 @@ void report_execute_startup_message (char *line, status_code_t status_code) // Prints build info line void report_build_info (char *line, bool extended) { + char buf[100]; + hal.stream.write("[VER:" GRBL_VERSION "."); hal.stream.write(uitoa(GRBL_BUILD)); hal.stream.write(":"); @@ -997,6 +999,9 @@ void report_build_info (char *line, bool extended) strcat(buf, "SED,"); #endif + if(hal.rtc.get_datetime) + strcat(buf, "RTC,"); + #ifdef PID_LOG strcat(buf, "PID,"); #endif @@ -2072,7 +2077,7 @@ static const char *get_pinname (pin_function_t function) return name ? name : "N/A"; } -static void report_pin (xbar_t *pin) +static void report_pin (xbar_t *pin, void *data) { hal.stream.write("[PIN:"); if(pin->port) @@ -2090,11 +2095,40 @@ static void report_pin (xbar_t *pin) status_code_t report_pins (sys_state_t state, char *args) { if(hal.enumerate_pins) - hal.enumerate_pins(false, report_pin); + hal.enumerate_pins(false, report_pin, NULL); return Status_OK; } +static void print_uito2a (char *prefix, uint32_t v) +{ + hal.stream.write(prefix); + if(v < 10) + hal.stream.write("0"); + hal.stream.write(uitoa(v)); +} + +status_code_t report_time (void) +{ + bool ok = false; + + if(hal.rtc.get_datetime) { + struct tm time; + if((ok = !!hal.rtc.get_datetime(&time))) { + hal.stream.write("[RTC:"); + hal.stream.write(uitoa(time.tm_year + 1900)); + print_uito2a("-", time.tm_mon + 1); + print_uito2a("-", time.tm_mday); + print_uito2a("T", time.tm_hour); + print_uito2a(":", time.tm_min); + print_uito2a(":", time.tm_sec); + hal.stream.write("]" ASCII_EOL); + } + } + + return ok ? Status_OK : Status_InvalidStatement; +} + void report_pid_log (void) { #ifdef PID_LOG diff --git a/report.h b/report.h index 9fda85b..e078fdb 100644 --- a/report.h +++ b/report.h @@ -117,6 +117,9 @@ status_code_t report_spindle_data (sys_state_t state, char *args); // Prints pin assignments status_code_t report_pins (sys_state_t state, char *args); +// Prints current RTC datetime in ISO8601 format (when available) +status_code_t report_time (void); + // Prints current PID log. void report_pid_log (void); diff --git a/settings.c b/settings.c index e4f10ac..e54a3a6 100644 --- a/settings.c +++ b/settings.c @@ -29,7 +29,7 @@ #include "hal.h" #include "defaults.h" -#include "limits.h" +#include "machine_limits.h" #include "nvs_buffer.h" #include "tool_change.h" #include "state_machine.h" diff --git a/settings.h b/settings.h index 98135c0..da52482 100644 --- a/settings.h +++ b/settings.h @@ -197,6 +197,11 @@ typedef enum { Setting_AdminPassword = 330, Setting_UserPassword = 331, + Setting_NTPServerURI = 332, + Setting_NTPServerURI_2 = 333, + Setting_NTPServerURI_3 = 334, + Setting_Timezone = 335, + Setting_DSTActive = 336, Setting_TrinamicDriver = 338, Setting_TrinamicHoming = 339, @@ -344,7 +349,7 @@ typedef union { struct { uint16_t report_inches :1, restore_overrides :1, - unused0 :1, + dst_active :1, // Daylight savings time sleep_enable :1, disable_laser_during_hold :1, force_initialization_alarm :1, diff --git a/stream.h b/stream.h index a57240f..de0aa28 100644 --- a/stream.h +++ b/stream.h @@ -82,8 +82,8 @@ typedef enum { StreamType_Bluetooth, StreamType_Telnet, StreamType_WebSocket, - StreamType_SDCard, - StreamType_FlashFs, + StreamType_SDCard, // deprecated, use StreamType_File instead + StreamType_File = StreamType_SDCard, StreamType_Redirected, StreamType_Null } stream_type_t; diff --git a/system.c b/system.c index b9a04f7..b13816d 100644 --- a/system.c +++ b/system.c @@ -28,7 +28,7 @@ #include "protocol.h" #include "tool_change.h" #include "state_machine.h" -#include "limits.h" +#include "machine_limits.h" #ifdef KINEMATICS_API #include "kinematics.h" #endif @@ -79,6 +79,7 @@ static status_code_t settings_reset (sys_state_t state, char *args); static status_code_t output_startup_lines (sys_state_t state, char *args); static status_code_t set_startup_line0 (sys_state_t state, char *args); static status_code_t set_startup_line1 (sys_state_t state, char *args); +static status_code_t rtc_action (sys_state_t state, char *args); #ifdef DEBUGOUT static status_code_t output_memmap (sys_state_t state, char *args); #endif @@ -232,6 +233,7 @@ PROGMEM static const sys_command_t sys_commands[] = { { "LIM", true, report_current_limit_state }, { "SD", false, report_spindle_data }, { "SR", false, spindle_reset_data }, + { "RTC", false, rtc_action }, #ifdef DEBUGOUT { "Q", true, output_memmap }, #endif @@ -286,6 +288,10 @@ void system_command_help (void) hal.stream.write("$PINS - enumerate pin bindings" ASCII_EOL); hal.stream.write("$LEV - output last control signal events" ASCII_EOL); hal.stream.write("$LIM - output current limit pins state" ASCII_EOL); + if(hal.rtc.get_datetime) { + hal.stream.write("$RTC - output current time" ASCII_EOL); + hal.stream.write("$RTC= - set current time" ASCII_EOL); + } #ifndef NO_SETTINGS_DESCRIPTIONS hal.stream.write("$SED= - output settings description for setting " ASCII_EOL); #endif @@ -869,6 +875,24 @@ static status_code_t set_startup_line1 (sys_state_t state, char *args) return set_startup_line(state, args, 1); } +static status_code_t rtc_action (sys_state_t state, char *args) +{ + status_code_t retval = Status_OK; + + if(args) { + + struct tm *time = get_datetime(args); + + if(time) + hal.rtc.set_datetime(time); + else + retval = Status_BadNumberFormat; + } else + retval = report_time(); + + return retval; +} + #ifdef DEBUGOUT #include "nvs_buffer.h" diff --git a/vfs.c b/vfs.c index 47fc5a0..105d9f6 100644 --- a/vfs.c +++ b/vfs.c @@ -101,6 +101,9 @@ static char *fs_getcwd (char *buf, size_t size) } static const vfs_t fs_null = { + .mode.directory = true, + .mode.hidden = true, + .mode.read_only = true, .fopen = fs_open, .fclose = fs_close, .fread = fs_read, @@ -412,19 +415,73 @@ bool vfs_unmount (const char *path) return true; } -#endif - -/* -struct tm *gmtime (const time_t *c_t) +vfs_drives_t *vfs_drives_open (void) { - static struct tm dummy = { - .tm_year = 70, - .tm_mon = 0, - .tm_mday = 1, - .tm_hour = 0, - .tm_min = 0 - }; + vfs_drives_t *handle; + vfs_mount_t *mount = &root; - return &dummy; + if((handle = malloc(sizeof(vfs_drives_t)))) { + + handle->mount = NULL; + do { + if(mount->vfs->mode.hidden) + mount = mount->next; + else + handle->mount = mount; + } while(mount && handle->mount == NULL); + + if(handle->mount == NULL) { + free(handle); + handle = NULL; + } + } + + return handle; } -*/ + +vfs_drive_t *vfs_drives_read (vfs_drives_t *handle) +{ + static vfs_drive_t drive; + + bool ok; + + if((ok = handle->mount != NULL)) { + + drive.name = handle->mount->vfs->fs_name; + drive.path = (const char *)handle->mount->path; + drive.mode = handle->mount->vfs->mode; + drive.fs = handle->mount->vfs; + + handle->mount = handle->mount->next; + + if(handle->mount) do { + if(!handle->mount->vfs->mode.hidden && handle->mount->vfs->fs_name) + break; + } while((handle->mount = handle->mount->next)); + } + + return ok ? &drive : NULL; +} + +void vfs_drives_close (vfs_drives_t *handle) +{ + free(handle); +} + +vfs_free_t *vfs_drive_getfree (vfs_drive_t *drive) +{ + static vfs_free_t free; + + const vfs_t *fs = drive->fs; + + return fs->fgetfree && fs->fgetfree(&free) ? &free : NULL; +} + +int vfs_drive_format (vfs_drive_t *drive) +{ + const vfs_t *fs = drive->fs; + + return fs->format ? fs->format() : -1; +} + +#endif diff --git a/vfs.h b/vfs.h index f6671a7..e01d14d 100644 --- a/vfs.h +++ b/vfs.h @@ -92,8 +92,8 @@ typedef struct { } vfs_dirent_t; typedef struct { - uint64_t size; - uint64_t used; + size_t size; + size_t used; } vfs_free_t; typedef struct { @@ -123,6 +123,7 @@ typedef int (*vfs_stat_ptr)(const char *filename, vfs_stat_t *st); typedef int (*vfs_utime_ptr)(const char *filename, struct tm *modified); typedef bool (*vfs_getfree_ptr)(vfs_free_t *free); +typedef int (*vfs_format_ptr)(void); typedef struct { @@ -147,6 +148,7 @@ typedef struct vfs_utime_ptr futime; vfs_getcwd_ptr fgetcwd; vfs_getfree_ptr fgetfree; + vfs_format_ptr format; } vfs_t; typedef struct vfs_mount @@ -156,6 +158,17 @@ typedef struct vfs_mount struct vfs_mount *next; } vfs_mount_t; +typedef struct { + vfs_mount_t *mount; +} vfs_drives_t; + +typedef struct { + const char *name; + const char *path; + vfs_st_mode_t mode; + const void *fs; +} vfs_drive_t; + extern int vfs_errno; char *vfs_fixpath (char *path); @@ -183,4 +196,10 @@ int vfs_stat (const char *filename, vfs_stat_t *st); int vfs_utime (const char *filename, struct tm *modified); vfs_free_t *vfs_fgetfree (const char *path); -#endif +vfs_drives_t *vfs_drives_open (void); +vfs_drive_t *vfs_drives_read (vfs_drives_t *handle); +void vfs_drives_close (vfs_drives_t *handle); +vfs_free_t *vfs_drive_getfree (vfs_drive_t *drive); +int vfs_drive_format (vfs_drive_t *drive); + +#endif // INCLUDE_VFS_H