/* Wemos8266RelaysLedDisplay/main.cpp */ #define COMPDATE __DATE__ __TIME__ #define APP_VERSION "0.2.18" #include #include #include #include #include #include #include "WebServer.h" #include #include "Relays.h" #include "Clock.h" #include "Display.h" // Button pin on the esp for selecting modes. 0 for Generic devices! #define MODEBUTTON D3 #define RELAY1_PIN D1 #define RELAY2_PIN D2 #define DEBUG_RELAYS false #define DEBUG_DISPLAY false #define STARTUP1_ANIMATION_DURATION_ms 2000 #define STARTUP2_ANIMATION_DURATION_ms 33000 IOTAppStory IAS(COMPDATE, MODEBUTTON); String deviceName = "wemosMatrixDisplay"; String chipId; Clock realTime("real"); Clock modelTime("Fremo"); Relays R; WebServer W(&R, &realTime, &modelTime); Display D(&realTime, &modelTime); WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); #define mkstring(x) #x // Field default values char *clockName = "FREMO"; char *clockSpeed_modelMsPerRealSec_String = "250"; char *relay1Pin_String = mkstring(RELAY1_PIN); char *relay2Pin_String = mkstring(RELAY2_PIN); int relay1Pin = RELAY1_PIN, relay2Pin = RELAY2_PIN; char *relayHoldTime_ms_String = "200"; char *relayMinOffTime_ms_String = "100"; // Clock Display Config Parameter static char * displayClockNameEvery_ms_String = "16000"; static char * displayClockNameDuration_ms_String = "1200"; void setupIAS(void) { #if defined ESP8266 // creat a unique deviceName for classroom situations (deviceName-123) chipId = String(ESP.getChipId()); chipId = "-"+chipId.substring(chipId.length()-3); deviceName += chipId; #endif // preset deviceName this is also your MDNS responder: http://deviceName.local IAS.preSetDeviceName(deviceName); IAS.preSetAppName(F("Wemos2RelaysMatrixDisplays")); IAS.preSetAppVersion(F(APP_VERSION)); IAS.preSetAutoUpdate(true); // define fields IAS.addField(clockName, "Clock Name", 8, 'T'); IAS.addField(clockSpeed_modelMsPerRealSec_String, "Model MilliSec per Real Sec", 8, 'N'); IAS.addField(displayClockNameEvery_ms_String, "Display clock name every (ms)", 5, 'N'); IAS.addField(displayClockNameDuration_ms_String, "Display clock name duration (ms)", 5, 'N'); IAS.addField(relay1Pin_String, "Pin Relay 1", 2, 'P'); IAS.addField(relay2Pin_String, "Pin Relay 2", 2, 'P'); IAS.addField(relayHoldTime_ms_String, "Relay hold time (ms)", 3, 'N'); IAS.addField(relayMinOffTime_ms_String, "Relay min off time (ms)", 3, 'N'); IAS.onModeButtonShortPress([]() { Serial.println(F(" If mode button is released, I will enter firmware update mode.")); Serial.println(F("*----------------------------------------------------------------------*")); D.print("|updt"); }); IAS.onModeButtonLongPress([]() { Serial.println(F(" If mode button is released, I will enter configuration mode.")); Serial.println(F("*----------------------------------------------------------------------*")); D.print("|cfg"); }); IAS.onFirstBoot([]() { Serial.println(F(" Manual reset necessary after serial upload!")); Serial.println(F("*----------------------------------------------------------------------*")); D.print("|rst"); ESP.reset(); }); IAS.onConfigMode([]() { D.print(" WiFi"); delay(400); D.print(":" + chipId); Serial.print(F("Entered config mode for Wifi, device=")); Serial.println(chipId); }); IAS.onFirmwareUpdateCheck([]() { // D.print("chk upd"); Serial.println(F("Firmware update check")); }); IAS.onFirmwareUpdateDownload([]() { D.print("dl+instl"); Serial.println(F("Download and install new firmware")); }); IAS.onFirmwareUpdateError([]() { // D.print("Err fwu"); Serial.println(F("Firmware update error")); }); // Optional parameter: What to do with EEPROM on First boot of the app? // 'F' Fully erase | 'P' Partial erase(default) | 'L' Leave intact IAS.begin('L'); delay(500); // Set to true to enable calling home frequently (disabled by default) IAS.setCallHome(true); // Call home interval in seconds, use 60s only for development. // Please change it to at least 2 hours in production IAS.setCallHomeInterval(120); IAS.callHome(true /*SPIFFS-check*/); modelTime.setSpeed_modelMsPerRealSecond(atoi(clockSpeed_modelMsPerRealSec_String)); relay1Pin = IAS.dPinConv(relay1Pin_String); relay2Pin = IAS.dPinConv(relay2Pin_String); R.setHoldTime_ms(atoi(relayHoldTime_ms_String)); R.setMinOffTime_ms(atoi(relayMinOffTime_ms_String)); D.setClockNameDisplayEvery_ms(atoi(displayClockNameEvery_ms_String)); D.setClockNameDurationTime_ms(atoi(displayClockNameDuration_ms_String)); Serial.println(F("Configuration used:")); Serial.print(F("Relay1 Pin: ")); Serial.println(relay1Pin); Serial.print(F("Relay2 Pin: ")); Serial.println(relay2Pin); Serial.print(F("Relay hold time (ms): ")); Serial.println(relayHoldTime_ms_String); Serial.print(F("Relay min off time (ms): ")); Serial.println(relayMinOffTime_ms_String); Serial.print(F("Clock speed (model ms per real time s): ")); Serial.println(clockSpeed_modelMsPerRealSec_String); Serial.print(F("Show clock name every (ms): ")); Serial.println(displayClockNameEvery_ms_String); Serial.print(F("Show clock name for (ms): ")); Serial.println(displayClockNameDuration_ms_String); } void setupFS() { if(!SPIFFS.begin()){ Serial.println(F(" SPIFFS Mount Failed")); return; } } void setup(void) { Serial.println(F("setup():")); D.begin(); setupFS(); setupIAS(); W.begin(); delay(100); R.begin(relay1Pin, relay2Pin); timeClient.begin(); Serial.println(F("setup() finished")); } static bool timeClientInitialized = false; static unsigned long lastTimeOutput_ms = 0; #define TIME_BETWEEN_REALTIME_UPDATE_ms 60000 void loop(void) { static int lastMinutes = 0; static unsigned long firstLoop_ts = 0; if (firstLoop_ts == 0) firstLoop_ts = millis(); if (!timeClientInitialized && WiFi.status() == WL_CONNECTED) { timeClient.begin(); timeClientInitialized = true; timeClient.update(); Serial.println(timeClient.getFormattedTime()); lastTimeOutput_ms = millis(); } IAS.loop(); if (timeClientInitialized && millis()-lastTimeOutput_ms > TIME_BETWEEN_REALTIME_UPDATE_ms) { timeClient.update(); Serial.println(timeClient.getFormattedTime()); lastTimeOutput_ms = millis(); } if (millis() < firstLoop_ts + STARTUP1_ANIMATION_DURATION_ms) { // Startup phase 1: Constant display of content, created during setup() return; } if (millis() < firstLoop_ts + STARTUP1_ANIMATION_DURATION_ms + STARTUP2_ANIMATION_DURATION_ms) { D.loopEyeAnimation(); return; } if (timeClientInitialized) { realTime.setTime(timeClient.getHours(), timeClient.getMinutes(), timeClient.getSeconds()); } else { realTime.setTime((millis() / 60 * 60 * 1000) % 24, (millis() / 60 * 1000) % 60, (millis() / 1000) % 60); } D.loop(); // toggle relays if (lastMinutes != realTime.getMinutes()) { R.toggle(); lastMinutes = realTime.getMinutes(); } R.loop(); }