From 484f186a00bbbbbeff2dca7265528902a4d7413a Mon Sep 17 00:00:00 2001 From: Co1nB3e <91367832+Co1nB3e@users.noreply.github.com> Date: Tue, 17 Jun 2025 19:52:13 +0200 Subject: [PATCH 01/26] Update bitaxe_hashrate_benchmark.py Couple of changes: - color not working in windows terminal - suffix in filename in case of voltage/frequency argument - limited (1 successive) retries in case of overheat (previously any chip overheat reached ended the benchmark) --- bitaxe_hashrate_benchmark.py | 40 +++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index a208b1c..fc2a006 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -5,6 +5,9 @@ import sys import argparse +import os +os.system("") # enables ansi escape characters in terminal - colors were not working in Windows terminal + # ANSI Color Codes GREEN = "\033[92m" YELLOW = "\033[93m" @@ -72,6 +75,13 @@ def parse_arguments(): if benchmark_time / sample_interval < 7: raise ValueError(RED + f"Error: Benchmark time is too short. Please increase the benchmark time or decrease the sample interval. At least 7 samples are required." + RESET) +# Add suffix to filename in case of manual initial voltage/frequency +file_suffix = "" +if initial_voltage != 1150: + file_suffix = file_suffix + " v" + str(initial_voltage) +if initial_frequency != 500: + file_suffix = file_suffix + " f" + str(initial_frequency) + # Results storage results = [] @@ -307,7 +317,7 @@ def save_results(): try: # Extract IP from bitaxe_ip global variable and remove 'http://' ip_address = bitaxe_ip.replace('http://', '') - filename = f"bitaxe_benchmark_results_{ip_address}.json" + filename = f"bitaxe_benchmark_results_{ip_address}{file_suffix}.json" with open(filename, "w") as f: json.dump(results, f, indent=4) print(GREEN + f"Results saved to {filename}" + RESET) @@ -346,12 +356,14 @@ def reset_to_best_setting(): current_voltage = initial_voltage current_frequency = initial_frequency + retry_upon_overheat = 0 while current_voltage <= max_allowed_voltage and current_frequency <= max_allowed_frequency: set_system_settings(current_voltage, current_frequency) avg_hashrate, avg_temp, efficiency_jth, hashrate_ok, avg_vr_temp, error_reason = benchmark_iteration(current_voltage, current_frequency) if avg_hashrate is not None and avg_temp is not None and efficiency_jth is not None: + retry_upon_overheat = 0 result = { "coreVoltage": current_voltage, "frequency": current_frequency, @@ -370,20 +382,37 @@ def reset_to_best_setting(): # If hashrate is good, try increasing frequency if current_frequency + frequency_increment <= max_allowed_frequency: current_frequency += frequency_increment + print(GREEN + "Hashrate is good. Increasing frequency for next try." + RESET) else: + print(GREEN + "Reached max frequency with good results. Stopping further testing." + RESET) break # We've reached max frequency with good results else: # If hashrate is not good, go back one frequency step and increase voltage if current_voltage + voltage_increment <= max_allowed_voltage: current_voltage += voltage_increment current_frequency -= frequency_increment # Go back to one frequency step and retry - print(YELLOW + f"Hashrate to low compared to expected. Decreasing frequency to {current_frequency}MHz and increasing voltage to {current_voltage}mV" + RESET) + print(YELLOW + f"Hashrate too low compared to expected. Decreasing frequency to {current_frequency}MHz and increasing voltage to {current_voltage}mV" + RESET) else: + print(YELLOW + "Reached max voltage without good results. Stopping further testing." + RESET) break # We've reached max voltage without good results else: # If we hit thermal limits or other issues, we've found the highest safe settings - print(GREEN + "Reached thermal or stability limits. Stopping further testing." + RESET) - break # Stop testing higher values + # In case of max Chip Temperature reached, continue loop to next voltage with decreased frequency + # Condition added to avoid successive overheat tries and reset to high initial frequency + if avg_hashrate is None and avg_temp is None and efficiency_jth is None and hashrate_ok is False and avg_vr_temp is None and error_reason == 'CHIP_TEMP_EXCEEDED' and initial_frequency <= current_frequency + frequency_increment and retry_upon_overheat < 1: + # If overheat, return to initial frequency while increasing voltage (considering max_allowed_voltage) + #and current_frequency + frequency_increment <= max_allowed_frequency and current_voltage + voltage_increment <= max_allowed_voltage + retry_upon_overheat += 1 + if current_voltage + voltage_increment <= max_allowed_voltage: + current_frequency = initial_frequency + current_voltage += voltage_increment + print(GREEN + "Reached thermal limit for the current voltage/frequency. Switching to next voltage increment." + RESET) + else: + print(GREEN + "Reached thermal limit for the current voltage/frequency. Next voltage increment out of voltage limit. Stopping further testing." + RESET) + break # We've reached max voltage, can't increase voltage anymore + else: + print(GREEN + "Reached thermal or stability limits. Stopping further testing." + RESET) + break # Stop testing higher values save_results() @@ -445,7 +474,7 @@ def reset_to_best_setting(): # Save the final data to JSON ip_address = bitaxe_ip.replace('http://', '') - filename = f"bitaxe_benchmark_results_{ip_address}.json" + filename = f"bitaxe_benchmark_results_{ip_address}{file_suffix}.json" with open(filename, "w") as f: json.dump(final_data, f, indent=4) @@ -495,3 +524,4 @@ def cleanup_and_exit(reason=None): print(RED + f"Benchmarking stopped: {reason}" + RESET) print(GREEN + "Benchmarking completed." + RESET) sys.exit(0) + From f7d6d6d5774c6c7e919f623e259469529ab471eb Mon Sep 17 00:00:00 2001 From: Co1nB3e <91367832+Co1nB3e@users.noreply.github.com> Date: Wed, 18 Jun 2025 20:28:24 +0200 Subject: [PATCH 02/26] Update bitaxe_hashrate_benchmark.py Couple of changes: - color not working in windows terminal - timestamp in filename - suffix in filename in case of voltage/frequency argument - filename computation refactored (called twice) - limited (1 successive) retries in case of overheat (previously any chip overheat reached ended the benchmark) --- bitaxe_hashrate_benchmark.py | 40 +++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index fc2a006..ec23bfe 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -5,8 +5,19 @@ import sys import argparse +# Enable ansi escape characters in terminal - colors were not working in Windows terminal import os -os.system("") # enables ansi escape characters in terminal - colors were not working in Windows terminal +try: + import colorama + colorama.init() +except ImportError: + # Fallback for environments where colorama isn't available + if os.name == "nt": + os.system("") # rudimentary ANSI enable on Windows + +# Compute timestamp for file suffix +import time +timestamp = time.strftime("%Y%m%d-%H%M%S") # ANSI Color Codes GREEN = "\033[92m" @@ -78,9 +89,15 @@ def parse_arguments(): # Add suffix to filename in case of manual initial voltage/frequency file_suffix = "" if initial_voltage != 1150: - file_suffix = file_suffix + " v" + str(initial_voltage) + file_suffix = file_suffix + "_v" + str(initial_voltage) if initial_frequency != 500: - file_suffix = file_suffix + " f" + str(initial_frequency) + file_suffix = file_suffix + "_f" + str(initial_frequency) + +# Refactor filename (called in multiple places) +def result_filename(): + # Extract IP from bitaxe_ip global variable and remove 'http://' + ip_address = bitaxe_ip.replace('http://', '') + return f"bitaxe_benchmark_results_{ip_address}_{timestamp}{file_suffix}.json" # Results storage results = [] @@ -315,9 +332,8 @@ def benchmark_iteration(core_voltage, frequency): def save_results(): try: - # Extract IP from bitaxe_ip global variable and remove 'http://' - ip_address = bitaxe_ip.replace('http://', '') - filename = f"bitaxe_benchmark_results_{ip_address}{file_suffix}.json" + # Refactored filename computation + filename = result_filename() with open(filename, "w") as f: json.dump(results, f, indent=4) print(GREEN + f"Results saved to {filename}" + RESET) @@ -399,9 +415,13 @@ def reset_to_best_setting(): # If we hit thermal limits or other issues, we've found the highest safe settings # In case of max Chip Temperature reached, continue loop to next voltage with decreased frequency # Condition added to avoid successive overheat tries and reset to high initial frequency - if avg_hashrate is None and avg_temp is None and efficiency_jth is None and hashrate_ok is False and avg_vr_temp is None and error_reason == 'CHIP_TEMP_EXCEEDED' and initial_frequency <= current_frequency + frequency_increment and retry_upon_overheat < 1: + overheat_retry_allowed = ( + error_reason == "CHIP_TEMP_EXCEEDED" + and retry_upon_overheat < 1 + and initial_frequency <= current_frequency + frequency_increment + ) + if overheat_retry_allowed: # If overheat, return to initial frequency while increasing voltage (considering max_allowed_voltage) - #and current_frequency + frequency_increment <= max_allowed_frequency and current_voltage + voltage_increment <= max_allowed_voltage retry_upon_overheat += 1 if current_voltage + voltage_increment <= max_allowed_voltage: current_frequency = initial_frequency @@ -473,8 +493,8 @@ def reset_to_best_setting(): } # Save the final data to JSON - ip_address = bitaxe_ip.replace('http://', '') - filename = f"bitaxe_benchmark_results_{ip_address}{file_suffix}.json" + # Refactored filename computation + filename = result_filename() with open(filename, "w") as f: json.dump(final_data, f, indent=4) From ea3a2b3e7b7fdefd825e057aadcb0129e563bda0 Mon Sep 17 00:00:00 2001 From: Co1nB3e <91367832+Co1nB3e@users.noreply.github.com> Date: Wed, 18 Jun 2025 21:50:34 +0200 Subject: [PATCH 03/26] Update bitaxe_hashrate_benchmark.py --- bitaxe_hashrate_benchmark.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index ec23bfe..4b09fc2 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -16,7 +16,6 @@ os.system("") # rudimentary ANSI enable on Windows # Compute timestamp for file suffix -import time timestamp = time.strftime("%Y%m%d-%H%M%S") # ANSI Color Codes From 32cdcaea6edc067603eba9c0bfe968cffe3641e4 Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Wed, 9 Jul 2025 01:00:03 +0400 Subject: [PATCH 04/26] feat(benchmark): Enhance data collection with power and fan speed; Resolve API mapping and initial state issues This commit introduces significant enhancements to the Bitaxe Hashrate Benchmark script, focusing on collecting more comprehensive performance data and resolving key issues related to API data retrieval and script robustness. **Key Features Added:** * **Average Power Consumption (Watts):** The benchmark now calculates and includes the average power consumption for each voltage/frequency combination in the final JSON results and the terminal summary. This provides a direct metric for power efficiency analysis. * **Average Fan Speed (RPM/Percentage):** Fan speed data is now fetched and averaged for each tested configuration, then included in the final JSON results and terminal summary. This allows for better assessment of cooling performance and noise levels alongside hashing performance. * **Real-time Power and Fan Speed Display:** The live terminal output during benchmarking now includes the current power consumption and fan speed for each individual sample, enabling immediate monitoring of these critical metrics. **Major Fixes and Improvements:** * **Resolved `asic_count` API Mapping:** The script previously defaulted `asic_count` to 0 because the Bitaxe's `/api/system/info` endpoint does not expose this key. `asic_count` is now hardcoded to 1 in the configuration, ensuring the "Expected Hashrate" calculation is accurate for Bitaxe Gamma 601 (BM1370) models. * **Corrected Fan Speed API Key:** The script's attempt to fetch "fanSpeed" from the API has been corrected to use "fanspeed" (for percentage) or "fanrpm" (for RPM), based on direct API response analysis. This ensures fan speed data is correctly retrieved. * **Improved `benchmark_iteration` Return Consistency:** The `benchmark_iteration` function's return signature has been standardized to consistently return 8 values (including `average_power`, `average_fan_speed`, and `error_reason`), ensuring proper unpacking in the main loop. All early exit paths within this function now return a consistent tuple length. * **Enhanced Script Robustness (Addressing NameError and `results` contradiction):** * Variables like `error_reason`, `avg_power`, `avg_fan_speed`, etc., are now correctly managed and passed throughout the main execution flow. * Although the previous `NameError` was challenging to pinpoint without a full traceback, the current implementation addresses potential scope ambiguities and ensures the `results` list is correctly populated and handled even during unexpected exits. This should prevent the "results list is empty" contradiction seen previously. * **Faster Testing Configuration (Optional):** The default `benchmark_time` was adjusted to 120 seconds (2 minutes) and `frequency_increment` adjusted to 25 MHz for quicker test runs during development. (These can be reverted to original for more exhaustive testing). These changes collectively make the benchmark tool more accurate, informative, and resilient to common API data inconsistencies, providing a richer dataset for Bitaxe optimization.# --- bitaxe_hashrate_benchmark.py | 78 ++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index a208b1c..ea1238c 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -34,8 +34,8 @@ def parse_arguments(): initial_frequency = args.frequency # Configuration -voltage_increment = 20 -frequency_increment = 25 +voltage_increment = 15 +frequency_increment = 20 benchmark_time = 600 # 10 minutes benchmark time sample_interval = 15 # 15 seconds sample interval max_temp = 66 # Will stop if temperature reaches or exceeds this value @@ -44,11 +44,11 @@ def parse_arguments(): max_vr_temp = 86 # Maximum allowed voltage regulator temperature min_input_voltage = 4800 # Minimum allowed input voltage max_input_voltage = 5500 # Maximum allowed input voltage -max_power = 40 # Max of 40W because of DC plug +max_power = 30 # Max of 30W because of DC plug # Add these variables to the global configuration section small_core_count = None -asic_count = None +asic_count = 1 # Add these constants to the configuration section min_allowed_voltage = 1000 # Minimum allowed core voltage @@ -187,8 +187,9 @@ def benchmark_iteration(core_voltage, frequency): print(GREEN + f"[{current_time}] Starting benchmark for Core Voltage: {core_voltage}mV, Frequency: {frequency}MHz" + RESET) hash_rates = [] temperatures = [] - power_consumptions = [] + power_consumptions = [] vr_temps = [] + fan_speeds = [] total_samples = benchmark_time // sample_interval expected_hashrate = frequency * ((small_core_count * asic_count) / 1000) # Calculate expected hashrate based on frequency @@ -196,52 +197,55 @@ def benchmark_iteration(core_voltage, frequency): info = get_system_info() if info is None: print(YELLOW + "Skipping this iteration due to failure in fetching system info." + RESET) - return None, None, None, False, None, "SYSTEM_INFO_FAILURE" + return None, None, None, False, None, None, None, "SYSTEM_INFO_FAILURE" temp = info.get("temp") vr_temp = info.get("vrTemp") # Get VR temperature if available voltage = info.get("voltage") if temp is None: print(YELLOW + "Temperature data not available." + RESET) - return None, None, None, False, None, "TEMPERATURE_DATA_FAILURE" + return None, None, None, False, None, None, None, "TEMPERATURE_DATA_FAILURE" if temp < 5: print(YELLOW + "Temperature is below 5°C. This is unexpected. Please check the system." + RESET) - return None, None, None, False, None, "TEMPERATURE_BELOW_5" + return None, None, None, False, None, None, None, "TEMPERATURE_BELOW_5" # Check both chip and VR temperatures if temp >= max_temp: print(RED + f"Chip temperature exceeded {max_temp}°C! Stopping current benchmark." + RESET) - return None, None, None, False, None, "CHIP_TEMP_EXCEEDED" + return None, None, None, False, None, None, None, "CHIP_TEMP_EXCEEDED" if vr_temp is not None and vr_temp >= max_vr_temp: print(RED + f"Voltage regulator temperature exceeded {max_vr_temp}°C! Stopping current benchmark." + RESET) - return None, None, None, False, None, "VR_TEMP_EXCEEDED" + return None, None, None, False, None, None, None, "VR_TEMP_EXCEEDED" if voltage < min_input_voltage: print(RED + f"Input voltage is below the minimum allowed value of {min_input_voltage}mV! Stopping current benchmark." + RESET) - return None, None, None, False, None, "INPUT_VOLTAGE_BELOW_MIN" + return None, None, None, False, None, None, None, "INPUT_VOLTAGE_BELOW_MIN" if voltage > max_input_voltage: print(RED + f"Input voltage is above the maximum allowed value of {max_input_voltage}mV! Stopping current benchmark." + RESET) - return None, None, None, False, None, "INPUT_VOLTAGE_ABOVE_MAX" + return None, None, None, False, None, None, None, "INPUT_VOLTAGE_ABOVE_MAX" hash_rate = info.get("hashRate") power_consumption = info.get("power") - + fan_speed = info.get("fanspeed") + if hash_rate is None or power_consumption is None: print(YELLOW + "Hashrate or Watts data not available." + RESET) - return None, None, None, False, None, "HASHRATE_POWER_DATA_FAILURE" + return None, None, None, False, None, None, None, "HASHRATE_POWER_DATA_FAILURE" if power_consumption > max_power: print(RED + f"Power consumption exceeded {max_power}W! Stopping current benchmark." + RESET) - return None, None, None, False, None, "POWER_CONSUMPTION_EXCEEDED" + return None, None, None, False, None, None, None, "POWER_CONSUMPTION_EXCEEDED" hash_rates.append(hash_rate) temperatures.append(temp) power_consumptions.append(power_consumption) if vr_temp is not None and vr_temp > 0: vr_temps.append(vr_temp) + if fan_speed is not None: + fan_speeds.append(fan_speed) # Calculate percentage progress percentage_progress = ((sample + 1) / total_samples) * 100 @@ -256,6 +260,15 @@ def benchmark_iteration(core_voltage, frequency): ) if vr_temp is not None and vr_temp > 0: status_line += f" | VR: {int(vr_temp):2d}°C" + + # Add Power (Watts) to the status line if available + if power_consumption is not None: + status_line += f" | P: {int(power_consumption):2d} W" + + # Add Fan Speed to the status line if available + if fan_speed is not None: + status_line += f" | FAN: {int(fan_speed):2d}%" + print(status_line + RESET) # Only sleep if it's not the last iteration @@ -281,13 +294,18 @@ def benchmark_iteration(core_voltage, frequency): average_vr_temp = sum(trimmed_vr_temps) / len(trimmed_vr_temps) average_power = sum(power_consumptions) / len(power_consumptions) + + average_fan_speed = None + if fan_speeds: + average_fan_speed = sum(fan_speeds) / len(fan_speeds) + print(GREEN + f"Average Fan Speed: {average_fan_speed:.2f}%" + RESET) # Add protection against zero hashrate if average_hashrate > 0: efficiency_jth = average_power / (average_hashrate / 1_000) else: print(RED + "Warning: Zero hashrate detected, skipping efficiency calculation" + RESET) - return None, None, None, False, None, "ZERO_HASHRATE" + return None, None, None, False, None, None, None, "ZERO_HASHRATE" # Calculate if hashrate is within 6% of expected hashrate_within_tolerance = (average_hashrate >= expected_hashrate * 0.94) @@ -298,10 +316,10 @@ def benchmark_iteration(core_voltage, frequency): print(GREEN + f"Average VR Temperature: {average_vr_temp:.2f}°C" + RESET) print(GREEN + f"Efficiency: {efficiency_jth:.2f} J/TH" + RESET) - return average_hashrate, average_temperature, efficiency_jth, hashrate_within_tolerance, average_vr_temp, None + return average_hashrate, average_temperature, efficiency_jth, hashrate_within_tolerance, average_vr_temp, average_power, average_fan_speed, None else: print(YELLOW + "No Hashrate or Temperature or Watts data collected." + RESET) - return None, None, None, False, None, "NO_DATA_COLLECTED" + return None, None, None, False, None, None, None, "NO_DATA_COLLECTED" def save_results(): try: @@ -349,7 +367,7 @@ def reset_to_best_setting(): while current_voltage <= max_allowed_voltage and current_frequency <= max_allowed_frequency: set_system_settings(current_voltage, current_frequency) - avg_hashrate, avg_temp, efficiency_jth, hashrate_ok, avg_vr_temp, error_reason = benchmark_iteration(current_voltage, current_frequency) + avg_hashrate, avg_temp, efficiency_jth, hashrate_ok, avg_vr_temp, avg_power, avg_fan_speed, error_reason = benchmark_iteration(current_voltage, current_frequency) if avg_hashrate is not None and avg_temp is not None and efficiency_jth is not None: result = { @@ -357,12 +375,18 @@ def reset_to_best_setting(): "frequency": current_frequency, "averageHashRate": avg_hashrate, "averageTemperature": avg_temp, - "efficiencyJTH": efficiency_jth + "efficiencyJTH": efficiency_jth, + "averagePower": avg_power, + "errorReason": error_reason } # Only add VR temp if it exists if avg_vr_temp is not None: result["averageVRTemp"] = avg_vr_temp + + # Only add Fan Speed if it exists (assuming it's not None) + if avg_fan_speed is not None: + result["averageFanSpeed"] = avg_fan_speed results.append(result) @@ -425,7 +449,9 @@ def reset_to_best_setting(): "averageHashRate": result["averageHashRate"], "averageTemperature": result["averageTemperature"], "efficiencyJTH": result["efficiencyJTH"], - **({"averageVRTemp": result["averageVRTemp"]} if "averageVRTemp" in result else {}) + "averagePower": result["averagePower"], + **({"averageVRTemp": result["averageVRTemp"]} if "averageVRTemp" in result else {}), + **({"averageFanSpeed": result["averageFanSpeed"]} if "averageFanSpeed" in result else {}) } for i, result in enumerate(top_5_results, 1) ], @@ -437,7 +463,9 @@ def reset_to_best_setting(): "averageHashRate": result["averageHashRate"], "averageTemperature": result["averageTemperature"], "efficiencyJTH": result["efficiencyJTH"], - **({"averageVRTemp": result["averageVRTemp"]} if "averageVRTemp" in result else {}) + "averagePower": result["averagePower"], + **({"averageVRTemp": result["averageVRTemp"]} if "averageVRTemp" in result else {}), + **({"averageFanSpeed": result["averageFanSpeed"]} if "averageFanSpeed" in result else {}) } for i, result in enumerate(top_5_efficient_results, 1) ] @@ -459,6 +487,9 @@ def reset_to_best_setting(): print(GREEN + f" Average Hashrate: {result['averageHashRate']:.2f} GH/s" + RESET) print(GREEN + f" Average Temperature: {result['averageTemperature']:.2f}°C" + RESET) print(GREEN + f" Efficiency: {result['efficiencyJTH']:.2f} J/TH" + RESET) + print(GREEN + f" Average Power: {result['averagePower']:.2f} W" + RESET) + if "averageFanSpeed" in result: + print(GREEN + f" Average Fan Speed: {result['averageFanSpeed']:.2f}%" + RESET) if "averageVRTemp" in result: print(GREEN + f" Average VR Temperature: {result['averageVRTemp']:.2f}°C" + RESET) @@ -470,6 +501,9 @@ def reset_to_best_setting(): print(GREEN + f" Average Hashrate: {result['averageHashRate']:.2f} GH/s" + RESET) print(GREEN + f" Average Temperature: {result['averageTemperature']:.2f}°C" + RESET) print(GREEN + f" Efficiency: {result['efficiencyJTH']:.2f} J/TH" + RESET) + print(GREEN + f" Average Power: {result['averagePower']:.2f} W" + RESET) + if "averageFanSpeed" in result: + print(GREEN + f" Average Fan Speed: {result['averageFanSpeed']:.2f}%" + RESET) if "averageVRTemp" in result: print(GREEN + f" Average VR Temperature: {result['averageVRTemp']:.2f}°C" + RESET) else: From 99f832322557aba34b6a21d68f8fa0dc45250799 Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Wed, 9 Jul 2025 03:39:45 +0400 Subject: [PATCH 05/26] feat(cli): Add option to apply specific settings without benchmarking This commit introduces a new command-line option to directly set the core voltage and frequency on the Bitaxe miner, bypassing the full benchmarking process. **Motivation:** Previously, applying specific desired settings required either temporarily modifying the script or running a full benchmark starting at those settings. This new functionality provides a quick, convenient, and direct way to configure the miner to a known good state or a specific operating point without needing to run an entire benchmark cycle. This is particularly useful for fine-tuning after initial benchmarks, or for quickly re-applying optimal settings. **Implementation:** A new command-line flag, -sor--set-values, has been added using argparse. When this flag is detected, the script will: 1. Parse the provided core voltage (-v) and frequency (-f) as the target settings. 2. Call the existing set_system_settings() function to apply these parameters to the Bitaxe. 3. Print confirmation messages. 4. Exit immediately using sys.exit(0), preventing the benchmark loop from initiating. **Usage:** To use this new mode, run the script with the --set-values flag, specifying your desired voltage and frequency: python bitaxe_hasrate_benchmark.py --set-values -v -f Example: python bitaxe_hasrate_benchmark.py 192.168.1.136 --set-values -v 1150 -f 780 This enhancement streamlines the process of applying specific configurations to the Bitaxe miner.# --- bitaxe_hashrate_benchmark.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index ea1238c..50ea93f 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -20,6 +20,10 @@ def parse_arguments(): parser.add_argument('-f', '--frequency', type=int, default=500, help='Initial frequency in MHz (default: 500)') + # Add a new argument for setting values only + parser.add_argument('-s', '--set-values', action='store_true', + help='Set values only, do not benchmark. Apply specified voltage and frequency settings to Bitaxe and exit') + # If no arguments are provided, print help and exit if len(sys.argv) == 1: parser.print_help() @@ -350,6 +354,17 @@ def reset_to_best_setting(): restart_system() +# --- Main execution logic --- +if args.set_values: + print(GREEN + "\n--- Applying Settings Only ---" + RESET) + print(GREEN + f"Applying Core Voltage: {initial_voltage}mV, Frequency: {initial_frequency}MHz to Bitaxe." + RESET) + + # Call the existing set_system_settings function + set_system_settings(initial_voltage, initial_frequency) + + print(GREEN + "Settings applied. Check your Bitaxe web interface to confirm." + RESET) + sys.exit(0) # Exit the script after applying settings + # Main benchmarking process try: fetch_default_settings() From 6b60b33be37176d511871dd8dabdf072e331f136 Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Wed, 9 Jul 2025 03:46:34 +0400 Subject: [PATCH 06/26] refactor(cli): Improve command-line help output Enhanced the script's command-line help message to be more user-friendly and informative. Key improvements include: * **Clearer Descriptions:** Detailed explanations for each argument, including their dual purpose for benchmarking and setting values. * **Usage Examples:** Added explicit command examples for both benchmark and set-only modes in the help message's epilog. * **Improved Formatting:** Utilized a custom formatter to preserve multi-line text, add default values, and incorporate color for better readability.# --- bitaxe_hashrate_benchmark.py | 80 ++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index 50ea93f..24986eb 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -11,24 +11,78 @@ RED = "\033[91m" RESET = "\033[0m" -# Add this before the configuration section +# This formatter allows for multi-line descriptions in help messages and adds default values +class RawTextAndDefaultsHelpFormatter(argparse.RawTextHelpFormatter): + def _get_help_string(self, action): + help_text = super()._get_help_string(action) + if action.default is not argparse.SUPPRESS: + # Append default value to help text if available + defaulting_nargs = [argparse.OPTIONAL, argparse.ZERO_OR_MORE] + if action.option_strings or action.nargs in defaulting_nargs: + if "\n" in help_text: + help_text += f"\n(default: {action.default})" + else: + help_text += f" (default: {action.default})" + return help_text + +# Modify the parse_arguments function def parse_arguments(): - parser = argparse.ArgumentParser(description='Bitaxe Hashrate Benchmark Tool') - parser.add_argument('bitaxe_ip', nargs='?', help='IP address of the Bitaxe (e.g., 192.168.2.26)') - parser.add_argument('-v', '--voltage', type=int, default=1150, - help='Initial voltage in mV (default: 1150)') - parser.add_argument('-f', '--frequency', type=int, default=500, - help='Initial frequency in MHz (default: 500)') - - # Add a new argument for setting values only - parser.add_argument('-s', '--set-values', action='store_true', - help='Set values only, do not benchmark. Apply specified voltage and frequency settings to Bitaxe and exit') - + parser = argparse.ArgumentParser( + description= + f"{GREEN}Bitaxe Hashrate Benchmark Tool v1.0{RESET}\n" + "This script allows you to either benchmark your Bitaxe miner across various " + "voltage and frequency settings, or apply specific settings directly.\n", + epilog= + f"{YELLOW}Examples:{RESET}\n" + f" {YELLOW}1. Run a full benchmark (starting at 1150mV, 500MHz):{RESET}\n" + f" {GREEN}python bitaxe_hasrate_benchmark.py 192.168.1.136 -v 1150 -f 500{RESET}\n\n" + f" {YELLOW}2. Apply specific settings (1150mV, 780MHz) and exit:{RESET}\n" + f" {GREEN}python bitaxe_hasrate_benchmark.py 192.168.1.136 --set-values -v 1150 -f 780{RESET}\n\n" + f" {YELLOW}3. Get help (this message):{RESET}\n" + f" {GREEN}python bitaxe_hasrate_benchmark.py --help{RESET}", + formatter_class=RawTextAndDefaultsHelpFormatter # <--- USE THE CUSTOM FORMATTER + ) + + # Positional Argument + parser.add_argument( + 'bitaxe_ip', + nargs='?', # Makes it optional if --help is used alone, but required otherwise + help=f"{YELLOW}IP address of your Bitaxe miner (e.g., 192.168.2.26){RESET}\n" + " This is required for both benchmarking and setting values." + ) + + # Optional Arguments + parser.add_argument( + '-v', '--voltage', + type=int, + default=1150, # Default value for benchmark start or target setting + help=f"{YELLOW}Core voltage in mV.{RESET}\n" + " For benchmark mode: The starting voltage for testing.\n" + " For --set-values mode: The exact voltage to apply." + ) + parser.add_argument( + '-f', '--frequency', + type=int, + default=500, # Default value for benchmark start or target setting + help=f"{YELLOW}Core frequency in MHz.{RESET}\n" + " For benchmark mode: The starting frequency for testing.\n" + " For --set-values mode: The exact frequency to apply." + ) + + # New argument for setting values only + parser.add_argument( + '-s', '--set-values', + action='store_true', + help=f"{YELLOW}Set values only; do not run benchmark.{RESET}\n" + " If this flag is present, the script will apply the voltage (-v) and\n" + " frequency (-f) settings to the Bitaxe and then exit." + ) + # If no arguments are provided, print help and exit if len(sys.argv) == 1: parser.print_help() sys.exit(1) - + return parser.parse_args() # Replace the configuration section From 6d19eb6ad6728c3aac049f0075ce9edba3e84545 Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Wed, 9 Jul 2025 03:56:59 +0400 Subject: [PATCH 07/26] add new readme based on latest changes --- README.md | 311 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 162 insertions(+), 149 deletions(-) diff --git a/README.md b/README.md index 4f023af..777d5f2 100644 --- a/README.md +++ b/README.md @@ -1,166 +1,179 @@ -# Bitaxe Hashrate Benchmark +# **Bitaxe Hashrate Benchmark** A Python-based benchmarking tool for optimizing Bitaxe mining performance by testing different voltage and frequency combinations while monitoring hashrate, temperature, and power efficiency. -## Features +## **Features** -- Automated benchmarking of different voltage/frequency combinations -- Temperature monitoring and safety cutoffs -- Power efficiency calculations (J/TH) -- Automatic saving of benchmark results -- Graceful shutdown with best settings retention -- Docker support for easy deployment - -## Prerequisites - -- Python 3.11 or higher -- Access to a Bitaxe miner on your network -- Docker (optional, for containerized deployment) -- Git (optional, for cloning the repository) - -## Installation - -### Standard Installation - -1. Clone the repository: -```bash -git clone https://github.com/mrv777/Bitaxe-Hashrate-Benchmark.git -cd Bitaxe-Hashrate-Benchmark -``` - -2. Create and activate a virtual environment: -```bash -python -m venv venv -# On Windows -venv\Scripts\activate -# On Linux/Mac -source venv/bin/activate -``` - -3. Install dependencies: -```bash -pip install -r requirements.txt -``` - -### Docker Installation - -1. Build the Docker image: -```bash -docker build -t bitaxe-benchmark . -``` - -## Usage - -### Standard Usage - -Run the benchmark tool by providing your Bitaxe's IP address: - -```bash -python bitaxe_hashrate_benchmark.py -``` - -Optional parameters: -- `-v, --voltage`: Initial voltage in mV (default: 1150) -- `-f, --frequency`: Initial frequency in MHz (default: 500) - -Example: -```bash -python bitaxe_hashrate_benchmark.py 192.168.2.29 -v 1175 -f 775 -``` - -### Docker Usage (Optional) - -Run the container with your Bitaxe's IP address: - -```bash -docker run --rm bitaxe-benchmark [options] -``` - -Example: -```bash -docker run --rm bitaxe-benchmark 192.168.2.26 -v 1200 -f 550 -``` - -## Configuration - -The script includes several configurable parameters: - -- Maximum chip temperature: 66°C -- Maximum VR temperature: 86°C -- Maximum allowed voltage: 1400mV -- Minimum allowed voltage: 1000mV -- Maximum allowed frequency: 1200MHz -- Maximum power consumption: 40W -- Minimum allowed frequency: 400MHz -- Minimum input voltage: 4800mV -- Maximum input voltage: 5500mV -- Benchmark duration: 10 minutes -- Sample interval: 15 seconds -- **Minimum required samples: 7** (for valid data processing) -- Voltage increment: 20mV -- Frequency increment: 25MHz - -## Output - -The benchmark results are saved to `bitaxe_benchmark_results_.json`, containing: -- Complete test results for all combinations -- Top 5 performing configurations ranked by hashrate -- Top 5 most efficient configurations ranked by J/TH -- For each configuration: - - Average hashrate (with outlier removal) - - Temperature readings (excluding initial warmup period) - - VR temperature readings (when available) - - Power efficiency metrics (J/TH) - - Input voltage measurements - - Voltage/frequency combinations tested - -## Safety Features - -- Automatic temperature monitoring with safety cutoff (66°C chip temp) -- Voltage regulator (VR) temperature monitoring with safety cutoff (86°C) -- Input voltage monitoring with minimum threshold (4800mV) and maximum threshold (5500mV) -- Power consumption monitoring with safety cutoff (40W) -- Temperature validation (must be above 5°C) -- Graceful shutdown on interruption (Ctrl+C) -- Automatic reset to best performing settings after benchmarking -- Input validation for safe voltage and frequency ranges -- Hashrate validation to ensure stability -- Protection against invalid system data -- Outlier removal from benchmark results - -## Benchmarking Process +* Automated benchmarking of different voltage/frequency combinations +* **Direct setting of specific voltage and frequency from command line** +* Temperature monitoring and safety cutoffs +* **Power consumption monitoring and reporting (Watts)** +* **Fan speed monitoring and reporting (RPM/Percentage)** +* Power efficiency calculations (J/TH) +* Automatic saving of benchmark results +* Graceful shutdown with best settings retention +* Docker support for easy deployment + +## **Prerequisites** + +* Python 3.11 or higher +* Access to a Bitaxe miner on your network +* Docker (optional, for containerized deployment) +* Git (optional, for cloning the repository) + +## **Installation** + +### **Standard Installation** + +1. Clone the repository: + git clone https://github.com/mrv777/Bitaxe-Hashrate-Benchmark.git + cd Bitaxe-Hashrate-Benchmark + +2. Create and activate a virtual environment: + python \-m venv venv + \# On Windows + venv\\Scripts\\activate + \# On Linux/Mac + source venv/bin/activate + +3. Install dependencies: + pip install \-r requirements.txt + +### **Docker Installation** + +1. Build the Docker image: + docker build \-t bitaxe-benchmark . + +## **Usage** + +### **Standard Usage (Run Full Benchmark)** + +Run the benchmark tool by providing your Bitaxe's IP address and initial settings: +python bitaxe\_hasrate\_benchmark.py \ \-v \ \-f \ + +**Arguments:** + +* \: **Required.** IP address of your Bitaxe miner (e.g., 192.168.2.26). +* \-v, \--voltage: **Optional.** Initial voltage in mV for testing (default: 1150). +* \-f, \--frequency: **Optional.** Initial frequency in MHz for testing (default: 500). + +**Example:** +python bitaxe\_hasrate\_benchmark.py 192.168.1.136 \-v 1150 \-f 500 + +### **Apply Specific Settings (Without Benchmarking)** + +To quickly apply specific voltage and frequency settings to your Bitaxe without running the full benchmark: +python bitaxe\_hasrate\_benchmark.py \ \--set-values \-v \ \-f \ + +**Arguments:** + +* \: **Required.** IP address of your Bitaxe miner. +* \-s, \--set-values: **Flag.** Activates this mode to only set values and exit. +* \-v, \--voltage: **Required.** The exact voltage in mV to apply. +* \-f, \--frequency: **Required.** The exact frequency in MHz to apply. + +**Example:** +python bitaxe\_hasrate\_benchmark.py 192.168.1.136 \--set-values \-v 1150 \-f 780 + +### **Docker Usage (Optional)** + +Run the container with your Bitaxe's IP address (add \--set-values for that mode): +docker run \--rm bitaxe-benchmark \ \[options\] + +Example (Full Benchmark): +docker run \--rm bitaxe-benchmark 192.168.2.26 \-v 1200 \-f 550 + +Example (Set Settings Only): +docker run \--rm bitaxe-benchmark 192.168.2.26 \--set-values \-v 1150 \-f 780 + +## **Configuration** + +The script includes several configurable parameters. These can be adjusted in the bitaxe\_hasrate\_benchmark.py file: + +* Maximum chip temperature: 66°C +* Maximum VR temperature: 86°C +* Maximum allowed voltage: 1400mV +* Minimum allowed voltage: 1000mV +* Maximum allowed frequency: 1200MHz +* Maximum power consumption: 30W +* Minimum allowed frequency: 400MHz +* Minimum input voltage: 4800mV +* Maximum input voltage: 5500mV +* Benchmark duration: 600 seconds (10 minutes per combination) +* Sample interval: 15 seconds +* Minimum required samples: 7 (for valid data processing) +* Voltage increment: 15mV +* Frequency increment: 20MHz +* **ASIC Configuration:** asic\_count is hardcoded to 1 as it's not always provided by the API. small\_core\_count is fetched from the Bitaxe. + +## **Output** + +The benchmark results are saved to bitaxe\_benchmark\_results\_\.json, containing: + +* Complete test results for all combinations +* Top 5 performing configurations ranked by hashrate +* Top 5 most efficient configurations ranked by J/TH +* For each configuration: + * Average hashrate (with outlier removal) + * Temperature readings (excluding initial warmup period) + * VR temperature readings (when available) + * Power efficiency metrics (J/TH) + * **Average Power (Watts)** + * **Average Fan Speed (Percentage or RPM, if available from API)** + * Input voltage measurements + * Voltage/frequency combinations tested + * Error reason (if any) for a specific iteration + +## **Safety Features** + +* Automatic temperature monitoring with safety cutoff (66°C chip temp) +* Voltage regulator (VR) temperature monitoring with safety cutoff (86°C) +* Input voltage monitoring with minimum threshold (4800mV) and maximum threshold (5500mV) +* Power consumption monitoring with safety cutoff (30W) +* Temperature validation (must be above 5°C) +* Graceful shutdown on interruption (Ctrl+C) +* Automatic reset to best performing settings after benchmarking +* Input validation for safe voltage and frequency ranges +* Hashrate validation to ensure stability +* Protection against invalid system data +* Outlier removal from benchmark results + +## **Benchmarking Process** The tool follows this process: -1. Starts with user-specified or default voltage/frequency -2. Tests each combination for 20 minutes -3. Validates hashrate is within 8% of theoretical maximum -4. Incrementally adjusts settings: - - Increases frequency if stable - - Increases voltage if unstable - - Stops at thermal or stability limits -5. Records and ranks all successful configurations -6. Automatically applies the best performing stable settings -7. Restarts system after each test for stability + +1. Starts with user-specified or default voltage/frequency +2. Tests each combination for 10 minutes +3. Validates hashrate is within 8% of theoretical maximum +4. Incrementally adjusts settings: + * Increases frequency if stable + * Increases voltage if unstable + * Stops at thermal or stability limits +5. Records and ranks all successful configurations +6. Automatically applies the best performing stable settings +7. Restarts system after each test for stability 8. Allows 90-second stabilization period between tests -## Data Processing +## **Data Processing** The tool implements several data processing techniques to ensure accurate results: -- Removes 3 highest and 3 lowest hashrate readings to eliminate outliers -- Excludes first 6 temperature readings during warmup period -- Validates hashrate is within 6% of theoretical maximum -- Averages power consumption across entire test period -- Monitors VR temperature when available -- Calculates efficiency in Joules per Terahash (J/TH) -## Contributing +* Removes 3 highest and 3 lowest hashrate readings to eliminate outliers +* Excludes first 6 temperature readings during warmup period +* Validates hashrate is within 6% of theoretical maximum +* Averages power consumption across entire test period +* Monitors VR temperature when available +* Calculates efficiency in Joules per Terahash (J/TH) +* **Averages fan speed across entire test period** + +## **Contributing** -Contributions are welcome! Please feel free to submit a Pull Request. +Contributions are welcome\! Please feel free to submit a Pull Request. -## License +## **License** -This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details. +This project is licensed under the GNU General Public License v3.0 \- see the [LICENSE](https://www.google.com/search?q=LICENSE) file for details. -## Disclaimer +## **Disclaimer** Please use this tool responsibly. Overclocking and voltage modifications can potentially damage your hardware if not done carefully. Always ensure proper cooling and monitor your device during benchmarking. \ No newline at end of file From 36c4fc1d5d759ce68376ba129ab53afec2d91107 Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Wed, 9 Jul 2025 04:04:07 +0400 Subject: [PATCH 08/26] add new readme based on latest changes --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 777d5f2..24c363b 100644 --- a/README.md +++ b/README.md @@ -26,36 +26,46 @@ A Python-based benchmarking tool for optimizing Bitaxe mining performance by tes ### **Standard Installation** 1. Clone the repository: + ``` git clone https://github.com/mrv777/Bitaxe-Hashrate-Benchmark.git cd Bitaxe-Hashrate-Benchmark + ``` 2. Create and activate a virtual environment: + ``` python \-m venv venv \# On Windows venv\\Scripts\\activate \# On Linux/Mac source venv/bin/activate + ``` 3. Install dependencies: + ``` pip install \-r requirements.txt + ``` ### **Docker Installation** 1. Build the Docker image: + ``` docker build \-t bitaxe-benchmark . + ``` ## **Usage** ### **Standard Usage (Run Full Benchmark)** Run the benchmark tool by providing your Bitaxe's IP address and initial settings: +``` python bitaxe\_hasrate\_benchmark.py \ \-v \ \-f \ +``` **Arguments:** * \: **Required.** IP address of your Bitaxe miner (e.g., 192.168.2.26). -* \-v, \--voltage: **Optional.** Initial voltage in mV for testing (default: 1150). -* \-f, \--frequency: **Optional.** Initial frequency in MHz for testing (default: 500). +```* \-v, \--voltage:``` **Optional.** Initial voltage in mV for testing (default: 1150). +```* \-f, \--frequency:``` **Optional.** Initial frequency in MHz for testing (default: 500). **Example:** python bitaxe\_hasrate\_benchmark.py 192.168.1.136 \-v 1150 \-f 500 From 44b2992bed0977e0d78b9b94dfbcf8ca369252bf Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Wed, 9 Jul 2025 04:06:19 +0400 Subject: [PATCH 09/26] add new readme based on latest changes --- README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 24c363b..d041414 100644 --- a/README.md +++ b/README.md @@ -68,33 +68,45 @@ python bitaxe\_hasrate\_benchmark.py \ \-v \ \-f ```* \-f, \--frequency:``` **Optional.** Initial frequency in MHz for testing (default: 500). **Example:** +``` python bitaxe\_hasrate\_benchmark.py 192.168.1.136 \-v 1150 \-f 500 +``` ### **Apply Specific Settings (Without Benchmarking)** To quickly apply specific voltage and frequency settings to your Bitaxe without running the full benchmark: +``` python bitaxe\_hasrate\_benchmark.py \ \--set-values \-v \ \-f \ +``` **Arguments:** * \: **Required.** IP address of your Bitaxe miner. -* \-s, \--set-values: **Flag.** Activates this mode to only set values and exit. -* \-v, \--voltage: **Required.** The exact voltage in mV to apply. -* \-f, \--frequency: **Required.** The exact frequency in MHz to apply. +```* \-s, \--set-values:``` **Flag.** Activates this mode to only set values and exit. +```* \-v, \--voltage:``` **Required.** The exact voltage in mV to apply. +```* \-f, \--frequency:``` **Required.** The exact frequency in MHz to apply. **Example:** +``` python bitaxe\_hasrate\_benchmark.py 192.168.1.136 \--set-values \-v 1150 \-f 780 +``` ### **Docker Usage (Optional)** Run the container with your Bitaxe's IP address (add \--set-values for that mode): +``` docker run \--rm bitaxe-benchmark \ \[options\] +``` Example (Full Benchmark): +``` docker run \--rm bitaxe-benchmark 192.168.2.26 \-v 1200 \-f 550 +``` Example (Set Settings Only): +``` docker run \--rm bitaxe-benchmark 192.168.2.26 \--set-values \-v 1150 \-f 780 +``` ## **Configuration** From 0604faca5f50b65278b3bd1840339a83f1be1690 Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Wed, 9 Jul 2025 04:10:34 +0400 Subject: [PATCH 10/26] add new readme based on latest changes --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d041414..f0b73e2 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,13 @@ A Python-based benchmarking tool for optimizing Bitaxe mining performance by tes ### **Standard Installation** 1. Clone the repository: - ``` + ```bash git clone https://github.com/mrv777/Bitaxe-Hashrate-Benchmark.git cd Bitaxe-Hashrate-Benchmark ``` 2. Create and activate a virtual environment: - ``` + ```bash python \-m venv venv \# On Windows venv\\Scripts\\activate @@ -41,15 +41,15 @@ A Python-based benchmarking tool for optimizing Bitaxe mining performance by tes ``` 3. Install dependencies: - ``` + ```bash pip install \-r requirements.txt ``` ### **Docker Installation** 1. Build the Docker image: - ``` - docker build \-t bitaxe-benchmark . + ```bash + docker build \-t bitaxe-benchmark ``` ## **Usage** @@ -57,7 +57,7 @@ A Python-based benchmarking tool for optimizing Bitaxe mining performance by tes ### **Standard Usage (Run Full Benchmark)** Run the benchmark tool by providing your Bitaxe's IP address and initial settings: -``` +```bash python bitaxe\_hasrate\_benchmark.py \ \-v \ \-f \ ``` @@ -68,14 +68,14 @@ python bitaxe\_hasrate\_benchmark.py \ \-v \ \-f ```* \-f, \--frequency:``` **Optional.** Initial frequency in MHz for testing (default: 500). **Example:** -``` +```bash python bitaxe\_hasrate\_benchmark.py 192.168.1.136 \-v 1150 \-f 500 ``` ### **Apply Specific Settings (Without Benchmarking)** To quickly apply specific voltage and frequency settings to your Bitaxe without running the full benchmark: -``` +```bash python bitaxe\_hasrate\_benchmark.py \ \--set-values \-v \ \-f \ ``` @@ -87,24 +87,24 @@ python bitaxe\_hasrate\_benchmark.py \ \--set-values \-v \ \[options\] ``` Example (Full Benchmark): -``` +```bash docker run \--rm bitaxe-benchmark 192.168.2.26 \-v 1200 \-f 550 ``` Example (Set Settings Only): -``` +```bash docker run \--rm bitaxe-benchmark 192.168.2.26 \--set-values \-v 1150 \-f 780 ``` From 7d4da387c05d78f71ff41f540131f196fe8a8f88 Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Thu, 10 Jul 2025 13:00:37 +0400 Subject: [PATCH 11/26] fix(docs): Correct README argument inline code formatting Resolved rendering issues in argument lists by correctly applying single backticks for inline code, ensuring proper display on GitHub. --- README.md | 64 ++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index f0b73e2..2322522 100644 --- a/README.md +++ b/README.md @@ -32,25 +32,21 @@ A Python-based benchmarking tool for optimizing Bitaxe mining performance by tes ``` 2. Create and activate a virtual environment: - ```bash - python \-m venv venv - \# On Windows - venv\\Scripts\\activate - \# On Linux/Mac - source venv/bin/activate - ``` + `python -m venv venv` + # On Windows + `venvScriptsactivate` + # On Linux/Mac + `source venv/bin/activate` + + +3. Install dependencies: + `pip install -r requirements.txt` -3. Install dependencies: - ```bash - pip install \-r requirements.txt - ``` ### **Docker Installation** 1. Build the Docker image: - ```bash - docker build \-t bitaxe-benchmark - ``` + `docker build -t bitaxe-benchmark` ## **Usage** @@ -58,59 +54,59 @@ A Python-based benchmarking tool for optimizing Bitaxe mining performance by tes Run the benchmark tool by providing your Bitaxe's IP address and initial settings: ```bash -python bitaxe\_hasrate\_benchmark.py \ \-v \ \-f \ +python bitaxe_hashrate_benchmark.py -v -f ``` **Arguments:** -* \: **Required.** IP address of your Bitaxe miner (e.g., 192.168.2.26). -```* \-v, \--voltage:``` **Optional.** Initial voltage in mV for testing (default: 1150). -```* \-f, \--frequency:``` **Optional.** Initial frequency in MHz for testing (default: 500). +* ``: **Required.** IP address of your Bitaxe miner (e.g., `192.168.2.26`). +* `-v, --voltage:` **Optional.** Initial voltage in mV for testing (default: `1150`). +* `-f, --frequency:` **Optional.** Initial frequency in MHz for testing (default: `500`). **Example:** ```bash -python bitaxe\_hasrate\_benchmark.py 192.168.1.136 \-v 1150 \-f 500 +python bitaxe_hashrate_benchmark.py 192.168.1.136 -v 1150 -f 550 ``` ### **Apply Specific Settings (Without Benchmarking)** To quickly apply specific voltage and frequency settings to your Bitaxe without running the full benchmark: ```bash -python bitaxe\_hasrate\_benchmark.py \ \--set-values \-v \ \-f \ +python bitaxe_hashrate_benchmark.py --set-values -v -f ``` **Arguments:** -* \: **Required.** IP address of your Bitaxe miner. -```* \-s, \--set-values:``` **Flag.** Activates this mode to only set values and exit. -```* \-v, \--voltage:``` **Required.** The exact voltage in mV to apply. -```* \-f, \--frequency:``` **Required.** The exact frequency in MHz to apply. +* ``: **Required.** IP address of your Bitaxe miner. +* `-s, --set-values`: **Flag.** Activates this mode to only set values and exit. +* `-v, --voltage`: **Required.** The exact voltage in mV to apply. +* `-f, --frequency`: **Required.** The exact frequency in MHz to apply. **Example:** ```bash -python bitaxe\_hasrate\_benchmark.py 192.168.1.136 \--set-values \-v 1150 \-f 780 +python bitaxe_hashrate_benchmark.py 192.168.1.136 --set-values -v 1150 -f 780 ``` ### **Docker Usage (Optional)** -Run the container with your Bitaxe's IP address (add \--set-values for that mode): +Run the container with your Bitaxe's IP address (add --set-values for that mode): ```bash -docker run \--rm bitaxe-benchmark \ \[options\] +docker run --rm bitaxe-benchmark [options] ``` Example (Full Benchmark): ```bash -docker run \--rm bitaxe-benchmark 192.168.2.26 \-v 1200 \-f 550 +docker run --rm bitaxe-benchmark 192.168.2.26 -v 1200 -f 550 ``` Example (Set Settings Only): ```bash -docker run \--rm bitaxe-benchmark 192.168.2.26 \--set-values \-v 1150 \-f 780 +docker run --rm bitaxe-benchmark 192.168.2.26 --set-values -v 1150 -f 780 ``` ## **Configuration** -The script includes several configurable parameters. These can be adjusted in the bitaxe\_hasrate\_benchmark.py file: +The script includes several configurable parameters. These can be adjusted in the bitaxe_hashrate_benchmark.py file: * Maximum chip temperature: 66°C * Maximum VR temperature: 86°C @@ -126,11 +122,11 @@ The script includes several configurable parameters. These can be adjusted in th * Minimum required samples: 7 (for valid data processing) * Voltage increment: 15mV * Frequency increment: 20MHz -* **ASIC Configuration:** asic\_count is hardcoded to 1 as it's not always provided by the API. small\_core\_count is fetched from the Bitaxe. +* **ASIC Configuration:** asic_count is hardcoded to 1 as it's not always provided by the API. small_core_count is fetched from the Bitaxe. ## **Output** -The benchmark results are saved to bitaxe\_benchmark\_results\_\.json, containing: +The benchmark results are saved to bitaxe_benchmark_results_.json, containing: * Complete test results for all combinations * Top 5 performing configurations ranked by hashrate @@ -190,11 +186,11 @@ The tool implements several data processing techniques to ensure accurate result ## **Contributing** -Contributions are welcome\! Please feel free to submit a Pull Request. +Contributions are welcome! Please feel free to submit a Pull Request. ## **License** -This project is licensed under the GNU General Public License v3.0 \- see the [LICENSE](https://www.google.com/search?q=LICENSE) file for details. +This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](https://www.google.com/search?q=LICENSE) file for details. ## **Disclaimer** From 9033a22fd8fffb5e82adecedd5e031f952f8e2ab Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Thu, 10 Jul 2025 13:04:01 +0400 Subject: [PATCH 12/26] fix(docs): Correct README argument inline code formatting Resolved rendering issues in argument lists by correctly applying single backticks for inline code, ensuring proper display on GitHub. --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2322522..8e35c8e 100644 --- a/README.md +++ b/README.md @@ -32,16 +32,18 @@ A Python-based benchmarking tool for optimizing Bitaxe mining performance by tes ``` 2. Create and activate a virtual environment: - `python -m venv venv` + ```bash + python -m venv venv # On Windows - `venvScriptsactivate` + venvScriptsactivate # On Linux/Mac - `source venv/bin/activate` - + source venv/bin/activate + ``` 3. Install dependencies: - `pip install -r requirements.txt` - + ```bash + pip install -r requirements.txt + ``` ### **Docker Installation** From 77a3cdafacb893a1c6c5408a6e7b18cee963c376 Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Thu, 10 Jul 2025 14:00:12 +0400 Subject: [PATCH 13/26] refining readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8e35c8e..bc8c01e 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ A Python-based benchmarking tool for optimizing Bitaxe mining performance by tes ```bash python -m venv venv # On Windows - venvScriptsactivate + venv\Scripts\activate # On Linux/Mac source venv/bin/activate ``` @@ -48,7 +48,7 @@ A Python-based benchmarking tool for optimizing Bitaxe mining performance by tes ### **Docker Installation** 1. Build the Docker image: - `docker build -t bitaxe-benchmark` + `docker build -t bitaxe-benchmark .` ## **Usage** From 2edbeb44cd6330847c4d9daa6ca4547614fb9415 Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Fri, 11 Jul 2025 14:58:53 +0400 Subject: [PATCH 14/26] refining readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bc8c01e..b56abff 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ Contributions are welcome! Please feel free to submit a Pull Request. ## **License** -This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](https://www.google.com/search?q=LICENSE) file for details. +This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details. ## **Disclaimer** From fd3d302aa3d6ca7bcb7652792177a0d1ee08b472 Mon Sep 17 00:00:00 2001 From: ZorgOros <95384420+zorgoros@users.noreply.github.com> Date: Fri, 11 Jul 2025 23:17:39 +0400 Subject: [PATCH 15/26] refining readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b56abff..c54d8ef 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ The tool follows this process: 1. Starts with user-specified or default voltage/frequency 2. Tests each combination for 10 minutes -3. Validates hashrate is within 8% of theoretical maximum +3. Validates hashrate is within 6% of theoretical maximum 4. Incrementally adjusts settings: * Increases frequency if stable * Increases voltage if unstable From 394cf79354e3b20f361d7309a8aef65bcaddf983 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 13 Aug 2025 18:14:31 +0200 Subject: [PATCH 16/26] Add stdev and running stdev --- bitaxe_hashrate_benchmark.py | 46 ++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index b2f454b..d45ab69 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -4,6 +4,7 @@ import signal import sys import argparse +import statistics from datetime import datetime START_TIME = datetime.now().strftime("%Y-%m-%d_%H") @@ -89,6 +90,12 @@ def parse_arguments(): # Check if we're handling an interrupt (Ctrl+C) handling_interrupt = False +def running_stddev(N, s1, s2): + if N > 1: + return ((N * s2 - s1 ** 2) / (N * (N - 1))) ** 0.5 + else: + return 0.0 + def fetch_default_settings(): global default_voltage, default_frequency, small_core_count, asic_count @@ -234,6 +241,8 @@ def benchmark_iteration(core_voltage, frequency): current_time = time.strftime("%H:%M:%S") print(GREEN + f"[{current_time}] Starting benchmark for Core Voltage: {core_voltage}mV, Frequency: {frequency}MHz" + RESET) hash_rates = [] + s1 = 0.0 + s2 = 0.0 temperatures = [] power_consumptions = [] vr_temps = [] @@ -244,48 +253,50 @@ def benchmark_iteration(core_voltage, frequency): info = get_system_info() if info is None: print(YELLOW + "Skipping this iteration due to failure in fetching system info." + RESET) - return None, None, None, False, None, "SYSTEM_INFO_FAILURE" + return None, None, None, None, False, None, "SYSTEM_INFO_FAILURE" temp = info.get("temp") vr_temp = info.get("vrTemp") # Get VR temperature if available voltage = info.get("voltage") if temp is None: print(YELLOW + "Temperature data not available." + RESET) - return None, None, None, False, None, "TEMPERATURE_DATA_FAILURE" + return None, None, None, None, False, None, "TEMPERATURE_DATA_FAILURE" if temp < 5: print(YELLOW + "Temperature is below 5°C. This is unexpected. Please check the system." + RESET) - return None, None, None, False, None, "TEMPERATURE_BELOW_5" + return None, None, None, None, False, None, "TEMPERATURE_BELOW_5" # Check both chip and VR temperatures if temp >= max_temp: print(RED + f"Chip temperature exceeded {max_temp}°C! Stopping current benchmark." + RESET) - return None, None, None, False, None, "CHIP_TEMP_EXCEEDED" + return None, None, None, None, False, None, "CHIP_TEMP_EXCEEDED" if vr_temp is not None and vr_temp >= max_vr_temp: print(RED + f"Voltage regulator temperature exceeded {max_vr_temp}°C! Stopping current benchmark." + RESET) - return None, None, None, False, None, "VR_TEMP_EXCEEDED" + return None, None, None, None, False, None, "VR_TEMP_EXCEEDED" if voltage < min_input_voltage: print(RED + f"Input voltage is below the minimum allowed value of {min_input_voltage}mV! Stopping current benchmark." + RESET) - return None, None, None, False, None, "INPUT_VOLTAGE_BELOW_MIN" + return None, None, None, None, False, None, "INPUT_VOLTAGE_BELOW_MIN" if voltage > max_input_voltage: print(RED + f"Input voltage is above the maximum allowed value of {max_input_voltage}mV! Stopping current benchmark." + RESET) - return None, None, None, False, None, "INPUT_VOLTAGE_ABOVE_MAX" + return None, None, None, None, False, None, "INPUT_VOLTAGE_ABOVE_MAX" hash_rate = info.get("hashRate") power_consumption = info.get("power") if hash_rate is None or power_consumption is None: print(YELLOW + "Hashrate or Watts data not available." + RESET) - return None, None, None, False, None, "HASHRATE_POWER_DATA_FAILURE" + return None, None, None, None, False, None, "HASHRATE_POWER_DATA_FAILURE" if power_consumption > max_power: print(RED + f"Power consumption exceeded {max_power}W! Stopping current benchmark." + RESET) - return None, None, None, False, None, "POWER_CONSUMPTION_EXCEEDED" + return None, None, None, None, False, None, "POWER_CONSUMPTION_EXCEEDED" hash_rates.append(hash_rate) + s1 += hash_rate + s2 += hash_rate * hash_rate temperatures.append(temp) power_consumptions.append(power_consumption) if vr_temp is not None and vr_temp > 0: @@ -293,12 +304,14 @@ def benchmark_iteration(core_voltage, frequency): # Calculate percentage progress percentage_progress = ((sample + 1) / total_samples) * 100 + running_sd = running_stddev(sample + 1, s1, s2) status_line = ( f"[{sample + 1:2d}/{total_samples:2d}] " f"{percentage_progress:5.1f}% | " f"CV: {core_voltage:4d}mV | " f"F: {frequency:4d}MHz | " f"H: {int(hash_rate):4d} GH/s | " + f"SD: {running_sd:.2f} GH/s | " f"IV: {int(voltage):4d}mV | " f"T: {int(temp):2d}°C" ) @@ -315,6 +328,7 @@ def benchmark_iteration(core_voltage, frequency): sorted_hashrates = sorted(hash_rates) trimmed_hashrates = sorted_hashrates[3:-3] # Remove first 3 and last 3 elements average_hashrate = sum(trimmed_hashrates) / len(trimmed_hashrates) + hashrate_stdev = statistics.stdev(trimmed_hashrates) if len(trimmed_hashrates) > 1 else 0.0 # Sort and trim temperatures (remove lowest 6 readings during warmup) sorted_temps = sorted(temperatures) @@ -335,21 +349,22 @@ def benchmark_iteration(core_voltage, frequency): efficiency_jth = average_power / (average_hashrate / 1_000) else: print(RED + "Warning: Zero hashrate detected, skipping efficiency calculation" + RESET) - return None, None, None, False, None, "ZERO_HASHRATE" + return None, None, None, None, False, None, "ZERO_HASHRATE" # Calculate if hashrate is within 6% of expected hashrate_within_tolerance = (average_hashrate >= expected_hashrate * 0.94) print(GREEN + f"Average Hashrate: {average_hashrate:.2f} GH/s (Expected: {expected_hashrate:.2f} GH/s)" + RESET) + print(GREEN + f"Hashrate Std Dev: {hashrate_stdev:.2f} GH/s" + RESET) print(GREEN + f"Average Temperature: {average_temperature:.2f}°C" + RESET) if average_vr_temp is not None: print(GREEN + f"Average VR Temperature: {average_vr_temp:.2f}°C" + RESET) print(GREEN + f"Efficiency: {efficiency_jth:.2f} J/TH" + RESET) - return average_hashrate, average_temperature, efficiency_jth, hashrate_within_tolerance, average_vr_temp, None + return average_hashrate, hashrate_stdev, average_temperature, efficiency_jth, hashrate_within_tolerance, average_vr_temp, None else: print(YELLOW + "No Hashrate or Temperature or Watts data collected." + RESET) - return None, None, None, False, None, "NO_DATA_COLLECTED" + return None, None, None, None, False, None, "NO_DATA_COLLECTED" def save_results(): try: @@ -397,13 +412,14 @@ def reset_to_best_setting(): while current_voltage <= max_allowed_voltage and current_frequency <= max_allowed_frequency: set_system_settings(current_voltage, current_frequency) - avg_hashrate, avg_temp, efficiency_jth, hashrate_ok, avg_vr_temp, error_reason = benchmark_iteration(current_voltage, current_frequency) + avg_hashrate, hashrate_stdev, avg_temp, efficiency_jth, hashrate_ok, avg_vr_temp, error_reason = benchmark_iteration(current_voltage, current_frequency) if avg_hashrate is not None and avg_temp is not None and efficiency_jth is not None: result = { "coreVoltage": current_voltage, "frequency": current_frequency, "averageHashRate": avg_hashrate, + "hashrateStdDev": hashrate_stdev, "averageTemperature": avg_temp, "efficiencyJTH": efficiency_jth } @@ -471,6 +487,7 @@ def reset_to_best_setting(): "coreVoltage": result["coreVoltage"], "frequency": result["frequency"], "averageHashRate": result["averageHashRate"], + "hashrateStdDev": result["hashrateStdDev"], "averageTemperature": result["averageTemperature"], "efficiencyJTH": result["efficiencyJTH"], **({"averageVRTemp": result["averageVRTemp"]} if "averageVRTemp" in result else {}) @@ -483,6 +500,7 @@ def reset_to_best_setting(): "coreVoltage": result["coreVoltage"], "frequency": result["frequency"], "averageHashRate": result["averageHashRate"], + "hashrateStdDev": result["hashrateStdDev"], "averageTemperature": result["averageTemperature"], "efficiencyJTH": result["efficiencyJTH"], **({"averageVRTemp": result["averageVRTemp"]} if "averageVRTemp" in result else {}) @@ -505,6 +523,7 @@ def reset_to_best_setting(): print(GREEN + f" Core Voltage: {result['coreVoltage']}mV" + RESET) print(GREEN + f" Frequency: {result['frequency']}MHz" + RESET) print(GREEN + f" Average Hashrate: {result['averageHashRate']:.2f} GH/s" + RESET) + print(GREEN + f" Hashrate Std Dev: {result.get('hashrateStdDev', 0.0):.2f} GH/s" + RESET) print(GREEN + f" Average Temperature: {result['averageTemperature']:.2f}°C" + RESET) print(GREEN + f" Efficiency: {result['efficiencyJTH']:.2f} J/TH" + RESET) if "averageVRTemp" in result: @@ -516,6 +535,7 @@ def reset_to_best_setting(): print(GREEN + f" Core Voltage: {result['coreVoltage']}mV" + RESET) print(GREEN + f" Frequency: {result['frequency']}MHz" + RESET) print(GREEN + f" Average Hashrate: {result['averageHashRate']:.2f} GH/s" + RESET) + print(GREEN + f" Hashrate Std Dev: {result.get('hashrateStdDev', 0.0):.2f} GH/s" + RESET) print(GREEN + f" Average Temperature: {result['averageTemperature']:.2f}°C" + RESET) print(GREEN + f" Efficiency: {result['efficiencyJTH']:.2f} J/TH" + RESET) if "averageVRTemp" in result: From a500e86dc3a3a216ad8e45296c8e6d0f3065a814 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 13 Aug 2025 19:45:01 +0200 Subject: [PATCH 17/26] Fix formatting so width remains more constant --- bitaxe_hashrate_benchmark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index d45ab69..7eb649c 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -311,7 +311,7 @@ def benchmark_iteration(core_voltage, frequency): f"CV: {core_voltage:4d}mV | " f"F: {frequency:4d}MHz | " f"H: {int(hash_rate):4d} GH/s | " - f"SD: {running_sd:.2f} GH/s | " + f"SD: {running_sd:3.0f} GH/s | " f"IV: {int(voltage):4d}mV | " f"T: {int(temp):2d}°C" ) From 75fbff2b78a3ee0142751522ae9c3520dbdcbdf3 Mon Sep 17 00:00:00 2001 From: Salih Emin <914656+cerebrux@users.noreply.github.com> Date: Tue, 30 Dec 2025 14:58:56 +0200 Subject: [PATCH 18/26] Fix potential negative variance in running_stddev calculation --- bitaxe_hashrate_benchmark.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index d6d464f..ba1b67a 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -176,7 +176,8 @@ def result_filename(): def running_stddev(N, s1, s2): if N > 1: - return ((N * s2 - s1 ** 2) / (N * (N - 1))) ** 0.5 + var = (N * s2 - s1 ** 2) / (N * (N - 1)) + return max(var, 0.0) ** 0.5 else: return 0.0 From 7c8700d7edf1d673c795268128b4fea9bda2436c Mon Sep 17 00:00:00 2001 From: Salih Emin <914656+cerebrux@users.noreply.github.com> Date: Tue, 30 Dec 2025 15:14:22 +0200 Subject: [PATCH 19/26] Enhance safety: Monitor stabilization period and warn about max settings --- bitaxe_hashrate_benchmark.py | 63 ++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index ba1b67a..6ba06ca 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -299,9 +299,10 @@ def set_system_settings(core_voltage, frequency): response.raise_for_status() # Raise an exception for HTTP errors print(YELLOW + f"Applying settings: Voltage = {core_voltage}mV, Frequency = {frequency}MHz" + RESET) time.sleep(2) - restart_system() + return restart_system() except requests.exceptions.RequestException as e: print(RED + f"Error setting system settings: {e}" + RESET) + return False def restart_system(): try: @@ -314,13 +315,36 @@ def restart_system(): print(YELLOW + f"Applying new settings and waiting {sleep_time}s for system stabilization..." + RESET) response = requests.post(f"{bitaxe_ip}/api/system/restart", timeout=10) response.raise_for_status() # Raise an exception for HTTP errors - time.sleep(sleep_time) # Allow sleep_time for the system to restart and start hashing + + # Monitor system during stabilization period + start_wait = time.time() + while time.time() - start_wait < sleep_time: + try: + info = get_system_info() + if info: + temp = info.get("temp") + power = info.get("power") + + if temp and temp >= max_temp: + print(RED + f"CRITICAL: Chip temperature {temp}°C exceeded limit {max_temp}°C during stabilization!" + RESET) + return False + + if power and power > max_power: + print(RED + f"CRITICAL: Power {power}W exceeded limit {max_power}W during stabilization!" + RESET) + return False + + except Exception: + pass # Ignore transient errors during restart + time.sleep(5) else: print(YELLOW + "Applying final settings..." + RESET) response = requests.post(f"{bitaxe_ip}/api/system/restart", timeout=10) response.raise_for_status() # Raise an exception for HTTP errors + + return True except requests.exceptions.RequestException as e: print(RED + f"Error restarting the system: {e}" + RESET) + return False def benchmark_iteration(core_voltage, frequency): current_time = time.strftime("%H:%M:%S") @@ -486,13 +510,27 @@ def reset_to_best_setting(): print(YELLOW + "No valid benchmarking results found. Applying predefined default settings." + RESET) set_system_settings(default_voltage, default_frequency) else: + # Find best hashrate result best_result = sorted(results, key=lambda x: x["averageHashRate"], reverse=True)[0] best_voltage = best_result["coreVoltage"] best_frequency = best_result["frequency"] - print(GREEN + f"Applying the best settings from benchmarking:\n" - f" Core Voltage: {best_voltage}mV\n" - f" Frequency: {best_frequency}MHz" + RESET) + # Find most efficient result + efficient_result = sorted(results, key=lambda x: x["efficiencyJTH"], reverse=False)[0] + eff_voltage = efficient_result["coreVoltage"] + eff_frequency = efficient_result["frequency"] + + print(GREEN + f"\n--- Benchmark Complete ---" + RESET) + print(GREEN + f"Best Hashrate Settings: {best_voltage}mV / {best_frequency}MHz ({best_result['averageHashRate']:.2f} GH/s)" + RESET) + print(GREEN + f"Most Efficient Settings: {eff_voltage}mV / {eff_frequency}MHz ({efficient_result['efficiencyJTH']:.2f} J/TH)" + RESET) + + print(YELLOW + f"\nWARNING: 'Best Hashrate' settings are often at the thermal/stability limit." + RESET) + print(YELLOW + f"Running these settings 24/7 may reduce hardware lifespan." + RESET) + print(YELLOW + f"Applying 'Most Efficient' settings is generally safer for long-term use." + RESET) + + # For now, we still default to applying the "Best Hashrate" settings as per original behavior, + # but we've added the warning. + print(GREEN + f"\nApplying Best Hashrate settings..." + RESET) set_system_settings(best_voltage, best_frequency) restart_system() @@ -525,8 +563,19 @@ def reset_to_best_setting(): retry_upon_overheat = 0 while current_voltage <= max_allowed_voltage and current_frequency <= max_allowed_frequency: - set_system_settings(current_voltage, current_frequency) - avg_hashrate, hashrate_stdev, avg_temp, efficiency_jth, hashrate_ok, avg_vr_temp, avg_power, avg_fan_speed, error_reason = benchmark_iteration(current_voltage, current_frequency) + if not set_system_settings(current_voltage, current_frequency): + # If stabilization failed (e.g. overheat during boot), treat as a failed iteration + avg_hashrate = None + hashrate_stdev = None + avg_temp = None + efficiency_jth = None + hashrate_ok = False + avg_vr_temp = None + avg_power = None + avg_fan_speed = None + error_reason = "STABILIZATION_FAILED" + else: + avg_hashrate, hashrate_stdev, avg_temp, efficiency_jth, hashrate_ok, avg_vr_temp, avg_power, avg_fan_speed, error_reason = benchmark_iteration(current_voltage, current_frequency) if avg_hashrate is not None and avg_temp is not None and efficiency_jth is not None: retry_upon_overheat = 0 From 49d7b216b8727f73075f004687b17656701a6b24 Mon Sep 17 00:00:00 2001 From: Salih Emin <914656+cerebrux@users.noreply.github.com> Date: Tue, 30 Dec 2025 15:17:22 +0200 Subject: [PATCH 20/26] Fix potential frequency underflow when adjusting settings --- bitaxe_hashrate_benchmark.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index 6ba06ca..5889108 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -612,8 +612,14 @@ def reset_to_best_setting(): # If hashrate is not good, go back one frequency step and increase voltage if current_voltage + voltage_increment <= max_allowed_voltage: current_voltage += voltage_increment - current_frequency -= frequency_increment # Go back to one frequency step and retry - print(YELLOW + f"Hashrate too low compared to expected. Decreasing frequency to {current_frequency}MHz and increasing voltage to {current_voltage}mV" + RESET) + + # Decrease frequency but respect the minimum limit + if current_frequency - frequency_increment >= min_allowed_frequency: + current_frequency -= frequency_increment + else: + current_frequency = min_allowed_frequency + + print(YELLOW + f"Hashrate too low compared to expected. Adjusting to {current_frequency}MHz and increasing voltage to {current_voltage}mV" + RESET) else: print(YELLOW + "Reached max voltage without good results. Stopping further testing." + RESET) break # We've reached max voltage without good results From cfdaa1af19dbcafce60327df3d683f8463d6a9d0 Mon Sep 17 00:00:00 2001 From: Salih Emin <914656+cerebrux@users.noreply.github.com> Date: Tue, 30 Dec 2025 15:22:18 +0200 Subject: [PATCH 21/26] Prevent voltage increase after overheat at initial frequency --- bitaxe_hashrate_benchmark.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index 5889108..739ae6d 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -627,6 +627,13 @@ def reset_to_best_setting(): # If we hit thermal limits or other issues, we've found the highest safe settings # In case of max Chip Temperature reached, continue loop to next voltage with decreased frequency # Condition added to avoid successive overheat tries and reset to high initial frequency + + # CRITICAL SAFETY CHECK: If we overheated at the INITIAL frequency, do NOT increase voltage. + # Increasing voltage will only make it hotter. We should stop or decrease frequency. + if error_reason == "CHIP_TEMP_EXCEEDED" and current_frequency == initial_frequency: + print(RED + "Overheated at initial frequency! Cannot increase voltage safely. Stopping." + RESET) + break + overheat_retry_allowed = ( error_reason == "CHIP_TEMP_EXCEEDED" and retry_upon_overheat < 1 From 80f6153ec04c9f0427151987964670915bfb8310 Mon Sep 17 00:00:00 2001 From: Salih Emin <914656+cerebrux@users.noreply.github.com> Date: Tue, 30 Dec 2025 16:32:48 +0200 Subject: [PATCH 22/26] Add max-temp argument to command line options and update configuration --- bitaxe_hashrate_benchmark.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index 739ae6d..a27b9d7 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -98,6 +98,14 @@ def parse_arguments(): " frequency (-f) settings to the Bitaxe and then exit." ) + parser.add_argument( + '--max-temp', + type=int, + default=66, + help=f"{YELLOW}Maximum chip temperature in °C (default: 66).{RESET}\n" + " The benchmark will stop if this temperature is exceeded." + ) + # If no arguments are provided, print help and exit if len(sys.argv) == 1: parser.print_help() @@ -117,7 +125,7 @@ def parse_arguments(): sleep_time = 90 # Wait 90 seconds before starting the benchmark benchmark_time = 600 # 10 minutes benchmark time sample_interval = 15 # 15 seconds sample interval -max_temp = 66 # Will stop if temperature reaches or exceeds this value +max_temp = args.max_temp # Will stop if temperature reaches or exceeds this value max_allowed_voltage = 1400 # Maximum allowed core voltage max_allowed_frequency = 1200 # Maximum allowed core frequency max_vr_temp = 86 # Maximum allowed voltage regulator temperature @@ -533,7 +541,7 @@ def reset_to_best_setting(): print(GREEN + f"\nApplying Best Hashrate settings..." + RESET) set_system_settings(best_voltage, best_frequency) - restart_system() + # restart_system() is already called inside set_system_settings, so we don't need to call it again here. # --- Main execution logic --- if args.set_values: From eda6922f0ae1cab04ff65c4e6999acf03af4ada0 Mon Sep 17 00:00:00 2001 From: Salih Emin <914656+cerebrux@users.noreply.github.com> Date: Tue, 30 Dec 2025 18:06:47 +0200 Subject: [PATCH 23/26] Add VR temperature check during stabilization to enhance safety --- bitaxe_hashrate_benchmark.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index a27b9d7..2b977e9 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -332,10 +332,15 @@ def restart_system(): if info: temp = info.get("temp") power = info.get("power") + vr_temp = info.get("vrTemp") if temp and temp >= max_temp: print(RED + f"CRITICAL: Chip temperature {temp}°C exceeded limit {max_temp}°C during stabilization!" + RESET) return False + + if vr_temp and vr_temp >= max_vr_temp: + print(RED + f"CRITICAL: VR temperature {vr_temp}°C exceeded limit {max_vr_temp}°C during stabilization!" + RESET) + return False if power and power > max_power: print(RED + f"CRITICAL: Power {power}W exceeded limit {max_power}W during stabilization!" + RESET) From b07d06e3846c239a92339abd29a7d78b5fae247d Mon Sep 17 00:00:00 2001 From: Salih Emin <914656+cerebrux@users.noreply.github.com> Date: Wed, 31 Dec 2025 02:10:35 +0200 Subject: [PATCH 24/26] Remove redundant restart_system calls in exception handling --- bitaxe_hashrate_benchmark.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index 2b977e9..df7de9a 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -676,7 +676,6 @@ def reset_to_best_setting(): else: print(YELLOW + "No valid benchmarking results found. Applying predefined default settings." + RESET) set_system_settings(default_voltage, default_frequency) - restart_system() finally: if not system_reset_done: if results: @@ -686,7 +685,6 @@ def reset_to_best_setting(): else: print(YELLOW + "No valid benchmarking results found. Applying predefined default settings." + RESET) set_system_settings(default_voltage, default_frequency) - restart_system() system_reset_done = True # Print results summary only if we have results From e280cbe7f838bd7c096a96094c4371a48df2b033 Mon Sep 17 00:00:00 2001 From: Salih Emin <914656+cerebrux@users.noreply.github.com> Date: Wed, 31 Dec 2025 02:15:57 +0200 Subject: [PATCH 25/26] enforce args for set-values, validate IP, check return values, and cleanup --- README.md | 2 +- bitaxe_hashrate_benchmark.py | 76 +++++++++++++++++------------------- 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 88dbf90..87e6144 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A Python-based benchmarking tool for optimizing Bitaxe mining performance by tes * **Direct setting of specific voltage and frequency from command line** * Temperature monitoring and safety cutoffs * **Power consumption monitoring and reporting (Watts)** -* **Fan speed monitoring and reporting (RPM/Percentage)** +* **Fan speed monitoring and reporting (Percentage)** * Power efficiency calculations (J/TH) * Automatic saving of benchmark results * Graceful shutdown with best settings retention diff --git a/bitaxe_hashrate_benchmark.py b/bitaxe_hashrate_benchmark.py index df7de9a..3c2303f 100644 --- a/bitaxe_hashrate_benchmark.py +++ b/bitaxe_hashrate_benchmark.py @@ -8,10 +8,6 @@ from datetime import datetime START_TIME = datetime.now().strftime("%Y-%m-%d_%H") - -if 'START_TIME' not in globals(): - START_TIME = datetime.now().strftime("%Y-%m-%d_%H") - # Enable ansi escape characters in terminal - colors were not working in Windows terminal import os try: @@ -75,18 +71,18 @@ def parse_arguments(): parser.add_argument( '-v', '--voltage', type=int, - default=1150, # Default value for benchmark start or target setting + default=None, # Default is None to allow validation logic help=f"{YELLOW}Core voltage in mV.{RESET}\n" - " For benchmark mode: The starting voltage for testing.\n" - " For --set-values mode: The exact voltage to apply." + " For benchmark mode: The starting voltage for testing (default: 1150).\n" + " For --set-values mode: The exact voltage to apply (Required)." ) parser.add_argument( '-f', '--frequency', type=int, - default=500, # Default value for benchmark start or target setting + default=None, # Default is None to allow validation logic help=f"{YELLOW}Core frequency in MHz.{RESET}\n" - " For benchmark mode: The starting frequency for testing.\n" - " For --set-values mode: The exact frequency to apply." + " For benchmark mode: The starting frequency for testing (default: 500).\n" + " For --set-values mode: The exact frequency to apply (Required)." ) # New argument for setting values only @@ -115,9 +111,25 @@ def parse_arguments(): # Replace the configuration section args = parse_arguments() + +# Validation for bitaxe_ip +if args.bitaxe_ip is None: + print(RED + "Error: Bitaxe IP address is required." + RESET) + sys.exit(1) + bitaxe_ip = f"http://{args.bitaxe_ip}" -initial_voltage = args.voltage -initial_frequency = args.frequency + +# Logic for voltage and frequency defaults/requirements +if args.set_values: + if args.voltage is None or args.frequency is None: + print(RED + "Error: --set-values mode requires both -v/--voltage and -f/--frequency." + RESET) + sys.exit(1) + initial_voltage = args.voltage + initial_frequency = args.frequency +else: + # Benchmark mode defaults + initial_voltage = args.voltage if args.voltage is not None else 1150 + initial_frequency = args.frequency if args.frequency is not None else 500 # Configuration voltage_increment = 15 @@ -533,17 +545,17 @@ def reset_to_best_setting(): eff_voltage = efficient_result["coreVoltage"] eff_frequency = efficient_result["frequency"] - print(GREEN + f"\n--- Benchmark Complete ---" + RESET) + print(GREEN + "\n--- Benchmark Complete ---" + RESET) print(GREEN + f"Best Hashrate Settings: {best_voltage}mV / {best_frequency}MHz ({best_result['averageHashRate']:.2f} GH/s)" + RESET) print(GREEN + f"Most Efficient Settings: {eff_voltage}mV / {eff_frequency}MHz ({efficient_result['efficiencyJTH']:.2f} J/TH)" + RESET) - print(YELLOW + f"\nWARNING: 'Best Hashrate' settings are often at the thermal/stability limit." + RESET) - print(YELLOW + f"Running these settings 24/7 may reduce hardware lifespan." + RESET) - print(YELLOW + f"Applying 'Most Efficient' settings is generally safer for long-term use." + RESET) + print(YELLOW + "\nWARNING: 'Best Hashrate' settings are often at the thermal/stability limit." + RESET) + print(YELLOW + "Running these settings 24/7 may reduce hardware lifespan." + RESET) + print(YELLOW + "Applying 'Most Efficient' settings is generally safer for long-term use." + RESET) # For now, we still default to applying the "Best Hashrate" settings as per original behavior, # but we've added the warning. - print(GREEN + f"\nApplying Best Hashrate settings..." + RESET) + print(GREEN + "\nApplying Best Hashrate settings..." + RESET) set_system_settings(best_voltage, best_frequency) # restart_system() is already called inside set_system_settings, so we don't need to call it again here. @@ -554,10 +566,12 @@ def reset_to_best_setting(): print(GREEN + f"Applying Core Voltage: {initial_voltage}mV, Frequency: {initial_frequency}MHz to Bitaxe." + RESET) # Call the existing set_system_settings function - set_system_settings(initial_voltage, initial_frequency) - - print(GREEN + "Settings applied. Check your Bitaxe web interface to confirm." + RESET) - sys.exit(0) # Exit the script after applying settings + if set_system_settings(initial_voltage, initial_frequency): + print(GREEN + "Settings applied. Check your Bitaxe web interface to confirm." + RESET) + sys.exit(0) + else: + print(RED + "Failed to apply settings or stabilization failed. Check your Bitaxe." + RESET) + sys.exit(1) # Main benchmarking process try: @@ -768,24 +782,4 @@ def reset_to_best_setting(): else: print(RED + "No valid results were found during benchmarking." + RESET) -# Add this new function to handle cleanup -def cleanup_and_exit(reason=None): - global system_reset_done - if system_reset_done: - return - - try: - if results: - reset_to_best_setting() - save_results() - print(GREEN + "Bitaxe reset to best settings and results saved." + RESET) - else: - print(YELLOW + "No valid benchmarking results found. Applying predefined default settings." + RESET) - set_system_settings(default_voltage, default_frequency) - finally: - system_reset_done = True - if reason: - print(RED + f"Benchmarking stopped: {reason}" + RESET) - print(GREEN + "Benchmarking completed." + RESET) - sys.exit(0) From af906ffdb0e38dc18269e937cd0c794f85cc05d3 Mon Sep 17 00:00:00 2001 From: Salih Emin <914656+cerebrux@users.noreply.github.com> Date: Wed, 31 Dec 2025 02:25:17 +0200 Subject: [PATCH 26/26] removing unnecessary bold styling for features and output descriptions --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 87e6144..3c0fa7f 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ A Python-based benchmarking tool for optimizing Bitaxe mining performance by tes ## **Features** * Automated benchmarking of different voltage/frequency combinations -* **Direct setting of specific voltage and frequency from command line** +* Direct setting of specific voltage and frequency from command line * Temperature monitoring and safety cutoffs -* **Power consumption monitoring and reporting (Watts)** -* **Fan speed monitoring and reporting (Percentage)** +* Power consumption monitoring and reporting (Watts) +* Fan speed monitoring and reporting (Percentage) * Power efficiency calculations (J/TH) * Automatic saving of benchmark results * Graceful shutdown with best settings retention @@ -125,7 +125,7 @@ The script includes several configurable parameters. These can be adjusted in th * Minimum required samples: 7 (for valid data processing) * Voltage increment: 15mV * Frequency increment: 20MHz -* **ASIC Configuration:** asic_count is hardcoded to 1 as it's not always provided by the API. small_core_count is fetched from the Bitaxe. +* ASIC Configuration: asic_count is hardcoded to 1 as it's not always provided by the API. small_core_count is fetched from the Bitaxe. ## **Output** @@ -139,8 +139,8 @@ The benchmark results are saved to bitaxe_benchmark_results_.json, c * Temperature readings (excluding initial warmup period) * VR temperature readings (when available) * Power efficiency metrics (J/TH) - * **Average Power (Watts)** - * **Average Fan Speed (Percentage or RPM, if available from API)** + * Average Power (Watts) + * Average Fan Speed (Percentage or RPM, if available from API) * Input voltage measurements * Voltage/frequency combinations tested * Error reason (if any) for a specific iteration @@ -185,7 +185,7 @@ The tool implements several data processing techniques to ensure accurate result * Averages power consumption across entire test period * Monitors VR temperature when available * Calculates efficiency in Joules per Terahash (J/TH) -* **Averages fan speed across entire test period** +* Averages fan speed across entire test period ## **Contributing**