Hello and a happy new year!
As I had some difficulties with the given Modbus library, I have a little present for you: I had some time to program an own code based on the "Modbus application protocol specification V1.1b". Of course, I cannot guarantee that everything is conform to the specification, but I seems to work. The current example can read and write on holding and coil registers an is easily adaptable. You can also extend it with an input register or take of the write rights for special addresses by changing the if cases. I tested it on the M5Stack core V2.7 with a COMMU module (M011) and I was able to communicate with “LabVIEW 2023 Q3 32 bit” using the NI Modbus library.
  1  from m5stack import *
  2  from m5ui import *
  3  from uiflow import *
  4
  5  setScreenColor(0xFFFFFF)
  6
  7  Label0 = M5TextBox(20, 80, "Holding Register", lcd.FONT_DejaVu18, 0xFF0000, rotate=0)
  8  Label1 = M5TextBox(20, 140, "Coil Register", lcd.FONT_DejaVu18, 0xFF0000, rotate=0)
  9  
 10  def Modbus_CRC(data):
 11    i0 = 0
 12    i1 = 1
 13    crc = 0xFFFF  
 14    for i0 in range(len(data)):
 15      crc ^= (data[i0])
 16      for i1 in range(8):
 17        if (crc & 0x0001) == 0x0000:
 18          crc >>= 1
 19        else:
 20          crc >>= 1
 21          crc ^= 0xA001
 22        pass
 23      pass
 24    return [(crc & 0xFF00) >> 8, crc & 0x00FF]
 25
 26  def GenerateErrorMessage(ID, function, code):
 27    message = [ID, 0x80 + function, code, 0, 0]
 28    CRC = Modbus_CRC(message[0 : 3])
 29    message[3] = CRC[1]
 30    message[4] = CRC[0]
 31    message = bytes(message)
 32    return(message)
 33  
 34  @timerSch.event('ModbusSlave')
 35  def tModbusSlave():
 36    global uart1, RegisterH, RegisterHStart, RegisterHSize, RegisterC, RegisterCStart, RegisterCSize, SlaveID
 37    message = uart1.read()
 38    if message != None:
 39      CRC = Modbus_CRC(message[0 : len(message) - 2])
 40      if (CRC[0] == message[len(message) - 1] and CRC[1] == message[len(message) - 2] and SlaveID == message[0]):
 41        function = message[1]
 42        address = (message[2] << 8) + message[3]
 43        # Read multiple coils
 44        if function == 1:
 45          quantity = (message[4] << 8) + message[5]        
 46          if quantity >= 0x01 and quantity <= 0x07CD0:
 47            if address >= RegisterCStart and address + quantity <= RegisterCStart + RegisterCSize:          
 48              message = [0] * (int((7 + quantity) / 8) + 5)
 49              message[0] = SlaveID
 50              message[1] = function
 51              message[2] = int((7 + quantity) / 8)
 52              i0 = 0           
 53              for i1 in range(message[2]):
 54                message[3 + i1] = 0
 55                for i2 in range(8):
 56                  if i0 < quantity:
 57                    if RegisterC[address - RegisterCStart + i0]:
 58                      message[3 + i1] += (1 << i2)
 59                    i0 += 1
 60                  else:
 61                    break  
 62              CRC = Modbus_CRC(message[0 : len(message) - 2])
 63              message[len(message) - 1] = CRC[0]
 64              message[len(message) - 2] = CRC[1]
 65              message = bytes(message)            
 66            else:                
 67              message = GenerateErrorMessage(SlaveID, function, 2)
 68          else:
 69            message = GenerateErrorMessage(SlaveID, function, 3)        
 70        # Read multiple holding registers
 71        elif function == 3:
 72          quantity = (message[4] << 8) + message[5]
 73          if quantity >= 0x01 and quantity <= 0x7D:
 74            if address >= RegisterHStart and address + quantity <= RegisterHStart + RegisterHSize:
 75              message = [0] * (quantity * 2 + 5)
 76              message[0] = SlaveID
 77              message[1] = function
 78              message[2] = 2 * quantity
 79              for i0 in range(quantity):
 80                message[3 + 2*i0] = (RegisterH[address + i0 - RegisterHStart] & 0xFF00) >> 8
 81                message[4 + 2*i0] = RegisterH[address + i0 - RegisterHStart] & 0xFF
 82              CRC = Modbus_CRC(message[0 : len(message) - 2])
 83              message[len(message) - 1] = CRC[0]
 84              message[len(message) - 2] = CRC[1]
 85              message = bytes(message)
 86            else:
 87              message = GenerateErrorMessage(SlaveID, function, 2)
 88          else:
 89            message = GenerateErrorMessage(SlaveID, function, 3)     
 90        # Write single coil
 91        elif function == 5:        
 92          if (message[4] == 0x00 or message[4] == 0xFF) and message[5] == 0x00:
 93            if address >= RegisterCStart and address < RegisterCStart + RegisterCSize:
 94              RegisterC[address - RegisterCStart] = (message[4] == 0xFF)
 95            else:
 96              message = GenerateErrorMessage(SlaveID, function, 2)
 97          else:
 98            message = GenerateErrorMessage(SlaveID, function, 3)
 99        # Write single holding register
100        elif function == 6:
101          if address >= 0 and address <= 0xFFFF:
102            if address >= RegisterHStart and address < RegisterHStart + RegisterHSize:
103              RegisterH[address - RegisterHStart] = (message[4] << 8) + message[5]
104            else:
105              message = GenerateErrorMessage(SlaveID, function, 2)
106          else:
107            message = GenerateErrorMessage(SlaveID, function, 3)
108        # Write multiple coils
109        elif function == 15:
110          quantity = (message[4] << 8) + message[5]         
111          if (quantity >= 1 and quantity <= 0x07B0 and message[6] == int((quantity + 7) / 8)):
112            if address >= RegisterCStart and address + quantity <= RegisterCStart + RegisterCSize:
113              i0 = 0
114              for i1 in range(message[6]):
115                for i2 in range(8):
116                  if i0 < quantity:
117                    RegisterC[address + i0 - RegisterCStart] = (message[7 + i1] & (1 << i2) > 0)
118                    i0 += 1
119                  else:
120                    break
121              message = [0] * 8
122              message[0] = SlaveID
123              message[1] = function 
124              message[2] = address >> 8
125              message[3] = 0xFF & address
126              message[4] = quantity >> 8
127              message[5] = 0xFF & quantity
128              CRC = Modbus_CRC(message[0 : 6])
129              message[6] = CRC[1]
130              message[7] = CRC[0]
131              message = bytes(message)
132            else:
133              message = GenerateErrorMessage(SlaveID, function, 2)
134          else:
135            message = GenerateErrorMessage(SlaveID, function, 3)          
136        # Write multiple holding registers
137        elif function == 16:
138          quantity = (message[4] << 8) + message[5]
139          if quantity >= 0x01 and quantity <= 0x7B and message[6] == 2 * quantity:
140            if address >= RegisterHStart and address + quantity <= RegisterHStart + RegisterHSize:
141              for i0 in range(quantity):
142                RegisterH[address + i0 - RegisterHStart] = (message[7 + 2 * i0] << 8) + message[8 + 2 * i0]        
143            else:
144              message = GenerateErrorMessage(SlaveID, function, 2)
145          else:
146            message = GenerateErrorMessage(SlaveID, function, 3)
147        uart1.write(message) 
148      Label0.setText(str(RegisterH))
149      text = "["
150      for i0 in range(len(RegisterC) - 1):      
151        if RegisterC[i0]:
152          text += "1, "
153        else:
154          text += "0, "
155      if RegisterC[len(RegisterC) - 1]:
156        Label1.setText(text + "1]")
157      else:
158        Label1.setText(text + "0]") 
159      pass
160  
161  # Start values of holding and coil registers
162  RegisterH = [10, 568, 65534, 21]
163  RegisterHStart = 0x10
164  RegisterHSize = len(RegisterH)
165  RegisterC = [False, True, False, False, True, False, True, True, False]
166  RegisterCStart = 0x20
167  RegisterCSize = len(RegisterC)
168  SlaveID = 0x11
169
170  function = 0
171  address = 0
172  quantity = 0
173  message = None
174  i = 0
175  data = None
176  CRC = None
177
178  uart1 = machine.UART(1, tx=17, rx=16)
179  uart1.init(9600, bits=8, parity=0, stop=1)
180  timerSch.run('ModbusSlave', 100, 0x00)