Arduino and NodeMCU Aquarium Weather


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



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
  1. // Aquarium Weather
  2. // pkvi
  3. // NodeMCU Code
  4. // ESP8266 Weather Credit:
  5. // https://github.com/ThingPulse/esp8266-weather-station
  6. // Weather
  7. #include <ESP8266WiFi.h>
  8. #include <JsonListener.h>
  9. #include "OpenWeatherMapCurrent.h"
  10. #include <math.h>
  11. #include <SoftwareSerial.h>
  12. // Serial to Arduino
  13. SoftwareSerial com(D6, D5); // Rx, Tx
  14. // Open Weather API
  15. OpenWeatherMapCurrent client;
  16. // API
  17. String OPEN_WEATHER_MAP_APP_ID = "api number";
  18. // Weather Address
  19. String OPEN_WEATHER_MAP_LOCATION_ID = "location id";
  20. // Language
  21. String OPEN_WEATHER_MAP_LANGUAGE = "en";
  22. // Metric?
  23. boolean IS_METRIC = true;
  24. // WIFI
  25. const char* ESP_HOST_NAME = "esp-" + ESP.getFlashChipId();
  26. const char* WIFI_SSID = "network name";
  27. const char* WIFI_PASSWORD = "network password";
  28. WiFiClient wifiClient;
  29. void connectWifi() {
  30. WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  31. Serial.println(WIFI_SSID);
  32. while (WiFi.status() != WL_CONNECTED) {
  33. delay(500);
  34. Serial.print(".");
  35. }
  36. Serial.println("Connected");
  37. Serial.println(WiFi.localIP());
  38. }
  39. // Weather Values
  40. int emit; // current time --> unix epoch
  41. int sunr; // sunrise
  42. int suns; // sunset
  43. int clou; // cloud percent
  44. int wind; // wind speed m/s
  45. String main; // conditions
  46. // Values to Send
  47. int light;
  48. int cloud;
  49. int event;
  50. int wind_speed;
  51. String sequence;
  52. // reset
  53. int mulligan = D7;
  54. void setup() {
  55. // for testing
  56. delay(5000);
  57. digitalWrite(mulligan, HIGH);
  58. delay(500);
  59. pinMode(mulligan, OUTPUT);
  60. Serial.begin(115200);
  61. while (!Serial) {
  62. delay(1);
  63. }
  64. delay(500);
  65. // Arduino
  66. com.begin(9600);
  67. connectWifi();
  68. }
  69. void loop() {
  70. local_weather();
  71. delay(1000);
  72. time_position();
  73. cloud_position();
  74. wind_position();
  75. event_position();
  76. aggregate();
  77. delay(1000);
  78. transmit();
  79. // Transmit Serial Every 30 Seconds
  80. // For 1 Hour Then Reset
  81. for (int ditto = 0; ditto < 120; ditto++) {
  82. transmit();
  83. delay(30000);
  84. }
  85. // reset
  86. // bcz -> NodeMCU n' such
  87. digitalWrite(mulligan, LOW);
  88. delay(1000);
  89. }
  90. void local_weather() {
  91. // Get the weather report
  92. OpenWeatherMapCurrentData data;
  93. client.setLanguage(OPEN_
  94. WEATHER_MAP_LANGUAGE);
  95. client.setMetric(IS_METRIC);
  96. client.updateCurrentById(&data, OPEN_WEATHER_MAP_APP_ID, OPEN_WEATHER_MAP_LOCATION_ID);
  97. // Convert Data to Values
  98. emit = data.observationTime;
  99. sunr = data.sunrise;
  100. suns = data.sunset;
  101. main = String(data.main);
  102. wind = data.windSpeed;
  103. clou = data.clouds;
  104. // Print Data
  105. Serial.println(emit);
  106. Serial.println(sunr);
  107. Serial.println(suns);
  108. Serial.println(main);
  109. Serial.println(wind);
  110. Serial.println(clou);
  111. Serial.println();
  112. }
  113. void time_position() {
  114. // before sunrise
  115. if (emit < sunr) {
  116. light = 0;
  117. // after sunset
  118. } else if (emit > suns) {
  119. light = 0;
  120. // after sunrise before sunset
  121. } else if (emit > sunr && emit < suns) {
  122. // maths ugh
  123. int total_dif = suns - sunr;
  124. int since = emit - sunr;
  125. float percent = (float)(since * 100) / total_dif;
  126. percent = round(percent);
  127. light = percent;
  128. } else {
  129. // error allowance
  130. light = 0;
  131. }
  132. Serial.println(light);
  133. }
  134. void cloud_position() {
  135. cloud = clou;
  136. Serial.println(cloud);
  137. }
  138. void wind_position() {
  139. wind_speed = round(wind);
  140. Serial.println(wind_speed);
  141. }
  142. void event_position() {
  143. // OpenWeather Conditions --> https://openweathermap.org/weather-conditions
  144. // Selecting the [main] following: 1 Thunderstorm, 2 Drizzle, 3 Rain, 4 Snow, 5 Fog, 6 Clouds
  145. if (main.indexOf("Thunderstorm") >= 0) {
  146. event = 1;
  147. } else if (main.indexOf("Drizzle") >= 0) {
  148. event = 2;
  149. } else if (main.indexOf("Rain") >= 0) {
  150. event = 3;
  151. } else if (main.indexOf("Snow") >= 0) {
  152. event = 4;
  153. } else if (main.indexOf("Fog") >= 0) {
  154. event = 5;
  155. } else if (main.indexOf("Clouds") >= 0) {
  156. event = 6;
  157. } else {
  158. event = 7;
  159. }
  160. Serial.println(event);
  161. }
  162. void aggregate() {
  163. // Combine values to one string to send
  164. // Done to ease serial limitations
  165. // Example: 0800500052 [10]
  166. // 080 = 80% of day
  167. // 050 = 50% clouds
  168. // 005 = 5 mph wind
  169. // 2 = Drizzle (see event_position)
  170. String light_str = String(light);
  171. if (light < 100 && light > 9) {
  172. light_str = String("0" + light_str);
  173. } else if (light < 10) {
  174. light_str = String("00" + light_str);
  175. } else {
  176. light_str = String(100);
  177. }
  178. String cloud_str = String(cloud);
  179. if (cloud < 100 && cloud > 9) {
  180. cloud_str = String("0" + cloud_str);
  181. } else if (cloud < 10) {
  182. cloud_str = String("00" + cloud_str);
  183. } else {
  184. cloud_str = String(100);
  185. }
  186. String wind_str = String(wind_speed);
  187. if (wind_speed < 100 && wind_speed > 9) {
  188. wind_str = String("0" + wind_str);
  189. } else if (wind_speed < 10) {
  190. wind_str = String("00" + wind_str);
  191. } else {
  192. wind_str = String(100);
  193. }
  194. String event_str = String(event);
  195. sequence = String(light_str + cloud_str + wind_str + event_str);
  196. Serial.println(sequence);
  197. Serial.println("");
  198. }
  199. void transmit() {
  200. com.println(sequence);
  201. }

Arduino
  1. // Aquarium Weather
  2. // pkvi
  3. // Arduino Code
  4. #include <VarSpeedServo.h>
  5. #include <FastLED.h>
  6. #include <SoftwareSerial.h>
  7. // Serial
  8. SoftwareSerial com(5, 6); // Rx, Tx
  9. // Light
  10. VarSpeedServo light_servo;
  11. const int light_servo_pin = 2;
  12. const int light_dimmer = A0; // 0-255
  13. // Cloud
  14. VarSpeedServo cloud_servo;
  15. const int cloud_servo_pin = 3;
  16. // Pump
  17. const int pump = A1; // 0-255
  18. // lever switch kills power to pump
  19. const int tank_level = 16;
  20. // Lightning (Neopixels)
  21. // FastLED Lightning --> James Bruce
  22. // Tip: Actually 20 but 50+ Spreads Effect
  23. #define NUM_LEDS 50
  24. #define DATA_PIN 4
  25. CRGB leds[NUM_LEDS];
  26. // Valves
  27. VarSpeedServo rain_valve_servo;
  28. const int rain_valve = 7;
  29. VarSpeedServo snow_valve_servo;
  30. const int snow_valve = 8;
  31. VarSpeedServo tank_valve_servo;
  32. const int tank_valve = 9;
  33. // Snow
  34. VarSpeedServo snow_maker_servo;
  35. const int snow_maker = 10;
  36. const int snow = 11;
  37. // Fog
  38. const int fog = 12;
  39. // Wind (meters/sec)
  40. const int fan = A3; // 0-255
  41. // Values
  42. String rec; // Weather Data
  43. String last; // Saved Weather
  44. const int off = 0; // MOSFET Controllers OFF
  45. const int top = 255; // Peak
  46. const int start = 0; // light + cloud
  47. const int middle = 65; // light + cloud
  48. const int full = 130; // light + cloud
  49. String light_percent;
  50. String cloud_percent;
  51. String wind_speed; // m/s
  52. String event;
  53. int light_value;
  54. int cloud_value;
  55. int wind_value;
  56. int event_value;
  57. int light_pos;
  58. int light_mos;
  59. int cloud_pos;
  60. int wind_pos;
  61. unsigned long who;
  62. unsigned long what;
  63. unsigned long when;
  64. int tank_level_state;
  65. int tide_state = 0;
  66. void setup() {
  67. Serial.begin(9600);
  68. com.begin(9600);
  69. // Declare and Set Defaults
  70. // Light
  71. light_servo.attach(light_servo_pin);
  72. light_servo.write(0, 80, false);
  73. pinMode(light_dimmer, OUTPUT);
  74. analogWrite(light_dimmer, off);
  75. // Cloud
  76. cloud_servo.attach(cloud_servo_pin);
  77. cloud_servo.write(130, 80, false);
  78. // Rain
  79. rain_valve_servo.attach(rain_valve);
  80. rain_valve_servo.write(0, 80, false);
  81. // Snow
  82. snow_valve_servo.attach(snow_valve);
  83. snow_valve_servo.write(0, 80, false);
  84. pinMode(snow, OUTPUT);
  85. digitalWrite(snow, LOW);
  86. // Pump
  87. tank_valve_servo.attach(tank_valve);
  88. tank_valve_servo.write(0, 80, true);
  89. pinMode(pump, OUTPUT);
  90. digitalWrite(pump, off);
  91. pinMode(tank_level, INPUT);
  92. // Fog
  93. pinMode(fog, OUTPUT);
  94. digitalWrite(fog, LOW);
  95. // Lightning
  96. FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  97. // Wind
  98. pinMode(fan, OUTPUT);
  99. analogWrite(fan, off);
  100. }
  101. void loop() {
  102. // Clean House
  103. com.flush();
  104. // Wait for Serial from NodeMCU
  105. while (!com.available()) {
  106. ; // do nothing
  107. }
  108. // What did they say?!
  109. rec = com.readString();
  110. Serial.println(rec);
  111. // Check if string is 10 characters
  112. // 0000000000
  113. if (rec.length() != 10) {
  114. // Check if different than last data
  115. if (rec != last) {
  116. // Process Data
  117. // 000-000-000-0
  118. // 1 Light %
  119. // 2 Cloud %
  120. // 3 Wind Speed
  121. // 4 Event
  122. // Parse Light
  123. light_percent = rec.substring(0, 2);
  124. light_value = light_percent.toInt();
  125. Serial.println(light_value);
  126. // Parse Cloud
  127. cloud_percent = rec.substring(3, 5);
  128. cloud_value = cloud_percent.toInt();
  129. Serial.println(cloud_value);
  130. // Parse Wind
  131. wind_speed = rec.substring(6, 8);
  132. wind_value = wind_speed.toInt();
  133. Serial.println(wind_value);
  134. // Parse Event
  135. event = rec.substring(9, 9);
  136. event_value = event.toInt();
  137. Serial.println(event_value);
  138. // Fire Functions
  139. light_okay();
  140. cloud_what();
  141. tide();
  142. wind_sure();
  143. event_dunno();
  144. // Save Last Data
  145. last = String(rec);
  146. } else {
  147. // Status Quo
  148. light_okay();
  149. cloud_what();
  150. tide();
  151. wind_sure();
  152. event_dunno();
  153. } // parse and event call
  154. } // if not 10 characters
  155. } // loop
  156. void light_okay() {
  157. // 130 = East 0 = West
  158. light_pos = map(light_value, 0, 100, 130, 0);
  159. light_servo.write(light_pos, 80, true);
  160. // Sunrise and Sunset
  161. if (light_pos > 115) {
  162. light_mos = map(light_pos, 130, 115, 0, 255);
  163. analogWrite(light_dimmer, light_mos);
  164. } if (light_pos < 15) {
  165. light_mos = map(light_pos, 15, 0, 255, 0);
  166. analogWrite(light_dimmer, light_mos);
  167. } else {
  168. analogWrite(light_dimmer, 255);
  169. }
  170. }
  171. void cloud_what() {
  172. if (event_value == 7) {
  173. // if clear --> position opposite after 50% light
  174. if (light_pos < 50) {
  175. cloud_servo.write(start, 80, true);
  176. } else if (light_pos >= 50) {
  177. cloud_servo.write(full, 80, true);
  178. }
  179. } else if (event_value == 6) {
  180. // if clouds then position percent
  181. // then percent relative to light position
  182. // 65 = 100%
  183. if (light_pos < 50) {
  184. cloud_pos = map(cloud_value, 0, 100, 130, 65);
  185. cloud_servo.write(cloud_pos, 80, true);
  186. } else if (light_pos >= 50) {
  187. cloud_pos = map(cloud_value, 0, 100, 0, 65);
  188. cloud_servo.write(cloud_pos, 80, true);
  189. }
  190. } else {
  191. // if event then cloud at peak
  192. cloud_servo.write(middle, 80, true);
  193. }
  194. }
  195. void wind_sure() {
  196. // m/s to percent-ish
  197. // 15 m/s = 33.6 mph
  198. if (wind_value <= 15) {
  199. wind_pos = map(wind_value, 0, 15, 0, 255);
  200. analogWrite(fan, wind_pos);
  201. } else if (wind_value > 15) {
  202. analogWrite(fan, 255);
  203. }
  204. }
  205. void event_dunno() {
  206. if (event_value == 1) {
  207. thunderstorm_boom();
  208. } else if (event_value == 2) {
  209. drizzle_fizz();
  210. } else if (event_value == 3) {
  211. rain_game();
  212. } else if (event_value == 4) {
  213. snow_man();
  214. } else if (event_value == 5) {
  215. fog_bottom();
  216. }
  217. }
  218. void tide() {
  219. // Every 6 Hours Change
  220. // Uses Arduino Active As Time
  221. // This is cheating! No data!
  222. // get time since program began
  223. when = millis();
  224. // first time or time reset?
  225. if (what < 1000) {
  226. what = when;
  227. // no action
  228. } else {
  229. // math difference
  230. who = when - what;
  231. // is it 6 hours ago?
  232. if (who >= 21600000) {
  233. // low or high tide?
  234. // 0 = high tide 1 = low tide
  235. if (tide_state == 0) {
  236. // open tank valve
  237. tank_valve_servo.write(90, 80, true);
  238. delay(1000);
  239. while (tank_level_state == LOW) {
  240. digitalWrite(pump, HIGH);
  241. // check float level
  242. tank_level_state = digitalRead(tank_level);
  243. }
  244. digitalWrite(pump, LOW);
  245. tank_valve_servo.write(0, 80, true);
  246. tide_state = 1;
  247. what = when;
  248. } else if (tide_state == 1) {
  249. // open tank valve to dump tank
  250. tank_valve_servo.write(90, 80, true);
  251. delay(20000); // adj
  252. tank_valve_servo.write(0, 80, true);
  253. tide_state = 0;
  254. what = when;
  255. }
  256. }
  257. }
  258. }
  259. void thunderstorm_boom() {
  260. // open rain valve
  261. analogWrite(light_dimmer, 120);
  262. rain_valve_servo.write(90, 80, true);
  263. delay(1000);
  264. // let it pour
  265. for (int x = 0; x < 25; x++) {
  266. digitalWrite(pump, HIGH);
  267. // call lightning for delay
  268. for (int bolt = 0; bolt < 10; bolt++) {
  269. lightning();
  270. delay(500);
  271. }
  272. digitalWrite(pump, LOW);
  273. // lightning again (so smart!)
  274. for (int bolt = 0; bolt < 10; bolt++) {
  275. lightning();
  276. delay(500);
  277. }
  278. }
  279. rain_valve_servo.write(0, 80, true);
  280. digitalWrite(pump, LOW);
  281. }
  282. void drizzle_fizz() {
  283. analogWrite(light_dimmer, 120);
  284. // open rain valve
  285. rain_valve_servo.write(90, 80, true);
  286. delay(1000);
  287. // less than rain
  288. for (int x = 0; x < 20; x++) {
  289. digitalWrite(pump, HIGH);
  290. delay(3000);
  291. digitalWrite(pump, LOW);
  292. delay(6000);
  293. }
  294. rain_valve_servo.write(0, 80, true);
  295. digitalWrite(pump, LOW);
  296. }
  297. void rain_game() {
  298. analogWrite(light_dimmer, 120);
  299. // open rain valve
  300. rain_valve_servo.write(90, 80, true);
  301. delay(1000);
  302. // let it pour
  303. for (int x = 0; x < 20; x++) {
  304. digitalWrite(pump, HIGH);
  305. delay(5000);
  306. digitalWrite(pump, LOW);
  307. delay(5000);
  308. }
  309. rain_valve_servo.write(0, 80, true);
  310. digitalWrite(pump, LOW);
  311. }
  312. // This requires tweaking!
  313. void snow_man() {
  314. analogWrite(light_dimmer, 120);
  315. // relay peltier elements + fan (2)
  316. digitalWrite(snow, HIGH);
  317. delay(5000);
  318. // open snow valve
  319. snow_valve_servo.write(90, 80, true);
  320. delay(1000);
  321. for (int redundant = 0; redundant = 50; redundant++) {
  322. // intermittent pump (drip)
  323. for (int x = 0; x < 20; x++) {
  324. digitalWrite(pump, HIGH);
  325. delay(3000);
  326. digitalWrite(pump, LOW);
  327. delay(5000);
  328. }
  329. // grate the snow
  330. for (int y = 0; y < 10; y++) {
  331. snow_maker_servo.write(45, 100, true);
  332. delay(500);
  333. snow_maker_servo.write(0, 100, true);
  334. delay(500);
  335. }
  336. }
  337. digitalWrite(snow, LOW);
  338. digitalWrite(pump, LOW);
  339. snow_valve_servo.write(0, 80, true);
  340. }
  341. void fog_bottom() {
  342. analogWrite(light_dimmer, 120);
  343. // turn on fog maker (relay)
  344. digitalWrite(fog, HIGH);
  345. // run for 60 seconds
  346. delay(60000);
  347. digitalWrite(fog, LOW);
  348. // return
  349. }
  350. void lightning() {
  351. switch (random(1, 3)) {
  352. case 1:
  353. thunderburst();
  354. delay(random(10, 500));
  355. break;
  356. case 2:
  357. rolling();
  358. break;
  359. case 3:
  360. crack();
  361. delay(random(50, 250));
  362. break;
  363. }
  364. }
  365. void reset() {
  366. for (int i = 0; i < NUM_LEDS; i++) {
  367. leds[i] = CHSV( 0, 0, 0);
  368. }
  369. FastLED.show();
  370. }
  371. void rolling() {
  372. for (int r = 0; r < random(2, 10); r++) {
  373. for (int i = 0; i < NUM_LEDS; i++) {
  374. if (random(0, 100) > 90) {
  375. leds[i] = CHSV( 0, 0, 255);
  376. }
  377. else {
  378. leds[i] = CHSV(0, 0, 0);
  379. }
  380. }
  381. FastLED.show();
  382. delay(random(5, 100));
  383. reset();
  384. }
  385. }
  386. void crack() {
  387. for (int i = 0; i < NUM_LEDS; i++) {
  388. leds[i] = CHSV( 0, 0, 255);
  389. }
  390. FastLED.show();
  391. delay(random(10, 100));
  392. reset();
  393. }
  394. void thunderburst() {
  395. int rs1 = random(0, NUM_LEDS / 2);
  396. int rl1 = random(10, 20);
  397. int rs2 = random(rs1 + rl1, NUM_LEDS);
  398. int rl2 = random(10, 20);
  399. for (int r = 0; r < random(3, 6); r++) {
  400. for (int i = 0; i < rl1; i++) {
  401. leds[i + rs1] = CHSV( 0, 0, 255);
  402. }
  403. if (rs2 + rl2 < NUM_LEDS) {
  404. for (int i = 0; i < rl2; i++) {
  405. leds[i + rs2] = CHSV( 0, 0, 255);
  406. }
  407. }
  408. FastLED.show();
  409. delay(random(10, 50));
  410. reset();
  411. delay(random(10, 50));
  412. }
  413. }

Menu
Index
Engineering
Entertainment
Literature
Miscellaneous
Contact
Search
tiktok.com/@pkvi.xyz
Why Ayh?
Miter
Miter
@pkvi
"...may not meet professional standards."
2,364 miters
123 tenons
Subscribe
0.00364