diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 353489a4..65571ec1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,5 +67,5 @@ jobs: generate_release_notes: true make_latest: true fail_on_unmatched_files: true - files: ${{ github.workspace }}/firmware/*/*.bin + files: ${{ github.workspace }}/**/*.bin diff --git a/platformio.ini b/platformio.ini index 7a3abd05..7506a8ce 100644 --- a/platformio.ini +++ b/platformio.ini @@ -594,7 +594,7 @@ framework = arduino extra_scripts = pre:auto_firmware_version.py post:post_build_merge.py - post:post_build_merge.py +board_build.f_cpu = 240000000L monitor_filters = esp32_exception_decoder time @@ -613,7 +613,10 @@ build_flags = -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 -D NERDMINERV2=1 + -D CORE_DEBUG_LEVEL=0 + -D CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL=0 ;-D DEBUG_MINING=1 + ;To enable I2C mining in future: -D ENABLE_I2C_MINING=1 lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 diff --git a/src/NerdMinerV2.ino.cpp b/src/NerdMinerV2.ino.cpp index 46ca5e9d..8928fe44 100644 --- a/src/NerdMinerV2.ino.cpp +++ b/src/NerdMinerV2.ino.cpp @@ -130,17 +130,24 @@ void setup() // Higher prio monitor task Serial.println(""); Serial.println("Initiating tasks..."); - char *name = (char*) malloc(32); - sprintf(name, "(%s)", "Monitor"); - BaseType_t res1 = xTaskCreatePinnedToCore(runMonitor, "Monitor", 10000, (void*)name, 5, NULL,1); + static const char monitor_name[] = "(Monitor)"; + #if defined(CONFIG_IDF_TARGET_ESP32) + // Reduced stack for ESP32 classic to save memory + BaseType_t res1 = xTaskCreatePinnedToCore(runMonitor, "Monitor", 8000, (void*)monitor_name, 5, NULL,1); + #else + BaseType_t res1 = xTaskCreatePinnedToCore(runMonitor, "Monitor", 10000, (void*)monitor_name, 5, NULL,1); + #endif /******** CREATE STRATUM TASK *****/ - sprintf(name, "(%s)", "Stratum"); - #if defined(ESP32_2432S028R) || defined(ESP32_2432S028_2USB) - // Free a little bit of the heap to the screen - BaseType_t res2 = xTaskCreatePinnedToCore(runStratumWorker, "Stratum", 13500, (void*)name, 4, NULL,1); + static const char stratum_name[] = "(Stratum)"; + #if defined(CONFIG_IDF_TARGET_ESP32) && !defined(ESP32_2432S028R) && !defined(ESP32_2432S028_2USB) + // Reduced stack for ESP32 classic to save memory + BaseType_t res2 = xTaskCreatePinnedToCore(runStratumWorker, "Stratum", 12000, (void*)stratum_name, 4, NULL,1); + #elif defined(ESP32_2432S028R) || defined(ESP32_2432S028_2USB) + // Free a little bit of the heap to the screen + BaseType_t res2 = xTaskCreatePinnedToCore(runStratumWorker, "Stratum", 13500, (void*)stratum_name, 4, NULL,1); #else - BaseType_t res2 = xTaskCreatePinnedToCore(runStratumWorker, "Stratum", 15000, (void*)name, 4, NULL,1); + BaseType_t res2 = xTaskCreatePinnedToCore(runStratumWorker, "Stratum", 15000, (void*)stratum_name, 4, NULL,1); #endif /******** CREATE MINER TASKS *****/ @@ -152,14 +159,27 @@ void setup() //BaseType_t res = xTaskCreate(runWorker, name, 35000, (void*)name, 1, NULL); TaskHandle_t minerTask1, minerTask2 = NULL; #ifdef HARDWARE_SHA265 + #if defined(CONFIG_IDF_TARGET_ESP32) + //xTaskCreate(minerWorkerHw, "MinerHw-0", 3584, (void*)0, 3, &minerTask1); // Reduced for ESP32 classic + xTaskCreate(minerWorkerSw, "MinerSw-0", 5000, (void*)0, 1, &minerTask1); // Reduced for ESP32 classic + #else xTaskCreate(minerWorkerHw, "MinerHw-0", 4096, (void*)0, 3, &minerTask1); + #endif #else + #if defined(CONFIG_IDF_TARGET_ESP32) + xTaskCreate(minerWorkerSw, "MinerSw-0", 5000, (void*)0, 1, &minerTask1); // Reduced for ESP32 classic + #else xTaskCreate(minerWorkerSw, "MinerSw-0", 6000, (void*)0, 1, &minerTask1); + #endif #endif esp_task_wdt_add(minerTask1); #if (SOC_CPU_CORES_NUM >= 2) + #if defined(CONFIG_IDF_TARGET_ESP32) + xTaskCreate(minerWorkerSw, "MinerSw-1", 5000, (void*)1, 1, &minerTask2); // Reduced for ESP32 classic + #else xTaskCreate(minerWorkerSw, "MinerSw-1", 6000, (void*)1, 1, &minerTask2); + #endif esp_task_wdt_add(minerTask2); #endif diff --git a/src/drivers/displays/tDisplayV1Driver.cpp b/src/drivers/displays/tDisplayV1Driver.cpp index c13c43bd..ccb0bd83 100644 --- a/src/drivers/displays/tDisplayV1Driver.cpp +++ b/src/drivers/displays/tDisplayV1Driver.cpp @@ -206,7 +206,7 @@ void tDisplay_BTCprice(unsigned long mElapsed) // Hashrate render.setFontSize(25); - render.setCursor(19, 122); + render.setCursor(19, 120); render.setFontColor(TFT_BLACK); render.rdrawString(data.currentHashRate.c_str(), 70, 103, TFT_BLACK); @@ -222,11 +222,11 @@ void tDisplay_BTCprice(unsigned long mElapsed) background.drawString(data.currentTime.c_str(), 148, 1, GFXFF); // Print BTC Price - background.setFreeFont(FF24); - background.setTextDatum(TR_DATUM); + background.setFreeFont(FF23); + background.setTextDatum(TL_DATUM); background.setTextSize(1); background.setTextColor(0xDEDB, TFT_BLACK); - background.drawString(data.btcPrice.c_str(), 70, 25, GFXFF); + background.drawString(data.btcPrice.c_str(), 82, 50, GFXFF); // Push prepared background to screen background.pushSprite(0, 0); @@ -253,7 +253,7 @@ void tDisplay_DoLedStuff(unsigned long frame) { } -CyclicScreenFunction tDisplayCyclicScreens[] = {tDisplay_MinerScreen, tDisplay_ClockScreen, tDisplay_GlobalHashScreen}; +CyclicScreenFunction tDisplayCyclicScreens[] = {tDisplay_MinerScreen, tDisplay_ClockScreen, tDisplay_GlobalHashScreen, tDisplay_BTCprice}; DisplayDriver tDisplayV1Driver = { tDisplay_Init, diff --git a/src/mining.cpp b/src/mining.cpp index 8fd73163..5736c247 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -17,13 +17,24 @@ #include #include #include "mbedtls/sha256.h" +//I2C mining support (disabled by default for performance) +#ifdef ENABLE_I2C_MINING #include "i2c_master.h" +#define I2C_SLAVE_ENABLED +#endif -//10 Jobs per second -#define NONCE_PER_JOB_SW 4096 -#define NONCE_PER_JOB_HW 16*1024 - -//#define I2C_SLAVE +// Reduce memory usage for ESP32 classic to prevent crashes +#if defined(CONFIG_IDF_TARGET_ESP32) + #define NONCE_PER_JOB_SW 2048 // Reduced from 4096 + #define NONCE_PER_JOB_HW 8*1024 // Reduced from 16*1024 + #define JOB_QUEUE_SIZE 2 // Reduced queue size + #define RESULT_QUEUE_SIZE 8 // Reduced result queue +#else + #define NONCE_PER_JOB_SW 4096 // Normal for S3/S2/C3 + #define NONCE_PER_JOB_HW 16*1024 + #define JOB_QUEUE_SIZE 4 // Normal queue size + #define RESULT_QUEUE_SIZE 16 // Normal result queue +#endif //#define SHA256_VALIDATE //#define RANDOM_NONCE @@ -93,8 +104,11 @@ bool checkPoolConnection(void) { //Try connecting pool IP if (!client.connect(serverIP, Settings.PoolPort)) { Serial.println("Imposible to connect to : " + Settings.PoolAddress); - WiFi.hostByName(Settings.PoolAddress.c_str(), serverIP); - Serial.printf("Resolved DNS got: %s\n", serverIP.toString()); + // Only retry DNS resolution if we have an invalid IP + if (serverIP == IPAddress(1,1,1,1)) { + WiFi.hostByName(Settings.PoolAddress.c_str(), serverIP); + Serial.printf("Resolved DNS got: %s\n", serverIP.toString()); + } return false; } @@ -229,7 +243,7 @@ void runStratumWorker(void *name) { std::map> s_submition_map; -#ifdef I2C_SLAVE +#ifdef I2C_SLAVE_ENABLED_ENABLED std::vector i2c_slave_vector; //scan for i2c slaves @@ -330,7 +344,8 @@ void runStratumWorker(void *name) { //Read pending messages from pool while(client.connected() && client.available()) { - String line = client.readStringUntil('\n'); + static String line; // Reutilizar el mismo objeto String + line = client.readStringUntil('\n'); //Serial.println(" Received message from pool"); stratum_method result = parse_mining_method(line); switch (result) @@ -384,7 +399,7 @@ void runStratumWorker(void *name) { #ifdef RANDOM_NONCE nonce_pool = RandomGet() & RANDOM_NONCE_MASK; #else - #ifdef I2C_SLAVE + #ifdef I2C_SLAVE_ENABLED if (!i2c_slave_vector.empty()) nonce_pool = 0x10000000; else @@ -419,7 +434,7 @@ void runStratumWorker(void *name) { #endif } } - #ifdef I2C_SLAVE + #ifdef I2C_SLAVE_ENABLED //Nonce for nonce_pool starts from 0x10000000 //For i2c slave we give nonces from 0x20000000, that is 0x10000000 nonces per slave i2c_feed_slaves(i2c_slave_vector, job_pool & 0xFF, 0x20, currentPoolDifficulty, mMiner.bytearray_blockheader); @@ -468,7 +483,7 @@ void runStratumWorker(void *name) { } std::list> job_result_list; - #ifdef I2C_SLAVE + #ifdef I2C_SLAVE_ENABLED if (i2c_slave_vector.empty() || job_pool == 0xFFFFFFFF) { vTaskDelay(50 / portTICK_PERIOD_MS); //Small delay @@ -516,7 +531,7 @@ void runStratumWorker(void *name) { s_job_result_list.clear(); #if 1 - while (s_job_request_list_sw.size() < 4) + while (s_job_request_list_sw.size() < JOB_QUEUE_SIZE) { JobPush( s_job_request_list_sw, job_pool, nonce_pool, NONCE_PER_JOB_SW, currentPoolDifficulty, mMiner.bytearray_blockheader, diget_mid, bake); #ifdef RANDOM_NONCE @@ -528,7 +543,7 @@ void runStratumWorker(void *name) { #endif #ifdef HARDWARE_SHA265 - while (s_job_request_list_hw.size() < 4) + while (s_job_request_list_hw.size() < JOB_QUEUE_SIZE) { #if defined(CONFIG_IDF_TARGET_ESP32) JobPush( s_job_request_list_hw, job_pool, nonce_pool, NONCE_PER_JOB_HW, currentPoolDifficulty, sha_buffer_swap, hw_midstate, bake); @@ -598,7 +613,7 @@ void minerWorkerSw(void * task_id) std::lock_guard lock(s_job_mutex); if (result) { - if (s_job_result_list.size() < 16) + if (s_job_result_list.size() < RESULT_QUEUE_SIZE) s_job_result_list.push_back(result); result.reset(); } @@ -804,7 +819,7 @@ void minerWorkerHw(void * task_id) std::lock_guard lock(s_job_mutex); if (result) { - if (s_job_result_list.size() < 16) + if (s_job_result_list.size() < RESULT_QUEUE_SIZE) s_job_result_list.push_back(result); result.reset(); } @@ -835,17 +850,13 @@ void minerWorkerHw(void * task_id) uint32_t nend = job->nonce_start + job->nonce_count; for (uint32_t n = job->nonce_start; n < nend; ++n) { - //nerd_sha_hal_wait_idle(); nerd_sha_ll_write_digest(digest_mid); - //nerd_sha_hal_wait_idle(); nerd_sha_ll_fill_text_block_sha256(sha_buffer, n); - //sha_ll_continue_block(SHA2_256); REG_WRITE(SHA_CONTINUE_REG, 1); sha_ll_load(SHA2_256); nerd_sha_hal_wait_idle(); nerd_sha_ll_fill_text_block_sha256_inter(); - //sha_ll_start_block(SHA2_256); REG_WRITE(SHA_START_REG, 1); sha_ll_load(SHA2_256); nerd_sha_hal_wait_idle(); @@ -1045,7 +1056,7 @@ void minerWorkerHw(void * task_id) std::lock_guard lock(s_job_mutex); if (result) { - if (s_job_result_list.size() < 16) + if (s_job_result_list.size() < RESULT_QUEUE_SIZE) s_job_result_list.push_back(result); result.reset(); } @@ -1067,16 +1078,12 @@ void minerWorkerHw(void * task_id) memcpy(sha_buffer, job->sha_buffer, 80); esp_sha_lock_engine(SHA2_256); + uint32_t processed_nonces = 0; // Track actually processed nonces for (uint32_t n = 0; n < job->nonce_count; ++n) { - //((uint32_t*)(sha_buffer+64+12))[0] = __builtin_bswap32(job->nonce_start+n); - - //sha_hal_hash_block(SHA2_256, s_test_buffer, 64/4, true); - //nerd_sha_hal_wait_idle(); nerd_sha_ll_fill_text_block_sha256(sha_buffer); sha_ll_start_block(SHA2_256); - //sha_hal_hash_block(SHA2_256, s_test_buffer+64, 64/4, false); nerd_sha_hal_wait_idle(); nerd_sha_ll_fill_text_block_sha256_upper(sha_buffer+64, job->nonce_start+n); sha_ll_continue_block(SHA2_256); @@ -1084,8 +1091,6 @@ void minerWorkerHw(void * task_id) nerd_sha_hal_wait_idle(); sha_ll_load(SHA2_256); - //sha_hal_hash_block(SHA2_256, interResult, 64/4, true); - nerd_sha_hal_wait_idle(); nerd_sha_ll_fill_text_block_sha256_double(); sha_ll_start_block(SHA2_256); @@ -1093,6 +1098,7 @@ void minerWorkerHw(void * task_id) sha_ll_load(SHA2_256); if (nerd_sha_ll_read_digest_swap_if(hash)) { + processed_nonces++; // Only count successful hash operations //~5 per second double diff_hash = diff_from_target(hash); if (diff_hash > result->difficulty) @@ -1109,10 +1115,12 @@ void minerWorkerHw(void * task_id) (uint8_t)(n & 0xFF) == 0 && s_working_current_job_id != job_in_work) { - result->nonce_count = n+1; + result->nonce_count = processed_nonces; // Use actual processed count break; } } + // Update final count with actual processed nonces + result->nonce_count = processed_nonces; esp_sha_unlock_engine(SHA2_256); } else vTaskDelay(2 / portTICK_PERIOD_MS); diff --git a/src/monitor.cpp b/src/monitor.cpp index 27e48a3f..fd6fa0af 100644 --- a/src/monitor.cpp +++ b/src/monitor.cpp @@ -64,6 +64,7 @@ void updateGlobalData(void){ //Make first API call to get global hash and current difficulty HTTPClient http; + http.setTimeout(10000); try { http.begin(getGlobalHash); int httpCode = http.GET(); @@ -71,7 +72,7 @@ void updateGlobalData(void){ if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); - DynamicJsonDocument doc(1024); + StaticJsonDocument<1024> doc; deserializeJson(doc, payload); String temp = ""; if (doc.containsKey("currentHashrate")) temp = String(doc["currentHashrate"].as()); @@ -96,7 +97,7 @@ void updateGlobalData(void){ if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); - DynamicJsonDocument doc(1024); + StaticJsonDocument<1024> doc; deserializeJson(doc, payload); String temp = ""; if (doc.containsKey("halfHourFee")) gData.halfHourFee = doc["halfHourFee"].as(); @@ -113,6 +114,7 @@ void updateGlobalData(void){ http.end(); } catch(...) { + Serial.println("Global data HTTP error caught"); http.end(); } } @@ -127,6 +129,7 @@ String getBlockHeight(void){ if (WiFi.status() != WL_CONNECTED) return current_block; HTTPClient http; + http.setTimeout(10000); try { http.begin(getHeightAPI); int httpCode = http.GET(); @@ -141,6 +144,7 @@ String getBlockHeight(void){ } http.end(); } catch(...) { + Serial.println("Height HTTP error caught"); http.end(); } } @@ -154,9 +158,14 @@ String getBTCprice(void){ if((mBTCUpdate == 0) || (millis() - mBTCUpdate > UPDATE_BTC_min * 60 * 1000)){ - if (WiFi.status() != WL_CONNECTED) return "$" + String(bitcoin_price); + if (WiFi.status() != WL_CONNECTED) { + static char price_buffer[16]; + snprintf(price_buffer, sizeof(price_buffer), "$%u", bitcoin_price); + return String(price_buffer); + } HTTPClient http; + http.setTimeout(10000); bool priceUpdated = false; try { @@ -166,7 +175,7 @@ String getBTCprice(void){ if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); - DynamicJsonDocument doc(1024); + StaticJsonDocument<1024> doc; deserializeJson(doc, payload); if (doc.containsKey("last_trade_price")) bitcoin_price = doc["last_trade_price"]; @@ -178,11 +187,14 @@ String getBTCprice(void){ http.end(); } catch(...) { + Serial.println("BTC price HTTP error caught"); http.end(); } } - return "$" + String(bitcoin_price); + static char price_buffer[16]; + snprintf(price_buffer, sizeof(price_buffer), "$%u", bitcoin_price); + return String(price_buffer); } unsigned long mTriggerUpdate = 0; @@ -428,7 +440,7 @@ pool_data getPoolData(void){ if (WiFi.status() != WL_CONNECTED) return pData; //Make first API call to get global hash and current difficulty HTTPClient http; - http.setReuse(true); + http.setTimeout(10000); try { String btcWallet = Settings.BtcWallet; // Serial.println(btcWallet); @@ -448,7 +460,7 @@ pool_data getPoolData(void){ filter["workersCount"] = true; filter["workers"][0]["sessionId"] = true; filter["workers"][0]["hashRate"] = true; - DynamicJsonDocument doc(2048); + StaticJsonDocument<2048> doc; deserializeJson(doc, payload, DeserializationOption::Filter(filter)); //Serial.println(serializeJsonPretty(doc, Serial)); if (doc.containsKey("workersCount")) pData.workersCount = doc["workersCount"].as(); diff --git a/src/utils.cpp b/src/utils.cpp index 05acc8ff..3120e8a2 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -227,16 +227,20 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ //mWorker.extranonce2 = "00000002"; //get coinbase - coinbase_hash_bin = hashlib.sha256(hashlib.sha256(binascii.unhexlify(coinbase)).digest()).digest() - String coinbase = mJob.coinb1 + mWorker.extranonce1 + mWorker.extranonce2 + mJob.coinb2; - Serial.print(" coinbase: "); Serial.println(coinbase); - size_t str_len = coinbase.length()/2; + // Use char buffer instead of String concatenation to avoid memory leaks + static char coinbase_buffer[512]; // Static buffer to avoid repeated allocation + snprintf(coinbase_buffer, sizeof(coinbase_buffer), "%s%s%s%s", + mJob.coinb1.c_str(), mWorker.extranonce1.c_str(), + mWorker.extranonce2.c_str(), mJob.coinb2.c_str()); + Serial.print(" coinbase: "); Serial.println(coinbase_buffer); + size_t str_len = strlen(coinbase_buffer)/2; uint8_t bytearray[str_len]; - size_t res = to_byte_array(coinbase.c_str(), str_len*2, bytearray); + size_t res = to_byte_array(coinbase_buffer, str_len*2, bytearray); #ifdef DEBUG_MINING Serial.print(" extranonce2: "); Serial.println(mWorker.extranonce2); - Serial.print(" coinbase: "); Serial.println(coinbase); + Serial.print(" coinbase: "); Serial.println(coinbase_buffer); Serial.print(" coinbase bytes - size: "); Serial.println(res); for (size_t i = 0; i < res; i++) Serial.printf("%02x", bytearray[i]); diff --git a/src/version.h b/src/version.h index 96ab4773..f401f5c2 100644 --- a/src/version.h +++ b/src/version.h @@ -1,6 +1,6 @@ #ifndef VERSION_H #define VERSION_H -#define CURRENT_VERSION "V1.8.0" +#define CURRENT_VERSION "V1.8.1" #endif // VERSION_H