Išmanus lauko apšvietimas

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:

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.

Parašykite komentarą

El. pašto adresas nebus skelbiamas. Būtini laukeliai pažymėti *