依赖文件

pymodbus==3.6.3
pyserial==3.5
  • 1.
  • 2.
import binascii
import math
import time
import serial
from pymodbus.client import ModbusSerialClient as ModbusClient
import struct

# # 设备串口配置
# port = '/dev/cu.usbserial-1130'  # 根据实际情况修改为实际串口号
# baudrate = 9600
# parity = 'N'
# stopbits = 1
# bytesize = 8
# # 创建串口连接
# ser = serial.Serial(port, baudrate, parity=parity, stopbits=stopbits, bytesize=bytesize)
# # 创建Modbus客户端
# print("Modbus RTU Client Connected")
# client = ModbusClient(port=ser,baudrate=9600)
# print(client)
# # 假设从地址1开始写入固件数据,每个寄存器存储两个字节
# if client.connect():
#     print("Modbus RTU Client Connected")
# else:
#     print("Failed to connect to Modbus RTU Client")
# firmware_file_path = 'firmware.bin'
# with open(firmware_file_path, 'rb') as f:
#     firmware_data = f.read()
# # 模拟将固件数据按每两个字节分组并写入Modbus寄存器
# register_address = 0x0001  # 起始寄存器地址
# for i in range(0, len(firmware_data), 2):
#     if i + 1 < len(firmware_data):  # 确保不超出范围
#         register_value = struct.unpack('>H', firmware_data[i:i+2])[0]  # 大端模式读取两个字节
#         client.write_register(register_address, register_value)
#         register_address += 1
#         # 可能需要根据设备响应速度调整延时
#         time.sleep(0.1)
# # 关闭Modbus客户端和串口连接
# client.close()
# ser.close()

# print("Firmware data sent via Modbus.")

# # 在这里添加向设备发送固件更新结束命令的逻辑,具体命令取决于设备实现



from pymodbus.client import ModbusSerialClient
from pymodbus.exceptions import ModbusException, ConnectionException
import logging
import hashlib

# 配置日志记录
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
client = ModbusSerialClient(port='/dev/cu.usbserial-B001VTZP', baudrate=57600,
                      stopbits=1, bytesize=8, parity='N',slaveaddress=1)    # 看文档,method='rtu'貌似没用

firmware_file_path = 'N00.bin'


def read_temperature_and_humidity(client):
    try:
        # 计算md5 数值是多少 
        # 计算分为多少包
        # 发送查询modbus版本
        # 发送OTA md5 文件

        # 读取现在的版本信息
        response = client.read_holding_registers(address=100, count=8, slave=1)
        if not response.isError():
            print("Register Values: ", response.registers)
        else:
            print("Failed to read registers")

        # 读取 文件的md5 并下发给设备
        with open(firmware_file_path, 'rb') as f:
                firmware_data = f.read() # read file as bytes
                readable_hash = hashlib.md5(firmware_data).hexdigest();
                file_length = len(firmware_data)
                file_length_bytes = file_length.to_bytes(4, byteorder='big')
                file_length_hex_str = binascii.hexlify(file_length_bytes).decode()
                file_length_byte_array = [file_length_hex_str[i:i+2] for i in range(0, len(file_length_hex_str), 2)]
        hash_bytes = bytes.fromhex(readable_hash)
        hex_string = binascii.hexlify(readable_hash.encode()).decode()
        byte_array = [hex_string[i:i+2] for i in range(0, len(hex_string), 2)]
        # 将文件长度的字节数组与MD5字节数组合并
        combined_byte_array = file_length_byte_array + byte_array
        print(type(combined_byte_array))
        print(combined_byte_array)
        byte_values = [int(x, 16) for x in combined_byte_array]
        combined_bytes = bytes(byte_values)
        #把数组写入write_registers
            # 将合并后的字节数据按照寄存器大小(假设每个寄存器是两个字节)分割成整数列表
        register_values = struct.unpack('>' + 'H' * (len(combined_bytes) // 2), combined_bytes)
        print(register_values)
        print("*****************")
        write_response=client.write_registers(address=108, values=register_values, slave=1)
        if not write_response.isError():
            print("回复成功")
        else:
            print("回复失败")
        # 下发分片任务
        i = 0
        register_address = 0x0001  # 起始寄存器地址
        chunk_size_bytes = 200  # 每次发送的字节数量,需确保是寄存器数量的整数倍(每个寄存器2字节)
        #需要分多少包 
        # mdobus_size = hex(int(math.ceil(len(firmware_data) / 200))).to_bytes(2, byteorder='big').hex()
        mdobus_size = (math.ceil(len(firmware_data) / 200))
        print("分片任务数:", mdobus_size)
        while i * chunk_size_bytes < len(firmware_data):
           
            
            # 假设 client 支持一次性写入多个寄存器,例如使用 write_registers 方法
            # 打印数值
            # 还剩200个字节则
            xianzai_len=chunk_size_bytes*i
            print(len(firmware_data))
            print(xianzai_len)
            print("第", i, "个分片,剩余", len(firmware_data)-chunk_size_bytes*i, "个字节")
            if chunk_size_bytes*i<=len(firmware_data)-200:
                chunk = firmware_data[i*chunk_size_bytes:(i+1)*chunk_size_bytes]
                register_values = struct.unpack('>' + 'H' * (chunk_size_bytes // 2), chunk)
                data_len=200;
            else:
                data_len=len(firmware_data)-chunk_size_bytes*i
                chunk = firmware_data[i * chunk_size_bytes:i * chunk_size_bytes + data_len]
                register_values = struct.unpack('>' + 'H' * (data_len // 2), chunk)
                
            # 加上分包和总包数
            # 将 mdobus_size 添加到 register_values 的开头
            register_values_list=list(register_values)
            register_values_list.insert(0, mdobus_size)
            register_values_list.insert(0, 0)
            register_values_list.insert(0, i+1)
            register_values_list.insert(0, 0)
            register_values_list.insert(0, data_len)
            print("register_values_list", register_values_list)
            register_values = tuple(register_values_list)
            print("*****************")
            print("register_values", register_values)
            write_response=client.write_registers(address=126, values=register_values, slave=1)
            if not write_response.isError():
                print("Multiple registers written successfully")
            else:
                print("Failed to write multiple registers")
            print(i * chunk_size_bytes)
            print(len(firmware_data))
            i += 1
            print(i * chunk_size_bytes)
            print("*************")
            time.sleep(0.3)


        # 可能需要根据设备响应速度调整延时
            

#         i = 0
#         register_address = 0x0001  # 起始寄存器地址
#         chunk_size_bytes=200
#         total_chunks = (len(firmware_data) + chunk_size_bytes - 1) // chunk_size_bytes  # 计算总分片数
#         chunk_size_bytes -= 2  # 减去分片序号和总分片数占用的两个字节

#         while i * chunk_size_bytes < len(firmware_data):
#             chunk = firmware_data[i*chunk_size_bytes:(i+1)*chunk_size_bytes]
            
#             # 添加分片序号(高位字节在前)
#             slice_number = i
#             packet = slice_number.to_bytes(2, byteorder='big') + chunk
            
#             # 假设 client 支持一次性写入多个寄存器,例如使用 write_registers 方法
#             register_values = struct.unpack('>' + 'H' * ((chunk_size_bytes + 2) // 2), packet)
            
#             print(register_values)

#             write_response = client.write_registers(address=register_address, values=register_values, slave=1)

#             if not write_response.isError():
#                 print("Multiple registers written successfully")
#             else:
#                 print("Failed to write multiple registers")

#             register_address += (chunk_size_bytes + 2) // 2  # 更新寄存器地址,因为每个寄存器2字节,且包含了序号信息

#             i += 1

#         # 可能需要根据设备响应速度调整延时
#             time.sleep(0.1)

# # 最后一个数据块可能不足一个完整的chunk_size_bytes,需单独处理
#         if i * chunk_size_bytes < len(firmware_data):
#             remaining_chunk = firmware_data[i*chunk_size_bytes:]
#             slice_number = i
#             packet = slice_number.to_bytes(2, byteorder='big') + remaining_chunk
#             register_values = struct.unpack('>' + 'H' * ((len(remaining_chunk) + 2) // 2), packet)
#             write_response = client.write_registers(address=register_address, values=register_values, slave=1)

#             if not write_response.isError():
#                 print("Remaining registers written successfully")
#             else:
#                 print("Failed to write remaining registers")


        # 读取寄存器地址0和1上的4个字节(两个寄存器)
        # result = client.read_input_registers(address=0, count=3, unit=1)  # 这个错了,这是读取输入寄存器的)0x04
        # result = client.read_holding_registers(address=0, count=3, unit=1)  # 这个才是读取输入寄存器的0x03  # unit参数错了,当前pymodbus版本没有这个参数,搞乌龙了,要不是用filelocator搜索函数用法,还真不知道- -
        # result = client.read_holding_registers(
        #     address=0, count=2, slave=1)  # 读取输入寄存器的0x03 # 读两个寄存器就ok,卖家说第三个寄存器是预留的,不用读

        # if result.isError():
        #     # 处理错误
        #     print("读取错误:", result)
        #     return None, None

        # 将读取到的结果转换为温度和湿度
        # registers = result.registers
        # temperature_reg = registers[0]
        # humidity_reg = registers[1]

        # # 检查是否有探头错误
        # if temperature_reg == 0x8000 or humidity_reg == 0x8000:
        #     print("探头错误")
        #     return None, None

        # # 计算实际的温度和湿度值
        # temperature = temperature_reg * 0.1
        # humidity = humidity_reg * 0.1

        # # 格式化温度和湿度值,保留一位小数
        # temperature = round(temperature, 1)
        # humidity = round(humidity, 1)

        return 1, 1

    except ModbusException as e:
        print("Modbus异常:", e)
        return None, None
    except Exception as e:
        # 捕获除ModbusException之外的所有异常
        print(f"An error occurred: {e}")
        return None, None


def main():
    try:
        if client.connect():  # 尝试连接到Modbus服务器/设备
            temperature, humidity = read_temperature_and_humidity(client)
            # if temperature is not None and humidity is not None:
            #     print(f"温度: {temperature}°C, 湿度: {humidity}%RH")
            client.close()  # 关闭连接
        else:
            print("无法连接到Modbus设备")
    except ConnectionException as e:
        print("连接异常:", e)


if __name__ == "__main__":
    main()
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
#导出 pip
pip freeze > requirements.txt
# 查看串口
···
ls /dev/cu.usbserial-*
···
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.