GithubHelp home page GithubHelp logo

Read Errors about dps-1200fb HOT 4 OPEN

raplin avatar raplin commented on September 6, 2024
Read Errors

from dps-1200fb.

Comments (4)

theroof24 avatar theroof24 commented on September 6, 2024

image

from dps-1200fb.

theroof24 avatar theroof24 commented on September 6, 2024

image

from dps-1200fb.

theroof24 avatar theroof24 commented on September 6, 2024

Additional note: fan set speed function works
And code was recompiled for python 3 on Raspberry Pi 3B

from dps-1200fb.

theroof24 avatar theroof24 commented on September 6, 2024

This is the new code created to work on python3.9.2 on Thonny 3.3.14


import time
import traceback
from smbus2 import SMBus, i2c_msg  # pip install smbus2

class PowerSupply:
    def __init__(self, i2c_bus=1, address=7):  # address 0..7 reflecting the i2c address select bits on the PSU edge connector
        self.i2c = SMBus(i2c_bus)  # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)
        self.address = 0x58 + address
        self.EE_address = 0x50 + address

        self.numReg = 0x58 // 2  # literally just 44
        self.lastReg = [0 for _ in range(self.numReg)]
        self.minReg = [0xffff for _ in range(self.numReg)]
        self.maxReg = [0 for _ in range(self.numReg)]

    # not very interesting - read the 24c02 eeprom (you can write it too)
    def read_eeprom(self):
        pos = 0
        data = ""
        while pos < 256:  # fixme
            data += self.i2c.read_i2c_block_data(self.EE_address, pos, 32)
            pos += 32
        print(" ".join(["%02x" % ord(d) for d in data]))

    def read_var(self, address, write_ints, read_count):
        # https://github.com/kplindegaard/smbus2  'dual i2c_rdrw'
        write = i2c_msg.write(address, write_ints)
        # if read_count:
        read = i2c_msg.read(address, read_count)
        self.i2c.i2c_rdwr(write, read)
        return [chr(n) for n in list(read)]

    def write_var(self, address, bytes_):
        as_ints = [ord(d) for d in bytes_]
        self.i2c.write_i2c_block_data(address, as_ints[0], as_ints[1:])

    # the most useful
    def read_dps1200(self, reg, count):
        cs = reg + (self.address << 1)
        reg_cs = ((0xff - cs) + 1) & 0xff  # this is the 'secret sauce' - if you don't add the checksum byte when reading a register the PSU will play dumb
        # checksum is [ i2c_address, reg_address, checksum ] where checksum is as above.
        write_ints = [reg, reg_cs]  # send register plus checksum
        # this should write [address,register,checksum] and then read two bytes (send address+read bit, read lsb, read msb)
        # note this device doesn't require you to use a "repeated start" I2C condition - you can just do start/write/stop;start/read/stop
        return self.read_var(self.address, write_ints, count)

    # Writable registers/cmds:
    # Nothing very interesting discovered so far; you can control the fan speed. Not yet discovered how to turn
    # the 12v output on/off in software.
    #
    # Random notes below:
    #
    # "interesting" write cmds;
    # 0x35  (if msb+lsb==0 clear c4+c5)
    #
    # 0x3b - checks bit 5 of lsb of write data, if set does (stuff), if bit 6 set does (other stuff), low 5 bits
    # stored @ 0xc8 (read with 0x3a)
    #        b0..2= 1=(see 0x344); sets 'fan_related' to 9000
    #               2=(see 0x190a) sets 'surprise_more_flags bit 6'
    #        b3=
    #        b4=
    #        b5=
    #        b6=
    #
    # ..cmds that write to ram..
    # 0x31 - if data=0 sets i2c_flags1 2 & 7 (0x2d8) = resets total_watts and uptime_secs
    # 0x33 - if data=0 resets MaxInputWatts
    # 0x35 - if data=0 resets MaxInputCurrent
    # 0x37 - if data=0 resets MaxRecordedCurrent
    # 0x3b - sets yet_more_flags:5, checks write data lsb:5
    # 0x3d - something like set min fan speed
    # 0x40  (writes 0xe5)
    # 0x41  (writes 0xd4)   <<d4= fan speed control - write 0x4000 to 0x40 = full speed fan (sets 'surprise_mnore_flags:5)')
    # 0x45  sets some voltage threshold if written  (sets a4:1)
    # 0x47  sets some other threshold when written (sets a4:2) -
    # 0x49  sets a4:3
    # 0x4b  sets a4:4
    # 50/51  (writes 0xee/ef) - default is 3200
    # 52/53 (0xa5/6) - some temp threshold
    # 54/55 (0xa7/8)  (sets some_major_flags:5) - eeprom related
    # 56/57 (0xa9/a)  (a9 is EEPROM read address with cmd 57)

    def write_dps1200(self, reg, value):
        val_lsb = value & 0xff
        val_msb = value >> 8
        cs = (self.address << 1) + reg + val_lsb + val_msb
        reg_cs = ((0xff - cs) + 1) & 0xff  # the checksum is the 'secret sauce'
        write_ints = [reg, val_lsb, val_msb, reg_cs]  # write these 4 bytes to i2c
        bytes_ = "".join([chr(n) for n in write_ints])
        return self.write_var(self.address, bytes_)

    def test_write(self):
        # try fuzzing things to see if we can find power on/off.. (not yet) 0x40 controls fan speed (bitfield)
        # for n in [0x35,0x3b,0x40,0x50,0x52,0x54,0x56]:
        for n in [0x35, 0x3b, 0x50, 0x52, 0x54, 0x56]:
            # for n in [0x40]:
            for b in range(16):
                value = (1 << b) - 1
                print("%02x : %04x" % (n, value))
                self.write_dps1200(n, value)
                time.sleep(0.5)

    # Readable registers - some of these are slightly guessed - comments welcome if you figure something new out or have a correction.
    REGS = {
        # note when looking at PIC disasm table; "lookup_ram_to_read_for_cmd", below numbers are <<1
        # the second arg is the scale factor
        0x01: ["FLAGS", 0],  # not sure but includes e.g. "power good"
        0x04: ["INPUT_VOLTAGE", 32.0],  # e.g. 120 (volts)
        0x05: ["AMPS_IN", 128.0],
        0x06: ["WATTS_IN", 2.0],
        0x07: ["OUTPUT_VOLTAGE", 254.5],
        # pretty sure this is right; unclear why scale is /254.5 not /256 but it's wrong - can't see how they'd not be measuring this to high precision
        0x08: ["AMPS_OUT", 128.0],
        # rather inaccurate at low output <10A (reads under) - appears to have internal load for stability so always reads about 1.5 even open circuit
        0x09: ["WATTS_OUT", 2.0],
        0x0d: ["TEMP1_INTAKE_FAHRENHEIT", 32.0],  # this is a guess - may be C or F but F looks more right
        0x0e: ["TEMP2_INTERNAL_FAHRENHEIT", 32.0],
        0x0f: ["FAN_SPEED_RPM", 1],
        # total guess at scale but this is def fan speed it may be counting ticks from the fan sensor which seem to be typically 2 ticks/revolution
        0x1a: ["?flags", 0],  # unknown (from disassembly)
        0x1b: ["?voltage", 1],  # unknown (from disassembly)
        (0x2c >> 1): ["WATT_SECONDS_IN", -4.0],
        # this is a special case; uses two consecutive regs to make a 32-bit value (the minus scale factor is a flag for that)
        (0x30 >> 1): ["ON_SECONDS", 2.0],
        (0x32 >> 1): ["PEAK_WATTS_IN", 2.0],
        (0x34 >> 1): ["MIN_AMPS_IN", 128.0],
        (0x36 >> 1): ["PEAK_AMPS_OUT", 128.0],
        (0x3A >> 1): ["COOL_FLAGS1", 0],  # unknown (from disassembly)
        (0x3c >> 1): ["COOL_FLAGS2", 0],  # unknown (from disassembly)
        (0x40 >> 1): ["FAN_TARGET_RPM", 1],  # unknown (from disassembly)
        (0x44 >> 1): ["VOLTAGE_THRESHOLD_1", 1],  # unknown (from disassembly)
        (0x46 >> 1): ["VOLTAGE_THRESHOLD_2", 1],  # unknown (from disassembly)
        (0x50 >> 1): ["MAYBE_UNDERVOLTAGE_THRESH", 32.0],  # unknown (from disassembly)
        (0x52 >> 1): ["MAYBE_OVERVOLTAGE_THRESH", 32.0],  # unknown (from disassembly)
        # reading 0x57 reads internal EEPROM space in CPU (just logging info, e.g. hours in use)
    }

    def read_dps1200_register(self, reg):
        data = self.read_dps1200(reg << 1, 3)  # if low bit set returns zeros (so use even # cmds)
        # check checksum (why not)
        reply_cs = 0
        for d in data:
            reply_cs += ord(d)
        reply_cs = ((0xff - reply_cs) + 1) & 0xff  # check reply checksum (not really required)
        
        data = data[:-1]
        value = ord(data[0]) | ord(data[1]) << 8
        return value

    def read(self):
        for n in range(self.numReg):
            try:
                value = self.read_dps1200_register(n)
                self.minReg[n] = min(self.minReg[n], value)
                self.maxReg[n] = max(self.maxReg[n], value)
                name = ""
                if n in self.REGS:
                    name, scale = self.REGS[n]
                    if scale < 0:
                        scale = -scale
                        value += self.read_dps1200_register(n + 1) << 16
                else:
                    scale = 1
                print("%02x\t%04x\t" % (n << 1, value))
                if scale:
                    print("%d\t%d\t%d\t(%d)\t%.3f\t%s" % (
                        value, self.minReg[n], self.maxReg[n], self.maxReg[n] - self.minReg[n], value / scale, name))
                else:
                    print("%s\t%s" % (bin(value), name))
            except Exception as ex:
                print("r %02x er %s" % (n, ex))
                print(traceback.format_exc())
        return
        # addr = self.address

    def force_fan_rpm(self, rpm):
        # sets (forces) fan speed; lowest it will accept is 3300rpm conversely, 16000RPM is very, very fast indeed.
        # You probably (untested) have to keep writing this regularly if you want to override what the firmware wants
        # to do based on its temp sensors
        self.write_dps1200(0x40, rpm)

ps = PowerSupply(i2c_bus=1, address=7)
# ps.readEEPROM() 

#ps.read_dps1200(0x04, 1)  # test - don't do this unless you really want to override the fan for some reason
##s.force_fan_rpm(1000)
ps.read()
#while True:
    #ps.force_fan_rpm(1000)
`

from dps-1200fb.

Related Issues (8)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.