Created
November 8, 2025 13:45
-
-
Save glw119/b3206fabddf9aeb5a957b253c58444c1 to your computer and use it in GitHub Desktop.
DELL电源PMBus读取数据
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| esphome: | |
| name: dell-power-supply-monitor | |
| friendly_name: dell_power_supply_monitor | |
| includes: | |
| - dell_power_supply/main.hpp | |
| - dell_power_supply/pmbus.cpp | |
| - dell_power_supply/pmbus.h | |
| esp8266: | |
| board: d1_mini | |
| api: | |
| encryption: | |
| key: "111" | |
| web_server: | |
| port: 80 | |
| ota: | |
| - platform: esphome | |
| password: "111" | |
| wifi: | |
| networks: | |
| - ssid: !secret iot_intranet_ssid | |
| password: !secret wifi_password | |
| priority: 100 | |
| ap: | |
| ssid: "Dell-Power-Supply-Monitor-D1" | |
| password: "111" | |
| channel: 1 | |
| captive_portal: | |
| external_components: | |
| - source: | |
| type: git | |
| url: https://github.com/robertklep/esphome-custom-component | |
| components: [ custom, custom_component ] | |
| i2c: | |
| sda: D2 | |
| scl: D1 | |
| scan: true | |
| id: pmbus_i2c_bus | |
| frequency: 100kHz | |
| sensor: | |
| - platform: dht | |
| pin: D7 | |
| temperature: | |
| name: "HDD Case Inlet Temperature" | |
| humidity: | |
| name: "HDD Case Inlet Humidity" | |
| update_interval: 60s | |
| - platform: dht | |
| pin: D6 | |
| temperature: | |
| name: "HDD Case Outlet Temperature" | |
| humidity: | |
| name: "HDD Case Outlet Humidity" | |
| update_interval: 60s | |
| - platform: wifi_signal | |
| name: "WiFi Signal Sensor" | |
| update_interval: 60s | |
| - platform: custom | |
| lambda: |- | |
| auto pwr = new DellPower(); | |
| App.register_component(pwr); | |
| return {pwr->v_in,pwr->i_in,pwr->w_in,pwr->e_in,pwr->v_out,pwr->i_out,pwr->w_out,pwr->temp1,pwr->temp2,pwr->temp3,pwr->fan}; | |
| sensors: | |
| - name: "Input voltage" | |
| accuracy_decimals: 1 | |
| unit_of_measurement: V | |
| filters: | |
| - lambda: 'return (x < 0 || x > 250) ? NAN : x;' | |
| - throttle: 1s # 添加限流,每秒最多更新一次 | |
| - delta: 0.1 # 变化小于0.1V时不更新 | |
| - name: "Input Current" | |
| accuracy_decimals: 1 | |
| unit_of_measurement: A | |
| filters: | |
| - lambda: 'return (x < 0 || x > 50) ? NAN : x;' | |
| - throttle: 1s # 添加限流,每秒最多更新一次 | |
| - delta: 0.1 # 变化小于0.1V时不更新 | |
| - name: "Input Power" | |
| accuracy_decimals: 1 | |
| unit_of_measurement: W | |
| filters: | |
| - lambda: 'return (x < 0 || x > 600) ? NAN : x;' | |
| - throttle: 1s # 添加限流,每秒最多更新一次 | |
| - delta: 0.1 # 变化小于0.1V时不更新 | |
| - name: "E_in" | |
| accuracy_decimals: 1 | |
| unit_of_measurement: "%" | |
| filters: | |
| - throttle: 1s # 添加限流,每秒最多更新一次 | |
| - delta: 0.1 # 变化小于0.1V时不更新 | |
| - name: "Output Voltage" | |
| accuracy_decimals: 1 | |
| unit_of_measurement: V | |
| filters: | |
| - lambda: 'return (x < 10 || x > 15) ? NAN : x;' | |
| - throttle: 1s # 添加限流,每秒最多更新一次 | |
| - delta: 0.1 # 变化小于0.1V时不更新 | |
| - name: "Output Current" | |
| accuracy_decimals: 1 | |
| unit_of_measurement: A | |
| filters: | |
| - lambda: 'return (x < 0 || x > 60) ? NAN : x;' | |
| - throttle: 1s # 添加限流,每秒最多更新一次 | |
| - delta: 0.1 # 变化小于0.1V时不更新 | |
| - name: "Output Power" | |
| accuracy_decimals: 1 | |
| unit_of_measurement: W | |
| filters: | |
| - lambda: 'return (x < 0 || x > 500) ? NAN : x;' | |
| - throttle: 1s # 添加限流,每秒最多更新一次 | |
| - delta: 0.1 # 变化小于0.1V时不更新 | |
| - name: "Temp 1" | |
| accuracy_decimals: 1 | |
| unit_of_measurement: °C | |
| filters: | |
| - lambda: 'return (x < 0 || x > 100) ? NAN : x;' | |
| - throttle: 1s # 添加限流,每秒最多更新一次 | |
| - delta: 0.1 # 变化小于0.1V时不更新 | |
| - name: "Temp 2" | |
| accuracy_decimals: 1 | |
| unit_of_measurement: °C | |
| filters: | |
| - lambda: 'return (x < 0 || x > 100) ? NAN : x;' | |
| - throttle: 1s # 添加限流,每秒最多更新一次 | |
| - delta: 0.1 # 变化小于0.1V时不更新 | |
| - name: "Temp 3" | |
| accuracy_decimals: 1 | |
| unit_of_measurement: °C | |
| filters: | |
| - lambda: 'return (x < 0 || x > 100) ? NAN : x;' | |
| - throttle: 1s # 添加限流,每秒最多更新一次 | |
| - delta: 0.1 # 变化小于0.1V时不更新 | |
| - name: "Fan Speed" | |
| accuracy_decimals: 1 | |
| unit_of_measurement: RPM | |
| filters: | |
| - lambda: 'return (x < 0 || x > 5000) ? NAN : x;' | |
| - throttle: 1s # 添加限流,每秒最多更新一次 | |
| - delta: 0.1 # 变化小于0.1V时不更新 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "pmbus.h" | |
| #include "esphome.h" | |
| PMBus psu; | |
| class DellPower : public PollingComponent { | |
| public: | |
| Sensor *v_in = new Sensor(); | |
| Sensor *i_in = new Sensor(); | |
| Sensor *w_in = new Sensor(); | |
| Sensor *e_in = new Sensor(); | |
| Sensor *v_out = new Sensor(); | |
| Sensor *i_out = new Sensor(); | |
| Sensor *w_out = new Sensor(); | |
| //Sensor *e_out = new Sensor(); | |
| Sensor *temp1 = new Sensor(); | |
| Sensor *temp2 = new Sensor(); | |
| Sensor *temp3 = new Sensor(); | |
| Sensor *fan = new Sensor(); | |
| DellPower(): PollingComponent(3000) { } | |
| // 这个3000大概指的是3000毫秒更新一次信息吧 | |
| void setup() override { | |
| Wire.setClock(100000); | |
| psu.readMFR(); | |
| ESP_LOGD("PSU", "manf.: %s", psu.mfr_id); | |
| ESP_LOGD("PSU", "model: %s", psu. mfr_model); | |
| ESP_LOGD("PSU", "revision: %s", psu.mfr_revision); | |
| ESP_LOGD("PSU", "location: %s", psu.mfr_location); | |
| ESP_LOGD("PSU", "date: %s", psu.mfr_date); | |
| ESP_LOGD("PSU", "serial: %s", psu.mfr_serial); | |
| } | |
| void update() override { | |
| psu.scan(); | |
| v_in->publish_state(psu.V_in); | |
| i_in->publish_state(psu.I_in); | |
| w_in->publish_state(psu.W_in); | |
| e_in->publish_state(psu.E_in); | |
| // v_out->publish_state(psu.V_out); | |
| v_out->publish_state(psu.I_out != 0 ? psu.W_out/psu.I_out : psu.V_out); | |
| i_out->publish_state(psu.I_out); | |
| w_out->publish_state(psu.W_out); | |
| //e_out->publish_state(psu.E_out); | |
| temp1->publish_state(psu.T[0]); | |
| temp2->publish_state(psu.T[1]); | |
| temp3->publish_state(psu.T[2]); | |
| fan->publish_state(psu.fan[0]); | |
| } | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Copyright (c) 2024 Tomosawa | |
| * https://github.com/Tomosawa/ | |
| * All rights reserved | |
| */ | |
| #pragma GCC diagnostic warning "-Wunused-variable" | |
| #define DIAGNOSTICS 1 | |
| #include "pmbus.h" | |
| #define PMBUS_ADDRESS 0x58 | |
| // PMBus 命令定义 | |
| // 基本信息 | |
| #define MFR_ID 0x99 | |
| #define MFR_MODEL 0x9A | |
| #define MFR_REVISION 0x9B | |
| #define MFR_LOCATION 0x9C | |
| #define MFR_DATE 0x9D | |
| #define MFR_SERIAL 0x9E | |
| // 规格信息 | |
| #define MFR_VIN_MIN 0xA0 | |
| #define MFR_VIN_MAX 0xA1 | |
| #define MFR_IIN_MAX 0xA2 | |
| #define MFR_PIN_MAX 0xA3 | |
| #define MFR_VOUT_MIN 0xA4 | |
| #define MFR_VOUT_MAX 0xA5 | |
| #define MFR_IOUT_MAX 0xA6 | |
| #define MFR_POUT_MAX 0xA7 | |
| #define MFR_TAMBIENT_MAX 0xA8 | |
| #define MFR_TAMBIENT_MIN 0xA9 | |
| // 输入输出信息 | |
| #define READ_EIN 0x86 | |
| #define READ_EOUT 0x87 | |
| #define READ_VIN 0x88 | |
| #define READ_IIN 0x89 | |
| #define READ_VCAP 0x8A | |
| #define READ_VOUT 0x8B | |
| #define READ_IOUT 0x8C | |
| #define READ_POUT 0x96 | |
| #define READ_PIN 0x97 | |
| // 温度相关 | |
| #define READ_TEMPERATURE_1 0x8D | |
| #define READ_TEMPERATURE_2 0x8E | |
| #define READ_TEMPERATURE_3 0x8F | |
| #define READ_FAN_SPEED_1 0x90 | |
| // 参数设置 | |
| #define VOUT_COMMAND 0x21 | |
| #define VOUT_MAX 0x24 | |
| #define FAN_COMMAND_1 0x3B | |
| #define VOUT_OV_FAULT_LIMIT 0x40 | |
| #define VOUT_OV_FAULT_RESPONSE 0x41 | |
| #define VOUT_OV_WARN_LIMIT 0x42 | |
| #define VOUT_UV_FAULT_LIMIT 0x44 | |
| #define VOUT_UV_FAULT_RESPONSE 0x45 | |
| #define IOUT_OC_FAULT_LIMIT 0x46 | |
| #define IOUT_OC_FAULT_RESPONSE 0x47 | |
| #define IOUT_OC_WARN_LIMIT 0x4A | |
| #define OT_FAULT_LIMIT 0x4F | |
| #define OT_FAULT_RESPONSE 0x50 | |
| #define OT_WARN_LIMIT 0x51 | |
| #define VIN_OV_FAULT_LIMIT 0x55 | |
| #define VIN_UV_W ARN_LIMIT 0x58 | |
| #define VIN_UV_FAULT_LIMIT 0x59 | |
| #define COEFFICIENTS 0x30 | |
| #define VOUT_MODE 0x20 | |
| #define WRITE_PROTECT 0x10 | |
| /* | |
| */ | |
| PMBus::PMBus() | |
| { | |
| // I2C = &Wire; | |
| } | |
| /** | |
| * 初始化PMBus模块。 | |
| * | |
| * @param pson_pin PSON引脚的编号。 | |
| * @param pskill_pin PSKILL引脚的编号。 | |
| * @param Debug_Serial 用于调试信息的串行流对象。 | |
| * @param i2c I2C通信接口对象,如果为null则不进行I2C通信。 | |
| * @return 返回0表示初始化成功。 | |
| */ | |
| int PMBus::init() | |
| { | |
| delay(500); | |
| return 0; | |
| } | |
| /* | |
| * 获取数据 | |
| */ | |
| int PMBus::scan() | |
| { | |
| // 判断时间间隔1秒才执行 | |
| static u_long last_scan = 0; | |
| u_long msecs = millis(); | |
| if ((msecs - last_scan) < 1000) | |
| { | |
| return 0; | |
| } | |
| last_scan = msecs; | |
| // 判断设备是否在线 | |
| if (!checkDeviceOnline()) | |
| { | |
| return 0; | |
| } | |
| // 读取厂商信息,仅一次 | |
| if (!bReadMFR) | |
| { | |
| readMFR(); | |
| // 同时读取只需要读一次的参数 | |
| isVOutLinear = GetVOutFormat(); | |
| // ESP_LOGD("VOUT isLinearFormat="); | |
| // ESP_LOGD(isVOutLinear); | |
| exponent = readVoutMode(VOUT_MODE); | |
| // ESP_LOGD("VOUT exponent="); | |
| // ESP_LOGD(exponent); | |
| //指定为-1,否则某些电源是-9会有问题 | |
| exponent = -1; | |
| // 读取 COEFFICIENTS | |
| readCoefficients(COEFFICIENTS, &coeff); | |
| // 解除写入保护 | |
| write_byte(WRITE_PROTECT, 0x00); | |
| uint8_t fanconfig = read_byte(0x3A); | |
| // ESP_LOGD(String(fanconfig,BIN)); | |
| } | |
| delay(1); | |
| E_in = read_linear(READ_EIN); | |
| delay(1); | |
| E_out = read_linear(READ_EOUT); | |
| delay(1); | |
| V_in = read_linear(READ_VIN); | |
| delay(1); | |
| I_in = read_linear(READ_IIN); | |
| delay(1); | |
| W_in = read_linear(READ_PIN); | |
| delay(1); | |
| V_out = read_linear16(READ_VOUT) / 1000.0; | |
| delay(1); | |
| I_out = read_linear(READ_IOUT); | |
| delay(1); | |
| W_out = read_linear(READ_POUT); | |
| delay(1); | |
| T[0] = read_linear(READ_TEMPERATURE_1); | |
| delay(1); | |
| T[1] = read_linear(READ_TEMPERATURE_2); | |
| delay(1); | |
| T[2] = read_linear(READ_TEMPERATURE_3); | |
| delay(1); | |
| fan[0] = read_linear(READ_FAN_SPEED_1); | |
| delay(1); | |
| readFanCommand(); | |
| return 1; | |
| } | |
| /* | |
| * 清除状态 | |
| */ | |
| void PMBus::clear_faults() | |
| { | |
| Wire.beginTransmission(PMBUS_ADDRESS); | |
| Wire.write(0x03); | |
| Wire.endTransmission(true); | |
| } | |
| void PMBus::write_byte(uint8_t reg, uint8_t value) | |
| { | |
| Wire.beginTransmission(PMBUS_ADDRESS); | |
| Wire.write(reg); | |
| Wire.write(value); | |
| Wire.endTransmission(true); | |
| } | |
| uint8_t PMBus::read_byte(uint8_t reg) | |
| { | |
| Wire.beginTransmission(PMBUS_ADDRESS); | |
| Wire.write(reg); | |
| Wire.endTransmission(false); | |
| Wire.requestFrom(PMBUS_ADDRESS, 1, 1); | |
| return Wire.read(); | |
| } | |
| uint16_t PMBus::read_word(uint8_t reg) | |
| { | |
| Wire.beginTransmission(PMBUS_ADDRESS); | |
| Wire.write(reg); | |
| Wire.endTransmission(false); | |
| Wire.requestFrom(PMBUS_ADDRESS, 2); | |
| uint16_t dataRaw = 0; | |
| if (Wire.available() >= 2) | |
| { | |
| // 读取高字节和低字节 | |
| uint8_t lowByte = Wire.read(); | |
| uint8_t highByte = Wire.read(); | |
| // 将高字节和低字节组合为16位的原始值 | |
| dataRaw = (highByte << 8) | lowByte; | |
| } | |
| return dataRaw; | |
| } | |
| void PMBus::write_word(uint8_t reg, uint16_t value) | |
| { | |
| // 将16位值分成两个字节 | |
| uint8_t value_lo = value & 0xFF; // 低位字节 | |
| uint8_t value_hi = (value >> 8) & 0xFF; // 高位字节 | |
| // 开始I2C传输 | |
| Wire.beginTransmission(PMBUS_ADDRESS); | |
| // 发送寄存器地址 | |
| Wire.write(reg); | |
| // 发送数据,先发送高位字节,再发送低位字节 | |
| Wire.write(value_lo); | |
| Wire.write(value_hi); | |
| // 结束I2C传输,确保数据被发送 | |
| Wire.endTransmission(); | |
| } | |
| String PMBus::read_string(uint8_t reg) | |
| { | |
| int len = 0; | |
| len = (int)read_byte(reg); | |
| String result; | |
| Wire.beginTransmission(PMBUS_ADDRESS); | |
| Wire.write(reg); | |
| Wire.endTransmission(false); | |
| Wire.requestFrom(PMBUS_ADDRESS, len, 1); | |
| for (int i = 0; i < len; i++) | |
| { | |
| int readData = Wire.read(); | |
| if (isPrintable(readData)) | |
| result += (char)readData; | |
| } | |
| return result; | |
| } | |
| void PMBus::read_block(uint8_t reg, int bytes, uint8_t *buffer) | |
| { | |
| Wire.beginTransmission(PMBUS_ADDRESS); | |
| Wire.write(reg); | |
| Wire.endTransmission(false); | |
| Wire.requestFrom(PMBUS_ADDRESS, bytes, 1); | |
| for (int i = 0; i < bytes; ++i) | |
| { | |
| buffer[i] = Wire.read(); | |
| } | |
| } | |
| float PMBus::read_linear(uint8_t reg) | |
| { | |
| uint16_t linear; | |
| linear = read_word(reg); | |
| // ESP_LOGD("Read register: 0x"); | |
| // ESP_LOGD(reg, HEX); | |
| return linear11ToFloat(linear); | |
| } | |
| uint16_t PMBus::convertHex2Dec(uint16_t hexData) | |
| { | |
| // 将输入的十进制数视为十六进制数,直接返回其对应的十进制值 | |
| uint16_t decValue = (hexData / 1000) * 4096 + ((hexData % 1000) / 100) * 256 + ((hexData % 100) / 10) * 16 + (hexData % 10); | |
| return decValue; | |
| } | |
| float PMBus::read_linear16(uint8_t reg) | |
| { | |
| uint16_t linear; | |
| linear = read_word(reg); | |
| // 根据VOUT_MODE中的 4:0 的值是-9进行转换 | |
| return ulinear16ToFloat(linear); | |
| } | |
| float PMBus::readVout(uint8_t reg) | |
| { | |
| // 读取 VOUT 寄存器 | |
| uint16_t rawVout; | |
| rawVout = read_word(reg); | |
| // ESP_LOGD("VOut_raw:"); | |
| // ESP_LOGD(rawVout); | |
| // 判断格式 | |
| if (isVOutLinear) | |
| { | |
| // 计算 LINEAR 格式电压 | |
| float voltage = rawVout * pow(2, exponent); // LINEAR11 | |
| return voltage; | |
| } | |
| else | |
| { | |
| // 计算 DIRECT 格式电压 | |
| // 计算实际电压 | |
| float actualVoltage = (rawVout * coeff.m / pow(2, coeff.R)) + coeff.b; | |
| return actualVoltage; | |
| } | |
| } | |
| /* | |
| * 将LINEAR11格式转换为浮点数 | |
| */ | |
| float PMBus::linear11ToFloat(uint16_t data) | |
| { | |
| // ESP_LOGD("linear11ToFloat raw_data="); | |
| // ESP_LOGD(data); | |
| int16_t exponent11 = (data >> 11) & 0x1F; // 获取5位指数 | |
| // ESP_LOGD("linear11ToFloat exponent11="); | |
| // ESP_LOGD(exponent11); | |
| if (exponent11 > 15) | |
| { | |
| exponent11 = exponent11 - 32; // 如果指数是负数,进行调整 | |
| } | |
| int16_t mantissa = data & 0x7FF; // 获取11位尾数 | |
| if (mantissa > 1023) | |
| { | |
| mantissa = mantissa - 2048; // 处理尾数的符号 | |
| } | |
| // ESP_LOGD("linear11ToFloat mantissa="); | |
| // ESP_LOGD(mantissa); | |
| // ESP_LOGD("linear11ToFloat result="); | |
| // ESP_LOGD(mantissa * pow(2, exponent11)); | |
| return mantissa * pow(2, exponent11); // 计算最终的浮点数值 | |
| } | |
| // 根据VOUT_MODE中的比例因子转换ULINEAR16格式 | |
| float PMBus::ulinear16ToFloat(uint16_t data) | |
| { | |
| uint16_t dec_data = convertHex2Dec(data); | |
| return dec_data * pow(2, exponent); | |
| } | |
| void PMBus::write_linear(uint8_t reg, float value) | |
| { | |
| uint16_t linear = float2linear(value); | |
| write_word(reg, linear); | |
| } | |
| float PMBus::read_direct(uint8_t reg) | |
| { | |
| Coefficient_t coeff; | |
| readCoefficients(COEFFICIENTS, &coeff); | |
| uint16_t data; | |
| data = read_word(reg); | |
| return (data * pow(10, 0 - coeff.R) - coeff.b) / coeff.m; | |
| } | |
| // 读取VOUT_MODE寄存器 | |
| int8_t PMBus::readVoutMode(uint8_t reg) | |
| { | |
| Wire.beginTransmission(PMBUS_ADDRESS); | |
| Wire.write(reg); // 请求VOUT_MODE寄存器 | |
| Wire.endTransmission(false); | |
| Wire.requestFrom(PMBUS_ADDRESS, 1); // 请求1字节数据 | |
| // 确保有数据可读,避免潜在的问题 | |
| if (Wire.available() < 1) | |
| { | |
| // Serial.println("Error: No data available from VOUT_MODE register"); | |
| return 0; // 错误处理,返回 0 或其他默认值 | |
| } | |
| uint8_t voutMode = Wire.read(); // 读取VOUT_MODE寄存器的值 | |
| // VOUT_MODE 的低5位包含比例因子指数(通常表示为2的幂) | |
| uint8_t exponent = voutMode & 0x1F; | |
| if (exponent > 15) | |
| { | |
| exponent = exponent - 32; // 如果指数是负数,进行调整 | |
| } | |
| return exponent; | |
| } | |
| bool PMBus::GetVOutFormat() | |
| { | |
| // 读取 VOUT_MODE 寄存器 | |
| Wire.beginTransmission(PMBUS_ADDRESS); | |
| Wire.write(VOUT_MODE); // VOUT_MODE 寄存器地址 | |
| Wire.endTransmission(false); | |
| Wire.requestFrom(PMBUS_ADDRESS, 1); // 请求 1 字节 | |
| if (Wire.available() < 1) | |
| { | |
| // ESP_LOGD("Error: Insufficient data available for VOUT_MODE"); | |
| return false; // 错误处理 | |
| } | |
| uint8_t voutMode = Wire.read(); // 读取 VOUT_MODE 值 | |
| // 检查 VOUT_MODE 的位来判断格式 | |
| // 假设 VOUT_MODE 的位 0 表示是否使用 LINEAR 格式 | |
| // 你需要根据具体的设备文档调整这部分逻辑 | |
| if (voutMode & 0x01) | |
| { // 假设低位为 1 表示 LINEAR 格式 | |
| return true; // LINEAR 格式 | |
| } | |
| else | |
| { | |
| return false; // DIRECT 格式 | |
| } | |
| } | |
| /* | |
| * 读COEFFICIENTS参数 | |
| */ | |
| void PMBus::readCoefficients(uint8_t reg, Coefficient_t *coeff) | |
| { | |
| // byte regCS = ((0xFF - (reg + (PMBUS_ADDRESS << 1))) + 1) & 0xFF; | |
| // Wire.beginTransmission(PMBUS_ADDRESS); | |
| // Wire.write(reg); | |
| // Wire.write(regCS); | |
| // Wire.endTransmission(false); | |
| // Wire.requestFrom(PMBUS_ADDRESS, 5, 1); | |
| // uint8_t byte1 = Wire.read(); | |
| // uint8_t byte2 = Wire.read(); | |
| // uint8_t byte3 = Wire.read(); | |
| // uint8_t byte4 = Wire.read(); | |
| // uint8_t byte5 = Wire.read(); | |
| // coeff->m = byte1 | (byte2 << 8); | |
| // coeff->b = byte3 | (byte4 << 8); | |
| // coeff->R = byte5; | |
| Wire.beginTransmission(PMBUS_ADDRESS); | |
| Wire.write(reg); // COEFFICIENTS 命令地址 | |
| Wire.endTransmission(false); | |
| Wire.requestFrom(PMBUS_ADDRESS, 6); // 请求 6 字节的数据 | |
| if (Wire.available() < 6) | |
| { | |
| // Serial.println("Error: Insufficient data available for COEFFICIENTS"); | |
| return; | |
| } | |
| // 读取 M, B 和 R 值 | |
| coeff->m = (Wire.read() | (Wire.read() << 8)); // 读取 2 字节 M | |
| coeff->b = (Wire.read() | (Wire.read() << 8)); // 读取 2 字节 B | |
| coeff->R = Wire.read(); // 读取 1 字节 R | |
| Wire.read(); | |
| // ESP_LOGD("Coefficient_M: "); | |
| // ESP_LOGD(coeff->m); | |
| // ESP_LOGD("Coefficient_B: "); | |
| // ESP_LOGD(coeff->b); | |
| // ESP_LOGD("Coefficient_R: "); | |
| // ESP_LOGD(coeff->R); | |
| } | |
| float PMBus::linear2float(uint16_t u16) | |
| { | |
| float value = 0; | |
| UniLinear uniLinear; | |
| uniLinear.u16 = u16; | |
| value = uniLinear.linear.y * pow(2.0f, uniLinear.linear.n); | |
| return value; | |
| } | |
| uint16_t PMBus::float2linear(float value) | |
| { | |
| UniLinear uniLinear; | |
| // 初始化为避免未初始化的警告 | |
| uniLinear.u16 = 0; | |
| // 计算n的值,即所需的偏移量 | |
| // 因为n是5位,所以它的范围是从-15到15(包括0) | |
| int n = floor(log2(value)); | |
| if (n < -15) | |
| n = -15; // 如果n太小,则将其限制在最小值 | |
| if (n > 15) | |
| n = 15; // 如果n太大,则将其限制在最大值 | |
| // 计算y的值,即偏移量调整后的值 | |
| // y应该是有符号的,所以需要将其转换为11位有符号整数 | |
| int y = round(value / pow(2.0f, n)); | |
| if (y < -2048) | |
| y = -2048; // 如果y太小,则将其限制在最小值 | |
| if (y > 2047) | |
| y = 2047; // 如果y太大,则将其限制在最大值 | |
| // 将计算出的y和n值放入联合体中 | |
| uniLinear.linear.y = y; | |
| uniLinear.linear.n = n; | |
| // 返回联合体中的u16值 | |
| return uniLinear.u16; | |
| } | |
| uint16_t PMBus::convertlinear11(uint16_t value) | |
| { | |
| // int16_t mantissa = value << (-exponent); // 计算 mantissa,根据 exponent 进行位移 | |
| // // 将 exponent 和 mantissa 打包为 16-bit 数据 | |
| // uint16_t linear11Value = (mantissa & 0x07FF) | ((exponent & 0x1F) << 11); | |
| // return linear11Value; | |
| // 计算 mantissa,根据 exponent 进行位移或缩放 | |
| int16_t mantissa = (int16_t)(value * pow(2, -exponent)); | |
| // 将 exponent 和 mantissa 打包为 16-bit 数据 | |
| uint16_t linear11Value = (mantissa & 0x07FF) | ((exponent & 0x1F) << 11); | |
| return linear11Value; | |
| } | |
| bool PMBus::checkOnOffState() | |
| { | |
| if (V_out < 1) | |
| return false; | |
| else | |
| return true; | |
| } | |
| bool PMBus::checkDeviceOnline() | |
| { | |
| Wire.beginTransmission(PMBUS_ADDRESS); | |
| byte error = Wire.endTransmission(); | |
| if (error != 0) | |
| { | |
| return false; | |
| } | |
| return true; | |
| } | |
| void PMBus::readMFR() | |
| { | |
| mfr_id = read_string(MFR_ID); | |
| delay(1); | |
| mfr_model = read_string(MFR_MODEL); | |
| delay(1); | |
| mfr_revision = read_string(MFR_REVISION); | |
| delay(1); | |
| mfr_location = read_string(MFR_LOCATION); | |
| delay(1); | |
| mfr_date = read_string(MFR_DATE); | |
| delay(1); | |
| mfr_serial = read_string(MFR_SERIAL); | |
| delay(1); | |
| bReadMFR = true; | |
| // ESP_LOGD("manf.: " + mfr_id); | |
| // ESP_LOGD("model: " + mfr_model); | |
| // ESP_LOGD("revision: " + mfr_revision); | |
| // ESP_LOGD("location: " + mfr_location); | |
| // ESP_LOGD("date: " + mfr_date); | |
| // ESP_LOGD("serial: " + mfr_serial); | |
| } | |
| void PMBus::recordTurnOnTime() | |
| { | |
| turn_on_time = millis(); | |
| } | |
| void PMBus::getRunTime() | |
| { | |
| total_power_on = millis() - turn_on_time; | |
| // int days, years; | |
| // days = (int)((long int)total_power_on / 86400l); | |
| // years = days / 365; | |
| // char text[50]; | |
| // sprintf(text, "on time: %lu s (%08lx) \r\n", total_power_on, total_power_on); | |
| // ESP_LOGD(text); | |
| // sprintf(text, "%d days, %d years\r\n", days, years); | |
| // ESP_LOGD(text); | |
| } | |
| float PMBus::readFanCommand() | |
| { | |
| fanSpeed = read_linear(FAN_COMMAND_1); | |
| return fanSpeed; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Copyright (c) 2024 Tomosawa | |
| * https://github.com/Tomosawa/ | |
| * All rights reserved | |
| */ | |
| #ifndef PMBUS_H | |
| #define PMBUS_H | |
| #include <Arduino.h> | |
| #include <Wire.h> | |
| #include <time.h> | |
| #define STATUS_REGISTERS 10 | |
| // I2C通讯频率 | |
| #define I2C_PMBUS_CLOCK 100000 | |
| // 与电源PMBus通讯类 | |
| class PMBus | |
| { | |
| public: | |
| PMBus(); | |
| int init(); | |
| int scan(); | |
| void clear_faults(); | |
| void off(); | |
| void on(); | |
| bool checkOnOffState(); | |
| bool checkDeviceOnline(); | |
| void getRunTime(); | |
| void recordTurnOnTime(); | |
| float readFanCommand(); | |
| void readMFR(); // 读取电源厂商信息 | |
| String mfr_id, mfr_model, mfr_revision, mfr_location, mfr_date, mfr_serial; | |
| float V_in = 0.0, I_in = 0.0, V_out = 0.0, I_out = 0.0, T[3] = {0.0, 0.0, 0.0}, fan[2] = {0.0, 0.0}, | |
| W_in = 0.0, W_out = 0.0, fanSpeed = 0.0, E_in = 0.0, E_out = 0.0; | |
| uint8_t pmbus_revision = 0, vout_mode = 0; | |
| uint8_t status_u8 = 0, status_vout = 0, status_iout = 0, status_input = 0, status_temperature = 0, | |
| status_cml = 0, status_other = 0, status_mfr_specific = 0, status_fans = 0, *status_byte[STATUS_REGISTERS]; | |
| uint16_t status_word = 0, vout_command = 0; | |
| long turn_on_time = 0, total_power_on = 0; | |
| typedef struct linear | |
| { | |
| int y : 11; | |
| int n : 5; | |
| } linear_t; | |
| typedef union | |
| { | |
| linear_t linear; | |
| uint16_t u16; | |
| } UniLinear; | |
| typedef struct Coefficient | |
| { | |
| uint16_t m; | |
| uint16_t b; | |
| uint16_t R; | |
| } Coefficient_t; | |
| private: | |
| uint8_t read_byte(uint8_t reg); | |
| void write_byte(uint8_t reg, uint8_t value); | |
| uint16_t read_word(uint8_t reg); | |
| void write_word(uint8_t reg, uint16_t value); | |
| String read_string(uint8_t reg); | |
| void read_block(uint8_t reg, int bytes, uint8_t *buffer); | |
| float read_linear(uint8_t reg); | |
| void write_linear(uint8_t reg, float value); | |
| float read_linear16(uint8_t reg); | |
| float read_direct(uint8_t reg); | |
| void readCoefficients(uint8_t reg, Coefficient_t *coeff); | |
| float linear2float(uint16_t u16); | |
| float linear11ToFloat(uint16_t data); | |
| float ulinear16ToFloat(uint16_t data); | |
| int8_t readVoutMode(uint8_t reg); | |
| uint16_t float2linear(float value); | |
| uint16_t convertlinear11(uint16_t value); | |
| float readVout(uint8_t reg); | |
| bool GetVOutFormat(); | |
| uint16_t convertHex2Dec(uint16_t hexData); | |
| private: | |
| bool bReadMFR = false; // 是否读取了厂商信息了 | |
| int8_t exponent = 0;// uLinear16计算用 | |
| bool isVOutLinear = true;// 输出电压的格式 | |
| Coefficient_t coeff; | |
| }; | |
| #endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment