diff --git a/main/global_state.h b/main/global_state.h index 72e13113d..046c252c9 100644 --- a/main/global_state.h +++ b/main/global_state.h @@ -34,7 +34,7 @@ typedef struct float hashrate_10m; float hashrate_1h; float error_percentage; - int64_t start_time; + int64_t start_time_us; uint64_t shares_accepted; uint64_t shares_rejected; uint64_t work_received; @@ -89,6 +89,7 @@ typedef struct char * asic_status; char * version; char * axeOSVersion; + uint64_t uptime_seconds; } SystemModule; typedef struct diff --git a/main/http_server/axe-os/src/app/components/home/home.component.html b/main/http_server/axe-os/src/app/components/home/home.component.html index 8aa0551e0..0e9762f05 100644 --- a/main/http_server/axe-os/src/app/components/home/home.component.html +++ b/main/http_server/axe-os/src/app/components/home/home.component.html @@ -28,7 +28,7 @@
- Hashrate + Hashrate
@@ -85,10 +85,10 @@
- Efficiency + Efficiency -
+
{{getEfficiency(info) | number: '1.2-2'}} J/Th @@ -124,13 +124,9 @@
-
-
- Shares -
- {{info.sharesAccepted | number: '1.0-0'}} -
-
+
Shares
+
+ {{info.sharesAccepted | number: '1.0-0'}}
0 @@ -155,30 +151,38 @@
-
-
- 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); } }