Istorija tokia, kad sutemus dėl saugumo ir jaukumo, lauke turi šviesti garažo šviestuvas. Kurį laiką viskas puikiai veikė su foto rėle, bet ji po 3-4 metų sugedo, o tokia pati vėl sugedo dar greičiau. Todėl sugalvojau pasigaminti išmanų apšvietimo jungiklį. Pagrindinis veikimo principas būtų, kad jis suskaičiuoja saulėlydžio ir saulėtekio laiką ir atitinkamu metu įjungia arba išjungia apšvietimą.
Šiam jungikliui pasigaminti mums reikės:
- NodeMCU plokštės, (ji turi WiFi modulį ir yra programuojama su Aurduino IDE)
- 3.3 v rėlės
- USB kroviklio
- USB laido
Modulių sujungimas
Pirmiausia sujungiame NodeMCU plokštę su rėle. Tam reikės 3 laidų ir juos sujungti pagal vieną iš šių schemų. Aš pasirinkau antrą variantą. Sujungti turi būti nesudėtinga, nes visi pin’ai pažymėti:
Toliau išardžiau usb kroviklį, jį pajungiau prie 220v, usb laidu sujungiau su NodeMCU. Neutralų laidą nuvedžiau į šviestuvą, o fazę per rėlę į šviestuvą. Visa dėžutė gavosi tokia:
Programinis kodas
Pagrindinis kodas buvo paimtas iš šio projekto. Šis projektas leidžia įjungti/išjungti rėlę nuotoliniu būdu, per naršyklę užkrovus NodeMCU web sąsają. Papildomai pridėjau galimybę matyti rėlės būsena (0 – išjungta, 1 – įjungta)
Kitas pavyzdys kuriuo rėmiausi buvo šis. Jo pagalba prisijungiama prie laiko sinchronizavimo serverio ir gaunamas dabartinis Lietuvos laikas. To reikia, nes NodeMCU savyje neturi vidinio laikrodžio.
Dar viena biblioteka – SunMoon. jos pagalba suskaičiuojamas Saulėlydis ir saulėtekis pagal dieną ir Lietuvos koordinates.
Turint visą informaciją, sutemus apšvietimas įjungiamas, patekėjus saulei – išjungiamas. Papildomai galima valdyti per web naršyklę. Tokiu atveju saulėlydžio/saulėtekio algoritmas ignoruojamas iki artimiausio ryto ar vakaro.
#include <sunMoon.h>
#include <Time.h>
#include <TimeLib.h>
#include <NTPClient.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#define OUR_latitude 54.687157 // Vilnius cordinates
#define OUR_longtitude 25.279652
#define OUR_timezone 180
// GitHub Page = https://github.com/Tommrodrigues/homebridge-nodemcu-relay
// Script Type = Relay Momentary, Switch, Modulation
// D4 = LOW activation
// D7 = HIGH activation
const int highPin = 13; //Declares "highPin" being pin 13 (D7) on NodeMCU
const int lowPin = 2; //Declaers "lowPin" being pin 2 (D4) on NodeMCU
const int redPin = 16; //Declaers "redPin" being pin 16 (Red LED)
// Required:
const char* ssid = "TAVO WIFI SSID"; //Name of your network
const char* password = "TAVO WIFI PASSW"; //Password for your network
// For Modulation:
const uint32_t ON_TIME = 5000; //Amount of time for relay to be on for MODULATION (ms)
const uint32_t OFF_TIME = 10000; //Amount of time for relay to be off for MODULATION (ms)
// For Momentary:
const int delayTimeOn = 1000; //Delay time for the on state for MOMENTARY (ms)
const int delayTimeOff = 1000; //Delay time for the off state for MOMENTARY (ms)
// NTP Servers:
static const char ntpServerName[] = "europe.pool.ntp.org";
const int timeZone = 3; // vilnius summer
sunMoon sm;
WiFiServer server(80);
WiFiUDP Udp;
time_t prevDisplay = 0; // when the sunrise sunset switch was checked
time_t ignoreTill; // if manual swicth was used, then ignore sunrise or sunset till next sunrise or sunset
time_t sRise; // todays sunrise time
time_t sSet; // todays sunset time
unsigned int localPort = 8888; // local port to listen for UDP packets
bool led_blinking;
bool led_on;
bool ignoreMe;
uint32_t last_toggle;
int value = LOW;
//Define functions
time_t getNtpTime();
void digitalClockDisplay();
void printDigits(int digits);
void sendNTPpacket(IPAddress &address);
void setIgnoreTime();
void printDate(time_t date);
void setup() {
Serial.begin(115200);
delay(10);
pinMode(lowPin, OUTPUT);
pinMode(highPin, OUTPUT);
pinMode(redPin, OUTPUT);
digitalWrite(highPin, LOW);
digitalWrite(lowPin, HIGH);
digitalWrite(redPin, LOW);
// Connect to WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
// Start the server
server.begin();
Serial.println("Server started");
// Print the IP address
Serial.print("Use this URL to connect: ");
Serial.print("http://");
Serial.print(WiFi.localIP());
Serial.println("/");
// Start the mDNS responder for nodemcu.local
if (!MDNS.begin("sviestuvas")) {
Serial.println("Error setting up MDNS responder!");
}
Serial.println("mDNS responder started");
digitalWrite(redPin, HIGH);
ignoreMe = false;
Serial.println("Starting UDP");
Udp.begin(localPort);
Serial.print("Local port: ");
Serial.println(Udp.localPort());
Serial.println("waiting for sync");
setSyncProvider(getNtpTime);
setSyncInterval(300);
sm.init(OUR_timezone, OUR_latitude, OUR_longtitude);
// Set as STATION only
WiFi.mode(WIFI_STA);
}
//START OF MODULATION FUNCTIONS
void update_led() {
uint32_t now = millis();
if (!led_blinking && !ignoreMe) {
digitalWrite(highPin, LOW);
digitalWrite(lowPin, HIGH);
led_on = false;
last_toggle = now - OFF_TIME;
return;
}
if (led_on && now - last_toggle >= ON_TIME && !ignoreMe) {
digitalWrite(highPin, LOW);
digitalWrite(lowPin, HIGH);
led_on = false;
last_toggle = now;
}
if (!led_on && now - last_toggle >= OFF_TIME && !ignoreMe) {
digitalWrite(highPin, HIGH);
digitalWrite(lowPin, LOW);
led_on = true;
last_toggle = now;
}
}
void start_blinking() {
digitalWrite(highPin, HIGH);
digitalWrite(lowPin, LOW);
led_blinking = true;
led_on = true;
last_toggle = millis();
}
void stop_blinking() {
digitalWrite(highPin, LOW);
digitalWrite(lowPin, HIGH);
led_blinking = false;
led_on = false;
}
//END OF MODULATION FUNCTIONS
//Main loop
void loop() {
update_led();
// Part where sunrise and sunset will be calculated and switch will be turned on or off
if (timeStatus() != timeNotSet) {
if (now() != prevDisplay) { //update the display only if time has changed
prevDisplay = now();
sRise = sm.sunRise();
sSet = sm.sunSet();
if(now() > ignoreTill) {
if(now() > sRise && now() < sSet) {
stop_blinking();
digitalWrite(highPin, LOW);
digitalWrite(lowPin, HIGH);
value = LOW;
ignoreMe = false;
} else {
stop_blinking();
digitalWrite(highPin, HIGH);
digitalWrite(lowPin, LOW);
value = HIGH;
ignoreMe = true;
}
}
}
}
// Web part:
// Check if a client has connected
WiFiClient client = server.available();
if (!client) {
return;
}
// Wait until the client sends some data
Serial.println("New client");
while(!client.available()){
delay(1);
}
// Read the first line of the request
String request = client.readStringUntil('\r');
Serial.println(request);
client.flush();
if (request.indexOf("/MODULATION=ON") != -1) {
ignoreMe = false;
start_blinking();
value = HIGH;
}
if (request.indexOf("/MODULATION=OFF") != -1) {
ignoreMe = false;
stop_blinking();
value = LOW;
}
if (request.indexOf("/MOMENTARY=ON") != -1) {
stop_blinking();
digitalWrite(lowPin, LOW);
digitalWrite(highPin, HIGH);
delay(delayTimeOn);
digitalWrite(highPin, LOW);
digitalWrite(lowPin, HIGH);
value = HIGH;
ignoreMe = false;
}
if (request.indexOf("/MOMENTARY=OFF") != -1) {
stop_blinking();
digitalWrite(lowPin, LOW);
digitalWrite(highPin, HIGH);
delay(delayTimeOff);
digitalWrite(highPin, LOW);
digitalWrite(lowPin, HIGH);
value = LOW;
ignoreMe = false;
}
if (request.indexOf("/SWITCH=ON") != -1) {
stop_blinking();
digitalWrite(highPin, HIGH);
digitalWrite(lowPin, LOW);
value = HIGH;
ignoreMe = true;
setIgnoreTime();
}
if (request.indexOf("/SWITCH=OFF") != -1) {
stop_blinking();
digitalWrite(highPin, LOW);
digitalWrite(lowPin, HIGH);
value = LOW;
ignoreMe = false;
setIgnoreTime();
}
// Return the response
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("");
if(request.indexOf("/STATUS") != -1) {
if(value == HIGH) {
client.print(1);
} else {
client.print(0);
}
} else {
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.print("Current state: ");
if(value == HIGH) {
client.print("On");
} else {
client.print("Off");
}
client.println("<br><br>");
client.println("<a href=\"/MODULATION=ON\"\"><button>Modulation On </button></a>");
client.println("<a href=\"/MODULATION=OFF\"\"><button>Modulation Off </button></a>");
client.println("<a href=\"/MOMENTARY=ON\"\"><button>Momentary On </button></a>");
client.println("<a href=\"/MOMENTARY=OFF\"\"><button>Momentary Off </button></a>");
client.println("<a href=\"/SWITCH=ON\"\"><button>Switch On </button></a>");
client.println("<a href=\"/SWITCH=OFF\"\"><button>Switch Off </button></a><br />");
client.println("</html>");
}
Serial.println("Client disonnected");
Serial.println("");
}
void digitalClockDisplay()
{
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(".");
Serial.print(month());
Serial.print(".");
Serial.print(year());
Serial.println();
}
void printDigits(int digits)
{
// utility for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if (digits < 10)
Serial.print('0');
Serial.print(digits);
}
/*-------- NTP code ----------*/
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
time_t getNtpTime()
{
IPAddress ntpServerIP; // NTP server's ip address
while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.println("Transmit NTP Request");
// get a random server from the pool
WiFi.hostByName(ntpServerName, ntpServerIP);
Serial.print(ntpServerName);
Serial.print(": ");
Serial.println(ntpServerIP);
sendNTPpacket(ntpServerIP);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Receive NTP Response");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response :-(");
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
void printDate(time_t date) {
char buff[20];
sprintf(buff, "%2d-%02d-%4d %02d:%02d:%02d",
day(date), month(date), year(date), hour(date), minute(date), second(date));
Serial.print(buff);
}
void setIgnoreTime()
{
if(now() < sRise) {
ignoreTill = sRise + 1;
} else if (now() < sSet) {
ignoreTill = sSet + 1;
} else {
ignoreTill = sRise + 86400;
}
}
Kaip valdyti?
Pagrindinė idėja, kad šis prietaisas veiktu autonomiškai, tai reiškia, kad jis pats vakare įjungs apšvietimą, o ryte išjungs. Bet galima ir bet kuriuo metu jį įjungti per web sąsają. Būdami tam pačiam namų tinkle, atidarykite interneto naršyklėje http://sviestuvas.local (šis adresas nusistatomas 100-ojoje eilutėje) ir pamatysite keletą mygtuku. Paspaudę Switch on, įjungsite apšvietimą.
Tai tiek, kitas žingsnis paversti šį jungiklį Apple HomeKit prietaisu ir valdyti, bei matyti šio prietaiso būseną savo telefone.

