Seit einiger Zeit versuche ich die aktuellen Kurse für BitCoin im Auge zu behalten, um günstige Zeitpunkte für eventuelle Zukäufe oder Verkäufe ausmachen zu können. Was ich mir dafür wünschen würde wäre ein kleines Gadget, dass mir die Kurse direkt anzeigt und eventuell ein Chart schreibt, wie der Tagesverlauf des BTC-Kurses in Dollar ist. US-Dollar deswegen, weil dies einfach eine internationale Referenzwährung ist, die auch auf anderen Charts gerne verwendet wird. Was liegt also näher, als sich ein kleines Gadget dafür selbst zu bauen.
Im Netz gibt es mehrere Seiten, die aktuelle BitCoin-Kurse listen. Einige davon haben auch eine API (Advanced Programmer Interface), die man zum direkten Auslesen der Daten nutzen kann. Allerdings reagieren diese kostenlosen APIs gerne mal allergisch und verweigern den Zugriff, wenn man sie zu häufig kontaktiert. Das Gadget hier nimmt daher nur alle 5 Minuten den aktuellen Kurs bei coingecko.com ab. Aus meiner Sicht und für meinen Bedarf absolut ausreichend.
Bauteile und Aufbau
Beim Griff in die Elektronikkiste war der WEMOS D1 mini schnell als zentrales Bauteil für dieses Vorhaben ausgemacht. Er hat WLAN an Board, ist gut am Markt verfügbar und entsprechend gut dokumentiert ist er ebenfalls.
Als Display für die Anzeige verwende ich ein 1.8Zoll TFT-Farbdisplay, was mir für die Darstellung vollkommen ausreicht.
Ein Vorteil, den das WEMOS Board liefert, sind die Spannungsversorgung mit 3.3V auch für das Display und die Möglichkeit, direkt über den USB-Stecker programmieren zu können. Es bedarf keines Level-Shifters und die serielle Kommunikation ist direkt auf dem Board mit implementiert.
Die Verkabelung der Bauteile ist nicht besonders schwierig, weshalb sich das Projekt durchaus als Einsteigerprojekt eignet. Wer nicht unbedingt jedes Detail des Programmcodes verstehen muss, sondern das Tool einfach nutzen möchte, kann dies tun. Der Code ist „plug and play“, wenn die Arduino Programmierumgebung eingerichtet ist.
Die Beschreibung wie diese eingerichtet und installiert werden muss, spare ich mir an dieser Stelle, weil es genügend Anleitungen dafür im Netz gibt (google is your friend). Wichtiger Hinweis: ich nutze die Arduino IDE in der Version 1.8.19. Diese ist unter Ubuntu/Linux derzeit als Standardpaket vorgesehen und funktioniert für meine Bedürfnisse einwandfrei.
Bauteilliste
- Den WEMOS D1 mini mit ESP8266 oder ESP12F Chip
- 1.8 Zoll TFT-Farb-Display (hier sogar mit SD-Kartenleser)
- Bezugsquelle: Eckstein-Shop
- technische Daten:
- 1.8 Zoll TFT LCD Display Modul 8 Pin (ST7735S) 128×160 SPI für Arduino mit SD-Kartenslot
- Typ: TFT
- Treiberchip: ST7735S
- Auflösung: 128*160 (Pixel)
- Anzeigefarbe: RGB 65K Farbe
- Modulschnittstelle: 4-Draht-SPI-Schnittstelle
- Aktive Fläche (AA-Fläche): 28,03 x 35,04 (mm)
- Modul-PCB-Größe: 34,5 x 58,0 (mm)
- Betriebstemperatur: -20℃~60℃
- Lagertemperatur: -30℃~70℃
- VCC-Versorgungsspannung: 3,3 V ~ 5 V
- Logik-IO-Portspannung: 3,3 V (TTL)
- Gewicht (mit Verpackung): 17 (g)
- 120 Ohm Widerstand (nicht zwingend erforderlich)
- 4 Schrauben M3x20
- 4 Muttern M3
Achtung! Der Widerstand wird benötigt, wenn das Display für die Hintergrundbeleuchtung keinen eigenen Vorwiderstand hat. Werden solche Hintergrundbeleuchtungen direkt an den Microcontroller angeschlossen, kann eventuell der Controller-Port oder die Beleuchtung durchbrennen.
Das von mir hier beschriebene Display hat den Widerstand bereits integriert und es bedarf daher keines externen Widerstandes.
Aufbau
Der Aufbau der wenigen Bauteile ist auf einem Steckboard schnell erledigt:

PIN-Belegung zwischen WEMOS und DISPLAY für den nachfolgenden Code:
| TFT-Display-Pin | Bedeutung | Wemos D1 mini Pin |
|---|---|---|
| VCC | +3.3V Stromversorgung | 3V3 |
| GND | Masse | GND |
| SCL / SCK | SPI-Clock | D5 (GPIO14) |
| SDA / MOSI | SPI-Daten | D7 (GPIO13) |
| RES / RST | Reset | D2 (GPIO4) |
| DC / RS / A0 | Daten/Command-Select | D1 (GPIO5) |
| CS | Chip-Select | D8 (GPIO15) |
| LED / BL | Hintergrundlicht | D3 (GPIO0) 3V3** |
Quellcode
Der Quellcode hat die Besonderheit, dass ich a) den SD-Kartenslot nicht nutze und b) einen NTP-Server abfrage, um die Uhrzeit im Display mit anzuzeigen. Das ist zwar keine zwingende Notwendigkeit, ist aber als Feature ganz nett.
Außerdem hat der Code einen Werbserver / Hotspot mit integriert. Man muss also sein WLAN Passwort und die SSID nicht hart im Code hinterlegen, sondern kann dies über den Hotspot eintragen und speichern. Dazu einfach bei der ersten Inbetriebnahme mit dem Handy oder Computer via WLAN auf das WLAN mit der SSID „BTC-CHART“ verbinden. Das Passwort hier im Code lautet „btcsetup“. Beides kann über die Variablen
const char* hotspotSSID=“BTC-CHART“;
const char* hotspotPASS=“btcsetup“;
jederzeit angepasst werden.
Bei Eintragen der WLAN-Credentials für das heimische WLAN habe ich das feld für die Passworteingabe im Klartext belassen. Wer seine Credentials über das Handy eingibt, kann so besser sehen, was er dort tippt.
Wer die Zugangsdaten im EEPROM wieder löschen will, kann dies hinterher im WLAN tun. Dazu einfach ungültige Werte in die Felder des Webclients eintragen und speichern.
Wenn der Connect ins heimische WLAN einmal funktioniert hat, ist das Gerät unter der IP-Adresse, die auf dem Display angezeigt wird, per http erreichbar. Wird auf dem Display z.B. die Adresse 192.168.178.99 angezeigt, reicht der Aufruf im Browser http://192.168.178.99, um die Setup-Seite wieder anzuzeigen und ggf neue WLAN-Zugangsdaten einzutragen.
Der große Block an Steuerzeichen der Variablen myImage[16*16] erzeugt ein farbiges BitCoin Logo im Format 16x16Pixel. Dies wird im Code rechts unter der Währung USD platziert. Zu erwähnen ist, dass dieser c-array hier im Bereich PROGMEM (Programmierspeicher) und nicht im RAM des Chips gespeichert wird. Der ESP8266/ESP12 hat nicht so viel RAM, deswegen wird dieser Teil des Codes in den anderen Speicherbereich ausgelagert, um Abstürze und ständiges Neustarten des Controllers zu vermeiden.
const uint16_t myImage[16*16] PROGMEM = {
0x0000, 0x0000, 0x0000, 0x20C0, 0x72A2, 0xA3E5,
...
};
Das Chart und der aktuelle Preis für BitCoin in USD wird bei steigenden Kursen in Grün, bei fallenden Kursen in Rot dargestellt. Jetzt aber Genug der Erläuterungen. Hier nun der vollständige und lauffähige Code:
/*
Der folgende Quellcode funktioniert mit meinen 1,8"(126x160dpi) TFT-Farbdisplays und verwendet
den Treiber ST7735S für das Display. Die entsprechende Library von Adafruit muss installiert sein:
WERKZEUGE -> BIBLIOTHEKEN VERWALTEN, dann nach Adafruit ST77 suchen. Die GFX library sollte dann
automatisch mit installiert werden.
Pinbelegung für den nachfolgenden code:
| TFT-Pin | Bedeutung | Wemos D1 mini Pin |
| ---------------- | --------------------- | -------------------------- |
| **VCC** | +3.3V Stromversorgung | **3V3** |
| **GND** | Masse | **GND** |
| **SCL / SCK** | SPI-Clock | **D5 (GPIO14)** |
| **SDA / MOSI** | SPI-Daten | **D7 (GPIO13)** |
| **RES / RST** | Reset | **D2 (GPIO4)** |
| **DC / RS / A0** | Daten/Command-Select | **D1 (GPIO5)** |
| **CS** | Chip-Select | **D8 (GPIO15)** |
| **LED / BL** | Hintergrundlicht | **D3 (GPIO0) **3V3** |
Diese Version V3 enthält einen Hotspot / Webserver zum eintragen der eigenen WLAN-Credentials.
# # # # Erstes Login # # # #
Mit Hotspot per WLAN verbinden:
SSID: BTC-CHART
Pass: btcsetup
Im Browser:
http://192.168.4.1
-> WLAN Zugangsdaten für das Heimnetz eintragen und speichern.
# # # # ardware # # # #
Board: WEMOS D1 mini
Display: https://eckstein-shop.de/12C8inch128x160SPISerialTFTLCDDisplayModul2BSDCardSlotArduinoKompatibel
Autor: Dirk Hahn IT-Services
*/
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
#include <WiFiClientSecureBearSSL.h>
#include <ArduinoJson.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
// -------------------- TFT --------------------
#define TFT_CS D8
#define TFT_DC D1
#define TFT_RST D2
#define TFT_BACKLIGHT D3
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
// -------------------- WIFI/EEPROM ------------
char wifi_ssid[32] = "";
char wifi_pass[32] = "";
const char* hotspotSSID="BTC-CHART";
const char* hotspotPASS="btcsetup";
String ipStr="";
ESP8266WebServer server(80);
// -------------------- WEB API --------------------
const char* btcSource = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd";
const char* sourceShort = "coingecko.com";
// -------------------- Uhrzeit --------------------
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 3600, 60000); // UTC+1, update 1 min
// -------------------- Chart --------------------
const int CHART_WIDTH = 160; // volle Breite
const int CHART_Y_START = 64; // untere Hälfte
const int CHART_HEIGHT = 64;
const int MAX_POINTS = 288; // 24h * 5min = 288 Punkte
float prices[MAX_POINTS];
int pointCount = 0;
// -------------------- Farben -------------------
uint16_t myGrey = tft.color565(150,150,150); // R=150, G=150, B=150
uint16_t myDarkGrey = tft.color565(100,100,100); //
uint16_t myGreen = tft.color565(100,200,100); //
// -------------------- Fetch-Interval --------------------
unsigned long lastFetch = 0;
const unsigned long fetchInterval = 5UL * 60UL * 1000UL; // 15 Minuten
// -------------------- BTC-Symbol -----------------------
// c-array eines Bitcoin-Symbols mit 16x16pixel
// Bild im Flash speichern
const uint16_t myImage[16*16] PROGMEM = {
0x0000, 0x0000, 0x0000, 0x20C0, 0x72A2, 0xA3E5, 0xBCC8, 0xCD29, 0xCD4A, 0xBC88, 0x9BA4, 0x6A61,
0x1880, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x51E1, 0xA3E5, 0xDD8A, 0xF66D, 0xFEAD, 0xFE8D,
0xF64D, 0xF64D, 0xEE2D, 0xCD29, 0x8303, 0x4180, 0x0000, 0x0000, 0x0000, 0x51E1, 0xB466, 0xEE2D,
0xFEAD, 0xEE2C, 0xD56A, 0xCD08, 0xCD29, 0xDDCC, 0xF68F, 0xF6AF, 0xE64D, 0x93A5, 0x4160, 0x0000,
0x20C0, 0xAC26, 0xF66D, 0xF68E, 0xD54A, 0xBCA7, 0xCD8E, 0xCDAF, 0xEEB3, 0xCD2A, 0xF66F, 0xF6D0,
0xFF92, 0xE66E, 0x8B23, 0x1880, 0x72C3, 0xDE2E, 0xFEF1, 0xDD6B, 0xC50B, 0xD5F0, 0xE672, 0xE692,
0xF714, 0xD5EF, 0xE64F, 0xF6F0, 0xF710, 0xFF51, 0xBD09, 0x59E0, 0xAC47, 0xF732, 0xF6B1, 0xD56B,
0xD56C, 0xDE51, 0xFF55, 0xE672, 0xDE31, 0xF714, 0xF6F4, 0xDDED, 0xF68D, 0xFF0F, 0xE64D, 0x8302,
0xC54B, 0xFF73, 0xE62F, 0xDD8C, 0xDDAD, 0xD58D, 0xFF35, 0xCD8E, 0xD58B, 0xDE51, 0xFF35, 0xD58C,
0xFE6D, 0xF68E, 0xF6AE, 0x9384, 0xCDAC, 0xFF92, 0xEE2E, 0xEE0D, 0xEE0E, 0xDDEE, 0xFF55, 0xE692,
0xE692, 0xFF35, 0xE692, 0xCD2A, 0xE5CB, 0xEE0D, 0xFEAE, 0xA405, 0xCD8B, 0xFF31, 0xEE2D, 0xEE4E,
0xF68F, 0xE64F, 0xFF55, 0xD610, 0xC56D, 0xDE31, 0xFF55, 0xDDEF, 0xD54A, 0xE5ED, 0xFEAE, 0xA3E5,
0xBCE9, 0xFEF0, 0xF68E, 0xFE8E, 0xFEAF, 0xE64F, 0xFF55, 0xD5CF, 0xD5AC, 0xCD8D, 0xFF55, 0xDE51,
0xCD09, 0xEE0C, 0xF64D, 0x8B64, 0xA3E5, 0xF68E, 0xF6AF, 0xF64D, 0xEE2E, 0xEED3, 0xFF55, 0xEED3,
0xEED3, 0xF714, 0xF6F4, 0xBD0B, 0xCD08, 0xF62C, 0xDD8B, 0x7AC1, 0x72A2, 0xCD4A, 0xF68E, 0xEE4D,
0xE5ED, 0xD5AE, 0xDE31, 0xDE52, 0xF6F4, 0xBD2D, 0xB489, 0xBC87, 0xE5AB, 0xF64D, 0xB447, 0x51E0,
0x20C0, 0x9BC4, 0xE60C, 0xF68E, 0xF66E, 0xDDAC, 0xD5AE, 0xCDAE, 0xE672, 0xC50A, 0xD56C, 0xE64F,
0xFED1, 0xCD6C, 0x7AC2, 0x1880, 0x0000, 0x51E1, 0xA406, 0xEE4F, 0xFF33, 0xF6D1, 0xEE70, 0xE62F,
0xE650, 0xEEB2, 0xF796, 0xFFD6, 0xD651, 0x8324, 0x4160, 0x0000, 0x0000, 0x0000, 0x51C1, 0x8B85,
0xCDEF, 0xEEB1, 0xFF32, 0xFF32, 0xFF53, 0xFF54, 0xE6D2, 0xBD2D, 0x82E3, 0x4160, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x1880, 0x6A41, 0x9364, 0xAC47, 0xB4C9, 0xB4A9, 0x9BE7, 0x8323, 0x51E0,
0x1880, 0x0000, 0x0000, 0x0000,
};
/* ####################################################################
* ######### Server Konfiguration #####################################
* ####################################################################
*/
// -------------------- WIFI-Daten Speichern im EEPROM ----------------
void saveWifiCredentials(const char* ssid, const char* pass) {
EEPROM.begin(128);
for (int i = 0; i < 32; i++) {
EEPROM.write(i, ssid[i]);
EEPROM.write(32+i, pass[i]);
}
EEPROM.commit();
}
// ------------------- WIFI-Daten laden aus EEPROM --------------------
void loadWifiCredentials() {
EEPROM.begin(128);
for (int i = 0; i < 32; i++) {
wifi_ssid[i] = EEPROM.read(i);
wifi_pass[i] = EEPROM.read(32+i);
}
}
// ------------------- Für Testzwecke: Daten aus dem EEPROM löschen ---
void clearWifiCredentials() {
EEPROM.begin(128);
for (int i = 0; i < 64; i++) {
EEPROM.write(i, 0);
}
EEPROM.commit();
// Auch die RAM-Kopien löschen
memset(wifi_ssid, 0, 32);
memset(wifi_pass, 0, 32);
}
// ------------------- WEBSITE des Servers ----------------------------
String wifiConfigPage() {
return "<html><meta name='viewport' content='width=device-width, initial-scale=1.0'>"
"<head><title>BTC-Chart setup</title></head><body style='background-color:Orange;'><h1 style='background-color:DodgerBlue;'>BTC-Chart WLAN Setup</h1>"
"<form action='/save'>"
"<h2>SSID:</h2><input name='ssid'><br>"
"<h2>Passwort:</h2><input name='pass' type='text'><br><br>"
"<input type='submit' value='Speichern'>"
"</form>"
"<br>Projekt-Homepage: <a href='https://dirk-hahn.de/bitcoin-chart-mit-wemos-d1-mini/' target='_top'>https://dirk-hahn.de/bitcoin-chart-mit-wemos-d1-mini/</a>"
"<hr><p><center><strong>Powered by Dirk Hahn IT-Services</strong></center></p><hr></body></html>";
}
// ------------------- HTTP-Handler für den Setup-Modus ---------------
void handleRoot() {
server.send(200, "text/html", wifiConfigPage());
}
// ------------------- Bestätigung des Speicherns der Daten -----------
void handleSave() {
String ss = server.arg("ssid");
String pw = server.arg("pass");
saveWifiCredentials(ss.c_str(), pw.c_str());
server.send(200, "text/html",
"<html><head><title>BTC-Chart setup</title></head><body style='background-color:Green;'><h1>Gespeichert!</h1>"
"<p><h2>Gerät startet neu...</h2></p></body></html>");
delay(2000);
ESP.restart();
}
/* ###############################################################################
* ##################### Display und Design ######################################
* ###############################################################################
*/
// ------------------- Hintergrundbeleuchtung steuern -------------------
void setBacklight(byte percent) {
if (percent > 100) percent = 100;
int pwm = map(percent, 0, 100, 0, 1023);
analogWrite(TFT_BACKLIGHT, pwm);
}
// -------------------- Kopfzeile -------------------------
void drawHeadline() {
tft.fillRect(0, 0, CHART_WIDTH, 10, myDarkGrey);
tft.fillRect(0, 0, 160, 10, myDarkGrey); //position x,y Seitenlänge a,b
tft.setTextColor(ST77XX_WHITE);
tft.setTextSize(1);
tft.setCursor(2, 2);
tft.print("BTC - USD");
//Quelle der BTC-Daten ausgeben
tft.setCursor(20, 11);
tft.setTextColor(myGreen);
tft.print(sourceShort);
//Währung anzeigen
tft.setCursor(130, 22);
tft.setTextColor(ST77XX_WHITE);
tft.print("USD");
//IP-Adresse anzeigen
tft.setTextColor(myGreen);
tft.setCursor(0, 49);
tft.print("IP:");
tft.print(ipStr);
// Pixel einzeln aus PROGMEM lesen für BTC-Symbol
for (uint16_t y=0; y<16; y++) {
for (uint16_t x=0; x<16; x++) {
uint16_t color = pgm_read_word(&myImage[y*16 + x]);
tft.drawPixel(x+130, y+33, color); // +130 um rechts zu landen; +33 um neben dem Preis zu landen
}
}
}
// -------------------- Fetch Bitcoin Price --------------------
float fetchBitcoinPrice() {
if (WiFi.status() != WL_CONNECTED) return -1;
WiFiClientSecure client;
client.setInsecure(); // kein Zertifikat prüfen (ESP8266 kompatibel)
HTTPClient https;
//https.begin(client, "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd");
https.begin(client, btcSource);
int httpCode = https.GET();
Serial.print("HTTP Code: ");
Serial.println(httpCode);
if (httpCode != 200) {
https.end();
return -1;
}
String payload = https.getString();
https.end();
Serial.println("Antwort:");
Serial.println(payload);
// JSON parsen {"bitcoin":{"usd":95312}}
StaticJsonDocument<256> doc;
DeserializationError err = deserializeJson(doc, payload);
if (err) return -1;
float price = doc["bitcoin"]["usd"];
return price;
}
// -------------------- Chart Zeichnen --------------------
void drawChart() {
// Bereich löschen
tft.fillRect(0, CHART_Y_START, CHART_WIDTH, CHART_HEIGHT, ST77XX_BLACK);
uint16_t color = ST77XX_WHITE;
if (pointCount < 1) return;
// -------------------- Min/Max Preis bestimmen --------------------
float minV = prices[0], maxV = prices[0];
for (int i = 1; i < pointCount; i++) {
if (prices[i] < minV) minV = prices[i];
if (prices[i] > maxV) maxV = prices[i];
}
float range = maxV - minV;
if (range < 0.1) range = 0.1;
// -------------------- Achsen zeichnen --------------------
tft.drawLine(0, CHART_Y_START + CHART_HEIGHT, CHART_WIDTH, CHART_Y_START + CHART_HEIGHT, ST77XX_WHITE); // X-Achse
tft.drawLine(0, CHART_Y_START, 0, CHART_Y_START + CHART_HEIGHT, ST77XX_WHITE); // Y-Achse
// Min/Max Y-Werte auf Achse schreiben
tft.setCursor(2, CHART_Y_START);
tft.setTextColor(ST77XX_WHITE);
tft.setTextSize(1);
tft.print(maxV, 0);
tft.setCursor(2, CHART_Y_START + CHART_HEIGHT - 8);
tft.print(minV, 0);
// -------------------- Kurve zeichnen --------------------
for (int i = 1; i < pointCount; i++) {
int x1 = map(i - 1, 0, pointCount - 1, 1, CHART_WIDTH - 1);
int x2 = map(i, 0, pointCount - 1, 1, CHART_WIDTH - 1);
int y1 = CHART_Y_START + CHART_HEIGHT - ((prices[i - 1] - minV) / range * CHART_HEIGHT);
int y2 = CHART_Y_START + CHART_HEIGHT - ((prices[i] - minV) / range * CHART_HEIGHT);
// Trendfarbe grün = steigend, rot = fallend
color = (prices[i] >= prices[i - 1]) ? ST77XX_GREEN : ST77XX_RED;
tft.drawLine(x1, y1, x2, y2, color);
}
// -------------------- Letzten BTC-Preis anzeigen --------------------
// Aktuellen Preis einblenden (Hauptanzeige, gross)
tft.fillRect(0, 21, 125, 26, ST77XX_BLACK);
tft.setTextSize(2);
tft.setCursor(8, 27);
tft.setTextColor(color);
tft.print(prices[pointCount - 1], 2);
tft.drawRect(0, 21, 125, 26, color);
}
/********************************************************************
* Hier beginnt das Setup
*
*/
// -------------------- Setup --------------------
void setup() {
Serial.begin(115200);
//clearWifiCredentials(); //Löschfunktion, wenn man die Website des Clients anpassen und testen möchte
bool wifiOk = false; //konnte WiFi verbunden werden?
//Backlight einschalten
pinMode(TFT_BACKLIGHT, OUTPUT);
analogWriteRange(1023); // 10-bit PWM
analogWriteFreq(20000); // flackerfrei (20 kHz)
analogWrite(TFT_BACKLIGHT, 1023); // volle Helligkeit
// TFT Initialisieren
tft.initR(INITR_BLACKTAB);
tft.setRotation(1);
tft.fillScreen(ST77XX_BLACK);
/*
* ##############################################
* Access-Point starten, wenn kein WLAN vorhanden
* ##############################################
*/
loadWifiCredentials();
if (strlen(wifi_ssid) > 0) {
WiFi.begin(wifi_ssid, wifi_pass);
unsigned long t = millis();
while (WiFi.status() != WL_CONNECTED && millis() - t < 7000) {
delay(100);
}
wifiOk = (WiFi.status() == WL_CONNECTED);
}
// Falls keine Verbindung → Access-Point starten
if (!wifiOk) {
// Ausgabe auf dem Display
tft.fillRect(0, 0, 160, 16, ST77XX_BLACK);
tft.setCursor(0, 0);
tft.setTextColor(ST77XX_YELLOW);
tft.setTextSize(1);
tft.println("Please connect to");
tft.setTextSize(2);
tft.println("SSID: ");
tft.println(hotspotSSID);
tft.println(" ");
tft.println("Password: ");
tft.println(hotspotPASS);
tft.setTextSize(1);
tft.println("via WiFi");
tft.println("Open browser with URL");
tft.println("http://192.168.4.1");
Serial.println("Starte Access Point...");
WiFi.mode(WIFI_AP);
//WiFi.softAP("BTC-CHART", "btcsetup");
WiFi.softAP(hotspotSSID, hotspotPASS);
server.on("/", handleRoot);
server.on("/save", handleSave);
server.begin();
// Setup-Modus
while (!wifiOk) {
server.handleClient();
delay(10);
}
}
// Nun im normalen Modus Webserver starten, wenn die WLAN-Credentials eingetragen wurden
server.on("/", handleRoot); // gleiche Seite, jetzt im Heimnetz erreichbar
server.on("/save", handleSave);
server.begin();
/*
* ################################################
* # Ausgabe auf dem Display nach WiFi-Connect #
* ################################################
*/
ipStr = WiFi.localIP().toString(); // IP-Adresse einlesen und in der drwaHeadline() ausgeben lassen
tft.fillRect(0, 0, 160, 16, ST77XX_BLACK);
drawHeadline();
// NTP starten
timeClient.begin();
}
// -------------------- Loop --------------------
void loop() {
server.handleClient();//Webserver pflegen
// Uhrzeit aktualisieren
timeClient.update();
String timeStr = timeClient.getFormattedTime();
// obere Zeile löschen
tft.fillRect(60, 0, CHART_WIDTH-70, 10, myDarkGrey);
tft.setTextColor(ST77XX_YELLOW);
tft.setTextSize(1);
tft.setCursor(60, 2);
tft.println(timeStr);
// alle 5 Minuten Bitcoin abrufen
unsigned long now = millis();
if (now - lastFetch > fetchInterval || pointCount == 0) {
lastFetch = now;
float price = fetchBitcoinPrice();
Serial.print("BTC Preis = ");
Serial.println(price);
if (price > 0) {
if (pointCount < MAX_POINTS) {
prices[pointCount++] = price;
} else {
// älteste Werte nach links verschieben
for (int i = 1; i < MAX_POINTS; i++)
prices[i - 1] = prices[i];
prices[MAX_POINTS - 1] = price;
}
drawChart();
}
}
setBacklight(100); // Helligkeit in Prozent
delay(500); // kleines Delay für NTP / Display
}
Wer das BTC-Chart nicht nur auf dem Steckboard betreiben möchte, kann sich auch ein kleines Gehäuse dafür drucken und die Verkabelung entsprechend verlöten.
3D-Druck und .STL-Datei
Für den 3D-Druck nutze ich einen Prusa MK4 Drucker und PETG-CF Fillament. Das -CF steht dabei für „Carbon Fiber“, also PETG mit Kohlefaser-Anteilen. Das Material lässt sich sehr gut drucken und ist enorm stabil. Derzeit nutze ich dazu das PETG-CF von TINMORRY, bestellbar z.B. bei AMAZON.
Wer das Gehäuse bearbeiten will, kann dies über TinkerCAD gerne machen. Das Gehäuse ist nicht sonderlich komplex und ebenfalls für Einsteiger geeignet. TinkerCAD richtet sich auch an Einsteiger des 3D-Drucks. Ein Account dort ist kostenlos und lohnt sich.
Der direkte Link:
Wer eine .STL-Datei zum Slicen benötigt, findet diese hier zum Download.
Viel Spaß beim Nachbauen!
Der Artikel wurde unter der Lizenz CC4 veröffentlicht und erlaubt die Nutzung und Weiterverarbeitung unter der Nennung des Autors. Das 3D-Modell unterliegt der Lizenzbedingung CC3.

