Mini DUCO Mining Rig esp8266, Mining Duino Coin dengan Esp01 board

Mini DUCO Mining Rig esp8266, Mining Duino Coin dengan Esp01 board

Mini DUCO Mining Rig esp8266, Mining Duino Coin dengan Esp01 board

Sebelumnya sudah kita buatkan langkah-langkahnya untuk melakukan mining Duino Coin menggunakan mikrokontroller berbasis esp8266 menggunakan nodeMCU pada postingan ini. Memahami dari konsep tersebut, saya berpikir untuk membuat mining rig dengan menggunakan esp-01, toh sama-sama keluarga esp8266. Nah, mengapa ESP-01? Dalam proses mining Duino Coin, hal yang perlu dimiliki oleh perangkat penambang adalah konektivitas dengan internet dan catu daya yang memadai. Dalam 2 hal tersebut, ESP-01 sudah dapat melakukannya. Selain itu, harga ESP-01 yang lebih murah dan  dimensinya yang kecil menjadikan ESP-01 layak kita coba untuk dijadikan perangkat penambang.

Apa itu Mining RIG?

Secara umum mining rig adalah perangkat "spesial" yang dikhususkan untuk proses mining. Nah spesial yang seperti apa? Special disini adalah desain yang dikususkan yang sudah menggambarkan berapa hashrate yang dihasilkan, berpa total jumlah penambang, berapa total daya yang digunakan, dll. Saat kita menggunakan 1 perangkat penambang, biasanya kita akan mendapatkan hashrate yang rendah. Hash Rate adalah ukuran kekuatan komputasi dalam jaringan cryptocurrency proof-of-work (PoW). Semakin besar hashrate maka semakin cepat proses penambangan yang dilakukan. Tetapi hal tersebut dianggap sebagai tindakan yang tidak fair oleh sistem DUCO. Untuk mencegah orang membuat ladang/pool penambangan besar yang melarang pengguna "biasa" untuk menambang, kami telah membuat sistem yang disebut penurunan efisiensi Kolka, yang menyebabkan setiap penambang tambahan mendapat penghasilan sedikit lebih sedikit dari yang sebelumnya. Rumusnya adalah efisiensi perangkat penambang terakhir * 100 - penurunan %, penurunan untuk AVR dan ESP adalah 4% dan untuk PC 20%. Nah rumus tersebut dikenal dengan Efficiency drop, yang intinya dalam melakukan mining DUCO apabila kita menggunakan banyak perangkat, maka hasilnya juga gak akan signifikan.

Desain perangkat

Dalam membuat perangkat ini, diperlukan bahan-bahan:

Dengan menggunakan power supply yang arus maxny hanya 1 A, maka supply ini cukup untuk 6-8 modul esp01 dengan sumber tegangannya menggunakan 2 modul AMS117 yang outpotnya 3.3V mengingat esp-01 menggunakan tegangan kerja sebesar 3.3V. Nah dengan bahan-bahan tersebut, maka rangkaiannya akan seperti berikut:

*Basic Setup untuk 1 modul

Dari skematic dasar dapat dilihat bahwa hanya 3 pin yang digunakan yaitu GND, VCC dan CH_PD. pin CH_PD dihubungkan ke VCC dimaksudkan agar pin CH_PD bernilai High yang dimaksudkan Chip dalam kondisi aktif. Nah dari rangkaian dasar diatas, kita kembangkan untuk dihubungkan dengan 4 buah modul ESP01 di setiap modul AMS117.

DUCO Mining Rig esp8266
Menggunakan switch On/Off sebelum modul AMS117

Silahkan kembangkan sesuai kreasi teman-teman ya, hasil dari kreasi saya seperti berikut (versi pcb Bolong) menggunakan 8 buah ESP-01:

DUCO Mining Rig esp8266

Mining Duino Coin with Esp 01 board

Upload Program ke Board ESP-01

Setelah perangkat selesai di buat, langkah selanjutnya ialah memasukkan program ke modul ESP-01. Ada beberapa hal yang perlu diperhatikan dalam melakukan proses pemrograman ini:

  • Modul ESP 01 tidak memiliki interface USB sehingga tidak bersifat plug n run seperti modul wemos, node MCU atau esp 32. Dengan demikian kita memerlukan bantuan ESP-01 Programer adapter untuk proses ini. 
  • Pada dasarnya modul ESP01 merupakan modul Wifi yang diperuntukkan sebagai pembantu perangkat mikrokontroller seperti Arduino UNO/NANO/MEGA untuk terkoneksi dengan jaringan wifi. Secara default dipasaran, modul ESP-01 telah diflash dengan firmware AT COMMAND. Nah dalam kondisi default ini, kita tidak dapat melakukan upload program ke modul ESP-01. Kita harus flash dulu modul ESP-01 menggunakan firmware nodeMCU. Cara flashing nya dapat di lihat ditutorial ini.

Dalam tutorial ini, saya asumsikan bahwa modul-modul ESP01 yang akan kiga gunakan sebagai penambang sudah diflash dengan firmware NodeMCU terlebih dahulu. Untuk program nya dapat kita gunakan program dibawah dengan mengubah beberapa nilai variabel. Pastikan kita sudah memiliki akun Duco Wallet. Silahkan simak post ini untuk cara registrasi Duco Wallet.

/*
   ____  __  __  ____  _  _  _____       ___  _____  ____  _  _
  (  _ \(  )(  )(_  _)( \( )(  _  )___  / __)(  _  )(_  _)( \( )
   )(_) ))(__)(  _)(_  )  (  )(_)((___)( (__  )(_)(  _)(_  )  (
  (____/(______)(____)(_)\_)(_____)     \___)(_____)(____)(_)\_)
  Official code for ESP8266 boards                   version 3.5

  Duino-Coin Team & Community 2019-2022 © MIT Licensed
  https://duinocoin.com
  https://github.com/revoxhere/duino-coin

  If you don't know where to start, visit official website and navigate to
  the Getting Started page. Have fun mining!
*/

/* If optimizations cause problems, change them to -O0 (the default)
  NOTE: For even better optimizations also edit your Crypto.h file.
  On linux that file can be found in the following location:
  ~/.arduino15//packages/esp8266/hardware/esp8266/3.0.2/cores/esp8266/ */
#pragma GCC optimize ("-Ofast")

/* If during compilation the line below causes a
  "fatal error: arduinoJson.h: No such file or directory"
  message to occur; it means that you do NOT have the
  ArduinoJSON library installed. To install it,
  go to the below link and follow the instructions:
  https://github.com/revoxhere/duino-coin/issues/832 */
#include <ArduinoJson.h>

/* If during compilation the line below causes a
  "fatal error: Crypto.h: No such file or directory"
  message to occur; it means that you do NOT have the
  latest version of the ESP8266/Arduino Core library.
  To install/upgrade it, go to the below link and
  follow the instructions of the readme file:
  https://github.com/esp8266/Arduino */
#include <bearssl/bearssl.h>
//#include <TypeConversion.h>

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <Ticker.h>
#include <ESP8266WebServer.h>

// Uncomment the line below if you wish to use a DHT sensor (Duino IoT beta)
// #define USE_DHT

// Uncomment the line below if you wish to register for IOT updates with an MQTT broker
// #define USE_MQTT

// If you don't know what MQTT means check this link:
// https://www.techtarget.com/iotagenda/definition/MQTT-MQ-Telemetry-Transport

#ifdef USE_DHT
float temp = 0.0;
float hum = 0.0;

// Install "DHT sensor library" if you get an error
#include <DHT.h>
// Change D3 to the pin you've connected your sensor to
#define DHTPIN D3
// Set DHT11 or DHT22 accordingly
#define DHTTYPE DHT11

DHT dht(DHTPIN, DHTTYPE);
#endif

#ifdef USE_MQTT
// Install "PubSubClient" if you get an error
#include <PubSubClient.h>

long lastMsg = 0;

// Change the part in brackets to your MQTT broker address
#define mqtt_server "broker.hivemq.com"
// broker.hivemq.com is for testing purposes, change it to your broker address

// Change this to your MQTT broker port
#define mqtt_port 1883
// If you want to use user and password for your MQTT broker, uncomment the line below
// #define mqtt_use_credentials

// Change the part in brackets to your MQTT broker username
#define mqtt_user "My cool mqtt username"
// Change the part in brackets to your MQTT broker password
#define mqtt_password "My secret mqtt pass"

// Change this if you want to send data to the topic every X milliseconds
#define mqtt_update_time 5000

// Change the part in brackets to your MQTT humidity topic
#define humidity_topic "sensor/humidity"
// Change the part in brackets to your MQTT temperature topic
#define temperature_topic "sensor/temperature"

WiFiClient espClient;
PubSubClient mqttClient(espClient);

void mqttReconnect()
{
  // Loop until we're reconnected
  while (!mqttClient.connected())
  {
    Serial.print("Attempting MQTT connection...");

    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);

    // Attempt to connect
#ifdef mqtt_use_credentials
    if (mqttClient.connect("ESP8266Client", mqtt_user, mqtt_password))
#else
    if (mqttClient.connect(clientId.c_str()))
#endif
    {
      Serial.println("connected");
    }
    else
    {
      Serial.print("failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}
#endif

namespace
{
// Change the part in brackets to your Duino-Coin username
const char *DUCO_USER = "**********";  //user dari duco wallet
// Change the part in brackets to your mining key (if you have enabled it in the wallet)
const char *MINER_KEY = "**********";  //mining key ( lihat di duco wallet)
// Change the part in brackets to your WiFi name
const char *SSID = "**********";       //nama wifi yang akan digunakan
// Change the part in brackets to your WiFi password
const char *PASSWORD = "**********";   //password wifinya
// Change the part in brackets if you want to set a custom miner name (use Auto to autogenerate, None for no name)
const char *RIG_IDENTIFIER = "**********";  //nama perangkat, harus beda tiap perangkat
// Set to true to use the 160 MHz overclock mode (and not get the first share rejected)
const bool USE_HIGHER_DIFF = true;
// Set to true if you want to host the dashboard page (available on ESPs IP address)
const bool WEB_DASHBOARD = false;
// Set to true if you want to update hashrate in browser without reloading the page
const bool WEB_HASH_UPDATER = false;
// Set to false if you want to disable the onboard led blinking when finding shares
const bool LED_BLINKING = true;

/* Do not change the lines below. These lines are static and dynamic variables
   that will be used by the program for counters and measurements. */
const char * DEVICE = "ESP8266";
const char * POOLPICKER_URL[] = {"https://server.duinocoin.com/getPool"};
const char * MINER_BANNER = "Official ESP8266 Miner";
const char * MINER_VER = "3.5";
unsigned int share_count = 0;
unsigned int port = 0;
unsigned int difficulty = 0;
float hashrate = 0;
String AutoRigName = "";
String host = "";
String node_id = "";

const char WEBSITE[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<!--
    Duino-Coin self-hosted dashboard
    MIT licensed
    Duino-Coin official 2019-2022
    https://github.com/revoxhere/duino-coin
    https://duinocoin.com
-->
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Duino-Coin @@DEVICE@@ dashboard</title>
    <link rel="stylesheet" href="https://server.duinocoin.com/assets/css/mystyles.css">
    <link rel="shortcut icon" href="https://github.com/revoxhere/duino-coin/blob/master/Resources/duco.png?raw=true">
    <link rel="icon" type="image/png" href="https://github.com/revoxhere/duino-coin/blob/master/Resources/duco.png?raw=true">
</head>
<body>
    <section class="section">
        <div class="container">
            <h1 class="title">
                <img class="icon" src="https://github.com/revoxhere/duino-coin/blob/master/Resources/duco.png?raw=true">
                @@DEVICE@@ <small>(@@ID@@)</small>
            </h1>
            <p class="subtitle">
                Self-hosted, lightweight, official dashboard for your <strong>Duino-Coin</strong> miner
            </p>
        </div>
        <br>
        <div class="container">
            <div class="columns">
                <div class="column">
                    <div class="box">
                        <p class="subtitle">
                            Mining statistics
                        </p>
                        <div class="columns is-multiline">
                            <div class="column" style="min-width:15em">
                                <div class="title is-size-5 mb-0">
                                    <span id="hashratex">@@HASHRATE@@</span>kH/s
                                </div>
                                <div class="heading is-size-5">
                                    Hashrate
                                </div>
                            </div>
                            <div class="column" style="min-width:15em">
                                <div class="title is-size-5 mb-0">
                                    @@DIFF@@
                                </div>
                                <div class="heading is-size-5">
                                    Difficulty
                                </div>
                            </div>
                            <div class="column" style="min-width:15em">
                                <div class="title is-size-5 mb-0">
                                    @@SHARES@@
                                </div>
                                <div class="heading is-size-5">
                                    Shares
                                </div>
                            </div>
                            <div class="column" style="min-width:15em">
                                <div class="title is-size-5 mb-0">
                                    @@NODE@@
                                </div>
                                <div class="heading is-size-5">
                                    Node
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="column">
                    <div class="box">
                        <p class="subtitle">
                            Device information
                        </p>
                        <div class="columns is-multiline">
                            <div class="column" style="min-width:15em">
                                <div class="title is-size-5 mb-0">
                                    @@DEVICE@@
                                </div>
                                <div class="heading is-size-5">
                                    Device type
                                </div>
                            </div>
                            <div class="column" style="min-width:15em">
                                <div class="title is-size-5 mb-0">
                                    @@ID@@
                                </div>
                                <div class="heading is-size-5">
                                    Device ID
                                </div>
                            </div>
                            <div class="column" style="min-width:15em">
                                <div class="title is-size-5 mb-0">
                                    @@MEMORY@@
                                </div>
                                <div class="heading is-size-5">
                                    Free memory
                                </div>
                            </div>
                            <div class="column" style="min-width:15em">
                                <div class="title is-size-5 mb-0">
                                    @@VERSION@@
                                </div>
                                <div class="heading is-size-5">
                                    Miner version
                                </div>
                            </div>
)====="
#ifdef USE_DHT
"                            <div class=\"column\" style=\"min-width:15em\">"
"                                <div class=\"title is-size-5 mb-0\">"
"                                    @@TEMP@@ °C"
"                                </div>"
"                                <div class=\"heading is-size-5\">"
"                                    Temperature"
"                                </div>"
"                            </div>"
"                            <div class=\"column\" style=\"min-width:15em\">"
"                                <div class=\"title is-size-5 mb-0\">"
"                                    @@HUM@@ %"
"                                </div>"
"                                <div class=\"heading is-size-5\">"
"                                    Humidity"
"                                </div>"
"                            </div>"
#endif
  R"=====(
                        </div>
                    </div>
                </div>
            </div>
            <br>
            <div class="has-text-centered">
                <div class="title is-size-6 mb-0">
                    Hosted on
                    <a href="http://@@IP_ADDR@@">
                        http://<b>@@IP_ADDR@@</b>
                    </a>
                    &bull;
                    <a href="https://duinocoin.com">
                        duinocoin.com
                    </a>
                    &bull;
                    <a href="https://github.com/revoxhere/duino-coin">
                        github.com/revoxhere/duino-coin
                    </a>
                </div>
            </div>
        </div>
        <script>
            setInterval(function(){
                getData();
            }, 3000);
            
            function getData() {
                var xhttp = new XMLHttpRequest();
                xhttp.onreadystatechange = function() {
                    if (this.readyState == 4 && this.status == 200) {
                        document.getElementById("hashratex").innerHTML = this.responseText;
                    }
                };
                xhttp.open("GET", "hashrateread", true);
                xhttp.send();
            }
        </script>
    </section>
</body>
</html>
)=====";

ESP8266WebServer server(80);

void hashupdater(){ //update hashrate every 3 sec in browser without reloading page
  server.send(200, "text/plain", String(hashrate / 1000));
  Serial.println("Update hashrate on page");
};

void UpdateHostPort(String input) {
  // Thanks @ricaun for the code
  DynamicJsonDocument doc(256);
  deserializeJson(doc, input);
  const char* name = doc["name"];
  
  host = String((const char*)doc["ip"]);
  port = int(doc["port"]);
  node_id = String(name);

  Serial.println("Poolpicker selected the best mining node: " + node_id);
}

String httpGetString(String URL) {
  String payload = "";
  WiFiClientSecure client;
  client.setInsecure();
  HTTPClient http;
  
  if (http.begin(client, URL)) {
    int httpCode = http.GET();
    
    if (httpCode == HTTP_CODE_OK) payload = http.getString();
    else Serial.printf("Error fetching node from poolpicker: %s\n", http.errorToString(httpCode).c_str());

    http.end();
  }
  return payload;
}

void UpdatePool() {
  String input = "";
  int waitTime = 1;
  int poolIndex = 0;
  int poolSize = sizeof(POOLPICKER_URL) / sizeof(char*);

  while (input == "") {
    Serial.println("Fetching mining node from the poolpicker in " + String(waitTime) + "s");
    input = httpGetString(POOLPICKER_URL[poolIndex]);
    poolIndex += 1;

    // Check if pool index needs to roll over
    if( poolIndex >= poolSize ){
      poolIndex %= poolSize;
      delay(waitTime * 1000);

      // Increase wait time till a maximum of 32 seconds (addresses: Limit connection requests on failure in ESP boards #1041)
      waitTime *= 2;
      if( waitTime > 32 )
        waitTime = 32;
    }
  }

  // Setup pool with new input
  UpdateHostPort(input);
}

WiFiClient client;
String client_buffer = "";
String chipID = "";
String START_DIFF = "";

// Loop WDT... please don't feed me...
// See lwdtcb() and lwdtFeed() below
Ticker lwdTimer;
#define LWD_TIMEOUT   20000

unsigned long lwdCurrentMillis = 0;
unsigned long lwdTimeOutMillis = LWD_TIMEOUT;

#define END_TOKEN  '\n'
#define SEP_TOKEN  ','

#define LED_BUILTIN 2

#define BLINK_SETUP_COMPLETE 2
#define BLINK_CLIENT_CONNECT 3
#define BLINK_RESET_DEVICE   5

void SetupWifi() {
  Serial.println("Connecting to: " + String(SSID));
  WiFi.mode(WIFI_STA); // Setup ESP in client mode
  WiFi.setSleepMode(WIFI_NONE_SLEEP);
  WiFi.begin(SSID, PASSWORD);

  int wait_passes = 0;
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (++wait_passes >= 10) {
      WiFi.begin(SSID, PASSWORD);
      wait_passes = 0;
    }
  }

  Serial.println("\n\nSuccessfully connected to WiFi");
  Serial.println("Local IP address: " + WiFi.localIP().toString());
  Serial.println("Rig name: " + String(RIG_IDENTIFIER));
  Serial.println();

  UpdatePool();
}

void SetupOTA() {
  // Prepare OTA handler
  ArduinoOTA.onStart([]() {
    Serial.println("Start");
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });

  ArduinoOTA.setHostname(RIG_IDENTIFIER); // Give port a name not just address
  ArduinoOTA.begin();
}

void blink(uint8_t count, uint8_t pin = LED_BUILTIN) {
  if (LED_BLINKING){
    uint8_t state = HIGH;

    for (int x = 0; x < (count << 1); ++x) {
      digitalWrite(pin, state ^= HIGH);
      delay(50);
    }
  } else {
    digitalWrite(LED_BUILTIN, HIGH);
  }
}

void RestartESP(String msg) {
  Serial.println(msg);
  Serial.println("Restarting ESP...");
  blink(BLINK_RESET_DEVICE);
  ESP.reset();
}

// Our new WDT to help prevent freezes
// code concept taken from https://sigmdel.ca/michel/program/esp8266/arduino/watchdogs2_en.html
void ICACHE_RAM_ATTR lwdtcb(void) {
  if ((millis() - lwdCurrentMillis > LWD_TIMEOUT) || (lwdTimeOutMillis - lwdCurrentMillis != LWD_TIMEOUT))
    RestartESP("Loop WDT Failed!");
}

void lwdtFeed(void) {
  lwdCurrentMillis = millis();
  lwdTimeOutMillis = lwdCurrentMillis + LWD_TIMEOUT;
}

void VerifyWifi() {
  while (WiFi.status() != WL_CONNECTED || WiFi.localIP() == IPAddress(0, 0, 0, 0))
    WiFi.reconnect();
}

void handleSystemEvents(void) {
  VerifyWifi();
  ArduinoOTA.handle();
  yield();
}

void waitForClientData(void) {
  client_buffer = "";

  while (client.connected()) {
    if (client.available()) {
      client_buffer = client.readStringUntil(END_TOKEN);
      if (client_buffer.length() == 1 && client_buffer[0] == END_TOKEN)
        client_buffer = "???\n"; // NOTE: Should never happen

      break;
    }
    handleSystemEvents();
  }
}

void ConnectToServer() {
  if (client.connected())
    return;

  Serial.println("\n\nConnecting to the Duino-Coin server...");
  while (!client.connect(host.c_str(), port));

  waitForClientData();
  Serial.println("Connected to the server. Server version: " + client_buffer );
  blink(BLINK_CLIENT_CONNECT); // Sucessfull connection with the server
}

bool max_micros_elapsed(unsigned long current, unsigned long max_elapsed) {
  static unsigned long _start = 0;

  if ((current - _start) > max_elapsed) {
    _start = current;
    return true;
  }
  return false;
}

void dashboard() {
  Serial.println("Handling HTTP client");

  String s = WEBSITE;
  s.replace("@@IP_ADDR@@", WiFi.localIP().toString());
  
  s.replace("@@HASHRATE@@", String(hashrate / 1000));
  s.replace("@@DIFF@@", String(difficulty / 100));
  s.replace("@@SHARES@@", String(share_count));
  s.replace("@@NODE@@", String(node_id));

  s.replace("@@DEVICE@@", String(DEVICE));
  s.replace("@@ID@@", String(RIG_IDENTIFIER));
  s.replace("@@MEMORY@@", String(ESP.getFreeHeap()));
  s.replace("@@VERSION@@", String(MINER_VER));
#ifdef USE_DHT
  s.replace("@@TEMP@@", String(temp));
  s.replace("@@HUM@@", String(hum));
#endif
  server.send(200, "text/html", s);
}

} // namespace

// https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TypeConversion.cpp
const char base36Chars[36] PROGMEM = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
const uint8_t base36CharValues[75] PROGMEM {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, // 0 to 9
          10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, // Upper case letters
          10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35  // Lower case letters
};
uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength) {
    assert(hexString.length() >= arrayLength * 2); 
    for (uint32_t i = 0; i < arrayLength; ++i) {
        uint8Array[i] = (pgm_read_byte(base36CharValues + hexString.charAt(i * 2) - '0') << 4) + pgm_read_byte(base36CharValues + hexString.charAt(i * 2 + 1) - '0');
    }
    return uint8Array;
}

struct MiningJob
{
  String last_block_hash;
  String expected_hash_str; 
  uint8_t expected_hash[20];
  unsigned int difficulty;

  bool parse(char* job_str)
  {
    String tokens[3];
    char *token = strtok(job_str, ",");
    for (int i = 0; token != NULL && i < 3; i++)
    {
      tokens[i] = token;
      token = strtok(NULL, ",");
    }

    last_block_hash = tokens[0];
    expected_hash_str = tokens[1];
    hexStringToUint8Array(expected_hash_str, expected_hash, 20);
    difficulty = tokens[2].toInt() * 100 + 1;

    return true;
  }
};


void setup() {
  Serial.begin(500000);
  Serial.println("\nDuino-Coin " + String(MINER_VER));
  pinMode(LED_BUILTIN, OUTPUT);

  #ifdef USE_MQTT
    mqttClient.setServer(mqtt_server, mqtt_port);
  #endif
  
  #ifdef USE_DHT
    Serial.println("Initializing DHT sensor");
    dht.begin();
    Serial.println("Test reading: " + String(dht.readHumidity()) + "% humidity");
    Serial.println("Test reading: temperature " + String(dht.readTemperature()) + "*C");
  #endif

  // Autogenerate ID if required
  chipID = String(ESP.getChipId(), HEX);
  
  if(strcmp(RIG_IDENTIFIER, "Auto") == 0 ){
    AutoRigName = "ESP8266-" + chipID;
    AutoRigName.toUpperCase();
    RIG_IDENTIFIER = AutoRigName.c_str();
  }

  SetupWifi();
  SetupOTA();

  lwdtFeed();
  lwdTimer.attach_ms(LWD_TIMEOUT, lwdtcb);
  if (USE_HIGHER_DIFF) START_DIFF = "ESP8266NH";
  else START_DIFF = "ESP8266N";

  if(WEB_DASHBOARD) {
    if (!MDNS.begin(RIG_IDENTIFIER)) {
      Serial.println("mDNS unavailable");
    }
    MDNS.addService("http", "tcp", 80);
    Serial.print("Configured mDNS for dashboard on http://" 
                  + String(RIG_IDENTIFIER)
                  + ".local (or http://"
                  + WiFi.localIP().toString()
                  + ")");
    server.on("/", dashboard);
    if (WEB_HASH_UPDATER) server.on("/hashrateread", hashupdater);
    server.begin();
  }

  blink(BLINK_SETUP_COMPLETE);
}

void loop() {
  br_sha1_context sha1_ctx, sha1_ctx_base;
  uint8_t hashArray[20];
  String duco_numeric_result_str;
  
  // 1 minute watchdog
  lwdtFeed();

  // OTA handlers
  VerifyWifi();
  ArduinoOTA.handle();
  if(WEB_DASHBOARD) server.handleClient();

  ConnectToServer();
  Serial.println("Asking for a new job for user: " + String(DUCO_USER));

  #ifndef USE_DHT
    client.print("JOB," + 
                 String(DUCO_USER) + SEP_TOKEN +
                 String(START_DIFF) + SEP_TOKEN +
                 String(MINER_KEY) + END_TOKEN);
  #endif

  #ifdef USE_DHT
    temp = dht.readTemperature();
    hum = dht.readHumidity();

    Serial.println("DHT readings: " + String(temp) + "*C, " + String(hum) + "%");
    client.print("JOB," + 
                 String(DUCO_USER) + SEP_TOKEN +
                 String(START_DIFF) + SEP_TOKEN +
                 String(MINER_KEY) + SEP_TOKEN +
                 String(temp) + "@" + String(hum) + END_TOKEN);
  #endif
  
  #ifdef USE_MQTT
  
  if (!mqttClient.connected()) {
    mqttReconnect();
  }
  mqttClient.loop();
    #ifdef USE_DHT
    long now = millis();
    if (now - lastMsg > mqtt_update_time) {
      lastMsg = now;
      mqttClient.publish(temperature_topic, String(temp).c_str(), true);
      mqttClient.publish(humidity_topic, String(hum).c_str(), true); 
    }
    #endif

  #endif

  waitForClientData();
  Serial.println("Received job with size of " + String(client_buffer));

  MiningJob job;
  job.parse((char*)client_buffer.c_str());
  difficulty = job.difficulty;

  Serial.println("Parsed job: " + job.last_block_hash + " " + job.expected_hash_str + " " + String(job.difficulty));

  if (USE_HIGHER_DIFF) system_update_cpu_freq(160);

  br_sha1_init(&sha1_ctx_base);
  br_sha1_update(&sha1_ctx_base, job.last_block_hash.c_str(), job.last_block_hash.length());

  float start_time = micros();
  max_micros_elapsed(start_time, 0);

  String result = "";
  if (LED_BLINKING) digitalWrite(LED_BUILTIN, LOW);
  for (unsigned int duco_numeric_result = 0; duco_numeric_result < job.difficulty; duco_numeric_result++) {
    // Difficulty loop
    sha1_ctx = sha1_ctx_base;
    duco_numeric_result_str = String(duco_numeric_result);

    br_sha1_update(&sha1_ctx, duco_numeric_result_str.c_str(), duco_numeric_result_str.length());
    br_sha1_out(&sha1_ctx, hashArray);

    if (memcmp(job.expected_hash, hashArray, 20) == 0) {
      // If result is found
      if (LED_BLINKING) digitalWrite(LED_BUILTIN, HIGH);
      unsigned long elapsed_time = micros() - start_time;
      float elapsed_time_s = elapsed_time * .000001f;
      hashrate = duco_numeric_result / elapsed_time_s;
      share_count++;
      client.print(String(duco_numeric_result)
                   + ","
                   + String(hashrate)
                   + ","
                   + String(MINER_BANNER)
                   + " "
                   + String(MINER_VER)
                   + ","
                   + String(RIG_IDENTIFIER)
                   + ",DUCOID"
                   + String(chipID)
                   + "\n");

      waitForClientData();
      Serial.println(client_buffer
                     + " share #"
                     + String(share_count)
                     + " (" + String(duco_numeric_result) + ")"
                     + " hashrate: "
                     + String(hashrate / 1000, 2)
                     + " kH/s ("
                     + String(elapsed_time_s)
                     + "s)");
      break;
    }
    if (max_micros_elapsed(micros(), 500000)) {
      handleSystemEvents();
    }
  }
}

Pastikan dulu pada bagian berikut ini telah diisi sesuai dengan aplikasi kita. Ganti bintang-bintang dengan isian yang benar.

// Change the part in brackets to your Duino-Coin username
const char *DUCO_USER = "**********";  //user dari duco wallet
// Change the part in brackets to your mining key (if you have enabled it in the wallet)
const char *MINER_KEY = "**********";  //mining key ( lihat di duco wallet)
// Change the part in brackets to your WiFi name
const char *SSID = "**********";       //nama wifi yang akan digunakan
// Change the part in brackets to your WiFi password
const char *PASSWORD = "**********";   //password wifinya
// Change the part in brackets if you want to set a custom miner name (use Auto to autogenerate, None for no name)
const char *RIG_IDENTIFIER = "**********";  //nama perangkat, harus beda tiap perangkat

Setelah program siap, pilih board pada Arduino IDE ke Generic ESP8266. Setelah board dipilih, silahkan compile dulu untuk mengecek apakah program sudah ok.

DUCO Mining Rig esp8266

Sebelum upload program ke board, kita perlu memodif ESP-01 Programer adapter. Modul ESP-01 akan masuk ke mode download saat pin GPON 0 / FLASH dalam kondisi LOW. Untuk membuat kondisi seperti ini maka kita akan membuat konektor jumper antara pin GPIO 0 dengan pin GND. Saat jumper dipasang maka modul ESP 01 akan dalam mode download. Saat jumper dilepas maka kita dapat melakukan pemantauan data via serial monitor.

Mining Duino Coin dengan Esp01 board

Modul ESP-01 Siap di program

Colokkan USB programmer nya ke laptop. pada arduino IDE, setting dulu COM port nya (terhubung ke Com berapa perangkatnya). Kalau udah siap semuanya maka gas kita tekan tombol upload. Tunggu proses upload program selesai, setelah selesai lepas programmer dari laptop, trus lepas jumper GPIO 0-GND, baru colokkan kembali programer ke USB laptop. Kita buka serial monitor, atur baudrate ke 500000. Apabila tidak ada kendala maka akan tampil data seperti berikut:

Mini DUCO Mining Rig esp8266

*lepas jumper untuk masuk ke mode aplikasi running (agar dapat diliat di serial monitor) 

Mining Duino Coin dengan Esp01 board

OK, module ESP01 siap digunakan untuk menambang. Silahkan upload program yang sama ke modul ESP yang lainnya dengan mengubah RIG Identifier nya terlebih dahulu. Setelah semua di program, maka RIG sudah siap dijalankan dan cuan cuan cuan (*sabar).

Mini DUCO Mining Rig esp8266, Mining Duino Coin dengan Esp01 board

Mining Duino Coin with Esp01 board