mirror of
https://github.com/ayufan/steam-deck-tools.git
synced 2026-03-10 06:53:57 +01:00
Support sensor value calculation (Pid and Quadratic)
This commit is contained in:
parent
e25e284e10
commit
4851b74edf
|
|
@ -1,253 +0,0 @@
|
|||
using LibreHardwareMonitor.Hardware;
|
||||
using LibreHardwareMonitor.Hardware.CPU;
|
||||
using Microsoft.VisualBasic.Devices;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FanControl
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
internal class FanControl : IDisposable
|
||||
{
|
||||
public enum FanMode
|
||||
{
|
||||
Default,
|
||||
MidWay,
|
||||
Max
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
internal class FanSensor
|
||||
{
|
||||
public string? Name { get; internal set; }
|
||||
public float? Value { get; internal set; }
|
||||
public ushort InducedRPM { get; internal set; }
|
||||
|
||||
internal string HardwareName { get; set; } = "";
|
||||
internal HardwareType HardwareType { get; set; }
|
||||
internal string SensorName { get; set; } = "";
|
||||
internal SensorType SensorType { get; set; }
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Name = null;
|
||||
Value = null;
|
||||
InducedRPM = 0;
|
||||
}
|
||||
|
||||
public bool Matches(ISensor sensor)
|
||||
{
|
||||
return sensor != null &&
|
||||
sensor.Hardware.HardwareType == HardwareType &&
|
||||
sensor.Hardware.Name.StartsWith(HardwareName) &&
|
||||
sensor.SensorType == SensorType &&
|
||||
sensor.Name == SensorName;
|
||||
}
|
||||
|
||||
public bool Update(ISensor hwSensor)
|
||||
{
|
||||
if (!Matches(hwSensor))
|
||||
return false;
|
||||
|
||||
System.Diagnostics.Trace.WriteLine(String.Format("{0}: {1} {2}, value: {3}, type: {4}",
|
||||
hwSensor.Identifier, hwSensor.Hardware.Name, hwSensor.Name, hwSensor.Value, hwSensor.SensorType));
|
||||
|
||||
return Update(
|
||||
String.Format("{0} {1}", hwSensor.Hardware.Name, hwSensor.Name),
|
||||
hwSensor.Value);
|
||||
}
|
||||
|
||||
public bool Update(string name, float? value)
|
||||
{
|
||||
if (!value.HasValue || value <= 0.0)
|
||||
return false;
|
||||
|
||||
Name = name;
|
||||
Value = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
public String FormattedValue()
|
||||
{
|
||||
if (!Value.HasValue)
|
||||
return "";
|
||||
|
||||
switch (SensorType)
|
||||
{
|
||||
case SensorType.Temperature:
|
||||
return Value.Value.ToString("F1") + "℃";
|
||||
case SensorType.Power:
|
||||
return Value.Value.ToString("F1") + "W";
|
||||
default:
|
||||
return Value.Value.ToString("F1");
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
[CategoryAttribute("Fan")]
|
||||
public FanMode Mode { get; private set; }
|
||||
|
||||
[CategoryAttribute("Fan")]
|
||||
public ushort CurrentRPM { get; private set; }
|
||||
|
||||
[CategoryAttribute("Fan")]
|
||||
public ushort DesiredRPM { get; private set; }
|
||||
|
||||
[CategoryAttribute("Sensor - APU"), DisplayName("Name")]
|
||||
public String? APUName { get { return allSensors["APU"].Name; } }
|
||||
[CategoryAttribute("Sensor - APU"), DisplayName("Power")]
|
||||
public String? APUPower { get { return allSensors["APU"].FormattedValue(); } }
|
||||
|
||||
[CategoryAttribute("Sensor - CPU"), DisplayName("Name")]
|
||||
public String? CPUName { get { return allSensors["CPU"].Name; } }
|
||||
[CategoryAttribute("Sensor - CPU"), DisplayName("Temperature")]
|
||||
public String? CPUTemperature { get { return allSensors["CPU"].FormattedValue(); } }
|
||||
|
||||
[CategoryAttribute("Sensor - GPU"), DisplayName("Name")]
|
||||
public String? GPUName { get { return allSensors["GPU"].Name; } }
|
||||
[CategoryAttribute("Sensor - GPU"), DisplayName("Temperature")]
|
||||
public String? GPUTemperature { get { return allSensors["GPU"].FormattedValue(); } }
|
||||
|
||||
[CategoryAttribute("Sensor - SSD"), DisplayName("Name")]
|
||||
public String? SSDName { get { return allSensors["SSD"].Name; } }
|
||||
[CategoryAttribute("Sensor - SSD"), DisplayName("Temperature")]
|
||||
public String? SSDTemperature { get { return allSensors["SSD"].FormattedValue(); } }
|
||||
[CategoryAttribute("Sensor - Battery"), DisplayName("Name")]
|
||||
public String? BatteryName { get { return allSensors["Batt"].Name; } }
|
||||
[CategoryAttribute("Sensor - Battery"), DisplayName("Temperature")]
|
||||
public String? BatteryTemperature { get { return allSensors["Batt"].FormattedValue(); } }
|
||||
|
||||
private Dictionary<string, FanSensor> allSensors = new Dictionary<string, FanSensor>
|
||||
{
|
||||
{ "APU",
|
||||
new FanSensor()
|
||||
{
|
||||
// TODO: Is this correct?
|
||||
HardwareName = "AMD Custom APU 0405",
|
||||
HardwareType = HardwareType.Cpu,
|
||||
SensorName = "Package",
|
||||
SensorType = SensorType.Power
|
||||
} },
|
||||
{ "CPU",
|
||||
new FanSensor()
|
||||
{
|
||||
HardwareName = "AMD Custom APU 0405",
|
||||
HardwareType = HardwareType.Cpu,
|
||||
SensorName = "Core (Tctl/Tdie)",
|
||||
SensorType = SensorType.Temperature
|
||||
} },
|
||||
{ "GPU",
|
||||
new FanSensor()
|
||||
{
|
||||
HardwareName = "AMD Custom GPU 0405",
|
||||
HardwareType = HardwareType.GpuAmd,
|
||||
SensorName = "GPU Core",
|
||||
SensorType = SensorType.Temperature
|
||||
} },
|
||||
{ "SSD",
|
||||
new FanSensor()
|
||||
{
|
||||
HardwareType = HardwareType.Storage,
|
||||
SensorName = "Temperature",
|
||||
SensorType = SensorType.Temperature
|
||||
} },
|
||||
{ "Batt",
|
||||
new FanSensor()
|
||||
{
|
||||
HardwareType = HardwareType.Battery,
|
||||
SensorName = "Temperature",
|
||||
SensorType = SensorType.Temperature
|
||||
} }
|
||||
};
|
||||
|
||||
LibreHardwareMonitor.Hardware.Computer libreHardwareComputer = new LibreHardwareMonitor.Hardware.Computer
|
||||
{
|
||||
IsCpuEnabled = true,
|
||||
IsGpuEnabled = true,
|
||||
IsStorageEnabled = true,
|
||||
IsBatteryEnabled = true
|
||||
};
|
||||
|
||||
public FanControl()
|
||||
{
|
||||
libreHardwareComputer.Open();
|
||||
}
|
||||
|
||||
private void visitHardware(IHardware hardware)
|
||||
{
|
||||
Dictionary<FanSensor, ISensor> matched = new Dictionary<FanSensor, ISensor>();
|
||||
|
||||
foreach (ISensor hwSensor in hardware.Sensors)
|
||||
{
|
||||
foreach (var sensor in allSensors.Values)
|
||||
{
|
||||
if (sensor.Matches(hwSensor))
|
||||
matched[sensor] = hwSensor;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched.Any())
|
||||
{
|
||||
hardware.Update();
|
||||
foreach (var sensor in matched)
|
||||
sensor.Key.Update(sensor.Value);
|
||||
}
|
||||
|
||||
foreach (IHardware subhardware in hardware.SubHardware)
|
||||
{
|
||||
visitHardware(subhardware);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
CurrentRPM = Vlv0100.GetFanRPM();
|
||||
DesiredRPM = Vlv0100.GetFanDesiredRPM();
|
||||
|
||||
foreach (var sensor in allSensors.Values)
|
||||
sensor.Reset();
|
||||
|
||||
foreach(var hardware in libreHardwareComputer.Hardware)
|
||||
visitHardware(hardware);
|
||||
|
||||
allSensors["Batt"].Update("VLV0100", Vlv0100.GetBattTemperature());
|
||||
}
|
||||
|
||||
public void SetMode(FanMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case FanMode.Default:
|
||||
Vlv0100.SetFanControl(false);
|
||||
break;
|
||||
|
||||
case FanMode.MidWay:
|
||||
Vlv0100.SetFanControl(true);
|
||||
Vlv0100.SetFanDesiredRPM(Vlv0100.MAX_FAN_RPM/2);
|
||||
break;
|
||||
|
||||
case FanMode.Max:
|
||||
Vlv0100.SetFanControl(true);
|
||||
Vlv0100.SetFanDesiredRPM(Vlv0100.MAX_FAN_RPM);
|
||||
break;
|
||||
}
|
||||
|
||||
this.Mode = mode;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
libreHardwareComputer.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
66
FanControl/FanControlForm.Designer.cs
generated
66
FanControl/FanControlForm.Designer.cs
generated
|
|
@ -36,14 +36,17 @@
|
|||
this.fanModeSelectNotifyMenu = new System.Windows.Forms.ToolStripComboBox();
|
||||
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
|
||||
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
|
||||
this.fanModeSelectMenu = new System.Windows.Forms.ToolStripComboBox();
|
||||
this.controlToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.propertyGridUpdateTimer = new System.Windows.Forms.Timer(this.components);
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.sensorWarningLabel = new System.Windows.Forms.Label();
|
||||
this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
|
||||
this.contextMenu.SuspendLayout();
|
||||
this.menuStrip1.SuspendLayout();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// fanLoopTimer
|
||||
|
|
@ -92,17 +95,6 @@
|
|||
this.toolStripMenuItem1.Text = "Exit";
|
||||
this.toolStripMenuItem1.Click += new System.EventHandler(this.formClose_Event);
|
||||
//
|
||||
// propertyGrid1
|
||||
//
|
||||
this.propertyGrid1.DisabledItemForeColor = System.Drawing.SystemColors.ControlText;
|
||||
this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.propertyGrid1.HelpVisible = false;
|
||||
this.propertyGrid1.Location = new System.Drawing.Point(0, 44);
|
||||
this.propertyGrid1.Name = "propertyGrid1";
|
||||
this.propertyGrid1.Size = new System.Drawing.Size(712, 885);
|
||||
this.propertyGrid1.TabIndex = 0;
|
||||
this.propertyGrid1.ToolbarVisible = false;
|
||||
//
|
||||
// menuStrip1
|
||||
//
|
||||
this.menuStrip1.ImageScalingSize = new System.Drawing.Size(32, 32);
|
||||
|
|
@ -145,12 +137,54 @@
|
|||
this.propertyGridUpdateTimer.Interval = 1000;
|
||||
this.propertyGridUpdateTimer.Tick += new System.EventHandler(this.propertyGridUpdateTimer_Tick);
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 1;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.sensorWarningLabel, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.propertyGrid1, 0, 0);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 44);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 2;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(712, 885);
|
||||
this.tableLayoutPanel1.TabIndex = 5;
|
||||
//
|
||||
// sensorWarningLabel
|
||||
//
|
||||
this.sensorWarningLabel.AutoSize = true;
|
||||
this.sensorWarningLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.sensorWarningLabel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.sensorWarningLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
|
||||
this.sensorWarningLabel.ForeColor = System.Drawing.Color.Red;
|
||||
this.sensorWarningLabel.Location = new System.Drawing.Point(3, 757);
|
||||
this.sensorWarningLabel.Name = "sensorWarningLabel";
|
||||
this.sensorWarningLabel.Size = new System.Drawing.Size(706, 128);
|
||||
this.sensorWarningLabel.TabIndex = 6;
|
||||
this.sensorWarningLabel.Text = "Some sensors are missing.\r\nThe fan behavior might be incorrect.\r\nWhich might resu" +
|
||||
"lt in device failure.\r\nUse at your own risk!";
|
||||
this.sensorWarningLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.sensorWarningLabel.Visible = false;
|
||||
//
|
||||
// propertyGrid1
|
||||
//
|
||||
this.propertyGrid1.DisabledItemForeColor = System.Drawing.SystemColors.ControlText;
|
||||
this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.propertyGrid1.HelpVisible = false;
|
||||
this.propertyGrid1.Location = new System.Drawing.Point(3, 3);
|
||||
this.propertyGrid1.Name = "propertyGrid1";
|
||||
this.propertyGrid1.Size = new System.Drawing.Size(706, 751);
|
||||
this.propertyGrid1.TabIndex = 1;
|
||||
this.propertyGrid1.ToolbarVisible = false;
|
||||
//
|
||||
// FanControlForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(13F, 32F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(712, 929);
|
||||
this.Controls.Add(this.propertyGrid1);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Controls.Add(this.menuStrip1);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.MainMenuStrip = this.menuStrip1;
|
||||
|
|
@ -162,6 +196,8 @@
|
|||
this.contextMenu.ResumeLayout(false);
|
||||
this.menuStrip1.ResumeLayout(false);
|
||||
this.menuStrip1.PerformLayout();
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
|
|
@ -171,7 +207,6 @@
|
|||
|
||||
private System.Windows.Forms.Timer fanLoopTimer;
|
||||
private NotifyIcon notifyIcon;
|
||||
private PropertyGrid propertyGrid1;
|
||||
private ContextMenuStrip contextMenu;
|
||||
private MenuStrip menuStrip1;
|
||||
private ToolStripComboBox fanModeSelectNotifyMenu;
|
||||
|
|
@ -181,5 +216,8 @@
|
|||
private ToolStripMenuItem controlToolStripMenuItem;
|
||||
private ToolStripMenuItem exitToolStripMenuItem;
|
||||
private System.Windows.Forms.Timer propertyGridUpdateTimer;
|
||||
private TableLayoutPanel tableLayoutPanel1;
|
||||
private Label sensorWarningLabel;
|
||||
private PropertyGrid propertyGrid1;
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ namespace FanControl
|
|||
{
|
||||
public partial class FanControlForm : Form
|
||||
{
|
||||
private FanControl fanControl = new FanControl();
|
||||
private FanController fanControl = new FanController();
|
||||
|
||||
public FanControlForm()
|
||||
{
|
||||
|
|
@ -23,7 +23,7 @@ namespace FanControl
|
|||
propertyGrid1.SelectedObject = fanControl;
|
||||
propertyGrid1.ExpandAllGridItems();
|
||||
|
||||
foreach (var item in Enum.GetValues(typeof(FanControl.FanMode)))
|
||||
foreach (var item in Enum.GetValues(typeof(FanController.FanMode)))
|
||||
{
|
||||
fanModeSelectMenu.Items.Add(item);
|
||||
fanModeSelectNotifyMenu.Items.Add(item);
|
||||
|
|
@ -38,7 +38,7 @@ namespace FanControl
|
|||
private void fanModeSelect_SelectedValueChanged(object sender, EventArgs e)
|
||||
{
|
||||
var comboBox = (ToolStripComboBox)sender;
|
||||
var selectedMode = (FanControl.FanMode)comboBox.SelectedItem;
|
||||
var selectedMode = (FanController.FanMode)comboBox.SelectedItem;
|
||||
fanControl.SetMode(selectedMode);
|
||||
fanModeSelectMenu.SelectedItem = selectedMode;
|
||||
fanModeSelectNotifyMenu.SelectedItem = selectedMode;
|
||||
|
|
@ -68,7 +68,7 @@ namespace FanControl
|
|||
private void FanControlForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
// Always revert to default on closing
|
||||
fanControl.SetMode(FanControl.FanMode.Default);
|
||||
fanControl.SetMode(FanController.FanMode.Default);
|
||||
}
|
||||
|
||||
private void fanLoopTimer_Tick(object sender, EventArgs e)
|
||||
|
|
@ -84,6 +84,7 @@ namespace FanControl
|
|||
var item = propertyGrid1.SelectedGridItem;
|
||||
propertyGrid1.Refresh();
|
||||
propertyGrid1.SelectedGridItem = item;
|
||||
sensorWarningLabel.Visible = fanControl.IsAnyInvalid();
|
||||
notifyIcon.Text = String.Format("Fan: {0} RPM Mode: {1}", fanControl.CurrentRPM, fanControl.Mode);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
129
FanControl/FanController.cs
Normal file
129
FanControl/FanController.cs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
using LibreHardwareMonitor.Hardware;
|
||||
using LibreHardwareMonitor.Hardware.CPU;
|
||||
using Microsoft.VisualBasic.Devices;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FanControl
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
internal partial class FanController : IDisposable
|
||||
{
|
||||
public enum FanMode
|
||||
{
|
||||
Default,
|
||||
MidWay,
|
||||
SteamOS,
|
||||
Max
|
||||
}
|
||||
|
||||
[CategoryAttribute("Fan")]
|
||||
public FanMode Mode { get; private set; }
|
||||
|
||||
[CategoryAttribute("Fan")]
|
||||
public ushort CurrentRPM { get; private set; }
|
||||
|
||||
[CategoryAttribute("Fan")]
|
||||
public ushort DesiredRPM { get; private set; }
|
||||
|
||||
private LibreHardwareMonitor.Hardware.Computer libreHardwareComputer = new LibreHardwareMonitor.Hardware.Computer
|
||||
{
|
||||
IsCpuEnabled = true,
|
||||
IsGpuEnabled = true,
|
||||
IsStorageEnabled = true,
|
||||
IsBatteryEnabled = true
|
||||
};
|
||||
|
||||
public FanController()
|
||||
{
|
||||
libreHardwareComputer.Open();
|
||||
}
|
||||
|
||||
private void visitHardware(IHardware hardware)
|
||||
{
|
||||
Dictionary<FanSensor, ISensor> matched = new Dictionary<FanSensor, ISensor>();
|
||||
|
||||
foreach (ISensor hwSensor in hardware.Sensors)
|
||||
{
|
||||
foreach (var sensor in allSensors.Values)
|
||||
{
|
||||
if (sensor.Matches(hwSensor))
|
||||
matched[sensor] = hwSensor;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched.Any())
|
||||
{
|
||||
hardware.Update();
|
||||
foreach (var sensor in matched)
|
||||
sensor.Key.Update(sensor.Value, Mode);
|
||||
}
|
||||
|
||||
foreach (IHardware subhardware in hardware.SubHardware)
|
||||
{
|
||||
visitHardware(subhardware);
|
||||
}
|
||||
}
|
||||
|
||||
private ushort getDesiredRPM()
|
||||
{
|
||||
ushort rpm = 0;
|
||||
foreach (var sensor in allSensors.Values)
|
||||
if (sensor.CalculatedRPM.HasValue)
|
||||
rpm = Math.Max(rpm, sensor.CalculatedRPM.Value);
|
||||
return rpm;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
foreach (var sensor in allSensors.Values)
|
||||
sensor.Reset();
|
||||
|
||||
foreach (var hardware in libreHardwareComputer.Hardware)
|
||||
visitHardware(hardware);
|
||||
|
||||
allSensors["Batt"].Update("VLV0100", Vlv0100.GetBattTemperature(), Mode);
|
||||
|
||||
Vlv0100.SetFanDesiredRPM(getDesiredRPM());
|
||||
|
||||
CurrentRPM = Vlv0100.GetFanRPM();
|
||||
DesiredRPM = Vlv0100.GetFanDesiredRPM();
|
||||
}
|
||||
|
||||
public void SetMode(FanMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case FanMode.Default:
|
||||
Vlv0100.SetFanControl(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
Vlv0100.SetFanControl(true);
|
||||
break;
|
||||
}
|
||||
|
||||
this.Mode = mode;
|
||||
}
|
||||
|
||||
public bool IsAnyInvalid()
|
||||
{
|
||||
foreach (var sensor in allSensors.Values)
|
||||
{
|
||||
if (!sensor.IsValid(Mode))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
libreHardwareComputer.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
175
FanControl/FanControllerSensors.cs
Normal file
175
FanControl/FanControllerSensors.cs
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
using LibreHardwareMonitor.Hardware;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FanControl
|
||||
{
|
||||
internal partial class FanController
|
||||
{
|
||||
private Dictionary<string, FanSensor> allSensors = new Dictionary<string, FanSensor>
|
||||
{
|
||||
{
|
||||
"APU", new FanSensor()
|
||||
{
|
||||
// TODO: Is this correct?
|
||||
HardwareName = "AMD Custom APU 0405",
|
||||
HardwareType = HardwareType.Cpu,
|
||||
SensorName = "Package",
|
||||
SensorType = SensorType.Power,
|
||||
ValueDeadZone = 0.1f,
|
||||
Profiles = new Dictionary<FanMode, FanSensor.Profile>()
|
||||
{
|
||||
{
|
||||
FanMode.MidWay, new FanSensor.Profile()
|
||||
{
|
||||
Type = FanSensor.Profile.ProfileType.Constant,
|
||||
MinRPM = Vlv0100.MAX_FAN_RPM / 2
|
||||
}
|
||||
},
|
||||
{
|
||||
FanMode.Max, new FanSensor.Profile()
|
||||
{
|
||||
Type = FanSensor.Profile.ProfileType.Constant,
|
||||
MinRPM = Vlv0100.MAX_FAN_RPM
|
||||
}
|
||||
},
|
||||
{
|
||||
FanMode.SteamOS, new FanSensor.Profile()
|
||||
{
|
||||
Type = FanSensor.Profile.ProfileType.Constant,
|
||||
MinRPM = 1500
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"CPU", new FanSensor()
|
||||
{
|
||||
HardwareName = "AMD Custom APU 0405",
|
||||
HardwareType = HardwareType.Cpu,
|
||||
SensorName = "Core (Tctl/Tdie)",
|
||||
SensorType = SensorType.Temperature,
|
||||
ValueDeadZone = 2.0f,
|
||||
Profiles = new Dictionary<FanMode, FanSensor.Profile>()
|
||||
{
|
||||
{
|
||||
FanMode.SteamOS, new FanSensor.Profile()
|
||||
{
|
||||
Type = FanSensor.Profile.ProfileType.Quadratic,
|
||||
MinInput = 55,
|
||||
MaxInput = 90,
|
||||
A = 2.286f,
|
||||
B = -188.6f,
|
||||
C = 5457.0f
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"GPU", new FanSensor()
|
||||
{
|
||||
HardwareName = "AMD Custom GPU 0405",
|
||||
HardwareType = HardwareType.GpuAmd,
|
||||
SensorName = "GPU Core",
|
||||
SensorType = SensorType.Temperature,
|
||||
ValueDeadZone = 2.0f,
|
||||
Profiles = new Dictionary<FanMode, FanSensor.Profile>()
|
||||
{
|
||||
{
|
||||
FanMode.SteamOS, new FanSensor.Profile()
|
||||
{
|
||||
Type = FanSensor.Profile.ProfileType.Quadratic,
|
||||
MinInput = 55,
|
||||
MaxInput = 90,
|
||||
A = 2.286f,
|
||||
B = -188.6f,
|
||||
C = 5457.0f
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"SSD", new FanSensor()
|
||||
{
|
||||
HardwareType = HardwareType.Storage,
|
||||
SensorName = "Temperature",
|
||||
SensorType = SensorType.Temperature,
|
||||
ValueDeadZone = 1.0f,
|
||||
Profiles = new Dictionary<FanMode, FanSensor.Profile>()
|
||||
{
|
||||
{
|
||||
FanMode.SteamOS, new FanSensor.Profile()
|
||||
{
|
||||
Type = FanSensor.Profile.ProfileType.Pid,
|
||||
MinInput = 30,
|
||||
MaxInput = 70,
|
||||
MaxRPM = 3000,
|
||||
PidSetPoint = 70,
|
||||
Kp = 0,
|
||||
Ki = -20,
|
||||
Kd = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Batt", new FanSensor()
|
||||
{
|
||||
HardwareType = HardwareType.Battery,
|
||||
SensorName = "Temperature",
|
||||
SensorType = SensorType.Temperature,
|
||||
ValueDeadZone = 1.0f,
|
||||
Profiles = new Dictionary<FanMode, FanSensor.Profile>()
|
||||
{
|
||||
{
|
||||
FanMode.SteamOS, new FanSensor.Profile()
|
||||
{
|
||||
// If battery goes over 40oC require 2kRPM
|
||||
Type = FanSensor.Profile.ProfileType.Constant,
|
||||
MinInput = 0,
|
||||
MaxInput = 40,
|
||||
MinRPM = 0,
|
||||
MaxRPM = 2000,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#region Sensor Properties for Property Grid
|
||||
[CategoryAttribute("Sensor - APU"), DisplayName("Name")]
|
||||
public String? APUName { get { return allSensors["APU"].Name; } }
|
||||
[CategoryAttribute("Sensor - APU"), DisplayName("Power")]
|
||||
public String? APUPower { get { return allSensors["APU"].FormattedValue(); } }
|
||||
|
||||
[CategoryAttribute("Sensor - CPU"), DisplayName("Name")]
|
||||
public String? CPUName { get { return allSensors["CPU"].Name; } }
|
||||
[CategoryAttribute("Sensor - CPU"), DisplayName("Temperature")]
|
||||
public String? CPUTemperature { get { return allSensors["CPU"].FormattedValue(); } }
|
||||
|
||||
[CategoryAttribute("Sensor - GPU"), DisplayName("Name")]
|
||||
public String? GPUName { get { return allSensors["GPU"].Name; } }
|
||||
[CategoryAttribute("Sensor - GPU"), DisplayName("Temperature")]
|
||||
public String? GPUTemperature { get { return allSensors["GPU"].FormattedValue(); } }
|
||||
|
||||
[CategoryAttribute("Sensor - SSD"), DisplayName("Name")]
|
||||
public String? SSDName { get { return allSensors["SSD"].Name; } }
|
||||
[CategoryAttribute("Sensor - SSD"), DisplayName("Temperature")]
|
||||
public String? SSDTemperature { get { return allSensors["SSD"].FormattedValue(); } }
|
||||
[CategoryAttribute("Sensor - Battery"), DisplayName("Name")]
|
||||
public String? BatteryName { get { return allSensors["Batt"].Name; } }
|
||||
[CategoryAttribute("Sensor - Battery"), DisplayName("Temperature")]
|
||||
public String? BatteryTemperature { get { return allSensors["Batt"].FormattedValue(); } }
|
||||
|
||||
#endregion Sensor Properties for Property Grid
|
||||
}
|
||||
}
|
||||
223
FanControl/FanSensor.cs
Normal file
223
FanControl/FanSensor.cs
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
using LibreHardwareMonitor.Hardware;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Security.Policy;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FanControl
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
internal class FanSensor
|
||||
{
|
||||
public string? Name { get; internal set; }
|
||||
public float? Value { get; internal set; }
|
||||
public ushort? CalculatedRPM { get; internal set; }
|
||||
|
||||
public float ValueDeadZone { get; set; }
|
||||
public int AvgSamples { get; set; } = 5;
|
||||
|
||||
internal string HardwareName { get; set; } = "";
|
||||
internal HardwareType HardwareType { get; set; }
|
||||
internal string SensorName { get; set; } = "";
|
||||
internal SensorType SensorType { get; set; }
|
||||
|
||||
private List<float> AllSamples = new List<float>();
|
||||
|
||||
internal Dictionary<FanController.FanMode, Profile> Profiles { get; set; } = new Dictionary<FanController.FanMode, Profile>();
|
||||
|
||||
internal class Profile
|
||||
{
|
||||
public enum ProfileType
|
||||
{
|
||||
Constant,
|
||||
Quadratic,
|
||||
Pid
|
||||
}
|
||||
|
||||
public ProfileType Type { get; set; }
|
||||
public float MinInput { get; set; }
|
||||
public float MaxInput { get; set; } = 90;
|
||||
public ushort MinRPM { get; set; }
|
||||
public ushort MaxRPM { get; set; } = ushort.MaxValue;
|
||||
|
||||
public float A { get; set; }
|
||||
public float B { get; set; }
|
||||
public float C { get; set; }
|
||||
|
||||
public float Kp { get; set; }
|
||||
public float Ki { get; set; }
|
||||
public float Kd { get; set; }
|
||||
public float PidSetPoint { get; set; }
|
||||
|
||||
private float? pidLastInput { get; set; }
|
||||
private float pidLastError { get; set; }
|
||||
private DateTime pidLastTime { get; set; }
|
||||
private float pidP { get; set; }
|
||||
private float pidI { get; set; }
|
||||
private float pidD { get; set; }
|
||||
|
||||
public ushort CalculateRPM(float input)
|
||||
{
|
||||
float rpm = 0;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case ProfileType.Constant:
|
||||
rpm = MinRPM;
|
||||
break;
|
||||
|
||||
case ProfileType.Quadratic:
|
||||
rpm = calculateQuadraticRPM(input);
|
||||
break;
|
||||
|
||||
case ProfileType.Pid:
|
||||
rpm = calculatePidRPM(input);
|
||||
break;
|
||||
}
|
||||
|
||||
if (input < MinInput)
|
||||
rpm = MinRPM;
|
||||
else if (input > MaxInput)
|
||||
rpm = MaxRPM;
|
||||
|
||||
rpm = Math.Clamp(rpm, (float)MinRPM, (float)MaxRPM);
|
||||
|
||||
return (ushort)rpm;
|
||||
}
|
||||
|
||||
private float calculateQuadraticRPM(float input)
|
||||
{
|
||||
return A * input * input + B * input + C;
|
||||
}
|
||||
|
||||
private float calculatePidRPM(float input)
|
||||
{
|
||||
if (!pidLastInput.HasValue)
|
||||
{
|
||||
pidLastInput = input;
|
||||
pidLastTime = DateTime.Now;
|
||||
return 0;
|
||||
}
|
||||
|
||||
float error = PidSetPoint - input;
|
||||
float dInput = input - pidLastInput.Value;
|
||||
float dt = Math.Min((float)(DateTime.Now - pidLastTime).TotalSeconds, 1.0f);
|
||||
|
||||
this.pidP = Kp * error;
|
||||
this.pidI += Ki * error * dt;
|
||||
this.pidI = Math.Min(this.pidI, this.MaxRPM);
|
||||
this.pidD -= Kd * dInput / dt;
|
||||
|
||||
pidLastInput = input;
|
||||
pidLastError = error;
|
||||
pidLastTime = DateTime.Now;
|
||||
|
||||
return pidP + pidI + pidD;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Name = null;
|
||||
Value = null;
|
||||
CalculatedRPM = 0;
|
||||
}
|
||||
|
||||
public bool Matches(ISensor sensor)
|
||||
{
|
||||
return sensor != null &&
|
||||
sensor.Hardware.HardwareType == HardwareType &&
|
||||
sensor.Hardware.Name.StartsWith(HardwareName) &&
|
||||
sensor.SensorType == SensorType &&
|
||||
sensor.Name == SensorName;
|
||||
}
|
||||
|
||||
public bool Update(ISensor hwSensor, FanController.FanMode mode)
|
||||
{
|
||||
if (!Matches(hwSensor))
|
||||
return false;
|
||||
|
||||
System.Diagnostics.Trace.WriteLine(String.Format("{0}: {1} {2}, value: {3}, type: {4}",
|
||||
hwSensor.Identifier, hwSensor.Hardware.Name, hwSensor.Name, hwSensor.Value, hwSensor.SensorType));
|
||||
|
||||
return Update(
|
||||
String.Format("{0} {1}", hwSensor.Hardware.Name, hwSensor.Name),
|
||||
hwSensor.Value, mode);
|
||||
}
|
||||
|
||||
public bool Update(string name, float? newValue, FanController.FanMode mode)
|
||||
{
|
||||
if (!newValue.HasValue || newValue <= 0.0)
|
||||
return false;
|
||||
|
||||
if (AllSamples.Count == 0 || Math.Abs(AllSamples.Last() - newValue.Value) > ValueDeadZone)
|
||||
{
|
||||
AllSamples.Add(newValue.Value);
|
||||
while (AllSamples.Count > AvgSamples)
|
||||
AllSamples.RemoveAt(0);
|
||||
}
|
||||
|
||||
float avgValue = 0.0f;
|
||||
foreach (var value in AllSamples)
|
||||
avgValue += value;
|
||||
|
||||
Name = name;
|
||||
Value = avgValue / AllSamples.Count;
|
||||
CalculatedRPM = CalculateRPM(mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsValid(FanController.FanMode mode)
|
||||
{
|
||||
// If we have profile, but no sensor value to consume it
|
||||
// it is invalid
|
||||
if (Profiles.ContainsKey(mode) && !Value.HasValue)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public String FormattedValue()
|
||||
{
|
||||
if (!Value.HasValue)
|
||||
return "";
|
||||
|
||||
String value = Value.Value.ToString("F1");
|
||||
|
||||
switch (SensorType)
|
||||
{
|
||||
case SensorType.Temperature:
|
||||
value += "℃";
|
||||
break;
|
||||
|
||||
case SensorType.Power:
|
||||
value += "W";
|
||||
break;
|
||||
}
|
||||
|
||||
if (CalculatedRPM.HasValue)
|
||||
{
|
||||
value += " (min: " + CalculatedRPM.ToString() + "RPM)";
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public ushort? CalculateRPM(FanController.FanMode mode)
|
||||
{
|
||||
if (!Profiles.ContainsKey(mode) || !Value.HasValue)
|
||||
return null;
|
||||
|
||||
var profile = Profiles[mode];
|
||||
return profile.CalculateRPM(Value.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue