Compare commits

...

2 Commits

23 changed files with 501 additions and 107 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@
.clang_complete .clang_complete
.gcc-flags.json .gcc-flags.json
firmware.bin firmware.bin
/data/*

View File

@ -19,6 +19,8 @@ lib_deps =
MD_MAX72XX MD_MAX72XX
IOTAppStory-ESP IOTAppStory-ESP
NTPClient NTPClient
ESPAsyncTCP
ESP Async WebServer
upload_port = /dev/cu.wchusbserial1420 upload_port = /dev/cu.wchusbserial1420
upload_speed = 921600 upload_speed = 921600

View File

@ -23,21 +23,21 @@ const MD_RobotEyes::animFrame_t MD_RobotEyes::seqWink[] /*PROGMEM*/ =
{ { 2, 0 }, FRAME_TIME/2 }, { { 2, 0 }, FRAME_TIME/2 },
{ { 3, 0 }, FRAME_TIME/2 }, { { 3, 0 }, FRAME_TIME/2 },
{ { 4, 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*/ = const MD_RobotEyes::animFrame_t MD_RobotEyes::seqRight[] /*PROGMEM*/ =
{ {
{ { 0, 0 }, FRAME_TIME }, { { 0, 0 }, FRAME_TIME },
{ { 6, 6 }, 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*/ = const MD_RobotEyes::animFrame_t MD_RobotEyes::seqLeft[] /*PROGMEM*/ =
{ {
{ { 0, 0 }, FRAME_TIME }, { { 0, 0 }, FRAME_TIME },
{ { 8, 8 }, 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*/ = 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 }, { { 00, 00 }, FRAME_TIME },
{ { 14, 14 }, FRAME_TIME }, { { 14, 14 }, FRAME_TIME },
{ { 15, 15 }, 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*/ = 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 }, { { 22, 17 }, FRAME_TIME },
{ { 23, 18 }, FRAME_TIME }, { { 23, 18 }, FRAME_TIME },
{ { 24, 19 }, FRAME_TIME }, { { 24, 19 }, FRAME_TIME },
{ { 25, 20 }, 2000 }, { { 25, 20 }, 1000 },
}; };
const MD_RobotEyes::animFrame_t MD_RobotEyes::seqSad[] /*PROGMEM*/ = 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 }, { { 00, 00 }, FRAME_TIME },
{ { 32, 27 }, FRAME_TIME }, { { 32, 27 }, FRAME_TIME },
{ { 33, 28 }, FRAME_TIME }, { { 33, 28 }, FRAME_TIME },
{ { 34, 29 }, 2000 }, { { 34, 29 }, 1000 },
}; };
const MD_RobotEyes::animFrame_t MD_RobotEyes::seqEvil[] /*PROGMEM*/ = const MD_RobotEyes::animFrame_t MD_RobotEyes::seqEvil[] /*PROGMEM*/ =
{ {
{ { 00, 00 }, FRAME_TIME }, { { 00, 00 }, FRAME_TIME },
{ { 39, 37 }, FRAME_TIME }, { { 39, 37 }, FRAME_TIME },
{ { 40, 38 }, 2000 }, { { 40, 38 }, 1000 },
}; };
const MD_RobotEyes::animFrame_t MD_RobotEyes::seqEvil2[] /*PROGMEM*/ = 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 }, { { 54, 17 }, FRAME_TIME },
{ { 55, 18 }, FRAME_TIME }, { { 55, 18 }, FRAME_TIME },
{ { 56, 19 }, FRAME_TIME }, { { 56, 19 }, FRAME_TIME },
{ { 57, 20 }, 2000 }, { { 57, 20 }, 1000 },
}; };
const MD_RobotEyes::animFrame_t MD_RobotEyes::seqSquint[] /*PROGMEM*/ = 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 }, { { 54, 54 }, FRAME_TIME },
{ { 55, 55 }, FRAME_TIME }, { { 55, 55 }, FRAME_TIME },
{ { 56, 56 }, FRAME_TIME }, { { 56, 56 }, FRAME_TIME },
{ { 57, 57 }, 2000 }, { { 57, 57 }, 1000 },
}; };
const MD_RobotEyes::animFrame_t MD_RobotEyes::seqDead[] /*PROGMEM*/ = const MD_RobotEyes::animFrame_t MD_RobotEyes::seqDead[] /*PROGMEM*/ =

85
src/Relays.cpp Normal file
View File

@ -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;
}

40
src/Relays.h Normal file
View File

@ -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 <Arduino.h>
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

BIN
src/data/btn-do.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
src/data/btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
src/data/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
src/data/fremoei.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/data/icon-0-75x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
src/data/icon-1-5x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
src/data/icon-1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
src/data/icon-2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
src/data/icon-3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/data/icon-4x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
src/data/img_fremo_sw.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/data/img_sh0.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

94
src/data/index.htm Normal file
View File

@ -0,0 +1,94 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Fastclock</title>
<link href="main.css" rel="stylesheet" type="text/css" />
<link rel="manifest" href="manifest.json">
<script type="text/javascript">
var button = document.getElementById("button");
var led = document.getElementById("led");
function ajaxGet(url, responseCallback) {
var xh = new XMLHttpRequest();
xh.onreadystatechange = function () {
if (xh.readyState == 4) {
if (xh.status == 200) {
var result = JSON.parse(xh.responseText);
responseCallback(result);
}
}
};
xh.open("GET", url, true);
xh.send(null);
};
function handleFwdClockResponse(result) {
if (result.result == "OK") {
led.src = "led-on.png";
var clockName = document.getElementById("clockNameId");
clockName.html = result.clockName;
var displayedTime = document.getElementById("displayedTimeId");
displayedTime.html = result.hours + ":" + result.minutes + " " + result.seconds;
} else {
led.src = "led-off.png";
}
};
function fwdClock() {
ajaxGet("/fwd", handleFwdClockResponse);
};
function handleClockStatusResponse(result) {
};
function getClockStatus() {
ajaxGet("/clock", handleClockStatusResponse);
setTimeout(function() {
getClockStatus();
}, 400);
};
function setDisplayedTime() {
var hoursInput = document.getElementById("displayedHoursInputId");
var minutesInput = document.getElementById("displayedMinutesInputId");
alert("Values=" + hoursInput.value + ":" + minutesInput.value);
}
function onBodyLoad() {
var button = document.getElementById("button");
button.onmousedown = function(event) {
event.stopPropagation();
event.preventDefault();
button.src = "btn-do.png";
fwdClock();
navigator.vibrate(20);
};
button.onmouseup = function() {
setTimeout(function() {
button.src = "btn.png";
}, 400);
};
setTimeout(function() {
getClockStatus();
}, 400);
};
</script>
</head>
<body id="index" onload="onBodyLoad()">
<div class="hdr"><img src="fremoei.gif"></div>
<div><label>Clock name: </label><span id="clockNameId"></span></div>
<div><label>Time: </label><span id="displayedTimeId"></span></div>
<div>
<input type="number" min="0" max="23" id="displayedHoursInputId">
<input type="number" min="0" max="59" id="displayedMinutesInputId">
<input type="button" onclick="setDisplayedTime">
</div>
<div class="btn_cnt"><img id="led" src="led-off.png"><a href="#"><img id="button" src="btn.png"></a></div>
</body>
</html>

BIN
src/data/led-off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/data/led-on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

47
src/data/main.css Normal file
View File

@ -0,0 +1,47 @@
html {
width: 100%;
height: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
*, *:before, *:after {
-webkit-box-sizing: inherit;
-moz-box-sizing: inherit;
box-sizing: inherit;
}
:focus {
outline: none;
outline-style: none;
box-shadow: none;
border-color: transparent;
}
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background: #EEE;
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
text-align: center;
color: #555;
}
.hdr {
width: 100%;
padding: 15px 0 15px 0;
}
.btn_cnt {
width: 111px;
margin: 0 auto 0 auto;
padding-top: 5%;
}
.btn_cnt a {
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
}

39
src/data/manifest.json Normal file
View File

@ -0,0 +1,39 @@
{
"name": "Fastclock Client",
"icons": [{
"src": "icon-0-75x.png",
"sizes": "36x36",
"type": "image/png",
"density": 0.75
}, {
"src": "icon-1x.png",
"sizes": "48x48",
"type": "image/png",
"density": 1.0
}, {
"src": "icon-1-5x.png",
"sizes": "72x72",
"type": "image/png",
"density": 1.5
}, {
"src": "icon-2x.png",
"sizes": "96x96",
"type": "image/png",
"density": 2.0
}, {
"src": "icon-3x.png",
"sizes": "144x144",
"type": "image/png",
"density": 3.0
}, {
"src": "icon-4x.png",
"sizes": "192x192",
"type": "image/png",
"density": 4.0
}],
"start_url": "index.htm",
"display": "standalone",
"orientation": "portrait",
"theme_color": "dimgray",
"background_color": "black"
}

View File

@ -2,6 +2,8 @@
*/ */
#define COMPDATE __DATE__ __TIME__ #define COMPDATE __DATE__ __TIME__
#define APP_VERSION "0.2.18"
// Button pin on the esp for selecting modes. 0 for Generic devices! // Button pin on the esp for selecting modes. 0 for Generic devices!
#define MODEBUTTON D3 #define MODEBUTTON D3
#define RELAY1_PIN D1 #define RELAY1_PIN D1
@ -12,8 +14,8 @@
#define VERTICAL_BAR_STARTS_TOP false #define VERTICAL_BAR_STARTS_TOP false
#define DEBUG_RELAYS false #define DEBUG_RELAYS false
#define DEBUG_DISPLAY false #define DEBUG_DISPLAY false
#define STARTUP1_ANIMATION_DURATION_ms 15000 #define STARTUP1_ANIMATION_DURATION_ms 2000
#define STARTUP2_ANIMATION_DURATION_ms 45000 #define STARTUP2_ANIMATION_DURATION_ms 33000
#include <Arduino.h> #include <Arduino.h>
#include <IOTAppStory.h> #include <IOTAppStory.h>
@ -24,6 +26,11 @@
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <WiFiUdp.h> #include <WiFiUdp.h>
#include "MD_RobotEyes.h" #include "MD_RobotEyes.h"
#include <FS.h>
#include <ESPAsyncTCP.h> // https://github.com/me-no-dev/AsyncTCP
#include <ESPAsyncWebServer.h>
#include <ESP8266HTTPClient.h>
#include "Relays.h"
IOTAppStory IAS(COMPDATE, MODEBUTTON); IOTAppStory IAS(COMPDATE, MODEBUTTON);
String deviceName = "wemosMatrixDisplay"; String deviceName = "wemosMatrixDisplay";
@ -52,9 +59,11 @@ Your hardware matches the setting for FC-16 modules. Please set FC16_HW.
// Arbitrary output pins // Arbitrary output pins
MD_Parola P = MD_Parola(HARDWARE_TYPE, DISPLAY_DATA_PIN, DISPLAY_CLK_PIN, DISPLAY_CS_PIN, MAX_DEVICES); MD_Parola P = MD_Parola(HARDWARE_TYPE, DISPLAY_DATA_PIN, DISPLAY_CLK_PIN, DISPLAY_CS_PIN, MAX_DEVICES);
MD_RobotEyes E; MD_RobotEyes E;
Relays R;
WiFiUDP ntpUDP; WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);
AsyncWebServer server(80);
// Field default values // Field default values
char *clockName = "FREMO"; char *clockName = "FREMO";
@ -64,9 +73,7 @@ char *relay1Pin_String = "D1";
char *relay2Pin_String = "D2"; char *relay2Pin_String = "D2";
int relay1Pin = D1, relay2Pin = D2; int relay1Pin = D1, relay2Pin = D2;
char *relayHoldTime_ms_String = "200"; char *relayHoldTime_ms_String = "200";
unsigned int relayHoldTime_ms = 200;
char *relayMinOffTime_ms_String = "100"; char *relayMinOffTime_ms_String = "100";
unsigned int relayMinOffTime_ms = 100;
unsigned int displayRefresh_ms = 200; unsigned int displayRefresh_ms = 200;
// Clock Display Config Parameter // Clock Display Config Parameter
@ -74,7 +81,12 @@ static char * displayClockNameEvery_ms_String = "16000";
static char * displayClockNameDuration_ms_String = "1200"; static char * displayClockNameDuration_ms_String = "1200";
static uint32_t displayClockNameEvery_ms = 16000; static uint32_t displayClockNameEvery_ms = 16000;
static uint32_t displayClockNameDuration_ms = 1200; 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) { void setupIAS(void) {
#if defined ESP8266 #if defined ESP8266
@ -87,7 +99,7 @@ void setupIAS(void) {
// preset deviceName this is also your MDNS responder: http://deviceName.local // preset deviceName this is also your MDNS responder: http://deviceName.local
IAS.preSetDeviceName(deviceName); IAS.preSetDeviceName(deviceName);
IAS.preSetAppName(F("Wemos2RelaysMatrixDisplays")); IAS.preSetAppName(F("Wemos2RelaysMatrixDisplays"));
IAS.preSetAppVersion(F("0.2.1")); IAS.preSetAppVersion(F(APP_VERSION));
IAS.preSetAutoUpdate(true); IAS.preSetAutoUpdate(true);
// define fields // define fields
@ -150,12 +162,12 @@ void setupIAS(void) {
// Call home interval in seconds, use 60s only for development. // Call home interval in seconds, use 60s only for development.
// Please change it to at least 2 hours in production // Please change it to at least 2 hours in production
IAS.setCallHomeInterval(120); IAS.setCallHomeInterval(120);
//IAS.callHome(false /*SPIFFS-check*/); IAS.callHome(true /*SPIFFS-check*/);
clockSpeed_modelMsPerRealSec = atoi(clockSpeed_modelMsPerRealSec_String); clockSpeed_modelMsPerRealSec = atoi(clockSpeed_modelMsPerRealSec_String);
relay1Pin = IAS.dPinConv(relay1Pin_String); relay1Pin = IAS.dPinConv(relay1Pin_String);
relay2Pin = IAS.dPinConv(relay2Pin_String); relay2Pin = IAS.dPinConv(relay2Pin_String);
relayHoldTime_ms = atoi(relayHoldTime_ms_String); R.setHoldTime_ms(atoi(relayHoldTime_ms_String));
relayMinOffTime_ms = atoi(relayMinOffTime_ms_String); R.setMinOffTime_ms(atoi(relayMinOffTime_ms_String));
displayClockNameEvery_ms = atoi(displayClockNameEvery_ms_String); displayClockNameEvery_ms = atoi(displayClockNameEvery_ms_String);
displayClockNameDuration_ms = atoi(displayClockNameDuration_ms_String); displayClockNameDuration_ms = atoi(displayClockNameDuration_ms_String);
@ -163,20 +175,13 @@ void setupIAS(void) {
Serial.print(F("Relay1 Pin: ")); Serial.println(relay1Pin); Serial.print(F("Relay1 Pin: ")); Serial.println(relay1Pin);
Serial.print(F("Relay2 Pin: ")); Serial.println(relay2Pin); 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("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 hold time (ms): ")); Serial.println(relayHoldTime_ms_String);
Serial.print(F("Relay min off time (ms): ")); Serial.println(relayMinOffTime_ms); 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("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 every (ms): ")); Serial.println(displayClockNameEvery_ms);
Serial.print(F("Show clock name for (ms): ")); Serial.println(displayClockNameDuration_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; static MD_MAX72XX *graphicDisplay = NULL;
void setupDisplay() { void setupDisplay() {
@ -224,15 +229,152 @@ void setupDisplay() {
P.print(intro); 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();
R.fwdToTime(hours, minutes);
});
server.on("/files", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println(F("\n Directory of FS requested"));
FSInfo fs_info;
String message = "";
if (!SPIFFS.info(fs_info)) {
message += "Cannot get info about file system! ";
}
// create json return
String json = "{";
if (message.length() > 0) {
json += "\"result\":\"Error\",";
json += "\"message\":\"" + message + "\"";
} else {
json += "\"result\":\"OK\",";
json += "\"files\":\"[";
Dir dir = SPIFFS.openDir("/");
boolean isFirstEntry = true;
while (dir.next()) {
if (isFirstEntry) { isFirstEntry = false; } else { json += ","; }
json += "{\"filename\":\"" + dir.fileName() + "\",\"size\":" + dir.fileSize() + "}";
}
json += "]";
}
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) void setup(void)
{ {
Serial.println(F("setup():")); Serial.println(F("setup():"));
setupDisplay(); setupDisplay();
setupFS();
setupIAS(); setupIAS();
setupWebServer();
delay(200); delay(200);
setupRelays(relay1Pin, relay2Pin); R.begin(relay1Pin, relay2Pin);
timeClient.begin(); timeClient.begin();
Serial.println(F("setup() finished")); Serial.println(F("setup() finished"));
} }
@ -242,51 +384,6 @@ static bool timeClientInitialized = false;
static unsigned long lastTimeOutput_ms = 0; static unsigned long lastTimeOutput_ms = 0;
#define TIME_BETWEEN_REALTIME_UPDATE_ms 60000 #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 typedef struct
{ {
@ -330,6 +427,7 @@ void loopStartupAnimation() {
index++; index++;
if (index >= ARRAY_SIZE(eSeq)) if (index >= ARRAY_SIZE(eSeq))
index = 0; index = 0;
P.displayClear();
#if DISPLAY_ANIM_NAME #if DISPLAY_ANIM_NAME
E.setText(eSeq[index].name); E.setText(eSeq[index].name);
#endif #endif
@ -363,14 +461,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) void loop(void)
{ {
int currentDisplayState; int currentDisplayState;
int hours, minutes, seconds;
char minuteProgressIndicator;
static int lastMinutes = 0; static int lastMinutes = 0;
static int lastSeconds = 0; static int lastSeconds = 0;
static char timeBuffer[10];
#define MsgSize 10 #define MsgSize 10
static char debugMsg[MsgSize+1]; static char debugMsg[MsgSize+1];
static int recentDisplayState = -1; static int recentDisplayState = -1;
@ -415,32 +525,6 @@ void loop(void)
if (minuteProgressIndicator > 9) minuteProgressIndicator = 9; if (minuteProgressIndicator > 9) minuteProgressIndicator = 9;
snprintf(timeBuffer, 10, "%c %2d:%02d", minuteProgressIndicator, hours, minutes); 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 // standard procedure to display
static uint32_t last_clock_refresh = 0; static uint32_t last_clock_refresh = 0;
static uint32_t lastTimeClockNameShown = 0; static uint32_t lastTimeClockNameShown = 0;
@ -455,7 +539,8 @@ void loop(void)
if ((millis() - lastTimeClockNameShown > displayClockNameEvery_ms) if ((millis() - lastTimeClockNameShown > displayClockNameEvery_ms)
&& (seconds < 60-doNotShowClockNameBeforeAndAfterMinuteChange_s) && (seconds < 60-doNotShowClockNameBeforeAndAfterMinuteChange_s)
&& (seconds > 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.setIntensity(2);
P.print(clockName); P.print(clockName);
lastTimeClockNameShown = millis(); lastTimeClockNameShown = millis();
@ -463,7 +548,8 @@ void loop(void)
} else { } else {
// showing clock // showing clock
if (millis() - last_clock_refresh > displayRefresh_ms) { 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.setIntensity(1);
P.print(timeBuffer); P.print(timeBuffer);
last_clock_refresh = millis(); last_clock_refresh = millis();
@ -473,10 +559,10 @@ void loop(void)
// toggle relays // toggle relays
if (lastMinutes != minutes) { if (lastMinutes != minutes) {
toggleRelays(); R.toggle();
lastMinutes = minutes; lastMinutes = minutes;
} }
lastSeconds = seconds; lastSeconds = seconds;
loopRelays(); R.loop();
} }