
Mit einer einfachen Mail eine Laufschrift auf ein Laufband zauber: das ist gar nicht so schwer. Und wer mag, kann sich noch ein passendes Gehäuse dazu drucken.
Hier finden sich die beiden STL Dateien für den Druck der Halterung.
Und hier folgt der Quellcode für die Programmierung des ESP8266. Bei seiner Erstellung hat mir mein Kollege Axel Schlüter sehr geholfen. Er hat den Code geglättet und große Teile überhaupt erst einmal funktionsfähig gemacht (die String-Verarbeitung in C ist und bleibt mir ein Greul…). Es wird die Uhrzeit angezeigt und alle 30 Sekunden nach einer neuen Mail gesucht. Der Inhalt der Betreffzeile der E-Mail wird dann als Laufschrift über das Display geschickt und danach die Mail gelöscht!
// Notwendig Libraries: // MD_MAX72XX, MD_Parola, NTPClient, Time, Timezone, ESP Mail Client #include <Arduino.h> #include <ESP8266WiFi.h> #include <MD_Parola.h> #include <NTPClient.h> #include <WiFiUdp.h> #include <Timezone.h> #include <ESP_Mail_Client.h> #define SPEAKER_PIN D8 #define CLK_PIN D3 // or SCK #define CS_PIN D2 // or SS #define DATA_PIN D1 // or MOSI #define MAX_DEVICES 4 #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define DELAYTIME 100 // in milliseconds #define WIFI_SSID "Deine-SSID" #define WIFI_PASSWORD "Dein-PW" #define POP3_SERVER "pop.Postfach" #define POP3_PORT 995 #define POP3_EMAIL "deine@mail.de" #define POP3_PASSWORD "Dein-MailPW" static MD_Parola display = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); static WiFiClientSecure client; static WiFiUDP wifiUDP; static NTPClient ntpClient(wifiUDP, "europe.pool.ntp.org"); static TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Sommerzeit (UTC+2) static TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Standardzeit (UTC+1) static Timezone timezone(CEST, CET); void setup() { pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); display.begin(); display.displayClear(); beep(); scrollTextAndWait("Gestartet"); connectWIFI(); client.setInsecure(); } void loop() { updateCurrentTime(); updateMail(); blink(); delay(1000); } static void updateMail() { static long nextUpdate = 0; if (millis() < nextUpdate) { return; } String message = ""; if (!client.connect(POP3_SERVER, POP3_PORT)) { scrollTextAndWait("Problem mit POP3!"); } else { String welcome = pop3Request("", false); // skip the welcome message print("%s\n", welcome.c_str()); pop3Request("USER " POP3_EMAIL, false); pop3Request("PASS " POP3_PASSWORD, false); String stat = pop3Request("STAT", false); print("%s\n", stat.c_str()); if (!stat.startsWith("+OK 0")) { message = pop3Request("RETR 1", true); pop3Request("DELE 1", false); } pop3Request("QUIT", false); client.stop(); } if (message.length() > 0) { print("message: %s\n", message.c_str()); beep(); scrollTextAndWait(message.c_str()); } nextUpdate = millis() + 30 * 1000; // next query in 30 seconds } static String pop3Request(String command, bool waitForSubject) { if (command.length() > 0) { client.print(command.c_str()); client.print("\r\n"); } String response = client.readStringUntil('\n'); if (!waitForSubject) { return response; } // read the subject from the email String subject = ""; bool subjectFound = false; while (client.connected()) { String line = client.readStringUntil('\n') + "\n"; if (line.startsWith("Subject: ")) { subjectFound = true; subject = line.substring(9); // Skip "Subject: " } else if (subjectFound) { if (line.startsWith(" ")) { subject += line; } else { subjectFound = false; } } else if (line.startsWith(".\r\n")) { break; } } return decodeRFC2047String(subject.c_str()); } static const String decodeRFC2047String(const char *encoded) { MB_FS mbfs; char decoded[256], *curr = decoded; memset(decoded, 0, sizeof(decoded)); RFC2047_Decoder().decode(&mbfs, decoded, encoded, sizeof(decoded)); String result = ""; while (*curr) { if (*curr == 0xc3) { // UTF-8 sequence start byte found curr++; // skip UTF-8 start byte switch (*curr++) { case 0xa4: result += "\xe4"; break; // 'ä' case 0xbc: result += "\xfc"; break; // 'ü' case 0xb6: result += "\xf6"; break; // 'ö' case 0x84: result += "\xc4"; break; // 'Ä' case 0x9c: result += "\xdc"; break; // 'Ü' case 0x96: result += "\xd6"; break; // 'Ö' case 0x9f: result += "\xdf"; break; // 'ß' } } else { result += *curr++; } } result.trim(); return result; } static void updateCurrentTime() { ntpClient.update(); time_t utc = ntpClient.getEpochTime(); time_t local = timezone.toLocal(utc); char *timestamp = ctime(&local); if (!timestamp) { return; } static char buffer[6] = {0}; // HH:MM + null terminator memcpy(buffer, ×tamp[11], 5); timestamp[strlen(timestamp) - 1] = '\0'; static bool showDivider = false; showDivider = !showDivider; if (!showDivider) { buffer[2] = ' '; } display.displayText(buffer, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT); display.displayAnimate(); } static void blink() { static bool isOn = false; isOn = !isOn; digitalWrite(LED_BUILTIN, isOn ? LOW : HIGH); } static void beep() { tone(SPEAKER_PIN, 1000, 440); noTone(SPEAKER_PIN); } static void scrollTextAndWait(String message) { display.displayClear(); display.displayScroll(message.c_str(), PA_RIGHT, PA_SCROLL_LEFT, DELAYTIME); while (!display.displayAnimate()) { blink(); delay(10); } } static void connectWIFI() { print("connecting\n"); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) { print("."); delay(200); } print("\nwifi connected, IP: %s\n", WiFi.localIP().toString().c_str()); scrollTextAndWait("WiFi verbunden"); } static void print(const char *fmt, ...) { va_list args; va_start(args, fmt); char text[128]; vsnprintf(text, sizeof(text), fmt, args); va_end(args); Serial.print(text); }