Vor etwa acht Jahren habe ich schon einmal über den ESP-32 im Zusammenhang mit NodeMCU berichtet. Vor ein paar Tagen ist mir dieses kleine, aber leistungsfähige Teil (Computer) wieder in die Hände gefallen. Dabei habe ich herausgefunden, dass man mit dem ESP-32 sogar Bitcoin minen kann! Mal was anderes als Monero. Gesagt, getan – ich habe es ausprobiert. Hier möchte ich auch einen Vergleich von drei Mining-Pools vorstellen und einen REST-Service in Java.
Habe alles mit dem MacOS geflasht, geht aber auch analog mit Windows. Also erst den Treiber installieren.
Starten der CP210x Driver App und beim Mac unter Allgemein- Anmeldeobjekte & Erweiterungen unter Treibererweiterungen aktivieren nicht vergessen.
Dann die Firmwar flashen. Achtung! Es muss ein Kabel verwendet werden, das auch die Datenleitungen und nicht nur den Strom liefert. Das war bei meinen ersten Versuchen die Fehlerursache, warum auf dem Mac keine Verbindung möglich war.
Das flashen in Google Chrom, im privaten Fenster aufrufen: https://flasher.bitronics.store/. Die Doku, was alles wie geht, siehe NerdMiner Doku.
Nach einem Restart, fängt der Miner an zu schürfen.
Energie Verbrauch
Der ESP-32 braucht ca. 0,5 Watt und wenn er ein ganzes Jahr rund um die Uhr läuft, benötigt er insgesamt:
Berechnung des jährlichen Energieverbrauchs: Umgerechnet in Kilowattstunden (kWh):
0,5 W * 24 Stunden/Tag * 365 Tage/Jahr = 4380 W = 4,3 kWh
Berechnung der Kosten:
Bei einem Strompreis von 0,30 € pro kWh:
4,38 kWh * 0,30 = 1,314 Euro
Ergebnis:
Jährlicher Energieverbrauch: 4,38 kWh
Gesamtkosten: ca. 1,31 €
Mining-Pool web.public-pool.io
Zuerst habe ich mal den Mining-Pool web.public-pool.io eingestellt. Die Hash-Rate ist so ca. 50-70KH/s, wie man hier sehen kann:
Achtung! Der Anbieter der Seite/Pool ist nicht ermittelbar (USA, (lookup 38.51.144.240)), und auch der Domain-Eintrag ist nicht sichtbar. Auch ist der Pool nicht sehr alt. Das Domain Zertifikat läuft am 27 Feb 2025 01:33:46 GMT ab. Also könnte es ein Scam sein. Ldt. Scam-Detector.com: Suspicious. Unsafe. Doubtfull. Score 24.8 von 100. Also nicht damit rechnen, was zu bekommen. 😉
Solo-Mining-Rechner
Wie wahrscheinlich ist es mit solch einer Hash-Rate eine Block zu minen? Mal einen Online Rechner dafür verwendet:
Also, unmöglich, oder doch nicht!
Mining-Pool unmineable.com
Dann noch mal den unmineable.com Pool getestet.
Der läuft aber seit dem 6.1.2025 nicht mehr mit so kleinen Hash-Raten, da muss man schon einen schnelleren Miner haben, die kleinen werden leider nun ausgesperrt, weil der Schwierigkeitsgrad (diff) angehoben wurde. Das habe ich nach einigen testen erst festgestellt. Eigentlich schade, dann hätte ich auch SOL oder andere Token minen können.
Mining-Pool pool.nerdminers.org
Ok, noch den „offiziellen“ Mining-Pool pool.nerdminers.org über Port 3333. Der hat aber nicht so eine schöne Übersicht.
Der liefert als User-Status nur vier Zeilen. Wer etwas mehr haben will, kann die Statistik über https://pool.nerdminers.org/users/bc1qjgupwgqavr34cf2275ws7h6yx6hmaf8rp24s78 aufrufen (eigene BTC ersetzen), dann kommt dieses JSon:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
{ "hashrate1m": "70.5K", "hashrate5m": "60.1K", "hashrate1hr": "57.4K", "hashrate1d": "9.94K", "hashrate7d": "1.54K", "lastshare": 1736521728, "workers": 1, "shares": 443.0, "bestshare": 0.05792322716563299, "bestever": 0.05792322716563299, "authorised": 1736504050, "worker": [ { "workername": "bc1qjgupwgqavr34cf2275ws7h6yx6hmaf8rp24s78.Kleinhirn", "hashrate1m": "70.5K", "hashrate5m": "60.1K", "hashrate1hr": "57.4K", "hashrate1d": "9.94K", "hashrate7d": "1.54K", "lastshare": 1736521728, "shares": 443.0, "bestshare": 0.05792322716563299, "bestever": 0.05792322716563299 } ] } |
Java Auswertung
Dazu habe ich dann eine Java-Swing Anwendung von gemacht:
1 2 3 4 5 6 7 8 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
package de.wenzlaff.twsolana; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; /** * BTC Gui. * * @author Thomas Wenzlaff */ public class TWNerdMinersGui { private static String bitcoinAdresse = "bc1qjgupwgqavr34cf2275ws7h6yx6hmaf8rp24s78"; public static void main(String[] args) { // Erstelle das Hauptfenster JFrame frame = new JFrame("TWNerdMinders"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(1024, 900); // Layout und Komponenten JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); // Eingabefeld für den Worker-Namen JPanel inputPanel = new JPanel(new FlowLayout()); JLabel bitcoinLabel = new JLabel("Bitcoin Adresse:"); JTextField bitcoinAdresseField = new JTextField(30); bitcoinAdresseField.setText(bitcoinAdresse); JButton fetchButton = new JButton("Abfrage"); inputPanel.add(bitcoinLabel); inputPanel.add(bitcoinAdresseField); inputPanel.add(fetchButton); // Textbereich für die Anzeige der Ergebnisse JTextArea resultArea = new JTextArea(); resultArea.setEditable(false); // Aktuelle Schriftart abrufen Font currentFont = resultArea.getFont(); // Neue Schriftart mit um 14 Punkte erhöhter Größe erstellen Font newFont = currentFont.deriveFont((float) (currentFont.getSize() + 14)); // Neue Schriftart setzen resultArea.setFont(newFont); JScrollPane scrollPane = new JScrollPane(resultArea); // Hinzufügen der Komponenten zum Hauptpanel panel.add(inputPanel, BorderLayout.NORTH); panel.add(scrollPane, BorderLayout.CENTER); // Hinzufügen des Panels zum Frame frame.add(panel); // Button-ActionListener fetchButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { bitcoinAdresse = bitcoinAdresseField.getText().trim(); if (!bitcoinAdresse.isEmpty()) { try { StringBuilder resultBuilder = TWNerdMindersCmd.getResult(bitcoinAdresse); resultArea.setText(resultBuilder.toString()); } catch (Exception ex) { JOptionPane.showMessageDialog(frame, "Fehler beim Abrufen der Daten: " + ex.getMessage(), "Fehler", JOptionPane.ERROR_MESSAGE); } } else { JOptionPane.showMessageDialog(frame, "Bitte geben Sie eine gültige Bitcoin-Adresse ein.", "Eingabefehler", JOptionPane.WARNING_MESSAGE); } } }); // Fenster sichtbar machen frame.setVisible(true); } } |
und
1 2 3 4 5 6 7 8 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
package de.wenzlaff.twsolana; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; /** * Abfrage der Bitcoin Minerdaten. * * @author Thomas Wenzlaff */ public class TWNerdMindersCmd { private static final String POOL_URL = "https://pool.nerdminers.org/users/"; public static void main(String[] args) { try { StringBuilder erg = getResult(args[0]); System.out.println(erg); } catch (IOException e) { System.err.println(e.getLocalizedMessage()); } } public static StringBuilder getResult(String bitcoinAdresse) throws IOException { // REST-API aufrufen und Daten abrufen String apiUrl = POOL_URL + bitcoinAdresse; String jsonResponse = fetchWorkerData(apiUrl); // JSON-Daten parsen und anzeigen JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject(); StringBuilder resultBuilder = new StringBuilder(); resultBuilder.append("Hashrate 1m: ").append(jsonObject.get("hashrate1m").getAsString()).append("\n"); resultBuilder.append("Hashrate 5m: ").append(jsonObject.get("hashrate5m").getAsString()).append("\n"); resultBuilder.append("Hashrate 1hr: ").append(jsonObject.get("hashrate1hr").getAsString()).append("\n"); resultBuilder.append("Hashrate 1d: ").append(jsonObject.get("hashrate1d").getAsString()).append("\n"); resultBuilder.append("Hashrate 7d: ").append(jsonObject.get("hashrate7d").getAsString()).append("\n"); resultBuilder.append("Last Share: ").append(jsonObject.get("lastshare").getAsLong()).append("\n"); resultBuilder.append("Workers: ").append(jsonObject.get("workers").getAsInt()).append("\n"); resultBuilder.append("Shares: ").append(jsonObject.get("shares").getAsDouble()).append("\n"); resultBuilder.append("Best Share: ").append(jsonObject.get("bestshare").getAsDouble()).append("\n"); resultBuilder.append("Best Ever: ").append(jsonObject.get("bestever").getAsDouble()).append("\n"); resultBuilder.append("Authorised: ").append(jsonObject.get("authorised").getAsDouble()).append("\n"); // Worker-spezifische Informationen JsonArray workers = jsonObject.getAsJsonArray("worker"); for (JsonElement workerElement : workers) { JsonObject worker = workerElement.getAsJsonObject(); resultBuilder.append("Workername: ").append(worker.get("workername").getAsString()).append("\n"); resultBuilder.append(" Hashrate 1m: ").append(worker.get("hashrate1m").getAsString()).append("\n"); resultBuilder.append(" Hashrate 5m: ").append(worker.get("hashrate5m").getAsString()).append("\n"); resultBuilder.append(" Hashrate 1hr: ").append(worker.get("hashrate1hr").getAsString()).append("\n"); resultBuilder.append(" Hashrate 1d: ").append(worker.get("hashrate1d").getAsString()).append("\n"); resultBuilder.append(" Hashrate 7d: ").append(worker.get("hashrate7d").getAsString()).append("\n"); resultBuilder.append(" Last Share: ").append(worker.get("lastshare").getAsLong()).append("\n"); resultBuilder.append(" Shares: ").append(worker.get("shares").getAsDouble()).append("\n"); resultBuilder.append(" Best Share: ").append(worker.get("bestshare").getAsDouble()).append("\n"); resultBuilder.append(" Best Ever: ").append(worker.get("bestever").getAsDouble()).append("\n\n"); } return resultBuilder; } public static String fetchWorkerData(String apiUrl) throws IOException { URL url = new URL(apiUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String inputLine; StringBuilder response = new StringBuilder(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); return response.toString(); } else { throw new IOException("HTTP-Fehlercode: " + responseCode); } } } |
Der ganze Java-Code ist in meinem Gitlab-Repo zu finden, auch mit einem Logger, der die Ergebnisse jede Minute in eine CSV-Datei schreibt. So kann man sich dann selbst Auswertungen erstellen.