diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index 67f5a5086..a9ac9f39d 100755
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -34,6 +34,7 @@ SRCS
"./thermal/TMP1075.c"
"./thermal/thermal.c"
"./thermal/PID.c"
+ "./thermal/auto_tune.c"
"./power/TPS546.c"
"./power/DS4432U.c"
"./power/INA260.c"
diff --git a/main/http_server/axe-os/src/app/app-routing.module.ts b/main/http_server/axe-os/src/app/app-routing.module.ts
index 8989ffa08..084393bf9 100644
--- a/main/http_server/axe-os/src/app/app-routing.module.ts
+++ b/main/http_server/axe-os/src/app/app-routing.module.ts
@@ -12,6 +12,7 @@ import { DesignComponent } from './components/design/design.component';
import { PoolComponent } from './components/pool/pool.component';
import { AppLayoutComponent } from './layout/app.layout.component';
import { ApModeGuard } from './guards/ap-mode.guard';
+import { AutotuneComponent } from './components/autotune/autotune.component';
const TITLE_PREFIX = 'AxeOS';
@@ -76,6 +77,11 @@ const routes: Routes = [
path: 'pool',
component: PoolComponent,
title: `${TITLE_PREFIX} Pool`,
+ },
+ {
+ path: 'autotune',
+ component: AutotuneComponent,
+ title: `${TITLE_PREFIX} Autotune`,
}
]
},
@@ -87,3 +93,4 @@ const routes: Routes = [
exports: [RouterModule]
})
export class AppRoutingModule { }
+
diff --git a/main/http_server/axe-os/src/app/app.module.ts b/main/http_server/axe-os/src/app/app.module.ts
index c1e11f9b1..7384f1781 100644
--- a/main/http_server/axe-os/src/app/app.module.ts
+++ b/main/http_server/axe-os/src/app/app.module.ts
@@ -25,6 +25,7 @@ import { SystemComponent } from './components/system/system.component';
import { UpdateComponent } from './components/update/update.component';
import { NetworkComponent } from './components/network/network.component';
import { SettingsComponent } from './components/settings/settings.component';
+import { AutotuneComponent } from './components/autotune/autotune.component';
import { SwarmComponent } from './components/swarm/swarm.component';
import { ThemeConfigComponent } from './components/design/theme-config.component';
import { DesignComponent } from './components/design/design.component';
@@ -43,6 +44,7 @@ import { DialogService, DialogListComponent } from './services/dialog.service';
const components = [
AppComponent,
EditComponent,
+ AutotuneComponent,
NetworkEditComponent,
HomeComponent,
ModalComponent,
diff --git a/main/http_server/axe-os/src/app/components/autotune/autotune.component.html b/main/http_server/axe-os/src/app/components/autotune/autotune.component.html
new file mode 100644
index 000000000..c98c81882
--- /dev/null
+++ b/main/http_server/axe-os/src/app/components/autotune/autotune.component.html
@@ -0,0 +1,50 @@
+
\ No newline at end of file
diff --git a/main/http_server/axe-os/src/app/components/autotune/autotune.component.spec.ts b/main/http_server/axe-os/src/app/components/autotune/autotune.component.spec.ts
new file mode 100644
index 000000000..614d080ae
--- /dev/null
+++ b/main/http_server/axe-os/src/app/components/autotune/autotune.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AutotuneComponent } from './autotune.component';
+
+describe('SettingsComponent', () => {
+ let component: AutotuneComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [AutotuneComponent]
+ });
+ fixture = TestBed.createComponent(AutotuneComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/main/http_server/axe-os/src/app/components/autotune/autotune.component.ts b/main/http_server/axe-os/src/app/components/autotune/autotune.component.ts
new file mode 100644
index 000000000..29032c9e8
--- /dev/null
+++ b/main/http_server/axe-os/src/app/components/autotune/autotune.component.ts
@@ -0,0 +1,199 @@
+import { Component, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { HttpErrorResponse } from '@angular/common/http';
+
+import { LoadingService } from 'src/app/services/loading.service';
+
+import { ToastrService } from 'ngx-toastr';
+import { forkJoin } from 'rxjs';
+import { SystemInfo, SystemService } from 'src/app/generated';
+import { AutotuneService } from 'src/app/generated/api/autotune.service';
+
+interface SliderConfig {
+ formControlName: string;
+ label: string;
+ min: number;
+ max: number;
+ step: number;
+ unit: string;
+ tooltip: string;
+}
+
+@Component({
+ selector: 'autotune',
+ templateUrl: './autotune.component.html',
+})
+export class AutotuneComponent implements OnInit {
+ public autotuneForm!: FormGroup;
+
+ public sliderConfigs: SliderConfig[] = [
+ {
+ formControlName: 'power_limit',
+ label: 'Power Limit',
+ min: 10,
+ max: 40,
+ step: 1,
+ unit: 'W',
+ tooltip: 'The maximum power limit that the miner is allowed to use, based on your power supply output. Default:25W'
+ },
+ {
+ formControlName: 'fan_limit',
+ label: 'Fan Limit',
+ min: 0,
+ max: 100,
+ step: 1,
+ unit: '%',
+ tooltip: 'Sets the maximum fan speed limit in percent. This ensures that fans do not exceed this speed, helping to control noise levels and reduce wear on the fans. Default:75%'
+ },
+ {
+ formControlName: 'osh_pow_limit',
+ label: 'Overshoot Power Limit',
+ min: 0,
+ max: 2.2,
+ step: 0.1,
+ unit: 'W',
+ tooltip: 'Maximum allowed power overshoot in watts. This provides a buffer for temporary spikes above the power limit, allowing for brief surges without triggering safety mechanisms. Default:0.2W'
+ },
+ {
+ formControlName: 'osh_fan_limit',
+ label: 'Overshoot Fanspeed',
+ min: 0,
+ max: 25,
+ step: 1,
+ unit: '%',
+ tooltip: 'Maximum allowed fan speed overshoot in percent. This provides a buffer for temporary spikes above the fan limit, allowing for brief increases without triggering safety mechanisms. Default:5%'
+ },
+ {
+ formControlName: 'max_volt_asic',
+ label: 'Max Voltage ASIC',
+ min: 1000,
+ max: 1400,
+ step: 1,
+ unit: 'mV',
+ tooltip: 'Maximum voltage for the ASIC in millivolts. This prevents over-voltage conditions that could damage hardware, ensuring safe operation within specified limits. Default:1400mV'
+ },
+ {
+ formControlName: 'max_freq_asic',
+ label: 'Max Frequency ASIC',
+ min: 400,
+ max: 1000,
+ step: 1,
+ unit: 'MHz',
+ tooltip: 'Maximum frequency for the ASIC in megahertz. This prevents overclocking beyond safe limits, ensuring stable and reliable performance. Default:1000MHz'
+ },
+ {
+ formControlName: 'max_temp_asic',
+ label: 'Max Temperature ASIC',
+ min: 20,
+ max: 70,
+ step: 1,
+ unit: '°C',
+ tooltip: 'Maximum temperature allowed for the ASIC in degrees Celsius. This ensures safe operation and prevents overheating that could damage hardware or affect performance. Default:65°C'
+ },
+ {
+ formControlName: 'max_temp_vr',
+ label: 'Max Temperature VR',
+ min: 20,
+ max: 90,
+ step: 1,
+ unit: '°C',
+ tooltip: 'Maximum temperature for the VoltageRegulator in degrees Celsius. This ensures thermal safety, preventing hardware damage due to overheating. Default:85°C'
+ },
+ ];
+
+ constructor(
+ private fb: FormBuilder,
+ private loadingService: LoadingService,
+ private systemService: SystemService,
+ private toastr: ToastrService,
+ private autotuneService: AutotuneService
+ ) { }
+
+ ngOnInit(): void {
+ this.autotuneForm = this.fb.group({
+ power_limit: [null, [Validators.required, Validators.min(1)]],
+ fan_limit: [null, [Validators.required, Validators.min(0)]],
+ osh_pow_limit: [null],
+ osh_fan_limit: [null],
+ max_volt_asic: [null, [Validators.required, Validators.min(1)]],
+ max_freq_asic: [null, [Validators.required, Validators.min(1)]],
+ max_temp_asic: [null, [Validators.required, Validators.min(1)]],
+ max_temp_vr: [null],
+ auto_tune: [false]
+ });
+
+ this.loadAutotuneSettings();
+ }
+
+ private loadAutotuneSettings(): void {
+ forkJoin({
+ info: this.systemService.getSystemInfo(),
+ asic: this.systemService.getAsicSettings(),
+ autotune: this.autotuneService.getAutotuneSettings()
+ }).pipe(
+ this.loadingService.lockUIUntilComplete()
+ ).subscribe({
+ next: ({ info, asic, autotune }) => {
+ this.updateSliderMinForPid(info);
+
+ const maxVoltage = asic.defaultVoltage * 1.25;
+ const maxFrequency = asic.defaultFrequency * 2;
+
+ this.sliderConfigs = this.sliderConfigs.map(config => {
+ if (config.formControlName === 'max_volt_asic') {
+ return { ...config, max: maxVoltage };
+ }
+ if (config.formControlName === 'max_freq_asic') {
+ return { ...config, max: maxFrequency };
+ }
+ return config;
+ });
+
+ this.autotuneForm.patchValue({
+ power_limit: autotune.power_limit,
+ fan_limit: autotune.fan_limit,
+ osh_pow_limit: autotune.osh_pow_limit,
+ osh_fan_limit: autotune.osh_fan_limit,
+ max_volt_asic: autotune.max_volt_asic,
+ max_freq_asic: autotune.max_freq_asic,
+ max_temp_asic: autotune.max_temp_asic,
+ max_temp_vr: autotune.max_temp_vr,
+ auto_tune: autotune.auto_tune ?? false
+ });
+ },
+ error: () => {
+ this.toastr.error('Failed to load autotune settings');
+ }
+ });
+ }
+
+ public updateAutotune(): void {
+ this.autotuneService.updateAutotuneSettings(this.autotuneForm.value).subscribe({
+ next: () => this.toastr.success('Autotune settings saved'),
+ error: (err: HttpErrorResponse) => this.toastr.error(`Could not save autotune settings. ${err.message}`)
+ });
+ }
+
+ private updateSliderMinForPid(info: SystemInfo): void {
+ // Check if PID is active (autofanspeed = 1)
+ const isPidActive = info.autofanspeed === 1;
+
+ // If PID is active, set the minimum value to (temptarget + 1)
+ // Otherwise keep the default minimum of 20
+ const minTemp = isPidActive ? (info.temptarget + 1) : 20;
+ const minFanspeed = isPidActive ? (info.minFanSpeed + 1) : 20;
+ const maxPower = info.maxPower;
+ // Update the slider configuration in our component
+ this.sliderConfigs.forEach(config => {
+ if (config.formControlName === 'max_temp_asic') {
+ config.min = minTemp;
+ }
+ if (config.formControlName === 'fan_limit') {
+ config.min = minFanspeed;
+ }
+ if (config.formControlName === 'power_limit') {
+ config.max = maxPower;
+ }
+ });
+ }
+}
diff --git a/main/http_server/axe-os/src/app/components/edit/edit.component.ts b/main/http_server/axe-os/src/app/components/edit/edit.component.ts
index 95dee8fc3..ae69831d6 100644
--- a/main/http_server/axe-os/src/app/components/edit/edit.component.ts
+++ b/main/http_server/axe-os/src/app/components/edit/edit.component.ts
@@ -162,7 +162,7 @@ export class EditComponent implements OnInit, OnDestroy, OnChanges {
Validators.max(this.displayTimeoutMaxValue)
]],
coreVoltage: [info.coreVoltage, [Validators.required]],
- frequency: [info.frequency, [Validators.required]],
+ frequency: [info.frequencySet, [Validators.required]],
autofanspeed: [info.autofanspeed == 1, [Validators.required]],
minfanspeed: [info.minFanSpeed, [Validators.required]],
manualFanSpeed: [info.manualFanSpeed, [Validators.required]],
diff --git a/main/http_server/axe-os/src/app/components/home/home.component.ts b/main/http_server/axe-os/src/app/components/home/home.component.ts
index 5a9250be7..d29f5047e 100644
--- a/main/http_server/axe-os/src/app/components/home/home.component.ts
+++ b/main/http_server/axe-os/src/app/components/home/home.component.ts
@@ -332,6 +332,7 @@ export class HomeComponent implements OnInit, OnDestroy {
stats.statistics.forEach((element: number[]) => {
switch (chartLabelValue(chartY1DataLabel)) {
case eChartLabel.asicVoltage:
+ case eChartLabel.asicVoltageSet:
case eChartLabel.voltage:
case eChartLabel.current:
element[idxChartY1Data] = element[idxChartY1Data] / 1000;
@@ -341,6 +342,7 @@ export class HomeComponent implements OnInit, OnDestroy {
}
switch (chartLabelValue(chartY2DataLabel)) {
case eChartLabel.asicVoltage:
+ case eChartLabel.asicVoltageSet:
case eChartLabel.voltage:
case eChartLabel.current:
element[idxChartY2Data] = element[idxChartY2Data] / 1000;
@@ -412,6 +414,7 @@ export class HomeComponent implements OnInit, OnDestroy {
info.current = info.current / 1000;
info.coreVoltageActual = info.coreVoltageActual / 1000;
info.coreVoltage = info.coreVoltage / 1000;
+ info.coreVoltageSet = info.coreVoltageSet / 1000;
return info;
}),
tap(info => {
@@ -471,9 +474,11 @@ export class HomeComponent implements OnInit, OnDestroy {
info.current = parseFloat(info.current.toFixed(1));
info.coreVoltageActual = parseFloat(info.coreVoltageActual.toFixed(2));
info.coreVoltage = parseFloat(info.coreVoltage.toFixed(2));
+ info.coreVoltageSet = parseFloat(info.coreVoltageSet.toFixed(2));
info.temp = parseFloat(info.temp.toFixed(1));
info.temp2 = parseFloat(info.temp2.toFixed(1));
info.responseTime = parseFloat(info.responseTime.toFixed(1));
+ info.frequency = parseFloat(info.frequency.toFixed(2));
return info;
}),
@@ -741,6 +746,7 @@ export class HomeComponent implements OnInit, OnDestroy {
case eChartLabel.asicTemp: return this.maxTemp;
case eChartLabel.vrTemp: return this.maxTemp + 25;
case eChartLabel.asicVoltage: return info.coreVoltage;
+ case eChartLabel.asicVoltageSet: return info.coreVoltageSet;
case eChartLabel.voltage: return info.nominalVoltage + .5;
case eChartLabel.power: return this.maxPower;
case eChartLabel.current: return this.maxPower / info.coreVoltage;
@@ -748,6 +754,7 @@ export class HomeComponent implements OnInit, OnDestroy {
case eChartLabel.fanRpm: return 7000;
case eChartLabel.fan2Rpm: return 7000;
case eChartLabel.responseTime: return 50;
+ case eChartLabel.frequency: return 0;
default: return 0;
}
}
@@ -762,6 +769,7 @@ export class HomeComponent implements OnInit, OnDestroy {
case eChartLabel.asicTemp: return info.temp;
case eChartLabel.vrTemp: return info.vrTemp;
case eChartLabel.asicVoltage: return info.coreVoltageActual;
+ case eChartLabel.asicVoltageSet: return info.coreVoltageSet;
case eChartLabel.voltage: return info.voltage;
case eChartLabel.power: return info.power;
case eChartLabel.current: return info.current;
@@ -771,6 +779,7 @@ export class HomeComponent implements OnInit, OnDestroy {
case eChartLabel.wifiRssi: return info.wifiRSSI;
case eChartLabel.freeHeap: return info.freeHeap;
case eChartLabel.responseTime: return info.responseTime;
+ case eChartLabel.frequency: return info.frequency;
default: return 0.0;
}
}
@@ -781,7 +790,8 @@ export class HomeComponent implements OnInit, OnDestroy {
case eChartLabel.asicTemp:
case eChartLabel.vrTemp: return {suffix: ' °C', precision: 1};
case eChartLabel.asicVoltage:
- case eChartLabel.voltage: return {suffix: ' V', precision: 1};
+ case eChartLabel.voltage:
+ case eChartLabel.asicVoltageSet: return {suffix: ' V', precision: 3};
case eChartLabel.power: return {suffix: ' W', precision: 1};
case eChartLabel.current: return {suffix: ' A', precision: 1};
case eChartLabel.fanSpeed: return {suffix: ' %', precision: 1};
@@ -789,6 +799,7 @@ export class HomeComponent implements OnInit, OnDestroy {
case eChartLabel.fan2Rpm: return {suffix: ' rpm', precision: 0};
case eChartLabel.wifiRssi: return {suffix: ' dBm', precision: 0};
case eChartLabel.responseTime: return {suffix: ' ms', precision: 1};
+ case eChartLabel.frequency: return {suffix: ' MHz', precision: 1};
default: return {suffix: '', precision: 0};
}
}
diff --git a/main/http_server/axe-os/src/app/layout/app.menu.component.ts b/main/http_server/axe-os/src/app/layout/app.menu.component.ts
index 1bab04a8a..b9ee476e3 100644
--- a/main/http_server/axe-os/src/app/layout/app.menu.component.ts
+++ b/main/http_server/axe-os/src/app/layout/app.menu.component.ts
@@ -34,6 +34,7 @@ export class AppMenuComponent implements OnInit {
{ label: 'Network', icon: 'pi pi-fw pi-wifi', routerLink: ['network'] },
{ label: 'Theme', icon: 'pi pi-fw pi-palette', routerLink: ['design'] },
{ label: 'Settings', icon: 'pi pi-fw pi-cog', routerLink: ['settings'] },
+ { label: 'Autotune', icon: 'pi pi-fw pi-sliders-h', routerLink: ['autotune'] },
{ label: 'Update', icon: 'pi pi-fw pi-sync', routerLink: ['update'] },
{ separator: true },
@@ -43,3 +44,4 @@ export class AppMenuComponent implements OnInit {
];
}
}
+
diff --git a/main/http_server/axe-os/src/app/services/system.service.ts b/main/http_server/axe-os/src/app/services/system.service.ts
index c4f8349de..379a6221b 100644
--- a/main/http_server/axe-os/src/app/services/system.service.ts
+++ b/main/http_server/axe-os/src/app/services/system.service.ts
@@ -4,9 +4,11 @@ import { delay, Observable, of, timeout } from 'rxjs';
import { eChartLabel } from 'src/models/enum/eChartLabel';
import { chartLabelKey } from 'src/models/enum/eChartLabel';
import { chartLabelValue } from 'src/models/enum/eChartLabel';
+import { IAutotuneSettings } from 'src/models/IAutotuneSettings';
import {
SystemInfo as ISystemInfo,
SystemStatistics as ISystemStatistics,
+
SystemASIC as ISystemASIC,
SystemASICASICModelEnum,
SystemService as GeneratedSystemService,
@@ -59,6 +61,7 @@ export class SystemApiService {
freeHeapSpiram: 200504,
coreVoltage: 1200,
coreVoltageActual: 1200,
+ coreVoltageSet: 1200,
hostname: "Bitaxe",
macAddr: "2C:54:91:88:C9:E3",
ssid: "default",
@@ -96,6 +99,7 @@ export class SystemApiService {
isUsingFallbackStratum: 0,
poolConnectionInfo: "IPv4 (TLS)",
frequency: 485,
+ frequencySet: 450,
version: "v2.12.0",
axeOSVersion: "v2.12.0",
idfVersion: "v5.5.1",
@@ -151,43 +155,43 @@ export class SystemApiService {
return this.generatedSystemService.getSystemStatistics(columnList).pipe(timeout(API_TIMEOUT));
}
- const hashrateData = [0,413.4903744405481,410.7764830376959,440.100549473198,430.5816012914026,452.5464981767163,414.9564271189586,498.7294609150379,411.1671601439723,491.327834852684];
- const powerData = [14.45068359375,14.86083984375,15.03173828125,15.1171875,15.1171875,15.1513671875,15.185546875,15.27099609375,15.30517578125,15.33935546875];
- const asicTempData = [-1,58.5,59.625,60.125,60.75,61.5,61.875,62.125,62.5,63];
- const vrTempData = [45,45,45,44,45,44,44,45,45,45];
- const asicVoltageData = [1221,1223,1219,1223,1217,1222,1221,1219,1221,1221];
- const voltageData = [5196.875,5204.6875,5196.875,5196.875,5196.875,5196.875,5196.875,5196.875,5196.875,5204.6875];
- const currentData = [2284.375,2284.375,2253.125,2284.375,2253.125,2231.25,2284.375,2253.125,2253.125,2284.375];
- const fanSpeedData = [48,52,50,52,53,54,50,50,48,48];
- const fanRpmData = [4032,3545,3904,3691,3564,3554,3691,3573,3701,4044];
- const fan2RpmData = [3545,3904,3691,3564,3554,3691,3573,3701,4044, 4032];
- const wifiRssiData = [-35,-34,-33,-34,-34,-34,-33,-35,-33,-34];
- const freeHeapData = [214504,212504,213504,210504,207504,209504,203504,202504,201504,200504];
- const responseTimeData = [15.1,14.5,14.3,15.1,13.1,16.1,28.6,18.4,17.7,17.6,18.0,15.5];
- const timestampData = [13131,18126,23125,28125,33125,38125,43125,48125,53125,58125];
+ const hashrateData = [0, 413.4903744405481, 410.7764830376959, 440.100549473198, 430.5816012914026, 452.5464981767163, 414.9564271189586, 498.7294609150379, 411.1671601439723, 491.327834852684];
+ const powerData = [14.45068359375, 14.86083984375, 15.03173828125, 15.1171875, 15.1171875, 15.1513671875, 15.185546875, 15.27099609375, 15.30517578125, 15.33935546875];
+ const asicTempData = [-1, 58.5, 59.625, 60.125, 60.75, 61.5, 61.875, 62.125, 62.5, 63];
+ const vrTempData = [45, 45, 45, 44, 45, 44, 44, 45, 45, 45];
+ const asicVoltageData = [1221, 1223, 1219, 1223, 1217, 1222, 1221, 1219, 1221, 1221];
+ const voltageData = [5196.875, 5204.6875, 5196.875, 5196.875, 5196.875, 5196.875, 5196.875, 5196.875, 5196.875, 5204.6875];
+ const currentData = [2284.375, 2284.375, 2253.125, 2284.375, 2253.125, 2231.25, 2284.375, 2253.125, 2253.125, 2284.375];
+ const fanSpeedData = [48, 52, 50, 52, 53, 54, 50, 50, 48, 48];
+ const fanRpmData = [4032, 3545, 3904, 3691, 3564, 3554, 3691, 3573, 3701, 4044];
+ const fan2RpmData = [3545, 3904, 3691, 3564, 3554, 3691, 3573, 3701, 4044, 4032];
+ const wifiRssiData = [-35, -34, -33, -34, -34, -34, -33, -35, -33, -34];
+ const freeHeapData = [214504, 212504, 213504, 210504, 207504, 209504, 203504, 202504, 201504, 200504];
+ const responseTimeData = [15.1, 14.5, 14.3, 15.1, 13.1, 16.1, 28.6, 18.4, 17.7, 17.6, 18.0, 15.5];
+ const timestampData = [13131, 18126, 23125, 28125, 33125, 38125, 43125, 48125, 53125, 58125];
columnList.push("timestamp");
let statisticsList: number[][] = [];
- for(let i: number = 0; i < 10; i++) {
+ for (let i: number = 0; i < 10; i++) {
statisticsList[i] = [];
- for(let j: number = 0; j < columnList.length; j++) {
+ for (let j: number = 0; j < columnList.length; j++) {
switch (chartLabelValue(columnList[j])) {
- case eChartLabel.hashrate: statisticsList[i][j] = hashrateData[i]; break;
- case eChartLabel.hashrate_1m: statisticsList[i][j] = hashrateData[i]; break;
- case eChartLabel.hashrate_10m: statisticsList[i][j] = hashrateData[i]; break;
- case eChartLabel.hashrate_1h: statisticsList[i][j] = hashrateData[i]; break;
- case eChartLabel.power: statisticsList[i][j] = powerData[i]; break;
- case eChartLabel.asicTemp: statisticsList[i][j] = asicTempData[i]; break;
- case eChartLabel.vrTemp: statisticsList[i][j] = vrTempData[i]; break;
- case eChartLabel.asicVoltage: statisticsList[i][j] = asicVoltageData[i]; break;
- case eChartLabel.voltage: statisticsList[i][j] = voltageData[i]; break;
- case eChartLabel.current: statisticsList[i][j] = currentData[i]; break;
- case eChartLabel.fanSpeed: statisticsList[i][j] = fanSpeedData[i]; break;
- case eChartLabel.fanRpm: statisticsList[i][j] = fanRpmData[i]; break;
- case eChartLabel.fan2Rpm: statisticsList[i][j] = fan2RpmData[i]; break;
- case eChartLabel.wifiRssi: statisticsList[i][j] = wifiRssiData[i]; break;
- case eChartLabel.freeHeap: statisticsList[i][j] = freeHeapData[i]; break;
+ case eChartLabel.hashrate: statisticsList[i][j] = hashrateData[i]; break;
+ case eChartLabel.hashrate_1m: statisticsList[i][j] = hashrateData[i]; break;
+ case eChartLabel.hashrate_10m: statisticsList[i][j] = hashrateData[i]; break;
+ case eChartLabel.hashrate_1h: statisticsList[i][j] = hashrateData[i]; break;
+ case eChartLabel.power: statisticsList[i][j] = powerData[i]; break;
+ case eChartLabel.asicTemp: statisticsList[i][j] = asicTempData[i]; break;
+ case eChartLabel.vrTemp: statisticsList[i][j] = vrTempData[i]; break;
+ case eChartLabel.asicVoltage: statisticsList[i][j] = asicVoltageData[i]; break;
+ case eChartLabel.voltage: statisticsList[i][j] = voltageData[i]; break;
+ case eChartLabel.current: statisticsList[i][j] = currentData[i]; break;
+ case eChartLabel.fanSpeed: statisticsList[i][j] = fanSpeedData[i]; break;
+ case eChartLabel.fanRpm: statisticsList[i][j] = fanRpmData[i]; break;
+ case eChartLabel.fan2Rpm: statisticsList[i][j] = fan2RpmData[i]; break;
+ case eChartLabel.wifiRssi: statisticsList[i][j] = wifiRssiData[i]; break;
+ case eChartLabel.freeHeap: statisticsList[i][j] = freeHeapData[i]; break;
case eChartLabel.responseTime: statisticsList[i][j] = responseTimeData[i]; break;
default:
if (columnList[j] === "timestamp") {
@@ -309,4 +313,28 @@ export class SystemApiService {
}
+
+
+ public getAutotune() {
+ if (environment.production) {
+ return this.httpClient.get('/api/system/autotune').pipe(timeout(5000));
+ }
+
+ // Mock data for development
+ return of({
+ power_limit: 20,
+ fan_limit: 75,
+ max_volt_asic: 1400,
+ max_freq_asic: 1000,
+ max_temp_asic: 65,
+ max_temp_vr: 85,
+ auto_tune: false,
+ osh_pow_limit: 0.2,
+ osh_fan_limit: 5,
+ }).pipe(delay(1000));
+ }
+
+ public updateAutotune(data: any) {
+ return this.httpClient.post('/api/system/autotune', data);
+ }
}
diff --git a/main/http_server/axe-os/src/models/IAutotuneSettings.ts b/main/http_server/axe-os/src/models/IAutotuneSettings.ts
new file mode 100644
index 000000000..667e746c2
--- /dev/null
+++ b/main/http_server/axe-os/src/models/IAutotuneSettings.ts
@@ -0,0 +1,11 @@
+export interface IAutotuneSettings {
+ power_limit: number;
+ fan_limit: number;
+ max_volt_asic: number;
+ max_freq_asic: number;
+ max_temp_asic: number;
+ max_temp_vr: number;
+ auto_tune: boolean;
+ osh_pow_limit: number;
+ osh_fan_limit: number;
+}
\ No newline at end of file
diff --git a/main/http_server/axe-os/src/models/enum/eChartLabel.ts b/main/http_server/axe-os/src/models/enum/eChartLabel.ts
index 6443df997..bea406074 100644
--- a/main/http_server/axe-os/src/models/enum/eChartLabel.ts
+++ b/main/http_server/axe-os/src/models/enum/eChartLabel.ts
@@ -7,6 +7,7 @@ export enum eChartLabel {
errorPercentage = 'Error %',
vrTemp = 'VR Temp',
asicVoltage = 'ASIC Voltage',
+ asicVoltageSet = 'ASIC Voltage Set',
voltage = 'Voltage',
power = 'Power',
current = 'Current',
@@ -16,6 +17,7 @@ export enum eChartLabel {
wifiRssi = 'Wi-Fi RSSI',
freeHeap = 'Free Heap',
responseTime = 'Response Time',
+ frequency = 'Frequency',
none = 'None'
}
diff --git a/main/http_server/http_server.c b/main/http_server/http_server.c
index d0ef19b25..ce0351eb6 100644
--- a/main/http_server/http_server.c
+++ b/main/http_server/http_server.c
@@ -40,6 +40,7 @@
#include "asic.h"
#include "TPS546.h"
#include "statistics_task.h"
+#include "auto_tune.h"
#include "theme_api.h" // Add theme API include
#include "axe-os/api/system/asic_settings.h"
#include "display.h"
@@ -58,6 +59,7 @@ static const char * STATS_LABEL_ERROR_PERCENTAGE = "errorPercentage";
static const char * STATS_LABEL_ASIC_TEMP = "asicTemp";
static const char * STATS_LABEL_VR_TEMP = "vrTemp";
static const char * STATS_LABEL_ASIC_VOLTAGE = "asicVoltage";
+static const char * STATS_LABEL_ASIC_VOLTAGE_SET = "asicVoltageSet";
static const char * STATS_LABEL_VOLTAGE = "voltage";
static const char * STATS_LABEL_POWER = "power";
static const char * STATS_LABEL_CURRENT = "current";
@@ -67,6 +69,7 @@ static const char * STATS_LABEL_FAN2_RPM = "fan2Rpm";
static const char * STATS_LABEL_WIFI_RSSI = "wifiRssi";
static const char * STATS_LABEL_FREE_HEAP = "freeHeap";
static const char * STATS_LABEL_RESPONSE_TIME = "responseTime";
+static const char * STATS_LABEL_FREQUENCY = "frequency";
static const char * STATS_LABEL_TIMESTAMP = "timestamp";
@@ -85,6 +88,7 @@ typedef enum
SRC_ASIC_TEMP,
SRC_VR_TEMP,
SRC_ASIC_VOLTAGE,
+ SRC_ASIC_VOLTAGE_SET,
SRC_VOLTAGE,
SRC_POWER,
SRC_CURRENT,
@@ -94,6 +98,7 @@ typedef enum
SRC_WIFI_RSSI,
SRC_FREE_HEAP,
SRC_RESPONSE_TIME,
+ SRC_FREQUENCY,
SRC_NONE // last
} DataSource;
@@ -111,12 +116,14 @@ DataSource strToDataSource(const char * sourceStr)
if (strcmp(sourceStr, STATS_LABEL_ASIC_TEMP) == 0) return SRC_ASIC_TEMP;
if (strcmp(sourceStr, STATS_LABEL_VR_TEMP) == 0) return SRC_VR_TEMP;
if (strcmp(sourceStr, STATS_LABEL_ASIC_VOLTAGE) == 0) return SRC_ASIC_VOLTAGE;
+ if (strcmp(sourceStr, STATS_LABEL_ASIC_VOLTAGE_SET) == 0) return SRC_ASIC_VOLTAGE_SET;
if (strcmp(sourceStr, STATS_LABEL_FAN_SPEED) == 0) return SRC_FAN_SPEED;
if (strcmp(sourceStr, STATS_LABEL_FAN_RPM) == 0) return SRC_FAN_RPM;
if (strcmp(sourceStr, STATS_LABEL_FAN2_RPM) == 0) return SRC_FAN2_RPM;
if (strcmp(sourceStr, STATS_LABEL_WIFI_RSSI) == 0) return SRC_WIFI_RSSI;
if (strcmp(sourceStr, STATS_LABEL_FREE_HEAP) == 0) return SRC_FREE_HEAP;
if (strcmp(sourceStr, STATS_LABEL_RESPONSE_TIME) == 0) return SRC_RESPONSE_TIME;
+ if (strcmp(sourceStr, STATS_LABEL_FREQUENCY) == 0) return SRC_FREQUENCY;
}
return SRC_NONE;
}
@@ -805,7 +812,7 @@ static esp_err_t GET_system_info(httpd_req_t * req)
char * stratumCert = nvs_config_get_string(NVS_CONFIG_STRATUM_CERT);
char * fallbackStratumCert = nvs_config_get_string(NVS_CONFIG_FALLBACK_STRATUM_CERT);
char * display = nvs_config_get_string(NVS_CONFIG_DISPLAY);
- float frequency = nvs_config_get_float(NVS_CONFIG_ASIC_FREQUENCY);
+ float frequency = GLOBAL_STATE->POWER_MANAGEMENT_MODULE.frequency_value;
uint8_t mac[6];
esp_wifi_get_mac(WIFI_IF_STA, mac);
@@ -846,7 +853,9 @@ static esp_err_t GET_system_info(httpd_req_t * req)
cJSON_AddNumberToObject(root, "coreVoltage", nvs_config_get_u16(NVS_CONFIG_ASIC_VOLTAGE));
cJSON_AddNumberToObject(root, "coreVoltageActual", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.core_voltage);
+ cJSON_AddNumberToObject(root, "coreVoltageSet", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.core_voltage);
cJSON_AddNumberToObject(root, "frequency", frequency);
+ cJSON_AddNumberToObject(root, "frequencySet", nvs_config_get_float(NVS_CONFIG_ASIC_FREQUENCY));
cJSON_AddStringToObject(root, "ssid", ssid);
cJSON_AddStringToObject(root, "macAddr", formattedMac);
cJSON_AddStringToObject(root, "hostname", hostname);
@@ -1024,6 +1033,7 @@ static esp_err_t GET_system_statistics(httpd_req_t * req)
if (dataSelection[SRC_VR_TEMP]) { cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_VR_TEMP)); }
if (dataSelection[SRC_ASIC_VOLTAGE]) { cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_ASIC_VOLTAGE)); }
if (dataSelection[SRC_VOLTAGE]) { cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_VOLTAGE)); }
+ if (dataSelection[SRC_ASIC_VOLTAGE_SET]) { cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_ASIC_VOLTAGE_SET)); }
if (dataSelection[SRC_POWER]) { cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_POWER)); }
if (dataSelection[SRC_CURRENT]) { cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_CURRENT)); }
if (dataSelection[SRC_FAN_SPEED]) { cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_FAN_SPEED)); }
@@ -1032,6 +1042,7 @@ static esp_err_t GET_system_statistics(httpd_req_t * req)
if (dataSelection[SRC_WIFI_RSSI]) { cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_WIFI_RSSI)); }
if (dataSelection[SRC_FREE_HEAP]) { cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_FREE_HEAP)); }
if (dataSelection[SRC_RESPONSE_TIME]) { cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_RESPONSE_TIME)); }
+ if (dataSelection[SRC_FREQUENCY]) { cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_FREQUENCY)); }
cJSON_AddItemToArray(labelArray, cJSON_CreateString(STATS_LABEL_TIMESTAMP));
cJSON_AddItemToObject(root, "labels", labelArray);
@@ -1050,6 +1061,7 @@ static esp_err_t GET_system_statistics(httpd_req_t * req)
if (dataSelection[SRC_ASIC_TEMP]) { cJSON_AddItemToArray(valueArray, cJSON_CreateFloat(statsData.chipTemperature)); }
if (dataSelection[SRC_VR_TEMP]) { cJSON_AddItemToArray(valueArray, cJSON_CreateFloat(statsData.vrTemperature)); }
if (dataSelection[SRC_ASIC_VOLTAGE]) { cJSON_AddItemToArray(valueArray, cJSON_CreateNumber(statsData.coreVoltageActual)); }
+ if (dataSelection[SRC_ASIC_VOLTAGE_SET]) { cJSON_AddItemToArray(valueArray, cJSON_CreateNumber(statsData.core_voltage)); }
if (dataSelection[SRC_VOLTAGE]) { cJSON_AddItemToArray(valueArray, cJSON_CreateFloat(statsData.voltage)); }
if (dataSelection[SRC_POWER]) { cJSON_AddItemToArray(valueArray, cJSON_CreateFloat(statsData.power)); }
if (dataSelection[SRC_CURRENT]) { cJSON_AddItemToArray(valueArray, cJSON_CreateFloat(statsData.current)); }
@@ -1059,6 +1071,7 @@ static esp_err_t GET_system_statistics(httpd_req_t * req)
if (dataSelection[SRC_WIFI_RSSI]) { cJSON_AddItemToArray(valueArray, cJSON_CreateNumber(statsData.wifiRSSI)); }
if (dataSelection[SRC_FREE_HEAP]) { cJSON_AddItemToArray(valueArray, cJSON_CreateNumber(statsData.freeHeap)); }
if (dataSelection[SRC_RESPONSE_TIME]) { cJSON_AddItemToArray(valueArray, cJSON_CreateFloat(statsData.responseTime)); }
+ if (dataSelection[SRC_FREQUENCY]) { cJSON_AddItemToArray(valueArray, cJSON_CreateNumber(statsData.frequency)); }
cJSON_AddItemToArray(valueArray, cJSON_CreateNumber(statsData.timestamp));
cJSON_AddItemToArray(statsArray, valueArray);
@@ -1230,6 +1243,110 @@ esp_err_t http_404_error_handler(httpd_req_t * req, httpd_err_code_t err)
return ESP_OK;
}
+esp_err_t GET_autotune_info(httpd_req_t * req)
+{
+ if (is_network_allowed(req) != ESP_OK) {
+ return httpd_resp_send_err(req, HTTPD_401_UNAUTHORIZED, "Unauthorized");
+ }
+ httpd_resp_set_type(req, "application/json");
+ if (set_cors_headers(req) != ESP_OK) {
+ httpd_resp_send_500(req);
+ return ESP_OK;
+ }
+
+ cJSON *root = cJSON_CreateObject();
+ cJSON_AddNumberToObject(root, nvs_config_get_settings(NVS_CONFIG_KEY_POWER_LIMIT)->nvs_key_name, AUTO_TUNE.power_limit);
+ cJSON_AddNumberToObject(root, nvs_config_get_settings(NVS_CONFIG_KEY_FAN_LIMIT)->nvs_key_name, AUTO_TUNE.fan_limit);
+ cJSON_AddNumberToObject(root, nvs_config_get_settings(NVS_CONFIG_KEY_MAX_VOLTAGE_ASIC)->nvs_key_name, AUTO_TUNE.max_voltage_asic);
+ cJSON_AddNumberToObject(root, nvs_config_get_settings(NVS_CONFIG_KEY_MAX_FREQUENCY_ASIC)->nvs_key_name, AUTO_TUNE.max_frequency_asic);
+ cJSON_AddNumberToObject(root, nvs_config_get_settings(NVS_CONFIG_KEY_MAX_TEMP_ASIC)->nvs_key_name, AUTO_TUNE.max_temp_asic);
+ cJSON_AddBoolToObject(root, nvs_config_get_settings(NVS_CONFIG_KEY_AUTO_TUNE_ENABLE)->nvs_key_name, AUTO_TUNE.auto_tune_hashrate);
+ cJSON_AddNumberToObject(root, nvs_config_get_settings(NVS_CONFIG_KEY_OVERSHOT_POWER_LIMIT)->nvs_key_name, AUTO_TUNE.overshot_power_limit);
+ cJSON_AddNumberToObject(root, nvs_config_get_settings(NVS_CONFIG_KEY_OVERSHOT_FAN_LIMIT)->nvs_key_name, AUTO_TUNE.overshot_fanspeed);
+ cJSON_AddNumberToObject(root, nvs_config_get_settings(NVS_CONFIG_KEY_MAX_TEMP_VR)->nvs_key_name, AUTO_TUNE.max_temp_vr);
+
+ const char *response = cJSON_Print(root);
+ httpd_resp_sendstr(req, response);
+ free((void *)response);
+ cJSON_Delete(root);
+ return ESP_OK;
+}
+
+esp_err_t POST_autotune_update(httpd_req_t * req)
+{
+ if (is_network_allowed(req) != ESP_OK) {
+ return httpd_resp_send_err(req, HTTPD_401_UNAUTHORIZED, "Unauthorized");
+ }
+ if (set_cors_headers(req) != ESP_OK) {
+ httpd_resp_send_500(req);
+ return ESP_OK;
+ }
+
+ int total_len = req->content_len;
+ char buf[512];
+ int received = 0, cur_len = 0;
+ if (total_len >= sizeof(buf)) {
+ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "content too long");
+ return ESP_OK;
+ }
+ while (cur_len < total_len) {
+ received = httpd_req_recv(req, buf + cur_len, total_len - cur_len);
+ if (received <= 0) {
+ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive data");
+ return ESP_OK;
+ }
+ cur_len += received;
+ }
+ buf[total_len] = '\0';
+
+ cJSON *root = cJSON_Parse(buf);
+ if (!root) {
+ httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON");
+ return ESP_OK;
+ }
+
+ cJSON *item;
+ if ((item = cJSON_GetObjectItem(root, nvs_config_get_settings(NVS_CONFIG_KEY_POWER_LIMIT)->nvs_key_name)) && cJSON_IsNumber(item)) {
+ AUTO_TUNE.power_limit = item->valuedouble;
+ nvs_config_set_u16(NVS_CONFIG_KEY_POWER_LIMIT, (uint16_t)AUTO_TUNE.power_limit);
+ }
+ if ((item = cJSON_GetObjectItem(root, nvs_config_get_settings(NVS_CONFIG_KEY_FAN_LIMIT)->nvs_key_name)) && cJSON_IsNumber(item)) {
+ AUTO_TUNE.fan_limit = item->valuedouble;
+ nvs_config_set_u16(NVS_CONFIG_KEY_FAN_LIMIT, (uint16_t)AUTO_TUNE.fan_limit);
+ }
+ if ((item = cJSON_GetObjectItem(root, nvs_config_get_settings(NVS_CONFIG_KEY_MAX_VOLTAGE_ASIC)->nvs_key_name)) && cJSON_IsNumber(item)) {
+ AUTO_TUNE.max_voltage_asic = item->valuedouble;
+ nvs_config_set_u16(NVS_CONFIG_KEY_MAX_VOLTAGE_ASIC, (uint16_t)AUTO_TUNE.max_voltage_asic);
+ }
+ if ((item = cJSON_GetObjectItem(root, nvs_config_get_settings(NVS_CONFIG_KEY_MAX_FREQUENCY_ASIC)->nvs_key_name)) && cJSON_IsNumber(item)) {
+ AUTO_TUNE.max_frequency_asic = item->valuedouble;
+ nvs_config_set_u16(NVS_CONFIG_KEY_MAX_FREQUENCY_ASIC, (uint16_t)AUTO_TUNE.max_frequency_asic);
+ }
+ if ((item = cJSON_GetObjectItem(root, nvs_config_get_settings(NVS_CONFIG_KEY_MAX_TEMP_ASIC)->nvs_key_name)) && cJSON_IsNumber(item)) {
+ AUTO_TUNE.max_temp_asic = item->valuedouble;
+ nvs_config_set_u16(NVS_CONFIG_KEY_MAX_TEMP_ASIC, (uint16_t)AUTO_TUNE.max_temp_asic);
+ }
+ if ((item = cJSON_GetObjectItem(root, nvs_config_get_settings(NVS_CONFIG_KEY_AUTO_TUNE_ENABLE)->nvs_key_name)) && cJSON_IsBool(item)) {
+ AUTO_TUNE.auto_tune_hashrate = item->valueint;
+ nvs_config_set_bool(NVS_CONFIG_KEY_AUTO_TUNE_ENABLE, (bool)AUTO_TUNE.auto_tune_hashrate);
+ }
+ if ((item = cJSON_GetObjectItem(root, nvs_config_get_settings(NVS_CONFIG_KEY_OVERSHOT_POWER_LIMIT)->nvs_key_name)) && cJSON_IsNumber(item)) {
+ AUTO_TUNE.overshot_power_limit = item->valuedouble;
+ nvs_config_set_float(NVS_CONFIG_KEY_OVERSHOT_POWER_LIMIT, AUTO_TUNE.overshot_power_limit);
+ }
+ if ((item = cJSON_GetObjectItem(root, nvs_config_get_settings(NVS_CONFIG_KEY_OVERSHOT_FAN_LIMIT)->nvs_key_name)) && cJSON_IsNumber(item)) {
+ AUTO_TUNE.overshot_fanspeed = (uint16_t)item->valuedouble;
+ nvs_config_set_u16(NVS_CONFIG_KEY_OVERSHOT_FAN_LIMIT, (uint16_t)AUTO_TUNE.overshot_fanspeed);
+ }
+ if ((item = cJSON_GetObjectItem(root, nvs_config_get_settings(NVS_CONFIG_KEY_MAX_TEMP_VR)->nvs_key_name)) && cJSON_IsNumber(item)) {
+ AUTO_TUNE.max_temp_vr = (uint16_t)item->valueint;
+ nvs_config_set_u16(NVS_CONFIG_KEY_MAX_TEMP_VR, (uint16_t)AUTO_TUNE.max_temp_vr);
+ }
+
+ cJSON_Delete(root);
+ httpd_resp_send_chunk(req, NULL, 0);
+ return ESP_OK;
+}
esp_err_t start_rest_server(void * pvParameters)
{
GLOBAL_STATE = (GlobalState *) pvParameters;
@@ -1254,7 +1371,7 @@ esp_err_t start_rest_server(void * pvParameters)
config.uri_match_fn = httpd_uri_match_wildcard;
config.stack_size = 8192;
config.max_open_sockets = 20;
- config.max_uri_handlers = 20;
+ config.max_uri_handlers = 22;
config.close_fn = websocket_close_fn;
config.lru_purge_enable = true;
@@ -1370,6 +1487,21 @@ esp_err_t start_rest_server(void * pvParameters)
};
httpd_register_uri_handler(server, &update_post_ota_www);
+ httpd_uri_t autotune_get_uri = {
+ .uri = "/api/system/autotune",
+ .method = HTTP_GET,
+ .handler = GET_autotune_info,
+ .user_ctx = rest_context
+ };
+ httpd_register_uri_handler(server, &autotune_get_uri);
+
+ httpd_uri_t autotune_post_uri = {
+ .uri = "/api/system/autotune",
+ .method = HTTP_POST,
+ .handler = POST_autotune_update,
+ .user_ctx = rest_context
+ };
+ httpd_register_uri_handler(server, &autotune_post_uri);
httpd_uri_t ws = {
.uri = "/api/ws",
.method = HTTP_GET,
diff --git a/main/http_server/openapi.yaml b/main/http_server/openapi.yaml
index c7bd3a27f..4e67db2bc 100644
--- a/main/http_server/openapi.yaml
+++ b/main/http_server/openapi.yaml
@@ -17,6 +17,62 @@ servers:
components:
schemas:
+ AutotuneSettings:
+ type: object
+ properties:
+ power_limit:
+ type: number
+ description: Power limit (in watts)
+ minimum: 0
+ maximum: 60
+ fan_limit:
+ type: number
+ description: Fan speed limit (%)
+ minimum: 0
+ maximum: 100
+ max_volt_asic:
+ type: number
+ description: Maximum voltage for ASIC chips (volts)
+ minimum: 0.5
+ maximum: 20
+ max_freq_asic:
+ type: number
+ description: Maximum frequency for ASIC chips (MHz)
+ minimum: 1100
+ maximum: 1400
+ max_temp_asic:
+ type: number
+ description: Maximum temperature for ASIC chips (°C)
+ minimum: 30
+ maximum: 90
+ auto_tune:
+ type: boolean
+ description: Whether autotuning is enabled to optimize hashrate
+ osh_pow_limit:
+ type: number
+ description: Power limit during overshoot conditions (watts)
+ minimum: 0
+ maximum: 2
+ osh_fan_limit:
+ type: number
+ description: Fan speed limit during overshoot conditions (%)
+ minimum: 0
+ maximum: 20
+ max_temp_vr:
+ type: number
+ description: Maximum temperature for VR (°C)
+ minimum: 40
+ maximum: 95
+ required:
+ - power_limit
+ - fan_limit
+ - max_voltage_asic
+ - max_frequency_asic
+ - max_temp_asic
+ - auto_tune_hashrate
+ - overshoot_power_limit
+ - overshoot_fanspeed
+ - max_temp_vr
GenericResponse:
type: object
required:
@@ -120,6 +176,7 @@ components:
- boardVersion
- coreVoltage
- coreVoltageActual
+ - coreVoltageSet
- current
- display
- fallbackStratumExtranonceSubscribe
@@ -138,6 +195,7 @@ components:
- freeHeapInternal
- freeHeapSpiram
- frequency
+ - frequencySet
- hashRate
- hashRate_1m
- hashRate_10m
@@ -217,6 +275,9 @@ components:
coreVoltageActual:
type: number
description: Actual measured ASIC core voltage
+ coreVoltageSet:
+ type: number
+ description: Actual set ASIC core voltage
current:
type: number
description: Current draw in milliamps
@@ -274,6 +335,9 @@ components:
frequency:
type: number
description: ASIC frequency in MHz
+ frequencySet:
+ type: number
+ description: ASIC frequency set in MHz
hashRate:
type: number
description: Current hashrate in Gh/s
@@ -609,12 +673,24 @@ components:
minimum: 1
examples:
- 850
+ coreVoltageSet:
+ type: integer
+ description: ASIC core voltage set in millivolts
+ minimum: 1
+ examples:
+ - 850
frequency:
type: integer
description: ASIC frequency in MHz
minimum: 1
examples:
- 450
+ frequencySet:
+ type: integer
+ description: ASIC frequency set in MHz
+ minimum: 1
+ examples:
+ - 450
rotation:
type: integer
description: Whether to rotate the screen orientation (0, 90, 180, 270 degrees)
@@ -893,3 +969,37 @@ paths:
description: Unauthorized - Client not in allowed network range
'500':
description: Internal server error
+ /api/system/autotune:
+ post:
+ summary: Update autotuning configuration
+ description: Updates the current autotuning settings via a JSON body. Requires valid authentication.
+ tags:
+ - Autotune
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AutotuneSettings'
+ responses:
+ '204':
+ description: Settings updated successfully (no content returned)
+ operationId: updateAutotuneSettings # <-- renamed from autotune
+
+ get:
+ summary: Get current autotuning configuration
+ description: Returns the current power, fan, temperature, and frequency limits used in autotuning.
+ tags:
+ - Autotune
+ responses:
+ '200':
+ description: Successful response with autotuning settings in JSON format
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AutotuneSettings'
+ '401':
+ description: Unauthorized - Client not in allowed network range
+ '500':
+ description: Internal server error
+ operationId: getAutotuneSettings # <-- renamed from autotune
diff --git a/main/nvs_config.c b/main/nvs_config.c
index 5d5fdeea3..e2a269cf8 100644
--- a/main/nvs_config.c
+++ b/main/nvs_config.c
@@ -42,8 +42,8 @@ static const char * TAG = "nvs_config";
static QueueHandle_t nvs_save_queue = NULL;
static nvs_handle_t handle;
-
-static Settings settings[NVS_CONFIG_COUNT] = {
+//removed NVS_CONFIG_COUNT to have the ability to control the settings read by http loop
+static Settings settings[] = {
[NVS_CONFIG_WIFI_SSID] = {.nvs_key_name = "wifissid", .type = TYPE_STR, .default_value = {.str = (char *)CONFIG_ESP_WIFI_SSID}, .rest_name = "ssid", .min = 1, .max = 32},
[NVS_CONFIG_WIFI_PASS] = {.nvs_key_name = "wifipass", .type = TYPE_STR, .default_value = {.str = (char *)CONFIG_ESP_WIFI_PASSWORD}, .rest_name = "wifiPass", .min = 0, .max = 63},
[NVS_CONFIG_HOSTNAME] = {.nvs_key_name = "hostname", .type = TYPE_STR, .default_value = {.str = (char *)CONFIG_LWIP_LOCAL_HOSTNAME}, .rest_name = "hostname", .min = 1, .max = 32},
@@ -107,11 +107,21 @@ static Settings settings[NVS_CONFIG_COUNT] = {
[NVS_CONFIG_TPS546] = {.nvs_key_name = "TPS546", .type = TYPE_BOOL},
[NVS_CONFIG_TMP1075] = {.nvs_key_name = "TMP1075", .type = TYPE_BOOL},
[NVS_CONFIG_POWER_CONSUMPTION_TARGET] = {.nvs_key_name = "power_cons_tgt", .type = TYPE_U16},
+
+ [NVS_CONFIG_KEY_POWER_LIMIT] = {.nvs_key_name = "power_limit", .type = TYPE_U16, .default_value = {.u16 = 20}},
+ [NVS_CONFIG_KEY_FAN_LIMIT] = {.nvs_key_name = "fan_limit", .type = TYPE_U16, .default_value = {.u16 = 75},},
+ [NVS_CONFIG_KEY_MAX_VOLTAGE_ASIC] = {.nvs_key_name = "max_volt_asic", .type = TYPE_U16, .default_value = {.u16 = 1400},},
+ [NVS_CONFIG_KEY_MAX_FREQUENCY_ASIC] = {.nvs_key_name = "max_freq_asic", .type = TYPE_U16, .default_value = {.u16 = 1000},},
+ [NVS_CONFIG_KEY_MAX_TEMP_ASIC] = {.nvs_key_name = "max_temp_asic", .type = TYPE_U16, .default_value = {.u16 = 61},},
+ [NVS_CONFIG_KEY_AUTO_TUNE_ENABLE] = {.nvs_key_name = "auto_tune", .type = TYPE_BOOL, .default_value = {.b = false}},
+ [NVS_CONFIG_KEY_OVERSHOT_POWER_LIMIT] = {.nvs_key_name = "osh_pow_limit", .type = TYPE_FLOAT, .default_value = {.f = 0.2f},},
+ [NVS_CONFIG_KEY_OVERSHOT_FAN_LIMIT] = {.nvs_key_name = "osh_fan_limit", .type = TYPE_U16, .default_value = {.u16 = 10},},
+ [NVS_CONFIG_KEY_MAX_TEMP_VR] = {.nvs_key_name = "max_temp_vr", .type = TYPE_U16, .default_value = {.u16 = 85},}
};
Settings *nvs_config_get_settings(NvsConfigKey key)
{
- if (key < 0 || key >= NVS_CONFIG_COUNT) {
+ if (key < 0 || key > NVS_ALL_CONFIG_COUNT) {
ESP_LOGE(TAG, "Invalid key enum %d", key);
return NULL;
}
@@ -226,7 +236,7 @@ esp_err_t nvs_config_init(void)
}
// Load all
- for (NvsConfigKey key = 0; key < NVS_CONFIG_COUNT; key++) {
+ for (NvsConfigKey key = 0; key < NVS_ALL_CONFIG_COUNT; key++) {
Settings *setting = &settings[key];
nvs_config_init_fallback(key, setting);
diff --git a/main/nvs_config.h b/main/nvs_config.h
index 14b600a2d..3a41bfdbb 100644
--- a/main/nvs_config.h
+++ b/main/nvs_config.h
@@ -70,7 +70,20 @@ typedef enum {
NVS_CONFIG_TPS546,
NVS_CONFIG_TMP1075,
NVS_CONFIG_POWER_CONSUMPTION_TARGET,
- NVS_CONFIG_COUNT
+ //all after that get not read when looping through the settings,but is still avail due nvs
+ NVS_CONFIG_COUNT,
+
+ NVS_CONFIG_KEY_POWER_LIMIT,
+ NVS_CONFIG_KEY_FAN_LIMIT,
+ NVS_CONFIG_KEY_MAX_VOLTAGE_ASIC,
+ NVS_CONFIG_KEY_MAX_FREQUENCY_ASIC,
+ NVS_CONFIG_KEY_MAX_TEMP_ASIC,
+ NVS_CONFIG_KEY_AUTO_TUNE_ENABLE,
+ NVS_CONFIG_KEY_OVERSHOT_POWER_LIMIT,
+ NVS_CONFIG_KEY_OVERSHOT_FAN_LIMIT,
+ NVS_CONFIG_KEY_MAX_TEMP_VR,
+ NVS_ALL_CONFIG_COUNT
+
} NvsConfigKey;
typedef enum {
@@ -92,6 +105,7 @@ typedef union {
} ConfigValue;
typedef struct {
+ //max key char length 15!
const char *nvs_key_name;
ConfigType type;
ConfigValue value;
diff --git a/main/tasks/hashrate_monitor_task.c b/main/tasks/hashrate_monitor_task.c
index 09f19612a..dbad7013b 100644
--- a/main/tasks/hashrate_monitor_task.c
+++ b/main/tasks/hashrate_monitor_task.c
@@ -13,7 +13,7 @@
#define HASHRATE_UNIT 0x100000uLL // Hashrate register unit (2^24 hashes)
-#define POLL_RATE 5000
+#define POLL_RATE 1000
#define HASHRATE_1M_SIZE (60000 / POLL_RATE) // 12
#define HASHRATE_10M_SIZE 10
#define HASHRATE_1H_SIZE 6
diff --git a/main/tasks/power_management_task.c b/main/tasks/power_management_task.c
index c5c37a43d..cac9be7c1 100644
--- a/main/tasks/power_management_task.c
+++ b/main/tasks/power_management_task.c
@@ -14,6 +14,7 @@
#include "PID.h"
#include "power.h"
#include "asic.h"
+#include "auto_tune.h"
#include "bm1370.h"
#include "utils.h"
#include "asic_init.h"
@@ -21,7 +22,6 @@
#include "driver/uart.h"
#define EPSILON 0.0001f
-#define POLL_RATE 1800
#define MAX_TEMP 90.0
#define THROTTLE_TEMP 75.0
#define SAFE_TEMP 45.0
@@ -33,6 +33,7 @@
#define TPS546_THROTTLE_TEMP 105.0
#define TPS546_MAX_TEMP 145.0
+#define POLL_RATE 1000
#define ASIC_REDUCTION 100.0
@@ -98,7 +99,8 @@ void POWER_MANAGEMENT_task(void * pvParameters)
pid_set_mode(&pid, AUTOMATIC); // This calls pid_initialize() internally
vTaskDelay(500 / portTICK_PERIOD_MS);
- uint16_t last_core_voltage = 0.0;
+ auto_tune_init(GLOBAL_STATE);
+ float last_core_voltage = 0.0;
uint16_t last_known_asic_voltage = 0;
float last_known_asic_frequency = 0.0;
@@ -131,7 +133,7 @@ void POWER_MANAGEMENT_task(void * pvParameters)
}
power_management->fan_perc = 100;
Thermal_set_fan_percent(&GLOBAL_STATE->DEVICE_CONFIG, 1);
-
+ auto_tune_set_auto_tune_hashrate(false);
VCORE_set_voltage(GLOBAL_STATE, 0.0f);
ESP_LOGI(TAG, "Setting RST pin to low due to overheat condition");
@@ -210,7 +212,7 @@ void POWER_MANAGEMENT_task(void * pvParameters)
}
//enable the PID auto control for the FAN if set
- if (nvs_config_get_bool(NVS_CONFIG_AUTO_FAN_SPEED)) {
+ if(nvs_config_get_bool(NVS_CONFIG_AUTO_FAN_SPEED)){
if (power_management->chip_temp_avg >= 0) { // Ignore invalid temperature readings (-1)
if (power_management->chip_temp2_avg > power_management->chip_temp_avg) {
pid_input = power_management->chip_temp2_avg;
@@ -284,17 +286,27 @@ void POWER_MANAGEMENT_task(void * pvParameters)
}
}
- uint16_t core_voltage = nvs_config_get_u16(NVS_CONFIG_ASIC_VOLTAGE);
- float asic_frequency = nvs_config_get_float(NVS_CONFIG_ASIC_FREQUENCY);
+ float core_voltage = 0;
+ float asic_frequency = 0;
+
+ if (!auto_tune_get_auto_tune_hashrate()) {
+ core_voltage = nvs_config_get_u16(NVS_CONFIG_ASIC_VOLTAGE);
+ asic_frequency = nvs_config_get_float(NVS_CONFIG_ASIC_FREQUENCY);
+ } else {
+ auto_tune();
+ core_voltage = auto_tune_get_voltage();
+ asic_frequency = auto_tune_get_frequency();
+ }
if (core_voltage != last_core_voltage) {
- ESP_LOGI(TAG, "setting new vcore voltage to %umV", core_voltage);
+ ESP_LOGI(TAG, "set vcore voltage from %fmV to %fmV", last_core_voltage, core_voltage);
VCORE_set_voltage(GLOBAL_STATE, (double) core_voltage / 1000.0);
last_core_voltage = core_voltage;
+ power_management->core_voltage = core_voltage;
}
if (asic_frequency != last_asic_frequency) {
- ESP_LOGI(TAG, "New ASIC frequency requested: %g MHz (current: %g MHz)", asic_frequency, last_asic_frequency);
+ ESP_LOGI(TAG, "set frequency from %.2f MHz to %.2f MHz", last_asic_frequency, asic_frequency);
bool success = ASIC_set_frequency(GLOBAL_STATE, asic_frequency);
diff --git a/main/tasks/statistics_task.c b/main/tasks/statistics_task.c
index 122dbeed3..ac32d3bac 100644
--- a/main/tasks/statistics_task.c
+++ b/main/tasks/statistics_task.c
@@ -148,6 +148,8 @@ void statistics_task(void * pvParameters)
statsData.wifiRSSI = wifiRSSI;
statsData.freeHeap = esp_get_free_heap_size();
statsData.responseTime = sys_module->response_time;
+ statsData.frequency = power_management->frequency_value;
+ statsData.core_voltage = power_management->core_voltage;
addStatisticData(&statsData);
}
diff --git a/main/tasks/statistics_task.h b/main/tasks/statistics_task.h
index 3705f62c7..f0ea8c827 100644
--- a/main/tasks/statistics_task.h
+++ b/main/tasks/statistics_task.h
@@ -14,9 +14,11 @@ struct StatisticsData
float chipTemperature;
float vrTemperature;
float power;
+ float frequency;
float voltage;
float current;
int16_t coreVoltageActual;
+ float core_voltage;
float fanSpeed;
uint16_t fanRPM;
uint16_t fan2RPM;
diff --git a/main/thermal/auto_tune.c b/main/thermal/auto_tune.c
new file mode 100644
index 000000000..130b8093e
--- /dev/null
+++ b/main/thermal/auto_tune.c
@@ -0,0 +1,300 @@
+#include "auto_tune.h"
+#include "PID.h"
+#include "esp_log.h"
+#include "global_state.h"
+#include "nvs_config.h"
+#include
+#include
+#define POLL_RATE 1000
+
+static const char * TAG = "auto_tune";
+
+auto_tune_settings AUTO_TUNE = {
+ .power_limit = 20,
+ .fan_limit = 75,
+ .step_volt = 0.1,
+ .step_freq_rampup = 0.5,
+ .step_freq = 0.2,
+ .autotune_step_frequency = 0,
+ .max_voltage_asic = 1400,
+ .max_frequency_asic = 1000,
+ .max_temp_asic = 65,
+ .max_temp_vr = 85,
+ .frequency = 525,
+ .voltage = 1150,
+ .auto_tune_hashrate = false,
+ .overshot_power_limit = 0.2, // watt
+ .overshot_fanspeed = 5, //%
+};
+
+#define HASHRATE_HISTORY_SIZE 30
+float last_core_voltage_auto;
+float last_asic_frequency_auto;
+float last_hashrate_auto;
+float current_hashrate_auto;
+float hashrate_history[HASHRATE_HISTORY_SIZE];
+int history_index = 0;
+bool history_initialized = false;
+
+bool lastVoltageSet = false;
+const int waitTime = 30;
+int waitCounter = 0;
+GlobalState * GLOBAL_STATE;
+
+#define MIN_FREQ 400
+#define MIN_VOLTAGE 1000
+
+enum TuneState
+{
+ sleep_before_warmup,
+ warmup,
+ working
+};
+
+enum TuneState state;
+
+void update_hashrate_history(float new_value)
+{
+ // Initialize history if not already done
+ if (!history_initialized) {
+ for (int i = 0; i < HASHRATE_HISTORY_SIZE; i++) {
+ hashrate_history[i] = new_value;
+ }
+ history_initialized = true;
+ }
+
+ // Add new value to circular buffer
+ hashrate_history[history_index] = new_value;
+ history_index = (history_index + 1) % HASHRATE_HISTORY_SIZE;
+}
+
+void auto_tune_init(GlobalState * _GLOBAL_STATE)
+{
+ GLOBAL_STATE = _GLOBAL_STATE;
+ AUTO_TUNE.frequency = nvs_config_get_float(NVS_CONFIG_ASIC_FREQUENCY);
+ AUTO_TUNE.voltage = nvs_config_get_u16(NVS_CONFIG_ASIC_VOLTAGE);
+ AUTO_TUNE.power_limit = nvs_config_get_u16(NVS_CONFIG_KEY_POWER_LIMIT);
+ AUTO_TUNE.fan_limit = nvs_config_get_u16(NVS_CONFIG_KEY_FAN_LIMIT);
+ AUTO_TUNE.max_voltage_asic = nvs_config_get_u16(NVS_CONFIG_KEY_MAX_VOLTAGE_ASIC);
+ AUTO_TUNE.max_frequency_asic = nvs_config_get_u16(NVS_CONFIG_KEY_MAX_FREQUENCY_ASIC);
+ AUTO_TUNE.max_temp_asic = nvs_config_get_u16(NVS_CONFIG_KEY_MAX_TEMP_ASIC);
+ AUTO_TUNE.auto_tune_hashrate = nvs_config_get_bool(NVS_CONFIG_KEY_AUTO_TUNE_ENABLE);
+ AUTO_TUNE.overshot_power_limit = nvs_config_get_float(NVS_CONFIG_KEY_OVERSHOT_POWER_LIMIT);
+ AUTO_TUNE.overshot_fanspeed = nvs_config_get_u16(NVS_CONFIG_KEY_OVERSHOT_FAN_LIMIT);
+ AUTO_TUNE.max_temp_vr = nvs_config_get_u16(NVS_CONFIG_KEY_MAX_TEMP_VR);
+
+ last_core_voltage_auto = AUTO_TUNE.voltage;
+ last_asic_frequency_auto = AUTO_TUNE.frequency;
+ last_hashrate_auto = GLOBAL_STATE->SYSTEM_MODULE.current_hashrate;
+ current_hashrate_auto = last_hashrate_auto;
+
+ // Initialize hashrate history
+ update_hashrate_history(last_hashrate_auto);
+
+ state = sleep_before_warmup;
+ waitCounter = 45 * 1000 / POLL_RATE;
+}
+
+bool waitForStartUp()
+{
+ return current_hashrate_auto > 0 && waitCounter <= 0;
+}
+
+bool can_increase_values()
+{
+ return GLOBAL_STATE->POWER_MANAGEMENT_MODULE.fan_perc < AUTO_TUNE.fan_limit &&
+ GLOBAL_STATE->POWER_MANAGEMENT_MODULE.power < AUTO_TUNE.power_limit &&
+ GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp_avg < AUTO_TUNE.max_temp_asic &&
+ GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp2_avg < AUTO_TUNE.max_temp_asic &&
+ GLOBAL_STATE->POWER_MANAGEMENT_MODULE.vr_temp < AUTO_TUNE.max_temp_vr;
+}
+
+bool limithit()
+{
+ return GLOBAL_STATE->POWER_MANAGEMENT_MODULE.fan_perc > AUTO_TUNE.fan_limit ||
+ GLOBAL_STATE->POWER_MANAGEMENT_MODULE.power > AUTO_TUNE.power_limit ||
+ GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp_avg > AUTO_TUNE.max_temp_asic ||
+ GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp2_avg > AUTO_TUNE.max_temp_asic;
+
+}
+
+bool critical_limithit()
+{
+ return GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp_avg > AUTO_TUNE.max_temp_asic ||
+ GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp2_avg > AUTO_TUNE.max_temp_asic ||
+ GLOBAL_STATE->POWER_MANAGEMENT_MODULE.power >= AUTO_TUNE.power_limit + AUTO_TUNE.overshot_power_limit ||
+ GLOBAL_STATE->POWER_MANAGEMENT_MODULE.fan_perc >= AUTO_TUNE.fan_limit + AUTO_TUNE.overshot_fanspeed ||
+ GLOBAL_STATE->POWER_MANAGEMENT_MODULE.vr_temp > AUTO_TUNE.max_temp_vr;
+}
+
+bool hashrate_increased_since_last_set()
+{
+ if (!history_initialized) {
+ return false; // Not enough data yet
+ }
+
+ int last_set_index = history_index;
+
+ // Check if hashrate increased since that point
+ float last_value = hashrate_history[last_set_index];
+ bool increased = false;
+
+ for (int i = 1; i < HASHRATE_HISTORY_SIZE; i++) {
+ int idx = (last_set_index + i) % HASHRATE_HISTORY_SIZE;
+ if (hashrate_history[idx] > last_value) {
+ increased = true;
+ break;
+ }
+ }
+
+ return increased;
+}
+
+static inline float clamp(float val, float min, float max)
+{
+ return (val < min) ? min : ((val > max) ? max : val);
+}
+
+void increase_values()
+{
+ if (!lastVoltageSet) {
+ last_asic_frequency_auto += AUTO_TUNE.autotune_step_frequency;;
+ } else {
+ last_core_voltage_auto += AUTO_TUNE.step_volt;
+ }
+}
+
+void respectLimits()
+{
+ last_asic_frequency_auto = clamp(last_asic_frequency_auto, MIN_FREQ, AUTO_TUNE.max_frequency_asic);
+ last_core_voltage_auto = clamp(last_core_voltage_auto, MIN_VOLTAGE, AUTO_TUNE.max_voltage_asic);
+
+ if (last_asic_frequency_auto == MIN_FREQ || last_core_voltage_auto == MIN_VOLTAGE) {
+ lastVoltageSet = true; // Assuming default voltage set to be initial value
+ }
+}
+
+bool check_dead_cores()
+{
+ int asic_count = GLOBAL_STATE->DEVICE_CONFIG.family.asic_count;
+ int domains = GLOBAL_STATE->DEVICE_CONFIG.family.asic.hash_domains;
+ bool core_died = false;
+ for(int i = 0; i < asic_count; i++) {
+ float avg_hash = 0;
+ for(int d = 0; d < domains; d++) {
+ avg_hash += GLOBAL_STATE->HASHRATE_MONITOR_MODULE.domain_measurements[i][d].hashrate;
+ }
+ avg_hash /= domains;
+ for(int d = 0; d < domains; d++) {
+ if(GLOBAL_STATE->HASHRATE_MONITOR_MODULE.domain_measurements[i][d].hashrate <= avg_hash * 0.1f)
+ core_died = true;
+ }
+ }
+ if(core_died)
+ {
+ last_asic_frequency_auto -= 1.f;
+ last_core_voltage_auto += AUTO_TUNE.step_volt;
+ lastVoltageSet = true;
+ ESP_LOGI(TAG,"Core died, increase voltage");
+ }
+ return core_died;
+}
+
+void dowork()
+{
+ if (critical_limithit()) {
+ last_asic_frequency_auto -= AUTO_TUNE.step_freq;
+ last_core_voltage_auto -= AUTO_TUNE.step_volt;
+ } else if (!check_dead_cores() && can_increase_values()) {
+ // Check if error increased since last voltage/frequency set
+ bool error_increased = hashrate_increased_since_last_set();
+
+ // If error did increase, switch the setting
+ if (error_increased) {
+ lastVoltageSet = !lastVoltageSet;
+ }
+ increase_values();
+ }
+
+ ESP_LOGI(TAG, "Hashrate %f Voltage %f Frequency %f", current_hashrate_auto, last_core_voltage_auto, last_asic_frequency_auto);
+
+ respectLimits();
+ AUTO_TUNE.voltage = last_core_voltage_auto;
+ AUTO_TUNE.frequency = last_asic_frequency_auto;
+}
+
+void auto_tune()
+{
+ bool pid_active = nvs_config_get_bool(NVS_CONFIG_AUTO_FAN_SPEED);
+ if(!pid_active)
+ {
+ int manspeed = nvs_config_get_u16(NVS_CONFIG_MANUAL_FAN_SPEED);
+ if(manspeed > AUTO_TUNE.fan_limit)
+ AUTO_TUNE.fan_limit = manspeed + 1;
+ }
+ else
+ {
+ int targettemp = nvs_config_get_u16(NVS_CONFIG_TEMP_TARGET);
+ if(targettemp > AUTO_TUNE.max_temp_asic)
+ AUTO_TUNE.max_temp_asic = targettemp + 1;
+ }
+ current_hashrate_auto = GLOBAL_STATE->SYSTEM_MODULE.hashrate_10m;
+ update_hashrate_history(current_hashrate_auto);
+
+ switch (state) {
+ case sleep_before_warmup:
+ if (GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp_avg == -1) {
+ break;
+ }
+
+ if (waitCounter-- > 0) {
+ ESP_LOGI(TAG, "state sleep_bevor_warmup %i", waitCounter);
+ break;
+ }
+
+ if (waitForStartUp()) {
+ state = warmup;
+ }
+ break;
+
+ case warmup:
+ AUTO_TUNE.autotune_step_frequency = AUTO_TUNE.step_freq_rampup;
+ dowork();
+ if (limithit()) {
+ AUTO_TUNE.autotune_step_frequency = AUTO_TUNE.step_freq;
+ state = working;
+ }
+ break;
+
+ case working:
+ if (limithit() && !critical_limithit()) {
+ check_dead_cores();
+ break; // Added this line to stop adjusting when limit is hit
+
+ } else {
+ dowork(); // Resume adjustments once limits are no longer breached
+ }
+ break;
+ }
+ last_hashrate_auto = current_hashrate_auto;
+}
+
+float auto_tune_get_frequency()
+{
+ return AUTO_TUNE.frequency;
+}
+
+float auto_tune_get_voltage()
+{
+ return AUTO_TUNE.voltage;
+}
+
+bool auto_tune_get_auto_tune_hashrate()
+{
+ return AUTO_TUNE.auto_tune_hashrate;
+}
+
+void auto_tune_set_auto_tune_hashrate(bool enable)
+{
+ AUTO_TUNE.auto_tune_hashrate = enable;
+}
\ No newline at end of file
diff --git a/main/thermal/auto_tune.h b/main/thermal/auto_tune.h
new file mode 100644
index 000000000..36ef59385
--- /dev/null
+++ b/main/thermal/auto_tune.h
@@ -0,0 +1,34 @@
+#ifndef AUTO_TUNE_H_
+#define AUTO_TUNE_H_
+#include
+#include
+#include "global_state.h"
+
+typedef struct
+{
+ float power_limit;
+ uint16_t fan_limit;
+ float step_volt;
+ float step_freq_rampup;
+ float step_freq;
+ float autotune_step_frequency;
+ uint8_t autotune_read_tick;
+ uint16_t max_voltage_asic;
+ uint16_t max_frequency_asic;
+ uint8_t max_temp_asic;
+ uint16_t max_temp_vr;
+ float frequency;
+ float voltage;
+ bool auto_tune_hashrate;
+ float overshot_power_limit;
+ uint16_t overshot_fanspeed;
+} auto_tune_settings;
+
+extern auto_tune_settings AUTO_TUNE;
+void auto_tune_init(GlobalState * gs);
+void auto_tune();
+float auto_tune_get_frequency();
+float auto_tune_get_voltage();
+bool auto_tune_get_auto_tune_hashrate();
+void auto_tune_set_auto_tune_hashrate(bool enable);
+#endif
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 219d545b8..192645a78 100755
--- a/readme.md
+++ b/readme.md
@@ -60,6 +60,7 @@ Available API endpoints:
* `/api/system/statistics` Get system statistics (data logging should be activated)
* `/api/system/statistics/dashboard` Get system statistics for dashboard
* `/api/system/wifi/scan` Scan for available Wi-Fi networks
+* `/api/system/autotune` Get Autotune settings information
**POST**
@@ -67,6 +68,7 @@ Available API endpoints:
* `/api/system/identify` Identify the device
* `/api/system/OTA` Update system firmware
* `/api/system/OTAWWW` Update AxeOS
+* `/api/system/autotune` Update Autotune settings
**PATCH**