2019-01-25 12:14:52 +00:00
|
|
|
/* Wemos8266RelaysLedDisplay/main.cpp
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define COMPDATE __DATE__ __TIME__
|
2019-01-29 08:49:11 +00:00
|
|
|
#define APP_VERSION "0.2.18"
|
|
|
|
|
2019-01-25 12:14:52 +00:00
|
|
|
// 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
|
2019-01-25 19:23:10 +00:00
|
|
|
#define DEBUG_DISPLAY false
|
2019-01-29 08:49:11 +00:00
|
|
|
#define STARTUP1_ANIMATION_DURATION_ms 2000
|
|
|
|
#define STARTUP2_ANIMATION_DURATION_ms 33000
|
2019-01-25 12:14:52 +00:00
|
|
|
|
|
|
|
#include <Arduino.h>
|
|
|
|
#include <IOTAppStory.h>
|
|
|
|
#include <MD_Parola.h>
|
|
|
|
#include <MD_MAX72xx.h>
|
|
|
|
#include <SPI.h>
|
|
|
|
#include <NTPClient.h>
|
|
|
|
#include <ESP8266WiFi.h>
|
|
|
|
#include <WiFiUdp.h>
|
2019-01-28 11:22:08 +00:00
|
|
|
#include "MD_RobotEyes.h"
|
2019-01-29 08:49:11 +00:00
|
|
|
#include <FS.h>
|
|
|
|
#include <ESPAsyncTCP.h> // https://github.com/me-no-dev/AsyncTCP
|
|
|
|
#include <ESPAsyncWebServer.h>
|
2019-01-29 10:26:35 +00:00
|
|
|
#include <ESP8266HTTPClient.h>
|
2019-01-29 08:49:11 +00:00
|
|
|
#include "Relays.h"
|
2019-01-25 12:14:52 +00:00
|
|
|
|
|
|
|
IOTAppStory IAS(COMPDATE, MODEBUTTON);
|
|
|
|
String deviceName = "wemosMatrixDisplay";
|
|
|
|
String chipId;
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
// 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);
|
2019-01-28 11:22:08 +00:00
|
|
|
MD_RobotEyes E;
|
2019-01-29 08:49:11 +00:00
|
|
|
Relays R;
|
2019-01-25 12:14:52 +00:00
|
|
|
|
|
|
|
WiFiUDP ntpUDP;
|
|
|
|
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);
|
2019-01-29 08:49:11 +00:00
|
|
|
AsyncWebServer server(80);
|
2019-01-25 12:14:52 +00:00
|
|
|
|
|
|
|
// Field default values
|
2019-01-25 19:23:10 +00:00
|
|
|
char *clockName = "FREMO";
|
2019-01-25 12:14:52 +00:00
|
|
|
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";
|
|
|
|
char *relayMinOffTime_ms_String = "100";
|
2019-01-28 11:22:08 +00:00
|
|
|
unsigned int displayRefresh_ms = 200;
|
2019-01-25 12:14:52 +00:00
|
|
|
|
2019-01-25 19:23:10 +00:00
|
|
|
// Clock Display Config Parameter
|
|
|
|
static char * displayClockNameEvery_ms_String = "16000";
|
|
|
|
static char * displayClockNameDuration_ms_String = "1200";
|
|
|
|
static uint32_t displayClockNameEvery_ms = 16000;
|
|
|
|
static uint32_t displayClockNameDuration_ms = 1200;
|
2019-01-29 08:49:11 +00:00
|
|
|
static uint16_t doNotShowClockNameBeforeAndAfterMinuteChange_s = 2;
|
|
|
|
|
|
|
|
static uint16_t hours, minutes, seconds;
|
|
|
|
static char minuteProgressIndicator;
|
|
|
|
static char timeBuffer[10];
|
|
|
|
|
2019-01-25 12:14:52 +00:00
|
|
|
|
|
|
|
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"));
|
2019-01-29 08:49:11 +00:00
|
|
|
IAS.preSetAppVersion(F(APP_VERSION));
|
2019-01-25 12:14:52 +00:00
|
|
|
IAS.preSetAutoUpdate(true);
|
|
|
|
|
|
|
|
// define fields
|
|
|
|
IAS.addField(clockName, "Clock Name", 8, 'T');
|
|
|
|
IAS.addField(clockSpeed_modelMsPerRealSec_String, "Model MilliSec per Real Sec", 8, 'N');
|
2019-01-25 19:23:10 +00:00
|
|
|
IAS.addField(displayClockNameEvery_ms_String, "Display clock name every (ms)", 5, 'N');
|
|
|
|
IAS.addField(displayClockNameDuration_ms_String, "Display clock name duration (ms)", 5, 'N');
|
2019-01-25 12:14:52 +00:00
|
|
|
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([]() {
|
2019-01-28 11:22:08 +00:00
|
|
|
Serial.println(F(" If mode button is released, I will enter firmware update mode."));
|
|
|
|
Serial.println(F("*----------------------------------------------------------------------*"));
|
2019-01-25 12:14:52 +00:00
|
|
|
P.print("|updt");
|
|
|
|
});
|
|
|
|
|
|
|
|
IAS.onModeButtonLongPress([]() {
|
2019-01-28 11:22:08 +00:00
|
|
|
Serial.println(F(" If mode button is released, I will enter configuration mode."));
|
|
|
|
Serial.println(F("*----------------------------------------------------------------------*"));
|
2019-01-25 12:14:52 +00:00
|
|
|
P.print("|cfg");
|
|
|
|
});
|
|
|
|
|
|
|
|
IAS.onFirstBoot([]() {
|
|
|
|
Serial.println(F(" Manual reset necessary after serial upload!"));
|
2019-01-28 11:22:08 +00:00
|
|
|
Serial.println(F("*----------------------------------------------------------------------*"));
|
2019-01-25 12:14:52 +00:00
|
|
|
P.print("|rst");
|
|
|
|
ESP.reset();
|
|
|
|
});
|
|
|
|
|
|
|
|
IAS.onConfigMode([]() {
|
2019-01-28 11:22:08 +00:00
|
|
|
P.print(" WiFi");
|
2019-01-25 12:14:52 +00:00
|
|
|
delay(400);
|
2019-01-28 11:22:08 +00:00
|
|
|
P.print(":" + chipId);
|
2019-01-25 12:14:52 +00:00
|
|
|
Serial.print(F("Entered config mode for Wifi, device=")); Serial.println(chipId);
|
|
|
|
});
|
|
|
|
|
|
|
|
IAS.onFirmwareUpdateCheck([]() {
|
2019-01-28 11:22:08 +00:00
|
|
|
// P.print("chk upd");
|
2019-01-25 12:14:52 +00:00
|
|
|
Serial.println(F("Firmware update check"));
|
|
|
|
});
|
|
|
|
|
|
|
|
IAS.onFirmwareUpdateDownload([]() {
|
2019-01-28 11:22:08 +00:00
|
|
|
P.print("dl+instl");
|
2019-01-25 12:14:52 +00:00
|
|
|
Serial.println(F("Download and install new firmware"));
|
|
|
|
});
|
|
|
|
|
|
|
|
IAS.onFirmwareUpdateError([]() {
|
2019-01-28 11:22:08 +00:00
|
|
|
// P.print("Err fwu");
|
2019-01-25 12:14:52 +00:00
|
|
|
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);
|
2019-01-29 10:26:35 +00:00
|
|
|
IAS.callHome(true /*SPIFFS-check*/);
|
2019-01-25 12:14:52 +00:00
|
|
|
clockSpeed_modelMsPerRealSec = atoi(clockSpeed_modelMsPerRealSec_String);
|
|
|
|
relay1Pin = IAS.dPinConv(relay1Pin_String);
|
|
|
|
relay2Pin = IAS.dPinConv(relay2Pin_String);
|
2019-01-29 08:49:11 +00:00
|
|
|
R.setHoldTime_ms(atoi(relayHoldTime_ms_String));
|
|
|
|
R.setMinOffTime_ms(atoi(relayMinOffTime_ms_String));
|
2019-01-25 19:23:10 +00:00
|
|
|
displayClockNameEvery_ms = atoi(displayClockNameEvery_ms_String);
|
|
|
|
displayClockNameDuration_ms = atoi(displayClockNameDuration_ms_String);
|
2019-01-25 12:14:52 +00:00
|
|
|
|
|
|
|
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"));
|
2019-01-29 08:49:11 +00:00
|
|
|
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);
|
2019-01-25 19:23:10 +00:00
|
|
|
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);
|
2019-01-25 12:14:52 +00:00
|
|
|
}
|
|
|
|
|
2019-01-28 11:22:08 +00:00
|
|
|
static MD_MAX72XX *graphicDisplay = NULL;
|
|
|
|
|
2019-01-25 12:14:52 +00:00
|
|
|
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
|
2019-01-25 19:23:10 +00:00
|
|
|
static uint8_t newZero[] = {0x05, 0x3e, 0x41, 0x41, 0x41, 0x3e, 0x00};
|
2019-01-25 12:14:52 +00:00
|
|
|
|
|
|
|
P.begin();
|
|
|
|
// P.setZoneEffect(0, true, PA_FLIP_LR);
|
2019-01-28 11:22:08 +00:00
|
|
|
graphicDisplay = P.getGraphicObject();
|
|
|
|
E.begin(graphicDisplay);
|
|
|
|
|
2019-01-25 12:14:52 +00:00
|
|
|
P.setIntensity(1);
|
2019-01-25 19:23:10 +00:00
|
|
|
for (charCode=1; charCode<=9; ++charCode) {
|
2019-01-25 12:14:52 +00:00
|
|
|
P.addChar(charCode, verticalBarFont+2*(charCode-1));
|
|
|
|
}
|
|
|
|
char intro[] = {':', '-', ')', ' ', 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00};
|
2019-01-25 19:23:10 +00:00
|
|
|
|
|
|
|
// replace the 0 characters, we do not like the "slash"
|
|
|
|
P.addChar('0', newZero);
|
2019-01-25 12:14:52 +00:00
|
|
|
P.print(intro);
|
|
|
|
}
|
2019-01-28 11:22:08 +00:00
|
|
|
|
2019-01-29 08:49:11 +00:00
|
|
|
//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){
|
2019-01-29 10:26:35 +00:00
|
|
|
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();
|
2019-01-29 08:49:11 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
server.on("/clock", HTTP_GET, [](AsyncWebServerRequest *request){
|
2019-01-29 10:26:35 +00:00
|
|
|
// 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();
|
2019-01-29 08:49:11 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
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();
|
2019-01-29 10:26:35 +00:00
|
|
|
R.fwdToTime(hours, minutes);
|
2019-01-29 08:49:11 +00:00
|
|
|
});
|
|
|
|
|
2019-01-29 10:26:35 +00:00
|
|
|
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();
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2019-01-29 08:49:11 +00:00
|
|
|
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("");
|
|
|
|
}
|
|
|
|
|
2019-01-28 11:22:08 +00:00
|
|
|
|
2019-01-25 12:14:52 +00:00
|
|
|
void setup(void)
|
|
|
|
{
|
|
|
|
Serial.println(F("setup():"));
|
|
|
|
setupDisplay();
|
2019-01-29 08:49:11 +00:00
|
|
|
setupFS();
|
2019-01-29 10:26:35 +00:00
|
|
|
setupIAS();
|
2019-01-29 08:49:11 +00:00
|
|
|
setupWebServer();
|
2019-01-28 11:22:08 +00:00
|
|
|
delay(200);
|
2019-01-29 08:49:11 +00:00
|
|
|
R.begin(relay1Pin, relay2Pin);
|
2019-01-25 12:14:52 +00:00
|
|
|
timeClient.begin();
|
|
|
|
Serial.println(F("setup() finished"));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool timeClientInitialized = false;
|
|
|
|
static unsigned long lastTimeOutput_ms = 0;
|
2019-01-28 11:22:08 +00:00
|
|
|
#define TIME_BETWEEN_REALTIME_UPDATE_ms 60000
|
2019-01-25 12:14:52 +00:00
|
|
|
|
|
|
|
|
2019-01-28 11:22:08 +00:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
char name[7];
|
|
|
|
MD_RobotEyes::emotion_t e;
|
|
|
|
uint16_t timePause; // in milliseconds
|
|
|
|
} sampleItem_t;
|
|
|
|
|
|
|
|
static const sampleItem_t eSeq[] =
|
|
|
|
{
|
|
|
|
{ "Nutral", MD_RobotEyes::E_NEUTRAL, 1000 },
|
|
|
|
{ "Blink" , MD_RobotEyes::E_BLINK, 1000 },
|
|
|
|
{ "Wink" , MD_RobotEyes::E_WINK, 1000 },
|
|
|
|
{ "Left" , MD_RobotEyes::E_LOOK_L, 1000 },
|
|
|
|
{ "Right" , MD_RobotEyes::E_LOOK_R, 1000 },
|
|
|
|
{ "Up" , MD_RobotEyes::E_LOOK_U, 1000 },
|
|
|
|
{ "Down" , MD_RobotEyes::E_LOOK_D, 1000 },
|
|
|
|
{ "Angry" , MD_RobotEyes::E_ANGRY, 1000 },
|
|
|
|
{ "Sad" , MD_RobotEyes::E_SAD, 1000 },
|
|
|
|
{ "Evil" , MD_RobotEyes::E_EVIL, 1000 },
|
|
|
|
{ "Evil2" , MD_RobotEyes::E_EVIL2, 1000 },
|
|
|
|
{ "Squint", MD_RobotEyes::E_SQUINT, 1000 },
|
|
|
|
{ "Dead" , MD_RobotEyes::E_DEAD, 1000 },
|
|
|
|
{ "ScanV" , MD_RobotEyes::E_SCAN_UD, 1000 },
|
|
|
|
{ "ScanH" , MD_RobotEyes::E_SCAN_LR, 1000 },
|
|
|
|
};
|
|
|
|
#define DISPLAY_ANIM_NAME false
|
|
|
|
|
|
|
|
void loopStartupAnimation() {
|
|
|
|
// show startup animation
|
|
|
|
boolean animationFinished = false;
|
|
|
|
static uint32_t timeStartDelay;
|
|
|
|
static uint8_t index = ARRAY_SIZE(eSeq);
|
|
|
|
static enum { S_IDLE, S_TEXT, S_ANIM, S_PAUSE } state = S_IDLE;
|
|
|
|
|
|
|
|
animationFinished = E.runAnimation();
|
|
|
|
|
|
|
|
switch (state)
|
|
|
|
{
|
|
|
|
case S_IDLE:
|
|
|
|
index++;
|
|
|
|
if (index >= ARRAY_SIZE(eSeq))
|
|
|
|
index = 0;
|
2019-01-29 08:49:11 +00:00
|
|
|
P.displayClear();
|
2019-01-28 11:22:08 +00:00
|
|
|
#if DISPLAY_ANIM_NAME
|
|
|
|
E.setText(eSeq[index].name);
|
|
|
|
#endif
|
|
|
|
state = S_TEXT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_TEXT: // wait for the text to finish
|
|
|
|
if (animationFinished) // text animation is finished
|
|
|
|
{
|
|
|
|
E.setAnimation(eSeq[index].e, true);
|
|
|
|
state = S_ANIM;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_ANIM: // checking animation is completed
|
|
|
|
if (animationFinished) // animation is finished
|
|
|
|
{
|
|
|
|
timeStartDelay = millis();
|
|
|
|
state = S_PAUSE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case S_PAUSE: // non blocking waiting for a period between animations
|
|
|
|
if (millis() - timeStartDelay >= eSeq[index].timePause)
|
|
|
|
state = S_IDLE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
state = S_IDLE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-01-25 12:14:52 +00:00
|
|
|
|
2019-01-29 08:49:11 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-25 12:14:52 +00:00
|
|
|
void loop(void)
|
|
|
|
{
|
|
|
|
int currentDisplayState;
|
|
|
|
static int lastMinutes = 0;
|
|
|
|
static int lastSeconds = 0;
|
|
|
|
#define MsgSize 10
|
|
|
|
static char debugMsg[MsgSize+1];
|
|
|
|
static int recentDisplayState = -1;
|
|
|
|
|
2019-01-28 11:22:08 +00:00
|
|
|
static unsigned long firstLoop_ts = 0;
|
|
|
|
if (firstLoop_ts == 0) firstLoop_ts = millis();
|
|
|
|
|
2019-01-25 12:14:52 +00:00
|
|
|
if (!timeClientInitialized && WiFi.status() == WL_CONNECTED) {
|
|
|
|
timeClient.begin();
|
|
|
|
timeClientInitialized = true;
|
2019-01-28 11:22:08 +00:00
|
|
|
timeClient.update();
|
|
|
|
Serial.println(timeClient.getFormattedTime());
|
|
|
|
lastTimeOutput_ms = millis();
|
2019-01-25 12:14:52 +00:00
|
|
|
}
|
|
|
|
IAS.loop();
|
|
|
|
|
2019-01-28 11:22:08 +00:00
|
|
|
if (timeClientInitialized && millis()-lastTimeOutput_ms > TIME_BETWEEN_REALTIME_UPDATE_ms) {
|
2019-01-25 12:14:52 +00:00
|
|
|
timeClient.update();
|
|
|
|
Serial.println(timeClient.getFormattedTime());
|
|
|
|
lastTimeOutput_ms = millis();
|
|
|
|
}
|
|
|
|
|
2019-01-28 11:22:08 +00:00
|
|
|
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) {
|
|
|
|
loopStartupAnimation();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-25 12:14:52 +00:00
|
|
|
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;
|
|
|
|
}
|
2019-01-25 19:23:10 +00:00
|
|
|
minuteProgressIndicator = seconds/6.7 + 1; // char code 1-8 show vertical bar
|
|
|
|
if (minuteProgressIndicator > 9) minuteProgressIndicator = 9;
|
|
|
|
snprintf(timeBuffer, 10, "%c %2d:%02d", minuteProgressIndicator, hours, minutes);
|
2019-01-25 12:14:52 +00:00
|
|
|
|
2019-01-25 19:23:10 +00:00
|
|
|
// standard procedure to display
|
|
|
|
static uint32_t last_clock_refresh = 0;
|
|
|
|
static uint32_t lastTimeClockNameShown = 0;
|
|
|
|
static boolean showingClockName = false;
|
|
|
|
|
|
|
|
if (showingClockName) {
|
|
|
|
if (millis() - lastTimeClockNameShown > displayClockNameDuration_ms) {
|
|
|
|
// stop showingClockName
|
|
|
|
showingClockName = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ((millis() - lastTimeClockNameShown > displayClockNameEvery_ms)
|
|
|
|
&& (seconds < 60-doNotShowClockNameBeforeAndAfterMinuteChange_s)
|
|
|
|
&& (seconds > doNotShowClockNameBeforeAndAfterMinuteChange_s)) {
|
2019-01-29 08:49:11 +00:00
|
|
|
//P.begin(); // re-initialize, that fixes display problems due to electrical relais feedbacks
|
|
|
|
reInitializeDisplay();
|
2019-01-28 11:22:08 +00:00
|
|
|
P.setIntensity(2);
|
2019-01-25 19:23:10 +00:00
|
|
|
P.print(clockName);
|
|
|
|
lastTimeClockNameShown = millis();
|
|
|
|
showingClockName = true;
|
|
|
|
} else {
|
|
|
|
// showing clock
|
|
|
|
if (millis() - last_clock_refresh > displayRefresh_ms) {
|
2019-01-29 08:49:11 +00:00
|
|
|
// P.begin(); // re-initialize, that fixes display problems due to electrical relais feedbacks
|
|
|
|
reInitializeDisplay();
|
2019-01-28 11:22:08 +00:00
|
|
|
P.setIntensity(1);
|
2019-01-25 19:23:10 +00:00
|
|
|
P.print(timeBuffer);
|
|
|
|
last_clock_refresh = millis();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-25 12:14:52 +00:00
|
|
|
// toggle relays
|
|
|
|
if (lastMinutes != minutes) {
|
2019-01-29 08:49:11 +00:00
|
|
|
R.toggle();
|
2019-01-25 12:14:52 +00:00
|
|
|
lastMinutes = minutes;
|
|
|
|
}
|
|
|
|
lastSeconds = seconds;
|
|
|
|
|
2019-01-29 08:49:11 +00:00
|
|
|
R.loop();
|
2019-01-25 12:14:52 +00:00
|
|
|
}
|