Now that the weather has warmed up enough I have installed the ESP8266 upgrade to my outdoor German train signal.
It uses some buttons and the image widget. Sadly the segmented buttons upper case all the captions, making the aspect names incorrect.
https://cabin-layout.mixmox.com/2019/03/Outdoor-signal-with-wi-fi-control.html
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include "Signal.h" // defines auth, ssid, password, version
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#include "ota.h"
// =========================================================
//output pins
// currently set for NodeMCU dont use D3-0 D4-2
const byte PHPR = 16; // D0
const byte PHPG = 5; //D1
const byte PHPY = 4; // D2
const byte PVRO2 = 14; // D5
const byte PVRG2 = 12; // D6
const byte PVRG1 = 13; // D7
const byte PVRO1 = 0; // D3 was 2; // D4
// colors
#define BLYNK_GREEN "#23C48E"
#define BLYNK_YELLOW "#ED9D00"
#define BLYNK_RED "#D3435C"
SimpleTimer timer;
// Signal modes
int Hp = 0;
int Vr = 0;
bool autoA = false; // auto aspect changes
int duration = 0 ; // duration of auto aspect in min
void alloff(){
digitalWrite(PHPR,RELAY_LOW);
digitalWrite(PHPG,RELAY_LOW);
digitalWrite(PHPY,RELAY_LOW);
digitalWrite(PVRO1,RELAY_LOW);
digitalWrite(PVRG1,RELAY_LOW);
digitalWrite(PVRO2,RELAY_LOW);
digitalWrite(PVRG2,RELAY_LOW);
}
void animate() {
static byte step = 0;
Serial.println(step);
static byte s[8];
switch (step) {
case 0 : { s[1] = RELAY_HIGH; s[2] = RELAY_HIGH; s[3] = RELAY_HIGH; s[4] = RELAY_HIGH; s[5] = RELAY_HIGH; s[6] = RELAY_HIGH; s[7] = RELAY_HIGH; break;}; //all on
case 1 : { s[1] = RELAY_LOW ; s[2] = RELAY_LOW ; s[3] = RELAY_LOW ; s[4] = RELAY_LOW ; s[5] = RELAY_LOW ; s[6] = RELAY_LOW ; s[7] = RELAY_LOW ; break;}; //all off
case 2 : { s[1] = RELAY_HIGH; break;}; //
case 3 : { s[1] = RELAY_LOW; s[2] = RELAY_HIGH ; break;}; //
case 4 : { s[2] = RELAY_LOW ; s[3] = RELAY_HIGH ; break;}; //
case 5 : { s[3] = RELAY_LOW ; s[4] = RELAY_HIGH ; break;}; //
case 6 : { s[4] = RELAY_LOW ; s[5] = RELAY_HIGH ; break;}; //
case 7 : { s[5] = RELAY_LOW ; s[6] = RELAY_HIGH ; break;}; //
case 8 : { s[6] = RELAY_LOW ; s[7] = RELAY_HIGH ; break;}; //
case 9 : { s[7] = RELAY_LOW ; break;}; //
case 10 :{ s[2] = RELAY_HIGH; s[5] = RELAY_HIGH; s[7] = RELAY_HIGH; break;}; // greens on
case 11 :{ s[2] = RELAY_LOW; s[3] = RELAY_HIGH; s[4] = RELAY_HIGH; s[5] = RELAY_LOW ; s[6] = RELAY_HIGH; s[7] = RELAY_LOW; break;}; // oranges on
case 16 :
case 20:
case 12 :{ s[3] = RELAY_LOW; s[4] = RELAY_HIGH; s[5] = RELAY_LOW ; s[6] = RELAY_LOW ; break;}; // 4 on
case 17 :
case 13 :{ s[4] = RELAY_LOW ; s[5] = RELAY_HIGH ; break;}; // 5 ON
case 18 :
case 14 :{ s[5] = RELAY_LOW ; s[7] = RELAY_HIGH ; break;}; // 7 ON
case 19 :
case 15 :{ s[6] = RELAY_HIGH ; s[7] = RELAY_LOW; break;}; // 6 ON
case 21 : { s[3] = RELAY_HIGH; s[4] = RELAY_LOW; break;}; // 3 ON
case 22 : { s[2] = RELAY_HIGH; s[3] = RELAY_LOW; break;}; // 2 ON
case 23 :{ s[1] = RELAY_HIGH; s[2] = RELAY_LOW; break;}; // 1 on
default : {step = 0; displayHp(); return;};
} // switch
// set the outputs
digitalWrite(PHPR,s[1]);
digitalWrite(PHPG,s[2]);
digitalWrite(PHPY,s[3]);
digitalWrite(PVRO1,s[4]);
digitalWrite(PVRG1,s[5]);
digitalWrite(PVRO2,s[6]);
digitalWrite(PVRG2,s[7]);
step++;
timer.setTimeout(600,animate); // call ourselves later
}
void showDuration() { // update the auto button to show minutes left
static int timerID;
if (autoA) {
if (duration==0){
autoAspect(); // start a new one
}else{ // still time left
//Serial.println("\r\n"); // force timestamp
//Serial.println(duration);
Blynk.setProperty(V21,"label","≈ " + String(duration--) + " min."); // place the duration on the auto button and decrement it
timerID = timer.setTimeout(60000L , showDuration); // call us back in a minute
}
}else { // not auto anymore
timer.deleteTimer(timerID);
Blynk.setProperty(V21,F("label"),F(" ")); // clear the duration
} // autoA
} // showDuration
/*
Auto aspect scheme:
autoAspect picks a new aspect and duration and calls showDuration.
showDuration will run every ~minute and update the time left and when it gets to zero, call autoAspect again if auto mode is still on.
*/
void autoAspect() { // change aspects randomly
//Serial.println("autoAspect");
// we may get called an extra time after cancelling auto mode
if (! autoA) { return;} // get out if not in auto mode
// if we were not in Hp0 we should switch to Hp0 for a while
if (Hp == 0) { // we were in Hp0 so pick another one at random
Hp = random(1,3); // get a random Hp but not Hp0
Vr = random(0,3); // get a random Vr
} else {
Hp = 0; // switch to Hp 0 for one cycle
}
Blynk.virtualWrite(V1, Hp + 1); //force app to new Hp
displayHp(); // do the lights
Blynk.virtualWrite(V2, Vr + 1); //force app to new Vr
//Serial.print(F("New aspect Hp "));Serial.println(Hp);
//Serial.print(F("New aspect Vr "));Serial.println(Vr);
duration = random(3,20); // 3 minutes to 20 minutes
//Serial.print(F("For "));Serial.println(String(duration) + F(" minutes"));
showImage(); // show the aspect in app
showDuration();
}
void showImage(){ // select the appropriate image in image widget
switch (Hp) {
case 0 : { Blynk.virtualWrite(V20,1); Blynk.virtualWrite(V0,F("Hp0")); return;} // Hp0
case 1 : { //Hp1
switch (Vr) {
case 0 : { Blynk.virtualWrite(V20,2); Blynk.virtualWrite(V0,F("Hp1Vr0")); return;} // Hp1Vr0
case 1 : { Blynk.virtualWrite(V20,4); Blynk.virtualWrite(V0,F("Hp1Vr1")); return;} // Hp1Vr1
case 2 : { Blynk.virtualWrite(V20,6); Blynk.virtualWrite(V0,F("Hp1Vr2")); return;} // Hp1Vr2
} // VR
} // Hp1
case 2 : { //Hp2
switch (Vr) {
case 0 : { Blynk.virtualWrite(V20,3); Blynk.virtualWrite(V0,F("Hp2Vr0")); return;} // Hp2Vr0
case 1 : { Blynk.virtualWrite(V20,5); Blynk.virtualWrite(V0,F("Hp2Vr1")); return;} // Hp2Vr1
case 2 : { Blynk.virtualWrite(V20,7); Blynk.virtualWrite(V0,F("Hp2Vr2")); return;} // Hp2Vr2
} // VR
} // Hp2
} // Hp
} // showImage
void displayHp() {
displayVr();
switch (Hp){
case 0:
digitalWrite(PHPR,RELAY_HIGH); digitalWrite(PHPG,RELAY_LOW); digitalWrite(PHPY,RELAY_LOW);
break;
case 1:
digitalWrite(PHPR,RELAY_LOW); digitalWrite(PHPG,RELAY_HIGH); digitalWrite(PHPY,RELAY_LOW);
break;
case 2:
digitalWrite(PHPR,RELAY_LOW); digitalWrite(PHPG,RELAY_HIGH); digitalWrite(PHPY,RELAY_HIGH);
break;
} // Switch
} // displayHp
void displayVr() {
if (Hp == 0) {
digitalWrite(PVRO1,RELAY_LOW); digitalWrite(PVRG1,RELAY_LOW); digitalWrite(PVRO2,RELAY_LOW); digitalWrite(PVRG2,RELAY_LOW);
}
else{
switch (Vr){
case 0:
digitalWrite(PVRO1,RELAY_HIGH); digitalWrite(PVRG1,RELAY_LOW); digitalWrite(PVRO2,RELAY_HIGH); digitalWrite(PVRG2,RELAY_LOW);
break;
case 1:
digitalWrite(PVRO1,RELAY_LOW); digitalWrite(PVRG1,RELAY_HIGH); digitalWrite(PVRO2,RELAY_LOW); digitalWrite(PVRG2,RELAY_HIGH);
break;
case 2:
digitalWrite(PVRO1,RELAY_LOW); digitalWrite(PVRG1,RELAY_HIGH); digitalWrite(PVRO2,RELAY_HIGH); digitalWrite(PVRG2,RELAY_LOW);
break;
} // switch Vr
} // Hp 0
} // displayVr
BLYNK_WRITE(V1) // Hp selector
{
Hp = param.asInt() - 1 ; // assigning incoming value
displayHp(); // forces Vr to update also
showImage();
}
BLYNK_WRITE(V2) { // Vr selector
Vr = param.asInt() -1; // assigning incoming value
displayVr();
showImage();
}
BLYNK_WRITE(V3) { // Network status
int ask = param.asInt() ; // assigning incoming value
if (ask == 1) {
long rssi = WiFi.RSSI();
Blynk.setProperty(V3,"offLabel","📶 " + String(rssi) + "dBm");
}
} // V3
BLYNK_WRITE(V21) { // auto mode
autoA = (param.asInt() == 1); // keep auto flag
if (autoA) {
autoAspect(); //
}else{
duration = 0;
showDuration();
}
}
BLYNK_WRITE(V22) { // off button
if (param.asInt() == 1) {
alloff();
}
}
BLYNK_WRITE(V23) { // test
if (param.asInt() == 1) {
animate(); //
}
}
BLYNK_WRITE(V30) { // update button pressed
if (param.asInt() == 1) {
Blynk.setProperty(V30,"label",F("Updating"));
checkForUpdates(); // won't return if there is an update attempt
showversion(V30);
}
}
/* not sure I need this, the Blynk server should have all this already and expect it to auto push it to the app
BLYNK_APP_CONNECTED() {
showImage();
displayVr();
showDuration();
}
*/
//============================================================
void setup()
{
Serial.begin(115200);
Blynk.begin(auth, ssid, pass); // get onto wifi network
printWifiStatus();
// set the relay pins as output
pinMode(PHPR,OUTPUT);
pinMode(PHPG,OUTPUT);
pinMode(PHPY,OUTPUT);
pinMode(PVRO1,OUTPUT);
pinMode(PVRO2,OUTPUT);
pinMode(PVRG1,OUTPUT);
pinMode(PVRG2,OUTPUT);
displayHp(); // forces display of initial Hp0 (& Vr0)
Blynk.virtualWrite(V1, 1); //force app to Hp0 also
Blynk.virtualWrite(V2, 1); //force app to Vr0 also
Blynk.virtualWrite(V21, 0); //force auto off
showDuration();
// show version of firmware
showversion(V30);
}
void loop()
{
Blynk.run();
timer.run();
}
I suspect @Lichtsignaal may enjoy this!