Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

M485 command for RS485 support in Marlin #25680

Merged
merged 38 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e4ae25e
Basic RS485 protocol/packeting
Jnesselr Oct 9, 2022
f380e24
Changing how errors are written out
Jnesselr Oct 9, 2022
461c61d
Updating Rev04 pins
Jnesselr Oct 9, 2022
b87a3ba
Fixing off by 2x error
Jnesselr Nov 2, 2022
1e2635d
32 byte send buffer size
Jnesselr Nov 2, 2022
5cf56eb
Print bytes written
Jnesselr Nov 2, 2022
2c5f689
Fix off by 2 error hopefully correctly this time
Jnesselr Nov 2, 2022
43d5c6b
Trying to add more debug information
Jnesselr Dec 5, 2022
42728ca
Increased send buffer size
Jnesselr Dec 12, 2022
78d6caf
Basic working implementation compatible with OpenPnP
Jnesselr Jan 21, 2023
ceb7859
Updated RS485 library to 0.0.5
Jnesselr Feb 23, 2023
42e7635
Updating Marlin's M485 command based on jam session
sphawes Apr 7, 2023
6255d72
Updated M485 command based on testing
sphawes Apr 14, 2023
20b96ed
Merge branch 'bugfix-2.1.x' into jnesselr/M485
Jnesselr Apr 14, 2023
0415ccf
Added RS485 lines to the base config
Jnesselr Apr 14, 2023
c44dced
Updated names to all have RS485 in them
Jnesselr Apr 14, 2023
5726a4d
misc. cleanup
thinkyhead Apr 16, 2023
a2c0cc0
🔧 Fix LCD_SERIAL_PORT config
thinkyhead Apr 25, 2023
8452dac
Merge branch 'bugfix-2.1.x' into pr/25680
thinkyhead Apr 25, 2023
1de0574
Merge remote-tracking branch 'upstream/bugfix-2.1.x' into pr/25680
thinkyhead May 2, 2023
7ccc72f
Merge branch 'bugfix-2.1.x' into pr/25680
thinkyhead Jul 2, 2023
5809d91
merge followup
thinkyhead Jul 2, 2023
ccbf936
Merge branch 'bugfix-2.1.x' into pr/25680
thinkyhead Aug 3, 2023
63a7840
Merge branch 'bugfix-2.1.x' into pr/25680
thinkyhead Oct 26, 2023
2ffeca2
Merge branch 'bugfix-2.1.x' into pr/25680
thinkyhead Nov 5, 2023
8d2e207
Merge branch 'bugfix-2.1.x' into pr/25680
thinkyhead Jan 22, 2024
4f48a2f
update error
thinkyhead Jan 22, 2024
6aff0cf
add a test
thinkyhead Jan 22, 2024
653fb1f
Merge branch 'bugfix-2.1.x' into pr/25680
thinkyhead Mar 23, 2024
f159022
Makes timing changes to M485. Updates M485 to interface with v0.0.9 o…
sphawes May 7, 2024
f1afaa1
updates features.ini to request v0.0.9 of the jnesselr/RS485 library
sphawes May 7, 2024
e6f22e3
up to 9
thinkyhead Jun 8, 2024
61d98c6
match to our ad hoc i2c "protocol"
thinkyhead Jun 8, 2024
a940203
update (c)
thinkyhead Jun 8, 2024
e270d52
shorter
thinkyhead Jun 14, 2024
76f954e
configurable protocol version
thinkyhead Jun 27, 2024
240bb60
pre-merge
thinkyhead Jul 8, 2024
707ad05
Merge remote-tracking branch 'upstream/bugfix-2.1.x' into pr/25680
thinkyhead Jul 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Marlin/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@
//#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 RS485_BUS_BUFFER_SIZE 128
#endif

// Enable the Bluetooth serial interface on AT90USB devices
//#define BLUETOOTH

Expand Down
8 changes: 8 additions & 0 deletions Marlin/src/HAL/STM32/HAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@
#endif
#endif

#ifdef RS485_SERIAL_PORT
#if WITHIN(RS485_SERIAL_PORT, 1, 6)
thinkyhead marked this conversation as resolved.
Show resolved Hide resolved
#define RS485_SERIAL MSERIAL(RS485_SERIAL_PORT)
#else
#error "RS485_SERIAL_PORT must be from 1 to 6."
#endif
#endif

/**
* TODO: review this to return 1 for pins that are not analog input
*/
Expand Down
11 changes: 11 additions & 0 deletions Marlin/src/HAL/STM32F1/HAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
thinkyhead marked this conversation as resolved.
Show resolved Hide resolved

/**
* TODO: review this to return 1 for pins that are not analog input
*/
Expand Down
8 changes: 8 additions & 0 deletions Marlin/src/MarlinCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,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 = MF_INITIALIZING;
Expand Down Expand Up @@ -1645,6 +1649,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
Expand Down
39 changes: 39 additions & 0 deletions Marlin/src/feature/rs485.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2023 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 <https://www.gnu.org/licenses/>.
*
*/
#include "../inc/MarlinConfig.h"

#if HAS_RS485_SERIAL

#include "rs485.h"

HardwareSerialBusIO rs485BusIO(&RS485_SERIAL);
RS485Bus<RS485_BUS_BUFFER_SIZE> 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
40 changes: 40 additions & 0 deletions Marlin/src/feature/rs485.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2023 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 <https://www.gnu.org/licenses/>.
*
*/
#pragma once

#include "../inc/MarlinConfigPre.h"

#include <rs485/rs485bus.hpp>
#include <rs485/bus_adapters/hardware_serial.h>

#include <rs485/protocols/photon.h>
#include <rs485/packetizer.h>

#define RS485_SEND_BUFFER_SIZE 32

extern HardwareSerialBusIO rs485BusIO;
extern RS485Bus<RS485_BUS_BUFFER_SIZE> rs485Bus;
extern PhotonProtocol rs485Protocol;
extern Packetizer rs485Packetizer;
extern uint8_t rs485Buffer[RS485_SEND_BUFFER_SIZE];

void rs485_init();
107 changes: 107 additions & 0 deletions Marlin/src/gcode/feature/rs485/M485.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2023 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 <https://www.gnu.org/licenses/>.
*
*/

#include "../../../inc/MarlinConfig.h"

#if HAS_RS485_SERIAL

#include "../../../feature/rs485.h"
#include "../../gcode.h"

void write_packet_data() {
for (size_t i = 0; i < rs485Packetizer.packetLength(); 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;
default: 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(10); // This can be super small since ideally any packets will already be in our buffer

// Read and ignore any packets that may have come in, before we write.
while (rs485Packetizer.hasPacket()) {
SERIAL_ECHOPGM("rs485-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
}

rs485Packetizer.setMaxReadTimeout(50000); // 50 ms

//millis_t startTime = millis();
bool hasPacket = rs485Packetizer.hasPacket();
//millis_t endTime = millis();
//SERIAL_ECHOLNPGM("rs485-time: ", endTime - startTime);

if (!hasPacket) { SERIAL_ECHOLNPGM("rs485-reply: TIMEOUT"); return; }

SERIAL_ECHOPGM("rs485-reply: ");
write_packet_data();
rs485Packetizer.clearPacket();
}

#endif // HAS_RS485_SERIAL
4 changes: 4 additions & 0 deletions Marlin/src/gcode/gcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions Marlin/src/gcode/gcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions Marlin/src/gcode/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions Marlin/src/inc/Conditionals_LCD.h
Original file line number Diff line number Diff line change
Expand Up @@ -1653,6 +1653,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
Expand Down
11 changes: 6 additions & 5 deletions Marlin/src/inc/Conditionals_post.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
15 changes: 15 additions & 0 deletions Marlin/src/inc/SanityCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -2843,6 +2843,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

Expand All @@ -2854,6 +2856,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
Expand All @@ -2867,6 +2871,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.
*/
Expand Down
3 changes: 3 additions & 0 deletions Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV3.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions Marlin/src/pins/stm32f4/pins_OPULO_LUMEN_REV4.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another 'burning question'…. Is a board required to have RS485-specific hardware, with specific pins assigned to it? If that is the case, then we can sanity-check the RS485_SERIAL port number and make sure it corresponds to the required hardware port.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just my 2c. This is the first time I see a RX enable pin on 485 serial communications, isn't it a resources waste?
RS485 is half duplex then you are transmitting you may not receive, then a master to slave protocol should be implemented, then to correclty handle communications answers, just purge RX buffer after TX.
I'm wrong or am I missing something ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is a board required to have RS485-specific hardware, with specific pins assigned to it?

Yep!

If that is the case, then we can sanity-check the RS485_SERIAL port number and make sure it corresponds to the required hardware port.

I'm not sure how to do that and my need your help.

@GMagician "resource waste" is always debatable. The code this PR uses already has a controller/peripheral protocol. Our long term goal is to make it so not even that is required and we can do cool things like having an autoreporter automatically report up to the host when there's a valid RS485 message, even if that isn't a response to an M485 command. So we're fine with the "waste" as it is now.

1 change: 1 addition & 0 deletions buildroot/tests/Opulo_Lumen_REV3
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions ini/features.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
# The order of the features matters for source-filter resolution inside of common-dependencies.py.

[features]
HAS_RS485_SERIAL = jnesselr/rs485@^0.0.7
build_src_filter=+<src/feature/rs485.cpp> +<src/gcode/feature/rs485>
YHCB2004 = red-scorp/LiquidCrystal_AIP31068@^1.0.4, red-scorp/SoftSPIB@^1.1.1
HAS_TFT_LVGL_UI = lvgl=https://github.com/makerbase-mks/LVGL-6.1.1-MKS/archive/a3ebe98bc6.zip
build_src_filter=+<src/lcd/extui/mks_ui>
Expand Down