KivyMD TCP Modbus客户端应用无法从服务器读取数据求助
KivyMD TCP Modbus客户端数据读取失败问题排查与解决
我基于教学视频开发了一款KivyMD TCP Modbus客户端应用,服务器连接步骤成功,但进入数据采集页面时出现数据读取失败异常:
Error during data status reading. Error during data setpoint reading. Error during data tempOven reading. Error during data tempOven reading. Error during data tempOven reading.
手动复刻教学代码,无语法错误但功能异常,以下是完整代码模块,求排查与解决建议:
main.py
from kivymd.app import MDApp from kivymd.uix.screen import MDScreen from datacard import CardCoil, CardHoldingRegister, CardInputRegister from pyModbusTCP.client import ModbusClient from kivymd.uix.snackbar import Snackbar from kivy.clock import Clock class MyWidget(MDScreen): """ Contructor """ def __init__(self,tags,**kwargs): self._clientMOD= ModbusClient() super().__init__(**kwargs) self._tags=tags self._ev=[] #pt4 min 58 for tag in self._tags: if tag["type"]=="input": self.ids.modbus_data.add_widget(CardInputRegister(tag,self._clientMOD)) elif tag["type"]== "holding": self.ids.modbus_data.add_widget(CardHoldingRegister(tag,self._clientMOD)) elif tag["type"]=="coil": self.ids.modbus_data.add_widget(CardCoil(tag,self._clientMOD)) def connect(self): if self.ids.bt_con.text== "CONNECT": try: self.ids.bt_con.text= "DISCONNECT" self._clientMOD.host= self.ids.hostname.text self._clientMOD.port = int(self.ids.port.text) self._clientMOD.open() Snackbar(text="Connected succesfully",bg_color=(0,1,0,1)).open() self._ev=[] for card in self.ids.modbus_data.children: if card.tag['type'] == "holding" or card.tag['type']== "coil": self._ev.append(Clock.schedule_once(card.update_card)) else: self._ev.append(Clock.schedule_interval(card.update_card,1)) except Exception as e: print("Erro de conexao com servidor: ", e.args) else: self.ids.bt_con.text="CONNECT" for event in self._ev: event.cancel() self._clientMOD.close() Snackbar(text="Disconnected",bg_color=(1,0,0,1)).open() class BasicApp(MDApp): __tags= [{'name':'tempOven','description':'Oven Tempture','unit':'ºC','address':1000,'type':"input"}, {'name':'setpoint','description':'Desired Tempture','unit':'ºC','address':2000,'type': "holding"}, {'name':'status','description':'Actuator state','address':1000,'type':"coil"}, ] def build(self): self.theme_cls.primary_palette= "Orange" self.theme_cls.primary_hue= "700" self.theme_cls.accent_palette="Orange" return MyWidget(self.__tags) if __name__=='__main__': BasicApp().run()
datacard.py
from kivymd.uix.card import MDCard,MDCard from pyModbusTCP.client import ModbusClient class DataCard(MDCard): title= "Data Card" def __init__(self,tag,mbusClient,**kwargs): self.tag= tag self.title=self.tag['description'] self._mbusclient = mbusClient super().__init__(**kwargs) def update_card(self,dt): try: if self._mbusclient.is_open(): self.set_data(self._read_data(self.tag['address'],1)[0]) except Exception as e: print("Error during data",self.tag['name'] ,"reading.") def write_card(self): try: if self._mbusclient.is_open(): self._write_data_fcn(self.tag['address'],self._get_data()) except Exception as e: print("Error during data" ,self.tag['name'] ,"writting.") class CardHoldingRegister(DataCard): def __init__(self,tag,mbusClient,**kwargs): super().__init__(tag,mbusClient,**kwargs) self._read_data=self._mbusclient.read_holding_registers self._write_data_fcn= self._mbusclient.write_single_register def set_data(self, data): self.ids.textfield.text= str(data) def get_data(self): return int(self.ids.textfield.text) class CardInputRegister(DataCard): def __init__(self,tag,mbusClient,**kwargs): super().__init__(tag,mbusClient,**kwargs) self._read_data= self._mbusclient.read_input_registers def set_data(self, data): self.ids.label.text= str(data) class CardCoil(DataCard): def __init__(self,tag,mbusClient,**kwargs): super().__init__(tag,mbusClient,**kwargs) self._read_data= self._mbusclient.read_coils self._write_data_fcn= self._mbusclient.write_single_coil def set_data(self, data): self.ids.switch.active= data #True / False def get_data(self): return self.ids.switch.active #RETURN = ajuste
.kv布局文件
#:kivy 1.11.1 <MyWidget>: MDBoxLayout: orientation:'vertical' MDTopAppBar: title: "Informatica industrial" MDBottomNavigation: panel_color: app.theme_cls.accent_color text_color_normal:0.4,0.4,0.4,1 text_color_active:0.8,0.8,0.8,1 MDBottomNavigationItem: name:"config" text:"CONFIGURATION" icon: "cog" MDBoxLayout: orientation: 'vertical' padding: "20p" spacing: "50dp" Image: source:"imgs/modbus.png" pos_hint: {"center_x":0.5,"center_y":0.5} size_hint: {1,0.2} MDTextField: id:hostname text:"127.0.0.1" hint_text: "IP Address" size_hint: {0.3, None} height: "60dp" #tentatativa e erro pos_hint: {"center_x":0.5,"center_y":0.5} MDTextField: id:port text:"502" hint_text: "Port" size_hint: {0.3, None} height: "60dp" pos_hint: {"center_x":0.5,"center_y":0.4} MDRoundFlatIconButton: id: bt_con text:"CONNECT" icon:'connection' pos_hint:{"center_x":0.5,"center_y":0.3} on_release: root.connect() MDBottomNavigationItem: name:"data" text:"DADOS" icon:"chart-donut" ScrollView: size_hint: (1,None) size: 800,600 #define o tamanho como o da janela 800x600 bar_pos_y: 'left' bard_width: 20 effect_cls: 'ScrollEffect' MDStackLayout: id: modbus_data size_hint: (1, None) padding: 0.05*600,"150dp" spacing: (800/5 - 2*0.05*800)/3 adaptive_height: True <DataCard>: orientation: 'vertical' padding: '10dp' size_hint:None,None size: 600/5 , "90dp" pos_hint:{"center_x": 0.5, "center_y":0.5} MDLabel: text: root.title size_hint_y: None height:self.texture_size[1] pos_hint: {'top':1} MDSeparator: height: "1dp" <CardHoldingRegister>: MDTextField: id:textfield helper_text: "Pressione Enter para enviar os dados" helper_text_mode:'persistent' multiline: False on_text_validate: root.write_card <CardInputRegister>: MDLabel: id:label <CardCoil>: MDSwitch: id: switch
问题排查与解决建议
- Modbus地址偏移修正
pyModbusTCP默认使用0起始地址,但多数Modbus服务器用1起始地址。你的标签地址设置为1000、2000,需要将地址减1后传入读取函数:
修改DataCard的update_card方法:
read_result = self._read_data(self.tag['address'] - 1, 1)
- 空读取结果处理
当读取失败时,_read_data会返回None,直接取[0]会报错,需先校验结果:
def update_card(self,dt): try: if self._mbusclient.is_open(): read_result = self._read_data(self.tag['address'] - 1, 1) if read_result and len(read_result) > 0: self.set_data(read_result[0]) else: print(f"读取{self.tag['name']}失败:无返回数据") except Exception as e: print(f"读取{self.tag['name']}出错:{str(e)}")
- 方法名拼写错误修复
DataCard的write_card方法中调用了不存在的_get_data(),应改为子类定义的get_data():
def write_card(self): try: if self._mbusclient.is_open(): self._write_data_fcn(self.tag['address'] - 1, self.get_data()) except Exception as e: print(f"写入{self.tag['name']}出错:{str(e)}")
- 调度延迟调整
schedule_once默认立即执行,可能在连接未完全建立时触发读取,添加微小延迟:
self._ev.append(Clock.schedule_once(card.update_card, 0.1))
- 服务器端验证
- 确认Modbus服务器运行正常,IP/端口正确,无防火墙拦截
- 用Modbus Poll等工具验证:1000(或999)地址的输入寄存器、2000(或1999)地址的保持寄存器、1000(或999)地址的线圈寄存器是否可正常访问
内容的提问来源于stack exchange,提问作者caio_alBR
相关产品推荐
相关产品推荐

