Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -611,30 +611,37 @@ monitor_filters =
board_build.arduino.memory_type = qio_opi
monitor_speed = 115200
upload_speed = 115200
# 2 x 4.5MB app, 6.875MB SPIFFS
;board_build.partitions = large_spiffs_16MB.csv
;board_build.partitions = default_8MB.csv
board_build.partitions = huge_app.csv
;board_build.partitions = default.csv
build_flags =
; T-Display-S3 = 16MB flash: dual app slots (OTA) + SPIFFS
board_upload.flash_size = 16MB
board_build.partitions = default_16MB.csv
build_flags =
-D LV_LVGL_H_INCLUDE_SIMPLE
-D BOARD_HAS_PSRAM
-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
; mot de passe OTA hors dépôt : exporter NERDMINER_OTA_PWD avant le build
-D OTA_PASSWORD='"${sysenv.NERDMINER_OTA_PWD}"'
;-D DEBUG_MINING=1
;To enable I2C mining in future: -D ENABLE_I2C_MINING=1
lib_deps =
lib_deps =
https://github.com/takkaO/OpenFontRender#v1.2
bblanchon/ArduinoJson@^6.21.5
https://github.com/tzapu/WiFiManager.git#v2.0.17
mathertel/oneButton@^2.6.1
arduino-libraries/NTPClient@^3.2.1
lib_ignore =
lib_ignore =
HANSOLOminerv2

; Flash par WiFi : pio run -e NerdminerV2-OTA -t upload --upload-port <ip du mineur>
; pio run -e NerdminerV2-OTA -t uploadfs --upload-port <ip> (config SPIFFS)
[env:NerdminerV2-OTA]
extends = env:NerdminerV2
upload_protocol = espota
upload_flags = --auth=${sysenv.NERDMINER_OTA_PWD}

;--------------------------------------------------------------------

[env:Lilygo-T-Embed]
Expand Down
113 changes: 107 additions & 6 deletions src/NerdMinerV2.ino.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@
#endif

#include <soc/soc_caps.h>
#include <ArduinoOTA.h>
#include <ESPmDNS.h>
#include <SPIFFS.h>
#include "version.h"

#ifndef OTA_PASSWORD
#define OTA_PASSWORD ""
#endif
//Refuse un firmware OTA sans mot de passe (NERDMINER_OTA_PWD absent au build)
static_assert(sizeof(OTA_PASSWORD) > 1, "OTA password vide : exporter NERDMINER_OTA_PWD avant le build");
//#define HW_SHA256_TEST

//3 seconds WDT
Expand Down Expand Up @@ -53,6 +63,10 @@ extern monitor_data mMonitor;
unsigned long start = millis();
const char* ntpServer = "pool.ntp.org";

//Task handles, global so the OTA hooks can suspend everything during a flash
TaskHandle_t minerTask1 = NULL, minerTask2 = NULL;
TaskHandle_t monitorTask = NULL, stratumTask = NULL;

//void runMonitor(void *name);


Expand Down Expand Up @@ -126,6 +140,13 @@ void setup()
/******** INIT WIFI ************/
init_WifiManager();

#ifdef OTA_ONLY_TEST
//Diagnostic build: bring up WiFi + OTA only, no miner/monitor/stratum tasks.
Serial.println("OTA_ONLY_TEST: no mining tasks started");
vTaskPrioritySet(NULL, 4);
return;
#endif

/******** CREATE TASK TO PRINT SCREEN *****/
//tft.pushImage(0, 0, MinerWidth, MinerHeight, MinerScreen);
// Higher prio monitor task
Expand All @@ -134,21 +155,21 @@ void setup()
static const char monitor_name[] = "(Monitor)";
#if defined(CONFIG_IDF_TARGET_ESP32)
// Increased stack for ESP32 classic due to NVS operations
BaseType_t res1 = xTaskCreatePinnedToCore(runMonitor, "Monitor", 9500, (void*)monitor_name, 5, NULL,1);
BaseType_t res1 = xTaskCreatePinnedToCore(runMonitor, "Monitor", 9500, (void*)monitor_name, 5, &monitorTask,1);
#else
BaseType_t res1 = xTaskCreatePinnedToCore(runMonitor, "Monitor", 10000, (void*)monitor_name, 5, NULL,1);
BaseType_t res1 = xTaskCreatePinnedToCore(runMonitor, "Monitor", 10000, (void*)monitor_name, 5, &monitorTask,1);
#endif

/******** CREATE STRATUM TASK *****/
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);
BaseType_t res2 = xTaskCreatePinnedToCore(runStratumWorker, "Stratum", 12000, (void*)stratum_name, 4, &stratumTask,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);
BaseType_t res2 = xTaskCreatePinnedToCore(runStratumWorker, "Stratum", 13500, (void*)stratum_name, 4, &stratumTask,1);
#else
BaseType_t res2 = xTaskCreatePinnedToCore(runStratumWorker, "Stratum", 15000, (void*)stratum_name, 4, NULL,1);
BaseType_t res2 = xTaskCreatePinnedToCore(runStratumWorker, "Stratum", 15000, (void*)stratum_name, 4, &stratumTask,1);
#endif

/******** CREATE MINER TASKS *****/
Expand All @@ -158,7 +179,6 @@ void setup()

// Start mining tasks
//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
Expand Down Expand Up @@ -201,6 +221,80 @@ void app_error_fault_handler(void *arg) {
esp_restart();
}

//OTA over WiFi: firmware (upload) and SPIFFS config (uploadfs) via espota.
//Init deferred until WiFi is up; handle() below is a non-blocking UDP poll.
static bool s_ota_ready = false;
static volatile bool s_ota_in_progress = false;
static volatile uint32_t s_ota_last_progress_ms = 0;

//ArduinoOTA.handle() blocks inside loop() for the whole transfer, so a stalled
//transfer (dead sender, wifi drop) can never be detected from loop() itself:
//without this task the miners would stay suspended forever.
static void otaStallWatchdog(void *unused) {
while (true) {
vTaskDelay(5000 / portTICK_PERIOD_MS);
if (s_ota_in_progress && (millis() - s_ota_last_progress_ms) > 90000) {
Serial.println("OTA: stalled >90s, restarting");
ESP.restart();
}
}
}

static void setupOTA() {
uint8_t mac[6];
WiFi.macAddress(mac);
static char host[24];
snprintf(host, sizeof(host), "nerdminer-%02x%02x", mac[4], mac[5]);
ArduinoOTA.setHostname(host);
ArduinoOTA.setPassword(OTA_PASSWORD);
ArduinoOTA.onStart([]() {
//Ask the miners to idle at a safe point and release the SHA engine lock,
//then give in-flight jobs a moment to finish. Suspending them mid-hash would
//keep that lock and deadlock Update.end()'s image SHA-256 verification.
ota_active = true;
vTaskDelay(300 / portTICK_PERIOD_MS);
if (minerTask1) esp_task_wdt_delete(minerTask1);
if (minerTask2) esp_task_wdt_delete(minerTask2);
//Suspend the monitor (screen redraw, SPI) and stratum (pool socket): they
//don't touch the SHA engine, so a plain suspend is safe and frees CPU/SPI.
if (monitorTask) vTaskSuspend(monitorTask);
if (stratumTask) vTaskSuspend(stratumTask);
if (ArduinoOTA.getCommand() == U_SPIFFS)
SPIFFS.end();
s_ota_last_progress_ms = millis();
s_ota_in_progress = true;
Serial.println("OTA: transfer started, mining suspended");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
s_ota_last_progress_ms = millis();
static unsigned int last = 0;
if (progress < last) last = 0; //new transfer
if (progress - last >= 131072 || progress == total) {
last = progress;
Serial.printf("OTA: %u/%u\n", progress, total);
}
});
ArduinoOTA.onEnd([]() {
s_ota_in_progress = false; //device reboots right after
});
ArduinoOTA.onError([](ota_error_t error) {
//No ESP.restart() here: ArduinoOTA reports the error detail to the sender
//AFTER this callback (Update.printError). Rearm the stall watchdog so the
//device reboots ~10s later instead, once the error left the socket.
Serial.printf("OTA: error %u, reboot in ~10s\n", error);
s_ota_in_progress = true;
s_ota_last_progress_ms = millis() - 20000;
});
ArduinoOTA.begin();
//Deployed version readable from the LAN: avahi-browse -r _arduino._tcp
MDNS.addServiceTxt("arduino", "tcp", "fw_version", CURRENT_VERSION);
#ifdef AUTO_VERSION
MDNS.addServiceTxt("arduino", "tcp", "fw_build", AUTO_VERSION);
#endif
xTaskCreate(otaStallWatchdog, "OTAdog", 2048, NULL, 1, NULL);
Serial.printf("OTA: ready as %s.local\n", host);
}

void loop() {
// keep watching the push buttons:
#ifdef PIN_BUTTON_1
Expand All @@ -216,5 +310,12 @@ void loop() {
#endif
wifiManagerProcess(); // avoid delays() in loop when non-blocking and other long running code

if (!s_ota_ready && WiFi.status() == WL_CONNECTED) {
setupOTA();
s_ota_ready = true;
}
if (s_ota_ready)
ArduinoOTA.handle();

vTaskDelay(50 / portTICK_PERIOD_MS);
}
10 changes: 10 additions & 0 deletions src/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ extern TSettings Settings;

IPAddress serverIP(1, 1, 1, 1); //Temporally save poolIPaddres

//Set by the OTA onStart hook. The hw miner holds the SHA engine lock
//(esp_sha_acquire_hardware) while hashing; suspending it mid-job would keep
//that lock and deadlock Update.end(), which verifies the image SHA-256.
//Instead the miners watch this flag and idle at a safe point, lock released.
volatile bool ota_active = false;

//Global work data
static WiFiClient client;
static miner_data mMiner; //Global miner data (Create a miner class TODO)
Expand Down Expand Up @@ -594,6 +600,7 @@ void minerWorkerSw(void * task_id)
uint32_t wdt_counter = 0;
while (1)
{
if (ota_active) { vTaskDelay(100 / portTICK_PERIOD_MS); continue; } //idle during OTA
{
std::lock_guard<std::mutex> lock(s_job_mutex);
if (result)
Expand Down Expand Up @@ -800,6 +807,9 @@ void minerWorkerHw(void * task_id)

while (1)
{
//Idle during OTA at a safe point: never suspended while holding the SHA
//engine lock, so Update.end()'s image SHA-256 verification can proceed.
if (ota_active) { vTaskDelay(100 / portTICK_PERIOD_MS); continue; }
{
std::lock_guard<std::mutex> lock(s_job_mutex);
if (result)
Expand Down
3 changes: 3 additions & 0 deletions src/mining.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
void runMonitor(void *name);

void runStratumWorker(void *name);

//Set true by the OTA onStart hook so the miners idle and release the SHA engine.
extern volatile bool ota_active;
void runMiner(void *name);

void minerWorkerSw(void * task_id);
Expand Down