News:

PROTON pic BASIC Compilers for PIC, PIC24, dsPIC33

Main Menu

BITMAIN APW9 power supply

Started by tolyan249, Mar 09, 2026, 02:33 PM

Previous topic - Next topic

tolyan249

There is a BITMAIN APW9 power supply, and I don't know if anyone has worked with it. It regulates the voltage using i2c, and I can't even scan its address because the 9th SDA pulse is always 0. As a result, the ACK always comes. If I remove the connector from the power supply, the 9th SDA pulse becomes a perfect 1.

Stephen Moss

#1
It may help people to resolve your problem if you could supply a link to/data for the I2C protocol, and the code you are using. Without that it would be difficult to see what you may be doing wrong.
After a quick search the only reference I could find was...
QuoteThe 4-PIN signal connector is the interface between the external control board and the power supply. The SDA/SCL is an I2C bus. The output voltage of the power supply can be adjusted through I2C. EN is the ENABLE signal of the power supply. The control board can enable the power supply through EN, active high.
From that, my guess would be that perhaps you have to set the EN pin Low to disable the PSU (no output/enable comms?) while you are communicating with it and then set it High to accept and either output the new voltage following correct communications, or the previous voltage if the instructions received were rejected.
But with so little information to go in I may be wrong.

tolyan249

#2
Yes, that's right, this power supply is running, but I don't know how to access it. I've read a lot about it, but I can't find complete information about its address and monitoring and control registers. It's not about the code; the code works on any i2c device, but it doesn't want to work on the APW9. Therefore, I'm asking if anyone has worked with this power supply.
It can receive data even when it's not turned on, as it has an additional 12V power supply. The pic16F1704 controller acts as a slave, managing and providing monitoring data.

 Thank you for your assistance.

RGV250

Have you tried asking the company?
There is a contact us at the bottom of this page. https://support.bitmain.com/hc/en-us/articles/900000069726-APW9-14-5V-21V-Specifications

Regards,
Bob

tolyan249

I don't think that BITMAIN will reveal its secrets about I2C.

Stephen Moss

Quote from: tolyan249 on Today at 01:08 AMI don't think that BITMAIN will reveal its secrets about I2C.
That would depend on whether or not they intended access via that port to be provided exclusively through another of there hardware/software products or for general purpose access by anyone, in which case they would have to make the specifications available so the people could create their own interface.

I am confused, from...
Quote from: tolyan249 on Mar 09, 2026, 02:33 PMIt regulates the voltage using i2c, and I can't even scan its address because the 9th SDA pulse is always 0.
it sounds like you want to be able to set its output voltage and current limit via the I2C port, which would make the I2C port on the PSU as slave, which is why you are trying to scan for its address.

However, in a following post you say that...
Quote from: tolyan249 on Mar 11, 2026, 11:18 AMThe pic16F1704 controller acts as a slave, managing and providing monitoring data.
Is the pic16F1704 the external device you are using to try an communicate with the PSU or the device in the PSU?
If it is the external device and it is a acting as a slave then that would suggest that the I2C port on the power supply must be then be acting as a master, if so then why are you trying to scan for the Address? Slaves do not need to know the address of the Master.

Quote from: tolyan249 on Mar 11, 2026, 11:18 AMIt's not about the code; the code works on any i2c device, but it doesn't want to work on the APW9.
The fact that is is not working with the APW9 suggests that is not the case, I2C typically runs at 100KHz, but is sometime run at 400KHz, particularly if there is a lot of data to send. Have you tried both frequencies in case you are running at the wrong speed?

tolyan249

#6
Yes, I tried 2 frequencies, and I tried arduino, thinking I might find something there.


APW9 (slave) , I assembled on PIC16F628A (master) , I2C address scanner, power supply monitoring.

I ordered a complete ASIC S17 assembly, and I will use a logic analyzer to monitor the i2c communication between the power supply and the miner.

https://github.com/TomyGM/Bitmain_APW9_Monitor
This is what the AI found, a Python program.
there is an address written 0x58 , but I can't find it to confirm the work.


!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Simple monitor for Bitmain APW9 power supply via I2C.
"""

import smbus
import time

I2C_ADDR = 0x58  # Typical I2C address of APW9

REG_STATUS = 0x00
REG_VOUT_H = 0x01
REG_VOUT_L = 0x02
REG_IOUT_H = 0x03
REG_IOUT_L = 0x04
REG_TEMP = 0x05

bus = smbus.SMBus(1)  # I2C bus number, usually 1 on Raspberry Pi


def read_word(high_reg, low_reg):
    """Read two I2C registers and combine into a 16-bit word."""
    high = bus.read_byte_data(I2C_ADDR, high_reg)
    low = bus.read_byte_data(I2C_ADDR, low_reg)
    return (high << 8) | low


def read_status():
    """Read status register."""
    return bus.read_byte_data(I2C_ADDR, REG_STATUS)


def read_voltage():
    """Read voltage output in volts."""
    raw = read_word(REG_VOUT_H, REG_VOUT_L)
    # Conversion depends on PSU specifics. Example:
    voltage = raw / 1000.0  # assuming raw is in millivolts
    return voltage


def read_current():
    """Read current output in amperes."""
    raw = read_word(REG_IOUT_H, REG_IOUT_L)
    # Conversion depends on PSU specifics. Example:
    current = raw / 1000.0  # assuming raw is in milliamperes
    return current


def read_temperature():
    """Read temperature in degrees Celsius."""
    raw = bus.read_byte_data(I2C_ADDR, REG_TEMP)
    # Conversion depends on PSU sensor format
    return raw


def main():
    print("APW9 monitor started. Press Ctrl+C to exit.")
    try:
        while True:
            status = read_status()
            voltage = read_voltage()
            current = read_current()
            temperature = read_temperature()

            print(f"Status: 0x{status:02X}, Voltage: {voltage:.2f} V, "
                  f"Current: {current:.2f} A, Temp: {temperature} C")

            time.sleep(2)

    except KeyboardInterrupt:
        print("\nMonitoring stopped.")


if __name__ == "__main__":
    main()






https://github.com/RobbieGee/BitmainAPW9Controller

#!/usr/bin/env python3

import smbus
import time
import sys

I2C_ADDR = 0x58  # APW9 I2C адрес, может отличаться, проверьте

class APW9Controller:
    CONTROL_REG = 0x06
    VOLTAGE_SET_H = 0x07
    VOLTAGE_SET_L = 0x08
    CURRENT_SET_H = 0x09
    CURRENT_SET_L = 0x0A
    STATUS_REG = 0x00
    VOLTAGE_READ_H = 0x01
    VOLTAGE_READ_L = 0x02
    CURRENT_READ_H = 0x03
    CURRENT_READ_L = 0x04
    TEMP_REG = 0x05

    POWER_ON = 0x01
    POWER_OFF = 0x00

    def __init__(self, bus_num=1):
        self.bus = smbus.SMBus(bus_num)

    def power_on(self):
        print("Sending POWER ON command")
        self.bus.write_byte_data(I2C_ADDR, self.CONTROL_REG, self.POWER_ON)

    def power_off(self):
        print("Sending POWER OFF command")
        self.bus.write_byte_data(I2C_ADDR, self.CONTROL_REG, self.POWER_OFF)

    def read_status(self):
        status = self.bus.read_byte_data(I2C_ADDR, self.STATUS_REG)
        print(f"Status Register: 0x{status:02X}")
        return status

    def read_voltage(self):
        vh = self.bus.read_byte_data(I2C_ADDR, self.VOLTAGE_READ_H)
        vl = self.bus.read_byte_data(I2C_ADDR, self.VOLTAGE_READ_L)
        raw = (vh << 8) | vl
        voltage = raw / 1000.0  # Предполагается в милливольтах
        print(f"Voltage: {voltage:.2f} V")
        return voltage

    def read_current(self):
        ch = self.bus.read_byte_data(I2C_ADDR, self.CURRENT_READ_H)
        cl = self.bus.read_byte_data(I2C_ADDR, self.CURRENT_READ_L)
        raw = (ch << 8) | cl
        current = raw / 1000.0  # Предполагается в миллиамперах
        print(f"Current: {current:.2f} A")
        return current

    def read_temperature(self):
        temp = self.bus.read_byte_data(I2C_ADDR, self.TEMP_REG)
        print(f"Temperature: {temp} °C")
        return temp

    def set_voltage(self, voltage):
        # Для установки напряжения нужно перевести в raw и разбить на 2 байта
        raw = int(voltage * 1000)
        vh = (raw >> 8) & 0xFF
        vl = raw & 0xFF
        self.bus.write_byte_data(I2C_ADDR, self.VOLTAGE_SET_H, vh)
        self.bus.write_byte_data(I2C_ADDR, self.VOLTAGE_SET_L, vl)
        print(f"Set voltage command sent: {voltage:.2f} V")

    def set_current(self, current):
        # Аналогично напряжению
        raw = int(current * 1000)
        ch = (raw >> 8) & 0xFF
        cl = raw & 0xFF
        self.bus.write_byte_data(I2C_ADDR, self.CURRENT_SET_H, ch)
        self.bus.write_byte_data(I2C_ADDR, self.CURRENT_SET_L, cl)
        print(f"Set current command sent: {current:.2f} A")


def print_usage():
    print("Usage:")
    print("  apw9_controller.py on            - Power ON")
    print("  apw9_controller.py off           - Power OFF")
    print("  apw9_controller.py status        - Read status")
    print("  apw9_controller.py read          - Read all telemetry")
    print("  apw9_controller.py set_voltage V - Set output voltage V (Volts)")
    print("  apw9_controller.py set_current C - Set output current C (Amps)")

def main():
    controller = APW9Controller()

    if len(sys.argv) < 2:
        print_usage()
        sys.exit(1)

    cmd = sys.argv[1].lower()

    if cmd == "on":
        controller.power_on()
    elif cmd == "off":
        controller.power_off()
    elif cmd == "status":
        controller.read_status()
    elif cmd == "read":
        controller.read_voltage()
        controller.read_current()
        controller.read_temperature()
        controller.read_status()
    elif cmd == "set_voltage" and len(sys.argv) == 3:
        try:
            v = float(sys.argv[2])
            controller.set_voltage(v)
        except ValueError:
            print("Invalid voltage value")
    elif cmd == "set_current" and len(sys.argv) == 3:
        try:
            c = float(sys.argv[2])
            controller.set_current(c)
        except ValueError:
            print("Invalid current value")
    else:
        print_usage()

if __name__ == "__main__":
    main()


 Thanks for your help.