Flags for MarlinSerial instance features (#21318)
This commit is contained in:
parent
368fcaee54
commit
f18da95d38
|
@ -27,11 +27,12 @@
|
||||||
// Useful macro for stopping the CPU on an unexpected condition
|
// Useful macro for stopping the CPU on an unexpected condition
|
||||||
// This is used like SERIAL_ECHOPAIR, that is: a key-value call of the local variables you want
|
// This is used like SERIAL_ECHOPAIR, that is: a key-value call of the local variables you want
|
||||||
// to dump to the serial port before stopping the CPU.
|
// to dump to the serial port before stopping the CPU.
|
||||||
#define BUG_ON(V...) do { SERIAL_ECHOPAIR(ONLY_FILENAME, __LINE__, ": "); SERIAL_ECHOLNPAIR(V); SERIAL_FLUSHTX(); *(char*)0 = 42; } while(0)
|
// \/ Don't replace by SERIAL_ECHOPAIR since ONLY_FILENAME cannot be transformed to a PGM string on Arduino and it breaks building
|
||||||
|
#define BUG_ON(V...) do { SERIAL_ECHO(ONLY_FILENAME); SERIAL_ECHO(__LINE__); SERIAL_ECHOLN(": "); SERIAL_ECHOLNPAIR(V); SERIAL_FLUSHTX(); *(char*)0 = 42; } while(0)
|
||||||
#elif ENABLED(MARLIN_DEV_MODE)
|
#elif ENABLED(MARLIN_DEV_MODE)
|
||||||
// Don't stop the CPU here, but at least dump the bug on the serial port
|
// Don't stop the CPU here, but at least dump the bug on the serial port
|
||||||
//#define BUG_ON(V...) do { SERIAL_ECHOPAIR(ONLY_FILENAME, __LINE__, ": BUG!\n"); SERIAL_ECHOLNPAIR(V); SERIAL_FLUSHTX(); } while(0)
|
// \/ Don't replace by SERIAL_ECHOPAIR since ONLY_FILENAME cannot be transformed to a PGM string on Arduino and it breaks building
|
||||||
#define BUG_ON(V...) NOOP
|
#define BUG_ON(V...) do { SERIAL_ECHO(ONLY_FILENAME); SERIAL_ECHO(__LINE__); SERIAL_ECHOLN(": BUG!"); SERIAL_ECHOLNPAIR(V); SERIAL_FLUSHTX(); } while(0)
|
||||||
#else
|
#else
|
||||||
// Release mode, let's ignore the bug
|
// Release mode, let's ignore the bug
|
||||||
#define BUG_ON(V...) NOOP
|
#define BUG_ON(V...) NOOP
|
||||||
|
|
|
@ -318,6 +318,16 @@
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Allow manipulating enumeration value like flags without ugly cast everywhere
|
||||||
|
#define ENUM_FLAGS(T) \
|
||||||
|
FORCE_INLINE constexpr T operator&(T x, T y) { return static_cast<T>(static_cast<int>(x) & static_cast<int>(y)); } \
|
||||||
|
FORCE_INLINE constexpr T operator|(T x, T y) { return static_cast<T>(static_cast<int>(x) | static_cast<int>(y)); } \
|
||||||
|
FORCE_INLINE constexpr T operator^(T x, T y) { return static_cast<T>(static_cast<int>(x) ^ static_cast<int>(y)); } \
|
||||||
|
FORCE_INLINE constexpr T operator~(T x) { return static_cast<T>(~static_cast<int>(x)); } \
|
||||||
|
FORCE_INLINE T & operator&=(T &x, T y) { return x &= y; } \
|
||||||
|
FORCE_INLINE T & operator|=(T &x, T y) { return x |= y; } \
|
||||||
|
FORCE_INLINE T & operator^=(T &x, T y) { return x ^= y; }
|
||||||
|
|
||||||
// C++11 solution that is standard compliant. <type_traits> is not available on all platform
|
// C++11 solution that is standard compliant. <type_traits> is not available on all platform
|
||||||
namespace Private {
|
namespace Private {
|
||||||
template<bool, typename _Tp = void> struct enable_if { };
|
template<bool, typename _Tp = void> struct enable_if { };
|
||||||
|
@ -357,23 +367,43 @@
|
||||||
return *str ? findStringEnd(str + 1) : str;
|
return *str ? findStringEnd(str + 1) : str;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether a string contains a slash
|
// Check whether a string contains a specific character
|
||||||
constexpr bool containsSlash(const char *str) {
|
constexpr bool contains(const char *str, const char ch) {
|
||||||
return *str == '/' ? true : (*str ? containsSlash(str + 1) : false);
|
return *str == ch ? true : (*str ? contains(str + 1, ch) : false);
|
||||||
}
|
}
|
||||||
// Find the last position of the slash
|
// Find the last position of the specific character (should be called with findStringEnd)
|
||||||
constexpr const char* findLastSlashPos(const char *str) {
|
constexpr const char* findLastPos(const char *str, const char ch) {
|
||||||
return *str == '/' ? (str + 1) : findLastSlashPos(str - 1);
|
return *str == ch ? (str + 1) : findLastPos(str - 1, ch);
|
||||||
}
|
}
|
||||||
// Compile-time evaluation of the last part of a file path
|
// Compile-time evaluation of the last part of a file path
|
||||||
// Typically used to shorten the path to file in compiled strings
|
// Typically used to shorten the path to file in compiled strings
|
||||||
// CompileTimeString::baseName(__FILE__) returns "macros.h" and not /path/to/Marlin/src/core/macros.h
|
// CompileTimeString::baseName(__FILE__) returns "macros.h" and not /path/to/Marlin/src/core/macros.h
|
||||||
constexpr const char* baseName(const char *str) {
|
constexpr const char* baseName(const char *str) {
|
||||||
return containsSlash(str) ? findLastSlashPos(findStringEnd(str)) : str;
|
return contains(str, '/') ? findLastPos(findStringEnd(str), '/') : str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the first occurence of a character in a string (or return the last position in the string)
|
||||||
|
constexpr const char* findFirst(const char *str, const char ch) {
|
||||||
|
return *str == ch || *str == 0 ? (str + 1) : findFirst(str + 1, ch);
|
||||||
|
}
|
||||||
|
// Compute the string length at compile time
|
||||||
|
constexpr unsigned stringLen(const char *str) {
|
||||||
|
return *str == 0 ? 0 : 1 + stringLen(str + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ONLY_FILENAME CompileTimeString::baseName(__FILE__)
|
#define ONLY_FILENAME CompileTimeString::baseName(__FILE__)
|
||||||
|
/** Get the templated type name. This does not depends on RTTI, but on the preprocessor, so it should be quite safe to use even on old compilers.
|
||||||
|
WARNING: DO NOT RENAME THIS FUNCTION (or change the text inside the function to match what the preprocessor will generate)
|
||||||
|
The name is chosen very short since the binary will store "const char* gtn(T*) [with T = YourTypeHere]" so avoid long function name here */
|
||||||
|
template <typename T>
|
||||||
|
inline const char* gtn(T*) {
|
||||||
|
// It works on GCC by instantiating __PRETTY_FUNCTION__ and parsing the result. So the syntax here is very limited to GCC output
|
||||||
|
constexpr unsigned verboseChatLen = sizeof("const char* gtn(T*) [with T = ") - 1;
|
||||||
|
static char templateType[sizeof(__PRETTY_FUNCTION__) - verboseChatLen] = {};
|
||||||
|
__builtin_memcpy(templateType, __PRETTY_FUNCTION__ + verboseChatLen, sizeof(__PRETTY_FUNCTION__) - verboseChatLen - 2);
|
||||||
|
return templateType;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,7 @@ inline void SERIAL_ECHO(serial_char_t x) { SERIAL_IMPL.write(x.c); }
|
||||||
#define AS_CHAR(C) serial_char_t(C)
|
#define AS_CHAR(C) serial_char_t(C)
|
||||||
|
|
||||||
// SERIAL_ECHO_F prints a floating point value with optional precision
|
// SERIAL_ECHO_F prints a floating point value with optional precision
|
||||||
inline void SERIAL_ECHO_F(EnsureDouble x, int digit = 2) { SERIAL_IMPL.print(x, digit); }
|
inline void SERIAL_ECHO_F(EnsureDouble x, int digit=2) { SERIAL_IMPL.print(x, digit); }
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void SERIAL_ECHOLN(T x) { SERIAL_IMPL.println(x); }
|
void SERIAL_ECHOLN(T x) { SERIAL_IMPL.println(x); }
|
||||||
|
|
|
@ -45,10 +45,6 @@ struct serial_index_t {
|
||||||
constexpr serial_index_t() : index(-1) {}
|
constexpr serial_index_t() : index(-1) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// flushTX is not implemented in all HAL, so use SFINAE to call the method where it is.
|
|
||||||
CALL_IF_EXISTS_IMPL(void, flushTX);
|
|
||||||
CALL_IF_EXISTS_IMPL(bool, connected, true);
|
|
||||||
|
|
||||||
// In order to catch usage errors in code, we make the base to encode number explicit
|
// In order to catch usage errors in code, we make the base to encode number explicit
|
||||||
// If given a number (and not this enum), the compiler will reject the overload, falling back to the (double, digit) version
|
// If given a number (and not this enum), the compiler will reject the overload, falling back to the (double, digit) version
|
||||||
// We don't want hidden conversion of the first parameter to double, so it has to be as hard to do for the compiler as creating this enum
|
// We don't want hidden conversion of the first parameter to double, so it has to be as hard to do for the compiler as creating this enum
|
||||||
|
@ -59,19 +55,34 @@ enum class PrintBase {
|
||||||
Bin = 2
|
Bin = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
// A simple forward struct that prevent the compiler to select print(double, int) as a default overload for any type different than
|
// A simple feature list enumeration
|
||||||
// double or float. For double or float, a conversion exists so the call will be transparent
|
enum class SerialFeature {
|
||||||
|
None = 0x00,
|
||||||
|
MeatPack = 0x01, //!< Enabled when Meatpack is present
|
||||||
|
BinaryFileTransfer = 0x02, //!< Enabled for BinaryFile transfer support (in the future)
|
||||||
|
Virtual = 0x04, //!< Enabled for virtual serial port (like Telnet / Websocket / ...)
|
||||||
|
Hookable = 0x08, //!< Enabled if the serial class supports a setHook method
|
||||||
|
};
|
||||||
|
ENUM_FLAGS(SerialFeature);
|
||||||
|
|
||||||
|
// flushTX is not implemented in all HAL, so use SFINAE to call the method where it is.
|
||||||
|
CALL_IF_EXISTS_IMPL(void, flushTX);
|
||||||
|
CALL_IF_EXISTS_IMPL(bool, connected, true);
|
||||||
|
CALL_IF_EXISTS_IMPL(SerialFeature, features, SerialFeature::None);
|
||||||
|
|
||||||
|
// A simple forward struct to prevent the compiler from selecting print(double, int) as a default overload
|
||||||
|
// for any type other than double/float. For double/float, a conversion exists so the call will be invisible.
|
||||||
struct EnsureDouble {
|
struct EnsureDouble {
|
||||||
double a;
|
double a;
|
||||||
FORCE_INLINE operator double() { return a; }
|
FORCE_INLINE operator double() { return a; }
|
||||||
// If the compiler breaks on ambiguity here, it's likely because you're calling print(X, base) with X not a double or a float, and a
|
// If the compiler breaks on ambiguity here, it's likely because print(X, base) is called with X not a double/float, and
|
||||||
// base that's not one of PrintBase's value. This exact code is made to detect such error, you NEED to set a base explicitely like this:
|
// a base that's not a PrintBase value. This code is made to detect the error. You MUST set a base explicitly like this:
|
||||||
// SERIAL_PRINT(v, PrintBase::Hex)
|
// SERIAL_PRINT(v, PrintBase::Hex)
|
||||||
FORCE_INLINE EnsureDouble(double a) : a(a) {}
|
FORCE_INLINE EnsureDouble(double a) : a(a) {}
|
||||||
FORCE_INLINE EnsureDouble(float a) : a(a) {}
|
FORCE_INLINE EnsureDouble(float a) : a(a) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Using Curiously Recurring Template Pattern here to avoid virtual table cost when compiling.
|
// Using Curiously-Recurring Template Pattern here to avoid virtual table cost when compiling.
|
||||||
// Since the real serial class is known at compile time, this results in the compiler writing
|
// Since the real serial class is known at compile time, this results in the compiler writing
|
||||||
// a completely efficient code.
|
// a completely efficient code.
|
||||||
template <class Child>
|
template <class Child>
|
||||||
|
@ -85,27 +96,44 @@ struct SerialBase {
|
||||||
SerialBase(const bool) {}
|
SerialBase(const bool) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define SerialChild static_cast<Child*>(this)
|
||||||
|
|
||||||
// Static dispatch methods below:
|
// Static dispatch methods below:
|
||||||
// The most important method here is where it all ends to:
|
// The most important method here is where it all ends to:
|
||||||
size_t write(uint8_t c) { return static_cast<Child*>(this)->write(c); }
|
size_t write(uint8_t c) { return SerialChild->write(c); }
|
||||||
|
|
||||||
// Called when the parser finished processing an instruction, usually build to nothing
|
// Called when the parser finished processing an instruction, usually build to nothing
|
||||||
void msgDone() { static_cast<Child*>(this)->msgDone(); }
|
void msgDone() const { SerialChild->msgDone(); }
|
||||||
// Called upon initialization
|
|
||||||
void begin(const long baudRate) { static_cast<Child*>(this)->begin(baudRate); }
|
// Called on initialization
|
||||||
// Called upon destruction
|
void begin(const long baudRate) { SerialChild->begin(baudRate); }
|
||||||
void end() { static_cast<Child*>(this)->end(); }
|
|
||||||
|
// Called on destruction
|
||||||
|
void end() { SerialChild->end(); }
|
||||||
|
|
||||||
/** Check for available data from the port
|
/** Check for available data from the port
|
||||||
@param index The port index, usually 0 */
|
@param index The port index, usually 0 */
|
||||||
int available(serial_index_t index = 0) { return static_cast<Child*>(this)->available(index); }
|
int available(serial_index_t index=0) const { return SerialChild->available(index); }
|
||||||
|
|
||||||
/** Read a value from the port
|
/** Read a value from the port
|
||||||
@param index The port index, usually 0 */
|
@param index The port index, usually 0 */
|
||||||
int read(serial_index_t index = 0) { return static_cast<Child*>(this)->read(index); }
|
int read(serial_index_t index=0) { return SerialChild->read(index); }
|
||||||
|
|
||||||
|
/** Combine the features of this serial instance and return it
|
||||||
|
@param index The port index, usually 0 */
|
||||||
|
SerialFeature features(serial_index_t index=0) const { return static_cast<const Child*>(this)->features(index); }
|
||||||
|
|
||||||
|
// Check if the serial port has a feature
|
||||||
|
bool has_feature(serial_index_t index, SerialFeature flag) const { (features(index) & flag) != SerialFeature::None; }
|
||||||
|
|
||||||
// Check if the serial port is connected (usually bypassed)
|
// Check if the serial port is connected (usually bypassed)
|
||||||
bool connected() { return static_cast<Child*>(this)->connected(); }
|
bool connected() const { return SerialChild->connected(); }
|
||||||
|
|
||||||
// Redirect flush
|
// Redirect flush
|
||||||
void flush() { static_cast<Child*>(this)->flush(); }
|
void flush() { SerialChild->flush(); }
|
||||||
|
|
||||||
// Not all implementation have a flushTX, so let's call them only if the child has the implementation
|
// Not all implementation have a flushTX, so let's call them only if the child has the implementation
|
||||||
void flushTX() { CALL_IF_EXISTS(void, static_cast<Child*>(this), flushTX); }
|
void flushTX() { CALL_IF_EXISTS(void, SerialChild, flushTX); }
|
||||||
|
|
||||||
// Glue code here
|
// Glue code here
|
||||||
FORCE_INLINE void write(const char *str) { while (*str) write(*str++); }
|
FORCE_INLINE void write(const char *str) { while (*str) write(*str++); }
|
||||||
|
|
|
@ -65,6 +65,8 @@ struct BaseSerial : public SerialBase< BaseSerial<SerialT> >, public SerialT {
|
||||||
bool connected() { return CALL_IF_EXISTS(bool, static_cast<SerialT*>(this), connected);; }
|
bool connected() { return CALL_IF_EXISTS(bool, static_cast<SerialT*>(this), connected);; }
|
||||||
void flushTX() { CALL_IF_EXISTS(void, static_cast<SerialT*>(this), flushTX); }
|
void flushTX() { CALL_IF_EXISTS(void, static_cast<SerialT*>(this), flushTX); }
|
||||||
|
|
||||||
|
SerialFeature features(serial_index_t index) const { return CALL_IF_EXISTS(SerialFeature, static_cast<const SerialT*>(this), features, index); }
|
||||||
|
|
||||||
// We have 2 implementation of the same method in both base class, let's say which one we want
|
// We have 2 implementation of the same method in both base class, let's say which one we want
|
||||||
using SerialT::available;
|
using SerialT::available;
|
||||||
using SerialT::read;
|
using SerialT::read;
|
||||||
|
@ -98,10 +100,11 @@ struct ConditionalSerial : public SerialBase< ConditionalSerial<SerialT> > {
|
||||||
bool connected() { return CALL_IF_EXISTS(bool, &out, connected); }
|
bool connected() { return CALL_IF_EXISTS(bool, &out, connected); }
|
||||||
void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); }
|
void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); }
|
||||||
|
|
||||||
int available(serial_index_t ) { return (int)out.available(); }
|
int available(serial_index_t) { return (int)out.available(); }
|
||||||
int read(serial_index_t ) { return (int)out.read(); }
|
int read(serial_index_t) { return (int)out.read(); }
|
||||||
int available() { return (int)out.available(); }
|
int available() { return (int)out.available(); }
|
||||||
int read() { return (int)out.read(); }
|
int read() { return (int)out.read(); }
|
||||||
|
SerialFeature features(serial_index_t index) const { return CALL_IF_EXISTS(SerialFeature, &out, features, index); }
|
||||||
|
|
||||||
ConditionalSerial(bool & conditionVariable, SerialT & out, const bool e) : BaseClassT(e), condition(conditionVariable), out(out) {}
|
ConditionalSerial(bool & conditionVariable, SerialT & out, const bool e) : BaseClassT(e), condition(conditionVariable), out(out) {}
|
||||||
};
|
};
|
||||||
|
@ -126,6 +129,7 @@ struct ForwardSerial : public SerialBase< ForwardSerial<SerialT> > {
|
||||||
int read(serial_index_t) { return (int)out.read(); }
|
int read(serial_index_t) { return (int)out.read(); }
|
||||||
int available() { return (int)out.available(); }
|
int available() { return (int)out.available(); }
|
||||||
int read() { return (int)out.read(); }
|
int read() { return (int)out.read(); }
|
||||||
|
SerialFeature features(serial_index_t index) const { return CALL_IF_EXISTS(SerialFeature, &out, features, index); }
|
||||||
|
|
||||||
ForwardSerial(const bool e, SerialT & out) : BaseClassT(e), out(out) {}
|
ForwardSerial(const bool e, SerialT & out) : BaseClassT(e), out(out) {}
|
||||||
};
|
};
|
||||||
|
@ -163,9 +167,15 @@ struct RuntimeSerial : public SerialBase< RuntimeSerial<SerialT> >, public Seria
|
||||||
|
|
||||||
// Underlying implementation might use Arduino's bool operator
|
// Underlying implementation might use Arduino's bool operator
|
||||||
bool connected() {
|
bool connected() {
|
||||||
return Private::HasMember_connected<SerialT>::value ? CALL_IF_EXISTS(bool, static_cast<SerialT*>(this), connected) : static_cast<SerialT*>(this)->operator bool();
|
return Private::HasMember_connected<SerialT>::value
|
||||||
|
? CALL_IF_EXISTS(bool, static_cast<SerialT*>(this), connected)
|
||||||
|
: static_cast<SerialT*>(this)->operator bool();
|
||||||
}
|
}
|
||||||
void flushTX() { CALL_IF_EXISTS(void, static_cast<SerialT*>(this), flushTX); }
|
|
||||||
|
void flushTX() { CALL_IF_EXISTS(void, static_cast<SerialT*>(this), flushTX); }
|
||||||
|
|
||||||
|
// Append Hookable for this class
|
||||||
|
SerialFeature features(serial_index_t index) const { return SerialFeature::Hookable | CALL_IF_EXISTS(SerialFeature, static_cast<const SerialT*>(this), features, index); }
|
||||||
|
|
||||||
void setHook(WriteHook writeHook = 0, EndOfMessageHook eofHook = 0, void * userPointer = 0) {
|
void setHook(WriteHook writeHook = 0, EndOfMessageHook eofHook = 0, void * userPointer = 0) {
|
||||||
// Order is important here as serial code can be called inside interrupts
|
// Order is important here as serial code can be called inside interrupts
|
||||||
|
@ -251,6 +261,15 @@ struct MultiSerial : public SerialBase< MultiSerial<Serial0T, Serial1T, offset,
|
||||||
if (portMask.enabled(SecondOutput)) CALL_IF_EXISTS(void, &serial1, flushTX);
|
if (portMask.enabled(SecondOutput)) CALL_IF_EXISTS(void, &serial1, flushTX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Forward feature queries
|
||||||
|
SerialFeature features(serial_index_t index) const {
|
||||||
|
if (index.within(0 + offset, step + offset - 1))
|
||||||
|
return serial0.features(index);
|
||||||
|
else if (index.within(step + offset, 2 * step + offset - 1))
|
||||||
|
return serial1.features(index);
|
||||||
|
return SerialFeature::None;
|
||||||
|
}
|
||||||
|
|
||||||
MultiSerial(Serial0T & serial0, Serial1T & serial1, const SerialMask mask = Both, const bool e = false) :
|
MultiSerial(Serial0T & serial0, Serial1T & serial1, const SerialMask mask = Both, const bool e = false) :
|
||||||
BaseClassT(e),
|
BaseClassT(e),
|
||||||
portMask(mask), serial0(serial0), serial1(serial1) {}
|
portMask(mask), serial0(serial0), serial1(serial1) {}
|
||||||
|
|
|
@ -142,6 +142,8 @@ struct MeatpackSerial : public SerialBase <MeatpackSerial < SerialT >> {
|
||||||
// Existing instances implement Arduino's operator bool, so use that if it's available
|
// Existing instances implement Arduino's operator bool, so use that if it's available
|
||||||
bool connected() { return Private::HasMember_connected<SerialT>::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; }
|
bool connected() { return Private::HasMember_connected<SerialT>::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; }
|
||||||
void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); }
|
void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); }
|
||||||
|
SerialFeature features(serial_index_t index) const { return SerialFeature::MeatPack | CALL_IF_EXISTS(SerialFeature, &out, features, index); }
|
||||||
|
|
||||||
|
|
||||||
int available(serial_index_t index) {
|
int available(serial_index_t index) {
|
||||||
if (charCount) return charCount; // The buffer still has data
|
if (charCount) return charCount; // The buffer still has data
|
||||||
|
|
|
@ -167,6 +167,11 @@
|
||||||
dump_delay_accuracy_check();
|
dump_delay_accuracy_check();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 7: // D7 dump the current serial port type (hence configuration)
|
||||||
|
SERIAL_ECHOLNPAIR("Current serial configuration RX_BS:", RX_BUFFER_SIZE, ", TX_BS:", TX_BUFFER_SIZE);
|
||||||
|
SERIAL_ECHOLN(gtn(&SERIAL_IMPL));
|
||||||
|
break;
|
||||||
|
|
||||||
case 100: { // D100 Disable heaters and attempt a hard hang (Watchdog Test)
|
case 100: { // D100 Disable heaters and attempt a hard hang (Watchdog Test)
|
||||||
SERIAL_ECHOLNPGM("Disabling heaters and attempting to trigger Watchdog");
|
SERIAL_ECHOLNPGM("Disabling heaters and attempting to trigger Watchdog");
|
||||||
SERIAL_ECHOLNPGM("(USE_WATCHDOG " TERN(USE_WATCHDOG, "ENABLED", "DISABLED") ")");
|
SERIAL_ECHOLNPGM("(USE_WATCHDOG " TERN(USE_WATCHDOG, "ENABLED", "DISABLED") ")");
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
|
|
||||||
#include "../gcode.h"
|
#include "../gcode.h"
|
||||||
#include "../../inc/MarlinConfig.h"
|
#include "../../inc/MarlinConfig.h"
|
||||||
|
#include "../queue.h" // for getting the command port
|
||||||
|
|
||||||
|
|
||||||
#if ENABLED(M115_GEOMETRY_REPORT)
|
#if ENABLED(M115_GEOMETRY_REPORT)
|
||||||
#include "../../module/motion.h"
|
#include "../../module/motion.h"
|
||||||
|
@ -59,6 +61,9 @@ void GcodeSuite::M115() {
|
||||||
|
|
||||||
#if ENABLED(EXTENDED_CAPABILITIES_REPORT)
|
#if ENABLED(EXTENDED_CAPABILITIES_REPORT)
|
||||||
|
|
||||||
|
// The port that sent M115
|
||||||
|
serial_index_t port = queue.ring_buffer.command_port();
|
||||||
|
|
||||||
// PAREN_COMMENTS
|
// PAREN_COMMENTS
|
||||||
TERN_(PAREN_COMMENTS, cap_line(PSTR("PAREN_COMMENTS"), true));
|
TERN_(PAREN_COMMENTS, cap_line(PSTR("PAREN_COMMENTS"), true));
|
||||||
|
|
||||||
|
@ -69,7 +74,7 @@ void GcodeSuite::M115() {
|
||||||
cap_line(PSTR("SERIAL_XON_XOFF"), ENABLED(SERIAL_XON_XOFF));
|
cap_line(PSTR("SERIAL_XON_XOFF"), ENABLED(SERIAL_XON_XOFF));
|
||||||
|
|
||||||
// BINARY_FILE_TRANSFER (M28 B1)
|
// BINARY_FILE_TRANSFER (M28 B1)
|
||||||
cap_line(PSTR("BINARY_FILE_TRANSFER"), ENABLED(BINARY_FILE_TRANSFER));
|
cap_line(PSTR("BINARY_FILE_TRANSFER"), ENABLED(BINARY_FILE_TRANSFER)); // TODO: Use SERIAL_IMPL.has_feature(port, SerialFeature::BinaryFileTransfer) once implemented
|
||||||
|
|
||||||
// EEPROM (M500, M501)
|
// EEPROM (M500, M501)
|
||||||
cap_line(PSTR("EEPROM"), ENABLED(EEPROM_SETTINGS));
|
cap_line(PSTR("EEPROM"), ENABLED(EEPROM_SETTINGS));
|
||||||
|
@ -148,7 +153,7 @@ void GcodeSuite::M115() {
|
||||||
cap_line(PSTR("COOLER_TEMPERATURE"), ENABLED(HAS_COOLER));
|
cap_line(PSTR("COOLER_TEMPERATURE"), ENABLED(HAS_COOLER));
|
||||||
|
|
||||||
// MEATPACK Compression
|
// MEATPACK Compression
|
||||||
cap_line(PSTR("MEATPACK"), ENABLED(HAS_MEATPACK));
|
cap_line(PSTR("MEATPACK"), SERIAL_IMPL.has_feature(port, SerialFeature::MeatPack));
|
||||||
|
|
||||||
// Machine Geometry
|
// Machine Geometry
|
||||||
#if ENABLED(M115_GEOMETRY_REPORT)
|
#if ENABLED(M115_GEOMETRY_REPORT)
|
||||||
|
|
Loading…
Reference in a new issue