diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index 770ef0b889f1..837716120d1e 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -112,6 +112,16 @@
//#define SERIAL_PORT_3 1
//#define BAUDRATE_3 250000 // :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] Enable to override BAUDRATE
+/**
+ * Select a serial port to communicate with RS485 protocol
+ * :[-1, 0, 1, 2, 3, 4, 5, 6, 7]
+ */
+//#define RS485_SERIAL_PORT 1
+#ifdef RS485_SERIAL_PORT
+ //#define M485_PROTOCOL 1 // Check your host for protocol compatibility
+ //#define RS485_BUS_BUFFER_SIZE 128
+#endif
+
// Enable the Bluetooth serial interface on AT90USB devices
//#define BLUETOOTH
diff --git a/Marlin/src/HAL/STM32/HAL.h b/Marlin/src/HAL/STM32/HAL.h
index 9c74a95ff0c4..e25ca27d8eda 100644
--- a/Marlin/src/HAL/STM32/HAL.h
+++ b/Marlin/src/HAL/STM32/HAL.h
@@ -117,6 +117,14 @@
#endif
#endif
+#ifdef RS485_SERIAL_PORT
+ #if WITHIN(RS485_SERIAL_PORT, 1, 9)
+ #define RS485_SERIAL MSERIAL(RS485_SERIAL_PORT)
+ #else
+ #error "RS485_SERIAL_PORT must be from 1 to 9."
+ #endif
+#endif
+
/**
* TODO: review this to return 1 for pins that are not analog input
*/
diff --git a/Marlin/src/HAL/STM32F1/HAL.h b/Marlin/src/HAL/STM32F1/HAL.h
index 007bf83b0974..c422f60500b6 100644
--- a/Marlin/src/HAL/STM32F1/HAL.h
+++ b/Marlin/src/HAL/STM32F1/HAL.h
@@ -143,6 +143,17 @@
#endif
#endif
+#ifdef RS485_SERIAL_PORT
+ #if RS485_SERIAL_PORT == -1
+ #define RS485_SERIAL UsbSerial
+ #elif WITHIN(RS485_SERIAL_PORT, 1, NUM_UARTS)
+ #define RS485_SERIAL MSERIAL(RS485_SERIAL_PORT)
+ #else
+ #define RS485_SERIAL MSERIAL(1) // dummy port
+ static_assert(false, "RS485_SERIAL_PORT must be from 1 to " STRINGIFY(NUM_UARTS) ".")
+ #endif
+#endif
+
/**
* TODO: review this to return 1 for pins that are not analog input
*/
diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp
index dbfb822015c4..9b508b0e9f9b 100644
--- a/Marlin/src/MarlinCore.cpp
+++ b/Marlin/src/MarlinCore.cpp
@@ -261,6 +261,10 @@
#include "tests/marlin_tests.h"
#endif
+#if HAS_RS485_SERIAL
+ #include "feature/rs485.h"
+#endif
+
PGMSTR(M112_KILL_STR, "M112 Shutdown");
MarlinState marlin_state = MarlinState::MF_INITIALIZING;
@@ -1642,6 +1646,10 @@ void setup() {
SETUP_RUN(bdl.init(I2C_BD_SDA_PIN, I2C_BD_SCL_PIN, I2C_BD_DELAY));
#endif
+ #if HAS_RS485_SERIAL
+ SETUP_RUN(rs485_init());
+ #endif
+
#if ENABLED(FT_MOTION)
SETUP_RUN(ftMotion.init());
#endif
diff --git a/Marlin/src/feature/power.cpp b/Marlin/src/feature/power.cpp
index 20eb63a6f160..c6dc56283692 100644
--- a/Marlin/src/feature/power.cpp
+++ b/Marlin/src/feature/power.cpp
@@ -201,7 +201,7 @@ void Power::power_off() {
/**
* Check all conditions that would signal power needing to be on.
*
- * @returns bool if power is needed
+ * @return bool if power is needed
*/
bool Power::is_power_needed() {
diff --git a/Marlin/src/feature/rs485.cpp b/Marlin/src/feature/rs485.cpp
new file mode 100644
index 000000000000..800918545081
--- /dev/null
+++ b/Marlin/src/feature/rs485.cpp
@@ -0,0 +1,39 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#include "../inc/MarlinConfig.h"
+
+#if HAS_RS485_SERIAL
+
+#include "rs485.h"
+
+HardwareSerialBusIO rs485BusIO(&RS485_SERIAL);
+RS485Bus rs485Bus(rs485BusIO, RS485_RX_ENABLE_PIN, RS485_TX_ENABLE_PIN);
+
+PhotonProtocol rs485Protocol;
+
+Packetizer rs485Packetizer(rs485Bus, rs485Protocol);
+
+uint8_t rs485Buffer[RS485_SEND_BUFFER_SIZE];
+
+void rs485_init() { RS485_SERIAL.begin(57600); }
+
+#endif // HAS_RS485_SERIAL
diff --git a/Marlin/src/feature/rs485.h b/Marlin/src/feature/rs485.h
new file mode 100644
index 000000000000..3327626a3c05
--- /dev/null
+++ b/Marlin/src/feature/rs485.h
@@ -0,0 +1,40 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#pragma once
+
+#include "../inc/MarlinConfigPre.h"
+
+#include
+#include
+
+#include
+#include
+
+#define RS485_SEND_BUFFER_SIZE 32
+
+extern HardwareSerialBusIO rs485BusIO;
+extern RS485Bus rs485Bus;
+extern PhotonProtocol rs485Protocol;
+extern Packetizer rs485Packetizer;
+extern uint8_t rs485Buffer[RS485_SEND_BUFFER_SIZE];
+
+void rs485_init();
diff --git a/Marlin/src/gcode/feature/rs485/M485.cpp b/Marlin/src/gcode/feature/rs485/M485.cpp
new file mode 100644
index 000000000000..03640a034eec
--- /dev/null
+++ b/Marlin/src/gcode/feature/rs485/M485.cpp
@@ -0,0 +1,127 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_RS485_SERIAL
+
+#include "../../../feature/rs485.h"
+#include "../../gcode.h"
+
+void write_packet_data() {
+
+ Packet packet = rs485Packetizer.getPacket();
+ for (size_t i = packet.startIndex; i <= packet.endIndex; i++) {
+ const uint8_t data = rs485Bus[i];
+ if (data < 0x10) SERIAL_ECHOPGM_P(PSTR("0"));
+ SERIAL_PRINT(data, PrintBase::Hex);
+ }
+
+ SERIAL_EOL();
+}
+
+static void rs485_write_failed(const PacketWriteResult writeResult) {
+ SERIAL_ERROR_START();
+ SERIAL_ECHOPGM("RS485: Write failed ");
+ switch (writeResult) {
+ case PacketWriteResult::FAILED_INTERRUPTED: SERIAL_ECHOPGM("interrupted"); break;
+ case PacketWriteResult::FAILED_BUFFER_FULL: SERIAL_ECHOPGM("buffer full"); break;
+ case PacketWriteResult::FAILED_TIMEOUT: SERIAL_ECHOPGM("timeout"); break;
+ }
+ SERIAL_EOL();
+}
+
+void GcodeSuite::M485() {
+ if (strlen(parser.string_arg) & 1) {
+ SERIAL_ERROR_MSG("String must contain an even number of bytes.");
+ return;
+ }
+
+ if (strlen(parser.string_arg) > RS485_SEND_BUFFER_SIZE * 2) {
+ SERIAL_ERROR_MSG("String too long (" STRINGIFY(RS485_SEND_BUFFER_SIZE) " bytes max).");
+ return;
+ }
+
+ // Convert the string to bytes in the buffer
+ for (size_t i = 0; i < strlen(parser.string_arg); i += 2) {
+ const uint8_t nybble1 = HEXCHR(parser.string_arg[i]),
+ nybble2 = HEXCHR(parser.string_arg[i + 1]);
+
+ if (nybble1 == -1 || nybble2 == -1) {
+ SERIAL_ERROR_START();
+ SERIAL_ECHOPGM("Not a hex character: ");
+ SERIAL_CHAR(nybble1 == -1 ? parser.string_arg[i] : parser.string_arg[i+1]);
+ SERIAL_EOL();
+ return;
+ }
+
+ rs485Buffer[i >> 1] = (nybble1 & 0x0F) << 4 | (nybble2 & 0x0F);
+ }
+
+ rs485Packetizer.setMaxReadTimeout(50000); // This can be super small since ideally any packets will already be in our buffer
+ rs485Packetizer.setFalsePacketVerificationTimeout(5000);
+
+ // Read and ignore any packets that may have come in, before we write.
+
+ while (rs485Packetizer.hasPacket()) {
+ #if M485_PROTOCOL >= 2
+ SERIAL_ECHO_START();
+ #endif
+ SERIAL_ECHO(F("rs485-"), F("unexpected-packet: "));
+ write_packet_data();
+ rs485Packetizer.clearPacket();
+ }
+
+ const PacketWriteResult writeResult = rs485Packetizer.writePacket(rs485Buffer, strlen(parser.string_arg) / 2);
+ switch (writeResult) {
+ default: rs485_write_failed(writeResult);
+ case PacketWriteResult::OK: break; // Nothing to do
+ }
+
+ //millis_t startTime = millis();
+ bool hasPacket = rs485Packetizer.hasPacket();
+ //millis_t endTime = millis();
+ //#if M485_PROTOCOL >= 2
+ // SERIAL_ECHO_START();
+ //#endif
+ //SERIAL_ECHOLN(F("rs485-"), F("time: "), endTime - startTime);
+
+ #if M485_PROTOCOL >= 2
+ SERIAL_ECHO_START();
+ #endif
+
+ SERIAL_ECHO(F("rs485-"));
+ if (!hasPacket) {
+ #if M485_PROTOCOL >= 2
+ SERIAL_ECHOLN(F("timeout"));
+ #else
+ SERIAL_ECHOLN(F("reply: TIMEOUT"));
+ #endif
+ return;
+ }
+
+ SERIAL_ECHO(F("reply: "));
+ write_packet_data();
+ rs485Packetizer.clearPacket();
+}
+
+#endif // HAS_RS485_SERIAL
diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp
index 9e81c7d4bc4b..2a8bfa4e662a 100644
--- a/Marlin/src/gcode/gcode.cpp
+++ b/Marlin/src/gcode/gcode.cpp
@@ -897,6 +897,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
case 430: M430(); break; // M430: Read the system current (A), voltage (V), and power (W)
#endif
+ #if HAS_RS485_SERIAL
+ case 485: M485(); break; // M485: Send RS485 packets
+ #endif
+
#if ENABLED(CANCEL_OBJECTS)
case 486: M486(); break; // M486: Identify and cancel objects
#endif
diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h
index 144d724cd9cd..bb2f0099949b 100644
--- a/Marlin/src/gcode/gcode.h
+++ b/Marlin/src/gcode/gcode.h
@@ -243,6 +243,7 @@
* M425 - Enable/Disable and tune backlash correction. (Requires BACKLASH_COMPENSATION and BACKLASH_GCODE)
* M428 - Set the home_offset based on the current_position. Nearest edge applies. (Disabled by NO_WORKSPACE_OFFSETS or DELTA)
* M430 - Read the system current, voltage, and power (Requires POWER_MONITOR_CURRENT, POWER_MONITOR_VOLTAGE, or POWER_MONITOR_FIXED_VOLTAGE)
+ * M485 - Send RS485 packets (Requires RS485_SERIAL_PORT)
* M486 - Identify and cancel objects. (Requires CANCEL_OBJECTS)
* M500 - Store parameters in EEPROM. (Requires EEPROM_SETTINGS)
* M501 - Restore parameters from EEPROM. (Requires EEPROM_SETTINGS)
@@ -1063,6 +1064,10 @@ class GcodeSuite {
static void M430();
#endif
+ #if HAS_RS485_SERIAL
+ static void M485();
+ #endif
+
#if ENABLED(CANCEL_OBJECTS)
static void M486();
#endif
diff --git a/Marlin/src/gcode/parser.cpp b/Marlin/src/gcode/parser.cpp
index 16c3b2d9bd0b..4b8bbb925fde 100644
--- a/Marlin/src/gcode/parser.cpp
+++ b/Marlin/src/gcode/parser.cpp
@@ -274,9 +274,12 @@ void GCodeParser::parse(char *p) {
// Only use string_arg for these M codes
if (letter == 'M') switch (codenum) {
- TERN_(GCODE_MACROS, case 810 ... 819:)
TERN_(EXPECTED_PRINTER_CHECK, case 16:)
- case 23: case 28: case 30: case 117 ... 118: case 928:
+ TERN_(SDSUPPORT, case 23: case 28: case 30: case 928:)
+ TERN_(HAS_STATUS_MESSAGE, case 117:)
+ TERN_(HAS_RS485_SERIAL, case 485:)
+ TERN_(GCODE_MACROS, case 810 ... 819:)
+ case 118:
string_arg = unescape_string(p);
return;
default: break;
diff --git a/Marlin/src/inc/Conditionals_LCD.h b/Marlin/src/inc/Conditionals_LCD.h
index ab5fd9e55cbd..4e5aa0bc0dc4 100644
--- a/Marlin/src/inc/Conditionals_LCD.h
+++ b/Marlin/src/inc/Conditionals_LCD.h
@@ -1669,6 +1669,9 @@
#if SERIAL_PORT == -1 || SERIAL_PORT_2 == -1 || SERIAL_PORT_3 == -1
#define HAS_USB_SERIAL 1
#endif
+#ifdef RS485_SERIAL_PORT
+ #define HAS_RS485_SERIAL 1
+#endif
#if SERIAL_PORT_2 == -2
#define HAS_ETHERNET 1
#endif
diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h
index 7af8536d90fe..e6f307877d0f 100644
--- a/Marlin/src/inc/Conditionals_post.h
+++ b/Marlin/src/inc/Conditionals_post.h
@@ -1827,11 +1827,12 @@
//
// Flag the indexed hardware serial ports in use
-#define SERIAL_IN_USE(N) ( (defined(SERIAL_PORT) && N == SERIAL_PORT) \
- || (defined(SERIAL_PORT_2) && N == SERIAL_PORT_2) \
- || (defined(SERIAL_PORT_3) && N == SERIAL_PORT_3) \
- || (defined(MMU2_SERIAL_PORT) && N == MMU2_SERIAL_PORT) \
- || (defined(LCD_SERIAL_PORT) && N == LCD_SERIAL_PORT) )
+#define SERIAL_IN_USE(N) ( (defined(SERIAL_PORT) && N == SERIAL_PORT) \
+ || (defined(SERIAL_PORT_2) && N == SERIAL_PORT_2) \
+ || (defined(SERIAL_PORT_3) && N == SERIAL_PORT_3) \
+ || (defined(MMU2_SERIAL_PORT) && N == MMU2_SERIAL_PORT) \
+ || (defined(LCD_SERIAL_PORT) && N == LCD_SERIAL_PORT) \
+ || (defined(RS485_SERIAL_PORT) && N == RS485_SERIAL_PORT) )
// Flag the named hardware serial ports in use
#define TMC_UART_IS(A,N) (defined(A##_HARDWARE_SERIAL) && (CAT(HW_,A##_HARDWARE_SERIAL) == HW_Serial##N || CAT(HW_,A##_HARDWARE_SERIAL) == HW_MSerial##N))
diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h
index 7113e9946b47..6b9deef84dc7 100644
--- a/Marlin/src/inc/SanityCheck.h
+++ b/Marlin/src/inc/SanityCheck.h
@@ -2919,6 +2919,8 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
#error "MMU2_SERIAL_PORT cannot be the same as SERIAL_PORT_2."
#elif defined(LCD_SERIAL_PORT) && MMU2_SERIAL_PORT == LCD_SERIAL_PORT
#error "MMU2_SERIAL_PORT cannot be the same as LCD_SERIAL_PORT."
+ #elif defined(RS485_SERIAL_PORT) && MMU2_SERIAL_PORT == RS485_SERIAL_PORT
+ #error "MMU2_SERIAL_PORT cannot be the same as RS485_SERIAL_PORT."
#endif
#endif
@@ -2930,6 +2932,8 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
#error "LCD_SERIAL_PORT cannot be the same as SERIAL_PORT."
#elif defined(SERIAL_PORT_2) && LCD_SERIAL_PORT == SERIAL_PORT_2
#error "LCD_SERIAL_PORT cannot be the same as SERIAL_PORT_2."
+ #elif defined(RS485_SERIAL_PORT) && LCD_SERIAL_PORT == RS485_SERIAL_PORT
+ #error "LCD_SERIAL_PORT cannot be the same as RS485_SERIAL_PORT."
#endif
#else
#if HAS_DGUS_LCD
@@ -2943,6 +2947,17 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
#endif
#endif
+/**
+ * RS485 bus requires a dedicated serial port
+ */
+#ifdef RS485_SERIAL_PORT
+ #if RS485_SERIAL_PORT == SERIAL_PORT
+ #error "RS485_SERIAL_PORT cannot be the same as SERIAL_PORT."
+ #elif defined(SERIAL_PORT_2) && RS485_SERIAL_PORT == SERIAL_PORT_2
+ #error "RS485_SERIAL_PORT cannot be the same as SERIAL_PORT_2."
+ #endif
+#endif
+
/**
* Check existing CS pins against enabled TMC SPI drivers.
*/
diff --git a/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV3.h b/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV3.h
index a256d2e7cb26..cf400335f531 100644
--- a/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV3.h
+++ b/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV3.h
@@ -206,3 +206,6 @@
#define INDEX_AUX3_PWM2 PB9
#define INDEX_AUX3_A1 PA0
#define INDEX_AUX3_A2 PA1
+
+#define RS485_TX_ENABLE_PIN PD11
+#define RS485_RX_ENABLE_PIN PD12
diff --git a/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV4.h b/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV4.h
index f7daa4c3ec41..9c374eae4444 100644
--- a/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV4.h
+++ b/Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV4.h
@@ -206,3 +206,6 @@
#define LUMEN_AUX3_PWM2 PB9
#define LUMEN_AUX3_A1 PA0
#define LUMEN_AUX3_A2 PA1
+
+#define RS485_TX_ENABLE_PIN PD11
+#define RS485_RX_ENABLE_PIN PD12
diff --git a/buildroot/tests/Opulo_Lumen_REV3 b/buildroot/tests/Opulo_Lumen_REV3
index f12f69011e78..436a71e64418 100755
--- a/buildroot/tests/Opulo_Lumen_REV3
+++ b/buildroot/tests/Opulo_Lumen_REV3
@@ -8,6 +8,7 @@ set -e
use_example_configs Opulo/Lumen_REV3
opt_disable TMC_DEBUG
+opt_set RS485_SERIAL_PORT 2 RS485_BUS_BUFFER_SIZE 128
exec_test $1 $2 "Opulo Lumen REV3 Pick-and-Place" "$3"
# cleanup
diff --git a/ini/features.ini b/ini/features.ini
index d60bdcc6cf66..375c26ffb782 100644
--- a/ini/features.ini
+++ b/ini/features.ini
@@ -23,7 +23,6 @@ MKS_WIFI_MODULE = QRCode=https://github.com/makerbase-mks
HAS_TRINAMIC_CONFIG = TMCStepper@~0.7.3
build_src_filter=+ + + + +
HAS_T(RINAMIC_CONFIG|MC_SPI) = build_src_filter=+
-HAS_STEALTHCHOP = build_src_filter=+
SR_LCD_3W_NL = SailfishLCD=https://github.com/mikeshub/SailfishLCD/archive/6f53c19a8a.zip
HAS_MOTOR_CURRENT_(I2C|DAC|SPI|PWM) = build_src_filter=+
HAS_MOTOR_CURRENT_I2C = SlowSoftI2CMaster
@@ -317,6 +316,7 @@ NONLINEAR_EXTRUSION = build_src_filter=+ +
PARK_HEAD_ON_PAUSE = build_src_filter=+
FILAMENT_LOAD_UNLOAD_GCODES = build_src_filter=+
+HAS_STEALTHCHOP = build_src_filter=+
CNC_WORKSPACE_PLANES = build_src_filter=+
CNC_COORDINATE_SYSTEMS = build_src_filter=+
HAS_HOME_OFFSET = build_src_filter=+
@@ -334,6 +334,8 @@ HAS_LCD_CONTRAST = build_src_filter=+
HAS_LCD_BRIGHTNESS = build_src_filter=+
HAS_SOUND = build_src_filter=+
+HAS_RS485_SERIAL = jnesselr/rs485@^0.0.9
+ build_src_filter=+ +
HAS_MULTI_LANGUAGE = build_src_filter=+
TOUCH_SCREEN_CALIBRATION = build_src_filter=+
ARC_SUPPORT = build_src_filter=+