ESP-Miner/main/system.c

642 lines
21 KiB
C

#include <inttypes.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "esp_app_desc.h"
#include "esp_netif.h"
#include "esp_timer.h"
#include "esp_wifi.h"
#include "lwip/inet.h"
#include "system.h"
#include "i2c_bitaxe.h"
#include "EMC2101.h"
#include "INA260.h"
#include "adc.h"
#include "connect.h"
#include "led_controller.h"
#include "nvs_config.h"
#include "oled.h"
#include "vcore.h"
static const char * TAG = "SystemModule";
static void _suffix_string(uint64_t, char *, size_t, int);
static esp_netif_t * netif;
QueueHandle_t user_input_queue;
//local function prototypes
static esp_err_t ensure_overheat_mode_config();
static void _show_overheat_screen(GlobalState * GLOBAL_STATE);
static void _clear_display(GlobalState * GLOBAL_STATE);
static void _init_connection(GlobalState * GLOBAL_STATE);
static void _update_connection(GlobalState * GLOBAL_STATE);
static void _update_screen_one(GlobalState * GLOBAL_STATE);
static void _update_screen_two(GlobalState * GLOBAL_STATE);
static void show_ap_information(const char * error, GlobalState * GLOBAL_STATE);
static void _check_for_best_diff(GlobalState * GLOBAL_STATE, double diff, uint8_t job_id);
static void _suffix_string(uint64_t val, char * buf, size_t bufsiz, int sigdigits);
void SYSTEM_init_system(GlobalState * GLOBAL_STATE)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
module->duration_start = 0;
module->historical_hashrate_rolling_index = 0;
module->historical_hashrate_init = 0;
module->current_hashrate = 0;
module->screen_page = 0;
module->shares_accepted = 0;
module->shares_rejected = 0;
module->best_nonce_diff = nvs_config_get_u64(NVS_CONFIG_BEST_DIFF, 0);
module->best_session_nonce_diff = 0;
module->start_time = esp_timer_get_time();
module->lastClockSync = 0;
module->FOUND_BLOCK = false;
module->startup_done = false;
// set the pool url
module->pool_url = nvs_config_get_string(NVS_CONFIG_STRATUM_URL, CONFIG_STRATUM_URL);
module->fallback_pool_url = nvs_config_get_string(NVS_CONFIG_FALLBACK_STRATUM_URL, CONFIG_FALLBACK_STRATUM_URL);
//set the pool port
module->pool_port = nvs_config_get_u16(NVS_CONFIG_STRATUM_PORT, CONFIG_STRATUM_PORT);
module->fallback_pool_port = nvs_config_get_u16(NVS_CONFIG_FALLBACK_STRATUM_PORT, CONFIG_FALLBACK_STRATUM_PORT);
// set fallback to false.
module->is_using_fallback = false;
// Initialize overheat_mode
module->overheat_mode = nvs_config_get_u16(NVS_CONFIG_OVERHEAT_MODE, 0);
ESP_LOGI(TAG, "Initial overheat_mode value: %d", module->overheat_mode);
// set the best diff string
_suffix_string(module->best_nonce_diff, module->best_diff_string, DIFF_STRING_SIZE, 0);
_suffix_string(module->best_session_nonce_diff, module->best_session_diff_string, DIFF_STRING_SIZE, 0);
// set the ssid string to blank
memset(module->ssid, 0, sizeof(module->ssid));
// set the wifi_status to blank
memset(module->wifi_status, 0, 20);
}
void SYSTEM_init_peripherals(GlobalState * GLOBAL_STATE) {
// Initialize the core voltage regulator
VCORE_init(GLOBAL_STATE);
VCORE_set_voltage(nvs_config_get_u16(NVS_CONFIG_ASIC_VOLTAGE, CONFIG_ASIC_VOLTAGE) / 1000.0, GLOBAL_STATE);
//init the EMC2101, if we have one
switch (GLOBAL_STATE->device_model) {
case DEVICE_MAX:
case DEVICE_ULTRA:
case DEVICE_SUPRA:
case DEVICE_GAMMA:
EMC2101_init(nvs_config_get_u16(NVS_CONFIG_INVERT_FAN_POLARITY, 1));
break;
default:
}
//initialize the INA260, if we have one.
switch (GLOBAL_STATE->device_model) {
case DEVICE_MAX:
case DEVICE_ULTRA:
case DEVICE_SUPRA:
if (GLOBAL_STATE->board_version < 402) {
// Initialize the LED controller
INA260_init();
}
break;
case DEVICE_GAMMA:
default:
}
vTaskDelay(500 / portTICK_PERIOD_MS);
// Ensure overheat_mode config exists
esp_err_t ret = ensure_overheat_mode_config();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to ensure overheat_mode config");
}
//Init the OLED
switch (GLOBAL_STATE->device_model) {
case DEVICE_MAX:
case DEVICE_ULTRA:
case DEVICE_SUPRA:
case DEVICE_GAMMA:
// oled
if (!OLED_init()) {
ESP_LOGI(TAG, "OLED init failed!");
} else {
ESP_LOGI(TAG, "OLED init success!");
// clear the oled screen
OLED_fill(0);
}
break;
default:
}
netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
user_input_queue = xQueueCreate(10, sizeof(char[10])); // Create a queue to handle user input events
_clear_display(GLOBAL_STATE);
_init_connection(GLOBAL_STATE);
}
void SYSTEM_task(void * pvParameters)
{
GlobalState * GLOBAL_STATE = (GlobalState *) pvParameters;
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
//_init_system(GLOBAL_STATE);
char input_event[10];
ESP_LOGI(TAG, "SYSTEM_task started");
while (GLOBAL_STATE->ASIC_functions.init_fn == NULL) {
show_ap_information("ASIC MODEL INVALID", GLOBAL_STATE);
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
// show the connection screen
while (!module->startup_done) {
_update_connection(GLOBAL_STATE);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
int current_screen = 0;
TickType_t last_update_time = xTaskGetTickCount();
while (1) {
// Check for overheat mode
if (module->overheat_mode == 1) {
_show_overheat_screen(GLOBAL_STATE);
vTaskDelay(5000 / portTICK_PERIOD_MS); // Update every 5 seconds
SYSTEM_update_overheat_mode(GLOBAL_STATE); // Check for changes
continue; // Skip the normal screen cycle
}
// Update the current screen
_clear_display(GLOBAL_STATE);
module->screen_page = current_screen;
switch (current_screen) {
case 0:
_update_screen_one(GLOBAL_STATE);
break;
case 1:
_update_screen_two(GLOBAL_STATE);
break;
}
// Wait for user input or timeout
bool input_received = false;
TickType_t current_time = xTaskGetTickCount();
TickType_t wait_time = pdMS_TO_TICKS(10000) - (current_time - last_update_time);
if (wait_time > 0) {
if (xQueueReceive(user_input_queue, &input_event, wait_time) == pdTRUE) {
input_received = true;
if (strcmp(input_event, "SHORT") == 0) {
ESP_LOGI(TAG, "Short button press detected, switching to next screen");
current_screen = (current_screen + 1) % 2;
} else if (strcmp(input_event, "LONG") == 0) {
ESP_LOGI(TAG, "Long button press detected, toggling WiFi SoftAP");
toggle_wifi_softap();
}
}
}
// If no input received and 10 seconds have passed, switch to the next screen
if (!input_received && (xTaskGetTickCount() - last_update_time) >= pdMS_TO_TICKS(10000)) {
current_screen = (current_screen + 1) % 2;
}
last_update_time = xTaskGetTickCount();
}
}
void SYSTEM_update_overheat_mode(GlobalState * GLOBAL_STATE)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
uint16_t new_overheat_mode = nvs_config_get_u16(NVS_CONFIG_OVERHEAT_MODE, 0);
if (new_overheat_mode != module->overheat_mode) {
module->overheat_mode = new_overheat_mode;
ESP_LOGI(TAG, "Overheat mode updated to: %d", module->overheat_mode);
}
}
void SYSTEM_notify_accepted_share(GlobalState * GLOBAL_STATE)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
module->shares_accepted++;
}
void SYSTEM_notify_rejected_share(GlobalState * GLOBAL_STATE)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
module->shares_rejected++;
}
void SYSTEM_notify_mining_started(GlobalState * GLOBAL_STATE)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
module->duration_start = esp_timer_get_time();
}
void SYSTEM_notify_new_ntime(GlobalState * GLOBAL_STATE, uint32_t ntime)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
// Hourly clock sync
if (module->lastClockSync + (60 * 60) > ntime) {
return;
}
ESP_LOGI(TAG, "Syncing clock");
module->lastClockSync = ntime;
struct timeval tv;
tv.tv_sec = ntime;
tv.tv_usec = 0;
settimeofday(&tv, NULL);
}
void SYSTEM_notify_found_nonce(GlobalState * GLOBAL_STATE, double found_diff, uint8_t job_id)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
// Calculate the time difference in seconds with sub-second precision
// hashrate = (nonce_difficulty * 2^32) / time_to_find
module->historical_hashrate[module->historical_hashrate_rolling_index] = GLOBAL_STATE->ASIC_difficulty;
module->historical_hashrate_time_stamps[module->historical_hashrate_rolling_index] = esp_timer_get_time();
module->historical_hashrate_rolling_index = (module->historical_hashrate_rolling_index + 1) % HISTORY_LENGTH;
// ESP_LOGI(TAG, "nonce_diff %.1f, ttf %.1f, res %.1f", nonce_diff, duration,
// historical_hashrate[historical_hashrate_rolling_index]);
if (module->historical_hashrate_init < HISTORY_LENGTH) {
module->historical_hashrate_init++;
} else {
module->duration_start =
module->historical_hashrate_time_stamps[(module->historical_hashrate_rolling_index + 1) % HISTORY_LENGTH];
}
double sum = 0;
for (int i = 0; i < module->historical_hashrate_init; i++) {
sum += module->historical_hashrate[i];
}
double duration = (double) (esp_timer_get_time() - module->duration_start) / 1000000;
double rolling_rate = (sum * 4294967296) / (duration * 1000000000);
if (module->historical_hashrate_init < HISTORY_LENGTH) {
module->current_hashrate = rolling_rate;
} else {
// More smoothing
module->current_hashrate = ((module->current_hashrate * 9) + rolling_rate) / 10;
}
// logArrayContents(historical_hashrate, HISTORY_LENGTH);
// logArrayContents(historical_hashrate_time_stamps, HISTORY_LENGTH);
_check_for_best_diff(GLOBAL_STATE, found_diff, job_id);
}
///
/// LOCAL FUNCTIONS
///
static void _show_overheat_screen(GlobalState * GLOBAL_STATE)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
esp_netif_ip_info_t ip_info;
switch (GLOBAL_STATE->device_model) {
case DEVICE_MAX:
case DEVICE_ULTRA:
case DEVICE_SUPRA:
case DEVICE_GAMMA:
if (OLED_status()) {
OLED_clearLine(0);
OLED_writeString(0, 0, "DEVICE OVERHEAT!");
OLED_clearLine(1);
OLED_writeString(0, 1, "See AxeOS settings");
OLED_clearLine(2);
OLED_clearLine(3);
esp_netif_get_ip_info(netif, &ip_info);
char ip_address_str[IP4ADDR_STRLEN_MAX];
esp_ip4addr_ntoa(&ip_info.ip, ip_address_str, IP4ADDR_STRLEN_MAX);
memset(module->oled_buf, 0, 20);
snprintf(module->oled_buf, 20, "IP: %s", ip_address_str);
OLED_writeString(0, 3, module->oled_buf);
}
break;
default:
break;
}
}
static void _update_screen_one(GlobalState * GLOBAL_STATE)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
PowerManagementModule * power_management = &GLOBAL_STATE->POWER_MANAGEMENT_MODULE;
switch (GLOBAL_STATE->device_model) {
case DEVICE_MAX:
case DEVICE_ULTRA:
case DEVICE_SUPRA:
case DEVICE_GAMMA:
if (OLED_status()) {
float efficiency = GLOBAL_STATE->POWER_MANAGEMENT_MODULE.power / (module->current_hashrate / 1000.0);
memset(module->oled_buf, 0, 20);
snprintf(module->oled_buf, 20, "Gh/s: %.2f", module->current_hashrate);
OLED_writeString(0, 0, module->oled_buf);
memset(module->oled_buf, 0, 20);
snprintf(module->oled_buf, 20, "J/Th: %.2f", efficiency);
OLED_writeString(0, 1, module->oled_buf);
memset(module->oled_buf, 0, 20);
snprintf(module->oled_buf, 20, module->FOUND_BLOCK ? "!!! BLOCK FOUND !!!" : "Best: %s", module->best_diff_string);
OLED_writeString(0, 2, module->oled_buf);
memset(module->oled_buf, 0, 20);
snprintf(module->oled_buf, 20, "Temp: %.1f C", power_management->chip_temp_avg);
OLED_writeString(0, 3, module->oled_buf);
}
break;
default:
break;
}
}
static void _update_screen_two(GlobalState * GLOBAL_STATE)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
esp_netif_ip_info_t ip_info;
switch (GLOBAL_STATE->device_model) {
case DEVICE_MAX:
case DEVICE_ULTRA:
case DEVICE_SUPRA:
case DEVICE_GAMMA:
if (OLED_status()) {
// Pool URL
OLED_writeString(0, 0, "Mining URL:");
memset(module->oled_buf, 0, 20);
if (module->is_using_fallback) {
snprintf(module->oled_buf, 20, "%.19s", module->fallback_pool_url);
} else {
snprintf(module->oled_buf, 20, "%.19s", module->pool_url);
}
OLED_writeString(0, 1, module->oled_buf);
// // Second line of pool URL
// memset(module->oled_buf, 0, 20);
// if (module->is_using_fallback) {
// snprintf(module->oled_buf, 20, "%.19s", module->fallback_pool_url + 13);
// } else {
// snprintf(module->oled_buf, 20, "%.19s", module->pool_url + 13);
// }
// OLED_writeString(0, 1, module->oled_buf);
// IP Address
OLED_writeString(0, 2, "Bitaxe IP:");
esp_netif_get_ip_info(netif, &ip_info);
char ip_address_str[IP4ADDR_STRLEN_MAX];
esp_ip4addr_ntoa(&ip_info.ip, ip_address_str, IP4ADDR_STRLEN_MAX);
memset(module->oled_buf, 0, 20);
snprintf(module->oled_buf, 20, "%s", ip_address_str);
OLED_writeString(0, 3, module->oled_buf);
}
break;
default:
break;
}
}
static void _clear_display(GlobalState * GLOBAL_STATE)
{
switch (GLOBAL_STATE->device_model) {
case DEVICE_MAX:
case DEVICE_ULTRA:
case DEVICE_SUPRA:
case DEVICE_GAMMA:
OLED_clearLine(0);
OLED_clearLine(1);
OLED_clearLine(2);
OLED_clearLine(3);
break;
default:
}
}
static void _init_connection(GlobalState * GLOBAL_STATE)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
switch (GLOBAL_STATE->device_model) {
case DEVICE_MAX:
case DEVICE_ULTRA:
case DEVICE_SUPRA:
case DEVICE_GAMMA:
if (OLED_status()) {
memset(module->oled_buf, 0, 20);
snprintf(module->oled_buf, 20, "Connecting to SSID:");
OLED_writeString(0, 0, module->oled_buf);
}
break;
default:
}
}
static void _update_connection(GlobalState * GLOBAL_STATE)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
switch (GLOBAL_STATE->device_model) {
case DEVICE_MAX:
case DEVICE_ULTRA:
case DEVICE_SUPRA:
case DEVICE_GAMMA:
if (OLED_status()) {
OLED_clearLine(2);
strncpy(module->oled_buf, module->ssid, sizeof(module->oled_buf));
module->oled_buf[sizeof(module->oled_buf) - 1] = 0;
OLED_writeString(0, 1, module->oled_buf);
memset(module->oled_buf, 0, 20);
snprintf(module->oled_buf, 20, "Configuration SSID:");
OLED_writeString(0, 2, module->oled_buf);
char ap_ssid[13];
generate_ssid(ap_ssid);
memset(module->oled_buf, 0, 20);
snprintf(module->oled_buf, 20, ap_ssid);
OLED_writeString(0, 3, module->oled_buf);
}
break;
default:
}
}
static void show_ap_information(const char * error, GlobalState * GLOBAL_STATE)
{
switch (GLOBAL_STATE->device_model) {
case DEVICE_MAX:
case DEVICE_ULTRA:
case DEVICE_SUPRA:
case DEVICE_GAMMA:
if (OLED_status()) {
_clear_display(GLOBAL_STATE);
if (error != NULL) {
OLED_writeString(0, 0, error);
}
OLED_writeString(0, 1, "Configuration SSID:");
char ap_ssid[13];
generate_ssid(ap_ssid);
OLED_writeString(0, 2, ap_ssid);
}
break;
default:
}
}
static double _calculate_network_difficulty(uint32_t nBits)
{
uint32_t mantissa = nBits & 0x007fffff; // Extract the mantissa from nBits
uint8_t exponent = (nBits >> 24) & 0xff; // Extract the exponent from nBits
double target = (double) mantissa * pow(256, (exponent - 3)); // Calculate the target value
double difficulty = (pow(2, 208) * 65535) / target; // Calculate the difficulty
return difficulty;
}
static void _check_for_best_diff(GlobalState * GLOBAL_STATE, double diff, uint8_t job_id)
{
SystemModule * module = &GLOBAL_STATE->SYSTEM_MODULE;
if ((uint64_t) diff > module->best_session_nonce_diff) {
module->best_session_nonce_diff = (uint64_t) diff;
_suffix_string((uint64_t) diff, module->best_session_diff_string, DIFF_STRING_SIZE, 0);
}
if ((uint64_t) diff <= module->best_nonce_diff) {
return;
}
module->best_nonce_diff = (uint64_t) diff;
nvs_config_set_u64(NVS_CONFIG_BEST_DIFF, module->best_nonce_diff);
// make the best_nonce_diff into a string
_suffix_string((uint64_t) diff, module->best_diff_string, DIFF_STRING_SIZE, 0);
double network_diff = _calculate_network_difficulty(GLOBAL_STATE->ASIC_TASK_MODULE.active_jobs[job_id]->target);
if (diff > network_diff) {
module->FOUND_BLOCK = true;
ESP_LOGI(TAG, "FOUND BLOCK!!!!!!!!!!!!!!!!!!!!!! %f > %f", diff, network_diff);
}
ESP_LOGI(TAG, "Network diff: %f", network_diff);
}
/* Convert a uint64_t value into a truncated string for displaying with its
* associated suitable for Mega, Giga etc. Buf array needs to be long enough */
static void _suffix_string(uint64_t val, char * buf, size_t bufsiz, int sigdigits)
{
const double dkilo = 1000.0;
const uint64_t kilo = 1000ull;
const uint64_t mega = 1000000ull;
const uint64_t giga = 1000000000ull;
const uint64_t tera = 1000000000000ull;
const uint64_t peta = 1000000000000000ull;
const uint64_t exa = 1000000000000000000ull;
char suffix[2] = "";
bool decimal = true;
double dval;
if (val >= exa) {
val /= peta;
dval = (double) val / dkilo;
strcpy(suffix, "E");
} else if (val >= peta) {
val /= tera;
dval = (double) val / dkilo;
strcpy(suffix, "P");
} else if (val >= tera) {
val /= giga;
dval = (double) val / dkilo;
strcpy(suffix, "T");
} else if (val >= giga) {
val /= mega;
dval = (double) val / dkilo;
strcpy(suffix, "G");
} else if (val >= mega) {
val /= kilo;
dval = (double) val / dkilo;
strcpy(suffix, "M");
} else if (val >= kilo) {
dval = (double) val / dkilo;
strcpy(suffix, "k");
} else {
dval = val;
decimal = false;
}
if (!sigdigits) {
if (decimal)
snprintf(buf, bufsiz, "%.3g%s", dval, suffix);
else
snprintf(buf, bufsiz, "%d%s", (unsigned int) dval, suffix);
} else {
/* Always show sigdigits + 1, padded on right with zeroes
* followed by suffix */
int ndigits = sigdigits - 1 - (dval > 0.0 ? floor(log10(dval)) : 0);
snprintf(buf, bufsiz, "%*.*f%s", sigdigits + 1, ndigits, dval, suffix);
}
}
static esp_err_t ensure_overheat_mode_config() {
uint16_t overheat_mode = nvs_config_get_u16(NVS_CONFIG_OVERHEAT_MODE, UINT16_MAX);
if (overheat_mode == UINT16_MAX) {
// Key doesn't exist or couldn't be read, set the default value
nvs_config_set_u16(NVS_CONFIG_OVERHEAT_MODE, 0);
ESP_LOGI(TAG, "Default value for overheat_mode set to 0");
} else {
// Key exists, log the current value
ESP_LOGI(TAG, "Existing overheat_mode value: %d", overheat_mode);
}
return ESP_OK;
}