Wemos8266RelaysLedDisplay/src/main.cpp

252 lines
8.7 KiB
C++

/* Wemos8266RelaysLedDisplay/main.cpp
* (C) Dirk Jahnke <dirk.jahnke@mailbox.org>
*/
#define COMPDATE __DATE__ __TIME__
// Following defines are set by build environment using -D parameter
// #define APP_VERSION "0.2.18"
// #define FS_UPDATE_BASE_URL "http://ota.iotjunkie.org/fc_client"
// #define FS_UPDATE_AUTH_USER "fcclient"
// #define FS_UPDATE_AUTH_PASSWORD "63Gs59PWveT6uQSh"
#include <Arduino.h>
#include <IOTAppStory.h>
#include <NTPClient.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <FS.h>
#include "WebServer.h"
#include <ESP8266HTTPClient.h>
#include "Relays.h"
#include "Clock.h"
#include "Display.h"
#include "FSUpdater.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 1000
#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);
FSUpdater fsUpdater(FS_UPDATE_BASE_URL, APP_VERSION, FS_UPDATE_AUTH_USER, FS_UPDATE_AUTH_PASSWORD);
#define mkstring(x) #x
// Field default values
#define CLOCK_NAME_LEN 8
#define CLOCK_SPEED_LEN 4
#define RELAY_PIN_LEN 2
#define RELAY_TIMING_LEN 3
#define CLOCK_NAME_DISPLAY_TIMING_LEN 5
static char def_clockName[CLOCK_NAME_LEN+1] = "FREMO";
static char def_clockSpeed_modelMsPerRealSec_String[CLOCK_SPEED_LEN+1] = "250";
static char def_relay1Pin_String[RELAY_PIN_LEN+1] = mkstring(D1);
static char def_relay2Pin_String[RELAY_PIN_LEN+1] = mkstring(D2);
static int relay1Pin = D1, relay2Pin = D2;
static char def_relayHoldTime_ms_String[RELAY_TIMING_LEN+1] = "200";
static char def_relayMinOffTime_ms_String[RELAY_TIMING_LEN+1] = "100";
// Clock Display Config Parameter
static char def_displayClockNameEvery_ms_String[CLOCK_NAME_DISPLAY_TIMING_LEN+1] = "16000";
static char def_displayClockNameDuration_ms_String[CLOCK_NAME_DISPLAY_TIMING_LEN+1] = "1200";
static char *clockName = def_clockName;
static char *clockSpeed_modelMsPerRealSec_String = def_clockSpeed_modelMsPerRealSec_String;
static char *relay1Pin_String = def_relay1Pin_String;
static char *relay2Pin_String = def_relay2Pin_String;
static char *relayHoldTime_ms_String = def_relayHoldTime_ms_String;
static char *relayMinOffTime_ms_String = def_relayMinOffTime_ms_String;
static char *displayClockNameEvery_ms_String = def_displayClockNameEvery_ms_String;
static char *displayClockNameDuration_ms_String = def_displayClockNameDuration_ms_String;
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", CLOCK_NAME_LEN, 'T');
IAS.addField(clockSpeed_modelMsPerRealSec_String, "Model MilliSec per Real Sec", CLOCK_SPEED_LEN, 'N');
IAS.addField(displayClockNameEvery_ms_String, "Display clock name every (ms)", CLOCK_NAME_DISPLAY_TIMING_LEN, 'N');
IAS.addField(displayClockNameDuration_ms_String, "Display clock name duration (ms)", CLOCK_NAME_DISPLAY_TIMING_LEN, 'N');
IAS.addField(relay1Pin_String, "Pin Relay 1", RELAY_PIN_LEN, 'P');
IAS.addField(relay2Pin_String, "Pin Relay 2", RELAY_PIN_LEN, 'P');
IAS.addField(relayHoldTime_ms_String, "Relay hold time (ms)", RELAY_TIMING_LEN, 'N');
IAS.addField(relayMinOffTime_ms_String, "Relay min off time (ms)", RELAY_TIMING_LEN, '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();
W.setIAS(&IAS);
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 unsigned int lastMinutes = 0;
static unsigned long lastIASLoop_ts = 0;
#define CALL_IAS_EVERY_ms 100
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();
}
if (millis() - lastIASLoop_ts > CALL_IAS_EVERY_ms) {
IAS.loop();
lastIASLoop_ts = millis();
}
if (timeClientInitialized && millis()-lastTimeOutput_ms > TIME_BETWEEN_REALTIME_UPDATE_ms) {
timeClient.update();
Serial.print("t-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 on minute change
if (lastMinutes != realTime.getMinutes()) {
R.toggle();
lastMinutes = realTime.getMinutes();
}
R.loop();
}