/* Wemos8266RelaysLedDisplay/main.cpp */ #define COMPDATE __DATE__ __TIME__ // Button pin on the esp for selecting modes. 0 for Generic devices! #define MODEBUTTON D3 #define RELAY1_PIN D1 #define RELAY2_PIN D2 #define DISPLAY_CLK_PIN D5 #define DISPLAY_DATA_PIN D7 #define DISPLAY_CS_PIN D6 #define VERTICAL_BAR_STARTS_TOP false #define DEBUG_RELAYS false #include #include #include #include #include #include #include #include IOTAppStory IAS(COMPDATE, MODEBUTTON); String deviceName = "wemosMatrixDisplay"; String chipId; const uint16_t WAIT_TIME = 1000; // Define the number of devices we have in the chain and the hardware interface // NOTE: These pin numbers will probably not work with your hardware and may // need to be adapted /* Mapper result, connector to ESP is at right (backside): Your responses produce these hardware parameters HW_DIG_ROWS 1 HW_REV_COLS 0 HW_REV_ROWS 0 Your hardware matches the setting for FC-16 modules. Please set FC16_HW. */ #define HARDWARE_TYPE MD_MAX72XX::FC16_HW #define MAX_DEVICES 4 #define CLK_PIN DISPLAY_CLK_PIN #define DATA_PIN DISPLAY_DATA_PIN #define CS_PIN DISPLAY_CS_PIN // Hardware SPI connection // MD_Parola P = MD_Parola(HARDWARE_TYPE, DISPLAY_CS_PIN, MAX_DEVICES); // Arbitrary output pins MD_Parola P = MD_Parola(HARDWARE_TYPE, DISPLAY_DATA_PIN, DISPLAY_CLK_PIN, DISPLAY_CS_PIN, MAX_DEVICES); WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); // Field default values char *clockName = "FastClk "; char *clockSpeed_modelMsPerRealSec_String = "250"; int clockSpeed_modelMsPerRealSec = 250; char *relay1Pin_String = "D1"; char *relay2Pin_String = "D2"; int relay1Pin = D1, relay2Pin = D2; char *relayHoldTime_ms_String = "200"; int relayHoldTime_ms = 200; char *relayMinOffTime_ms_String = "100"; int relayMinOffTime_ms = 100; 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("0.0.1")); 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(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 in firmware update mode.")); Serial.println(F("*-------------------------------------------------------------------------*")); P.print("|updt"); }); IAS.onModeButtonLongPress([]() { Serial.println(F(" If mode button is released, I will enter in configuration mode.")); Serial.println(F("*-------------------------------------------------------------------------*")); P.print("|cfg"); }); IAS.onFirstBoot([]() { Serial.println(F(" Manual reset necessary after serial upload!")); Serial.println(F("*-------------------------------------------------------------------------*")); P.print("|rst"); ESP.reset(); }); IAS.onConfigMode([]() { P.print("WiFi"); delay(400); P.print("*" + chipId); Serial.print(F("Entered config mode for Wifi, device=")); Serial.println(chipId); }); IAS.onFirmwareUpdateCheck([]() { P.print("chk upd"); Serial.println(F("Firmware update check")); }); IAS.onFirmwareUpdateDownload([]() { P.print("dl&instl"); Serial.println(F("Download and install new firmware")); }); IAS.onFirmwareUpdateError([]() { P.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(false /*SPIFFS-check*/); clockSpeed_modelMsPerRealSec = atoi(clockSpeed_modelMsPerRealSec_String); relay1Pin = IAS.dPinConv(relay1Pin_String); relay2Pin = IAS.dPinConv(relay2Pin_String); relayHoldTime_ms = atoi(relayHoldTime_ms_String); relayMinOffTime_ms = atoi(relayMinOffTime_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("Clock speed: ")); Serial.print(clockSpeed_modelMsPerRealSec); Serial.println(F(" model ms per real sec")); Serial.print(F("Relay hold time (ms): ")); Serial.println(relayHoldTime_ms); Serial.print(F("Relay min off time (ms): ")); Serial.println(relayMinOffTime_ms); } void setupRelays(int relay1Pin, int relay2Pin) { pinMode(relay1Pin, OUTPUT); pinMode(relay2Pin, OUTPUT); digitalWrite(relay1Pin, LOW); digitalWrite(relay2Pin, LOW); } void setupDisplay() { int charCode; #if VERTICAL_BAR_STARTS_TOP static uint8_t verticalBarFont[] = { 1, 0x00, /* blank */ 1, 0x01, /* 1 dot */ 1, 0x03, /* 2 dots */ 1, 0x07, 1, 0x0f, 1, 0x1f, 1, 0x3f, 1, 0x7f, 1, 0xff, /* vertical bar completely set */ }; // columns from right to left, each byte is a single column #else static uint8_t verticalBarFont[] = { 1, 0x00, /* blank */ 1, 0x80, /* 1 dot */ 1, 0xc0, /* 2 dots */ 1, 0xe0, 1, 0xf0, 1, 0xf8, 1, 0xfc, 1, 0xfe, 1, 0xff, /* vertical bar completely set */ }; // columns from right to left, each byte is a single column #endif P.begin(); // P.setZoneEffect(0, true, PA_FLIP_LR); P.setIntensity(1); for (charCode=1; charCode<=8; ++charCode) { P.addChar(charCode, verticalBarFont+2*(charCode-1)); } char intro[] = {':', '-', ')', ' ', 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00}; P.print(intro); } void setup(void) { Serial.println(F("setup():")); setupDisplay(); setupIAS(); delay(500); setupRelays(relay1Pin, relay2Pin); delay(500); timeClient.begin(); Serial.println(F("setup() finished")); } static bool timeClientInitialized = false; static unsigned long lastTimeOutput_ms = 0; #define TIME_BETWEEN_TIME_REPORTS_ms 60000 static unsigned long last_relay_off_ts=0, last_relay_hold_ts=0; enum RelayState { RELAY_STATE_OFF=0, RELAY_STATE_ON_EVEN_MINUTE, RELAY_STATE_ON_ODD_MINUTE }; static RelayState relaysState = RELAY_STATE_OFF; static RelayState lastRelayOnState = RELAY_STATE_ON_EVEN_MINUTE; static bool relayCanSwitch=true; void toggleRelays() { if (relayCanSwitch) { if (lastRelayOnState == RELAY_STATE_ON_EVEN_MINUTE) { digitalWrite(relay1Pin, HIGH); digitalWrite(relay2Pin, LOW); relaysState = RELAY_STATE_ON_ODD_MINUTE; // P.print("R-OEv"); } else { digitalWrite(relay1Pin, LOW); digitalWrite(relay2Pin, HIGH); relaysState = RELAY_STATE_ON_EVEN_MINUTE; // P.print("R-OOd"); } lastRelayOnState = relaysState; } // else P.print("R-OErr"); relayCanSwitch = false; last_relay_hold_ts = millis(); Serial.println(F("Toggle Relays")); } void relaysOff(void) { digitalWrite(relay1Pin, LOW); digitalWrite(relay2Pin, LOW); last_relay_off_ts = millis(); relaysState = RELAY_STATE_OFF; // P.print("R-Off"); } void loopRelays(void) { if (relaysState == RELAY_STATE_OFF) { if (millis() - last_relay_off_ts > relayMinOffTime_ms) { relayCanSwitch = true; } } else { if (millis() - last_relay_hold_ts > relayHoldTime_ms) { relaysOff(); } } } void loop(void) { int currentDisplayState; int hours, minutes, seconds; char minuteProgressIndicator; static int lastMinutes = 0; static int lastSeconds = 0; static char timeBuffer[10]; #define MsgSize 10 static char debugMsg[MsgSize+1]; static int recentDisplayState = -1; if (!timeClientInitialized && WiFi.status() == WL_CONNECTED) { timeClient.begin(); timeClientInitialized = true; } IAS.loop(); if (timeClientInitialized && millis()-lastTimeOutput_ms > TIME_BETWEEN_TIME_REPORTS_ms) { timeClient.update(); Serial.println(timeClient.getFormattedTime()); lastTimeOutput_ms = millis(); } if (timeClientInitialized) { hours = timeClient.getHours(); minutes = timeClient.getMinutes(); seconds = timeClient.getSeconds(); } else { hours = (millis() / 60 * 60 * 1000) % 24; minutes = (millis() / 60 * 1000) % 60; seconds = (millis() / 1000) % 60; } minuteProgressIndicator = seconds/7.5 + 1; // char code 1-8 show vertical bar snprintf(timeBuffer, 10, "%c %02d:%02d", minuteProgressIndicator, hours, minutes); /* DEBUG */ #if DEBUG_RELAYS if (seconds != lastSeconds) { switch (seconds % 4) { case 0: digitalWrite(relay1Pin, HIGH); break; case 1: digitalWrite(relay1Pin, LOW); break; case 2: digitalWrite(relay2Pin, HIGH); break; case 3: digitalWrite(relay2Pin, LOW); break; } Serial.print("Rel dbg: "); Serial.println(seconds, HEX); delay(5); } #endif /* END DEBUG */ currentDisplayState = seconds / 5; // Value 0..11 static bool executed = false; if (recentDisplayState != currentDisplayState) executed = false; #define ExecOnce(p) {if (!executed) {executed=true; p;}} switch (currentDisplayState) { case 0: ExecOnce(P.print(timeBuffer)); break; case 1: snprintf(debugMsg, MsgSize, "%c t%cr%c", minuteProgressIndicator, timeClientInitialized ? 'x':'-', (relaysState == RELAY_STATE_OFF)?'-':(relaysState == RELAY_STATE_ON_EVEN_MINUTE?'e':'o')); ExecOnce(P.print(debugMsg)); break; case 2: ExecOnce(P.print(timeBuffer)); break; case 3: // if (lastSeconds != seconds) P.print(seconds); break; case 4: ExecOnce(P.print(timeBuffer)); break; case 5: ExecOnce(P.print(timeBuffer)); break; case 6: switch (minutes % 3) { case 0: snprintf(debugMsg, MsgSize, "%c s%d", minuteProgressIndicator, clockSpeed_modelMsPerRealSec); break; case 1: snprintf(debugMsg, MsgSize, "%c h%d", minuteProgressIndicator, relayHoldTime_ms); break; case 2: snprintf(debugMsg, MsgSize, "%c o%d", minuteProgressIndicator, relayMinOffTime_ms); break; } ExecOnce(P.print(debugMsg)); break; case 7: ExecOnce(P.print(timeBuffer)); break; case 8: /*snprintf(debugMsg, MsgSize, "t%cr%c", timeClientInitialized ? 'x':'-', (relaysState == RELAY_STATE_OFF)?'-':(relaysState == RELAY_STATE_ON_EVEN_MINUTE?'e':'o')); ExecOnce(P.print(debugMsg)); break;*/ case 9: ExecOnce(P.print(timeBuffer)); break; case 10: if (lastSeconds != seconds) P.printf("%c %d", minuteProgressIndicator, seconds); break; case 11: ExecOnce(P.print(timeBuffer)); break; default: ExecOnce(P.print("default")); break; } recentDisplayState = currentDisplayState; // toggle relays if (lastMinutes != minutes) { toggleRelays(); lastMinutes = minutes; } lastSeconds = seconds; loopRelays(); }