Reproducing real-time local weather for your aquarium occupants.
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 Arduino
Arduino 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/s
Conditions 1. Sunrise to Sunset 2. Clouds 3. Rain or Drizzle 4. Thunderstorm 5. Snow 6. Fog 7. Wind 8. Tides
Ingredients 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
5 gallon tank demonstrated aka "(too) small foot print" version
Notations 1. Water temperature remains static, bcz the above are novelties that shd not harm the aquarium inhabitants --> also cause for not using artificial "snow". 2. This. https://sketchucation.com/pluginstore?pauthor=fredo6 3. Ran out of black filament for a spell, painted some natural with black acrylic.. surprised. 4. Keep it simple, stupid --> variables of if/then when controlling several elements at once, reason why NodeMCU isolated the data collection/aggregation while displacing labor to the Arduino. 5. "Cheating" on tides as OpenWeatherMap does not provide - will oscillate tides based on (2) by 5.75 by 28 cycle. 6. Debated a mp3 module for thunder.. figured less is more when it comes to obnoxious whirring and wheezing. 7. Peltier cascading! 8. https://github.com/ThingPulse/esp8266-weather-station 9. Wire mesh strainer from the dollar store.. smart. 10. Snow? Snaybe. Clumping permitted a hint of falling ice (slush) but this was 92% fail. 11. Larger tank would better allow cloud to light distribution.. tracks?!
NodeMCU
// Aquarium Weather // Vije Miller // NodeMCU Code // ESP8266 Weather Credit: // https://github.com/ThingPulse/esp8266-weather-station
// 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;
// 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 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
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); } }