OpenWeatherMap 1. Current Time 2. Sunrise 3. Sunset 4. Condition 5. Wind Speed 6. Cloud %NodeMCU 1. Get weather data 2. Segregate conditions 3. Convert values to strings 4. Aggregate sequence 5. Serial to ArduinoArduino 1. Receive and test serial data 2. Split data to functions 3. Set light position to time of day 4. Set cloud percent relative to light position 5. Activate weather event (rain, etc) 6. Set fan speed to mapped wind speed m/sConditions 1. Sunrise to Sunset 2. Clouds 3. Rain or Drizzle 4. Thunderstorm 5. Snow 6. Fog 7. Wind 8. Tides NodeMCU Arduino Uno MG995 Servo (2) MOSFET (3) + 10K TIP120 (2) + 2.2K Water Pump 5V Neopixels + 2.2K + 1000uf Vinyl Tubing 3/16ID Peltier Element (2) + Heat Sink 9G Servo (4) Gang Valve (3) 40mm 12V Fan 80mm 5V Fan Lever Switch 160 LED 5600K 24V 12V-500W 9V 5V Ultrasonic Mist Maker Wire Strainer (Mesh) Acrylic Tube NodeMCU// Aquarium Weather // pkvi // NodeMCU Code // ESP8266 Weather Credit: // https://github.com/ThingPulse/esp8266-weather-station // Weather #include <ESP8266WiFi.h> #include <JsonListener.h> #include "OpenWeatherMapCurrent.h" #include <math.h> #include <SoftwareSerial.h> // Serial to Arduino SoftwareSerial com (D6, D5) ; // Rx, Tx // Open Weather API OpenWeatherMapCurrent client; // API String OPEN_WEATHER_MAP_APP_ID = "api number" ; // Weather Address String OPEN_WEATHER_MAP_LOCATION_ID = "location id" ; // Language String OPEN_WEATHER_MAP_LANGUAGE = "en" ; // Metric? boolean IS_METRIC = true ; // WIFI const char * ESP_HOST_NAME = "esp-" + ESP.getFlashChipId ();const char * WIFI_SSID = "network name" ;const char * WIFI_PASSWORD = "network password" ;WiFiClient wifiClient; void connectWifi () { WiFi.begin (WIFI_SSID, WIFI_PASSWORD); Serial.println (WIFI_SSID); while (WiFi.status () != WL_CONNECTED) { delay (500 ); Serial.print ("." ); } Serial.println ("Connected" ); Serial.println (WiFi.localIP ()); } // Weather Values int emit; // current time --> unix epoch int sunr; // sunrise int suns; // sunset int clou; // cloud percent int wind; // wind speed m/s String main; // conditions // Values to Send int light;int cloud;int event;int wind_speed;String sequence; // reset int mulligan = D7;void setup () { // for testing delay (5000 ); digitalWrite (mulligan, HIGH); delay (500 ); pinMode (mulligan, OUTPUT); Serial.begin (115200 ); while (!Serial) { delay (1 ); } delay (500 ); // Arduino com.begin (9600 ); connectWifi (); } void loop () { local_weather (); delay (1000 ); time_position (); cloud_position (); wind_position (); event_position (); aggregate (); delay (1000 ); transmit (); // Transmit Serial Every 30 Seconds // For 1 Hour Then Reset for (int ditto = 0 ; ditto < 120 ; ditto++) { transmit (); delay (30000 ); } // reset // bcz -> NodeMCU n' such digitalWrite (mulligan, LOW); delay (1000 ); } void local_weather () { // Get the weather report OpenWeatherMapCurrentData data; client.setLanguage (OPEN_ WEATHER_MAP_LANGUAGE); client.setMetric (IS_METRIC); client.updateCurrentById (&data, OPEN_WEATHER_MAP_APP_ID, OPEN_WEATHER_MAP_LOCATION_ID); // Convert Data to Values emit = data.observationTime; sunr = data.sunrise; suns = data.sunset; main = String (data.main); wind = data.windSpeed; clou = data.clouds; // Print Data Serial.println (emit); Serial.println (sunr); Serial.println (suns); Serial.println (main); Serial.println (wind); Serial.println (clou); Serial.println (); } void time_position () { // before sunrise if (emit < sunr) { light = 0 ; // after sunset } else if (emit > suns) { light = 0 ; // after sunrise before sunset } else if (emit > sunr && emit < suns) { // maths ugh int total_dif = suns - sunr; int since = emit - sunr; float percent = (float )(since * 100 ) / total_dif; percent = round (percent); light = percent; } else { // error allowance light = 0 ; } Serial.println (light); } void cloud_position () { cloud = clou; Serial.println (cloud); } void wind_position () { wind_speed = round (wind); Serial.println (wind_speed); } void event_position () { // OpenWeather Conditions --> https://openweathermap.org/weather-conditions // Selecting the [main] following: 1 Thunderstorm, 2 Drizzle, 3 Rain, 4 Snow, 5 Fog, 6 Clouds if (main.indexOf ("Thunderstorm" ) >= 0 ) { event = 1 ; } else if (main.indexOf ("Drizzle" ) >= 0 ) { event = 2 ; } else if (main.indexOf ("Rain" ) >= 0 ) { event = 3 ; } else if (main.indexOf ("Snow" ) >= 0 ) { event = 4 ; } else if (main.indexOf ("Fog" ) >= 0 ) { event = 5 ; } else if (main.indexOf ("Clouds" ) >= 0 ) { event = 6 ; } else { event = 7 ; } Serial.println (event); } void aggregate () { // Combine values to one string to send // Done to ease serial limitations // Example: 0800500052 [10] // 080 = 80% of day // 050 = 50% clouds // 005 = 5 mph wind // 2 = Drizzle (see event_position) String light_str = String (light); if (light < 100 && light > 9 ) { light_str = String ("0" + light_str); } else if (light < 10 ) { light_str = String ("00" + light_str); } else { light_str = String (100 ); } String cloud_str = String (cloud); if (cloud < 100 && cloud > 9 ) { cloud_str = String ("0" + cloud_str); } else if (cloud < 10 ) { cloud_str = String ("00" + cloud_str); } else { cloud_str = String (100 ); } String wind_str = String (wind_speed); if (wind_speed < 100 && wind_speed > 9 ) { wind_str = String ("0" + wind_str); } else if (wind_speed < 10 ) { wind_str = String ("00" + wind_str); } else { wind_str = String (100 ); } String event_str = String (event); sequence = String (light_str + cloud_str + wind_str + event_str); Serial.println (sequence); Serial.println ("" ); } void transmit () { com.println (sequence); } Arduino// Aquarium Weather // pkvi // Arduino Code #include <VarSpeedServo.h> #include <FastLED.h> #include <SoftwareSerial.h> // Serial SoftwareSerial com (5 , 6 ) ; // Rx, Tx // Light VarSpeedServo light_servo; const int light_servo_pin = 2 ;const int light_dimmer = A0; // 0-255 // Cloud VarSpeedServo cloud_servo; const int cloud_servo_pin = 3 ;// Pump const int pump = A1; // 0-255 // lever switch kills power to pump const int tank_level = 16 ;// Lightning (Neopixels) // FastLED Lightning --> James Bruce // Tip: Actually 20 but 50+ Spreads Effect #define NUM_LEDS 50 #define DATA_PIN 4 CRGB leds[NUM_LEDS]; // Valves VarSpeedServo rain_valve_servo; const int rain_valve = 7 ;VarSpeedServo snow_valve_servo; const int snow_valve = 8 ;VarSpeedServo tank_valve_servo; const int tank_valve = 9 ;// Snow VarSpeedServo snow_maker_servo; const int snow_maker = 10 ;const int snow = 11 ;// Fog const int fog = 12 ;// Wind (meters/sec) const int fan = A3; // 0-255 // Values String rec; // Weather Data String last; // Saved Weather const int off = 0 ; // MOSFET Controllers OFF const int top = 255 ; // Peak const int start = 0 ; // light + cloud const int middle = 65 ; // light + cloud const int full = 130 ; // light + cloud String light_percent; String cloud_percent; String wind_speed; // m/s String event; int light_value;int cloud_value;int wind_value;int event_value;int light_pos;int light_mos;int cloud_pos;int wind_pos;unsigned long who;unsigned long what;unsigned long when;int tank_level_state;int tide_state = 0 ;void setup () { Serial.begin (9600 ); com.begin (9600 ); // Declare and Set Defaults // Light light_servo.attach (light_servo_pin); light_servo.write (0 , 80 , false ); pinMode (light_dimmer, OUTPUT); analogWrite (light_dimmer, off); // Cloud cloud_servo.attach (cloud_servo_pin); cloud_servo.write (130 , 80 , false ); // Rain rain_valve_servo.attach (rain_valve); rain_valve_servo.write (0 , 80 , false ); // Snow snow_valve_servo.attach (snow_valve); snow_valve_servo.write (0 , 80 , false ); pinMode (snow, OUTPUT); digitalWrite (snow, LOW); // Pump tank_valve_servo.attach (tank_valve); tank_valve_servo.write (0 , 80 , true ); pinMode (pump, OUTPUT); digitalWrite (pump, off); pinMode (tank_level, INPUT); // Fog pinMode (fog, OUTPUT); digitalWrite (fog, LOW); // Lightning FastLED.addLeds <WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS); // Wind pinMode (fan, OUTPUT); analogWrite (fan, off); } void loop () { // Clean House com.flush (); // Wait for Serial from NodeMCU while (!com.available ()) { ; // do nothing } // What did they say?! rec = com.readString (); Serial.println (rec); // Check if string is 10 characters // 0000000000 if (rec.length () != 10 ) { // Check if different than last data if (rec != last) { // Process Data // 000-000-000-0 // 1 Light % // 2 Cloud % // 3 Wind Speed // 4 Event // Parse Light light_percent = rec.substring (0 , 2 ); light_value = light_percent.toInt (); Serial.println (light_value); // Parse Cloud cloud_percent = rec.substring (3 , 5 ); cloud_value = cloud_percent.toInt (); Serial.println (cloud_value); // Parse Wind wind_speed = rec.substring (6 , 8 ); wind_value = wind_speed.toInt (); Serial.println (wind_value); // Parse Event event = rec.substring (9 , 9 ); event_value = event.toInt (); Serial.println (event_value); // Fire Functions light_okay (); cloud_what (); tide (); wind_sure (); event_dunno (); // Save Last Data last = String (rec); } else { // Status Quo light_okay (); cloud_what (); tide (); wind_sure (); event_dunno (); } // parse and event call } // if not 10 characters } // loop void light_okay () { // 130 = East 0 = West light_pos = map (light_value, 0 , 100 , 130 , 0 ); light_servo.write (light_pos, 80 , true ); // Sunrise and Sunset if (light_pos > 115 ) { light_mos = map (light_pos, 130 , 115 , 0 , 255 ); analogWrite (light_dimmer, light_mos); } if (light_pos < 15 ) { light_mos = map (light_pos, 15 , 0 , 255 , 0 ); analogWrite (light_dimmer, light_mos); } else { analogWrite (light_dimmer, 255 ); } } void cloud_what () { if (event_value == 7 ) { // if clear --> position opposite after 50% light if (light_pos < 50 ) { cloud_servo.write (start, 80 , true ); } else if (light_pos >= 50 ) { cloud_servo.write (full, 80 , true ); } } else if (event_value == 6 ) { // if clouds then position percent // then percent relative to light position // 65 = 100% if (light_pos < 50 ) { cloud_pos = map (cloud_value, 0 , 100 , 130 , 65 ); cloud_servo.write (cloud_pos, 80 , true ); } else if (light_pos >= 50 ) { cloud_pos = map (cloud_value, 0 , 100 , 0 , 65 ); cloud_servo.write (cloud_pos, 80 , true ); } } else { // if event then cloud at peak cloud_servo.write (middle, 80 , true ); } } void wind_sure () { // m/s to percent-ish // 15 m/s = 33.6 mph if (wind_value <= 15 ) { wind_pos = map (wind_value, 0 , 15 , 0 , 255 ); analogWrite (fan, wind_pos); } else if (wind_value > 15 ) { analogWrite (fan, 255 ); } } void event_dunno () { if (event_value == 1 ) { thunderstorm_boom (); } else if (event_value == 2 ) { drizzle_fizz (); } else if (event_value == 3 ) { rain_game (); } else if (event_value == 4 ) { snow_man (); } else if (event_value == 5 ) { fog_bottom (); } } void tide () { // Every 6 Hours Change // Uses Arduino Active As Time // This is cheating! No data! // get time since program began when = millis (); // first time or time reset? if (what < 1000 ) { what = when; // no action } else { // math difference who = when - what; // is it 6 hours ago? if (who >= 21600000 ) { // low or high tide? // 0 = high tide 1 = low tide if (tide_state == 0 ) { // open tank valve tank_valve_servo.write (90 , 80 , true ); delay (1000 ); while (tank_level_state == LOW) { digitalWrite (pump, HIGH); // check float level tank_level_state = digitalRead (tank_level); } digitalWrite (pump, LOW); tank_valve_servo.write (0 , 80 , true ); tide_state = 1 ; what = when; } else if (tide_state == 1 ) { // open tank valve to dump tank tank_valve_servo.write (90 , 80 , true ); delay (20000 ); // adj tank_valve_servo.write (0 , 80 , true ); tide_state = 0 ; what = when; } } } } void thunderstorm_boom () { // open rain valve analogWrite (light_dimmer, 120 ); rain_valve_servo.write (90 , 80 , true ); delay (1000 ); // let it pour for (int x = 0 ; x < 25 ; x++) { digitalWrite (pump, HIGH); // call lightning for delay for (int bolt = 0 ; bolt < 10 ; bolt++) { lightning (); delay (500 ); } digitalWrite (pump, LOW); // lightning again (so smart!) for (int bolt = 0 ; bolt < 10 ; bolt++) { lightning (); delay (500 ); } } rain_valve_servo.write (0 , 80 , true ); digitalWrite (pump, LOW); } void drizzle_fizz () { analogWrite (light_dimmer, 120 ); // open rain valve rain_valve_servo.write (90 , 80 , true ); delay (1000 ); // less than rain for (int x = 0 ; x < 20 ; x++) { digitalWrite (pump, HIGH); delay (3000 ); digitalWrite (pump, LOW); delay (6000 ); } rain_valve_servo.write (0 , 80 , true ); digitalWrite (pump, LOW); } void rain_game () { analogWrite (light_dimmer, 120 ); // open rain valve rain_valve_servo.write (90 , 80 , true ); delay (1000 ); // let it pour for (int x = 0 ; x < 20 ; x++) { digitalWrite (pump, HIGH); delay (5000 ); digitalWrite (pump, LOW); delay (5000 ); } rain_valve_servo.write (0 , 80 , true ); digitalWrite (pump, LOW); } // This requires tweaking! void snow_man () { analogWrite (light_dimmer, 120 ); // relay peltier elements + fan (2) digitalWrite (snow, HIGH); delay (5000 ); // open snow valve snow_valve_servo.write (90 , 80 , true ); delay (1000 ); for (int redundant = 0 ; redundant = 50 ; redundant++) { // intermittent pump (drip) for (int x = 0 ; x < 20 ; x++) { digitalWrite (pump, HIGH); delay (3000 ); digitalWrite (pump, LOW); delay (5000 ); } // grate the snow for (int y = 0 ; y < 10 ; y++) { snow_maker_servo.write (45 , 100 , true ); delay (500 ); snow_maker_servo.write (0 , 100 , true ); delay (500 ); } } digitalWrite (snow, LOW); digitalWrite (pump, LOW); snow_valve_servo.write (0 , 80 , true ); } void fog_bottom () { analogWrite (light_dimmer, 120 ); // turn on fog maker (relay) digitalWrite (fog, HIGH); // run for 60 seconds delay (60000 ); digitalWrite (fog, LOW); // return } void lightning () { switch (random (1 , 3 )) { case 1 : thunderburst (); delay (random (10 , 500 )); break ; case 2 : rolling (); break ; case 3 : crack (); delay (random (50 , 250 )); break ; } } void reset () { for (int i = 0 ; i < NUM_LEDS; i++) { leds[i] = CHSV ( 0 , 0 , 0 ); } FastLED.show (); } void rolling () { for (int r = 0 ; r < random (2 , 10 ); r++) { for (int i = 0 ; i < NUM_LEDS; i++) { if (random (0 , 100 ) > 90 ) { leds[i] = CHSV ( 0 , 0 , 255 ); } else { leds[i] = CHSV (0 , 0 , 0 ); } } FastLED.show (); delay (random (5 , 100 )); reset (); } } void crack () { for (int i = 0 ; i < NUM_LEDS; i++) { leds[i] = CHSV ( 0 , 0 , 255 ); } FastLED.show (); delay (random (10 , 100 )); reset (); } void thunderburst () { int rs1 = random (0 , NUM_LEDS / 2 ); int rl1 = random (10 , 20 ); int rs2 = random (rs1 + rl1, NUM_LEDS); int rl2 = random (10 , 20 ); for (int r = 0 ; r < random (3 , 6 ); r++) { for (int i = 0 ; i < rl1; i++) { leds[i + rs1] = CHSV ( 0 , 0 , 255 ); } if (rs2 + rl2 < NUM_LEDS) { for (int i = 0 ; i < rl2; i++) { leds[i + rs2] = CHSV ( 0 , 0 , 255 ); } } FastLED.show (); delay (random (10 , 50 )); reset (); delay (random (10 , 50 )); } }