Wemos8266RelaysLedDisplay/src/main.cpp

374 lines
11 KiB
C++

/* 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 <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>
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();
}