From 3dfd08e495d23303c3fa59fe0993a4c1762a89c5 Mon Sep 17 00:00:00 2001 From: Dirk Jahnke Date: Tue, 29 Jan 2019 09:49:11 +0100 Subject: [PATCH] Refactoring, web server enabled, 1st service calls created. --- .gitignore | 1 + platformio.ini | 2 + src/MD_RobotEyes_Data.h | 18 +-- src/Relays.cpp | 85 +++++++++++++ src/Relays.h | 40 +++++++ src/data/btn-do.png | Bin 0 -> 9725 bytes src/data/btn.png | Bin 0 -> 8769 bytes src/data/favicon.ico | Bin 0 -> 4150 bytes src/data/fremoei.gif | Bin 0 -> 3267 bytes src/data/icon-0-75x.png | Bin 0 -> 1846 bytes src/data/icon-1-5x.png | Bin 0 -> 4412 bytes src/data/icon-1x.png | Bin 0 -> 2638 bytes src/data/icon-2x.png | Bin 0 -> 6439 bytes src/data/icon-3x.png | Bin 0 -> 11161 bytes src/data/icon-4x.png | Bin 0 -> 16764 bytes src/data/img_fremo_sw.gif | Bin 0 -> 2196 bytes src/data/img_sh0.gif | Bin 0 -> 191 bytes src/data/index.htm | 94 +++++++++++++++ src/data/led-off.png | Bin 0 -> 1248 bytes src/data/led-on.png | Bin 0 -> 2533 bytes src/data/main.css | 47 ++++++++ src/data/manifest.json | 39 ++++++ src/main.cpp | 245 +++++++++++++++++++++++--------------- 23 files changed, 465 insertions(+), 106 deletions(-) create mode 100644 src/Relays.cpp create mode 100644 src/Relays.h create mode 100644 src/data/btn-do.png create mode 100644 src/data/btn.png create mode 100755 src/data/favicon.ico create mode 100755 src/data/fremoei.gif create mode 100644 src/data/icon-0-75x.png create mode 100644 src/data/icon-1-5x.png create mode 100644 src/data/icon-1x.png create mode 100644 src/data/icon-2x.png create mode 100644 src/data/icon-3x.png create mode 100644 src/data/icon-4x.png create mode 100644 src/data/img_fremo_sw.gif create mode 100644 src/data/img_sh0.gif create mode 100644 src/data/index.htm create mode 100644 src/data/led-off.png create mode 100644 src/data/led-on.png create mode 100644 src/data/main.css create mode 100644 src/data/manifest.json diff --git a/.gitignore b/.gitignore index addf833..d6ae83e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ .clang_complete .gcc-flags.json firmware.bin +/data/* diff --git a/platformio.ini b/platformio.ini index efbba14..65afd88 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,6 +19,8 @@ lib_deps = MD_MAX72XX IOTAppStory-ESP NTPClient + ESPAsyncTCP + ESP Async WebServer upload_port = /dev/cu.wchusbserial1420 upload_speed = 921600 diff --git a/src/MD_RobotEyes_Data.h b/src/MD_RobotEyes_Data.h index 2fbc6c2..b738bdb 100644 --- a/src/MD_RobotEyes_Data.h +++ b/src/MD_RobotEyes_Data.h @@ -23,21 +23,21 @@ const MD_RobotEyes::animFrame_t MD_RobotEyes::seqWink[] /*PROGMEM*/ = { { 2, 0 }, FRAME_TIME/2 }, { { 3, 0 }, FRAME_TIME/2 }, { { 4, 0 }, FRAME_TIME/2 }, - { { 5, 0 }, FRAME_TIME * 2 }, + { { 5, 0 }, FRAME_TIME }, }; const MD_RobotEyes::animFrame_t MD_RobotEyes::seqRight[] /*PROGMEM*/ = { { { 0, 0 }, FRAME_TIME }, { { 6, 6 }, FRAME_TIME }, - { { 7, 7 }, FRAME_TIME * 5 }, + { { 7, 7 }, FRAME_TIME * 3 }, }; const MD_RobotEyes::animFrame_t MD_RobotEyes::seqLeft[] /*PROGMEM*/ = { { { 0, 0 }, FRAME_TIME }, { { 8, 8 }, FRAME_TIME }, - { { 9, 9 }, FRAME_TIME * 5 }, + { { 9, 9 }, FRAME_TIME * 3 }, }; const MD_RobotEyes::animFrame_t MD_RobotEyes::seqUp[] /*PROGMEM*/ = @@ -53,7 +53,7 @@ const MD_RobotEyes::animFrame_t MD_RobotEyes::seqDown[] /*PROGMEM*/ = { { 00, 00 }, FRAME_TIME }, { { 14, 14 }, FRAME_TIME }, { { 15, 15 }, FRAME_TIME }, - { { 16, 16 }, FRAME_TIME * 5 }, + { { 16, 16 }, FRAME_TIME * 3 }, }; const MD_RobotEyes::animFrame_t MD_RobotEyes::seqAngry[] /*PROGMEM*/ = @@ -62,7 +62,7 @@ const MD_RobotEyes::animFrame_t MD_RobotEyes::seqAngry[] /*PROGMEM*/ = { { 22, 17 }, FRAME_TIME }, { { 23, 18 }, FRAME_TIME }, { { 24, 19 }, FRAME_TIME }, - { { 25, 20 }, 2000 }, + { { 25, 20 }, 1000 }, }; const MD_RobotEyes::animFrame_t MD_RobotEyes::seqSad[] /*PROGMEM*/ = @@ -70,14 +70,14 @@ const MD_RobotEyes::animFrame_t MD_RobotEyes::seqSad[] /*PROGMEM*/ = { { 00, 00 }, FRAME_TIME }, { { 32, 27 }, FRAME_TIME }, { { 33, 28 }, FRAME_TIME }, - { { 34, 29 }, 2000 }, + { { 34, 29 }, 1000 }, }; const MD_RobotEyes::animFrame_t MD_RobotEyes::seqEvil[] /*PROGMEM*/ = { { { 00, 00 }, FRAME_TIME }, { { 39, 37 }, FRAME_TIME }, - { { 40, 38 }, 2000 }, + { { 40, 38 }, 1000 }, }; const MD_RobotEyes::animFrame_t MD_RobotEyes::seqEvil2[] /*PROGMEM*/ = @@ -86,7 +86,7 @@ const MD_RobotEyes::animFrame_t MD_RobotEyes::seqEvil2[] /*PROGMEM*/ = { { 54, 17 }, FRAME_TIME }, { { 55, 18 }, FRAME_TIME }, { { 56, 19 }, FRAME_TIME }, - { { 57, 20 }, 2000 }, + { { 57, 20 }, 1000 }, }; const MD_RobotEyes::animFrame_t MD_RobotEyes::seqSquint[] /*PROGMEM*/ = @@ -95,7 +95,7 @@ const MD_RobotEyes::animFrame_t MD_RobotEyes::seqSquint[] /*PROGMEM*/ = { { 54, 54 }, FRAME_TIME }, { { 55, 55 }, FRAME_TIME }, { { 56, 56 }, FRAME_TIME }, - { { 57, 57 }, 2000 }, + { { 57, 57 }, 1000 }, }; const MD_RobotEyes::animFrame_t MD_RobotEyes::seqDead[] /*PROGMEM*/ = diff --git a/src/Relays.cpp b/src/Relays.cpp new file mode 100644 index 0000000..190725a --- /dev/null +++ b/src/Relays.cpp @@ -0,0 +1,85 @@ +#include "Relays.h" + +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 Relays::begin(int _relay1Pin, int _relay2Pin) { + relay1Pin = _relay1Pin; + relay2Pin = _relay2Pin; + pinMode(relay1Pin, OUTPUT); + pinMode(relay2Pin, OUTPUT); + digitalWrite(relay1Pin, LOW); + digitalWrite(relay2Pin, LOW); +} + +void Relays::toggle() { + 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(); + // increment displayed time + displayedTime_minutes++; + if (displayedTime_minutes >= 60) { + displayedTime_hours++; + displayedTime_minutes -= 60; + if (displayedTime_hours >= 24) { + displayedTime_hours = 0; + } + } + if (fastforwardMode) { + if (displayedTime_hours == targetHours && displayedTime_minutes == targetMinutes) { + // wanted time reached! + fastforwardMode = false; + } + } + Serial.println(F("Toggle Relays")); +} + +void Relays::off(void) { + digitalWrite(relay1Pin, LOW); + digitalWrite(relay2Pin, LOW); + last_relay_off_ts = millis(); + relaysState = RELAY_STATE_OFF; + // P.print("R-Off"); +} + +void Relays::loop(void) { + if (relaysState == RELAY_STATE_OFF) { + if (millis() - last_relay_off_ts > minOffTime_ms) { + relayCanSwitch = true; + if (fastforwardMode) toggle(); + } + } else { + if (millis() - last_relay_hold_ts > holdTime_ms) { + off(); + } + } +} + +void Relays::fwdToTime(unsigned int hours, unsigned int minutes) { + // @TODO + // as we have a 12h clock, we adjust displayedTime to be before the wanted time + if (hours < displayedTime_hours || + (hours == displayedTime_hours && minutes < displayedTime_minutes)) { + displayedTime_hours -= 12; + if (displayedTime_hours < 0) displayedTime_hours += 24; + } + targetHours = hours; + targetMinutes = minutes; + fastforwardMode = true; +} diff --git a/src/Relays.h b/src/Relays.h new file mode 100644 index 0000000..280ae5b --- /dev/null +++ b/src/Relays.h @@ -0,0 +1,40 @@ +// +// FILE: Relays.h +// VERSION: 1.0 +// PURPOSE: Handle two relays used to toggle voltage polarity to control a clock +// +// + +#ifndef _relaysLoaded +#define _relaysLoaded true + +#include + +class Relays { +public: + Relays():holdTime_ms(200), minOffTime_ms(100), fastforwardMode(false) {}; + void begin(int _relay1Pin, int _relay2Pin); + void toggle(); + void off(); + void loop(); + void setHoldTime_ms(unsigned int newValue) { holdTime_ms = newValue; } + void setMinOffTime_ms(unsigned int newValue) { minOffTime_ms = newValue; } + unsigned int getHoldTime_ms() { return holdTime_ms; } + unsigned int getMinOffTime_ms() { return minOffTime_ms; } + void setDisplayedTime(unsigned int h, unsigned int m) { displayedTime_hours = h; displayedTime_minutes = m; } + unsigned int getDisplayedTime_hours() { return displayedTime_hours; } + unsigned int getDisplayedTime_minutes() { return displayedTime_minutes; } + void fwdToTime(unsigned int hours, unsigned int minutes); +protected: + int relay1Pin, relay2Pin; + unsigned int holdTime_ms; + unsigned int minOffTime_ms; + unsigned int displayedTime_hours; + unsigned int displayedTime_minutes; + unsigned int targetHours; + unsigned int targetMinutes; + boolean fastforwardMode; + +}; + +#endif diff --git a/src/data/btn-do.png b/src/data/btn-do.png new file mode 100644 index 0000000000000000000000000000000000000000..ec3d62dbbdad51410ee8f834b78a3304952ab476 GIT binary patch literal 9725 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 literal 0 HcmV?d00001 diff --git a/src/data/favicon.ico b/src/data/favicon.ico new file mode 100755 index 0000000000000000000000000000000000000000..1e72158c1f2954bb9595344082f0447d24561484 GIT binary patch literal 4150 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 B>XQHf literal 0 HcmV?d00001 diff --git a/src/data/icon-0-75x.png b/src/data/icon-0-75x.png new file mode 100644 index 0000000000000000000000000000000000000000..7e4889b519f66c7f6b1ab1f0e96b0953df42f055 GIT binary patch literal 1846 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 literal 0 HcmV?d00001 diff --git a/src/data/icon-2x.png b/src/data/icon-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..28e0d8733317793fa6c1ada5b8247e38dd073264 GIT binary patch literal 6439 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 literal 0 HcmV?d00001 diff --git a/src/data/icon-4x.png b/src/data/icon-4x.png new file mode 100644 index 0000000000000000000000000000000000000000..cfb0e6231566273ca5fc92664256c75b9f04c86d GIT binary patch literal 16764 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 literal 0 HcmV?d00001 diff --git a/src/data/img_sh0.gif b/src/data/img_sh0.gif new file mode 100644 index 0000000000000000000000000000000000000000..3b4fec49e6a72ce9a09120d9e49f0341da30348b GIT binary patch literal 191 zcmZ?wbhEHbgww1=H^yjUhePjFC-)+DJeN|;>5JHG+$re zoSYn6TidX(Ff%i=&6_u~v$KbUgoucUl$MrqaB$ez*nIi&g_V_+iHV7snVE}=i-m=S zk&*HL|NjgO42nNlfFuKh4u}BR$-rv!;8^JamXbTmN}ZS-eYe}i#R^NNsfIYJPU3pz otW#$(gTeXH1PvY0Y0sU$>KI9#V<>8rP?8FK-Y_F2NRh!B04gptJpcdz literal 0 HcmV?d00001 diff --git a/src/data/index.htm b/src/data/index.htm new file mode 100644 index 0000000..b0b56fc --- /dev/null +++ b/src/data/index.htm @@ -0,0 +1,94 @@ + + + + + + + Fastclock + + + + + + +
+
+
+
+ + + +
+
+ + + diff --git a/src/data/led-off.png b/src/data/led-off.png new file mode 100644 index 0000000000000000000000000000000000000000..89f78cbb1c68fe6377d65d9b8692cfc911b023d2 GIT binary patch literal 1248 zcmV<61Rwi}P)8F4TA}R+iJ+xAZ z0!G2m20M1Vv7LCm<6Yj@d8S%W;ehrqqO;PY&Fsu3`1EPo((*re%mJxJdPv$O)d4>wh00kn1~^4p zA-zs|t>C)O)rEx%L|yVb9oOr0QYKlZl+TY`*Bu-S@%Woo>+8cHaAD7cKBo_ z;SS`kJXw>Zmq>4uURhsWo}V?0x%%E-CQA=8pfK_E?3X_Ka-q=k!tnb$-R^B_{z%#< zbxET$Z;h0JoS@r9(mSNLHZEOSvfJ(1exqScAV6K1T0*j^1_;EQZ42uEb}IO4R%>=ZfMxh^g)~FSiB-j&RY89sggq4EjZMpX4+gg|AErtr zWSSF9A(N@jhT&Yl*|duA-!j2WGZa#+6u0>~_fMXgEp0{7yKAPIXIF&@Bb6Z8Al0kY z>V^9LeuiDNz%>H^Q<#Mc;3{ZOVIp{3;Vk3KTrrG~iH}1zOZSvY*_FRBTNI)cAO`bz zC@nf3fFjBnmp}k=e4bj?n|RH}(2**T{JkT;*K;#SGFh@t?r*R;dS(NvkAyM= zaF6<2dgevZ3)C#ZRZqdM1SHRP-#a-;O@P=z*>GXO99+@?7^P&xCny)=WSm!#bm5>%Mv9BB3~nX0hD{2RS17-V zF#O>O7(w)5ISJRMXDt%nB`o*%F`nN$zq(q36$40_62b|h{#_J(^{Qd4(TC6UkWl0w z7$OPD5;6c_9ClbNb~kU{yo=#onWI#(Bpa~#Ad2o2)HVf@&mZow)v#G)Q#Y`QY@mIJ zEeDV5*!F03W8?nZ(vpX+qog1cu_UcbOvDcxr*TK-!ZVXZpQL?I+`ID{KBF*5YzU#RYx>MP4pj2Z+CY zSW&v^7*NGC9s@H!M8XVW>+U`hX&(E<2z^J$LkF-Pm(Gy{RPFfVX^F@HPzKhMVJUNu z4=A|^e=3|Z?y3)$HKc~rkQ!1$YDf*KAvL6i)Q}qTnS=aOfB^u|y4d?!?`qKi0000< KMNUMnLSTZwj!g9c literal 0 HcmV?d00001 diff --git a/src/data/led-on.png b/src/data/led-on.png new file mode 100644 index 0000000000000000000000000000000000000000..d2d8c715af8fd26e4d322be01454e2c0b26393c3 GIT binary patch literal 2533 zcmVPLTJ&Xes`+!{++vX?0QL%S+51t_N6j3|z%t2w14z}vZQyJHnS}%)1H~kuHn3xV zgHNz{W+_FM6A~??ZGx3)z?iz~04ANYQB#>cVNO^TDB~6jwVBT=Ss4tIEEp{$Yy{<& z0F%s_RF!EwrVJD(l_)o;AF~`2A15cIDJ&!kD4DlafsT3CJb)%Rl~h%bD1mWOuuP%0 zR-jxF7i|Dd_f7&Si+PU0E>UCeV>StzaKSPXb1XJM_jgfYTWGMsMA8REJY`v%1cOwv zfHIXeNs(FDV%E_lnK5TLmhP)0dL~Vix@H&cSg5V@3XoZcBU9Bhs42-%h7c97qk+n+ zwz61>85>MX6BMATO&iNy!NR_roYyuPkS-pk0UZT!7F4MzTq8xW^sV2H2-|KpVFOPD3!y3D2T3`&*#jO_3UzztH-TlZF0t_r|Hsl*nir=eOZL5Uzef^<@lNxu&jf-HL; z9B}TCx^8Ur`@al2o!<-f?@=SQ1$SH>ndayvh2<7C7-J^0O`en-$x6}p7lew2o;LuT zBNZ)^s(J)c;2hi(WIss;V}o&0;TMQb5(IBd2f-i9Nzy3Oy+9iwaWb-|JBl&K5=?s! z+2kDU{QUPXUAo#x503CN$`C?G&?hzZ#WzWeJI=uI2ua2yB}e$$ zq3VhasZu*BmA(si@6He)gLXTH!^1dd1#nDdO(6s{4(IGcO6Ac2InBfqHa2iF)JE#) z4_jgAT?J(1yi_9k4aCEZn4;G7{Tkf9Jp+!D!0v7jwzhf-l?B_i^PJL?en_Wc3UXb_ zY?ufW!ix$gBW%zm7$L#JXq@^2Y_K`>E#^gYM)SU3fR&XQ=ypS>)sAu$o0T62zJ`xa z!J8mY23t&$I_G}H^{@%R5$M_2=>E}tqF<}cK(#82*8MuxXDdnVj!jc8N&&nz7sn)= z#1-l|YDv0Jk_(3_IyNuHs0A?v(An7v3&S3|?!;>{L8{6Ms4C%8mU7rate1n}v=c>D z5$J_85$Y(Ka55tsWJAD+#>9Ak*b@3p5Je<;9x}VDv|*qT>=zv zLAMJh)SB&f@3-&0_YqBm1b7PddZ;N;s@9s@CQ$=@w!3te3a98yxRoTm|9888IcT+h zMX4HhQ*ls)2?2SQ@d!vEK>>Jr3cIZquHL!xWbVq9V|e=X03JU+f@jYnO~DH-9|)bx zT}>)0`@=7fL7d}4rP5#U{SFbl`=@TVw%=_2kosKs5fSJ`9CSu;lB7DYL6V}7pa5K0 zTl?Z?@4WL7G#W2(dwUNaJvxCGFXGfW(H0D4kJDvESy}0xX$!%Wp26ax3)im~e{=cr zyMKQF{myQ^{-ZW|N|DKkK<}j>g_99BxZmkSYxnN${N(MoA40439JaSVhldaA@ad;r zh~q@#aBQTCFP)w1PGwFNj%EM+HJv12b=8NPH%oBq)(i#oYQ5cF{_Fkwzpn4^-z2G4 zrV0gD{D3@bzuNb+W--*xcO24?g%DKK!sn>KY6olT_VDcCG6V zu4Rx}C=>8`&>J^AO0LWB+H14apJ$7UHHv$)6c(!B`yw>>ke!jb1+?&lR4<0p(*W1jx@>8w4b#&{ z1X`wCuRv0z;2DQ-e0+?1dreZ!CVc$y;Xp-&9m)Y!s)?C$Cd$;cBsmB>npBq<~+ zzL%E^aP68;tq2PX9)Ws;IRSBW6vEzK4|aCC@Z?FDfIBFt*)YI||km{i()4X$&NFIG#m+%b> zO)TM5f!X}roqSeyV{o1mlO zS-$KPE(1l0lwajZv+Mz^4A3U)8LRQ2rnHcL7@A0=m1xw|^kRgq+GZc`B?bpHmY}1- z@ubx<+t2<+_i_YUzT#p7jTVmQ3{t1AreYChq$=~MdEetvy(p0x8oZ$CZKx#k zrUtVprQ~@F!~~<0DrUW_EvTyYY3pzxhXb;K&;qJLi0N%%l?|f8;n*evrUU^rNq}~q zYqA^L;w>6L^Aw-JV)41>xFL #include @@ -24,6 +26,10 @@ #include #include #include "MD_RobotEyes.h" +#include +#include // https://github.com/me-no-dev/AsyncTCP +#include +#include "Relays.h" IOTAppStory IAS(COMPDATE, MODEBUTTON); String deviceName = "wemosMatrixDisplay"; @@ -52,9 +58,11 @@ Your hardware matches the setting for FC-16 modules. Please set FC16_HW. // Arbitrary output pins MD_Parola P = MD_Parola(HARDWARE_TYPE, DISPLAY_DATA_PIN, DISPLAY_CLK_PIN, DISPLAY_CS_PIN, MAX_DEVICES); MD_RobotEyes E; +Relays R; WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); +AsyncWebServer server(80); // Field default values char *clockName = "FREMO"; @@ -64,9 +72,7 @@ char *relay1Pin_String = "D1"; char *relay2Pin_String = "D2"; int relay1Pin = D1, relay2Pin = D2; char *relayHoldTime_ms_String = "200"; -unsigned int relayHoldTime_ms = 200; char *relayMinOffTime_ms_String = "100"; -unsigned int relayMinOffTime_ms = 100; unsigned int displayRefresh_ms = 200; // Clock Display Config Parameter @@ -74,7 +80,12 @@ static char * displayClockNameEvery_ms_String = "16000"; static char * displayClockNameDuration_ms_String = "1200"; static uint32_t displayClockNameEvery_ms = 16000; static uint32_t displayClockNameDuration_ms = 1200; -static uint32_t doNotShowClockNameBeforeAndAfterMinuteChange_s = 2; +static uint16_t doNotShowClockNameBeforeAndAfterMinuteChange_s = 2; + +static uint16_t hours, minutes, seconds; +static char minuteProgressIndicator; +static char timeBuffer[10]; + void setupIAS(void) { #if defined ESP8266 @@ -87,7 +98,7 @@ void setupIAS(void) { // preset deviceName this is also your MDNS responder: http://deviceName.local IAS.preSetDeviceName(deviceName); IAS.preSetAppName(F("Wemos2RelaysMatrixDisplays")); - IAS.preSetAppVersion(F("0.2.1")); + IAS.preSetAppVersion(F(APP_VERSION)); IAS.preSetAutoUpdate(true); // define fields @@ -154,8 +165,8 @@ void setupIAS(void) { 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); + R.setHoldTime_ms(atoi(relayHoldTime_ms_String)); + R.setMinOffTime_ms(atoi(relayMinOffTime_ms_String)); displayClockNameEvery_ms = atoi(displayClockNameEvery_ms_String); displayClockNameDuration_ms = atoi(displayClockNameDuration_ms_String); @@ -163,20 +174,13 @@ void setupIAS(void) { 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); + 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); Serial.print(F("Show clock name every (ms): ")); Serial.println(displayClockNameEvery_ms); Serial.print(F("Show clock name for (ms): ")); Serial.println(displayClockNameDuration_ms); } -void setupRelays(int relay1Pin, int relay2Pin) { - pinMode(relay1Pin, OUTPUT); - pinMode(relay2Pin, OUTPUT); - digitalWrite(relay1Pin, LOW); - digitalWrite(relay2Pin, LOW); -} - static MD_MAX72XX *graphicDisplay = NULL; void setupDisplay() { @@ -224,15 +228,118 @@ void setupDisplay() { P.print(intro); } +//called when the url is not defined here return 404 +void onRequest(AsyncWebServerRequest *request){ + //Handle Unknown Request + request->send(404); +} + +void setupFS() { + if(!SPIFFS.begin()){ + Serial.println(F(" SPIFFS Mount Failed")); + return; + } +} + +void setupWebServer() { + server.on("/fwd", HTTP_GET, [](AsyncWebServerRequest *request){ + Serial.println(F("\n WebApp button pressed -- move clock forward")); + + R.toggle(); + + // create json return + String json = "{"; + json += "\"result\":\"OK\","; + json += "\"clockName\":\"" + String(clockName) + "\","; + json += "\"hours\":\"" + String(hours) + "\","; + json += "\"minutes\":\"" + String(minutes) + "\","; + json += "\"seconds\":\"" + String(seconds) + "\""; + json += "}"; + + // return json to WebApp + request->send(200, F("text/json"), json); + json = String(); + }); + + server.on("/clock", HTTP_GET, [](AsyncWebServerRequest *request){ + // create json return + String json = "{"; + json += "\"clockName\":\""+String(clockName)+"\","; + json += "\"clockSpeed\":\""+String(clockSpeed_modelMsPerRealSec)+"\","; + json += "\"relayHoldTime_ms\":\""+String(R.getHoldTime_ms())+"\","; + json += "\"relayMinOffTime_ms\":\""+String(R.getMinOffTime_ms())+"\","; + json += "\"displayRefresh_ms\":\""+String(displayRefresh_ms)+"\","; + json += "\"displayClockNameEvery_ms\":\""+String(displayClockNameEvery_ms)+"\","; + json += "\"displayClockNameDuration_ms\":\""+String(displayClockNameDuration_ms)+"\","; + json += "\"doNotShowClockNameBeforeAndAfterMinuteChange_s\":\""+String(doNotShowClockNameBeforeAndAfterMinuteChange_s)+"\","; + json += "\"real_hours\":\""+String(hours)+"\","; + json += "\"real_minutes\":\""+String(minutes)+"\","; + json += "\"real_seconds\":\""+String(seconds)+"\","; + json += "\"model_hours\":\""+String(hours)+"\","; + json += "\"model_minutes\":\""+String(minutes)+"\","; + json += "\"model_seconds\":\""+String(seconds)+"\""; + json += "}"; + + // return json to WebApp + request->send(200, F("text/json"), json); + json = String(); + }); + + server.on("/setDT", HTTP_GET, [](AsyncWebServerRequest *request){ + String h, m, message; + Serial.println(F("\n Setting displayed time of clock")); + + message = ""; + if (request->hasParam("h")) { + h = request->getParam("h")->value(); + } else { + message += "Parameter h for hours missing. "; + } + if (request->hasParam("m")) { + m = request->getParam("m")->value(); + } else { + message += "Parameter m for minutes missing. "; + } + + R.setDisplayedTime(h.toInt(), m.toInt()); + h = String(); + m = String(); + + // create json return + String json = "{"; + if (message.length() > 0) { + json += "\"result\":\"Error\","; + json += "\"message\": \"" + message + "\""; + } else { + json += "\"result\":\"OK\""; + } + json += "}"; + + // return json to WebApp + request->send(200, F("text/json"), json); + json = String(); + }); + + server.serveStatic("/", SPIFFS, "/"); + server.onNotFound(onRequest); + + // start the HTTP server + server.begin(); + Serial.print(F("HTTP server started at: ")); + Serial.println(WiFi.localIP()); + Serial.println(""); +} + void setup(void) { Serial.println(F("setup():")); setupDisplay(); setupIAS(); + setupFS(); + setupWebServer(); delay(200); - setupRelays(relay1Pin, relay2Pin); - + R.begin(relay1Pin, relay2Pin); timeClient.begin(); Serial.println(F("setup() finished")); } @@ -242,51 +349,6 @@ static bool timeClientInitialized = false; static unsigned long lastTimeOutput_ms = 0; #define TIME_BETWEEN_REALTIME_UPDATE_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(); - } - } -} typedef struct { @@ -330,6 +392,7 @@ void loopStartupAnimation() { index++; if (index >= ARRAY_SIZE(eSeq)) index = 0; + P.displayClear(); #if DISPLAY_ANIM_NAME E.setText(eSeq[index].name); #endif @@ -363,14 +426,26 @@ void loopStartupAnimation() { } } +void reInitializeDisplay() { + static unsigned long last_reinit_ts = 0; + #define REINIT_AFTER_ms 5000 + #define AVOID_REINIT_BEFORE_AND_AFTER_FULLMINUTE_FOR_s 3 + + if (last_reinit_ts == 0) last_reinit_ts = millis(); + + if (millis() - last_reinit_ts > REINIT_AFTER_ms + && seconds < 60 - AVOID_REINIT_BEFORE_AND_AFTER_FULLMINUTE_FOR_s + && seconds > AVOID_REINIT_BEFORE_AND_AFTER_FULLMINUTE_FOR_s) { + P.begin(); + last_reinit_ts = millis(); + } +} + 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; @@ -415,32 +490,6 @@ void loop(void) if (minuteProgressIndicator > 9) minuteProgressIndicator = 9; snprintf(timeBuffer, 10, "%c %2d:%02d", minuteProgressIndicator, hours, minutes); - //P.displayAnimate(); - //P.displayClear(); - /* 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 */ - - // standard procedure to display static uint32_t last_clock_refresh = 0; static uint32_t lastTimeClockNameShown = 0; @@ -455,7 +504,8 @@ void loop(void) if ((millis() - lastTimeClockNameShown > displayClockNameEvery_ms) && (seconds < 60-doNotShowClockNameBeforeAndAfterMinuteChange_s) && (seconds > doNotShowClockNameBeforeAndAfterMinuteChange_s)) { - P.begin(); // re-initialize, that fixes display problems due to electrical relais feedbacks + //P.begin(); // re-initialize, that fixes display problems due to electrical relais feedbacks + reInitializeDisplay(); P.setIntensity(2); P.print(clockName); lastTimeClockNameShown = millis(); @@ -463,7 +513,8 @@ void loop(void) } else { // showing clock if (millis() - last_clock_refresh > displayRefresh_ms) { - //P.displayClear(); + // P.begin(); // re-initialize, that fixes display problems due to electrical relais feedbacks + reInitializeDisplay(); P.setIntensity(1); P.print(timeBuffer); last_clock_refresh = millis(); @@ -473,10 +524,10 @@ void loop(void) // toggle relays if (lastMinutes != minutes) { - toggleRelays(); + R.toggle(); lastMinutes = minutes; } lastSeconds = seconds; - loopRelays(); + R.loop(); }