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 portfel2
Zainstaluj Arduino IDE
Pobierz i zainstaluj Arduino IDE ze strony oficjalnej.
Pobierz Arduino IDEDodaj obsługę ESP32:
- Otwórz Arduino IDE
- Przejdź do File → Preferences
- W polu "Additional Board Manager URLs" wklej:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json - Przejdź do Tools → Board → Boards Manager
- Wyszukaj "ESP32" i zainstaluj
3
Zainstaluj bibliotekę ArduinoJson
Miner wymaga biblioteki ArduinoJson do komunikacji z poolem.
- W Arduino IDE przejdź do Sketch → Include Library → Manage Libraries
- Wyszukaj "ArduinoJson"
- 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
- Podłącz ESP32 kablem USB do komputera
- W Arduino IDE wybierz:
- Tools → Board → ESP32 Dev Module
- Tools → Port → (wybierz port COM ESP32)
- Kliknij przycisk Upload (strzałka w prawo)
- 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!