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.