Jak kopać Malmar Coin?

Kompletny przewodnik kopania MLM na ESP32

Co potrzebujesz?

ESP32

Płytka ESP32 (WROOM, DevKit, NodeMCU)

WiFi

Połączenie z internetem

Arduino IDE

Do wgrania kodu na ESP32

Portfel

Adres MLM do odbierania nagród

Krok po kroku

1

Utwórz portfel

Najpierw potrzebujesz adresu portfela, na który będą wpływać wykopane monety.

Utwórz portfel
2

Zainstaluj Arduino IDE

Pobierz i zainstaluj Arduino IDE ze strony oficjalnej.

Pobierz Arduino IDE
Dodaj obsługę ESP32:
  1. Otwórz Arduino IDE
  2. Przejdź do File → Preferences
  3. W polu "Additional Board Manager URLs" wklej: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  4. Przejdź do Tools → Board → Boards Manager
  5. Wyszukaj "ESP32" i zainstaluj
3

Zainstaluj bibliotekę ArduinoJson

Miner wymaga biblioteki ArduinoJson do komunikacji z poolem.

  1. W Arduino IDE przejdź do Sketch → Include Library → Manage Libraries
  2. Wyszukaj "ArduinoJson"
  3. Zainstaluj najnowszą wersję (autor: Benoit Blanchon)
4

Skopiuj kod minera

Skopiuj poniższy kod i wklej do Arduino IDE.

Ważne! Zmień dane WiFi i adres portfela w kodzie!
/*
 * MalmarCoin ESP32 Dual-Core Miner
 * Wersja: 1.0.0
 * 
 * Multi-core SHA256 miner dla ESP32
 * Wykorzystuje oba rdzenie procesora
 */

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "mbedtls/sha256.h"

// ============================================
// KONFIGURACJA - ZMIEŃ NA SWOJE DANE!
// ============================================

const char* WIFI_SSID = "TWOJA_SIEC_WIFI";
const char* WIFI_PASSWORD = "TWOJE_HASLO_WIFI";

const char* POOL_URL = "http://malmar.portaleai.pl/";
const char* WALLET_ADDRESS = "TWOJ_ADRES_PORTFELA_MLM";

const char* DEVICE_NAME = "ESP32-Miner-1";

// ============================================
// ZMIENNE GLOBALNE
// ============================================

String deviceId;
volatile unsigned long hashCount[2] = {0, 0};
volatile bool miningActive = false;
volatile bool solutionFound = false;
volatile unsigned long foundNonce = 0;

String currentJobId;
String currentBlockTemplate;
int currentDifficulty = 3;

TaskHandle_t miningTask0;
TaskHandle_t miningTask1;

SemaphoreHandle_t xMutex;

unsigned long lastJobTime = 0;
unsigned long lastStatsTime = 0;

// ============================================
// FUNKCJE SHA256
// ============================================

void sha256(const char* input, size_t len, char* output) {
    unsigned char hash[32];
    mbedtls_sha256_context ctx;
    
    mbedtls_sha256_init(&ctx);
    mbedtls_sha256_starts(&ctx, 0);
    mbedtls_sha256_update(&ctx, (const unsigned char*)input, len);
    mbedtls_sha256_finish(&ctx, hash);
    mbedtls_sha256_free(&ctx);
    
    for (int i = 0; i < 32; i++) {
        sprintf(output + (i * 2), "%02x", hash[i]);
    }
    output[64] = '\0';
}

bool checkHash(const char* hash, int difficulty) {
    for (int i = 0; i < difficulty; i++) {
        if (hash[i] != '0') return false;
    }
    return true;
}

// ============================================
// ZADANIE MINING DLA RDZENIA
// ============================================

void miningTaskFunction(void* parameter) {
    int coreId = (int)parameter;
    char hashBuffer[65];
    char inputBuffer[200];
    
    Serial.printf("[Core %d] Mining task started\n", coreId);
    
    while (true) {
        if (miningActive && !solutionFound && currentBlockTemplate.length() > 0) {
            unsigned long nonce = coreId;
            unsigned long maxNonce = 0xFFFFFFFF;
            
            while (miningActive && !solutionFound && nonce < maxNonce) {
                sprintf(inputBuffer, "%s%lu", currentBlockTemplate.c_str(), nonce);
                
                sha256(inputBuffer, strlen(inputBuffer), hashBuffer);
                
                if (checkHash(hashBuffer, currentDifficulty)) {
                    if (xSemaphoreTake(xMutex, portMAX_DELAY)) {
                        if (!solutionFound) {
                            solutionFound = true;
                            foundNonce = nonce;
                            Serial.printf("[Core %d] FOUND! Nonce: %lu, Hash: %s\n", 
                                          coreId, nonce, hashBuffer);
                        }
                        xSemaphoreGive(xMutex);
                    }
                    break;
                }
                
                hashCount[coreId]++;
                nonce += 2;
                
                if (hashCount[coreId] % 10000 == 0) {
                    vTaskDelay(1);
                }
            }
        } else {
            vTaskDelay(100);
        }
    }
}

// ============================================
// KOMUNIKACJA Z POOL
// ============================================

bool getJob() {
    if (WiFi.status() != WL_CONNECTED) {
        return false;
    }
    
    HTTPClient http;
    String url = String(POOL_URL) + "/api.php?action=get_job&wallet=" + 
                 WALLET_ADDRESS + "&device_id=" + deviceId + 
                 "&device_name=" + DEVICE_NAME;
    
    http.begin(url);
    http.setTimeout(10000);
    int httpCode = http.GET();
    
    if (httpCode == 200) {
        String payload = http.getString();
        
        StaticJsonDocument<1024> doc;
        DeserializationError error = deserializeJson(doc, payload);
        
        if (!error && doc["success"]) {
            currentJobId = doc["job_id"].as<String>();
            currentBlockTemplate = doc["block_template"].as<String>();
            currentDifficulty = doc["difficulty"];
            
            Serial.printf("New job: %s, difficulty: %d\n", 
                          currentJobId.c_str(), currentDifficulty);
            
            http.end();
            return true;
        }
    } else {
        Serial.printf("HTTP Error: %d\n", httpCode);
    }
    
    http.end();
    return false;
}

bool submitShare(unsigned long nonce, float hashrate) {
    if (WiFi.status() != WL_CONNECTED) {
        return false;
    }
    
    char hashBuffer[65];
    char inputBuffer[200];
    sprintf(inputBuffer, "%s%lu", currentBlockTemplate.c_str(), nonce);
    sha256(inputBuffer, strlen(inputBuffer), hashBuffer);
    
    HTTPClient http;
    String url = String(POOL_URL) + "/api.php?action=submit_share";
    
    http.begin(url);
    http.addHeader("Content-Type", "application/json");
    http.setTimeout(10000);
    
    StaticJsonDocument<512> doc;
    doc["job_id"] = currentJobId;
    doc["nonce"] = nonce;
    doc["hash"] = String(hashBuffer);
    doc["device_id"] = deviceId;
    doc["wallet"] = WALLET_ADDRESS;
    doc["hashrate"] = hashrate;
    
    String payload;
    serializeJson(doc, payload);
    
    int httpCode = http.POST(payload);
    
    if (httpCode == 200) {
        String response = http.getString();
        
        StaticJsonDocument<512> resDoc;
        deserializeJson(resDoc, response);
        
        if (resDoc["accepted"]) {
            Serial.println("Share accepted!");
            
            if (resDoc["block_found"]) {
                float reward = resDoc["reward"];
                Serial.printf("*** BLOCK FOUND! Reward: %.8f ***\n", reward);
            }
            
            http.end();
            return true;
        } else {
            Serial.println("Share rejected!");
        }
    }
    
    http.end();
    return false;
}

// ============================================
// SETUP
// ============================================

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    Serial.println();
    Serial.println("================================");
    Serial.println("  MalmarCoin ESP32 Miner v1.0");
    Serial.println("================================");
    Serial.println();
    
    // Generuj ID urządzenia
    uint64_t chipid = ESP.getEfuseMac();
    char chipIdStr[17];
    sprintf(chipIdStr, "%016llX", chipid);
    deviceId = String(chipIdStr);
    Serial.printf("Device ID: %s\n", deviceId.c_str());
    Serial.printf("Device Name: %s\n", DEVICE_NAME);
    
    // Połącz z WiFi
    Serial.printf("Connecting to WiFi: %s", WIFI_SSID);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    
    int attempts = 0;
    while (WiFi.status() != WL_CONNECTED && attempts < 30) {
        delay(500);
        Serial.print(".");
        attempts++;
    }
    
    if (WiFi.status() == WL_CONNECTED) {
        Serial.println();
        Serial.println("WiFi connected!");
        Serial.printf("IP: %s\n", WiFi.localIP().toString().c_str());
    } else {
        Serial.println();
        Serial.println("WiFi connection failed! Restarting...");
        delay(3000);
        ESP.restart();
    }
    
    // Stwórz mutex
    xMutex = xSemaphoreCreateMutex();
    
    // Uruchom mining na obu rdzeniach
    xTaskCreatePinnedToCore(
        miningTaskFunction,
        "MiningCore0",
        10000,
        (void*)0,
        1,
        &miningTask0,
        0
    );
    
    xTaskCreatePinnedToCore(
        miningTaskFunction,
        "MiningCore1",
        10000,
        (void*)1,
        1,
        &miningTask1,
        1
    );
    
    Serial.println("Mining tasks created on both cores!");
    Serial.println();
}

// ============================================
// LOOP
// ============================================

void loop() {
    // Sprawdź WiFi
    if (WiFi.status() != WL_CONNECTED) {
        Serial.println("WiFi disconnected! Reconnecting...");
        miningActive = false;
        WiFi.reconnect();
        delay(5000);
        return;
    }
    
    // Pobierz nowy job
    if (millis() - lastJobTime > 30000 || solutionFound || !miningActive) {
        
        if (solutionFound) {
            float totalHashes = hashCount[0] + hashCount[1];
            float elapsed = (millis() - lastJobTime) / 1000.0;
            float hashrate = elapsed > 0 ? totalHashes / elapsed : 0;
            
            miningActive = false;
            submitShare(foundNonce, hashrate);
            
            solutionFound = false;
            foundNonce = 0;
            hashCount[0] = 0;
            hashCount[1] = 0;
        }
        
        if (getJob()) {
            miningActive = true;
            lastJobTime = millis();
        } else {
            Serial.println("Failed to get job, retrying in 5s...");
            delay(5000);
        }
    }
    
    // Wyświetl statystyki
    if (millis() - lastStatsTime > 5000) {
        float totalHashes = hashCount[0] + hashCount[1];
        float elapsed = (millis() - lastJobTime) / 1000.0;
        float hashrate = elapsed > 0 ? totalHashes / elapsed : 0;
        
        Serial.printf("Hashrate: %.2f H/s | Core0: %lu | Core1: %lu | Total: %.0f\n",
                      hashrate, hashCount[0], hashCount[1], totalHashes);
        
        lastStatsTime = millis();
    }
    
    delay(100);
}
5

Skonfiguruj minera

Zmień następujące wartości w kodzie:

WIFI_SSID Nazwa Twojej sieci WiFi
WIFI_PASSWORD Hasło do WiFi
WALLET_ADDRESS Twój adres portfela MLM
POOL_URL http://malmar.portaleai.pl/malmar
DEVICE_NAME Nazwa urządzenia (opcjonalnie)
6

Wgraj kod na ESP32

  1. Podłącz ESP32 kablem USB do komputera
  2. W Arduino IDE wybierz:
    • Tools → Board → ESP32 Dev Module
    • Tools → Port → (wybierz port COM ESP32)
  3. Kliknij przycisk Upload (strzałka w prawo)
  4. Poczekaj na zakończenie wgrywania
7

Sprawdź działanie

Otwórz Serial Monitor (Tools → Serial Monitor) i ustaw prędkość na 115200 baud.

Powinieneś zobaczyć:

================================
MalmarCoin ESP32 Miner v1.0
================================
Device ID: A1B2C3D4E5F6...
Connecting to WiFi: TwojaSSiec...
WiFi connected!
IP: 192.168.1.100
Mining tasks created on both cores!
New job: abc123..., difficulty: 3
Hashrate: 25.50 H/s | Core0: 12800 | Core1: 12750

Twój miner działa! Sprawdź statystyki na:

Informacje o Pool

Pool URL: http://malmar.portaleai.pl/malmar
Algorytm: SHA-256
Nagroda za blok: 10 MLM
Trudność share: 3
Minimalna wypłata: 1 MLM
Prowizja: 0%

Często zadawane pytania

ESP32 osiąga około 20-30 H/s przy kopaniu SHA-256. Wykorzystując oba rdzenie (dual-core) możesz zwiększyć wydajność o około 80-90% w porównaniu do single-core.

ESP32 podczas intensywnego kopania zużywa około 150-200 mA przy 5V, co daje około 0.75-1W. Miesięczny koszt prądu to kilka groszy.

Tak! Możesz podłączyć dowolną liczbę ESP32 do tego samego portfela. Każde urządzenie będzie widoczne osobno w panelu Pool. Wystarczy zmienić DEVICE_NAME dla każdego urządzenia.

  • Sprawdź siłę sygnału WiFi
  • Upewnij się, że serwer jest dostępny
  • Sprawdź czy adres portfela jest poprawny
  • Zrestartuj ESP32
  • Sprawdź Serial Monitor dla błędów

Monety są przyznawane natychmiast po znalezieniu bloku. Nagroda za blok wynosi 10 MLM. Możesz śledzić swoje saldo w Portfelu.

ESP8266 ma tylko jeden rdzeń i mniej RAM, więc będzie znacznie wolniejszy (około 5-10 H/s). Zalecamy używanie ESP32 dla lepszej wydajności. Kod wymaga modyfikacji dla ESP8266.

Rozwiązywanie problemów

"Failed to connect to WiFi"
  • Sprawdź nazwę sieci WiFi (SSID) - wielkość liter ma znaczenie
  • Sprawdź hasło WiFi
  • Upewnij się, że sieć działa na 2.4 GHz (ESP32 nie obsługuje 5 GHz)
"Failed to get job"
  • Sprawdź adres POOL_URL
  • Upewnij się, że serwer jest dostępny
  • Sprawdź poprawność adresu portfela
"Share rejected"
  • To normalne - niektóre share mogą być odrzucone
  • Sprawdź czy job nie wygasł (timeout)
  • Upewnij się, że zegar ESP32 jest zsynchronizowany
Błąd kompilacji w Arduino IDE
  • Upewnij się, że zainstalowałeś ESP32 board
  • Zainstaluj bibliotekę ArduinoJson
  • Wybierz odpowiednią płytkę (ESP32 Dev Module)

Gotowy do kopania?

Dołącz do społeczności minerów Malmar Coin!