Advertisement
gquiring

Qmorphingclock

Jan 22nd, 2024
104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 50.11 KB | Source Code | 0 0
  1. /*
  2. Thanks to all who made this project possible!
  3.  
  4. remix from HarryFun's great Morphing Digital Clock idea https://github.com/hwiguna/HariFun_166_Morphing_Clock
  5. awesome build tutorial here:https://www.instructables.com/id/Morphing-Digital-Clock/
  6.  
  7. openweathermap compatibility added thanks to Imirel's remix https://github.com/lmirel/MorphingClockRemix
  8. some bits and pieces from kolle86's MorphingClockRemixRemix https://github.com/kolle86/MorphingClockRemixRemix
  9. small seconds by Trenck
  10.  
  11. this remix by timz3818 adds am/pm, working night mode (change brightness and colors), auto dissapearing first digit, and lower brightness icons
  12.  
  13. This remix by Gary Quiring
  14. https://github.com/gquiring/MorphingClockQ
  15.  
  16. Update: 2/20/2023
  17. There was always a 5-6 second delay getting the weather. It turns out it was not openweathermap. The code was waiting for a newline response that never showed up
  18. The default timeout was the issue, I lowered it to .5 seconds and it works perfectly now. This caused the last digit in the seconds to morph incorrectly
  19. The default interval for checking the weather is now every 2 minutes.
  20.  
  21. Update: 2/18/2023
  22. Added hostname: MorphClockQ (helps insteading of using IP address for web config)
  23. Changed the weather direction to a different color. When the Wind is from the South the S looks like a 5
  24. Fixed humidity if less than 10%, display issue
  25.  
  26. 3/2022
  27. Changed NTP calls, would not compile
  28. USA Daylight savings time won't work with NTPClientlib, it's default is EU DST, change the line below for USA default
  29. library/NTPCLientlib/src/NTClientlib.h
  30. #define DEFAULT_DST_ZONE DST_ZONE_USA
  31.  
  32. Fixed Morphing for Hour change, it no longer clears the screen and starts over
  33. Removed day/night mode
  34. Changed some RGB colors and made default colors for each area (Wind, Weather Text, Clock, Date)
  35. Changed Tiny font for number 1. It looked weird with straight line always right justified
  36. Removed leading zero from date display
  37. Changed wind = 0 to display "CALM"
  38. Add Weather text toggle for text or animation. I found the animation hard to understand. Text displays words like Cloudy
  39. Weather Animation/text toggle can be changed from web and stored to config file
  40. Fixed inconsistenty with web control variables. The code was not always referencing the changed variable but the hardcoded
  41. variables in params.h. Another words changing the settings on the web had no effect on the clock/weather display.
  42. Fixed the config file writing. location and apiKey were declared as Strings which was corrupting the file write. No clue why
  43. it was easier to declare them as char arrays and it worked.
  44. Added Reset Config File to web options. Most likely for developer use only. It's handy when the file is corrupt.
  45. Added urldecode function to remove %20's from the web entries
  46. Added day of week to the date line
  47. Wind and humidity will be alternately displayed every 10 seconds
  48. Created Wifi Connection function. It will try config file first and then params.h for SSID and Password
  49. Web logic was broken for changing SSID and Password, it never checked to see if it could connect before saving the settings
  50. Added Metric/Imperial options to the web interface
  51. Added brightness option to config file and web interface
  52. Added Color Palettes to web and config file
  53. Commented out OTA feature for the web interaface, the code is not excuted anywhere in the routines, not sure what it was for
  54. =====================================================================
  55. === INSTALLATION INSTRUCTIONS ===
  56. =====================================================================
  57. Youtube: https://youtu.be/5IvTE6gUA08
  58.  
  59. If you don't want to manually download the libraries I have them all in a zip file
  60. https://drive.google.com/file/d/1cQjsZGft_tuw0jCCs2JDoIu5awqr7lbc/
  61.  
  62.  
  63. copy paramsEDITTHISFIRST.h to params.h
  64. Edit params.h and fill in your SSID, Password and other settings
  65. Required libraries to compile:
  66. AdaFruit GFX Library v1.10.4 (Install all dependancies)
  67. PxMatrix LED Matrix Library by Dominic Buchstaller v1.3.0
  68. Wifi Manager by TZAPU .16.0 (listed as Tablatronix)
  69. NTPClientLib by German Martin 3.0.2 Beta
  70. Timelib by Paul Stoffregen 1.6.1
  71. Arduino JSON 5.13.5 (Do not install 6.x)
  72. ESP Async UDP Not in the IDE library use the link below
  73. https://github.com/me-no-dev/ESPAsyncUDP
  74.  
  75. From the File, Preferences menu, install this additional Link in the board
  76. Manager URL option:
  77. http://arduino.esp8266.com/stable/package_esp8266com_index.json
  78.  
  79. From the Tools menu, Board Manager select ESP8266 2.7.4 anything 3.0 or greater won't compile
  80.  
  81. To fix the Daylight Savings option for USA you must use an external editor
  82. You need to edit this file: (located in your Arduino library directory)
  83. library/NTPCLientlib/src/NTClientlib.h
  84. #define DEFAULT_DST_ZONE DST_ZONE_USA //It's default is EU
  85. ======================================================================
  86.  
  87. ///////////////////////////////////////////////////////////////////////////////////////////////////
  88. COMPONENTS
  89.  
  90. P3 RGB Pixel panel HD Display 64x32 dot Matrix SMD2121 Led Module Indoor Screen Full Color Video Wall 192x96mm Message Board
  91. https://www.aliexpress.com/item/32728985432.html
  92.  
  93. MELIFE 3pcs ESP8266 WiFi Development Module CH340 Serial Wireless Module NodeMcu Lua 4M WiFi WLAN Internet New Version Dev Board
  94. https://www.amazon.com/dp/B09F8GCVC8
  95.  
  96. ALITOVE 5V 8A 40W AC to DC Adapter Power Supply Converter Transformer 5.5x2.5mm Plug AC 100V~240V Input
  97. https://www.amazon.com/dp/B078RZBL8X
  98.  
  99. EDGELEC 120pcs Breadboard Jumper Wires 7.8 inch (7.8CM)
  100. https://www.amazon.com/dp/B07GD2BWPY
  101. ///////////////////////////////////////////////////////////////////////////////////////////////////
  102.  
  103. provided 'AS IS', use at your own risk
  104. */
  105.  
  106.  
  107. // we need these:
  108. #include <TimeLib.h>
  109. #include <NtpClientLib.h>
  110. #include <ESP8266WiFi.h>
  111. #include <PxMatrix.h>
  112. #include "FS.h"
  113. #include <ArduinoJson.h>
  114. #include "TinyFont.h"
  115. #include "Digit.h"
  116. #include "Digitsec.h"
  117. #include "params.h"
  118.  
  119. //ESP8266 setup
  120. #ifdef ESP8266
  121. #include <Ticker.h>
  122. Ticker display_ticker;
  123. #define P_LAT 16
  124. #define P_A 5
  125. #define P_B 4
  126. #define P_C 15
  127. #define P_D 12
  128. #define P_E 0
  129. #define P_OE 2
  130. #endif
  131.  
  132. // Pins for LED MATRIX
  133. PxMATRIX display(64, 32, P_LAT, P_OE, P_A, P_B, P_C, P_D, P_E);
  134.  
  135.  
  136. //void getWeather(); What is this doing here?
  137. // needed variables
  138. byte hh;
  139. byte mm;
  140. byte ss;
  141. byte ntpsync = 1;
  142. // const char ntpsvr[] = "pool.ntp.org";
  143. const char ntpsvr[] = "time.google.com";
  144.  
  145.  
  146. /////////==========NOTES==========//////////
  147. // by default, this is setup for displays using FM6126A drivers and 1/16 scan
  148. // if not using a display with FM6126A, remove/modify display.setDriverChip(FM6126A); and display.begin(16); as needed
  149. // See PxMAtrix documentation for more info regarding getting your LED matrix working
  150. //
  151. // double reset will not work, set WIFI info in params.h instead
  152. //
  153. //once connected to WIFI, you can change time zone by going to:
  154. // http://clock.ip.address/timezone/desired_offset
  155. // ex. http://clock.ip.address/timezone/-8 sets GMT -8
  156. //
  157. // the default icons are dim. you can use the commented out ones instead if you want brighter icons
  158. //
  159. // Known issues: the display will freeze for 5-10sec when weather info is updated
  160.  
  161.  
  162.  
  163.  
  164. /////////========== CONFIGURATION ==========//////////
  165. // Set your wifi info, api key, date format, unit type, and location in params.h
  166.  
  167.  
  168. //If you have more than one Morphing Clock you will need to change the hostname
  169. const char *WiFi_hostname = "MorphClockQ";
  170.  
  171. //If you want to adjust color/brightness/position of screen objects you can do that in the following sections.
  172. byte day_bright = 70; //sets daytime brightness; 70 is default. values higher than this may not work.
  173. byte dim_bright = 20; // sets brightness for partly dimmed mode
  174. byte nm_bright = 25; // sets brightness for night mode
  175.  
  176. //=== SEGMENTS ===
  177. // This section determines the position of the HH:MM ss digits onscreen with format digit#(&display, 0, x_offset, y_offset, irrelevant_color)
  178.  
  179. byte digit_offset_amount;
  180.  
  181. byte Digitsec_x = 56 + digit_offset_amount;
  182. byte Digit_x = 62 + digit_offset_amount;
  183.  
  184. Digitsec digit0(&display, 0, Digitsec_x - 7 * 1, 14, display.color565(255, 255, 255));
  185. Digitsec digit1(&display, 0, Digitsec_x - 7 * 2, 14, display.color565(255, 255, 255));
  186. Digit digit2(&display, 0, Digit_x - 4 - 9 * 3, 8, display.color565(255, 255, 255));
  187. Digit digit3(&display, 0, Digit_x - 4 - 9 * 4, 8, display.color565(255, 255, 255));
  188. Digit digit4(&display, 0, Digit_x - 7 - 9 * 5, 8, display.color565(255, 255, 255));
  189. Digit digit5(&display, 0, Digit_x - 7 - 9 * 6, 8, display.color565(255, 255, 255));
  190.  
  191.  
  192. //=== COLORS ===
  193. // this defines the colors used for the time, date, and wind info
  194. // format: display.color565 (R,G,B), with RGB values from 0 to 255
  195. // default is set really dim (~10% of max), increase for more brightness if needed
  196.  
  197. int color_disp = display.color565(40, 40, 50); // primary color
  198.  
  199.  
  200. // some other colors
  201. // R G B
  202. int cc_blk = display.color565(0, 0, 0); // black
  203. int cc_wht = display.color565(25, 25, 25); // white
  204. int cc_bwht = display.color565(255, 255, 255); // bright white
  205. int cc_red = display.color565(50, 0, 0); // red
  206. int cc_bred = display.color565(255, 0, 0); // bright red
  207. int cc_org = display.color565(25, 10, 0); // orange
  208. int cc_borg = display.color565(255, 165, 0); // bright orange
  209. int cc_grn = display.color565(0, 45, 0); // green
  210. int cc_bgrn = display.color565(0, 255, 0); // bright green
  211. int cc_blu = display.color565(0, 0, 150); // blue
  212. int cc_bblu = display.color565(0, 128, 255); // bright blue
  213. int cc_ylw = display.color565(45, 45, 0); // yellow
  214. int cc_bylw = display.color565(255, 255, 0); // bright yellow
  215. int cc_gry = display.color565(10, 10, 10); // gray
  216. int cc_bgry = display.color565(128, 128, 128); // bright gray
  217. int cc_dgr = display.color565(3, 3, 3); // dark grey
  218. int cc_cyan = display.color565(0, 30, 30); // cyan
  219. int cc_bcyan = display.color565(0, 255, 255); // bright cyan
  220. int cc_ppl = display.color565(25, 0, 25); // purple
  221. int cc_bppl = display.color565(255, 0, 255); // bright purple
  222.  
  223. // Colors for time, wind, date and weather text (Temperature color varies based on actual temp)
  224. int cc_time;
  225. int cc_wind;
  226. int cc_date;
  227. int cc_wtext;
  228.  
  229. //===OTHER SETTINGS===
  230. int ani_speed = 500; // sets animation speed
  231. int weather_refresh = 1; // sets weather refresh interval in minutes; must be between 1 and 59
  232. int morph_off = 0; // display issue due to weather check
  233.  
  234. //=== POSITION ===
  235. // to stop seeing data use "nosee" to move its position outside of the display range
  236. byte nosee = 100;
  237.  
  238. // Set weather icon position; TF_cols = 4 by default
  239. byte img_x = 7 * TF_COLS + 1;
  240. byte img_y = 2;
  241.  
  242. // Date position
  243. byte date_x = 2;
  244. byte date_y = 26;
  245.  
  246. // Temperature position
  247. byte tmp_x = 12 * TF_COLS;
  248. byte tmp_y = 2;
  249.  
  250. // Wind position and label
  251. // I'm not clear on why the first position is shifted by TF_COLS wasting space
  252. // byte wind_x = 1*TF_COLS;
  253. byte wind_x = 0;
  254. byte wind_y = 2;
  255.  
  256. // Pressure position and label
  257. byte press_x = nosee;
  258. byte press_y = nosee;
  259. char press_label[] = "";
  260.  
  261. // Humidity postion and label
  262. byte humi_x = nosee;
  263. byte humi_y = nosee;
  264. char humi_label[] = "%";
  265.  
  266. // Text weather condition
  267. byte wtext_x = 5 * TF_COLS - 3;
  268. byte wtext_y = 2;
  269.  
  270. String wind_lstr = " ";
  271. String humi_lstr = " ";
  272. int wind_humi;
  273. String wind_direction[10] = { " ", "N ", "NE", "E ", "SE", "S ", "SW", "W ", "NW", "N " };
  274. String weather_text[12] = {" "," SUNNY ","P-CLOUDY","OVERCAST"," RAIN ","T-STORMS"," SNOW "," HAZY "," CLEAR "," FOGGY "," CLOUDY ","OVERCAST"};
  275.  
  276. const char server[] = "api.openweathermap.org";
  277. WiFiClient client;
  278. int in = -10000;
  279. int tempMax = -10000;
  280. int tempM = -10000;
  281. int presM = -10000;
  282. int humiM = -10000;
  283. int wind_speed = -10000;
  284. int condM = -1; //-1 - undefined, 0 - unk, 1 - sunny, 2 - cloudy, 3 - overcast, 4 - rainy, 5 - thunders, 6 - snow
  285. String condS = "";
  286. int wind_nr;
  287.  
  288. //Keep count on how many times it failed to connect to weather service
  289. int failed_connect = 0;
  290. int failed_data = 0;
  291. int failed_missing = 0;
  292.  
  293. WiFiServer httpsvr(80); //Initialize the server on Port 80
  294.  
  295. //settings
  296. #define NVARS 13
  297. #define LVARS 40
  298. char c_vars[NVARS][LVARS];
  299. typedef enum e_vars {
  300. EV_SSID,
  301. EV_PASS,
  302. EV_TZ,
  303. EV_24H,
  304. EV_METRIC,
  305. EV_DATEFMT,
  306. EV_OWMK,
  307. EV_GEOLOC,
  308. EV_DST,
  309. EV_OTA,
  310. EV_WANI,
  311. EV_PALET,
  312. EV_BRIGHT
  313. };
  314.  
  315. // Color palette
  316. void select_palette() {
  317. int x;
  318. x = atoi(c_vars[EV_PALET]);
  319.  
  320. switch (x) {
  321. case 1:
  322. cc_time = cc_cyan;
  323. cc_wind = cc_ylw;
  324. cc_date = cc_grn;
  325. cc_wtext = cc_wht;
  326. break;
  327. case 2:
  328. cc_time = cc_red;
  329. cc_wind = cc_ylw;
  330. cc_date = cc_blu;
  331. cc_wtext = cc_grn;
  332. break;
  333. case 3:
  334. cc_time = cc_blu;
  335. cc_wind = cc_grn;
  336. cc_date = cc_ylw;
  337. cc_wtext = cc_wht;
  338. break;
  339. case 4:
  340. cc_time = cc_ylw;
  341. cc_wind = cc_cyan;
  342. cc_date = cc_blu;
  343. cc_wtext = cc_grn;
  344. break;
  345. case 5:
  346. cc_time = cc_bblu;
  347. cc_wind = cc_grn;
  348. cc_date = cc_ylw;
  349. cc_wtext = cc_grn;
  350. break;
  351. case 6:
  352. cc_time = cc_org;
  353. cc_wind = cc_red;
  354. cc_date = cc_grn;
  355. cc_wtext = cc_ylw;
  356. break;
  357. case 7:
  358. cc_time = cc_grn;
  359. cc_wind = cc_ppl;
  360. cc_date = cc_cyan;
  361. cc_wtext = cc_ylw;
  362. break;
  363. default:
  364. cc_time = cc_cyan;
  365. cc_wind = cc_ylw;
  366. cc_date = cc_grn;
  367. cc_wtext = cc_wht;
  368. break;
  369. }
  370. }
  371.  
  372. //#ifdef ESP8266
  373. // ISR for display refresh
  374. void display_updater() {
  375. int x;
  376. x = atoi(c_vars[EV_BRIGHT]);
  377. if (x > 70 or x < 0)
  378. x = 70;
  379.  
  380. display.display(x); //set brightness
  381. }
  382.  
  383. //#endif
  384.  
  385. bool toBool(String s) {
  386. return s.equals("true");
  387. }
  388.  
  389. int vars_read() {
  390. //Remove file for testing when it has corrupt data
  391. //SPIFFS.remove("/mvars.cfg");
  392. //File varf = SPIFFS.open ("/mvars.cfg", "w");
  393. // varf.close ();
  394. //return 1;
  395.  
  396. File varf = SPIFFS.open("/mvars.cfg", "r");
  397. if (!varf) {
  398. Serial.println("Failed to open config file");
  399. return 0;
  400. }
  401. //read vars
  402. for (int i = 0; i < NVARS; i++)
  403. for (int j = 0; j < LVARS; j++)
  404. c_vars[i][j] = (char)varf.read();
  405.  
  406. varf.close();
  407. show_config_vars();
  408. return 1;
  409. }
  410.  
  411. int vars_write() {
  412. File varf = SPIFFS.open("/mvars.cfg", "w");
  413. if (!varf) {
  414. Serial.println("Failed to open config file");
  415. return 0;
  416. }
  417. Serial.println("Writing config file ......");
  418. for (int i = 0; i < NVARS; i++) {
  419. for (int j = 0; j < LVARS; j++) {
  420. if (varf.write(c_vars[i][j]) != 1)
  421. Serial.println("error writing var");
  422. }
  423. }
  424. //
  425. varf.close();
  426. return 1;
  427. }
  428.  
  429. unsigned char h2int(char c) {
  430. if (c >= '0' && c <= '9') {
  431. return ((unsigned char)c - '0');
  432. }
  433. if (c >= 'a' && c <= 'f') {
  434. return ((unsigned char)c - 'a' + 10);
  435. }
  436. if (c >= 'A' && c <= 'F') {
  437. return ((unsigned char)c - 'A' + 10);
  438. }
  439. return (0);
  440. }
  441.  
  442. //Strip out URL encoding
  443. String urldecode(String str) {
  444.  
  445. String encodedString = "";
  446. char c;
  447. char code0;
  448. char code1;
  449. for (int i = 0; i < str.length(); i++) {
  450. c = str.charAt(i);
  451. if (c == '+') {
  452. encodedString += ' ';
  453. } else if (c == '%') {
  454. i++;
  455. code0 = str.charAt(i);
  456. i++;
  457. code1 = str.charAt(i);
  458. c = (h2int(code0) << 4) | h2int(code1);
  459. encodedString += c;
  460. } else {
  461.  
  462. encodedString += c;
  463. }
  464.  
  465. yield();
  466. }
  467.  
  468. return encodedString;
  469. }
  470.  
  471. //Debugging
  472. void show_config_vars() {
  473. Serial.println("From config file ....");
  474.  
  475. Serial.print("SSID=");
  476. Serial.println(c_vars[EV_SSID]);
  477. Serial.print("Password=");
  478. Serial.println(c_vars[EV_PASS]);
  479. Serial.print("Timezone=");
  480. Serial.println(c_vars[EV_TZ]);
  481. Serial.print("Military=");
  482. Serial.println(c_vars[EV_24H]);
  483. Serial.print("Metric=");
  484. Serial.println(c_vars[EV_METRIC]);
  485. Serial.print("Date-Format=");
  486. Serial.println(c_vars[EV_DATEFMT]);
  487. Serial.print("apiKey=");
  488. Serial.println(c_vars[EV_OWMK]);
  489. Serial.print("Location=");
  490. Serial.println(c_vars[EV_GEOLOC]);
  491. Serial.print("DST=");
  492. Serial.println(c_vars[EV_DST]);
  493. Serial.print("Weather Animation=");
  494. Serial.println(c_vars[EV_WANI]);
  495. Serial.print("Color Palette=");
  496. Serial.println(c_vars[EV_PALET]);
  497. Serial.print("Brightness=");
  498. Serial.println(c_vars[EV_BRIGHT]);
  499. }
  500.  
  501. //If the config file is not setup copy from params.h
  502. void init_config_vars() {
  503. strcpy(c_vars[EV_SSID], wifi_ssid);
  504. strcpy(c_vars[EV_PASS], wifi_pass);
  505. strcpy(c_vars[EV_TZ], timezone);
  506. strcpy(c_vars[EV_24H], military);
  507. strcpy(c_vars[EV_METRIC], u_metric);
  508. strcpy(c_vars[EV_DATEFMT], date_fmt);
  509. strcpy(c_vars[EV_OWMK], apiKey);
  510. strcpy(c_vars[EV_GEOLOC], location);
  511. strcpy(c_vars[EV_DST], dst_sav);
  512. strcpy(c_vars[EV_WANI], w_animation);
  513. strcpy(c_vars[EV_PALET], c_palet);
  514. strcpy(c_vars[EV_BRIGHT], brightness);
  515. }
  516.  
  517. //Wifi Connection
  518. int connect_wifi(String n_wifi, String n_pass) {
  519. int c_cnt = 0;
  520. Serial.print("Trying WiFi Connect:");
  521. Serial.println(n_wifi);
  522.  
  523. WiFi.hostname(WiFi_hostname); //hostname not in params.h
  524.  
  525. WiFi.begin(n_wifi, n_pass);
  526. WiFi.mode(WIFI_STA);
  527. while (WiFi.status() != WL_CONNECTED) {
  528. delay(500);
  529. Serial.print(".");
  530. c_cnt++;
  531. if (c_cnt > 50) {
  532. Serial.println("Wifi Connect Failed");
  533. return 1;
  534. }
  535. }
  536. Serial.println("success!");
  537. Serial.print("IP Address is: ");
  538. Serial.println(WiFi.localIP()); //
  539. return 0;
  540. }
  541.  
  542. void setup() {
  543. Serial.begin(9600);
  544. while (!Serial)
  545. delay(500); //delay for Leonardo
  546. display.begin(16);
  547. // display.setDriverChip(FM6126A);
  548. // display.setMuxDelay(0,1,0,0,0);
  549.  
  550. #ifdef ESP8266
  551. display_ticker.attach(0.002, display_updater);
  552. #endif
  553.  
  554. TFDrawText(&display, String(" MORPH CLOCK "), 0, 1, cc_blu);
  555. TFDrawText(&display, String(" STARTING UP "), 0, 10, cc_blu);
  556.  
  557. // Read the config file
  558. if (SPIFFS.begin()) {
  559. Serial.println("SPIFFS Initialize....ok");
  560. if (!vars_read()) {
  561. init_config_vars(); //Copy from params.h to EV array
  562. }
  563. } else {
  564. Serial.println("SPIFFS Initialization...failed");
  565. }
  566.  
  567. String lstr = String("TIMEZONE:") + String(c_vars[EV_TZ]);
  568. TFDrawText(&display, lstr, 4, 24, cc_cyan);
  569.  
  570. show_config_vars();
  571.  
  572.  
  573. //connect to wifi network
  574.  
  575. if (connect_wifi(c_vars[EV_SSID], c_vars[EV_PASS]) == 1) { // Try settings in config file
  576. TFDrawText(&display, String("WIFI FAILED CONFIG"), 1, 10, cc_grn);
  577. if (connect_wifi(wifi_ssid, wifi_pass) == 1) { // Try settings in params.h
  578. Serial.println("Cannot connect to anything, RESTART ESP");
  579. TFDrawText(&display, String("WIFI FAILED PARAMS.H"), 1, 10, cc_grn);
  580. resetclock();
  581. }
  582. }
  583.  
  584. TFDrawText(&display, String("WIFI CONNECTED "), 3, 10, cc_grn);
  585. TFDrawText(&display, String(WiFi.localIP().toString()), 4, 17, cc_grn);
  586.  
  587. select_palette();
  588.  
  589. // delay (3000); Why wait?
  590.  
  591. getWeather();
  592. if (wind_speed < 0) //sometimes weather does not work on first attempt
  593. getWeather();
  594.  
  595. httpsvr.begin(); // Start the HTTP Server
  596.  
  597. //start NTP
  598. Serial.print("TimeZone for NTP.Begin:");
  599. Serial.println(c_vars[EV_TZ]);
  600. NTP.begin(ntpsvr, String(c_vars[EV_TZ]).toInt(), toBool(String(c_vars[EV_DST])));
  601. NTP.setInterval(10); //force rapid sync in 10sec
  602. //
  603. NTP.onNTPSyncEvent([](NTPSyncEvent_t ntpEvent) {
  604. switch (ntpEvent) {
  605. case noResponse:
  606. Serial.print("Time Sync error: ");
  607. Serial.println("NTP server not reachable");
  608. break;
  609. case invalidAddress:
  610. Serial.print("Time Sync error: ");
  611. Serial.println("Invalid NTP server address");
  612. break;
  613. default:
  614. Serial.print("Got NTP time: ");
  615. Serial.println(NTP.getTimeDateString(NTP.getLastNTPSync()));
  616. ntpsync = 1;
  617. break;
  618. }
  619. });
  620.  
  621. //prep screen for clock display
  622.  
  623. display.fillScreen(0);
  624. }
  625.  
  626. void getWeather() {
  627. if (!sizeof(apiKey)) {
  628. Serial.println("Missing API KEY for weather data, skipping");
  629. return;
  630. }
  631.  
  632. Serial.println("Weather: Connect started");
  633.  
  634. if (client.connect(server, 80)) {
  635. client.print("GET /data/2.5/weather?q=" + String(c_vars[EV_GEOLOC]) + "&appid=" + String(c_vars[EV_OWMK]) + "&cnt=1");
  636. if (String(c_vars[EV_METRIC]) == "Y")
  637. client.println("&units=metric");
  638. else
  639. client.println("&units=imperial");
  640.  
  641. client.println("Host: api.openweathermap.org");
  642. client.println("Connection: close");
  643. client.println();
  644. } else {
  645. Serial.println("Weather: Unable to connect");
  646. failed_connect = failed_connect + 1;
  647. if (failed_connect >= 5) {
  648. Serial.println("Weather: 5 failed attempts to connect. Reset");
  649. resetclock();
  650. }
  651. }
  652. Serial.println("Weather: Connect completed");
  653.  
  654. // Sample of what the weather API sends back
  655. // {"coord":{"lon":-80.1757,"lat":33.0185},"weather":[{"id":741,"main":"Fog","description":"fog","icon":"50n"},
  656. // {"id":500,"main":"Rain","description":"light rain","icon":"10n"}],"base":"stations","main":{"temp":55.47,
  657. // "feels_like":55.33,"temp_min":52.81,"temp_max":57.79,"pressure":1014,"humidity":98},"visibility":402,
  658. // "wind":{"speed":4.61,"deg":0},"rain":{"1h":0.25},"clouds":{"all":100},
  659. // "dt":1647516313,"sys":{"type":2,"id":2034311,"country":"US","sunrise":1647516506,
  660. // "sunset":1647559782},"timezone":-14400,"id":4597919,"name":"Summerville","cod":200}
  661.  
  662.  
  663. String sval = "";
  664. int bT, bT2;
  665. failed_missing = 0;
  666.  
  667. client.setTimeout(500); //We need to reduce the timeout delay for the readStringUntil
  668. String line = client.readStringUntil('\n'); //apparently the returned data does not have a new line so a timeout occurs
  669.  
  670. if (!line.length()) {
  671. Serial.println("Weather: Unable to retrieve data");
  672. return;
  673. }
  674.  
  675. bT = line.indexOf("\"icon\":\"");
  676. if (bT > 0) {
  677. bT2 = line.indexOf("\"", bT + 8);
  678. sval = line.substring(bT + 8, bT2);
  679. //0 - unk, 1 - sunny, 2 - cloudy, 3 - overcast, 4 - rainy, 5 - thunders, 6 - snow
  680.  
  681. if (sval.equals("01d"))
  682. condM = 1; //sunny
  683. else if (sval.equals("01n"))
  684. condM = 8; //clear night
  685. else if (sval.equals("02d"))
  686. condM = 2; //partly cloudy day
  687. else if (sval.equals("02n"))
  688. condM = 10; //partly cloudy night
  689. else if (sval.equals("03d"))
  690. condM = 3; //overcast day
  691. else if (sval.equals("03n"))
  692. condM = 11; //overcast night
  693. else if (sval.equals("04d"))
  694. condM = 3; //overcast day
  695. else if (sval.equals("04n"))
  696. condM = 11; //overcast night
  697. else if (sval.equals("09d"))
  698. condM = 4; //rain
  699. else if (sval.equals("09n"))
  700. condM = 4;
  701. else if (sval.equals("10d"))
  702. condM = 4;
  703. else if (sval.equals("10n"))
  704. condM = 4;
  705. else if (sval.equals("11d"))
  706. condM = 5; //thunder
  707. else if (sval.equals("11n"))
  708. condM = 5;
  709. else if (sval.equals("13d"))
  710. condM = 6; //snow
  711. else if (sval.equals("13n"))
  712. condM = 6;
  713. else if (sval.equals("50d"))
  714. condM = 7; //haze (day)
  715. else if (sval.equals("50n"))
  716. condM = 9; //fog (night)
  717.  
  718. condS = sval;
  719. }
  720. //tempM
  721. bT = line.indexOf("\"temp\":");
  722. if (bT > 0) {
  723. bT2 = line.indexOf(",\"", bT + 7);
  724. sval = line.substring(bT + 7, bT2);
  725. tempM = sval.toInt();
  726. } else {
  727. Serial.println("temp NOT found!");
  728. Serial.println(line);
  729. failed_missing = failed_missing + 1;
  730. }
  731.  
  732. //humiM
  733. bT = line.indexOf("\"humidity\":");
  734. if (bT > 0) {
  735. bT2 = line.indexOf(",\"", bT + 11);
  736. sval = line.substring(bT + 11, bT2);
  737. humiM = sval.toInt();
  738. } else {
  739. Serial.println("humidity NOT found!");
  740. failed_missing = failed_missing + 1;
  741. }
  742.  
  743. //wind speed
  744. bT = line.indexOf("\"speed\":");
  745. if (bT > 0) {
  746. bT2 = line.indexOf(",\"", bT + 8);
  747. sval = line.substring(bT + 8, bT2);
  748. wind_speed = sval.toInt();
  749. } else {
  750. wind_speed = -10000;
  751. Serial.println("windspeed NOT found");
  752. failed_missing = failed_missing + 1;
  753. }
  754. // bT = line.indexOf ("\"timezone\":"); // Timezone offset
  755. // if (bT > 0)
  756. // {
  757. // int tz;
  758. // bT2 = line.indexOf (",\"", bT + 11);
  759. // sval = line.substring (bT + 11, bT2);
  760. // tz = sval.toInt()/3600;
  761. // }
  762. // else
  763. // Serial.println ("timezone offset NOT found!");
  764.  
  765. //wind direction
  766. bT = line.indexOf("\"deg\":");
  767. if (bT > 0) {
  768. bT2 = line.indexOf(",\"", bT + 6);
  769. sval = line.substring(bT + 6, bT2);
  770. wind_nr = round(((sval.toInt() % 360)) / 45.0) + 1;
  771. } else {
  772. wind_nr = 0;
  773. Serial.println("Wind direction NOT found");
  774. failed_missing = failed_missing + 1;
  775. }
  776.  
  777. if (failed_missing > 0)
  778. Serial.println(line);
  779. }
  780. // End of Get Weather
  781.  
  782. #include "TinyIcons.h"
  783. #include "WeatherIcons.h"
  784.  
  785. int xo = 1, yo = 26;
  786. char use_ani = 0;
  787. void draw_weather_conditions() {
  788. //0 - unk, 1 - sunny, 2 - cloudy, 3 - overcast, 4 - rainy, 5 - thunders, 6 - snow, 7, hazy, 9 - fog
  789.  
  790. xo = img_x;
  791. yo = img_y;
  792.  
  793. if (condM == 0) {
  794. Serial.print("!weather condition icon unknown, show: ");
  795. Serial.println(condS);
  796. int cc_dgr = display.color565(30, 30, 30);
  797. //draw the first 5 letters from the unknown weather condition
  798. String lstr = condS.substring(0, (condS.length() > 5 ? 5 : condS.length()));
  799. lstr.toUpperCase();
  800. TFDrawText(&display, lstr, xo, yo, cc_dgr);
  801. } else {
  802. // TFDrawText (&display, String(" "), xo, yo, 0);
  803. }
  804. xo = img_x;
  805. yo = img_y;
  806. switch (condM) {
  807. case 0: //unk
  808. break;
  809. case 1: //sunny
  810. DrawIcon(&display, sunny_ico, xo, yo, 10, 5);
  811. use_ani = 1;
  812. break;
  813. case 2: //cloudy
  814. DrawIcon(&display, cloudy_ico, xo, yo, 10, 5);
  815. use_ani = 1;
  816. break;
  817. case 3: //overcast
  818. DrawIcon(&display, ovrcst_ico, xo, yo, 10, 5);
  819. use_ani = 1;
  820. break;
  821. case 4: //rainy
  822. DrawIcon(&display, rain_ico, xo, yo, 10, 5);
  823. use_ani = 1;
  824. break;
  825. case 5: //thunders
  826. DrawIcon(&display, thndr_ico, xo, yo, 10, 5);
  827. use_ani = 1;
  828. break;
  829. case 6: //snow
  830. DrawIcon(&display, snow_ico, xo, yo, 10, 5);
  831. use_ani = 1;
  832. break;
  833. case 7: //mist
  834. DrawIcon(&display, mist_ico, xo, yo, 10, 5);
  835. use_ani = 1;
  836. break;
  837. case 8: //clear night
  838. DrawIcon(&display, moony_ico, xo, yo, 10, 5);
  839. use_ani = 1;
  840. break;
  841. case 9: //fog night
  842. DrawIcon(&display, mistn_ico, xo, yo, 10, 5);
  843. use_ani = 1;
  844. break;
  845. case 10: //partly cloudy night
  846. DrawIcon(&display, cloudyn_ico, xo, yo, 10, 5);
  847. use_ani = 1;
  848. break;
  849. case 11: //cloudy night
  850. DrawIcon(&display, ovrcstn_ico, xo, yo, 10, 5);
  851. use_ani = 1;
  852. break;
  853. }
  854. }
  855.  
  856.  
  857. void draw_wind() {
  858. if (wind_speed < 0)
  859. return;
  860. wind_lstr = String(wind_speed);
  861. if (wind_speed != 0) {
  862. switch (wind_lstr.length()) { //We have to pad the string to exactly 4 characters
  863. case 1:
  864. wind_lstr = String(wind_lstr) + String(" ");
  865. break;
  866. }
  867. TFDrawText(&display, wind_direction[wind_nr], wind_x, wind_y, cc_wht); //Change Wind Direction color for readability
  868. TFDrawText(&display, wind_lstr, wind_x + 8, wind_y, cc_wind);
  869. } else {
  870. wind_lstr = String("CALM");
  871. TFDrawText(&display, wind_lstr, wind_x, wind_y, cc_wind);
  872. }
  873. wind_humi = 1; //Reset switch for toggling wind or humidity display
  874. }
  875.  
  876. //
  877. void draw_weather() {
  878. int value = 0;
  879.  
  880. if (tempM == -10000 && humiM == -10000 && presM == -10000) {
  881. Serial.println("No weather data available");
  882. failed_data = failed_data + 1;
  883. if (failed_data >= 5)
  884. resetclock();
  885. return;
  886. }
  887.  
  888. //-temperature
  889. int lcc = cc_red;
  890. char tmp_Metric;
  891. if (String(c_vars[EV_METRIC]) == "Y") {
  892. tmp_Metric = 'C';
  893. lcc = cc_red;
  894. if (tempM < 30)
  895. lcc = cc_org;
  896. if (tempM < 25)
  897. lcc = cc_ylw;
  898. if (tempM < 20)
  899. lcc = cc_grn;
  900. if (tempM < 15)
  901. lcc = cc_blu;
  902. if (tempM < 10)
  903. lcc = cc_cyan;
  904. if (tempM < 1)
  905. lcc = cc_wht;
  906. } else {
  907. tmp_Metric = 'F';
  908. //F
  909. if (tempM < 90)
  910. lcc = cc_grn;
  911. if (tempM < 75)
  912. lcc = cc_blu;
  913. if (tempM < 43)
  914. lcc = cc_wht;
  915. }
  916.  
  917. String lstr = String(tempM) + String(tmp_Metric);
  918.  
  919. //Padding Temp with spaces to keep position the same
  920. switch (lstr.length()) {
  921. case 2:
  922. lstr = String(" ") + String(lstr);
  923. break;
  924. case 3:
  925. lstr = String(" ") + String(lstr);
  926. break;
  927. }
  928.  
  929. TFDrawText(&display, lstr, tmp_x, tmp_y, lcc); // draw temperature
  930.  
  931. //weather conditions
  932. //-humidity
  933. lcc = cc_red;
  934. if (humiM < 80)
  935. lcc = cc_org;
  936. if (humiM < 60)
  937. lcc = cc_grn;
  938. if (humiM < 40)
  939. lcc = cc_blu;
  940. if (humiM < 20)
  941. lcc = cc_wht;
  942.  
  943. // Pad humi to exactly 4 characters
  944. humi_lstr = String(humiM) + String(humi_label);
  945. switch (humi_lstr.length()) {
  946. case 2:
  947. humi_lstr = String(humi_lstr) + String(" ");
  948. break;
  949. case 3:
  950. humi_lstr = String(humi_lstr) + String(" ");
  951. break;
  952. }
  953. TFDrawText(&display, humi_lstr, humi_x, humi_y, lcc); // humidity
  954.  
  955. int cc = color_disp;
  956. cc = color_disp;
  957.  
  958. //Draw wind speed and direction
  959. draw_wind();
  960. wind_humi = 1; //Reset switch for toggling wind or humidity display
  961.  
  962. if (String(c_vars[EV_WANI]) == "N") {
  963. TFDrawText(&display, weather_text[condM], wtext_x, wtext_y, cc_wtext);
  964. } else {
  965. draw_weather_conditions();
  966. }
  967. }
  968. // End display weather
  969.  
  970. void draw_date() {
  971. //date below the clock
  972. long tnow = now();
  973. String lstr = "";
  974.  
  975. for (int i = 0; i < 5; i += 2) {
  976. switch (date_fmt[i]) {
  977. case 'D':
  978. lstr += (day(tnow) < 10 ? " " + String(day(tnow)) : String(day(tnow)));
  979. if (i < 4)
  980. lstr += date_fmt[i + 1];
  981. break;
  982. case 'M': //Replaced leading 0 with space where double quotes are
  983. lstr += (month(tnow) < 10 ? " " + String(month(tnow)) : String(month(tnow)));
  984. if (i < 4)
  985. lstr += date_fmt[i + 1];
  986. break;
  987. case 'Y':
  988. lstr += String(year(tnow));
  989. if (i < 4)
  990. lstr += date_fmt[i + 1];
  991. break;
  992. }
  993. }
  994. //
  995. String DayofWeek = " ";
  996. switch (weekday(tnow)) {
  997. case 1:
  998. DayofWeek = " SUN";
  999. break;
  1000. case 2:
  1001. DayofWeek = " MON";
  1002. break;
  1003. case 3:
  1004. DayofWeek = " TUE";
  1005. break;
  1006. case 4:
  1007. DayofWeek = " WED";
  1008. break;
  1009. case 5:
  1010. DayofWeek = " THR";
  1011. break;
  1012. case 6:
  1013. DayofWeek = " FRI";
  1014. break;
  1015. case 7:
  1016. DayofWeek = " SAT";
  1017. break;
  1018. }
  1019.  
  1020. lstr += String(DayofWeek);
  1021.  
  1022. if (lstr.length())
  1023. TFDrawText(&display, lstr, date_x, date_y, cc_date);
  1024. }
  1025.  
  1026.  
  1027. void draw_animations(int stp) {
  1028. //weather icon animation
  1029. String lstr = "";
  1030. xo = img_x;
  1031. yo = img_y;
  1032. //0 - unk, 1 - sunny, 2 - cloudy, 3 - overcast, 4 - rainy, 5 - thunders, 6 - snow
  1033. if (use_ani) {
  1034. int *af = NULL;
  1035. switch (condM) {
  1036. case 1:
  1037. af = suny_ani[stp % 5];
  1038. break;
  1039. case 2:
  1040. af = clod_ani[stp % 10];
  1041. break;
  1042. case 3:
  1043. af = ovct_ani[stp % 5];
  1044. break;
  1045. case 4:
  1046. af = rain_ani[stp % 5];
  1047. break;
  1048. case 5:
  1049. af = thun_ani[stp % 5];
  1050. break;
  1051. case 6:
  1052. af = snow_ani[stp % 5];
  1053. break;
  1054. case 7:
  1055. af = mist_ani[stp % 4];
  1056. break;
  1057. case 8:
  1058. af = mony_ani[stp % 17];
  1059. break;
  1060. case 9:
  1061. af = mistn_ani[stp % 4];
  1062. break;
  1063. case 10:
  1064. af = clodn_ani[stp % 10];
  1065. break;
  1066. case 11:
  1067. af = ovctn_ani[stp % 1];
  1068. break;
  1069. }
  1070. //draw animation
  1071. if (af)
  1072. DrawIcon(&display, af, xo, yo, 10, 5);
  1073. }
  1074. }
  1075.  
  1076. byte prevhh = 0;
  1077. byte prevmm = 0;
  1078. byte prevss = 0;
  1079. long tnow;
  1080. WiFiClient httpcli;
  1081.  
  1082.  
  1083. //convert hex letter to value
  1084. char hexchar2code(const char *hb) {
  1085. if (*hb >= 'a')
  1086. return *hb - 'a' + 10;
  1087. if (*hb >= 'A')
  1088. return *hb - 'A' + 10;
  1089. return *hb - '0';
  1090. }
  1091.  
  1092. //To find the values they are sandwiched between search and it always ends before "HTTP /"
  1093. //Pidx + ? is length of string searching for ie "?geoloc=" = length 8, pidx + 8
  1094. //pidx2 is end of string location for HTTP /
  1095. void web_server() {
  1096. httpcli = httpsvr.available();
  1097. if (httpcli) {
  1098. char svf = 0, rst = 0;
  1099. //Read what the browser has sent into a String class and print the request to the monitor
  1100. String httprq = httpcli.readString();
  1101. // Looking under the hood
  1102. // Serial.println (httprq);
  1103. int pidx = -1;
  1104. //
  1105. String httprsp = "HTTP/1.1 200 OK\r\n";
  1106. httprsp += "Content-type: text/html\r\n\r\n";
  1107. httprsp += "<!DOCTYPE HTML>\r\n<html>\r\n";
  1108.  
  1109. if ((pidx = httprq.indexOf("GET /datetime/")) != -1) {
  1110. int pidx2 = httprq.indexOf(" ", pidx + 14);
  1111. if (pidx2 != -1) {
  1112. String datetime = httprq.substring(pidx + 14, pidx2);
  1113. //display.setBrightness (bri.toInt ());
  1114. int yy = datetime.substring(0, 4).toInt();
  1115. int MM = datetime.substring(4, 6).toInt();
  1116. int dd = datetime.substring(6, 8).toInt();
  1117. int hh = datetime.substring(8, 10).toInt();
  1118. int mm = datetime.substring(10, 12).toInt();
  1119. int ss = 0;
  1120. if (datetime.length() == 14) {
  1121. ss = datetime.substring(12, 14).toInt();
  1122. }
  1123. //void setTime(int hr,int min,int sec,int dy, int mnth, int yr)
  1124. setTime(hh, mm, ss, dd, MM, yy);
  1125. ntpsync = 1;
  1126. }
  1127. }
  1128.  
  1129. else if (httprq.indexOf("GET /ota/") != -1) {
  1130. //GET /ota/?otaloc=192.168.2.38%3A8000%2Fespweather.bin HTTP/1.1
  1131. pidx = httprq.indexOf("?otaloc=");
  1132. int pidx2 = httprq.indexOf(" HTTP/", pidx);
  1133. if (pidx2 > 0) {
  1134. strncpy(c_vars[EV_OTA], httprq.substring(pidx + 8, pidx2).c_str(), LVARS * 3);
  1135. //debug_print (">ota1:");
  1136. //debug_println (c_vars[EV_OTA]);
  1137. char *bc = c_vars[EV_OTA];
  1138. int ck = 0;
  1139. //debug_print (">ota2:");
  1140. //debug_println (bc);
  1141. //convert in place url %HH excaped chars
  1142. while (*bc > 0 && ck < LVARS * 3) {
  1143. if (*bc == '%') {
  1144. //convert URL chars to ascii
  1145. c_vars[EV_OTA][ck] = hexchar2code(bc + 1) << 4 | hexchar2code(bc + 2);
  1146. bc += 2;
  1147. } else
  1148. c_vars[EV_OTA][ck] = *bc;
  1149. //next one
  1150. //debug_println (c_vars[EV_OTA][ck]);
  1151. bc++;
  1152. ck++;
  1153. }
  1154. c_vars[EV_OTA][ck] = 0;
  1155. svf = 1;
  1156. }
  1157. }
  1158. //location
  1159. else if (httprq.indexOf("GET /geoloc/") != -1) {
  1160. pidx = httprq.indexOf("?geoloc=");
  1161. int pidx2 = httprq.indexOf(" HTTP/", pidx);
  1162. if (pidx2 > 0) {
  1163. String location = urldecode(httprq.substring(pidx + 8, pidx2).c_str());
  1164. strncpy(c_vars[EV_GEOLOC], location.c_str(), LVARS * 3);
  1165. getWeather();
  1166. draw_weather_conditions();
  1167. svf = 1;
  1168. }
  1169. }
  1170. //openweathermap.org key
  1171. else if (httprq.indexOf("GET /owm/") != -1) {
  1172. pidx = httprq.indexOf("?owmkey=");
  1173. int pidx2 = httprq.indexOf(" HTTP/", pidx);
  1174. if (pidx2 > 0) {
  1175. strncpy(c_vars[EV_OWMK], httprq.substring(pidx + 8, pidx2).c_str(), LVARS * 3);
  1176. getWeather();
  1177. draw_weather_conditions();
  1178. svf = 1;
  1179. }
  1180. //
  1181. } else if (httprq.indexOf("GET /wifi/") != -1) {
  1182. //GET /wifi/?ssid=ssid&pass=pass HTTP/1.1
  1183. pidx = httprq.indexOf("?ssid=");
  1184. int pidx2 = httprq.indexOf("&pass=");
  1185. String ssid = httprq.substring(pidx + 6, pidx2);
  1186. pidx = httprq.indexOf(" HTTP/", pidx2);
  1187. String pass = httprq.substring(pidx2 + 6, pidx);
  1188. if (connect_wifi(ssid.c_str(), pass.c_str()) == 0) {
  1189. strncpy(c_vars[EV_SSID], ssid.c_str(), LVARS * 2);
  1190. strncpy(c_vars[EV_PASS], pass.c_str(), LVARS * 2);
  1191. svf = 1;
  1192. // rst = 1;
  1193. } else {
  1194. Serial.println("Wifi Connect failed, will try prior SSID and Password");
  1195. if (connect_wifi(c_vars[EV_SSID], c_vars[EV_PASS]) == 1)
  1196. ESP.restart(); //Give up reboot
  1197. }
  1198.  
  1199. } else if (httprq.indexOf("GET /daylight/on ") != -1) {
  1200. strcpy(c_vars[EV_DST], "true");
  1201. NTP.begin(ntpsvr, String(c_vars[EV_TZ]).toInt(), toBool(String(c_vars[EV_DST])));
  1202. httprsp += "<strong>daylight: on</strong><br>";
  1203. svf = 1;
  1204. } else if (httprq.indexOf("GET /daylight/off ") != -1) {
  1205. strcpy(c_vars[EV_DST], "false");
  1206. NTP.begin(ntpsvr, String(c_vars[EV_TZ]).toInt(), toBool(String(c_vars[EV_DST])));
  1207. httprsp += "<strong>daylight: off</strong><br>";
  1208. svf = 1;
  1209. } else if (httprq.indexOf("GET /metric/on ") != -1) {
  1210. strcpy(c_vars[EV_METRIC], "Y");
  1211. httprsp += "<strong>metric: on</strong><br>";
  1212. getWeather();
  1213. draw_weather_conditions();
  1214. svf = 1;
  1215. } else if (httprq.indexOf("GET /metric/off ") != -1) {
  1216. strcpy(c_vars[EV_METRIC], "N");
  1217. httprsp += "<strong>metric: off</strong><br>";
  1218. getWeather();
  1219. draw_weather_conditions();
  1220. svf = 1;
  1221. } else if ((pidx = httprq.indexOf("GET /brightness/")) != -1) {
  1222. int pidx2 = httprq.indexOf(" ", pidx + 16);
  1223. if (pidx2 != -1) {
  1224. String bri = httprq.substring(pidx + 16, pidx2);
  1225. strcpy(c_vars[EV_BRIGHT], bri.c_str());
  1226. display_updater();
  1227. ntpsync = 1; //force full redraw
  1228. svf = 1;
  1229. }
  1230. } else if ((pidx = httprq.indexOf("GET /timezone/")) != -1) {
  1231. int pidx2 = httprq.indexOf(" ", pidx + 14);
  1232. if (pidx2 != -1) {
  1233. String tz = httprq.substring(pidx + 14, pidx2);
  1234. strcpy(c_vars[EV_TZ], tz.c_str());
  1235. NTP.begin(ntpsvr, String(c_vars[EV_TZ]).toInt(), toBool(String(c_vars[EV_DST])));
  1236. httprsp += "<strong>timezone:" + tz + "</strong><br>";
  1237. svf = 1;
  1238. } else {
  1239. httprsp += "<strong>!invalid timezone!</strong><br>";
  1240. }
  1241. } else if (httprq.indexOf("GET /weather_animation/on ") != -1) {
  1242. strcpy(c_vars[EV_WANI], "Y");
  1243. httprsp += "<strong>Weather Animation: on</strong><br>";
  1244. TFDrawText(&display, " ", wtext_x, wtext_y, 0);
  1245. getWeather();
  1246. draw_weather_conditions();
  1247. ntpsync = 1;
  1248. svf = 1;
  1249. } else if (httprq.indexOf("GET /weather_animation/off ") != -1) {
  1250. strcpy(c_vars[EV_WANI], "N");
  1251. httprsp += "<strong>Weather Animation: off</strong><br>";
  1252. getWeather();
  1253. draw_weather_conditions();
  1254. ntpsync = 1;
  1255. svf = 1;
  1256. } else if (httprq.indexOf("GET /military/on ") != -1) {
  1257. strcpy(c_vars[EV_24H], "Y");
  1258. httprsp += "<strong>Military Time: on</strong><br>";
  1259. prevhh = -1;
  1260. svf = 1;
  1261. } else if (httprq.indexOf("GET /military/off ") != -1) {
  1262. strcpy(c_vars[EV_24H], "N");
  1263. httprsp += "<strong>Military Time: off</strong><br>";
  1264. prevhh = -1;
  1265. svf = 1;
  1266. }
  1267. //Reset Config file
  1268. else if (httprq.indexOf("GET /reset_config_file ") != -1) {
  1269. init_config_vars();
  1270. vars_write();
  1271. vars_read();
  1272. httprsp += "<strong>Config file resetted</strong><br>";
  1273. } else if ((pidx = httprq.indexOf("GET /colorpalet/")) != -1) {
  1274. int pidx2 = httprq.indexOf(" ", pidx + 16);
  1275. if (pidx2 != -1) {
  1276. String pal = httprq.substring(pidx + 16, pidx2);
  1277. strcpy(c_vars[EV_PALET], pal.c_str());
  1278. httprsp += "<strong>Color Palet:" + pal + "</strong><br>";
  1279. svf = 1;
  1280. rst = 1;
  1281. }
  1282. }
  1283.  
  1284. //
  1285. httprsp += "<br>MORPH CLOCK CONFIG<br>";
  1286. httprsp += "<br>Use the following configuration links<br>";
  1287. httprsp += "<a href='/daylight/on'>Daylight Savings on</a>&nbsp &nbsp &nbsp";
  1288. httprsp += "<a href='/daylight/off'>Daylight Savings off</a><br><br>";
  1289. httprsp += "<a href='/military/on'>Military Time on</a>&nbsp &nbsp &nbsp";
  1290. httprsp += "<a href='/military/off'>Military Time off</a><br><br>";
  1291. httprsp += "<a href='/metric/on'>Metric System</a>&nbsp &nbsp &nbsp";
  1292. httprsp += "<a href='/metric/off'>Imperial System</a><br><br>";
  1293. httprsp += "<a href='/weather_animation/on'>Weather Animation on</a>&nbsp &nbsp &nbsp";
  1294. httprsp += "<a href='/weather_animation/off'>Weather Animation off</a><br><br>";
  1295.  
  1296. httprsp += "<a href='/timezone/-5'>East Coast USA</a>&nbsp &nbsp &nbsp";
  1297. httprsp += "<a href='/timezone/-6'>Central USA</a>&nbsp &nbsp &nbsp";
  1298. httprsp += "<a href='/timezone/-7'>Mountain USA</a>&nbsp &nbsp &nbsp";
  1299. httprsp += "<a href='/timezone/-8'>Pacific USA</a><br>";
  1300. httprsp += "use /timezone/x for timezone 'x'<br><br>";
  1301.  
  1302. httprsp += "<a href='/colorpalet/1'>Clock Color Cyan</a>&nbsp &nbsp &nbsp";
  1303. httprsp += "<a href='/colorpalet/2'>Clock Color Red</a>&nbsp &nbsp &nbsp";
  1304. httprsp += "<a href='/colorpalet/3'>Clock Color Blue</a>&nbsp &nbsp &nbsp<br>";
  1305. httprsp += "<a href='/colorpalet/4'>Clock Color Yellow</a>&nbsp &nbsp &nbsp";
  1306. httprsp += "<a href='/colorpalet/5'>Clock Color Bright Blue</a>&nbsp &nbsp &nbsp";
  1307. httprsp += "<a href='/colorpalet/6'>Clock Color Orange</a>&nbsp &nbsp &nbsp";
  1308. httprsp += "<a href='/colorpalet/7'>Clock Color Green</a>&nbsp &nbsp &nbsp<br><br>";
  1309.  
  1310. httprsp += "<a href='/brightness/70'>Brightness 70</a>&nbsp &nbsp &nbsp";
  1311. httprsp += "<a href='/brightness/35'>Brightness 35</a>&nbsp &nbsp &nbsp";
  1312. httprsp += "<a href='/brightness/0'>Turn off display</a><br>";
  1313. httprsp += "Use /brightness/x for display brightness 'x'<br>";
  1314.  
  1315. //openweathermap.org
  1316. httprsp += "<br>openweathermap.org API key<br>";
  1317. httprsp += "<form action='/owm/'>"
  1318. "http://<input type='text' size=\"35\" name='owmkey' value='"
  1319. + String(c_vars[EV_OWMK]) + "'>(hex string)<br>"
  1320. "<input type='submit' value='set OWM key'></form><br>";
  1321.  
  1322. //geo location
  1323. httprsp += "<br>Location: City,Country<br>";
  1324. httprsp += "<form action='/geoloc/'>"
  1325. "http://<input type='text' name='geoloc' value='"
  1326. + String(c_vars[EV_GEOLOC]) + "'>(e.g.: New York City,NY)<br>"
  1327. "<input type='submit' value='set Location'></form><br>";
  1328.  
  1329. //GQ
  1330. //I have no idea what someone intended for this to do?
  1331. //EV_OTA is not accessed in the code for any routines
  1332. //I left the vars in place but will comment out in the web interface since it does nothing
  1333. //
  1334. //OTA
  1335. // httprsp += "<br>OTA update configuration (every minute)<br>";
  1336. // httprsp += "<form action='/ota/'>" \
  1337. // "http://<input type='text' name='otaloc' value='" + String(c_vars[EV_OTA]) + "'>(ip address:port/filename)<br>" \
  1338. // "<input type='submit' value='set OTA location'></form><br>";
  1339.  
  1340. httprsp += "<br>wifi configuration<br>";
  1341. httprsp += "<form action='/wifi/'>"
  1342. "ssid:<input type='text' name='ssid'>"
  1343. + String(c_vars[EV_SSID]) + "<br>"
  1344. "pass:<input type='text' name='pass'>"
  1345. + String(c_vars[EV_PASS]) + "<br>"
  1346. "<input type='submit' value='set wifi'></form><br>";
  1347.  
  1348.  
  1349. //Reset config file (You probably will never need to but it's really handy for debugging)
  1350. httprsp += "<a href='/reset_config_file'>Reset Config file to defaults</a><br><br>";
  1351.  
  1352. httprsp += "Current Configuration<br>";
  1353. httprsp += "Daylight: " + String(c_vars[EV_DST]) + "<br>";
  1354. httprsp += "Military: " + String(c_vars[EV_24H]) + "<br>";
  1355. httprsp += "Metric: " + String(c_vars[EV_METRIC]) + "<br>";
  1356. httprsp += "Timezone: " + String(c_vars[EV_TZ]) + "<br>";
  1357. httprsp += "Weather Animation: " + String(c_vars[EV_WANI]) + "<br>";
  1358. httprsp += "Color palette: " + String(c_vars[EV_PALET]) + "<br>";
  1359. httprsp += "Brightness: " + String(c_vars[EV_BRIGHT]) + "<br>";
  1360. httprsp += "<br><a href='/'>home</a><br>";
  1361. httprsp += "<br>"
  1362. "<script language='javascript'>"
  1363. "var today = new Date();"
  1364. "var hh = today.getHours();"
  1365. "var mm = today.getMinutes();"
  1366. "if(hh<10)hh='0'+hh;"
  1367. "if(mm<59)mm=1+mm;"
  1368. "if(mm<10)mm='0'+mm;"
  1369. "var dd = today.getDate();"
  1370. "var MM = today.getMonth()+1;"
  1371. "if(dd<10)dd='0'+dd;"
  1372. "if(MM<10)MM='0'+MM;"
  1373. "var yyyy = today.getFullYear();"
  1374. "document.write('set date and time to <a href=/datetime/'+yyyy+MM+dd+hh+mm+'>'+yyyy+'.'+MM+'.'+dd+' '+hh+':'+mm+':00</a><br>');"
  1375. "document.write('using current date and time '+today);"
  1376. "</script>";
  1377. httprsp += "</html>\r\n";
  1378. httpcli.flush(); //clear previous info in the stream
  1379. httpcli.print(httprsp); // Send the response to the client
  1380. delay(1);
  1381. //save settings?
  1382. if (svf) {
  1383. if (vars_write() > 0)
  1384. Serial.println("Variables stored");
  1385. else
  1386. Serial.println("Variables storing failed");
  1387. }
  1388.  
  1389. if (rst)
  1390. resetclock();
  1391. }
  1392. }
  1393. //
  1394. //Web server end
  1395. //
  1396.  
  1397. //Restart Clock
  1398. void resetclock() {
  1399. display.fillScreen(0);
  1400. TFDrawText(&display, String(" RESTART "), 10, 9, cc_blu);
  1401. TFDrawText(&display, String("MORPH CLOCK"), 10, 16, cc_blu);
  1402. delay(2000);
  1403. ESP.reset();
  1404. }
  1405.  
  1406. void draw_am_pm() {
  1407. // this sets AM/PM display and is disabled when military time is used
  1408. if (String(c_vars[EV_24H]) == "N") {
  1409. if (hh >= 12)
  1410. TFDrawText(&display, String(" PM"), 42, 19, cc_time);
  1411. else
  1412. TFDrawText(&display, String(" AM"), 42, 19, cc_time);
  1413. }
  1414. }
  1415.  
  1416. void set_digit_color() {
  1417. digit0.SetColor(cc_time);
  1418. digit1.SetColor(cc_time);
  1419. digit2.SetColor(cc_time);
  1420. digit3.SetColor(cc_time);
  1421. digit4.SetColor(cc_time);
  1422.  
  1423. // Don't print leading zero if not Military
  1424. if (String(c_vars[EV_24H]) == "N" && hh < 10)
  1425. digit5.SetColor(cc_blk);
  1426. else
  1427. digit5.SetColor(cc_time);
  1428. }
  1429.  
  1430. //
  1431. // Main program
  1432. //
  1433. void loop() {
  1434.  
  1435. digit1.DrawColon(cc_time);
  1436. digit3.DrawColon(cc_time);
  1437.  
  1438. static int i = 0;
  1439. static int last = 0;
  1440. static int cm;
  1441.  
  1442. //handle web server requests
  1443. web_server();
  1444.  
  1445. //animations?
  1446. cm = millis();
  1447. if ((cm - last) > ani_speed) // animation speed
  1448. {
  1449. last = cm;
  1450. i++;
  1451. }
  1452. //time changes every miliseconds, we only want to draw when digits actually change.
  1453.  
  1454. tnow = now();
  1455. hh = hour(tnow);
  1456. mm = minute(tnow);
  1457. ss = second(tnow);
  1458. //
  1459.  
  1460. //GQGQ if (ntpsync or (hh != prevhh)) Fixed morphing bug that required refreshing the screen for hh change
  1461. if (ntpsync) {
  1462.  
  1463. ntpsync = 0;
  1464. //
  1465. prevss = ss;
  1466. prevmm = mm;
  1467. prevhh = hh;
  1468.  
  1469. //we had a sync so draw without morphing
  1470.  
  1471. //clear screen
  1472. display_updater();
  1473. display.fillScreen(0);
  1474. Serial.println("Display cleared");
  1475.  
  1476. //date and weather
  1477. draw_date();
  1478. draw_am_pm();
  1479. draw_weather();
  1480. //
  1481.  
  1482. //military time?
  1483. if (hh > 12 && String(c_vars[EV_24H]) == "N") // when not using military time
  1484. hh -= 12;
  1485. if (hh == 0 && String(c_vars[EV_24H]) == "N") // this makes the first hour of the day 12a when military time isn't used.
  1486. hh += 12;
  1487.  
  1488. set_digit_color();
  1489.  
  1490. digit0.Draw(ss % 10);
  1491. digit1.Draw(ss / 10);
  1492. digit2.Draw(mm % 10);
  1493. digit3.Draw(mm / 10);
  1494. digit4.Draw(hh % 10);
  1495. digit5.Draw(hh / 10);
  1496. } else {
  1497. //seconds
  1498. if (ss != prevss) {
  1499.  
  1500. int s0 = ss % 10;
  1501. int s1 = ss / 10;
  1502. set_digit_color;
  1503.  
  1504. //There is a delay problem when checking the weather site which causes the morph to scramble the digit
  1505. //We must force no morphing to make sure the digit is displayed correctly
  1506.  
  1507. if (morph_off == 0) {
  1508. if (s0 != digit0.Value()) digit0.Morph(s0);
  1509. } else {
  1510. digit0.SetColor(cc_blk); //This else block is currently not used
  1511. digit0.Draw(8); //Turn off all segments to black
  1512. morph_off = 0;
  1513. digit0.SetColor(cc_time);
  1514. digit0.Draw(s0);
  1515. }
  1516. if (s1 != digit1.Value()) digit1.Morph(s1);
  1517.  
  1518. prevss = ss;
  1519. //refresh weather at 31sec in the minute
  1520. if (ss == 31 && ((mm % weather_refresh) == 0)) {
  1521. getWeather();
  1522. morph_off = 0; //Currently not using, set to 0 this was to address weather delay
  1523. } else if ((ss % 10) == 0) { // Toggle display every 10 seconds between wind and humidity
  1524. if (wind_humi == 1) {
  1525. TFDrawText(&display, humi_lstr, wind_x, wind_y, cc_wind);
  1526. wind_humi = 0;
  1527. } else {
  1528. draw_wind();
  1529. wind_humi = 1;
  1530. }
  1531. }
  1532. }
  1533. //minutes
  1534. if (mm != prevmm) {
  1535. int m0 = mm % 10;
  1536. int m1 = mm / 10;
  1537. if (m0 != digit2.Value()) digit2.Morph(m0);
  1538. if (m1 != digit3.Value()) digit3.Morph(m1);
  1539. prevmm = mm;
  1540. draw_weather();
  1541. }
  1542. //hours
  1543.  
  1544. if (hh != prevhh) {
  1545. display_updater();
  1546. prevhh = hh;
  1547.  
  1548. draw_date();
  1549. draw_am_pm();
  1550.  
  1551. //military time?
  1552. if (hh > 12 && String(c_vars[EV_24H]) == "N")
  1553. hh -= 12;
  1554. //
  1555. if (hh == 0 && String(c_vars[EV_24H]) == "N") // this makes the first hour of the day 12a when military time isn't used.
  1556. hh += 12;
  1557.  
  1558.  
  1559. int h0 = hh % 10;
  1560. int h1 = hh / 10;
  1561. set_digit_color();
  1562.  
  1563. digit4.Morph(h0);
  1564.  
  1565. if (String(c_vars[EV_24H]) == "N" && hh < 10) //We have to clear leading zero to black
  1566. digit5.Draw(h1);
  1567.  
  1568. if (h1 != digit5.Value()) digit5.Morph(h1);
  1569. } //hh changed
  1570.  
  1571. if (String(c_vars[EV_WANI]) == "Y")
  1572. draw_animations(i);
  1573.  
  1574.  
  1575. //set NTP sync interval as needed
  1576. if (NTP.getInterval() < 3600 && year(now()) > 1970) {
  1577. //reset the sync interval if we're already in sync
  1578. NTP.setInterval(3600 * 24); //re-sync once a day
  1579. Serial.println("24h Sync Enabled");
  1580. }
  1581. //
  1582. //delay (0);
  1583. }
  1584. }
  1585.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement