diff --git a/components/asic/asic.c b/components/asic/asic.c index 202fa95..2eea522 100644 --- a/components/asic/asic.c +++ b/components/asic/asic.c @@ -19,6 +19,16 @@ uint8_t ASIC_init(GlobalState * GLOBAL_STATE) { return ESP_OK; } +void ASIC_hold_reset_low(GlobalState * GLOBAL_STATE) { + switch (GLOBAL_STATE->device_model) { + case BITFORGE_NANO: + BM1370_hold_reset_low(); + break; + default: + break; + } +} + uint8_t ASIC_get_asic_count(GlobalState * GLOBAL_STATE) { switch (GLOBAL_STATE->device_model) { case BITFORGE_NANO: diff --git a/components/asic/bm1370.c b/components/asic/bm1370.c index 0edfe49..6c91322 100644 --- a/components/asic/bm1370.c +++ b/components/asic/bm1370.c @@ -414,6 +414,11 @@ static void _reset(void) vTaskDelay(100 / portTICK_PERIOD_MS); } +void BM1370_hold_reset_low(void) +{ + gpio_set_level(GPIO_ASIC_RESET, 0); +} + // static void _send_read_address(void) // { diff --git a/components/asic/include/asic.h b/components/asic/include/asic.h index e68c48a..e30dfec 100644 --- a/components/asic/include/asic.h +++ b/components/asic/include/asic.h @@ -8,6 +8,7 @@ #define BITFORGE_NANO_ASIC_COUNT 2 uint8_t ASIC_init(GlobalState * GLOBAL_STATE); +void ASIC_hold_reset_low(GlobalState * GLOBAL_STATE); uint8_t ASIC_get_asic_count(GlobalState * GLOBAL_STATE); uint16_t ASIC_get_small_core_count(GlobalState * GLOBAL_STATE); task_result * ASIC_process_work(GlobalState * GLOBAL_STATE); diff --git a/components/asic/include/bm1370.h b/components/asic/include/bm1370.h index fc7b9b0..0b4ecb7 100644 --- a/components/asic/include/bm1370.h +++ b/components/asic/include/bm1370.h @@ -38,6 +38,7 @@ typedef struct __attribute__((__packed__)) } BM1370_job; uint8_t BM1370_init(uint64_t frequency, uint16_t asic_count); +void BM1370_hold_reset_low(void); void BM1370_send_work(void * GLOBAL_STATE, bm_job * next_bm_job); void BM1370_set_job_difficulty_mask(int); void BM1370_set_version_mask(uint32_t version_mask); diff --git a/main/global_state.h b/main/global_state.h index 0a4d0fc..c821351 100644 --- a/main/global_state.h +++ b/main/global_state.h @@ -4,6 +4,7 @@ #include #include #include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "asic_task.h" #include "bm1370.h" #include "common.h" @@ -32,6 +33,14 @@ typedef enum ASIC_BM1370, } AsicModel; +typedef enum +{ + MINING_STATE_MINING = 0, + MINING_STATE_PAUSING, + MINING_STATE_PAUSED, + MINING_STATE_RESUMING, +} MiningState; + // typedef struct // { // uint8_t (*init_fn)(uint64_t, uint16_t); @@ -86,6 +95,8 @@ typedef struct bool is_using_fallback; char pool_connection_info[64]; uint16_t overheat_mode; + volatile bool mining_paused; + volatile MiningState mining_state; uint16_t power_fault; uint32_t lastClockSync; bool is_screen_active; @@ -144,6 +155,8 @@ typedef struct portMUX_TYPE stratum_mux; bool ASIC_initalized; + bool mining_control_ready; + TaskHandle_t power_management_task_handle; bool psram_is_available; } GlobalState; diff --git a/main/http_server/http_server.c b/main/http_server/http_server.c index 85d3402..4506120 100644 --- a/main/http_server/http_server.c +++ b/main/http_server/http_server.c @@ -718,6 +718,86 @@ static esp_err_t POST_restart(httpd_req_t * req) return ESP_OK; } +static const char * mining_state_name(MiningState state) +{ + switch (state) { + case MINING_STATE_PAUSING: + return "pausing"; + case MINING_STATE_PAUSED: + return "paused"; + case MINING_STATE_RESUMING: + return "resuming"; + case MINING_STATE_MINING: + default: + return "mining"; + } +} + +static esp_err_t set_mining_paused(httpd_req_t * req, bool paused) +{ + if (is_network_allowed(req) != ESP_OK) { + return httpd_resp_send_err(req, HTTPD_401_UNAUTHORIZED, "Unauthorized"); + } + + if (!GLOBAL_STATE->mining_control_ready) { + httpd_resp_set_status(req, "503 Service Unavailable"); + httpd_resp_set_type(req, "application/json"); + return httpd_resp_sendstr(req, "{\"message\":\"Mining subsystem is not ready\"}"); + } + + if (set_cors_headers(req) != ESP_OK) { + httpd_resp_send_500(req); + return ESP_OK; + } + + SystemModule * system = &GLOBAL_STATE->SYSTEM_MODULE; + system->mining_paused = paused; + if (paused) { + if (system->mining_state != MINING_STATE_PAUSED) { + system->mining_state = MINING_STATE_PAUSING; + } + } else if (system->mining_state != MINING_STATE_MINING) { + system->mining_state = MINING_STATE_RESUMING; + } + + if (GLOBAL_STATE->power_management_task_handle != NULL) { + xTaskNotifyGive(GLOBAL_STATE->power_management_task_handle); + } + + ESP_LOGI(TAG, "Mining %s by API request", paused ? "pause requested" : "resume requested"); + + cJSON * root = cJSON_CreateObject(); + if (root == NULL) { + return httpd_resp_send_500(req); + } + + cJSON_AddStringToObject(root, "message", + paused ? "Mining pause requested" : "Mining resume requested"); + cJSON_AddBoolToObject(root, "miningPaused", paused); + cJSON_AddStringToObject(root, "miningState", mining_state_name(system->mining_state)); + + char * response = cJSON_PrintUnformatted(root); + cJSON_Delete(root); + if (response == NULL) { + return httpd_resp_send_500(req); + } + + httpd_resp_set_type(req, "application/json"); + esp_err_t result = httpd_resp_sendstr(req, response); + free(response); + return result; +} + +static esp_err_t POST_mining_pause(httpd_req_t * req) +{ + return set_mining_paused(req, true); +} + +static esp_err_t POST_mining_resume(httpd_req_t * req) +{ + return set_mining_paused(req, false); +} + /* Simple handler for getting system handler */ static esp_err_t GET_system_info(httpd_req_t * req) { @@ -755,7 +835,7 @@ static esp_err_t GET_system_info(httpd_req_t * req) cJSON * root = cJSON_CreateObject(); cJSON_AddNumberToObject(root, "power", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.power); cJSON_AddNumberToObject(root, "voltage", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.voltage); - cJSON_AddNumberToObject(root, "current", Power_get_current(GLOBAL_STATE)); + cJSON_AddNumberToObject(root, "current", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.current); cJSON_AddNumberToObject(root, "temp", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp_avg); cJSON_AddNumberToObject(root, "vrTemp", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.vr_temp); cJSON_AddNumberToObject(root, "maxPower", Power_get_max_settings(GLOBAL_STATE)); @@ -821,6 +901,9 @@ static esp_err_t GET_system_info(httpd_req_t * req) cJSON_AddStringToObject(root, "runningPartition", esp_ota_get_running_partition()->label); cJSON_AddNumberToObject(root, "overheat_mode", nvs_config_get_u16(NVS_CONFIG_OVERHEAT_MODE, 0)); + cJSON_AddBoolToObject(root, "miningPaused", GLOBAL_STATE->SYSTEM_MODULE.mining_paused); + cJSON_AddStringToObject(root, "miningState", + mining_state_name(GLOBAL_STATE->SYSTEM_MODULE.mining_state)); cJSON_AddNumberToObject(root, "overclockEnabled", nvs_config_get_u16(NVS_CONFIG_OVERCLOCK_ENABLED, 0)); cJSON_AddNumberToObject(root, "autofanspeed", nvs_config_get_u16(NVS_CONFIG_AUTO_FAN_SPEED, 1)); @@ -1279,6 +1362,22 @@ esp_err_t start_rest_server(void * pvParameters) .uri = "/api/system/restart", .method = HTTP_OPTIONS, .handler = handle_options_request, .user_ctx = NULL}; httpd_register_uri_handler(server, &system_restart_options_uri); + httpd_uri_t system_mining_pause_uri = { + .uri = "/api/system/pause", .method = HTTP_POST, .handler = POST_mining_pause, .user_ctx = rest_context}; + httpd_register_uri_handler(server, &system_mining_pause_uri); + + httpd_uri_t system_mining_pause_options_uri = { + .uri = "/api/system/pause", .method = HTTP_OPTIONS, .handler = handle_options_request, .user_ctx = NULL}; + httpd_register_uri_handler(server, &system_mining_pause_options_uri); + + httpd_uri_t system_mining_resume_uri = { + .uri = "/api/system/resume", .method = HTTP_POST, .handler = POST_mining_resume, .user_ctx = rest_context}; + httpd_register_uri_handler(server, &system_mining_resume_uri); + + httpd_uri_t system_mining_resume_options_uri = { + .uri = "/api/system/resume", .method = HTTP_OPTIONS, .handler = handle_options_request, .user_ctx = NULL}; + httpd_register_uri_handler(server, &system_mining_resume_options_uri); + httpd_uri_t update_system_settings_uri = { .uri = "/api/system", .method = HTTP_PATCH, .handler = PATCH_update_settings, .user_ctx = rest_context}; httpd_register_uri_handler(server, &update_system_settings_uri); diff --git a/main/http_server/openapi.yaml b/main/http_server/openapi.yaml index 1ecb254..c92f521 100644 --- a/main/http_server/openapi.yaml +++ b/main/http_server/openapi.yaml @@ -99,6 +99,8 @@ components: - isPSRAMAvailable - isUsingFallbackStratum - macAddr + - miningPaused + - miningState - overheat_mode - power - runningPartition @@ -146,7 +148,7 @@ components: description: Actual measured ASIC core voltage current: type: number - description: Current draw in milliamps + description: Live board input current in milliamps, including while mining is paused fallbackStratumPort: type: number description: Fallback stratum server port @@ -158,7 +160,7 @@ components: description: Fallback stratum username fanrpm: type: number - description: Current fan speed in RPM + description: Current fan speed in RPM, including while mining is paused fanSpeed: type: number description: Current fan speed percentage @@ -195,12 +197,19 @@ components: macAddr: type: string description: Device MAC address + miningPaused: + type: boolean + description: Whether mining pause has been requested + miningState: + type: string + description: Current mining hardware transition state + enum: [mining, pausing, paused, resuming] overheat_mode: type: number description: Overheat protection mode power: type: number - description: Power consumption in watts + description: Live board input power in watts, including controller and fans while mining is paused runningPartition: type: string description: Currently active OTA partition @@ -230,7 +239,7 @@ components: description: Primary stratum username temp: type: number - description: Average chip temperature + description: Average ASIC temperature, or 0 while ASIC thermal diodes are unavailable during pause uptimeSeconds: type: number description: System uptime in seconds @@ -239,7 +248,7 @@ components: description: Firmware version voltage: type: number - description: Input voltage + description: Live board input voltage in millivolts, including while mining is paused vrTemp: type: number description: Voltage regulator temperature @@ -376,6 +385,48 @@ components: description: Internal server error paths: + /api/system/pause: + post: + summary: Pause mining while keeping the controller online + responses: + "200": + description: Pause request accepted + content: + application/json: + schema: + type: object + required: [message, miningPaused, miningState] + properties: + message: + type: string + miningPaused: + type: boolean + miningState: + type: string + enum: [mining, pausing, paused, resuming] + "503": + description: Mining subsystem is not ready + /api/system/resume: + post: + summary: Resume mining and reinitialize the ASIC chain + responses: + "200": + description: Resume request accepted + content: + application/json: + schema: + type: object + required: [message, miningPaused, miningState] + properties: + message: + type: string + miningPaused: + type: boolean + miningState: + type: string + enum: [mining, pausing, paused, resuming] + "503": + description: Mining subsystem is not ready /api/system/wifi/scan: get: summary: Scan for available WiFi networks diff --git a/main/main.c b/main/main.c index 7c8c99f..4fe2bdd 100644 --- a/main/main.c +++ b/main/main.c @@ -119,7 +119,8 @@ void app_main(void) SYSTEM_init_peripherals(&GLOBAL_STATE); - xTaskCreate(POWER_MANAGEMENT_task, "power management", 8192, (void *) &GLOBAL_STATE, 10, NULL); + xTaskCreate(POWER_MANAGEMENT_task, "power management", 8192, (void *) &GLOBAL_STATE, 10, + &GLOBAL_STATE.power_management_task_handle); //start the API for AxeOS start_rest_server((void *) &GLOBAL_STATE); @@ -175,6 +176,7 @@ void app_main(void) SERIAL_clear_buffer(); GLOBAL_STATE.ASIC_initalized = true; + GLOBAL_STATE.mining_control_ready = true; xTaskCreate(stratum_task, "stratum admin", 8192, (void *) &GLOBAL_STATE, 5, NULL); xTaskCreate(create_jobs_task, "stratum miner", 8192, (void *) &GLOBAL_STATE, 10, NULL); diff --git a/main/system.c b/main/system.c index 88945ef..dfcd165 100644 --- a/main/system.c +++ b/main/system.c @@ -96,6 +96,10 @@ void SYSTEM_init_system(GlobalState * GLOBAL_STATE) module->overheat_mode = nvs_config_get_u16(NVS_CONFIG_OVERHEAT_MODE, 0); ESP_LOGI(TAG, "Initial overheat_mode value: %d", module->overheat_mode); + // Pause is intentionally not persisted: every clean boot starts mining. + module->mining_paused = false; + module->mining_state = MINING_STATE_MINING; + //Initialize power_fault fault mode module->power_fault = 0; diff --git a/main/tasks/asic_result_task.c b/main/tasks/asic_result_task.c index c33944f..bf15227 100644 --- a/main/tasks/asic_result_task.c +++ b/main/tasks/asic_result_task.c @@ -19,6 +19,11 @@ void ASIC_result_task(void *pvParameters) while (1) { + if (!GLOBAL_STATE->ASIC_initalized) { + vTaskDelay(100 / portTICK_PERIOD_MS); + continue; + } + //task_result *asic_result = (*GLOBAL_STATE->ASIC_functions.receive_result_fn)(GLOBAL_STATE); task_result *asic_result = ASIC_process_work(GLOBAL_STATE); diff --git a/main/tasks/asic_task.c b/main/tasks/asic_task.c index 5def6cf..b201ca8 100644 --- a/main/tasks/asic_task.c +++ b/main/tasks/asic_task.c @@ -37,6 +37,12 @@ void ASIC_task(void *pvParameters) bm_job *next_bm_job = (bm_job *)queue_dequeue(&GLOBAL_STATE->ASIC_jobs_queue); + if (!GLOBAL_STATE->ASIC_initalized) { + free_bm_job(next_bm_job); + vTaskDelay(100 / portTICK_PERIOD_MS); + continue; + } + if (next_bm_job->pool_diff != GLOBAL_STATE->stratum_difficulty) { ESP_LOGI(TAG, "New pool difficulty %.2f", next_bm_job->pool_diff); diff --git a/main/tasks/create_jobs_task.c b/main/tasks/create_jobs_task.c index 432c4a7..d907e78 100644 --- a/main/tasks/create_jobs_task.c +++ b/main/tasks/create_jobs_task.c @@ -49,7 +49,9 @@ void create_jobs_task(void *pvParameters) } uint64_t extranonce_2 = 0; - while (queue_count(&GLOBAL_STATE->stratum_queue) < 1 && GLOBAL_STATE->abandon_work == 0) + while (queue_count(&GLOBAL_STATE->stratum_queue) < 1 && + GLOBAL_STATE->abandon_work == 0 && + !GLOBAL_STATE->SYSTEM_MODULE.mining_paused) { if (should_generate_more_work(GLOBAL_STATE)) { diff --git a/main/tasks/hashrate_monitor_task.c b/main/tasks/hashrate_monitor_task.c index 794e48d..55a5f58 100644 --- a/main/tasks/hashrate_monitor_task.c +++ b/main/tasks/hashrate_monitor_task.c @@ -54,6 +54,28 @@ static void clear_measurements(HashrateMonitorModule * HASHRATE_MONITOR_MODULE, memset(HASHRATE_MONITOR_MODULE->error_measurement, 0, asic_count * sizeof(measurement_t)); } +void hashrate_monitor_reset(void *pvParameters) +{ + GlobalState * GLOBAL_STATE = (GlobalState *)pvParameters; + HashrateMonitorModule * HASHRATE_MONITOR_MODULE = &GLOBAL_STATE->HASHRATE_MONITOR_MODULE; + + if (!HASHRATE_MONITOR_MODULE->is_initialized) { + HASHRATE_MONITOR_MODULE->hashrate = 0.0f; + HASHRATE_MONITOR_MODULE->error_count = 0; + return; + } + + int asic_count = ASIC_get_asic_count(GLOBAL_STATE); + + pthread_mutex_lock(&HASHRATE_MONITOR_MODULE->measurement_lock); + clear_measurements(HASHRATE_MONITOR_MODULE, asic_count, BM1370_HASH_DOMAINS); + HASHRATE_MONITOR_MODULE->hashrate = 0.0f; + HASHRATE_MONITOR_MODULE->error_count = 0; + pthread_mutex_unlock(&HASHRATE_MONITOR_MODULE->measurement_lock); + + ESP_LOGI(TAG, "Hashrate estimator reset"); +} + float hash_counter_to_ghs(uint32_t duration_ms, uint32_t counter) { if (duration_ms == 0) return 0.0f; @@ -158,6 +180,12 @@ void hashrate_monitor_task(void *pvParameters) TickType_t taskWakeTime = xTaskGetTickCount(); while (1) { + if (!GLOBAL_STATE->ASIC_initalized) { + taskWakeTime = xTaskGetTickCount(); + vTaskDelay(100 / portTICK_PERIOD_MS); + continue; + } + BM1370_read_registers(); ESP_LOGD(TAG, "Sent register read commands"); @@ -166,9 +194,6 @@ void hashrate_monitor_task(void *pvParameters) pthread_mutex_lock(&HASHRATE_MONITOR_MODULE->measurement_lock); float hashrate = sum_hashrates(HASHRATE_MONITOR_MODULE->total_measurement, asic_count); uint32_t error_count = sum_values(HASHRATE_MONITOR_MODULE->error_measurement, asic_count); - pthread_mutex_unlock(&HASHRATE_MONITOR_MODULE->measurement_lock); - - ESP_LOGI(TAG, "Calculated hashrate: %.2f GH/s", hashrate); if (hashrate == 0.0) { HASHRATE_MONITOR_MODULE->hashrate = 0.0; @@ -176,14 +201,17 @@ void hashrate_monitor_task(void *pvParameters) if (HASHRATE_MONITOR_MODULE->hashrate == 0.0f) { // Initialize with current hashrate HASHRATE_MONITOR_MODULE->hashrate = hashrate; - ESP_LOGI(TAG, "Initial hashrate set to %.2f GH/s", hashrate); } else { HASHRATE_MONITOR_MODULE->hashrate = ((HASHRATE_MONITOR_MODULE->hashrate * (EMA_ALPHA - 1)) + hashrate) / EMA_ALPHA; - ESP_LOGI(TAG, "EMA hashrate: %.2f GH/s", HASHRATE_MONITOR_MODULE->hashrate); } } HASHRATE_MONITOR_MODULE->error_count = error_count; + float filtered_hashrate = HASHRATE_MONITOR_MODULE->hashrate; + pthread_mutex_unlock(&HASHRATE_MONITOR_MODULE->measurement_lock); + + ESP_LOGI(TAG, "Calculated hashrate: %.2f GH/s", hashrate); + ESP_LOGI(TAG, "Reported hashrate: %.2f GH/s", filtered_hashrate); vTaskDelayUntil(&taskWakeTime, POLL_RATE / portTICK_PERIOD_MS); } @@ -200,6 +228,10 @@ void hashrate_monitor_register_read(void *pvParameters, register_type_t register int asic_count = ASIC_get_asic_count(GLOBAL_STATE); int hash_domains = BM1370_HASH_DOMAINS; + if (!GLOBAL_STATE->ASIC_initalized || !HASHRATE_MONITOR_MODULE->is_initialized) { + return; + } + ESP_LOGD(TAG, "Register read: type=%d, asic=%d, value=0x%08"PRIX32, register_type, asic_nr, value); if (asic_nr >= asic_count) { diff --git a/main/tasks/hashrate_monitor_task.h b/main/tasks/hashrate_monitor_task.h index 9ded101..2ecf694 100644 --- a/main/tasks/hashrate_monitor_task.h +++ b/main/tasks/hashrate_monitor_task.h @@ -23,6 +23,7 @@ typedef struct { void hashrate_monitor_task(void *pvParameters); void hashrate_monitor_register_read(void *pvParameters, register_type_t register_type, uint8_t asic_nr, uint32_t value); +void hashrate_monitor_reset(void *pvParameters); float hash_counter_to_ghs(uint32_t duration_ms, uint32_t counter); #endif /* HASHRATE_MONITOR_TASK_H_ */ diff --git a/main/tasks/power_management_task.c b/main/tasks/power_management_task.c index bc4ba47..adb3b8f 100644 --- a/main/tasks/power_management_task.c +++ b/main/tasks/power_management_task.c @@ -15,6 +15,7 @@ #include "power.h" #include "asic.h" #include "adc.h" +#include "hashrate_monitor_task.h" #define POLL_RATE 2000 #define MAX_TEMP 90.0 @@ -27,11 +28,107 @@ #define TPS546_THROTTLE_TEMP 105.0 #define TPS546_MAX_TEMP 145.0 +#define PAUSED_FAN_SPEED 30.0 static const char * TAG = "power_management"; static bool even = false; +static void update_input_telemetry(GlobalState * GLOBAL_STATE) +{ + PowerManagementModule * power_management = &GLOBAL_STATE->POWER_MANAGEMENT_MODULE; + + power_management->voltage = Power_get_input_voltage(GLOBAL_STATE); + power_management->current = Power_get_current(GLOBAL_STATE); + power_management->power = Power_get_power(GLOBAL_STATE); +} + +static void update_paused_telemetry(GlobalState * GLOBAL_STATE) +{ + PowerManagementModule * power_management = &GLOBAL_STATE->POWER_MANAGEMENT_MODULE; + + update_input_telemetry(GLOBAL_STATE); + + PAC9544_selectChannel(even + 2U); + vTaskDelay(pdMS_TO_TICKS(10)); + power_management->vr_temp = Power_get_vreg_temp(GLOBAL_STATE); + power_management->fan_rpm[even] = Thermal_getFanSpeed(); + + // ASIC thermal diodes are not valid while VCORE is disabled. + power_management->chip_temp[0] = 0.0f; + power_management->chip_temp[1] = 0.0f; + power_management->chip_temp_avg = 0.0f; + + even = !even; +} + +static void mining_stop(GlobalState * GLOBAL_STATE) +{ + ESP_LOGI(TAG, "Stopping mining"); + + // Stop the worker tasks before changing the ASIC clock and power state. + GLOBAL_STATE->ASIC_initalized = false; + hashrate_monitor_reset(GLOBAL_STATE); + + // Match AxeOS: wind the chain down before switching off the TPS546 output. + if (!ASIC_set_frequency(GLOBAL_STATE, 50.0f)) { + ESP_LOGW(TAG, "Unable to reduce ASIC frequency before pause"); + } + + if (VCORE_set_voltage(0.0f, GLOBAL_STATE) != ESP_OK) { + ESP_LOGE(TAG, "Unable to turn off ASIC core voltage"); + } + ASIC_hold_reset_low(GLOBAL_STATE); + + // Let in-flight UART work finish, then discard stale bytes. + vTaskDelay(500 / portTICK_PERIOD_MS); + SERIAL_clear_buffer(); + vTaskDelay(100 / portTICK_PERIOD_MS); + + hashrate_monitor_reset(GLOBAL_STATE); + GLOBAL_STATE->POWER_MANAGEMENT_MODULE.fan_perc = PAUSED_FAN_SPEED; + Thermal_setFanSpeedPercent(PAUSED_FAN_SPEED / 100.0f); + + ESP_LOGI(TAG, "Mining stopped"); +} + +static bool mining_start(GlobalState * GLOBAL_STATE) +{ + ESP_LOGI(TAG, "Starting mining"); + + uint16_t voltage = nvs_config_get_u16(NVS_CONFIG_ASIC_VOLTAGE, CONFIG_ASIC_VOLTAGE); + if (VCORE_set_voltage((double) voltage / 1000.0, GLOBAL_STATE) != ESP_OK) { + ESP_LOGE(TAG, "Unable to restore ASIC core voltage"); + return false; + } + + vTaskDelay(500 / portTICK_PERIOD_MS); + + // A freshly reset BM1370 chain starts at the default UART baud. + SERIAL_set_baud(115200); + SERIAL_clear_buffer(); + vTaskDelay(100 / portTICK_PERIOD_MS); + + uint8_t chip_count = ASIC_init(GLOBAL_STATE); + if (chip_count == 0) { + ESP_LOGE(TAG, "Mining resume failed: ASIC chain not detected"); + ASIC_hold_reset_low(GLOBAL_STATE); + VCORE_set_voltage(0.0f, GLOBAL_STATE); + return false; + } + + SERIAL_set_baud(ASIC_set_max_baud(GLOBAL_STATE)); + SERIAL_clear_buffer(); + + // ASIC counters restart with the chain. Discard every pre-pause baseline + // before register polling is allowed to resume. + hashrate_monitor_reset(GLOBAL_STATE); + GLOBAL_STATE->ASIC_initalized = true; + + ESP_LOGI(TAG, "Mining started successfully (%u chip(s))", chip_count); + return true; +} + // Set the fan speed between 35% min and 100% max based on ASIC and VR temperatures. // Uses the higher of the two temperature-based fan speed requirements. // ASIC: 45°C (35%) → 75°C (100%) @@ -102,13 +199,45 @@ void POWER_MANAGEMENT_task(void * pvParameters) vTaskDelay(500 / portTICK_PERIOD_MS); uint16_t last_core_voltage = 0.0; uint16_t last_asic_frequency = power_management->frequency_value; + bool is_user_paused = false; while (1) { + if (sys_module->mining_paused && !is_user_paused) { + sys_module->mining_state = MINING_STATE_PAUSING; + mining_stop(GLOBAL_STATE); + is_user_paused = true; + if (!sys_module->mining_paused) { + sys_module->mining_state = MINING_STATE_RESUMING; + continue; + } + sys_module->mining_state = MINING_STATE_PAUSED; + } else if (!sys_module->mining_paused && is_user_paused) { + sys_module->mining_state = MINING_STATE_RESUMING; + if (mining_start(GLOBAL_STATE)) { + is_user_paused = false; + if (sys_module->mining_paused) { + sys_module->mining_state = MINING_STATE_PAUSING; + continue; + } + sys_module->mining_state = MINING_STATE_MINING; + } else { + // Keep the externally visible state honest if recovery fails. + sys_module->mining_paused = true; + sys_module->mining_state = MINING_STATE_PAUSED; + } + } + + if (is_user_paused) { + // Keep board-level telemetry live while the ASIC rail is off. + update_paused_telemetry(GLOBAL_STATE); + ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(POLL_RATE)); + continue; + } + PAC9544_selectChannel(even + 2U); vTaskDelay(pdMS_TO_TICKS(10)); // Allow PAC9544 channel switch to settle - power_management->voltage = Power_get_input_voltage(GLOBAL_STATE); - power_management->power = Power_get_power(GLOBAL_STATE); + update_input_telemetry(GLOBAL_STATE); #ifdef POWER_DEBUG ESP_LOGI(TAG, "POWER: %f", power_management->power); #endif @@ -221,7 +350,7 @@ void POWER_MANAGEMENT_task(void * pvParameters) VCORE_check_fault(GLOBAL_STATE); even = !even; - // looper: - vTaskDelay(POLL_RATE / portTICK_PERIOD_MS); + // API pause/resume requests wake this task without shortening sensor polling. + ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(POLL_RATE)); } } diff --git a/main/tasks/stratum_task.c b/main/tasks/stratum_task.c index f31a328..16016e4 100644 --- a/main/tasks/stratum_task.c +++ b/main/tasks/stratum_task.c @@ -367,6 +367,11 @@ void stratum_task(void * pvParameters) ESP_LOGI(TAG, "Opening connection to pool: %s:%d", stratum_url, port); while (1) { + if (GLOBAL_STATE->SYSTEM_MODULE.mining_paused) { + vTaskDelay(500 / portTICK_PERIOD_MS); + continue; + } + if (!is_wifi_connected()) { ESP_LOGI(TAG, "WiFi disconnected, attempting to reconnect..."); vTaskDelay(10000 / portTICK_PERIOD_MS); @@ -475,6 +480,13 @@ void stratum_task(void * pvParameters) GLOBAL_STATE->abandon_work = 0; while (1) { + if (GLOBAL_STATE->SYSTEM_MODULE.mining_paused) { + ESP_LOGI(TAG, "Mining paused, disconnecting from pool"); + retry_attempts = 0; + stratum_close_connection(GLOBAL_STATE); + break; + } + char * line = STRATUM_V1_receive_jsonrpc_line(GLOBAL_STATE->transport); if (!line) { ESP_LOGE(TAG, "Failed to receive JSON-RPC line, reconnecting...");