-
-
-
Best Difficulty
-
-
- {{info.bestDiff | diffSuffix}}
-
- all-time best
-
-
-
-
-
+
Best Difficulty
+
+
+ {{info.bestDiff | diffSuffix}}
+
+ all-time best
+
+
+
-
-
+
+
{{info.bestSessionDiff | diffSuffix}}
- session best
- ({{ info.uptimeSeconds | dateAgo: { intervals: 2, strict:true, short:true } }})
+ session best
+ ({{ info.uptimeSeconds | dateAgo: { intervals: 2, strict:true, short:true } }})
+
+
+
+ {{info.totalLog2Work | number:'1.6-6'}}
+
+
+
+
diff --git a/main/http_server/axe-os/src/app/components/system/system.component.ts b/main/http_server/axe-os/src/app/components/system/system.component.ts
index f7c13e597..6791c47b6 100644
--- a/main/http_server/axe-os/src/app/components/system/system.component.ts
+++ b/main/http_server/axe-os/src/app/components/system/system.component.ts
@@ -7,6 +7,7 @@ import { LoadingService } from 'src/app/services/loading.service';
import { DateAgoPipe } from 'src/app/pipes/date-ago.pipe';
import { ByteSuffixPipe } from 'src/app/pipes/byte-suffix.pipe';
import { SystemInfo as ISystemInfo, SystemASIC as ISystemASIC, GenericResponse, } from 'src/app/generated';
+import { formatNumber } from '@angular/common';
type TableRow = {
label: string;
@@ -89,6 +90,9 @@ export class SystemComponent implements OnInit, OnDestroy {
{ label: 'Board Version', value: data.info.boardVersion },
{ label: 'ASIC Type', value: (data.asic.asicCount > 1 ? data.asic.asicCount + 'x ' : ' ') + data.asic.ASICModel, class: 'pb-3' },
{ label: 'Uptime', value: DateAgoPipe.transform(data.info.uptimeSeconds) },
+ { label: 'Total Uptime', value: DateAgoPipe.transform(data.info.totalUptimeSeconds || 0) },
+ { label: 'Total Work (log2)', value: (data.info.totalLog2Work || 0).toFixed(6) },
+ { label: 'Total Hashes', value: formatNumber(data.info.totalHashes || 0, 'en-us')},
{ label: 'Reset Reason', value: data.info.resetReason, class: 'pb-3' },
{ label: 'Wi-Fi SSID', value: data.info.ssid, isSensitiveData: true },
{ label: 'Wi-Fi Status', value: data.info.wifiStatus },
diff --git a/main/http_server/http_server.c b/main/http_server/http_server.c
index d8bc92070..cc12fb777 100644
--- a/main/http_server/http_server.c
+++ b/main/http_server/http_server.c
@@ -903,7 +903,10 @@ static esp_err_t GET_system_info(httpd_req_t * req)
cJSON_AddItemToArray(error_array, error_obj);
}
- cJSON_AddNumberToObject(root, "uptimeSeconds", (esp_timer_get_time() - GLOBAL_STATE->SYSTEM_MODULE.start_time) / 1000000);
+ cJSON_AddNumberToObject(root, "uptimeSeconds", GLOBAL_STATE->SYSTEM_MODULE.uptime_seconds);
+ cJSON_AddNumberToObject(root, "totalUptimeSeconds", SYSTEM_noinit_get_total_uptime_seconds());
+ cJSON_AddNumberToObject(root, "totalHashes", SYSTEM_noinit_get_total_hashes());
+ cJSON_AddNumberToObject(root, "totalLog2Work", SYSTEM_noinit_get_total_log2_work());
cJSON_AddNumberToObject(root, "smallCoreCount", GLOBAL_STATE->DEVICE_CONFIG.family.asic.small_core_count);
cJSON_AddStringToObject(root, "ASICModel", GLOBAL_STATE->DEVICE_CONFIG.family.asic.name);
cJSON_AddStringToObject(root, "stratumURL", stratumURL);
diff --git a/main/http_server/openapi.yaml b/main/http_server/openapi.yaml
index ffcb46706..d29ed96d0 100644
--- a/main/http_server/openapi.yaml
+++ b/main/http_server/openapi.yaml
@@ -360,6 +360,15 @@ components:
sharesRejected:
type: number
description: Number of rejected shares
+ totalUptimeSeconds:
+ type: number
+ description: Total uptime in seconds
+ totalHashes:
+ type: number
+ description: Total hashes done by the device
+ totalLog2Work:
+ type: number
+ description: Log2 of total hashes
sharesRejectedReasons:
type: array
description: Reason(s) shares were rejected
diff --git a/main/nvs_config.c b/main/nvs_config.c
index af83e6bc5..5673616cf 100644
--- a/main/nvs_config.c
+++ b/main/nvs_config.c
@@ -109,6 +109,9 @@ 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_TOTAL_UPTIME] = {.nvs_key_name = "total_uptime", .type = TYPE_U64},
+ [NVS_CONFIG_CUMULATIVE_HASHES_HIGH] = {.nvs_key_name = "cum_hashes_hi", .type = TYPE_U64},
+ [NVS_CONFIG_CUMULATIVE_HASHES_LOW] = {.nvs_key_name = "cum_hashes_lo", .type = TYPE_U64},
};
Settings *nvs_config_get_settings(NvsConfigKey key)
diff --git a/main/nvs_config.h b/main/nvs_config.h
index cb4ab646c..6589d93a1 100644
--- a/main/nvs_config.h
+++ b/main/nvs_config.h
@@ -72,6 +72,11 @@ typedef enum {
NVS_CONFIG_TPS546,
NVS_CONFIG_TMP1075,
NVS_CONFIG_POWER_CONSUMPTION_TARGET,
+
+ NVS_CONFIG_TOTAL_UPTIME,
+ NVS_CONFIG_CUMULATIVE_HASHES_HIGH,
+ NVS_CONFIG_CUMULATIVE_HASHES_LOW,
+
NVS_CONFIG_COUNT
} NvsConfigKey;
diff --git a/main/screen.c b/main/screen.c
index b820aeb39..185526913 100644
--- a/main/screen.c
+++ b/main/screen.c
@@ -83,6 +83,7 @@ static float current_hashrate;
static float current_power;
static uint64_t current_difficulty;
static float current_chip_temp;
+static uint32_t current_uptime_seconds;
#define NOTIFICATION_SHARE_ACCEPTED (1 << 0)
#define NOTIFICATION_SHARE_REJECTED (1 << 1)
@@ -647,26 +648,26 @@ static void uptime_update_cb(lv_timer_t * timer)
{
if (wifi_uptime_label) {
char uptime[50];
- uint32_t uptime_seconds = (esp_timer_get_time() - GLOBAL_STATE->SYSTEM_MODULE.start_time) / 1000000;
-
- uint32_t days = uptime_seconds / (24 * 3600);
- uptime_seconds %= (24 * 3600);
- uint32_t hours = uptime_seconds / 3600;
- uptime_seconds %= 3600;
- uint32_t minutes = uptime_seconds / 60;
- uptime_seconds %= 60;
-
- if (days > 0) {
- snprintf(uptime, sizeof(uptime), "Uptime: %ldd %ldh %ldm %lds", days, hours, minutes, uptime_seconds);
- } else if (hours > 0) {
- snprintf(uptime, sizeof(uptime), "Uptime: %ldh %ldm %lds", hours, minutes, uptime_seconds);
- } else if (minutes > 0) {
- snprintf(uptime, sizeof(uptime), "Uptime: %ldm %lds", minutes, uptime_seconds);
- } else {
- snprintf(uptime, sizeof(uptime), "Uptime: %lds", uptime_seconds);
- }
-
- if (strcmp(lv_label_get_text(wifi_uptime_label), uptime) != 0) {
+ uint32_t uptime_seconds = (esp_timer_get_time() - GLOBAL_STATE->SYSTEM_MODULE.start_time_us) / 1000000;
+ if (current_uptime_seconds != uptime_seconds) {
+ current_uptime_seconds = uptime_seconds;
+ uint32_t days = uptime_seconds / (24 * 3600);
+ uptime_seconds %= (24 * 3600);
+ uint32_t hours = uptime_seconds / 3600;
+ uptime_seconds %= 3600;
+ uint32_t minutes = uptime_seconds / 60;
+ uptime_seconds %= 60;
+
+ if (days > 0) {
+ snprintf(uptime, sizeof(uptime), "Uptime: %ldd %ldh %ldm %lds", days, hours, minutes, uptime_seconds);
+ } else if (hours > 0) {
+ snprintf(uptime, sizeof(uptime), "Uptime: %ldh %ldm %lds", hours, minutes, uptime_seconds);
+ } else if (minutes > 0) {
+ snprintf(uptime, sizeof(uptime), "Uptime: %ldm %lds", minutes, uptime_seconds);
+ } else {
+ snprintf(uptime, sizeof(uptime), "Uptime: %lds", uptime_seconds);
+ }
+
lv_label_set_text(wifi_uptime_label, uptime);
}
}
diff --git a/main/system.c b/main/system.c
index daee2ddfd..3f95ab4b3 100644
--- a/main/system.c
+++ b/main/system.c
@@ -11,6 +11,7 @@
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_check.h"
+#include "esp_attr.h"
#include "driver/gpio.h"
#include "esp_app_desc.h"
@@ -19,7 +20,7 @@
#include "lwip/inet.h"
#include "system.h"
-#include "i2c_bitaxe.h"
+#include "global_state.h"
#include "INA260.h"
#include "adc.h"
#include "connect.h"
@@ -31,6 +32,22 @@
#include "thermal.h"
#include "utils.h"
+#define NVS_COUNTER_UPDATE_INTERVAL_MS 60 * 60 * 1000 // Update NVS once per hour
+#define NOINIT_SENTINEL_VALUE 0x4C4F4732 // "LOG2" in hex
+
+typedef struct
+{
+ uint64_t total_uptime; // Total uptime in seconds
+ uint64_t cumulative_hashes_high; // High 64 bits of 128-bit cumulative hash count
+ uint64_t cumulative_hashes_low; // Low 64 bits of 128-bit cumulative hash count
+ uint32_t sentinel; // Magic value to detect valid noinit data
+} NoinitState;
+
+__NOINIT_ATTR static NoinitState noinit_state; // Noinit state survives soft reboots but is lost on power cycle
+static uint64_t last_update_time_ms;
+static uint64_t last_nvs_write_time_ms;
+static uint64_t total_uptime_at_system_start;
+
static const char * TAG = "system";
//local function prototypes
@@ -45,11 +62,35 @@ void SYSTEM_init_system(GlobalState * GLOBAL_STATE)
module->shares_rejected = 0;
module->best_nonce_diff = nvs_config_get_u64(NVS_CONFIG_BEST_DIFF);
module->best_session_nonce_diff = 0;
- module->start_time = esp_timer_get_time();
+ module->start_time_us = esp_timer_get_time();
module->lastClockSync = 0;
module->block_found = 0;
module->show_new_block = false;
+ if (noinit_state.sentinel != NOINIT_SENTINEL_VALUE) {
+ noinit_state.sentinel = NOINIT_SENTINEL_VALUE;
+ noinit_state.total_uptime = 0;
+ noinit_state.cumulative_hashes_high = 0;
+ noinit_state.cumulative_hashes_low = 0;
+ }
+
+ // Load values from NVS (persist across power cycle)
+ uint64_t nvs_total_uptime = nvs_config_get_u64(NVS_CONFIG_TOTAL_UPTIME);
+ if (nvs_total_uptime > noinit_state.total_uptime) {
+ noinit_state.total_uptime = nvs_total_uptime;
+ }
+ total_uptime_at_system_start = noinit_state.total_uptime;
+
+ uint64_t nvs_hashes_high = nvs_config_get_u64(NVS_CONFIG_CUMULATIVE_HASHES_HIGH);
+ uint64_t nvs_hashes_low = nvs_config_get_u64(NVS_CONFIG_CUMULATIVE_HASHES_LOW);
+ if (nvs_hashes_high > noinit_state.cumulative_hashes_high) {
+ noinit_state.cumulative_hashes_high = nvs_hashes_high;
+ noinit_state.cumulative_hashes_low = nvs_hashes_low;
+ } else if (nvs_hashes_high == noinit_state.cumulative_hashes_high &&
+ nvs_hashes_low > noinit_state.cumulative_hashes_low) {
+ noinit_state.cumulative_hashes_low = nvs_hashes_low;
+ }
+
// Initialize network address strings
strcpy(module->ip_addr_str, "");
strcpy(module->ipv6_addr_str, "");
@@ -115,7 +156,8 @@ void SYSTEM_init_system(GlobalState * GLOBAL_STATE)
pthread_mutex_init(&GLOBAL_STATE->valid_jobs_lock, NULL);
}
-void SYSTEM_init_versions(GlobalState * GLOBAL_STATE) {
+void SYSTEM_init_versions(GlobalState * GLOBAL_STATE)
+{
const esp_app_desc_t *app_desc = esp_app_get_description();
// Store the firmware version
@@ -276,3 +318,72 @@ static esp_err_t ensure_overheat_mode_config() {
return ESP_OK;
}
+
+void SYSTEM_noinit_update(SystemModule * SYSTEM_MODULE)
+{
+ uint64_t current_time_ms = esp_timer_get_time() / 1000;
+
+ // Initialize last_update_time on first call
+ if (last_update_time_ms == 0) {
+ last_update_time_ms = current_time_ms;
+ last_nvs_write_time_ms = current_time_ms;
+ return;
+ }
+
+ uint64_t elapsed_ms = current_time_ms - last_update_time_ms;
+ last_update_time_ms = current_time_ms;
+
+ // Only update if at least 1 second has passed
+ if (elapsed_ms < 1000) {
+ return;
+ }
+
+ SYSTEM_MODULE->uptime_seconds = (esp_timer_get_time() - SYSTEM_MODULE->start_time_us) / 1000000;
+ noinit_state.total_uptime = total_uptime_at_system_start + SYSTEM_MODULE->uptime_seconds;
+
+ // Update cumulative hashes: hashrate (GH/s) × milliseconds × 1e6 = raw hashes
+ uint64_t hashes_done = elapsed_ms * 1e6 * SYSTEM_MODULE->current_hashrate;
+ uint64_t new_low = noinit_state.cumulative_hashes_low + hashes_done;
+ if (new_low < noinit_state.cumulative_hashes_low) {
+ noinit_state.cumulative_hashes_high++;
+ }
+ noinit_state.cumulative_hashes_low = new_low;
+
+ // Persist to NVS once per hour to reduce wear
+ if (current_time_ms - last_nvs_write_time_ms >= NVS_COUNTER_UPDATE_INTERVAL_MS) {
+ nvs_config_set_u64(NVS_CONFIG_TOTAL_UPTIME, noinit_state.total_uptime);
+ nvs_config_set_u64(NVS_CONFIG_CUMULATIVE_HASHES_HIGH, noinit_state.cumulative_hashes_high);
+ nvs_config_set_u64(NVS_CONFIG_CUMULATIVE_HASHES_LOW, noinit_state.cumulative_hashes_low);
+ last_nvs_write_time_ms = current_time_ms;
+ }
+}
+
+uint64_t SYSTEM_noinit_get_total_uptime_seconds()
+{
+ return noinit_state.total_uptime;
+}
+
+// Convert 128-bit to double: high * 2^64 + low. Loses precision for very large values, but sufficient for display
+double SYSTEM_noinit_get_total_hashes()
+{
+ return (double)noinit_state.cumulative_hashes_high * 18446744073709551616.0 + (double)noinit_state.cumulative_hashes_low;
+}
+
+double SYSTEM_noinit_get_total_log2_work()
+{
+ // If high part is 0, just compute log2 of low part
+ if (noinit_state.cumulative_hashes_high == 0) {
+ if (noinit_state.cumulative_hashes_low == 0) {
+ return 0.0;
+ }
+ return log2((double)noinit_state.cumulative_hashes_low);
+ }
+
+ // For 128-bit value: log2(high * 2^64 + low) = 64 + log2(high + low/2^64)
+ // Since low/2^64 is very small compared to high, we approximate:
+ // log2(high * 2^64 + low) ≈ 64 + log2(high) for large values
+ // More precise: 64 + log2(high + low/2^64)
+ double high_plus_fraction = (double)noinit_state.cumulative_hashes_high +
+ (double)noinit_state.cumulative_hashes_low / 18446744073709551616.0;
+ return 64.0 + log2(high_plus_fraction);
+}
diff --git a/main/system.h b/main/system.h
index 57040cecf..0207b922f 100644
--- a/main/system.h
+++ b/main/system.h
@@ -13,4 +13,9 @@ void SYSTEM_notify_rejected_share(GlobalState * GLOBAL_STATE, char * error_msg);
void SYSTEM_notify_found_nonce(GlobalState * GLOBAL_STATE, double diff, uint8_t job_id);
void SYSTEM_notify_new_ntime(GlobalState * GLOBAL_STATE, uint32_t ntime);
+void SYSTEM_noinit_update(SystemModule * SYSTEM_MODULE);
+uint64_t SYSTEM_noinit_get_total_uptime_seconds();
+double SYSTEM_noinit_get_total_hashes();
+double SYSTEM_noinit_get_total_log2_work();
+
#endif /* SYSTEM_H_ */
diff --git a/main/tasks/create_jobs_task.c b/main/tasks/create_jobs_task.c
index d258138cd..9a510e210 100644
--- a/main/tasks/create_jobs_task.c
+++ b/main/tasks/create_jobs_task.c
@@ -38,9 +38,9 @@ void create_jobs_task(void *pvParameters)
ESP_LOGI(TAG, "ASIC Ready!");
while (1) {
- uint64_t start_time = esp_timer_get_time();
+ uint64_t start_time_us = esp_timer_get_time();
mining_notify *new_mining_notification = (mining_notify *)queue_dequeue_timeout(&GLOBAL_STATE->stratum_queue, timeout_ms);
- timeout_ms -= (esp_timer_get_time() - start_time) / 1000;
+ timeout_ms -= (esp_timer_get_time() - start_time_us) / 1000;
if (new_mining_notification != NULL) {
if (current_mining_notification != NULL) {
diff --git a/main/tasks/hashrate_monitor_task.c b/main/tasks/hashrate_monitor_task.c
index cb253a5cd..3c38085ed 100644
--- a/main/tasks/hashrate_monitor_task.c
+++ b/main/tasks/hashrate_monitor_task.c
@@ -167,6 +167,8 @@ void hashrate_monitor_task(void *pvParameters)
if(current_hashrate > 0.0f) update_hashrate_averages(SYSTEM_MODULE);
+ SYSTEM_noinit_update(SYSTEM_MODULE);
+
vTaskDelayUntil(&taskWakeTime, POLL_RATE / portTICK_PERIOD_MS);
}
}